diff options
Diffstat (limited to 'drivers/net/phy/marvell.c')
-rw-r--r-- | drivers/net/phy/marvell.c | 131 |
1 files changed, 124 insertions, 7 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index f0574073a2a3..32a8503a7acd 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c | |||
@@ -58,9 +58,25 @@ | |||
58 | #define MII_M1111_RX_DELAY 0x80 | 58 | #define MII_M1111_RX_DELAY 0x80 |
59 | #define MII_M1111_TX_DELAY 0x2 | 59 | #define MII_M1111_TX_DELAY 0x2 |
60 | #define MII_M1111_PHY_EXT_SR 0x1b | 60 | #define MII_M1111_PHY_EXT_SR 0x1b |
61 | #define MII_M1111_HWCFG_MODE_MASK 0xf | 61 | |
62 | #define MII_M1111_HWCFG_MODE_RGMII 0xb | 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 | ||
63 | #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 | 65 | #define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 |
66 | #define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000 | ||
67 | #define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000 | ||
68 | |||
69 | #define MII_M1111_COPPER 0 | ||
70 | #define MII_M1111_FIBER 1 | ||
71 | |||
72 | #define MII_M1011_PHY_STATUS 0x11 | ||
73 | #define MII_M1011_PHY_STATUS_1000 0x8000 | ||
74 | #define MII_M1011_PHY_STATUS_100 0x4000 | ||
75 | #define MII_M1011_PHY_STATUS_SPD_MASK 0xc000 | ||
76 | #define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 | ||
77 | #define MII_M1011_PHY_STATUS_RESOLVED 0x0800 | ||
78 | #define MII_M1011_PHY_STATUS_LINK 0x0400 | ||
79 | |||
64 | 80 | ||
65 | MODULE_DESCRIPTION("Marvell PHY driver"); | 81 | MODULE_DESCRIPTION("Marvell PHY driver"); |
66 | MODULE_AUTHOR("Andy Fleming"); | 82 | MODULE_AUTHOR("Andy Fleming"); |
@@ -141,12 +157,22 @@ static int marvell_config_aneg(struct phy_device *phydev) | |||
141 | static int m88e1111_config_init(struct phy_device *phydev) | 157 | static int m88e1111_config_init(struct phy_device *phydev) |
142 | { | 158 | { |
143 | int err; | 159 | int err; |
160 | int temp; | ||
161 | int mode; | ||
162 | |||
163 | /* Enable Fiber/Copper auto selection */ | ||
164 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); | ||
165 | temp |= MII_M1111_HWCFG_FIBER_COPPER_AUTO; | ||
166 | phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); | ||
167 | |||
168 | temp = phy_read(phydev, MII_BMCR); | ||
169 | temp |= BMCR_RESET; | ||
170 | phy_write(phydev, MII_BMCR, temp); | ||
144 | 171 | ||
145 | if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || | 172 | if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || |
146 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || | 173 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || |
147 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || | 174 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || |
148 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { | 175 | (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { |
149 | int temp; | ||
150 | 176 | ||
151 | temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); | 177 | temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); |
152 | if (temp < 0) | 178 | if (temp < 0) |
@@ -171,7 +197,13 @@ static int m88e1111_config_init(struct phy_device *phydev) | |||
171 | return temp; | 197 | return temp; |
172 | 198 | ||
173 | temp &= ~(MII_M1111_HWCFG_MODE_MASK); | 199 | temp &= ~(MII_M1111_HWCFG_MODE_MASK); |
174 | temp |= MII_M1111_HWCFG_MODE_RGMII; | 200 | |
201 | mode = phy_read(phydev, MII_M1111_PHY_EXT_CR); | ||
202 | |||
203 | if (mode & MII_M1111_HWCFG_FIBER_COPPER_RES) | ||
204 | temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; | ||
205 | else | ||
206 | temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; | ||
175 | 207 | ||
176 | err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); | 208 | err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); |
177 | if (err < 0) | 209 | if (err < 0) |
@@ -179,8 +211,6 @@ static int m88e1111_config_init(struct phy_device *phydev) | |||
179 | } | 211 | } |
180 | 212 | ||
181 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { | 213 | if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
182 | int temp; | ||
183 | |||
184 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); | 214 | temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); |
185 | if (temp < 0) | 215 | if (temp < 0) |
186 | return temp; | 216 | return temp; |
@@ -262,6 +292,93 @@ static int m88e1145_config_init(struct phy_device *phydev) | |||
262 | return 0; | 292 | return 0; |
263 | } | 293 | } |
264 | 294 | ||
295 | /* marvell_read_status | ||
296 | * | ||
297 | * Generic status code does not detect Fiber correctly! | ||
298 | * Description: | ||
299 | * Check the link, then figure out the current state | ||
300 | * by comparing what we advertise with what the link partner | ||
301 | * advertises. Start by checking the gigabit possibilities, | ||
302 | * then move on to 10/100. | ||
303 | */ | ||
304 | static int marvell_read_status(struct phy_device *phydev) | ||
305 | { | ||
306 | int adv; | ||
307 | int err; | ||
308 | int lpa; | ||
309 | int status = 0; | ||
310 | |||
311 | /* Update the link, but return if there | ||
312 | * was an error */ | ||
313 | err = genphy_update_link(phydev); | ||
314 | if (err) | ||
315 | return err; | ||
316 | |||
317 | if (AUTONEG_ENABLE == phydev->autoneg) { | ||
318 | status = phy_read(phydev, MII_M1011_PHY_STATUS); | ||
319 | if (status < 0) | ||
320 | return status; | ||
321 | |||
322 | lpa = phy_read(phydev, MII_LPA); | ||
323 | if (lpa < 0) | ||
324 | return lpa; | ||
325 | |||
326 | adv = phy_read(phydev, MII_ADVERTISE); | ||
327 | if (adv < 0) | ||
328 | return adv; | ||
329 | |||
330 | lpa &= adv; | ||
331 | |||
332 | if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) | ||
333 | phydev->duplex = DUPLEX_FULL; | ||
334 | else | ||
335 | phydev->duplex = DUPLEX_HALF; | ||
336 | |||
337 | status = status & MII_M1011_PHY_STATUS_SPD_MASK; | ||
338 | phydev->pause = phydev->asym_pause = 0; | ||
339 | |||
340 | switch (status) { | ||
341 | case MII_M1011_PHY_STATUS_1000: | ||
342 | phydev->speed = SPEED_1000; | ||
343 | break; | ||
344 | |||
345 | case MII_M1011_PHY_STATUS_100: | ||
346 | phydev->speed = SPEED_100; | ||
347 | break; | ||
348 | |||
349 | default: | ||
350 | phydev->speed = SPEED_10; | ||
351 | break; | ||
352 | } | ||
353 | |||
354 | if (phydev->duplex == DUPLEX_FULL) { | ||
355 | phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; | ||
356 | phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; | ||
357 | } | ||
358 | } else { | ||
359 | int bmcr = phy_read(phydev, MII_BMCR); | ||
360 | |||
361 | if (bmcr < 0) | ||
362 | return bmcr; | ||
363 | |||
364 | if (bmcr & BMCR_FULLDPLX) | ||
365 | phydev->duplex = DUPLEX_FULL; | ||
366 | else | ||
367 | phydev->duplex = DUPLEX_HALF; | ||
368 | |||
369 | if (bmcr & BMCR_SPEED1000) | ||
370 | phydev->speed = SPEED_1000; | ||
371 | else if (bmcr & BMCR_SPEED100) | ||
372 | phydev->speed = SPEED_100; | ||
373 | else | ||
374 | phydev->speed = SPEED_10; | ||
375 | |||
376 | phydev->pause = phydev->asym_pause = 0; | ||
377 | } | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
265 | static struct phy_driver marvell_drivers[] = { | 382 | static struct phy_driver marvell_drivers[] = { |
266 | { | 383 | { |
267 | .phy_id = 0x01410c60, | 384 | .phy_id = 0x01410c60, |
@@ -296,7 +413,7 @@ static struct phy_driver marvell_drivers[] = { | |||
296 | .flags = PHY_HAS_INTERRUPT, | 413 | .flags = PHY_HAS_INTERRUPT, |
297 | .config_init = &m88e1111_config_init, | 414 | .config_init = &m88e1111_config_init, |
298 | .config_aneg = &marvell_config_aneg, | 415 | .config_aneg = &marvell_config_aneg, |
299 | .read_status = &genphy_read_status, | 416 | .read_status = &marvell_read_status, |
300 | .ack_interrupt = &marvell_ack_interrupt, | 417 | .ack_interrupt = &marvell_ack_interrupt, |
301 | .config_intr = &marvell_config_intr, | 418 | .config_intr = &marvell_config_intr, |
302 | .driver = { .owner = THIS_MODULE }, | 419 | .driver = { .owner = THIS_MODULE }, |