linux/drivers/phy/rockchip/phy-rockchip-pcie.c
<<
>>
Prefs
   1/*
   2 * Rockchip PCIe PHY driver
   3 *
   4 * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
   5 * Copyright (C) 2016 ROCKCHIP, Inc.
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/clk.h>
  18#include <linux/delay.h>
  19#include <linux/io.h>
  20#include <linux/mfd/syscon.h>
  21#include <linux/module.h>
  22#include <linux/of.h>
  23#include <linux/of_address.h>
  24#include <linux/of_platform.h>
  25#include <linux/phy/phy.h>
  26#include <linux/platform_device.h>
  27#include <linux/regmap.h>
  28#include <linux/reset.h>
  29
  30/*
  31 * The higher 16-bit of this register is used for write protection
  32 * only if BIT(x + 16) set to 1 the BIT(x) can be written.
  33 */
  34#define HIWORD_UPDATE(val, mask, shift) \
  35                ((val) << (shift) | (mask) << ((shift) + 16))
  36
  37#define PHY_MAX_LANE_NUM      4
  38#define PHY_CFG_DATA_SHIFT    7
  39#define PHY_CFG_ADDR_SHIFT    1
  40#define PHY_CFG_DATA_MASK     0xf
  41#define PHY_CFG_ADDR_MASK     0x3f
  42#define PHY_CFG_RD_MASK       0x3ff
  43#define PHY_CFG_WR_ENABLE     1
  44#define PHY_CFG_WR_DISABLE    1
  45#define PHY_CFG_WR_SHIFT      0
  46#define PHY_CFG_WR_MASK       1
  47#define PHY_CFG_PLL_LOCK      0x10
  48#define PHY_CFG_CLK_TEST      0x10
  49#define PHY_CFG_CLK_SCC       0x12
  50#define PHY_CFG_SEPE_RATE     BIT(3)
  51#define PHY_CFG_PLL_100M      BIT(3)
  52#define PHY_PLL_LOCKED        BIT(9)
  53#define PHY_PLL_OUTPUT        BIT(10)
  54#define PHY_LANE_A_STATUS     0x30
  55#define PHY_LANE_B_STATUS     0x31
  56#define PHY_LANE_C_STATUS     0x32
  57#define PHY_LANE_D_STATUS     0x33
  58#define PHY_LANE_RX_DET_SHIFT 11
  59#define PHY_LANE_RX_DET_TH    0x1
  60#define PHY_LANE_IDLE_OFF     0x1
  61#define PHY_LANE_IDLE_MASK    0x1
  62#define PHY_LANE_IDLE_A_SHIFT 3
  63#define PHY_LANE_IDLE_B_SHIFT 4
  64#define PHY_LANE_IDLE_C_SHIFT 5
  65#define PHY_LANE_IDLE_D_SHIFT 6
  66
  67struct rockchip_pcie_data {
  68        unsigned int pcie_conf;
  69        unsigned int pcie_status;
  70        unsigned int pcie_laneoff;
  71};
  72
  73struct rockchip_pcie_phy {
  74        struct rockchip_pcie_data *phy_data;
  75        struct regmap *reg_base;
  76        struct reset_control *phy_rst;
  77        struct clk *clk_pciephy_ref;
  78};
  79
  80static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
  81                              u32 addr, u32 data)
  82{
  83        regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
  84                     HIWORD_UPDATE(data,
  85                                   PHY_CFG_DATA_MASK,
  86                                   PHY_CFG_DATA_SHIFT) |
  87                     HIWORD_UPDATE(addr,
  88                                   PHY_CFG_ADDR_MASK,
  89                                   PHY_CFG_ADDR_SHIFT));
  90        udelay(1);
  91        regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
  92                     HIWORD_UPDATE(PHY_CFG_WR_ENABLE,
  93                                   PHY_CFG_WR_MASK,
  94                                   PHY_CFG_WR_SHIFT));
  95        udelay(1);
  96        regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
  97                     HIWORD_UPDATE(PHY_CFG_WR_DISABLE,
  98                                   PHY_CFG_WR_MASK,
  99                                   PHY_CFG_WR_SHIFT));
 100}
 101
 102static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy,
 103                             u32 addr)
 104{
 105        u32 val;
 106
 107        regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
 108                     HIWORD_UPDATE(addr,
 109                                   PHY_CFG_RD_MASK,
 110                                   PHY_CFG_ADDR_SHIFT));
 111        regmap_read(rk_phy->reg_base,
 112                    rk_phy->phy_data->pcie_status,
 113                    &val);
 114        return val;
 115}
 116
 117static int rockchip_pcie_phy_power_off(struct phy *phy)
 118{
 119        struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
 120        int err = 0;
 121
 122        err = reset_control_assert(rk_phy->phy_rst);
 123        if (err) {
 124                dev_err(&phy->dev, "assert phy_rst err %d\n", err);
 125                return err;
 126        }
 127
 128        return 0;
 129}
 130
 131static int rockchip_pcie_phy_power_on(struct phy *phy)
 132{
 133        struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
 134        int err = 0;
 135        u32 status;
 136        unsigned long timeout;
 137
 138        err = reset_control_deassert(rk_phy->phy_rst);
 139        if (err) {
 140                dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
 141                return err;
 142        }
 143
 144        regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
 145                     HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
 146                                   PHY_CFG_ADDR_MASK,
 147                                   PHY_CFG_ADDR_SHIFT));
 148
 149        /*
 150         * No documented timeout value for phy operation below,
 151         * so we make it large enough here. And we use loop-break
 152         * method which should not be harmful.
 153         */
 154        timeout = jiffies + msecs_to_jiffies(1000);
 155
 156        err = -EINVAL;
 157        while (time_before(jiffies, timeout)) {
 158                regmap_read(rk_phy->reg_base,
 159                            rk_phy->phy_data->pcie_status,
 160                            &status);
 161                if (status & PHY_PLL_LOCKED) {
 162                        dev_dbg(&phy->dev, "pll locked!\n");
 163                        err = 0;
 164                        break;
 165                }
 166                msleep(20);
 167        }
 168
 169        if (err) {
 170                dev_err(&phy->dev, "pll lock timeout!\n");
 171                goto err_pll_lock;
 172        }
 173
 174        phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
 175        phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
 176
 177        err = -ETIMEDOUT;
 178        while (time_before(jiffies, timeout)) {
 179                regmap_read(rk_phy->reg_base,
 180                            rk_phy->phy_data->pcie_status,
 181                            &status);
 182                if (!(status & PHY_PLL_OUTPUT)) {
 183                        dev_dbg(&phy->dev, "pll output enable done!\n");
 184                        err = 0;
 185                        break;
 186                }
 187                msleep(20);
 188        }
 189
 190        if (err) {
 191                dev_err(&phy->dev, "pll output enable timeout!\n");
 192                goto err_pll_lock;
 193        }
 194
 195        regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
 196                     HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
 197                                   PHY_CFG_ADDR_MASK,
 198                                   PHY_CFG_ADDR_SHIFT));
 199        err = -EINVAL;
 200        while (time_before(jiffies, timeout)) {
 201                regmap_read(rk_phy->reg_base,
 202                            rk_phy->phy_data->pcie_status,
 203                            &status);
 204                if (status & PHY_PLL_LOCKED) {
 205                        dev_dbg(&phy->dev, "pll relocked!\n");
 206                        err = 0;
 207                        break;
 208                }
 209                msleep(20);
 210        }
 211
 212        if (err) {
 213                dev_err(&phy->dev, "pll relock timeout!\n");
 214                goto err_pll_lock;
 215        }
 216
 217        return 0;
 218
 219err_pll_lock:
 220        reset_control_assert(rk_phy->phy_rst);
 221        return err;
 222}
 223
 224static int rockchip_pcie_phy_init(struct phy *phy)
 225{
 226        struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
 227        int err = 0;
 228
 229        err = clk_prepare_enable(rk_phy->clk_pciephy_ref);
 230        if (err) {
 231                dev_err(&phy->dev, "Fail to enable pcie ref clock.\n");
 232                goto err_refclk;
 233        }
 234
 235        err = reset_control_assert(rk_phy->phy_rst);
 236        if (err) {
 237                dev_err(&phy->dev, "assert phy_rst err %d\n", err);
 238                goto err_reset;
 239        }
 240
 241        return err;
 242
 243err_reset:
 244        clk_disable_unprepare(rk_phy->clk_pciephy_ref);
 245err_refclk:
 246        return err;
 247}
 248
 249static int rockchip_pcie_phy_exit(struct phy *phy)
 250{
 251        struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
 252
 253        clk_disable_unprepare(rk_phy->clk_pciephy_ref);
 254
 255        return 0;
 256}
 257
 258static const struct phy_ops ops = {
 259        .init           = rockchip_pcie_phy_init,
 260        .exit           = rockchip_pcie_phy_exit,
 261        .power_on       = rockchip_pcie_phy_power_on,
 262        .power_off      = rockchip_pcie_phy_power_off,
 263        .owner          = THIS_MODULE,
 264};
 265
 266static const struct rockchip_pcie_data rk3399_pcie_data = {
 267        .pcie_conf = 0xe220,
 268        .pcie_status = 0xe2a4,
 269        .pcie_laneoff = 0xe214,
 270};
 271
 272static const struct of_device_id rockchip_pcie_phy_dt_ids[] = {
 273        {
 274                .compatible = "rockchip,rk3399-pcie-phy",
 275                .data = &rk3399_pcie_data,
 276        },
 277        {}
 278};
 279
 280MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids);
 281
 282static int rockchip_pcie_phy_probe(struct platform_device *pdev)
 283{
 284        struct device *dev = &pdev->dev;
 285        struct rockchip_pcie_phy *rk_phy;
 286        struct phy *generic_phy;
 287        struct phy_provider *phy_provider;
 288        struct regmap *grf;
 289        const struct of_device_id *of_id;
 290
 291        grf = syscon_node_to_regmap(dev->parent->of_node);
 292        if (IS_ERR(grf)) {
 293                dev_err(dev, "Cannot find GRF syscon\n");
 294                return PTR_ERR(grf);
 295        }
 296
 297        rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
 298        if (!rk_phy)
 299                return -ENOMEM;
 300
 301        of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev);
 302        if (!of_id)
 303                return -EINVAL;
 304
 305        rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data;
 306        rk_phy->reg_base = grf;
 307
 308        rk_phy->phy_rst = devm_reset_control_get(dev, "phy");
 309        if (IS_ERR(rk_phy->phy_rst)) {
 310                if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER)
 311                        dev_err(dev,
 312                                "missing phy property for reset controller\n");
 313                return PTR_ERR(rk_phy->phy_rst);
 314        }
 315
 316        rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk");
 317        if (IS_ERR(rk_phy->clk_pciephy_ref)) {
 318                dev_err(dev, "refclk not found.\n");
 319                return PTR_ERR(rk_phy->clk_pciephy_ref);
 320        }
 321
 322        generic_phy = devm_phy_create(dev, dev->of_node, &ops);
 323        if (IS_ERR(generic_phy)) {
 324                dev_err(dev, "failed to create PHY\n");
 325                return PTR_ERR(generic_phy);
 326        }
 327
 328        phy_set_drvdata(generic_phy, rk_phy);
 329        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 330
 331        return PTR_ERR_OR_ZERO(phy_provider);
 332}
 333
 334static struct platform_driver rockchip_pcie_driver = {
 335        .probe          = rockchip_pcie_phy_probe,
 336        .driver         = {
 337                .name   = "rockchip-pcie-phy",
 338                .of_match_table = rockchip_pcie_phy_dt_ids,
 339        },
 340};
 341
 342module_platform_driver(rockchip_pcie_driver);
 343
 344MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
 345MODULE_DESCRIPTION("Rockchip PCIe PHY driver");
 346MODULE_LICENSE("GPL v2");
 347