Hi Yifeng, On 6/7/21 4:38 PM, Yifeng Zhao wrote: > Add clock, phy and other configuration, it is convenient to support > new controller. Here a short summary of the changes: > - support HS200 and HS400 config by dts > - Remove OF_PLATDATA related code > - Reorder header inclusion > - Add data width config for 1, 4, 8 bits > - Add phy ops > - add ops set_ios_post to modify the parameters of phy when the > clock changes
Does it impossible to move to phy subsystem about codes relevant to phy? > > Signed-off-by: Yifeng Zhao <yifeng.z...@rock-chips.com> > --- > > drivers/mmc/rockchip_sdhci.c | 334 +++++++++++++++++++++++++++++++---- > 1 file changed, 303 insertions(+), 31 deletions(-) > > diff --git a/drivers/mmc/rockchip_sdhci.c b/drivers/mmc/rockchip_sdhci.c > index d95f8b2a15..05ed998eca 100644 > --- a/drivers/mmc/rockchip_sdhci.c > +++ b/drivers/mmc/rockchip_sdhci.c > @@ -6,33 +6,285 @@ > */ > > #include <common.h> > +#include <clk.h> > #include <dm.h> > +#include <dm/ofnode.h> > #include <dt-structs.h> > +#include <linux/delay.h> > #include <linux/err.h> > #include <linux/libfdt.h> > #include <malloc.h> > #include <mapmem.h> > #include <sdhci.h> > -#include <clk.h> > +#include <syscon.h> > > +#include <asm/arch-rockchip/clock.h> > +#include <asm/arch-rockchip/hardware.h> > /* 400KHz is max freq for card ID etc. Use that as min */ > #define EMMC_MIN_FREQ 400000 > +#define KHz (1000) > +#define MHz (1000 * KHz) > + > +#define PHYCTRL_CALDONE_MASK 0x1 > +#define PHYCTRL_CALDONE_SHIFT 0x6 > +#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 > > 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; > + uint start; > + > + writel(RK_CLRSETBITS(7 << 4, 0), &phy->emmcphy_con[6]); > + writel(RK_CLRSETBITS(1 << 11, 1 << 11), &phy->emmcphy_con[0]); > + writel(RK_CLRSETBITS(0xf << 7, 6 << 7), &phy->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), &phy->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(&phy->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), &phy->emmcphy_con[0]); > + writel(RK_CLRSETBITS(1 << 1, 1 << 1), &phy->emmcphy_con[6]); > + > + start = get_timer(0); > + > + do { > + udelay(1); > + dllrdy = readl(&phy->emmcphy_status); > + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; > + if (dllrdy == PHYCTRL_DLLRDY_DONE) > + break; > + } while (get_timer(start) < 50000); > + > + if (dllrdy != PHYCTRL_DLLRDY_DONE) > + printf("%s: dllrdy timeout.\n", __func__); > +} > + > +static void rk3399_emmc_phy_power_off(struct rockchip_emmc_phy *phy) > +{ > + writel(RK_CLRSETBITS(1, 0), &phy->emmcphy_con[6]); > + writel(RK_CLRSETBITS(1 << 1, 0), &phy->emmcphy_con[6]); > +} > + > +static int rockchip_emmc_set_clock(struct sdhci_host *host, unsigned int > clock) Does it impossible to reuse sdhci_set_clock()? It's almmost similar, except clk_set_rate(). > +{ > + unsigned int div, clk = 0, timeout; > + unsigned int input_clk; > + struct rockchip_sdhc *priv = > + container_of(host, struct rockchip_sdhc, host); > + > + /* Wait max 20 ms */ > + timeout = 2000; > + while (sdhci_readl(host, SDHCI_PRESENT_STATE) & > + (SDHCI_CMD_INHIBIT | SDHCI_DATA_INHIBIT)) { > + if (timeout == 0) { > + printf("%s: Timeout to wait cmd & data inhibit\n", > + __func__); > + return -EBUSY; > + } > + > + timeout--; > + udelay(10); > + } > + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); > + > + if (clock == 0) > + return 0; > + > + input_clk = clk_set_rate(&priv->emmc_clk, clock); > + if (IS_ERR_VALUE(input_clk)) > + input_clk = host->max_clk; > + > + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { > + /* > + * Check if the Host Controller supports Programmable Clock > + * Mode. > + */ > + if (host->clk_mul) { > + for (div = 1; div <= 1024; div++) { > + if ((input_clk / div) <= clock) > + break; > + } > + > + /* > + * Set Programmable Clock Mode in the Clock > + * Control register. > + */ > + clk = SDHCI_PROG_CLOCK_MODE; > + div--; > + } else { > + /* Version 3.00 divisors must be a multiple of 2. */ > + if (input_clk <= clock) { > + div = 1; > + } else { > + for (div = 2; > + div < SDHCI_MAX_DIV_SPEC_300; > + div += 2) { > + if ((input_clk / div) <= clock) > + break; > + } > + } > + div >>= 1; > + } > + } else { > + /* Version 2.00 divisors must be a power of 2. */ > + for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) { > + if ((input_clk / div) <= clock) > + break; > + } > + div >>= 1; > + } > + > + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; > + clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) > + << SDHCI_DIVIDER_HI_SHIFT; > + clk |= SDHCI_CLOCK_INT_EN; > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); > + > + /* Wait max 20 ms */ > + timeout = 2000; > + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) > + & SDHCI_CLOCK_INT_STABLE)) { > + if (timeout == 0) { > + printf("%s: Internal clock never stabilised.\n", > + __func__); > + return -EBUSY; > + } > + timeout--; > + udelay(10); > + } > + clk |= SDHCI_CLOCK_CARD_EN; > + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); > + host->clock = clock; > + > + return 0; > +} > + > +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 -ENODEV; > + } > + > + grf_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); > + if (grf_base < 0) > + printf("%s Get syscon grf failed", __func__); > + grf_phy_offset = ofnode_read_u32_default(phy_node, "reg", 0); > + > + priv->phy = (struct rockchip_emmc_phy *)(grf_base + grf_phy_offset); > + > + return 0; > +} > + > +static int rk3399_sdhci_emmc_set_clock(struct sdhci_host *host, unsigned int > clock) > +{ > + struct rockchip_sdhc *priv = > + container_of(host, struct rockchip_sdhc, host); > + int cycle_phy = host->clock != clock && > + clock > EMMC_MIN_FREQ; > + > + if (cycle_phy) > + rk3399_emmc_phy_power_off(priv->phy); > + > + rockchip_emmc_set_clock(host, clock); > + > + if (cycle_phy) > + rk3399_emmc_phy_power_on(priv->phy, clock); > + > + return 0; > +} > + > +static int sdhci_set_ios_post(struct sdhci_host *host) rockchip_sdhci_set_ios_post? > +{ > + struct mmc *mmc = host->mmc; > + struct rockchip_sdhc *priv = container_of(host, struct rockchip_sdhc, > host); > + struct sdhci_data *data = (struct sdhci_data > *)dev_get_driver_data(priv->dev); > + > + data->emmc_set_clock(host, mmc->clock); > + > + return 0; > +} > + > +static struct sdhci_ops rockchip_sdhci_ops = { > + .set_ios_post = sdhci_set_ios_post, > }; > > -static int arasan_sdhci_probe(struct udevice *dev) > +static int rockchip_sdhci_probe(struct udevice *dev) > { > + struct sdhci_data *data = (struct sdhci_data *)dev_get_driver_data(dev); > struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); > struct rockchip_sdhc_plat *plat = dev_get_plat(dev); > struct rockchip_sdhc *prv = dev_get_priv(dev); > @@ -40,17 +292,22 @@ static int arasan_sdhci_probe(struct udevice *dev) > int max_frequency, ret; > struct clk clk; > > -#if CONFIG_IS_ENABLED(OF_PLATDATA) > - struct dtd_rockchip_rk3399_sdhci_5_1 *dtplat = &plat->dtplat; > - > - host->name = dev->name; > - host->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]); > - max_frequency = dtplat->max_frequency; > - ret = clk_get_by_driver_info(dev, dtplat->clocks, &clk); > -#else > max_frequency = dev_read_u32_default(dev, "max-frequency", 0); > + switch (dev_read_u32_default(dev, "bus-width", 4)) { > + case 8: > + host->host_caps |= MMC_MODE_8BIT; > + break; > + case 4: > + host->host_caps |= MMC_MODE_4BIT; > + break; > + case 1: > + break; > + default: > + printf("Invalid \"bus-width\" value\n"); > + return -EINVAL; > + } Use mmc_of_parse(). > ret = clk_get_by_index(dev, 0, &clk); > -#endif > + > if (!ret) { > ret = clk_set_rate(&clk, max_frequency); > if (IS_ERR_VALUE(ret)) > @@ -59,16 +316,25 @@ static int arasan_sdhci_probe(struct udevice *dev) > printf("%s fail to get clk\n", __func__); > } > > + prv->emmc_clk = clk; > + prv->dev = dev; > + ret = data->get_phy(dev); > + if (ret) > + return ret; > + > + ret = data->emmc_phy_init(dev); > + if (ret) > + return ret; > + > + host->ops = &rockchip_sdhci_ops; > + > host->quirks = SDHCI_QUIRK_WAIT_SEND_CMD; > host->max_clk = max_frequency; > - /* > - * The sdhci-driver only supports 4bit and 8bit, as sdhci_setup_cfg > - * doesn't allow us to clear MMC_MODE_4BIT. Consequently, we don't > - * check for other bus-width values. > - */ > - if (host->bus_width == 8) > - host->host_caps |= MMC_MODE_8BIT; > > + if (dev_read_bool(dev, "mmc-hs200-1_8v")) > + host->host_caps |= MMC_MODE_HS200; > + else if (dev_read_bool(dev, "mmc-hs400-1_8v")) > + host->host_caps |= MMC_MODE_HS400; > host->mmc = &plat->mmc; > host->mmc->priv = &prv->host; > host->mmc->dev = dev; > @@ -81,15 +347,12 @@ static int arasan_sdhci_probe(struct udevice *dev) > return sdhci_probe(dev); > } > > -static int arasan_sdhci_of_to_plat(struct udevice *dev) > +static int sdhci_of_to_plat(struct udevice *dev) > { > -#if !CONFIG_IS_ENABLED(OF_PLATDATA) > struct sdhci_host *host = dev_get_priv(dev); > > host->name = dev->name; > host->ioaddr = dev_read_addr_ptr(dev); > - host->bus_width = dev_read_u32_default(dev, "bus-width", 4); > -#endif > > return 0; > } > @@ -101,19 +364,28 @@ static int rockchip_sdhci_bind(struct udevice *dev) > return sdhci_bind(dev, &plat->mmc, &plat->cfg); > } > > -static const struct udevice_id arasan_sdhci_ids[] = { > - { .compatible = "arasan,sdhci-5.1" }, > +static const struct sdhci_data rk3399_data = { > + .emmc_set_clock = rk3399_sdhci_emmc_set_clock, > + .get_phy = rk3399_emmc_get_phy, > + .emmc_phy_init = rk3399_emmc_phy_init, > +}; > + > +static const struct udevice_id sdhci_ids[] = { > + { > + .compatible = "arasan,sdhci-5.1", > + .data = (ulong)&rk3399_data, > + }, > { } > }; > > U_BOOT_DRIVER(arasan_sdhci_drv) = { > - .name = "rockchip_rk3399_sdhci_5_1", > + .name = "rockchip_sdhci_5_1", > .id = UCLASS_MMC, > - .of_match = arasan_sdhci_ids, > - .of_to_plat = arasan_sdhci_of_to_plat, > + .of_match = sdhci_ids, > + .of_to_plat = sdhci_of_to_plat, > .ops = &sdhci_ops, > .bind = rockchip_sdhci_bind, > - .probe = arasan_sdhci_probe, > + .probe = rockchip_sdhci_probe, > .priv_auto = sizeof(struct rockchip_sdhc), > .plat_auto = sizeof(struct rockchip_sdhc_plat), > }; >