From 4d54a25ba5df8d4fd1c1604d1526c9279237ac27 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 20 Jun 2016 10:56:40 -0700 Subject: phy: rockchip-emmc: give DLL some extra time to be ready According to the databook, 10.2us is the max time for dll to be ready to work. However in testing, some chips need 20us for dll to be ready. This patch adds some extra margin for dllrdy to be ready, fixing our -ETIMEDOUT issues. Signed-off-by: Shawn Lin Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 6ebcf3e41c46..48cbe691a889 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -119,10 +119,11 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); /* - * After enable analog DLL circuits, we need extra 10.2us - * for dll to be ready for work. + * After enable analog DLL circuits, we need an extra 10.2us + * for dll to be ready for work. But according to testing, we + * find some chips need more than 25us. */ - udelay(11); + udelay(30); regmap_read(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_STATUS, &dllrdy); -- cgit v1.2.1 From d74857720d41c58f74966b5d06ebfa9111a62c69 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 20 Jun 2016 10:56:41 -0700 Subject: phy: rockchip-emmc: configure frequency range and drive impedance Signal integrity analysis has suggested we set these values. Do this in power_on(), so that they get reconfigured after suspend/resume. Signed-off-by: Shawn Lin Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 48cbe691a889..f2f75cf69af1 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -56,6 +56,19 @@ #define PHYCTRL_DLLRDY_SHIFT 0x5 #define PHYCTRL_DLLRDY_DONE 0x1 #define PHYCTRL_DLLRDY_GOING 0x0 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_FREQSEL_MASK 0x3 +#define PHYCTRL_FREQSEL_SHIFT 0xc +#define PHYCTRL_DR_MASK 0x7 +#define PHYCTRL_DR_SHIFT 0x4 +#define PHYCTRL_DR_50OHM 0x0 +#define PHYCTRL_DR_33OHM 0x1 +#define PHYCTRL_DR_66OHM 0x2 +#define PHYCTRL_DR_100OHM 0x3 +#define PHYCTRL_DR_40OHM 0x4 struct rockchip_emmc_phy { unsigned int reg_offset; @@ -154,6 +167,20 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); int ret = 0; + /* DLL operation: 200 MHz */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(PHYCTRL_FREQSEL_200M, + PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Drive impedance: 50 Ohm */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_DR_50OHM, + PHYCTRL_DR_MASK, + PHYCTRL_DR_SHIFT)); + /* Power up emmc phy analog blocks */ ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); if (ret) -- cgit v1.2.1 From 36b5d460261f16563f9196c49c936b3e17d237e3 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 20 Jun 2016 10:56:42 -0700 Subject: phy: rockchip-emmc: configure default output tap delay The output tap delay controls helps maintain the hold requirements for eMMC. The exact value is dependent on the SoC and other factors, though it isn't really an exact science. But the default of 0 is not very good, as it doesn't give the eMMC much hold time, so let's bump up to 4 (approx 90 degree phase?). If we need to configure this any further (e.g., based on board or speed factors), we may need to consider a device tree representation. Suggested-by: Shawn Lin Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index f2f75cf69af1..a0b87cc6c818 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -69,6 +69,11 @@ #define PHYCTRL_DR_66OHM 0x2 #define PHYCTRL_DR_100OHM 0x3 #define PHYCTRL_DR_40OHM 0x4 +#define PHYCTRL_OTAPDLYENA 0x1 +#define PHYCTRL_OTAPDLYENA_MASK 0x1 +#define PHYCTRL_OTAPDLYENA_SHIFT 0xb +#define PHYCTRL_OTAPDLYSEL_MASK 0xf +#define PHYCTRL_OTAPDLYSEL_SHIFT 0x7 struct rockchip_emmc_phy { unsigned int reg_offset; @@ -181,6 +186,20 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_DR_MASK, PHYCTRL_DR_SHIFT)); + /* Output tap delay: enable */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(PHYCTRL_OTAPDLYENA, + PHYCTRL_OTAPDLYENA_MASK, + PHYCTRL_OTAPDLYENA_SHIFT)); + + /* Output tap delay */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(4, + PHYCTRL_OTAPDLYSEL_MASK, + PHYCTRL_OTAPDLYSEL_SHIFT)); + /* Power up emmc phy analog blocks */ ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); if (ret) -- cgit v1.2.1 From 675f65c444e85ab889d299d2a7ce62d5d478e478 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 20 Jun 2016 10:56:43 -0700 Subject: phy: rockchip-emmc: reindent the register definitions Some of the spacing was wrong (spaces instead of tabs), and due to longer entries added later, the columns weren't aligned. Let's get everything consistent. Signed-off-by: Brian Norris Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 76 ++++++++++++++++++++--------------------- 1 file changed, 38 insertions(+), 38 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index a0b87cc6c818..a69f53630e67 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -31,44 +31,44 @@ ((val) << (shift) | (mask) << ((shift) + 16)) /* Register definition */ -#define GRF_EMMCPHY_CON0 0x0 -#define GRF_EMMCPHY_CON1 0x4 -#define GRF_EMMCPHY_CON2 0x8 -#define GRF_EMMCPHY_CON3 0xc -#define GRF_EMMCPHY_CON4 0x10 -#define GRF_EMMCPHY_CON5 0x14 -#define GRF_EMMCPHY_CON6 0x18 -#define GRF_EMMCPHY_STATUS 0x20 - -#define PHYCTRL_PDB_MASK 0x1 -#define PHYCTRL_PDB_SHIFT 0x0 -#define PHYCTRL_PDB_PWR_ON 0x1 -#define PHYCTRL_PDB_PWR_OFF 0x0 -#define PHYCTRL_ENDLL_MASK 0x1 -#define PHYCTRL_ENDLL_SHIFT 0x1 -#define PHYCTRL_ENDLL_ENABLE 0x1 -#define PHYCTRL_ENDLL_DISABLE 0x0 -#define PHYCTRL_CALDONE_MASK 0x1 -#define PHYCTRL_CALDONE_SHIFT 0x6 -#define PHYCTRL_CALDONE_DONE 0x1 -#define PHYCTRL_CALDONE_GOING 0x0 -#define PHYCTRL_DLLRDY_MASK 0x1 -#define PHYCTRL_DLLRDY_SHIFT 0x5 -#define PHYCTRL_DLLRDY_DONE 0x1 -#define PHYCTRL_DLLRDY_GOING 0x0 -#define PHYCTRL_FREQSEL_200M 0x0 -#define PHYCTRL_FREQSEL_50M 0x1 -#define PHYCTRL_FREQSEL_100M 0x2 -#define PHYCTRL_FREQSEL_150M 0x3 -#define PHYCTRL_FREQSEL_MASK 0x3 -#define PHYCTRL_FREQSEL_SHIFT 0xc -#define PHYCTRL_DR_MASK 0x7 -#define PHYCTRL_DR_SHIFT 0x4 -#define PHYCTRL_DR_50OHM 0x0 -#define PHYCTRL_DR_33OHM 0x1 -#define PHYCTRL_DR_66OHM 0x2 -#define PHYCTRL_DR_100OHM 0x3 -#define PHYCTRL_DR_40OHM 0x4 +#define GRF_EMMCPHY_CON0 0x0 +#define GRF_EMMCPHY_CON1 0x4 +#define GRF_EMMCPHY_CON2 0x8 +#define GRF_EMMCPHY_CON3 0xc +#define GRF_EMMCPHY_CON4 0x10 +#define GRF_EMMCPHY_CON5 0x14 +#define GRF_EMMCPHY_CON6 0x18 +#define GRF_EMMCPHY_STATUS 0x20 + +#define PHYCTRL_PDB_MASK 0x1 +#define PHYCTRL_PDB_SHIFT 0x0 +#define PHYCTRL_PDB_PWR_ON 0x1 +#define PHYCTRL_PDB_PWR_OFF 0x0 +#define PHYCTRL_ENDLL_MASK 0x1 +#define PHYCTRL_ENDLL_SHIFT 0x1 +#define PHYCTRL_ENDLL_ENABLE 0x1 +#define PHYCTRL_ENDLL_DISABLE 0x0 +#define PHYCTRL_CALDONE_MASK 0x1 +#define PHYCTRL_CALDONE_SHIFT 0x6 +#define PHYCTRL_CALDONE_DONE 0x1 +#define PHYCTRL_CALDONE_GOING 0x0 +#define PHYCTRL_DLLRDY_MASK 0x1 +#define PHYCTRL_DLLRDY_SHIFT 0x5 +#define PHYCTRL_DLLRDY_DONE 0x1 +#define PHYCTRL_DLLRDY_GOING 0x0 +#define PHYCTRL_FREQSEL_200M 0x0 +#define PHYCTRL_FREQSEL_50M 0x1 +#define PHYCTRL_FREQSEL_100M 0x2 +#define PHYCTRL_FREQSEL_150M 0x3 +#define PHYCTRL_FREQSEL_MASK 0x3 +#define PHYCTRL_FREQSEL_SHIFT 0xc +#define PHYCTRL_DR_MASK 0x7 +#define PHYCTRL_DR_SHIFT 0x4 +#define PHYCTRL_DR_50OHM 0x0 +#define PHYCTRL_DR_33OHM 0x1 +#define PHYCTRL_DR_66OHM 0x2 +#define PHYCTRL_DR_100OHM 0x3 +#define PHYCTRL_DR_40OHM 0x4 #define PHYCTRL_OTAPDLYENA 0x1 #define PHYCTRL_OTAPDLYENA_MASK 0x1 #define PHYCTRL_OTAPDLYENA_SHIFT 0xb -- cgit v1.2.1 From 49f9ccd8882c1b97512ece16ad68ad0594c54bbc Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:44 -0700 Subject: phy: rockchip-emmc: Increase lock time allowance Previous PHY code waited a fixed amount of time for the DLL to lock at power on time. Unfortunately, the time for the DLL to lock is actually a bit more dynamic and can be longer if the card clock is slower. Instead of waiting a fixed 30 us, let's now dynamically wait until the lock bit gets set. We'll wait up to 10 ms which should be OK even if the card clock is at the super slow 100 kHz. On its own, this change makes the PHY power on code a little more robust. Before this change the PHY was relying on the eMMC code to make sure the PHY was only powered on when the card clock was set to at least 50 MHz before, though this reliance wasn't documented anywhere. This change will be even more useful in future changes where we actually need to be able to wait for a DLL lock at slower clock speeds. Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Shawn Lin Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index a69f53630e67..2d059c046978 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -85,6 +85,7 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, { unsigned int caldone; unsigned int dllrdy; + unsigned long timeout; /* * Keep phyctrl_pdb and phyctrl_endll low to allow @@ -137,15 +138,26 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); /* - * After enable analog DLL circuits, we need an extra 10.2us - * for dll to be ready for work. But according to testing, we - * find some chips need more than 25us. + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100 kHZ) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * Hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. */ - udelay(30); - regmap_read(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_STATUS, - &dllrdy); - dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + timeout = jiffies + msecs_to_jiffies(10); + do { + udelay(1); + + regmap_read(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_STATUS, + &dllrdy); + dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK; + if (dllrdy == PHYCTRL_DLLRDY_DONE) + break; + } while (!time_after(jiffies, timeout)); + if (dllrdy != PHYCTRL_DLLRDY_DONE) { pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n"); return -ETIMEDOUT; -- cgit v1.2.1 From 352051ef1ff5efa8ba050bc3a581dbf136a1f97b Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:52 -0700 Subject: phy: rockchip-emmc: Minor code cleanup in rockchip_emmc_phy_power_on/off() There's no reason to store the return value of rockchip_emmc_phy_power() in a variable nor to check it. Just return it. Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Shawn Lin Tested-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 2d059c046978..23fe50864526 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -169,20 +169,14 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, static int rockchip_emmc_phy_power_off(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - int ret = 0; /* Power down emmc phy analog blocks */ - ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); - if (ret) - return ret; - - return 0; + return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); } static int rockchip_emmc_phy_power_on(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - int ret = 0; /* DLL operation: 200 MHz */ regmap_write(rk_phy->reg_base, @@ -213,11 +207,7 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_OTAPDLYSEL_SHIFT)); /* Power up emmc phy analog blocks */ - ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); - if (ret) - return ret; - - return 0; + return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); } static const struct phy_ops ops = { -- cgit v1.2.1 From 52c0624a10cce0cdc8be2d15462b9c8d76b7700d Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 20 Jun 2016 10:56:53 -0700 Subject: phy: rockchip-emmc: Set phyctrl_frqsel based on card clock The "phyctrl_frqsel" is described in the Arasan datasheet [1] as "the frequency range of DLL operation". Although the Rockchip variant of this PHY has different ranges than the reference Arasan PHY it appears as if the functionality is similar. We should set this phyctrl field properly. Note: as per Rockchip engineers, apparently the "phyctrl_frqsel" is actually only useful in HS200 / HS400 modes even though the DLL itself it used for some purposes in all modes. See the discussion in the earlier change in this series: ("mmc: sdhci-of-arasan: Always power the PHY off/on when clock changes"). In any case, it shouldn't hurt to set this always. Note that this change should allow boards to run at HS200 / HS400 speed modes while running at 100 MHz or 150 MHz. In fact, running HS400 at 150 MHz (giving 300 MB/s) is the main motivation of this series, since performance is still good but signal integrity problems are less prevelant at 150 MHz. [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 101 +++++++++++++++++++++++++++++++++++----- 1 file changed, 89 insertions(+), 12 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 23fe50864526..9dce958233a0 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -14,6 +14,7 @@ * GNU General Public License for more details. */ +#include #include #include #include @@ -78,15 +79,53 @@ struct rockchip_emmc_phy { unsigned int reg_offset; struct regmap *reg_base; + struct clk *emmcclk; }; -static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, - bool on_off) +static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) { + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); unsigned int caldone; unsigned int dllrdy; + unsigned int freqsel = PHYCTRL_FREQSEL_200M; unsigned long timeout; + if (rk_phy->emmcclk != NULL) { + unsigned long rate = clk_get_rate(rk_phy->emmcclk); + unsigned long ideal_rate; + unsigned long diff; + + switch (rate) { + case 0 ... 74999999: + ideal_rate = 50000000; + freqsel = PHYCTRL_FREQSEL_50M; + break; + case 75000000 ... 124999999: + ideal_rate = 100000000; + freqsel = PHYCTRL_FREQSEL_100M; + break; + case 125000000 ... 174999999: + ideal_rate = 150000000; + freqsel = PHYCTRL_FREQSEL_150M; + break; + default: + ideal_rate = 200000000; + break; + }; + + diff = (rate > ideal_rate) ? + rate - ideal_rate : ideal_rate - rate; + + /* + * In order for tuning delays to be accurate we need to be + * pretty spot on for the DLL range, so warn if we're too + * far off. Also warn if we're above the 200 MHz max. Don't + * warn for really slow rates since we won't be tuning then. + */ + if ((rate > 50000000 && diff > 15000000) || (rate > 200000000)) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + } + /* * Keep phyctrl_pdb and phyctrl_endll low to allow * initialization of CALIO state M/C DFFs @@ -132,6 +171,13 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return -ETIMEDOUT; } + /* Set the frequency of the DLL operation */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON0, + HIWORD_UPDATE(freqsel, PHYCTRL_FREQSEL_MASK, + PHYCTRL_FREQSEL_SHIFT)); + + /* Turn on the DLL */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, @@ -166,25 +212,54 @@ static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy, return 0; } -static int rockchip_emmc_phy_power_off(struct phy *phy) +static int rockchip_emmc_phy_init(struct phy *phy) +{ + struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + int ret = 0; + + /* + * We purposely get the clock here and not in probe to avoid the + * circular dependency problem. We expect: + * - PHY driver to probe + * - SDHCI driver to start probe + * - SDHCI driver to register it's clock + * - SDHCI driver to get the PHY + * - SDHCI driver to init the PHY + * + * The clock is optional, so upon any error we just set to NULL. + * + * NOTE: we don't do anything special for EPROBE_DEFER here. Given the + * above expected use case, EPROBE_DEFER isn't sensible to expect, so + * it's just like any other error. + */ + rk_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + if (IS_ERR(rk_phy->emmcclk)) { + dev_dbg(&phy->dev, "Error getting emmcclk: %d\n", ret); + rk_phy->emmcclk = NULL; + } + + return ret; +} + +static int rockchip_emmc_phy_exit(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); + clk_put(rk_phy->emmcclk); + + return 0; +} + +static int rockchip_emmc_phy_power_off(struct phy *phy) +{ /* Power down emmc phy analog blocks */ - return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF); + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_OFF); } static int rockchip_emmc_phy_power_on(struct phy *phy) { struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy); - /* DLL operation: 200 MHz */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON0, - HIWORD_UPDATE(PHYCTRL_FREQSEL_200M, - PHYCTRL_FREQSEL_MASK, - PHYCTRL_FREQSEL_SHIFT)); - /* Drive impedance: 50 Ohm */ regmap_write(rk_phy->reg_base, rk_phy->reg_offset + GRF_EMMCPHY_CON6, @@ -207,10 +282,12 @@ static int rockchip_emmc_phy_power_on(struct phy *phy) PHYCTRL_OTAPDLYSEL_SHIFT)); /* Power up emmc phy analog blocks */ - return rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON); + return rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON); } static const struct phy_ops ops = { + .init = rockchip_emmc_phy_init, + .exit = rockchip_emmc_phy_exit, .power_on = rockchip_emmc_phy_power_on, .power_off = rockchip_emmc_phy_power_off, .owner = THIS_MODULE, -- cgit v1.2.1 From 4e2ea670861f2068082da310f25815c7498a41dc Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 27 Jun 2016 10:39:26 -0700 Subject: phy: rockchip-emmc: Be tolerant to card clock of 0 in power on It's possible that there are some reasons to turn the PHY on while the clock is 0. In this case we just won't wait for the DLL to lock. This is a bit of a stopgap until we figure out exactly when we're supposed to wait for the DLL to lock and when we're supposed to power cycle the PHY. Note: this patch should help with suspend/resume where the system will try to turn the PHY back on when the clock is 0. Signed-off-by: Douglas Anderson Reviewed-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 22 deletions(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index 9dce958233a0..a2aa6aca7dec 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -88,15 +88,36 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) unsigned int caldone; unsigned int dllrdy; unsigned int freqsel = PHYCTRL_FREQSEL_200M; + unsigned long rate; unsigned long timeout; - if (rk_phy->emmcclk != NULL) { - unsigned long rate = clk_get_rate(rk_phy->emmcclk); + /* + * Keep phyctrl_pdb and phyctrl_endll low to allow + * initialization of CALIO state M/C DFFs + */ + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, + PHYCTRL_PDB_MASK, + PHYCTRL_PDB_SHIFT)); + regmap_write(rk_phy->reg_base, + rk_phy->reg_offset + GRF_EMMCPHY_CON6, + HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, + PHYCTRL_ENDLL_MASK, + PHYCTRL_ENDLL_SHIFT)); + + /* Already finish power_off above */ + if (on_off == PHYCTRL_PDB_PWR_OFF) + return 0; + + rate = clk_get_rate(rk_phy->emmcclk); + + if (rate != 0) { unsigned long ideal_rate; unsigned long diff; switch (rate) { - case 0 ... 74999999: + case 1 ... 74999999: ideal_rate = 50000000; freqsel = PHYCTRL_FREQSEL_50M; break; @@ -126,25 +147,6 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); } - /* - * Keep phyctrl_pdb and phyctrl_endll low to allow - * initialization of CALIO state M/C DFFs - */ - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF, - PHYCTRL_PDB_MASK, - PHYCTRL_PDB_SHIFT)); - regmap_write(rk_phy->reg_base, - rk_phy->reg_offset + GRF_EMMCPHY_CON6, - HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE, - PHYCTRL_ENDLL_MASK, - PHYCTRL_ENDLL_SHIFT)); - - /* Already finish power_off above */ - if (on_off == PHYCTRL_PDB_PWR_OFF) - return 0; - /* * According to the user manual, calpad calibration * cycle takes more than 2us without the minimal recommended @@ -183,6 +185,19 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE, PHYCTRL_ENDLL_MASK, PHYCTRL_ENDLL_SHIFT)); + + /* + * We turned on the DLL even though the rate was 0 because we the + * clock might be turned on later. ...but we can't wait for the DLL + * to lock when the rate is 0 because it will never lock with no + * input clock. + * + * Technically we should be checking the lock later when the clock + * is turned on, but for now we won't. + */ + if (rate == 0) + return 0; + /* * After enabling analog DLL circuits docs say that we need 10.2 us if * our source clock is at 50 MHz and that lock time scales linearly -- cgit v1.2.1 From 95cc46fee6e177668087f18549c07daec30c16c8 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 27 Jun 2016 10:39:27 -0700 Subject: phy: rockchip-emmc: Wait even longer for the DLL to lock Two times out of 2000 reboots I ran into the error message "rockchip_emmc_phy_power: dllrdy timeout". Presumably there is some corner case where the DLL just takes a little longer to timeout. Let's give it even more time to handle these corner cases. Signed-off-by: Douglas Anderson Acked-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson --- drivers/phy/phy-rockchip-emmc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'drivers/phy') diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c index a2aa6aca7dec..fd57345ffed2 100644 --- a/drivers/phy/phy-rockchip-emmc.c +++ b/drivers/phy/phy-rockchip-emmc.c @@ -206,8 +206,18 @@ static int rockchip_emmc_phy_power(struct phy *phy, bool on_off) * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms * Hopefully we won't be running at 100 kHz, but we should still make * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. We still busy wait here because: + * - In most cases it should be super fast. + * - This is not called lots during normal operation so it shouldn't + * be a power or performance problem to busy wait. We expect it + * only at boot / resume. In both cases, eMMC is probably on the + * critical path so busy waiting a little extra time should be OK. */ - timeout = jiffies + msecs_to_jiffies(10); + timeout = jiffies + msecs_to_jiffies(50); do { udelay(1); -- cgit v1.2.1