Loading Documentation/devicetree/bindings/mmc/sdhci-msm.txt +3 −5 Original line number Diff line number Diff line Loading @@ -91,11 +91,9 @@ Optional Properties: contents will not be retained. It is software responsibility to restore the SDCC registers before resuming to normal operation. - qcom,force-sdhc1-probe: Force probing sdhc1 even if it is not the boot device. - qcom,ddr-config: Certain chipsets and platforms require particular settings for the RCLK delay DLL configuration register for HS400 mode to work. This value can vary between platforms and msms. If a msm/platform require a different DLL setting than the default/POR setting for HS400 mode, it can be specified using this field. - qcom,dll-hsr-list: List of DLL-HSR values which are tuned for given process-node and platform. The sequence of values in this list must follow the sequence listed in sdhci_msm_dll_hsr data structure. In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. - qcom,<supply>-lpm_sup - specifies whether supply can be kept in low power mode (lpm). Loading drivers/mmc/host/sdhci-msm.c +191 −57 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ * drivers/mmc/host/sdhci-msm.c - Qualcomm Technologies, Inc. MSM SDHCI Platform * driver source file * * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -155,12 +155,12 @@ #define CORE_FLL_CYCLE_CNT (1 << 18) #define CORE_DLL_CLOCK_DISABLE (1 << 21) #define DDR_CONFIG_POR_VAL 0x80040853 #define DDR_CONFIG_PRG_RCLK_DLY_MASK 0x1FF #define DDR_CONFIG_PRG_RCLK_DLY 115 #define DDR_CONFIG_2_POR_VAL 0x80040873 #define DDR_CONFIG_POR_VAL 0x80040873 #define DLL_USR_CTL_POR_VAL 0x10800 #define ENABLE_DLL_LOCK_STATUS (1 << 26) #define FINE_TUNE_MODE_EN (1 << 27) #define BIAS_OK_SIGNAL (1 << 29) #define DLL_CONFIG_3_POR_VAL 0x10 /* 512 descriptors */ #define SDHCI_MSM_MAX_SEGMENTS (1 << 9) Loading Loading @@ -203,8 +203,9 @@ struct sdhci_msm_offset { u32 CORE_DDR_200_CFG; u32 CORE_VENDOR_SPEC3; u32 CORE_DLL_CONFIG_2; u32 CORE_DLL_CONFIG_3; u32 CORE_DDR_CONFIG; u32 CORE_DDR_CONFIG_2; u32 CORE_DDR_CONFIG_OLD; /* Applcable to sddcc minor ver < 0x49 only */ u32 CORE_DLL_USR_CTL; /* Present on SDCC5.1 onwards */ }; Loading Loading @@ -233,8 +234,8 @@ struct sdhci_msm_offset sdhci_msm_offset_mci_removed = { .CORE_DDR_200_CFG = 0x224, .CORE_VENDOR_SPEC3 = 0x250, .CORE_DLL_CONFIG_2 = 0x254, .CORE_DDR_CONFIG = 0x258, .CORE_DDR_CONFIG_2 = 0x25C, .CORE_DLL_CONFIG_3 = 0x258, .CORE_DDR_CONFIG = 0x25C, .CORE_DLL_USR_CTL = 0x388, }; Loading Loading @@ -263,8 +264,9 @@ struct sdhci_msm_offset sdhci_msm_offset_mci_present = { .CORE_DDR_200_CFG = 0x184, .CORE_VENDOR_SPEC3 = 0x1B0, .CORE_DLL_CONFIG_2 = 0x1B4, .CORE_DDR_CONFIG = 0x1B8, .CORE_DDR_CONFIG_2 = 0x1BC, .CORE_DLL_CONFIG_3 = 0x1B8, .CORE_DDR_CONFIG_OLD = 0x1B8, /* Applicable to sdcc minor ver < 0x49 */ .CORE_DDR_CONFIG = 0x1BC, }; u8 sdhci_msm_readb_relaxed(struct sdhci_host *host, u32 offset) Loading Loading @@ -366,6 +368,14 @@ enum vdd_io_level { VDD_IO_SET_LEVEL, }; enum dll_init_context { DLL_INIT_NORMAL = 0, DLL_INIT_FROM_CX_COLLAPSE_EXIT, }; static unsigned int sdhci_msm_get_sup_clk_rate(struct sdhci_host *host, u32 req_clk); /* MSM platform specific tuning */ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) Loading Loading @@ -530,11 +540,13 @@ 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. */ if (msm_host->dll_hsr->dll_config & (0xF << 20)) { writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) & ~(0xF << 20)) | (grey_coded_phase_table[phase] << 20)), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); } /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */ writel_relaxed((readl_relaxed(host->ioaddr + Loading Loading @@ -710,7 +722,8 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host) } /* Initialize the DLL (Programmable Delay Line ) */ static int msm_init_cm_dll(struct sdhci_host *host) static int msm_init_cm_dll(struct sdhci_host *host, enum dll_init_context init_context) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; Loading Loading @@ -763,22 +776,44 @@ static int msm_init_cm_dll(struct sdhci_host *host) writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) | CORE_DLL_PDN), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_cm_dll_set_freq(host); if (msm_host->use_updated_dll_reset) { u32 mclk_freq = 0; u32 actual_clk = sdhci_msm_get_sup_clk_rate(host, host->clock); /* * Only configure the mclk_freq in normal DLL init * context. If the DLL init is coming from * CX Collapse Exit context, the host->clock may be zero. * The DLL_CONFIG_2 register has already been restored to * proper value prior to getting here. */ if (init_context == DLL_INIT_NORMAL) { switch (actual_clk) { case 202000000: case 201500000: case 200000000: mclk_freq = 42; break; case 192000000: mclk_freq = 40; break; default: mclk_freq = (u32)((actual_clk / TCXO_FREQ) * 4); pr_info_once("%s: %s: Non standard clk freq =%u\n", mmc_hostname(mmc), __func__, actual_clk); } if ((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2) & CORE_FLL_CYCLE_CNT)) mclk_freq = (u32) ((host->clock / TCXO_FREQ) * 8); else mclk_freq = (u32) ((host->clock / TCXO_FREQ) * 4); mclk_freq *= 2; writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2) & ~(0xFF << 10)) | (mclk_freq << 10)), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); } /* wait for 5us before enabling DLL clock */ udelay(5); } Loading @@ -794,7 +829,6 @@ static int msm_init_cm_dll(struct sdhci_host *host) host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); if (msm_host->use_updated_dll_reset) { msm_cm_dll_set_freq(host); /* Enable the DLL clock */ writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2) Loading @@ -804,11 +838,39 @@ static int msm_init_cm_dll(struct sdhci_host *host) /* * Configure DLL user control register to enable DLL status * This setting is applicable to SDCC v5.1 onwards only * This setting is applicable to SDCC v5.1 onwards only. * * Configure Tassadar DLL (Only applicable for 7FF projects) * */ if (msm_host->need_dll_user_ctl) { writel_relaxed(DLL_USR_CTL_POR_VAL | ENABLE_DLL_LOCK_STATUS, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); if (msm_host->dll_hsr) { writel_relaxed(msm_host->dll_hsr->dll_usr_ctl, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); writel_relaxed(msm_host->dll_hsr->dll_config_3, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); } else { writel_relaxed(DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN | ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); writel_relaxed(DLL_CONFIG_3_POR_VAL, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); } } /* * Update the lower byte of DLL_CONFIG only with HSR values. * Since these are the static settings. */ if (msm_host->dll_hsr) { writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) & (~0xff)) | (msm_host->dll_hsr->dll_config & 0xff)), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); } /* Set DLL_EN bit to 1. */ Loading Loading @@ -976,7 +1038,7 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) struct sdhci_msm_host *msm_host = pltfm_host->priv; const struct sdhci_msm_offset *msm_host_offset = msm_host->offset; u32 dll_status, ddr_config; u32 dll_status; int ret = 0; pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__); Loading @@ -987,16 +1049,16 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) */ if (msm_host->pdata->rclk_wa) { writel_relaxed(msm_host->pdata->ddr_config, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG_2); msm_host_offset->CORE_DDR_CONFIG); } else if (msm_host->dll_hsr && msm_host->dll_hsr->ddr_config) { writel_relaxed(msm_host->dll_hsr->ddr_config, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); } else if (msm_host->rclk_delay_fix) { writel_relaxed(DDR_CONFIG_2_POR_VAL, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG_2); } else { ddr_config = DDR_CONFIG_POR_VAL & ~DDR_CONFIG_PRG_RCLK_DLY_MASK; ddr_config |= DDR_CONFIG_PRG_RCLK_DLY; writel_relaxed(ddr_config, host->ioaddr + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); } else { writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG_OLD); } if (msm_host->enhanced_strobe && mmc_card_strobe(msm_host->mmc->card)) Loading Loading @@ -1065,7 +1127,7 @@ static int sdhci_msm_enhanced_strobe(struct sdhci_host *host) /* * Reset the tuning block. */ ret = msm_init_cm_dll(host); ret = msm_init_cm_dll(host, DLL_INIT_NORMAL); if (ret) goto out; Loading @@ -1092,7 +1154,7 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) * Retuning in HS400 (DDR mode) will fail, just reset the * tuning block and restore the saved tuning phase. */ ret = msm_init_cm_dll(host); ret = msm_init_cm_dll(host, DLL_INIT_NORMAL); if (ret) goto out; Loading @@ -1100,12 +1162,13 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); if (ret) goto out; if (msm_host->dll_hsr->dll_config & CORE_CMD_DAT_TRACK_SEL) { /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) | CORE_CMD_DAT_TRACK_SEL), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); } if (msm_host->use_cdclp533) /* Calibrate CDCLP533 DLL HW */ Loading Loading @@ -1215,7 +1278,7 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) tuned_phase_cnt = 0; /* first of all reset the tuning block */ rc = msm_init_cm_dll(host); rc = msm_init_cm_dll(host, DLL_INIT_NORMAL); if (rc) goto kfree; Loading Loading @@ -1965,6 +2028,32 @@ static int sdhci_msm_get_socrev(struct device *dev, } #endif static int sdhci_msm_dt_parse_hsr_info(struct device *dev, struct sdhci_msm_host *msm_host) { u32 *dll_hsr_table = NULL; int dll_hsr_table_len, dll_hsr_reg_count; int ret = 0; if (sdhci_msm_dt_get_array(dev, "qcom,dll-hsr-list", &dll_hsr_table, &dll_hsr_table_len, 0)) goto skip_hsr; dll_hsr_reg_count = sizeof(struct sdhci_msm_dll_hsr) / sizeof(u32); if (dll_hsr_table_len != dll_hsr_reg_count) { dev_err(dev, "Number of HSR entries are not matching\n"); ret = -EINVAL; } else { msm_host->dll_hsr = (struct sdhci_msm_dll_hsr *)dll_hsr_table; } skip_hsr: if (!msm_host->dll_hsr) dev_info(dev, "Failed to get dll hsr settings from dt\n"); return ret; } /* Parse platform data */ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, Loading Loading @@ -2142,6 +2231,9 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, if (!of_property_read_u32(np, "qcom,ddr-config", &pdata->ddr_config)) pdata->rclk_wa = true; if (sdhci_msm_dt_parse_hsr_info(dev, msm_host)) goto out; /* * rclk_wa is not required if soc version is mentioned and * is not base version. Loading Loading @@ -3140,6 +3232,19 @@ static void sdhci_msm_registers_save(struct sdhci_host *host) sdhci_readl(host, SDHCI_CAPABILITIES_1); msm_host->regs_restore.testbus_config = readl_relaxed(host->ioaddr + msm_host_offset->CORE_TESTBUS_CONFIG); msm_host->regs_restore.dll_config = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_host->regs_restore.dll_config2 = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); msm_host->regs_restore.dll_config = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_host->regs_restore.dll_config2 = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); msm_host->regs_restore.dll_config3 = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); msm_host->regs_restore.dll_usr_ctl = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); msm_host->regs_restore.is_valid = true; pr_debug("%s: %s: registers saved. PWRCTL_MASK = 0x%x\n", Loading @@ -3155,6 +3260,7 @@ static void sdhci_msm_registers_restore(struct sdhci_host *host) u8 irq_status; const struct sdhci_msm_offset *msm_host_offset = msm_host->offset; struct mmc_ios ios = host->mmc->ios; if (!msm_host->regs_restore.is_supported || !msm_host->regs_restore.is_valid) Loading Loading @@ -3206,6 +3312,24 @@ static void sdhci_msm_registers_restore(struct sdhci_host *host) writel_relaxed(msm_host->regs_restore.vendor_pwrctl_mask, host->ioaddr + msm_host_offset->CORE_PWRCTL_MASK); if (((ios.timing == MMC_TIMING_MMC_HS400) || (ios.timing == MMC_TIMING_MMC_HS200) || (ios.timing == MMC_TIMING_UHS_SDR104)) && (ios.clock > CORE_FREQ_100MHZ)) { writel_relaxed(msm_host->regs_restore.dll_config2, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); writel_relaxed(msm_host->regs_restore.dll_config3, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); writel_relaxed(msm_host->regs_restore.dll_usr_ctl, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); writel_relaxed(msm_host->regs_restore.dll_config & ~(CORE_DLL_RST | CORE_DLL_PDN), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_init_cm_dll(host, DLL_INIT_FROM_CX_COLLAPSE_EXIT); msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); } pr_debug("%s: %s: registers restored. PWRCTL_MASK = 0x%x\n", mmc_hostname(host->mmc), __func__, readl_relaxed(host->ioaddr + Loading Loading @@ -3512,9 +3636,9 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) * DLL input clock */ writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DDR_CONFIG)) msm_host_offset->CORE_DLL_CONFIG_3)) | RCLK_TOGGLE), host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); msm_host_offset->CORE_DLL_CONFIG_3); /* ensure above write as toggling same bit quickly */ wmb(); udelay(2); Loading @@ -3523,9 +3647,9 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) * DLL input clock */ writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DDR_CONFIG)) msm_host_offset->CORE_DLL_CONFIG_3)) & ~RCLK_TOGGLE), host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); msm_host_offset->CORE_DLL_CONFIG_3); } if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533) { /* Loading Loading @@ -3768,24 +3892,34 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) msm_host_offset->CORE_MCI_FIFO_CNT), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_MCI_STATUS)); pr_info("DLL cfg: 0x%08x | DLL sts: 0x%08x | SDCC ver: 0x%08x\n", pr_info("DLL sts: 0x%08x | DLL cfg: 0x%08x | DLL cfg2: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_STATUS), readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_DLL_CONFIG_2)); pr_info("DLL cfg3: 0x%08x | DLL usr ctl: 0x%08x | DDR cfg: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_STATUS), msm_host_offset->CORE_DLL_CONFIG_3), readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_MCI_VERSION)); pr_info("Vndr func: 0x%08x | Vndr adma err : addr0: 0x%08x addr1: 0x%08x\n", msm_host_offset->CORE_DDR_CONFIG)); pr_info("SDCC ver: 0x%08x | Vndr adma err : addr0: 0x%08x addr1: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC), msm_host_offset->CORE_MCI_VERSION), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_ADMA_ERR_ADDR0), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_ADMA_ERR_ADDR1)); pr_info("Vndr func2: 0x%08x\n", pr_info("Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_FUNC2)); msm_host_offset->CORE_VENDOR_SPEC), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_FUNC2), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC3)); /* * tbsel indicates [2:0] bits and tbsel2 indicates [7:4] bits * of CORE_TESTBUS_CONFIG register. Loading Loading @@ -4577,7 +4711,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host, * starts coming. */ if ((major == 1) && ((minor == 0x42) || (minor == 0x46) || (minor == 0x49) || (minor >= 0x6b))) (minor == 0x49) || (minor == 0x4D) || (minor >= 0x6b))) msm_host->use_14lpp_dll = true; /* Fake 3.0V support for SDIO devices which requires such voltage */ Loading Loading @@ -4951,7 +5085,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) sdhci_set_default_hw_caps(msm_host, host); /* * Set the PAD_PWR_SWTICH_EN bit so that the PAD_PWR_SWITCH bit can * 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 + Loading drivers/mmc/host/sdhci-msm.h +19 −1 Original line number Diff line number Diff line /* * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -195,6 +195,23 @@ struct sdhci_msm_regs_restore { u32 hc_3c_3e; u32 hc_caps_1; u32 testbus_config; u32 dll_config; u32 dll_config2; u32 dll_config3; u32 dll_usr_ctl; }; /* * DLL registers which needs be programmed with HSR settings. * Add any new register only at the end and don't change the * seqeunce. */ struct sdhci_msm_dll_hsr { u32 dll_config; u32 dll_config_2; u32 dll_config_3; u32 dll_usr_ctl; u32 ddr_config; }; struct sdhci_msm_debug_data { Loading Loading @@ -258,6 +275,7 @@ struct sdhci_msm_host { int soc_min_rev; struct workqueue_struct *pm_qos_wq; bool need_dll_user_ctl; struct sdhci_msm_dll_hsr *dll_hsr; }; extern char *saved_command_line; Loading Loading
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +3 −5 Original line number Diff line number Diff line Loading @@ -91,11 +91,9 @@ Optional Properties: contents will not be retained. It is software responsibility to restore the SDCC registers before resuming to normal operation. - qcom,force-sdhc1-probe: Force probing sdhc1 even if it is not the boot device. - qcom,ddr-config: Certain chipsets and platforms require particular settings for the RCLK delay DLL configuration register for HS400 mode to work. This value can vary between platforms and msms. If a msm/platform require a different DLL setting than the default/POR setting for HS400 mode, it can be specified using this field. - qcom,dll-hsr-list: List of DLL-HSR values which are tuned for given process-node and platform. The sequence of values in this list must follow the sequence listed in sdhci_msm_dll_hsr data structure. In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. - qcom,<supply>-lpm_sup - specifies whether supply can be kept in low power mode (lpm). Loading
drivers/mmc/host/sdhci-msm.c +191 −57 Original line number Diff line number Diff line Loading @@ -2,7 +2,7 @@ * drivers/mmc/host/sdhci-msm.c - Qualcomm Technologies, Inc. MSM SDHCI Platform * driver source file * * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -155,12 +155,12 @@ #define CORE_FLL_CYCLE_CNT (1 << 18) #define CORE_DLL_CLOCK_DISABLE (1 << 21) #define DDR_CONFIG_POR_VAL 0x80040853 #define DDR_CONFIG_PRG_RCLK_DLY_MASK 0x1FF #define DDR_CONFIG_PRG_RCLK_DLY 115 #define DDR_CONFIG_2_POR_VAL 0x80040873 #define DDR_CONFIG_POR_VAL 0x80040873 #define DLL_USR_CTL_POR_VAL 0x10800 #define ENABLE_DLL_LOCK_STATUS (1 << 26) #define FINE_TUNE_MODE_EN (1 << 27) #define BIAS_OK_SIGNAL (1 << 29) #define DLL_CONFIG_3_POR_VAL 0x10 /* 512 descriptors */ #define SDHCI_MSM_MAX_SEGMENTS (1 << 9) Loading Loading @@ -203,8 +203,9 @@ struct sdhci_msm_offset { u32 CORE_DDR_200_CFG; u32 CORE_VENDOR_SPEC3; u32 CORE_DLL_CONFIG_2; u32 CORE_DLL_CONFIG_3; u32 CORE_DDR_CONFIG; u32 CORE_DDR_CONFIG_2; u32 CORE_DDR_CONFIG_OLD; /* Applcable to sddcc minor ver < 0x49 only */ u32 CORE_DLL_USR_CTL; /* Present on SDCC5.1 onwards */ }; Loading Loading @@ -233,8 +234,8 @@ struct sdhci_msm_offset sdhci_msm_offset_mci_removed = { .CORE_DDR_200_CFG = 0x224, .CORE_VENDOR_SPEC3 = 0x250, .CORE_DLL_CONFIG_2 = 0x254, .CORE_DDR_CONFIG = 0x258, .CORE_DDR_CONFIG_2 = 0x25C, .CORE_DLL_CONFIG_3 = 0x258, .CORE_DDR_CONFIG = 0x25C, .CORE_DLL_USR_CTL = 0x388, }; Loading Loading @@ -263,8 +264,9 @@ struct sdhci_msm_offset sdhci_msm_offset_mci_present = { .CORE_DDR_200_CFG = 0x184, .CORE_VENDOR_SPEC3 = 0x1B0, .CORE_DLL_CONFIG_2 = 0x1B4, .CORE_DDR_CONFIG = 0x1B8, .CORE_DDR_CONFIG_2 = 0x1BC, .CORE_DLL_CONFIG_3 = 0x1B8, .CORE_DDR_CONFIG_OLD = 0x1B8, /* Applicable to sdcc minor ver < 0x49 */ .CORE_DDR_CONFIG = 0x1BC, }; u8 sdhci_msm_readb_relaxed(struct sdhci_host *host, u32 offset) Loading Loading @@ -366,6 +368,14 @@ enum vdd_io_level { VDD_IO_SET_LEVEL, }; enum dll_init_context { DLL_INIT_NORMAL = 0, DLL_INIT_FROM_CX_COLLAPSE_EXIT, }; static unsigned int sdhci_msm_get_sup_clk_rate(struct sdhci_host *host, u32 req_clk); /* MSM platform specific tuning */ static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll) Loading Loading @@ -530,11 +540,13 @@ 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. */ if (msm_host->dll_hsr->dll_config & (0xF << 20)) { writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) & ~(0xF << 20)) | (grey_coded_phase_table[phase] << 20)), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); } /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */ writel_relaxed((readl_relaxed(host->ioaddr + Loading Loading @@ -710,7 +722,8 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host) } /* Initialize the DLL (Programmable Delay Line ) */ static int msm_init_cm_dll(struct sdhci_host *host) static int msm_init_cm_dll(struct sdhci_host *host, enum dll_init_context init_context) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = pltfm_host->priv; Loading Loading @@ -763,22 +776,44 @@ static int msm_init_cm_dll(struct sdhci_host *host) writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) | CORE_DLL_PDN), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_cm_dll_set_freq(host); if (msm_host->use_updated_dll_reset) { u32 mclk_freq = 0; u32 actual_clk = sdhci_msm_get_sup_clk_rate(host, host->clock); /* * Only configure the mclk_freq in normal DLL init * context. If the DLL init is coming from * CX Collapse Exit context, the host->clock may be zero. * The DLL_CONFIG_2 register has already been restored to * proper value prior to getting here. */ if (init_context == DLL_INIT_NORMAL) { switch (actual_clk) { case 202000000: case 201500000: case 200000000: mclk_freq = 42; break; case 192000000: mclk_freq = 40; break; default: mclk_freq = (u32)((actual_clk / TCXO_FREQ) * 4); pr_info_once("%s: %s: Non standard clk freq =%u\n", mmc_hostname(mmc), __func__, actual_clk); } if ((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2) & CORE_FLL_CYCLE_CNT)) mclk_freq = (u32) ((host->clock / TCXO_FREQ) * 8); else mclk_freq = (u32) ((host->clock / TCXO_FREQ) * 4); mclk_freq *= 2; writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2) & ~(0xFF << 10)) | (mclk_freq << 10)), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); } /* wait for 5us before enabling DLL clock */ udelay(5); } Loading @@ -794,7 +829,6 @@ static int msm_init_cm_dll(struct sdhci_host *host) host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); if (msm_host->use_updated_dll_reset) { msm_cm_dll_set_freq(host); /* Enable the DLL clock */ writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2) Loading @@ -804,11 +838,39 @@ static int msm_init_cm_dll(struct sdhci_host *host) /* * Configure DLL user control register to enable DLL status * This setting is applicable to SDCC v5.1 onwards only * This setting is applicable to SDCC v5.1 onwards only. * * Configure Tassadar DLL (Only applicable for 7FF projects) * */ if (msm_host->need_dll_user_ctl) { writel_relaxed(DLL_USR_CTL_POR_VAL | ENABLE_DLL_LOCK_STATUS, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); if (msm_host->dll_hsr) { writel_relaxed(msm_host->dll_hsr->dll_usr_ctl, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); writel_relaxed(msm_host->dll_hsr->dll_config_3, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); } else { writel_relaxed(DLL_USR_CTL_POR_VAL | FINE_TUNE_MODE_EN | ENABLE_DLL_LOCK_STATUS | BIAS_OK_SIGNAL, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); writel_relaxed(DLL_CONFIG_3_POR_VAL, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); } } /* * Update the lower byte of DLL_CONFIG only with HSR values. * Since these are the static settings. */ if (msm_host->dll_hsr) { writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) & (~0xff)) | (msm_host->dll_hsr->dll_config & 0xff)), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); } /* Set DLL_EN bit to 1. */ Loading Loading @@ -976,7 +1038,7 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) struct sdhci_msm_host *msm_host = pltfm_host->priv; const struct sdhci_msm_offset *msm_host_offset = msm_host->offset; u32 dll_status, ddr_config; u32 dll_status; int ret = 0; pr_debug("%s: Enter %s\n", mmc_hostname(host->mmc), __func__); Loading @@ -987,16 +1049,16 @@ static int sdhci_msm_cm_dll_sdc4_calibration(struct sdhci_host *host) */ if (msm_host->pdata->rclk_wa) { writel_relaxed(msm_host->pdata->ddr_config, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG_2); msm_host_offset->CORE_DDR_CONFIG); } else if (msm_host->dll_hsr && msm_host->dll_hsr->ddr_config) { writel_relaxed(msm_host->dll_hsr->ddr_config, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); } else if (msm_host->rclk_delay_fix) { writel_relaxed(DDR_CONFIG_2_POR_VAL, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG_2); } else { ddr_config = DDR_CONFIG_POR_VAL & ~DDR_CONFIG_PRG_RCLK_DLY_MASK; ddr_config |= DDR_CONFIG_PRG_RCLK_DLY; writel_relaxed(ddr_config, host->ioaddr + writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); } else { writel_relaxed(DDR_CONFIG_POR_VAL, host->ioaddr + msm_host_offset->CORE_DDR_CONFIG_OLD); } if (msm_host->enhanced_strobe && mmc_card_strobe(msm_host->mmc->card)) Loading Loading @@ -1065,7 +1127,7 @@ static int sdhci_msm_enhanced_strobe(struct sdhci_host *host) /* * Reset the tuning block. */ ret = msm_init_cm_dll(host); ret = msm_init_cm_dll(host, DLL_INIT_NORMAL); if (ret) goto out; Loading @@ -1092,7 +1154,7 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) * Retuning in HS400 (DDR mode) will fail, just reset the * tuning block and restore the saved tuning phase. */ ret = msm_init_cm_dll(host); ret = msm_init_cm_dll(host, DLL_INIT_NORMAL); if (ret) goto out; Loading @@ -1100,12 +1162,13 @@ static int sdhci_msm_hs400_dll_calibration(struct sdhci_host *host) ret = msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); if (ret) goto out; if (msm_host->dll_hsr->dll_config & CORE_CMD_DAT_TRACK_SEL) { /* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ writel_relaxed((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG) | CORE_CMD_DAT_TRACK_SEL), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); } if (msm_host->use_cdclp533) /* Calibrate CDCLP533 DLL HW */ Loading Loading @@ -1215,7 +1278,7 @@ int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) tuned_phase_cnt = 0; /* first of all reset the tuning block */ rc = msm_init_cm_dll(host); rc = msm_init_cm_dll(host, DLL_INIT_NORMAL); if (rc) goto kfree; Loading Loading @@ -1965,6 +2028,32 @@ static int sdhci_msm_get_socrev(struct device *dev, } #endif static int sdhci_msm_dt_parse_hsr_info(struct device *dev, struct sdhci_msm_host *msm_host) { u32 *dll_hsr_table = NULL; int dll_hsr_table_len, dll_hsr_reg_count; int ret = 0; if (sdhci_msm_dt_get_array(dev, "qcom,dll-hsr-list", &dll_hsr_table, &dll_hsr_table_len, 0)) goto skip_hsr; dll_hsr_reg_count = sizeof(struct sdhci_msm_dll_hsr) / sizeof(u32); if (dll_hsr_table_len != dll_hsr_reg_count) { dev_err(dev, "Number of HSR entries are not matching\n"); ret = -EINVAL; } else { msm_host->dll_hsr = (struct sdhci_msm_dll_hsr *)dll_hsr_table; } skip_hsr: if (!msm_host->dll_hsr) dev_info(dev, "Failed to get dll hsr settings from dt\n"); return ret; } /* Parse platform data */ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, Loading Loading @@ -2142,6 +2231,9 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, if (!of_property_read_u32(np, "qcom,ddr-config", &pdata->ddr_config)) pdata->rclk_wa = true; if (sdhci_msm_dt_parse_hsr_info(dev, msm_host)) goto out; /* * rclk_wa is not required if soc version is mentioned and * is not base version. Loading Loading @@ -3140,6 +3232,19 @@ static void sdhci_msm_registers_save(struct sdhci_host *host) sdhci_readl(host, SDHCI_CAPABILITIES_1); msm_host->regs_restore.testbus_config = readl_relaxed(host->ioaddr + msm_host_offset->CORE_TESTBUS_CONFIG); msm_host->regs_restore.dll_config = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_host->regs_restore.dll_config2 = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); msm_host->regs_restore.dll_config = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_host->regs_restore.dll_config2 = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); msm_host->regs_restore.dll_config3 = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); msm_host->regs_restore.dll_usr_ctl = readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); msm_host->regs_restore.is_valid = true; pr_debug("%s: %s: registers saved. PWRCTL_MASK = 0x%x\n", Loading @@ -3155,6 +3260,7 @@ static void sdhci_msm_registers_restore(struct sdhci_host *host) u8 irq_status; const struct sdhci_msm_offset *msm_host_offset = msm_host->offset; struct mmc_ios ios = host->mmc->ios; if (!msm_host->regs_restore.is_supported || !msm_host->regs_restore.is_valid) Loading Loading @@ -3206,6 +3312,24 @@ static void sdhci_msm_registers_restore(struct sdhci_host *host) writel_relaxed(msm_host->regs_restore.vendor_pwrctl_mask, host->ioaddr + msm_host_offset->CORE_PWRCTL_MASK); if (((ios.timing == MMC_TIMING_MMC_HS400) || (ios.timing == MMC_TIMING_MMC_HS200) || (ios.timing == MMC_TIMING_UHS_SDR104)) && (ios.clock > CORE_FREQ_100MHZ)) { writel_relaxed(msm_host->regs_restore.dll_config2, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_2); writel_relaxed(msm_host->regs_restore.dll_config3, host->ioaddr + msm_host_offset->CORE_DLL_CONFIG_3); writel_relaxed(msm_host->regs_restore.dll_usr_ctl, host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL); writel_relaxed(msm_host->regs_restore.dll_config & ~(CORE_DLL_RST | CORE_DLL_PDN), host->ioaddr + msm_host_offset->CORE_DLL_CONFIG); msm_init_cm_dll(host, DLL_INIT_FROM_CX_COLLAPSE_EXIT); msm_config_cm_dll_phase(host, msm_host->saved_tuning_phase); } pr_debug("%s: %s: registers restored. PWRCTL_MASK = 0x%x\n", mmc_hostname(host->mmc), __func__, readl_relaxed(host->ioaddr + Loading Loading @@ -3512,9 +3636,9 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) * DLL input clock */ writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DDR_CONFIG)) msm_host_offset->CORE_DLL_CONFIG_3)) | RCLK_TOGGLE), host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); msm_host_offset->CORE_DLL_CONFIG_3); /* ensure above write as toggling same bit quickly */ wmb(); udelay(2); Loading @@ -3523,9 +3647,9 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) * DLL input clock */ writel_relaxed(((readl_relaxed(host->ioaddr + msm_host_offset->CORE_DDR_CONFIG)) msm_host_offset->CORE_DLL_CONFIG_3)) & ~RCLK_TOGGLE), host->ioaddr + msm_host_offset->CORE_DDR_CONFIG); msm_host_offset->CORE_DLL_CONFIG_3); } if (!host->mmc->ios.old_rate && !msm_host->use_cdclp533) { /* Loading Loading @@ -3768,24 +3892,34 @@ void sdhci_msm_dump_vendor_regs(struct sdhci_host *host) msm_host_offset->CORE_MCI_FIFO_CNT), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_MCI_STATUS)); pr_info("DLL cfg: 0x%08x | DLL sts: 0x%08x | SDCC ver: 0x%08x\n", pr_info("DLL sts: 0x%08x | DLL cfg: 0x%08x | DLL cfg2: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_STATUS), readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_CONFIG), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_DLL_CONFIG_2)); pr_info("DLL cfg3: 0x%08x | DLL usr ctl: 0x%08x | DDR cfg: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_STATUS), msm_host_offset->CORE_DLL_CONFIG_3), readl_relaxed(host->ioaddr + msm_host_offset->CORE_DLL_USR_CTL), sdhci_msm_readl_relaxed(host, msm_host_offset->CORE_MCI_VERSION)); pr_info("Vndr func: 0x%08x | Vndr adma err : addr0: 0x%08x addr1: 0x%08x\n", msm_host_offset->CORE_DDR_CONFIG)); pr_info("SDCC ver: 0x%08x | Vndr adma err : addr0: 0x%08x addr1: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC), msm_host_offset->CORE_MCI_VERSION), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_ADMA_ERR_ADDR0), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_ADMA_ERR_ADDR1)); pr_info("Vndr func2: 0x%08x\n", pr_info("Vndr func: 0x%08x | Vndr func2 : 0x%08x Vndr func3: 0x%08x\n", readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_FUNC2)); msm_host_offset->CORE_VENDOR_SPEC), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC_FUNC2), readl_relaxed(host->ioaddr + msm_host_offset->CORE_VENDOR_SPEC3)); /* * tbsel indicates [2:0] bits and tbsel2 indicates [7:4] bits * of CORE_TESTBUS_CONFIG register. Loading Loading @@ -4577,7 +4711,7 @@ static void sdhci_set_default_hw_caps(struct sdhci_msm_host *msm_host, * starts coming. */ if ((major == 1) && ((minor == 0x42) || (minor == 0x46) || (minor == 0x49) || (minor >= 0x6b))) (minor == 0x49) || (minor == 0x4D) || (minor >= 0x6b))) msm_host->use_14lpp_dll = true; /* Fake 3.0V support for SDIO devices which requires such voltage */ Loading Loading @@ -4951,7 +5085,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) sdhci_set_default_hw_caps(msm_host, host); /* * Set the PAD_PWR_SWTICH_EN bit so that the PAD_PWR_SWITCH bit can * 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 + Loading
drivers/mmc/host/sdhci-msm.h +19 −1 Original line number Diff line number Diff line /* * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -195,6 +195,23 @@ struct sdhci_msm_regs_restore { u32 hc_3c_3e; u32 hc_caps_1; u32 testbus_config; u32 dll_config; u32 dll_config2; u32 dll_config3; u32 dll_usr_ctl; }; /* * DLL registers which needs be programmed with HSR settings. * Add any new register only at the end and don't change the * seqeunce. */ struct sdhci_msm_dll_hsr { u32 dll_config; u32 dll_config_2; u32 dll_config_3; u32 dll_usr_ctl; u32 ddr_config; }; struct sdhci_msm_debug_data { Loading Loading @@ -258,6 +275,7 @@ struct sdhci_msm_host { int soc_min_rev; struct workqueue_struct *pm_qos_wq; bool need_dll_user_ctl; struct sdhci_msm_dll_hsr *dll_hsr; }; extern char *saved_command_line; Loading