GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / net / sungem_phy.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * PHY drivers for the sungem ethernet driver.
4  *
5  * This file could be shared with other drivers.
6  *
7  * (c) 2002-2007, Benjamin Herrenscmidt (benh@kernel.crashing.org)
8  *
9  * TODO:
10  *  - Add support for PHYs that provide an IRQ line
11  *  - Eventually moved the entire polling state machine in
12  *    there (out of the eth driver), so that it can easily be
13  *    skipped on PHYs that implement it in hardware.
14  *  - On LXT971 & BCM5201, Apple uses some chip specific regs
15  *    to read the link status. Figure out why and if it makes
16  *    sense to do the same (magic aneg ?)
17  *  - Apple has some additional power management code for some
18  *    Broadcom PHYs that they "hide" from the OpenSource version
19  *    of darwin, still need to reverse engineer that
20  */
21
22
23 #include <linux/module.h>
24
25 #include <linux/kernel.h>
26 #include <linux/types.h>
27 #include <linux/netdevice.h>
28 #include <linux/etherdevice.h>
29 #include <linux/mii.h>
30 #include <linux/ethtool.h>
31 #include <linux/delay.h>
32 #include <linux/of.h>
33 #include <linux/sungem_phy.h>
34
35 /* Link modes of the BCM5400 PHY */
36 static const int phy_BCM5400_link_table[8][3] = {
37         { 0, 0, 0 },    /* No link */
38         { 0, 0, 0 },    /* 10BT Half Duplex */
39         { 1, 0, 0 },    /* 10BT Full Duplex */
40         { 0, 1, 0 },    /* 100BT Half Duplex */
41         { 0, 1, 0 },    /* 100BT Half Duplex */
42         { 1, 1, 0 },    /* 100BT Full Duplex*/
43         { 1, 0, 1 },    /* 1000BT */
44         { 1, 0, 1 },    /* 1000BT */
45 };
46
47 static inline int __sungem_phy_read(struct mii_phy* phy, int id, int reg)
48 {
49         return phy->mdio_read(phy->dev, id, reg);
50 }
51
52 static inline void __sungem_phy_write(struct mii_phy* phy, int id, int reg, int val)
53 {
54         phy->mdio_write(phy->dev, id, reg, val);
55 }
56
57 static inline int sungem_phy_read(struct mii_phy* phy, int reg)
58 {
59         return phy->mdio_read(phy->dev, phy->mii_id, reg);
60 }
61
62 static inline void sungem_phy_write(struct mii_phy* phy, int reg, int val)
63 {
64         phy->mdio_write(phy->dev, phy->mii_id, reg, val);
65 }
66
67 static int reset_one_mii_phy(struct mii_phy* phy, int phy_id)
68 {
69         u16 val;
70         int limit = 10000;
71
72         val = __sungem_phy_read(phy, phy_id, MII_BMCR);
73         val &= ~(BMCR_ISOLATE | BMCR_PDOWN);
74         val |= BMCR_RESET;
75         __sungem_phy_write(phy, phy_id, MII_BMCR, val);
76
77         udelay(100);
78
79         while (--limit) {
80                 val = __sungem_phy_read(phy, phy_id, MII_BMCR);
81                 if ((val & BMCR_RESET) == 0)
82                         break;
83                 udelay(10);
84         }
85         if ((val & BMCR_ISOLATE) && limit > 0)
86                 __sungem_phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);
87
88         return limit <= 0;
89 }
90
91 static int bcm5201_init(struct mii_phy* phy)
92 {
93         u16 data;
94
95         data = sungem_phy_read(phy, MII_BCM5201_MULTIPHY);
96         data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
97         sungem_phy_write(phy, MII_BCM5201_MULTIPHY, data);
98
99         sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);
100
101         return 0;
102 }
103
104 static int bcm5201_suspend(struct mii_phy* phy)
105 {
106         sungem_phy_write(phy, MII_BCM5201_INTERRUPT, 0);
107         sungem_phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
108
109         return 0;
110 }
111
112 static int bcm5221_init(struct mii_phy* phy)
113 {
114         u16 data;
115
116         data = sungem_phy_read(phy, MII_BCM5221_TEST);
117         sungem_phy_write(phy, MII_BCM5221_TEST,
118                 data | MII_BCM5221_TEST_ENABLE_SHADOWS);
119
120         data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);
121         sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,
122                 data | MII_BCM5221_SHDOW_AUX_STAT2_APD);
123
124         data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
125         sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
126                 data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);
127
128         data = sungem_phy_read(phy, MII_BCM5221_TEST);
129         sungem_phy_write(phy, MII_BCM5221_TEST,
130                 data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);
131
132         return 0;
133 }
134
135 static int bcm5221_suspend(struct mii_phy* phy)
136 {
137         u16 data;
138
139         data = sungem_phy_read(phy, MII_BCM5221_TEST);
140         sungem_phy_write(phy, MII_BCM5221_TEST,
141                 data | MII_BCM5221_TEST_ENABLE_SHADOWS);
142
143         data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
144         sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
145                   data | MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE);
146
147         return 0;
148 }
149
150 static int bcm5241_init(struct mii_phy* phy)
151 {
152         u16 data;
153
154         data = sungem_phy_read(phy, MII_BCM5221_TEST);
155         sungem_phy_write(phy, MII_BCM5221_TEST,
156                 data | MII_BCM5221_TEST_ENABLE_SHADOWS);
157
158         data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);
159         sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,
160                 data | MII_BCM5221_SHDOW_AUX_STAT2_APD);
161
162         data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
163         sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
164                 data & ~MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);
165
166         data = sungem_phy_read(phy, MII_BCM5221_TEST);
167         sungem_phy_write(phy, MII_BCM5221_TEST,
168                 data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);
169
170         return 0;
171 }
172
173 static int bcm5241_suspend(struct mii_phy* phy)
174 {
175         u16 data;
176
177         data = sungem_phy_read(phy, MII_BCM5221_TEST);
178         sungem_phy_write(phy, MII_BCM5221_TEST,
179                 data | MII_BCM5221_TEST_ENABLE_SHADOWS);
180
181         data = sungem_phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
182         sungem_phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
183                   data | MII_BCM5241_SHDOW_AUX_MODE4_STANDBYPWR);
184
185         return 0;
186 }
187
188 static int bcm5400_init(struct mii_phy* phy)
189 {
190         u16 data;
191
192         /* Configure for gigabit full duplex */
193         data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);
194         data |= MII_BCM5400_AUXCONTROL_PWR10BASET;
195         sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);
196
197         data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);
198         data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
199         sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);
200
201         udelay(100);
202
203         /* Reset and configure cascaded 10/100 PHY */
204         (void)reset_one_mii_phy(phy, 0x1f);
205
206         data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
207         data |= MII_BCM5201_MULTIPHY_SERIALMODE;
208         __sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);
209
210         data = sungem_phy_read(phy, MII_BCM5400_AUXCONTROL);
211         data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
212         sungem_phy_write(phy, MII_BCM5400_AUXCONTROL, data);
213
214         return 0;
215 }
216
217 static int bcm5400_suspend(struct mii_phy* phy)
218 {
219 #if 0 /* Commented out in Darwin... someone has those dawn docs ? */
220         sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);
221 #endif
222         return 0;
223 }
224
225 static int bcm5401_init(struct mii_phy* phy)
226 {
227         u16 data;
228         int rev;
229
230         rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;
231         if (rev == 0 || rev == 3) {
232                 /* Some revisions of 5401 appear to need this
233                  * initialisation sequence to disable, according
234                  * to OF, "tap power management"
235                  *
236                  * WARNING ! OF and Darwin don't agree on the
237                  * register addresses. OF seem to interpret the
238                  * register numbers below as decimal
239                  *
240                  * Note: This should (and does) match tg3_init_5401phy_dsp
241                  *       in the tg3.c driver. -DaveM
242                  */
243                 sungem_phy_write(phy, 0x18, 0x0c20);
244                 sungem_phy_write(phy, 0x17, 0x0012);
245                 sungem_phy_write(phy, 0x15, 0x1804);
246                 sungem_phy_write(phy, 0x17, 0x0013);
247                 sungem_phy_write(phy, 0x15, 0x1204);
248                 sungem_phy_write(phy, 0x17, 0x8006);
249                 sungem_phy_write(phy, 0x15, 0x0132);
250                 sungem_phy_write(phy, 0x17, 0x8006);
251                 sungem_phy_write(phy, 0x15, 0x0232);
252                 sungem_phy_write(phy, 0x17, 0x201f);
253                 sungem_phy_write(phy, 0x15, 0x0a20);
254         }
255
256         /* Configure for gigabit full duplex */
257         data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);
258         data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
259         sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);
260
261         udelay(10);
262
263         /* Reset and configure cascaded 10/100 PHY */
264         (void)reset_one_mii_phy(phy, 0x1f);
265
266         data = __sungem_phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
267         data |= MII_BCM5201_MULTIPHY_SERIALMODE;
268         __sungem_phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);
269
270         return 0;
271 }
272
273 static int bcm5401_suspend(struct mii_phy* phy)
274 {
275 #if 0 /* Commented out in Darwin... someone has those dawn docs ? */
276         sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);
277 #endif
278         return 0;
279 }
280
281 static int bcm5411_init(struct mii_phy* phy)
282 {
283         u16 data;
284
285         /* Here's some more Apple black magic to setup
286          * some voltage stuffs.
287          */
288         sungem_phy_write(phy, 0x1c, 0x8c23);
289         sungem_phy_write(phy, 0x1c, 0x8ca3);
290         sungem_phy_write(phy, 0x1c, 0x8c23);
291
292         /* Here, Apple seems to want to reset it, do
293          * it as well
294          */
295         sungem_phy_write(phy, MII_BMCR, BMCR_RESET);
296         sungem_phy_write(phy, MII_BMCR, 0x1340);
297
298         data = sungem_phy_read(phy, MII_BCM5400_GB_CONTROL);
299         data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
300         sungem_phy_write(phy, MII_BCM5400_GB_CONTROL, data);
301
302         udelay(10);
303
304         /* Reset and configure cascaded 10/100 PHY */
305         (void)reset_one_mii_phy(phy, 0x1f);
306
307         return 0;
308 }
309
310 static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
311 {
312         u16 ctl, adv;
313
314         phy->autoneg = 1;
315         phy->speed = SPEED_10;
316         phy->duplex = DUPLEX_HALF;
317         phy->pause = 0;
318         phy->advertising = advertise;
319
320         /* Setup standard advertise */
321         adv = sungem_phy_read(phy, MII_ADVERTISE);
322         adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
323         if (advertise & ADVERTISED_10baseT_Half)
324                 adv |= ADVERTISE_10HALF;
325         if (advertise & ADVERTISED_10baseT_Full)
326                 adv |= ADVERTISE_10FULL;
327         if (advertise & ADVERTISED_100baseT_Half)
328                 adv |= ADVERTISE_100HALF;
329         if (advertise & ADVERTISED_100baseT_Full)
330                 adv |= ADVERTISE_100FULL;
331         sungem_phy_write(phy, MII_ADVERTISE, adv);
332
333         /* Start/Restart aneg */
334         ctl = sungem_phy_read(phy, MII_BMCR);
335         ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
336         sungem_phy_write(phy, MII_BMCR, ctl);
337
338         return 0;
339 }
340
341 static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
342 {
343         u16 ctl;
344
345         phy->autoneg = 0;
346         phy->speed = speed;
347         phy->duplex = fd;
348         phy->pause = 0;
349
350         ctl = sungem_phy_read(phy, MII_BMCR);
351         ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);
352
353         /* First reset the PHY */
354         sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
355
356         /* Select speed & duplex */
357         switch(speed) {
358         case SPEED_10:
359                 break;
360         case SPEED_100:
361                 ctl |= BMCR_SPEED100;
362                 break;
363         case SPEED_1000:
364         default:
365                 return -EINVAL;
366         }
367         if (fd == DUPLEX_FULL)
368                 ctl |= BMCR_FULLDPLX;
369         sungem_phy_write(phy, MII_BMCR, ctl);
370
371         return 0;
372 }
373
374 static int genmii_poll_link(struct mii_phy *phy)
375 {
376         u16 status;
377
378         (void)sungem_phy_read(phy, MII_BMSR);
379         status = sungem_phy_read(phy, MII_BMSR);
380         if ((status & BMSR_LSTATUS) == 0)
381                 return 0;
382         if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))
383                 return 0;
384         return 1;
385 }
386
387 static int genmii_read_link(struct mii_phy *phy)
388 {
389         u16 lpa;
390
391         if (phy->autoneg) {
392                 lpa = sungem_phy_read(phy, MII_LPA);
393
394                 if (lpa & (LPA_10FULL | LPA_100FULL))
395                         phy->duplex = DUPLEX_FULL;
396                 else
397                         phy->duplex = DUPLEX_HALF;
398                 if (lpa & (LPA_100FULL | LPA_100HALF))
399                         phy->speed = SPEED_100;
400                 else
401                         phy->speed = SPEED_10;
402                 phy->pause = 0;
403         }
404         /* On non-aneg, we assume what we put in BMCR is the speed,
405          * though magic-aneg shouldn't prevent this case from occurring
406          */
407
408         return 0;
409 }
410
411 static int generic_suspend(struct mii_phy* phy)
412 {
413         sungem_phy_write(phy, MII_BMCR, BMCR_PDOWN);
414
415         return 0;
416 }
417
418 static int bcm5421_init(struct mii_phy* phy)
419 {
420         u16 data;
421         unsigned int id;
422
423         id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));
424
425         /* Revision 0 of 5421 needs some fixups */
426         if (id == 0x002060e0) {
427                 /* This is borrowed from MacOS
428                  */
429                 sungem_phy_write(phy, 0x18, 0x1007);
430                 data = sungem_phy_read(phy, 0x18);
431                 sungem_phy_write(phy, 0x18, data | 0x0400);
432                 sungem_phy_write(phy, 0x18, 0x0007);
433                 data = sungem_phy_read(phy, 0x18);
434                 sungem_phy_write(phy, 0x18, data | 0x0800);
435                 sungem_phy_write(phy, 0x17, 0x000a);
436                 data = sungem_phy_read(phy, 0x15);
437                 sungem_phy_write(phy, 0x15, data | 0x0200);
438         }
439
440         /* Pick up some init code from OF for K2 version */
441         if ((id & 0xfffffff0) == 0x002062e0) {
442                 sungem_phy_write(phy, 4, 0x01e1);
443                 sungem_phy_write(phy, 9, 0x0300);
444         }
445
446         /* Check if we can enable automatic low power */
447 #ifdef CONFIG_PPC_PMAC
448         if (phy->platform_data) {
449                 struct device_node *np = of_get_parent(phy->platform_data);
450                 int can_low_power = 1;
451                 if (np == NULL || of_get_property(np, "no-autolowpower", NULL))
452                         can_low_power = 0;
453                 if (can_low_power) {
454                         /* Enable automatic low-power */
455                         sungem_phy_write(phy, 0x1c, 0x9002);
456                         sungem_phy_write(phy, 0x1c, 0xa821);
457                         sungem_phy_write(phy, 0x1c, 0x941d);
458                 }
459         }
460 #endif /* CONFIG_PPC_PMAC */
461
462         return 0;
463 }
464
465 static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)
466 {
467         u16 ctl, adv;
468
469         phy->autoneg = 1;
470         phy->speed = SPEED_10;
471         phy->duplex = DUPLEX_HALF;
472         phy->pause = 0;
473         phy->advertising = advertise;
474
475         /* Setup standard advertise */
476         adv = sungem_phy_read(phy, MII_ADVERTISE);
477         adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
478         if (advertise & ADVERTISED_10baseT_Half)
479                 adv |= ADVERTISE_10HALF;
480         if (advertise & ADVERTISED_10baseT_Full)
481                 adv |= ADVERTISE_10FULL;
482         if (advertise & ADVERTISED_100baseT_Half)
483                 adv |= ADVERTISE_100HALF;
484         if (advertise & ADVERTISED_100baseT_Full)
485                 adv |= ADVERTISE_100FULL;
486         if (advertise & ADVERTISED_Pause)
487                 adv |= ADVERTISE_PAUSE_CAP;
488         if (advertise & ADVERTISED_Asym_Pause)
489                 adv |= ADVERTISE_PAUSE_ASYM;
490         sungem_phy_write(phy, MII_ADVERTISE, adv);
491
492         /* Setup 1000BT advertise */
493         adv = sungem_phy_read(phy, MII_1000BASETCONTROL);
494         adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);
495         if (advertise & SUPPORTED_1000baseT_Half)
496                 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
497         if (advertise & SUPPORTED_1000baseT_Full)
498                 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
499         sungem_phy_write(phy, MII_1000BASETCONTROL, adv);
500
501         /* Start/Restart aneg */
502         ctl = sungem_phy_read(phy, MII_BMCR);
503         ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
504         sungem_phy_write(phy, MII_BMCR, ctl);
505
506         return 0;
507 }
508
509 static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd)
510 {
511         u16 ctl;
512
513         phy->autoneg = 0;
514         phy->speed = speed;
515         phy->duplex = fd;
516         phy->pause = 0;
517
518         ctl = sungem_phy_read(phy, MII_BMCR);
519         ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
520
521         /* First reset the PHY */
522         sungem_phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
523
524         /* Select speed & duplex */
525         switch(speed) {
526         case SPEED_10:
527                 break;
528         case SPEED_100:
529                 ctl |= BMCR_SPEED100;
530                 break;
531         case SPEED_1000:
532                 ctl |= BMCR_SPD2;
533         }
534         if (fd == DUPLEX_FULL)
535                 ctl |= BMCR_FULLDPLX;
536
537         // XXX Should we set the sungem to GII now on 1000BT ?
538
539         sungem_phy_write(phy, MII_BMCR, ctl);
540
541         return 0;
542 }
543
544 static int bcm54xx_read_link(struct mii_phy *phy)
545 {
546         int link_mode;
547         u16 val;
548
549         if (phy->autoneg) {
550                 val = sungem_phy_read(phy, MII_BCM5400_AUXSTATUS);
551                 link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>
552                              MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);
553                 phy->duplex = phy_BCM5400_link_table[link_mode][0] ?
554                         DUPLEX_FULL : DUPLEX_HALF;
555                 phy->speed = phy_BCM5400_link_table[link_mode][2] ?
556                                 SPEED_1000 :
557                                 (phy_BCM5400_link_table[link_mode][1] ?
558                                  SPEED_100 : SPEED_10);
559                 val = sungem_phy_read(phy, MII_LPA);
560                 phy->pause = (phy->duplex == DUPLEX_FULL) &&
561                         ((val & LPA_PAUSE) != 0);
562         }
563         /* On non-aneg, we assume what we put in BMCR is the speed,
564          * though magic-aneg shouldn't prevent this case from occurring
565          */
566
567         return 0;
568 }
569
570 static int marvell88e1111_init(struct mii_phy* phy)
571 {
572         u16 rev;
573
574         /* magic init sequence for rev 0 */
575         rev = sungem_phy_read(phy, MII_PHYSID2) & 0x000f;
576         if (rev == 0) {
577                 sungem_phy_write(phy, 0x1d, 0x000a);
578                 sungem_phy_write(phy, 0x1e, 0x0821);
579
580                 sungem_phy_write(phy, 0x1d, 0x0006);
581                 sungem_phy_write(phy, 0x1e, 0x8600);
582
583                 sungem_phy_write(phy, 0x1d, 0x000b);
584                 sungem_phy_write(phy, 0x1e, 0x0100);
585
586                 sungem_phy_write(phy, 0x1d, 0x0004);
587                 sungem_phy_write(phy, 0x1e, 0x4850);
588         }
589         return 0;
590 }
591
592 #define BCM5421_MODE_MASK       (1 << 5)
593
594 static int bcm5421_poll_link(struct mii_phy* phy)
595 {
596         u32 phy_reg;
597         int mode;
598
599         /* find out in what mode we are */
600         sungem_phy_write(phy, MII_NCONFIG, 0x1000);
601         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
602
603         mode = (phy_reg & BCM5421_MODE_MASK) >> 5;
604
605         if ( mode == BCM54XX_COPPER)
606                 return genmii_poll_link(phy);
607
608         /* try to find out whether we have a link */
609         sungem_phy_write(phy, MII_NCONFIG, 0x2000);
610         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
611
612         if (phy_reg & 0x0020)
613                 return 0;
614         else
615                 return 1;
616 }
617
618 static int bcm5421_read_link(struct mii_phy* phy)
619 {
620         u32 phy_reg;
621         int mode;
622
623         /* find out in what mode we are */
624         sungem_phy_write(phy, MII_NCONFIG, 0x1000);
625         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
626
627         mode = (phy_reg & BCM5421_MODE_MASK ) >> 5;
628
629         if ( mode == BCM54XX_COPPER)
630                 return bcm54xx_read_link(phy);
631
632         phy->speed = SPEED_1000;
633
634         /* find out whether we are running half- or full duplex */
635         sungem_phy_write(phy, MII_NCONFIG, 0x2000);
636         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
637
638         if ( (phy_reg & 0x0080) >> 7)
639                 phy->duplex |=  DUPLEX_HALF;
640         else
641                 phy->duplex |=  DUPLEX_FULL;
642
643         return 0;
644 }
645
646 static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg)
647 {
648         /* enable fiber mode */
649         sungem_phy_write(phy, MII_NCONFIG, 0x9020);
650         /* LEDs active in both modes, autosense prio = fiber */
651         sungem_phy_write(phy, MII_NCONFIG, 0x945f);
652
653         if (!autoneg) {
654                 /* switch off fibre autoneg */
655                 sungem_phy_write(phy, MII_NCONFIG, 0xfc01);
656                 sungem_phy_write(phy, 0x0b, 0x0004);
657         }
658
659         phy->autoneg = autoneg;
660
661         return 0;
662 }
663
664 #define BCM5461_FIBER_LINK      (1 << 2)
665 #define BCM5461_MODE_MASK       (3 << 1)
666
667 static int bcm5461_poll_link(struct mii_phy* phy)
668 {
669         u32 phy_reg;
670         int mode;
671
672         /* find out in what mode we are */
673         sungem_phy_write(phy, MII_NCONFIG, 0x7c00);
674         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
675
676         mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;
677
678         if ( mode == BCM54XX_COPPER)
679                 return genmii_poll_link(phy);
680
681         /* find out whether we have a link */
682         sungem_phy_write(phy, MII_NCONFIG, 0x7000);
683         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
684
685         if (phy_reg & BCM5461_FIBER_LINK)
686                 return 1;
687         else
688                 return 0;
689 }
690
691 #define BCM5461_FIBER_DUPLEX    (1 << 3)
692
693 static int bcm5461_read_link(struct mii_phy* phy)
694 {
695         u32 phy_reg;
696         int mode;
697
698         /* find out in what mode we are */
699         sungem_phy_write(phy, MII_NCONFIG, 0x7c00);
700         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
701
702         mode = (phy_reg & BCM5461_MODE_MASK ) >> 1;
703
704         if ( mode == BCM54XX_COPPER) {
705                 return bcm54xx_read_link(phy);
706         }
707
708         phy->speed = SPEED_1000;
709
710         /* find out whether we are running half- or full duplex */
711         sungem_phy_write(phy, MII_NCONFIG, 0x7000);
712         phy_reg = sungem_phy_read(phy, MII_NCONFIG);
713
714         if (phy_reg & BCM5461_FIBER_DUPLEX)
715                 phy->duplex |=  DUPLEX_FULL;
716         else
717                 phy->duplex |=  DUPLEX_HALF;
718
719         return 0;
720 }
721
722 static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg)
723 {
724         /* select fiber mode, enable 1000 base-X registers */
725         sungem_phy_write(phy, MII_NCONFIG, 0xfc0b);
726
727         if (autoneg) {
728                 /* enable fiber with no autonegotiation */
729                 sungem_phy_write(phy, MII_ADVERTISE, 0x01e0);
730                 sungem_phy_write(phy, MII_BMCR, 0x1140);
731         } else {
732                 /* enable fiber with autonegotiation */
733                 sungem_phy_write(phy, MII_BMCR, 0x0140);
734         }
735
736         phy->autoneg = autoneg;
737
738         return 0;
739 }
740
741 static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)
742 {
743         u16 ctl, adv;
744
745         phy->autoneg = 1;
746         phy->speed = SPEED_10;
747         phy->duplex = DUPLEX_HALF;
748         phy->pause = 0;
749         phy->advertising = advertise;
750
751         /* Setup standard advertise */
752         adv = sungem_phy_read(phy, MII_ADVERTISE);
753         adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
754         if (advertise & ADVERTISED_10baseT_Half)
755                 adv |= ADVERTISE_10HALF;
756         if (advertise & ADVERTISED_10baseT_Full)
757                 adv |= ADVERTISE_10FULL;
758         if (advertise & ADVERTISED_100baseT_Half)
759                 adv |= ADVERTISE_100HALF;
760         if (advertise & ADVERTISED_100baseT_Full)
761                 adv |= ADVERTISE_100FULL;
762         if (advertise & ADVERTISED_Pause)
763                 adv |= ADVERTISE_PAUSE_CAP;
764         if (advertise & ADVERTISED_Asym_Pause)
765                 adv |= ADVERTISE_PAUSE_ASYM;
766         sungem_phy_write(phy, MII_ADVERTISE, adv);
767
768         /* Setup 1000BT advertise & enable crossover detect
769          * XXX How do we advertise 1000BT ? Darwin source is
770          * confusing here, they read from specific control and
771          * write to control... Someone has specs for those
772          * beasts ?
773          */
774         adv = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
775         adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX;
776         adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
777                         MII_1000BASETCONTROL_HALFDUPLEXCAP);
778         if (advertise & SUPPORTED_1000baseT_Half)
779                 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
780         if (advertise & SUPPORTED_1000baseT_Full)
781                 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
782         sungem_phy_write(phy, MII_1000BASETCONTROL, adv);
783
784         /* Start/Restart aneg */
785         ctl = sungem_phy_read(phy, MII_BMCR);
786         ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
787         sungem_phy_write(phy, MII_BMCR, ctl);
788
789         return 0;
790 }
791
792 static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd)
793 {
794         u16 ctl, ctl2;
795
796         phy->autoneg = 0;
797         phy->speed = speed;
798         phy->duplex = fd;
799         phy->pause = 0;
800
801         ctl = sungem_phy_read(phy, MII_BMCR);
802         ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
803         ctl |= BMCR_RESET;
804
805         /* Select speed & duplex */
806         switch(speed) {
807         case SPEED_10:
808                 break;
809         case SPEED_100:
810                 ctl |= BMCR_SPEED100;
811                 break;
812         /* I'm not sure about the one below, again, Darwin source is
813          * quite confusing and I lack chip specs
814          */
815         case SPEED_1000:
816                 ctl |= BMCR_SPD2;
817         }
818         if (fd == DUPLEX_FULL)
819                 ctl |= BMCR_FULLDPLX;
820
821         /* Disable crossover. Again, the way Apple does it is strange,
822          * though I don't assume they are wrong ;)
823          */
824         ctl2 = sungem_phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
825         ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX |
826                 MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX |
827                 MII_1000BASETCONTROL_FULLDUPLEXCAP |
828                 MII_1000BASETCONTROL_HALFDUPLEXCAP);
829         if (speed == SPEED_1000)
830                 ctl2 |= (fd == DUPLEX_FULL) ?
831                         MII_1000BASETCONTROL_FULLDUPLEXCAP :
832                         MII_1000BASETCONTROL_HALFDUPLEXCAP;
833         sungem_phy_write(phy, MII_1000BASETCONTROL, ctl2);
834
835         // XXX Should we set the sungem to GII now on 1000BT ?
836
837         sungem_phy_write(phy, MII_BMCR, ctl);
838
839         return 0;
840 }
841
842 static int marvell_read_link(struct mii_phy *phy)
843 {
844         u16 status, pmask;
845
846         if (phy->autoneg) {
847                 status = sungem_phy_read(phy, MII_M1011_PHY_SPEC_STATUS);
848                 if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
849                         return -EAGAIN;
850                 if (status & MII_M1011_PHY_SPEC_STATUS_1000)
851                         phy->speed = SPEED_1000;
852                 else if (status & MII_M1011_PHY_SPEC_STATUS_100)
853                         phy->speed = SPEED_100;
854                 else
855                         phy->speed = SPEED_10;
856                 if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
857                         phy->duplex = DUPLEX_FULL;
858                 else
859                         phy->duplex = DUPLEX_HALF;
860                 pmask = MII_M1011_PHY_SPEC_STATUS_TX_PAUSE |
861                         MII_M1011_PHY_SPEC_STATUS_RX_PAUSE;
862                 phy->pause = (status & pmask) == pmask;
863         }
864         /* On non-aneg, we assume what we put in BMCR is the speed,
865          * though magic-aneg shouldn't prevent this case from occurring
866          */
867
868         return 0;
869 }
870
871 #define MII_BASIC_FEATURES \
872         (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |      \
873          SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |    \
874          SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII |     \
875          SUPPORTED_Pause)
876
877 /* On gigabit capable PHYs, we advertise Pause support but not asym pause
878  * support for now as I'm not sure it's supported and Darwin doesn't do
879  * it neither. --BenH.
880  */
881 #define MII_GBIT_FEATURES \
882         (MII_BASIC_FEATURES |   \
883          SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)
884
885 /* Broadcom BCM 5201 */
886 static const struct mii_phy_ops bcm5201_phy_ops = {
887         .init           = bcm5201_init,
888         .suspend        = bcm5201_suspend,
889         .setup_aneg     = genmii_setup_aneg,
890         .setup_forced   = genmii_setup_forced,
891         .poll_link      = genmii_poll_link,
892         .read_link      = genmii_read_link,
893 };
894
895 static struct mii_phy_def bcm5201_phy_def = {
896         .phy_id         = 0x00406210,
897         .phy_id_mask    = 0xfffffff0,
898         .name           = "BCM5201",
899         .features       = MII_BASIC_FEATURES,
900         .magic_aneg     = 1,
901         .ops            = &bcm5201_phy_ops
902 };
903
904 /* Broadcom BCM 5221 */
905 static const struct mii_phy_ops bcm5221_phy_ops = {
906         .suspend        = bcm5221_suspend,
907         .init           = bcm5221_init,
908         .setup_aneg     = genmii_setup_aneg,
909         .setup_forced   = genmii_setup_forced,
910         .poll_link      = genmii_poll_link,
911         .read_link      = genmii_read_link,
912 };
913
914 static struct mii_phy_def bcm5221_phy_def = {
915         .phy_id         = 0x004061e0,
916         .phy_id_mask    = 0xfffffff0,
917         .name           = "BCM5221",
918         .features       = MII_BASIC_FEATURES,
919         .magic_aneg     = 1,
920         .ops            = &bcm5221_phy_ops
921 };
922
923 /* Broadcom BCM 5241 */
924 static const struct mii_phy_ops bcm5241_phy_ops = {
925         .suspend        = bcm5241_suspend,
926         .init           = bcm5241_init,
927         .setup_aneg     = genmii_setup_aneg,
928         .setup_forced   = genmii_setup_forced,
929         .poll_link      = genmii_poll_link,
930         .read_link      = genmii_read_link,
931 };
932 static struct mii_phy_def bcm5241_phy_def = {
933         .phy_id         = 0x0143bc30,
934         .phy_id_mask    = 0xfffffff0,
935         .name           = "BCM5241",
936         .features       = MII_BASIC_FEATURES,
937         .magic_aneg     = 1,
938         .ops            = &bcm5241_phy_ops
939 };
940
941 /* Broadcom BCM 5400 */
942 static const struct mii_phy_ops bcm5400_phy_ops = {
943         .init           = bcm5400_init,
944         .suspend        = bcm5400_suspend,
945         .setup_aneg     = bcm54xx_setup_aneg,
946         .setup_forced   = bcm54xx_setup_forced,
947         .poll_link      = genmii_poll_link,
948         .read_link      = bcm54xx_read_link,
949 };
950
951 static struct mii_phy_def bcm5400_phy_def = {
952         .phy_id         = 0x00206040,
953         .phy_id_mask    = 0xfffffff0,
954         .name           = "BCM5400",
955         .features       = MII_GBIT_FEATURES,
956         .magic_aneg     = 1,
957         .ops            = &bcm5400_phy_ops
958 };
959
960 /* Broadcom BCM 5401 */
961 static const struct mii_phy_ops bcm5401_phy_ops = {
962         .init           = bcm5401_init,
963         .suspend        = bcm5401_suspend,
964         .setup_aneg     = bcm54xx_setup_aneg,
965         .setup_forced   = bcm54xx_setup_forced,
966         .poll_link      = genmii_poll_link,
967         .read_link      = bcm54xx_read_link,
968 };
969
970 static struct mii_phy_def bcm5401_phy_def = {
971         .phy_id         = 0x00206050,
972         .phy_id_mask    = 0xfffffff0,
973         .name           = "BCM5401",
974         .features       = MII_GBIT_FEATURES,
975         .magic_aneg     = 1,
976         .ops            = &bcm5401_phy_ops
977 };
978
979 /* Broadcom BCM 5411 */
980 static const struct mii_phy_ops bcm5411_phy_ops = {
981         .init           = bcm5411_init,
982         .suspend        = generic_suspend,
983         .setup_aneg     = bcm54xx_setup_aneg,
984         .setup_forced   = bcm54xx_setup_forced,
985         .poll_link      = genmii_poll_link,
986         .read_link      = bcm54xx_read_link,
987 };
988
989 static struct mii_phy_def bcm5411_phy_def = {
990         .phy_id         = 0x00206070,
991         .phy_id_mask    = 0xfffffff0,
992         .name           = "BCM5411",
993         .features       = MII_GBIT_FEATURES,
994         .magic_aneg     = 1,
995         .ops            = &bcm5411_phy_ops
996 };
997
998 /* Broadcom BCM 5421 */
999 static const struct mii_phy_ops bcm5421_phy_ops = {
1000         .init           = bcm5421_init,
1001         .suspend        = generic_suspend,
1002         .setup_aneg     = bcm54xx_setup_aneg,
1003         .setup_forced   = bcm54xx_setup_forced,
1004         .poll_link      = bcm5421_poll_link,
1005         .read_link      = bcm5421_read_link,
1006         .enable_fiber   = bcm5421_enable_fiber,
1007 };
1008
1009 static struct mii_phy_def bcm5421_phy_def = {
1010         .phy_id         = 0x002060e0,
1011         .phy_id_mask    = 0xfffffff0,
1012         .name           = "BCM5421",
1013         .features       = MII_GBIT_FEATURES,
1014         .magic_aneg     = 1,
1015         .ops            = &bcm5421_phy_ops
1016 };
1017
1018 /* Broadcom BCM 5421 built-in K2 */
1019 static const struct mii_phy_ops bcm5421k2_phy_ops = {
1020         .init           = bcm5421_init,
1021         .suspend        = generic_suspend,
1022         .setup_aneg     = bcm54xx_setup_aneg,
1023         .setup_forced   = bcm54xx_setup_forced,
1024         .poll_link      = genmii_poll_link,
1025         .read_link      = bcm54xx_read_link,
1026 };
1027
1028 static struct mii_phy_def bcm5421k2_phy_def = {
1029         .phy_id         = 0x002062e0,
1030         .phy_id_mask    = 0xfffffff0,
1031         .name           = "BCM5421-K2",
1032         .features       = MII_GBIT_FEATURES,
1033         .magic_aneg     = 1,
1034         .ops            = &bcm5421k2_phy_ops
1035 };
1036
1037 static const struct mii_phy_ops bcm5461_phy_ops = {
1038         .init           = bcm5421_init,
1039         .suspend        = generic_suspend,
1040         .setup_aneg     = bcm54xx_setup_aneg,
1041         .setup_forced   = bcm54xx_setup_forced,
1042         .poll_link      = bcm5461_poll_link,
1043         .read_link      = bcm5461_read_link,
1044         .enable_fiber   = bcm5461_enable_fiber,
1045 };
1046
1047 static struct mii_phy_def bcm5461_phy_def = {
1048         .phy_id         = 0x002060c0,
1049         .phy_id_mask    = 0xfffffff0,
1050         .name           = "BCM5461",
1051         .features       = MII_GBIT_FEATURES,
1052         .magic_aneg     = 1,
1053         .ops            = &bcm5461_phy_ops
1054 };
1055
1056 /* Broadcom BCM 5462 built-in Vesta */
1057 static const struct mii_phy_ops bcm5462V_phy_ops = {
1058         .init           = bcm5421_init,
1059         .suspend        = generic_suspend,
1060         .setup_aneg     = bcm54xx_setup_aneg,
1061         .setup_forced   = bcm54xx_setup_forced,
1062         .poll_link      = genmii_poll_link,
1063         .read_link      = bcm54xx_read_link,
1064 };
1065
1066 static struct mii_phy_def bcm5462V_phy_def = {
1067         .phy_id         = 0x002060d0,
1068         .phy_id_mask    = 0xfffffff0,
1069         .name           = "BCM5462-Vesta",
1070         .features       = MII_GBIT_FEATURES,
1071         .magic_aneg     = 1,
1072         .ops            = &bcm5462V_phy_ops
1073 };
1074
1075 /* Marvell 88E1101 amd 88E1111 */
1076 static const struct mii_phy_ops marvell88e1101_phy_ops = {
1077         .suspend        = generic_suspend,
1078         .setup_aneg     = marvell_setup_aneg,
1079         .setup_forced   = marvell_setup_forced,
1080         .poll_link      = genmii_poll_link,
1081         .read_link      = marvell_read_link
1082 };
1083
1084 static const struct mii_phy_ops marvell88e1111_phy_ops = {
1085         .init           = marvell88e1111_init,
1086         .suspend        = generic_suspend,
1087         .setup_aneg     = marvell_setup_aneg,
1088         .setup_forced   = marvell_setup_forced,
1089         .poll_link      = genmii_poll_link,
1090         .read_link      = marvell_read_link
1091 };
1092
1093 /* two revs in darwin for the 88e1101 ... I could use a datasheet
1094  * to get the proper names...
1095  */
1096 static struct mii_phy_def marvell88e1101v1_phy_def = {
1097         .phy_id         = 0x01410c20,
1098         .phy_id_mask    = 0xfffffff0,
1099         .name           = "Marvell 88E1101v1",
1100         .features       = MII_GBIT_FEATURES,
1101         .magic_aneg     = 1,
1102         .ops            = &marvell88e1101_phy_ops
1103 };
1104 static struct mii_phy_def marvell88e1101v2_phy_def = {
1105         .phy_id         = 0x01410c60,
1106         .phy_id_mask    = 0xfffffff0,
1107         .name           = "Marvell 88E1101v2",
1108         .features       = MII_GBIT_FEATURES,
1109         .magic_aneg     = 1,
1110         .ops            = &marvell88e1101_phy_ops
1111 };
1112 static struct mii_phy_def marvell88e1111_phy_def = {
1113         .phy_id         = 0x01410cc0,
1114         .phy_id_mask    = 0xfffffff0,
1115         .name           = "Marvell 88E1111",
1116         .features       = MII_GBIT_FEATURES,
1117         .magic_aneg     = 1,
1118         .ops            = &marvell88e1111_phy_ops
1119 };
1120
1121 /* Generic implementation for most 10/100 PHYs */
1122 static const struct mii_phy_ops generic_phy_ops = {
1123         .setup_aneg     = genmii_setup_aneg,
1124         .setup_forced   = genmii_setup_forced,
1125         .poll_link      = genmii_poll_link,
1126         .read_link      = genmii_read_link
1127 };
1128
1129 static struct mii_phy_def genmii_phy_def = {
1130         .phy_id         = 0x00000000,
1131         .phy_id_mask    = 0x00000000,
1132         .name           = "Generic MII",
1133         .features       = MII_BASIC_FEATURES,
1134         .magic_aneg     = 0,
1135         .ops            = &generic_phy_ops
1136 };
1137
1138 static struct mii_phy_def* mii_phy_table[] = {
1139         &bcm5201_phy_def,
1140         &bcm5221_phy_def,
1141         &bcm5241_phy_def,
1142         &bcm5400_phy_def,
1143         &bcm5401_phy_def,
1144         &bcm5411_phy_def,
1145         &bcm5421_phy_def,
1146         &bcm5421k2_phy_def,
1147         &bcm5461_phy_def,
1148         &bcm5462V_phy_def,
1149         &marvell88e1101v1_phy_def,
1150         &marvell88e1101v2_phy_def,
1151         &marvell88e1111_phy_def,
1152         &genmii_phy_def,
1153         NULL
1154 };
1155
1156 int sungem_phy_probe(struct mii_phy *phy, int mii_id)
1157 {
1158         int rc;
1159         u32 id;
1160         struct mii_phy_def* def;
1161         int i;
1162
1163         /* We do not reset the mii_phy structure as the driver
1164          * may re-probe the PHY regulary
1165          */
1166         phy->mii_id = mii_id;
1167
1168         /* Take PHY out of isloate mode and reset it. */
1169         rc = reset_one_mii_phy(phy, mii_id);
1170         if (rc)
1171                 goto fail;
1172
1173         /* Read ID and find matching entry */
1174         id = (sungem_phy_read(phy, MII_PHYSID1) << 16 | sungem_phy_read(phy, MII_PHYSID2));
1175         printk(KERN_DEBUG KBUILD_MODNAME ": " "PHY ID: %x, addr: %x\n",
1176                id, mii_id);
1177         for (i=0; (def = mii_phy_table[i]) != NULL; i++)
1178                 if ((id & def->phy_id_mask) == def->phy_id)
1179                         break;
1180         /* Should never be NULL (we have a generic entry), but... */
1181         if (def == NULL)
1182                 goto fail;
1183
1184         phy->def = def;
1185
1186         return 0;
1187 fail:
1188         phy->speed = 0;
1189         phy->duplex = 0;
1190         phy->pause = 0;
1191         phy->advertising = 0;
1192         return -ENODEV;
1193 }
1194
1195 EXPORT_SYMBOL(sungem_phy_probe);
1196 MODULE_LICENSE("GPL");