pci: Add Rockchip PCIe PHY controller driver
[oweals/u-boot.git] / drivers / pci / pcie_rockchip_phy.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Rockchip PCIe PHY driver
4  *
5  * Copyright (c) 2016 Rockchip, Inc.
6  * Copyright (c) 2020 Amarula Solutions(India)
7  */
8
9 #include <common.h>
10 #include <clk.h>
11 #include <dm.h>
12 #include <dm/device_compat.h>
13 #include <reset.h>
14 #include <syscon.h>
15 #include <asm/gpio.h>
16 #include <asm/io.h>
17 #include <linux/iopoll.h>
18 #include <asm/arch-rockchip/clock.h>
19
20 #include "pcie_rockchip.h"
21
22 DECLARE_GLOBAL_DATA_PTR;
23
24 static void phy_wr_cfg(struct rockchip_pcie_phy *phy, u32 addr, u32 data)
25 {
26         u32 reg;
27
28         reg = HIWORD_UPDATE_MASK(data, PHY_CFG_DATA_MASK, PHY_CFG_DATA_SHIFT);
29         reg |= HIWORD_UPDATE_MASK(addr, PHY_CFG_ADDR_MASK, PHY_CFG_ADDR_SHIFT);
30         writel(reg, phy->reg_base + PCIE_PHY_CONF);
31
32         udelay(1);
33
34         reg = HIWORD_UPDATE_MASK(PHY_CFG_WR_ENABLE,
35                                  PHY_CFG_WR_MASK,
36                                  PHY_CFG_WR_SHIFT);
37         writel(reg, phy->reg_base + PCIE_PHY_CONF);
38
39         udelay(1);
40
41         reg = HIWORD_UPDATE_MASK(PHY_CFG_WR_DISABLE,
42                                  PHY_CFG_WR_MASK,
43                                  PHY_CFG_WR_SHIFT);
44         writel(reg, phy->reg_base + PCIE_PHY_CONF);
45 }
46
47 static int rockchip_pcie_phy_power_on(struct rockchip_pcie_phy *phy)
48 {
49         int ret = 0;
50         u32 reg, status;
51
52         ret = reset_deassert(&phy->phy_rst);
53         if (ret) {
54                 dev_err(dev, "failed to assert phy reset\n");
55                 return ret;
56         }
57
58         reg = HIWORD_UPDATE_MASK(PHY_CFG_PLL_LOCK,
59                                  PHY_CFG_ADDR_MASK,
60                                  PHY_CFG_ADDR_SHIFT);
61         writel(reg, phy->reg_base + PCIE_PHY_CONF);
62
63         reg = HIWORD_UPDATE_MASK(!PHY_LANE_IDLE_OFF,
64                                  PHY_LANE_IDLE_MASK,
65                                  PHY_LANE_IDLE_A_SHIFT);
66         writel(reg, phy->reg_base + PCIE_PHY_LANEOFF);
67
68         ret = -EINVAL;
69         ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
70                                        status,
71                                        status & PHY_PLL_LOCKED,
72                                        20 * 1000,
73                                        50);
74         if (ret) {
75                 dev_err(&phy->dev, "pll lock timeout!\n");
76                 goto err_pll_lock;
77         }
78
79         phy_wr_cfg(phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
80         phy_wr_cfg(phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
81
82         ret = -ETIMEDOUT;
83         ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
84                                        status,
85                                        !(status & PHY_PLL_OUTPUT),
86                                        20 * 1000,
87                                        50);
88         if (ret) {
89                 dev_err(&phy->dev, "pll output enable timeout!\n");
90                 goto err_pll_lock;
91         }
92
93         reg = HIWORD_UPDATE_MASK(PHY_CFG_PLL_LOCK,
94                                  PHY_CFG_ADDR_MASK,
95                                  PHY_CFG_ADDR_SHIFT);
96         writel(reg, phy->reg_base + PCIE_PHY_CONF);
97
98         ret = -EINVAL;
99         ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
100                                        status,
101                                        status & PHY_PLL_LOCKED,
102                                        20 * 1000,
103                                        50);
104         if (ret) {
105                 dev_err(&phy->dev, "pll relock timeout!\n");
106                 goto err_pll_lock;
107         }
108
109         return 0;
110
111 err_pll_lock:
112         reset_assert(&phy->phy_rst);
113         return ret;
114 }
115
116 static int rockchip_pcie_phy_power_off(struct rockchip_pcie_phy *phy)
117 {
118         int ret;
119         u32 reg;
120
121         reg = HIWORD_UPDATE_MASK(PHY_LANE_IDLE_OFF,
122                                  PHY_LANE_IDLE_MASK,
123                                  PHY_LANE_IDLE_A_SHIFT);
124         writel(reg, phy->reg_base + PCIE_PHY_LANEOFF);
125
126         ret = reset_assert(&phy->phy_rst);
127         if (ret) {
128                 dev_err(dev, "failed to assert phy reset\n");
129                 return ret;
130         }
131
132         return 0;
133 }
134
135 static int rockchip_pcie_phy_init(struct rockchip_pcie_phy *phy)
136 {
137         int ret;
138
139         ret = clk_enable(&phy->refclk);
140         if (ret) {
141                 dev_err(dev, "failed to enable refclk clock\n");
142                 return ret;
143         }
144
145         ret = reset_assert(&phy->phy_rst);
146         if (ret) {
147                 dev_err(dev, "failed to assert phy reset\n");
148                 goto err_reset;
149         }
150
151         return 0;
152
153 err_reset:
154         clk_disable(&phy->refclk);
155         return ret;
156 }
157
158 static int rockchip_pcie_phy_exit(struct rockchip_pcie_phy *phy)
159 {
160         clk_disable(&phy->refclk);
161
162         return 0;
163 }
164
165 static struct rockchip_pcie_phy_ops pcie_phy_ops = {
166         .init = rockchip_pcie_phy_init,
167         .power_on = rockchip_pcie_phy_power_on,
168         .power_off = rockchip_pcie_phy_power_off,
169         .exit = rockchip_pcie_phy_exit,
170 };
171
172 int rockchip_pcie_phy_get(struct udevice *dev)
173 {
174         struct rockchip_pcie *priv = dev_get_priv(dev);
175         struct rockchip_pcie_phy *phy_priv = &priv->rk_phy;
176         ofnode phy_node;
177         u32 phandle;
178         int ret;
179
180         phandle = dev_read_u32_default(dev, "phys", 0);
181         phy_node = ofnode_get_by_phandle(phandle);
182         if (!ofnode_valid(phy_node)) {
183                 dev_err(dev, "failed to found pcie-phy\n");
184                 return -ENODEV;
185         }
186
187         phy_priv->reg_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
188
189         ret = clk_get_by_index_nodev(phy_node, 0, &phy_priv->refclk);
190         if (ret) {
191                 dev_err(dev, "failed to get refclk clock phandle\n");
192                 return ret;
193         }
194
195         ret = reset_get_by_index_nodev(phy_node, 0, &phy_priv->phy_rst);
196         if (ret) {
197                 dev_err(dev, "failed to get phy reset phandle\n");
198                 return ret;
199         }
200
201         phy_priv->ops = &pcie_phy_ops;
202         priv->phy = phy_priv;
203
204         return 0;
205 }