1 /* 2 * B53 register access through MII registers 3 * 4 * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <linux/kernel.h> 20 #include <linux/phy.h> 21 #include <linux/module.h> 22 #include <linux/delay.h> 23 #include <linux/brcmphy.h> 24 #include <linux/rtnetlink.h> 25 #include <net/dsa.h> 26 27 #include "b53_priv.h" 28 29 /* MII registers */ 30 #define REG_MII_PAGE 0x10 /* MII Page register */ 31 #define REG_MII_ADDR 0x11 /* MII Address register */ 32 #define REG_MII_DATA0 0x18 /* MII Data register 0 */ 33 #define REG_MII_DATA1 0x19 /* MII Data register 1 */ 34 #define REG_MII_DATA2 0x1a /* MII Data register 2 */ 35 #define REG_MII_DATA3 0x1b /* MII Data register 3 */ 36 37 #define REG_MII_PAGE_ENABLE BIT(0) 38 #define REG_MII_ADDR_WRITE BIT(0) 39 #define REG_MII_ADDR_READ BIT(1) 40 41 static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op) 42 { 43 int i; 44 u16 v; 45 int ret; 46 struct mii_bus *bus = dev->priv; 47 48 if (dev->current_page != page) { 49 /* set page number */ 50 v = (page << 8) | REG_MII_PAGE_ENABLE; 51 ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, 52 REG_MII_PAGE, v); 53 if (ret) 54 return ret; 55 dev->current_page = page; 56 } 57 58 /* set register address */ 59 v = (reg << 8) | op; 60 ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_ADDR, v); 61 if (ret) 62 return ret; 63 64 /* check if operation completed */ 65 for (i = 0; i < 5; ++i) { 66 v = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, 67 REG_MII_ADDR); 68 if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ))) 69 break; 70 usleep_range(10, 100); 71 } 72 73 if (WARN_ON(i == 5)) 74 return -EIO; 75 76 return 0; 77 } 78 79 static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val) 80 { 81 struct mii_bus *bus = dev->priv; 82 int ret; 83 84 ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); 85 if (ret) 86 return ret; 87 88 *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, 89 REG_MII_DATA0) & 0xff; 90 91 return 0; 92 } 93 94 static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val) 95 { 96 struct mii_bus *bus = dev->priv; 97 int ret; 98 99 ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); 100 if (ret) 101 return ret; 102 103 *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0); 104 105 return 0; 106 } 107 108 static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val) 109 { 110 struct mii_bus *bus = dev->priv; 111 int ret; 112 113 ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); 114 if (ret) 115 return ret; 116 117 *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0); 118 *val |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, 119 REG_MII_DATA1) << 16; 120 121 return 0; 122 } 123 124 static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val) 125 { 126 struct mii_bus *bus = dev->priv; 127 u64 temp = 0; 128 int i; 129 int ret; 130 131 ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); 132 if (ret) 133 return ret; 134 135 for (i = 2; i >= 0; i--) { 136 temp <<= 16; 137 temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, 138 REG_MII_DATA0 + i); 139 } 140 141 *val = temp; 142 143 return 0; 144 } 145 146 static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val) 147 { 148 struct mii_bus *bus = dev->priv; 149 u64 temp = 0; 150 int i; 151 int ret; 152 153 ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ); 154 if (ret) 155 return ret; 156 157 for (i = 3; i >= 0; i--) { 158 temp <<= 16; 159 temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, 160 REG_MII_DATA0 + i); 161 } 162 163 *val = temp; 164 165 return 0; 166 } 167 168 static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value) 169 { 170 struct mii_bus *bus = dev->priv; 171 int ret; 172 173 ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, 174 REG_MII_DATA0, value); 175 if (ret) 176 return ret; 177 178 return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); 179 } 180 181 static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg, 182 u16 value) 183 { 184 struct mii_bus *bus = dev->priv; 185 int ret; 186 187 ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, 188 REG_MII_DATA0, value); 189 if (ret) 190 return ret; 191 192 return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); 193 } 194 195 static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg, 196 u32 value) 197 { 198 struct mii_bus *bus = dev->priv; 199 unsigned int i; 200 u32 temp = value; 201 202 for (i = 0; i < 2; i++) { 203 int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, 204 REG_MII_DATA0 + i, 205 temp & 0xffff); 206 if (ret) 207 return ret; 208 temp >>= 16; 209 } 210 211 return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); 212 } 213 214 static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg, 215 u64 value) 216 { 217 struct mii_bus *bus = dev->priv; 218 unsigned int i; 219 u64 temp = value; 220 221 for (i = 0; i < 3; i++) { 222 int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, 223 REG_MII_DATA0 + i, 224 temp & 0xffff); 225 if (ret) 226 return ret; 227 temp >>= 16; 228 } 229 230 return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); 231 } 232 233 static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg, 234 u64 value) 235 { 236 struct mii_bus *bus = dev->priv; 237 unsigned int i; 238 u64 temp = value; 239 240 for (i = 0; i < 4; i++) { 241 int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, 242 REG_MII_DATA0 + i, 243 temp & 0xffff); 244 if (ret) 245 return ret; 246 temp >>= 16; 247 } 248 249 return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE); 250 } 251 252 static int b53_mdio_phy_read16(struct b53_device *dev, int addr, int reg, 253 u16 *value) 254 { 255 struct mii_bus *bus = dev->priv; 256 257 *value = mdiobus_read_nested(bus, addr, reg); 258 259 return 0; 260 } 261 262 static int b53_mdio_phy_write16(struct b53_device *dev, int addr, int reg, 263 u16 value) 264 { 265 struct mii_bus *bus = dev->bus; 266 267 return mdiobus_write_nested(bus, addr, reg, value); 268 } 269 270 static const struct b53_io_ops b53_mdio_ops = { 271 .read8 = b53_mdio_read8, 272 .read16 = b53_mdio_read16, 273 .read32 = b53_mdio_read32, 274 .read48 = b53_mdio_read48, 275 .read64 = b53_mdio_read64, 276 .write8 = b53_mdio_write8, 277 .write16 = b53_mdio_write16, 278 .write32 = b53_mdio_write32, 279 .write48 = b53_mdio_write48, 280 .write64 = b53_mdio_write64, 281 .phy_read16 = b53_mdio_phy_read16, 282 .phy_write16 = b53_mdio_phy_write16, 283 }; 284 285 #define B53_BRCM_OUI_1 0x0143bc00 286 #define B53_BRCM_OUI_2 0x03625c00 287 #define B53_BRCM_OUI_3 0x00406000 288 #define B53_BRCM_OUI_4 0x01410c00 289 #define B53_BRCM_OUI_5 0xae025000 290 291 static int b53_mdio_probe(struct mdio_device *mdiodev) 292 { 293 struct b53_device *dev; 294 u32 phy_id; 295 int ret; 296 297 /* allow the generic PHY driver to take over the non-management MDIO 298 * addresses 299 */ 300 if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) { 301 dev_err(&mdiodev->dev, "leaving address %d to PHY\n", 302 mdiodev->addr); 303 return -ENODEV; 304 } 305 306 /* read the first port's id */ 307 phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16; 308 phy_id |= mdiobus_read(mdiodev->bus, 0, 3); 309 310 /* BCM5325, BCM539x (OUI_1) 311 * BCM53125, BCM53128 (OUI_2) 312 * BCM5365 (OUI_3) 313 */ 314 if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 && 315 (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 && 316 (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 && 317 (phy_id & 0xfffffc00) != B53_BRCM_OUI_4 && 318 (phy_id & 0xfffffc00) != B53_BRCM_OUI_5) { 319 dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id); 320 return -ENODEV; 321 } 322 323 /* First probe will come from SWITCH_MDIO controller on the 7445D0 324 * switch, which will conflict with the 7445 integrated switch 325 * pseudo-phy (we end-up programming both). In that case, we return 326 * -EPROBE_DEFER for the first time we get here, and wait until we come 327 * back with the slave MDIO bus which has the correct indirection 328 * layer setup 329 */ 330 if (of_machine_is_compatible("brcm,bcm7445d0") && 331 strcmp(mdiodev->bus->name, "sf2 slave mii")) 332 return -EPROBE_DEFER; 333 334 dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus); 335 if (!dev) 336 return -ENOMEM; 337 338 /* we don't use page 0xff, so force a page set */ 339 dev->current_page = 0xff; 340 dev->bus = mdiodev->bus; 341 342 dev_set_drvdata(&mdiodev->dev, dev); 343 344 ret = b53_switch_register(dev); 345 if (ret) { 346 dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret); 347 return ret; 348 } 349 350 return ret; 351 } 352 353 static void b53_mdio_remove(struct mdio_device *mdiodev) 354 { 355 struct b53_device *dev = dev_get_drvdata(&mdiodev->dev); 356 357 if (!dev) 358 return; 359 360 b53_switch_remove(dev); 361 } 362 363 static void b53_mdio_shutdown(struct mdio_device *mdiodev) 364 { 365 struct b53_device *dev = dev_get_drvdata(&mdiodev->dev); 366 367 if (!dev) 368 return; 369 370 b53_switch_shutdown(dev); 371 372 dev_set_drvdata(&mdiodev->dev, NULL); 373 } 374 375 static const struct of_device_id b53_of_match[] = { 376 { .compatible = "brcm,bcm5325" }, 377 { .compatible = "brcm,bcm53115" }, 378 { .compatible = "brcm,bcm53125" }, 379 { .compatible = "brcm,bcm53128" }, 380 { .compatible = "brcm,bcm53134" }, 381 { .compatible = "brcm,bcm5365" }, 382 { .compatible = "brcm,bcm5389" }, 383 { .compatible = "brcm,bcm5395" }, 384 { .compatible = "brcm,bcm5397" }, 385 { .compatible = "brcm,bcm5398" }, 386 { /* sentinel */ }, 387 }; 388 MODULE_DEVICE_TABLE(of, b53_of_match); 389 390 static struct mdio_driver b53_mdio_driver = { 391 .probe = b53_mdio_probe, 392 .remove = b53_mdio_remove, 393 .shutdown = b53_mdio_shutdown, 394 .mdiodrv.driver = { 395 .name = "bcm53xx", 396 .of_match_table = b53_of_match, 397 }, 398 }; 399 mdio_module_driver(b53_mdio_driver); 400 401 MODULE_DESCRIPTION("B53 MDIO access driver"); 402 MODULE_LICENSE("Dual BSD/GPL"); 403