]>
Commit | Line | Data |
---|---|---|
3ef2050a RPNO |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * NXP C45 PHY driver | |
4 | * | |
5 | * Copyright 2021 NXP | |
6 | * Author: Radu Pirea <radu-nicolae.pirea@oss.nxp.com> | |
7 | */ | |
d678a59d | 8 | #include <common.h> |
3ef2050a RPNO |
9 | #include <dm.h> |
10 | #include <dm/devres.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/math64.h> | |
13 | #include <linux/mdio.h> | |
14 | #include <phy.h> | |
15 | ||
16 | #define PHY_ID_TJA_1103 0x001BB010 | |
55fc0cbb | 17 | #define PHY_ID_TJA_1120 0x001BB031 |
3ef2050a RPNO |
18 | |
19 | #define VEND1_DEVICE_CONTROL 0x0040 | |
20 | #define DEVICE_CONTROL_RESET BIT(15) | |
21 | #define DEVICE_CONTROL_CONFIG_GLOBAL_EN BIT(14) | |
22 | #define DEVICE_CONTROL_CONFIG_ALL_EN BIT(13) | |
23 | ||
24 | #define VEND1_PORT_CONTROL 0x8040 | |
25 | #define PORT_CONTROL_EN BIT(14) | |
26 | ||
27 | #define VEND1_PHY_CONTROL 0x8100 | |
28 | #define PHY_CONFIG_EN BIT(14) | |
29 | #define PHY_START_OP BIT(0) | |
30 | ||
31 | #define VEND1_PHY_CONFIG 0x8108 | |
32 | #define PHY_CONFIG_AUTO BIT(0) | |
33 | ||
34 | #define VEND1_PORT_INFRA_CONTROL 0xAC00 | |
35 | #define PORT_INFRA_CONTROL_EN BIT(14) | |
36 | ||
37 | #define VEND1_RXID 0xAFCC | |
38 | #define VEND1_TXID 0xAFCD | |
39 | #define ID_ENABLE BIT(15) | |
40 | ||
41 | #define VEND1_ABILITIES 0xAFC4 | |
42 | #define RGMII_ID_ABILITY BIT(15) | |
43 | #define RGMII_ABILITY BIT(14) | |
44 | #define RMII_ABILITY BIT(10) | |
45 | #define REVMII_ABILITY BIT(9) | |
46 | #define MII_ABILITY BIT(8) | |
47 | #define SGMII_ABILITY BIT(0) | |
48 | ||
49 | #define VEND1_MII_BASIC_CONFIG 0xAFC6 | |
50 | #define MII_BASIC_CONFIG_REV BIT(8) | |
51 | #define MII_BASIC_CONFIG_SGMII 0x9 | |
52 | #define MII_BASIC_CONFIG_RGMII 0x7 | |
53 | #define MII_BASIC_CONFIG_RMII 0x5 | |
54 | #define MII_BASIC_CONFIG_MII 0x4 | |
55 | ||
56 | #define RGMII_PERIOD_PS 8000U | |
57 | #define PS_PER_DEGREE div_u64(RGMII_PERIOD_PS, 360) | |
58 | #define MIN_ID_PS 1644U | |
59 | #define MAX_ID_PS 2260U | |
60 | #define DEFAULT_ID_PS 2000U | |
61 | ||
62 | #define RESET_DELAY_MS 25 | |
63 | #define CONF_EN_DELAY_US 450 | |
64 | ||
65 | struct nxp_c45_phy { | |
66 | u32 tx_delay; | |
67 | u32 rx_delay; | |
68 | }; | |
69 | ||
70 | static int nxp_c45_soft_reset(struct phy_device *phydev) | |
71 | { | |
72 | int tries = 10, ret; | |
73 | ||
74 | ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONTROL, | |
75 | DEVICE_CONTROL_RESET); | |
76 | if (ret) | |
77 | return ret; | |
78 | ||
79 | do { | |
80 | ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, | |
81 | VEND1_DEVICE_CONTROL); | |
82 | if (!(ret & DEVICE_CONTROL_RESET)) | |
83 | return 0; | |
84 | mdelay(RESET_DELAY_MS); | |
85 | } while (tries--); | |
86 | ||
87 | return -EIO; | |
88 | } | |
89 | ||
90 | static int nxp_c45_start_op(struct phy_device *phydev) | |
91 | { | |
92 | return phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONTROL, | |
93 | PHY_START_OP); | |
94 | } | |
95 | ||
96 | static int nxp_c45_config_enable(struct phy_device *phydev) | |
97 | { | |
98 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_DEVICE_CONTROL, | |
99 | DEVICE_CONTROL_CONFIG_GLOBAL_EN | | |
100 | DEVICE_CONTROL_CONFIG_ALL_EN); | |
101 | udelay(CONF_EN_DELAY_US); | |
102 | ||
103 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_CONTROL, | |
104 | PORT_CONTROL_EN); | |
105 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONTROL, | |
106 | PHY_CONFIG_EN); | |
107 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PORT_INFRA_CONTROL, | |
108 | PORT_INFRA_CONTROL_EN); | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
113 | static u64 nxp_c45_get_phase_shift(u64 phase_offset_raw) | |
114 | { | |
115 | /* The delay in degree phase is 73.8 + phase_offset_raw * 0.9. | |
116 | * To avoid floating point operations we'll multiply by 10 | |
117 | * and get 1 decimal point precision. | |
118 | */ | |
119 | phase_offset_raw *= 10; | |
120 | phase_offset_raw -= 738; | |
121 | return div_u64(phase_offset_raw, 9); | |
122 | } | |
123 | ||
124 | static void nxp_c45_disable_delays(struct phy_device *phydev) | |
125 | { | |
126 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, 0); | |
127 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, 0); | |
128 | } | |
129 | ||
130 | static int nxp_c45_check_delay(struct phy_device *phydev, u32 delay) | |
131 | { | |
132 | if (delay < MIN_ID_PS) { | |
133 | pr_err("%s: delay value smaller than %u\n", | |
134 | phydev->drv->name, MIN_ID_PS); | |
135 | return -EINVAL; | |
136 | } | |
137 | ||
138 | if (delay > MAX_ID_PS) { | |
139 | pr_err("%s: delay value higher than %u\n", | |
140 | phydev->drv->name, MAX_ID_PS); | |
141 | return -EINVAL; | |
142 | } | |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | static int nxp_c45_get_delays(struct phy_device *phydev) | |
148 | { | |
149 | struct nxp_c45_phy *priv = phydev->priv; | |
150 | int ret; | |
151 | ||
152 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || | |
153 | phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { | |
154 | ret = dev_read_u32(phydev->dev, "tx-internal-delay-ps", | |
155 | &priv->tx_delay); | |
156 | if (ret) | |
157 | priv->tx_delay = DEFAULT_ID_PS; | |
158 | ||
159 | ret = nxp_c45_check_delay(phydev, priv->tx_delay); | |
160 | if (ret) { | |
161 | pr_err("%s: tx-internal-delay-ps invalid value\n", | |
162 | phydev->drv->name); | |
163 | return ret; | |
164 | } | |
165 | } | |
166 | ||
167 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || | |
168 | phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { | |
169 | ret = dev_read_u32(phydev->dev, "rx-internal-delay-ps", | |
170 | &priv->rx_delay); | |
171 | if (ret) | |
172 | priv->rx_delay = DEFAULT_ID_PS; | |
173 | ||
174 | ret = nxp_c45_check_delay(phydev, priv->rx_delay); | |
175 | if (ret) { | |
176 | pr_err("%s: rx-internal-delay-ps invalid value\n", | |
177 | phydev->drv->name); | |
178 | return ret; | |
179 | } | |
180 | } | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static void nxp_c45_set_delays(struct phy_device *phydev) | |
186 | { | |
187 | struct nxp_c45_phy *priv = phydev->priv; | |
188 | u64 tx_delay = priv->tx_delay; | |
189 | u64 rx_delay = priv->rx_delay; | |
190 | u64 degree; | |
191 | ||
192 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || | |
193 | phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { | |
194 | degree = div_u64(tx_delay, PS_PER_DEGREE); | |
195 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, | |
196 | ID_ENABLE | nxp_c45_get_phase_shift(degree)); | |
197 | } else { | |
198 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_TXID, 0); | |
199 | } | |
200 | ||
201 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID || | |
202 | phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { | |
203 | degree = div_u64(rx_delay, PS_PER_DEGREE); | |
204 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, | |
205 | ID_ENABLE | nxp_c45_get_phase_shift(degree)); | |
206 | } else { | |
207 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_RXID, 0); | |
208 | } | |
209 | } | |
210 | ||
211 | static int nxp_c45_set_phy_mode(struct phy_device *phydev) | |
212 | { | |
213 | int ret; | |
214 | ||
215 | ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_ABILITIES); | |
216 | pr_debug("%s: Clause 45 managed PHY abilities 0x%x\n", | |
217 | phydev->drv->name, ret); | |
218 | ||
219 | switch (phydev->interface) { | |
220 | case PHY_INTERFACE_MODE_RGMII: | |
221 | if (!(ret & RGMII_ABILITY)) { | |
222 | pr_err("%s: rgmii mode not supported\n", | |
223 | phydev->drv->name); | |
224 | return -EINVAL; | |
225 | } | |
226 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, | |
227 | MII_BASIC_CONFIG_RGMII); | |
228 | nxp_c45_disable_delays(phydev); | |
229 | break; | |
230 | case PHY_INTERFACE_MODE_RGMII_ID: | |
231 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
232 | case PHY_INTERFACE_MODE_RGMII_RXID: | |
233 | if (!(ret & RGMII_ID_ABILITY)) { | |
234 | pr_err("%s: rgmii-id, rgmii-txid, rgmii-rxid modes are not supported\n", | |
235 | phydev->drv->name); | |
236 | return -EINVAL; | |
237 | } | |
238 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, | |
239 | MII_BASIC_CONFIG_RGMII); | |
240 | ret = nxp_c45_get_delays(phydev); | |
241 | if (ret) | |
242 | return ret; | |
243 | ||
244 | nxp_c45_set_delays(phydev); | |
245 | break; | |
246 | case PHY_INTERFACE_MODE_MII: | |
247 | if (!(ret & MII_ABILITY)) { | |
248 | pr_err("%s: mii mode not supported\n", | |
249 | phydev->drv->name); | |
250 | return -EINVAL; | |
251 | } | |
252 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, | |
253 | MII_BASIC_CONFIG_MII); | |
254 | break; | |
255 | case PHY_INTERFACE_MODE_RMII: | |
256 | if (!(ret & RMII_ABILITY)) { | |
257 | pr_err("%s: rmii mode not supported\n", | |
258 | phydev->drv->name); | |
259 | return -EINVAL; | |
260 | } | |
261 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, | |
262 | MII_BASIC_CONFIG_RMII); | |
263 | break; | |
264 | case PHY_INTERFACE_MODE_SGMII: | |
265 | if (!(ret & SGMII_ABILITY)) { | |
266 | pr_err("%s: sgmii mode not supported\n", | |
267 | phydev->drv->name); | |
268 | return -EINVAL; | |
269 | } | |
270 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_MII_BASIC_CONFIG, | |
271 | MII_BASIC_CONFIG_SGMII); | |
272 | break; | |
273 | case PHY_INTERFACE_MODE_INTERNAL: | |
274 | break; | |
275 | default: | |
276 | return -EINVAL; | |
277 | } | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | static int nxp_c45_config(struct phy_device *phydev) | |
283 | { | |
284 | int ret; | |
285 | ||
286 | ret = nxp_c45_soft_reset(phydev); | |
287 | if (ret) | |
288 | return ret; | |
289 | ||
290 | ret = nxp_c45_config_enable(phydev); | |
291 | if (ret) { | |
292 | pr_err("%s: Failed to enable config\n", phydev->drv->name); | |
293 | return ret; | |
294 | } | |
295 | ||
296 | phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_PHY_CONFIG, | |
297 | PHY_CONFIG_AUTO); | |
298 | ||
299 | ret = nxp_c45_set_phy_mode(phydev); | |
300 | if (ret) { | |
301 | pr_err("%s: Failed to set phy mode\n", phydev->drv->name); | |
302 | return ret; | |
303 | } | |
304 | ||
305 | phydev->autoneg = AUTONEG_DISABLE; | |
306 | ||
307 | return nxp_c45_start_op(phydev); | |
308 | } | |
309 | ||
84e57e7d RPNO |
310 | static int nxp_c45_speed(struct phy_device *phydev) |
311 | { | |
312 | int val; | |
313 | ||
314 | val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1); | |
315 | if (val < 0) | |
316 | return val; | |
317 | ||
318 | if (val & MDIO_PMA_CTRL1_SPEED100) | |
319 | phydev->speed = SPEED_100; | |
55fc0cbb RPNO |
320 | else if (val & MDIO_PMA_CTRL1_SPEED1000) |
321 | phydev->speed = SPEED_1000; | |
84e57e7d RPNO |
322 | else |
323 | phydev->speed = 0; | |
324 | ||
325 | return 0; | |
326 | } | |
327 | ||
3ef2050a RPNO |
328 | static int nxp_c45_startup(struct phy_device *phydev) |
329 | { | |
330 | u32 reg; | |
84e57e7d | 331 | int ret; |
3ef2050a RPNO |
332 | |
333 | reg = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_STAT1); | |
334 | phydev->link = !!(reg & MDIO_STAT1_LSTATUS); | |
84e57e7d RPNO |
335 | ret = nxp_c45_speed(phydev); |
336 | if (ret < 0) | |
337 | return ret; | |
338 | ||
3ef2050a RPNO |
339 | phydev->duplex = DUPLEX_FULL; |
340 | return 0; | |
341 | } | |
342 | ||
343 | static int nxp_c45_probe(struct phy_device *phydev) | |
344 | { | |
345 | struct nxp_c45_phy *priv; | |
346 | ||
347 | priv = devm_kzalloc(phydev->priv, sizeof(*priv), GFP_KERNEL); | |
348 | if (!priv) | |
349 | return -ENOMEM; | |
350 | ||
351 | phydev->priv = priv; | |
352 | ||
353 | return 0; | |
354 | } | |
355 | ||
6c43208a RPNO |
356 | #define NXP_C45_COMMON_FEATURES (SUPPORTED_TP | \ |
357 | SUPPORTED_MII) | |
358 | ||
7e02c3ac | 359 | U_BOOT_PHY_DRIVER(nxp_c45_tja1103) = { |
3ef2050a RPNO |
360 | .name = "NXP C45 TJA1103", |
361 | .uid = PHY_ID_TJA_1103, | |
362 | .mask = 0xfffff0, | |
6c43208a | 363 | .features = NXP_C45_COMMON_FEATURES | SUPPORTED_100baseT_Full, |
3ef2050a RPNO |
364 | .probe = &nxp_c45_probe, |
365 | .config = &nxp_c45_config, | |
366 | .startup = &nxp_c45_startup, | |
367 | .shutdown = &genphy_shutdown, | |
368 | }; | |
55fc0cbb RPNO |
369 | |
370 | U_BOOT_PHY_DRIVER(nxp_c45_tja1120) = { | |
371 | .name = "NXP C45 TJA1120", | |
372 | .uid = PHY_ID_TJA_1120, | |
373 | .mask = 0xfffff0, | |
374 | .features = NXP_C45_COMMON_FEATURES | SUPPORTED_1000baseT_Full, | |
375 | .probe = &nxp_c45_probe, | |
376 | .config = &nxp_c45_config, | |
377 | .startup = &nxp_c45_startup, | |
378 | .shutdown = &genphy_shutdown, | |
379 | }; |