blob: 82fbd235e502966e430836da14a66c423d79074b [file] [log] [blame]
Thomas Petazzonifc8f5ad2012-11-12 17:03:47 +01001/*
2 * Driver for the MDIO interface of Marvell network interfaces.
3 *
4 * Since the MDIO interface of Marvell network interfaces is shared
5 * between all network interfaces, having a single driver allows to
6 * handle concurrent accesses properly (you may have four Ethernet
7 * ports, but they in fact share the same SMI interface to access the
8 * MDIO bus). Moreover, this MDIO interface code is similar between
9 * the mv643xx_eth driver and the mvneta driver. For now, it is only
10 * used by the mvneta driver, but it could later be used by the
11 * mv643xx_eth driver as well.
12 *
13 * Copyright (C) 2012 Marvell
14 *
15 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
16 *
17 * This file is licensed under the terms of the GNU General Public
18 * License version 2. This program is licensed "as is" without any
19 * warranty of any kind, whether express or implied.
20 */
21
22#include <linux/init.h>
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/mutex.h>
26#include <linux/phy.h>
27#include <linux/of_address.h>
28#include <linux/of_mdio.h>
29#include <linux/platform_device.h>
30
31#include <asm/delay.h>
32
33#define MVMDIO_SMI_DATA_SHIFT 0
34#define MVMDIO_SMI_PHY_ADDR_SHIFT 16
35#define MVMDIO_SMI_PHY_REG_SHIFT 21
36#define MVMDIO_SMI_READ_OPERATION BIT(26)
37#define MVMDIO_SMI_WRITE_OPERATION 0
38#define MVMDIO_SMI_READ_VALID BIT(27)
39#define MVMDIO_SMI_BUSY BIT(28)
40
41struct orion_mdio_dev {
42 struct mutex lock;
43 void __iomem *smireg;
44};
45
46/*
47 * Wait for the SMI unit to be ready for another operation
48 */
49static int orion_mdio_wait_ready(struct mii_bus *bus)
50{
51 struct orion_mdio_dev *dev = bus->priv;
52 int count;
53 u32 val;
54
55 count = 0;
56 while (1) {
57 val = readl(dev->smireg);
58 if (!(val & MVMDIO_SMI_BUSY))
59 break;
60
61 if (count > 100) {
62 dev_err(bus->parent, "Timeout: SMI busy for too long\n");
63 return -ETIMEDOUT;
64 }
65
66 udelay(10);
67 count++;
68 }
69
70 return 0;
71}
72
73static int orion_mdio_read(struct mii_bus *bus, int mii_id,
74 int regnum)
75{
76 struct orion_mdio_dev *dev = bus->priv;
77 int count;
78 u32 val;
79 int ret;
80
81 mutex_lock(&dev->lock);
82
83 ret = orion_mdio_wait_ready(bus);
84 if (ret < 0) {
85 mutex_unlock(&dev->lock);
86 return ret;
87 }
88
89 writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
90 (regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
91 MVMDIO_SMI_READ_OPERATION),
92 dev->smireg);
93
94 /* Wait for the value to become available */
95 count = 0;
96 while (1) {
97 val = readl(dev->smireg);
98 if (val & MVMDIO_SMI_READ_VALID)
99 break;
100
101 if (count > 100) {
102 dev_err(bus->parent, "Timeout when reading PHY\n");
103 mutex_unlock(&dev->lock);
104 return -ETIMEDOUT;
105 }
106
107 udelay(10);
108 count++;
109 }
110
111 mutex_unlock(&dev->lock);
112
113 return val & 0xFFFF;
114}
115
116static int orion_mdio_write(struct mii_bus *bus, int mii_id,
117 int regnum, u16 value)
118{
119 struct orion_mdio_dev *dev = bus->priv;
120 int ret;
121
122 mutex_lock(&dev->lock);
123
124 ret = orion_mdio_wait_ready(bus);
125 if (ret < 0) {
126 mutex_unlock(&dev->lock);
127 return ret;
128 }
129
130 writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
131 (regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
132 MVMDIO_SMI_WRITE_OPERATION |
133 (value << MVMDIO_SMI_DATA_SHIFT)),
134 dev->smireg);
135
136 mutex_unlock(&dev->lock);
137
138 return 0;
139}
140
141static int orion_mdio_reset(struct mii_bus *bus)
142{
143 return 0;
144}
145
146static int __devinit orion_mdio_probe(struct platform_device *pdev)
147{
148 struct device_node *np = pdev->dev.of_node;
149 struct mii_bus *bus;
150 struct orion_mdio_dev *dev;
151 int i, ret;
152
153 bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
154 if (!bus) {
155 dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
156 return -ENOMEM;
157 }
158
159 bus->name = "orion_mdio_bus";
160 bus->read = orion_mdio_read;
161 bus->write = orion_mdio_write;
162 bus->reset = orion_mdio_reset;
163 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii",
164 dev_name(&pdev->dev));
165 bus->parent = &pdev->dev;
166
167 bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
168 if (!bus->irq) {
169 dev_err(&pdev->dev, "Cannot allocate PHY IRQ array\n");
170 mdiobus_free(bus);
171 return -ENOMEM;
172 }
173
174 for (i = 0; i < PHY_MAX_ADDR; i++)
175 bus->irq[i] = PHY_POLL;
176
177 dev = bus->priv;
178 dev->smireg = of_iomap(pdev->dev.of_node, 0);
179 if (!dev->smireg) {
180 dev_err(&pdev->dev, "No SMI register address given in DT\n");
181 kfree(bus->irq);
182 mdiobus_free(bus);
183 return -ENODEV;
184 }
185
186 mutex_init(&dev->lock);
187
188 ret = of_mdiobus_register(bus, np);
189 if (ret < 0) {
190 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
191 iounmap(dev->smireg);
192 kfree(bus->irq);
193 mdiobus_free(bus);
194 return ret;
195 }
196
197 platform_set_drvdata(pdev, bus);
198
199 return 0;
200}
201
202static int __devexit orion_mdio_remove(struct platform_device *pdev)
203{
204 struct mii_bus *bus = platform_get_drvdata(pdev);
205 mdiobus_unregister(bus);
206 kfree(bus->irq);
207 mdiobus_free(bus);
208 return 0;
209}
210
211static const struct of_device_id orion_mdio_match[] = {
212 { .compatible = "marvell,orion-mdio" },
213 { }
214};
215MODULE_DEVICE_TABLE(of, orion_mdio_match);
216
217static struct platform_driver orion_mdio_driver = {
218 .probe = orion_mdio_probe,
219 .remove = __devexit_p(orion_mdio_remove),
220 .driver = {
221 .name = "orion-mdio",
222 .of_match_table = orion_mdio_match,
223 },
224};
225
226module_platform_driver(orion_mdio_driver);
227
228MODULE_DESCRIPTION("Marvell MDIO interface driver");
229MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
230MODULE_LICENSE("GPL");