[PATCH 09/11] phy: rockchip-emmc: Set phyctrl_frqsel based on card clock
Shawn Lin
shawn.lin at rock-chips.com
Mon Jun 13 01:54:41 PDT 2016
On 2016/6/8 6:44, Douglas Anderson wrote:
> 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.
Thanks for doing this, but I think we should limit freq if assigning
max-frequency from DT more explicitly since the PHY could only support
50/100/150/200M for hs200/400? Otherwise I can't say if the PHY could
always work well. i.e if geting 125000000 ... 174999999 , you code make
the phyctrl_frqsel to be 150M, so it will be 15% missing of precision
for tuning delay element. Ideally, the sample point should be in the
middle of window, but I don't know if there is a bad HW design makes
the window small enough which need special care about it.
>
> [1]: https://arasan.com/wp-content/media/eMMC-5-1-Total-Solution_Rev-1-3.pdf
>
> Signed-off-by: Douglas Anderson <dianders at chromium.org>
> ---
> drivers/phy/phy-rockchip-emmc.c | 74 +++++++++++++++++++++++++++++++----------
> 1 file changed, 57 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
> index 8336053aea5c..0fce7359d468 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 <linux/clk.h>
> #include <linux/delay.h>
> #include <linux/mfd/syscon.h>
> #include <linux/module.h>
> @@ -78,16 +79,61 @@
> 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;
>
> /*
> + * We purposely get the clock here and not in probe to avoid the
> + * circular dependency problem. We expect:
> + * - PHY driver to probe
> + * - USB driver to start probe
> + * - USB driver to register it's clock
> + * - USB driver to get the PHY
> + * - USB driver to power on the PHY
USB?
> + */
> + if (!rk_phy->emmcclk) {
> + rk_phy->emmcclk = devm_clk_get(&phy->dev, "emmcclk");
> +
> + /* Don't expect defer at this point; try next time */
> + if (PTR_ERR(rk_phy->emmcclk) == -EPROBE_DEFER) {
> + dev_warn(&phy->dev, "Unexpected emmcclk defer\n");
> + rk_phy->emmcclk = NULL;
> + }
> + }
> +
> + if (!IS_ERR_OR_NULL(rk_phy->emmcclk)) {
> + unsigned long rate = clk_get_rate(rk_phy->emmcclk);
> +
> + switch (rate) {
> + case 0 ... 74999999:
> + /* Nominal 50 MHz */
> + freqsel = PHYCTRL_FREQSEL_50M;
> + break;
> + case 75000000 ... 124999999:
> + /* Nominal 100 MHz */
> + freqsel = PHYCTRL_FREQSEL_100M;
> + break;
> + case 125000000 ... 174999999:
> + /* Nominal 150 MHz */
> + freqsel = PHYCTRL_FREQSEL_150M;
> + break;
> + default:
> + if (rate > 200000000)
> + dev_warn(&phy->dev, "Unsupported rate: %lu\n",
> + rate);
> + break;
> + };
> + }
> +
> + /*
> * Keep phyctrl_pdb and phyctrl_endll low to allow
> * initialization of CALIO state M/C DFFs
> */
> @@ -132,6 +178,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,
> @@ -167,15 +220,8 @@ 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(phy, PHYCTRL_PDB_PWR_OFF);
> }
>
> static int rockchip_emmc_phy_power_on(struct phy *phy)
> @@ -183,12 +229,6 @@ 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,
> @@ -212,7 +252,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);
> + ret = rockchip_emmc_phy_power(phy, PHYCTRL_PDB_PWR_ON);
> if (ret)
> return ret;
>
>
--
Best Regards
Shawn Lin
More information about the Linux-rockchip
mailing list