Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.0-rc3 372 lines 9.4 kB view raw
1/* 2 * Broadcom UniMAC MDIO bus controller driver 3 * 4 * Copyright (C) 2014-2017 Broadcom 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 */ 11 12#include <linux/kernel.h> 13#include <linux/phy.h> 14#include <linux/platform_device.h> 15#include <linux/sched.h> 16#include <linux/module.h> 17#include <linux/io.h> 18#include <linux/delay.h> 19#include <linux/clk.h> 20 21#include <linux/of.h> 22#include <linux/of_platform.h> 23#include <linux/of_mdio.h> 24 25#include <linux/platform_data/mdio-bcm-unimac.h> 26 27#define MDIO_CMD 0x00 28#define MDIO_START_BUSY (1 << 29) 29#define MDIO_READ_FAIL (1 << 28) 30#define MDIO_RD (2 << 26) 31#define MDIO_WR (1 << 26) 32#define MDIO_PMD_SHIFT 21 33#define MDIO_PMD_MASK 0x1F 34#define MDIO_REG_SHIFT 16 35#define MDIO_REG_MASK 0x1F 36 37#define MDIO_CFG 0x04 38#define MDIO_C22 (1 << 0) 39#define MDIO_C45 0 40#define MDIO_CLK_DIV_SHIFT 4 41#define MDIO_CLK_DIV_MASK 0x3F 42#define MDIO_SUPP_PREAMBLE (1 << 12) 43 44struct unimac_mdio_priv { 45 struct mii_bus *mii_bus; 46 void __iomem *base; 47 int (*wait_func) (void *wait_func_data); 48 void *wait_func_data; 49 struct clk *clk; 50 u32 clk_freq; 51}; 52 53static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset) 54{ 55 /* MIPS chips strapped for BE will automagically configure the 56 * peripheral registers for CPU-native byte order. 57 */ 58 if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 59 return __raw_readl(priv->base + offset); 60 else 61 return readl_relaxed(priv->base + offset); 62} 63 64static inline void unimac_mdio_writel(struct unimac_mdio_priv *priv, u32 val, 65 u32 offset) 66{ 67 if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) 68 __raw_writel(val, priv->base + offset); 69 else 70 writel_relaxed(val, priv->base + offset); 71} 72 73static inline void unimac_mdio_start(struct unimac_mdio_priv *priv) 74{ 75 u32 reg; 76 77 reg = unimac_mdio_readl(priv, MDIO_CMD); 78 reg |= MDIO_START_BUSY; 79 unimac_mdio_writel(priv, reg, MDIO_CMD); 80} 81 82static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv) 83{ 84 return unimac_mdio_readl(priv, MDIO_CMD) & MDIO_START_BUSY; 85} 86 87static int unimac_mdio_poll(void *wait_func_data) 88{ 89 struct unimac_mdio_priv *priv = wait_func_data; 90 unsigned int timeout = 1000; 91 92 do { 93 if (!unimac_mdio_busy(priv)) 94 return 0; 95 96 usleep_range(1000, 2000); 97 } while (--timeout); 98 99 if (!timeout) 100 return -ETIMEDOUT; 101 102 return 0; 103} 104 105static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) 106{ 107 struct unimac_mdio_priv *priv = bus->priv; 108 int ret; 109 u32 cmd; 110 111 /* Prepare the read operation */ 112 cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT); 113 unimac_mdio_writel(priv, cmd, MDIO_CMD); 114 115 /* Start MDIO transaction */ 116 unimac_mdio_start(priv); 117 118 ret = priv->wait_func(priv->wait_func_data); 119 if (ret) 120 return ret; 121 122 cmd = unimac_mdio_readl(priv, MDIO_CMD); 123 124 /* Some broken devices are known not to release the line during 125 * turn-around, e.g: Broadcom BCM53125 external switches, so check for 126 * that condition here and ignore the MDIO controller read failure 127 * indication. 128 */ 129 if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) 130 return -EIO; 131 132 return cmd & 0xffff; 133} 134 135static int unimac_mdio_write(struct mii_bus *bus, int phy_id, 136 int reg, u16 val) 137{ 138 struct unimac_mdio_priv *priv = bus->priv; 139 u32 cmd; 140 141 /* Prepare the write operation */ 142 cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | 143 (reg << MDIO_REG_SHIFT) | (0xffff & val); 144 unimac_mdio_writel(priv, cmd, MDIO_CMD); 145 146 unimac_mdio_start(priv); 147 148 return priv->wait_func(priv->wait_func_data); 149} 150 151/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with 152 * their internal MDIO management controller making them fail to successfully 153 * be read from or written to for the first transaction. We insert a dummy 154 * BMSR read here to make sure that phy_get_device() and get_phy_id() can 155 * correctly read the PHY MII_PHYSID1/2 registers and successfully register a 156 * PHY device for this peripheral. 157 * 158 * Once the PHY driver is registered, we can workaround subsequent reads from 159 * there (e.g: during system-wide power management). 160 * 161 * bus->reset is invoked before mdiobus_scan during mdiobus_register and is 162 * therefore the right location to stick that workaround. Since we do not want 163 * to read from non-existing PHYs, we either use bus->phy_mask or do a manual 164 * Device Tree scan to limit the search area. 165 */ 166static int unimac_mdio_reset(struct mii_bus *bus) 167{ 168 struct device_node *np = bus->dev.of_node; 169 struct device_node *child; 170 u32 read_mask = 0; 171 int addr; 172 173 if (!np) { 174 read_mask = ~bus->phy_mask; 175 } else { 176 for_each_available_child_of_node(np, child) { 177 addr = of_mdio_parse_addr(&bus->dev, child); 178 if (addr < 0) 179 continue; 180 181 read_mask |= 1 << addr; 182 } 183 } 184 185 for (addr = 0; addr < PHY_MAX_ADDR; addr++) { 186 if (read_mask & 1 << addr) { 187 dev_dbg(&bus->dev, "Workaround for PHY @ %d\n", addr); 188 mdiobus_read(bus, addr, MII_BMSR); 189 } 190 } 191 192 return 0; 193} 194 195static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv) 196{ 197 unsigned long rate; 198 u32 reg, div; 199 200 /* Keep the hardware default values */ 201 if (!priv->clk_freq) 202 return; 203 204 if (!priv->clk) 205 rate = 250000000; 206 else 207 rate = clk_get_rate(priv->clk); 208 209 div = (rate / (2 * priv->clk_freq)) - 1; 210 if (div & ~MDIO_CLK_DIV_MASK) { 211 pr_warn("Incorrect MDIO clock frequency, ignoring\n"); 212 return; 213 } 214 215 /* The MDIO clock is the reference clock (typicaly 250Mhz) divided by 216 * 2 x (MDIO_CLK_DIV + 1) 217 */ 218 reg = unimac_mdio_readl(priv, MDIO_CFG); 219 reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT); 220 reg |= div << MDIO_CLK_DIV_SHIFT; 221 unimac_mdio_writel(priv, reg, MDIO_CFG); 222} 223 224static int unimac_mdio_probe(struct platform_device *pdev) 225{ 226 struct unimac_mdio_pdata *pdata = pdev->dev.platform_data; 227 struct unimac_mdio_priv *priv; 228 struct device_node *np; 229 struct mii_bus *bus; 230 struct resource *r; 231 int ret; 232 233 np = pdev->dev.of_node; 234 235 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 236 if (!priv) 237 return -ENOMEM; 238 239 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 240 if (!r) 241 return -EINVAL; 242 243 /* Just ioremap, as this MDIO block is usually integrated into an 244 * Ethernet MAC controller register range 245 */ 246 priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 247 if (!priv->base) { 248 dev_err(&pdev->dev, "failed to remap register\n"); 249 return -ENOMEM; 250 } 251 252 priv->clk = devm_clk_get(&pdev->dev, NULL); 253 if (PTR_ERR(priv->clk) == -EPROBE_DEFER) 254 return PTR_ERR(priv->clk); 255 else 256 priv->clk = NULL; 257 258 ret = clk_prepare_enable(priv->clk); 259 if (ret) 260 return ret; 261 262 if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq)) 263 priv->clk_freq = 0; 264 265 unimac_mdio_clk_set(priv); 266 267 priv->mii_bus = mdiobus_alloc(); 268 if (!priv->mii_bus) { 269 ret = -ENOMEM; 270 goto out_clk_disable; 271 } 272 273 bus = priv->mii_bus; 274 bus->priv = priv; 275 if (pdata) { 276 bus->name = pdata->bus_name; 277 priv->wait_func = pdata->wait_func; 278 priv->wait_func_data = pdata->wait_func_data; 279 bus->phy_mask = ~pdata->phy_mask; 280 } else { 281 bus->name = "unimac MII bus"; 282 priv->wait_func_data = priv; 283 priv->wait_func = unimac_mdio_poll; 284 } 285 bus->parent = &pdev->dev; 286 bus->read = unimac_mdio_read; 287 bus->write = unimac_mdio_write; 288 bus->reset = unimac_mdio_reset; 289 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id); 290 291 ret = of_mdiobus_register(bus, np); 292 if (ret) { 293 dev_err(&pdev->dev, "MDIO bus registration failed\n"); 294 goto out_mdio_free; 295 } 296 297 platform_set_drvdata(pdev, priv); 298 299 dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus at 0x%p\n", priv->base); 300 301 return 0; 302 303out_mdio_free: 304 mdiobus_free(bus); 305out_clk_disable: 306 clk_disable_unprepare(priv->clk); 307 return ret; 308} 309 310static int unimac_mdio_remove(struct platform_device *pdev) 311{ 312 struct unimac_mdio_priv *priv = platform_get_drvdata(pdev); 313 314 mdiobus_unregister(priv->mii_bus); 315 mdiobus_free(priv->mii_bus); 316 clk_disable_unprepare(priv->clk); 317 318 return 0; 319} 320 321static int __maybe_unused unimac_mdio_suspend(struct device *d) 322{ 323 struct unimac_mdio_priv *priv = dev_get_drvdata(d); 324 325 clk_disable_unprepare(priv->clk); 326 327 return 0; 328} 329 330static int __maybe_unused unimac_mdio_resume(struct device *d) 331{ 332 struct unimac_mdio_priv *priv = dev_get_drvdata(d); 333 int ret; 334 335 ret = clk_prepare_enable(priv->clk); 336 if (ret) 337 return ret; 338 339 unimac_mdio_clk_set(priv); 340 341 return 0; 342} 343 344static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops, 345 unimac_mdio_suspend, unimac_mdio_resume); 346 347static const struct of_device_id unimac_mdio_ids[] = { 348 { .compatible = "brcm,genet-mdio-v5", }, 349 { .compatible = "brcm,genet-mdio-v4", }, 350 { .compatible = "brcm,genet-mdio-v3", }, 351 { .compatible = "brcm,genet-mdio-v2", }, 352 { .compatible = "brcm,genet-mdio-v1", }, 353 { .compatible = "brcm,unimac-mdio", }, 354 { /* sentinel */ }, 355}; 356MODULE_DEVICE_TABLE(of, unimac_mdio_ids); 357 358static struct platform_driver unimac_mdio_driver = { 359 .driver = { 360 .name = UNIMAC_MDIO_DRV_NAME, 361 .of_match_table = unimac_mdio_ids, 362 .pm = &unimac_mdio_pm_ops, 363 }, 364 .probe = unimac_mdio_probe, 365 .remove = unimac_mdio_remove, 366}; 367module_platform_driver(unimac_mdio_driver); 368 369MODULE_AUTHOR("Broadcom Corporation"); 370MODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller"); 371MODULE_LICENSE("GPL"); 372MODULE_ALIAS("platform:" UNIMAC_MDIO_DRV_NAME);