mvmdio.c (11815B)
1/* 2 * Driver for the MDIO interface of Marvell network interfaces. 3 * 4 * Since the MDIO interface of Marvell network interfaces is shared 5 * between all network interfaces, having a single driver allows to 6 * handle concurrent accesses properly (you may have four Ethernet 7 * ports, but they in fact share the same SMI interface to access 8 * the MDIO bus). This driver is currently used by the mvneta and 9 * mv643xx_eth drivers. 10 * 11 * Copyright (C) 2012 Marvell 12 * 13 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 14 * 15 * This file is licensed under the terms of the GNU General Public 16 * License version 2. This program is licensed "as is" without any 17 * warranty of any kind, whether express or implied. 18 */ 19 20#include <linux/acpi.h> 21#include <linux/acpi_mdio.h> 22#include <linux/clk.h> 23#include <linux/delay.h> 24#include <linux/interrupt.h> 25#include <linux/io.h> 26#include <linux/kernel.h> 27#include <linux/module.h> 28#include <linux/of_device.h> 29#include <linux/of_mdio.h> 30#include <linux/phy.h> 31#include <linux/platform_device.h> 32#include <linux/sched.h> 33#include <linux/wait.h> 34 35#define MVMDIO_SMI_DATA_SHIFT 0 36#define MVMDIO_SMI_PHY_ADDR_SHIFT 16 37#define MVMDIO_SMI_PHY_REG_SHIFT 21 38#define MVMDIO_SMI_READ_OPERATION BIT(26) 39#define MVMDIO_SMI_WRITE_OPERATION 0 40#define MVMDIO_SMI_READ_VALID BIT(27) 41#define MVMDIO_SMI_BUSY BIT(28) 42#define MVMDIO_ERR_INT_CAUSE 0x007C 43#define MVMDIO_ERR_INT_SMI_DONE 0x00000010 44#define MVMDIO_ERR_INT_MASK 0x0080 45 46#define MVMDIO_XSMI_MGNT_REG 0x0 47#define MVMDIO_XSMI_PHYADDR_SHIFT 16 48#define MVMDIO_XSMI_DEVADDR_SHIFT 21 49#define MVMDIO_XSMI_WRITE_OPERATION (0x5 << 26) 50#define MVMDIO_XSMI_READ_OPERATION (0x7 << 26) 51#define MVMDIO_XSMI_READ_VALID BIT(29) 52#define MVMDIO_XSMI_BUSY BIT(30) 53#define MVMDIO_XSMI_ADDR_REG 0x8 54 55/* 56 * SMI Timeout measurements: 57 * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt) 58 * - Armada 370 (Globalscale Mirabox): 41us to 43us (Polled) 59 */ 60#define MVMDIO_SMI_TIMEOUT 1000 /* 1000us = 1ms */ 61#define MVMDIO_SMI_POLL_INTERVAL_MIN 45 62#define MVMDIO_SMI_POLL_INTERVAL_MAX 55 63 64#define MVMDIO_XSMI_POLL_INTERVAL_MIN 150 65#define MVMDIO_XSMI_POLL_INTERVAL_MAX 160 66 67struct orion_mdio_dev { 68 void __iomem *regs; 69 struct clk *clk[4]; 70 /* 71 * If we have access to the error interrupt pin (which is 72 * somewhat misnamed as it not only reflects internal errors 73 * but also reflects SMI completion), use that to wait for 74 * SMI access completion instead of polling the SMI busy bit. 75 */ 76 int err_interrupt; 77 wait_queue_head_t smi_busy_wait; 78}; 79 80enum orion_mdio_bus_type { 81 BUS_TYPE_SMI, 82 BUS_TYPE_XSMI 83}; 84 85struct orion_mdio_ops { 86 int (*is_done)(struct orion_mdio_dev *); 87 unsigned int poll_interval_min; 88 unsigned int poll_interval_max; 89}; 90 91/* Wait for the SMI unit to be ready for another operation 92 */ 93static int orion_mdio_wait_ready(const struct orion_mdio_ops *ops, 94 struct mii_bus *bus) 95{ 96 struct orion_mdio_dev *dev = bus->priv; 97 unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT); 98 unsigned long end = jiffies + timeout; 99 int timedout = 0; 100 101 while (1) { 102 if (ops->is_done(dev)) 103 return 0; 104 else if (timedout) 105 break; 106 107 if (dev->err_interrupt <= 0) { 108 usleep_range(ops->poll_interval_min, 109 ops->poll_interval_max); 110 111 if (time_is_before_jiffies(end)) 112 ++timedout; 113 } else { 114 /* wait_event_timeout does not guarantee a delay of at 115 * least one whole jiffie, so timeout must be no less 116 * than two. 117 */ 118 if (timeout < 2) 119 timeout = 2; 120 wait_event_timeout(dev->smi_busy_wait, 121 ops->is_done(dev), timeout); 122 123 ++timedout; 124 } 125 } 126 127 dev_err(bus->parent, "Timeout: SMI busy for too long\n"); 128 return -ETIMEDOUT; 129} 130 131static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) 132{ 133 return !(readl(dev->regs) & MVMDIO_SMI_BUSY); 134} 135 136static const struct orion_mdio_ops orion_mdio_smi_ops = { 137 .is_done = orion_mdio_smi_is_done, 138 .poll_interval_min = MVMDIO_SMI_POLL_INTERVAL_MIN, 139 .poll_interval_max = MVMDIO_SMI_POLL_INTERVAL_MAX, 140}; 141 142static int orion_mdio_smi_read(struct mii_bus *bus, int mii_id, 143 int regnum) 144{ 145 struct orion_mdio_dev *dev = bus->priv; 146 u32 val; 147 int ret; 148 149 if (regnum & MII_ADDR_C45) 150 return -EOPNOTSUPP; 151 152 ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); 153 if (ret < 0) 154 return ret; 155 156 writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | 157 (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | 158 MVMDIO_SMI_READ_OPERATION), 159 dev->regs); 160 161 ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); 162 if (ret < 0) 163 return ret; 164 165 val = readl(dev->regs); 166 if (!(val & MVMDIO_SMI_READ_VALID)) { 167 dev_err(bus->parent, "SMI bus read not valid\n"); 168 return -ENODEV; 169 } 170 171 return val & GENMASK(15, 0); 172} 173 174static int orion_mdio_smi_write(struct mii_bus *bus, int mii_id, 175 int regnum, u16 value) 176{ 177 struct orion_mdio_dev *dev = bus->priv; 178 int ret; 179 180 if (regnum & MII_ADDR_C45) 181 return -EOPNOTSUPP; 182 183 ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); 184 if (ret < 0) 185 return ret; 186 187 writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | 188 (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | 189 MVMDIO_SMI_WRITE_OPERATION | 190 (value << MVMDIO_SMI_DATA_SHIFT)), 191 dev->regs); 192 193 return 0; 194} 195 196static int orion_mdio_xsmi_is_done(struct orion_mdio_dev *dev) 197{ 198 return !(readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & MVMDIO_XSMI_BUSY); 199} 200 201static const struct orion_mdio_ops orion_mdio_xsmi_ops = { 202 .is_done = orion_mdio_xsmi_is_done, 203 .poll_interval_min = MVMDIO_XSMI_POLL_INTERVAL_MIN, 204 .poll_interval_max = MVMDIO_XSMI_POLL_INTERVAL_MAX, 205}; 206 207static int orion_mdio_xsmi_read(struct mii_bus *bus, int mii_id, 208 int regnum) 209{ 210 struct orion_mdio_dev *dev = bus->priv; 211 u16 dev_addr = (regnum >> 16) & GENMASK(4, 0); 212 int ret; 213 214 if (!(regnum & MII_ADDR_C45)) 215 return -EOPNOTSUPP; 216 217 ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); 218 if (ret < 0) 219 return ret; 220 221 writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); 222 writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | 223 (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | 224 MVMDIO_XSMI_READ_OPERATION, 225 dev->regs + MVMDIO_XSMI_MGNT_REG); 226 227 ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); 228 if (ret < 0) 229 return ret; 230 231 if (!(readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & 232 MVMDIO_XSMI_READ_VALID)) { 233 dev_err(bus->parent, "XSMI bus read not valid\n"); 234 return -ENODEV; 235 } 236 237 return readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0); 238} 239 240static int orion_mdio_xsmi_write(struct mii_bus *bus, int mii_id, 241 int regnum, u16 value) 242{ 243 struct orion_mdio_dev *dev = bus->priv; 244 u16 dev_addr = (regnum >> 16) & GENMASK(4, 0); 245 int ret; 246 247 if (!(regnum & MII_ADDR_C45)) 248 return -EOPNOTSUPP; 249 250 ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); 251 if (ret < 0) 252 return ret; 253 254 writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); 255 writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | 256 (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | 257 MVMDIO_XSMI_WRITE_OPERATION | value, 258 dev->regs + MVMDIO_XSMI_MGNT_REG); 259 260 return 0; 261} 262 263static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) 264{ 265 struct orion_mdio_dev *dev = dev_id; 266 267 if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) & 268 MVMDIO_ERR_INT_SMI_DONE) { 269 writel(~MVMDIO_ERR_INT_SMI_DONE, 270 dev->regs + MVMDIO_ERR_INT_CAUSE); 271 wake_up(&dev->smi_busy_wait); 272 return IRQ_HANDLED; 273 } 274 275 return IRQ_NONE; 276} 277 278static int orion_mdio_probe(struct platform_device *pdev) 279{ 280 enum orion_mdio_bus_type type; 281 struct resource *r; 282 struct mii_bus *bus; 283 struct orion_mdio_dev *dev; 284 int i, ret; 285 286 type = (enum orion_mdio_bus_type)device_get_match_data(&pdev->dev); 287 288 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 289 if (!r) { 290 dev_err(&pdev->dev, "No SMI register address given\n"); 291 return -ENODEV; 292 } 293 294 bus = devm_mdiobus_alloc_size(&pdev->dev, 295 sizeof(struct orion_mdio_dev)); 296 if (!bus) 297 return -ENOMEM; 298 299 switch (type) { 300 case BUS_TYPE_SMI: 301 bus->read = orion_mdio_smi_read; 302 bus->write = orion_mdio_smi_write; 303 break; 304 case BUS_TYPE_XSMI: 305 bus->read = orion_mdio_xsmi_read; 306 bus->write = orion_mdio_xsmi_write; 307 break; 308 } 309 310 bus->name = "orion_mdio_bus"; 311 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", 312 dev_name(&pdev->dev)); 313 bus->parent = &pdev->dev; 314 315 dev = bus->priv; 316 dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 317 if (!dev->regs) { 318 dev_err(&pdev->dev, "Unable to remap SMI register\n"); 319 return -ENODEV; 320 } 321 322 init_waitqueue_head(&dev->smi_busy_wait); 323 324 if (pdev->dev.of_node) { 325 for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { 326 dev->clk[i] = of_clk_get(pdev->dev.of_node, i); 327 if (PTR_ERR(dev->clk[i]) == -EPROBE_DEFER) { 328 ret = -EPROBE_DEFER; 329 goto out_clk; 330 } 331 if (IS_ERR(dev->clk[i])) 332 break; 333 clk_prepare_enable(dev->clk[i]); 334 } 335 336 if (!IS_ERR(of_clk_get(pdev->dev.of_node, 337 ARRAY_SIZE(dev->clk)))) 338 dev_warn(&pdev->dev, 339 "unsupported number of clocks, limiting to the first " 340 __stringify(ARRAY_SIZE(dev->clk)) "\n"); 341 } else { 342 dev->clk[0] = clk_get(&pdev->dev, NULL); 343 if (PTR_ERR(dev->clk[0]) == -EPROBE_DEFER) { 344 ret = -EPROBE_DEFER; 345 goto out_clk; 346 } 347 if (!IS_ERR(dev->clk[0])) 348 clk_prepare_enable(dev->clk[0]); 349 } 350 351 352 dev->err_interrupt = platform_get_irq_optional(pdev, 0); 353 if (dev->err_interrupt > 0 && 354 resource_size(r) < MVMDIO_ERR_INT_MASK + 4) { 355 dev_err(&pdev->dev, 356 "disabling interrupt, resource size is too small\n"); 357 dev->err_interrupt = 0; 358 } 359 if (dev->err_interrupt > 0) { 360 ret = devm_request_irq(&pdev->dev, dev->err_interrupt, 361 orion_mdio_err_irq, 362 IRQF_SHARED, pdev->name, dev); 363 if (ret) 364 goto out_mdio; 365 366 writel(MVMDIO_ERR_INT_SMI_DONE, 367 dev->regs + MVMDIO_ERR_INT_MASK); 368 369 } else if (dev->err_interrupt == -EPROBE_DEFER) { 370 ret = -EPROBE_DEFER; 371 goto out_mdio; 372 } 373 374 /* For the platforms not supporting DT/ACPI fall-back 375 * to mdiobus_register via of_mdiobus_register. 376 */ 377 if (is_acpi_node(pdev->dev.fwnode)) 378 ret = acpi_mdiobus_register(bus, pdev->dev.fwnode); 379 else 380 ret = of_mdiobus_register(bus, pdev->dev.of_node); 381 if (ret < 0) { 382 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); 383 goto out_mdio; 384 } 385 386 platform_set_drvdata(pdev, bus); 387 388 return 0; 389 390out_mdio: 391 if (dev->err_interrupt > 0) 392 writel(0, dev->regs + MVMDIO_ERR_INT_MASK); 393 394out_clk: 395 for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { 396 if (IS_ERR(dev->clk[i])) 397 break; 398 clk_disable_unprepare(dev->clk[i]); 399 clk_put(dev->clk[i]); 400 } 401 402 return ret; 403} 404 405static int orion_mdio_remove(struct platform_device *pdev) 406{ 407 struct mii_bus *bus = platform_get_drvdata(pdev); 408 struct orion_mdio_dev *dev = bus->priv; 409 int i; 410 411 if (dev->err_interrupt > 0) 412 writel(0, dev->regs + MVMDIO_ERR_INT_MASK); 413 mdiobus_unregister(bus); 414 415 for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { 416 if (IS_ERR(dev->clk[i])) 417 break; 418 clk_disable_unprepare(dev->clk[i]); 419 clk_put(dev->clk[i]); 420 } 421 422 return 0; 423} 424 425static const struct of_device_id orion_mdio_match[] = { 426 { .compatible = "marvell,orion-mdio", .data = (void *)BUS_TYPE_SMI }, 427 { .compatible = "marvell,xmdio", .data = (void *)BUS_TYPE_XSMI }, 428 { } 429}; 430MODULE_DEVICE_TABLE(of, orion_mdio_match); 431 432#ifdef CONFIG_ACPI 433static const struct acpi_device_id orion_mdio_acpi_match[] = { 434 { "MRVL0100", BUS_TYPE_SMI }, 435 { "MRVL0101", BUS_TYPE_XSMI }, 436 { }, 437}; 438MODULE_DEVICE_TABLE(acpi, orion_mdio_acpi_match); 439#endif 440 441static struct platform_driver orion_mdio_driver = { 442 .probe = orion_mdio_probe, 443 .remove = orion_mdio_remove, 444 .driver = { 445 .name = "orion-mdio", 446 .of_match_table = orion_mdio_match, 447 .acpi_match_table = ACPI_PTR(orion_mdio_acpi_match), 448 }, 449}; 450 451module_platform_driver(orion_mdio_driver); 452 453MODULE_DESCRIPTION("Marvell MDIO interface driver"); 454MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); 455MODULE_LICENSE("GPL"); 456MODULE_ALIAS("platform:orion-mdio");