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.3-rc6 179 lines 4.5 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Allwinner EMAC MDIO interface driver 4 * 5 * Copyright 2012-2013 Stefan Roese <sr@denx.de> 6 * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com> 7 * 8 * Based on the Linux driver provided by Allwinner: 9 * Copyright (C) 1997 Sten Wang 10 */ 11 12#include <linux/delay.h> 13#include <linux/kernel.h> 14#include <linux/module.h> 15#include <linux/mutex.h> 16#include <linux/of_address.h> 17#include <linux/of_mdio.h> 18#include <linux/phy.h> 19#include <linux/platform_device.h> 20#include <linux/regulator/consumer.h> 21 22#define EMAC_MAC_MCMD_REG (0x00) 23#define EMAC_MAC_MADR_REG (0x04) 24#define EMAC_MAC_MWTD_REG (0x08) 25#define EMAC_MAC_MRDD_REG (0x0c) 26#define EMAC_MAC_MIND_REG (0x10) 27#define EMAC_MAC_SSRR_REG (0x14) 28 29#define MDIO_TIMEOUT (msecs_to_jiffies(100)) 30 31struct sun4i_mdio_data { 32 void __iomem *membase; 33 struct regulator *regulator; 34}; 35 36static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 37{ 38 struct sun4i_mdio_data *data = bus->priv; 39 unsigned long timeout_jiffies; 40 int value; 41 42 /* issue the phy address and reg */ 43 writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG); 44 /* pull up the phy io line */ 45 writel(0x1, data->membase + EMAC_MAC_MCMD_REG); 46 47 /* Wait read complete */ 48 timeout_jiffies = jiffies + MDIO_TIMEOUT; 49 while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) { 50 if (time_is_before_jiffies(timeout_jiffies)) 51 return -ETIMEDOUT; 52 msleep(1); 53 } 54 55 /* push down the phy io line */ 56 writel(0x0, data->membase + EMAC_MAC_MCMD_REG); 57 /* and read data */ 58 value = readl(data->membase + EMAC_MAC_MRDD_REG); 59 60 return value; 61} 62 63static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum, 64 u16 value) 65{ 66 struct sun4i_mdio_data *data = bus->priv; 67 unsigned long timeout_jiffies; 68 69 /* issue the phy address and reg */ 70 writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG); 71 /* pull up the phy io line */ 72 writel(0x1, data->membase + EMAC_MAC_MCMD_REG); 73 74 /* Wait read complete */ 75 timeout_jiffies = jiffies + MDIO_TIMEOUT; 76 while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) { 77 if (time_is_before_jiffies(timeout_jiffies)) 78 return -ETIMEDOUT; 79 msleep(1); 80 } 81 82 /* push down the phy io line */ 83 writel(0x0, data->membase + EMAC_MAC_MCMD_REG); 84 /* and write data */ 85 writel(value, data->membase + EMAC_MAC_MWTD_REG); 86 87 return 0; 88} 89 90static int sun4i_mdio_probe(struct platform_device *pdev) 91{ 92 struct device_node *np = pdev->dev.of_node; 93 struct mii_bus *bus; 94 struct sun4i_mdio_data *data; 95 struct resource *res; 96 int ret; 97 98 bus = mdiobus_alloc_size(sizeof(*data)); 99 if (!bus) 100 return -ENOMEM; 101 102 bus->name = "sun4i_mii_bus"; 103 bus->read = &sun4i_mdio_read; 104 bus->write = &sun4i_mdio_write; 105 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev)); 106 bus->parent = &pdev->dev; 107 108 data = bus->priv; 109 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 110 data->membase = devm_ioremap_resource(&pdev->dev, res); 111 if (IS_ERR(data->membase)) { 112 ret = PTR_ERR(data->membase); 113 goto err_out_free_mdiobus; 114 } 115 116 data->regulator = devm_regulator_get(&pdev->dev, "phy"); 117 if (IS_ERR(data->regulator)) { 118 if (PTR_ERR(data->regulator) == -EPROBE_DEFER) { 119 ret = -EPROBE_DEFER; 120 goto err_out_free_mdiobus; 121 } 122 123 dev_info(&pdev->dev, "no regulator found\n"); 124 data->regulator = NULL; 125 } else { 126 ret = regulator_enable(data->regulator); 127 if (ret) 128 goto err_out_free_mdiobus; 129 } 130 131 ret = of_mdiobus_register(bus, np); 132 if (ret < 0) 133 goto err_out_disable_regulator; 134 135 platform_set_drvdata(pdev, bus); 136 137 return 0; 138 139err_out_disable_regulator: 140 if (data->regulator) 141 regulator_disable(data->regulator); 142err_out_free_mdiobus: 143 mdiobus_free(bus); 144 return ret; 145} 146 147static int sun4i_mdio_remove(struct platform_device *pdev) 148{ 149 struct mii_bus *bus = platform_get_drvdata(pdev); 150 151 mdiobus_unregister(bus); 152 mdiobus_free(bus); 153 154 return 0; 155} 156 157static const struct of_device_id sun4i_mdio_dt_ids[] = { 158 { .compatible = "allwinner,sun4i-a10-mdio" }, 159 160 /* Deprecated */ 161 { .compatible = "allwinner,sun4i-mdio" }, 162 { } 163}; 164MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids); 165 166static struct platform_driver sun4i_mdio_driver = { 167 .probe = sun4i_mdio_probe, 168 .remove = sun4i_mdio_remove, 169 .driver = { 170 .name = "sun4i-mdio", 171 .of_match_table = sun4i_mdio_dt_ids, 172 }, 173}; 174 175module_platform_driver(sun4i_mdio_driver); 176 177MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver"); 178MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>"); 179MODULE_LICENSE("GPL v2");