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 v4.20-rc1 193 lines 4.5 kB view raw
1/* MOXA ART Ethernet (RTL8201CP) MDIO interface driver 2 * 3 * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com> 4 * 5 * This file is licensed under the terms of the GNU General Public 6 * License version 2. This program is licensed "as is" without any 7 * warranty of any kind, whether express or implied. 8 */ 9 10#include <linux/delay.h> 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/mutex.h> 14#include <linux/of_address.h> 15#include <linux/of_mdio.h> 16#include <linux/phy.h> 17#include <linux/platform_device.h> 18#include <linux/regulator/consumer.h> 19 20#define REG_PHY_CTRL 0 21#define REG_PHY_WRITE_DATA 4 22 23/* REG_PHY_CTRL */ 24#define MIIWR BIT(27) /* init write sequence (auto cleared)*/ 25#define MIIRD BIT(26) 26#define REGAD_MASK 0x3e00000 27#define PHYAD_MASK 0x1f0000 28#define MIIRDATA_MASK 0xffff 29 30/* REG_PHY_WRITE_DATA */ 31#define MIIWDATA_MASK 0xffff 32 33struct moxart_mdio_data { 34 void __iomem *base; 35}; 36 37static int moxart_mdio_read(struct mii_bus *bus, int mii_id, int regnum) 38{ 39 struct moxart_mdio_data *data = bus->priv; 40 u32 ctrl = 0; 41 unsigned int count = 5; 42 43 dev_dbg(&bus->dev, "%s\n", __func__); 44 45 ctrl |= MIIRD | ((mii_id << 16) & PHYAD_MASK) | 46 ((regnum << 21) & REGAD_MASK); 47 48 writel(ctrl, data->base + REG_PHY_CTRL); 49 50 do { 51 ctrl = readl(data->base + REG_PHY_CTRL); 52 53 if (!(ctrl & MIIRD)) 54 return ctrl & MIIRDATA_MASK; 55 56 mdelay(10); 57 count--; 58 } while (count > 0); 59 60 dev_dbg(&bus->dev, "%s timed out\n", __func__); 61 62 return -ETIMEDOUT; 63} 64 65static int moxart_mdio_write(struct mii_bus *bus, int mii_id, 66 int regnum, u16 value) 67{ 68 struct moxart_mdio_data *data = bus->priv; 69 u32 ctrl = 0; 70 unsigned int count = 5; 71 72 dev_dbg(&bus->dev, "%s\n", __func__); 73 74 ctrl |= MIIWR | ((mii_id << 16) & PHYAD_MASK) | 75 ((regnum << 21) & REGAD_MASK); 76 77 value &= MIIWDATA_MASK; 78 79 writel(value, data->base + REG_PHY_WRITE_DATA); 80 writel(ctrl, data->base + REG_PHY_CTRL); 81 82 do { 83 ctrl = readl(data->base + REG_PHY_CTRL); 84 85 if (!(ctrl & MIIWR)) 86 return 0; 87 88 mdelay(10); 89 count--; 90 } while (count > 0); 91 92 dev_dbg(&bus->dev, "%s timed out\n", __func__); 93 94 return -ETIMEDOUT; 95} 96 97static int moxart_mdio_reset(struct mii_bus *bus) 98{ 99 int data, i; 100 101 for (i = 0; i < PHY_MAX_ADDR; i++) { 102 data = moxart_mdio_read(bus, i, MII_BMCR); 103 if (data < 0) 104 continue; 105 106 data |= BMCR_RESET; 107 if (moxart_mdio_write(bus, i, MII_BMCR, data) < 0) 108 continue; 109 } 110 111 return 0; 112} 113 114static int moxart_mdio_probe(struct platform_device *pdev) 115{ 116 struct device_node *np = pdev->dev.of_node; 117 struct mii_bus *bus; 118 struct moxart_mdio_data *data; 119 struct resource *res; 120 int ret, i; 121 122 bus = mdiobus_alloc_size(sizeof(*data)); 123 if (!bus) 124 return -ENOMEM; 125 126 bus->name = "MOXA ART Ethernet MII"; 127 bus->read = &moxart_mdio_read; 128 bus->write = &moxart_mdio_write; 129 bus->reset = &moxart_mdio_reset; 130 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d-mii", pdev->name, pdev->id); 131 bus->parent = &pdev->dev; 132 133 /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect, 134 * of_mdiobus_register() sets these PHY_POLL. 135 * Ideally, the interrupt from MAC controller could be used to 136 * detect link state changes, not polling, i.e. if there was 137 * a way phy_driver could set PHY_HAS_INTERRUPT but have that 138 * interrupt handled in ethernet drivercode. 139 */ 140 for (i = 0; i < PHY_MAX_ADDR; i++) 141 bus->irq[i] = PHY_IGNORE_INTERRUPT; 142 143 data = bus->priv; 144 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 145 data->base = devm_ioremap_resource(&pdev->dev, res); 146 if (IS_ERR(data->base)) { 147 ret = PTR_ERR(data->base); 148 goto err_out_free_mdiobus; 149 } 150 151 ret = of_mdiobus_register(bus, np); 152 if (ret < 0) 153 goto err_out_free_mdiobus; 154 155 platform_set_drvdata(pdev, bus); 156 157 return 0; 158 159err_out_free_mdiobus: 160 mdiobus_free(bus); 161 return ret; 162} 163 164static int moxart_mdio_remove(struct platform_device *pdev) 165{ 166 struct mii_bus *bus = platform_get_drvdata(pdev); 167 168 mdiobus_unregister(bus); 169 mdiobus_free(bus); 170 171 return 0; 172} 173 174static const struct of_device_id moxart_mdio_dt_ids[] = { 175 { .compatible = "moxa,moxart-mdio" }, 176 { } 177}; 178MODULE_DEVICE_TABLE(of, moxart_mdio_dt_ids); 179 180static struct platform_driver moxart_mdio_driver = { 181 .probe = moxart_mdio_probe, 182 .remove = moxart_mdio_remove, 183 .driver = { 184 .name = "moxart-mdio", 185 .of_match_table = moxart_mdio_dt_ids, 186 }, 187}; 188 189module_platform_driver(moxart_mdio_driver); 190 191MODULE_DESCRIPTION("MOXA ART MDIO interface driver"); 192MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>"); 193MODULE_LICENSE("GPL");