mmc: sdhci-msm: Add clk-rates, DDR, HS400 support
Subsystems (5)
v5 cover mmc: sdhci-msm: Add clk-rates, DDR, HS400 support
Hi,
This is v5 version of the patch series with addition of HS400 patches in this. These are tested on internal msm8996 & db410c HW.
Though there is a discussion going on w.r.t. clk-rates DT entry[6] but I would like to get below patches reviewed, since these patches should not be functionally impacted.
Changes from v4 -> v5 :-
1. Added HS400 sdhci-msm controller specific changes:- (Patch 10, 11, 12)
2. Addressed comment from Adrian on Patch 07 @[3].
3. Addressed comment from Arnd on Patch 03, to directly add clk_table into sdhci_msm_host. [4]
4. Addressed comment from Bjorn to not enforce having clk-rates property in DT for older targets based on discussion at [5]
5. Retained Acks from Adrian on patches (01 & 02 & 06) where there were no changes made while addressing above comments.
Older history:-
This is v4 version of the patch series.
Patches 01, 02, 05 & 06 were Acked-by Adrian.
Changes from v3 -> v4 :-
1. Addressed comments from Adrian on Patch 03, 07, 08.
2. Addressed comments from Bjorn on Patch 03.
3. Added clk-rate support for sdhc DT nodes to all MSM platforms. in Pacth 04.
4. Rebased on next branch of Ulf.
Changes from v2 -> v3 :-
1. Addded Patch 01 based on Bjorn comment[2] -
This fixes/unrolls the poor coding style of read/writes of registers from base sdhci-msm driver.
2. Fixed/unrolled poor style of reads/writes of registers in Patch 02, based on Bjorn comment[2]. Also changed name of flag from use_updated_dll_reset -> use_14lpp_dll_reset.
Changes from v1->v2 :-
1. Removed patch 06 & 08 from v1 patch series[1] (which were introducing unnecessary quirks). Instead have implemented __sdhci_msm_set_clock version of sdhci_set_clock in sdhci_msm driver itself in patch 07 of this patch series.
2. Enabled extra quirk (SDHCI_QUIRK2_PRESET_VALUE_BROKEN) in patch 05 of this patch series.
Description of patches :-
This patchset adds clk-rates & other required changes to upstream sdhci-msm driver from codeaurora tree.
It has been tested on a db410c Dragonboard and msm8996 based platform.
Patch 0002 - Adds updated dll sequence for newer controllers which has minor_version >= 0x42. This is required for msm8996.
MSM controller HW recommendation is to use the base MCI clock and directly control this MCI clock at GCC in order to change the clk-rate.
Patches 03-07 bring in required change for this to sdhci-msm and DT.
MSM controller would require 2x clock rate from source for DDR bus speed modes. Patch 08 adds this support.
Patch 09 - adds DDR support in DT for sdhc1 of msm8916.
[1]:- http://www.spinics.net/lists/linux-mmc/msg38467.html
[2]:- http://www.spinics.net/lists/linux-mmc/msg38578.html
[3]:- https://patchwork.kernel.org/patch/9289345/
[4]:- https://www.spinics.net/lists/linux-mmc/msg39107.html
[5]:- http://www.spinics.net/lists/linux-mmc/msg38749.html
[6]:- https://patchwork.kernel.org/patch/9297381/
Ritesh Harjani (9):
mmc: sdhci-msm: Change poor style writel/readl of registers
mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
ARM: dts: qcom: Add clk-rates to sdhc1 & sdhc2
mmc: sdhci-msm: Add get_min_clock() and get_max_clock() callback
mmc: sdhci-msm: Enable few quirks
mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
mmc: sdhci-msm: Add clock changes for DDR mode.
arm64: dts: qcom: msm8916: Add ddr support to sdhc1
sdhci: sdhci-msm: update dll configuration
Venkat Gopalakrishnan (3):
mmc: sdhci-msm: Update DLL reset sequence
mmc: sdhci-msm: Add HS400 platform support
mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
.../devicetree/bindings/mmc/sdhci-msm.txt | 1 + arch/arm/boot/dts/qcom-apq8084.dtsi | 4 + arch/arm/boot/dts/qcom-msm8974.dtsi | 4 + arch/arm64/boot/dts/qcom/msm8916.dtsi | 5 + arch/arm64/boot/dts/qcom/msm8996.dtsi | 2 + drivers/mmc/host/sdhci-msm.c | 721 ++++++++++++++++++++- 6 files changed, 703 insertions(+), 34 deletions(-)
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
A message that is not in the archive (sent off-list or not yet received): 1475678440-3525-1-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org
PATCH v5 1/12 mmc: sdhci-msm: Change poor style writel/readl of registers
This patch changes the poor style of writel/readl registers into more readable format. Also to avoid mixed style format of readl/writel in sdhci-msm driver.
--- drivers/mmc/host/sdhci-msm.c | 54 ++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 22 deletions(-)
drivers/mmc/host/sdhci-msm.c +32 -22
@@ -137,8 +137,9 @@ static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - |CORE_CK_OUT_EN ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CK_OUT_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */ rc =msm_dll_poll_ck_out_en (host,1 );
@@ -305,6 +306,7 @@ static int msm_init_cm_dll(struct sdhci_host *host)struct mmc_host *mmc = host->mmc;int wait_cnt =50 ;unsigned long flags; +u32 config =0 ;spin_lock_irqsave (&host->lock, flags);
@@ -313,33 +315,40 @@ static int msm_init_cm_dll(struct sdhci_host *host)* tuning is in progress. Keeping PWRSAVE ON may * turn off the clock. */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ) - & ~CORE_CLK_PWRSAVE ), host->ioaddr +CORE_VENDOR_SPEC ); + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_CLK_PWRSAVE ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC );/* Write 1 to DLL_RST bit of DLL_CONFIG register */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - |CORE_DLL_RST ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Write 1 to DLL_PDN bit of DLL_CONFIG register */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - |CORE_DLL_PDN ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );msm_cm_dll_set_freq (host);/* Write 0 to DLL_RST bit of DLL_CONFIG register */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - & ~CORE_DLL_RST ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config &= ~CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Write 0 to DLL_PDN bit of DLL_CONFIG register */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - & ~CORE_DLL_PDN ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config &= ~CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Set DLL_EN bit to 1. */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - |CORE_DLL_EN ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Set CK_OUT_EN bit to 1. */ -writel_relaxed ((readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ) - |CORE_CK_OUT_EN ), host->ioaddr +CORE_DLL_CONFIG ); + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CK_OUT_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
@@ -536,7 +545,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)struct resource *core_memres;int ret;u16 host_version, core_minor; -u32 core_version, caps; +u32 core_version, config;u8 core_major; host =sdhci_pltfm_init (pdev, &sdhci_msm_pdata,sizeof (*msm_host));
@@ -605,8 +614,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) }/* Reset the core and Enable SDHC mode */ -writel_relaxed (readl_relaxed (msm_host->core_mem +CORE_POWER ) | -CORE_SW_RST , msm_host->core_mem +CORE_POWER ); + config =readl_relaxed (msm_host->core_mem +CORE_POWER ); + config |=CORE_SW_RST ; +writel_relaxed (config, msm_host->core_mem +CORE_POWER );/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */ usleep_range (1000 ,5000 );
@@ -636,9 +646,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)* controller versions and must be explicitly enabled. */ if (core_major >=1 && core_minor !=0x11 && core_minor !=0x12 ) { - caps =readl_relaxed (host->ioaddr +SDHCI_CAPABILITIES ); - caps |=SDHCI_CAN_VDD_300 |SDHCI_CAN_DO_8BIT ; -writel_relaxed (caps, host->ioaddr + + 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 ); }
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v5 2/12 mmc: sdhci-msm: Update DLL reset sequence
In reply to a message not in the archiveFrom: Venkat Gopalakrishnan <[email protected]>
SDCC core with minor version >= 0x42 introduced new 14lpp DLL. This has additional requirements in the reset sequence for DLL tuning. Make necessary changes as needed.
Without this patch we see below errors on such SDHC controllers
sdhci_msm 7464900.sdhci: mmc0: DLL failed to LOCK
mmc0: tuning execution failed: -110
--- drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+)
drivers/mmc/host/sdhci-msm.c +48 -0
@@ -58,11 +58,17 @@#define CORE_DLL_CONFIG 0x100#define CORE_DLL_STATUS 0x108 +#define CORE_DLL_CONFIG_2 0x1b4 +#define CORE_FLL_CYCLE_CNT BIT(18) +#define CORE_DLL_CLOCK_DISABLE BIT(21) +#define CORE_VENDOR_SPEC 0x10c#define CORE_CLK_PWRSAVE BIT(1)#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c +#define TCXO_FREQ 19200000 +#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)#define CMUX_SHIFT_PHASE_SHIFT 24
@@ -76,6 +82,7 @@ struct sdhci_msm_host {struct clk *pclk;/* SDHC peripheral bus clock */ struct clk *bus_clk;/* SDHC bus voter clock */ struct mmc_host *mmc; +bool use_14lpp_dll_reset; };/* Platform specific tuning */
@@ -304,6 +311,8 @@ static inline void msm_cm_dll_set_freq(struct sdhci_host *host)static int msm_init_cm_dll (struct sdhci_host *host) {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);int wait_cnt =50 ;unsigned long flags;u32 config =0 ;
@@ -319,6 +328,16 @@ static int msm_init_cm_dll(struct sdhci_host *host) config &= ~CORE_CLK_PWRSAVE ;writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); +if (msm_host->use_14lpp_dll_reset) { + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config &= ~CORE_CK_OUT_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG_2 ); + config |=CORE_DLL_CLOCK_DISABLE ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG_2 ); + } +/* Write 1 to DLL_RST bit of DLL_CONFIG register */ config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); config |=CORE_DLL_RST ;
@@ -330,6 +349,24 @@ static int msm_init_cm_dll(struct sdhci_host *host)writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG );msm_cm_dll_set_freq (host); +if (msm_host->use_14lpp_dll_reset) { +u32 mclk_freq =0 ; + +if ((readl_relaxed (host->ioaddr +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 ); + + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG_2 ); + config &= ~(0xFF <<10 ); + config |= mclk_freq <<10 ; + +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG_2 ); +/* wait for 5us before enabling DLL clock */ +udelay (5 ); + } +/* Write 0 to DLL_RST bit of DLL_CONFIG register */ config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); config &= ~CORE_DLL_RST ;
@@ -340,6 +377,14 @@ static int msm_init_cm_dll(struct sdhci_host *host) config &= ~CORE_DLL_PDN ;writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); +if (msm_host->use_14lpp_dll_reset) { +msm_cm_dll_set_freq (host); +/* Enable the DLL clock */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG_2 ); + config &= ~CORE_DLL_CLOCK_DISABLE ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG_2 ); + } +/* Set DLL_EN bit to 1. */ config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); config |=CORE_DLL_EN ;
@@ -641,6 +686,9 @@ static int sdhci_msm_probe(struct platform_device *pdev)dev_dbg (&pdev->dev,"MCI Version: 0x%08x, major: 0x%04x, minor: 0x%02x\n" , core_version, core_major, core_minor); +if ((core_major ==1 ) && (core_minor >=0x42 )) + msm_host->use_14lpp_dll_reset = true; +/* * Support for some capabilities is not advertised by newer * controller versions and must be explicitly enabled.
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v5 10/12 mmc: sdhci-msm: Add HS400 platform support
In reply to a message not in the archiveFrom: Venkat Gopalakrishnan <[email protected]>
The following msm platform specific changes are added to support HS400.
- Allow tuning for HS400 mode.
- Configure HS400 timing mode using the VENDOR_SPECIFIC_FUNC register.
--- drivers/mmc/host/sdhci-msm.c | 124 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 11 deletions(-)
drivers/mmc/host/sdhci-msm.c +113 -11
@@ -31,6 +31,7 @@#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
@@ -64,11 +65,18 @@#define CORE_VENDOR_SPEC 0x10c#define CORE_CLK_PWRSAVE BIT(1) +#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_HC_SELECT_IN_EN (1 << 18) +#define CORE_HC_SELECT_IN_HS400 (6 << 19) +#define CORE_HC_SELECT_IN_MASK (7 << 19)#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000 +#define CORE_FREQ_100MHZ (100 * 1000 * 1000)#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
@@ -87,6 +95,8 @@ struct sdhci_msm_host {u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; +bool tuning_done; +bool calibration_done; };/* Platform specific tuning */
@@ -175,8 +185,8 @@ out:* Find out the greatest range of consecuitive selected * DLL clock output phases that can be used as sampling * setting for SD3.0 UHS-I card read operation (in SDR104 -* timing mode) or for eMMC4.5 card read operation (in HS200 -* timing mode). +* timing mode) or for eMMC4.5 card read operation (in +* HS400/HS200 timing mode). * Select the 3/4 of the range and configure the DLL with the * selected DLL clock output phase. */
@@ -428,9 +438,10 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)* Tuning is required for SDR104, HS200 and HS400 cards and * if clock frequency is greater than 100MHz in these modes. */ -if (host->clock <=100 *1000 *1000 || - !((ios.timing ==MMC_TIMING_MMC_HS200 ) || - (ios.timing ==MMC_TIMING_UHS_SDR104 ))) +if (host->clock <=CORE_FREQ_100MHZ || + !((ios.timing ==MMC_TIMING_MMC_HS400 ) || + (ios.timing ==MMC_TIMING_MMC_HS200 ) || + (ios.timing ==MMC_TIMING_UHS_SDR104 )))return 0 ; retry:
@@ -488,7 +499,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) {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);u16 ctrl_2; +u32 config; ctrl_2 =sdhci_readw (host,SDHCI_HOST_CONTROL2 );/* Select Bus Speed Mode for host */
@@ -503,6 +517,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,case MMC_TIMING_UHS_SDR50 : ctrl_2 |=SDHCI_CTRL_UHS_SDR50 ;break ; +case MMC_TIMING_MMC_HS400 :case MMC_TIMING_MMC_HS200 :case MMC_TIMING_UHS_SDR104 : ctrl_2 |=SDHCI_CTRL_UHS_SDR104 ;
@@ -519,11 +534,33 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,* provide feedback clock, the mode selection can be any value less * than 3'b011 in bits [2:0] of HOST CONTROL2 register. */ -if (host->clock <=100000000 && - (uhs ==MMC_TIMING_MMC_HS400 || - uhs ==MMC_TIMING_MMC_HS200 || - uhs ==MMC_TIMING_UHS_SDR104 )) - ctrl_2 &= ~SDHCI_CTRL_UHS_MASK ; +if (host->clock <=CORE_FREQ_100MHZ ) { +if ((uhs ==MMC_TIMING_MMC_HS400 ) || + (uhs ==MMC_TIMING_MMC_HS200 ) || + (uhs ==MMC_TIMING_UHS_SDR104 )) + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK ; +/* +* Make sure DLL is disabled when not required +* +* Write 1 to DLL_RST bit of DLL_CONFIG register +*/ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 1 to DLL_PDN bit of DLL_CONFIG register */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +wmb ();/* drain writebuffer */ + +/* +* The DLL needs to be restored and CDCLP533 recalibrated +* when the clock frequency is set back to 400MHz. +*/ + msm_host->calibration_done = false; + }dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);
@@ -670,7 +707,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios; -u32 msm_clock; +u32 msm_clock, config;int rc;if (!clock)
@@ -691,6 +728,66 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) } msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); +/* +* In general all timing modes are controlled via UHS mode select in +* Host Control2 register. eMMC specific HS200/HS400 doesn't have +* their respective modes defined here, hence we use these values. +* +* HS200 - SDR104 (Since they both are equivalent in functionality) +* HS400 - This involves multiple configurations +* Initially SDR104 - when tuning is required as HS200 +* Then when switching to DDR @ 400MHz (HS400) we use +* the vendor specific HC_SELECT_IN to control the mode. +* +* In addition to controlling the modes we also need to select the +* correct input clock for DLL depending on the mode. +* +* HS400 - divided clock (free running MCLK/2) +* All other modes - default (free running MCLK) +*/ +if (curr_ios.timing ==MMC_TIMING_MMC_HS400 ) { +/* Select the divided clock (free running MCLK/2) */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_HS400 ; + +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); +/* +* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC +* register +*/ +if (msm_host->tuning_done && !msm_host->calibration_done) { +/* +* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN +* field in VENDOR_SPEC_FUNC +*/ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config |=CORE_HC_SELECT_IN_HS400 ; + config |=CORE_HC_SELECT_IN_EN ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + } + }else { +/* Select the default clock (free running MCLK) */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_DFLT ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + +/* +* Disable HC_SELECT_IN to be able to use the UHS mode select +* configuration from Host Control2 register for all other +* modes. +* +* 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 &= ~CORE_HC_SELECT_IN_EN ; + config &= ~CORE_HC_SELECT_IN_MASK ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + } +wmb ();/* drain writebuffer */ +if ((msm_clock != msm_host->clk_rate) && msm_host->clk_table) { rc =clk_set_rate (msm_host->clk, msm_clock);if (rc) {
@@ -876,6 +973,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)/* Set HC_MODE_EN bit in HC_MODE register */ writel_relaxed (HC_MODE_EN , (msm_host->core_mem +CORE_HC_MODE )); +/* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */ + 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_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) >>
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Adrian Hunter
maintainerRe: [PATCH v5 10/12] mmc: sdhci-msm: Add HS400 platform support
Quoted 64 lines · Ritesh Harjani
From: Venkat Gopalakrishnan <[email protected]>
The following msm platform specific changes are added to support HS400.
- Allow tuning for HS400 mode.
- Configure HS400 timing mode using theVENDOR_SPECIFIC_FUNCregister.Signed-off-by Venkat Gopalakrishnan <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 124 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 11 deletions(-)drivers/mmc/host/sdhci-msm.c +12
-2@@ -31,6 +31,7 @@#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@@ -64,11 +65,18 @@#define CORE_VENDOR_SPEC 0x10c#define CORE_CLK_PWRSAVE BIT(1) +#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_HC_SELECT_IN_EN (1 << 18) +#define CORE_HC_SELECT_IN_HS400 (6 << 19) +#define CORE_HC_SELECT_IN_MASK (7 << 19)#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000 +#define CORE_FREQ_100MHZ (100 * 1000 * 1000)#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)@@ -87,6 +95,8 @@ struct sdhci_msm_host {u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; +bool tuning_done; +bool calibration_done; };/* Platform specific tuning */ @@ -175,8 +185,8 @@ out:* Find out the greatest range of consecuitive selected * DLL clock output phases that can be used as sampling * setting for SD3.0 UHS-I card read operation (in SDR104 -* timing mode) or for eMMC4.5 card read operation (in HS200 -* timing mode). +* timing mode) or for eMMC4.5 card read operation (in +* HS400/HS200 timing mode). * Select the 3/4 of the range and configure the DLL with the * selected DLL clock output phase. */
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) · line 444
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +4
-3@@ -428,9 +438,10 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)* Tuning is required for SDR104, HS200 and HS400 cards and * if clock frequency is greater than 100MHz in these modes. */ -if (host->clock <=100 *1000 *1000 || - !((ios.timing ==MMC_TIMING_MMC_HS200 ) || - (ios.timing ==MMC_TIMING_UHS_SDR104 ))) +if (host->clock <=CORE_FREQ_100MHZ || + !((ios.timing ==MMC_TIMING_MMC_HS400 ) || + (ios.timing ==MMC_TIMING_MMC_HS200 ) || + (ios.timing ==MMC_TIMING_UHS_SDR104 )))
Don't need () around ios.timing == MMC_TIMING_MMC_HS400 etc
Quoted 41 lines
drivers/mmc/host/sdhci-msm.c +14
-5return 0 ; retry:@@ -488,7 +499,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) {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);u16 ctrl_2; +u32 config; ctrl_2 =sdhci_readw (host,SDHCI_HOST_CONTROL2 );/* Select Bus Speed Mode for host */ @@ -503,6 +517,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,case MMC_TIMING_UHS_SDR50 : ctrl_2 |=SDHCI_CTRL_UHS_SDR50 ;break ; +case MMC_TIMING_MMC_HS400 :case MMC_TIMING_MMC_HS200 :case MMC_TIMING_UHS_SDR104 : ctrl_2 |=SDHCI_CTRL_UHS_SDR104 ;@@ -519,11 +534,33 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,* provide feedback clock, the mode selection can be any value less * than 3'b011 in bits [2:0] of HOST CONTROL2 register. */ -if (host->clock <=100000000 && - (uhs ==MMC_TIMING_MMC_HS400 || - uhs ==MMC_TIMING_MMC_HS200 || - uhs ==MMC_TIMING_UHS_SDR104 )) - ctrl_2 &= ~SDHCI_CTRL_UHS_MASK ; +if (host->clock <=CORE_FREQ_100MHZ ) { +if ((uhs ==MMC_TIMING_MMC_HS400 ) || + (uhs ==MMC_TIMING_MMC_HS200 ) || + (uhs ==MMC_TIMING_UHS_SDR104 )) + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK ; +/* +* Make sure DLL is disabled when not required +* +* Write 1 to DLL_RST bit of DLL_CONFIG register +*/
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, · line 556
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 1 to DLL_PDN bit of DLL_CONFIG register */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +wmb ();/* drain writebuffer */
Memory barrier could use more explanation.
Quoted 42 lines
drivers/mmc/host/sdhci-msm.c +27
-1+ +/* +* The DLL needs to be restored and CDCLP533 recalibrated +* when the clock frequency is set back to 400MHz. +*/ + msm_host->calibration_done = false; + }dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);@@ -670,7 +707,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios; -u32 msm_clock; +u32 msm_clock, config;int rc;if (!clock)@@ -691,6 +728,66 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) } msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); +/* +* In general all timing modes are controlled via UHS mode select in +* Host Control2 register. eMMC specific HS200/HS400 doesn't have +* their respective modes defined here, hence we use these values. +* +* HS200 - SDR104 (Since they both are equivalent in functionality) +* HS400 - This involves multiple configurations +* Initially SDR104 - when tuning is required as HS200 +* Then when switching to DDR @ 400MHz (HS400) we use +* the vendor specific HC_SELECT_IN to control the mode. +* +* In addition to controlling the modes we also need to select the +* correct input clock for DLL depending on the mode. +* +* HS400 - divided clock (free running MCLK/2) +* All other modes - default (free running MCLK) +*/ +if (curr_ios.timing ==MMC_TIMING_MMC_HS400 ) { +/* Select the divided clock (free running MCLK/2) */
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 759
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_HS400 ; + +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); +/* +* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC +* register +*/ +if (msm_host->tuning_done && !msm_host->calibration_done) {
In this patch, tuning_done and calibration_done are never true. Is that intended?
Quoted 20 lines
drivers/mmc/host/sdhci-msm.c +20
-0+/* +* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN +* field in VENDOR_SPEC_FUNC +*/ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config |=CORE_HC_SELECT_IN_HS400 ; + config |=CORE_HC_SELECT_IN_EN ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + } + }else { +/* Select the default clock (free running MCLK) */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_DFLT ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + +/* +* Disable HC_SELECT_IN to be able to use the UHS mode select +* configuration from Host Control2 register for all other +* modes.
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 789
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+* +* 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 &= ~CORE_HC_SELECT_IN_EN ; + config &= ~CORE_HC_SELECT_IN_MASK ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + } +wmb ();/* drain writebuffer */
Memory barrier could use more explanation.
Quoted 17 lines
drivers/mmc/host/sdhci-msm.c +6
-0+if ((msm_clock != msm_host->clk_rate) && msm_host->clk_table) { rc =clk_set_rate (msm_host->clk, msm_clock);if (rc) {@@ -876,6 +973,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)/* Set HC_MODE_EN bit in HC_MODE register */ writel_relaxed (HC_MODE_EN , (msm_host->core_mem +CORE_HC_MODE )); +/* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */ + 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_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) >>
Re: [PATCH v5 10/12] mmc: sdhci-msm: Add HS400 platform support
Hi Adrian,
Thanks for the complete review.
Quoted 69 lines · Adrian Hunter
Quoted 68 lines · Ritesh Harjani
From: Venkat Gopalakrishnan <[email protected]>
The following msm platform specific changes are added to support HS400.
- Allow tuning for HS400 mode.
- Configure HS400 timing mode using theVENDOR_SPECIFIC_FUNCregister.Signed-off-by Venkat Gopalakrishnan <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 124 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 113 insertions(+), 11 deletions(-)drivers/mmc/host/sdhci-msm.c +12
-3@@ -31,6 +31,7 @@#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@@ -64,11 +65,18 @@#define CORE_VENDOR_SPEC 0x10c#define CORE_CLK_PWRSAVE BIT(1) +#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_HC_SELECT_IN_EN (1 << 18) +#define CORE_HC_SELECT_IN_HS400 (6 << 19) +#define CORE_HC_SELECT_IN_MASK (7 << 19)#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000 +#define CORE_FREQ_100MHZ (100 * 1000 * 1000)#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)@@ -87,6 +95,8 @@ struct sdhci_msm_host {u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; +bool tuning_done; +bool calibration_done; };/* Platform specific tuning */ @@ -175,8 +185,8 @@ out:* Find out the greatest range of consecuitive selected * DLL clock output phases that can be used as sampling * setting for SD3.0 UHS-I card read operation (in SDR104 -* timing mode) or for eMMC4.5 card read operation (in HS200 -* timing mode). +* timing mode) or for eMMC4.5 card read operation (in +* HS400/HS200 timing mode). * Select the 3/4 of the range and configure the DLL with the * selected DLL clock output phase. */ @@ -428,9 +438,10 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)* Tuning is required for SDR104, HS200 and HS400 cards and * if clock frequency is greater than 100MHz in these modes. */ - if (host->clock <= 100 * 1000 * 1000 ||
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode) · line 444
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +4
-2- !((ios.timing ==MMC_TIMING_MMC_HS200 ) || - (ios.timing == MMC_TIMING_UHS_SDR104))) +if (host->clock <=CORE_FREQ_100MHZ || + !((ios.timing ==MMC_TIMING_MMC_HS400 ) || + (ios.timing ==MMC_TIMING_MMC_HS200 ) || + (ios.timing ==MMC_TIMING_UHS_SDR104 )))Don't need () around ios.timing ==
MMC_TIMING_MMC_HS400etc
Sure. Done.
Quoted 46 lines
Quoted 46 lines
drivers/mmc/host/sdhci-msm.c +18
-5return 0 ; retry:@@ -488,7 +499,10 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) {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);u16 ctrl_2; +u32 config; ctrl_2 =sdhci_readw (host,SDHCI_HOST_CONTROL2 );/* Select Bus Speed Mode for host */ @@ -503,6 +517,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,case MMC_TIMING_UHS_SDR50 : ctrl_2 |=SDHCI_CTRL_UHS_SDR50 ;break ; +case MMC_TIMING_MMC_HS400 :case MMC_TIMING_MMC_HS200 :case MMC_TIMING_UHS_SDR104 : ctrl_2 |=SDHCI_CTRL_UHS_SDR104 ;@@ -519,11 +534,33 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,* provide feedback clock, the mode selection can be any value less * than 3'b011 in bits [2:0] of HOST CONTROL2 register. */ -if (host->clock <=100000000 && - (uhs ==MMC_TIMING_MMC_HS400 || - uhs ==MMC_TIMING_MMC_HS200 || - uhs ==MMC_TIMING_UHS_SDR104 )) - ctrl_2 &= ~SDHCI_CTRL_UHS_MASK ; +if (host->clock <=CORE_FREQ_100MHZ ) { +if ((uhs ==MMC_TIMING_MMC_HS400 ) || + (uhs ==MMC_TIMING_MMC_HS200 ) || + (uhs ==MMC_TIMING_UHS_SDR104 )) + ctrl_2 &= ~SDHCI_CTRL_UHS_MASK ; +/* +* Make sure DLL is disabled when not required +* +* Write 1 to DLL_RST bit of DLL_CONFIG register +*/ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_RST ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); +
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host, · line 556
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +6
-0+/* Write 1 to DLL_PDN bit of DLL_CONFIG register */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_DLL_PDN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +wmb ();/* drain writebuffer */ Memory barrier could use more explanation.
Ok.
Quoted 49 lines
Quoted 49 lines
drivers/mmc/host/sdhci-msm.c +33
-1+ +/* +* The DLL needs to be restored and CDCLP533 recalibrated +* when the clock frequency is set back to 400MHz. +*/ + msm_host->calibration_done = false; + }dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);@@ -670,7 +707,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios; -u32 msm_clock; +u32 msm_clock, config;int rc;if (!clock)@@ -691,6 +728,66 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) } msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); +/* +* In general all timing modes are controlled via UHS mode select in +* Host Control2 register. eMMC specific HS200/HS400 doesn't have +* their respective modes defined here, hence we use these values. +* +* HS200 - SDR104 (Since they both are equivalent in functionality) +* HS400 - This involves multiple configurations +* Initially SDR104 - when tuning is required as HS200 +* Then when switching to DDR @ 400MHz (HS400) we use +* the vendor specific HC_SELECT_IN to control the mode. +* +* In addition to controlling the modes we also need to select the +* correct input clock for DLL depending on the mode. +* +* HS400 - divided clock (free running MCLK/2) +* All other modes - default (free running MCLK) +*/ +if (curr_ios.timing ==MMC_TIMING_MMC_HS400 ) { +/* Select the divided clock (free running MCLK/2) */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_HS400 ; + +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); +/*
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 759
Quoted 4 lines
drivers/mmc/host/sdhci-msm.c +4
-0+* Select HS400 mode using the HC_SELECT_IN from VENDOR SPEC +* register +*/ +if (msm_host->tuning_done && !msm_host->calibration_done) {In this patch,
tuning_doneandcalibration_doneare never true. Is that intended?
Select HS400 which is done below should happen only after the tuning is performed. Hence the check of that variable above. We set tuning_done = true in the next patch.
Please let me know if any concerns.
Similarly calibration_done is also getting set to true in the next patch.
Quoted 26 lines
Quoted 26 lines
drivers/mmc/host/sdhci-msm.c +24
-0+/* +* Write 0x6 to HC_SELECT_IN and 1 to HC_SELECT_IN_EN +* field in VENDOR_SPEC_FUNC +*/ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config |=CORE_HC_SELECT_IN_HS400 ; + config |=CORE_HC_SELECT_IN_EN ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + } + }else { +/* Select the default clock (free running MCLK) */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_MCLK_SEL_MASK ; + config |=CORE_HC_MCLK_SEL_DFLT ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + +/* +* Disable HC_SELECT_IN to be able to use the UHS mode select +* configuration from Host Control2 register for all other +* modes. +* +* Write 0 to HC_SELECT_IN and HC_SELECT_IN_EN field +* in VENDOR_SPEC_FUNC +*/
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 789
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +6
-0+ config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); + config &= ~CORE_HC_SELECT_IN_EN ; + config &= ~CORE_HC_SELECT_IN_MASK ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); + } +wmb ();/* drain writebuffer */ Memory barrier could use more explanation.
Ok. Sure.
Quoted 19 lines
Quoted 18 lines
drivers/mmc/host/sdhci-msm.c +6
-0+if ((msm_clock != msm_host->clk_rate) && msm_host->clk_table) { rc =clk_set_rate (msm_host->clk, msm_clock);if (rc) {@@ -876,6 +973,11 @@ static int sdhci_msm_probe(struct platform_device *pdev)/* Set HC_MODE_EN bit in HC_MODE register */ writel_relaxed (HC_MODE_EN , (msm_host->core_mem +CORE_HC_MODE )); +/* Set FF_CLK_SW_RST_DIS bit in HC_MODE register */ + 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_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) >>
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v5 11/12 mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
In reply to a message not in the archiveFrom: Venkat Gopalakrishnan <[email protected]>
In HS400 mode a new RCLK is introduced on the interface for read data transfers. The eMMC5.0 device transmits the read data to the host with respect to rising and falling edges of RCLK. In order to ensure correct operation of read data transfers in HS400 mode, the incoming RX data needs to be sampled by delayed version of RCLK.
The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be initialized, configured and enabled once during HS400 mode switch and when operational voltage/clock is changed.
--- drivers/mmc/host/sdhci-msm.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+)
drivers/mmc/host/sdhci-msm.c +178 -0
@@ -57,6 +57,7 @@#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
@@ -72,8 +73,36 @@#define CORE_HC_SELECT_IN_HS400 (6 << 19)#define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_CSR_CDC_CTLR_CFG0 0x130 +#define CORE_SW_TRIG_FULL_CALIB BIT(16) +#define CORE_HW_AUTOCAL_ENA BIT(17) + +#define CORE_CSR_CDC_CTLR_CFG1 0x134 +#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 +#define CORE_TIMER_ENA BIT(16) + +#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C +#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 +#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 +#define CORE_CDC_OFFSET_CFG 0x14C +#define CORE_CSR_CDC_DELAY_CFG 0x150 +#define CORE_CDC_SLAVE_DDA_CFG 0x160 +#define CORE_CSR_CDC_STATUS0 0x164 +#define CORE_CALIBRATION_DONE BIT(0) + +#define CORE_CDC_ERROR_CODE_MASK 0x7000000 + +#define CORE_CSR_CDC_GEN_CFG 0x178 +#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_START_CDC_TRAFFIC BIT(6) +#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c +#define INVALID_TUNING_PHASE -1#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000#define CORE_FREQ_100MHZ (100 * 1000 * 1000)
@@ -97,6 +126,7 @@ struct sdhci_msm_host {bool use_14lpp_dll_reset;bool tuning_done;bool calibration_done; +u8 saved_tuning_phase; };/* Platform specific tuning */
@@ -426,6 +456,136 @@ static int msm_init_cm_dll(struct sdhci_host *host)return 0 ; } +static int sdhci_msm_cdclp533_calibration (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 wait_cnt, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_CDC_T4_DLY_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config &= ~CORE_CDC_SWITCH_BYPASS_OFF ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config |=CORE_CDC_SWITCH_RC_EN ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* +* Perform CDC Register Initialization Sequence +* +* CORE_CSR_CDC_CTLR_CFG0 0x11800EC +* CORE_CSR_CDC_CTLR_CFG1 0x3011111 +* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000 +* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4 +* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020 +* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19 +* CORE_CSR_CDC_DELAY_CFG 0x3AC +* CORE_CDC_OFFSET_CFG 0x0 +* CORE_CDC_SLAVE_DDA_CFG 0x16334 +*/ + +writel_relaxed (0x11800EC , host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); +writel_relaxed (0x3011111 , host->ioaddr +CORE_CSR_CDC_CTLR_CFG1 ); +writel_relaxed (0x1201000 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); +writel_relaxed (0x4 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG1 ); +writel_relaxed (0xCB732020 , host->ioaddr +CORE_CSR_CDC_REFCOUNT_CFG ); +writel_relaxed (0xB19 , host->ioaddr +CORE_CSR_CDC_COARSE_CAL_CFG ); +writel_relaxed (0x3AC , host->ioaddr +CORE_CSR_CDC_DELAY_CFG ); +writel_relaxed (0x0 , host->ioaddr +CORE_CDC_OFFSET_CFG ); +writel_relaxed (0x16334 , host->ioaddr +CORE_CDC_SLAVE_DDA_CFG ); + +/* CDC HW Calibration */ + +/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config &= ~CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_HW_AUTOCAL_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + config |=CORE_TIMER_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + +wmb ();/* drain writebuffer */ + +/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ + wait_cnt =50 ; +while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CALIBRATION_DONE )) { +/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ +if (--wait_cnt ==0 ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + ret = -ETIMEDOUT ; + goto out; + } +/* wait for 1us before polling again */ +udelay (1 ); + } + +/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ + ret =readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CDC_ERROR_CODE_MASK ; +if (ret) { +pr_err ("%s: %s: CDC Error Code %d\n" , +mmc_hostname (host->mmc), __func__, ret); + ret = -EINVAL ; + goto out; + } + +/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config |=CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;
@@ -433,6 +593,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)int rc;struct mmc_host *mmc = host->mmc;struct mmc_ios ios = host->mmc->ios; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);/* * Tuning is required for SDR104, HS200 and HS400 cards and
@@ -457,6 +619,7 @@ retry:if (rc)return rc; + msm_host->saved_tuning_phase = phase; rc =mmc_send_tuning (mmc, opcode,NULL );if (!rc) {/* Tuning is successful at this tuning point */
@@ -492,6 +655,8 @@ retry: rc = -EIO ; } +if (!rc) + msm_host->tuning_done = true;return rc; }
@@ -565,6 +730,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);sdhci_writew (host, ctrl_2,SDHCI_HOST_CONTROL2 ); + +spin_unlock_irq (&host->lock); + +/* CDCLP533 HW calibration is only required for HS400 mode*/ +if (host->clock >CORE_FREQ_100MHZ && + msm_host->tuning_done && !msm_host->calibration_done && + (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) +if (!sdhci_msm_cdclp533_calibration (host)) + msm_host->calibration_done = true; + +spin_lock_irq (&host->lock); }static void sdhci_msm_voltage_switch (struct sdhci_host *host)
@@ -907,6 +1083,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)sdhci_msm_populate_dt (&pdev->dev, msm_host); + msm_host->saved_tuning_phase =INVALID_TUNING_PHASE ; +/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR (msm_host->bus_clk)) {
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project. -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Adrian Hunter
maintainerRe: [PATCH v5 11/12] mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
Quoted 257 lines · Ritesh Harjani
From: Venkat Gopalakrishnan <[email protected]>
In HS400 mode a new RCLK is introduced on the interface for read data transfers. The eMMC5.0 device transmits the read data to the host with respect to rising and falling edges of RCLK. In order to ensure correct operation of read data transfers in HS400 mode, the incoming RX data needs to be sampled by delayed version of RCLK.
The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be initialized, configured and enabled once during HS400 mode switch and when operational voltage/clock is changed.
Signed-off-by Venkat Gopalakrishnan <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+)drivers/mmc/host/sdhci-msm.c +176
-0@@ -57,6 +57,7 @@#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@@ -72,8 +73,36 @@#define CORE_HC_SELECT_IN_HS400 (6 << 19)#define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_CSR_CDC_CTLR_CFG0 0x130 +#define CORE_SW_TRIG_FULL_CALIB BIT(16) +#define CORE_HW_AUTOCAL_ENA BIT(17) + +#define CORE_CSR_CDC_CTLR_CFG1 0x134 +#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 +#define CORE_TIMER_ENA BIT(16) + +#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C +#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 +#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 +#define CORE_CDC_OFFSET_CFG 0x14C +#define CORE_CSR_CDC_DELAY_CFG 0x150 +#define CORE_CDC_SLAVE_DDA_CFG 0x160 +#define CORE_CSR_CDC_STATUS0 0x164 +#define CORE_CALIBRATION_DONE BIT(0) + +#define CORE_CDC_ERROR_CODE_MASK 0x7000000 + +#define CORE_CSR_CDC_GEN_CFG 0x178 +#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_START_CDC_TRAFFIC BIT(6) +#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c +#define INVALID_TUNING_PHASE -1#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000#define CORE_FREQ_100MHZ (100 * 1000 * 1000)@@ -97,6 +126,7 @@ struct sdhci_msm_host {bool use_14lpp_dll_reset;bool tuning_done;bool calibration_done; +u8 saved_tuning_phase; };/* Platform specific tuning */ @@ -426,6 +456,136 @@ static int msm_init_cm_dll(struct sdhci_host *host)return 0 ; } +static int sdhci_msm_cdclp533_calibration (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 wait_cnt, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_CDC_T4_DLY_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config &= ~CORE_CDC_SWITCH_BYPASS_OFF ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config |=CORE_CDC_SWITCH_RC_EN ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* +* Perform CDC Register Initialization Sequence +* +* CORE_CSR_CDC_CTLR_CFG0 0x11800EC +* CORE_CSR_CDC_CTLR_CFG1 0x3011111 +* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000 +* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4 +* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020 +* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19 +* CORE_CSR_CDC_DELAY_CFG 0x3AC +* CORE_CDC_OFFSET_CFG 0x0 +* CORE_CDC_SLAVE_DDA_CFG 0x16334 +*/ + +writel_relaxed (0x11800EC , host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); +writel_relaxed (0x3011111 , host->ioaddr +CORE_CSR_CDC_CTLR_CFG1 ); +writel_relaxed (0x1201000 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); +writel_relaxed (0x4 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG1 ); +writel_relaxed (0xCB732020 , host->ioaddr +CORE_CSR_CDC_REFCOUNT_CFG ); +writel_relaxed (0xB19 , host->ioaddr +CORE_CSR_CDC_COARSE_CAL_CFG ); +writel_relaxed (0x3AC , host->ioaddr +CORE_CSR_CDC_DELAY_CFG ); +writel_relaxed (0x0 , host->ioaddr +CORE_CDC_OFFSET_CFG ); +writel_relaxed (0x16334 , host->ioaddr +CORE_CDC_SLAVE_DDA_CFG ); + +/* CDC HW Calibration */ + +/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config &= ~CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_HW_AUTOCAL_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + config |=CORE_TIMER_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + +wmb ();/* drain writebuffer */ + +/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ + wait_cnt =50 ; +while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CALIBRATION_DONE )) { +/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ +if (--wait_cnt ==0 ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + ret = -ETIMEDOUT ; + goto out; + } +/* wait for 1us before polling again */ +udelay (1 ); + } + +/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ + ret =readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CDC_ERROR_CODE_MASK ; +if (ret) { +pr_err ("%s: %s: CDC Error Code %d\n" , +mmc_hostname (host->mmc), __func__, ret); + ret = -EINVAL ; + goto out; + } + +/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config |=CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;@@ -433,6 +593,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)int rc;struct mmc_host *mmc = host->mmc;struct mmc_ios ios = host->mmc->ios; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);/* * Tuning is required for SDR104, HS200 and HS400 cards and @@ -457,6 +619,7 @@ retry:if (rc)return rc; + msm_host->saved_tuning_phase = phase; rc =mmc_send_tuning (mmc, opcode,NULL );if (!rc) {/* Tuning is successful at this tuning point */ @@ -492,6 +655,8 @@ retry: rc = -EIO ; } +if (!rc) + msm_host->tuning_done = true;return rc; }@@ -565,6 +730,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);sdhci_writew (host, ctrl_2,SDHCI_HOST_CONTROL2 ); + +spin_unlock_irq (&host->lock); + +/* CDCLP533 HW calibration is only required for HS400 mode*/ +if (host->clock >CORE_FREQ_100MHZ && + msm_host->tuning_done && !msm_host->calibration_done && + (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) +if (!sdhci_msm_cdclp533_calibration (host)) + msm_host->calibration_done = true; + +spin_lock_irq (&host->lock); }static void sdhci_msm_voltage_switch (struct sdhci_host *host)
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_probe(struct platform_device *pdev) · line 1086
Quoted 4 lines
drivers/mmc/host/sdhci-msm.c +1
-0@@ -907,6 +1083,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)sdhci_msm_populate_dt (&pdev->dev, msm_host); + msm_host->saved_tuning_phase =INVALID_TUNING_PHASE ;
There is never a check for INVALID_TUNING_PHASE which begs the question: why have it?
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c +1
-0+/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR (msm_host->bus_clk)) {
A message that is not in the archive (sent off-list or not yet received): 183c2e6a-179b-b042-aef9-d1e5cb90b17d-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org
Re: [PATCH v5 11/12] mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
Hi Adrian,
Quoted 259 lines · Adrian Hunter
Quoted 258 lines · Ritesh Harjani
From: Venkat Gopalakrishnan <[email protected]>
In HS400 mode a new RCLK is introduced on the interface for read data transfers. The eMMC5.0 device transmits the read data to the host with respect to rising and falling edges of RCLK. In order to ensure correct operation of read data transfers in HS400 mode, the incoming RX data needs to be sampled by delayed version of RCLK.
The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be initialized, configured and enabled once during HS400 mode switch and when operational voltage/clock is changed.
Signed-off-by Venkat Gopalakrishnan <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+)drivers/mmc/host/sdhci-msm.c +176
-0@@ -57,6 +57,7 @@#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@@ -72,8 +73,36 @@#define CORE_HC_SELECT_IN_HS400 (6 << 19)#define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_CSR_CDC_CTLR_CFG0 0x130 +#define CORE_SW_TRIG_FULL_CALIB BIT(16) +#define CORE_HW_AUTOCAL_ENA BIT(17) + +#define CORE_CSR_CDC_CTLR_CFG1 0x134 +#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 +#define CORE_TIMER_ENA BIT(16) + +#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C +#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 +#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 +#define CORE_CDC_OFFSET_CFG 0x14C +#define CORE_CSR_CDC_DELAY_CFG 0x150 +#define CORE_CDC_SLAVE_DDA_CFG 0x160 +#define CORE_CSR_CDC_STATUS0 0x164 +#define CORE_CALIBRATION_DONE BIT(0) + +#define CORE_CDC_ERROR_CODE_MASK 0x7000000 + +#define CORE_CSR_CDC_GEN_CFG 0x178 +#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_START_CDC_TRAFFIC BIT(6) +#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c +#define INVALID_TUNING_PHASE -1#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000#define CORE_FREQ_100MHZ (100 * 1000 * 1000)@@ -97,6 +126,7 @@ struct sdhci_msm_host {bool use_14lpp_dll_reset;bool tuning_done;bool calibration_done; +u8 saved_tuning_phase; };/* Platform specific tuning */ @@ -426,6 +456,136 @@ static int msm_init_cm_dll(struct sdhci_host *host)return 0 ; } +static int sdhci_msm_cdclp533_calibration (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 wait_cnt, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_CDC_T4_DLY_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config &= ~CORE_CDC_SWITCH_BYPASS_OFF ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config |=CORE_CDC_SWITCH_RC_EN ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* +* Perform CDC Register Initialization Sequence +* +* CORE_CSR_CDC_CTLR_CFG0 0x11800EC +* CORE_CSR_CDC_CTLR_CFG1 0x3011111 +* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000 +* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4 +* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020 +* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19 +* CORE_CSR_CDC_DELAY_CFG 0x3AC +* CORE_CDC_OFFSET_CFG 0x0 +* CORE_CDC_SLAVE_DDA_CFG 0x16334 +*/ + +writel_relaxed (0x11800EC , host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); +writel_relaxed (0x3011111 , host->ioaddr +CORE_CSR_CDC_CTLR_CFG1 ); +writel_relaxed (0x1201000 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); +writel_relaxed (0x4 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG1 ); +writel_relaxed (0xCB732020 , host->ioaddr +CORE_CSR_CDC_REFCOUNT_CFG ); +writel_relaxed (0xB19 , host->ioaddr +CORE_CSR_CDC_COARSE_CAL_CFG ); +writel_relaxed (0x3AC , host->ioaddr +CORE_CSR_CDC_DELAY_CFG ); +writel_relaxed (0x0 , host->ioaddr +CORE_CDC_OFFSET_CFG ); +writel_relaxed (0x16334 , host->ioaddr +CORE_CDC_SLAVE_DDA_CFG ); + +/* CDC HW Calibration */ + +/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config &= ~CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_HW_AUTOCAL_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + config |=CORE_TIMER_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + +wmb ();/* drain writebuffer */ + +/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ + wait_cnt =50 ; +while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CALIBRATION_DONE )) { +/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ +if (--wait_cnt ==0 ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + ret = -ETIMEDOUT ; + goto out; + } +/* wait for 1us before polling again */ +udelay (1 ); + } + +/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ + ret =readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CDC_ERROR_CODE_MASK ; +if (ret) { +pr_err ("%s: %s: CDC Error Code %d\n" , +mmc_hostname (host->mmc), __func__, ret); + ret = -EINVAL ; + goto out; + } + +/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config |=CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;@@ -433,6 +593,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)int rc;struct mmc_host *mmc = host->mmc;struct mmc_ios ios = host->mmc->ios; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);/* * Tuning is required for SDR104, HS200 and HS400 cards and @@ -457,6 +619,7 @@ retry:if (rc)return rc; + msm_host->saved_tuning_phase = phase; rc =mmc_send_tuning (mmc, opcode,NULL );if (!rc) {/* Tuning is successful at this tuning point */ @@ -492,6 +655,8 @@ retry: rc = -EIO ; } +if (!rc) + msm_host->tuning_done = true;return rc; }@@ -565,6 +730,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);sdhci_writew (host, ctrl_2,SDHCI_HOST_CONTROL2 ); + +spin_unlock_irq (&host->lock); + +/* CDCLP533 HW calibration is only required for HS400 mode*/ +if (host->clock >CORE_FREQ_100MHZ && + msm_host->tuning_done && !msm_host->calibration_done && + (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) +if (!sdhci_msm_cdclp533_calibration (host)) + msm_host->calibration_done = true; + +spin_lock_irq (&host->lock); }static void sdhci_msm_voltage_switch (struct sdhci_host *host)@@ -907,6 +1083,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_probe(struct platform_device *pdev) · line 1086
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +1
-0sdhci_msm_populate_dt (&pdev->dev, msm_host); + msm_host->saved_tuning_phase =INVALID_TUNING_PHASE ;There is never a check for
INVALID_TUNING_PHASEwhich begs the question: why have it?
phase value can be between 0x0 to 0xf. So during probe saved_tuning_phase is getting initialized with -1 value.
Let me know if any concern, we can remove it as well.
But wont it look incorrect if we initialize it with some valid phase value before even tuning is completed?
Quoted 7 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_probe(struct platform_device *pdev) · line 1090
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +1
-0+/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR (msm_host->bus_clk)) {
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): 6993d3a2-7961-2507-60d2-153c14e0bc17-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org
Re: [PATCH v5 11/12] mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
Quoted 272 lines · Ritesh Harjani
Hi Adrian,
Quoted 263 lines · Adrian Hunter
Quoted 262 lines · Ritesh Harjani
From: Venkat Gopalakrishnan <[email protected]>
In HS400 mode a new RCLK is introduced on the interface for read data transfers. The eMMC5.0 device transmits the read data to the host with respect to rising and falling edges of RCLK. In order to ensure correct operation of read data transfers in HS400 mode, the incoming RX data needs to be sampled by delayed version of RCLK.
The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be initialized, configured and enabled once during HS400 mode switch and when operational voltage/clock is changed.
Signed-off-by Venkat Gopalakrishnan <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+)drivers/mmc/host/sdhci-msm.c +162
-0@@ -57,6 +57,7 @@#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@@ -72,8 +73,36 @@#define CORE_HC_SELECT_IN_HS400 (6 << 19)#define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_CSR_CDC_CTLR_CFG0 0x130 +#define CORE_SW_TRIG_FULL_CALIB BIT(16) +#define CORE_HW_AUTOCAL_ENA BIT(17) + +#define CORE_CSR_CDC_CTLR_CFG1 0x134 +#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 +#define CORE_TIMER_ENA BIT(16) + +#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C +#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 +#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 +#define CORE_CDC_OFFSET_CFG 0x14C +#define CORE_CSR_CDC_DELAY_CFG 0x150 +#define CORE_CDC_SLAVE_DDA_CFG 0x160 +#define CORE_CSR_CDC_STATUS0 0x164 +#define CORE_CALIBRATION_DONE BIT(0) + +#define CORE_CDC_ERROR_CODE_MASK 0x7000000 + +#define CORE_CSR_CDC_GEN_CFG 0x178 +#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_START_CDC_TRAFFIC BIT(6) +#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c +#define INVALID_TUNING_PHASE -1#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000#define CORE_FREQ_100MHZ (100 * 1000 * 1000)@@ -97,6 +126,7 @@ struct sdhci_msm_host {bool use_14lpp_dll_reset;bool tuning_done;bool calibration_done; +u8 saved_tuning_phase; };/* Platform specific tuning */ @@ -426,6 +456,136 @@ static int msm_init_cm_dll(struct sdhci_host *host)return 0 ; } +static int sdhci_msm_cdclp533_calibration (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 wait_cnt, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_CDC_T4_DLY_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config &= ~CORE_CDC_SWITCH_BYPASS_OFF ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config |=CORE_CDC_SWITCH_RC_EN ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* +* Perform CDC Register Initialization Sequence +* +* CORE_CSR_CDC_CTLR_CFG0 0x11800EC +* CORE_CSR_CDC_CTLR_CFG1 0x3011111 +* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000 +* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4 +* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020 +* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19 +* CORE_CSR_CDC_DELAY_CFG 0x3AC +* CORE_CDC_OFFSET_CFG 0x0 +* CORE_CDC_SLAVE_DDA_CFG 0x16334 +*/ + +writel_relaxed (0x11800EC , host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); +writel_relaxed (0x3011111 , host->ioaddr +CORE_CSR_CDC_CTLR_CFG1 ); +writel_relaxed (0x1201000 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); +writel_relaxed (0x4 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG1 ); +writel_relaxed (0xCB732020 , host->ioaddr +CORE_CSR_CDC_REFCOUNT_CFG ); +writel_relaxed (0xB19 , host->ioaddr +CORE_CSR_CDC_COARSE_CAL_CFG ); +writel_relaxed (0x3AC , host->ioaddr +CORE_CSR_CDC_DELAY_CFG ); +writel_relaxed (0x0 , host->ioaddr +CORE_CDC_OFFSET_CFG ); +writel_relaxed (0x16334 , host->ioaddr +CORE_CDC_SLAVE_DDA_CFG ); + +/* CDC HW Calibration */ + +/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config &= ~CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_HW_AUTOCAL_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + config |=CORE_TIMER_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + +wmb ();/* drain writebuffer */ + +/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ + wait_cnt =50 ; +while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CALIBRATION_DONE )) { +/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ +if (--wait_cnt ==0 ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + ret = -ETIMEDOUT ; + goto out; + } +/* wait for 1us before polling again */ +udelay (1 ); + } + +/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ + ret =readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CDC_ERROR_CODE_MASK ; +if (ret) { +pr_err ("%s: %s: CDC Error Code %d\n" , +mmc_hostname (host->mmc), __func__, ret); + ret = -EINVAL ; + goto out; + } + +/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config |=CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;@@ -433,6 +593,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)int rc;struct mmc_host *mmc = host->mmc;struct mmc_ios ios = host->mmc->ios; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);/* * Tuning is required for SDR104, HS200 and HS400 cards and
drivers/mmc/host/sdhci-msm.c +14
-0@@ -457,6 +619,7 @@ retry:if (rc)return rc; + msm_host->saved_tuning_phase = phase; rc =mmc_send_tuning (mmc, opcode,NULL );if (!rc) {/* Tuning is successful at this tuning point */ @@ -492,6 +655,8 @@ retry: rc = -EIO ; } +if (!rc) + msm_host->tuning_done = true;return rc; }@@ -565,6 +730,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);sdhci_writew (host, ctrl_2,SDHCI_HOST_CONTROL2 ); + +spin_unlock_irq (&host->lock); + +/* CDCLP533 HW calibration is only required for HS400 mode*/ +if (host->clock >CORE_FREQ_100MHZ && + msm_host->tuning_done && !msm_host->calibration_done && + (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) +if (!sdhci_msm_cdclp533_calibration (host)) + msm_host->calibration_done = true; + +spin_lock_irq (&host->lock); }static void
sdhci_msm_voltage_switch(struct sdhci_host *host)drivers/mmc/host/sdhci-msm.c +0
-0@@ -907,6 +1083,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)Quoted 6 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_probe(struct platform_device *pdev) · line 1086
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +1
-0sdhci_msm_populate_dt (&pdev->dev, msm_host); + msm_host->saved_tuning_phase =INVALID_TUNING_PHASE ;There is never a check for
INVALID_TUNING_PHASEwhich begs the question: why have it?
Quoted 6 lines
phase value can be between
0x0to0xf. So during probesaved_tuning_phaseis getting initialized with -1 value.Let me know if any concern, we can remove it as well.
But wont it look incorrect if we initialize it with some valid phase value before even tuning is completed?
INVALID_TUNING_PHASE is fine, I would just expect it be checked e.g. in msm_config_cm_dll_phase
if (phase > 0xf) return -EINVAL;
Quoted 9 lines
Quoted 8 lines
Quoted 7 lines
drivers/mmc/host/sdhci-msm.c +1
-0+/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR(msm_host->bus_clk)) {
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v5 11/12] mmc: sdhci-msm: Add calibration tuning for CDCLP533 circuit
Hi Adrian,
Quoted 280 lines · Adrian Hunter
Quoted 272 lines · Ritesh Harjani
Hi Adrian,
Quoted 263 lines · Adrian Hunter
Quoted 262 lines · Ritesh Harjani
From: Venkat Gopalakrishnan <[email protected]>
In HS400 mode a new RCLK is introduced on the interface for read data transfers. The eMMC5.0 device transmits the read data to the host with respect to rising and falling edges of RCLK. In order to ensure correct operation of read data transfers in HS400 mode, the incoming RX data needs to be sampled by delayed version of RCLK.
The CDCLP533 delay circuit shifts the RCLK by T/4. It needs to be initialized, configured and enabled once during HS400 mode switch and when operational voltage/clock is changed.
Signed-off-by Venkat Gopalakrishnan <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+)drivers/mmc/host/sdhci-msm.c +162
-0@@ -57,6 +57,7 @@#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@@ -72,8 +73,36 @@#define CORE_HC_SELECT_IN_HS400 (6 << 19)#define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_CSR_CDC_CTLR_CFG0 0x130 +#define CORE_SW_TRIG_FULL_CALIB BIT(16) +#define CORE_HW_AUTOCAL_ENA BIT(17) + +#define CORE_CSR_CDC_CTLR_CFG1 0x134 +#define CORE_CSR_CDC_CAL_TIMER_CFG0 0x138 +#define CORE_TIMER_ENA BIT(16) + +#define CORE_CSR_CDC_CAL_TIMER_CFG1 0x13C +#define CORE_CSR_CDC_REFCOUNT_CFG 0x140 +#define CORE_CSR_CDC_COARSE_CAL_CFG 0x144 +#define CORE_CDC_OFFSET_CFG 0x14C +#define CORE_CSR_CDC_DELAY_CFG 0x150 +#define CORE_CDC_SLAVE_DDA_CFG 0x160 +#define CORE_CSR_CDC_STATUS0 0x164 +#define CORE_CALIBRATION_DONE BIT(0) + +#define CORE_CDC_ERROR_CODE_MASK 0x7000000 + +#define CORE_CSR_CDC_GEN_CFG 0x178 +#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_START_CDC_TRAFFIC BIT(6) +#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c +#define INVALID_TUNING_PHASE -1#define TCXO_FREQ 19200000#define SDHCI_MSM_MIN_CLOCK 400000#define CORE_FREQ_100MHZ (100 * 1000 * 1000)@@ -97,6 +126,7 @@ struct sdhci_msm_host {bool use_14lpp_dll_reset;bool tuning_done;bool calibration_done; +u8 saved_tuning_phase; };/* Platform specific tuning */ @@ -426,6 +456,136 @@ static int msm_init_cm_dll(struct sdhci_host *host)return 0 ; } +static int sdhci_msm_cdclp533_calibration (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 wait_cnt, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); + +/* Write 0 to CDC_T4_DLY_SEL field in VENDOR_SPEC_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_CDC_T4_DLY_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* Write 0 to CDC_SWITCH_BYPASS_OFF field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config &= ~CORE_CDC_SWITCH_BYPASS_OFF ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 1 to CDC_SWITCH_RC_EN field in CORE_CSR_CDC_GEN_CFG */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + config |=CORE_CDC_SWITCH_RC_EN ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_GEN_CFG ); + +/* Write 0 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config &= ~CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); + +/* +* Perform CDC Register Initialization Sequence +* +* CORE_CSR_CDC_CTLR_CFG0 0x11800EC +* CORE_CSR_CDC_CTLR_CFG1 0x3011111 +* CORE_CSR_CDC_CAL_TIMER_CFG0 0x1201000 +* CORE_CSR_CDC_CAL_TIMER_CFG1 0x4 +* CORE_CSR_CDC_REFCOUNT_CFG 0xCB732020 +* CORE_CSR_CDC_COARSE_CAL_CFG 0xB19 +* CORE_CSR_CDC_DELAY_CFG 0x3AC +* CORE_CDC_OFFSET_CFG 0x0 +* CORE_CDC_SLAVE_DDA_CFG 0x16334 +*/ + +writel_relaxed (0x11800EC , host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); +writel_relaxed (0x3011111 , host->ioaddr +CORE_CSR_CDC_CTLR_CFG1 ); +writel_relaxed (0x1201000 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); +writel_relaxed (0x4 , host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG1 ); +writel_relaxed (0xCB732020 , host->ioaddr +CORE_CSR_CDC_REFCOUNT_CFG ); +writel_relaxed (0xB19 , host->ioaddr +CORE_CSR_CDC_COARSE_CAL_CFG ); +writel_relaxed (0x3AC , host->ioaddr +CORE_CSR_CDC_DELAY_CFG ); +writel_relaxed (0x0 , host->ioaddr +CORE_CDC_OFFSET_CFG ); +writel_relaxed (0x16334 , host->ioaddr +CORE_CDC_SLAVE_DDA_CFG ); + +/* CDC HW Calibration */ + +/* Write 1 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 0 to SW_TRIG_FULL_CALIB field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config &= ~CORE_SW_TRIG_FULL_CALIB ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to HW_AUTOCAL_ENA field in CORE_CSR_CDC_CTLR_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + config |=CORE_HW_AUTOCAL_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CTLR_CFG0 ); + +/* Write 1 to TIMER_ENA field in CORE_CSR_CDC_CAL_TIMER_CFG0 */ + config =readl_relaxed (host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + config |=CORE_TIMER_ENA ; +writel_relaxed (config, host->ioaddr +CORE_CSR_CDC_CAL_TIMER_CFG0 ); + +wmb ();/* drain writebuffer */ + +/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ + wait_cnt =50 ; +while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CALIBRATION_DONE )) { +/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ +if (--wait_cnt ==0 ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" , +mmc_hostname (host->mmc), __func__); + ret = -ETIMEDOUT ; + goto out; + } +/* wait for 1us before polling again */ +udelay (1 ); + } + +/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ + ret =readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) + &CORE_CDC_ERROR_CODE_MASK ; +if (ret) { +pr_err ("%s: %s: CDC Error Code %d\n" , +mmc_hostname (host->mmc), __func__, ret); + ret = -EINVAL ; + goto out; + } + +/* Write 1 to START_CDC_TRAFFIC field in CORE_DDR200_CFG */ + config =readl_relaxed (host->ioaddr +CORE_DDR_200_CFG ); + config |=CORE_START_CDC_TRAFFIC ; +writel_relaxed (config, host->ioaddr +CORE_DDR_200_CFG ); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;@@ -433,6 +593,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)int rc;struct mmc_host *mmc = host->mmc;struct mmc_ios ios = host->mmc->ios; +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);/* * Tuning is required for SDR104, HS200 and HS400 cards and
drivers/mmc/host/sdhci-msm.c +14
-0@@ -457,6 +619,7 @@ retry:if (rc)return rc; + msm_host->saved_tuning_phase = phase; rc =mmc_send_tuning (mmc, opcode,NULL );if (!rc) {/* Tuning is successful at this tuning point */ @@ -492,6 +655,8 @@ retry: rc = -EIO ; } +if (!rc) + msm_host->tuning_done = true;return rc; }@@ -565,6 +730,17 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,dev_dbg (mmc_dev (mmc),"%s: clock=%u uhs=%u ctrl_2=0x%x\n" ,mmc_hostname (host->mmc), host->clock, uhs, ctrl_2);sdhci_writew (host, ctrl_2,SDHCI_HOST_CONTROL2 ); + +spin_unlock_irq (&host->lock); + +/* CDCLP533 HW calibration is only required for HS400 mode*/ +if (host->clock >CORE_FREQ_100MHZ && + msm_host->tuning_done && !msm_host->calibration_done && + (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) +if (!sdhci_msm_cdclp533_calibration (host)) + msm_host->calibration_done = true; + +spin_lock_irq (&host->lock); }static void
sdhci_msm_voltage_switch(struct sdhci_host *host)drivers/mmc/host/sdhci-msm.c +0
-0@@ -907,6 +1083,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)Quoted 6 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_probe(struct platform_device *pdev) · line 1086
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +1
-0sdhci_msm_populate_dt (&pdev->dev, msm_host); + msm_host->saved_tuning_phase =INVALID_TUNING_PHASE ;There is never a check for
INVALID_TUNING_PHASEwhich begs the question: why have it?Quoted 6 lines
phase value can be between
0x0to0xf. So during probesaved_tuning_phaseis getting initialized with -1 value.Let me know if any concern, we can remove it as well.
But wont it look incorrect if we initialize it with some valid phase value before even tuning is completed?
Quoted 5 lines
INVALID_TUNING_PHASEis fine, I would just expect it be checked e.g. inmsm_config_cm_dll_phaseif (phase > 0xf) return -EINVAL;
Sure will add these checks.
Quoted 16 lines
Quoted 10 lines
Quoted 9 lines
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +1
-0+/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR(msm_host->bus_clk)) {--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v5 3/12 mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
In reply to Ritesh HarjaniThis adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own set_clock ops to be implemented. For this, supported clk-rates needs to be populated in sdhci_msm_pltfm_data.
--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1 -0
@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units - Hz. Example:
drivers/mmc/host/sdhci-msm.c +48 -0
66 lines not on this page · raw message
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Adrian Hunter
maintainerRe: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Quoted 37 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units - Hz. Example:drivers/mmc/host/sdhci-msm.c +2
-0@@ -81,6 +81,8 @@ struct sdhci_msm_host {struct clk *clk;/* main SD/MMC bus clock */ struct clk *pclk;/* SDHC peripheral bus clock */ struct clk *bus_clk;/* SDHC bus voter clock */ +u32 *clk_table; +int clk_table_sz;struct mmc_host *mmc;bool use_14lpp_dll_reset; };
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 588
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c +2
-0@@ -582,6 +584,50 @@ static const struct sdhci_pltfm_data sdhci_msm_pdata = { .ops = &sdhci_msm_ops, }; +static int sdhci_msm_dt_get_array (struct device *dev,const char *prop_name, +u32 **table,int *size)
It is nice to align to the open parenthesis. Have a look at the checks from checkpatch --strict
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 594
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +6
-0+{ +struct device_node *np = dev->of_node; +int count, ret; +u32 *arr; + + count =of_property_count_elems_of_size (np, prop_name,sizeof (u32));
Shouldn't this be of_property_count_u32_elems()
Quoted 4 lines
drivers/mmc/host/sdhci-msm.c +4
-0+if (count <0 ) { +dev_warn (dev,"%s: Invalid dt property, err(%d)\n" , + prop_name, count); +return count;
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 608
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ } + + arr =kcalloc (count,sizeof (*arr),GFP_KERNEL ); +if (!arr) +return -ENOMEM ; + + ret =of_property_read_u32_array (np, prop_name, arr, count); +if (ret) { +kfree (arr); + dev_warn(dev, "%s Invalid dt array property, err(%d)\n",
'err(%d)' is an unusual style for printing error numbers. 'error %d' looks better.
Also in some messages you have 'DT' and others 'dt'. Also here it is '%s' but above '%s:', and some messages start with a lower case letter and some upper case. Please try to make everything consistent.
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 618
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ prop_name, ret); +return ret; + } + *table = arr; + *size = count; +return 0 ; +} + +void sdhci_msm_populate_dt (struct device *dev, +struct sdhci_msm_host *msm_host)
Align to open parenthesis
Quoted 25 lines
drivers/mmc/host/sdhci-msm.c +14
-0+{ +int table_sz =0 ; +u32 *table =NULL ; + +if (sdhci_msm_dt_get_array (dev,"clk-rates" , &table, &table_sz)) { +dev_warn (dev,"failed in DT parsing for supported clk-rates\n" ); +return ; + } + msm_host->clk_table = table; + msm_host->clk_table_sz = table_sz; +} +static int sdhci_msm_probe (struct platform_device *pdev) {struct sdhci_host *host;@@ -608,6 +654,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)sdhci_get_of_property (pdev); +sdhci_msm_populate_dt (&pdev->dev, msm_host); +/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR (msm_host->bus_clk)) {
A message that is not in the archive (sent off-list or not yet received): 7e5c2bfe-0a67-71e2-d083-49d9a712482e-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org
Re: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Hi Adrian,
Thanks for the review. I will address your comments. Responses inline.
--
Regards
Ritesh
Quoted 41 lines · Adrian Hunter
Quoted 40 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units - Hz. Example:drivers/mmc/host/sdhci-msm.c +2
-0@@ -81,6 +81,8 @@ struct sdhci_msm_host {struct clk *clk;/* main SD/MMC bus clock */ struct clk *pclk;/* SDHC peripheral bus clock */ struct clk *bus_clk;/* SDHC bus voter clock */ +u32 *clk_table; +int clk_table_sz;struct mmc_host *mmc;bool use_14lpp_dll_reset; };@@ -582,6 +584,50 @@ static const struct sdhci_pltfm_data sdhci_msm_pdata = { .ops = &sdhci_msm_ops, };
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 588
Quoted 2 lines
drivers/mmc/host/sdhci-msm.c +2
-0+static int sdhci_msm_dt_get_array (struct device *dev,const char *prop_name, +u32 **table,int *size)It is nice to align to the open parenthesis. Have a look at the checks from checkpatch --strict
Sure, will do that.
Quoted 9 lines
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 594
Quoted 7 lines
drivers/mmc/host/sdhci-msm.c +6
-0+{ +struct device_node *np = dev->of_node; +int count, ret; +u32 *arr; + + count =of_property_count_elems_of_size (np, prop_name,sizeof (u32));Shouldn't this be
of_property_count_u32_elems()
Yes, I will make the change.
Quoted 2 lines
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 595
Quoted 2 lines
drivers/mmc/host/sdhci-msm.c +1
-0+if (count <0 ) {
will change this to below to avoid empty property problem in DT which you mentioned in the next patch.
if (count <= 0) {Quoted 8 lines
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +8
-0+dev_warn (dev,"%s: Invalid dt property, err(%d)\n" , + prop_name, count); +return count; + } + + arr =kcalloc (count,sizeof (*arr),GFP_KERNEL ); +if (!arr) +return -ENOMEM ;
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 608
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c +5
-0+ + ret =of_property_read_u32_array (np, prop_name, arr, count); +if (ret) { +kfree (arr); + dev_warn(dev, "%s Invalid dt array property, err(%d)\n",'err(%d)' is an unusual style for printing error numbers. 'error %d' looks better.
Done.
Quoted 5 lines
Also in some messages you have 'DT' and others 'dt'. Also here it is '%s' but above '%s:', and some messages start with a lower case letter and some upper case. Please try to make everything consistent.
Sure thanks.
Quoted 4 lines
Quoted 4 lines
drivers/mmc/host/sdhci-msm.c +4
-0+ prop_name, ret); +return ret; + } + *table = arr;
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static const struct sdhci_pltfm_data sdhci_msm_pdata = { · line 618
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +6
-0+ *size = count; +return 0 ; +} + +void sdhci_msm_populate_dt (struct device *dev, +struct sdhci_msm_host *msm_host)Align to open parenthesis
Done.
Quoted 20 lines
Quoted 20 lines
drivers/mmc/host/sdhci-msm.c +12
-0+{ +int table_sz =0 ; +u32 *table =NULL ; + +if (sdhci_msm_dt_get_array (dev,"clk-rates" , &table, &table_sz)) { +dev_warn (dev,"failed in DT parsing for supported clk-rates\n" ); +return ; + } + msm_host->clk_table = table; + msm_host->clk_table_sz = table_sz; +} +static int sdhci_msm_probe (struct platform_device *pdev) {struct sdhci_host *host;@@ -608,6 +654,8 @@ static int sdhci_msm_probe(struct platform_device *pdev)sdhci_get_of_property (pdev);
Quoted 12 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_probe(struct platform_device *pdev) · line 661
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +2
-0+sdhci_msm_populate_dt (&pdev->dev, msm_host); +/* Setup SDCC bus voter clock. */ msm_host->bus_clk =devm_clk_get (&pdev->dev,"bus" );if (!IS_ERR (msm_host->bus_clk)) {--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): 1475678440-3525-4-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org
Re: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Quoted 16 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Documentation/devicetree/bindings/mmc/sdhci-msm.txt +0
-0
Documentation/devicetree/bindings/mmc/sdhci-msm.txt Required properties: · line 20
Quoted 4 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units - Hz.
Why can't some combination of assigned-clock-rates and querying the clock provider for rates be used here?
Minimally this would need unit suffix and either be made common or have a vendor prefix.
Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Hi Rob,
Thanks for review.
Quoted 12 lines · Rob Herring
Quoted 11 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)
Quoted 12 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt Required properties: · line 20
Quoted 9 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units - Hz.Why can't some combination of assigned-clock-rates and querying the clock provider for rates be used here?
From what I understood, assigned-clock-rates would only work for setting some default clock rates for certain clocks by calling of_clk_set_defaults.
Whereas the requirement here is -
That since SDHC msm directly controls the clk(core clock) at source, it's sdhci-msm driver needs to know the supported clk-rates by the underlying platform to configure the nearest floor value supported on this platform (when the request arrives from the core layer to switch the clock).
Hence the table of clk-rates is provided for sdhci-msm.
Quoted 3 lines
Minimally this would need unit suffix and either be made common or have a vendor prefix.
Sure will this work in that case - "qcom-clk-rates"
Quoted 7 lines
Rob
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
Rob Herring
maintainerRe: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Quoted 41 lines · Ritesh Harjani
Hi Rob,
Thanks for review.
Quoted 15 lines · Rob Herring
Quoted 13 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Quoted 16 lines
Quoted 11 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units -Quoted 1 line
Hz.
Why can't some combination of assigned-clock-rates and querying the clock provider for rates be used here?
From what I understood, assigned-clock-rates would only work for setting some default clock rates for certain clocks by calling
of_clk_set_defaults.
Quoted 5 lines
Whereas the requirement here is -
That since SDHC msm directly controls the clk(core clock) at source, it's sdhci-msm driver needs to know the supported clk-rates by the underlying platform to configure the nearest floor value supported on this platform (when the request arrives from the core layer to switch the clock).
Why does clk_round_rate not work for you? That will round down to the nearest frequency supported.
Quoted 7 lines
Hence the table of clk-rates is provided for sdhci-msm.
Quoted 3 lines
Minimally this would need unit suffix and either be made common or have a vendor prefix.
Sure will this work in that case - "qcom-clk-rates"
"qcom,clk-rates", but I'm not yet convinced this is right.
Rob
Re: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Hi Rob
Quoted 42 lines · Rob Herring
Quoted 41 lines · Ritesh Harjani
Hi Rob,
Thanks for review.
Quoted 15 lines · Rob Herring
Quoted 13 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Quoted 16 lines
Quoted 11 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional) +- clk-rates: Array of supported GCC clock frequencies for sdhc, Units -Quoted 1 line
Hz.
Why can't some combination of assigned-clock-rates and querying the clock provider for rates be used here?
From what I understood, assigned-clock-rates would only work for setting some default clock rates for certain clocks by calling
of_clk_set_defaults.
Quoted 8 lines
Quoted 5 lines
Whereas the requirement here is -
That since SDHC msm directly controls the clk(core clock) at source, it's sdhci-msm driver needs to know the supported clk-rates by the underlying platform to configure the nearest floor value supported on this platform (when the request arrives from the core layer to switch the clock).Why does
clk_round_ratenot work for you? That will round down to the nearest frequency supported.
clk_round_rate will round off to nearest supported "ceil" frequency. But we require nearest rounded off "floor" frequency.
Quoted 17 lines
Quoted 8 lines
Hence the table of clk-rates is provided for sdhci-msm.
Quoted 3 lines
Minimally this would need unit suffix and either be made common or have a vendor prefix.
Sure will this work in that case - "qcom-clk-rates"
"qcom,clk-rates", but I'm not yet convinced this is right.
Rob
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Rob Herring
maintainerRe: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Quoted 61 lines · Ritesh Harjani
Hi Rob
Quoted 57 lines · Rob Herring
Quoted 52 lines · Ritesh Harjani
Hi Rob,
Thanks for review.
Quoted 27 lines · Rob Herring
Quoted 24 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Documentation/devicetree/bindings/mmc/sdhci-msm.txt +0
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional)Quoted 8 lines
Quoted 3 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-1+- clk-rates: Array of supported GCC clock frequencies for sdhc, Units -Hz.
Why can't some combination of assigned-clock-rates and querying the clock provider for rates be used here?
From what I understood, assigned-clock-rates would only work for setting some default clock rates for certain clocks by calling
of_clk_set_defaults.Whereas the requirement here is -
That since SDHC msm directly controls the clk(core clock) at source, it's sdhci-msm driver needs to know the supported clk-rates by the underlying platform to configure the nearest floor value supported on this platform (when the request arrives from the core layer to switch the clock).
Quoted 5 lines
Quoted 2 lines
Why does
clk_round_ratenot work for you? That will round down to the nearest frequency supported.
clk_round_ratewill round off to nearest supported "ceil" frequency. But we require nearest rounded off "floor" frequency.
Then fix the clk framework to do what you want. This doesn't need to be in DT.
Rob
Re: [PATCH v5 03/12] mmc: sdhci-msm: add pltfm_data support to get clk-rates from DT
Hi Rob,
Quoted 62 lines · Rob Herring
Quoted 61 lines · Ritesh Harjani
Hi Rob
Quoted 57 lines · Rob Herring
Quoted 52 lines · Ritesh Harjani
Hi Rob,
Thanks for review.
Quoted 27 lines · Rob Herring
Quoted 24 lines · Ritesh Harjani
This adds support for sdhc-msm controllers to get supported clk-rates from DT. sdhci-msm would need it's own
set_clockops to be implemented. For this, supported clk-rates needs to be populated insdhci_msm_pltfm_data.Signed-off-by Ritesh Harjani <[email protected]>--- .../devicetree/bindings/mmc/sdhci-msm.txt | 1 + drivers/mmc/host/sdhci-msm.c | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+)Documentation/devicetree/bindings/mmc/sdhci-msm.txt +0
-0@@ -17,6 +17,7 @@ Required properties: "iface" - Main peripheral bus clock (PCLK/HCLK - AHB Bus clock) (required) "core" - SDC MMC clock (MCLK) (required) "bus" - SDCC bus voter clock (optional)Quoted 8 lines
Quoted 3 lines
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +1
-1+- clk-rates: Array of supported GCC clock frequencies for sdhc, Units -Hz.
Why can't some combination of assigned-clock-rates and querying the clock provider for rates be used here?
From what I understood, assigned-clock-rates would only work for setting some default clock rates for certain clocks by calling
of_clk_set_defaults.Whereas the requirement here is -
That since SDHC msm directly controls the clk(core clock) at source, it's sdhci-msm driver needs to know the supported clk-rates by the underlying platform to configure the nearest floor value supported on this platform (when the request arrives from the core layer to switch the clock).
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
Why does
clk_round_ratenot work for you? That will round down to the nearest frequency supported.
clk_round_ratewill round off to nearest supported "ceil" frequency. But we require nearest rounded off "floor" frequency.Then fix the clk framework to do what you want. This doesn't need to be in DT.
Sure. Discussed with clk driver. Will make the required changes in qcom clk driver to fix this.
Will soon publish the new series.
Quoted 7 lines
Rob
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v5 4/12 ARM: dts: qcom: Add clk-rates to sdhc1 & sdhc2
In reply to Ritesh HarjaniAdd msm supported clk-rates for all sdhc nodes.
--- arch/arm/boot/dts/qcom-apq8084.dtsi | 4 ++++ arch/arm/boot/dts/qcom-msm8974.dtsi | 4 ++++ arch/arm64/boot/dts/qcom/msm8916.dtsi | 4 ++++ arch/arm64/boot/dts/qcom/msm8996.dtsi | 2 ++ 4 files changed, 14 insertions(+)
arch/arm/boot/dts/qcom-apq8084.dtsi +4 -0
@@ -434,6 +434,8 @@ interrupt-names = "hc_irq", "pwr_irq"; clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; clock-names = "core", "iface"; + clk-rates = <400000 25000000 50000000 100000000 + 200000000>; status = "disabled"; };
@@ -445,6 +447,8 @@ interrupt-names = "hc_irq", "pwr_irq"; clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; clock-names = "core", "iface"; + clk-rates = <400000 25000000 50000000 100000000 + 200000000>; status = "disabled"; };
arch/arm/boot/dts/qcom-msm8974.dtsi +4 -0
@@ -566,6 +566,8 @@ interrupt-names = "hc_irq", "pwr_irq"; clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; clock-names = "core", "iface"; + clk-rates = <400000 25000000 50000000 100000000 + 200000000>; status = "disabled"; };
@@ -577,6 +579,8 @@ interrupt-names = "hc_irq", "pwr_irq"; clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; clock-names = "core", "iface"; + clk-rates = <400000 25000000 50000000 100000000 + 200000000>; status = "disabled"; };
arch/arm64/boot/dts/qcom/msm8916.dtsi +4 -0
@@ -512,6 +512,8 @@ clocks = <&gcc GCC_SDCC1_APPS_CLK>, <&gcc GCC_SDCC1_AHB_CLK>; clock-names = "core", "iface"; + clk-rates = <400000 25000000 50000000 100000000 + 177770000>; bus-width = <8>; non-removable; status = "disabled";
@@ -527,6 +529,8 @@ clocks = <&gcc GCC_SDCC2_APPS_CLK>, <&gcc GCC_SDCC2_AHB_CLK>; clock-names = "core", "iface"; + clk-rates = <400000 25000000 50000000 100000000 + 200000000>; bus-width = <4>; status = "disabled"; };
arch/arm64/boot/dts/qcom/msm8996.dtsi +2 -0
@@ -410,6 +410,8 @@ clock-names = "iface", "core"; clocks = <&gcc GCC_SDCC2_AHB_CLK>, <&gcc GCC_SDCC2_APPS_CLK>; + clk-rates = <400000 25000000 50000000 100000000 + 200000000>; bus-width = <4>; };
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
PATCH v5 5/12 mmc: sdhci-msm: Add get_min_clock() and get_max_clock() callback
In reply to Ritesh HarjaniThis add get_min_clock() and get_max_clock() callback for sdhci-msm. sdhci-msm min/max clocks may be different hence implement these callbacks.
--- drivers/mmc/host/sdhci-msm.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
drivers/mmc/host/sdhci-msm.c +29 -0
@@ -68,6 +68,7 @@#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c#define TCXO_FREQ 19200000 +#define SDHCI_MSM_MIN_CLOCK 400000#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
@@ -561,6 +562,32 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)return IRQ_HANDLED ; } +static unsigned int sdhci_msm_get_max_clock (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; + +if (msm_host->clk_table) { + count = msm_host->clk_table_sz; +return msm_host->clk_table[count -1 ]; + }else { +return clk_round_rate (msm_host->clk,ULONG_MAX ); + } +} + +static unsigned int sdhci_msm_get_min_clock (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); + +if (msm_host->clk_table) { +return msm_host->clk_table[0 ]; + }else { +return SDHCI_MSM_MIN_CLOCK ; + } +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},
@@ -572,6 +599,8 @@ static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, .set_clock = sdhci_set_clock, + .get_min_clock = sdhci_msm_get_min_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,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Adrian Hunter
maintainerRe: [PATCH v5 05/12] mmc: sdhci-msm: Add get_min_clock() and get_max_clock() callback
Quoted 23 lines · Ritesh Harjani
This add
get_min_clock()andget_max_clock()callback for sdhci-msm. sdhci-msm min/max clocks may be different hence implement these callbacks.Signed-off-by Sahitya Tummala <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)drivers/mmc/host/sdhci-msm.c +1
-0@@ -68,6 +68,7 @@#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c#define TCXO_FREQ 19200000 +#define SDHCI_MSM_MIN_CLOCK 400000#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 571
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +7
-0@@ -561,6 +562,32 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)return IRQ_HANDLED ; } +static unsigned int sdhci_msm_get_max_clock (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; + +if (msm_host->clk_table) {
I don't know if DT allows empty arrays, but if it does, might not msm_host->clk_table_sz be zero here.
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 574
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +3
-0+ count = msm_host->clk_table_sz; +return msm_host->clk_table[count -1 ]; + }else {
'else' is redundant here.
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 584
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+return clk_round_rate (msm_host->clk,ULONG_MAX ); + } +} + +static unsigned int sdhci_msm_get_min_clock (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); + +if (msm_host->clk_table) {
Same about possibility of msm_host->clk_table_sz being zero.
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 586
Quoted 2 lines
drivers/mmc/host/sdhci-msm.c +2
-0+return msm_host->clk_table[0 ]; + }else {
'else' is redundant here.
Quoted 17 lines
drivers/mmc/host/sdhci-msm.c +6
-0+return SDHCI_MSM_MIN_CLOCK ; + } +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},@@ -572,6 +599,8 @@ static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, .set_clock = sdhci_set_clock, + .get_min_clock = sdhci_msm_get_min_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,
Re: [PATCH v5 05/12] mmc: sdhci-msm: Add get_min_clock() and get_max_clock() callback
Hi Adrian,
Quoted 27 lines · Adrian Hunter
Quoted 26 lines · Ritesh Harjani
This add
get_min_clock()andget_max_clock()callback for sdhci-msm. sdhci-msm min/max clocks may be different hence implement these callbacks.Signed-off-by Sahitya Tummala <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)drivers/mmc/host/sdhci-msm.c +1
-0@@ -68,6 +68,7 @@#define CORE_VENDOR_SPEC_CAPABILITIES0 0x11c#define TCXO_FREQ 19200000 +#define SDHCI_MSM_MIN_CLOCK 400000#define CDR_SELEXT_SHIFT 20#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)@@ -561,6 +562,32 @@ static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data)return IRQ_HANDLED ; }
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 571
Quoted 7 lines
drivers/mmc/host/sdhci-msm.c +7
-0+static unsigned int sdhci_msm_get_max_clock (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; + +if (msm_host->clk_table) {I don't know if DT allows empty arrays, but if it does, might not
msm_host->clk_table_szbe zero here.
Sure, I will take care of this as mentioned in the response of previous patch 03.
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 574
Quoted 4 lines
drivers/mmc/host/sdhci-msm.c +3
-0+ count = msm_host->clk_table_sz; +return msm_host->clk_table[count -1 ]; + }else {'else' is redundant here.
Done. Will remove it.
Quoted 13 lines
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 584
Quoted 11 lines
drivers/mmc/host/sdhci-msm.c +10
-0+return clk_round_rate (msm_host->clk,ULONG_MAX ); + } +} + +static unsigned int sdhci_msm_get_min_clock (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); + +if (msm_host->clk_table) {Same about possibility of
msm_host->clk_table_szbeing zero.
Yes, as mentioned. Will take care of this.
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c static irqreturn_t sdhci_msm_pwr_irq(int irq, void *data) · line 586
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +2
-0+return msm_host->clk_table[0 ]; + }else {'else' is redundant here.
Done.
Quoted 24 lines
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +4
-0+return SDHCI_MSM_MIN_CLOCK ; + } +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},drivers/mmc/host/sdhci-msm.c static const struct sdhci_ops sdhci_msm_ops = { · line 606
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +2
-0@@ -572,6 +599,8 @@ static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, .set_clock = sdhci_set_clock, + .get_min_clock = sdhci_msm_get_min_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,--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v5 6/12 mmc: sdhci-msm: Enable few quirks
In reply to Ritesh Harjanisdhc-msm controller needs this SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN & SDHCI_QUIRK2_PRESET_VALUE_BROKEN to be set. Hence setting it.
--- drivers/mmc/host/sdhci-msm.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/mmc/host/sdhci-msm.c +3 -1
@@ -609,7 +609,9 @@ static const struct sdhci_ops sdhci_msm_ops = {static const struct sdhci_pltfm_data sdhci_msm_pdata = { .quirks =SDHCI_QUIRK_BROKEN_CARD_DETECTION |SDHCI_QUIRK_NO_CARD_NO_RESET | -SDHCI_QUIRK_SINGLE_POWER_WRITE , +SDHCI_QUIRK_SINGLE_POWER_WRITE | +SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN , + .quirks2 =SDHCI_QUIRK2_PRESET_VALUE_BROKEN , .ops = &sdhci_msm_ops, };
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
PATCH v5 7/12 mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
In reply to Ritesh Harjanisdhci-msm controller may have different clk-rates for each bus speed mode. Thus implement set_clock callback for sdhci-msm driver.
--- drivers/mmc/host/sdhci-msm.c | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-)
drivers/mmc/host/sdhci-msm.c +109 -1
@@ -84,6 +84,7 @@ struct sdhci_msm_host {struct clk *bus_clk;/* SDHC bus voter clock */ u32 *clk_table;int clk_table_sz; +u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; };
@@ -588,6 +589,113 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) } } +static unsigned int sdhci_msm_get_msm_clk_rate (struct sdhci_host *host, +u32 req_clk) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; +unsigned int sel_clk =-1 ; + +if (!msm_host->clk_table) +return clk_round_rate (msm_host->clk,ULONG_MAX ); + + count = msm_host->clk_table_sz; + +while (count--) { + sel_clk = msm_host->clk_table[count]; +if (req_clk >= sel_clk) +return sel_clk; + } + +return sel_clk; +} + +/** +* __sdhci_msm_set_clock - sdhci_msm clock control. +* +* Description: +* Implement MSM version of sdhci_set_clock. +* This is required since MSM controller does not +* use internal divider and instead directly control +* the GCC clock as per HW recommendation. +**/ +void __sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +u16 clk; +unsigned long timeout; + +/* +* Keep actual_clock as zero - +* - since there is no divider used so no need of having actual_clock. +* - MSM controller uses SDCLK for data timeout calculation. If +* actual_clock is zero, host->clock is taken for calculation. +*/ + host->mmc->actual_clock =0 ; + +sdhci_writew (host,0 ,SDHCI_CLOCK_CONTROL ); + +if (clock ==0 ) +return ; + +/* +* MSM controller do not use clock divider. +* Thus read SDHCI_CLOCK_CONTROL and only enable +* clock with no divider value programmed. +*/ + clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL ); + + clk |=SDHCI_CLOCK_INT_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL ); + +/* Wait max 20 ms */ + timeout =20 ; +while (!((clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL )) + &SDHCI_CLOCK_INT_STABLE )) { +if (timeout ==0 ) { +pr_err ("%s: Internal clock never stabilised.\n" , +mmc_hostname (host->mmc)); +return ; + } + timeout--; +mdelay (1 ); + } + + clk |=SDHCI_CLOCK_CARD_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL ); +} + +static void sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +u32 msm_clock; +int rc; + +if (!clock) + goto out; + +spin_unlock_irq (&host->lock); +if ((clock != msm_host->clk_rate) && msm_host->clk_table) { + msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); + rc =clk_set_rate (msm_host->clk, msm_clock); +if (rc) { +pr_err ("%s: failed to set clock at rate %u, requested clock rate %u\n" , +mmc_hostname (host->mmc), msm_clock, clock); + goto out; + } + msm_host->clk_rate = clock; +pr_debug ("%s: setting clock at rate %lu\n" , +mmc_hostname (host->mmc),clk_get_rate (msm_host->clk)); + } + +spin_lock_irq (&host->lock); +out: +if (!msm_host->clk_table) +return sdhci_set_clock (host, clock); +__sdhci_msm_set_clock (host, clock); +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},
@@ -598,7 +706,7 @@ MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, - .set_clock = sdhci_set_clock, + .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
A message that is not in the archive (sent off-list or not yet received): 1475678440-3525-8-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org
Re: [PATCH v5 07/12] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
Quoted 100 lines · Ritesh Harjani
sdhci-msm controller may have different clk-rates for each bus speed mode. Thus implement
set_clockcallback for sdhci-msm driver.Signed-off-by Sahitya Tummala <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-)drivers/mmc/host/sdhci-msm.c +75
-0@@ -84,6 +84,7 @@ struct sdhci_msm_host {struct clk *bus_clk;/* SDHC bus voter clock */ u32 *clk_table;int clk_table_sz; +u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; };@@ -588,6 +589,113 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) } } +static unsigned int sdhci_msm_get_msm_clk_rate (struct sdhci_host *host, +u32 req_clk) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; +unsigned int sel_clk =-1 ; + +if (!msm_host->clk_table) +return clk_round_rate (msm_host->clk,ULONG_MAX ); + + count = msm_host->clk_table_sz; + +while (count--) { + sel_clk = msm_host->clk_table[count]; +if (req_clk >= sel_clk) +return sel_clk; + } + +return sel_clk; +} + +/** +* __sdhci_msm_set_clock - sdhci_msm clock control. +* +* Description: +* Implement MSM version of sdhci_set_clock. +* This is required since MSM controller does not +* use internal divider and instead directly control +* the GCC clock as per HW recommendation. +**/ +void __sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +u16 clk; +unsigned long timeout; + +/* +* Keep actual_clock as zero - +* - since there is no divider used so no need of having actual_clock. +* - MSM controller uses SDCLK for data timeout calculation. If +* actual_clock is zero, host->clock is taken for calculation. +*/ + host->mmc->actual_clock =0 ; + +sdhci_writew (host,0 ,SDHCI_CLOCK_CONTROL ); + +if (clock ==0 ) +return ; + +/* +* MSM controller do not use clock divider. +* Thus read SDHCI_CLOCK_CONTROL and only enable +* clock with no divider value programmed. +*/ + clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL ); + + clk |=SDHCI_CLOCK_INT_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL ); + +/* Wait max 20 ms */ + timeout =20 ; +while (!((clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL )) + &SDHCI_CLOCK_INT_STABLE )) { +if (timeout ==0 ) { +pr_err ("%s: Internal clock never stabilised.\n" , +mmc_hostname (host->mmc)); +return ; + } + timeout--; +mdelay (1 ); + } + + clk |=SDHCI_CLOCK_CARD_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL );
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 675
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+} + +static void sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +u32 msm_clock; +int rc; + +if (!clock)
Wouldn't you still need to set msm_host->clk_rate = clock in this case
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ goto out; + +spin_unlock_irq (&host->lock); +if ((clock != msm_host->clk_rate) && msm_host->clk_table) { + msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); + rc =clk_set_rate (msm_host->clk, msm_clock); +if (rc) { +pr_err ("%s: failed to set clock at rate %u, requested clock rate %u\n" , +mmc_hostname (host->mmc), msm_clock, clock); + goto out;
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 695
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ } + msm_host->clk_rate = clock; +pr_debug ("%s: setting clock at rate %lu\n" , +mmc_hostname (host->mmc),clk_get_rate (msm_host->clk)); + } + +spin_lock_irq (&host->lock); +out: +if (!msm_host->clk_table) +return sdhci_set_clock (host, clock);
Could put the above 2 lines at the start and then no need to check msm_host->clk_table again.
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +3
-0+__sdhci_msm_set_clock (host, clock); +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},
drivers/mmc/host/sdhci-msm.c MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); · line 712
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +1
-1@@ -598,7 +706,7 @@ MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, - .set_clock = sdhci_set_clock, + .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width,
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Adrian Hunter
maintainerRe: [PATCH v5 07/12] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
Quoted 114 lines · Adrian Hunter
Quoted 100 lines · Ritesh Harjani
sdhci-msm controller may have different clk-rates for each bus speed mode. Thus implement
set_clockcallback for sdhci-msm driver.Signed-off-by Sahitya Tummala <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-)drivers/mmc/host/sdhci-msm.c +75
-0@@ -84,6 +84,7 @@ struct sdhci_msm_host {struct clk *bus_clk;/* SDHC bus voter clock */ u32 *clk_table;int clk_table_sz; +u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; };@@ -588,6 +589,113 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) } } +static unsigned int sdhci_msm_get_msm_clk_rate (struct sdhci_host *host, +u32 req_clk) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; +unsigned int sel_clk =-1 ; + +if (!msm_host->clk_table) +return clk_round_rate (msm_host->clk,ULONG_MAX ); + + count = msm_host->clk_table_sz; + +while (count--) { + sel_clk = msm_host->clk_table[count]; +if (req_clk >= sel_clk) +return sel_clk; + } + +return sel_clk; +} + +/** +* __sdhci_msm_set_clock - sdhci_msm clock control. +* +* Description: +* Implement MSM version of sdhci_set_clock. +* This is required since MSM controller does not +* use internal divider and instead directly control +* the GCC clock as per HW recommendation. +**/ +void __sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +u16 clk; +unsigned long timeout; + +/* +* Keep actual_clock as zero - +* - since there is no divider used so no need of having actual_clock. +* - MSM controller uses SDCLK for data timeout calculation. If +* actual_clock is zero, host->clock is taken for calculation. +*/ + host->mmc->actual_clock =0 ; + +sdhci_writew (host,0 ,SDHCI_CLOCK_CONTROL ); + +if (clock ==0 ) +return ; + +/* +* MSM controller do not use clock divider. +* Thus read SDHCI_CLOCK_CONTROL and only enable +* clock with no divider value programmed. +*/ + clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL ); + + clk |=SDHCI_CLOCK_INT_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL ); + +/* Wait max 20 ms */ + timeout =20 ; +while (!((clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL )) + &SDHCI_CLOCK_INT_STABLE )) { +if (timeout ==0 ) { +pr_err ("%s: Internal clock never stabilised.\n" , +mmc_hostname (host->mmc)); +return ; + } + timeout--; +mdelay (1 ); + } + + clk |=SDHCI_CLOCK_CARD_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL );drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 675
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+} + +static void sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +u32 msm_clock; +int rc; + +if (!clock)Wouldn't you still need to set
msm_host->clk_rate= clock in this case
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 685
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ goto out; + +spin_unlock_irq (&host->lock); +if ((clock != msm_host->clk_rate) && msm_host->clk_table) { + msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); + rc =clk_set_rate (msm_host->clk, msm_clock); +if (rc) { +pr_err ("%s: failed to set clock at rate %u, requested clock rate %u\n" , +mmc_hostname (host->mmc), msm_clock, clock); + goto out;
'goto out' leaves spinlock unlocked
Quoted 32 lines
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 695
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ } + msm_host->clk_rate = clock; +pr_debug ("%s: setting clock at rate %lu\n" , +mmc_hostname (host->mmc),clk_get_rate (msm_host->clk)); + } + +spin_lock_irq (&host->lock); +out: +if (!msm_host->clk_table) +return sdhci_set_clock (host, clock);Could put the above 2 lines at the start and then no need to check
msm_host->clk_tableagain.Quoted 16 lines
drivers/mmc/host/sdhci-msm.c +4
-1+__sdhci_msm_set_clock (host, clock); +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},@@ -598,7 +706,7 @@ MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, - .set_clock = sdhci_set_clock, + .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width,
A message that is not in the archive (sent off-list or not yet received): d35224cf-52e0-5ccc-9596-1c338df41c36-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org
Re: [PATCH v5 07/12] mmc: sdhci-msm: Implement set_clock callback for sdhci-msm
Hi Adrian,
Quoted 106 lines · Adrian Hunter
Quoted 105 lines · Adrian Hunter
Quoted 104 lines · Ritesh Harjani
sdhci-msm controller may have different clk-rates for each bus speed mode. Thus implement
set_clockcallback for sdhci-msm driver.Signed-off-by Sahitya Tummala <[email protected]>Signed-off-by Ritesh Harjani <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 1 deletion(-)drivers/mmc/host/sdhci-msm.c +79
-0@@ -84,6 +84,7 @@ struct sdhci_msm_host {struct clk *bus_clk;/* SDHC bus voter clock */ u32 *clk_table;int clk_table_sz; +u32 clk_rate;struct mmc_host *mmc;bool use_14lpp_dll_reset; };@@ -588,6 +589,113 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) } } +static unsigned int sdhci_msm_get_msm_clk_rate (struct sdhci_host *host, +u32 req_clk) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int count; +unsigned int sel_clk =-1 ; + +if (!msm_host->clk_table) +return clk_round_rate (msm_host->clk,ULONG_MAX ); + + count = msm_host->clk_table_sz; + +while (count--) { + sel_clk = msm_host->clk_table[count]; +if (req_clk >= sel_clk) +return sel_clk; + } + +return sel_clk; +} + +/** +* __sdhci_msm_set_clock - sdhci_msm clock control. +* +* Description: +* Implement MSM version of sdhci_set_clock. +* This is required since MSM controller does not +* use internal divider and instead directly control +* the GCC clock as per HW recommendation. +**/ +void __sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{ +u16 clk; +unsigned long timeout; + +/* +* Keep actual_clock as zero - +* - since there is no divider used so no need of having actual_clock. +* - MSM controller uses SDCLK for data timeout calculation. If +* actual_clock is zero, host->clock is taken for calculation. +*/ + host->mmc->actual_clock =0 ; + +sdhci_writew (host,0 ,SDHCI_CLOCK_CONTROL ); + +if (clock ==0 ) +return ; + +/* +* MSM controller do not use clock divider. +* Thus read SDHCI_CLOCK_CONTROL and only enable +* clock with no divider value programmed. +*/ + clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL ); + + clk |=SDHCI_CLOCK_INT_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL ); + +/* Wait max 20 ms */ + timeout =20 ; +while (!((clk =sdhci_readw (host,SDHCI_CLOCK_CONTROL )) + &SDHCI_CLOCK_INT_STABLE )) { +if (timeout ==0 ) { +pr_err ("%s: Internal clock never stabilised.\n" , +mmc_hostname (host->mmc)); +return ; + } + timeout--; +mdelay (1 ); + } + + clk |=SDHCI_CLOCK_CARD_EN ; +sdhci_writew (host, clk,SDHCI_CLOCK_CONTROL ); +} + +static void sdhci_msm_set_clock (struct sdhci_host *host,unsigned int clock) +{
Quoted 8 lines
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 675
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c +6
-0+struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +u32 msm_clock; +int rc; + +if (!clock)Wouldn't you still need to set
msm_host->clk_rate= clock in this case
Yes. Will do that.
Quoted 13 lines
Quoted 11 lines
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 685
Quoted 11 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ goto out; + +spin_unlock_irq (&host->lock); +if ((clock != msm_host->clk_rate) && msm_host->clk_table) { + msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); + rc =clk_set_rate (msm_host->clk, msm_clock); +if (rc) { +pr_err ("%s: failed to set clock at rate %u, requested clock rate %u\n" , +mmc_hostname (host->mmc), msm_clock, clock); + goto out;'goto out' leaves spinlock unlocked
Thanks for catching it.
Quoted 14 lines
Quoted 14 lines
drivers/mmc/host/sdhci-msm.c static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) · line 695
Quoted 11 lines
drivers/mmc/host/sdhci-msm.c +10
-0+ } + msm_host->clk_rate = clock; +pr_debug ("%s: setting clock at rate %lu\n" , +mmc_hostname (host->mmc),clk_get_rate (msm_host->clk)); + } + +spin_lock_irq (&host->lock); +out: +if (!msm_host->clk_table) +return sdhci_set_clock (host, clock);Could put the above 2 lines at the start and then no need to check
msm_host->clk_tableagain.
I wanted to make this change, but then may be because of later patches which modifies sdhci_msm_set_clock for UHS modes, I did not move this to top. I reviewed that part again, I will make the necessary changes.
Thanks.
Quoted 20 lines
Quoted 19 lines
Quoted 9 lines
drivers/mmc/host/sdhci-msm.c +3
-0+__sdhci_msm_set_clock (host, clock); +} +static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible ="qcom,sdhci-msm-v4" }, {},drivers/mmc/host/sdhci-msm.c MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match); · line 712
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +1
-1@@ -598,7 +706,7 @@ MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);static const struct sdhci_ops sdhci_msm_ops = { .platform_execute_tuning = sdhci_msm_execute_tuning, .reset = sdhci_reset, - .set_clock = sdhci_set_clock, + .set_clock = sdhci_msm_set_clock, .get_min_clock = sdhci_msm_get_min_clock, .get_max_clock = sdhci_msm_get_max_clock, .set_bus_width = sdhci_set_bus_width,
Quoted 5 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v5 8/12 mmc: sdhci-msm: Add clock changes for DDR mode.
In reply to Ritesh HarjaniSDHC MSM controller need 2x clock for MCLK at GCC. Hence make required changes to have 2x clock for DDR timing modes.
--- drivers/mmc/host/sdhci-msm.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
drivers/mmc/host/sdhci-msm.c +20 -5
@@ -669,6 +669,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) {struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +struct mmc_ios curr_ios = host->mmc->ios;u32 msm_clock;int rc;
@@ -676,15 +677,29 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) goto out;spin_unlock_irq (&host->lock); -if ((clock != msm_host->clk_rate) && msm_host->clk_table) { - msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); +if (((curr_ios.timing ==MMC_TIMING_UHS_DDR50 ) || + (curr_ios.timing ==MMC_TIMING_MMC_DDR52 ) || + (curr_ios.timing ==MMC_TIMING_MMC_HS400 )) && + msm_host->clk_table) { +/* +* The SDHC requires internal clock frequency to be double the +* actual clock that will be set for DDR mode. The controller +* uses the faster clock(100/400MHz) for some of its parts and +* send the actual required clock (50/200MHz) to the card. +*/ + clock *=2 ; + } + msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); + +if ((msm_clock != msm_host->clk_rate) && msm_host->clk_table) { rc =clk_set_rate (msm_host->clk, msm_clock);if (rc) { -pr_err ("%s: failed to set clock at rate %u, requested clock rate %u\n" , -mmc_hostname (host->mmc), msm_clock, clock); +pr_err ("%s: failed to set clock at rate %u at timing %d\n" , +mmc_hostname (host->mmc), msm_clock, + curr_ios.timing); goto out; } - msm_host->clk_rate = clock; + msm_host->clk_rate = msm_clock;pr_debug ("%s: setting clock at rate %lu\n" ,mmc_hostname (host->mmc),clk_get_rate (msm_host->clk)); }
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
A message that is not in the archive (sent off-list or not yet received): 1475678440-3525-9-git-send-email-riteshh-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org
Adrian Hunter
Acked-byRe: [PATCH v5 08/12] mmc: sdhci-msm: Add clock changes for DDR mode.
Quoted 5 lines · Ritesh Harjani
SDHC MSM controller need 2x clock for MCLK at GCC. Hence make required changes to have 2x clock for DDR timing modes.
Signed-off-by Ritesh Harjani <[email protected]>
Quoted 42 lines
--- drivers/mmc/host/sdhci-msm.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)drivers/mmc/host/sdhci-msm.c +16
-4@@ -669,6 +669,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) {struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +struct mmc_ios curr_ios = host->mmc->ios;u32 msm_clock;int rc;@@ -676,15 +677,29 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) goto out;spin_unlock_irq (&host->lock); -if ((clock != msm_host->clk_rate) && msm_host->clk_table) { - msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); +if (((curr_ios.timing ==MMC_TIMING_UHS_DDR50 ) || + (curr_ios.timing ==MMC_TIMING_MMC_DDR52 ) || + (curr_ios.timing ==MMC_TIMING_MMC_HS400 )) && + msm_host->clk_table) { +/* +* The SDHC requires internal clock frequency to be double the +* actual clock that will be set for DDR mode. The controller +* uses the faster clock(100/400MHz) for some of its parts and +* send the actual required clock (50/200MHz) to the card. +*/ + clock *=2 ; + } + msm_clock =sdhci_msm_get_msm_clk_rate (host, clock); + +if ((msm_clock != msm_host->clk_rate) && msm_host->clk_table) { rc =clk_set_rate (msm_host->clk, msm_clock);if (rc) { -pr_err ("%s: failed to set clock at rate %u, requested clock rate %u\n" , -mmc_hostname (host->mmc), msm_clock, clock);
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 705
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +4
-1+pr_err ("%s: failed to set clock at rate %u at timing %d\n" , +mmc_hostname (host->mmc), msm_clock, + curr_ios.timing); goto out; } - msm_host->clk_rate = clock; + msm_host->clk_rate = msm_clock;pr_debug ("%s: setting clock at rate %lu\n" ,mmc_hostname (host->mmc),clk_get_rate (msm_host->clk)); }
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v5 9/12 arm64: dts: qcom: msm8916: Add ddr support to sdhc1
In reply to Ritesh HarjaniThis adds mmc-ddr-1_8v support to DT for sdhc1 of msm8916.
--- arch/arm64/boot/dts/qcom/msm8916.dtsi | 1 + 1 file changed, 1 insertion(+)
arch/arm64/boot/dts/qcom/msm8916.dtsi +1 -0
@@ -514,6 +514,7 @@ clock-names = "core", "iface"; clk-rates = <400000 25000000 50000000 100000000 177770000>; + mmc-ddr-1_8v; bus-width = <8>; non-removable; status = "disabled";
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
PATCH v5 12/12 sdhci: sdhci-msm: update dll configuration
In reply to Ritesh HarjaniThe newer msm sdhci's cores use a different DLL hardware for HS400. Update the configuration and calibration of the newer DLL block.
The HS400 DLL block used previously is CDC LP 533 and requires programming multiple registers and waiting for configuration to complete and then enable it. It has about 18 register writes and two register reads.
The newer HS400 DLL block is SDC4 DLL and requires two register writes for configuration and one register read to confirm that it is initialized. There is an additional register write to enable the power save mode for SDC4 DLL block.
--- drivers/mmc/host/sdhci-msm.c | 141 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 14 deletions(-)
drivers/mmc/host/sdhci-msm.c +127 -14
@@ -19,6 +19,7 @@#include <linux/delay.h> #include <linux/mmc/mmc.h> #include <linux/slab.h> +#include <linux/iopoll.h> #include "sdhci-pltfm.h"
@@ -50,6 +51,7 @@#define INT_MASK 0xf#define MAX_PHASES 16#define CORE_DLL_LOCK BIT(7) +#define CORE_DDR_DLL_LOCK BIT(11)#define CORE_DLL_EN BIT(16)#define CORE_CDR_EN BIT(17)#define CORE_CK_OUT_EN BIT(18)
@@ -61,6 +63,7 @@#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)
@@ -99,6 +102,11 @@#define CORE_DDR_200_CFG 0x184#define CORE_CDC_T4_DLY_SEL BIT(0)#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
@@ -127,6 +135,7 @@ struct sdhci_msm_host {bool tuning_done;bool calibration_done;u8 saved_tuning_phase; +bool use_cdclp533; };/* Platform specific tuning */
@@ -460,7 +469,7 @@ static int sdhci_msm_cdclp533_calibration(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 wait_cnt, config; +u32 config, calib_done;int ret;pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__);
@@ -552,18 +561,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)wmb ();/* drain writebuffer */ /* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ - wait_cnt =50 ; -while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) - &CORE_CALIBRATION_DONE )) { -/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ -if (--wait_cnt ==0 ) { -pr_err ("%s: %s: CDC Calibration was not completed\n" , + ret =readl_poll_timeout (host->ioaddr +CORE_CSR_CDC_STATUS0 , + calib_done, (calib_done &CORE_CALIBRATION_DONE ),1 ,50 ); + +if (ret == -ETIMEDOUT ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" ,mmc_hostname (host->mmc), __func__); - ret = -ETIMEDOUT ; - goto out; - } -/* wait for 1us before polling again */ -udelay (1 ); + goto out; }/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */
@@ -586,6 +590,86 @@ out:return ret; } +static int sdhci_msm_cm_dll_sdc4_calibration (struct sdhci_host *host) +{ +u32 dll_status, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* 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 ); + +/* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG_2 ); + config |=CORE_DDR_CAL_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG_2 ); + +/* Poll on DDR_DLL_LOCK bit in CORE_DLL_STATUS to be set */ + ret =readl_poll_timeout (host->ioaddr +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" , +mmc_hostname (host->mmc), __func__); + goto out; + } + +/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC3 ); + config |=CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC3 ); +wmb ();/* drain writebuffer */ +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} + +static int sdhci_msm_hs400_dll_calibration (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int ret; +u32 config; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); +if (msm_host->use_cdclp533) +/* Calibrate CDCLP533 DLL HW */ + ret =sdhci_msm_cdclp533_calibration (host); +else +/* Calibrate CM_DLL_SDC4 HW */ + ret =sdhci_msm_cm_dll_sdc4_calibration (host); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;
@@ -737,7 +821,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,if (host->clock >CORE_FREQ_100MHZ && msm_host->tuning_done && !msm_host->calibration_done && (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) -if (!sdhci_msm_cdclp533_calibration (host)) +if (!sdhci_msm_hs400_dll_calibration (host)) msm_host->calibration_done = true;spin_lock_irq (&host->lock);
@@ -883,7 +967,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios; -u32 msm_clock, config; +u32 msm_clock, config, dll_lock;int rc;if (!clock)
@@ -942,7 +1026,29 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) config |=CORE_HC_SELECT_IN_EN ;writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); } +if (!msm_host->clk_rate && !msm_host->use_cdclp533) { +/* +* Poll on DLL_LOCK and DDR_DLL_LOCK bits in +* CORE_DLL_STATUS to be set. This should get set +* with in 15 us at 200 MHz. +*/ + rc =readl_poll_timeout (host->ioaddr +CORE_DLL_STATUS , + dll_lock, (dll_lock & (CORE_DLL_LOCK | +CORE_DDR_DLL_LOCK )),10 ,1000 ); +if (rc == -ETIMEDOUT ) +pr_err ("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n" , +mmc_hostname (host->mmc), dll_lock); + } }else { +if (!msm_host->use_cdclp533) { +/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + config =readl_relaxed (host->ioaddr + +CORE_VENDOR_SPEC3 ); + config &= ~CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr + +CORE_VENDOR_SPEC3 ); + } +/* Select the default clock (free running MCLK) */ config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); config &= ~CORE_HC_MCLK_SEL_MASK ;
@@ -1172,6 +1278,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->use_14lpp_dll_reset = true;/* +* SDCC 5 controller with major version 1, minor version 0x34 and later +* with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. +*/ +if ((core_major ==1 ) && (core_minor <0x34 )) + msm_host->use_cdclp533 = true; + +/* * Support for some capabilities is not advertised by newer * controller versions and must be explicitly enabled. */
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project.
Adrian Hunter
maintainerRe: [PATCH v5 12/12] sdhci: sdhci-msm: update dll configuration
Quoted 77 lines · Ritesh Harjani
The newer msm sdhci's cores use a different DLL hardware for HS400. Update the configuration and calibration of the newer DLL block.
The HS400 DLL block used previously is CDC LP 533 and requires programming multiple registers and waiting for configuration to complete and then enable it. It has about 18 register writes and two register reads.
The newer HS400 DLL block is SDC4 DLL and requires two register writes for configuration and one register read to confirm that it is initialized. There is an additional register write to enable the power save mode for SDC4 DLL block.
Signed-off-by Ritesh Harjani <[email protected]>Signed-off-by Krishna Konda <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 141 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 14 deletions(-)drivers/mmc/host/sdhci-msm.c +10
-1@@ -19,6 +19,7 @@#include <linux/delay.h> #include <linux/mmc/mmc.h> #include <linux/slab.h> +#include <linux/iopoll.h> #include "sdhci-pltfm.h" @@ -50,6 +51,7 @@#define INT_MASK 0xf#define MAX_PHASES 16#define CORE_DLL_LOCK BIT(7) +#define CORE_DDR_DLL_LOCK BIT(11)#define CORE_DLL_EN BIT(16)#define CORE_CDR_EN BIT(17)#define CORE_CK_OUT_EN BIT(18)@@ -61,6 +63,7 @@#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)@@ -99,6 +102,11 @@#define CORE_DDR_200_CFG 0x184#define CORE_CDC_T4_DLY_SEL BIT(0)#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@@ -127,6 +135,7 @@ struct sdhci_msm_host {bool tuning_done;bool calibration_done;u8 saved_tuning_phase; +bool use_cdclp533; };/* Platform specific tuning */ @@ -460,7 +469,7 @@ static int sdhci_msm_cdclp533_calibration(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 wait_cnt, config; +u32 config, calib_done;int ret;pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__);
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) · line 564
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +1
-6@@ -552,18 +561,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)wmb ();/* drain writebuffer */ /* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ - wait_cnt =50 ; -while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) - &CORE_CALIBRATION_DONE )) { -/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ -if (--wait_cnt ==0 ) { - pr_err("%s: %s: CDC Calibration was not completed\n", + ret = readl_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0,
This code was added in a previous patch, so it would make more sense to make it use readl_poll_timeout in the first place. Was there a reason to use readl_poll_timeout instead of readl_relaxed_poll_timeout()?
Quoted 31 lines
drivers/mmc/host/sdhci-msm.c +18
-5+ calib_done, (calib_done & CORE_CALIBRATION_DONE), 1, 50); + +if (ret == -ETIMEDOUT ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" ,mmc_hostname (host->mmc), __func__); - ret = -ETIMEDOUT ; - goto out; - } -/* wait for 1us before polling again */ -udelay (1 ); + goto out; }/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ @@ -586,6 +590,86 @@ out:return ret; } +static int sdhci_msm_cm_dll_sdc4_calibration (struct sdhci_host *host) +{ +u32 dll_status, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* 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.
drivers/mmc/host/sdhci-msm.c out: · line 615
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+*/ +writel_relaxed (DDR_CONFIG_POR_VAL , host->ioaddr +CORE_DDR_CONFIG ); + +/* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG_2 ); + config |=CORE_DDR_CAL_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG_2 ); + +/* Poll on DDR_DLL_LOCK bit in CORE_DLL_STATUS to be set */ + ret = readl_poll_timeout(host->ioaddr + CORE_DLL_STATUS,
Was there a reason to use readl_poll_timeout instead of readl_relaxed_poll_timeout()?
Quoted 38 lines
drivers/mmc/host/sdhci-msm.c +38
-0+ 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" , +mmc_hostname (host->mmc), __func__); + goto out; + } + +/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC3 ); + config |=CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC3 ); +wmb ();/* drain writebuffer */ +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} + +static int sdhci_msm_hs400_dll_calibration (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int ret; +u32 config; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase);
drivers/mmc/host/sdhci-msm.c out: · line 663
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +10
-0+if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG ); + config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); +if (msm_host->use_cdclp533) +/* Calibrate CDCLP533 DLL HW */ + ret =sdhci_msm_cdclp533_calibration (host);
sdhci_msm_cdclp533_calibration() does some of the steps above all over again. Is that intended?
Quoted 31 lines
drivers/mmc/host/sdhci-msm.c +11
-2+else +/* Calibrate CM_DLL_SDC4 HW */ + ret =sdhci_msm_cm_dll_sdc4_calibration (host); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;@@ -737,7 +821,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,if (host->clock >CORE_FREQ_100MHZ && msm_host->tuning_done && !msm_host->calibration_done && (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) -if (!sdhci_msm_cdclp533_calibration (host)) +if (!sdhci_msm_hs400_dll_calibration (host)) msm_host->calibration_done = true;spin_lock_irq (&host->lock);@@ -883,7 +967,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios; -u32 msm_clock, config; +u32 msm_clock, config, dll_lock;int rc;if (!clock)
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 1033
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +5
-0@@ -942,7 +1026,29 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) config |=CORE_HC_SELECT_IN_EN ;writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); } +if (!msm_host->clk_rate && !msm_host->use_cdclp533) { +/* +* Poll on DLL_LOCK and DDR_DLL_LOCK bits in +* CORE_DLL_STATUS to be set. This should get set +* with in 15 us at 200 MHz.
'with in' -> 'within'
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 1035
Quoted 2 lines
drivers/mmc/host/sdhci-msm.c +2
-0+*/ + rc = readl_poll_timeout(host->ioaddr + CORE_DLL_STATUS,
Was there a reason to use readl_poll_timeout instead of readl_relaxed_poll_timeout()?
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 1037
Quoted 2 lines
drivers/mmc/host/sdhci-msm.c +2
-0+ dll_lock, (dll_lock & (CORE_DLL_LOCK | + CORE_DDR_DLL_LOCK)), 10, 1000);
The comment says 'DLL_LOCK and DDR_DLL_LOCK' but the logic looks 'DLL_LOCK or DDR_DLL_LOCK'
Quoted 32 lines
drivers/mmc/host/sdhci-msm.c +20
-0+if (rc == -ETIMEDOUT ) +pr_err ("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n" , +mmc_hostname (host->mmc), dll_lock); + } }else { +if (!msm_host->use_cdclp533) { +/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + config =readl_relaxed (host->ioaddr + +CORE_VENDOR_SPEC3 ); + config &= ~CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr + +CORE_VENDOR_SPEC3 ); + } +/* Select the default clock (free running MCLK) */ config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); config &= ~CORE_HC_MCLK_SEL_MASK ;@@ -1172,6 +1278,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->use_14lpp_dll_reset = true;/* +* SDCC 5 controller with major version 1, minor version 0x34 and later +* with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. +*/ +if ((core_major ==1 ) && (core_minor <0x34 )) + msm_host->use_cdclp533 = true; + +/* * Support for some capabilities is not advertised by newer * controller versions and must be explicitly enabled. */
Re: [PATCH v5 12/12] sdhci: sdhci-msm: update dll configuration
Hi Adrian,
Quoted 80 lines · Adrian Hunter
Quoted 79 lines · Ritesh Harjani
The newer msm sdhci's cores use a different DLL hardware for HS400. Update the configuration and calibration of the newer DLL block.
The HS400 DLL block used previously is CDC LP 533 and requires programming multiple registers and waiting for configuration to complete and then enable it. It has about 18 register writes and two register reads.
The newer HS400 DLL block is SDC4 DLL and requires two register writes for configuration and one register read to confirm that it is initialized. There is an additional register write to enable the power save mode for SDC4 DLL block.
Signed-off-by Ritesh Harjani <[email protected]>Signed-off-by Krishna Konda <[email protected]>--- drivers/mmc/host/sdhci-msm.c | 141 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 14 deletions(-)drivers/mmc/host/sdhci-msm.c +10
-1@@ -19,6 +19,7 @@#include <linux/delay.h> #include <linux/mmc/mmc.h> #include <linux/slab.h> +#include <linux/iopoll.h> #include "sdhci-pltfm.h" @@ -50,6 +51,7 @@#define INT_MASK 0xf#define MAX_PHASES 16#define CORE_DLL_LOCK BIT(7) +#define CORE_DDR_DLL_LOCK BIT(11)#define CORE_DLL_EN BIT(16)#define CORE_CDR_EN BIT(17)#define CORE_CK_OUT_EN BIT(18)@@ -61,6 +63,7 @@#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)@@ -99,6 +102,11 @@#define CORE_DDR_200_CFG 0x184#define CORE_CDC_T4_DLY_SEL BIT(0)#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@@ -127,6 +135,7 @@ struct sdhci_msm_host {bool tuning_done;bool calibration_done;u8 saved_tuning_phase; +bool use_cdclp533; };/* Platform specific tuning */ @@ -460,7 +469,7 @@ static int sdhci_msm_cdclp533_calibration(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 wait_cnt, config; +u32 config, calib_done;int ret;pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__);@@ -552,18 +561,13 @@ static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host)wmb ();/* drain writebuffer */
Quoted 12 lines
drivers/mmc/host/sdhci-msm.c static int sdhci_msm_cdclp533_calibration(struct sdhci_host *host) · line 564
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c +1
-6/* Poll on CALIBRATION_DONE field in CORE_CSR_CDC_STATUS0 to be 1 */ - wait_cnt =50 ; -while (!(readl_relaxed (host->ioaddr +CORE_CSR_CDC_STATUS0 ) - &CORE_CALIBRATION_DONE )) { -/* max. wait for 50us sec for CALIBRATION_DONE bit to be set */ -if (--wait_cnt ==0 ) { - pr_err("%s: %s: CDC Calibration was not completed\n", + ret = readl_poll_timeout(host->ioaddr + CORE_CSR_CDC_STATUS0,This code was added in a previous patch, so it would make more sense to make it use
readl_poll_timeoutin the first place. Was there a reason to usereadl_poll_timeoutinstead ofreadl_relaxed_poll_timeout()?
Sure will make the change in previous patch. Thanks for pointing out about readl_relaxed_poll_timeout. I think I only missed it, will check once again.
Quoted 37 lines
Quoted 37 lines
drivers/mmc/host/sdhci-msm.c +23
-5+ calib_done, (calib_done & CORE_CALIBRATION_DONE), 1, 50); + +if (ret == -ETIMEDOUT ) { +pr_err ("%s: %s: CDC Calibration was not completed\n" ,mmc_hostname (host->mmc), __func__); - ret = -ETIMEDOUT ; - goto out; - } -/* wait for 1us before polling again */ -udelay (1 ); + goto out; }/* Verify CDC_ERROR_CODE field in CORE_CSR_CDC_STATUS0 is 0 */ @@ -586,6 +590,86 @@ out:return ret; } +static int sdhci_msm_cm_dll_sdc4_calibration (struct sdhci_host *host) +{ +u32 dll_status, config; +int ret; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* 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 ); + +/* Write 1 to DDR_CAL_EN field in CORE_DLL_CONFIG_2 */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG_2 );
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c out: · line 615
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c +5
-0+ config |=CORE_DDR_CAL_EN ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG_2 ); + +/* Poll on DDR_DLL_LOCK bit in CORE_DLL_STATUS to be set */ + ret = readl_poll_timeout(host->ioaddr + CORE_DLL_STATUS,Was there a reason to use
readl_poll_timeoutinstead ofreadl_relaxed_poll_timeout()?
Sure will check it.
Quoted 44 lines
Quoted 44 lines
drivers/mmc/host/sdhci-msm.c +43
-0+ 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" , +mmc_hostname (host->mmc), __func__); + goto out; + } + +/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC3 ); + config |=CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC3 ); +wmb ();/* drain writebuffer */ +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} + +static int sdhci_msm_hs400_dll_calibration (struct sdhci_host *host) +{ +struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host); +struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host); +int ret; +u32 config; + +pr_debug ("%s: Enter %s\n" ,mmc_hostname (host->mmc), __func__); + +/* +* Retuning in HS400 (DDR mode) will fail, just reset the +* tuning block and restore the saved tuning phase. +*/ + ret =msm_init_cm_dll (host); +if (ret) + goto out; + +/* Set the selected phase in delay line hw block */ + ret =msm_config_cm_dll_phase (host, msm_host->saved_tuning_phase); +if (ret) + goto out; + +/* Write 1 to CMD_DAT_TRACK_SEL field in DLL_CONFIG */ + config =readl_relaxed (host->ioaddr +CORE_DLL_CONFIG );
Quoted 8 lines
drivers/mmc/host/sdhci-msm.c out: · line 663
Quoted 5 lines
drivers/mmc/host/sdhci-msm.c +5
-0+ config |=CORE_CMD_DAT_TRACK_SEL ; +writel_relaxed (config, host->ioaddr +CORE_DLL_CONFIG ); +if (msm_host->use_cdclp533) +/* Calibrate CDCLP533 DLL HW */ + ret =sdhci_msm_cdclp533_calibration (host);
sdhci_msm_cdclp533_calibration()does some of the steps above all over again. Is that intended?
Yes, this was as per the HW sequence.
Quoted 31 lines
Quoted 31 lines
drivers/mmc/host/sdhci-msm.c +11
-2+else +/* Calibrate CM_DLL_SDC4 HW */ + ret =sdhci_msm_cm_dll_sdc4_calibration (host); +out: +pr_debug ("%s: Exit %s, ret:%d\n" ,mmc_hostname (host->mmc), + __func__, ret); +return ret; +} +static int sdhci_msm_execute_tuning (struct sdhci_host *host,u32 opcode) {int tuning_seq_cnt =3 ;@@ -737,7 +821,7 @@ static void sdhci_msm_set_uhs_signaling(struct sdhci_host *host,if (host->clock >CORE_FREQ_100MHZ && msm_host->tuning_done && !msm_host->calibration_done && (mmc->ios.timing ==MMC_TIMING_MMC_HS400 )) -if (!sdhci_msm_cdclp533_calibration (host)) +if (!sdhci_msm_hs400_dll_calibration (host)) msm_host->calibration_done = true;spin_lock_irq (&host->lock);@@ -883,7 +967,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)struct sdhci_pltfm_host *pltfm_host =sdhci_priv (host);struct sdhci_msm_host *msm_host =sdhci_pltfm_priv (pltfm_host);struct mmc_ios curr_ios = host->mmc->ios; -u32 msm_clock, config; +u32 msm_clock, config, dll_lock;int rc;
Quoted 12 lines
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 1033
Quoted 10 lines
drivers/mmc/host/sdhci-msm.c +5
-0if (!clock)@@ -942,7 +1026,29 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) config |=CORE_HC_SELECT_IN_EN ;writel_relaxed (config, host->ioaddr +CORE_VENDOR_SPEC ); } +if (!msm_host->clk_rate && !msm_host->use_cdclp533) { +/* +* Poll on DLL_LOCK and DDR_DLL_LOCK bits in +* CORE_DLL_STATUS to be set. This should get set +* with in 15 us at 200 MHz. 'with in' -> 'within'
Done.
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 1035
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +2
-0+*/ + rc = readl_poll_timeout(host->ioaddr + CORE_DLL_STATUS,Was there a reason to use
readl_poll_timeoutinstead ofreadl_relaxed_poll_timeout()?
Sure will check it.
Quoted 6 lines
drivers/mmc/host/sdhci-msm.c static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) · line 1037
Quoted 3 lines
drivers/mmc/host/sdhci-msm.c +2
-0+ dll_lock, (dll_lock & (CORE_DLL_LOCK | + CORE_DDR_DLL_LOCK)), 10, 1000);The comment says '
DLL_LOCKandDDR_DLL_LOCK' but the logic looks 'DLL_LOCKorDDR_DLL_LOCK'
Ok, I will fix the comment.
will double confirm the HW spec as well.
Quoted 34 lines
Quoted 33 lines
drivers/mmc/host/sdhci-msm.c +20
-0+if (rc == -ETIMEDOUT ) +pr_err ("%s: Unable to get DLL_LOCK/DDR_DLL_LOCK, dll_status: 0x%08x\n" , +mmc_hostname (host->mmc), dll_lock); + } }else { +if (!msm_host->use_cdclp533) { +/* set CORE_PWRSAVE_DLL bit in CORE_VENDOR_SPEC3 */ + config =readl_relaxed (host->ioaddr + +CORE_VENDOR_SPEC3 ); + config &= ~CORE_PWRSAVE_DLL ; +writel_relaxed (config, host->ioaddr + +CORE_VENDOR_SPEC3 ); + } +/* Select the default clock (free running MCLK) */ config =readl_relaxed (host->ioaddr +CORE_VENDOR_SPEC ); config &= ~CORE_HC_MCLK_SEL_MASK ;@@ -1172,6 +1278,13 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->use_14lpp_dll_reset = true;/* +* SDCC 5 controller with major version 1, minor version 0x34 and later +* with HS 400 mode support will use CM DLL instead of CDC LP 533 DLL. +*/ +if ((core_major ==1 ) && (core_minor <0x34 )) + msm_host->use_cdclp533 = true; + +/* * Support for some capabilities is not advertised by newer * controller versions and must be explicitly enabled. */