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