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