at v2.6.26-rc7 270 lines 7.6 kB view raw
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 45MODULE_DESCRIPTION("Broadcom PHY driver"); 46MODULE_AUTHOR("Maciej W. Rozycki"); 47MODULE_LICENSE("GPL"); 48 49static 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 73static 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 85static 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 102static 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 137static 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 151static 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 165static 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 179static 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 193static 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 207static 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 221static 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 245out_5482: 246 phy_driver_unregister(&bcm5481_driver); 247out_5481: 248 phy_driver_unregister(&bcm5464_driver); 249out_5464: 250 phy_driver_unregister(&bcm5461_driver); 251out_5461: 252 phy_driver_unregister(&bcm5421_driver); 253out_5421: 254 phy_driver_unregister(&bcm5411_driver); 255out_5411: 256 return ret; 257} 258 259static 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 269module_init(broadcom_init); 270module_exit(broadcom_exit);