Loading drivers/mmc/host/sdhci-msm.c +608 −24 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading Loading @@ -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) Loading Loading @@ -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 */ Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) Loading @@ -1439,6 +1572,99 @@ static int sdhci_msm_dt_parse_hsr_info(struct device *dev, 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); Loading Loading @@ -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); Loading @@ -1593,6 +2057,8 @@ 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; struct mmc_host *mmc = host->mmc; irq_status = msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status); Loading Loading @@ -1623,25 +2089,61 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) udelay(10); } if (mmc->ops->get_cd && !mmc->ops->get_cd(mmc) && irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW)) { return; } /* 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) { io_level = REQ_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; } if (irq_status & CORE_PWRCTL_IO_HIGH) { io_level = REQ_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; } /* Loading Loading @@ -2398,11 +2900,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; Loading Loading @@ -2449,7 +3027,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, Loading Loading @@ -2524,6 +3106,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, Loading @@ -2531,7 +3120,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; } } Loading Loading @@ -2563,15 +3152,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 Loading @@ -2587,12 +3168,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 Loading @@ -2612,7 +3187,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); Loading @@ -2625,9 +3200,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); Loading @@ -2650,6 +3230,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) 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); Loading @@ -2676,6 +3258,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); Loading Loading
drivers/mmc/host/sdhci-msm.c +608 −24 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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) Loading Loading @@ -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) Loading Loading @@ -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 */ Loading @@ -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; Loading @@ -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; Loading Loading @@ -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) Loading @@ -1439,6 +1572,99 @@ static int sdhci_msm_dt_parse_hsr_info(struct device *dev, 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); Loading Loading @@ -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); Loading @@ -1593,6 +2057,8 @@ 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; struct mmc_host *mmc = host->mmc; irq_status = msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status); Loading Loading @@ -1623,25 +2089,61 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) udelay(10); } if (mmc->ops->get_cd && !mmc->ops->get_cd(mmc) && irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW)) { return; } /* 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) { io_level = REQ_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; } if (irq_status & CORE_PWRCTL_IO_HIGH) { io_level = REQ_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; } /* Loading Loading @@ -2398,11 +2900,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; Loading Loading @@ -2449,7 +3027,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, Loading Loading @@ -2524,6 +3106,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, Loading @@ -2531,7 +3120,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; } } Loading Loading @@ -2563,15 +3152,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 Loading @@ -2587,12 +3168,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 Loading @@ -2612,7 +3187,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); Loading @@ -2625,9 +3200,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); Loading @@ -2650,6 +3230,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) 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); Loading @@ -2676,6 +3258,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); Loading