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