aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/marvell
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2012-11-12 11:03:47 -0500
committerThomas Petazzoni <thomas.petazzoni@free-electrons.com>2012-11-16 04:20:52 -0500
commitfc8f5aded1cf9f5505c55694b36174621c7ac88c (patch)
tree9d27d420875319912d21537261bb1a45b8590223 /drivers/net/ethernet/marvell
parent77b67063bb6bce6d475e910d3b886a606d0d91f7 (diff)
net: mvmdio: new Marvell MDIO driver
This patch adds a separate driver for the MDIO interface of the Marvell Ethernet controllers. There are two reasons to have a separate driver rather than including it inside the MAC driver itself: *) The MDIO interface is shared by all Ethernet ports, so a driver must guarantee non-concurrent accesses to this MDIO interface. The most logical way is to have a separate driver that handles this single MDIO interface, used by all Ethernet ports. *) The MDIO interface is the same between the existing mv643xx_eth driver and the new mvneta driver. Even though it is for now only used by the mvneta driver, it will in the future be used by the mv643xx_eth driver as well. Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Acked-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/marvell')
-rw-r--r--drivers/net/ethernet/marvell/Kconfig11
-rw-r--r--drivers/net/ethernet/marvell/Makefile1
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c230
3 files changed, 242 insertions, 0 deletions
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 0029934748bc..232ccb3cb08b 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -31,6 +31,17 @@ config MV643XX_ETH
31 Some boards that use the Discovery chipset are the Momenco 31 Some boards that use the Discovery chipset are the Momenco
32 Ocelot C and Jaguar ATX and Pegasos II. 32 Ocelot C and Jaguar ATX and Pegasos II.
33 33
34config MVMDIO
35 tristate "Marvell MDIO interface support"
36 ---help---
37 This driver supports the MDIO interface found in the network
38 interface units of the Marvell EBU SoCs (Kirkwood, Orion5x,
39 Dove, Armada 370 and Armada XP).
40
41 For now, this driver is only needed for the MVNETA driver
42 (used on Armada 370 and XP), but it could be used in the
43 future by the MV643XX_ETH driver.
44
34config PXA168_ETH 45config PXA168_ETH
35 tristate "Marvell pxa168 ethernet support" 46 tristate "Marvell pxa168 ethernet support"
36 depends on CPU_PXA168 47 depends on CPU_PXA168
diff --git a/drivers/net/ethernet/marvell/Makefile b/drivers/net/ethernet/marvell/Makefile
index 57e3234a37ba..0438599fba47 100644
--- a/drivers/net/ethernet/marvell/Makefile
+++ b/drivers/net/ethernet/marvell/Makefile
@@ -3,6 +3,7 @@
3# 3#
4 4
5obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o 5obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
6obj-$(CONFIG_MVMDIO) += mvmdio.o
6obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o 7obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
7obj-$(CONFIG_SKGE) += skge.o 8obj-$(CONFIG_SKGE) += skge.o
8obj-$(CONFIG_SKY2) += sky2.o 9obj-$(CONFIG_SKY2) += sky2.o
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
new file mode 100644
index 000000000000..82fbd235e502
--- /dev/null
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -0,0 +1,230 @@
1/*
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");