1 /* 2 * Driver for (BCM4706)? GBit MAC core on BCMA bus. 3 * 4 * Copyright (C) 2012 Rafał Miłecki <zajec5@gmail.com> 5 * 6 * Licensed under the GNU/GPL. See COPYING for details. 7 */ 8 9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 10 11 #include <linux/bcma/bcma.h> 12 #include <linux/brcmphy.h> 13 #include <linux/of_mdio.h> 14 #include "bgmac.h" 15 16 static bool bcma_mdio_wait_value(struct bcma_device *core, u16 reg, u32 mask, 17 u32 value, int timeout) 18 { 19 u32 val; 20 int i; 21 22 for (i = 0; i < timeout / 10; i++) { 23 val = bcma_read32(core, reg); 24 if ((val & mask) == value) 25 return true; 26 udelay(10); 27 } 28 dev_err(&core->dev, "Timeout waiting for reg 0x%X\n", reg); 29 return false; 30 } 31 32 /************************************************** 33 * PHY ops 34 **************************************************/ 35 36 static u16 bcma_mdio_phy_read(struct bgmac *bgmac, u8 phyaddr, u8 reg) 37 { 38 struct bcma_device *core; 39 u16 phy_access_addr; 40 u16 phy_ctl_addr; 41 u32 tmp; 42 43 BUILD_BUG_ON(BGMAC_PA_DATA_MASK != BCMA_GMAC_CMN_PA_DATA_MASK); 44 BUILD_BUG_ON(BGMAC_PA_ADDR_MASK != BCMA_GMAC_CMN_PA_ADDR_MASK); 45 BUILD_BUG_ON(BGMAC_PA_ADDR_SHIFT != BCMA_GMAC_CMN_PA_ADDR_SHIFT); 46 BUILD_BUG_ON(BGMAC_PA_REG_MASK != BCMA_GMAC_CMN_PA_REG_MASK); 47 BUILD_BUG_ON(BGMAC_PA_REG_SHIFT != BCMA_GMAC_CMN_PA_REG_SHIFT); 48 BUILD_BUG_ON(BGMAC_PA_WRITE != BCMA_GMAC_CMN_PA_WRITE); 49 BUILD_BUG_ON(BGMAC_PA_START != BCMA_GMAC_CMN_PA_START); 50 BUILD_BUG_ON(BGMAC_PC_EPA_MASK != BCMA_GMAC_CMN_PC_EPA_MASK); 51 BUILD_BUG_ON(BGMAC_PC_MCT_MASK != BCMA_GMAC_CMN_PC_MCT_MASK); 52 BUILD_BUG_ON(BGMAC_PC_MCT_SHIFT != BCMA_GMAC_CMN_PC_MCT_SHIFT); 53 BUILD_BUG_ON(BGMAC_PC_MTE != BCMA_GMAC_CMN_PC_MTE); 54 55 if (bgmac->bcma.core->id.id == BCMA_CORE_4706_MAC_GBIT) { 56 core = bgmac->bcma.core->bus->drv_gmac_cmn.core; 57 phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS; 58 phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL; 59 } else { 60 core = bgmac->bcma.core; 61 phy_access_addr = BGMAC_PHY_ACCESS; 62 phy_ctl_addr = BGMAC_PHY_CNTL; 63 } 64 65 tmp = bcma_read32(core, phy_ctl_addr); 66 tmp &= ~BGMAC_PC_EPA_MASK; 67 tmp |= phyaddr; 68 bcma_write32(core, phy_ctl_addr, tmp); 69 70 tmp = BGMAC_PA_START; 71 tmp |= phyaddr << BGMAC_PA_ADDR_SHIFT; 72 tmp |= reg << BGMAC_PA_REG_SHIFT; 73 bcma_write32(core, phy_access_addr, tmp); 74 75 if (!bcma_mdio_wait_value(core, phy_access_addr, BGMAC_PA_START, 0, 76 1000)) { 77 dev_err(&core->dev, "Reading PHY %d register 0x%X failed\n", 78 phyaddr, reg); 79 return 0xffff; 80 } 81 82 return bcma_read32(core, phy_access_addr) & BGMAC_PA_DATA_MASK; 83 } 84 85 /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphywr */ 86 static int bcma_mdio_phy_write(struct bgmac *bgmac, u8 phyaddr, u8 reg, 87 u16 value) 88 { 89 struct bcma_device *core; 90 u16 phy_access_addr; 91 u16 phy_ctl_addr; 92 u32 tmp; 93 94 if (bgmac->bcma.core->id.id == BCMA_CORE_4706_MAC_GBIT) { 95 core = bgmac->bcma.core->bus->drv_gmac_cmn.core; 96 phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS; 97 phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL; 98 } else { 99 core = bgmac->bcma.core; 100 phy_access_addr = BGMAC_PHY_ACCESS; 101 phy_ctl_addr = BGMAC_PHY_CNTL; 102 } 103 104 tmp = bcma_read32(core, phy_ctl_addr); 105 tmp &= ~BGMAC_PC_EPA_MASK; 106 tmp |= phyaddr; 107 bcma_write32(core, phy_ctl_addr, tmp); 108 109 bcma_write32(bgmac->bcma.core, BGMAC_INT_STATUS, BGMAC_IS_MDIO); 110 if (bcma_read32(bgmac->bcma.core, BGMAC_INT_STATUS) & BGMAC_IS_MDIO) 111 dev_warn(&core->dev, "Error setting MDIO int\n"); 112 113 tmp = BGMAC_PA_START; 114 tmp |= BGMAC_PA_WRITE; 115 tmp |= phyaddr << BGMAC_PA_ADDR_SHIFT; 116 tmp |= reg << BGMAC_PA_REG_SHIFT; 117 tmp |= value; 118 bcma_write32(core, phy_access_addr, tmp); 119 120 if (!bcma_mdio_wait_value(core, phy_access_addr, BGMAC_PA_START, 0, 121 1000)) { 122 dev_err(&core->dev, "Writing to PHY %d register 0x%X failed\n", 123 phyaddr, reg); 124 return -ETIMEDOUT; 125 } 126 127 return 0; 128 } 129 130 /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyinit */ 131 static void bcma_mdio_phy_init(struct bgmac *bgmac) 132 { 133 struct bcma_chipinfo *ci = &bgmac->bcma.core->bus->chipinfo; 134 u8 i; 135 136 /* For some legacy hardware we do chipset-based PHY initialization here 137 * without even detecting PHY ID. It's hacky and should be cleaned as 138 * soon as someone can test it. 139 */ 140 if (ci->id == BCMA_CHIP_ID_BCM5356) { 141 for (i = 0; i < 5; i++) { 142 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x008b); 143 bcma_mdio_phy_write(bgmac, i, 0x15, 0x0100); 144 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000f); 145 bcma_mdio_phy_write(bgmac, i, 0x12, 0x2aaa); 146 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b); 147 } 148 return; 149 } 150 if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg != 10) || 151 (ci->id == BCMA_CHIP_ID_BCM4749 && ci->pkg != 10) || 152 (ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg != 9)) { 153 struct bcma_drv_cc *cc = &bgmac->bcma.core->bus->drv_cc; 154 155 bcma_chipco_chipctl_maskset(cc, 2, ~0xc0000000, 0); 156 bcma_chipco_chipctl_maskset(cc, 4, ~0x80000000, 0); 157 for (i = 0; i < 5; i++) { 158 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000f); 159 bcma_mdio_phy_write(bgmac, i, 0x16, 0x5284); 160 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b); 161 bcma_mdio_phy_write(bgmac, i, 0x17, 0x0010); 162 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000f); 163 bcma_mdio_phy_write(bgmac, i, 0x16, 0x5296); 164 bcma_mdio_phy_write(bgmac, i, 0x17, 0x1073); 165 bcma_mdio_phy_write(bgmac, i, 0x17, 0x9073); 166 bcma_mdio_phy_write(bgmac, i, 0x16, 0x52b6); 167 bcma_mdio_phy_write(bgmac, i, 0x17, 0x9273); 168 bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b); 169 } 170 return; 171 } 172 173 /* For all other hw do initialization using PHY subsystem. */ 174 if (bgmac->net_dev && bgmac->net_dev->phydev) 175 phy_init_hw(bgmac->net_dev->phydev); 176 } 177 178 /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyreset */ 179 static int bcma_mdio_phy_reset(struct mii_bus *bus) 180 { 181 struct bgmac *bgmac = bus->priv; 182 u8 phyaddr = bgmac->phyaddr; 183 184 if (phyaddr == BGMAC_PHY_NOREGS) 185 return 0; 186 187 bcma_mdio_phy_write(bgmac, phyaddr, MII_BMCR, BMCR_RESET); 188 udelay(100); 189 if (bcma_mdio_phy_read(bgmac, phyaddr, MII_BMCR) & BMCR_RESET) 190 dev_err(bgmac->dev, "PHY reset failed\n"); 191 bcma_mdio_phy_init(bgmac); 192 193 return 0; 194 } 195 196 /************************************************** 197 * MII 198 **************************************************/ 199 200 static int bcma_mdio_mii_read(struct mii_bus *bus, int mii_id, int regnum) 201 { 202 return bcma_mdio_phy_read(bus->priv, mii_id, regnum); 203 } 204 205 static int bcma_mdio_mii_write(struct mii_bus *bus, int mii_id, int regnum, 206 u16 value) 207 { 208 return bcma_mdio_phy_write(bus->priv, mii_id, regnum, value); 209 } 210 211 struct mii_bus *bcma_mdio_mii_register(struct bgmac *bgmac) 212 { 213 struct bcma_device *core = bgmac->bcma.core; 214 struct mii_bus *mii_bus; 215 struct device_node *np; 216 int err; 217 218 mii_bus = mdiobus_alloc(); 219 if (!mii_bus) { 220 err = -ENOMEM; 221 goto err; 222 } 223 224 mii_bus->name = "bcma_mdio mii bus"; 225 sprintf(mii_bus->id, "%s-%d-%d", "bcma_mdio", core->bus->num, 226 core->core_unit); 227 mii_bus->priv = bgmac; 228 mii_bus->read = bcma_mdio_mii_read; 229 mii_bus->write = bcma_mdio_mii_write; 230 mii_bus->reset = bcma_mdio_phy_reset; 231 mii_bus->parent = &core->dev; 232 mii_bus->phy_mask = ~(1 << bgmac->phyaddr); 233 234 np = of_get_child_by_name(core->dev.of_node, "mdio"); 235 236 err = of_mdiobus_register(mii_bus, np); 237 of_node_put(np); 238 if (err) { 239 dev_err(&core->dev, "Registration of mii bus failed\n"); 240 goto err_free_bus; 241 } 242 243 return mii_bus; 244 245 err_free_bus: 246 mdiobus_free(mii_bus); 247 err: 248 return ERR_PTR(err); 249 } 250 EXPORT_SYMBOL_GPL(bcma_mdio_mii_register); 251 252 void bcma_mdio_mii_unregister(struct mii_bus *mii_bus) 253 { 254 if (!mii_bus) 255 return; 256 257 mdiobus_unregister(mii_bus); 258 mdiobus_free(mii_bus); 259 } 260 EXPORT_SYMBOL_GPL(bcma_mdio_mii_unregister); 261 262 MODULE_AUTHOR("Rafał Miłecki"); 263 MODULE_DESCRIPTION("Broadcom iProc GBit BCMA MDIO helpers"); 264 MODULE_LICENSE("GPL"); 265