Skip to content

Commit fc8f5ad

Browse files
committed
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>
1 parent 77b6706 commit fc8f5ad

File tree

4 files changed

+277
-0
lines changed

4 files changed

+277
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
* Marvell MDIO Ethernet Controller interface
2+
3+
The Ethernet controllers of the Marvel Kirkwood, Dove, Orion5x,
4+
MV78xx0, Armada 370 and Armada XP have an identical unit that provides
5+
an interface with the MDIO bus. This driver handles this MDIO
6+
interface.
7+
8+
Required properties:
9+
- compatible: "marvell,orion-mdio"
10+
- reg: address and length of the SMI register
11+
12+
The child nodes of the MDIO driver are the individual PHY devices
13+
connected to this MDIO bus. They must have a "reg" property given the
14+
PHY address on the MDIO bus.
15+
16+
Example at the SoC level:
17+
18+
mdio {
19+
#address-cells = <1>;
20+
#size-cells = <0>;
21+
compatible = "marvell,orion-mdio";
22+
reg = <0xd0072004 0x4>;
23+
};
24+
25+
And at the board level:
26+
27+
mdio {
28+
phy0: ethernet-phy@0 {
29+
reg = <0>;
30+
};
31+
32+
phy1: ethernet-phy@1 {
33+
reg = <1>;
34+
};
35+
}

drivers/net/ethernet/marvell/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ config MV643XX_ETH
3131
Some boards that use the Discovery chipset are the Momenco
3232
Ocelot C and Jaguar ATX and Pegasos II.
3333

34+
config 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+
3445
config PXA168_ETH
3546
tristate "Marvell pxa168 ethernet support"
3647
depends on CPU_PXA168

drivers/net/ethernet/marvell/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#
44

55
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
6+
obj-$(CONFIG_MVMDIO) += mvmdio.o
67
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
78
obj-$(CONFIG_SKGE) += skge.o
89
obj-$(CONFIG_SKY2) += sky2.o

drivers/net/ethernet/marvell/mvmdio.c

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
41+
struct 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+
*/
49+
static 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+
73+
static 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+
116+
static 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+
141+
static int orion_mdio_reset(struct mii_bus *bus)
142+
{
143+
return 0;
144+
}
145+
146+
static 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+
202+
static 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+
211+
static const struct of_device_id orion_mdio_match[] = {
212+
{ .compatible = "marvell,orion-mdio" },
213+
{ }
214+
};
215+
MODULE_DEVICE_TABLE(of, orion_mdio_match);
216+
217+
static 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+
226+
module_platform_driver(orion_mdio_driver);
227+
228+
MODULE_DESCRIPTION("Marvell MDIO interface driver");
229+
MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
230+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)