b53_mdio.c (9761B)
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 41static 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 79static 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 94static 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 108static 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 124static 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 146static 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 168static 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 181static 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 195static 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 214static 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 233static 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 252static 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 262static 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 270static 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 290static int b53_mdio_probe(struct mdio_device *mdiodev) 291{ 292 struct b53_device *dev; 293 u32 phy_id; 294 int ret; 295 296 /* allow the generic PHY driver to take over the non-management MDIO 297 * addresses 298 */ 299 if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) { 300 dev_err(&mdiodev->dev, "leaving address %d to PHY\n", 301 mdiodev->addr); 302 return -ENODEV; 303 } 304 305 /* read the first port's id */ 306 phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16; 307 phy_id |= mdiobus_read(mdiodev->bus, 0, 3); 308 309 /* BCM5325, BCM539x (OUI_1) 310 * BCM53125, BCM53128 (OUI_2) 311 * BCM5365 (OUI_3) 312 */ 313 if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 && 314 (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 && 315 (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 && 316 (phy_id & 0xfffffc00) != B53_BRCM_OUI_4) { 317 dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id); 318 return -ENODEV; 319 } 320 321 /* First probe will come from SWITCH_MDIO controller on the 7445D0 322 * switch, which will conflict with the 7445 integrated switch 323 * pseudo-phy (we end-up programming both). In that case, we return 324 * -EPROBE_DEFER for the first time we get here, and wait until we come 325 * back with the slave MDIO bus which has the correct indirection 326 * layer setup 327 */ 328 if (of_machine_is_compatible("brcm,bcm7445d0") && 329 strcmp(mdiodev->bus->name, "sf2 slave mii")) 330 return -EPROBE_DEFER; 331 332 dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus); 333 if (!dev) 334 return -ENOMEM; 335 336 /* we don't use page 0xff, so force a page set */ 337 dev->current_page = 0xff; 338 dev->bus = mdiodev->bus; 339 340 dev_set_drvdata(&mdiodev->dev, dev); 341 342 ret = b53_switch_register(dev); 343 if (ret) { 344 dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret); 345 return ret; 346 } 347 348 return ret; 349} 350 351static void b53_mdio_remove(struct mdio_device *mdiodev) 352{ 353 struct b53_device *dev = dev_get_drvdata(&mdiodev->dev); 354 355 if (!dev) 356 return; 357 358 b53_switch_remove(dev); 359 360 dev_set_drvdata(&mdiodev->dev, NULL); 361} 362 363static 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 375static 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,bcm5365" }, 381 { .compatible = "brcm,bcm5389" }, 382 { .compatible = "brcm,bcm5395" }, 383 { .compatible = "brcm,bcm5397" }, 384 { .compatible = "brcm,bcm5398" }, 385 { /* sentinel */ }, 386}; 387MODULE_DEVICE_TABLE(of, b53_of_match); 388 389static struct mdio_driver b53_mdio_driver = { 390 .probe = b53_mdio_probe, 391 .remove = b53_mdio_remove, 392 .shutdown = b53_mdio_shutdown, 393 .mdiodrv.driver = { 394 .name = "bcm53xx", 395 .of_match_table = b53_of_match, 396 }, 397}; 398mdio_module_driver(b53_mdio_driver); 399 400MODULE_DESCRIPTION("B53 MDIO access driver"); 401MODULE_LICENSE("Dual BSD/GPL");