[PATCH 0/5] mmc: sdhci-msm: Corrections to implementation of power irq
Register writes which change voltage of IO lines or turn the IO bus on/off require sdhc controller to be ready before progressing further. Once a register write which affects IO lines is done, the driver should wait for power irq from controller. Once the irq comes, the driver should acknowledge the irq by writing to power control register. If the acknowledgement is not given to controller, the controller may not complete the corresponding register write action and this can mess up the controller if drivers proceeds without power irq completing. Sahitya Tummala (2): mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset mmc: sdhci-msm: Add support to wait for power irq Subhash Jadavani (1): mmc: sdhci-msm: fix issue with power irq Vijay Viswanath (2): mmc: sdhci-msm: Add ops to do sdhc register write defconfig: msm: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS arch/arm64/configs/defconfig | 1 + drivers/mmc/host/sdhci-msm.c | 233 ++- 2 files changed, 229 insertions(+), 5 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 1/5] mmc: sdhci-msm: fix issue with power irq
From: Subhash Jadavani SDCC controller reset (SW_RST) during probe may trigger power irq if previous status of PWRCTL was either BUS_ON or IO_HIGH_V. So before we enable the power irq interrupt in GIC (by registering the interrupt handler), we need to ensure that any pending power irq interrupt status is acknowledged otherwise power irq interrupt handler would be fired prematurely. Signed-off-by: Subhash Jadavani Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 9d601dc..0957199 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1128,6 +1128,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) u16 host_version, core_minor; u32 core_version, config; u8 core_major; + u32 irq_status, irq_ctl; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) @@ -1250,6 +1251,28 @@ static int sdhci_msm_probe(struct platform_device *pdev) CORE_VENDOR_SPEC_CAPABILITIES0); } + /* +* 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 +* interrupt in GIC, any pending power irq interrupt should be +* acknowledged. Otherwise power irq interrupt handler would be +* fired prematurely. +*/ + + irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + irq_ctl = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL); + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) + irq_ctl |= CORE_PWRCTL_BUS_SUCCESS; + if (irq_status & (CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW)) + irq_ctl |= CORE_PWRCTL_IO_SUCCESS; + writel_relaxed(irq_ctl, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* Ensure that above writes are propogated before interrupt enablement +* in GIC. +*/ + mb(); + /* Setup IRQ for handling power/voltage tasks with PMIC */ msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); if (msm_host->pwr_irq < 0) { @@ -1259,6 +1282,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto clk_disable; } + /* Enable pwr irq interrupts */ + writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, dev_name(&pdev->dev), host); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 2/5] mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset
From: Sahitya Tummala There is a rare scenario in HW, where the first clear pulse could be lost when the actual reset and clear/read of status register are happening at the same time. Fix this by retrying upto 10 times to ensure the status register gets cleared. Otherwise, this will lead to a spurious power IRQ which results in system instability. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 43 --- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 0957199..f3e0489 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -995,17 +995,51 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } -static void sdhci_msm_voltage_switch(struct sdhci_host *host) +static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", + mmc_hostname(host->mmc), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); +} + +static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; + int retry = 10; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + /* +* There is a rare HW scenario where the first clear pulse could be +* lost when actual reset and clear/read of status register is +* happening at a time. Hence, retry for at least 10 times to make +* sure status register is cleared. Otherwise, this will result in +* a spurious power IRQ resulting in system instability. +*/ + while (irq_status & readl_relaxed(msm_host->core_mem + + CORE_PWRCTL_STATUS)) { + if (retry == 0) { + pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", + mmc_hostname(host->mmc), irq_status); + sdhci_msm_dump_pwr_ctrl_regs(host); + WARN_ON(1); + } + writel_relaxed(irq_status, + msm_host->core_mem + CORE_PWRCTL_CLEAR); + retry--; + udelay(10); + } + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) irq_ack |= CORE_PWRCTL_BUS_SUCCESS; if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) @@ -1017,13 +1051,17 @@ static void sdhci_msm_voltage_switch(struct sdhci_host *host) * switches are handled by the sdhci core, so just report success. */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + + pr_debug("%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n", + mmc_hostname(msm_host->mmc), __func__, irq, irq_status, + irq_ack); } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; - sdhci_msm_voltage_switch(host); + sdhci_msm_handle_pwr_irq(host, irq); return IRQ_HANDLED; } @@ -1106,7 +1144,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, - .voltage_switch = sdhci_msm_voltage_switch, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 4/5] mmc: sdhci-msm: Add ops to do sdhc register write
Register writes which change voltage of IO lines or turn the IO bus on/off require controller to be ready before progressing further. When the controller is ready, it will generate a power irq which needs to be handled. The thread which initiated the register write should wait for power irq to complete. This will be done through the new sdhc msm write APIs which will check whether the particular write can trigger a power irq and wait for it with a timeout if it is expected. The SDHC core power control IRQ gets triggered when - * There is a state change in power control bit (bit 0) of SDHCI_POWER_CONTROL register. * There is a state change in 1.8V enable bit (bit 3) of SDHCI_HOST_CONTROL2 register. * Bit 1 of SDHCI_SOFTWARE_RESET is set. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 39 +++ 1 file changed, 39 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 6d3b1fd..6571880 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1250,6 +1250,41 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) __sdhci_msm_set_clock(host, clock); } +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static void __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) +{ + u32 req_type = 0; + + switch (reg) { + case SDHCI_HOST_CONTROL2: + req_type = (val & SDHCI_CTRL_VDD_180) ? REQ_IO_LOW : + REQ_IO_HIGH; + break; + case SDHCI_SOFTWARE_RESET: + if (host->pwr && (val & SDHCI_RESET_ALL)) + req_type = REQ_BUS_OFF; + break; + case SDHCI_POWER_CONTROL: + req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; + break; + } + + if (req_type) + sdhci_msm_check_power_status(host, req_type); +} + +static void sdhci_msm_writew(struct sdhci_host *host, u16 val, int reg) +{ + writew_relaxed(val, host->ioaddr + reg); + __sdhci_msm_check_write(host, val, reg); +} + +static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) +{ + writeb_relaxed(val, host->ioaddr + reg); + __sdhci_msm_check_write(host, val, reg); +} +#endif static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1264,6 +1299,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + .write_w = sdhci_msm_writew, + .write_b = sdhci_msm_writeb, +#endif }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 5/5] defconfig: msm: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS
Enable CONFIG_MMC_SDHCI_IO_ACCESSORS so that SDHC controller specific register read and write APIs, if registered, can be used. Signed-off-by: Vijay Viswanath --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 65cdd87..a3c93ed 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -398,6 +398,7 @@ CONFIG_MMC_SDHCI_CADENCE=y CONFIG_MMC_SDHCI_TEGRA=y CONFIG_MMC_MESON_GX=y CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y CONFIG_MMC_SPI=y CONFIG_MMC_SDHI=y CONFIG_MMC_DW=y -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH 3/5] mmc: sdhci-msm: Add support to wait for power irq
From: Sahitya Tummala Add support API which will check if power irq is expected to be generated and wait for the power irq to come and complete if the irq is expected. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 125 ++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index f3e0489..6d3b1fd 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -123,6 +123,10 @@ #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) #define MSM_MMC_AUTOSUSPEND_DELAY_MS 50 + +/* Timeout value to avoid infinite waiting for pwr_irq */ +#define MSM_PWR_IRQ_TIMEOUT_MS 5000 + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -138,6 +142,11 @@ struct sdhci_msm_host { bool calibration_done; u8 saved_tuning_phase; bool use_cdclp533; + u32 curr_pwr_state; + u32 curr_io_level; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + struct completion pwr_irq_completion; +#endif }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -995,6 +1004,90 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static inline void sdhci_msm_init_pwr_irq_completion( + struct sdhci_msm_host *msm_host) +{ + init_completion(&msm_host->pwr_irq_completion); +} + +static inline void sdhci_msm_complete_pwr_irq_completion( + struct sdhci_msm_host *msm_host) +{ + complete(&msm_host->pwr_irq_completion); +} + +/* + * sdhci_msm_check_power_status API should be called when registers writes + * which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens. + * To what state the register writes will change the IO lines should be passed + * as the argument req_type. This API will check whether the IO line's state + * is already the expected state and will wait for power irq only if + * power irq is expected to be trigerred based on the current IO line state + * and expected IO line state. + */ +static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + unsigned long flags; + bool done = false; + + spin_lock_irqsave(&host->lock, flags); + pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", + mmc_hostname(host->mmc), __func__, req_type, + msm_host->curr_pwr_state, msm_host->curr_io_level); + + /* +* The IRQ for request type IO High/LOW will be generated when - +* there is a state change in 1.8V enable bit (bit 3) of +* SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0 +* which indicates 3.3V IO voltage. So, when MMC core layer tries +* to set it to 3.3V before card detection happens, the +* IRQ doesn't get triggered as there is no state change in this bit. +* The driver already handles this case by changing the IO voltage +* level to high as part of controller power up sequence. Hence, check +* for host->pwr to handle a case where IO voltage high request is +* issued even before controller power up. +*/ + if ((req_type & REQ_IO_HIGH) && !host->pwr) { + pr_debug("%s: do not wait for power IRQ that never comes\n", + mmc_hostname(host->mmc)); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + if ((req_type & msm_host->curr_pwr_state) || + (req_type & msm_host->curr_io_level)) + done = true; + spin_unlock_irqrestore(&host->lock, flags); + /* +* This is needed here to hanlde a case where IRQ gets +* triggered even before this function is called so that +* x->done counter of completion gets reset. Otherwise, +* next call to wait_for_completion returns immediately +* without actually waiting for the IRQ to be handled. +*/ + if (done) + init_completion(&msm_host->pwr_irq_completion); + else if (!wait_for_completion_timeout(&msm_host->pwr_irq_completion, + msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) + __WARN_printf("%s: request(%d) timed out waiting for pwr_irq\n", + mmc_hostname(host->mmc), req_type); + pr_debug("%s: %s: request %d done\n", mmc_hostname(host-&g
[PATCH RFC 0/2] Internal voltage control for platform drivers
Certain SDHC controllers may require that voltage switching happen after sepcial conditions. Added a QUIRK for such controllers to use. For SDHCI-MSM controllers, power irq is a signal from controller to SW that it is ready for voltage switch. So added support to register voltage regulators from the msm driver and use them. Voltage switching from core layer is causing CRC/cmd timeout errors in some chipsets. Tested on: sdm845, db410c Requies patch series:"[PATCH V3 0/4] Changes for SDCC5 version" Vijay Viswanath (2): mmc: sdhci: Allow platform controlled voltage switching mmc: sdhci-msm: Use internal voltage control drivers/mmc/host/sdhci-msm.c | 99 ++-- drivers/mmc/host/sdhci.c | 20 ++--- drivers/mmc/host/sdhci.h | 2 + 3 files changed, 104 insertions(+), 17 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 1/2] mmc: sdhci: Allow platform controlled voltage switching
Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. Add a quirk, which can be used by drivers of such controllers. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci.c | 20 +++- drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..f0346d4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1615,7 +1615,8 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, void sdhci_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) { - if (IS_ERR(host->mmc->supply.vmmc)) + if (IS_ERR(host->mmc->supply.vmmc) || + (host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) sdhci_set_power_noreg(host, mode, vdd); else sdhci_set_power_reg(host, mode, vdd); @@ -2009,7 +2010,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, ctrl &= ~SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - if (!IS_ERR(mmc->supply.vqmmc)) { + if (!IS_ERR(mmc->supply.vqmmc) && + !(host->quirks2 & + SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 3.3V signalling voltage failed\n", @@ -2032,7 +2035,8 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, case MMC_SIGNAL_VOLTAGE_180: if (!(host->flags & SDHCI_SIGNALING_180)) return -EINVAL; - if (!IS_ERR(mmc->supply.vqmmc)) { + if (!IS_ERR(mmc->supply.vqmmc) && + !(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 1.8V signalling voltage failed\n", @@ -3485,7 +3489,10 @@ int sdhci_setup_host(struct sdhci_host *host) * the host can take the appropriate action if regulators are not * available. */ - ret = mmc_regulator_get_supply(mmc); + if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) + ret = mmc_regulator_get_supply(mmc); + else + ret = 0; if (ret) return ret; @@ -3736,7 +3743,10 @@ int sdhci_setup_host(struct sdhci_host *host) /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); + if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) + ret = regulator_enable(mmc->supply.vqmmc); + else + ret = 0; if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 170, 195)) host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 23966f8..3b0c97a 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -450,6 +450,8 @@ struct sdhci_host { * obtainable timeout. */ #define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT(1<<17) +/* Regulator voltage changes are being done from platform layer */ +#define SDHCI_QUIRK2_INTERNAL_PWR_CTL (1<<18) int irq;/* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 2/2] mmc: sdhci-msm: Use internal voltage control
Some sdhci-msm controllers require that voltage switching be done after the HW is ready for it. The HW informs its readiness through power irq. The voltage switching should happen only then. Use the quirk for internal voltage switching and then control the voltage switching using power irq. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 99 ++-- 1 file changed, 87 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index a0dc3e1..ebdde29 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -43,7 +43,9 @@ #define CORE_PWRCTL_IO_LOW BIT(2) #define CORE_PWRCTL_IO_HIGHBIT(3) #define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_BUS_FAILBIT(1) #define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define CORE_PWRCTL_IO_FAIL BIT(3) #define REQ_BUS_OFFBIT(0) #define REQ_BUS_ON BIT(1) #define REQ_IO_LOW BIT(2) @@ -258,6 +260,7 @@ struct sdhci_msm_host { bool mci_removed; const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_offset *offset; + bool pltfm_init_done; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -1314,8 +1317,9 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = host->mmc; u32 irq_status, irq_ack = 0; - int retry = 10; + int retry = 10, ret = 0; u32 pwr_state = 0, io_level = 0; u32 config; const struct sdhci_msm_offset *msm_offset = msm_host->offset; @@ -1351,14 +1355,59 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) /* Handle BUS ON/OFF*/ if (irq_status & CORE_PWRCTL_BUS_ON) { - pwr_state = REQ_BUS_ON; - io_level = REQ_IO_HIGH; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + if (mmc->supply.vmmc) { + ret = regulator_set_load(mmc->supply.vmmc, 80); + ret |= mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + mmc->ios.vdd); + if (ret) + pr_err("%s: vmmc enable failed: %d\n", + mmc_hostname(mmc), ret); + } + if (mmc->supply.vqmmc && !ret) { + ret = regulator_set_load(mmc->supply.vqmmc, 22000); + ret |= mmc_regulator_set_ocr(mmc, mmc->supply.vqmmc, + mmc->ios.vdd); + if (!ret) + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) + pr_err("%s: vqmmc enable failed: %d\n", + mmc_hostname(mmc), ret); + } + if (!ret) { + pwr_state = REQ_BUS_ON; + io_level = REQ_IO_HIGH; + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + } else { + pr_err("%s: BUS_ON req failed(%d). irq_status: 0x%08x\n", + mmc_hostname(mmc), ret, irq_status); + irq_ack |= CORE_PWRCTL_BUS_FAIL; + } } if (irq_status & CORE_PWRCTL_BUS_OFF) { - pwr_state = REQ_BUS_OFF; - io_level = REQ_IO_LOW; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + if (mmc->supply.vmmc && msm_host->pltfm_init_done) { + ret = regulator_set_load(mmc->supply.vmmc, 0); + ret |= mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + mmc->ios.vdd); + if (ret) + pr_err("%s: vqmmc disabling failed: %d\n", + mmc_hostname(mmc), ret); + } + if (mmc->supply.vqmmc && msm_host->pltfm_init_done && !ret) { + ret = regulator_set_load(mmc->supply.vqmmc, 0); + ret |= regulator_disable(mmc->supply.vqmmc); + if (ret) + pr_err("%s: vqmmc disabling failed: %d\n", + mmc_hostname(mmc), ret); + } + if (!ret) { + pwr_state = REQ_BUS_OFF; + io_level = REQ_IO_LOW; + irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + } el
[PATCH V1 0/3] Internal voltage control for platform drivers
Certain SDHC controllers may require that voltage switching happen after sepcial conditions. Added a QUIRK for such controllers to use. For SDHCI-MSM controllers, power irq is a signal from controller to SW that it is ready for voltage switch. So added support to register voltage regulators from the msm driver and use them. Voltage switching from core layer is causing CRC/cmd timeout errors in some chipsets. Changes since RFC: - Added DT option to pass regulator load values the sdhci-msm driver should vote during BUS_ON and BUS_OFF power irq. - Removed quirk and used local flags in sdhci_add_host to to avoid scenario of both sdhci & sdhci-msm layer controlling the regulators. - Introduced two sdhci msm layer APIs to replace the respective sdhci layer APIs. This is again to stop sdhci layer from controlling regulators if sdhci_msm layer is present. Tested on: sdm845 Requies patch series:"[PATCH V3 0/4] Changes for SDCC5 version" Vijay Viswanath (3): mmc: sdhci: Allow platform controlled voltage switching Documentation: sdhci-msm: Add entries for passing load values mmc: sdhci-msm: Use internal voltage control .../devicetree/bindings/mmc/sdhci-msm.txt | 6 + drivers/mmc/host/sdhci-msm.c | 220 +++-- drivers/mmc/host/sdhci.c | 21 +- 3 files changed, 231 insertions(+), 16 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V1 1/3] mmc: sdhci: Allow platform controlled voltage switching
Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. During setup/cleanup of host, check whether regulator enable/disable was already done by platform driver. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci.c | 21 - 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..494a1e2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3472,6 +3472,7 @@ int sdhci_setup_host(struct sdhci_host *host) unsigned int override_timeout_clk; u32 max_clk; int ret; + bool enable_vqmmc = false; WARN_ON(host == NULL); if (host == NULL) @@ -3485,7 +3486,12 @@ int sdhci_setup_host(struct sdhci_host *host) * the host can take the appropriate action if regulators are not * available. */ - ret = mmc_regulator_get_supply(mmc); + if (!mmc->supply.vmmc) { + ret = mmc_regulator_get_supply(mmc); + enable_vqmmc = true; + } else { + ret = 0; + } if (ret) return ret; @@ -3736,7 +3742,10 @@ int sdhci_setup_host(struct sdhci_host *host) /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); + if (enable_vqmmc) + ret = regulator_enable(mmc->supply.vqmmc); + else + ret = 0; if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 170, 195)) host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | @@ -3984,7 +3993,7 @@ int sdhci_setup_host(struct sdhci_host *host) return 0; unreg: - if (!IS_ERR(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && enable_vqmmc) regulator_disable(mmc->supply.vqmmc); undma: if (host->align_buffer) @@ -4002,7 +4011,8 @@ void sdhci_cleanup_host(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; - if (!IS_ERR(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && + (regulator_is_enabled(mmc->supply.vqmmc) > 0)) regulator_disable(mmc->supply.vqmmc); if (host->align_buffer) @@ -4135,7 +4145,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->finish_tasklet); - if (!IS_ERR(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && + (regulator_is_enabled(mmc->supply.vqmmc) > 0)) regulator_disable(mmc->supply.vqmmc); if (host->align_buffer) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V1 2/3] Documentation: sdhci-msm: Add entries for passing load values
The load a particular sdhc controller should request from a regulator is device specific and hence each device should individually vote for the required load. Signed-off-by: Vijay Viswanath --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 6 ++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 502b3b8..ee34f28 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -26,6 +26,11 @@ Required properties: "cal" - reference clock for RCLK delay calibration (optional) "sleep" - sleep clock for RCLK delay calibration (optional) +Optional properties: +- -current-level - specifies load levels for supply during BUS_ON + and BUS_OFF states in power irq. Should be specified in + pairs (lpm, hpm), for BUS_OFF and BUS_ON respectively. + Units uA. Example: sdhc_1: sdhci@f9824900 { @@ -37,6 +42,7 @@ Example: vmmc-supply = <&pm8941_l20>; vqmmc-supply = <&pm8941_s3>; + vqmmc-current-level = <200 22000>; pinctrl-names = "default"; pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>; -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V1 3/3] mmc: sdhci-msm: Use internal voltage control
Some sdhci-msm controllers require that voltage switching be done after the HW is ready for it. The HW informs its readiness through power irq. The voltage switching should happen only then. Use the quirk for internal voltage switching and then control the voltage switching using power irq. Signed-off-by: Asutosh Das Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Veerabhadrarao Badiganti Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 220 --- 1 file changed, 209 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index a0dc3e1..47732a2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -43,7 +43,9 @@ #define CORE_PWRCTL_IO_LOW BIT(2) #define CORE_PWRCTL_IO_HIGHBIT(3) #define CORE_PWRCTL_BUS_SUCCESS BIT(0) +#define CORE_PWRCTL_BUS_FAILBIT(1) #define CORE_PWRCTL_IO_SUCCESS BIT(2) +#define CORE_PWRCTL_IO_FAIL BIT(3) #define REQ_BUS_OFFBIT(0) #define REQ_BUS_ON BIT(1) #define REQ_IO_LOW BIT(2) @@ -236,6 +238,14 @@ struct sdhci_msm_variant_info { const struct sdhci_msm_offset *offset; }; +/* Holds voltage regulator information to be used by the driver*/ +struct sdhci_msm_vreg_info { + bool vmmc_load; + u32 vmmc_level[2]; + bool vqmmc_load; + u32 vqmmc_level[2]; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -258,6 +268,8 @@ struct sdhci_msm_host { bool mci_removed; const struct sdhci_msm_variant_ops *var_ops; const struct sdhci_msm_offset *offset; + struct sdhci_msm_vreg_info vreg_info; + bool pltfm_init_done; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -1314,11 +1326,13 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = host->mmc; u32 irq_status, irq_ack = 0; - int retry = 10; + int retry = 10, ret = 0; u32 pwr_state = 0, io_level = 0; u32 config; const struct sdhci_msm_offset *msm_offset = msm_host->offset; + struct sdhci_msm_vreg_info vreg_info = msm_host->vreg_info; irq_status = msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status); @@ -1351,14 +1365,91 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) /* Handle BUS ON/OFF*/ if (irq_status & CORE_PWRCTL_BUS_ON) { - pwr_state = REQ_BUS_ON; - io_level = REQ_IO_HIGH; - irq_ack |= CORE_PWRCTL_BUS_SUCCESS; + if (!IS_ERR(mmc->supply.vmmc)) { + if (vreg_info.vmmc_load) + ret = regulator_set_load(mmc->supply.vmmc, + vreg_info.vmmc_level[1]); + ret |= mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + mmc->ios.vdd); + if (ret) + pr_err("%s: vmmc enable failed: %d\n", + mmc_hostname(mmc), ret); + } + + if (!IS_ERR(mmc->supply.vqmmc) && !ret) { + struct mmc_ios ios; + + if (vreg_info.vqmmc_load) + ret = regulator_set_load(mmc->supply.vqmmc, + vreg_info.vqmmc_level[1]); + /* +* The IO voltage regulator maynot always support +* a voltage close to vdd. Set IO voltage based on +* capability of the regulator. +*/ + if (msm_host->caps_0 & CORE_3_0V_SUPPORT) + ios.signal_voltage = MMC_SIGNAL_VOLTAGE_330; + else if (msm_host->caps_0 & CORE_1_8V_SUPPORT) + ios.signal_voltage = MMC_SIGNAL_VOLTAGE_180; + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + pr_err("%s: %s: setting signal voltage: %d\n", + mmc_hostname(mmc), __func__, + ios.signal_voltage); + ret |= mmc_regulator_set_vqmmc(mmc, &ios); + } + + if (!ret) + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) + pr_err("%s: vqm
Re: [PATCH RFC 2/7] mmc: core: devfreq: Add devfreq based clock scaling support
Hi Sayali, On 7/13/2018 3:22 PM, Sayali Lokhande wrote: This change adds the use of devfreq to MMC. Both eMMC and SD card will use it. For some workloads, such as video playback, it isn't necessary for these cards to run at high speed. Running at lower frequency, for example 52MHz, in such cases can still meet the deadlines for data transfers. Scaling down the clock frequency dynamically has power savings not only because the bus is running at lower frequency but also has an advantage of scaling down the system core voltage, if supported. Provide an ondemand clock scaling support similar to the cpufreq ondemand governor having two thresholds, up_threshold and down_threshold to decide whether to increase the frequency or scale it down respectively. The sampling interval is in the order of milliseconds. If sampling interval is too low, frequent switching of frequencies can lead to high power consumption and if sampling interval is too high, the clock scaling logic would take long time to realize that the underlying hardware (controller and card) is busy and scale up the clocks. Signed-off-by: Talel Shenhar Signed-off-by: Sayali Lokhande --- .../devicetree/bindings/mmc/sdhci-msm.txt | 10 + drivers/mmc/core/core.c| 560 + drivers/mmc/core/core.h| 7 + drivers/mmc/core/debugfs.c | 46 ++ drivers/mmc/core/host.c| 8 + drivers/mmc/core/mmc.c | 200 +++- drivers/mmc/core/sd.c | 72 ++- drivers/mmc/host/sdhci-msm.c | 37 ++ drivers/mmc/host/sdhci-pltfm.c | 11 + drivers/mmc/host/sdhci.c | 27 + drivers/mmc/host/sdhci.h | 8 + include/linux/mmc/card.h | 5 + include/linux/mmc/host.h | 70 +++ 13 files changed, 1059 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 502b3b8..bd8470a 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -26,6 +26,15 @@ Required properties: "cal" - reference clock for RCLK delay calibration (optional) "sleep" - sleep clock for RCLK delay calibration (optional) +Optional Properties: +- qcom,devfreq,freq-table - specifies supported frequencies for clock scaling. + Clock scaling logic shall toggle between these frequencies based + on card load. In case the defined frequencies are over or below + the supported card frequencies, they will be overridden + during card init. In case this entry is not supplied, + the driver will construct one based on the card + supported max and min frequencies. + The frequencies must be ordered from lowest to highest. Example: sdhc_1: sdhci@f9824900 { @@ -43,6 +52,7 @@ Example: clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; clock-names = "core", "iface"; + qcom,devfreq,freq-table = <5200 2>; }; sdhc_2: sdhci@f98a4900 { diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 281826d..0eaee42 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,556 @@ static inline void mmc_should_fail_request(struct mmc_host *host, #endif /* CONFIG_FAIL_MMC_REQUEST */ +static bool mmc_is_data_request(struct mmc_request *mmc_request) +{ + switch (mmc_request->cmd->opcode) { + case MMC_READ_SINGLE_BLOCK: + case MMC_READ_MULTIPLE_BLOCK: + case MMC_WRITE_BLOCK: + case MMC_WRITE_MULTIPLE_BLOCK: + return true; + default: + return false; + } +} + +static void mmc_clk_scaling_start_busy(struct mmc_host *host, bool lock_needed) +{ + struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling; + + if (!clk_scaling->enable) + return; + + if (lock_needed) + spin_lock_bh(&clk_scaling->lock); + + clk_scaling->start_busy = ktime_get(); + clk_scaling->is_busy_started = true; + + if (lock_needed) + spin_unlock_bh(&clk_scaling->lock); +} + +static void mmc_clk_scaling_stop_busy(struct mmc_host *host, bool lock_needed) +{ + struct mmc_devfeq_clk_scaling *clk_scaling = &host->clk_scaling; + + if (!clk_scaling->enable) + return; + + if (lock_needed) + s
Re: [PATCH V1 1/3] mmc: sdhci: Allow platform controlled voltage switching
Hi Adrian, On 7/25/2018 5:23 PM, Adrian Hunter wrote: On 20/07/18 13:46, Vijay Viswanath wrote: Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. During setup/cleanup of host, check whether regulator enable/disable was already done by platform driver. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci.c | 21 - 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..494a1e2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3472,6 +3472,7 @@ int sdhci_setup_host(struct sdhci_host *host) unsigned int override_timeout_clk; u32 max_clk; int ret; + bool enable_vqmmc = false; WARN_ON(host == NULL); if (host == NULL) @@ -3485,7 +3486,12 @@ int sdhci_setup_host(struct sdhci_host *host) * the host can take the appropriate action if regulators are not * available. */ - ret = mmc_regulator_get_supply(mmc); + if (!mmc->supply.vmmc) { + ret = mmc_regulator_get_supply(mmc); + enable_vqmmc = true; + } else { + ret = 0; + } if (ret) return ret; Why not if (!mmc->supply.vmmc) { ret = mmc_regulator_get_supply(mmc); if (ret) return ret; enable_vqmmc = true; } looks neater. Will do. @@ -3736,7 +3742,10 @@ int sdhci_setup_host(struct sdhci_host *host) /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); + if (enable_vqmmc) + ret = regulator_enable(mmc->supply.vqmmc); Please introduce host->vqmmc_enabled = !ret; Any reason to introduce vqmmc_enabled variable in sdhci_host structure ? We can get around with a local variable and using regulator_is_enabled API. + else + ret = 0; if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 170, 195)) host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | @@ -3984,7 +3993,7 @@ int sdhci_setup_host(struct sdhci_host *host) return 0; unreg: - if (!IS_ERR(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && enable_vqmmc) And just make this if (host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); undma: if (host->align_buffer) @@ -4002,7 +4011,8 @@ void sdhci_cleanup_host(struct sdhci_host *host) { struct mmc_host *mmc = host->mmc; - if (!IS_ERR(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && + (regulator_is_enabled(mmc->supply.vqmmc) > 0)) if (host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); if (host->align_buffer) @@ -4135,7 +4145,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->finish_tasklet); - if (!IS_ERR(mmc->supply.vqmmc)) + if (!IS_ERR(mmc->supply.vqmmc) && + (regulator_is_enabled(mmc->supply.vqmmc) > 0)) if (host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); if (host->align_buffer) -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Thanks, Vijay
[PATCH RFC 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm
>From the HPG: In some platform, SDCC controller can be connected to either an eMMC device or an SD card. The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. For SD usage the default value of this signal is ???0???, and SD driver changes it to ???1??? as a part of voltage switching sequence. For eMMC usage, SW should configure this signal to ???1??? and supply 1.8v to PADs before starting any activity on the eMMC BUS. To set this signal, write the following in the SDC1_SDCC_HC_VENDOR_SPECIFIC_FUNC register: HC_IO_PAD_PWR_SWITCH: bit 16 HC_IO_PAD_PWR_SWITCH_EN: bit 15 Krishna Konda (1): mmc: sdhci-msm: support voltage pad switching Vijay Viswanath (1): mmc: sdhci-msm: Add support to store supported vdd-io voltages drivers/mmc/host/sdhci-msm.c | 76 1 file changed, 76 insertions(+) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 38 ++ 1 file changed, 38 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..5c23e92 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -23,6 +23,7 @@ #include #include "sdhci-pltfm.h" +#include #define CORE_MCI_VERSION 0x50 #define CORE_VERSION_MAJOR_SHIFT 28 @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1313,6 +1318,35 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + int i, count; + u32 caps = 0, vdd_uV; + + if (!IS_ERR(mmc->supply.vqmmc)) { + count = regulator_count_voltages(supply); + if (count < 0) + return count; + for (i = 0; i < count; i++) { + vdd_uV = regulator_list_voltage(supply, i); + if (vdd_uV <= 0) + continue; + if (vdd_uV > 270) + caps |= CORE_3_0V_SUPPORT; + if (vdd_uV < 195) + caps |= CORE_1_8V_SUPPORT; + } + } + msm_host->caps_0 |= caps; + pr_debug("%s: %s: supported caps: 0x%08x\n", mmc_hostname(mmc), + __func__, caps); + + return 0; +} + + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1564,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + ret = sdhci_msm_set_regulator_caps(msm_host); + if (ret) + dev_err(&pdev->dev, "%s: Failed to set regulator caps: %d\n", + __func__, ret); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 2/2] mmc: sdhci-msm: support voltage pad switching
From: Krishna Konda The PADs for sdhc controller are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 38 ++ 1 file changed, 38 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 5c23e92..f5728a8 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,6 +78,8 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) @@ -1166,6 +1168,35 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* SDHC has core_mem and hc_mem device memory and these memory +* addresses do not fall within 1KB region. Hence, any update to +* core_mem address space would require an mb() to ensure this gets +* completed before its next update to registers within hc_mem. +*/ + mb(); + /* +* We should unset IO PAD PWR switch only if the register write can +* set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply is only +* 1.8V. In such targets, even during REQ_IO_HIGH, the IO PAD PWR +* switch must be kept set to reflect actual regulator voltage. This +* way, during initialization of controllers with only 1.8V, we will +* set the IO PAD bit without waiting for a REQ_IO_LOW. +*/ + if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) & + ~CORE_IO_PAD_PWR_SWITCH), host->ioaddr + + CORE_VENDOR_SPEC); + else if ((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) | + CORE_IO_PAD_PWR_SWITCH), host->ioaddr + + CORE_VENDOR_SPEC); + /* Ensure that the IO PAD switches are updated before proceeding */ + mb(); + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1518,6 +1549,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) } /* +* Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH bit can +* be used as required later on. +*/ + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) | + CORE_IO_PAD_PWR_SWITCH_EN), host->ioaddr + + CORE_VENDOR_SPEC); + /* * 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 * interrupt in GIC, any pending power irq interrupt should be -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH V2 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
Hi Dough, Jeremy, On 3/3/2018 4:38 AM, Jeremy McNicoll wrote: On 2018-03-02 10:23 AM, Doug Anderson wrote: Hi, On Sun, Feb 11, 2018 at 10:01 PM, Vijay Viswanath wrote: During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath ---  drivers/mmc/host/sdhci-msm.c | 38 ++  1 file changed, 38 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..5c23e92 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -23,6 +23,7 @@  #include  #include "sdhci-pltfm.h" +#include This is a strange sort order for this include file. Why is it after the local include?  #define CORE_MCI_VERSION  0x50  #define CORE_VERSION_MAJOR_SHIFT  28 @@ -81,6 +82,9 @@  #define CORE_HC_SELECT_IN_HS400   (6 << 19)  #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + Is there something magical about 25 and 26? This is a new caps field, so I'd have expected 0 and 1. Yes, these bits are the same corresponding to the capabilities in the Capabilities Register (offset 0x40). The bit positions become important when capabilities register doesn't show support to some voltages, but we can support those voltages. At that time, we will have to fake capabilities. The changes for those are currently not yet pushed up.  #define CORE_CSR_CDC_CTLR_CFG0 0x130  #define CORE_SW_TRIG_FULL_CALIB   BIT(16)  #define CORE_HW_AUTOCAL_ENA   BIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; +  u32 caps_0;  };  static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1313,6 +1318,35 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type);  } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ +  struct mmc_host *mmc = msm_host->mmc; +  struct regulator *supply = mmc->supply.vqmmc; +  int i, count; +  u32 caps = 0, vdd_uV; + +  if (!IS_ERR(mmc->supply.vqmmc)) { +  count = regulator_count_voltages(supply); +  if (count < 0) +  return count; +  for (i = 0; i < count; i++) { +  vdd_uV = regulator_list_voltage(supply, i); +  if (vdd_uV <= 0) +  continue; +  if (vdd_uV > 270) +  caps |= CORE_3_0V_SUPPORT; +  if (vdd_uV < 195) +  caps |= CORE_1_8V_SUPPORT; +  } Shouldn't you be using regulator_is_supported_voltage() rather than open coding? Also: I've never personally worked on a device where it was used, but there is definitely a concept floating about of a voltage level of 1.2V. Maybe should copy the ranges from mmc_regulator_set_vqmmc()? regulator_is_supported_voltage() checks for a range and it also uses regulator_list_voltage() internally. regulator_list_voltage() is also an exported API for use by drivers AFAIK. Please correct if it is not. Also: seems like you should have some way to deal with "caps" ending up w/ no bits set. IIRC you can have a regulator that can be enabled / disabled but doesn't list a voltage, so if someone messed up their device tree you could end up in this case. Should you print a warning? ...or treat it as if we support "3.0V"? ...or ? I guess it depends on how do you want patch #2 to behave in that case. Both, initialize it to sane value and print something. This way at least you have a good chance of booting and not hard hanging and you are given a reasonable message indicating what needs to be fixed. -jeremy +  } How should things behave if vqmmc is an error? In that case is it important to not set "CORE_IO_PAD_PWR_SWITCH_EN" in patch set #2? ...or should you set "CORE_IO_PAD_PWR_SWITCH_EN" but then make sure you don't set "CORE_IO_PAD_PWR_SWITCH"? Thanks for the suggestion. If the regulators exit and doesn't list the voltages, then I believe initialization itself will not happen. We will not have any available ocr and in sdhci_setup_host it should fail. But these enhancements can be incorporated. Since this patch is already acknowledged, I will incorporate these changes in a subsequent patch. +  msm_host->caps_0 |= caps; +  pr_debug("%s: %s: supported caps: 0x%08x\n", mmc_hostname(mmc), + Â
Re: [PATCH V4 2/2] mmc: sdhci-msm: support voltage pad switching
On 3/29/2018 4:23 AM, Doug Anderson wrote: Hi, On Wed, Mar 28, 2018 at 6:08 AM, Vijay Viswanath wrote: From: Krishna Konda The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 64 ++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2fcd9010..bbf9626 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,12 +78,15 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) #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_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) @@ -1109,7 +1112,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; u32 pwr_state = 0, io_level = 0; - + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1166,6 +1169,45 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* If we don't have info regarding the voltage levels supported by +* regulators, don't change the IO PAD PWR SWITCH. +*/ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + /* Ensure order between core_mem and hc_mem */ + mb(); Like in v2, I don't understand why you need a mb() before the read from CORE_VENDOR_SPEC. No reads or writes to the core_mem will affect the value you're reading here, so you need no barrier. If you need a barrier before the _write_ to CORE_VENDOR_SPEC then add it below. Then in the case where the config doesn't change you have no barriers. + /* +* We should unset IO PAD PWR switch only if the register write +* can set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply +* is only 1.8V. In such targets, even during REQ_IO_HIGH, the +* IO PAD PWR switch must be kept set to reflect actual +* regulator voltage. This way, during initialization of +* controllers with only 1.8V, we will set the IO PAD bit +* without waiting for a REQ_IO_LOW. +*/ For the above comment, what about just: new_config = config if (msm_host->caps_0 == CORE_1_8V_SUPPORT) { new_config |= CORE_IO_PAD_PWR_SWITCH; } else if (msm_host->caps_0 == CORE_3_3V_SUPPORT) { new_config &= ~CORE_IO_PAD_PWR_SWITCH; } else if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { if (io_level & REQ_IO_HIGH) new_config &= ~CORE_IO_PAD_PWR_SWITCH; else if (io_level & REQ_IO_LOW) new_config |= CORE_IO_PAD_PWR_SWITCH; } This looks a big mess of if/else. Does the above implementation have better performance compared to having two if/else with bit operations inside ? The latter looks much cleaner and faster. If regulator only supports 3V and we get a io_low from BUS_OFF ( REQ_IO_LOW should never come if we don't support 1.8V), it is ok to set io pad. if (config != new_config) { ... } AKA: first check if it only supports one voltage and pick that one. Else if it supports both you can use the request. This might be more important if you get rid of the initial setting in sdhci_msm_set_regulator_caps() as I'm suggesting. + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + + if (((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & + CORE_3_0V_SUPPORT)) && + (config & CORE_IO_PAD_PWR_SWITCH)) { +
Re: [PATCH V1 1/3] mmc: sdhci-msm: Define new Register address map
On 5/22/2018 11:39 PM, Evan Green wrote: Hi Vijay, On Thu, May 17, 2018 at 3:30 AM Vijay Viswanath wrote: From: Sayali Lokhande For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. Define a new data structure where we can statically define the address offsets for the registers in different SDCC versions. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 89 1 file changed, 89 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916..2524455 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -137,6 +137,95 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +struct sdhci_msm_offset { + u32 core_hc_mode; + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +static const struct sdhci_msm_offset sdhci_msm_v5_offset = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +static const struct sdhci_msm_offset sdhci_msm_mci_offset = { + .core_hc_mode = 0x78, + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0CC, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xDC, + .core_pwrctl_mask = 0xE0, + .core_pwrctl_clear = 0xE4, + .core_pwrctl_ctl = 0xE8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10C, + .core_vendor_spec_adma_err_addr0 = 0x114, + .core_vendor_spec_adma_err_addr1 = 0x118, + .core_vendor_spec_func2 = 0x110, + .core_vendor_spec_capabilities0 = 0x11C, + .core_ddr_200_cfg = 0x184, + .core_vendor_spec3 = 0x1B0, + .core_dll_config_2 = 0x1B4, + .core_ddr_config = 0x1B8, + .core_ddr_config_2 = 0x1BC, +}; + I notice a lot of these are never used in the subsequent patches of this series. I guess more register definitions are always better than fewer, it's just a shame that they take up space now. Did you just add everything that was different between v4 and v5, or how did you come up with this set? Also, I think lowercase hex letters are preferred. I verified that the v5 register offsets look good, at least for the registers I have documentation for. Yeah, felt it better to include all registers even they are not used currently. Will change to use lowercase hex letters -Evan
Re: [PATCH V1 2/3] mmc: sdhci-msm: Add msm version specific ops and data structures
On 5/22/2018 11:40 PM, Evan Green wrote: On Thu, May 17, 2018 at 3:30 AM Vijay Viswanath wrote: In addition to offsets of certain registers changing, the registers in core_mem have been shifted to HC mem as well. To access these registers, define msm version specific functions. These functions can be loaded into the function pointers at the time of probe based on the msm version detected. Also defind new data structure to hold version specific Ops and register addresses. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 112 +++ 1 file changed, 112 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2524455..bb2bb59 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -226,6 +226,25 @@ struct sdhci_msm_offset { .core_ddr_config_2 = 0x1BC, }; +struct sdhci_msm_variant_ops { + u8 (*msm_readb_relaxed)(struct sdhci_host *host, u32 offset); I don't see any uses of msm_readb_relaxed or msm_writeb_relaxed in this patch or the next one. Are these needed? They are not used as of now. Kept them since they can have use later. Felt it better to define base functions and addresses now itself. + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writeb_relaxed)(u8 val, struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + + Remove extra blank line. struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -245,8 +264,75 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u8 sdhci_msm_mci_variant_readb_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readb_relaxed(msm_host->core_mem + offset); +} + +static u8 sdhci_msm_v5_variant_readb_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readb_relaxed(host->ioaddr + offset); +} + +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writeb_relaxed(u8 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writeb_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writeb_relaxed(u8 val, struct sdhci_host *host, + u32 offset) +{ + writeb_relaxed(val, host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, struct sdhci_host *host, You squeaked over 80 characters here. Move the second parameter down with the third. -Evan Thanks for going through the patch thoroughly. Will address the comments. Thanks, Vijay
Re: [PATCH V1 3/3] mmc: host: Register changes for sdcc V5
On 5/22/2018 11:42 PM, Evan Green wrote: Hi Vijay. Thanks for this patch. On Thu, May 17, 2018 at 3:30 AM Vijay Viswanath wrote: From: Sayali Lokhande For SDCC version 5.0.0 and higher, new compatible string "qcom,sdhci-msm-v5" is added. Based on the msm variant, pick the relevant variant data and use it for register read/write to msm specific registers. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- .../devicetree/bindings/mmc/sdhci-msm.txt | 5 +- drivers/mmc/host/sdhci-msm.c | 344 + 2 files changed, 222 insertions(+), 127 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4..c2b7b2b 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,10 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain "qcom,sdhci-msm-v4" or "qcom,sdhci-msm-v5". +For SDCC version 5.0.0, MCI registers are removed from SDCC +interface and some registers are moved to HC. New compatible +string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb2bb59..408e6b2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -33,16 +33,11 @@ #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) -#define CORE_HC_MODE 0x78 Remove CORE_MCI_VERSION as well. Missed it. Will remove #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 #define CORE_SW_RSTBIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL0xe8 #define CORE_PWRCTL_BUS_OFFBIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_ENBIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_ENBIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +102,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SELBIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK40 @@ -380,10 +368,14 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) u32 wait_cnt = 50; u8 ck_out_en; struct mmc_host *mmc = host->mmc; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; I notice this pattern is pasted all over the place in order to get to the offsets. Maybe a macro or inlined function would be cleaner to get you to directly to the sdhci_msm_offset struct from sdhci_host, rather than this blob of paste soup everywhere. In some places you do seem to use the intermediate locals, so those cases wouldn't need to use the new helper. /* Poll for CK_OUT_EN bit. max. poll time = 50us */ - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN);
[PATCH V3 1/4] mmc: sdhci-msm: Define new Register address map
From: Sayali Lokhande For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. Define a new data structure where we can statically define the address offsets for the registers in different SDCC versions. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Reviewed-by: Evan Green Acked-by: Adrian Hunter --- drivers/mmc/host/sdhci-msm.c | 89 1 file changed, 89 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916..4050c99 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -137,6 +137,95 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +struct sdhci_msm_offset { + u32 core_hc_mode; + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +static const struct sdhci_msm_offset sdhci_msm_v5_offset = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +static const struct sdhci_msm_offset sdhci_msm_mci_offset = { + .core_hc_mode = 0x78, + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0cc, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xdc, + .core_pwrctl_mask = 0xe0, + .core_pwrctl_clear = 0xe4, + .core_pwrctl_ctl = 0xe8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10c, + .core_vendor_spec_adma_err_addr0 = 0x114, + .core_vendor_spec_adma_err_addr1 = 0x118, + .core_vendor_spec_func2 = 0x110, + .core_vendor_spec_capabilities0 = 0x11c, + .core_ddr_200_cfg = 0x184, + .core_vendor_spec3 = 0x1b0, + .core_dll_config_2 = 0x1b4, + .core_ddr_config = 0x1b8, + .core_ddr_config_2 = 0x1bc, +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V3 0/4] Changes for SDCC5 version
With SDCC5, the MCI register space got removed and the offset/order of several registers have changed. Based on SDCC version used and the register, we need to pick the base address and offset. Depends on patch series: "[PATCH V5 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm" Changes since RFC: Dropped voltage regulator changes in sdhci-msm Split the "Register changes for sdcc V5" patch Instead of checking mci removal for deciding which base addr to use, new function pointers are defined for the 2 variants of sdcc: 1) MCI present 2) V5 (mci removed) Instead of string comparing with the compatible string from DT file, the sdhci_msm_probe will now pick the data associated with the compatible entry and use it to load variant specific address offsets and msm variant specific read/write ops. Changes since V1: Removed unused msm_reab & msm_writeb APIs Changed certain register addresses from uppercase to lowercase hex letters Removed extra lines and spaces Split "[PATCH V1 0/3] Changes for SDCC5 version" patch into two, one for Documentation and other for the driver changes. Changes since V2: Used lower case for macro function defenitions Removed unused function pointers for msm_readb & msm_writeb Sayali Lokhande (3): mmc: sdhci-msm: Define new Register address map Documentation: sdhci-msm: Add new compatible string for SDCC v5 mmc: host: Register changes for sdcc V5 Vijay Viswanath (1): mmc: sdhci-msm: Add msm version specific ops and data structures .../devicetree/bindings/mmc/sdhci-msm.txt | 7 +- drivers/mmc/host/sdhci-msm.c | 511 - 2 files changed, 391 insertions(+), 127 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V3 2/4] mmc: sdhci-msm: Add msm version specific ops and data structures
In addition to offsets of certain registers changing, the registers in core_mem have been shifted to HC mem as well. To access these registers, define msm version specific functions. These functions can be loaded into the function pointers at the time of probe based on the msm version detected. Also defind new data structure to hold version specific Ops and register addresses. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Reviewed-by: Evan Green --- drivers/mmc/host/sdhci-msm.c | 75 1 file changed, 75 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4050c99..3d01bc2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -226,6 +226,22 @@ struct sdhci_msm_offset { .core_ddr_config_2 = 0x1bc, }; +struct sdhci_msm_variant_ops { + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -245,8 +261,45 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + writel_relaxed(val, host->ioaddr + offset); +} + static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, unsigned int clock) { @@ -1481,6 +1534,28 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static const struct sdhci_msm_variant_ops mci_var_ops = { + .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_ops v5_var_ops = { + .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_mci_var = { + .mci_removed = false, + .var_ops = &mci_var_ops, + .offset = &sdhci_msm_mci_offset, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { + .mci_removed = true, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V3 3/4] Documentation: sdhci-msm: Add new compatible string for SDCC v5
From: Sayali Lokhande For SDCC version 5.0.0 and higher, new compatible string "qcom,sdhci-msm-v5" is added. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Acked-by: Rob Herring --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4..502b3b8 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,12 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain: + "qcom,sdhci-msm-v4" for sdcc versions less than 5.0 + "qcom,sdhci-msm-v5" for sdcc versions >= 5.0 + For SDCC version 5.0.0, MCI registers are removed from SDCC + interface and some registers are moved to HC. New compatible + string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V3 4/4] mmc: host: Register changes for sdcc V5
From: Sayali Lokhande Add support to use the new compatible string "qcom,sdhci-msm-v5". Based on the msm variant, pick the relevant variant data and use it for register read/write to msm specific registers. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath Reviewed-by: Evan Green --- drivers/mmc/host/sdhci-msm.c | 347 +++ 1 file changed, 221 insertions(+), 126 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3d01bc2..418dbb0 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -33,16 +33,11 @@ #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) -#define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 #define CORE_SW_RSTBIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL0xe8 #define CORE_PWRCTL_BUS_OFFBIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_ENBIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_ENBIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +102,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SELBIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK40 @@ -137,6 +125,12 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +#define msm_host_readl(msm_host, host, offset) \ + msm_host->var_ops->msm_readl_relaxed(host, offset) + +#define msm_host_writel(msm_host, val, host, offset) \ + msm_host->var_ops->msm_writel_relaxed(val, host, offset) + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -266,6 +260,14 @@ struct sdhci_msm_host { const struct sdhci_msm_offset *offset; }; +static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return msm_host->offset; +} + /* * APIs to read/write to vendor specific registers which were there in the * core_mem region before MCI was removed. @@ -347,10 +349,12 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) u32 wait_cnt = 50; u8 ck_out_en; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Poll for CK_OUT_EN bit. max. poll time = 50us */ - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); while (ck_out_en != poll) { if (--wait_cnt == 0) { @@ -360,8 +364,8 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) } udelay(1); - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); } return 0; @@ -377,16 +381,18 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) unsigned long flags; u32 config; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); if (phase > 0xf) return -EINVAL; spin
Re: [PATCH v2 4/4] mmc: sdhci-msm: Add sdhci msm register write APIs which wait for pwr irq
Hi Georgi, Thanks for testing the patch on 8096 and pointing out this issue. The issue is coming because, when card is removed, the HOST_CONTROL2 register is retaining the 1.8V Signalling enable bit till SDHCI reset happens after a new card is inserted. Adding the change you suggested can avoid this wait, but I feel a better solution is to clear the 1.8V signalling bit when the card is removed. When a new card is inserted, we shouldn't be keeping the 1.8V bit set until we send cmd11 to the SD card. A new SD card should start with 3V. One solution is to explicitly clear the HOST_CONTROL2 register when card is removed. Other way is to revert the commit: 9718f84b85396e090ca42fafa730410d286d61e3 "mmc: sdhci-msm: Do not reset the controller if no card in the slot" The sdhci-msm doesn't require "SDHCI_QUIRK_NO_CARD_NO_RESET". The issue which above commit is trying to avoid is fixed by the pwr-irq patches. Resetting the controller will clear the HOST_CONTROL2 register and avoid this issue. Can you please try this ? I tested reverting the QUIRK on two platforms: db410c(8916) and sdm845. SD card insert/remove worked fine after that and I didn't get any "Reset 0x1 never completed" error during card insert/remove or shutdown. Thanks, Vijay On 5/29/2018 5:49 PM, Georgi Djakov wrote: Hello Vijay, On 09/27/2017 08:34 AM, Vijay Viswanath wrote: Register writes which change voltage of IO lines or turn the IO bus on/off require controller to be ready before progressing further. When the controller is ready, it will generate a power irq which needs to be handled. The thread which initiated the register write should wait for power irq to complete. This will be done through the new sdhc msm write APIs which will check whether the particular write can trigger a power irq and wait for it with a timeout if it is expected. The SDHC core power control IRQ gets triggered when - * There is a state change in power control bit (bit 0) of SDHCI_POWER_CONTROL register. * There is a state change in 1.8V enable bit (bit 3) of SDHCI_HOST_CONTROL2 register. * Bit 1 of SDHCI_SOFTWARE_RESET is set. Also add support APIs which are used by sdhc msm write APIs to check if power irq is expected to be generated and wait for the power irq to come and complete if the irq is expected. This patch requires CONFIG_MMC_SDHCI_IO_ACCESSORS to be enabled. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 173 ++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c [..] +/* + * sdhci_msm_check_power_status API should be called when registers writes + * which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens. + * To what state the register writes will change the IO lines should be passed + * as the argument req_type. This API will check whether the IO line's state + * is already the expected state and will wait for power irq only if + * power irq is expected to be trigerred based on the current IO line state + * and expected IO line state. + */ +static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + bool done = false; + + pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", + mmc_hostname(host->mmc), __func__, req_type, + msm_host->curr_pwr_state, msm_host->curr_io_level); + + /* +* The IRQ for request type IO High/LOW will be generated when - +* there is a state change in 1.8V enable bit (bit 3) of +* SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0 +* which indicates 3.3V IO voltage. So, when MMC core layer tries +* to set it to 3.3V before card detection happens, the +* IRQ doesn't get triggered as there is no state change in this bit. +* The driver already handles this case by changing the IO voltage +* level to high as part of controller power up sequence. Hence, check +* for host->pwr to handle a case where IO voltage high request is +* issued even before controller power up. +*/ + if ((req_type & REQ_IO_HIGH) && !host->pwr) { + pr_debug("%s: do not wait for power IRQ that never comes, req_type: %d\n", + mmc_hostname(host->mmc), req_type); + return; + } + if ((req_type & msm_host->curr_pwr_state) || + (req_type & msm_host->curr_io_level)) + done = true; + /* +* This is needed here to handle cases where register writes will +* not change the
Re: [PATCH V2 2/4] mmc: sdhci-msm: Add msm version specific ops and data structures
Hi Stephen, On 6/13/2018 5:06 AM, Stephen Boyd wrote: Quoting Vijay Viswanath (2018-05-29 02:52:39) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4050c99..2a66aa0 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -226,6 +226,24 @@ struct sdhci_msm_offset { .core_ddr_config_2 = 0x1bc, }; +struct sdhci_msm_variant_ops { + u8 (*msm_readb_relaxed)(struct sdhci_host *host, u32 offset); + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writeb_relaxed)(u8 val, struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ This is sort of odd. Usually we have a read/write function that swizzles based on register variants, and that's contained with that function. Now it's the other way. +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -245,8 +263,45 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); Is core_mem assigned in the new hardware? Maybe that needs to be 'repurposed' for vendor specific registers on v5 and renamed to something like msm_host::vendor_base or something like that. There is no core_mem in the new hardware. We can assign hc_mem address to core_mem variable (if SDCC5) and do away with the need of special read/write functions, but I feel thats a bad approach and misleading. +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + writel_relaxed(val, host->ioaddr + offset); +} + static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, unsigned int clock) { @@ -1481,6 +1536,28 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static const struct sdhci_msm_variant_ops mci_var_ops = { + .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_ops v5_var_ops = { + .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_mci_var = { + .mci_removed = 0, Please use true and false instead of 0 and 1 when the type is bool. Will do + .var_ops = &mci_var_ops, + .offset = &sdhci_msm_mci_offset, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { + .mci_removed = 1, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH V2 4/4] mmc: host: Register changes for sdcc V5
On 6/13/2018 4:55 AM, Stephen Boyd wrote: Quoting Vijay Viswanath (2018-05-29 02:52:41) @@ -137,6 +125,12 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +#define MSM_HOST_READL(msm_host, host, offset) \ + msm_host->var_ops->msm_readl_relaxed(host, offset) + +#define MSM_HOST_WRITEL(msm_host, val, host, offset) \ + msm_host->var_ops->msm_writel_relaxed(val, host, offset) Is there a reason these macros are capitalized? We don't have READL and WRITEL macros in the kernel because function-like macros are typically lowercase. will change them to lower case. Didn't notice that... + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -268,6 +262,14 @@ struct sdhci_msm_host { const struct sdhci_msm_offset *offset; }; +const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) static? will do +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return msm_host->offset; +} + -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V1 1/3] mmc: sdhci-msm: Define new Register address map
From: Sayali Lokhande For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. Define a new data structure where we can statically define the address offsets for the registers in different SDCC versions. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 89 1 file changed, 89 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916..2524455 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -137,6 +137,95 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +struct sdhci_msm_offset { + u32 core_hc_mode; + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +static const struct sdhci_msm_offset sdhci_msm_v5_offset = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +static const struct sdhci_msm_offset sdhci_msm_mci_offset = { + .core_hc_mode = 0x78, + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0CC, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xDC, + .core_pwrctl_mask = 0xE0, + .core_pwrctl_clear = 0xE4, + .core_pwrctl_ctl = 0xE8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10C, + .core_vendor_spec_adma_err_addr0 = 0x114, + .core_vendor_spec_adma_err_addr1 = 0x118, + .core_vendor_spec_func2 = 0x110, + .core_vendor_spec_capabilities0 = 0x11C, + .core_ddr_200_cfg = 0x184, + .core_vendor_spec3 = 0x1B0, + .core_dll_config_2 = 0x1B4, + .core_ddr_config = 0x1B8, + .core_ddr_config_2 = 0x1BC, +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V1 3/3] mmc: host: Register changes for sdcc V5
From: Sayali Lokhande For SDCC version 5.0.0 and higher, new compatible string "qcom,sdhci-msm-v5" is added. Based on the msm variant, pick the relevant variant data and use it for register read/write to msm specific registers. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- .../devicetree/bindings/mmc/sdhci-msm.txt | 5 +- drivers/mmc/host/sdhci-msm.c | 344 + 2 files changed, 222 insertions(+), 127 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4..c2b7b2b 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,10 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain "qcom,sdhci-msm-v4" or "qcom,sdhci-msm-v5". +For SDCC version 5.0.0, MCI registers are removed from SDCC +interface and some registers are moved to HC. New compatible +string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb2bb59..408e6b2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -33,16 +33,11 @@ #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) -#define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 #define CORE_SW_RSTBIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL0xe8 #define CORE_PWRCTL_BUS_OFFBIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_ENBIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_ENBIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +102,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SELBIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK40 @@ -380,10 +368,14 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) u32 wait_cnt = 50; u8 ck_out_en; struct mmc_host *mmc = host->mmc; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; /* Poll for CK_OUT_EN bit. max. poll time = 50us */ - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); while (ck_out_en != poll) { if (--wait_cnt == 0) { @@ -393,8 +385,8 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) } udelay(1); - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); } return 0; @@ -410,16 +402,20 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) unsigned long flags; u32 config; struct mmc
[PATCH V1 0/3] Changes for SDCC5 version
With SDCC5, the MCI register space got removed and the offset/order of several registers have changed. Based on SDCC version used and the register, we need to pick the base address and offset. Changes since RFC: Dropped voltage regulator changes in sdhci-msm Split the "Register changes for sdcc V5" patch Instead of checking mci removal for deciding which base addr to use, new function pointers are defined for the 2 variants of sdcc: 1) MCI present 2) V5 (mci removed) Instead of string comparing with the compatible string from DT file, the sdhci_msm_probe will now pick the data associated with the compatible entry and use it to load variant specific address offsets and msm variant specific read/write ops. Sayali Lokhande (2): mmc: sdhci-msm: Define new Register address map mmc: host: Register changes for sdcc V5 Vijay Viswanath (1): mmc: sdhci-msm: Add msm version specific ops and data structures .../devicetree/bindings/mmc/sdhci-msm.txt | 5 +- drivers/mmc/host/sdhci-msm.c | 545 - 2 files changed, 423 insertions(+), 127 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V1 2/3] mmc: sdhci-msm: Add msm version specific ops and data structures
In addition to offsets of certain registers changing, the registers in core_mem have been shifted to HC mem as well. To access these registers, define msm version specific functions. These functions can be loaded into the function pointers at the time of probe based on the msm version detected. Also defind new data structure to hold version specific Ops and register addresses. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 112 +++ 1 file changed, 112 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2524455..bb2bb59 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -226,6 +226,25 @@ struct sdhci_msm_offset { .core_ddr_config_2 = 0x1BC, }; +struct sdhci_msm_variant_ops { + u8 (*msm_readb_relaxed)(struct sdhci_host *host, u32 offset); + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writeb_relaxed)(u8 val, struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -245,8 +264,75 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u8 sdhci_msm_mci_variant_readb_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readb_relaxed(msm_host->core_mem + offset); +} + +static u8 sdhci_msm_v5_variant_readb_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readb_relaxed(host->ioaddr + offset); +} + +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writeb_relaxed(u8 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writeb_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writeb_relaxed(u8 val, struct sdhci_host *host, + u32 offset) +{ + writeb_relaxed(val, host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, struct sdhci_host *host, + u32 offset) +{ + writel_relaxed(val, host->ioaddr + offset); +} + static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, unsigned int clock) { @@ -1481,6 +1567,32 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static const struct sdhci_msm_variant_ops mci_var_ops = { + .msm_readb_relaxed = sdhci_msm_mci_variant_readb_relaxed, + .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, + .msm_writeb_relaxed = sdhci_msm_mci_variant_writeb_relaxed, + .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_ops v5_var_ops = { + .msm_readb_relaxed = sdhci_msm_v5_variant_readb_relaxed, + .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, + .msm_writeb_relaxed = sdhci_msm_v5_variant_writeb_relaxed, + .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, +}; + +static const struct sdhci_
Re: [PATCH RFC 1/2] mmc: sdhci: Allow platform controlled voltage switching
On 7/10/2018 4:37 PM, Adrian Hunter wrote: On 21/06/18 15:23, Vijay Viswanath wrote: Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. Add a quirk, which can be used by drivers of such controllers. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci.c | 20 +++- drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..f0346d4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1615,7 +1615,8 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, void sdhci_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) { - if (IS_ERR(host->mmc->supply.vmmc)) + if (IS_ERR(host->mmc->supply.vmmc) || + (host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) I think you should provide your own ->set_power() instead of this will do sdhci_set_power_noreg(host, mode, vdd); else sdhci_set_power_reg(host, mode, vdd); @@ -2009,7 +2010,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, ctrl &= ~SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - if (!IS_ERR(mmc->supply.vqmmc)) { + if (!IS_ERR(mmc->supply.vqmmc) && + !(host->quirks2 & + SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { And your own ->start_signal_voltage_switch() sdhci_msm_start_signal_voltage_switch() would be an exact copy of sdhci_start_signal_voltage_switch(). will incorporate this if not using quirk. ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 3.3V signalling voltage failed\n", @@ -2032,7 +2035,8 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, case MMC_SIGNAL_VOLTAGE_180: if (!(host->flags & SDHCI_SIGNALING_180)) return -EINVAL; - if (!IS_ERR(mmc->supply.vqmmc)) { + if (!IS_ERR(mmc->supply.vqmmc) && + !(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { ret = mmc_regulator_set_vqmmc(mmc, ios); if (ret) { pr_warn("%s: Switching to 1.8V signalling voltage failed\n", @@ -3485,7 +3489,10 @@ int sdhci_setup_host(struct sdhci_host *host) * the host can take the appropriate action if regulators are not * available. */ - ret = mmc_regulator_get_supply(mmc); + if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) Since we expect mmc_regulator_get_supply() to have been called, this could be: if (!mmc->supply.vmmc) { ret = mmc_regulator_get_supply(mmc); enable_vqmmc = true; } else { ret = 0; } >> + ret = mmc_regulator_get_supply(mmc); + else + ret = 0; if (ret) return ret; @@ -3736,7 +3743,10 @@ int sdhci_setup_host(struct sdhci_host *host) /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); + if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) And this could be: if (enable_vqmmc) ret = regulator_enable(mmc->supply.vqmmc); else ret = 0; > However, you still need to ensure regulator_disable(mmc->supply.vqmmc) is only called if regulator_enable() was called. I missed this. Will cover it. Also I missed one more place where we are doing regulator_disable. During sdhci-msm unbinding, we would end up doing an extra regulator disable (thanks Evan for pointing it out) in sdhci_remove_host. To avoid the quirk( or having any flag), it would require copying the code of sdhci_start_signal_voltage_switch() and sdhci_remove_host() and creating 2 new functions in sdhci_msm layer which would do the exact same as above, with just the regulator parts removed. This looks messy (considering any future changes to the 2 sdhci API will need to be copied to their duplicate sdhci_msm API) and a bit overkill to avoid quirk. At the same time, I don't know how useful such a quirk would be to other platform drivers. Please let me know your view/suggestions. + ret = regulator_enable(mmc-&g
Re: [PATCH RFC 1/2] mmc: sdhci: Allow platform controlled voltage switching
On 7/17/2018 1:00 PM, Adrian Hunter wrote: On 17/07/18 08:14, Vijay Viswanath wrote: On 7/10/2018 4:37 PM, Adrian Hunter wrote: On 21/06/18 15:23, Vijay Viswanath wrote: Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. Add a quirk, which can be used by drivers of such controllers. Signed-off-by: Vijay Viswanath ---  drivers/mmc/host/sdhci.c | 20 +++-  drivers/mmc/host/sdhci.h | 2 ++  2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..f0346d4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1615,7 +1615,8 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,  void sdhci_set_power(struct sdhci_host *host, unsigned char mode,   unsigned short vdd)  { -   if (IS_ERR(host->mmc->supply.vmmc)) +   if (IS_ERR(host->mmc->supply.vmmc) || +   (host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) I think you should provide your own ->set_power() instead of this will do  sdhci_set_power_noreg(host, mode, vdd);  else  sdhci_set_power_reg(host, mode, vdd); @@ -2009,7 +2010,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,  ctrl &= ~SDHCI_CTRL_VDD_180;  sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);  -   if (!IS_ERR(mmc->supply.vqmmc)) { +   if (!IS_ERR(mmc->supply.vqmmc) && +   !(host->quirks2 & +   SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { And your own ->start_signal_voltage_switch() sdhci_msm_start_signal_voltage_switch() would be an exact copy of sdhci_start_signal_voltage_switch(). will incorporate this if not using quirk.  ret = mmc_regulator_set_vqmmc(mmc, ios);  if (ret) {  pr_warn("%s: Switching to 3.3V signalling voltage failed\n", @@ -2032,7 +2035,8 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,  case MMC_SIGNAL_VOLTAGE_180:  if (!(host->flags & SDHCI_SIGNALING_180))  return -EINVAL; -   if (!IS_ERR(mmc->supply.vqmmc)) { +   if (!IS_ERR(mmc->supply.vqmmc) && +   !(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) {  ret = mmc_regulator_set_vqmmc(mmc, ios);  if (ret) {  pr_warn("%s: Switching to 1.8V signalling voltage failed\n", @@ -3485,7 +3489,10 @@ int sdhci_setup_host(struct sdhci_host *host)   * the host can take the appropriate action if regulators are not   * available.   */ -   ret = mmc_regulator_get_supply(mmc); +   if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) Since we expect mmc_regulator_get_supply() to have been called, this could be: if (!mmc->supply.vmmc) {    ret = mmc_regulator_get_supply(mmc);    enable_vqmmc = true; } else {    ret = 0; } +   ret = mmc_regulator_get_supply(mmc); +   else +   ret = 0;  if (ret)  return ret;  @@ -3736,7 +3743,10 @@ int sdhci_setup_host(struct sdhci_host *host)   /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */  if (!IS_ERR(mmc->supply.vqmmc)) { -   ret = regulator_enable(mmc->supply.vqmmc); +   if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) And this could be:    if (enable_vqmmc)    ret = regulator_enable(mmc->supply.vqmmc);    else    ret = 0;  > However, you still need to ensure regulator_disable(mmc->supply.vqmmc) is only called if regulator_enable() was called. I missed this. Will cover it. Also I missed one more place where we are doing regulator_disable. During sdhci-msm unbinding, we would end up doing an extra regulator disable (thanks Evan for pointing it out) in sdhci_remove_host. To avoid the quirk( or having any flag), it would require copying the code of sdhci_start_signal_voltage_switch() and sdhci_remove_host() and creating You do not need to duplicate sdhci_remove_host(), just change it so that it only disables what was enabled i.e. if (host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); Ok, so we will be adding a new flag "vqmmc_enabled" in sdhci_host, ryt ? Just wanted to clarify 2 new functions in sdhci_msm layer which would do the exact same as above, with just the regulator parts removed. This looks messy (considering any future changes to the 2 sdhci API will need to be copied to their duplicate sdhci_msm API) and a bit overkill to avoid quirk. At the same time, I don't know how useful such a quirk would be to other platform drivers. Please let me know your vi
Re: [PATCH RFC 1/2] mmc: sdhci: Allow platform controlled voltage switching
On 7/17/2018 2:12 PM, Adrian Hunter wrote: On 17/07/18 11:40, Vijay Viswanath wrote: On 7/17/2018 1:00 PM, Adrian Hunter wrote: On 17/07/18 08:14, Vijay Viswanath wrote: On 7/10/2018 4:37 PM, Adrian Hunter wrote: On 21/06/18 15:23, Vijay Viswanath wrote: Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. Add a quirk, which can be used by drivers of such controllers. Signed-off-by: Vijay Viswanath ---   drivers/mmc/host/sdhci.c | 20 +++-   drivers/mmc/host/sdhci.h | 2 ++   2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..f0346d4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1615,7 +1615,8 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,   void sdhci_set_power(struct sdhci_host *host, unsigned char mode,    unsigned short vdd)   { -   if (IS_ERR(host->mmc->supply.vmmc)) +   if (IS_ERR(host->mmc->supply.vmmc) || +   (host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) I think you should provide your own ->set_power() instead of this will do   sdhci_set_power_noreg(host, mode, vdd);   else   sdhci_set_power_reg(host, mode, vdd); @@ -2009,7 +2010,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,   ctrl &= ~SDHCI_CTRL_VDD_180;   sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);   -   if (!IS_ERR(mmc->supply.vqmmc)) { +   if (!IS_ERR(mmc->supply.vqmmc) && +   !(host->quirks2 & +   SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { And your own ->start_signal_voltage_switch() sdhci_msm_start_signal_voltage_switch() would be an exact copy of sdhci_start_signal_voltage_switch(). will incorporate this if not using quirk.   ret = mmc_regulator_set_vqmmc(mmc, ios);   if (ret) {   pr_warn("%s: Switching to 3.3V signalling voltage failed\n", @@ -2032,7 +2035,8 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,   case MMC_SIGNAL_VOLTAGE_180:   if (!(host->flags & SDHCI_SIGNALING_180))   return -EINVAL; -   if (!IS_ERR(mmc->supply.vqmmc)) { +   if (!IS_ERR(mmc->supply.vqmmc) && +   !(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) {   ret = mmc_regulator_set_vqmmc(mmc, ios);   if (ret) {   pr_warn("%s: Switching to 1.8V signalling voltage failed\n", @@ -3485,7 +3489,10 @@ int sdhci_setup_host(struct sdhci_host *host)    * the host can take the appropriate action if regulators are not    * available.    */ -   ret = mmc_regulator_get_supply(mmc); +   if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) Since we expect mmc_regulator_get_supply() to have been called, this could be:  if (!mmc->supply.vmmc) { ret = mmc_regulator_get_supply(mmc); enable_vqmmc = true;  } else { ret = 0;  } +   ret = mmc_regulator_get_supply(mmc); +   else +   ret = 0;   if (ret)   return ret;   @@ -3736,7 +3743,10 @@ int sdhci_setup_host(struct sdhci_host *host)    /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */   if (!IS_ERR(mmc->supply.vqmmc)) { -   ret = regulator_enable(mmc->supply.vqmmc); +   if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) And this could be: if (enable_vqmmc) ret = regulator_enable(mmc->supply.vqmmc); else ret = 0;   > However, you still need to ensure regulator_disable(mmc->supply.vqmmc) is only called if regulator_enable() was called. I missed this. Will cover it. Also I missed one more place where we are doing regulator_disable. During sdhci-msm unbinding, we would end up doing an extra regulator disable (thanks Evan for pointing it out) in sdhci_remove_host. To avoid the quirk( or having any flag), it would require copying the code of sdhci_start_signal_voltage_switch() and sdhci_remove_host() and creating You do not need to duplicate sdhci_remove_host(), just change it so that it only disables what was enabled i.e. if (host->vqmmc_enabled)    regulator_disable(mmc->supply.vqmmc); Ok, so we will be adding a new flag "vqmmc_enabled" in sdhci_host, ryt ? Yes Ok. Any particular reason why we are avoiding quirk and instead adding a new flag ? Just wanted to clarify 2 new functions in sdhci_msm layer which would do the exact same as above, with just the regulator parts removed. This looks messy (considering any future changes to the 2 sdhci API will need to
Re: [PATCH RFC 1/2] mmc: sdhci: Allow platform controlled voltage switching
On 7/17/2018 3:24 PM, Adrian Hunter wrote: On 17/07/18 12:45, Vijay Viswanath wrote: On 7/17/2018 2:12 PM, Adrian Hunter wrote: On 17/07/18 11:40, Vijay Viswanath wrote: On 7/17/2018 1:00 PM, Adrian Hunter wrote: On 17/07/18 08:14, Vijay Viswanath wrote: On 7/10/2018 4:37 PM, Adrian Hunter wrote: On 21/06/18 15:23, Vijay Viswanath wrote: Some controllers can have internal mechanism to inform the SW that it is ready for voltage switching. For such controllers, changing voltage before the HW is ready can result in various issues. Add a quirk, which can be used by drivers of such controllers. Signed-off-by: Vijay Viswanath ---    drivers/mmc/host/sdhci.c | 20 +++-    drivers/mmc/host/sdhci.h | 2 ++    2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1c828e0..f0346d4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1615,7 +1615,8 @@ void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode,    void sdhci_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd)    { -   if (IS_ERR(host->mmc->supply.vmmc)) +   if (IS_ERR(host->mmc->supply.vmmc) || +   (host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) I think you should provide your own ->set_power() instead of this will do    sdhci_set_power_noreg(host, mode, vdd);    else    sdhci_set_power_reg(host, mode, vdd); @@ -2009,7 +2010,9 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,    ctrl &= ~SDHCI_CTRL_VDD_180;    sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);    -   if (!IS_ERR(mmc->supply.vqmmc)) { +   if (!IS_ERR(mmc->supply.vqmmc) && +   !(host->quirks2 & +   SDHCI_QUIRK2_INTERNAL_PWR_CTL)) { And your own ->start_signal_voltage_switch() sdhci_msm_start_signal_voltage_switch() would be an exact copy of sdhci_start_signal_voltage_switch(). will incorporate this if not using quirk.    ret = mmc_regulator_set_vqmmc(mmc, ios);    if (ret) {    pr_warn("%s: Switching to 3.3V signalling voltage failed\n", @@ -2032,7 +2035,8 @@ int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,    case MMC_SIGNAL_VOLTAGE_180:    if (!(host->flags & SDHCI_SIGNALING_180))    return -EINVAL; -   if (!IS_ERR(mmc->supply.vqmmc)) { +   if (!IS_ERR(mmc->supply.vqmmc) && +   !(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) {    ret = mmc_regulator_set_vqmmc(mmc, ios);    if (ret) {    pr_warn("%s: Switching to 1.8V signalling voltage failed\n", @@ -3485,7 +3489,10 @@ int sdhci_setup_host(struct sdhci_host *host) * the host can take the appropriate action if regulators are not * available. */ -   ret = mmc_regulator_get_supply(mmc); +   if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) Since we expect mmc_regulator_get_supply() to have been called, this could be:   if (!mmc->supply.vmmc) {  ret = mmc_regulator_get_supply(mmc);  enable_vqmmc = true;   } else {  ret = 0;   } +   ret = mmc_regulator_get_supply(mmc); +   else +   ret = 0;    if (ret)    return ret;    @@ -3736,7 +3743,10 @@ int sdhci_setup_host(struct sdhci_host *host)     /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */    if (!IS_ERR(mmc->supply.vqmmc)) { -   ret = regulator_enable(mmc->supply.vqmmc); +   if (!(host->quirks2 & SDHCI_QUIRK2_INTERNAL_PWR_CTL)) And this could be:  if (enable_vqmmc)  ret = regulator_enable(mmc->supply.vqmmc);  else  ret = 0;    > However, you still need to ensure regulator_disable(mmc->supply.vqmmc) is only called if regulator_enable() was called. I missed this. Will cover it. Also I missed one more place where we are doing regulator_disable. During sdhci-msm unbinding, we would end up doing an extra regulator disable (thanks Evan for pointing it out) in sdhci_remove_host. To avoid the quirk( or having any flag), it would require copying the code of sdhci_start_signal_voltage_switch() and sdhci_remove_host() and creating You do not need to duplicate sdhci_remove_host(), just change it so that it only disables what was enabled i.e.  if (host->vqmmc_enabled) regulator_disable(mmc->supply.vqmmc); Ok, so we will be adding a new flag "vqmmc_enabled" in sdhci_host, ryt ? Yes Ok. Any particular reason why we are avoiding quirk and instead adding a new flag ? It moves more in the direction of letting drivers do what they want, rather than trying to mak
[PATCH V3 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 36 +++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..a822f10 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1103,7 +1108,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; int retry = 10; - int pwr_state = 0, io_level = 0; + u32 pwr_state = 0, io_level = 0; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); @@ -1313,6 +1318,31 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + u32 caps = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (regulator_is_supported_voltage(supply, 170, 195)) + caps |= CORE_1_8V_SUPPORT; + if (regulator_is_supported_voltage(supply, 270, 360)) + caps |= CORE_3_0V_SUPPORT; + + if (!caps) + pr_warn("%s: %s: 1.8/3V not supported for vqmmc\n", + mmc_hostname(mmc), __func__); + } + + msm_host->caps_0 |= caps; + pr_debug("%s: %s: supported caps: 0x%08x\n", mmc_hostname(mmc), + __func__, caps); + + return 0; +} + + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1560,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + ret = sdhci_msm_set_regulator_caps(msm_host); + if (ret) + dev_err(&pdev->dev, "Failed to set regulator caps: %d\n", + ret); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V3 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm
>From the HPG: In some platform, SDCC controller can be connected to either an eMMC device or an SD card. The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. For SD usage the default value of this signal is ???0???, and SD driver changes it to ???1??? as a part of voltage switching sequence. For eMMC usage, SW should configure this signal to ???1??? and supply 1.8v to PADs before starting any activity on the eMMC BUS. To set this signal, write the following in the SDC1_SDCC_HC_VENDOR_SPECIFIC_FUNC register: HC_IO_PAD_PWR_SWITCH: bit 16 HC_IO_PAD_PWR_SWITCH_EN: bit 15 Changes since v1: Modified comments on io_pad related changes. Split some read+modify+write commands to multiple lines Changes since v2: IO_PAD_PWR_SWITCH_EN will be set only if we have info regarding what voltage is suported by the regulators. Replaced regulator_list_voltage() API with regulator_is_supported_voltage(). Krishna Konda (1): mmc: sdhci-msm: support voltage pad switching Vijay Viswanath (1): mmc: sdhci-msm: Add support to store supported vdd-io voltages drivers/mmc/host/sdhci-msm.c | 99 +++- 1 file changed, 97 insertions(+), 2 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V3 2/2] mmc: sdhci-msm: support voltage pad switching
From: Krishna Konda The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 65 ++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index a822f10..2ffb7bb 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,12 +78,15 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) #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_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) @@ -1109,7 +1112,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; u32 pwr_state = 0, io_level = 0; - + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1166,11 +1169,51 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* If we don't have info regarding the voltage levels supported by +* regulators, don't change the IO PAD PWR SWITCH. +*/ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + /* Ensure order between core_mem and hc_mem */ + mb(); + /* +* We should unset IO PAD PWR switch only if the register write +* can set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply +* is only 1.8V. In such targets, even during REQ_IO_HIGH, the +* IO PAD PWR switch must be kept set to reflect actual +* regulator voltage. This way, during initialization of +* controllers with only 1.8V, we will set the IO PAD bit +* without waiting for a REQ_IO_LOW. +*/ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + + if (((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & + CORE_3_0V_SUPPORT)) && + (config & CORE_IO_PAD_PWR_SWITCH)) { + config &= ~CORE_IO_PAD_PWR_SWITCH; + writel_relaxed(config, + host->ioaddr + CORE_VENDOR_SPEC); + /* IO PAD register is in different memory space */ + mb(); + } else if (((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) && + !(config & CORE_IO_PAD_PWR_SWITCH)) { + config |= CORE_IO_PAD_PWR_SWITCH; + writel_relaxed(config, + host->ioaddr + CORE_VENDOR_SPEC); + /* IO PAD bit is in different memory space */ + mb(); + } + } + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) msm_host->curr_io_level = io_level; + pr_debug("%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n", mmc_hostname(msm_host->mmc), __func__, irq, irq_status, irq_ack); @@ -1322,7 +1365,8 @@ static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) { struct mmc_host *mmc = msm_host->mmc; struct regulator *supply = mmc->supply.vqmmc; - u32 caps = 0; + u32 caps = 0, config; + struct sdhci_host *host = mmc_priv(mmc); if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 170, 195)) @@ -1335,6 +1379,23
[PATCH V4 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm
>From the HPG: In some platform, SDCC controller can be connected to either an eMMC device or an SD card. The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. For SD usage the default value of this signal is ???0???, and SD driver changes it to ???1??? as a part of voltage switching sequence. For eMMC usage, SW should configure this signal to ???1??? and supply 1.8v to PADs before starting any activity on the eMMC BUS. To set this signal, write the following in the SDC1_SDCC_HC_VENDOR_SPECIFIC_FUNC register: HC_IO_PAD_PWR_SWITCH: bit 16 HC_IO_PAD_PWR_SWITCH_EN: bit 15 Changes since v1: Modified comments on io_pad related changes. Split some read+modify+write commands to multiple lines Changes since v2: IO_PAD_PWR_SWITCH_EN will be set only if we have info regarding what voltage is supported by the regulators. Replaced regulator_list_voltage() API with regulator_is_supported_voltage(). Changes since v3: Removed unnecessary prints and extra lines. Krishna Konda (1): mmc: sdhci-msm: support voltage pad switching Vijay Viswanath (1): mmc: sdhci-msm: Add support to store supported vdd-io voltages drivers/mmc/host/sdhci-msm.c | 99 +++- 1 file changed, 97 insertions(+), 2 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V4 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 35 ++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..2fcd9010 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1103,7 +1108,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; int retry = 10; - int pwr_state = 0, io_level = 0; + u32 pwr_state = 0, io_level = 0; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); @@ -1313,6 +1318,30 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + u32 caps = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (regulator_is_supported_voltage(supply, 170, 195)) + caps |= CORE_1_8V_SUPPORT; + if (regulator_is_supported_voltage(supply, 270, 360)) + caps |= CORE_3_0V_SUPPORT; + + if (!caps) + pr_warn("%s: %s: 1.8/3V not supported for vqmmc\n", + mmc_hostname(mmc), __func__); + } + + msm_host->caps_0 |= caps; + pr_debug("%s: %s: supported caps: 0x%08x\n", mmc_hostname(mmc), + __func__, caps); + + return 0; +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1559,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + ret = sdhci_msm_set_regulator_caps(msm_host); + if (ret) + dev_err(&pdev->dev, "Failed to set regulator caps: %d\n", + ret); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V4 2/2] mmc: sdhci-msm: support voltage pad switching
From: Krishna Konda The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 64 ++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2fcd9010..bbf9626 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,12 +78,15 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) #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_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) @@ -1109,7 +1112,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; u32 pwr_state = 0, io_level = 0; - + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1166,6 +1169,45 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* If we don't have info regarding the voltage levels supported by +* regulators, don't change the IO PAD PWR SWITCH. +*/ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + /* Ensure order between core_mem and hc_mem */ + mb(); + /* +* We should unset IO PAD PWR switch only if the register write +* can set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply +* is only 1.8V. In such targets, even during REQ_IO_HIGH, the +* IO PAD PWR switch must be kept set to reflect actual +* regulator voltage. This way, during initialization of +* controllers with only 1.8V, we will set the IO PAD bit +* without waiting for a REQ_IO_LOW. +*/ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + + if (((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & + CORE_3_0V_SUPPORT)) && + (config & CORE_IO_PAD_PWR_SWITCH)) { + config &= ~CORE_IO_PAD_PWR_SWITCH; + writel_relaxed(config, + host->ioaddr + CORE_VENDOR_SPEC); + /* IO PAD register is in different memory space */ + mb(); + } else if (((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) && + !(config & CORE_IO_PAD_PWR_SWITCH)) { + config |= CORE_IO_PAD_PWR_SWITCH; + writel_relaxed(config, + host->ioaddr + CORE_VENDOR_SPEC); + /* IO PAD bit is in different memory space */ + mb(); + } + } + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1322,7 +1364,8 @@ static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) { struct mmc_host *mmc = msm_host->mmc; struct regulator *supply = mmc->supply.vqmmc; - u32 caps = 0; + u32 caps = 0, config; + struct sdhci_host *host = mmc_priv(mmc); if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 170, 195)) @@ -1335,6 +1378,23 @@ static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) mmc_hostname(mmc), __func__); } + if (caps) { + /* +* Set the PAD_PWR_SWITCH_EN bi
[PATCH RFC 0/4] Changes for SDCC5 version
With SDCC5, the MCI register space got removed and the offset/order of several registers have changed. Based on SDCC version used and the register, we need to pick the base address and offset. Also power irq is a signal from controller to SW that it is ready for voltage switch. So added support to register voltage regulators in the msm driver and use them. With this core layer will not have to take care of voltage regulators. Chips which are currently using core layer regulator APIs can continue to do so, while newer chips can utilize power irq for voltage switch. Depends on patch series: [PATCH V5 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm https://lkml.org/lkml/2018/4/20/370 Asutosh Das (1): mmc: sdhci-msm: Add and use voltage regulator related APIs Sahitya Tummala (2): host: sdhci: fix current caps when there is no host->vmmc host: sdhci-msm: implement get_current_limit() host op Sayali Lokhande (1): mmc: host: Register changes for sdcc V5 .../devicetree/bindings/mmc/sdhci-msm.txt | 32 +- drivers/mmc/host/sdhci-msm.c | 1027 +--- drivers/mmc/host/sdhci.c | 11 +- drivers/mmc/host/sdhci.h |1 + 4 files changed, 912 insertions(+), 159 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 1/4] mmc: host: Register changes for sdcc V5
From: Sayali Lokhande For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. This change is to support MCI register removal for msmfalcon. New compatible string "qcom,sdhci-msm-v5" is added for msmfalcon to support this change. Change-Id: I0febfd9bb436a8eff20c20107dd4180c9781 Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- .../devicetree/bindings/mmc/sdhci-msm.txt | 5 +- drivers/mmc/host/sdhci-msm.c | 485 +++-- 2 files changed, 365 insertions(+), 125 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4..c2b7b2b 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,10 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain "qcom,sdhci-msm-v4" or "qcom,sdhci-msm-v5". +For SDCC version 5.0.0, MCI registers are removed from SDCC +interface and some registers are moved to HC. New compatible +string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916..d4d432b 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -39,10 +39,6 @@ #define CORE_SW_RSTBIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL0xe8 #define CORE_PWRCTL_BUS_OFFBIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +59,13 @@ #define CORE_CDR_EXT_ENBIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_ENBIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +103,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SELBIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK40 @@ -137,6 +126,93 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +struct sdhci_msm_offset { + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +struct sdhci_msm_offset sdhci_msm_offset_mci_removed = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x2
[PATCH RFC 3/4] host: sdhci: fix current caps when there is no host->vmmc
From: Sahitya Tummala When the regulators are not managed by SDHCI host driver (i.e., when host->vmmc and host->vmmcq are absent), get the regulator current capabilities through a new host op get_current_limit(). Signed-off-by: Sahitya Tummala Signed-off-by: Sayali Lokhande [vvisw...@codeaurora.org: fixed trivial merge conflicts] Signed-off-by: Vijay Viswanath Change-Id: I3370a2411beec1f03cc5f102bf95cd816c60351e --- drivers/mmc/host/sdhci.c | 11 --- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0d5fcca..edc2ccd 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3570,10 +3570,15 @@ int sdhci_setup_host(struct sdhci_host *host) * value. */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); - if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) { - int curr = regulator_get_current_limit(mmc->supply.vmmc); - if (curr > 0) { + if (!max_current_caps) { + u32 curr = 0; + + if (!IS_ERR(mmc->supply.vmmc)) + curr = regulator_get_current_limit(mmc->supply.vmmc); + else if (host->ops->get_current_limit) + curr = host->ops->get_current_limit(host); + if (curr > 0) { /* convert to SDHCI_MAX_CURRENT format */ curr = curr/1000; /* convert to mA */ curr = curr/SDHCI_MAX_CURRENT_MULTIPLIER; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 54bc444..a01af78 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -584,6 +584,7 @@ struct sdhci_ops { void(*adma_workaround)(struct sdhci_host *host, u32 intmask); void(*card_event)(struct sdhci_host *host); void(*voltage_switch)(struct sdhci_host *host); + unsigned int(*get_current_limit)(struct sdhci_host *host); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 4/4] host: sdhci-msm: implement get_current_limit() host op
From: Sahitya Tummala This is needed to get the current capabilities of vdd regulator that is not managed by SDHCI driver. Change-Id: I927c14b9890f1d672fe8a3e89d0b334f43463b36 Signed-off-by: Sahitya Tummala Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 13 + 1 file changed, 13 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 0e0f12d..083b4a5 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2079,6 +2079,18 @@ static void sdhci_msm_set_default_hw_caps(struct device *dev, {}, }; +static unsigned int sdhci_msm_get_current_limit(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct sdhci_msm_reg_data *vdd_data = msm_host->pdata.vdd_data; + u32 max_curr = 0; + + if (vdd_data) + max_curr = vdd_data->hpm_uA; + return max_curr; +} + MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); static const struct sdhci_ops sdhci_msm_ops = { @@ -2090,6 +2102,7 @@ static void sdhci_msm_set_default_hw_caps(struct device *dev, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, .write_w = sdhci_msm_writew, .write_b = sdhci_msm_writeb, + .get_current_limit = sdhci_msm_get_current_limit, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH RFC 2/4] mmc: sdhci-msm: Add and use voltage regulator related APIs
From: Asutosh Das Some platforms require that the voltage switching happen only after the register write occurs and controller is ready for the switch. When the controller is ready, it will inform through power irq. Add voltage regulator APIs and use them during power irq to switch voltage instead of relying on core layer voltage switching. Change-Id: Iaa98686e71a5bfe0092c68e9ffa563b060c5ac60 Signed-off-by: Asutosh Das Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Subhash Jadavani Signed-off-by: Vijay Viswanath --- .../devicetree/bindings/mmc/sdhci-msm.txt | 27 +- drivers/mmc/host/sdhci-msm.c | 537 +++-- 2 files changed, 529 insertions(+), 35 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index c2b7b2b..c454046 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -23,6 +23,22 @@ Required properties: "xo"- TCXO clock (optional) "cal" - reference clock for RCLK delay calibration (optional) "sleep" - sleep clock for RCLK delay calibration (optional) +- -supply: phandle to the regulator device tree node if voltage + regulators needs to be handled from within sdhci-msm layer. + Supported "supply-name" are "vdd" and "vdd-io". + +Optional Properties: + - qcom,-always-on - specifies whether supply should be kept + "on" always. + - qcom,-lpm_sup - specifies whether supply can be kept in low + power mode (lpm). + - qcom,-voltage_level - specifies voltage levels for supply. + Should be specified in pairs (min, max), units uV. + - qcom,-current_level - specifies load levels for supply in lpm + or high power mode (hpm). Should be specified in + pairs (lpm, hpm), units uA. + + Example: @@ -33,8 +49,15 @@ Example: bus-width = <8>; non-removable; - vmmc-supply = <&pm8941_l20>; - vqmmc-supply = <&pm8941_s3>; + vdd-supply = <&pm8941_l20>; + qcom,vdd-voltage-level = <295 295>; + qcom,vdd-current-level = <9000 80>; + + vdd-io-supply = <&pm8941_s3>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <180 295>; + qcom,vdd-io-current-level = <6 22000>; pinctrl-names = "default"; pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index d4d432b..0e0f12d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -213,6 +213,33 @@ struct sdhci_msm_offset sdhci_msm_offset_mci_present = { .core_ddr_config_2 = 0x1BC, }; +/* 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; +}; + +struct sdhci_msm_pltfm_data { + /* Change-Id: Ide3a658ad51a3c3d4a05c47c0e8f013f647c9516 */ + struct sdhci_msm_reg_data *vdd_data; + struct sdhci_msm_reg_data *vdd_io_data; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -234,6 +261,8 @@ struct sdhci_msm_host { u32 caps_0; bool mci_removed; const struct sdhci_msm_offset *offset; + bool pltfm_init_done; + struct sdhci_msm_pltfm_data pdata; }; /* @@ -298,6 +327,336 @@ void sdhci_msm_vendor_writel_relaxed(u32 val, struct sdhci_host *host, writel_relaxed(val, base_addr + offset); } +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, +}; + +#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)
[PATCH V2 0/4] Changes for SDCC5 version
With SDCC5, the MCI register space got removed and the offset/order of several registers have changed. Based on SDCC version used and the register, we need to pick the base address and offset. Changes since RFC: Dropped voltage regulator changes in sdhci-msm Split the "Register changes for sdcc V5" patch Instead of checking mci removal for deciding which base addr to use, new function pointers are defined for the 2 variants of sdcc: 1) MCI present 2) V5 (mci removed) Instead of string comparing with the compatible string from DT file, the sdhci_msm_probe will now pick the data associated with the compatible entry and use it to load variant specific address offsets and msm variant specific read/write ops. Changes since V1: Removed unused msm_reab & msm_writeb APIs Changed certain register addresses from uppercase to lowercase hex letters Removed extra lines and spaces Split "[PATCH V1 0/3] Changes for SDCC5 version" patch into two, one for Documentation and other for the driver changes. Sayali Lokhande (2): mmc: sdhci-msm: Define new Register address map Documentation: sdhci-msm: Add new compatible string for SDCC v5 Vijay Viswanath (2): mmc: sdhci-msm: Add msm version specific ops and data structures mmc: host: Register changes for sdcc V5 .../devicetree/bindings/mmc/sdhci-msm.txt | 7 +- drivers/mmc/host/sdhci-msm.c | 513 - 2 files changed, 393 insertions(+), 127 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V2 1/4] mmc: sdhci-msm: Define new Register address map
From: Sayali Lokhande For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. Define a new data structure where we can statically define the address offsets for the registers in different SDCC versions. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 89 1 file changed, 89 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916..4050c99 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -137,6 +137,95 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +struct sdhci_msm_offset { + u32 core_hc_mode; + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +static const struct sdhci_msm_offset sdhci_msm_v5_offset = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +static const struct sdhci_msm_offset sdhci_msm_mci_offset = { + .core_hc_mode = 0x78, + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0cc, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xdc, + .core_pwrctl_mask = 0xe0, + .core_pwrctl_clear = 0xe4, + .core_pwrctl_ctl = 0xe8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10c, + .core_vendor_spec_adma_err_addr0 = 0x114, + .core_vendor_spec_adma_err_addr1 = 0x118, + .core_vendor_spec_func2 = 0x110, + .core_vendor_spec_capabilities0 = 0x11c, + .core_ddr_200_cfg = 0x184, + .core_vendor_spec3 = 0x1b0, + .core_dll_config_2 = 0x1b4, + .core_ddr_config = 0x1b8, + .core_ddr_config_2 = 0x1bc, +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V2 2/4] mmc: sdhci-msm: Add msm version specific ops and data structures
In addition to offsets of certain registers changing, the registers in core_mem have been shifted to HC mem as well. To access these registers, define msm version specific functions. These functions can be loaded into the function pointers at the time of probe based on the msm version detected. Also defind new data structure to hold version specific Ops and register addresses. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 77 1 file changed, 77 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4050c99..2a66aa0 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -226,6 +226,24 @@ struct sdhci_msm_offset { .core_ddr_config_2 = 0x1bc, }; +struct sdhci_msm_variant_ops { + u8 (*msm_readb_relaxed)(struct sdhci_host *host, u32 offset); + u32 (*msm_readl_relaxed)(struct sdhci_host *host, u32 offset); + void (*msm_writeb_relaxed)(u8 val, struct sdhci_host *host, u32 offset); + void (*msm_writel_relaxed)(u32 val, struct sdhci_host *host, + u32 offset); +}; + +/* + * From V5, register spaces have changed. Wrap this info in a structure + * and choose the data_structure based on version info mentioned in DT. + */ +struct sdhci_msm_variant_info { + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -245,8 +263,45 @@ struct sdhci_msm_host { wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; u32 caps_0; + bool mci_removed; + const struct sdhci_msm_variant_ops *var_ops; + const struct sdhci_msm_offset *offset; }; +/* + * APIs to read/write to vendor specific registers which were there in the + * core_mem region before MCI was removed. + */ +static u32 sdhci_msm_mci_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return readl_relaxed(msm_host->core_mem + offset); +} + +static u32 sdhci_msm_v5_variant_readl_relaxed(struct sdhci_host *host, + u32 offset) +{ + return readl_relaxed(host->ioaddr + offset); +} + +static void sdhci_msm_mci_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + writel_relaxed(val, msm_host->core_mem + offset); +} + +static void sdhci_msm_v5_variant_writel_relaxed(u32 val, + struct sdhci_host *host, u32 offset) +{ + writel_relaxed(val, host->ioaddr + offset); +} + static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, unsigned int clock) { @@ -1481,6 +1536,28 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } +static const struct sdhci_msm_variant_ops mci_var_ops = { + .msm_readl_relaxed = sdhci_msm_mci_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_mci_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_ops v5_var_ops = { + .msm_readl_relaxed = sdhci_msm_v5_variant_readl_relaxed, + .msm_writel_relaxed = sdhci_msm_v5_variant_writel_relaxed, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_mci_var = { + .mci_removed = 0, + .var_ops = &mci_var_ops, + .offset = &sdhci_msm_mci_offset, +}; + +static const struct sdhci_msm_variant_info sdhci_msm_v5_var = { + .mci_removed = 1, + .var_ops = &v5_var_ops, + .offset = &sdhci_msm_v5_offset, +}; + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V2 3/4] Documentation: sdhci-msm: Add new compatible string for SDCC v5
From: Sayali Lokhande For SDCC version 5.0.0 and higher, new compatible string "qcom,sdhci-msm-v5" is added. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4..502b3b8 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,12 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain: + "qcom,sdhci-msm-v4" for sdcc versions less than 5.0 + "qcom,sdhci-msm-v5" for sdcc versions >= 5.0 + For SDCC version 5.0.0, MCI registers are removed from SDCC + interface and some registers are moved to HC. New compatible + string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V2 4/4] mmc: host: Register changes for sdcc V5
Add support to use the new compatible string "qcom,sdhci-msm-v5". Based on the msm variant, pick the relevant variant data and use it for register read/write to msm specific registers. Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 347 +++ 1 file changed, 221 insertions(+), 126 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2a66aa0..4d0fd2d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -33,16 +33,11 @@ #define CORE_MCI_GENERICS 0x70 #define SWITCHABLE_SIGNALING_VOLTAGE BIT(29) -#define CORE_HC_MODE 0x78 #define HC_MODE_EN 0x1 #define CORE_POWER 0x0 #define CORE_SW_RSTBIT(7) #define FF_CLK_SW_RST_DIS BIT(13) -#define CORE_PWRCTL_STATUS 0xdc -#define CORE_PWRCTL_MASK 0xe0 -#define CORE_PWRCTL_CLEAR 0xe4 -#define CORE_PWRCTL_CTL0xe8 #define CORE_PWRCTL_BUS_OFFBIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_ENBIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_ENBIT(0) #define CORE_FLL_CYCLE_CNT BIT(18) #define CORE_DLL_CLOCK_DISABLE BIT(21) -#define CORE_VENDOR_SPEC 0x10c -#define CORE_VENDOR_SPEC_POR_VAL 0xa1c +#define CORE_VENDOR_SPEC_POR_VAL 0xa1c #define CORE_CLK_PWRSAVE BIT(1) #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) @@ -111,17 +102,14 @@ #define CORE_CDC_SWITCH_BYPASS_OFF BIT(0) #define CORE_CDC_SWITCH_RC_EN BIT(1) -#define CORE_DDR_200_CFG 0x184 #define CORE_CDC_T4_DLY_SELBIT(0) #define CORE_CMDIN_RCLK_EN BIT(1) #define CORE_START_CDC_TRAFFIC BIT(6) -#define CORE_VENDOR_SPEC3 0x1b0 + #define CORE_PWRSAVE_DLL BIT(3) -#define CORE_DDR_CONFIG0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK40 @@ -137,6 +125,12 @@ /* Timeout value to avoid infinite waiting for pwr_irq */ #define MSM_PWR_IRQ_TIMEOUT_MS 5000 +#define MSM_HOST_READL(msm_host, host, offset) \ + msm_host->var_ops->msm_readl_relaxed(host, offset) + +#define MSM_HOST_WRITEL(msm_host, val, host, offset) \ + msm_host->var_ops->msm_writel_relaxed(val, host, offset) + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -268,6 +262,14 @@ struct sdhci_msm_host { const struct sdhci_msm_offset *offset; }; +const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + return msm_host->offset; +} + /* * APIs to read/write to vendor specific registers which were there in the * core_mem region before MCI was removed. @@ -349,10 +351,12 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) u32 wait_cnt = 50; u8 ck_out_en; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Poll for CK_OUT_EN bit. max. poll time = 50us */ - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); while (ck_out_en != poll) { if (--wait_cnt == 0) { @@ -362,8 +366,8 @@ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) } udelay(1); - ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) & - CORE_CK_OUT_EN); + ck_out_en = !!(readl_relaxed(host->ioaddr + + msm_offset->core_dll_config) & CORE_CK_OUT_EN); } return 0; @@ -379,16 +383,18 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) unsigned long flags; u32 config; struct mmc_host *mmc = host->mmc; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); if (phase > 0xf) return -EINVAL; spin_lock_irqsave(&host->lock, flags); - config = rea
Re: [PATCH V4 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
On 3/29/2018 4:22 AM, Doug Anderson wrote: Hi, On Wed, Mar 28, 2018 at 6:08 AM, Vijay Viswanath wrote: During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 35 ++- 1 file changed, 34 insertions(+), 1 deletion(-) Since I commented on v2, please copy me for this series going forward. Thanks. Will do. Sorry I missed. diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..2fcd9010 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1103,7 +1108,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; int retry = 10; - int pwr_state = 0, io_level = 0; + u32 pwr_state = 0, io_level = 0; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); @@ -1313,6 +1318,30 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) This function always returns 0. Make it void. +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + u32 caps = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (regulator_is_supported_voltage(supply, 170, 195)) + caps |= CORE_1_8V_SUPPORT; + if (regulator_is_supported_voltage(supply, 270, 360)) + caps |= CORE_3_0V_SUPPORT; + + if (!caps) + pr_warn("%s: %s: 1.8/3V not supported for vqmmc\n", + mmc_hostname(mmc), __func__); Please remove __func__. You already have the unique thing to find the right driver (AKA mmc_hostname(mmc)) and the string itself should be enough from there. + } + + msm_host->caps_0 |= caps; + pr_debug("%s: %s: supported caps: 0x%08x\n", mmc_hostname(mmc), + __func__, caps); Same, no need for __func__. will remove all unnecessary __func__ references. + + return 0; +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1559,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + ret = sdhci_msm_set_regulator_caps(msm_host); + if (ret) + dev_err(&pdev->dev, "Failed to set regulator caps: %d\n", + ret); If you find some reason _not_ to make sdhci_msm_set_regulator_caps() return "void" as per above, you should actually do something about this error. You've used "dev_err" which makes me feel like you consider this a serious error. Presumably it should cause the probe to fail? > -Doug yeah, we don't need to print anything here as a warning is printed in sdhci_msm_set_regulator_caps() anyway.
Re: [PATCH V2 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
On 3/7/2018 9:42 PM, Doug Anderson wrote: Hi, On Tue, Mar 6, 2018 at 11:13 PM, Vijay Viswanath wrote: Hi Dough, Jeremy, On 3/3/2018 4:38 AM, Jeremy McNicoll wrote: On 2018-03-02 10:23 AM, Doug Anderson wrote: Hi, On Sun, Feb 11, 2018 at 10:01 PM, Vijay Viswanath wrote: During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 38 ++ 1 file changed, 38 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..5c23e92 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -23,6 +23,7 @@ #include #include "sdhci-pltfm.h" +#include This is a strange sort order for this include file. Why is it after the local include? #define CORE_MCI_VERSION 0x50 #define CORE_VERSION_MAJOR_SHIFT 28 @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + Is there something magical about 25 and 26? This is a new caps field, so I'd have expected 0 and 1. Yes, these bits are the same corresponding to the capabilities in the Capabilities Register (offset 0x40). The bit positions become important when capabilities register doesn't show support to some voltages, but we can support those voltages. At that time, we will have to fake capabilities. The changes for those are currently not yet pushed up. #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1313,6 +1318,35 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + int i, count; + u32 caps = 0, vdd_uV; + + if (!IS_ERR(mmc->supply.vqmmc)) { + count = regulator_count_voltages(supply); + if (count < 0) + return count; + for (i = 0; i < count; i++) { + vdd_uV = regulator_list_voltage(supply, i); + if (vdd_uV <= 0) + continue; + if (vdd_uV > 270) + caps |= CORE_3_0V_SUPPORT; + if (vdd_uV < 195) + caps |= CORE_1_8V_SUPPORT; + } Shouldn't you be using regulator_is_supported_voltage() rather than open coding? Also: I've never personally worked on a device where it was used, but there is definitely a concept floating about of a voltage level of 1.2V. Maybe should copy the ranges from mmc_regulator_set_vqmmc()? regulator_is_supported_voltage() checks for a range and it also uses regulator_list_voltage() internally. regulator_list_voltage() is also an exported API for use by drivers AFAIK. Please correct if it is not. Sure, regulator_list_voltage() is valid to call. I'm not saying that your code is wrong or violates abstractions, just that it's essentially re-implementing regulator_is_supported_voltage() for very little gain. Calling regulator_is_supported_voltage() is better because: 1. In theory, it should generate less code. Sure, it might loop twice with the current implementation of regulator_is_supported_voltage(), but for a non-time-critical section like this smaller code is likely better than faster code (decreases kernel size / uses up less cache space, etc). 2. If regulator_is_supported_voltage() is ever improved to be more efficient you'll get that improvement automatically. If someone happened to source vqmmc from a PWM regulator, for instance, trying to enumerate all voltages like this would be a disaster. 3. Code will be simpler to understand. You can replace your whole loop with: if (regulator_is_supported_voltage(mmc->supply.vqmmc, 170, 195)) caps |= CORE_1_8V_SUPPORT if (regulator_is_supported_voltage(mmc->supply.vqmmc, 270, 360)) caps |= CORE_3_0V_SUPPORT Also: seems like you should have some way to deal with "caps" ending up w/ no bits set. IIRC you can have a regulator that can be enabled / disabled but doesn't list a voltage, so if som
Re: [PATCH RFC 1/4] mmc: host: Register changes for sdcc V5
On 5/2/2018 1:58 PM, Ulf Hansson wrote: On 1 May 2018 at 12:39, Vijay Viswanath wrote: From: Sayali Lokhande For SDCC version 5.0.0, MCI registers are removed from SDCC interface and some registers are moved to HC. This change is to support MCI register removal for msmfalcon. New compatible string "qcom,sdhci-msm-v5" is added for msmfalcon to support this change. To simplify review, I would recommend to split this up in a few more pieces, a few re-factoring while the final change adds the qcom,sdhci-msm-v5 support. Will do Change-Id: I0febfd9bb436a8eff20c20107dd4180c9781 Signed-off-by: Sayali Lokhande Signed-off-by: Vijay Viswanath --- .../devicetree/bindings/mmc/sdhci-msm.txt | 5 +- Please move DT changes into separate patches and make sure to sent it to DT maintainers as well. drivers/mmc/host/sdhci-msm.c | 485 +++-- 2 files changed, 365 insertions(+), 125 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index bfdcdc4..c2b7b2b 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -4,7 +4,10 @@ This file documents differences between the core properties in mmc.txt and the properties used by the sdhci-msm driver. Required properties: -- compatible: Should contain "qcom,sdhci-msm-v4". +- compatible: Should contain "qcom,sdhci-msm-v4" or "qcom,sdhci-msm-v5". +For SDCC version 5.0.0, MCI registers are removed from SDCC +interface and some registers are moved to HC. New compatible +string is added to support this change - "qcom,sdhci-msm-v5". - reg: Base address and length of the register in the following order: - Host controller register map (required) - SD Core register map (required) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916..d4d432b 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c [...] +struct sdhci_msm_offset { I think a better idea is to create a struct sdhci_msm_variant and make it contain all the variant specific data. So, not only the offsets, but other variant data as well. More comments below. + u32 core_mci_data_cnt; + u32 core_mci_status; + u32 core_mci_fifo_cnt; + u32 core_mci_version; + u32 core_generics; + u32 core_testbus_config; + u32 core_testbus_sel2_bit; + u32 core_testbus_ena; + u32 core_testbus_sel2; + u32 core_pwrctl_status; + u32 core_pwrctl_mask; + u32 core_pwrctl_clear; + u32 core_pwrctl_ctl; + u32 core_sdcc_debug_reg; + u32 core_dll_config; + u32 core_dll_status; + u32 core_vendor_spec; + u32 core_vendor_spec_adma_err_addr0; + u32 core_vendor_spec_adma_err_addr1; + u32 core_vendor_spec_func2; + u32 core_vendor_spec_capabilities0; + u32 core_ddr_200_cfg; + u32 core_vendor_spec3; + u32 core_dll_config_2; + u32 core_ddr_config; + u32 core_ddr_config_2; +}; + +struct sdhci_msm_offset sdhci_msm_offset_mci_removed = { + .core_mci_data_cnt = 0x35c, + .core_mci_status = 0x324, + .core_mci_fifo_cnt = 0x308, + .core_mci_version = 0x318, + .core_generics = 0x320, + .core_testbus_config = 0x32c, + .core_testbus_sel2_bit = 3, + .core_testbus_ena = (1 << 31), + .core_testbus_sel2 = (1 << 3), + .core_pwrctl_status = 0x240, + .core_pwrctl_mask = 0x244, + .core_pwrctl_clear = 0x248, + .core_pwrctl_ctl = 0x24c, + .core_sdcc_debug_reg = 0x358, + .core_dll_config = 0x200, + .core_dll_status = 0x208, + .core_vendor_spec = 0x20c, + .core_vendor_spec_adma_err_addr0 = 0x214, + .core_vendor_spec_adma_err_addr1 = 0x218, + .core_vendor_spec_func2 = 0x210, + .core_vendor_spec_capabilities0 = 0x21c, + .core_ddr_200_cfg = 0x224, + .core_vendor_spec3 = 0x250, + .core_dll_config_2 = 0x254, + .core_ddr_config = 0x258, + .core_ddr_config_2 = 0x25c, +}; + +struct sdhci_msm_offset sdhci_msm_offset_mci_present = { + .core_mci_data_cnt = 0x30, + .core_mci_status = 0x34, + .core_mci_fifo_cnt = 0x44, + .core_mci_version = 0x050, + .core_generics = 0x70, + .core_testbus_config = 0x0CC, + .core_testbus_sel2_bit = 4, + .core_testbus_ena = (1 << 3), + .core_testbus_sel2 = (1 << 4), + .core_pwrctl_status = 0xDC, + .core_pwrctl_mask = 0xE0, + .core_pwrctl_clear = 0xE4, + .core_pwrctl_ctl = 0xE8, + .core_sdcc_debug_reg = 0x124, + .core_dll_config = 0x100, + .core_dll_status = 0x108, + .core_vendor_spec = 0x10C, + .core_vendor_spe
Re: [PATCH RFC 2/4] mmc: sdhci-msm: Add and use voltage regulator related APIs
On 5/2/2018 2:19 PM, Ulf Hansson wrote: On 1 May 2018 at 12:39, Vijay Viswanath wrote: From: Asutosh Das Some platforms require that the voltage switching happen only after the register write occurs and controller is ready for the switch. When the controller is ready, it will inform through power irq. Add voltage regulator APIs and use them during power irq to switch voltage instead of relying on core layer voltage switching. This is way to simplified. 529 lines of new code deserves some more explanations. Change-Id: Iaa98686e71a5bfe0092c68e9ffa563b060c5ac60 Remove this. Signed-off-by: Asutosh Das Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Subhash Jadavani Signed-off-by: Vijay Viswanath --- .../devicetree/bindings/mmc/sdhci-msm.txt | 27 +- Split DT changes and add DT maintainers. Will do drivers/mmc/host/sdhci-msm.c | 537 +++-- 2 files changed, 529 insertions(+), 35 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index c2b7b2b..c454046 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -23,6 +23,22 @@ Required properties: "xo"- TCXO clock (optional) "cal" - reference clock for RCLK delay calibration (optional) "sleep" - sleep clock for RCLK delay calibration (optional) +- -supply: phandle to the regulator device tree node if voltage + regulators needs to be handled from within sdhci-msm layer. + Supported "supply-name" are "vdd" and "vdd-io". + +Optional Properties: + - qcom,-always-on - specifies whether supply should be kept + "on" always. + - qcom,-lpm_sup - specifies whether supply can be kept in low + power mode (lpm). + - qcom,-voltage_level - specifies voltage levels for supply. + Should be specified in pairs (min, max), units uV. + - qcom,-current_level - specifies load levels for supply in lpm + or high power mode (hpm). Should be specified in + pairs (lpm, hpm), units uA. + + This looks really weird. What's so special here that requires you to have your own specific regulator bindings? Example: @@ -33,8 +49,15 @@ Example: bus-width = <8>; non-removable; - vmmc-supply = <&pm8941_l20>; - vqmmc-supply = <&pm8941_s3>; + vdd-supply = <&pm8941_l20>; + qcom,vdd-voltage-level = <295 295>; + qcom,vdd-current-level = <9000 80>; + + vdd-io-supply = <&pm8941_s3>; + qcom,vdd-io-always-on; + qcom,vdd-io-lpm-sup; + qcom,vdd-io-voltage-level = <180 295>; + qcom,vdd-io-current-level = <6 22000>; pinctrl-names = "default"; pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>; diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index d4d432b..0e0f12d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -213,6 +213,33 @@ struct sdhci_msm_offset sdhci_msm_offset_mci_present = { .core_ddr_config_2 = 0x1BC, }; +/* 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; +}; + +struct sdhci_msm_pltfm_data { + /* Change-Id: Ide3a658ad51a3c3d4a05c47c0e8f013f647c9516 */ + struct sdhci_msm_reg_data *vdd_data; + struct sdhci_msm_reg_data *vdd_io_data; +}; + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -234,6 +261,8 @@ struct sdhci_msm_host { u32 caps_0; bool mci_removed; const struct sdhci_msm_offset *offset; + bool pltfm_init_done; + struct sdhci_msm_pltfm_data pdata; }; /* @@ -298,6 +327,336 @@ void sdhci_msm_vendor_writel_relaxed(u32 val, struct sdhci_host *host, writel_relaxed(val, base_addr + offset); } +enum vdd_io_level { + /* set vdd_io_data->low_vol_level */ + VDD_IO_LOW, + /* set
[PATCH V5 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm
>From the HPG: In some platform, SDCC controller can be connected to either an eMMC device or an SD card. The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. For SD usage the default value of this signal is ???0???, and SD driver changes it to ???1??? as a part of voltage switching sequence. For eMMC usage, SW should configure this signal to ???1??? and supply 1.8v to PADs before starting any activity on the eMMC BUS. To set this signal, write the following in the SDC1_SDCC_HC_VENDOR_SPECIFIC_FUNC register: HC_IO_PAD_PWR_SWITCH: bit 16 HC_IO_PAD_PWR_SWITCH_EN: bit 15 Changes since v1: Modified comments on io_pad related changes. Split some read+modify+write commands to multiple lines Changes since v2: IO_PAD_PWR_SWITCH_EN will be set only if we have info regarding what voltage is supported by the regulators. Replaced regulator_list_voltage() API with regulator_is_supported_voltage(). Changes since v3: Removed unnecessary prints and extra lines. Changes since v4: Removed unnecessary mb() within sdhci_msm_handle_pwr_irq() since wakeup calls have implicit write barriers. Krishna Konda (1): mmc: sdhci-msm: support voltage pad switching Vijay Viswanath (1): mmc: sdhci-msm: Add support to store supported vdd-io voltages drivers/mmc/host/sdhci-msm.c | 99 +++- 1 file changed, 97 insertions(+), 2 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V5 2/2] mmc: sdhci-msm: support voltage pad switching
The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath Reviewed-by: Douglas Anderson --- drivers/mmc/host/sdhci-msm.c | 57 ++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index edd30a2..bb11916 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,12 +78,15 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) #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_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) @@ -1109,7 +1112,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; u32 pwr_state = 0, io_level = 0; - + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1166,6 +1169,38 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* If we don't have info regarding the voltage levels supported by +* regulators, don't change the IO PAD PWR SWITCH. +*/ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + u32 new_config; + /* +* We should unset IO PAD PWR switch only if the register write +* can set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply +* is only 1.8V. In such targets, even during REQ_IO_HIGH, the +* IO PAD PWR switch must be kept set to reflect actual +* regulator voltage. This way, during initialization of +* controllers with only 1.8V, we will set the IO PAD bit +* without waiting for a REQ_IO_LOW. +*/ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + new_config = config; + + if ((io_level & REQ_IO_HIGH) && + (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + new_config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) + new_config |= CORE_IO_PAD_PWR_SWITCH; + + if (config ^ new_config) + writel_relaxed(new_config, + host->ioaddr + CORE_VENDOR_SPEC); + } + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1322,7 +1357,8 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) { struct mmc_host *mmc = msm_host->mmc; struct regulator *supply = mmc->supply.vqmmc; - u32 caps = 0; + u32 caps = 0, config; + struct sdhci_host *host = mmc_priv(mmc); if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 170, 195)) @@ -1335,6 +1371,23 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) mmc_hostname(mmc)); } + if (caps) { + /* +* Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH +* bit can be used as required later on. +*/ + u32 io_level = msm_host->curr_io_level; + + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config |= CORE_IO_PAD_PWR_SWITCH_EN; + + if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) + config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_lev
[PATCH V5 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath Reviewed-by: Douglas Anderson --- drivers/mmc/host/sdhci-msm.c | 29 - 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..edd30a2 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1103,7 +1108,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; int retry = 10; - int pwr_state = 0, io_level = 0; + u32 pwr_state = 0, io_level = 0; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); @@ -1313,6 +1318,27 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + u32 caps = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (regulator_is_supported_voltage(supply, 170, 195)) + caps |= CORE_1_8V_SUPPORT; + if (regulator_is_supported_voltage(supply, 270, 360)) + caps |= CORE_3_0V_SUPPORT; + + if (!caps) + pr_warn("%s: 1.8/3V not supported for vqmmc\n", + mmc_hostname(mmc)); + } + + msm_host->caps_0 |= caps; + pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1556,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + sdhci_msm_set_regulator_caps(msm_host); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH V4 2/2] mmc: sdhci-msm: support voltage pad switching
On 4/13/2018 10:38 PM, Doug Anderson wrote: Hi, On Fri, Apr 6, 2018 at 2:48 AM, Vijay Viswanath wrote: On 3/29/2018 4:23 AM, Doug Anderson wrote: Hi, On Wed, Mar 28, 2018 at 6:08 AM, Vijay Viswanath wrote: From: Krishna Konda The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 64 ++-- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 2fcd9010..bbf9626 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,12 +78,15 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) #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_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) @@ -1109,7 +1112,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; u32 pwr_state = 0, io_level = 0; - + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1166,6 +1169,45 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* If we don't have info regarding the voltage levels supported by +* regulators, don't change the IO PAD PWR SWITCH. +*/ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + /* Ensure order between core_mem and hc_mem */ + mb(); Like in v2, I don't understand why you need a mb() before the read from CORE_VENDOR_SPEC. No reads or writes to the core_mem will affect the value you're reading here, so you need no barrier. If you need a barrier before the _write_ to CORE_VENDOR_SPEC then add it below. Then in the case where the config doesn't change you have no barriers. + /* +* We should unset IO PAD PWR switch only if the register write +* can set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply +* is only 1.8V. In such targets, even during REQ_IO_HIGH, the +* IO PAD PWR switch must be kept set to reflect actual +* regulator voltage. This way, during initialization of +* controllers with only 1.8V, we will set the IO PAD bit +* without waiting for a REQ_IO_LOW. +*/ For the above comment, what about just: new_config = config if (msm_host->caps_0 == CORE_1_8V_SUPPORT) { new_config |= CORE_IO_PAD_PWR_SWITCH; } else if (msm_host->caps_0 == CORE_3_3V_SUPPORT) { new_config &= ~CORE_IO_PAD_PWR_SWITCH; } else if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { if (io_level & REQ_IO_HIGH) new_config &= ~CORE_IO_PAD_PWR_SWITCH; else if (io_level & REQ_IO_LOW) new_config |= CORE_IO_PAD_PWR_SWITCH; } This looks a big mess of if/else. Does the above implementation have better performance compared to having two if/else with bit operations inside ? The latter looks much cleaner and faster. If regulator only supports 3V and we get a io_low from BUS_OFF ( REQ_IO_LOW should never come if we don't support 1.8V), it is ok to set io pad. Yeah, I think it's ugly no matter what. Personally I find the if/then/else easier to follow than the complicated conditions split across multiple lines. I seem to remember there was something that my version did differently than yours too (hence the "this might be more important if you get rid of the initial setting"), let's see if I can figure it out again. Mine says: - if it has exactly 1.8 or 3.3 support: set that. - else if it supports both: set whatev
Re: [PATCH RFC 2/2] mmc: sdhci-msm: support voltage pad switching
On 2/3/2018 3:21 AM, Bjorn Andersson wrote: On Thu 18 Jan 00:05 PST 2018, Vijay Viswanath wrote: From: Krishna Konda The PADs for sdhc controller are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. I'll try to find some time to check that this doesn't break 8916 and 8974...again... Thanks! Btwn, I had tested the code in db410c. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 38 ++ 1 file changed, 38 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 5c23e92..f5728a8 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,6 +78,8 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400(3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK(7 << 19) @@ -1166,6 +1168,35 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* +* SDHC has core_mem and hc_mem device memory and these memory +* addresses do not fall within 1KB region. Hence, any update to +* core_mem address space would require an mb() to ensure this gets +* completed before its next update to registers within hc_mem. +*/ + mb(); If you just use writel() instead of writel_relaxed() you don't need to sprinkle the driver with comments like this. And you really should be able to just say "Ensure ordering between core_mem and hc_mem writes" if you really feel like making it explicit. + /* +* We should unset IO PAD PWR switch only if the register write can +* set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply is only +* 1.8V. In such targets, even during REQ_IO_HIGH, the IO PAD PWR +* switch must be kept set to reflect actual regulator voltage. This +* way, during initialization of controllers with only 1.8V, we will +* set the IO PAD bit without waiting for a REQ_IO_LOW. +*/ + if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) & + ~CORE_IO_PAD_PWR_SWITCH), host->ioaddr + + CORE_VENDOR_SPEC); Please split this up in read, modify and write operations. Will do + else if ((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) | + CORE_IO_PAD_PWR_SWITCH), host->ioaddr + + CORE_VENDOR_SPEC); + /* Ensure that the IO PAD switches are updated before proceeding */ That's not what "mb()" does, it ensures that any writes that was done before this line will hit the hardware before any writes that is done after this line. Will update the comments. But again, using writel() would save us from doing this explicitly throughout the code. + mb(); + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1518,6 +1549,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) } /* +* Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH bit can +* be used as required later on. +*/ + writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC) | + CORE_IO_PAD_PWR_SWITCH_EN), host->ioaddr + + CORE_VENDOR_SPEC); Please rewrite as 3 operations. Do we need to set the pwr switch value as well? Or we're fine relying on the existing value here? After the IO_PAD_PWR_SWTCH is enabled, we will call sdhci_msm_handle_pwr_irq. If there is any pending power irq interrupt, that will set the appropriate pwr switch value. Otherwise, an appropriate value will get set during REQ_BUS_ON event. Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH V2 0/2] mmc: sdhci-msm: Configuring IO_PAD support for sdhci-msm
>From the HPG: In some platform, SDCC controller can be connected to either an eMMC device or an SD card. The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. For SD usage the default value of this signal is ???0???, and SD driver changes it to ???1??? as a part of voltage switching sequence. For eMMC usage, SW should configure this signal to ???1??? and supply 1.8v to PADs before starting any activity on the eMMC BUS. To set this signal, write the following in the SDC1_SDCC_HC_VENDOR_SPECIFIC_FUNC register: HC_IO_PAD_PWR_SWITCH: bit 16 HC_IO_PAD_PWR_SWITCH_EN: bit 15 Changes since v1: Modified comments on io_pad related changes. Split some read+modify+write commands to multiple lines Krishna Konda (1): mmc: sdhci-msm: support voltage pad switching Vijay Viswanath (1): mmc: sdhci-msm: Add support to store supported vdd-io voltages drivers/mmc/host/sdhci-msm.c | 72 1 file changed, 72 insertions(+) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V2 2/2] mmc: sdhci-msm: support voltage pad switching
From: Krishna Konda The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 34 ++ 1 file changed, 34 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 5c23e92..96c81df 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,6 +78,8 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) @@ -1109,6 +,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; int pwr_state = 0, io_level = 0; + u32 config = 0; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); @@ -1166,6 +1169,30 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* Ensure order between core_mem and hc_mem */ + mb(); + /* +* We should unset IO PAD PWR switch only if the register write can +* set IO lines high and the regulator also switches to 3 V. +* Else, we should keep the IO PAD PWR switch set. +* This is applicable to certain targets where eMMC vccq supply is only +* 1.8V. In such targets, even during REQ_IO_HIGH, the IO PAD PWR +* switch must be kept set to reflect actual regulator voltage. This +* way, during initialization of controllers with only 1.8V, we will +* set the IO PAD bit without waiting for a REQ_IO_LOW. +*/ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + + if ((io_level & REQ_IO_HIGH) && (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) + config |= CORE_IO_PAD_PWR_SWITCH; + + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + /* Ensure IO pad update before any further register read/writes */ + mb(); + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1518,6 +1545,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) } /* +* Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH bit can +* be used as required later on. +*/ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config |= CORE_IO_PAD_PWR_SWITCH_EN; + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + /* * 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 * interrupt in GIC, any pending power irq interrupt should be -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH V2 1/2] mmc: sdhci-msm: Add support to store supported vdd-io voltages
During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 38 ++ 1 file changed, 38 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291..5c23e92 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -23,6 +23,7 @@ #include #include "sdhci-pltfm.h" +#include #define CORE_MCI_VERSION 0x50 #define CORE_VERSION_MAJOR_SHIFT 28 @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400(6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIBBIT(16) #define CORE_HW_AUTOCAL_ENABIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1313,6 +1318,35 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static int sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + int i, count; + u32 caps = 0, vdd_uV; + + if (!IS_ERR(mmc->supply.vqmmc)) { + count = regulator_count_voltages(supply); + if (count < 0) + return count; + for (i = 0; i < count; i++) { + vdd_uV = regulator_list_voltage(supply, i); + if (vdd_uV <= 0) + continue; + if (vdd_uV > 270) + caps |= CORE_3_0V_SUPPORT; + if (vdd_uV < 195) + caps |= CORE_1_8V_SUPPORT; + } + } + msm_host->caps_0 |= caps; + pr_debug("%s: %s: supported caps: 0x%08x\n", mmc_hostname(mmc), + __func__, caps); + + return 0; +} + + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1564,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + ret = sdhci_msm_set_regulator_caps(msm_host); + if (ret) + dev_err(&pdev->dev, "%s: Failed to set regulator caps: %d\n", + __func__, ret); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH v1 0/5] mmc: sdhci-msm: Corrections to implementation of power irq
Hi Adrian, Ulf, I have addressed the comments on previous patch series. Can you please tell how I should proceed from here? Thanks, Vijay On 8/30/2017 6:21 PM, Vijay Viswanath wrote: Register writes which change voltage of IO lines or turn the IO bus on/off require sdhc controller to be ready before progressing further. Once a register write which affects IO lines is done, the driver should wait for power irq from controller. Once the irq comes, the driver should acknowledge the irq by writing to power control register. If the acknowledgement is not given to controller, the controller may not complete the corresponding register write action and this can mess up the controller if drivers proceeds without power irq completing. Changes since RFC: wait_for_completion_timeout replaced with wait_event_timeout when waiting for power irq. Removed the spinlock within power irq handler and API which waits for power irq. Added comments to sdhci msm register write functions, warning that they can sleep. Sdhci msm register write functions will do a memory barrier before writing to the register if the particular register can trigger power irq. Instead of enabling SDHCI IO ACCESSORS config in arm64/defconfig, it will be selected in mmc/host/Kconfig if the platform is MMC_SDHCI_MSM. Sahitya Tummala (2): mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset mmc: sdhci-msm: Add support to wait for power irq Subhash Jadavani (1): mmc: sdhci-msm: fix issue with power irq Vijay Viswanath (2): mmc: sdhci-msm: Add ops to do sdhc register write mmc: Kconfig: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-msm.c | 253 ++- 2 files changed, 249 insertions(+), 5 deletions(-)
[PATCH v1 2/5] mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset
From: Sahitya Tummala There is a rare scenario in HW, where the first clear pulse could be lost when the actual reset and clear/read of status register are happening at the same time. Fix this by retrying upto 10 times to ensure the status register gets cleared. Otherwise, this will lead to a spurious power IRQ which results in system instability. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 46 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index d636251..42a65ab 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -995,17 +995,52 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } -static void sdhci_msm_voltage_switch(struct sdhci_host *host) +static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", + mmc_hostname(host->mmc), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); +} + +static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; + int retry = 10; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + /* +* There is a rare HW scenario where the first clear pulse could be +* lost when actual reset and clear/read of status register is +* happening at a time. Hence, retry for at least 10 times to make +* sure status register is cleared. Otherwise, this will result in +* a spurious power IRQ resulting in system instability. +*/ + while (irq_status & readl_relaxed(msm_host->core_mem + + CORE_PWRCTL_STATUS)) { + if (retry == 0) { + pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", + mmc_hostname(host->mmc), irq_status); + sdhci_msm_dump_pwr_ctrl_regs(host); + WARN_ON(1); + break; + } + writel_relaxed(irq_status, + msm_host->core_mem + CORE_PWRCTL_CLEAR); + retry--; + udelay(10); + } + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) irq_ack |= CORE_PWRCTL_BUS_SUCCESS; if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) @@ -1017,13 +1052,17 @@ static void sdhci_msm_voltage_switch(struct sdhci_host *host) * switches are handled by the sdhci core, so just report success. */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + + pr_debug("%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n", + mmc_hostname(msm_host->mmc), __func__, irq, irq_status, + irq_ack); } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; - sdhci_msm_voltage_switch(host); + sdhci_msm_handle_pwr_irq(host, irq); return IRQ_HANDLED; } @@ -1106,7 +1145,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, - .voltage_switch = sdhci_msm_voltage_switch, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { @@ -1257,7 +1295,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) * acknowledged. Otherwise power irq interrupt handler would be * fired prematurely. */ - sdhci_msm_voltage_switch(host); + sdhci_msm_handle_pwr_irq(host, 0); /* * Ensure that above writes are propogated before interrupt enablement -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 5/5] mmc: Kconfig: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS
Enable CONFIG_MMC_SDHCI_IO_ACCESSORS so that SDHC controller specific register read and write APIs, if registered, can be used. --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2db84dd..64a9298 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -420,6 +420,7 @@ config MMC_SDHCI_MSM tristate "Qualcomm SDHCI Controller Support" depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on MMC_SDHCI_PLTFM + select CONFIG_MMC_SDHCI_IO_ACCESSORS help This selects the Secure Digital Host Controller Interface (SDHCI) support present in Qualcomm SOCs. The controller supports -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 4/5] mmc: sdhci-msm: Add ops to do sdhc register write
Register writes which change voltage of IO lines or turn the IO bus on/off require controller to be ready before progressing further. When the controller is ready, it will generate a power irq which needs to be handled. The thread which initiated the register write should wait for power irq to complete. This will be done through the new sdhc msm write APIs which will check whether the particular write can trigger a power irq and wait for it with a timeout if it is expected. The SDHC core power control IRQ gets triggered when - * There is a state change in power control bit (bit 0) of SDHCI_POWER_CONTROL register. * There is a state change in 1.8V enable bit (bit 3) of SDHCI_HOST_CONTROL2 register. * Bit 1 of SDHCI_SOFTWARE_RESET is set. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 69 +++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index e3e385e..a4a78b5 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1070,7 +1070,6 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) __WARN_printf("%s: pwr_irq for req: (%d) timed out\n", mmc_hostname(host->mmc), req_type); } - msm_host->pwr_irq_flag = 0; pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), __func__, req_type); } @@ -1250,6 +1249,70 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) __sdhci_msm_set_clock(host, clock); } +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +/* + * Platform specific register write functions. This is so that, if any + * register write needs to be followed up by platform specific actions, + * they can be added here. These functions can go to sleep when writes + * to certain registers are done. + * These functions are relying on sdhci_set_ios not using spinlock. + */ +static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + u32 req_type = 0; + + switch (reg) { + case SDHCI_HOST_CONTROL2: + req_type = (val & SDHCI_CTRL_VDD_180) ? REQ_IO_LOW : + REQ_IO_HIGH; + break; + case SDHCI_SOFTWARE_RESET: + if (host->pwr && (val & SDHCI_RESET_ALL)) + req_type = REQ_BUS_OFF; + break; + case SDHCI_POWER_CONTROL: + req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; + break; + } + + if (req_type) { + msm_host->pwr_irq_flag = 0; + /* +* Since this register write may trigger a power irq, ensure +* all previous register writes are complete by this point. +*/ + mb(); + } + return req_type; +} + +/* This function may sleep*/ +static void sdhci_msm_writew(struct sdhci_host *host, u16 val, int reg) +{ + u32 req_type = 0; + + req_type = __sdhci_msm_check_write(host, val, reg); + writew_relaxed(val, host->ioaddr + reg); + + if (req_type) + sdhci_msm_check_power_status(host, req_type); +} + +/* This function may sleep*/ +static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 req_type = 0; + + req_type = __sdhci_msm_check_write(host, val, reg); + + writeb_relaxed(val, host->ioaddr + reg); + + if (req_type) + sdhci_msm_check_power_status(host, req_type); +} +#endif static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1264,6 +1327,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + .write_w = sdhci_msm_writew, + .write_b = sdhci_msm_writeb, +#endif }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 3/5] mmc: sdhci-msm: Add support to wait for power irq
From: Sahitya Tummala Add support API which will check if power irq is expected to be generated and wait for the power irq to come and complete if the irq is expected. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 124 ++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 42a65ab..e3e385e 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -123,6 +123,10 @@ #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) #define MSM_MMC_AUTOSUSPEND_DELAY_MS 50 + +/* Timeout value to avoid infinite waiting for pwr_irq */ +#define MSM_PWR_IRQ_TIMEOUT_MS 5000 + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -138,6 +142,12 @@ struct sdhci_msm_host { bool calibration_done; u8 saved_tuning_phase; bool use_cdclp533; + u32 curr_pwr_state; + u32 curr_io_level; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + wait_queue_head_t pwr_irq_wait; + bool pwr_irq_flag; +#endif }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -995,6 +1005,87 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host) +{ + init_waitqueue_head(&msm_host->pwr_irq_wait); +} + +static inline void sdhci_msm_complete_pwr_irq_wait( + struct sdhci_msm_host *msm_host) +{ + wake_up(&msm_host->pwr_irq_wait); +} + +/* + * sdhci_msm_check_power_status API should be called when registers writes + * which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens. + * To what state the register writes will change the IO lines should be passed + * as the argument req_type. This API will check whether the IO line's state + * is already the expected state and will wait for power irq only if + * power irq is expected to be trigerred based on the current IO line state + * and expected IO line state. + */ +static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + bool done = false; + + pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", + mmc_hostname(host->mmc), __func__, req_type, + msm_host->curr_pwr_state, msm_host->curr_io_level); + + /* +* The IRQ for request type IO High/LOW will be generated when - +* there is a state change in 1.8V enable bit (bit 3) of +* SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0 +* which indicates 3.3V IO voltage. So, when MMC core layer tries +* to set it to 3.3V before card detection happens, the +* IRQ doesn't get triggered as there is no state change in this bit. +* The driver already handles this case by changing the IO voltage +* level to high as part of controller power up sequence. Hence, check +* for host->pwr to handle a case where IO voltage high request is +* issued even before controller power up. +*/ + if ((req_type & REQ_IO_HIGH) && !host->pwr) { + pr_debug("%s: do not wait for power IRQ that never comes, req_type: %d\n", + mmc_hostname(host->mmc), req_type); + return; + } + if ((req_type & msm_host->curr_pwr_state) || + (req_type & msm_host->curr_io_level)) + done = true; + /* +* This is needed here to hanlde a case where IRQ gets +* triggered even before this function is called so that +* x->done counter of completion gets reset. Otherwise, +* next call to wait_for_completion returns immediately +* without actually waiting for the IRQ to be handled. +*/ + if (!done) { + if (!wait_event_timeout(msm_host->pwr_irq_wait, + msm_host->pwr_irq_flag, + msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) + __WARN_printf("%s: pwr_irq for req: (%d) timed out\n", + mmc_hostname(host->mmc), req_type); + } + msm_host->pwr_irq_flag = 0; + pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), + __func__, req_type); +} +#else +static inline void sdhci_msm_init_pwr_irq_completion( + struct sdhci_msm_host *msm_host) +{ +}
[PATCH v1 0/5] mmc: sdhci-msm: Corrections to implementation of power irq
Register writes which change voltage of IO lines or turn the IO bus on/off require sdhc controller to be ready before progressing further. Once a register write which affects IO lines is done, the driver should wait for power irq from controller. Once the irq comes, the driver should acknowledge the irq by writing to power control register. If the acknowledgement is not given to controller, the controller may not complete the corresponding register write action and this can mess up the controller if drivers proceeds without power irq completing. Changes since RFC: wait_for_completion_timeout replaced with wait_event_timeout when waiting for power irq. Removed the spinlock within power irq handler and API which waits for power irq. Added comments to sdhci msm register write functions, warning that they can sleep. Sdhci msm register write functions will do a memory barrier before writing to the register if the particular register can trigger power irq. Instead of enabling SDHCI IO ACCESSORS config in arm64/defconfig, it will be selected in mmc/host/Kconfig if the platform is MMC_SDHCI_MSM. Sahitya Tummala (2): mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset mmc: sdhci-msm: Add support to wait for power irq Subhash Jadavani (1): mmc: sdhci-msm: fix issue with power irq Vijay Viswanath (2): mmc: sdhci-msm: Add ops to do sdhc register write mmc: Kconfig: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-msm.c | 253 ++- 2 files changed, 249 insertions(+), 5 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v1 1/5] mmc: sdhci-msm: fix issue with power irq
From: Subhash Jadavani SDCC controller reset (SW_RST) during probe may trigger power irq if previous status of PWRCTL was either BUS_ON or IO_HIGH_V. So before we enable the power irq interrupt in GIC (by registering the interrupt handler), we need to ensure that any pending power irq interrupt status is acknowledged otherwise power irq interrupt handler would be fired prematurely. Signed-off-by: Subhash Jadavani Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 18 ++ 1 file changed, 18 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 9d601dc..d636251 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1250,6 +1250,21 @@ static int sdhci_msm_probe(struct platform_device *pdev) CORE_VENDOR_SPEC_CAPABILITIES0); } + /* +* 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 +* interrupt in GIC, any pending power irq interrupt should be +* acknowledged. Otherwise power irq interrupt handler would be +* fired prematurely. +*/ + sdhci_msm_voltage_switch(host); + + /* +* Ensure that above writes are propogated before interrupt enablement +* in GIC. +*/ + mb(); + /* Setup IRQ for handling power/voltage tasks with PMIC */ msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); if (msm_host->pwr_irq < 0) { @@ -1259,6 +1274,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto clk_disable; } + /* Enable pwr irq interrupts */ + writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, dev_name(&pdev->dev), host); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH 1/5] mmc: sdhci-msm: fix issue with power irq
On 8/24/2017 1:10 PM, Adrian Hunter wrote: On 18/08/17 08:19, Vijay Viswanath wrote: From: Subhash Jadavani SDCC controller reset (SW_RST) during probe may trigger power irq if previous status of PWRCTL was either BUS_ON or IO_HIGH_V. So before we enable the power irq interrupt in GIC (by registering the interrupt handler), we need to ensure that any pending power irq interrupt status is acknowledged otherwise power irq interrupt handler would be fired prematurely. Signed-off-by: Subhash Jadavani Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 26 ++ 1 file changed, 26 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 9d601dc..0957199 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1128,6 +1128,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) u16 host_version, core_minor; u32 core_version, config; u8 core_major; + u32 irq_status, irq_ctl; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) @@ -1250,6 +1251,28 @@ static int sdhci_msm_probe(struct platform_device *pdev) CORE_VENDOR_SPEC_CAPABILITIES0); } + /* +* 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 +* interrupt in GIC, any pending power irq interrupt should be +* acknowledged. Otherwise power irq interrupt handler would be +* fired prematurely. +*/ + + irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + irq_ctl = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL); + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) + irq_ctl |= CORE_PWRCTL_BUS_SUCCESS; + if (irq_status & (CORE_PWRCTL_IO_HIGH | CORE_PWRCTL_IO_LOW)) + irq_ctl |= CORE_PWRCTL_IO_SUCCESS; + writel_relaxed(irq_ctl, msm_host->core_mem + CORE_PWRCTL_CTL); This looks a lot like sdhci_msm_voltage_switch(). Can clearing the interrupt be a common function? This is better. Will change the patches to do it this way. + /* +* Ensure that above writes are propogated before interrupt enablement +* in GIC. +*/ + mb(); + /* Setup IRQ for handling power/voltage tasks with PMIC */ msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); if (msm_host->pwr_irq < 0) { @@ -1259,6 +1282,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto clk_disable; } + /* Enable pwr irq interrupts */ + writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, dev_name(&pdev->dev), host);
Re: [PATCH 2/5] mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset
On 8/24/2017 1:12 PM, Adrian Hunter wrote: On 18/08/17 08:19, Vijay Viswanath wrote: From: Sahitya Tummala There is a rare scenario in HW, where the first clear pulse could be lost when the actual reset and clear/read of status register are happening at the same time. Fix this by retrying upto 10 times to ensure the status register gets cleared. Otherwise, this will lead to a spurious power IRQ which results in system instability. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 43 --- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 0957199..f3e0489 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -995,17 +995,51 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } -static void sdhci_msm_voltage_switch(struct sdhci_host *host) +static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", + mmc_hostname(host->mmc), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); +} + +static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; + int retry = 10; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + /* +* There is a rare HW scenario where the first clear pulse could be +* lost when actual reset and clear/read of status register is +* happening at a time. Hence, retry for at least 10 times to make +* sure status register is cleared. Otherwise, this will result in +* a spurious power IRQ resulting in system instability. +*/ + while (irq_status & readl_relaxed(msm_host->core_mem + + CORE_PWRCTL_STATUS)) { + if (retry == 0) { + pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", + mmc_hostname(host->mmc), irq_status); + sdhci_msm_dump_pwr_ctrl_regs(host); + WARN_ON(1); Is it your intention to loop forever here? A bad mistake from my side. Will add break here. + } + writel_relaxed(irq_status, + msm_host->core_mem + CORE_PWRCTL_CLEAR); + retry--; + udelay(10); + } + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) irq_ack |= CORE_PWRCTL_BUS_SUCCESS; if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) @@ -1017,13 +1051,17 @@ static void sdhci_msm_voltage_switch(struct sdhci_host *host) * switches are handled by the sdhci core, so just report success. */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + + pr_debug("%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n", + mmc_hostname(msm_host->mmc), __func__, irq, irq_status, + irq_ack); } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; - sdhci_msm_voltage_switch(host); + sdhci_msm_handle_pwr_irq(host, irq); return IRQ_HANDLED; } @@ -1106,7 +1144,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, - .voltage_switch = sdhci_msm_voltage_switch, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 3/5] mmc: sdhci-msm: Add support to wait for power irq
On 8/24/2017 3:35 PM, Adrian Hunter wrote: On 18/08/17 08:19, Vijay Viswanath wrote: From: Sahitya Tummala Add support API which will check if power irq is expected to be generated and wait for the power irq to come and complete if the irq is expected. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 125 ++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index f3e0489..6d3b1fd 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -123,6 +123,10 @@ #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) #define MSM_MMC_AUTOSUSPEND_DELAY_MS 50 + +/* Timeout value to avoid infinite waiting for pwr_irq */ +#define MSM_PWR_IRQ_TIMEOUT_MS 5000 + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -138,6 +142,11 @@ struct sdhci_msm_host { bool calibration_done; u8 saved_tuning_phase; bool use_cdclp533; + u32 curr_pwr_state; + u32 curr_io_level; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + struct completion pwr_irq_completion; +#endif }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -995,6 +1004,90 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static inline void sdhci_msm_init_pwr_irq_completion( + struct sdhci_msm_host *msm_host) +{ + init_completion(&msm_host->pwr_irq_completion); +} + +static inline void sdhci_msm_complete_pwr_irq_completion( + struct sdhci_msm_host *msm_host) +{ + complete(&msm_host->pwr_irq_completion); +} + +/* + * sdhci_msm_check_power_status API should be called when registers writes + * which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens. + * To what state the register writes will change the IO lines should be passed + * as the argument req_type. This API will check whether the IO line's state + * is already the expected state and will wait for power irq only if + * power irq is expected to be trigerred based on the current IO line state + * and expected IO line state. + */ +static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + unsigned long flags; + bool done = false; + + spin_lock_irqsave(&host->lock, flags); + pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", + mmc_hostname(host->mmc), __func__, req_type, + msm_host->curr_pwr_state, msm_host->curr_io_level); + + /* +* The IRQ for request type IO High/LOW will be generated when - +* there is a state change in 1.8V enable bit (bit 3) of +* SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0 +* which indicates 3.3V IO voltage. So, when MMC core layer tries +* to set it to 3.3V before card detection happens, the +* IRQ doesn't get triggered as there is no state change in this bit. +* The driver already handles this case by changing the IO voltage +* level to high as part of controller power up sequence. Hence, check +* for host->pwr to handle a case where IO voltage high request is +* issued even before controller power up. +*/ + if ((req_type & REQ_IO_HIGH) && !host->pwr) { + pr_debug("%s: do not wait for power IRQ that never comes\n", + mmc_hostname(host->mmc)); + spin_unlock_irqrestore(&host->lock, flags); + return; + } + if ((req_type & msm_host->curr_pwr_state) || + (req_type & msm_host->curr_io_level)) + done = true; + spin_unlock_irqrestore(&host->lock, flags); + /* +* This is needed here to hanlde a case where IRQ gets +* triggered even before this function is called so that +* x->done counter of completion gets reset. Otherwise, +* next call to wait_for_completion returns immediately +* without actually waiting for the IRQ to be handled. +*/ + if (done) + init_completion(&msm_host->pwr_irq_completion); + else if (!wait_for_completion_timeout(&msm_host->pwr_irq_completion, + msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) This all looks a bit more complicated and fragile than it needs to be. You are waiting for an event so you really want to be using wait_event_ti
Re: [PATCH 4/5] mmc: sdhci-msm: Add ops to do sdhc register write
On 8/24/2017 3:41 PM, Adrian Hunter wrote: On 18/08/17 08:19, Vijay Viswanath wrote: Register writes which change voltage of IO lines or turn the IO bus on/off require controller to be ready before progressing further. When the controller is ready, it will generate a power irq which needs to be handled. The thread which initiated the register write should wait for power irq to complete. This will be done through the new sdhc msm write APIs which will check whether the particular write can trigger a power irq and wait for it with a timeout if it is expected. The SDHC core power control IRQ gets triggered when - * There is a state change in power control bit (bit 0) of SDHCI_POWER_CONTROL register. * There is a state change in 1.8V enable bit (bit 3) of SDHCI_HOST_CONTROL2 register. * Bit 1 of SDHCI_SOFTWARE_RESET is set. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 39 +++ 1 file changed, 39 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 6d3b1fd..6571880 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1250,6 +1250,41 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) __sdhci_msm_set_clock(host, clock); } +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS +static void __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg) +{ + u32 req_type = 0; + + switch (reg) { + case SDHCI_HOST_CONTROL2: + req_type = (val & SDHCI_CTRL_VDD_180) ? REQ_IO_LOW : + REQ_IO_HIGH; + break; + case SDHCI_SOFTWARE_RESET: + if (host->pwr && (val & SDHCI_RESET_ALL)) + req_type = REQ_BUS_OFF; + break; + case SDHCI_POWER_CONTROL: + req_type = !val ? REQ_BUS_OFF : REQ_BUS_ON; + break; + } + + if (req_type) So you are really relying on these register writes not being done in an atomic context. Since the spin lock was removed from sdhci_set_ios() that seems to be true, but it would be good to add a comment here acknowledging that you are depending on that. Will add the comments mentioning that this function can sleep and that it should not be called from atomic contexts. + sdhci_msm_check_power_status(host, req_type); +} + +static void sdhci_msm_writew(struct sdhci_host *host, u16 val, int reg) +{ + writew_relaxed(val, host->ioaddr + reg); + __sdhci_msm_check_write(host, val, reg); +} + +static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) +{ + writeb_relaxed(val, host->ioaddr + reg); + __sdhci_msm_check_write(host, val, reg); +} +#endif static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1264,6 +1299,10 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + .write_w = sdhci_msm_writew, + .write_b = sdhci_msm_writeb, +#endif }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 5/5] defconfig: msm: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS
On 8/22/2017 3:08 PM, Ulf Hansson wrote: On 18 August 2017 at 07:19, Vijay Viswanath wrote: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS so that SDHC controller specific register read and write APIs, if registered, can be used. Signed-off-by: Vijay Viswanath --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 65cdd87..a3c93ed 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -398,6 +398,7 @@ CONFIG_MMC_SDHCI_CADENCE=y CONFIG_MMC_SDHCI_TEGRA=y CONFIG_MMC_MESON_GX=y CONFIG_MMC_SDHCI_MSM=y +CONFIG_MMC_SDHCI_IO_ACCESSORS=y CONFIG_MMC_SPI=y CONFIG_MMC_SDHI=y CONFIG_MMC_DW=y CONFIG_MMC_SDHCI_IO_ACCESSORS is intended to be selected by those SDHCI variants that needs it. May therefore suggest you add a "select line" for MMC_SDHCI_MSM in drivers/mmc/host/Kconfig: select CONFIG_MMC_SDHCI_IO_ACCESSORS if ARM64 Will do this way. Thanks! Kind regards Uffe -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v1 5/5] mmc: Kconfig: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS
On 9/14/2017 12:04 PM, Adrian Hunter wrote: On 30/08/17 15:51, Vijay Viswanath wrote: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS so that SDHC controller specific register read and write APIs, if registered, can be used. Missing signed-off Why don't you put this patch earlier in the patch set and then you don't need all the ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS ? This will involve merging the current patches 3 & 4 into one (Some functions defined under ifdef in patch 3 are used only in patch 4). Will that be fine ? --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2db84dd..64a9298 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -420,6 +420,7 @@ config MMC_SDHCI_MSM tristate "Qualcomm SDHCI Controller Support" depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on MMC_SDHCI_PLTFM + select CONFIG_MMC_SDHCI_IO_ACCESSORS CONFIG_MMC_SDHCI_IO_ACCESSORS -> MMC_SDHCI_IO_ACCESSORS help This selects the Secure Digital Host Controller Interface (SDHCI) support present in Qualcomm SOCs. The controller supports
[PATCH v2 0/4] mmc: sdhci-msm: Corrections to implementation of power irq
Register writes which change voltage of IO lines or turn the IO bus on/off require sdhc controller to be ready before progressing further. Once a register write which affects IO lines is done, the driver should wait for power irq from controller. Once the irq comes, the driver should acknowledge the irq by writing to power control register. If the acknowledgement is not given to controller, the controller may not complete the corresponding register write action and this can mess up the controller if drivers proceeds without power irq completing. Changes since v1: Patch enabling MMC_IO_ACCESSORS in Kconfig moved up the patch list. Also corrected a mistake in the patch. Removed all ifdef CONFIG_MMC_IO_ACCESSORS since the patches using it will come after MMC_IO_ACCESSORS are enabled. Merged the patches 3 & 4 of earlier series into 1 patch (patch 4 in current series). Corrected a mistake in a comment text in patch 3 of previous series. Changes since RFC: wait_for_completion_timeout replaced with wait_event_timeout when waiting for power irq. Removed the spinlock within power irq handler and API which waits for power irq. Added comments to sdhci msm register write functions, warning that they can sleep. Sdhci msm register write functions will do a memory barrier before writing to the register if the particular register can trigger power irq. Instead of enabling SDHCI IO ACCESSORS config in arm64/defconfig, it will be selected in mmc/host/Kconfig if the platform is MMC_SDHCI_MSM. Sahitya Tummala (1): mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset Subhash Jadavani (1): mmc: sdhci-msm: fix issue with power irq Vijay Viswanath (2): mmc: Kconfig: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS mmc: sdhci-msm: Add sdhci msm register write APIs which wait for pwr irq drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-msm.c | 235 ++- 2 files changed, 231 insertions(+), 5 deletions(-) -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 4/4] mmc: sdhci-msm: Add sdhci msm register write APIs which wait for pwr irq
Register writes which change voltage of IO lines or turn the IO bus on/off require controller to be ready before progressing further. When the controller is ready, it will generate a power irq which needs to be handled. The thread which initiated the register write should wait for power irq to complete. This will be done through the new sdhc msm write APIs which will check whether the particular write can trigger a power irq and wait for it with a timeout if it is expected. The SDHC core power control IRQ gets triggered when - * There is a state change in power control bit (bit 0) of SDHCI_POWER_CONTROL register. * There is a state change in 1.8V enable bit (bit 3) of SDHCI_HOST_CONTROL2 register. * Bit 1 of SDHCI_SOFTWARE_RESET is set. Also add support APIs which are used by sdhc msm write APIs to check if power irq is expected to be generated and wait for the power irq to come and complete if the irq is expected. This patch requires CONFIG_MMC_SDHCI_IO_ACCESSORS to be enabled. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 173 ++- 1 file changed, 171 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 42a65ab..b72a487 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -123,6 +123,10 @@ #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) #define MSM_MMC_AUTOSUSPEND_DELAY_MS 50 + +/* Timeout value to avoid infinite waiting for pwr_irq */ +#define MSM_PWR_IRQ_TIMEOUT_MS 5000 + struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ @@ -138,6 +142,10 @@ struct sdhci_msm_host { bool calibration_done; u8 saved_tuning_phase; bool use_cdclp533; + u32 curr_pwr_state; + u32 curr_io_level; + wait_queue_head_t pwr_irq_wait; + bool pwr_irq_flag; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -995,6 +1003,73 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } +static inline void sdhci_msm_init_pwr_irq_wait(struct sdhci_msm_host *msm_host) +{ + init_waitqueue_head(&msm_host->pwr_irq_wait); +} + +static inline void sdhci_msm_complete_pwr_irq_wait( + struct sdhci_msm_host *msm_host) +{ + wake_up(&msm_host->pwr_irq_wait); +} + +/* + * sdhci_msm_check_power_status API should be called when registers writes + * which can toggle sdhci IO bus ON/OFF or change IO lines HIGH/LOW happens. + * To what state the register writes will change the IO lines should be passed + * as the argument req_type. This API will check whether the IO line's state + * is already the expected state and will wait for power irq only if + * power irq is expected to be trigerred based on the current IO line state + * and expected IO line state. + */ +static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + bool done = false; + + pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", + mmc_hostname(host->mmc), __func__, req_type, + msm_host->curr_pwr_state, msm_host->curr_io_level); + + /* +* The IRQ for request type IO High/LOW will be generated when - +* there is a state change in 1.8V enable bit (bit 3) of +* SDHCI_HOST_CONTROL2 register. The reset state of that bit is 0 +* which indicates 3.3V IO voltage. So, when MMC core layer tries +* to set it to 3.3V before card detection happens, the +* IRQ doesn't get triggered as there is no state change in this bit. +* The driver already handles this case by changing the IO voltage +* level to high as part of controller power up sequence. Hence, check +* for host->pwr to handle a case where IO voltage high request is +* issued even before controller power up. +*/ + if ((req_type & REQ_IO_HIGH) && !host->pwr) { + pr_debug("%s: do not wait for power IRQ that never comes, req_type: %d\n", + mmc_hostname(host->mmc), req_type); + return; + } + if ((req_type & msm_host->curr_pwr_state) || + (req_type & msm_host->curr_io_level)) + done = true; + /* +* This is needed here to handle cases where register writes will +* not change the current bus state or io level of the controller. +* In this case, no power irq will be triggerred and we should +* not wait. +*/ + if (!done) { +
[PATCH v2 3/4] mmc: Kconfig: Enable CONFIG_MMC_SDHCI_IO_ACCESSORS
Enable CONFIG_MMC_SDHCI_IO_ACCESSORS so that SDHC controller specific register read and write APIs, if registered, can be used. Signed-off-by: Vijay Viswanath --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 2db84dd..11bfb82 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -420,6 +420,7 @@ config MMC_SDHCI_MSM tristate "Qualcomm SDHCI Controller Support" depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS help This selects the Secure Digital Host Controller Interface (SDHCI) support present in Qualcomm SOCs. The controller supports -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 1/4] mmc: sdhci-msm: fix issue with power irq
From: Subhash Jadavani SDCC controller reset (SW_RST) during probe may trigger power irq if previous status of PWRCTL was either BUS_ON or IO_HIGH_V. So before we enable the power irq interrupt in GIC (by registering the interrupt handler), we need to ensure that any pending power irq interrupt status is acknowledged otherwise power irq interrupt handler would be fired prematurely. Signed-off-by: Subhash Jadavani Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 18 ++ 1 file changed, 18 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 9d601dc..d636251 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1250,6 +1250,21 @@ static int sdhci_msm_probe(struct platform_device *pdev) CORE_VENDOR_SPEC_CAPABILITIES0); } + /* +* 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 +* interrupt in GIC, any pending power irq interrupt should be +* acknowledged. Otherwise power irq interrupt handler would be +* fired prematurely. +*/ + sdhci_msm_voltage_switch(host); + + /* +* Ensure that above writes are propogated before interrupt enablement +* in GIC. +*/ + mb(); + /* Setup IRQ for handling power/voltage tasks with PMIC */ msm_host->pwr_irq = platform_get_irq_byname(pdev, "pwr_irq"); if (msm_host->pwr_irq < 0) { @@ -1259,6 +1274,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) goto clk_disable; } + /* Enable pwr irq interrupts */ + writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, dev_name(&pdev->dev), host); -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
[PATCH v2 2/4] mmc: sdhci-msm: Fix HW issue with power IRQ handling during reset
From: Sahitya Tummala There is a rare scenario in HW, where the first clear pulse could be lost when the actual reset and clear/read of status register are happening at the same time. Fix this by retrying upto 10 times to ensure the status register gets cleared. Otherwise, this will lead to a spurious power IRQ which results in system instability. Signed-off-by: Sahitya Tummala Signed-off-by: Vijay Viswanath --- drivers/mmc/host/sdhci-msm.c | 46 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index d636251..42a65ab 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -995,17 +995,52 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, sdhci_msm_hs400(host, &mmc->ios); } -static void sdhci_msm_voltage_switch(struct sdhci_host *host) +static void sdhci_msm_dump_pwr_ctrl_regs(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + pr_err("%s: PWRCTL_STATUS: 0x%08x | PWRCTL_MASK: 0x%08x | PWRCTL_CTL: 0x%08x\n", + mmc_hostname(host->mmc), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_MASK), + readl_relaxed(msm_host->core_mem + CORE_PWRCTL_CTL)); +} + +static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; + int retry = 10; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + /* +* There is a rare HW scenario where the first clear pulse could be +* lost when actual reset and clear/read of status register is +* happening at a time. Hence, retry for at least 10 times to make +* sure status register is cleared. Otherwise, this will result in +* a spurious power IRQ resulting in system instability. +*/ + while (irq_status & readl_relaxed(msm_host->core_mem + + CORE_PWRCTL_STATUS)) { + if (retry == 0) { + pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", + mmc_hostname(host->mmc), irq_status); + sdhci_msm_dump_pwr_ctrl_regs(host); + WARN_ON(1); + break; + } + writel_relaxed(irq_status, + msm_host->core_mem + CORE_PWRCTL_CLEAR); + retry--; + udelay(10); + } + if (irq_status & (CORE_PWRCTL_BUS_ON | CORE_PWRCTL_BUS_OFF)) irq_ack |= CORE_PWRCTL_BUS_SUCCESS; if (irq_status & (CORE_PWRCTL_IO_LOW | CORE_PWRCTL_IO_HIGH)) @@ -1017,13 +1052,17 @@ static void sdhci_msm_voltage_switch(struct sdhci_host *host) * switches are handled by the sdhci core, so just report success. */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + + pr_debug("%s: %s: Handled IRQ(%d), irq_status=0x%x, ack=0x%x\n", + mmc_hostname(msm_host->mmc), __func__, irq, irq_status, + irq_ack); } static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) { struct sdhci_host *host = (struct sdhci_host *)data; - sdhci_msm_voltage_switch(host); + sdhci_msm_handle_pwr_irq(host, irq); return IRQ_HANDLED; } @@ -1106,7 +1145,6 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = sdhci_msm_set_uhs_signaling, - .voltage_switch = sdhci_msm_voltage_switch, }; static const struct sdhci_pltfm_data sdhci_msm_pdata = { @@ -1257,7 +1295,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) * acknowledged. Otherwise power irq interrupt handler would be * fired prematurely. */ - sdhci_msm_voltage_switch(host); + sdhci_msm_handle_pwr_irq(host, 0); /* * Ensure that above writes are propogated before interrupt enablement -- Qualcomm India Private Limited, on behalf of Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Re: [PATCH v2 4/4] mmc: sdhci-msm: Add sdhci msm register write APIs which wait for pwr irq
On 10/14/2017 1:01 PM, Bjorn Andersson wrote: On Tue 26 Sep 22:34 PDT 2017, Vijay Viswanath wrote: diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c [..] + if (!done) { + if (!wait_event_timeout(msm_host->pwr_irq_wait, + msm_host->pwr_irq_flag, + msecs_to_jiffies(MSM_PWR_IRQ_TIMEOUT_MS))) + __WARN_printf("%s: pwr_irq for req: (%d) timed out\n", + mmc_hostname(host->mmc), req_type); Bumped my MSM8974AB device to latest linux-next and found this patch; I see this print every five seconds during boot and the eMMC never comes up. Regards, Bjorn -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Hi Bjorn, I couldn't get one 8974 device. I tested the latest linux-next on db410c and its not showing any issue. 8974 is an older msm and has some differences in the controller like your "mmc: sdhci-msm: Enable delay circuit calibration clocks" patch. I am currently going through the programming guide to see what could be the reason. Can you please share the full logs from the device with me so that I can focus my search? Thanks, Vijay