uboot/drivers/phy/rockchip/phy-rockchip-pcie.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only)
   2/*
   3 * Rockchip PCIe PHY driver
   4 *
   5 * Copyright (C) 2020 Amarula Solutions(India)
   6 * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
   7 * Copyright (C) 2016 ROCKCHIP, Inc.
   8 */
   9
  10#include <common.h>
  11#include <clk.h>
  12#include <dm.h>
  13#include <asm/global_data.h>
  14#include <dm/device_compat.h>
  15#include <generic-phy.h>
  16#include <reset.h>
  17#include <syscon.h>
  18#include <asm/gpio.h>
  19#include <asm/io.h>
  20#include <linux/iopoll.h>
  21#include <asm/arch-rockchip/clock.h>
  22
  23DECLARE_GLOBAL_DATA_PTR;
  24
  25/*
  26 * The higher 16-bit of this register is used for write protection
  27 * only if BIT(x + 16) set to 1 the BIT(x) can be written.
  28 */
  29#define HIWORD_UPDATE(val, mask, shift) \
  30                ((val) << (shift) | (mask) << ((shift) + 16))
  31
  32#define PHY_MAX_LANE_NUM      4
  33#define PHY_CFG_DATA_SHIFT    7
  34#define PHY_CFG_ADDR_SHIFT    1
  35#define PHY_CFG_DATA_MASK     0xf
  36#define PHY_CFG_ADDR_MASK     0x3f
  37#define PHY_CFG_RD_MASK       0x3ff
  38#define PHY_CFG_WR_ENABLE     1
  39#define PHY_CFG_WR_DISABLE    1
  40#define PHY_CFG_WR_SHIFT      0
  41#define PHY_CFG_WR_MASK       1
  42#define PHY_CFG_PLL_LOCK      0x10
  43#define PHY_CFG_CLK_TEST      0x10
  44#define PHY_CFG_CLK_SCC       0x12
  45#define PHY_CFG_SEPE_RATE     BIT(3)
  46#define PHY_CFG_PLL_100M      BIT(3)
  47#define PHY_PLL_LOCKED        BIT(9)
  48#define PHY_PLL_OUTPUT        BIT(10)
  49#define PHY_LANE_RX_DET_SHIFT 11
  50#define PHY_LANE_RX_DET_TH    0x1
  51#define PHY_LANE_IDLE_OFF     0x1
  52#define PHY_LANE_IDLE_MASK    0x1
  53#define PHY_LANE_IDLE_A_SHIFT 3
  54#define PHY_LANE_IDLE_B_SHIFT 4
  55#define PHY_LANE_IDLE_C_SHIFT 5
  56#define PHY_LANE_IDLE_D_SHIFT 6
  57
  58struct rockchip_pcie_phy_data {
  59        unsigned int pcie_conf;
  60        unsigned int pcie_status;
  61        unsigned int pcie_laneoff;
  62};
  63
  64struct rockchip_pcie_phy {
  65        void *reg_base;
  66        struct clk refclk;
  67        struct reset_ctl phy_rst;
  68        const struct rockchip_pcie_phy_data *data;
  69};
  70
  71static void phy_wr_cfg(struct rockchip_pcie_phy *priv, u32 addr, u32 data)
  72{
  73        u32 reg;
  74
  75        reg = HIWORD_UPDATE(data, PHY_CFG_DATA_MASK, PHY_CFG_DATA_SHIFT);
  76        reg |= HIWORD_UPDATE(addr, PHY_CFG_ADDR_MASK, PHY_CFG_ADDR_SHIFT);
  77        writel(reg, priv->reg_base + priv->data->pcie_conf);
  78
  79        udelay(1);
  80
  81        reg = HIWORD_UPDATE(PHY_CFG_WR_ENABLE,
  82                            PHY_CFG_WR_MASK,
  83                            PHY_CFG_WR_SHIFT);
  84        writel(reg, priv->reg_base + priv->data->pcie_conf);
  85
  86        udelay(1);
  87
  88        reg = HIWORD_UPDATE(PHY_CFG_WR_DISABLE,
  89                            PHY_CFG_WR_MASK,
  90                            PHY_CFG_WR_SHIFT);
  91        writel(reg, priv->reg_base + priv->data->pcie_conf);
  92}
  93
  94static int rockchip_pcie_phy_power_on(struct phy *phy)
  95{
  96        struct rockchip_pcie_phy *priv = dev_get_priv(phy->dev);
  97        int ret = 0;
  98        u32 reg, status;
  99
 100        ret = reset_deassert(&priv->phy_rst);
 101        if (ret) {
 102                dev_err(phy->dev, "failed to assert phy reset\n");
 103                return ret;
 104        }
 105
 106        reg = HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
 107                            PHY_CFG_ADDR_MASK,
 108                            PHY_CFG_ADDR_SHIFT);
 109        writel(reg, priv->reg_base + priv->data->pcie_conf);
 110
 111        reg = HIWORD_UPDATE(!PHY_LANE_IDLE_OFF,
 112                            PHY_LANE_IDLE_MASK,
 113                            PHY_LANE_IDLE_A_SHIFT);
 114        writel(reg, priv->reg_base + priv->data->pcie_laneoff);
 115
 116        ret = -EINVAL;
 117        ret = readl_poll_sleep_timeout(priv->reg_base + priv->data->pcie_status,
 118                                       status,
 119                                       status & PHY_PLL_LOCKED,
 120                                       20 * 1000,
 121                                       50);
 122        if (ret) {
 123                dev_err(phy->dev, "pll lock timeout!\n");
 124                goto err_pll_lock;
 125        }
 126
 127        phy_wr_cfg(priv, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
 128        phy_wr_cfg(priv, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
 129
 130        ret = -ETIMEDOUT;
 131        ret = readl_poll_sleep_timeout(priv->reg_base + priv->data->pcie_status,
 132                                       status,
 133                                       !(status & PHY_PLL_OUTPUT),
 134                                       20 * 1000,
 135                                       50);
 136        if (ret) {
 137                dev_err(phy->dev, "pll output enable timeout!\n");
 138                goto err_pll_lock;
 139        }
 140
 141        reg = HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
 142                            PHY_CFG_ADDR_MASK,
 143                            PHY_CFG_ADDR_SHIFT);
 144        writel(reg, priv->reg_base + priv->data->pcie_conf);
 145
 146        ret = -EINVAL;
 147        ret = readl_poll_sleep_timeout(priv->reg_base + priv->data->pcie_status,
 148                                       status,
 149                                       status & PHY_PLL_LOCKED,
 150                                       20 * 1000,
 151                                       50);
 152        if (ret) {
 153                dev_err(phy->dev, "pll relock timeout!\n");
 154                goto err_pll_lock;
 155        }
 156
 157        return 0;
 158
 159err_pll_lock:
 160        reset_assert(&priv->phy_rst);
 161        return ret;
 162}
 163
 164static int rockchip_pcie_phy_power_off(struct phy *phy)
 165{
 166        struct rockchip_pcie_phy *priv = dev_get_priv(phy->dev);
 167        int ret;
 168        u32 reg;
 169
 170        reg = HIWORD_UPDATE(PHY_LANE_IDLE_OFF,
 171                            PHY_LANE_IDLE_MASK,
 172                            PHY_LANE_IDLE_A_SHIFT);
 173        writel(reg, priv->reg_base + priv->data->pcie_laneoff);
 174
 175        ret = reset_assert(&priv->phy_rst);
 176        if (ret) {
 177                dev_err(phy->dev, "failed to assert phy reset\n");
 178                return ret;
 179        }
 180
 181        return 0;
 182}
 183
 184static int rockchip_pcie_phy_init(struct phy *phy)
 185{
 186        struct rockchip_pcie_phy *priv = dev_get_priv(phy->dev);
 187        int ret;
 188
 189        ret = clk_enable(&priv->refclk);
 190        if (ret) {
 191                dev_err(phy->dev, "failed to enable refclk clock\n");
 192                return ret;
 193        }
 194
 195        ret = reset_assert(&priv->phy_rst);
 196        if (ret) {
 197                dev_err(phy->dev, "failed to assert phy reset\n");
 198                goto err_reset;
 199        }
 200
 201        return 0;
 202
 203err_reset:
 204        clk_disable(&priv->refclk);
 205        return ret;
 206}
 207
 208static int rockchip_pcie_phy_exit(struct phy *phy)
 209{
 210        struct rockchip_pcie_phy *priv = dev_get_priv(phy->dev);
 211
 212        clk_disable(&priv->refclk);
 213
 214        return 0;
 215}
 216
 217static struct phy_ops rockchip_pcie_phy_ops = {
 218        .init = rockchip_pcie_phy_init,
 219        .power_on = rockchip_pcie_phy_power_on,
 220        .power_off = rockchip_pcie_phy_power_off,
 221        .exit = rockchip_pcie_phy_exit,
 222};
 223
 224static int rockchip_pcie_phy_probe(struct udevice *dev)
 225{
 226        struct rockchip_pcie_phy *priv = dev_get_priv(dev);
 227        int ret;
 228
 229        priv->data = (const struct rockchip_pcie_phy_data *)
 230                                                dev_get_driver_data(dev);
 231        if (!priv->data)
 232                return -EINVAL;
 233
 234        priv->reg_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
 235
 236        ret = clk_get_by_name(dev, "refclk", &priv->refclk);
 237        if (ret) {
 238                dev_err(dev, "failed to get refclk clock phandle\n");
 239                return ret;
 240        }
 241
 242        ret = reset_get_by_name(dev, "phy", &priv->phy_rst);
 243        if (ret) {
 244                dev_err(dev, "failed to get phy reset phandle\n");
 245                return ret;
 246        }
 247
 248        return 0;
 249}
 250
 251static const struct rockchip_pcie_phy_data rk3399_pcie_data = {
 252        .pcie_conf = 0xe220,
 253        .pcie_status = 0xe2a4,
 254        .pcie_laneoff = 0xe214,
 255};
 256
 257static const struct udevice_id rockchip_pcie_phy_ids[] = {
 258        {
 259                .compatible = "rockchip,rk3399-pcie-phy",
 260                .data = (ulong)&rk3399_pcie_data,
 261        },
 262        { /* sentile */ }
 263};
 264
 265U_BOOT_DRIVER(rockchip_pcie_phy) = {
 266        .name   = "rockchip_pcie_phy",
 267        .id     = UCLASS_PHY,
 268        .of_match = rockchip_pcie_phy_ids,
 269        .ops = &rockchip_pcie_phy_ops,
 270        .probe = rockchip_pcie_phy_probe,
 271        .priv_auto      = sizeof(struct rockchip_pcie_phy),
 272};
 273