diff options
Diffstat (limited to 'drivers/net/phy/marvell.c')
| -rw-r--r-- | drivers/net/phy/marvell.c | 164 |
1 files changed, 151 insertions, 13 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index f0bd1a1aba3a..e8b9c53c304b 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c | |||
| @@ -30,11 +30,14 @@ | |||
| 30 | #include <linux/ethtool.h> | 30 | #include <linux/ethtool.h> |
| 31 | #include <linux/phy.h> | 31 | #include <linux/phy.h> |
| 32 | #include <linux/marvell_phy.h> | 32 | #include <linux/marvell_phy.h> |
| 33 | #include <linux/of.h> | ||
| 33 | 34 | ||
| 34 | #include <asm/io.h> | 35 | #include <asm/io.h> |
| 35 | #include <asm/irq.h> | 36 | #include <asm/irq.h> |
| 36 | #include <asm/uaccess.h> | 37 | #include <asm/uaccess.h> |
| 37 | 38 | ||
| 39 | #define MII_MARVELL_PHY_PAGE 22 | ||
| 40 | |||
| 38 | #define MII_M1011_IEVENT 0x13 | 41 | #define MII_M1011_IEVENT 0x13 |
| 39 | #define MII_M1011_IEVENT_CLEAR 0x0000 | 42 | #define MII_M1011_IEVENT_CLEAR 0x0000 |
| 40 | 43 | ||
| @@ -80,7 +83,6 @@ | |||
| 80 | #define MII_88E1121_PHY_LED_CTRL 16 | 83 | #define MII_88E1121_PHY_LED_CTRL 16 |
| 81 | #define MII_88E1121_PHY_LED_PAGE 3 | 84 | #define MII_88E1121_PHY_LED_PAGE 3 |
| 82 | #define MII_88E1121_PHY_LED_DEF 0x0030 | 85 | #define MII_88E1121_PHY_LED_DEF 0x0030 |
| 83 | #define MII_88E1121_PHY_PAGE 22 | ||
| 84 | 86 | ||
| 85 | #define MII_M1011_PHY_STATUS 0x11 | 87 | #define MII_M1011_PHY_STATUS 0x11 |
| 86 | #define MII_M1011_PHY_STATUS_1000 0x8000 | 88 | #define MII_M1011_PHY_STATUS_1000 0x8000 |
| @@ -186,13 +188,94 @@ static int marvell_config_aneg(struct phy_device *phydev) | |||
| 186 | return 0; | 188 | return 0; |
| 187 | } | 189 | } |
| 188 | 190 | ||
| 191 | #ifdef CONFIG_OF_MDIO | ||
| 192 | /* | ||
| 193 | * Set and/or override some configuration registers based on the | ||
| 194 | * marvell,reg-init property stored in the of_node for the phydev. | ||
| 195 | * | ||
| 196 | * marvell,reg-init = <reg-page reg mask value>,...; | ||
| 197 | * | ||
| 198 | * There may be one or more sets of <reg-page reg mask value>: | ||
| 199 | * | ||
| 200 | * reg-page: which register bank to use. | ||
| 201 | * reg: the register. | ||
| 202 | * mask: if non-zero, ANDed with existing register value. | ||
| 203 | * value: ORed with the masked value and written to the regiser. | ||
| 204 | * | ||
| 205 | */ | ||
| 206 | static int marvell_of_reg_init(struct phy_device *phydev) | ||
| 207 | { | ||
| 208 | const __be32 *paddr; | ||
| 209 | int len, i, saved_page, current_page, page_changed, ret; | ||
| 210 | |||
| 211 | if (!phydev->dev.of_node) | ||
| 212 | return 0; | ||
| 213 | |||
| 214 | paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len); | ||
| 215 | if (!paddr || len < (4 * sizeof(*paddr))) | ||
| 216 | return 0; | ||
| 217 | |||
| 218 | saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE); | ||
| 219 | if (saved_page < 0) | ||
| 220 | return saved_page; | ||
| 221 | page_changed = 0; | ||
| 222 | current_page = saved_page; | ||
| 223 | |||
| 224 | ret = 0; | ||
| 225 | len /= sizeof(*paddr); | ||
| 226 | for (i = 0; i < len - 3; i += 4) { | ||
| 227 | u16 reg_page = be32_to_cpup(paddr + i); | ||
| 228 | u16 reg = be32_to_cpup(paddr + i + 1); | ||
| 229 | u16 mask = be32_to_cpup(paddr + i + 2); | ||
| 230 | u16 val_bits = be32_to_cpup(paddr + i + 3); | ||
| 231 | int val; | ||
| 232 | |||
| 233 | if (reg_page != current_page) { | ||
| 234 | current_page = reg_page; | ||
| 235 | page_changed = 1; | ||
| 236 | ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page); | ||
| 237 | if (ret < 0) | ||
| 238 | goto err; | ||
| 239 | } | ||
| 240 | |||
| 241 | val = 0; | ||
| 242 | if (mask) { | ||
| 243 | val = phy_read(phydev, reg); | ||
| 244 | if (val < 0) { | ||
| 245 | ret = val; | ||
| 246 | goto err; | ||
| 247 | } | ||
| 248 | val &= mask; | ||
| 249 | } | ||
| 250 | val |= val_bits; | ||
| 251 | |||
| 252 | ret = phy_write(phydev, reg, val); | ||
| 253 | if (ret < 0) | ||
| 254 | goto err; | ||
| 255 | |||
| 256 | } | ||
| 257 | err: | ||
| 258 | if (page_changed) { | ||
| 259 | i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page); | ||
| 260 | if (ret == 0) | ||
| 261 | ret = i; | ||
| 262 | } | ||
| 263 | return ret; | ||
| 264 | } | ||
| 265 | #else | ||
| 266 | static int marvell_of_reg_init(struct phy_device *phydev) | ||
| 267 | { | ||
| 268 | return 0; | ||
| 269 | } | ||
| 270 | #endif /* CONFIG_OF_MDIO */ | ||
| 271 | |||
| 189 | static int m88e1121_config_aneg(struct phy_device *phydev) | 272 | static int m88e1121_config_aneg(struct phy_device *phydev) |
| 190 | { | 273 | { |
| 191 | int err, oldpage, mscr; | 274 | int err, oldpage, mscr; |
| 192 | 275 | ||
| 193 | oldpage = phy_read(phydev, MII_88E1121_PHY_PAGE); | 276 | oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); |
| 194 | 277 | ||
| 195 | err = phy_write(phydev, MII_88E1121_PHY_PAGE, | 278 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, |
| 196 | MII_88E1121_PHY_MSCR_PAGE); | 279 | MII_88E1121_PHY_MSCR_PAGE); |
| 197 | if (err < 0) | 280 | if (err < 0) |
| 198 | return err; | 281 | return err; |
| @@ -218,7 +301,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev) | |||
| 218 | return err; | 301 | return err; |
| 219 | } | 302 | } |
| 220 | 303 | ||
| 221 | phy_write(phydev, MII_88E1121_PHY_PAGE, oldpage); | 304 | phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); |
| 222 | 305 | ||
| 223 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | 306 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); |
| 224 | if (err < 0) | 307 | if (err < 0) |
| @@ -229,11 +312,11 @@ static int m88e1121_config_aneg(struct phy_device *phydev) | |||
| 229 | if (err < 0) | 312 | if (err < 0) |
| 230 | return err; | 313 | return err; |
| 231 | 314 | ||
| 232 | oldpage = phy_read(phydev, MII_88E1121_PHY_PAGE); | 315 | oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); |
| 233 | 316 | ||
| 234 | phy_write(phydev, MII_88E1121_PHY_PAGE, MII_88E1121_PHY_LED_PAGE); | 317 | phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_88E1121_PHY_LED_PAGE); |
| 235 | phy_write(phydev, MII_88E1121_PHY_LED_CTRL, MII_88E1121_PHY_LED_DEF); | 318 | phy_write(phydev, MII_88E1121_PHY_LED_CTRL, MII_88E1121_PHY_LED_DEF); |
| 236 | phy_write(phydev, MII_88E1121_PHY_PAGE, oldpage); | 319 | phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); |
| 237 | 320 | ||
| 238 | err = genphy_config_aneg(phydev); | 321 | err = genphy_config_aneg(phydev); |
| 239 | 322 | ||
| @@ -244,9 +327,9 @@ static int m88e1318_config_aneg(struct phy_device *phydev) | |||
| 244 | { | 327 | { |
| 245 | int err, oldpage, mscr; | 328 | int err, oldpage, mscr; |
| 246 | 329 | ||
| 247 | oldpage = phy_read(phydev, MII_88E1121_PHY_PAGE); | 330 | oldpage = phy_read(phydev, MII_MARVELL_PHY_PAGE); |
| 248 | 331 | ||
| 249 | err = phy_write(phydev, MII_88E1121_PHY_PAGE, | 332 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, |
| 250 | MII_88E1121_PHY_MSCR_PAGE); | 333 | MII_88E1121_PHY_MSCR_PAGE); |
| 251 | if (err < 0) | 334 | if (err < 0) |
| 252 | return err; | 335 | return err; |
| @@ -258,7 +341,7 @@ static int m88e1318_config_aneg(struct phy_device *phydev) | |||
| 258 | if (err < 0) | 341 | if (err < 0) |
| 259 | return err; | 342 | return err; |
| 260 | 343 | ||
| 261 | err = phy_write(phydev, MII_88E1121_PHY_PAGE, oldpage); | 344 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, oldpage); |
| 262 | if (err < 0) | 345 | if (err < 0) |
| 263 | return err; | 346 | return err; |
| 264 | 347 | ||
| @@ -368,6 +451,9 @@ static int m88e1111_config_init(struct phy_device *phydev) | |||
| 368 | return err; | 451 | return err; |
| 369 | } | 452 | } |
| 370 | 453 | ||
| 454 | err = marvell_of_reg_init(phydev); | ||
| 455 | if (err < 0) | ||
| 456 | return err; | ||
| 371 | 457 | ||
| 372 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | 458 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); |
| 373 | if (err < 0) | 459 | if (err < 0) |
| @@ -398,7 +484,7 @@ static int m88e1118_config_init(struct phy_device *phydev) | |||
| 398 | int err; | 484 | int err; |
| 399 | 485 | ||
| 400 | /* Change address */ | 486 | /* Change address */ |
| 401 | err = phy_write(phydev, 0x16, 0x0002); | 487 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002); |
| 402 | if (err < 0) | 488 | if (err < 0) |
| 403 | return err; | 489 | return err; |
| 404 | 490 | ||
| @@ -408,7 +494,7 @@ static int m88e1118_config_init(struct phy_device *phydev) | |||
| 408 | return err; | 494 | return err; |
| 409 | 495 | ||
| 410 | /* Change address */ | 496 | /* Change address */ |
| 411 | err = phy_write(phydev, 0x16, 0x0003); | 497 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0003); |
| 412 | if (err < 0) | 498 | if (err < 0) |
| 413 | return err; | 499 | return err; |
| 414 | 500 | ||
| @@ -420,8 +506,42 @@ static int m88e1118_config_init(struct phy_device *phydev) | |||
| 420 | if (err < 0) | 506 | if (err < 0) |
| 421 | return err; | 507 | return err; |
| 422 | 508 | ||
| 509 | err = marvell_of_reg_init(phydev); | ||
| 510 | if (err < 0) | ||
| 511 | return err; | ||
| 512 | |||
| 423 | /* Reset address */ | 513 | /* Reset address */ |
| 424 | err = phy_write(phydev, 0x16, 0x0); | 514 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); |
| 515 | if (err < 0) | ||
| 516 | return err; | ||
| 517 | |||
| 518 | err = phy_write(phydev, MII_BMCR, BMCR_RESET); | ||
| 519 | if (err < 0) | ||
| 520 | return err; | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | |||
| 525 | static int m88e1149_config_init(struct phy_device *phydev) | ||
| 526 | { | ||
| 527 | int err; | ||
| 528 | |||
| 529 | /* Change address */ | ||
| 530 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0002); | ||
| 531 | if (err < 0) | ||
| 532 | return err; | ||
| 533 | |||
| 534 | /* Enable 1000 Mbit */ | ||
| 535 | err = phy_write(phydev, 0x15, 0x1048); | ||
| 536 | if (err < 0) | ||
| 537 | return err; | ||
| 538 | |||
| 539 | err = marvell_of_reg_init(phydev); | ||
| 540 | if (err < 0) | ||
| 541 | return err; | ||
| 542 | |||
| 543 | /* Reset address */ | ||
| 544 | err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); | ||
| 425 | if (err < 0) | 545 | if (err < 0) |
| 426 | return err; | 546 | return err; |
| 427 | 547 | ||
| @@ -491,6 +611,10 @@ static int m88e1145_config_init(struct phy_device *phydev) | |||
| 491 | } | 611 | } |
| 492 | } | 612 | } |
| 493 | 613 | ||
| 614 | err = marvell_of_reg_init(phydev); | ||
| 615 | if (err < 0) | ||
| 616 | return err; | ||
| 617 | |||
| 494 | return 0; | 618 | return 0; |
| 495 | } | 619 | } |
| 496 | 620 | ||
| @@ -685,6 +809,19 @@ static struct phy_driver marvell_drivers[] = { | |||
| 685 | .driver = { .owner = THIS_MODULE }, | 809 | .driver = { .owner = THIS_MODULE }, |
| 686 | }, | 810 | }, |
| 687 | { | 811 | { |
| 812 | .phy_id = MARVELL_PHY_ID_88E1149R, | ||
| 813 | .phy_id_mask = MARVELL_PHY_ID_MASK, | ||
| 814 | .name = "Marvell 88E1149R", | ||
| 815 | .features = PHY_GBIT_FEATURES, | ||
| 816 | .flags = PHY_HAS_INTERRUPT, | ||
| 817 | .config_init = &m88e1149_config_init, | ||
| 818 | .config_aneg = &m88e1118_config_aneg, | ||
| 819 | .read_status = &genphy_read_status, | ||
| 820 | .ack_interrupt = &marvell_ack_interrupt, | ||
| 821 | .config_intr = &marvell_config_intr, | ||
| 822 | .driver = { .owner = THIS_MODULE }, | ||
| 823 | }, | ||
| 824 | { | ||
| 688 | .phy_id = MARVELL_PHY_ID_88E1240, | 825 | .phy_id = MARVELL_PHY_ID_88E1240, |
| 689 | .phy_id_mask = MARVELL_PHY_ID_MASK, | 826 | .phy_id_mask = MARVELL_PHY_ID_MASK, |
| 690 | .name = "Marvell 88E1240", | 827 | .name = "Marvell 88E1240", |
| @@ -735,6 +872,7 @@ static struct mdio_device_id __maybe_unused marvell_tbl[] = { | |||
| 735 | { 0x01410e10, 0xfffffff0 }, | 872 | { 0x01410e10, 0xfffffff0 }, |
| 736 | { 0x01410cb0, 0xfffffff0 }, | 873 | { 0x01410cb0, 0xfffffff0 }, |
| 737 | { 0x01410cd0, 0xfffffff0 }, | 874 | { 0x01410cd0, 0xfffffff0 }, |
| 875 | { 0x01410e50, 0xfffffff0 }, | ||
| 738 | { 0x01410e30, 0xfffffff0 }, | 876 | { 0x01410e30, 0xfffffff0 }, |
| 739 | { 0x01410e90, 0xfffffff0 }, | 877 | { 0x01410e90, 0xfffffff0 }, |
| 740 | { } | 878 | { } |
