Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / phy / mdio-moxart.c
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
33 struct moxart_mdio_data {
34         void __iomem            *base;
35 };
36
37 static 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
65 static 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
97 static 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
114 static 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         bus->irq = devm_kzalloc(&pdev->dev, sizeof(int) * PHY_MAX_ADDR,
134                         GFP_KERNEL);
135         if (!bus->irq) {
136                 ret = -ENOMEM;
137                 goto err_out_free_mdiobus;
138         }
139
140         /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
141          * of_mdiobus_register() sets these PHY_POLL.
142          * Ideally, the interrupt from MAC controller could be used to
143          * detect link state changes, not polling, i.e. if there was
144          * a way phy_driver could set PHY_HAS_INTERRUPT but have that
145          * interrupt handled in ethernet drivercode.
146          */
147         for (i = 0; i < PHY_MAX_ADDR; i++)
148                 bus->irq[i] = PHY_IGNORE_INTERRUPT;
149
150         data = bus->priv;
151         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
152         data->base = devm_ioremap_resource(&pdev->dev, res);
153         if (IS_ERR(data->base)) {
154                 ret = PTR_ERR(data->base);
155                 goto err_out_free_mdiobus;
156         }
157
158         ret = of_mdiobus_register(bus, np);
159         if (ret < 0)
160                 goto err_out_free_mdiobus;
161
162         platform_set_drvdata(pdev, bus);
163
164         return 0;
165
166 err_out_free_mdiobus:
167         mdiobus_free(bus);
168         return ret;
169 }
170
171 static int moxart_mdio_remove(struct platform_device *pdev)
172 {
173         struct mii_bus *bus = platform_get_drvdata(pdev);
174
175         mdiobus_unregister(bus);
176         mdiobus_free(bus);
177
178         return 0;
179 }
180
181 static const struct of_device_id moxart_mdio_dt_ids[] = {
182         { .compatible = "moxa,moxart-mdio" },
183         { }
184 };
185 MODULE_DEVICE_TABLE(of, moxart_mdio_dt_ids);
186
187 static struct platform_driver moxart_mdio_driver = {
188         .probe = moxart_mdio_probe,
189         .remove = moxart_mdio_remove,
190         .driver = {
191                 .name = "moxart-mdio",
192                 .of_match_table = moxart_mdio_dt_ids,
193         },
194 };
195
196 module_platform_driver(moxart_mdio_driver);
197
198 MODULE_DESCRIPTION("MOXA ART MDIO interface driver");
199 MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
200 MODULE_LICENSE("GPL");