7e935924793d8c734c09cd7a36ac54e3889788c2
[linux-2.6-microblaze.git] / drivers / net / phy / broadcom.c
1 /*
2  *      drivers/net/phy/broadcom.c
3  *
4  *      Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
5  *      transceivers.
6  *
7  *      Copyright (c) 2006  Maciej W. Rozycki
8  *
9  *      Inspired by code written by Amy Fong.
10  *
11  *      This program is free software; you can redistribute it and/or
12  *      modify it under the terms of the GNU General Public License
13  *      as published by the Free Software Foundation; either version
14  *      2 of the License, or (at your option) any later version.
15  */
16
17 #include <linux/module.h>
18 #include <linux/phy.h>
19
20 #define MII_BCM54XX_ECR         0x10    /* BCM54xx extended control register */
21 #define MII_BCM54XX_ECR_IM      0x1000  /* Interrupt mask */
22 #define MII_BCM54XX_ECR_IF      0x0800  /* Interrupt force */
23
24 #define MII_BCM54XX_ESR         0x11    /* BCM54xx extended status register */
25 #define MII_BCM54XX_ESR_IS      0x1000  /* Interrupt status */
26
27 #define MII_BCM54XX_EXP_DATA    0x15    /* Expansion register data */
28 #define MII_BCM54XX_EXP_SEL     0x17    /* Expansion register select */
29 #define MII_BCM54XX_EXP_SEL_SSD 0x0e00  /* Secondary SerDes select */
30 #define MII_BCM54XX_EXP_SEL_ER  0x0f00  /* Expansion register select */
31
32 #define MII_BCM54XX_AUX_CTL     0x18    /* Auxiliary control register */
33 #define MII_BCM54XX_ISR         0x1a    /* BCM54xx interrupt status register */
34 #define MII_BCM54XX_IMR         0x1b    /* BCM54xx interrupt mask register */
35 #define MII_BCM54XX_INT_CRCERR  0x0001  /* CRC error */
36 #define MII_BCM54XX_INT_LINK    0x0002  /* Link status changed */
37 #define MII_BCM54XX_INT_SPEED   0x0004  /* Link speed change */
38 #define MII_BCM54XX_INT_DUPLEX  0x0008  /* Duplex mode changed */
39 #define MII_BCM54XX_INT_LRS     0x0010  /* Local receiver status changed */
40 #define MII_BCM54XX_INT_RRS     0x0020  /* Remote receiver status changed */
41 #define MII_BCM54XX_INT_SSERR   0x0040  /* Scrambler synchronization error */
42 #define MII_BCM54XX_INT_UHCD    0x0080  /* Unsupported HCD negotiated */
43 #define MII_BCM54XX_INT_NHCD    0x0100  /* No HCD */
44 #define MII_BCM54XX_INT_NHCDL   0x0200  /* No HCD link */
45 #define MII_BCM54XX_INT_ANPR    0x0400  /* Auto-negotiation page received */
46 #define MII_BCM54XX_INT_LC      0x0800  /* All counters below 128 */
47 #define MII_BCM54XX_INT_HC      0x1000  /* Counter above 32768 */
48 #define MII_BCM54XX_INT_MDIX    0x2000  /* MDIX status change */
49 #define MII_BCM54XX_INT_PSERR   0x4000  /* Pair swap error */
50
51 #define MII_BCM54XX_SHD         0x1c    /* 0x1c shadow registers */
52 #define MII_BCM54XX_SHD_WRITE   0x8000
53 #define MII_BCM54XX_SHD_VAL(x)  ((x & 0x1f) << 10)
54 #define MII_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
55
56 /*
57  * Broadcom LED source encodings.  These are used in BCM5461, BCM5481,
58  * BCM5482, and possibly some others.
59  */
60 #define BCM_LED_SRC_LINKSPD1    0x0
61 #define BCM_LED_SRC_LINKSPD2    0x1
62 #define BCM_LED_SRC_XMITLED     0x2
63 #define BCM_LED_SRC_ACTIVITYLED 0x3
64 #define BCM_LED_SRC_FDXLED      0x4
65 #define BCM_LED_SRC_SLAVE       0x5
66 #define BCM_LED_SRC_INTR        0x6
67 #define BCM_LED_SRC_QUALITY     0x7
68 #define BCM_LED_SRC_RCVLED      0x8
69 #define BCM_LED_SRC_MULTICOLOR1 0xa
70 #define BCM_LED_SRC_OPENSHORT   0xb
71 #define BCM_LED_SRC_OFF         0xe     /* Tied high */
72 #define BCM_LED_SRC_ON          0xf     /* Tied low */
73
74 /*
75  * BCM5482: Shadow registers
76  * Shadow values go into bits [14:10] of register 0x1c to select a shadow
77  * register to access.
78  */
79 #define BCM5482_SHD_LEDS1       0x0d    /* 01101: LED Selector 1 */
80                                         /* LED3 / ~LINKSPD[2] selector */
81 #define BCM5482_SHD_LEDS1_LED3(src)     ((src & 0xf) << 4)
82                                         /* LED1 / ~LINKSPD[1] selector */
83 #define BCM5482_SHD_LEDS1_LED1(src)     ((src & 0xf) << 0)
84 #define BCM5482_SHD_SSD         0x14    /* 10100: Secondary SerDes control */
85 #define BCM5482_SHD_SSD_LEDM    0x0008  /* SSD LED Mode enable */
86 #define BCM5482_SHD_SSD_EN      0x0001  /* SSD enable */
87 #define BCM5482_SHD_MODE        0x1f    /* 11111: Mode Control Register */
88 #define BCM5482_SHD_MODE_1000BX 0x0001  /* Enable 1000BASE-X registers */
89
90 /*
91  * BCM5482: Secondary SerDes registers
92  */
93 #define BCM5482_SSD_1000BX_CTL          0x00    /* 1000BASE-X Control */
94 #define BCM5482_SSD_1000BX_CTL_PWRDOWN  0x0800  /* Power-down SSD */
95 #define BCM5482_SSD_SGMII_SLAVE         0x15    /* SGMII Slave Register */
96 #define BCM5482_SSD_SGMII_SLAVE_EN      0x0002  /* Slave mode enable */
97 #define BCM5482_SSD_SGMII_SLAVE_AD      0x0001  /* Slave auto-detection */
98
99 /*
100  * Device flags for PHYs that can be configured for different operating
101  * modes.
102  */
103 #define PHY_BCM_FLAGS_VALID             0x80000000
104 #define PHY_BCM_FLAGS_INTF_XAUI         0x00000020
105 #define PHY_BCM_FLAGS_INTF_SGMII        0x00000010
106 #define PHY_BCM_FLAGS_MODE_1000BX       0x00000002
107 #define PHY_BCM_FLAGS_MODE_COPPER       0x00000001
108
109 MODULE_DESCRIPTION("Broadcom PHY driver");
110 MODULE_AUTHOR("Maciej W. Rozycki");
111 MODULE_LICENSE("GPL");
112
113 /*
114  * Indirect register access functions for the 1000BASE-T/100BASE-TX/10BASE-T
115  * 0x1c shadow registers.
116  */
117 static int bcm54xx_shadow_read(struct phy_device *phydev, u16 shadow)
118 {
119         phy_write(phydev, MII_BCM54XX_SHD, MII_BCM54XX_SHD_VAL(shadow));
120         return MII_BCM54XX_SHD_DATA(phy_read(phydev, MII_BCM54XX_SHD));
121 }
122
123 static int bcm54xx_shadow_write(struct phy_device *phydev, u16 shadow, u16 val)
124 {
125         return phy_write(phydev, MII_BCM54XX_SHD,
126                          MII_BCM54XX_SHD_WRITE |
127                          MII_BCM54XX_SHD_VAL(shadow) |
128                          MII_BCM54XX_SHD_DATA(val));
129 }
130
131 /* Indirect register access functions for the Expansion Registers */
132 static int bcm54xx_exp_read(struct phy_device *phydev, u8 regnum)
133 {
134         int val;
135
136         val = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
137         if (val < 0)
138                 return val;
139
140         val = phy_read(phydev, MII_BCM54XX_EXP_DATA);
141
142         /* Restore default value.  It's O.K. if this write fails. */
143         phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
144
145         return val;
146 }
147
148 static int bcm54xx_exp_write(struct phy_device *phydev, u8 regnum, u16 val)
149 {
150         int ret;
151
152         ret = phy_write(phydev, MII_BCM54XX_EXP_SEL, regnum);
153         if (ret < 0)
154                 return ret;
155
156         ret = phy_write(phydev, MII_BCM54XX_EXP_DATA, val);
157
158         /* Restore default value.  It's O.K. if this write fails. */
159         phy_write(phydev, MII_BCM54XX_EXP_SEL, 0);
160
161         return ret;
162 }
163
164 static int bcm54xx_config_init(struct phy_device *phydev)
165 {
166         int reg, err;
167
168         reg = phy_read(phydev, MII_BCM54XX_ECR);
169         if (reg < 0)
170                 return reg;
171
172         /* Mask interrupts globally.  */
173         reg |= MII_BCM54XX_ECR_IM;
174         err = phy_write(phydev, MII_BCM54XX_ECR, reg);
175         if (err < 0)
176                 return err;
177
178         /* Unmask events we are interested in.  */
179         reg = ~(MII_BCM54XX_INT_DUPLEX |
180                 MII_BCM54XX_INT_SPEED |
181                 MII_BCM54XX_INT_LINK);
182         err = phy_write(phydev, MII_BCM54XX_IMR, reg);
183         if (err < 0)
184                 return err;
185         return 0;
186 }
187
188 static int bcm5482_config_init(struct phy_device *phydev)
189 {
190         int err, reg;
191
192         err = bcm54xx_config_init(phydev);
193
194         if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
195                 /*
196                  * Enable secondary SerDes and its use as an LED source
197                  */
198                 reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_SSD);
199                 bcm54xx_shadow_write(phydev, BCM5482_SHD_SSD,
200                                      reg |
201                                      BCM5482_SHD_SSD_LEDM |
202                                      BCM5482_SHD_SSD_EN);
203
204                 /*
205                  * Enable SGMII slave mode and auto-detection
206                  */
207                 reg = BCM5482_SSD_SGMII_SLAVE | MII_BCM54XX_EXP_SEL_SSD;
208                 err = bcm54xx_exp_read(phydev, reg);
209                 if (err < 0)
210                         return err;
211                 err = bcm54xx_exp_write(phydev, reg, err |
212                                         BCM5482_SSD_SGMII_SLAVE_EN |
213                                         BCM5482_SSD_SGMII_SLAVE_AD);
214                 if (err < 0)
215                         return err;
216
217                 /*
218                  * Disable secondary SerDes powerdown
219                  */
220                 reg = BCM5482_SSD_1000BX_CTL | MII_BCM54XX_EXP_SEL_SSD;
221                 err = bcm54xx_exp_read(phydev, reg);
222                 if (err < 0)
223                         return err;
224                 err = bcm54xx_exp_write(phydev, reg,
225                                         err & ~BCM5482_SSD_1000BX_CTL_PWRDOWN);
226                 if (err < 0)
227                         return err;
228
229                 /*
230                  * Select 1000BASE-X register set (primary SerDes)
231                  */
232                 reg = bcm54xx_shadow_read(phydev, BCM5482_SHD_MODE);
233                 bcm54xx_shadow_write(phydev, BCM5482_SHD_MODE,
234                                      reg | BCM5482_SHD_MODE_1000BX);
235
236                 /*
237                  * LED1=ACTIVITYLED, LED3=LINKSPD[2]
238                  * (Use LED1 as secondary SerDes ACTIVITY LED)
239                  */
240                 bcm54xx_shadow_write(phydev, BCM5482_SHD_LEDS1,
241                         BCM5482_SHD_LEDS1_LED1(BCM_LED_SRC_ACTIVITYLED) |
242                         BCM5482_SHD_LEDS1_LED3(BCM_LED_SRC_LINKSPD2));
243
244                 /*
245                  * Auto-negotiation doesn't seem to work quite right
246                  * in this mode, so we disable it and force it to the
247                  * right speed/duplex setting.  Only 'link status'
248                  * is important.
249                  */
250                 phydev->autoneg = AUTONEG_DISABLE;
251                 phydev->speed = SPEED_1000;
252                 phydev->duplex = DUPLEX_FULL;
253         }
254
255         return err;
256 }
257
258 static int bcm5482_read_status(struct phy_device *phydev)
259 {
260         int err;
261
262         err = genphy_read_status(phydev);
263
264         if (phydev->dev_flags & PHY_BCM_FLAGS_MODE_1000BX) {
265                 /*
266                  * Only link status matters for 1000Base-X mode, so force
267                  * 1000 Mbit/s full-duplex status
268                  */
269                 if (phydev->link) {
270                         phydev->speed = SPEED_1000;
271                         phydev->duplex = DUPLEX_FULL;
272                 }
273         }
274
275         return err;
276 }
277
278 static int bcm54xx_ack_interrupt(struct phy_device *phydev)
279 {
280         int reg;
281
282         /* Clear pending interrupts.  */
283         reg = phy_read(phydev, MII_BCM54XX_ISR);
284         if (reg < 0)
285                 return reg;
286
287         return 0;
288 }
289
290 static int bcm54xx_config_intr(struct phy_device *phydev)
291 {
292         int reg, err;
293
294         reg = phy_read(phydev, MII_BCM54XX_ECR);
295         if (reg < 0)
296                 return reg;
297
298         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
299                 reg &= ~MII_BCM54XX_ECR_IM;
300         else
301                 reg |= MII_BCM54XX_ECR_IM;
302
303         err = phy_write(phydev, MII_BCM54XX_ECR, reg);
304         return err;
305 }
306
307 static int bcm5481_config_aneg(struct phy_device *phydev)
308 {
309         int ret;
310
311         /* Aneg firsly. */
312         ret = genphy_config_aneg(phydev);
313
314         /* Then we can set up the delay. */
315         if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
316                 u16 reg;
317
318                 /*
319                  * There is no BCM5481 specification available, so down
320                  * here is everything we know about "register 0x18". This
321                  * at least helps BCM5481 to successfuly receive packets
322                  * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
323                  * says: "This sets delay between the RXD and RXC signals
324                  * instead of using trace lengths to achieve timing".
325                  */
326
327                 /* Set RDX clk delay. */
328                 reg = 0x7 | (0x7 << 12);
329                 phy_write(phydev, 0x18, reg);
330
331                 reg = phy_read(phydev, 0x18);
332                 /* Set RDX-RXC skew. */
333                 reg |= (1 << 8);
334                 /* Write bits 14:0. */
335                 reg |= (1 << 15);
336                 phy_write(phydev, 0x18, reg);
337         }
338
339         return ret;
340 }
341
342 static struct phy_driver bcm5411_driver = {
343         .phy_id         = 0x00206070,
344         .phy_id_mask    = 0xfffffff0,
345         .name           = "Broadcom BCM5411",
346         .features       = PHY_GBIT_FEATURES |
347                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
348         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
349         .config_init    = bcm54xx_config_init,
350         .config_aneg    = genphy_config_aneg,
351         .read_status    = genphy_read_status,
352         .ack_interrupt  = bcm54xx_ack_interrupt,
353         .config_intr    = bcm54xx_config_intr,
354         .driver         = { .owner = THIS_MODULE },
355 };
356
357 static struct phy_driver bcm5421_driver = {
358         .phy_id         = 0x002060e0,
359         .phy_id_mask    = 0xfffffff0,
360         .name           = "Broadcom BCM5421",
361         .features       = PHY_GBIT_FEATURES |
362                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
363         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
364         .config_init    = bcm54xx_config_init,
365         .config_aneg    = genphy_config_aneg,
366         .read_status    = genphy_read_status,
367         .ack_interrupt  = bcm54xx_ack_interrupt,
368         .config_intr    = bcm54xx_config_intr,
369         .driver         = { .owner = THIS_MODULE },
370 };
371
372 static struct phy_driver bcm5461_driver = {
373         .phy_id         = 0x002060c0,
374         .phy_id_mask    = 0xfffffff0,
375         .name           = "Broadcom BCM5461",
376         .features       = PHY_GBIT_FEATURES |
377                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
378         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
379         .config_init    = bcm54xx_config_init,
380         .config_aneg    = genphy_config_aneg,
381         .read_status    = genphy_read_status,
382         .ack_interrupt  = bcm54xx_ack_interrupt,
383         .config_intr    = bcm54xx_config_intr,
384         .driver         = { .owner = THIS_MODULE },
385 };
386
387 static struct phy_driver bcm5464_driver = {
388         .phy_id         = 0x002060b0,
389         .phy_id_mask    = 0xfffffff0,
390         .name           = "Broadcom BCM5464",
391         .features       = PHY_GBIT_FEATURES |
392                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
393         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
394         .config_init    = bcm54xx_config_init,
395         .config_aneg    = genphy_config_aneg,
396         .read_status    = genphy_read_status,
397         .ack_interrupt  = bcm54xx_ack_interrupt,
398         .config_intr    = bcm54xx_config_intr,
399         .driver         = { .owner = THIS_MODULE },
400 };
401
402 static struct phy_driver bcm5481_driver = {
403         .phy_id         = 0x0143bca0,
404         .phy_id_mask    = 0xfffffff0,
405         .name           = "Broadcom BCM5481",
406         .features       = PHY_GBIT_FEATURES |
407                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
408         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
409         .config_init    = bcm54xx_config_init,
410         .config_aneg    = bcm5481_config_aneg,
411         .read_status    = genphy_read_status,
412         .ack_interrupt  = bcm54xx_ack_interrupt,
413         .config_intr    = bcm54xx_config_intr,
414         .driver         = { .owner = THIS_MODULE },
415 };
416
417 static struct phy_driver bcm5482_driver = {
418         .phy_id         = 0x0143bcb0,
419         .phy_id_mask    = 0xfffffff0,
420         .name           = "Broadcom BCM5482",
421         .features       = PHY_GBIT_FEATURES |
422                           SUPPORTED_Pause | SUPPORTED_Asym_Pause,
423         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
424         .config_init    = bcm5482_config_init,
425         .config_aneg    = genphy_config_aneg,
426         .read_status    = bcm5482_read_status,
427         .ack_interrupt  = bcm54xx_ack_interrupt,
428         .config_intr    = bcm54xx_config_intr,
429         .driver         = { .owner = THIS_MODULE },
430 };
431
432 static int __init broadcom_init(void)
433 {
434         int ret;
435
436         ret = phy_driver_register(&bcm5411_driver);
437         if (ret)
438                 goto out_5411;
439         ret = phy_driver_register(&bcm5421_driver);
440         if (ret)
441                 goto out_5421;
442         ret = phy_driver_register(&bcm5461_driver);
443         if (ret)
444                 goto out_5461;
445         ret = phy_driver_register(&bcm5464_driver);
446         if (ret)
447                 goto out_5464;
448         ret = phy_driver_register(&bcm5481_driver);
449         if (ret)
450                 goto out_5481;
451         ret = phy_driver_register(&bcm5482_driver);
452         if (ret)
453                 goto out_5482;
454         return ret;
455
456 out_5482:
457         phy_driver_unregister(&bcm5481_driver);
458 out_5481:
459         phy_driver_unregister(&bcm5464_driver);
460 out_5464:
461         phy_driver_unregister(&bcm5461_driver);
462 out_5461:
463         phy_driver_unregister(&bcm5421_driver);
464 out_5421:
465         phy_driver_unregister(&bcm5411_driver);
466 out_5411:
467         return ret;
468 }
469
470 static void __exit broadcom_exit(void)
471 {
472         phy_driver_unregister(&bcm5482_driver);
473         phy_driver_unregister(&bcm5481_driver);
474         phy_driver_unregister(&bcm5464_driver);
475         phy_driver_unregister(&bcm5461_driver);
476         phy_driver_unregister(&bcm5421_driver);
477         phy_driver_unregister(&bcm5411_driver);
478 }
479
480 module_init(broadcom_init);
481 module_exit(broadcom_exit);