Re: [PATCH v2 1/3] mmc: rockchip_sdhci: add phy and clock config for rk3399
On 2021/6/28 17:19, Yifeng Zhao wrote: Add clock, phy and other configuration, it is convenient to support new controller. Here a short summary of the changes: - Add mmc_of_parse to parse dts config. - Remove OF_PLATDATA related code. - Reorder header inclusion. - Add phy ops. One more generic question, could phy part be moved to a phy driver? Regards, Peng. - add ops set_ios_post to modify the parameters of phy when the clock changes. - Add execute tuning api for hs200 tuning Signed-off-by: Yifeng Zhao --- Changes in v2: - Add mmc_of_parse to parse dts config. - Used read_poll_timeout api to check dll lock status - Add execute tuning api for hs200 tuning drivers/mmc/rockchip_sdhci.c | 310 +++ 1 file changed, 274 insertions(+), 36 deletions(-) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index d95f8b2a15..2973911446 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -6,90 +6,319 @@ */ #include +#include #include +#include #include +#include #include #include +#include #include #include +#include "mmc_private.h" #include -#include +#include +#include +#include /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 40 +#define KHz(1000) +#define MHz(1000 * KHz) +#define SDHCI_TUNING_LOOP_COUNT40 + +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_DLLRDY_MASK0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE0x1 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_DLL_LOCK_WO_TMOUT(x) \ + x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ + PHYCTRL_DLLRDY_DONE) struct rockchip_sdhc_plat { -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct dtd_rockchip_rk3399_sdhci_5_1 dtplat; -#endif struct mmc_config cfg; struct mmc mmc; }; +struct rockchip_emmc_phy { + u32 emmcphy_con[7]; + u32 reserved; + u32 emmcphy_status; +}; + struct rockchip_sdhc { struct sdhci_host host; + struct udevice *dev; void *base; + struct rockchip_emmc_phy *phy; + struct clk emmc_clk; +}; + +struct sdhci_data { + int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock); + int (*emmc_phy_init)(struct udevice *dev); + int (*get_phy)(struct udevice *dev); +}; + +static int rk3399_emmc_phy_init(struct udevice *dev) +{ + return 0; +} + +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) +{ + u32 caldone, dllrdy, freqsel; + + writel(RK_CLRSETBITS(7 << 4, 0), >emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 11, 1 << 11), >emmcphy_con[0]); + writel(RK_CLRSETBITS(0xf << 7, 6 << 7), >emmcphy_con[0]); + + /* +* According to the user manual, calpad calibration +* cycle takes more than 2us without the minimal recommended +* value, so we may need a little margin here +*/ + udelay(3); + writel(RK_CLRSETBITS(1, 1), >emmcphy_con[6]); + + /* +* According to the user manual, it asks driver to +* wait 5us for calpad busy trimming. But it seems that +* 5us of caldone isn't enough for all cases. +*/ + udelay(500); + caldone = readl(>emmcphy_status); + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; + if (caldone != PHYCTRL_CALDONE_DONE) { + printf("%s: caldone timeout.\n", __func__); + return; + } + + /* Set the frequency of the DLL operation */ + if (clock < 75 * MHz) + freqsel = PHYCTRL_FREQSEL_50M; + else if (clock < 125 * MHz) + freqsel = PHYCTRL_FREQSEL_100M; + else if (clock < 175 * MHz) + freqsel = PHYCTRL_FREQSEL_150M; + else + freqsel = PHYCTRL_FREQSEL_200M; + + /* Set the frequency of the DLL operation */ + writel(RK_CLRSETBITS(3 << 12, freqsel << 12), >emmcphy_con[0]); + writel(RK_CLRSETBITS(1 << 1, 1 << 1), >emmcphy_con[6]); + + read_poll_timeout(readl, >emmcphy_status, dllrdy, + PHYCTRL_DLL_LOCK_WO_TMOUT(dllrdy), 1, 5000); +} + +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) +{ + writel(RK_CLRSETBITS(1, 0), >emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 1, 0), >emmcphy_con[6]); +} + +static int rk3399_emmc_get_phy(struct udevice *dev) +{ + struct rockchip_sdhc *priv = dev_get_priv(dev); + + ofnode phy_node; + void *grf_base; + u32 grf_phy_offset, phandle; + + phandle = dev_read_u32_default(dev, "phys", 0); +
Re: [PATCH v2 1/3] mmc: rockchip_sdhci: add phy and clock config for rk3399
On 2021/6/28 17:19, Yifeng Zhao wrote: Add clock, phy and other configuration, it is convenient to support new controller. Here a short summary of the changes: - Add mmc_of_parse to parse dts config. - Remove OF_PLATDATA related code. - Reorder header inclusion. - Add phy ops. - add ops set_ios_post to modify the parameters of phy when the clock changes. - Add execute tuning api for hs200 tuning Signed-off-by: Yifeng Zhao --- Changes in v2: - Add mmc_of_parse to parse dts config. - Used read_poll_timeout api to check dll lock status - Add execute tuning api for hs200 tuning drivers/mmc/rockchip_sdhci.c | 310 +++ 1 file changed, 274 insertions(+), 36 deletions(-) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index d95f8b2a15..2973911446 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -6,90 +6,319 @@ */ #include +#include #include +#include #include +#include #include #include +#include #include #include +#include "mmc_private.h" #include -#include +#include +#include +#include /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 40 +#define KHz(1000) +#define MHz(1000 * KHz) +#define SDHCI_TUNING_LOOP_COUNT40 + +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_DLLRDY_MASK0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE0x1 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_DLL_LOCK_WO_TMOUT(x) \ + x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ + PHYCTRL_DLLRDY_DONE) struct rockchip_sdhc_plat { -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct dtd_rockchip_rk3399_sdhci_5_1 dtplat; -#endif Please explain why remove this? struct mmc_config cfg; struct mmc mmc; }; +struct rockchip_emmc_phy { + u32 emmcphy_con[7]; + u32 reserved; + u32 emmcphy_status; +}; + struct rockchip_sdhc { struct sdhci_host host; + struct udevice *dev; void *base; + struct rockchip_emmc_phy *phy; + struct clk emmc_clk; +}; + +struct sdhci_data { + int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock); + int (*emmc_phy_init)(struct udevice *dev); + int (*get_phy)(struct udevice *dev); +}; + +static int rk3399_emmc_phy_init(struct udevice *dev) +{ + return 0; +} + +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) +{ + u32 caldone, dllrdy, freqsel; + + writel(RK_CLRSETBITS(7 << 4, 0), >emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 11, 1 << 11), >emmcphy_con[0]); + writel(RK_CLRSETBITS(0xf << 7, 6 << 7), >emmcphy_con[0]); Would clrsetbits_le32 work? + + /* +* According to the user manual, calpad calibration +* cycle takes more than 2us without the minimal recommended +* value, so we may need a little margin here +*/ + udelay(3); + writel(RK_CLRSETBITS(1, 1), >emmcphy_con[6]); Ditto. Regards, Peng. + + /* +* According to the user manual, it asks driver to +* wait 5us for calpad busy trimming. But it seems that +* 5us of caldone isn't enough for all cases. +*/ + udelay(500); + caldone = readl(>emmcphy_status); + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; + if (caldone != PHYCTRL_CALDONE_DONE) { + printf("%s: caldone timeout.\n", __func__); + return; + } + + /* Set the frequency of the DLL operation */ + if (clock < 75 * MHz) + freqsel = PHYCTRL_FREQSEL_50M; + else if (clock < 125 * MHz) + freqsel = PHYCTRL_FREQSEL_100M; + else if (clock < 175 * MHz) + freqsel = PHYCTRL_FREQSEL_150M; + else + freqsel = PHYCTRL_FREQSEL_200M; + + /* Set the frequency of the DLL operation */ + writel(RK_CLRSETBITS(3 << 12, freqsel << 12), >emmcphy_con[0]); + writel(RK_CLRSETBITS(1 << 1, 1 << 1), >emmcphy_con[6]); Ditto. + + read_poll_timeout(readl, >emmcphy_status, dllrdy, + PHYCTRL_DLL_LOCK_WO_TMOUT(dllrdy), 1, 5000); +} + +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) +{ + writel(RK_CLRSETBITS(1, 0), >emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 1, 0), >emmcphy_con[6]); +} + +static int rk3399_emmc_get_phy(struct udevice *dev) +{ + struct rockchip_sdhc *priv = dev_get_priv(dev); + + ofnode phy_node; + void *grf_base; + u32 grf_phy_offset, phandle; + + phandle = dev_read_u32_default(dev, "phys",
Re: Re: [PATCH v2 1/3] mmc: rockchip_sdhci: add phy and clock config for rk3399
Hi Jaehoon, >RK_CLRSETBITS macro is (((clr) | (set))) << 16) | (set). >Which bit is cleared? When (7 << 4) is set, it means that cleared? Yes, this ops clear the bit4-bit6. >> + >> +if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200 && >> host->mmc->bus_width == 8) >> +sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), >> SDHCI_BLOCK_SIZE); >> +else >> +sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), >> SDHCI_BLOCK_SIZE); > >Why it needs to pass to 7? It seems that always (dma & 0x7) in >SDHCI_MAKE_BLKSZ. >#define SDHCI_MAKE_BLKSZ(blksz) (1 << 12) | (blksz & 0xFFF)? In sdhci.h defined : #define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) #define SDHCI_DEFAULT_BOUNDARY_ARG (7) I will modify the code using SDHCI_DEFAULT_BOUNDARY_ARG, refer to iproc_sdhci.c blk_size = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 64); if (opcode == MMC_CMD_SEND_TUNING_BLOCK_HS200 && host->mmc->bus_width == 8) blk_size = SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, 128); sdhci_writew(host, blk_size, SDHCI_BLOCK_SIZE); >Hi Yifeng, > >On 6/28/21 6:19 PM, Yifeng Zhao wrote: >> Add clock, phy and other configuration, it is convenient to support >> new controller. Here a short summary of the changes: >> - Add mmc_of_parse to parse dts config. >> - Remove OF_PLATDATA related code. >> - Reorder header inclusion. >> - Add phy ops. >> - add ops set_ios_post to modify the parameters of phy when the >> clock changes. >> - Add execute tuning api for hs200 tuning >> >> Signed-off-by: Yifeng Zhao >> --- >> >> Changes in v2: >> - Add mmc_of_parse to parse dts config. >> - Used read_poll_timeout api to check dll lock status >> - Add execute tuning api for hs200 tuning >> >> drivers/mmc/rockchip_sdhci.c | 310 +++ >> 1 file changed, 274 insertions(+), 36 deletions(-) >> >> diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c >> index d95f8b2a15..2973911446 100644 >> --- a/drivers/mmc/rockchip_sdhci.c >> +++ b/drivers/mmc/rockchip_sdhci.c >> @@ -6,90 +6,319 @@ >> */ >> >> #include >> +#include >> #include >> +#include >> #include >> +#include >> #include >> #include >> +#include >> #include >> #include >> +#include "mmc_private.h" >> #include >> -#include >> +#include >> +#include >> +#include >> >> /* 400KHz is max freq for card ID etc. Use that as min */ >> #define EMMC_MIN_FREQ 40 >> +#define KHz (1000) >> +#define MHz (1000 * KHz) >> +#define SDHCI_TUNING_LOOP_COUNT 40 >> + >> +#define PHYCTRL_CALDONE_MASK0x1 >> +#define PHYCTRL_CALDONE_SHIFT 0x6 >> +#define PHYCTRL_CALDONE_DONE0x1 >> +#define PHYCTRL_DLLRDY_MASK 0x1 >> +#define PHYCTRL_DLLRDY_SHIFT0x5 >> +#define PHYCTRL_DLLRDY_DONE 0x1 >> +#define PHYCTRL_FREQSEL_200M0x0 >> +#define PHYCTRL_FREQSEL_50M 0x1 >> +#define PHYCTRL_FREQSEL_100M0x2 >> +#define PHYCTRL_FREQSEL_150M0x3 >> +#define PHYCTRL_DLL_LOCK_WO_TMOUT(x)\ >> +x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ >> +PHYCTRL_DLLRDY_DONE) >> >> struct rockchip_sdhc_plat { >> -#if CONFIG_IS_ENABLED(OF_PLATDATA) >> -struct dtd_rockchip_rk3399_sdhci_5_1 dtplat; >> -#endif >> struct mmc_config cfg; >> struct mmc mmc; >> }; >> >> +struct rockchip_emmc_phy { >> +u32 emmcphy_con[7]; >> +u32 reserved; >> +u32 emmcphy_status; >> +}; >> + >> struct rockchip_sdhc { >> struct sdhci_host host; >> +struct udevice *dev; >> void *base; >> +struct rockchip_emmc_phy *phy; >> +struct clk emmc_clk; >> +}; >> + >> +struct sdhci_data { >> +int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock); >> +int (*emmc_phy_init)(struct udevice *dev); >> +int (*get_phy)(struct udevice *dev); >> +}; >> + >> +static int rk3399_emmc_phy_init(struct udevice *dev) >> +{ >> +return 0; >> +} >> + >> +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 >> clock) >> +{ >> +u32 caldone, dllrdy, freqsel; >> + >> +writel(RK_CLRSETBITS(7 << 4, 0), >emmcphy_con[6]); > >RK_CLRSETBITS macro is (((clr) | (set))) << 16) | (set). >Which bit is cleared? When (7 << 4) is set, it means that cleared? > > >> +writel(RK_CLRSETBITS(1 << 11, 1 << 11), >emmcphy_con[0]); >> +writel(RK_CLRSETBITS(0xf << 7, 6 << 7), >emmcphy_con[0]); >> + >> +/* >> + * According to the user manual, calpad calibration >> + * cycle takes more than 2us
Re: [PATCH v2 1/3] mmc: rockchip_sdhci: add phy and clock config for rk3399
Hi Yifeng, On 6/28/21 6:19 PM, Yifeng Zhao wrote: > Add clock, phy and other configuration, it is convenient to support > new controller. Here a short summary of the changes: > - Add mmc_of_parse to parse dts config. > - Remove OF_PLATDATA related code. > - Reorder header inclusion. > - Add phy ops. > - add ops set_ios_post to modify the parameters of phy when the > clock changes. > - Add execute tuning api for hs200 tuning > > Signed-off-by: Yifeng Zhao > --- > > Changes in v2: > - Add mmc_of_parse to parse dts config. > - Used read_poll_timeout api to check dll lock status > - Add execute tuning api for hs200 tuning > > drivers/mmc/rockchip_sdhci.c | 310 +++ > 1 file changed, 274 insertions(+), 36 deletions(-) > > diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c > index d95f8b2a15..2973911446 100644 > --- a/drivers/mmc/rockchip_sdhci.c > +++ b/drivers/mmc/rockchip_sdhci.c > @@ -6,90 +6,319 @@ > */ > > #include > +#include > #include > +#include > #include > +#include > #include > #include > +#include > #include > #include > +#include "mmc_private.h" > #include > -#include > +#include > +#include > +#include > > /* 400KHz is max freq for card ID etc. Use that as min */ > #define EMMC_MIN_FREQ40 > +#define KHz (1000) > +#define MHz (1000 * KHz) > +#define SDHCI_TUNING_LOOP_COUNT 40 > + > +#define PHYCTRL_CALDONE_MASK 0x1 > +#define PHYCTRL_CALDONE_SHIFT0x6 > +#define PHYCTRL_CALDONE_DONE 0x1 > +#define PHYCTRL_DLLRDY_MASK 0x1 > +#define PHYCTRL_DLLRDY_SHIFT 0x5 > +#define PHYCTRL_DLLRDY_DONE 0x1 > +#define PHYCTRL_FREQSEL_200M 0x0 > +#define PHYCTRL_FREQSEL_50M 0x1 > +#define PHYCTRL_FREQSEL_100M 0x2 > +#define PHYCTRL_FREQSEL_150M 0x3 > +#define PHYCTRL_DLL_LOCK_WO_TMOUT(x) \ > + x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ > + PHYCTRL_DLLRDY_DONE) > > struct rockchip_sdhc_plat { > -#if CONFIG_IS_ENABLED(OF_PLATDATA) > - struct dtd_rockchip_rk3399_sdhci_5_1 dtplat; > -#endif > struct mmc_config cfg; > struct mmc mmc; > }; > > +struct rockchip_emmc_phy { > + u32 emmcphy_con[7]; > + u32 reserved; > + u32 emmcphy_status; > +}; > + > struct rockchip_sdhc { > struct sdhci_host host; > + struct udevice *dev; > void *base; > + struct rockchip_emmc_phy *phy; > + struct clk emmc_clk; > +}; > + > +struct sdhci_data { > + int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock); > + int (*emmc_phy_init)(struct udevice *dev); > + int (*get_phy)(struct udevice *dev); > +}; > + > +static int rk3399_emmc_phy_init(struct udevice *dev) > +{ > + return 0; > +} > + > +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 > clock) > +{ > + u32 caldone, dllrdy, freqsel; > + > + writel(RK_CLRSETBITS(7 << 4, 0), >emmcphy_con[6]); RK_CLRSETBITS macro is (((clr) | (set))) << 16) | (set). Which bit is cleared? When (7 << 4) is set, it means that cleared? > + writel(RK_CLRSETBITS(1 << 11, 1 << 11), >emmcphy_con[0]); > + writel(RK_CLRSETBITS(0xf << 7, 6 << 7), >emmcphy_con[0]); > + > + /* > + * According to the user manual, calpad calibration > + * cycle takes more than 2us without the minimal recommended > + * value, so we may need a little margin here > + */ > + udelay(3); > + writel(RK_CLRSETBITS(1, 1), >emmcphy_con[6]); > + > + /* > + * According to the user manual, it asks driver to > + * wait 5us for calpad busy trimming. But it seems that > + * 5us of caldone isn't enough for all cases. > + */ > + udelay(500); > + caldone = readl(>emmcphy_status); > + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; > + if (caldone != PHYCTRL_CALDONE_DONE) { > + printf("%s: caldone timeout.\n", __func__); > + return; > + } > + > + /* Set the frequency of the DLL operation */ > + if (clock < 75 * MHz) > + freqsel = PHYCTRL_FREQSEL_50M; > + else if (clock < 125 * MHz) > + freqsel = PHYCTRL_FREQSEL_100M; > + else if (clock < 175 * MHz) > + freqsel = PHYCTRL_FREQSEL_150M; > + else > + freqsel = PHYCTRL_FREQSEL_200M; > + > + /* Set the frequency of the DLL operation */ > + writel(RK_CLRSETBITS(3 << 12, freqsel << 12), >emmcphy_con[0]); > + writel(RK_CLRSETBITS(1 << 1, 1 << 1), >emmcphy_con[6]); > + > + read_poll_timeout(readl, >emmcphy_status, dllrdy, > + PHYCTRL_DLL_LOCK_WO_TMOUT(dllrdy), 1, 5000); > +} > + > +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) > +{ > + writel(RK_CLRSETBITS(1, 0), >emmcphy_con[6]); > + writel(RK_CLRSETBITS(1 << 1, 0), >emmcphy_con[6]); > +} > + > +static int rk3399_emmc_get_phy(struct udevice *dev) > +{ > +
[PATCH v2 1/3] mmc: rockchip_sdhci: add phy and clock config for rk3399
Add clock, phy and other configuration, it is convenient to support new controller. Here a short summary of the changes: - Add mmc_of_parse to parse dts config. - Remove OF_PLATDATA related code. - Reorder header inclusion. - Add phy ops. - add ops set_ios_post to modify the parameters of phy when the clock changes. - Add execute tuning api for hs200 tuning Signed-off-by: Yifeng Zhao --- Changes in v2: - Add mmc_of_parse to parse dts config. - Used read_poll_timeout api to check dll lock status - Add execute tuning api for hs200 tuning drivers/mmc/rockchip_sdhci.c | 310 +++ 1 file changed, 274 insertions(+), 36 deletions(-) diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c index d95f8b2a15..2973911446 100644 --- a/drivers/mmc/rockchip_sdhci.c +++ b/drivers/mmc/rockchip_sdhci.c @@ -6,90 +6,319 @@ */ #include +#include #include +#include #include +#include #include #include +#include #include #include +#include "mmc_private.h" #include -#include +#include +#include +#include /* 400KHz is max freq for card ID etc. Use that as min */ #define EMMC_MIN_FREQ 40 +#define KHz(1000) +#define MHz(1000 * KHz) +#define SDHCI_TUNING_LOOP_COUNT40 + +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_DLLRDY_MASK0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE0x1 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_DLL_LOCK_WO_TMOUT(x) \ + x) >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK) ==\ + PHYCTRL_DLLRDY_DONE) struct rockchip_sdhc_plat { -#if CONFIG_IS_ENABLED(OF_PLATDATA) - struct dtd_rockchip_rk3399_sdhci_5_1 dtplat; -#endif struct mmc_config cfg; struct mmc mmc; }; +struct rockchip_emmc_phy { + u32 emmcphy_con[7]; + u32 reserved; + u32 emmcphy_status; +}; + struct rockchip_sdhc { struct sdhci_host host; + struct udevice *dev; void *base; + struct rockchip_emmc_phy *phy; + struct clk emmc_clk; +}; + +struct sdhci_data { + int (*emmc_set_clock)(struct sdhci_host *host, unsigned int clock); + int (*emmc_phy_init)(struct udevice *dev); + int (*get_phy)(struct udevice *dev); +}; + +static int rk3399_emmc_phy_init(struct udevice *dev) +{ + return 0; +} + +static void rk3399_emmc_phy_power_on(struct rockchip_emmc_phy *phy, u32 clock) +{ + u32 caldone, dllrdy, freqsel; + + writel(RK_CLRSETBITS(7 << 4, 0), >emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 11, 1 << 11), >emmcphy_con[0]); + writel(RK_CLRSETBITS(0xf << 7, 6 << 7), >emmcphy_con[0]); + + /* +* According to the user manual, calpad calibration +* cycle takes more than 2us without the minimal recommended +* value, so we may need a little margin here +*/ + udelay(3); + writel(RK_CLRSETBITS(1, 1), >emmcphy_con[6]); + + /* +* According to the user manual, it asks driver to +* wait 5us for calpad busy trimming. But it seems that +* 5us of caldone isn't enough for all cases. +*/ + udelay(500); + caldone = readl(>emmcphy_status); + caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK; + if (caldone != PHYCTRL_CALDONE_DONE) { + printf("%s: caldone timeout.\n", __func__); + return; + } + + /* Set the frequency of the DLL operation */ + if (clock < 75 * MHz) + freqsel = PHYCTRL_FREQSEL_50M; + else if (clock < 125 * MHz) + freqsel = PHYCTRL_FREQSEL_100M; + else if (clock < 175 * MHz) + freqsel = PHYCTRL_FREQSEL_150M; + else + freqsel = PHYCTRL_FREQSEL_200M; + + /* Set the frequency of the DLL operation */ + writel(RK_CLRSETBITS(3 << 12, freqsel << 12), >emmcphy_con[0]); + writel(RK_CLRSETBITS(1 << 1, 1 << 1), >emmcphy_con[6]); + + read_poll_timeout(readl, >emmcphy_status, dllrdy, + PHYCTRL_DLL_LOCK_WO_TMOUT(dllrdy), 1, 5000); +} + +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) +{ + writel(RK_CLRSETBITS(1, 0), >emmcphy_con[6]); + writel(RK_CLRSETBITS(1 << 1, 0), >emmcphy_con[6]); +} + +static int rk3399_emmc_get_phy(struct udevice *dev) +{ + struct rockchip_sdhc *priv = dev_get_priv(dev); + + ofnode phy_node; + void *grf_base; + u32 grf_phy_offset, phandle; + + phandle = dev_read_u32_default(dev, "phys", 0); + phy_node = ofnode_get_by_phandle(phandle); + if (!ofnode_valid(phy_node)) { + debug("Not found emmc phy device\n"); + return