Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * B53 register access through MII registers
0003  *
0004  * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
0005  *
0006  * Permission to use, copy, modify, and/or distribute this software for any
0007  * purpose with or without fee is hereby granted, provided that the above
0008  * copyright notice and this permission notice appear in all copies.
0009  *
0010  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0011  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0012  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0013  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0014  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0015  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0016  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0017  */
0018 
0019 #include <linux/kernel.h>
0020 #include <linux/phy.h>
0021 #include <linux/module.h>
0022 #include <linux/delay.h>
0023 #include <linux/brcmphy.h>
0024 #include <linux/rtnetlink.h>
0025 #include <net/dsa.h>
0026 
0027 #include "b53_priv.h"
0028 
0029 /* MII registers */
0030 #define REG_MII_PAGE    0x10    /* MII Page register */
0031 #define REG_MII_ADDR    0x11    /* MII Address register */
0032 #define REG_MII_DATA0   0x18    /* MII Data register 0 */
0033 #define REG_MII_DATA1   0x19    /* MII Data register 1 */
0034 #define REG_MII_DATA2   0x1a    /* MII Data register 2 */
0035 #define REG_MII_DATA3   0x1b    /* MII Data register 3 */
0036 
0037 #define REG_MII_PAGE_ENABLE     BIT(0)
0038 #define REG_MII_ADDR_WRITE      BIT(0)
0039 #define REG_MII_ADDR_READ       BIT(1)
0040 
0041 static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
0042 {
0043     int i;
0044     u16 v;
0045     int ret;
0046     struct mii_bus *bus = dev->priv;
0047 
0048     if (dev->current_page != page) {
0049         /* set page number */
0050         v = (page << 8) | REG_MII_PAGE_ENABLE;
0051         ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0052                        REG_MII_PAGE, v);
0053         if (ret)
0054             return ret;
0055         dev->current_page = page;
0056     }
0057 
0058     /* set register address */
0059     v = (reg << 8) | op;
0060     ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_ADDR, v);
0061     if (ret)
0062         return ret;
0063 
0064     /* check if operation completed */
0065     for (i = 0; i < 5; ++i) {
0066         v = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0067                     REG_MII_ADDR);
0068         if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
0069             break;
0070         usleep_range(10, 100);
0071     }
0072 
0073     if (WARN_ON(i == 5))
0074         return -EIO;
0075 
0076     return 0;
0077 }
0078 
0079 static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
0080 {
0081     struct mii_bus *bus = dev->priv;
0082     int ret;
0083 
0084     ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
0085     if (ret)
0086         return ret;
0087 
0088     *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0089                    REG_MII_DATA0) & 0xff;
0090 
0091     return 0;
0092 }
0093 
0094 static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
0095 {
0096     struct mii_bus *bus = dev->priv;
0097     int ret;
0098 
0099     ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
0100     if (ret)
0101         return ret;
0102 
0103     *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0);
0104 
0105     return 0;
0106 }
0107 
0108 static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
0109 {
0110     struct mii_bus *bus = dev->priv;
0111     int ret;
0112 
0113     ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
0114     if (ret)
0115         return ret;
0116 
0117     *val = mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR, REG_MII_DATA0);
0118     *val |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0119                     REG_MII_DATA1) << 16;
0120 
0121     return 0;
0122 }
0123 
0124 static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
0125 {
0126     struct mii_bus *bus = dev->priv;
0127     u64 temp = 0;
0128     int i;
0129     int ret;
0130 
0131     ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
0132     if (ret)
0133         return ret;
0134 
0135     for (i = 2; i >= 0; i--) {
0136         temp <<= 16;
0137         temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0138                      REG_MII_DATA0 + i);
0139     }
0140 
0141     *val = temp;
0142 
0143     return 0;
0144 }
0145 
0146 static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
0147 {
0148     struct mii_bus *bus = dev->priv;
0149     u64 temp = 0;
0150     int i;
0151     int ret;
0152 
0153     ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
0154     if (ret)
0155         return ret;
0156 
0157     for (i = 3; i >= 0; i--) {
0158         temp <<= 16;
0159         temp |= mdiobus_read_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0160                         REG_MII_DATA0 + i);
0161     }
0162 
0163     *val = temp;
0164 
0165     return 0;
0166 }
0167 
0168 static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
0169 {
0170     struct mii_bus *bus = dev->priv;
0171     int ret;
0172 
0173     ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0174                    REG_MII_DATA0, value);
0175     if (ret)
0176         return ret;
0177 
0178     return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
0179 }
0180 
0181 static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
0182                 u16 value)
0183 {
0184     struct mii_bus *bus = dev->priv;
0185     int ret;
0186 
0187     ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0188                    REG_MII_DATA0, value);
0189     if (ret)
0190         return ret;
0191 
0192     return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
0193 }
0194 
0195 static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
0196                 u32 value)
0197 {
0198     struct mii_bus *bus = dev->priv;
0199     unsigned int i;
0200     u32 temp = value;
0201 
0202     for (i = 0; i < 2; i++) {
0203         int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0204                            REG_MII_DATA0 + i,
0205                            temp & 0xffff);
0206         if (ret)
0207             return ret;
0208         temp >>= 16;
0209     }
0210 
0211     return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
0212 }
0213 
0214 static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
0215                 u64 value)
0216 {
0217     struct mii_bus *bus = dev->priv;
0218     unsigned int i;
0219     u64 temp = value;
0220 
0221     for (i = 0; i < 3; i++) {
0222         int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0223                            REG_MII_DATA0 + i,
0224                            temp & 0xffff);
0225         if (ret)
0226             return ret;
0227         temp >>= 16;
0228     }
0229 
0230     return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
0231 }
0232 
0233 static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
0234                 u64 value)
0235 {
0236     struct mii_bus *bus = dev->priv;
0237     unsigned int i;
0238     u64 temp = value;
0239 
0240     for (i = 0; i < 4; i++) {
0241         int ret = mdiobus_write_nested(bus, BRCM_PSEUDO_PHY_ADDR,
0242                            REG_MII_DATA0 + i,
0243                            temp & 0xffff);
0244         if (ret)
0245             return ret;
0246         temp >>= 16;
0247     }
0248 
0249     return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
0250 }
0251 
0252 static int b53_mdio_phy_read16(struct b53_device *dev, int addr, int reg,
0253                    u16 *value)
0254 {
0255     struct mii_bus *bus = dev->priv;
0256 
0257     *value = mdiobus_read_nested(bus, addr, reg);
0258 
0259     return 0;
0260 }
0261 
0262 static int b53_mdio_phy_write16(struct b53_device *dev, int addr, int reg,
0263                 u16 value)
0264 {
0265     struct mii_bus *bus = dev->bus;
0266 
0267     return mdiobus_write_nested(bus, addr, reg, value);
0268 }
0269 
0270 static const struct b53_io_ops b53_mdio_ops = {
0271     .read8 = b53_mdio_read8,
0272     .read16 = b53_mdio_read16,
0273     .read32 = b53_mdio_read32,
0274     .read48 = b53_mdio_read48,
0275     .read64 = b53_mdio_read64,
0276     .write8 = b53_mdio_write8,
0277     .write16 = b53_mdio_write16,
0278     .write32 = b53_mdio_write32,
0279     .write48 = b53_mdio_write48,
0280     .write64 = b53_mdio_write64,
0281     .phy_read16 = b53_mdio_phy_read16,
0282     .phy_write16 = b53_mdio_phy_write16,
0283 };
0284 
0285 #define B53_BRCM_OUI_1  0x0143bc00
0286 #define B53_BRCM_OUI_2  0x03625c00
0287 #define B53_BRCM_OUI_3  0x00406000
0288 #define B53_BRCM_OUI_4  0x01410c00
0289 
0290 static int b53_mdio_probe(struct mdio_device *mdiodev)
0291 {
0292     struct b53_device *dev;
0293     u32 phy_id;
0294     int ret;
0295 
0296     /* allow the generic PHY driver to take over the non-management MDIO
0297      * addresses
0298      */
0299     if (mdiodev->addr != BRCM_PSEUDO_PHY_ADDR && mdiodev->addr != 0) {
0300         dev_err(&mdiodev->dev, "leaving address %d to PHY\n",
0301             mdiodev->addr);
0302         return -ENODEV;
0303     }
0304 
0305     /* read the first port's id */
0306     phy_id = mdiobus_read(mdiodev->bus, 0, 2) << 16;
0307     phy_id |= mdiobus_read(mdiodev->bus, 0, 3);
0308 
0309     /* BCM5325, BCM539x (OUI_1)
0310      * BCM53125, BCM53128 (OUI_2)
0311      * BCM5365 (OUI_3)
0312      */
0313     if ((phy_id & 0xfffffc00) != B53_BRCM_OUI_1 &&
0314         (phy_id & 0xfffffc00) != B53_BRCM_OUI_2 &&
0315         (phy_id & 0xfffffc00) != B53_BRCM_OUI_3 &&
0316         (phy_id & 0xfffffc00) != B53_BRCM_OUI_4) {
0317         dev_err(&mdiodev->dev, "Unsupported device: 0x%08x\n", phy_id);
0318         return -ENODEV;
0319     }
0320 
0321     /* First probe will come from SWITCH_MDIO controller on the 7445D0
0322      * switch, which will conflict with the 7445 integrated switch
0323      * pseudo-phy (we end-up programming both). In that case, we return
0324      * -EPROBE_DEFER for the first time we get here, and wait until we come
0325      * back with the slave MDIO bus which has the correct indirection
0326      * layer setup
0327      */
0328     if (of_machine_is_compatible("brcm,bcm7445d0") &&
0329         strcmp(mdiodev->bus->name, "sf2 slave mii"))
0330         return -EPROBE_DEFER;
0331 
0332     dev = b53_switch_alloc(&mdiodev->dev, &b53_mdio_ops, mdiodev->bus);
0333     if (!dev)
0334         return -ENOMEM;
0335 
0336     /* we don't use page 0xff, so force a page set */
0337     dev->current_page = 0xff;
0338     dev->bus = mdiodev->bus;
0339 
0340     dev_set_drvdata(&mdiodev->dev, dev);
0341 
0342     ret = b53_switch_register(dev);
0343     if (ret) {
0344         dev_err(&mdiodev->dev, "failed to register switch: %i\n", ret);
0345         return ret;
0346     }
0347 
0348     return ret;
0349 }
0350 
0351 static void b53_mdio_remove(struct mdio_device *mdiodev)
0352 {
0353     struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
0354 
0355     if (!dev)
0356         return;
0357 
0358     b53_switch_remove(dev);
0359 
0360     dev_set_drvdata(&mdiodev->dev, NULL);
0361 }
0362 
0363 static void b53_mdio_shutdown(struct mdio_device *mdiodev)
0364 {
0365     struct b53_device *dev = dev_get_drvdata(&mdiodev->dev);
0366 
0367     if (!dev)
0368         return;
0369 
0370     b53_switch_shutdown(dev);
0371 
0372     dev_set_drvdata(&mdiodev->dev, NULL);
0373 }
0374 
0375 static const struct of_device_id b53_of_match[] = {
0376     { .compatible = "brcm,bcm5325" },
0377     { .compatible = "brcm,bcm53115" },
0378     { .compatible = "brcm,bcm53125" },
0379     { .compatible = "brcm,bcm53128" },
0380     { .compatible = "brcm,bcm5365" },
0381     { .compatible = "brcm,bcm5389" },
0382     { .compatible = "brcm,bcm5395" },
0383     { .compatible = "brcm,bcm5397" },
0384     { .compatible = "brcm,bcm5398" },
0385     { /* sentinel */ },
0386 };
0387 MODULE_DEVICE_TABLE(of, b53_of_match);
0388 
0389 static struct mdio_driver b53_mdio_driver = {
0390     .probe  = b53_mdio_probe,
0391     .remove = b53_mdio_remove,
0392     .shutdown = b53_mdio_shutdown,
0393     .mdiodrv.driver = {
0394         .name = "bcm53xx",
0395         .of_match_table = b53_of_match,
0396     },
0397 };
0398 mdio_module_driver(b53_mdio_driver);
0399 
0400 MODULE_DESCRIPTION("B53 MDIO access driver");
0401 MODULE_LICENSE("Dual BSD/GPL");