18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Driver for the MDIO interface of Marvell network interfaces. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Since the MDIO interface of Marvell network interfaces is shared 58c2ecf20Sopenharmony_ci * between all network interfaces, having a single driver allows to 68c2ecf20Sopenharmony_ci * handle concurrent accesses properly (you may have four Ethernet 78c2ecf20Sopenharmony_ci * ports, but they in fact share the same SMI interface to access 88c2ecf20Sopenharmony_ci * the MDIO bus). This driver is currently used by the mvneta and 98c2ecf20Sopenharmony_ci * mv643xx_eth drivers. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 168c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 178c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/clk.h> 218c2ecf20Sopenharmony_ci#include <linux/delay.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/kernel.h> 258c2ecf20Sopenharmony_ci#include <linux/module.h> 268c2ecf20Sopenharmony_ci#include <linux/of_device.h> 278c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 288c2ecf20Sopenharmony_ci#include <linux/phy.h> 298c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 308c2ecf20Sopenharmony_ci#include <linux/sched.h> 318c2ecf20Sopenharmony_ci#include <linux/wait.h> 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define MVMDIO_SMI_DATA_SHIFT 0 348c2ecf20Sopenharmony_ci#define MVMDIO_SMI_PHY_ADDR_SHIFT 16 358c2ecf20Sopenharmony_ci#define MVMDIO_SMI_PHY_REG_SHIFT 21 368c2ecf20Sopenharmony_ci#define MVMDIO_SMI_READ_OPERATION BIT(26) 378c2ecf20Sopenharmony_ci#define MVMDIO_SMI_WRITE_OPERATION 0 388c2ecf20Sopenharmony_ci#define MVMDIO_SMI_READ_VALID BIT(27) 398c2ecf20Sopenharmony_ci#define MVMDIO_SMI_BUSY BIT(28) 408c2ecf20Sopenharmony_ci#define MVMDIO_ERR_INT_CAUSE 0x007C 418c2ecf20Sopenharmony_ci#define MVMDIO_ERR_INT_SMI_DONE 0x00000010 428c2ecf20Sopenharmony_ci#define MVMDIO_ERR_INT_MASK 0x0080 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_MGNT_REG 0x0 458c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_PHYADDR_SHIFT 16 468c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_DEVADDR_SHIFT 21 478c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_WRITE_OPERATION (0x5 << 26) 488c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_READ_OPERATION (0x7 << 26) 498c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_READ_VALID BIT(29) 508c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_BUSY BIT(30) 518c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_ADDR_REG 0x8 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* 548c2ecf20Sopenharmony_ci * SMI Timeout measurements: 558c2ecf20Sopenharmony_ci * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt) 568c2ecf20Sopenharmony_ci * - Armada 370 (Globalscale Mirabox): 41us to 43us (Polled) 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define MVMDIO_SMI_TIMEOUT 1000 /* 1000us = 1ms */ 598c2ecf20Sopenharmony_ci#define MVMDIO_SMI_POLL_INTERVAL_MIN 45 608c2ecf20Sopenharmony_ci#define MVMDIO_SMI_POLL_INTERVAL_MAX 55 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_POLL_INTERVAL_MIN 150 638c2ecf20Sopenharmony_ci#define MVMDIO_XSMI_POLL_INTERVAL_MAX 160 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct orion_mdio_dev { 668c2ecf20Sopenharmony_ci void __iomem *regs; 678c2ecf20Sopenharmony_ci struct clk *clk[4]; 688c2ecf20Sopenharmony_ci /* 698c2ecf20Sopenharmony_ci * If we have access to the error interrupt pin (which is 708c2ecf20Sopenharmony_ci * somewhat misnamed as it not only reflects internal errors 718c2ecf20Sopenharmony_ci * but also reflects SMI completion), use that to wait for 728c2ecf20Sopenharmony_ci * SMI access completion instead of polling the SMI busy bit. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci int err_interrupt; 758c2ecf20Sopenharmony_ci wait_queue_head_t smi_busy_wait; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cienum orion_mdio_bus_type { 798c2ecf20Sopenharmony_ci BUS_TYPE_SMI, 808c2ecf20Sopenharmony_ci BUS_TYPE_XSMI 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct orion_mdio_ops { 848c2ecf20Sopenharmony_ci int (*is_done)(struct orion_mdio_dev *); 858c2ecf20Sopenharmony_ci unsigned int poll_interval_min; 868c2ecf20Sopenharmony_ci unsigned int poll_interval_max; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci/* Wait for the SMI unit to be ready for another operation 908c2ecf20Sopenharmony_ci */ 918c2ecf20Sopenharmony_cistatic int orion_mdio_wait_ready(const struct orion_mdio_ops *ops, 928c2ecf20Sopenharmony_ci struct mii_bus *bus) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = bus->priv; 958c2ecf20Sopenharmony_ci unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT); 968c2ecf20Sopenharmony_ci unsigned long end = jiffies + timeout; 978c2ecf20Sopenharmony_ci int timedout = 0; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci while (1) { 1008c2ecf20Sopenharmony_ci if (ops->is_done(dev)) 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci else if (timedout) 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (dev->err_interrupt <= 0) { 1068c2ecf20Sopenharmony_ci usleep_range(ops->poll_interval_min, 1078c2ecf20Sopenharmony_ci ops->poll_interval_max); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (time_is_before_jiffies(end)) 1108c2ecf20Sopenharmony_ci ++timedout; 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci /* wait_event_timeout does not guarantee a delay of at 1138c2ecf20Sopenharmony_ci * least one whole jiffie, so timeout must be no less 1148c2ecf20Sopenharmony_ci * than two. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci if (timeout < 2) 1178c2ecf20Sopenharmony_ci timeout = 2; 1188c2ecf20Sopenharmony_ci wait_event_timeout(dev->smi_busy_wait, 1198c2ecf20Sopenharmony_ci ops->is_done(dev), timeout); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci ++timedout; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci dev_err(bus->parent, "Timeout: SMI busy for too long\n"); 1268c2ecf20Sopenharmony_ci return -ETIMEDOUT; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci return !(readl(dev->regs) & MVMDIO_SMI_BUSY); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct orion_mdio_ops orion_mdio_smi_ops = { 1358c2ecf20Sopenharmony_ci .is_done = orion_mdio_smi_is_done, 1368c2ecf20Sopenharmony_ci .poll_interval_min = MVMDIO_SMI_POLL_INTERVAL_MIN, 1378c2ecf20Sopenharmony_ci .poll_interval_max = MVMDIO_SMI_POLL_INTERVAL_MAX, 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistatic int orion_mdio_smi_read(struct mii_bus *bus, int mii_id, 1418c2ecf20Sopenharmony_ci int regnum) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = bus->priv; 1448c2ecf20Sopenharmony_ci u32 val; 1458c2ecf20Sopenharmony_ci int ret; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (regnum & MII_ADDR_C45) 1488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); 1518c2ecf20Sopenharmony_ci if (ret < 0) 1528c2ecf20Sopenharmony_ci return ret; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | 1558c2ecf20Sopenharmony_ci (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | 1568c2ecf20Sopenharmony_ci MVMDIO_SMI_READ_OPERATION), 1578c2ecf20Sopenharmony_ci dev->regs); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); 1608c2ecf20Sopenharmony_ci if (ret < 0) 1618c2ecf20Sopenharmony_ci return ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci val = readl(dev->regs); 1648c2ecf20Sopenharmony_ci if (!(val & MVMDIO_SMI_READ_VALID)) { 1658c2ecf20Sopenharmony_ci dev_err(bus->parent, "SMI bus read not valid\n"); 1668c2ecf20Sopenharmony_ci return -ENODEV; 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return val & GENMASK(15, 0); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int orion_mdio_smi_write(struct mii_bus *bus, int mii_id, 1738c2ecf20Sopenharmony_ci int regnum, u16 value) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = bus->priv; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (regnum & MII_ADDR_C45) 1798c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); 1828c2ecf20Sopenharmony_ci if (ret < 0) 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | 1868c2ecf20Sopenharmony_ci (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | 1878c2ecf20Sopenharmony_ci MVMDIO_SMI_WRITE_OPERATION | 1888c2ecf20Sopenharmony_ci (value << MVMDIO_SMI_DATA_SHIFT)), 1898c2ecf20Sopenharmony_ci dev->regs); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int orion_mdio_xsmi_is_done(struct orion_mdio_dev *dev) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci return !(readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & MVMDIO_XSMI_BUSY); 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic const struct orion_mdio_ops orion_mdio_xsmi_ops = { 2008c2ecf20Sopenharmony_ci .is_done = orion_mdio_xsmi_is_done, 2018c2ecf20Sopenharmony_ci .poll_interval_min = MVMDIO_XSMI_POLL_INTERVAL_MIN, 2028c2ecf20Sopenharmony_ci .poll_interval_max = MVMDIO_XSMI_POLL_INTERVAL_MAX, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int orion_mdio_xsmi_read(struct mii_bus *bus, int mii_id, 2068c2ecf20Sopenharmony_ci int regnum) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = bus->priv; 2098c2ecf20Sopenharmony_ci u16 dev_addr = (regnum >> 16) & GENMASK(4, 0); 2108c2ecf20Sopenharmony_ci int ret; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!(regnum & MII_ADDR_C45)) 2138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); 2168c2ecf20Sopenharmony_ci if (ret < 0) 2178c2ecf20Sopenharmony_ci return ret; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); 2208c2ecf20Sopenharmony_ci writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | 2218c2ecf20Sopenharmony_ci (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | 2228c2ecf20Sopenharmony_ci MVMDIO_XSMI_READ_OPERATION, 2238c2ecf20Sopenharmony_ci dev->regs + MVMDIO_XSMI_MGNT_REG); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); 2268c2ecf20Sopenharmony_ci if (ret < 0) 2278c2ecf20Sopenharmony_ci return ret; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci if (!(readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & 2308c2ecf20Sopenharmony_ci MVMDIO_XSMI_READ_VALID)) { 2318c2ecf20Sopenharmony_ci dev_err(bus->parent, "XSMI bus read not valid\n"); 2328c2ecf20Sopenharmony_ci return -ENODEV; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic int orion_mdio_xsmi_write(struct mii_bus *bus, int mii_id, 2398c2ecf20Sopenharmony_ci int regnum, u16 value) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = bus->priv; 2428c2ecf20Sopenharmony_ci u16 dev_addr = (regnum >> 16) & GENMASK(4, 0); 2438c2ecf20Sopenharmony_ci int ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (!(regnum & MII_ADDR_C45)) 2468c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); 2498c2ecf20Sopenharmony_ci if (ret < 0) 2508c2ecf20Sopenharmony_ci return ret; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); 2538c2ecf20Sopenharmony_ci writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | 2548c2ecf20Sopenharmony_ci (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | 2558c2ecf20Sopenharmony_ci MVMDIO_XSMI_WRITE_OPERATION | value, 2568c2ecf20Sopenharmony_ci dev->regs + MVMDIO_XSMI_MGNT_REG); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci return 0; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = dev_id; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) & 2668c2ecf20Sopenharmony_ci MVMDIO_ERR_INT_SMI_DONE) { 2678c2ecf20Sopenharmony_ci writel(~MVMDIO_ERR_INT_SMI_DONE, 2688c2ecf20Sopenharmony_ci dev->regs + MVMDIO_ERR_INT_CAUSE); 2698c2ecf20Sopenharmony_ci wake_up(&dev->smi_busy_wait); 2708c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci return IRQ_NONE; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int orion_mdio_probe(struct platform_device *pdev) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci enum orion_mdio_bus_type type; 2798c2ecf20Sopenharmony_ci struct resource *r; 2808c2ecf20Sopenharmony_ci struct mii_bus *bus; 2818c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev; 2828c2ecf20Sopenharmony_ci int i, ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci type = (enum orion_mdio_bus_type)of_device_get_match_data(&pdev->dev); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 2878c2ecf20Sopenharmony_ci if (!r) { 2888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No SMI register address given\n"); 2898c2ecf20Sopenharmony_ci return -ENODEV; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci bus = devm_mdiobus_alloc_size(&pdev->dev, 2938c2ecf20Sopenharmony_ci sizeof(struct orion_mdio_dev)); 2948c2ecf20Sopenharmony_ci if (!bus) 2958c2ecf20Sopenharmony_ci return -ENOMEM; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci switch (type) { 2988c2ecf20Sopenharmony_ci case BUS_TYPE_SMI: 2998c2ecf20Sopenharmony_ci bus->read = orion_mdio_smi_read; 3008c2ecf20Sopenharmony_ci bus->write = orion_mdio_smi_write; 3018c2ecf20Sopenharmony_ci break; 3028c2ecf20Sopenharmony_ci case BUS_TYPE_XSMI: 3038c2ecf20Sopenharmony_ci bus->read = orion_mdio_xsmi_read; 3048c2ecf20Sopenharmony_ci bus->write = orion_mdio_xsmi_write; 3058c2ecf20Sopenharmony_ci break; 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci bus->name = "orion_mdio_bus"; 3098c2ecf20Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", 3108c2ecf20Sopenharmony_ci dev_name(&pdev->dev)); 3118c2ecf20Sopenharmony_ci bus->parent = &pdev->dev; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci dev = bus->priv; 3148c2ecf20Sopenharmony_ci dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 3158c2ecf20Sopenharmony_ci if (!dev->regs) { 3168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to remap SMI register\n"); 3178c2ecf20Sopenharmony_ci return -ENODEV; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci init_waitqueue_head(&dev->smi_busy_wait); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (pdev->dev.of_node) { 3238c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { 3248c2ecf20Sopenharmony_ci dev->clk[i] = of_clk_get(pdev->dev.of_node, i); 3258c2ecf20Sopenharmony_ci if (PTR_ERR(dev->clk[i]) == -EPROBE_DEFER) { 3268c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3278c2ecf20Sopenharmony_ci goto out_clk; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci if (IS_ERR(dev->clk[i])) 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci clk_prepare_enable(dev->clk[i]); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!IS_ERR(of_clk_get(pdev->dev.of_node, 3358c2ecf20Sopenharmony_ci ARRAY_SIZE(dev->clk)))) 3368c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 3378c2ecf20Sopenharmony_ci "unsupported number of clocks, limiting to the first " 3388c2ecf20Sopenharmony_ci __stringify(ARRAY_SIZE(dev->clk)) "\n"); 3398c2ecf20Sopenharmony_ci } else { 3408c2ecf20Sopenharmony_ci dev->clk[0] = clk_get(&pdev->dev, NULL); 3418c2ecf20Sopenharmony_ci if (PTR_ERR(dev->clk[0]) == -EPROBE_DEFER) { 3428c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3438c2ecf20Sopenharmony_ci goto out_clk; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci if (!IS_ERR(dev->clk[0])) 3468c2ecf20Sopenharmony_ci clk_prepare_enable(dev->clk[0]); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dev->err_interrupt = platform_get_irq_optional(pdev, 0); 3518c2ecf20Sopenharmony_ci if (dev->err_interrupt > 0 && 3528c2ecf20Sopenharmony_ci resource_size(r) < MVMDIO_ERR_INT_MASK + 4) { 3538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3548c2ecf20Sopenharmony_ci "disabling interrupt, resource size is too small\n"); 3558c2ecf20Sopenharmony_ci dev->err_interrupt = 0; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci if (dev->err_interrupt > 0) { 3588c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, dev->err_interrupt, 3598c2ecf20Sopenharmony_ci orion_mdio_err_irq, 3608c2ecf20Sopenharmony_ci IRQF_SHARED, pdev->name, dev); 3618c2ecf20Sopenharmony_ci if (ret) 3628c2ecf20Sopenharmony_ci goto out_mdio; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci writel(MVMDIO_ERR_INT_SMI_DONE, 3658c2ecf20Sopenharmony_ci dev->regs + MVMDIO_ERR_INT_MASK); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci } else if (dev->err_interrupt == -EPROBE_DEFER) { 3688c2ecf20Sopenharmony_ci ret = -EPROBE_DEFER; 3698c2ecf20Sopenharmony_ci goto out_mdio; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci ret = of_mdiobus_register(bus, pdev->dev.of_node); 3738c2ecf20Sopenharmony_ci if (ret < 0) { 3748c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); 3758c2ecf20Sopenharmony_ci goto out_mdio; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, bus); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ciout_mdio: 3838c2ecf20Sopenharmony_ci if (dev->err_interrupt > 0) 3848c2ecf20Sopenharmony_ci writel(0, dev->regs + MVMDIO_ERR_INT_MASK); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciout_clk: 3878c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { 3888c2ecf20Sopenharmony_ci if (IS_ERR(dev->clk[i])) 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci clk_disable_unprepare(dev->clk[i]); 3918c2ecf20Sopenharmony_ci clk_put(dev->clk[i]); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic int orion_mdio_remove(struct platform_device *pdev) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct mii_bus *bus = platform_get_drvdata(pdev); 4008c2ecf20Sopenharmony_ci struct orion_mdio_dev *dev = bus->priv; 4018c2ecf20Sopenharmony_ci int i; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (dev->err_interrupt > 0) 4048c2ecf20Sopenharmony_ci writel(0, dev->regs + MVMDIO_ERR_INT_MASK); 4058c2ecf20Sopenharmony_ci mdiobus_unregister(bus); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { 4088c2ecf20Sopenharmony_ci if (IS_ERR(dev->clk[i])) 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci clk_disable_unprepare(dev->clk[i]); 4118c2ecf20Sopenharmony_ci clk_put(dev->clk[i]); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic const struct of_device_id orion_mdio_match[] = { 4188c2ecf20Sopenharmony_ci { .compatible = "marvell,orion-mdio", .data = (void *)BUS_TYPE_SMI }, 4198c2ecf20Sopenharmony_ci { .compatible = "marvell,xmdio", .data = (void *)BUS_TYPE_XSMI }, 4208c2ecf20Sopenharmony_ci { } 4218c2ecf20Sopenharmony_ci}; 4228c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, orion_mdio_match); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic struct platform_driver orion_mdio_driver = { 4258c2ecf20Sopenharmony_ci .probe = orion_mdio_probe, 4268c2ecf20Sopenharmony_ci .remove = orion_mdio_remove, 4278c2ecf20Sopenharmony_ci .driver = { 4288c2ecf20Sopenharmony_ci .name = "orion-mdio", 4298c2ecf20Sopenharmony_ci .of_match_table = orion_mdio_match, 4308c2ecf20Sopenharmony_ci }, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cimodule_platform_driver(orion_mdio_driver); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell MDIO interface driver"); 4368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>"); 4378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4388c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:orion-mdio"); 439