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
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) +{ + 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) +{ + 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; + } 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), }; -- 2.17.1