515 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * phy-uniphier-ahci.c - PHY driver for UniPhier AHCI controller4 * Copyright 2016-2020, Socionext Inc.5 * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com>6 */7 8#include <linux/bitfield.h>9#include <linux/bitops.h>10#include <linux/clk.h>11#include <linux/iopoll.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/of_platform.h>15#include <linux/phy/phy.h>16#include <linux/platform_device.h>17#include <linux/reset.h>18 19struct uniphier_ahciphy_priv {20 struct device *dev;21 void __iomem *base;22 struct clk *clk, *clk_parent, *clk_parent_gio;23 struct reset_control *rst, *rst_parent, *rst_parent_gio;24 struct reset_control *rst_pm, *rst_tx, *rst_rx;25 const struct uniphier_ahciphy_soc_data *data;26};27 28struct uniphier_ahciphy_soc_data {29 int (*init)(struct uniphier_ahciphy_priv *priv);30 int (*power_on)(struct uniphier_ahciphy_priv *priv);31 int (*power_off)(struct uniphier_ahciphy_priv *priv);32 bool is_legacy;33 bool is_ready_high;34 bool is_phy_clk;35};36 37/* for Pro4 */38#define CKCTRL0 0x039#define CKCTRL0_CK_OFF BIT(9)40#define CKCTRL0_NCY_MASK GENMASK(8, 4)41#define CKCTRL0_NCY5_MASK GENMASK(3, 2)42#define CKCTRL0_PRESCALE_MASK GENMASK(1, 0)43#define CKCTRL1 0x444#define CKCTRL1_LOS_LVL_MASK GENMASK(20, 16)45#define CKCTRL1_TX_LVL_MASK GENMASK(12, 8)46#define RXTXCTRL 0x847#define RXTXCTRL_RX_EQ_VALL_MASK GENMASK(31, 29)48#define RXTXCTRL_RX_DPLL_MODE_MASK GENMASK(28, 26)49#define RXTXCTRL_TX_ATTEN_MASK GENMASK(14, 12)50#define RXTXCTRL_TX_BOOST_MASK GENMASK(11, 8)51#define RXTXCTRL_TX_EDGERATE_MASK GENMASK(3, 2)52#define RXTXCTRL_TX_CKO_EN BIT(0)53#define RSTPWR 0x3054#define RSTPWR_RX_EN_VAL BIT(18)55 56/* for PXs2/PXs3 */57#define CKCTRL 0x058#define CKCTRL_P0_READY BIT(15)59#define CKCTRL_P0_RESET BIT(10)60#define CKCTRL_REF_SSP_EN BIT(9)61#define TXCTRL0 0x462#define TXCTRL0_AMP_G3_MASK GENMASK(22, 16)63#define TXCTRL0_AMP_G2_MASK GENMASK(14, 8)64#define TXCTRL0_AMP_G1_MASK GENMASK(6, 0)65#define TXCTRL1 0x866#define TXCTRL1_DEEMPH_G3_MASK GENMASK(21, 16)67#define TXCTRL1_DEEMPH_G2_MASK GENMASK(13, 8)68#define TXCTRL1_DEEMPH_G1_MASK GENMASK(5, 0)69#define RXCTRL 0xc70#define RXCTRL_LOS_LVL_MASK GENMASK(20, 16)71#define RXCTRL_LOS_BIAS_MASK GENMASK(10, 8)72#define RXCTRL_RX_EQ_MASK GENMASK(2, 0)73 74static int uniphier_ahciphy_pro4_init(struct uniphier_ahciphy_priv *priv)75{76 u32 val;77 78 /* set phy MPLL parameters */79 val = readl(priv->base + CKCTRL0);80 val &= ~CKCTRL0_NCY_MASK;81 val |= FIELD_PREP(CKCTRL0_NCY_MASK, 0x6);82 val &= ~CKCTRL0_NCY5_MASK;83 val |= FIELD_PREP(CKCTRL0_NCY5_MASK, 0x2);84 val &= ~CKCTRL0_PRESCALE_MASK;85 val |= FIELD_PREP(CKCTRL0_PRESCALE_MASK, 0x1);86 writel(val, priv->base + CKCTRL0);87 88 /* setup phy control parameters */89 val = readl(priv->base + CKCTRL1);90 val &= ~CKCTRL1_LOS_LVL_MASK;91 val |= FIELD_PREP(CKCTRL1_LOS_LVL_MASK, 0x10);92 val &= ~CKCTRL1_TX_LVL_MASK;93 val |= FIELD_PREP(CKCTRL1_TX_LVL_MASK, 0x06);94 writel(val, priv->base + CKCTRL1);95 96 val = readl(priv->base + RXTXCTRL);97 val &= ~RXTXCTRL_RX_EQ_VALL_MASK;98 val |= FIELD_PREP(RXTXCTRL_RX_EQ_VALL_MASK, 0x6);99 val &= ~RXTXCTRL_RX_DPLL_MODE_MASK;100 val |= FIELD_PREP(RXTXCTRL_RX_DPLL_MODE_MASK, 0x3);101 val &= ~RXTXCTRL_TX_ATTEN_MASK;102 val |= FIELD_PREP(RXTXCTRL_TX_ATTEN_MASK, 0x3);103 val &= ~RXTXCTRL_TX_BOOST_MASK;104 val |= FIELD_PREP(RXTXCTRL_TX_BOOST_MASK, 0x5);105 val &= ~RXTXCTRL_TX_EDGERATE_MASK;106 val |= FIELD_PREP(RXTXCTRL_TX_EDGERATE_MASK, 0x0);107 writel(val, priv->base + RXTXCTRL);108 109 return 0;110}111 112static int uniphier_ahciphy_pro4_power_on(struct uniphier_ahciphy_priv *priv)113{114 u32 val;115 int ret;116 117 /* enable reference clock for phy */118 val = readl(priv->base + CKCTRL0);119 val &= ~CKCTRL0_CK_OFF;120 writel(val, priv->base + CKCTRL0);121 122 /* enable TX clock */123 val = readl(priv->base + RXTXCTRL);124 val |= RXTXCTRL_TX_CKO_EN;125 writel(val, priv->base + RXTXCTRL);126 127 /* wait until RX is ready */128 ret = readl_poll_timeout(priv->base + RSTPWR, val,129 !(val & RSTPWR_RX_EN_VAL), 200, 2000);130 if (ret) {131 dev_err(priv->dev, "Failed to check whether Rx is ready\n");132 goto out_disable_clock;133 }134 135 /* release all reset */136 ret = reset_control_deassert(priv->rst_pm);137 if (ret) {138 dev_err(priv->dev, "Failed to release PM reset\n");139 goto out_disable_clock;140 }141 142 ret = reset_control_deassert(priv->rst_tx);143 if (ret) {144 dev_err(priv->dev, "Failed to release Tx reset\n");145 goto out_reset_pm_assert;146 }147 148 ret = reset_control_deassert(priv->rst_rx);149 if (ret) {150 dev_err(priv->dev, "Failed to release Rx reset\n");151 goto out_reset_tx_assert;152 }153 154 return 0;155 156out_reset_tx_assert:157 reset_control_assert(priv->rst_tx);158out_reset_pm_assert:159 reset_control_assert(priv->rst_pm);160 161out_disable_clock:162 /* disable TX clock */163 val = readl(priv->base + RXTXCTRL);164 val &= ~RXTXCTRL_TX_CKO_EN;165 writel(val, priv->base + RXTXCTRL);166 167 /* disable reference clock for phy */168 val = readl(priv->base + CKCTRL0);169 val |= CKCTRL0_CK_OFF;170 writel(val, priv->base + CKCTRL0);171 172 return ret;173}174 175static int uniphier_ahciphy_pro4_power_off(struct uniphier_ahciphy_priv *priv)176{177 u32 val;178 179 reset_control_assert(priv->rst_rx);180 reset_control_assert(priv->rst_tx);181 reset_control_assert(priv->rst_pm);182 183 /* disable TX clock */184 val = readl(priv->base + RXTXCTRL);185 val &= ~RXTXCTRL_TX_CKO_EN;186 writel(val, priv->base + RXTXCTRL);187 188 /* disable reference clock for phy */189 val = readl(priv->base + CKCTRL0);190 val |= CKCTRL0_CK_OFF;191 writel(val, priv->base + CKCTRL0);192 193 return 0;194}195 196static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv,197 bool enable)198{199 u32 val;200 201 val = readl(priv->base + CKCTRL);202 203 if (enable) {204 val |= CKCTRL_REF_SSP_EN;205 writel(val, priv->base + CKCTRL);206 val &= ~CKCTRL_P0_RESET;207 writel(val, priv->base + CKCTRL);208 } else {209 val |= CKCTRL_P0_RESET;210 writel(val, priv->base + CKCTRL);211 val &= ~CKCTRL_REF_SSP_EN;212 writel(val, priv->base + CKCTRL);213 }214}215 216static int uniphier_ahciphy_pxs2_power_on(struct uniphier_ahciphy_priv *priv)217{218 int ret;219 u32 val;220 221 uniphier_ahciphy_pxs2_enable(priv, true);222 223 /* wait until PLL is ready */224 if (priv->data->is_ready_high)225 ret = readl_poll_timeout(priv->base + CKCTRL, val,226 (val & CKCTRL_P0_READY), 200, 400);227 else228 ret = readl_poll_timeout(priv->base + CKCTRL, val,229 !(val & CKCTRL_P0_READY), 200, 400);230 if (ret) {231 dev_err(priv->dev, "Failed to check whether PHY PLL is ready\n");232 uniphier_ahciphy_pxs2_enable(priv, false);233 }234 235 return ret;236}237 238static int uniphier_ahciphy_pxs2_power_off(struct uniphier_ahciphy_priv *priv)239{240 uniphier_ahciphy_pxs2_enable(priv, false);241 242 return 0;243}244 245static int uniphier_ahciphy_pxs3_init(struct uniphier_ahciphy_priv *priv)246{247 int i;248 u32 val;249 250 /* setup port parameter */251 val = readl(priv->base + TXCTRL0);252 val &= ~TXCTRL0_AMP_G3_MASK;253 val |= FIELD_PREP(TXCTRL0_AMP_G3_MASK, 0x73);254 val &= ~TXCTRL0_AMP_G2_MASK;255 val |= FIELD_PREP(TXCTRL0_AMP_G2_MASK, 0x46);256 val &= ~TXCTRL0_AMP_G1_MASK;257 val |= FIELD_PREP(TXCTRL0_AMP_G1_MASK, 0x42);258 writel(val, priv->base + TXCTRL0);259 260 val = readl(priv->base + TXCTRL1);261 val &= ~TXCTRL1_DEEMPH_G3_MASK;262 val |= FIELD_PREP(TXCTRL1_DEEMPH_G3_MASK, 0x23);263 val &= ~TXCTRL1_DEEMPH_G2_MASK;264 val |= FIELD_PREP(TXCTRL1_DEEMPH_G2_MASK, 0x05);265 val &= ~TXCTRL1_DEEMPH_G1_MASK;266 val |= FIELD_PREP(TXCTRL1_DEEMPH_G1_MASK, 0x05);267 268 val = readl(priv->base + RXCTRL);269 val &= ~RXCTRL_LOS_LVL_MASK;270 val |= FIELD_PREP(RXCTRL_LOS_LVL_MASK, 0x9);271 val &= ~RXCTRL_LOS_BIAS_MASK;272 val |= FIELD_PREP(RXCTRL_LOS_BIAS_MASK, 0x2);273 val &= ~RXCTRL_RX_EQ_MASK;274 val |= FIELD_PREP(RXCTRL_RX_EQ_MASK, 0x1);275 276 /* dummy read 25 times to make a wait time for the phy to stabilize */277 for (i = 0; i < 25; i++)278 readl(priv->base + CKCTRL);279 280 return 0;281}282 283static int uniphier_ahciphy_init(struct phy *phy)284{285 struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);286 int ret;287 288 ret = clk_prepare_enable(priv->clk_parent_gio);289 if (ret)290 return ret;291 292 ret = clk_prepare_enable(priv->clk_parent);293 if (ret)294 goto out_clk_gio_disable;295 296 ret = reset_control_deassert(priv->rst_parent_gio);297 if (ret)298 goto out_clk_disable;299 300 ret = reset_control_deassert(priv->rst_parent);301 if (ret)302 goto out_rst_gio_assert;303 304 if (priv->data->init) {305 ret = priv->data->init(priv);306 if (ret)307 goto out_rst_assert;308 }309 310 return 0;311 312out_rst_assert:313 reset_control_assert(priv->rst_parent);314out_rst_gio_assert:315 reset_control_assert(priv->rst_parent_gio);316out_clk_disable:317 clk_disable_unprepare(priv->clk_parent);318out_clk_gio_disable:319 clk_disable_unprepare(priv->clk_parent_gio);320 321 return ret;322}323 324static int uniphier_ahciphy_exit(struct phy *phy)325{326 struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);327 328 reset_control_assert(priv->rst_parent);329 reset_control_assert(priv->rst_parent_gio);330 clk_disable_unprepare(priv->clk_parent);331 clk_disable_unprepare(priv->clk_parent_gio);332 333 return 0;334}335 336static int uniphier_ahciphy_power_on(struct phy *phy)337{338 struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);339 int ret = 0;340 341 ret = clk_prepare_enable(priv->clk);342 if (ret)343 return ret;344 345 ret = reset_control_deassert(priv->rst);346 if (ret)347 goto out_clk_disable;348 349 if (priv->data->power_on) {350 ret = priv->data->power_on(priv);351 if (ret)352 goto out_reset_assert;353 }354 355 return 0;356 357out_reset_assert:358 reset_control_assert(priv->rst);359out_clk_disable:360 clk_disable_unprepare(priv->clk);361 362 return ret;363}364 365static int uniphier_ahciphy_power_off(struct phy *phy)366{367 struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy);368 int ret = 0;369 370 if (priv->data->power_off)371 ret = priv->data->power_off(priv);372 373 reset_control_assert(priv->rst);374 clk_disable_unprepare(priv->clk);375 376 return ret;377}378 379static const struct phy_ops uniphier_ahciphy_ops = {380 .init = uniphier_ahciphy_init,381 .exit = uniphier_ahciphy_exit,382 .power_on = uniphier_ahciphy_power_on,383 .power_off = uniphier_ahciphy_power_off,384 .owner = THIS_MODULE,385};386 387static int uniphier_ahciphy_probe(struct platform_device *pdev)388{389 struct device *dev = &pdev->dev;390 struct uniphier_ahciphy_priv *priv;391 struct phy *phy;392 struct phy_provider *phy_provider;393 394 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);395 if (!priv)396 return -ENOMEM;397 398 priv->dev = dev;399 priv->data = of_device_get_match_data(dev);400 if (WARN_ON(!priv->data))401 return -EINVAL;402 403 priv->base = devm_platform_ioremap_resource(pdev, 0);404 if (IS_ERR(priv->base))405 return PTR_ERR(priv->base);406 407 priv->clk_parent = devm_clk_get(dev, "link");408 if (IS_ERR(priv->clk_parent))409 return PTR_ERR(priv->clk_parent);410 411 if (priv->data->is_phy_clk) {412 priv->clk = devm_clk_get(dev, "phy");413 if (IS_ERR(priv->clk))414 return PTR_ERR(priv->clk);415 }416 417 priv->rst_parent = devm_reset_control_get_shared(dev, "link");418 if (IS_ERR(priv->rst_parent))419 return PTR_ERR(priv->rst_parent);420 421 priv->rst = devm_reset_control_get_shared(dev, "phy");422 if (IS_ERR(priv->rst))423 return PTR_ERR(priv->rst);424 425 if (priv->data->is_legacy) {426 priv->clk_parent_gio = devm_clk_get(dev, "gio");427 if (IS_ERR(priv->clk_parent_gio))428 return PTR_ERR(priv->clk_parent_gio);429 priv->rst_parent_gio =430 devm_reset_control_get_shared(dev, "gio");431 if (IS_ERR(priv->rst_parent_gio))432 return PTR_ERR(priv->rst_parent_gio);433 434 priv->rst_pm = devm_reset_control_get_shared(dev, "pm");435 if (IS_ERR(priv->rst_pm))436 return PTR_ERR(priv->rst_pm);437 438 priv->rst_tx = devm_reset_control_get_shared(dev, "tx");439 if (IS_ERR(priv->rst_tx))440 return PTR_ERR(priv->rst_tx);441 442 priv->rst_rx = devm_reset_control_get_shared(dev, "rx");443 if (IS_ERR(priv->rst_rx))444 return PTR_ERR(priv->rst_rx);445 }446 447 phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops);448 if (IS_ERR(phy)) {449 dev_err(dev, "failed to create phy\n");450 return PTR_ERR(phy);451 }452 453 phy_set_drvdata(phy, priv);454 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);455 if (IS_ERR(phy_provider))456 return PTR_ERR(phy_provider);457 458 return 0;459}460 461static const struct uniphier_ahciphy_soc_data uniphier_pro4_data = {462 .init = uniphier_ahciphy_pro4_init,463 .power_on = uniphier_ahciphy_pro4_power_on,464 .power_off = uniphier_ahciphy_pro4_power_off,465 .is_legacy = true,466 .is_phy_clk = false,467};468 469static const struct uniphier_ahciphy_soc_data uniphier_pxs2_data = {470 .power_on = uniphier_ahciphy_pxs2_power_on,471 .power_off = uniphier_ahciphy_pxs2_power_off,472 .is_legacy = false,473 .is_ready_high = false,474 .is_phy_clk = false,475};476 477static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = {478 .init = uniphier_ahciphy_pxs3_init,479 .power_on = uniphier_ahciphy_pxs2_power_on,480 .power_off = uniphier_ahciphy_pxs2_power_off,481 .is_legacy = false,482 .is_ready_high = true,483 .is_phy_clk = true,484};485 486static const struct of_device_id uniphier_ahciphy_match[] = {487 {488 .compatible = "socionext,uniphier-pro4-ahci-phy",489 .data = &uniphier_pro4_data,490 },491 {492 .compatible = "socionext,uniphier-pxs2-ahci-phy",493 .data = &uniphier_pxs2_data,494 },495 {496 .compatible = "socionext,uniphier-pxs3-ahci-phy",497 .data = &uniphier_pxs3_data,498 },499 { /* Sentinel */ },500};501MODULE_DEVICE_TABLE(of, uniphier_ahciphy_match);502 503static struct platform_driver uniphier_ahciphy_driver = {504 .probe = uniphier_ahciphy_probe,505 .driver = {506 .name = "uniphier-ahci-phy",507 .of_match_table = uniphier_ahciphy_match,508 },509};510module_platform_driver(uniphier_ahciphy_driver);511 512MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");513MODULE_DESCRIPTION("UniPhier PHY driver for AHCI controller");514MODULE_LICENSE("GPL v2");515