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); 271