1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/net/ethernet/ibm/emac/phy.c |
4 | * |
5 | * Driver for PowerPC 4xx on-chip ethernet controller, PHY support. |
6 | * Borrowed from sungem_phy.c, though I only kept the generic MII |
7 | * driver for now. |
8 | * |
9 | * This file should be shared with other drivers or eventually |
10 | * merged as the "low level" part of miilib |
11 | * |
12 | * Copyright 2007 Benjamin Herrenschmidt, IBM Corp. |
13 | * <benh@kernel.crashing.org> |
14 | * |
15 | * Based on the arch/ppc version of the driver: |
16 | * |
17 | * (c) 2003, Benjamin Herrenscmidt (benh@kernel.crashing.org) |
18 | * (c) 2004-2005, Eugene Surovegin <ebs@ebshome.net> |
19 | * |
20 | */ |
21 | #include <linux/module.h> |
22 | #include <linux/kernel.h> |
23 | #include <linux/types.h> |
24 | #include <linux/netdevice.h> |
25 | #include <linux/mii.h> |
26 | #include <linux/ethtool.h> |
27 | #include <linux/delay.h> |
28 | |
29 | #include "emac.h" |
30 | #include "phy.h" |
31 | |
32 | #define phy_read _phy_read |
33 | #define phy_write _phy_write |
34 | |
35 | static inline int _phy_read(struct mii_phy *phy, int reg) |
36 | { |
37 | return phy->mdio_read(phy->dev, phy->address, reg); |
38 | } |
39 | |
40 | static inline void _phy_write(struct mii_phy *phy, int reg, int val) |
41 | { |
42 | phy->mdio_write(phy->dev, phy->address, reg, val); |
43 | } |
44 | |
45 | static inline int gpcs_phy_read(struct mii_phy *phy, int reg) |
46 | { |
47 | return phy->mdio_read(phy->dev, phy->gpcs_address, reg); |
48 | } |
49 | |
50 | static inline void gpcs_phy_write(struct mii_phy *phy, int reg, int val) |
51 | { |
52 | phy->mdio_write(phy->dev, phy->gpcs_address, reg, val); |
53 | } |
54 | |
55 | int emac_mii_reset_phy(struct mii_phy *phy) |
56 | { |
57 | int val; |
58 | int limit = 10000; |
59 | |
60 | val = phy_read(phy, MII_BMCR); |
61 | val &= ~(BMCR_ISOLATE | BMCR_ANENABLE); |
62 | val |= BMCR_RESET; |
63 | phy_write(phy, MII_BMCR, val); |
64 | |
65 | udelay(300); |
66 | |
67 | while (--limit) { |
68 | val = phy_read(phy, MII_BMCR); |
69 | if (val >= 0 && (val & BMCR_RESET) == 0) |
70 | break; |
71 | udelay(10); |
72 | } |
73 | if ((val & BMCR_ISOLATE) && limit > 0) |
74 | phy_write(phy, MII_BMCR, val: val & ~BMCR_ISOLATE); |
75 | |
76 | return limit <= 0; |
77 | } |
78 | |
79 | int emac_mii_reset_gpcs(struct mii_phy *phy) |
80 | { |
81 | int val; |
82 | int limit = 10000; |
83 | |
84 | val = gpcs_phy_read(phy, MII_BMCR); |
85 | val &= ~(BMCR_ISOLATE | BMCR_ANENABLE); |
86 | val |= BMCR_RESET; |
87 | gpcs_phy_write(phy, MII_BMCR, val); |
88 | |
89 | udelay(300); |
90 | |
91 | while (--limit) { |
92 | val = gpcs_phy_read(phy, MII_BMCR); |
93 | if (val >= 0 && (val & BMCR_RESET) == 0) |
94 | break; |
95 | udelay(10); |
96 | } |
97 | if ((val & BMCR_ISOLATE) && limit > 0) |
98 | gpcs_phy_write(phy, MII_BMCR, val: val & ~BMCR_ISOLATE); |
99 | |
100 | if (limit > 0 && phy->mode == PHY_INTERFACE_MODE_SGMII) { |
101 | /* Configure GPCS interface to recommended setting for SGMII */ |
102 | gpcs_phy_write(phy, reg: 0x04, val: 0x8120); /* AsymPause, FDX */ |
103 | gpcs_phy_write(phy, reg: 0x07, val: 0x2801); /* msg_pg, toggle */ |
104 | gpcs_phy_write(phy, reg: 0x00, val: 0x0140); /* 1Gbps, FDX */ |
105 | } |
106 | |
107 | return limit <= 0; |
108 | } |
109 | |
110 | static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) |
111 | { |
112 | int ctl, adv; |
113 | |
114 | phy->autoneg = AUTONEG_ENABLE; |
115 | phy->speed = SPEED_10; |
116 | phy->duplex = DUPLEX_HALF; |
117 | phy->pause = phy->asym_pause = 0; |
118 | phy->advertising = advertise; |
119 | |
120 | ctl = phy_read(phy, MII_BMCR); |
121 | if (ctl < 0) |
122 | return ctl; |
123 | ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); |
124 | |
125 | /* First clear the PHY */ |
126 | phy_write(phy, MII_BMCR, val: ctl); |
127 | |
128 | /* Setup standard advertise */ |
129 | adv = phy_read(phy, MII_ADVERTISE); |
130 | if (adv < 0) |
131 | return adv; |
132 | adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP | |
133 | ADVERTISE_PAUSE_ASYM); |
134 | if (advertise & ADVERTISED_10baseT_Half) |
135 | adv |= ADVERTISE_10HALF; |
136 | if (advertise & ADVERTISED_10baseT_Full) |
137 | adv |= ADVERTISE_10FULL; |
138 | if (advertise & ADVERTISED_100baseT_Half) |
139 | adv |= ADVERTISE_100HALF; |
140 | if (advertise & ADVERTISED_100baseT_Full) |
141 | adv |= ADVERTISE_100FULL; |
142 | if (advertise & ADVERTISED_Pause) |
143 | adv |= ADVERTISE_PAUSE_CAP; |
144 | if (advertise & ADVERTISED_Asym_Pause) |
145 | adv |= ADVERTISE_PAUSE_ASYM; |
146 | phy_write(phy, MII_ADVERTISE, val: adv); |
147 | |
148 | if (phy->features & |
149 | (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) { |
150 | adv = phy_read(phy, MII_CTRL1000); |
151 | if (adv < 0) |
152 | return adv; |
153 | adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); |
154 | if (advertise & ADVERTISED_1000baseT_Full) |
155 | adv |= ADVERTISE_1000FULL; |
156 | if (advertise & ADVERTISED_1000baseT_Half) |
157 | adv |= ADVERTISE_1000HALF; |
158 | phy_write(phy, MII_CTRL1000, val: adv); |
159 | } |
160 | |
161 | /* Start/Restart aneg */ |
162 | ctl = phy_read(phy, MII_BMCR); |
163 | ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); |
164 | phy_write(phy, MII_BMCR, val: ctl); |
165 | |
166 | return 0; |
167 | } |
168 | |
169 | static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) |
170 | { |
171 | int ctl; |
172 | |
173 | phy->autoneg = AUTONEG_DISABLE; |
174 | phy->speed = speed; |
175 | phy->duplex = fd; |
176 | phy->pause = phy->asym_pause = 0; |
177 | |
178 | ctl = phy_read(phy, MII_BMCR); |
179 | if (ctl < 0) |
180 | return ctl; |
181 | ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); |
182 | |
183 | /* First clear the PHY */ |
184 | phy_write(phy, MII_BMCR, val: ctl | BMCR_RESET); |
185 | |
186 | /* Select speed & duplex */ |
187 | switch (speed) { |
188 | case SPEED_10: |
189 | break; |
190 | case SPEED_100: |
191 | ctl |= BMCR_SPEED100; |
192 | break; |
193 | case SPEED_1000: |
194 | ctl |= BMCR_SPEED1000; |
195 | break; |
196 | default: |
197 | return -EINVAL; |
198 | } |
199 | if (fd == DUPLEX_FULL) |
200 | ctl |= BMCR_FULLDPLX; |
201 | phy_write(phy, MII_BMCR, val: ctl); |
202 | |
203 | return 0; |
204 | } |
205 | |
206 | static int genmii_poll_link(struct mii_phy *phy) |
207 | { |
208 | int status; |
209 | |
210 | /* Clear latched value with dummy read */ |
211 | phy_read(phy, MII_BMSR); |
212 | status = phy_read(phy, MII_BMSR); |
213 | if (status < 0 || (status & BMSR_LSTATUS) == 0) |
214 | return 0; |
215 | if (phy->autoneg == AUTONEG_ENABLE && !(status & BMSR_ANEGCOMPLETE)) |
216 | return 0; |
217 | return 1; |
218 | } |
219 | |
220 | static int genmii_read_link(struct mii_phy *phy) |
221 | { |
222 | if (phy->autoneg == AUTONEG_ENABLE) { |
223 | int glpa = 0; |
224 | int lpa = phy_read(phy, MII_LPA) & phy_read(phy, MII_ADVERTISE); |
225 | if (lpa < 0) |
226 | return lpa; |
227 | |
228 | if (phy->features & |
229 | (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)) { |
230 | int adv = phy_read(phy, MII_CTRL1000); |
231 | glpa = phy_read(phy, MII_STAT1000); |
232 | |
233 | if (glpa < 0 || adv < 0) |
234 | return adv; |
235 | |
236 | glpa &= adv << 2; |
237 | } |
238 | |
239 | phy->speed = SPEED_10; |
240 | phy->duplex = DUPLEX_HALF; |
241 | phy->pause = phy->asym_pause = 0; |
242 | |
243 | if (glpa & (LPA_1000FULL | LPA_1000HALF)) { |
244 | phy->speed = SPEED_1000; |
245 | if (glpa & LPA_1000FULL) |
246 | phy->duplex = DUPLEX_FULL; |
247 | } else if (lpa & (LPA_100FULL | LPA_100HALF)) { |
248 | phy->speed = SPEED_100; |
249 | if (lpa & LPA_100FULL) |
250 | phy->duplex = DUPLEX_FULL; |
251 | } else if (lpa & LPA_10FULL) |
252 | phy->duplex = DUPLEX_FULL; |
253 | |
254 | if (phy->duplex == DUPLEX_FULL) { |
255 | phy->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; |
256 | phy->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; |
257 | } |
258 | } else { |
259 | int bmcr = phy_read(phy, MII_BMCR); |
260 | if (bmcr < 0) |
261 | return bmcr; |
262 | |
263 | if (bmcr & BMCR_FULLDPLX) |
264 | phy->duplex = DUPLEX_FULL; |
265 | else |
266 | phy->duplex = DUPLEX_HALF; |
267 | if (bmcr & BMCR_SPEED1000) |
268 | phy->speed = SPEED_1000; |
269 | else if (bmcr & BMCR_SPEED100) |
270 | phy->speed = SPEED_100; |
271 | else |
272 | phy->speed = SPEED_10; |
273 | |
274 | phy->pause = phy->asym_pause = 0; |
275 | } |
276 | return 0; |
277 | } |
278 | |
279 | /* Generic implementation for most 10/100/1000 PHYs */ |
280 | static const struct mii_phy_ops generic_phy_ops = { |
281 | .setup_aneg = genmii_setup_aneg, |
282 | .setup_forced = genmii_setup_forced, |
283 | .poll_link = genmii_poll_link, |
284 | .read_link = genmii_read_link |
285 | }; |
286 | |
287 | static struct mii_phy_def genmii_phy_def = { |
288 | .phy_id = 0x00000000, |
289 | .phy_id_mask = 0x00000000, |
290 | .name = "Generic MII" , |
291 | .ops = &generic_phy_ops |
292 | }; |
293 | |
294 | /* CIS8201 */ |
295 | #define MII_CIS8201_10BTCSR 0x16 |
296 | #define TENBTCSR_ECHO_DISABLE 0x2000 |
297 | #define MII_CIS8201_EPCR 0x17 |
298 | #define EPCR_MODE_MASK 0x3000 |
299 | #define EPCR_GMII_MODE 0x0000 |
300 | #define EPCR_RGMII_MODE 0x1000 |
301 | #define EPCR_TBI_MODE 0x2000 |
302 | #define EPCR_RTBI_MODE 0x3000 |
303 | #define MII_CIS8201_ACSR 0x1c |
304 | #define ACSR_PIN_PRIO_SELECT 0x0004 |
305 | |
306 | static int cis8201_init(struct mii_phy *phy) |
307 | { |
308 | int epcr; |
309 | |
310 | epcr = phy_read(phy, MII_CIS8201_EPCR); |
311 | if (epcr < 0) |
312 | return epcr; |
313 | |
314 | epcr &= ~EPCR_MODE_MASK; |
315 | |
316 | switch (phy->mode) { |
317 | case PHY_INTERFACE_MODE_TBI: |
318 | epcr |= EPCR_TBI_MODE; |
319 | break; |
320 | case PHY_INTERFACE_MODE_RTBI: |
321 | epcr |= EPCR_RTBI_MODE; |
322 | break; |
323 | case PHY_INTERFACE_MODE_GMII: |
324 | epcr |= EPCR_GMII_MODE; |
325 | break; |
326 | case PHY_INTERFACE_MODE_RGMII: |
327 | default: |
328 | epcr |= EPCR_RGMII_MODE; |
329 | } |
330 | |
331 | phy_write(phy, MII_CIS8201_EPCR, val: epcr); |
332 | |
333 | /* MII regs override strap pins */ |
334 | phy_write(phy, MII_CIS8201_ACSR, |
335 | phy_read(phy, MII_CIS8201_ACSR) | ACSR_PIN_PRIO_SELECT); |
336 | |
337 | /* Disable TX_EN -> CRS echo mode, otherwise 10/HDX doesn't work */ |
338 | phy_write(phy, MII_CIS8201_10BTCSR, |
339 | phy_read(phy, MII_CIS8201_10BTCSR) | TENBTCSR_ECHO_DISABLE); |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | static const struct mii_phy_ops cis8201_phy_ops = { |
345 | .init = cis8201_init, |
346 | .setup_aneg = genmii_setup_aneg, |
347 | .setup_forced = genmii_setup_forced, |
348 | .poll_link = genmii_poll_link, |
349 | .read_link = genmii_read_link |
350 | }; |
351 | |
352 | static struct mii_phy_def cis8201_phy_def = { |
353 | .phy_id = 0x000fc410, |
354 | .phy_id_mask = 0x000ffff0, |
355 | .name = "CIS8201 Gigabit Ethernet" , |
356 | .ops = &cis8201_phy_ops |
357 | }; |
358 | |
359 | static struct mii_phy_def bcm5248_phy_def = { |
360 | |
361 | .phy_id = 0x0143bc00, |
362 | .phy_id_mask = 0x0ffffff0, |
363 | .name = "BCM5248 10/100 SMII Ethernet" , |
364 | .ops = &generic_phy_ops |
365 | }; |
366 | |
367 | static int m88e1111_init(struct mii_phy *phy) |
368 | { |
369 | pr_debug("%s: Marvell 88E1111 Ethernet\n" , __func__); |
370 | phy_write(phy, reg: 0x14, val: 0x0ce3); |
371 | phy_write(phy, reg: 0x18, val: 0x4101); |
372 | phy_write(phy, reg: 0x09, val: 0x0e00); |
373 | phy_write(phy, reg: 0x04, val: 0x01e1); |
374 | phy_write(phy, reg: 0x00, val: 0x9140); |
375 | phy_write(phy, reg: 0x00, val: 0x1140); |
376 | |
377 | return 0; |
378 | } |
379 | |
380 | static int m88e1112_init(struct mii_phy *phy) |
381 | { |
382 | /* |
383 | * Marvell 88E1112 PHY needs to have the SGMII MAC |
384 | * interace (page 2) properly configured to |
385 | * communicate with the 460EX/GT GPCS interface. |
386 | */ |
387 | |
388 | u16 reg_short; |
389 | |
390 | pr_debug("%s: Marvell 88E1112 Ethernet\n" , __func__); |
391 | |
392 | /* Set access to Page 2 */ |
393 | phy_write(phy, reg: 0x16, val: 0x0002); |
394 | |
395 | phy_write(phy, reg: 0x00, val: 0x0040); /* 1Gbps */ |
396 | reg_short = (u16)(phy_read(phy, reg: 0x1a)); |
397 | reg_short |= 0x8000; /* bypass Auto-Negotiation */ |
398 | phy_write(phy, reg: 0x1a, val: reg_short); |
399 | emac_mii_reset_phy(phy); /* reset MAC interface */ |
400 | |
401 | /* Reset access to Page 0 */ |
402 | phy_write(phy, reg: 0x16, val: 0x0000); |
403 | |
404 | return 0; |
405 | } |
406 | |
407 | static int et1011c_init(struct mii_phy *phy) |
408 | { |
409 | u16 reg_short; |
410 | |
411 | reg_short = (u16)(phy_read(phy, reg: 0x16)); |
412 | reg_short &= ~(0x7); |
413 | reg_short |= 0x6; /* RGMII Trace Delay*/ |
414 | phy_write(phy, reg: 0x16, val: reg_short); |
415 | |
416 | reg_short = (u16)(phy_read(phy, reg: 0x17)); |
417 | reg_short &= ~(0x40); |
418 | phy_write(phy, reg: 0x17, val: reg_short); |
419 | |
420 | phy_write(phy, reg: 0x1c, val: 0x74f0); |
421 | return 0; |
422 | } |
423 | |
424 | static const struct mii_phy_ops et1011c_phy_ops = { |
425 | .init = et1011c_init, |
426 | .setup_aneg = genmii_setup_aneg, |
427 | .setup_forced = genmii_setup_forced, |
428 | .poll_link = genmii_poll_link, |
429 | .read_link = genmii_read_link |
430 | }; |
431 | |
432 | static struct mii_phy_def et1011c_phy_def = { |
433 | .phy_id = 0x0282f000, |
434 | .phy_id_mask = 0x0fffff00, |
435 | .name = "ET1011C Gigabit Ethernet" , |
436 | .ops = &et1011c_phy_ops |
437 | }; |
438 | |
439 | |
440 | |
441 | |
442 | |
443 | static const struct mii_phy_ops m88e1111_phy_ops = { |
444 | .init = m88e1111_init, |
445 | .setup_aneg = genmii_setup_aneg, |
446 | .setup_forced = genmii_setup_forced, |
447 | .poll_link = genmii_poll_link, |
448 | .read_link = genmii_read_link |
449 | }; |
450 | |
451 | static struct mii_phy_def m88e1111_phy_def = { |
452 | |
453 | .phy_id = 0x01410CC0, |
454 | .phy_id_mask = 0x0ffffff0, |
455 | .name = "Marvell 88E1111 Ethernet" , |
456 | .ops = &m88e1111_phy_ops, |
457 | }; |
458 | |
459 | static const struct mii_phy_ops m88e1112_phy_ops = { |
460 | .init = m88e1112_init, |
461 | .setup_aneg = genmii_setup_aneg, |
462 | .setup_forced = genmii_setup_forced, |
463 | .poll_link = genmii_poll_link, |
464 | .read_link = genmii_read_link |
465 | }; |
466 | |
467 | static struct mii_phy_def m88e1112_phy_def = { |
468 | .phy_id = 0x01410C90, |
469 | .phy_id_mask = 0x0ffffff0, |
470 | .name = "Marvell 88E1112 Ethernet" , |
471 | .ops = &m88e1112_phy_ops, |
472 | }; |
473 | |
474 | static int ar8035_init(struct mii_phy *phy) |
475 | { |
476 | phy_write(phy, reg: 0x1d, val: 0x5); /* Address debug register 5 */ |
477 | phy_write(phy, reg: 0x1e, val: 0x2d47); /* Value copied from u-boot */ |
478 | phy_write(phy, reg: 0x1d, val: 0xb); /* Address hib ctrl */ |
479 | phy_write(phy, reg: 0x1e, val: 0xbc20); /* Value copied from u-boot */ |
480 | |
481 | return 0; |
482 | } |
483 | |
484 | static const struct mii_phy_ops ar8035_phy_ops = { |
485 | .init = ar8035_init, |
486 | .setup_aneg = genmii_setup_aneg, |
487 | .setup_forced = genmii_setup_forced, |
488 | .poll_link = genmii_poll_link, |
489 | .read_link = genmii_read_link, |
490 | }; |
491 | |
492 | static struct mii_phy_def ar8035_phy_def = { |
493 | .phy_id = 0x004dd070, |
494 | .phy_id_mask = 0xfffffff0, |
495 | .name = "Atheros 8035 Gigabit Ethernet" , |
496 | .ops = &ar8035_phy_ops, |
497 | }; |
498 | |
499 | static struct mii_phy_def *mii_phy_table[] = { |
500 | &et1011c_phy_def, |
501 | &cis8201_phy_def, |
502 | &bcm5248_phy_def, |
503 | &m88e1111_phy_def, |
504 | &m88e1112_phy_def, |
505 | &ar8035_phy_def, |
506 | &genmii_phy_def, |
507 | NULL |
508 | }; |
509 | |
510 | int emac_mii_phy_probe(struct mii_phy *phy, int address) |
511 | { |
512 | struct mii_phy_def *def; |
513 | int i; |
514 | u32 id; |
515 | |
516 | phy->autoneg = AUTONEG_DISABLE; |
517 | phy->advertising = 0; |
518 | phy->address = address; |
519 | phy->speed = SPEED_10; |
520 | phy->duplex = DUPLEX_HALF; |
521 | phy->pause = phy->asym_pause = 0; |
522 | |
523 | /* Take PHY out of isolate mode and reset it. */ |
524 | if (emac_mii_reset_phy(phy)) |
525 | return -ENODEV; |
526 | |
527 | /* Read ID and find matching entry */ |
528 | id = (phy_read(phy, MII_PHYSID1) << 16) | phy_read(phy, MII_PHYSID2); |
529 | for (i = 0; (def = mii_phy_table[i]) != NULL; i++) |
530 | if ((id & def->phy_id_mask) == def->phy_id) |
531 | break; |
532 | /* Should never be NULL (we have a generic entry), but... */ |
533 | if (!def) |
534 | return -ENODEV; |
535 | |
536 | phy->def = def; |
537 | |
538 | /* Determine PHY features if needed */ |
539 | phy->features = def->features; |
540 | if (!phy->features) { |
541 | u16 bmsr = phy_read(phy, MII_BMSR); |
542 | if (bmsr & BMSR_ANEGCAPABLE) |
543 | phy->features |= SUPPORTED_Autoneg; |
544 | if (bmsr & BMSR_10HALF) |
545 | phy->features |= SUPPORTED_10baseT_Half; |
546 | if (bmsr & BMSR_10FULL) |
547 | phy->features |= SUPPORTED_10baseT_Full; |
548 | if (bmsr & BMSR_100HALF) |
549 | phy->features |= SUPPORTED_100baseT_Half; |
550 | if (bmsr & BMSR_100FULL) |
551 | phy->features |= SUPPORTED_100baseT_Full; |
552 | if (bmsr & BMSR_ESTATEN) { |
553 | u16 esr = phy_read(phy, MII_ESTATUS); |
554 | if (esr & ESTATUS_1000_TFULL) |
555 | phy->features |= SUPPORTED_1000baseT_Full; |
556 | if (esr & ESTATUS_1000_THALF) |
557 | phy->features |= SUPPORTED_1000baseT_Half; |
558 | } |
559 | phy->features |= SUPPORTED_MII; |
560 | } |
561 | |
562 | /* Setup default advertising */ |
563 | phy->advertising = phy->features; |
564 | |
565 | return 0; |
566 | } |
567 | |
568 | MODULE_LICENSE("GPL" ); |
569 | |