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