From 2b10ed6df276c27dac0e6f66922de7c173428dde Mon Sep 17 00:00:00 2001 From: "Bao D. Nguyen" Date: Fri, 13 Mar 2020 16:02:59 -0700 Subject: [PATCH] mmc: sdhci-msm: Port the SD card code to Lahaina platforms Port the regulator, quirks/caps logic from Qualcomm's Kona target to Lahaina target. This is a snapshot of the sdhci-msm driver changes taken as of msm-4.19 commit <74f401c241ba> ("mmc: sdhci-msm: Port base platform driver from 4.14 to 4.19"). Change-Id: I5f5e4af3249ccd8d7f0767e8370860dcae5d78dd Signed-off-by: Bao D. Nguyen --- drivers/mmc/host/sdhci-msm.c | 625 +++++++++++++++++++++++++++++++++-- 1 file changed, 601 insertions(+), 24 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 84b7ad22e272..9d86fe96d303 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #define CORE_VERSION_MAJOR_SHIFT 28 #define CORE_VERSION_MAJOR_MASK (0xf << CORE_VERSION_MAJOR_SHIFT) #define CORE_VERSION_MINOR_MASK 0xff +#define CORE_VERSION_TARGET_MASK 0x000000FF #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) @@ -35,7 +36,9 @@ #define CORE_PWRCTL_IO_LOW BIT(2) #define CORE_PWRCTL_IO_HIGH BIT(3) #define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_BUS_FAIL BIT(1) #define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define CORE_PWRCTL_IO_FAIL BIT(3) #define REQ_BUS_OFF BIT(0) #define REQ_BUS_ON BIT(1) #define REQ_IO_LOW BIT(2) @@ -67,10 +70,13 @@ #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_8_BIT_SUPPORT (1 << 18) #define CORE_3_0V_SUPPORT (1 << 25) #define CORE_1_8V_SUPPORT (1 << 26) #define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT) +#define CORE_SYS_BUS_SUPPORT_64_BIT BIT(28) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIB BIT(16) #define CORE_HW_AUTOCAL_ENA BIT(17) @@ -297,11 +303,70 @@ struct sdhci_msm_regs_restore { u32 dll_usr_ctl; }; +enum vdd_io_level { + /* set vdd_io_data->low_vol_level */ + VDD_IO_LOW, + /* set vdd_io_data->high_vol_level */ + VDD_IO_HIGH, + /* + * set whatever there in voltage_level (third argument) of + * sdhci_msm_set_vdd_io_vol() function. + */ + VDD_IO_SET_LEVEL, +}; + enum dll_init_context { DLL_INIT_NORMAL = 0, DLL_INIT_FROM_CX_COLLAPSE_EXIT, }; +/* This structure keeps information per regulator */ +struct sdhci_msm_reg_data { + /* voltage regulator handle */ + struct regulator *reg; + /* regulator name */ + const char *name; + /* voltage level to be set */ + u32 low_vol_level; + u32 high_vol_level; + /* Load values for low power and high power mode */ + u32 lpm_uA; + u32 hpm_uA; + + /* is this regulator enabled? */ + bool is_enabled; + /* is this regulator needs to be always on? */ + bool is_always_on; + /* is low power mode setting required for this regulator? */ + bool lpm_sup; + bool set_voltage_sup; +}; + +/* + * This structure keeps information for all the + * regulators required for a SDCC slot. + */ +struct sdhci_msm_slot_reg_data { + /* keeps VDD/VCC regulator info */ + struct sdhci_msm_reg_data *vdd_data; + /* keeps VDD IO regulator info */ + struct sdhci_msm_reg_data *vdd_io_data; +}; + +struct sdhci_msm_pltfm_data { + /* Supported UHS-I Modes */ + u32 caps; + + /* More capabilities */ + u32 caps2; + + unsigned long mmc_bus_width; + struct sdhci_msm_slot_reg_data *vreg_data; + bool nonremovable; + bool nonhotplug; + bool largeaddressbus; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -310,6 +375,7 @@ struct sdhci_msm_host { struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/ struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */ unsigned long clk_rate; + struct sdhci_msm_pltfm_data *pdata; struct mmc_host *mmc; bool use_14lpp_dll_reset; bool tuning_done; @@ -331,6 +397,8 @@ struct sdhci_msm_host { bool skip_bus_bw_voting; struct sdhci_msm_bus_vote_data *bus_vote_data; struct delayed_work bus_vote_work; + bool pltfm_init_done; + bool core_3_0v_support; bool use_7nm_dll; struct sdhci_msm_dll_hsr *dll_hsr; struct sdhci_msm_regs_restore regs_restore; @@ -1413,6 +1481,71 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } +#define MAX_PROP_SIZE 32 +static int sdhci_msm_dt_parse_vreg_info(struct device *dev, + struct sdhci_msm_reg_data **vreg_data, const char *vreg_name) +{ + int len, ret = 0; + const __be32 *prop; + char prop_name[MAX_PROP_SIZE]; + struct sdhci_msm_reg_data *vreg; + struct device_node *np = dev->of_node; + + snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name); + if (!of_parse_phandle(np, prop_name, 0)) { + dev_info(dev, "No vreg data found for %s\n", vreg_name); + return ret; + } + + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + ret = -ENOMEM; + return ret; + } + + vreg->name = vreg_name; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-always-on", vreg_name); + if (of_get_property(np, prop_name, NULL)) + vreg->is_always_on = true; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-lpm-sup", vreg_name); + if (of_get_property(np, prop_name, NULL)) + vreg->lpm_sup = true; + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-voltage-level", vreg_name); + prop = of_get_property(np, prop_name, &len); + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_warn(dev, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + } else { + vreg->low_vol_level = be32_to_cpup(&prop[0]); + vreg->high_vol_level = be32_to_cpup(&prop[1]); + } + + snprintf(prop_name, MAX_PROP_SIZE, + "qcom,%s-current-level", vreg_name); + prop = of_get_property(np, prop_name, &len); + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_warn(dev, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + } else { + vreg->lpm_uA = be32_to_cpup(&prop[0]); + vreg->hpm_uA = be32_to_cpup(&prop[1]); + } + + *vreg_data = vreg; + dev_dbg(dev, "%s: %s %s vol=[%d %d]uV, curr=[%d %d]uA\n", + vreg->name, vreg->is_always_on ? "always_on," : "", + vreg->lpm_sup ? "lpm_sup," : "", vreg->low_vol_level, + vreg->high_vol_level, vreg->lpm_uA, vreg->hpm_uA); + + return ret; +} + static int sdhci_msm_dt_parse_hsr_info(struct device *dev, struct sdhci_msm_host *msm_host) @@ -1439,6 +1572,99 @@ skip_hsr: return ret; } +/* Parse platform data */ +static +struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, + struct sdhci_msm_host *msm_host) +{ + struct sdhci_msm_pltfm_data *pdata = NULL; + struct device_node *np = dev->of_node; + u32 bus_width = 0; + int len, i; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto out; + + of_property_read_u32(np, "qcom,bus-width", &bus_width); + if (bus_width == 8) + pdata->mmc_bus_width = MMC_CAP_8_BIT_DATA; + else if (bus_width == 4) + pdata->mmc_bus_width = MMC_CAP_4_BIT_DATA; + else { + dev_notice(dev, "invalid bus-width, default to 1-bit mode\n"); + pdata->mmc_bus_width = 0; + } + + pdata->vreg_data = devm_kzalloc(dev, sizeof(struct + sdhci_msm_slot_reg_data), + GFP_KERNEL); + if (!pdata->vreg_data) { + dev_err(dev, "failed to allocate memory for vreg data\n"); + goto out; + } + + if (sdhci_msm_dt_parse_vreg_info(dev, &pdata->vreg_data->vdd_data, + "vdd")) { + dev_err(dev, "failed parsing vdd data\n"); + goto out; + } + if (sdhci_msm_dt_parse_vreg_info(dev, + &pdata->vreg_data->vdd_io_data, + "vdd-io")) { + dev_err(dev, "failed parsing vdd-io data\n"); + goto out; + } + + len = of_property_count_strings(np, "qcom,bus-speed-mode"); + + for (i = 0; i < len; i++) { + const char *name = NULL; + + of_property_read_string_index(np, + "qcom,bus-speed-mode", i, &name); + if (!name) + continue; + + if (!strcmp(name, "HS400_1p8v")) + pdata->caps2 |= MMC_CAP2_HS400_1_8V; + else if (!strcmp(name, "HS400_1p2v")) + pdata->caps2 |= MMC_CAP2_HS400_1_2V; + else if (!strcmp(name, "HS200_1p8v")) + pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR; + else if (!strcmp(name, "HS200_1p2v")) + pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; + else if (!strcmp(name, "DDR_1p8v")) + pdata->caps |= MMC_CAP_1_8V_DDR + | MMC_CAP_UHS_DDR50; + else if (!strcmp(name, "DDR_1p2v")) + pdata->caps |= MMC_CAP_1_2V_DDR + | MMC_CAP_UHS_DDR50; + } + + if (of_get_property(np, "qcom,nonremovable", NULL)) + pdata->nonremovable = true; + + if (of_get_property(np, "qcom,nonhotplug", NULL)) + pdata->nonhotplug = true; + + pdata->largeaddressbus = + of_property_read_bool(np, "qcom,large-address-bus"); + + if (of_get_property(np, "qcom,core_3_0v_support", NULL)) + msm_host->core_3_0v_support = true; + + msm_host->regs_restore.is_supported = + of_property_read_bool(np, "qcom,restore-after-cx-collapse"); + + if (sdhci_msm_dt_parse_hsr_info(dev, msm_host)) + goto out; + + return pdata; +out: + return NULL; +} + static inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host) { init_waitqueue_head(&msm_host->pwr_irq_wait); @@ -1584,6 +1810,244 @@ static int sdhci_msm_clear_pwrctl_status(struct sdhci_host *host, u32 value) return ret; } +/* Regulator utility functions */ +static int sdhci_msm_vreg_init_reg(struct device *dev, + struct sdhci_msm_reg_data *vreg) +{ + int ret = 0; + + /* check if regulator is already initialized? */ + if (vreg->reg) + goto out; + + /* Get the regulator handle */ + vreg->reg = devm_regulator_get(dev, vreg->name); + if (IS_ERR(vreg->reg)) { + ret = PTR_ERR(vreg->reg); + pr_err("%s: devm_regulator_get(%s) failed. ret=%d\n", + __func__, vreg->name, ret); + goto out; + } + + if (regulator_count_voltages(vreg->reg) > 0) { + vreg->set_voltage_sup = true; + /* sanity check */ + if (!vreg->high_vol_level || !vreg->hpm_uA) { + pr_err("%s: %s invalid constraints specified\n", + __func__, vreg->name); + ret = -EINVAL; + } + } + +out: + return ret; +} + +static int sdhci_msm_vreg_set_optimum_mode(struct sdhci_msm_reg_data + *vreg, int uA_load) +{ + int ret = 0; + + /* + * regulators that do not support regulator_set_voltage also + * do not support regulator_set_optimum_mode + */ + if (vreg->set_voltage_sup) { + ret = regulator_set_load(vreg->reg, uA_load); + if (ret < 0) + pr_err("%s: regulator_set_load(reg=%s,uA_load=%d) failed. ret=%d\n", + __func__, vreg->name, uA_load, ret); + else + /* + * regulator_set_load() can return non zero + * value even for success case. + */ + ret = 0; + } + return ret; +} + +static int sdhci_msm_vreg_set_voltage(struct sdhci_msm_reg_data *vreg, + int min_uV, int max_uV) +{ + int ret = 0; + + if (vreg->set_voltage_sup) { + ret = regulator_set_voltage(vreg->reg, min_uV, max_uV); + if (ret) { + pr_err("%s: regulator_set_voltage(%s)failed. min_uV=%d,max_uV=%d,ret=%d\n", + __func__, vreg->name, min_uV, max_uV, ret); + } + } + + return ret; +} + +static int sdhci_msm_vreg_enable(struct sdhci_msm_reg_data *vreg) +{ + int ret = 0; + + /* Put regulator in HPM (high power mode) */ + ret = sdhci_msm_vreg_set_optimum_mode(vreg, vreg->hpm_uA); + if (ret < 0) + return ret; + + if (!vreg->is_enabled) { + /* Set voltage level */ + ret = sdhci_msm_vreg_set_voltage(vreg, vreg->high_vol_level, + vreg->high_vol_level); + if (ret) + return ret; + } + ret = regulator_enable(vreg->reg); + if (ret) { + pr_err("%s: regulator_enable(%s) failed. ret=%d\n", + __func__, vreg->name, ret); + return ret; + } + vreg->is_enabled = true; + return ret; +} + +static int sdhci_msm_vreg_disable(struct sdhci_msm_reg_data *vreg) +{ + int ret = 0; + + /* Never disable regulator marked as always_on */ + if (vreg->is_enabled && !vreg->is_always_on) { + ret = regulator_disable(vreg->reg); + if (ret) { + pr_err("%s: regulator_disable(%s) failed. ret=%d\n", + __func__, vreg->name, ret); + goto out; + } + vreg->is_enabled = false; + + ret = sdhci_msm_vreg_set_optimum_mode(vreg, 0); + if (ret < 0) + goto out; + + /* Set min. voltage level to 0 */ + ret = sdhci_msm_vreg_set_voltage(vreg, 0, vreg->high_vol_level); + if (ret) + goto out; + } else if (vreg->is_enabled && vreg->is_always_on) { + if (vreg->lpm_sup) { + /* Put always_on regulator in LPM (low power mode) */ + ret = sdhci_msm_vreg_set_optimum_mode(vreg, + vreg->lpm_uA); + if (ret < 0) + goto out; + } + } +out: + return ret; +} + +static int sdhci_msm_setup_vreg(struct sdhci_msm_pltfm_data *pdata, + bool enable, bool is_init) +{ + int ret = 0, i; + struct sdhci_msm_slot_reg_data *curr_slot; + struct sdhci_msm_reg_data *vreg_table[2]; + + curr_slot = pdata->vreg_data; + if (!curr_slot) { + pr_debug("%s: vreg info unavailable,assuming the slot is powered by always on domain\n", + __func__); + goto out; + } + + vreg_table[0] = curr_slot->vdd_data; + vreg_table[1] = curr_slot->vdd_io_data; + + for (i = 0; i < ARRAY_SIZE(vreg_table); i++) { + if (vreg_table[i]) { + if (enable) + ret = sdhci_msm_vreg_enable(vreg_table[i]); + else + ret = sdhci_msm_vreg_disable(vreg_table[i]); + if (ret) + goto out; + } + } +out: + return ret; +} + +/* This init function should be called only once for each SDHC slot */ +static int sdhci_msm_vreg_init(struct device *dev, + struct sdhci_msm_pltfm_data *pdata, + bool is_init) +{ + int ret = 0; + struct sdhci_msm_slot_reg_data *curr_slot; + struct sdhci_msm_reg_data *curr_vdd_reg, *curr_vdd_io_reg; + + curr_slot = pdata->vreg_data; + if (!curr_slot) + goto out; + + curr_vdd_reg = curr_slot->vdd_data; + curr_vdd_io_reg = curr_slot->vdd_io_data; + + if (!is_init) + /* Deregister all regulators from regulator framework */ + goto out; + + /* + * Get the regulator handle from voltage regulator framework + * and then try to set the voltage level for the regulator + */ + if (curr_vdd_reg) { + ret = sdhci_msm_vreg_init_reg(dev, curr_vdd_reg); + if (ret) + goto out; + } + if (curr_vdd_io_reg) + ret = sdhci_msm_vreg_init_reg(dev, curr_vdd_io_reg); +out: + if (ret) + dev_err(dev, "vreg reset failed (%d)\n", ret); + + return ret; +} + +static int sdhci_msm_set_vdd_io_vol(struct sdhci_msm_pltfm_data *pdata, + enum vdd_io_level level, + unsigned int voltage_level) +{ + int ret = 0; + int set_level; + struct sdhci_msm_reg_data *vdd_io_reg; + + if (!pdata->vreg_data) + return ret; + + vdd_io_reg = pdata->vreg_data->vdd_io_data; + if (vdd_io_reg && vdd_io_reg->is_enabled) { + switch (level) { + case VDD_IO_LOW: + set_level = vdd_io_reg->low_vol_level; + break; + case VDD_IO_HIGH: + set_level = vdd_io_reg->high_vol_level; + break; + case VDD_IO_SET_LEVEL: + set_level = voltage_level; + break; + default: + pr_err("%s: invalid argument level = %d\n", + __func__, level); + ret = -EINVAL; + return ret; + } + ret = sdhci_msm_vreg_set_voltage(vdd_io_reg, set_level, + set_level); + } + return ret; +} + static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1593,6 +2057,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 pwr_state = 0, io_level = 0; u32 config; const struct sdhci_msm_offset *msm_offset = msm_host->offset; + int ret = 0; irq_status = msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status); @@ -1625,23 +2090,53 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) /* Handle BUS ON/OFF*/ if (irq_status & CORE_PWRCTL_BUS_ON) { + ret = sdhci_msm_setup_vreg(msm_host->pdata, true, false); + if (!ret) + ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, + VDD_IO_HIGH, 0); + if (ret) + irq_ack |= CORE_PWRCTL_BUS_FAIL; + else + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + pwr_state = REQ_BUS_ON; io_level = REQ_IO_HIGH; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; } if (irq_status & CORE_PWRCTL_BUS_OFF) { + if (msm_host->pltfm_init_done) + ret = sdhci_msm_setup_vreg(msm_host->pdata, + false, false); + if (!ret) + ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, + VDD_IO_LOW, 0); + if (ret) + irq_ack |= CORE_PWRCTL_BUS_FAIL; + else + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + pwr_state = REQ_BUS_OFF; io_level = REQ_IO_LOW; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; } /* Handle IO LOW/HIGH */ if (irq_status & CORE_PWRCTL_IO_LOW) { + /* Switch voltage Low */ + ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, VDD_IO_LOW, 0); + if (ret) + irq_ack |= CORE_PWRCTL_IO_FAIL; + else + irq_ack |= CORE_PWRCTL_IO_SUCCESS; + io_level = REQ_IO_LOW; - irq_ack |= CORE_PWRCTL_IO_SUCCESS; } if (irq_status & CORE_PWRCTL_IO_HIGH) { + /* Switch voltage High */ + ret = sdhci_msm_set_vdd_io_vol(msm_host->pdata, VDD_IO_HIGH, 0); + if (ret) + irq_ack |= CORE_PWRCTL_IO_FAIL; + else + irq_ack |= CORE_PWRCTL_IO_SUCCESS; + io_level = REQ_IO_HIGH; - irq_ack |= CORE_PWRCTL_IO_SUCCESS; } /* @@ -2398,11 +2893,87 @@ static const struct sdhci_ops sdhci_msm_ops = { static const struct sdhci_pltfm_data sdhci_msm_pdata = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_SINGLE_POWER_WRITE | - SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .ops = &sdhci_msm_ops, }; +static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host, + struct sdhci_host *host) +{ + u32 version, caps = 0; + u16 minor; + u8 major; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); + + version = msm_host_readl(msm_host, host, + msm_offset->core_mci_version); + + major = (version & CORE_VERSION_MAJOR_MASK) >> + CORE_VERSION_MAJOR_SHIFT; + minor = version & CORE_VERSION_TARGET_MASK; + + caps = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); + + /* + * Starting with SDCC 5 controller (core major version = 1) + * controller won't advertise 3.0v, 1.8v and 8-bit features + * except for some targets. + */ + if (major >= 1 && minor != 0x11 && minor != 0x12) { + struct sdhci_msm_reg_data *vdd_io_reg; + /* + * Enable 1.8V support capability on controllers that + * support dual voltage + */ + vdd_io_reg = msm_host->pdata->vreg_data->vdd_io_data; + if (vdd_io_reg && (vdd_io_reg->high_vol_level > 2700000)) + caps |= CORE_3_0V_SUPPORT; + if (vdd_io_reg && (vdd_io_reg->low_vol_level < 1950000)) + caps |= CORE_1_8V_SUPPORT; + if (msm_host->pdata->mmc_bus_width == MMC_CAP_8_BIT_DATA) + caps |= CORE_8_BIT_SUPPORT; + } + + /* + * SDCC 5 controller with major version 1, minor version 0x34 and later + * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. + */ + if ((major == 1) && (minor < 0x34)) + msm_host->use_cdclp533 = true; + + + if (major == 1 && minor >= 0x42) + msm_host->use_14lpp_dll_reset = true; + + /* Fake 3.0V support for SDIO devices which requires such voltage */ + if (msm_host->core_3_0v_support) { + caps |= CORE_3_0V_SUPPORT; + writel_relaxed((readl_relaxed(host->ioaddr + + SDHCI_CAPABILITIES) | caps), host->ioaddr + + msm_offset->core_vendor_spec_capabilities0); + } + + /* + * Mask 64-bit support for controller with 32-bit address bus so that + * smaller descriptor size will be used and improve memory consumption. + */ + if (!msm_host->pdata->largeaddressbus) + caps &= ~CORE_SYS_BUS_SUPPORT_64_BIT; + + writel_relaxed(caps, host->ioaddr + + msm_offset->core_vendor_spec_capabilities0); + /* keep track of the value in SDHCI_CAPABILITIES */ + msm_host->caps_0 = caps; + + /* 7FF projects with 7nm DLL */ + if ((major == 1) && ((minor == 0x6e) || (minor == 0x71) || + (minor == 0x72))) + msm_host->use_7nm_dll = true; +} + static int sdhci_msm_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -2449,7 +3020,11 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; - sdhci_msm_dt_parse_hsr_info(&pdev->dev, msm_host); + msm_host->pdata = sdhci_msm_populate_pdata(dev, msm_host); + if (!msm_host->pdata) { + dev_err(&pdev->dev, "DT parsing error\n"); + goto pltfm_free; + } msm_host->regs_restore.is_supported = of_property_read_bool(dev->of_node, @@ -2524,6 +3099,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (!msm_host->skip_bus_bw_voting) sdhci_msm_bus_voting(host, true); + /* Setup regulators */ + ret = sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, true); + if (ret) { + dev_err(&pdev->dev, "Regulator setup failed (%d)\n", ret); + goto bus_unregister; + } + if (!msm_host->mci_removed) { core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); msm_host->core_mem = devm_ioremap_resource(&pdev->dev, @@ -2531,7 +3113,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (IS_ERR(msm_host->core_mem)) { ret = PTR_ERR(msm_host->core_mem); - goto bus_unregister; + goto vreg_deinit; } } @@ -2563,15 +3145,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n", core_version, core_major, core_minor); - if (core_major == 1 && core_minor >= 0x42) - msm_host->use_14lpp_dll_reset = true; - - /* - * SDCC 5 controller with major version 1, minor version 0x34 and later - * with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. - */ - if (core_major == 1 && core_minor < 0x34) - msm_host->use_cdclp533 = true; + sdhci_set_default_hw_caps(msm_host, host); /* * Support for some capabilities is not advertised by newer @@ -2587,12 +3161,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (core_major == 1 && core_minor >= 0x49) msm_host->updated_ddr_cfg = true; - /* 7FF projects with 7nm DLL */ - if ((core_major == 1) && ((core_minor == 0x6e) || - (core_minor == 0x71) || - (core_minor == 0x72))) - msm_host->use_7nm_dll = true; - /* * Power on reset state may trigger power irq if previous status of * PWRCTL was either BUS_ON or IO_HIGH_V. So before enabling pwr irq @@ -2612,7 +3180,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); if (msm_host->pwr_irq < 0) { ret = msm_host->pwr_irq; - goto bus_unregister; + goto vreg_deinit; } sdhci_msm_init_pwr_irq_wait(msm_host); @@ -2625,9 +3193,14 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_name(&pdev->dev), host); if (ret) { dev_err(&pdev->dev, "Request IRQ failed (%d)\n", ret); - goto bus_unregister; + goto vreg_deinit; } + msm_host->mmc->caps |= MMC_CAP_AGGRESSIVE_PM; + msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; + + msm_host->pltfm_init_done = true; + pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -2650,6 +3223,8 @@ pm_runtime_disable: pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); +vreg_deinit: + sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); bus_unregister: if (!msm_host->skip_bus_bw_voting) { sdhci_msm_bus_cancel_work_and_set_vote(host, 0); @@ -2676,6 +3251,8 @@ static int sdhci_msm_remove(struct platform_device *pdev) sdhci_remove_host(host, dead); + sdhci_msm_vreg_init(&pdev->dev, msm_host->pdata, false); + pm_runtime_get_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev);