diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-msm.c')
-rw-r--r-- | drivers/mmc/host/sdhci-msm.c | 510 |
1 files changed, 384 insertions, 126 deletions
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 646bf377ba77..3cc8bfee6c18 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_RST BIT(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_CTL 0xe8 #define CORE_PWRCTL_BUS_OFF BIT(0) #define CORE_PWRCTL_BUS_ON BIT(1) #define CORE_PWRCTL_IO_LOW BIT(2) @@ -63,17 +58,13 @@ #define CORE_CDR_EXT_EN BIT(19) #define CORE_DLL_PDN BIT(29) #define CORE_DLL_RST BIT(30) -#define CORE_DLL_CONFIG 0x100 #define CORE_CMD_DAT_TRACK_SEL BIT(0) -#define CORE_DLL_STATUS 0x108 -#define CORE_DLL_CONFIG_2 0x1b4 #define CORE_DDR_CAL_EN BIT(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_SEL BIT(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_CONFIG 0x1b8 #define DDR_CONFIG_POR_VAL 0x80040853 -#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c #define INVALID_TUNING_PHASE -1 #define SDHCI_MSM_MIN_CLOCK 400000 @@ -137,6 +125,117 @@ /* 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; + 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_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 */ @@ -156,8 +255,53 @@ 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; }; +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. + */ +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) { @@ -205,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) { @@ -218,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; @@ -235,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_lock_irqsave(&host->lock, flags); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN); config |= (CORE_CDR_EXT_EN | CORE_DLL_EN); - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */ rc = msm_dll_poll_ck_out_en(host, 0); @@ -255,24 +403,24 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase) * Write the selected DLL clock output phase (0 ... 15) * to CDR_SELEXT bit field of DLL_CONFIG register. */ - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~CDR_SELEXT_MASK; config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ rc = msm_dll_poll_ck_out_en(host, 1); if (rc) goto err_out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CDR_EN; config &= ~CORE_CDR_EXT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); goto out; err_out: @@ -398,6 +546,8 @@ static int msm_find_most_appropriate_phase(struct sdhci_host *host, static inline void msm_cm_dll_set_freq(struct sdhci_host *host) { u32 mclk_freq = 0, config; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); /* Program the MCLK value to MCLK_FREQ bit field */ if (host->clock <= 112000000) @@ -417,10 +567,10 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host) else if (host->clock <= 200000000) mclk_freq = 7; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config &= ~CMUX_SHIFT_PHASE_MASK; config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); } /* Initialize the DLL (Programmable Delay Line) */ @@ -432,6 +582,8 @@ static int msm_init_cm_dll(struct sdhci_host *host) int wait_cnt = 50; unsigned long flags; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; spin_lock_irqsave(&host->lock, flags); @@ -440,34 +592,43 @@ static int msm_init_cm_dll(struct sdhci_host *host) * tuning is in progress. Keeping PWRSAVE ON may * turn off the clock. */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_CLK_PWRSAVE; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); if (msm_host->use_14lpp_dll_reset) { - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config |= CORE_DLL_CLOCK_DISABLE; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); msm_cm_dll_set_freq(host); if (msm_host->use_14lpp_dll_reset && !IS_ERR_OR_NULL(msm_host->xo_clk)) { u32 mclk_freq = 0; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= CORE_FLL_CYCLE_CNT; if (config) mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 8), @@ -476,40 +637,52 @@ static int msm_init_cm_dll(struct sdhci_host *host) mclk_freq = DIV_ROUND_CLOSEST_ULL((host->clock * 4), clk_get_rate(msm_host->xo_clk)); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= ~(0xFF << 10); config |= mclk_freq << 10; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); /* wait for 5us before enabling DLL clock */ udelay(5); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config &= ~CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); if (msm_host->use_14lpp_dll_reset) { msm_cm_dll_set_freq(host); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config_2); config &= ~CORE_DLL_CLOCK_DISABLE; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config_2); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_CK_OUT_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ - while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) & + while (!(readl_relaxed(host->ioaddr + msm_offset->core_dll_status) & CORE_DLL_LOCK)) { /* max. wait for 50us sec for LOCK bit to be set */ if (--wait_cnt == 0) { @@ -530,19 +703,21 @@ static void msm_hc_select_default(struct sdhci_host *host) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; if (!msm_host->use_cdclp533) { config = readl_relaxed(host->ioaddr + - CORE_VENDOR_SPEC3); + msm_offset->core_vendor_spec3); config &= ~CORE_PWRSAVE_DLL; writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC3); + msm_offset->core_vendor_spec3); } - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_MCLK_SEL_MASK; config |= CORE_HC_MCLK_SEL_DFLT; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Disable HC_SELECT_IN to be able to use the UHS mode select @@ -551,10 +726,10 @@ static void msm_hc_select_default(struct sdhci_host *host) * Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field * in VENDOR_SPEC_FUNC */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_SELECT_IN_EN; config &= ~CORE_HC_SELECT_IN_MASK; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Make sure above writes impacting free running MCLK are completed @@ -570,32 +745,36 @@ static void msm_hc_select_hs400(struct sdhci_host *host) struct mmc_ios ios = host->mmc->ios; u32 config, dll_lock; int rc; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; /* Select the divided clock (free running MCLK/2) */ - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); config &= ~CORE_HC_MCLK_SEL_MASK; config |= CORE_HC_MCLK_SEL_HS400; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); /* * Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC * register */ if ((msm_host->tuning_done || ios.enhanced_strobe) && !msm_host->calibration_done) { - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); config |= CORE_HC_SELECT_IN_HS400; config |= CORE_HC_SELECT_IN_EN; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, host->ioaddr + + msm_offset->core_vendor_spec); } if (!msm_host->clk_rate && !msm_host->use_cdclp533) { /* * Poll on DLL_LOCK or DDR_DLL_LOCK bits in - * CORE_DLL_STATUS to be set. This should get set + * core_dll_status to be set. This should get set * within 15 us at 200 MHz. */ rc = readl_relaxed_poll_timeout(host->ioaddr + - CORE_DLL_STATUS, + msm_offset->core_dll_status, dll_lock, (dll_lock & (CORE_DLL_LOCK | @@ -647,6 +826,8 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 config, calib_done; int ret; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); @@ -663,13 +844,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) if (ret) goto out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config); config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config &= ~CORE_CDC_T4_DLY_SEL; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); config = readl_relaxed(host->ioaddr + CORE_CSR_CDC_GEN_CFG); config &= ~CORE_CDC_SWITCH_BYPASS_OFF; @@ -679,9 +860,9 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) config |= CORE_CDC_SWITCH_RC_EN; writel_relaxed(config, host->ioaddr + CORE_CSR_CDC_GEN_CFG); - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config &= ~CORE_START_CDC_TRAFFIC; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); /* Perform CDC Register Initialization Sequence */ @@ -733,9 +914,9 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + msm_offset->core_ddr_200_cfg); config |= CORE_START_CDC_TRAFFIC; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + msm_offset->core_ddr_200_cfg); out: pr_debug("%s: %s: Exit, ret %d\n", mmc_hostname(host->mmc), __func__, ret); @@ -747,32 +928,38 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; u32 dll_status, config; int ret; + const struct sdhci_msm_offset *msm_offset = + sdhci_priv_msm_offset(host); pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); /* - * Currently the CORE_DDR_CONFIG register defaults to desired + * Currently the core_ddr_config register defaults to desired * configuration on reset. Currently reprogramming the power on * reset (POR) value in case it might have been modified by * bootloaders. In the future, if this changes, then the desired * values will need to be programmed appropriately. */ - writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + CORE_DDR_CONFIG); + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + + msm_offset->core_ddr_config); if (mmc->ios.enhanced_strobe) { - config = readl_relaxed(host->ioaddr + CORE_DDR_200_CFG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_ddr_200_cfg); config |= CORE_CMDIN_RCLK_EN; - writel_relaxed(config, host->ioaddr + CORE_DDR_200_CFG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_ddr_200_cfg); } - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG_2); + config = readl_relaxed(host->ioaddr + msm_offset->core_dll_config_2); config |= CORE_DDR_CAL_EN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG_2); + writel_relaxed(config, host->ioaddr + msm_offset->core_dll_config_2); - ret = readl_relaxed_poll_timeout(host->ioaddr + CORE_DLL_STATUS, - dll_status, - (dll_status & CORE_DDR_DLL_LOCK), - 10, 1000); + ret = readl_relaxed_poll_timeout(host->ioaddr + + msm_offset->core_dll_status, + dll_status, + (dll_status & CORE_DDR_DLL_LOCK), + 10, 1000); if (ret == -ETIMEDOUT) { pr_err("%s: %s: CM_DLL_SDC4 calibration was not completed\n", @@ -780,9 +967,9 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) goto out; } - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC3); + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec3); config |= CORE_PWRSAVE_DLL; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC3); + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec3); /* * Drain writebuffer to ensure above DLL calibration @@ -802,6 +989,8 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) struct mmc_host *mmc = host->mmc; int ret; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: Enter\n", mmc_hostname(host->mmc), __func__); @@ -819,9 +1008,11 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) msm_host->saved_tuning_phase); if (ret) goto out; - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_CMD_DAT_TRACK_SEL; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); } if (msm_host->use_cdclp533) @@ -951,6 +1142,8 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u16 ctrl_2; u32 config; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */ @@ -991,13 +1184,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, * DLL is not required for clock <= 100MHz * Thus, make sure DLL it is disabled when not required */ - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_RST; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); - config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG); + config = readl_relaxed(host->ioaddr + + msm_offset->core_dll_config); config |= CORE_DLL_PDN; - writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG); + writel_relaxed(config, host->ioaddr + + msm_offset->core_dll_config); /* * The DLL needs to be restored and CDCLP533 recalibrated @@ -1039,7 +1236,9 @@ 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; - u32 val; + u32 val = SWITCHABLE_SIGNALING_VOLTAGE; + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; pr_debug("%s: %s: request %d curr_pwr_state %x curr_io_level %x\n", mmc_hostname(host->mmc), __func__, req_type, @@ -1048,8 +1247,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) /* * The power interrupt will not be generated for signal voltage * switches if SWITCHABLE_SIGNALING_VOLTAGE in MCI_GENERICS is not set. + * Since sdhci-msm-v5, this bit has been removed and SW must consider + * it as always set. */ - val = readl(msm_host->core_mem + CORE_MCI_GENERICS); + if (!msm_host->mci_removed) + val = msm_host_readl(msm_host, host, + msm_offset->core_generics); if ((req_type & REQ_IO_HIGH || req_type & REQ_IO_LOW) && !(val & SWITCHABLE_SIGNALING_VOLTAGE)) { return; @@ -1097,12 +1300,14 @@ 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); + const struct sdhci_msm_offset *msm_offset = + msm_host->offset; 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)); + mmc_hostname(host->mmc), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_status), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_mask), + msm_host_readl(msm_host, host, msm_offset->core_pwrctl_ctl)); } static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) @@ -1113,11 +1318,14 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) int retry = 10; u32 pwr_state = 0, io_level = 0; u32 config; + const struct sdhci_msm_offset *msm_offset = msm_host->offset; - irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); + irq_status = msm_host_readl(msm_host, host, + msm_offset->core_pwrctl_status); irq_status &= INT_MASK; - writel_relaxed(irq_status, msm_host->core_mem + CORE_PWRCTL_CLEAR); + msm_host_writel(msm_host, irq_status, host, + msm_offset->core_pwrctl_clear); /* * There is a rare HW scenario where the first clear pulse could be @@ -1126,8 +1334,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * 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)) { + while (irq_status & msm_host_readl(msm_host, host, + msm_offset->core_pwrctl_status)) { if (retry == 0) { pr_err("%s: Timedout clearing (0x%x) pwrctl status register\n", mmc_hostname(host->mmc), irq_status); @@ -1135,8 +1343,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) WARN_ON(1); break; } - writel_relaxed(irq_status, - msm_host->core_mem + CORE_PWRCTL_CLEAR); + msm_host_writel(msm_host, irq_status, host, + msm_offset->core_pwrctl_clear); retry--; udelay(10); } @@ -1167,7 +1375,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * report back if it succeded or not to this register. The voltage * switches are handled by the sdhci core, so just report success. */ - writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + msm_host_writel(msm_host, irq_ack, host, + msm_offset->core_pwrctl_ctl); /* * If we don't have info regarding the voltage levels supported by @@ -1186,7 +1395,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) * 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); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); new_config = config; if ((io_level & REQ_IO_HIGH) && @@ -1197,8 +1407,8 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) new_config |= CORE_IO_PAD_PWR_SWITCH; if (config ^ new_config) - writel_relaxed(new_config, - host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(new_config, host->ioaddr + + msm_offset->core_vendor_spec); } if (pwr_state) @@ -1359,6 +1569,7 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) struct regulator *supply = mmc->supply.vqmmc; u32 caps = 0, config; struct sdhci_host *host = mmc_priv(mmc); + const struct sdhci_msm_offset *msm_offset = msm_host->offset; if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 1700000, 1950000)) @@ -1378,7 +1589,8 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) */ u32 io_level = msm_host->curr_io_level; - config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config = readl_relaxed(host->ioaddr + + msm_offset->core_vendor_spec); config |= CORE_IO_PAD_PWR_SWITCH_EN; if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) @@ -1386,14 +1598,38 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT)) config |= CORE_IO_PAD_PWR_SWITCH; - writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + writel_relaxed(config, + host->ioaddr + msm_offset->core_vendor_spec); } msm_host->caps_0 |= caps; 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" }, + {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, + {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, {}, }; @@ -1429,6 +1665,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) u16 host_version, core_minor; u32 core_version, config; u8 core_major; + const struct sdhci_msm_offset *msm_offset; + const struct sdhci_msm_variant_info *var_info; host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host)); if (IS_ERR(host)) @@ -1444,6 +1682,18 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto pltfm_free; + /* + * Based on the compatible string, load the required msm host info from + * the data associated with the version info. + */ + var_info = of_device_get_match_data(&pdev->dev); + + msm_host->mci_removed = var_info->mci_removed; + msm_host->var_ops = var_info->var_ops; + msm_host->offset = var_info->offset; + + msm_offset = msm_host->offset; + sdhci_get_of_property(pdev); msm_host->saved_tuning_phase = INVALID_TUNING_PHASE; @@ -1508,32 +1758,39 @@ static int sdhci_msm_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "TCXO clk not present (%d)\n", ret); } - core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); - msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres); + if (!msm_host->mci_removed) { + core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + msm_host->core_mem = devm_ioremap_resource(&pdev->dev, + core_memres); - if (IS_ERR(msm_host->core_mem)) { - dev_err(&pdev->dev, "Failed to remap registers\n"); - ret = PTR_ERR(msm_host->core_mem); - goto clk_disable; + if (IS_ERR(msm_host->core_mem)) { + ret = PTR_ERR(msm_host->core_mem); + goto clk_disable; + } } /* Reset the vendor spec register to power on reset state */ writel_relaxed(CORE_VENDOR_SPEC_POR_VAL, - host->ioaddr + CORE_VENDOR_SPEC); - - /* Set HC_MODE_EN bit in HC_MODE register */ - writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE)); - - config = readl_relaxed(msm_host->core_mem + CORE_HC_MODE); - config |= FF_CLK_SW_RST_DIS; - writel_relaxed(config, msm_host->core_mem + CORE_HC_MODE); + host->ioaddr + msm_offset->core_vendor_spec); + + if (!msm_host->mci_removed) { + /* Set HC_MODE_EN bit in HC_MODE register */ + msm_host_writel(msm_host, HC_MODE_EN, host, + msm_offset->core_hc_mode); + config = msm_host_readl(msm_host, host, + msm_offset->core_hc_mode); + config |= FF_CLK_SW_RST_DIS; + msm_host_writel(msm_host, config, host, + msm_offset->core_hc_mode); + } host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n", host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT)); - core_version = readl_relaxed(msm_host->core_mem + CORE_MCI_VERSION); + core_version = msm_host_readl(msm_host, host, + msm_offset->core_mci_version); core_major = (core_version & CORE_VERSION_MAJOR_MASK) >> CORE_VERSION_MAJOR_SHIFT; core_minor = core_version & CORE_VERSION_MINOR_MASK; @@ -1558,7 +1815,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) config = readl_relaxed(host->ioaddr + SDHCI_CAPABILITIES); config |= SDHCI_CAN_VDD_300 | SDHCI_CAN_DO_8BIT; writel_relaxed(config, host->ioaddr + - CORE_VENDOR_SPEC_CAPABILITIES0); + msm_offset->core_vendor_spec_capabilities0); } /* @@ -1587,7 +1844,8 @@ static int sdhci_msm_probe(struct platform_device *pdev) sdhci_msm_init_pwr_irq_wait(msm_host); /* Enable pwr irq interrupts */ - writel_relaxed(INT_MASK, msm_host->core_mem + CORE_PWRCTL_MASK); + msm_host_writel(msm_host, INT_MASK, host, + msm_offset->core_pwrctl_mask); ret = devm_request_threaded_irq(&pdev->dev, msm_host->pwr_irq, NULL, sdhci_msm_pwr_irq, IRQF_ONESHOT, |