blob: 49800b25907db7d9abd2f4672a456dac6399227c [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * PHY drivers for the sungem ethernet driver.
Jeff Garzik6aa20a22006-09-13 13:24:59 -04003 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * This file could be shared with other drivers.
Jeff Garzik6aa20a22006-09-13 13:24:59 -04005 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * (c) 2002, Benjamin Herrenscmidt (benh@kernel.crashing.org)
7 *
8 * TODO:
9 * - Implement WOL
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23#include <linux/module.h>
24
25#include <linux/kernel.h>
26#include <linux/sched.h>
27#include <linux/types.h>
28#include <linux/netdevice.h>
29#include <linux/etherdevice.h>
30#include <linux/mii.h>
31#include <linux/ethtool.h>
32#include <linux/delay.h>
33
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -070034#ifdef CONFIG_PPC_PMAC
35#include <asm/prom.h>
36#endif
37
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include "sungem_phy.h"
39
40/* Link modes of the BCM5400 PHY */
Arjan van de Venf71e1302006-03-03 21:33:57 -050041static const int phy_BCM5400_link_table[8][3] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 { 0, 0, 0 }, /* No link */
43 { 0, 0, 0 }, /* 10BT Half Duplex */
44 { 1, 0, 0 }, /* 10BT Full Duplex */
45 { 0, 1, 0 }, /* 100BT Half Duplex */
46 { 0, 1, 0 }, /* 100BT Half Duplex */
47 { 1, 1, 0 }, /* 100BT Full Duplex*/
48 { 1, 0, 1 }, /* 1000BT */
49 { 1, 0, 1 }, /* 1000BT */
50};
51
52static inline int __phy_read(struct mii_phy* phy, int id, int reg)
53{
54 return phy->mdio_read(phy->dev, id, reg);
55}
56
57static inline void __phy_write(struct mii_phy* phy, int id, int reg, int val)
58{
59 phy->mdio_write(phy->dev, id, reg, val);
60}
61
62static inline int phy_read(struct mii_phy* phy, int reg)
63{
64 return phy->mdio_read(phy->dev, phy->mii_id, reg);
65}
66
67static inline void phy_write(struct mii_phy* phy, int reg, int val)
68{
69 phy->mdio_write(phy->dev, phy->mii_id, reg, val);
70}
71
72static int reset_one_mii_phy(struct mii_phy* phy, int phy_id)
73{
74 u16 val;
75 int limit = 10000;
Jeff Garzik6aa20a22006-09-13 13:24:59 -040076
Linus Torvalds1da177e2005-04-16 15:20:36 -070077 val = __phy_read(phy, phy_id, MII_BMCR);
78 val &= ~(BMCR_ISOLATE | BMCR_PDOWN);
79 val |= BMCR_RESET;
80 __phy_write(phy, phy_id, MII_BMCR, val);
81
82 udelay(100);
83
84 while (limit--) {
85 val = __phy_read(phy, phy_id, MII_BMCR);
86 if ((val & BMCR_RESET) == 0)
87 break;
88 udelay(10);
89 }
90 if ((val & BMCR_ISOLATE) && limit > 0)
91 __phy_write(phy, phy_id, MII_BMCR, val & ~BMCR_ISOLATE);
Jeff Garzik6aa20a22006-09-13 13:24:59 -040092
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 return (limit <= 0);
94}
95
96static int bcm5201_init(struct mii_phy* phy)
97{
98 u16 data;
99
100 data = phy_read(phy, MII_BCM5201_MULTIPHY);
101 data &= ~MII_BCM5201_MULTIPHY_SUPERISOLATE;
102 phy_write(phy, MII_BCM5201_MULTIPHY, data);
103
104 phy_write(phy, MII_BCM5201_INTERRUPT, 0);
105
106 return 0;
107}
108
109static int bcm5201_suspend(struct mii_phy* phy)
110{
111 phy_write(phy, MII_BCM5201_INTERRUPT, 0);
112 phy_write(phy, MII_BCM5201_MULTIPHY, MII_BCM5201_MULTIPHY_SUPERISOLATE);
113
114 return 0;
115}
116
117static int bcm5221_init(struct mii_phy* phy)
118{
119 u16 data;
120
121 data = phy_read(phy, MII_BCM5221_TEST);
122 phy_write(phy, MII_BCM5221_TEST,
123 data | MII_BCM5221_TEST_ENABLE_SHADOWS);
124
125 data = phy_read(phy, MII_BCM5221_SHDOW_AUX_STAT2);
126 phy_write(phy, MII_BCM5221_SHDOW_AUX_STAT2,
127 data | MII_BCM5221_SHDOW_AUX_STAT2_APD);
128
129 data = phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
130 phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
131 data | MII_BCM5221_SHDOW_AUX_MODE4_CLKLOPWR);
132
133 data = phy_read(phy, MII_BCM5221_TEST);
134 phy_write(phy, MII_BCM5221_TEST,
135 data & ~MII_BCM5221_TEST_ENABLE_SHADOWS);
136
137 return 0;
138}
139
140static int bcm5221_suspend(struct mii_phy* phy)
141{
142 u16 data;
143
144 data = phy_read(phy, MII_BCM5221_TEST);
145 phy_write(phy, MII_BCM5221_TEST,
146 data | MII_BCM5221_TEST_ENABLE_SHADOWS);
147
148 data = phy_read(phy, MII_BCM5221_SHDOW_AUX_MODE4);
149 phy_write(phy, MII_BCM5221_SHDOW_AUX_MODE4,
150 data | MII_BCM5221_SHDOW_AUX_MODE4_IDDQMODE);
151
152 return 0;
153}
154
155static int bcm5400_init(struct mii_phy* phy)
156{
157 u16 data;
158
159 /* Configure for gigabit full duplex */
160 data = phy_read(phy, MII_BCM5400_AUXCONTROL);
161 data |= MII_BCM5400_AUXCONTROL_PWR10BASET;
162 phy_write(phy, MII_BCM5400_AUXCONTROL, data);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 data = phy_read(phy, MII_BCM5400_GB_CONTROL);
165 data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
166 phy_write(phy, MII_BCM5400_GB_CONTROL, data);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400167
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 udelay(100);
169
170 /* Reset and configure cascaded 10/100 PHY */
171 (void)reset_one_mii_phy(phy, 0x1f);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400172
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 data = __phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
174 data |= MII_BCM5201_MULTIPHY_SERIALMODE;
175 __phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);
176
177 data = phy_read(phy, MII_BCM5400_AUXCONTROL);
178 data &= ~MII_BCM5400_AUXCONTROL_PWR10BASET;
179 phy_write(phy, MII_BCM5400_AUXCONTROL, data);
180
181 return 0;
182}
183
184static int bcm5400_suspend(struct mii_phy* phy)
185{
186#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
187 phy_write(phy, MII_BMCR, BMCR_PDOWN);
188#endif
189 return 0;
190}
191
192static int bcm5401_init(struct mii_phy* phy)
193{
194 u16 data;
195 int rev;
196
197 rev = phy_read(phy, MII_PHYSID2) & 0x000f;
198 if (rev == 0 || rev == 3) {
199 /* Some revisions of 5401 appear to need this
200 * initialisation sequence to disable, according
201 * to OF, "tap power management"
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400202 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 * WARNING ! OF and Darwin don't agree on the
204 * register addresses. OF seem to interpret the
205 * register numbers below as decimal
206 *
207 * Note: This should (and does) match tg3_init_5401phy_dsp
208 * in the tg3.c driver. -DaveM
209 */
210 phy_write(phy, 0x18, 0x0c20);
211 phy_write(phy, 0x17, 0x0012);
212 phy_write(phy, 0x15, 0x1804);
213 phy_write(phy, 0x17, 0x0013);
214 phy_write(phy, 0x15, 0x1204);
215 phy_write(phy, 0x17, 0x8006);
216 phy_write(phy, 0x15, 0x0132);
217 phy_write(phy, 0x17, 0x8006);
218 phy_write(phy, 0x15, 0x0232);
219 phy_write(phy, 0x17, 0x201f);
220 phy_write(phy, 0x15, 0x0a20);
221 }
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400222
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 /* Configure for gigabit full duplex */
224 data = phy_read(phy, MII_BCM5400_GB_CONTROL);
225 data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
226 phy_write(phy, MII_BCM5400_GB_CONTROL, data);
227
228 udelay(10);
229
230 /* Reset and configure cascaded 10/100 PHY */
231 (void)reset_one_mii_phy(phy, 0x1f);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400232
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 data = __phy_read(phy, 0x1f, MII_BCM5201_MULTIPHY);
234 data |= MII_BCM5201_MULTIPHY_SERIALMODE;
235 __phy_write(phy, 0x1f, MII_BCM5201_MULTIPHY, data);
236
237 return 0;
238}
239
240static int bcm5401_suspend(struct mii_phy* phy)
241{
242#if 0 /* Commented out in Darwin... someone has those dawn docs ? */
243 phy_write(phy, MII_BMCR, BMCR_PDOWN);
244#endif
245 return 0;
246}
247
248static int bcm5411_init(struct mii_phy* phy)
249{
250 u16 data;
251
252 /* Here's some more Apple black magic to setup
253 * some voltage stuffs.
254 */
255 phy_write(phy, 0x1c, 0x8c23);
256 phy_write(phy, 0x1c, 0x8ca3);
257 phy_write(phy, 0x1c, 0x8c23);
258
259 /* Here, Apple seems to want to reset it, do
260 * it as well
261 */
262 phy_write(phy, MII_BMCR, BMCR_RESET);
263 phy_write(phy, MII_BMCR, 0x1340);
264
265 data = phy_read(phy, MII_BCM5400_GB_CONTROL);
266 data |= MII_BCM5400_GB_CONTROL_FULLDUPLEXCAP;
267 phy_write(phy, MII_BCM5400_GB_CONTROL, data);
268
269 udelay(10);
270
271 /* Reset and configure cascaded 10/100 PHY */
272 (void)reset_one_mii_phy(phy, 0x1f);
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400273
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 return 0;
275}
276
Johannes Bergd47f3642006-04-19 15:42:28 -0700277static int generic_suspend(struct mii_phy* phy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278{
279 phy_write(phy, MII_BMCR, BMCR_PDOWN);
280
281 return 0;
282}
283
284static int bcm5421_init(struct mii_phy* phy)
285{
286 u16 data;
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700287 unsigned int id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700289 id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2));
290
291 /* Revision 0 of 5421 needs some fixups */
292 if (id == 0x002060e0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 /* This is borrowed from MacOS
294 */
295 phy_write(phy, 0x18, 0x1007);
296 data = phy_read(phy, 0x18);
297 phy_write(phy, 0x18, data | 0x0400);
298 phy_write(phy, 0x18, 0x0007);
299 data = phy_read(phy, 0x18);
300 phy_write(phy, 0x18, data | 0x0800);
301 phy_write(phy, 0x17, 0x000a);
302 data = phy_read(phy, 0x15);
303 phy_write(phy, 0x15, data | 0x0200);
304 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700306 /* Pick up some init code from OF for K2 version */
307 if ((id & 0xfffffff0) == 0x002062e0) {
308 phy_write(phy, 4, 0x01e1);
309 phy_write(phy, 9, 0x0300);
310 }
311
312 /* Check if we can enable automatic low power */
313#ifdef CONFIG_PPC_PMAC
314 if (phy->platform_data) {
315 struct device_node *np = of_get_parent(phy->platform_data);
316 int can_low_power = 1;
317 if (np == NULL || get_property(np, "no-autolowpower", NULL))
318 can_low_power = 0;
319 if (can_low_power) {
320 /* Enable automatic low-power */
321 phy_write(phy, 0x1c, 0x9002);
322 phy_write(phy, 0x1c, 0xa821);
323 phy_write(phy, 0x1c, 0x941d);
324 }
325 }
326#endif /* CONFIG_PPC_PMAC */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 return 0;
329}
330
Jens Osterkamp8ec93452006-05-04 05:59:41 -0400331static int bcm5421_enable_fiber(struct mii_phy* phy)
332{
333 /* enable fiber mode */
334 phy_write(phy, MII_NCONFIG, 0x9020);
335 /* LEDs active in both modes, autosense prio = fiber */
336 phy_write(phy, MII_NCONFIG, 0x945f);
337
338 /* switch off fibre autoneg */
339 phy_write(phy, MII_NCONFIG, 0xfc01);
340 phy_write(phy, 0x0b, 0x0004);
341
342 return 0;
343}
344
345static int bcm5461_enable_fiber(struct mii_phy* phy)
346{
Jens Osterkamp48cf2702006-05-24 23:33:11 +0200347 phy_write(phy, MII_NCONFIG, 0xfc0c);
348 phy_write(phy, MII_BMCR, 0x4140);
349 phy_write(phy, MII_NCONFIG, 0xfc0b);
Jens Osterkamp8ec93452006-05-04 05:59:41 -0400350 phy_write(phy, MII_BMCR, 0x0140);
351
352 return 0;
353}
354
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise)
356{
357 u16 ctl, adv;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 phy->autoneg = 1;
360 phy->speed = SPEED_10;
361 phy->duplex = DUPLEX_HALF;
362 phy->pause = 0;
363 phy->advertising = advertise;
364
365 /* Setup standard advertise */
366 adv = phy_read(phy, MII_ADVERTISE);
367 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
368 if (advertise & ADVERTISED_10baseT_Half)
369 adv |= ADVERTISE_10HALF;
370 if (advertise & ADVERTISED_10baseT_Full)
371 adv |= ADVERTISE_10FULL;
372 if (advertise & ADVERTISED_100baseT_Half)
373 adv |= ADVERTISE_100HALF;
374 if (advertise & ADVERTISED_100baseT_Full)
375 adv |= ADVERTISE_100FULL;
376 phy_write(phy, MII_ADVERTISE, adv);
377
378 /* Setup 1000BT advertise */
379 adv = phy_read(phy, MII_1000BASETCONTROL);
380 adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP|MII_1000BASETCONTROL_HALFDUPLEXCAP);
381 if (advertise & SUPPORTED_1000baseT_Half)
382 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
383 if (advertise & SUPPORTED_1000baseT_Full)
384 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
385 phy_write(phy, MII_1000BASETCONTROL, adv);
386
387 /* Start/Restart aneg */
388 ctl = phy_read(phy, MII_BMCR);
389 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
390 phy_write(phy, MII_BMCR, ctl);
391
392 return 0;
393}
394
395static int bcm54xx_setup_forced(struct mii_phy *phy, int speed, int fd)
396{
397 u16 ctl;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400398
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 phy->autoneg = 0;
400 phy->speed = speed;
401 phy->duplex = fd;
402 phy->pause = 0;
403
404 ctl = phy_read(phy, MII_BMCR);
405 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
406
407 /* First reset the PHY */
408 phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
409
410 /* Select speed & duplex */
411 switch(speed) {
412 case SPEED_10:
413 break;
414 case SPEED_100:
415 ctl |= BMCR_SPEED100;
416 break;
417 case SPEED_1000:
418 ctl |= BMCR_SPD2;
419 }
420 if (fd == DUPLEX_FULL)
421 ctl |= BMCR_FULLDPLX;
422
423 // XXX Should we set the sungem to GII now on 1000BT ?
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400424
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 phy_write(phy, MII_BMCR, ctl);
426
427 return 0;
428}
429
430static int bcm54xx_read_link(struct mii_phy *phy)
431{
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400432 int link_mode;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 u16 val;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400434
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 if (phy->autoneg) {
436 val = phy_read(phy, MII_BCM5400_AUXSTATUS);
437 link_mode = ((val & MII_BCM5400_AUXSTATUS_LINKMODE_MASK) >>
438 MII_BCM5400_AUXSTATUS_LINKMODE_SHIFT);
439 phy->duplex = phy_BCM5400_link_table[link_mode][0] ? DUPLEX_FULL : DUPLEX_HALF;
440 phy->speed = phy_BCM5400_link_table[link_mode][2] ?
441 SPEED_1000 :
442 (phy_BCM5400_link_table[link_mode][1] ? SPEED_100 : SPEED_10);
443 val = phy_read(phy, MII_LPA);
444 phy->pause = ((val & LPA_PAUSE) != 0);
445 }
446 /* On non-aneg, we assume what we put in BMCR is the speed,
447 * though magic-aneg shouldn't prevent this case from occurring
448 */
449
450 return 0;
451}
452
453static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise)
454{
455 u16 ctl, adv;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400456
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 phy->autoneg = 1;
458 phy->speed = SPEED_10;
459 phy->duplex = DUPLEX_HALF;
460 phy->pause = 0;
461 phy->advertising = advertise;
462
463 /* Setup standard advertise */
464 adv = phy_read(phy, MII_ADVERTISE);
465 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
466 if (advertise & ADVERTISED_10baseT_Half)
467 adv |= ADVERTISE_10HALF;
468 if (advertise & ADVERTISED_10baseT_Full)
469 adv |= ADVERTISE_10FULL;
470 if (advertise & ADVERTISED_100baseT_Half)
471 adv |= ADVERTISE_100HALF;
472 if (advertise & ADVERTISED_100baseT_Full)
473 adv |= ADVERTISE_100FULL;
474 phy_write(phy, MII_ADVERTISE, adv);
475
476 /* Setup 1000BT advertise & enable crossover detect
477 * XXX How do we advertise 1000BT ? Darwin source is
478 * confusing here, they read from specific control and
479 * write to control... Someone has specs for those
480 * beasts ?
481 */
482 adv = phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
483 adv |= MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX;
484 adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
485 MII_1000BASETCONTROL_HALFDUPLEXCAP);
486 if (advertise & SUPPORTED_1000baseT_Half)
487 adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
488 if (advertise & SUPPORTED_1000baseT_Full)
489 adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
490 phy_write(phy, MII_1000BASETCONTROL, adv);
491
492 /* Start/Restart aneg */
493 ctl = phy_read(phy, MII_BMCR);
494 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
495 phy_write(phy, MII_BMCR, ctl);
496
497 return 0;
498}
499
500static int marvell_setup_forced(struct mii_phy *phy, int speed, int fd)
501{
502 u16 ctl, ctl2;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400503
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 phy->autoneg = 0;
505 phy->speed = speed;
506 phy->duplex = fd;
507 phy->pause = 0;
508
509 ctl = phy_read(phy, MII_BMCR);
510 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPD2|BMCR_ANENABLE);
511 ctl |= BMCR_RESET;
512
513 /* Select speed & duplex */
514 switch(speed) {
515 case SPEED_10:
516 break;
517 case SPEED_100:
518 ctl |= BMCR_SPEED100;
519 break;
520 /* I'm not sure about the one below, again, Darwin source is
521 * quite confusing and I lack chip specs
522 */
523 case SPEED_1000:
524 ctl |= BMCR_SPD2;
525 }
526 if (fd == DUPLEX_FULL)
527 ctl |= BMCR_FULLDPLX;
528
529 /* Disable crossover. Again, the way Apple does it is strange,
530 * though I don't assume they are wrong ;)
531 */
532 ctl2 = phy_read(phy, MII_M1011_PHY_SPEC_CONTROL);
533 ctl2 &= ~(MII_M1011_PHY_SPEC_CONTROL_MANUAL_MDIX |
534 MII_M1011_PHY_SPEC_CONTROL_AUTO_MDIX |
535 MII_1000BASETCONTROL_FULLDUPLEXCAP |
536 MII_1000BASETCONTROL_HALFDUPLEXCAP);
537 if (speed == SPEED_1000)
538 ctl2 |= (fd == DUPLEX_FULL) ?
539 MII_1000BASETCONTROL_FULLDUPLEXCAP :
540 MII_1000BASETCONTROL_HALFDUPLEXCAP;
541 phy_write(phy, MII_1000BASETCONTROL, ctl2);
542
543 // XXX Should we set the sungem to GII now on 1000BT ?
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400544
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 phy_write(phy, MII_BMCR, ctl);
546
547 return 0;
548}
549
550static int marvell_read_link(struct mii_phy *phy)
551{
552 u16 status;
553
554 if (phy->autoneg) {
555 status = phy_read(phy, MII_M1011_PHY_SPEC_STATUS);
556 if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
557 return -EAGAIN;
558 if (status & MII_M1011_PHY_SPEC_STATUS_1000)
559 phy->speed = SPEED_1000;
560 else if (status & MII_M1011_PHY_SPEC_STATUS_100)
561 phy->speed = SPEED_100;
562 else
563 phy->speed = SPEED_10;
564 if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
565 phy->duplex = DUPLEX_FULL;
566 else
567 phy->duplex = DUPLEX_HALF;
568 phy->pause = 0; /* XXX Check against spec ! */
569 }
570 /* On non-aneg, we assume what we put in BMCR is the speed,
571 * though magic-aneg shouldn't prevent this case from occurring
572 */
573
574 return 0;
575}
576
577static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise)
578{
579 u16 ctl, adv;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400580
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 phy->autoneg = 1;
582 phy->speed = SPEED_10;
583 phy->duplex = DUPLEX_HALF;
584 phy->pause = 0;
585 phy->advertising = advertise;
586
587 /* Setup standard advertise */
588 adv = phy_read(phy, MII_ADVERTISE);
589 adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
590 if (advertise & ADVERTISED_10baseT_Half)
591 adv |= ADVERTISE_10HALF;
592 if (advertise & ADVERTISED_10baseT_Full)
593 adv |= ADVERTISE_10FULL;
594 if (advertise & ADVERTISED_100baseT_Half)
595 adv |= ADVERTISE_100HALF;
596 if (advertise & ADVERTISED_100baseT_Full)
597 adv |= ADVERTISE_100FULL;
598 phy_write(phy, MII_ADVERTISE, adv);
599
600 /* Start/Restart aneg */
601 ctl = phy_read(phy, MII_BMCR);
602 ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
603 phy_write(phy, MII_BMCR, ctl);
604
605 return 0;
606}
607
608static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd)
609{
610 u16 ctl;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 phy->autoneg = 0;
613 phy->speed = speed;
614 phy->duplex = fd;
615 phy->pause = 0;
616
617 ctl = phy_read(phy, MII_BMCR);
618 ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE);
619
620 /* First reset the PHY */
621 phy_write(phy, MII_BMCR, ctl | BMCR_RESET);
622
623 /* Select speed & duplex */
624 switch(speed) {
625 case SPEED_10:
626 break;
627 case SPEED_100:
628 ctl |= BMCR_SPEED100;
629 break;
630 case SPEED_1000:
631 default:
632 return -EINVAL;
633 }
634 if (fd == DUPLEX_FULL)
635 ctl |= BMCR_FULLDPLX;
636 phy_write(phy, MII_BMCR, ctl);
637
638 return 0;
639}
640
641static int genmii_poll_link(struct mii_phy *phy)
642{
643 u16 status;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 (void)phy_read(phy, MII_BMSR);
646 status = phy_read(phy, MII_BMSR);
647 if ((status & BMSR_LSTATUS) == 0)
648 return 0;
649 if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE))
650 return 0;
651 return 1;
652}
653
654static int genmii_read_link(struct mii_phy *phy)
655{
656 u16 lpa;
657
658 if (phy->autoneg) {
659 lpa = phy_read(phy, MII_LPA);
660
661 if (lpa & (LPA_10FULL | LPA_100FULL))
662 phy->duplex = DUPLEX_FULL;
663 else
664 phy->duplex = DUPLEX_HALF;
665 if (lpa & (LPA_100FULL | LPA_100HALF))
666 phy->speed = SPEED_100;
667 else
668 phy->speed = SPEED_10;
669 phy->pause = 0;
670 }
671 /* On non-aneg, we assume what we put in BMCR is the speed,
672 * though magic-aneg shouldn't prevent this case from occurring
673 */
674
675 return 0;
676}
677
678
679#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \
680 SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \
681 SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII)
682#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
683 SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)
684
685/* Broadcom BCM 5201 */
686static struct mii_phy_ops bcm5201_phy_ops = {
687 .init = bcm5201_init,
688 .suspend = bcm5201_suspend,
689 .setup_aneg = genmii_setup_aneg,
690 .setup_forced = genmii_setup_forced,
691 .poll_link = genmii_poll_link,
692 .read_link = genmii_read_link,
693};
694
695static struct mii_phy_def bcm5201_phy_def = {
696 .phy_id = 0x00406210,
697 .phy_id_mask = 0xfffffff0,
698 .name = "BCM5201",
699 .features = MII_BASIC_FEATURES,
700 .magic_aneg = 1,
701 .ops = &bcm5201_phy_ops
702};
703
704/* Broadcom BCM 5221 */
705static struct mii_phy_ops bcm5221_phy_ops = {
706 .suspend = bcm5221_suspend,
707 .init = bcm5221_init,
708 .setup_aneg = genmii_setup_aneg,
709 .setup_forced = genmii_setup_forced,
710 .poll_link = genmii_poll_link,
711 .read_link = genmii_read_link,
712};
713
714static struct mii_phy_def bcm5221_phy_def = {
715 .phy_id = 0x004061e0,
716 .phy_id_mask = 0xfffffff0,
717 .name = "BCM5221",
718 .features = MII_BASIC_FEATURES,
719 .magic_aneg = 1,
720 .ops = &bcm5221_phy_ops
721};
722
723/* Broadcom BCM 5400 */
724static struct mii_phy_ops bcm5400_phy_ops = {
725 .init = bcm5400_init,
726 .suspend = bcm5400_suspend,
727 .setup_aneg = bcm54xx_setup_aneg,
728 .setup_forced = bcm54xx_setup_forced,
729 .poll_link = genmii_poll_link,
730 .read_link = bcm54xx_read_link,
731};
732
733static struct mii_phy_def bcm5400_phy_def = {
734 .phy_id = 0x00206040,
735 .phy_id_mask = 0xfffffff0,
736 .name = "BCM5400",
737 .features = MII_GBIT_FEATURES,
738 .magic_aneg = 1,
739 .ops = &bcm5400_phy_ops
740};
741
742/* Broadcom BCM 5401 */
743static struct mii_phy_ops bcm5401_phy_ops = {
744 .init = bcm5401_init,
745 .suspend = bcm5401_suspend,
746 .setup_aneg = bcm54xx_setup_aneg,
747 .setup_forced = bcm54xx_setup_forced,
748 .poll_link = genmii_poll_link,
749 .read_link = bcm54xx_read_link,
750};
751
752static struct mii_phy_def bcm5401_phy_def = {
753 .phy_id = 0x00206050,
754 .phy_id_mask = 0xfffffff0,
755 .name = "BCM5401",
756 .features = MII_GBIT_FEATURES,
757 .magic_aneg = 1,
758 .ops = &bcm5401_phy_ops
759};
760
761/* Broadcom BCM 5411 */
762static struct mii_phy_ops bcm5411_phy_ops = {
763 .init = bcm5411_init,
Johannes Bergd47f3642006-04-19 15:42:28 -0700764 .suspend = generic_suspend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 .setup_aneg = bcm54xx_setup_aneg,
766 .setup_forced = bcm54xx_setup_forced,
767 .poll_link = genmii_poll_link,
768 .read_link = bcm54xx_read_link,
769};
770
771static struct mii_phy_def bcm5411_phy_def = {
772 .phy_id = 0x00206070,
773 .phy_id_mask = 0xfffffff0,
774 .name = "BCM5411",
775 .features = MII_GBIT_FEATURES,
776 .magic_aneg = 1,
777 .ops = &bcm5411_phy_ops
778};
779
780/* Broadcom BCM 5421 */
781static struct mii_phy_ops bcm5421_phy_ops = {
782 .init = bcm5421_init,
Johannes Bergd47f3642006-04-19 15:42:28 -0700783 .suspend = generic_suspend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 .setup_aneg = bcm54xx_setup_aneg,
785 .setup_forced = bcm54xx_setup_forced,
786 .poll_link = genmii_poll_link,
787 .read_link = bcm54xx_read_link,
Jens Osterkamp8ec93452006-05-04 05:59:41 -0400788 .enable_fiber = bcm5421_enable_fiber,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789};
790
791static struct mii_phy_def bcm5421_phy_def = {
792 .phy_id = 0x002060e0,
793 .phy_id_mask = 0xfffffff0,
794 .name = "BCM5421",
795 .features = MII_GBIT_FEATURES,
796 .magic_aneg = 1,
797 .ops = &bcm5421_phy_ops
798};
799
800/* Broadcom BCM 5421 built-in K2 */
801static struct mii_phy_ops bcm5421k2_phy_ops = {
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700802 .init = bcm5421_init,
Johannes Bergd47f3642006-04-19 15:42:28 -0700803 .suspend = generic_suspend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 .setup_aneg = bcm54xx_setup_aneg,
805 .setup_forced = bcm54xx_setup_forced,
806 .poll_link = genmii_poll_link,
807 .read_link = bcm54xx_read_link,
808};
809
810static struct mii_phy_def bcm5421k2_phy_def = {
811 .phy_id = 0x002062e0,
812 .phy_id_mask = 0xfffffff0,
813 .name = "BCM5421-K2",
814 .features = MII_GBIT_FEATURES,
815 .magic_aneg = 1,
816 .ops = &bcm5421k2_phy_ops
817};
818
Jens Osterkamp8ec93452006-05-04 05:59:41 -0400819static struct mii_phy_ops bcm5461_phy_ops = {
820 .init = bcm5421_init,
821 .suspend = generic_suspend,
822 .setup_aneg = bcm54xx_setup_aneg,
823 .setup_forced = bcm54xx_setup_forced,
824 .poll_link = genmii_poll_link,
825 .read_link = bcm54xx_read_link,
826 .enable_fiber = bcm5461_enable_fiber,
827};
828
829static struct mii_phy_def bcm5461_phy_def = {
830 .phy_id = 0x002060c0,
831 .phy_id_mask = 0xfffffff0,
832 .name = "BCM5461",
833 .features = MII_GBIT_FEATURES,
834 .magic_aneg = 1,
835 .ops = &bcm5461_phy_ops
836};
837
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700838/* Broadcom BCM 5462 built-in Vesta */
839static struct mii_phy_ops bcm5462V_phy_ops = {
840 .init = bcm5421_init,
Johannes Bergd47f3642006-04-19 15:42:28 -0700841 .suspend = generic_suspend,
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700842 .setup_aneg = bcm54xx_setup_aneg,
843 .setup_forced = bcm54xx_setup_forced,
844 .poll_link = genmii_poll_link,
845 .read_link = bcm54xx_read_link,
846};
847
848static struct mii_phy_def bcm5462V_phy_def = {
849 .phy_id = 0x002060d0,
850 .phy_id_mask = 0xfffffff0,
851 .name = "BCM5462-Vesta",
852 .features = MII_GBIT_FEATURES,
853 .magic_aneg = 1,
854 .ops = &bcm5462V_phy_ops
855};
856
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857/* Marvell 88E1101 (Apple seem to deal with 2 different revs,
858 * I masked out the 8 last bits to get both, but some specs
859 * would be useful here) --BenH.
860 */
861static struct mii_phy_ops marvell_phy_ops = {
Johannes Bergd47f3642006-04-19 15:42:28 -0700862 .suspend = generic_suspend,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863 .setup_aneg = marvell_setup_aneg,
864 .setup_forced = marvell_setup_forced,
865 .poll_link = genmii_poll_link,
866 .read_link = marvell_read_link
867};
868
869static struct mii_phy_def marvell_phy_def = {
870 .phy_id = 0x01410c00,
871 .phy_id_mask = 0xffffff00,
872 .name = "Marvell 88E1101",
873 .features = MII_GBIT_FEATURES,
874 .magic_aneg = 1,
875 .ops = &marvell_phy_ops
876};
877
878/* Generic implementation for most 10/100 PHYs */
879static struct mii_phy_ops generic_phy_ops = {
880 .setup_aneg = genmii_setup_aneg,
881 .setup_forced = genmii_setup_forced,
882 .poll_link = genmii_poll_link,
883 .read_link = genmii_read_link
884};
885
886static struct mii_phy_def genmii_phy_def = {
887 .phy_id = 0x00000000,
888 .phy_id_mask = 0x00000000,
889 .name = "Generic MII",
890 .features = MII_BASIC_FEATURES,
891 .magic_aneg = 0,
892 .ops = &generic_phy_ops
893};
894
895static struct mii_phy_def* mii_phy_table[] = {
896 &bcm5201_phy_def,
897 &bcm5221_phy_def,
898 &bcm5400_phy_def,
899 &bcm5401_phy_def,
900 &bcm5411_phy_def,
901 &bcm5421_phy_def,
902 &bcm5421k2_phy_def,
Jens Osterkamp8ec93452006-05-04 05:59:41 -0400903 &bcm5461_phy_def,
Benjamin Herrenschmidt3c326fe2005-07-07 17:56:09 -0700904 &bcm5462V_phy_def,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 &marvell_phy_def,
906 &genmii_phy_def,
907 NULL
908};
909
910int mii_phy_probe(struct mii_phy *phy, int mii_id)
911{
912 int rc;
913 u32 id;
914 struct mii_phy_def* def;
915 int i;
916
917 /* We do not reset the mii_phy structure as the driver
918 * may re-probe the PHY regulary
919 */
920 phy->mii_id = mii_id;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400921
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 /* Take PHY out of isloate mode and reset it. */
923 rc = reset_one_mii_phy(phy, mii_id);
924 if (rc)
925 goto fail;
926
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400927 /* Read ID and find matching entry */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928 id = (phy_read(phy, MII_PHYSID1) << 16 | phy_read(phy, MII_PHYSID2));
929 printk(KERN_DEBUG "PHY ID: %x, addr: %x\n", id, mii_id);
930 for (i=0; (def = mii_phy_table[i]) != NULL; i++)
931 if ((id & def->phy_id_mask) == def->phy_id)
932 break;
933 /* Should never be NULL (we have a generic entry), but... */
934 if (def == NULL)
935 goto fail;
936
937 phy->def = def;
Jeff Garzik6aa20a22006-09-13 13:24:59 -0400938
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 return 0;
940fail:
941 phy->speed = 0;
942 phy->duplex = 0;
943 phy->pause = 0;
944 phy->advertising = 0;
945 return -ENODEV;
946}
947
948EXPORT_SYMBOL(mii_phy_probe);
949MODULE_LICENSE("GPL");
950