Commit | Line | Data |
---|---|---|
00db8189 AF |
1 | /* |
2 | * drivers/net/phy/marvell.c | |
3 | * | |
4 | * Driver for Marvell PHYs | |
5 | * | |
6 | * Author: Andy Fleming | |
7 | * | |
8 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | */ | |
00db8189 | 16 | #include <linux/kernel.h> |
00db8189 AF |
17 | #include <linux/string.h> |
18 | #include <linux/errno.h> | |
19 | #include <linux/unistd.h> | |
00db8189 AF |
20 | #include <linux/interrupt.h> |
21 | #include <linux/init.h> | |
22 | #include <linux/delay.h> | |
23 | #include <linux/netdevice.h> | |
24 | #include <linux/etherdevice.h> | |
25 | #include <linux/skbuff.h> | |
26 | #include <linux/spinlock.h> | |
27 | #include <linux/mm.h> | |
28 | #include <linux/module.h> | |
00db8189 AF |
29 | #include <linux/mii.h> |
30 | #include <linux/ethtool.h> | |
31 | #include <linux/phy.h> | |
2f495c39 | 32 | #include <linux/marvell_phy.h> |
00db8189 AF |
33 | |
34 | #include <asm/io.h> | |
35 | #include <asm/irq.h> | |
36 | #include <asm/uaccess.h> | |
37 | ||
27d916d6 DD |
38 | #define MII_MARVELL_PHY_PAGE 22 |
39 | ||
00db8189 AF |
40 | #define MII_M1011_IEVENT 0x13 |
41 | #define MII_M1011_IEVENT_CLEAR 0x0000 | |
42 | ||
43 | #define MII_M1011_IMASK 0x12 | |
44 | #define MII_M1011_IMASK_INIT 0x6400 | |
45 | #define MII_M1011_IMASK_CLEAR 0x0000 | |
46 | ||
76884679 AF |
47 | #define MII_M1011_PHY_SCR 0x10 |
48 | #define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 | |
49 | ||
50 | #define MII_M1145_PHY_EXT_CR 0x14 | |
51 | #define MII_M1145_RGMII_RX_DELAY 0x0080 | |
52 | #define MII_M1145_RGMII_TX_DELAY 0x0002 | |
53 | ||
76884679 AF |
54 | #define MII_M1111_PHY_LED_CONTROL 0x18 |
55 | #define MII_M1111_PHY_LED_DIRECT 0x4100 | |
56 | #define MII_M1111_PHY_LED_COMBINE 0x411c | |
895ee682 KP |
57 | #define MII_M1111_PHY_EXT_CR 0x14 |
58 | #define MII_M1111_RX_DELAY 0x80 | |
59 | #define MII_M1111_TX_DELAY 0x2 | |
60 | #define MII_M1111_PHY_EXT_SR 0x1b | |
be937f1f AS |
61 | |
62 | #define MII_M1111_HWCFG_MODE_MASK 0xf | |
63 | #define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb | |
64 | #define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3 | |
4117b5be | 65 | #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 |
5f8cbc13 | 66 | #define MII_M1111_HWCFG_MODE_COPPER_RTBI 0x9 |
be937f1f AS |
67 | #define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000 |
68 | #define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000 | |
69 | ||
70 | #define MII_M1111_COPPER 0 | |
71 | #define MII_M1111_FIBER 1 | |
72 | ||
c477d044 CC |
73 | #define MII_88E1121_PHY_MSCR_PAGE 2 |
74 | #define MII_88E1121_PHY_MSCR_REG 21 | |
75 | #define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5) | |
76 | #define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4) | |
77 | #define MII_88E1121_PHY_MSCR_DELAY_MASK (~(0x3 << 4)) | |
78 | ||
337ac9d5 CC |
79 | #define MII_88E1318S_PHY_MSCR1_REG 16 |
80 | #define MII_88E1318S_PHY_MSCR1_PAD_ODD BIT(6) | |
3ff1c259 | 81 | |
140bc929 SP |
82 | #define MII_88E1121_PHY_LED_CTRL 16 |
83 | #define MII_88E1121_PHY_LED_PAGE 3 | |
84 | #define MII_88E1121_PHY_LED_DEF 0x0030 | |
140bc929 | 85 | |
be937f1f AS |
86 | #define MII_M1011_PHY_STATUS 0x11 |
87 | #define MII_M1011_PHY_STATUS_1000 0x8000 | |
88 | #define MII_M1011_PHY_STATUS_100 0x4000 | |
89 | #define MII_M1011_PHY_STATUS_SPD_MASK 0xc000 | |
90 | #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 | |
91 | #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 | |
92 | #define MII_M1011_PHY_STATUS_LINK 0x0400 | |
93 | ||
76884679 | 94 | |
00db8189 AF |
95 | MODULE_DESCRIPTION("Marvell PHY driver"); |
96 | MODULE_AUTHOR("Andy Fleming"); | |
97 | MODULE_LICENSE("GPL"); | |
98 | ||
99 | static int marvell_ack_interrupt(struct phy_device *phydev) | |
100 | { | |
101 | int err; | |
102 | ||
103 | /* Clear the interrupts by reading the reg */ | |
104 | err = phy_read(phydev, MII_M1011_IEVENT); | |
105 | ||
106 | if (err < 0) | |
107 | return err; | |
108 | ||
109 | return 0; | |
110 | } | |
111 | ||
112 | static int marvell_config_intr(struct phy_device *phydev) | |
113 | { | |
114 | int err; | |
115 | ||
76884679 | 116 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) |
00db8189 AF |
117 | err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT); |
118 | else | |
119 | err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); | |
120 | ||
121 | return err; | |
122 | } | |
123 | ||
124 | static int marvell_config_aneg(struct phy_device *phydev) | |
125 | { | |
126 | int err; | |
127 | ||
128 | /* The Marvell PHY has an errata which requires | |
129 | * that certain registers get written in order | |
130 | * to restart autonegotiation */ | |
131 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | |
132 | ||
133 | if (err < 0) | |
134 | return err; | |
135 | ||
136 | err = phy_write(phydev, 0x1d, 0x1f); | |
137 | if (err < 0) | |
138 | return err; | |
139 | ||
140 | err = phy_write(phydev, 0x1e, 0x200c); | |
141 | if (err < 0) | |
142 | return err; | |
143 | ||
144 | err = phy_write(phydev, 0x1d, 0x5); | |
145 | if (err < 0) | |
146 | return err; | |
147 | ||
148 | err = phy_write(phydev, 0x1e, 0); | |
149 | if (err < 0) | |
150 | return err; | |
151 | ||
152 | err = phy_write(phydev, 0x1e, 0x100); | |
153 | if (err < 0) | |
154 | return err; | |
155 | ||
76884679 AF |
156 | err = phy_write(phydev, MII_M1011_PHY_SCR, |
157 | MII_M1011_PHY_SCR_AUTO_CROSS); | |
158 | if (err < 0) | |
159 | return err; | |
160 | ||
161 | err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, | |
162 | MII_M1111_PHY_LED_DIRECT); | |
163 | if (err < 0) | |
164 | return err; | |
00db8189 AF |
165 | |
166 | err = genphy_config_aneg(phydev); | |
8ff44985 AV |
167 | if (err < 0) |
168 | return err; | |
00db8189 | 169 | |
8ff44985 AV |
170 | if (phydev->autoneg != AUTONEG_ENABLE) { |
171 | int bmcr; | |
172 | ||
173 | /* | |
174 | * A write to speed/duplex bits (that is performed by | |
175 | * genphy_config_aneg() call above) must be followed by | |
176 | * a software reset. Otherwise, the write has no effect. | |
177 | */ | |
178 | bmcr = phy_read(phydev, MII_BMCR); | |
179 | if (bmcr < 0) | |
180 | return bmcr; | |
181 | ||
182 | err = phy_write(phydev, MII_BMCR, bmcr | BMCR_RESET); | |
183 | if (err < 0) | |
184 | return err; | |
185 | } | |
186 | ||
187 | return 0; | |
00db8189 AF |
188 | } |
189 | ||
140bc929 SP |
190 | static int m88e1121_config_aneg(struct phy_device *phydev) |
191 | { | |
c477d044 CC |
192 | int err, oldpage, mscr; |
193 | ||
27d916d6 | 194 | oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); |
c477d044 | 195 | |
27d916d6 | 196 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, |
c477d044 CC |
197 | MII_88E1121_PHY_MSCR_PAGE); |
198 | if (err < 0) | |
199 | return err; | |
be8c6480 AP |
200 | |
201 | if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || | |
202 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || | |
203 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || | |
204 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { | |
205 | ||
206 | mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) & | |
207 | MII_88E1121_PHY_MSCR_DELAY_MASK; | |
208 | ||
209 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) | |
210 | mscr |= (MII_88E1121_PHY_MSCR_RX_DELAY | | |
211 | MII_88E1121_PHY_MSCR_TX_DELAY); | |
212 | else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) | |
213 | mscr |= MII_88E1121_PHY_MSCR_RX_DELAY; | |
214 | else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) | |
215 | mscr |= MII_88E1121_PHY_MSCR_TX_DELAY; | |
216 | ||
217 | err = phy_write(phydev, MII_88E1121_PHY_MSCR_REG, mscr); | |
218 | if (err < 0) | |
219 | return err; | |
220 | } | |
c477d044 | 221 | |
27d916d6 | 222 | phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); |
140bc929 SP |
223 | |
224 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | |
225 | if (err < 0) | |
226 | return err; | |
227 | ||
228 | err = phy_write(phydev, MII_M1011_PHY_SCR, | |
229 | MII_M1011_PHY_SCR_AUTO_CROSS); | |
230 | if (err < 0) | |
231 | return err; | |
232 | ||
27d916d6 | 233 | oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); |
140bc929 | 234 | |
27d916d6 | 235 | phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE); |
140bc929 | 236 | phy_write(phydev, MII_88E1121_PHY_LED_CTRL, MII_88E1121_PHY_LED_DEF); |
27d916d6 | 237 | phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); |
140bc929 SP |
238 | |
239 | err = genphy_config_aneg(phydev); | |
240 | ||
241 | return err; | |
242 | } | |
243 | ||
337ac9d5 | 244 | static int m88e1318_config_aneg(struct phy_device *phydev) |
3ff1c259 CC |
245 | { |
246 | int err, oldpage, mscr; | |
247 | ||
27d916d6 | 248 | oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); |
3ff1c259 | 249 | |
27d916d6 | 250 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, |
3ff1c259 CC |
251 | MII_88E1121_PHY_MSCR_PAGE); |
252 | if (err < 0) | |
253 | return err; | |
254 | ||
337ac9d5 CC |
255 | mscr = phy_read(phydev, MII_88E1318S_PHY_MSCR1_REG); |
256 | mscr |= MII_88E1318S_PHY_MSCR1_PAD_ODD; | |
3ff1c259 | 257 | |
337ac9d5 | 258 | err = phy_write(phydev, MII_88E1318S_PHY_MSCR1_REG, mscr); |
3ff1c259 CC |
259 | if (err < 0) |
260 | return err; | |
261 | ||
27d916d6 | 262 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); |
3ff1c259 CC |
263 | if (err < 0) |
264 | return err; | |
265 | ||
266 | return m88e1121_config_aneg(phydev); | |
267 | } | |
268 | ||
895ee682 KP |
269 | static int m88e1111_config_init(struct phy_device *phydev) |
270 | { | |
271 | int err; | |
be937f1f | 272 | int temp; |
be937f1f AS |
273 | |
274 | /* Enable Fiber/Copper auto selection */ | |
275 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); | |
9cf8fa43 | 276 | temp &= ~MII_M1111_HWCFG_FIBER_COPPER_AUTO; |
be937f1f AS |
277 | phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); |
278 | ||
279 | temp = phy_read(phydev, MII_BMCR); | |
280 | temp |= BMCR_RESET; | |
281 | phy_write(phydev, MII_BMCR, temp); | |
895ee682 KP |
282 | |
283 | if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || | |
9daf5a76 KP |
284 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || |
285 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || | |
286 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { | |
895ee682 | 287 | |
9daf5a76 KP |
288 | temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); |
289 | if (temp < 0) | |
290 | return temp; | |
895ee682 | 291 | |
9daf5a76 | 292 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { |
895ee682 | 293 | temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); |
9daf5a76 KP |
294 | } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { |
295 | temp &= ~MII_M1111_TX_DELAY; | |
296 | temp |= MII_M1111_RX_DELAY; | |
297 | } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { | |
298 | temp &= ~MII_M1111_RX_DELAY; | |
299 | temp |= MII_M1111_TX_DELAY; | |
895ee682 KP |
300 | } |
301 | ||
9daf5a76 KP |
302 | err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); |
303 | if (err < 0) | |
304 | return err; | |
305 | ||
895ee682 KP |
306 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); |
307 | if (temp < 0) | |
308 | return temp; | |
309 | ||
310 | temp &= ~(MII_M1111_HWCFG_MODE_MASK); | |
be937f1f | 311 | |
7239016d | 312 | if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES) |
be937f1f AS |
313 | temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; |
314 | else | |
315 | temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; | |
895ee682 KP |
316 | |
317 | err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); | |
318 | if (err < 0) | |
319 | return err; | |
320 | } | |
321 | ||
4117b5be | 322 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
4117b5be KJ |
323 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); |
324 | if (temp < 0) | |
325 | return temp; | |
326 | ||
327 | temp &= ~(MII_M1111_HWCFG_MODE_MASK); | |
328 | temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK; | |
32d0c1e1 | 329 | temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; |
4117b5be KJ |
330 | |
331 | err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); | |
332 | if (err < 0) | |
333 | return err; | |
334 | } | |
335 | ||
5f8cbc13 LYB |
336 | if (phydev->interface == PHY_INTERFACE_MODE_RTBI) { |
337 | temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); | |
338 | if (temp < 0) | |
339 | return temp; | |
340 | temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); | |
341 | err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); | |
342 | if (err < 0) | |
343 | return err; | |
344 | ||
345 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); | |
346 | if (temp < 0) | |
347 | return temp; | |
348 | temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES); | |
349 | temp |= 0x7 | MII_M1111_HWCFG_FIBER_COPPER_AUTO; | |
350 | err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); | |
351 | if (err < 0) | |
352 | return err; | |
353 | ||
354 | /* soft reset */ | |
355 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | |
356 | if (err < 0) | |
357 | return err; | |
358 | do | |
359 | temp = phy_read(phydev, MII_BMCR); | |
360 | while (temp & BMCR_RESET); | |
361 | ||
362 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); | |
363 | if (temp < 0) | |
364 | return temp; | |
365 | temp &= ~(MII_M1111_HWCFG_MODE_MASK | MII_M1111_HWCFG_FIBER_COPPER_RES); | |
366 | temp |= MII_M1111_HWCFG_MODE_COPPER_RTBI | MII_M1111_HWCFG_FIBER_COPPER_AUTO; | |
367 | err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); | |
368 | if (err < 0) | |
369 | return err; | |
370 | } | |
371 | ||
372 | ||
895ee682 KP |
373 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); |
374 | if (err < 0) | |
375 | return err; | |
376 | ||
377 | return 0; | |
378 | } | |
379 | ||
605f196e RM |
380 | static int m88e1118_config_aneg(struct phy_device *phydev) |
381 | { | |
382 | int err; | |
383 | ||
384 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | |
385 | if (err < 0) | |
386 | return err; | |
387 | ||
388 | err = phy_write(phydev, MII_M1011_PHY_SCR, | |
389 | MII_M1011_PHY_SCR_AUTO_CROSS); | |
390 | if (err < 0) | |
391 | return err; | |
392 | ||
393 | err = genphy_config_aneg(phydev); | |
394 | return 0; | |
395 | } | |
396 | ||
397 | static int m88e1118_config_init(struct phy_device *phydev) | |
398 | { | |
399 | int err; | |
400 | ||
401 | /* Change address */ | |
27d916d6 | 402 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002); |
605f196e RM |
403 | if (err < 0) |
404 | return err; | |
405 | ||
406 | /* Enable 1000 Mbit */ | |
407 | err = phy_write(phydev, 0x15, 0x1070); | |
408 | if (err < 0) | |
409 | return err; | |
410 | ||
411 | /* Change address */ | |
27d916d6 | 412 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003); |
605f196e RM |
413 | if (err < 0) |
414 | return err; | |
415 | ||
416 | /* Adjust LED Control */ | |
2f495c39 BH |
417 | if (phydev->dev_flags & MARVELL_PHY_M1118_DNS323_LEDS) |
418 | err = phy_write(phydev, 0x10, 0x1100); | |
419 | else | |
420 | err = phy_write(phydev, 0x10, 0x021e); | |
605f196e RM |
421 | if (err < 0) |
422 | return err; | |
423 | ||
424 | /* Reset address */ | |
27d916d6 | 425 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); |
605f196e RM |
426 | if (err < 0) |
427 | return err; | |
428 | ||
429 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | |
430 | if (err < 0) | |
431 | return err; | |
432 | ||
433 | return 0; | |
434 | } | |
435 | ||
76884679 AF |
436 | static int m88e1145_config_init(struct phy_device *phydev) |
437 | { | |
438 | int err; | |
439 | ||
440 | /* Take care of errata E0 & E1 */ | |
441 | err = phy_write(phydev, 0x1d, 0x001b); | |
442 | if (err < 0) | |
443 | return err; | |
444 | ||
445 | err = phy_write(phydev, 0x1e, 0x418f); | |
446 | if (err < 0) | |
447 | return err; | |
448 | ||
449 | err = phy_write(phydev, 0x1d, 0x0016); | |
450 | if (err < 0) | |
451 | return err; | |
452 | ||
453 | err = phy_write(phydev, 0x1e, 0xa2da); | |
454 | if (err < 0) | |
455 | return err; | |
456 | ||
895ee682 | 457 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { |
76884679 AF |
458 | int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); |
459 | if (temp < 0) | |
460 | return temp; | |
461 | ||
462 | temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); | |
463 | ||
464 | err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); | |
465 | if (err < 0) | |
466 | return err; | |
467 | ||
2f495c39 | 468 | if (phydev->dev_flags & MARVELL_PHY_M1145_FLAGS_RESISTANCE) { |
76884679 AF |
469 | err = phy_write(phydev, 0x1d, 0x0012); |
470 | if (err < 0) | |
471 | return err; | |
472 | ||
473 | temp = phy_read(phydev, 0x1e); | |
474 | if (temp < 0) | |
475 | return temp; | |
476 | ||
477 | temp &= 0xf03f; | |
478 | temp |= 2 << 9; /* 36 ohm */ | |
479 | temp |= 2 << 6; /* 39 ohm */ | |
480 | ||
481 | err = phy_write(phydev, 0x1e, temp); | |
482 | if (err < 0) | |
483 | return err; | |
484 | ||
485 | err = phy_write(phydev, 0x1d, 0x3); | |
486 | if (err < 0) | |
487 | return err; | |
488 | ||
489 | err = phy_write(phydev, 0x1e, 0x8000); | |
490 | if (err < 0) | |
491 | return err; | |
492 | } | |
493 | } | |
494 | ||
495 | return 0; | |
496 | } | |
00db8189 | 497 | |
be937f1f AS |
498 | /* marvell_read_status |
499 | * | |
500 | * Generic status code does not detect Fiber correctly! | |
f0c88f9c | 501 | * Description: |
be937f1f AS |
502 | * Check the link, then figure out the current state |
503 | * by comparing what we advertise with what the link partner | |
504 | * advertises. Start by checking the gigabit possibilities, | |
505 | * then move on to 10/100. | |
506 | */ | |
507 | static int marvell_read_status(struct phy_device *phydev) | |
508 | { | |
509 | int adv; | |
510 | int err; | |
511 | int lpa; | |
512 | int status = 0; | |
513 | ||
514 | /* Update the link, but return if there | |
515 | * was an error */ | |
516 | err = genphy_update_link(phydev); | |
517 | if (err) | |
518 | return err; | |
519 | ||
520 | if (AUTONEG_ENABLE == phydev->autoneg) { | |
521 | status = phy_read(phydev, MII_M1011_PHY_STATUS); | |
522 | if (status < 0) | |
523 | return status; | |
524 | ||
525 | lpa = phy_read(phydev, MII_LPA); | |
526 | if (lpa < 0) | |
527 | return lpa; | |
528 | ||
529 | adv = phy_read(phydev, MII_ADVERTISE); | |
530 | if (adv < 0) | |
531 | return adv; | |
532 | ||
533 | lpa &= adv; | |
534 | ||
535 | if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) | |
536 | phydev->duplex = DUPLEX_FULL; | |
537 | else | |
538 | phydev->duplex = DUPLEX_HALF; | |
539 | ||
540 | status = status & MII_M1011_PHY_STATUS_SPD_MASK; | |
541 | phydev->pause = phydev->asym_pause = 0; | |
542 | ||
543 | switch (status) { | |
544 | case MII_M1011_PHY_STATUS_1000: | |
545 | phydev->speed = SPEED_1000; | |
546 | break; | |
547 | ||
548 | case MII_M1011_PHY_STATUS_100: | |
549 | phydev->speed = SPEED_100; | |
550 | break; | |
551 | ||
552 | default: | |
553 | phydev->speed = SPEED_10; | |
554 | break; | |
555 | } | |
556 | ||
557 | if (phydev->duplex == DUPLEX_FULL) { | |
558 | phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; | |
559 | phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; | |
560 | } | |
561 | } else { | |
562 | int bmcr = phy_read(phydev, MII_BMCR); | |
563 | ||
564 | if (bmcr < 0) | |
565 | return bmcr; | |
566 | ||
567 | if (bmcr & BMCR_FULLDPLX) | |
568 | phydev->duplex = DUPLEX_FULL; | |
569 | else | |
570 | phydev->duplex = DUPLEX_HALF; | |
571 | ||
572 | if (bmcr & BMCR_SPEED1000) | |
573 | phydev->speed = SPEED_1000; | |
574 | else if (bmcr & BMCR_SPEED100) | |
575 | phydev->speed = SPEED_100; | |
576 | else | |
577 | phydev->speed = SPEED_10; | |
578 | ||
579 | phydev->pause = phydev->asym_pause = 0; | |
580 | } | |
581 | ||
582 | return 0; | |
583 | } | |
584 | ||
dcd07be3 AG |
585 | static int m88e1121_did_interrupt(struct phy_device *phydev) |
586 | { | |
587 | int imask; | |
588 | ||
589 | imask = phy_read(phydev, MII_M1011_IEVENT); | |
590 | ||
591 | if (imask & MII_M1011_IMASK_INIT) | |
592 | return 1; | |
593 | ||
594 | return 0; | |
595 | } | |
596 | ||
e5479239 OJ |
597 | static struct phy_driver marvell_drivers[] = { |
598 | { | |
2f495c39 BH |
599 | .phy_id = MARVELL_PHY_ID_88E1101, |
600 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
e5479239 OJ |
601 | .name = "Marvell 88E1101", |
602 | .features = PHY_GBIT_FEATURES, | |
603 | .flags = PHY_HAS_INTERRUPT, | |
604 | .config_aneg = &marvell_config_aneg, | |
605 | .read_status = &genphy_read_status, | |
606 | .ack_interrupt = &marvell_ack_interrupt, | |
607 | .config_intr = &marvell_config_intr, | |
ac8c635a | 608 | .driver = { .owner = THIS_MODULE }, |
e5479239 | 609 | }, |
85cfb534 | 610 | { |
2f495c39 BH |
611 | .phy_id = MARVELL_PHY_ID_88E1112, |
612 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
85cfb534 OJ |
613 | .name = "Marvell 88E1112", |
614 | .features = PHY_GBIT_FEATURES, | |
615 | .flags = PHY_HAS_INTERRUPT, | |
616 | .config_init = &m88e1111_config_init, | |
617 | .config_aneg = &marvell_config_aneg, | |
618 | .read_status = &genphy_read_status, | |
619 | .ack_interrupt = &marvell_ack_interrupt, | |
620 | .config_intr = &marvell_config_intr, | |
ac8c635a | 621 | .driver = { .owner = THIS_MODULE }, |
85cfb534 | 622 | }, |
e5479239 | 623 | { |
2f495c39 BH |
624 | .phy_id = MARVELL_PHY_ID_88E1111, |
625 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
e5479239 OJ |
626 | .name = "Marvell 88E1111", |
627 | .features = PHY_GBIT_FEATURES, | |
628 | .flags = PHY_HAS_INTERRUPT, | |
629 | .config_init = &m88e1111_config_init, | |
630 | .config_aneg = &marvell_config_aneg, | |
be937f1f | 631 | .read_status = &marvell_read_status, |
e5479239 OJ |
632 | .ack_interrupt = &marvell_ack_interrupt, |
633 | .config_intr = &marvell_config_intr, | |
ac8c635a | 634 | .driver = { .owner = THIS_MODULE }, |
e5479239 | 635 | }, |
605f196e | 636 | { |
2f495c39 BH |
637 | .phy_id = MARVELL_PHY_ID_88E1118, |
638 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
605f196e RM |
639 | .name = "Marvell 88E1118", |
640 | .features = PHY_GBIT_FEATURES, | |
641 | .flags = PHY_HAS_INTERRUPT, | |
642 | .config_init = &m88e1118_config_init, | |
643 | .config_aneg = &m88e1118_config_aneg, | |
644 | .read_status = &genphy_read_status, | |
645 | .ack_interrupt = &marvell_ack_interrupt, | |
646 | .config_intr = &marvell_config_intr, | |
647 | .driver = {.owner = THIS_MODULE,}, | |
648 | }, | |
140bc929 | 649 | { |
2f495c39 BH |
650 | .phy_id = MARVELL_PHY_ID_88E1121R, |
651 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
140bc929 SP |
652 | .name = "Marvell 88E1121R", |
653 | .features = PHY_GBIT_FEATURES, | |
654 | .flags = PHY_HAS_INTERRUPT, | |
655 | .config_aneg = &m88e1121_config_aneg, | |
656 | .read_status = &marvell_read_status, | |
657 | .ack_interrupt = &marvell_ack_interrupt, | |
658 | .config_intr = &marvell_config_intr, | |
dcd07be3 | 659 | .did_interrupt = &m88e1121_did_interrupt, |
140bc929 SP |
660 | .driver = { .owner = THIS_MODULE }, |
661 | }, | |
3ff1c259 | 662 | { |
337ac9d5 | 663 | .phy_id = MARVELL_PHY_ID_88E1318S, |
6ba74014 | 664 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
337ac9d5 | 665 | .name = "Marvell 88E1318S", |
3ff1c259 CC |
666 | .features = PHY_GBIT_FEATURES, |
667 | .flags = PHY_HAS_INTERRUPT, | |
337ac9d5 | 668 | .config_aneg = &m88e1318_config_aneg, |
3ff1c259 CC |
669 | .read_status = &marvell_read_status, |
670 | .ack_interrupt = &marvell_ack_interrupt, | |
671 | .config_intr = &marvell_config_intr, | |
672 | .did_interrupt = &m88e1121_did_interrupt, | |
673 | .driver = { .owner = THIS_MODULE }, | |
674 | }, | |
e5479239 | 675 | { |
2f495c39 BH |
676 | .phy_id = MARVELL_PHY_ID_88E1145, |
677 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
e5479239 OJ |
678 | .name = "Marvell 88E1145", |
679 | .features = PHY_GBIT_FEATURES, | |
680 | .flags = PHY_HAS_INTERRUPT, | |
681 | .config_init = &m88e1145_config_init, | |
682 | .config_aneg = &marvell_config_aneg, | |
683 | .read_status = &genphy_read_status, | |
684 | .ack_interrupt = &marvell_ack_interrupt, | |
685 | .config_intr = &marvell_config_intr, | |
ac8c635a OJ |
686 | .driver = { .owner = THIS_MODULE }, |
687 | }, | |
688 | { | |
2f495c39 BH |
689 | .phy_id = MARVELL_PHY_ID_88E1240, |
690 | .phy_id_mask = MARVELL_PHY_ID_MASK, | |
ac8c635a OJ |
691 | .name = "Marvell 88E1240", |
692 | .features = PHY_GBIT_FEATURES, | |
693 | .flags = PHY_HAS_INTERRUPT, | |
694 | .config_init = &m88e1111_config_init, | |
695 | .config_aneg = &marvell_config_aneg, | |
696 | .read_status = &genphy_read_status, | |
697 | .ack_interrupt = &marvell_ack_interrupt, | |
698 | .config_intr = &marvell_config_intr, | |
699 | .driver = { .owner = THIS_MODULE }, | |
700 | }, | |
00db8189 AF |
701 | }; |
702 | ||
703 | static int __init marvell_init(void) | |
704 | { | |
76884679 | 705 | int ret; |
e5479239 | 706 | int i; |
76884679 | 707 | |
e5479239 OJ |
708 | for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) { |
709 | ret = phy_driver_register(&marvell_drivers[i]); | |
76884679 | 710 | |
e5479239 OJ |
711 | if (ret) { |
712 | while (i-- > 0) | |
713 | phy_driver_unregister(&marvell_drivers[i]); | |
714 | return ret; | |
715 | } | |
716 | } | |
76884679 AF |
717 | |
718 | return 0; | |
00db8189 AF |
719 | } |
720 | ||
721 | static void __exit marvell_exit(void) | |
722 | { | |
e5479239 OJ |
723 | int i; |
724 | ||
725 | for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) | |
726 | phy_driver_unregister(&marvell_drivers[i]); | |
00db8189 AF |
727 | } |
728 | ||
729 | module_init(marvell_init); | |
730 | module_exit(marvell_exit); | |
4e4f10f6 | 731 | |
cf93c945 | 732 | static struct mdio_device_id __maybe_unused marvell_tbl[] = { |
4e4f10f6 DW |
733 | { 0x01410c60, 0xfffffff0 }, |
734 | { 0x01410c90, 0xfffffff0 }, | |
735 | { 0x01410cc0, 0xfffffff0 }, | |
736 | { 0x01410e10, 0xfffffff0 }, | |
737 | { 0x01410cb0, 0xfffffff0 }, | |
738 | { 0x01410cd0, 0xfffffff0 }, | |
739 | { 0x01410e30, 0xfffffff0 }, | |
3ff1c259 | 740 | { 0x01410e90, 0xfffffff0 }, |
4e4f10f6 DW |
741 | { } |
742 | }; | |
743 | ||
744 | MODULE_DEVICE_TABLE(mdio, marvell_tbl); |