blob: 44ca4bc7919e31b543f11f45733a6df092c37696 [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>
Simon Glass401d1c42020-10-30 21:38:53 -060013#include <asm/global_data.h>
Jagan Teki7bdeb4e2020-07-09 23:41:01 +053014#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) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400102 dev_err(phy->dev, "failed to assert phy reset\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530103 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) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400123 dev_err(phy->dev, "pll lock timeout!\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530124 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) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400137 dev_err(phy->dev, "pll output enable timeout!\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530138 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) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400153 dev_err(phy->dev, "pll relock timeout!\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530154 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) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400177 dev_err(phy->dev, "failed to assert phy reset\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530178 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) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400191 dev_err(phy->dev, "failed to enable refclk clock\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530192 return ret;
193 }
194
195 ret = reset_assert(&priv->phy_rst);
196 if (ret) {
Sean Andersone9e1bd12020-09-15 10:45:03 -0400197 dev_err(phy->dev, "failed to assert phy reset\n");
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530198 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,
Simon Glass41575d82020-12-03 16:55:17 -0700271 .priv_auto = sizeof(struct rockchip_pcie_phy),
Jagan Teki7bdeb4e2020-07-09 23:41:01 +0530272};