PHY: add BCM5464 support to broadcom PHY driver
[pandora-kernel.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_ISR         0x1a    /* BCM54xx interrupt status register */
28 #define MII_BCM54XX_IMR         0x1b    /* BCM54xx interrupt mask register */
29 #define MII_BCM54XX_INT_CRCERR  0x0001  /* CRC error */
30 #define MII_BCM54XX_INT_LINK    0x0002  /* Link status changed */
31 #define MII_BCM54XX_INT_SPEED   0x0004  /* Link speed change */
32 #define MII_BCM54XX_INT_DUPLEX  0x0008  /* Duplex mode changed */
33 #define MII_BCM54XX_INT_LRS     0x0010  /* Local receiver status changed */
34 #define MII_BCM54XX_INT_RRS     0x0020  /* Remote receiver status changed */
35 #define MII_BCM54XX_INT_SSERR   0x0040  /* Scrambler synchronization error */
36 #define MII_BCM54XX_INT_UHCD    0x0080  /* Unsupported HCD negotiated */
37 #define MII_BCM54XX_INT_NHCD    0x0100  /* No HCD */
38 #define MII_BCM54XX_INT_NHCDL   0x0200  /* No HCD link */
39 #define MII_BCM54XX_INT_ANPR    0x0400  /* Auto-negotiation page received */
40 #define MII_BCM54XX_INT_LC      0x0800  /* All counters below 128 */
41 #define MII_BCM54XX_INT_HC      0x1000  /* Counter above 32768 */
42 #define MII_BCM54XX_INT_MDIX    0x2000  /* MDIX status change */
43 #define MII_BCM54XX_INT_PSERR   0x4000  /* Pair swap error */
44
45 MODULE_DESCRIPTION("Broadcom PHY driver");
46 MODULE_AUTHOR("Maciej W. Rozycki");
47 MODULE_LICENSE("GPL");
48
49 static int bcm54xx_config_init(struct phy_device *phydev)
50 {
51         int reg, err;
52
53         reg = phy_read(phydev, MII_BCM54XX_ECR);
54         if (reg < 0)
55                 return reg;
56
57         /* Mask interrupts globally.  */
58         reg |= MII_BCM54XX_ECR_IM;
59         err = phy_write(phydev, MII_BCM54XX_ECR, reg);
60         if (err < 0)
61                 return err;
62
63         /* Unmask events we are interested in.  */
64         reg = ~(MII_BCM54XX_INT_DUPLEX |
65                 MII_BCM54XX_INT_SPEED |
66                 MII_BCM54XX_INT_LINK);
67         err = phy_write(phydev, MII_BCM54XX_IMR, reg);
68         if (err < 0)
69                 return err;
70         return 0;
71 }
72
73 static int bcm54xx_ack_interrupt(struct phy_device *phydev)
74 {
75         int reg;
76
77         /* Clear pending interrupts.  */
78         reg = phy_read(phydev, MII_BCM54XX_ISR);
79         if (reg < 0)
80                 return reg;
81
82         return 0;
83 }
84
85 static int bcm54xx_config_intr(struct phy_device *phydev)
86 {
87         int reg, err;
88
89         reg = phy_read(phydev, MII_BCM54XX_ECR);
90         if (reg < 0)
91                 return reg;
92
93         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
94                 reg &= ~MII_BCM54XX_ECR_IM;
95         else
96                 reg |= MII_BCM54XX_ECR_IM;
97
98         err = phy_write(phydev, MII_BCM54XX_ECR, reg);
99         return err;
100 }
101
102 static int bcm5481_config_aneg(struct phy_device *phydev)
103 {
104         int ret;
105
106         /* Aneg firsly. */
107         ret = genphy_config_aneg(phydev);
108
109         /* Then we can set up the delay. */
110         if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
111                 u16 reg;
112
113                 /*
114                  * There is no BCM5481 specification available, so down
115                  * here is everything we know about "register 0x18". This
116                  * at least helps BCM5481 to successfuly receive packets
117                  * on MPC8360E-RDK board. Peter Barada <peterb@logicpd.com>
118                  * says: "This sets delay between the RXD and RXC signals
119                  * instead of using trace lengths to achieve timing".
120                  */
121
122                 /* Set RDX clk delay. */
123                 reg = 0x7 | (0x7 << 12);
124                 phy_write(phydev, 0x18, reg);
125
126                 reg = phy_read(phydev, 0x18);
127                 /* Set RDX-RXC skew. */
128                 reg |= (1 << 8);
129                 /* Write bits 14:0. */
130                 reg |= (1 << 15);
131                 phy_write(phydev, 0x18, reg);
132         }
133
134         return ret;
135 }
136
137 static struct phy_driver bcm5411_driver = {
138         .phy_id         = 0x00206070,
139         .phy_id_mask    = 0xfffffff0,
140         .name           = "Broadcom BCM5411",
141         .features       = PHY_GBIT_FEATURES,
142         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
143         .config_init    = bcm54xx_config_init,
144         .config_aneg    = genphy_config_aneg,
145         .read_status    = genphy_read_status,
146         .ack_interrupt  = bcm54xx_ack_interrupt,
147         .config_intr    = bcm54xx_config_intr,
148         .driver         = { .owner = THIS_MODULE },
149 };
150
151 static struct phy_driver bcm5421_driver = {
152         .phy_id         = 0x002060e0,
153         .phy_id_mask    = 0xfffffff0,
154         .name           = "Broadcom BCM5421",
155         .features       = PHY_GBIT_FEATURES,
156         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
157         .config_init    = bcm54xx_config_init,
158         .config_aneg    = genphy_config_aneg,
159         .read_status    = genphy_read_status,
160         .ack_interrupt  = bcm54xx_ack_interrupt,
161         .config_intr    = bcm54xx_config_intr,
162         .driver         = { .owner = THIS_MODULE },
163 };
164
165 static struct phy_driver bcm5461_driver = {
166         .phy_id         = 0x002060c0,
167         .phy_id_mask    = 0xfffffff0,
168         .name           = "Broadcom BCM5461",
169         .features       = PHY_GBIT_FEATURES,
170         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
171         .config_init    = bcm54xx_config_init,
172         .config_aneg    = genphy_config_aneg,
173         .read_status    = genphy_read_status,
174         .ack_interrupt  = bcm54xx_ack_interrupt,
175         .config_intr    = bcm54xx_config_intr,
176         .driver         = { .owner = THIS_MODULE },
177 };
178
179 static struct phy_driver bcm5464_driver = {
180         .phy_id         = 0x002060b0,
181         .phy_id_mask    = 0xfffffff0,
182         .name           = "Broadcom BCM5464",
183         .features       = PHY_GBIT_FEATURES,
184         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
185         .config_init    = bcm54xx_config_init,
186         .config_aneg    = genphy_config_aneg,
187         .read_status    = genphy_read_status,
188         .ack_interrupt  = bcm54xx_ack_interrupt,
189         .config_intr    = bcm54xx_config_intr,
190         .driver         = { .owner = THIS_MODULE },
191 };
192
193 static struct phy_driver bcm5481_driver = {
194         .phy_id         = 0x0143bca0,
195         .phy_id_mask    = 0xfffffff0,
196         .name           = "Broadcom BCM5481",
197         .features       = PHY_GBIT_FEATURES,
198         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
199         .config_init    = bcm54xx_config_init,
200         .config_aneg    = bcm5481_config_aneg,
201         .read_status    = genphy_read_status,
202         .ack_interrupt  = bcm54xx_ack_interrupt,
203         .config_intr    = bcm54xx_config_intr,
204         .driver         = { .owner = THIS_MODULE },
205 };
206
207 static struct phy_driver bcm5482_driver = {
208         .phy_id         = 0x0143bcb0,
209         .phy_id_mask    = 0xfffffff0,
210         .name           = "Broadcom BCM5482",
211         .features       = PHY_GBIT_FEATURES,
212         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
213         .config_init    = bcm54xx_config_init,
214         .config_aneg    = genphy_config_aneg,
215         .read_status    = genphy_read_status,
216         .ack_interrupt  = bcm54xx_ack_interrupt,
217         .config_intr    = bcm54xx_config_intr,
218         .driver         = { .owner = THIS_MODULE },
219 };
220
221 static int __init broadcom_init(void)
222 {
223         int ret;
224
225         ret = phy_driver_register(&bcm5411_driver);
226         if (ret)
227                 goto out_5411;
228         ret = phy_driver_register(&bcm5421_driver);
229         if (ret)
230                 goto out_5421;
231         ret = phy_driver_register(&bcm5461_driver);
232         if (ret)
233                 goto out_5461;
234         ret = phy_driver_register(&bcm5464_driver);
235         if (ret)
236                 goto out_5464;
237         ret = phy_driver_register(&bcm5481_driver);
238         if (ret)
239                 goto out_5481;
240         ret = phy_driver_register(&bcm5482_driver);
241         if (ret)
242                 goto out_5482;
243         return ret;
244
245 out_5482:
246         phy_driver_unregister(&bcm5481_driver);
247 out_5481:
248         phy_driver_unregister(&bcm5464_driver);
249 out_5464:
250         phy_driver_unregister(&bcm5461_driver);
251 out_5461:
252         phy_driver_unregister(&bcm5421_driver);
253 out_5421:
254         phy_driver_unregister(&bcm5411_driver);
255 out_5411:
256         return ret;
257 }
258
259 static void __exit broadcom_exit(void)
260 {
261         phy_driver_unregister(&bcm5482_driver);
262         phy_driver_unregister(&bcm5481_driver);
263         phy_driver_unregister(&bcm5464_driver);
264         phy_driver_unregister(&bcm5461_driver);
265         phy_driver_unregister(&bcm5421_driver);
266         phy_driver_unregister(&bcm5411_driver);
267 }
268
269 module_init(broadcom_init);
270 module_exit(broadcom_exit);