aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaxime Ripard <maxime.ripard@free-electrons.com>2013-05-29 23:49:21 -0400
committerDavid S. Miller <davem@davemloft.net>2013-05-31 20:23:07 -0400
commit4bdcb1dd9feb03608e12cfa46aba385035af8ea5 (patch)
tree0f66691f74f56fe0c5a1a4ab59a790620bb9268d
parent492205050d77bcc4f85f6dc0da6b6fdbca1d6ff7 (diff)
net: Add MDIO bus driver for the Allwinner EMAC
This patch adds a separate driver for the MDIO interface of the Allwinner ethernet controllers. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Tested-by: Richard Genoud <richard.genoud@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt26
-rw-r--r--drivers/net/phy/Kconfig10
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/mdio-sun4i.c194
4 files changed, 231 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt b/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt
new file mode 100644
index 000000000000..00b9f9a3ec1d
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/allwinner,sun4i-mdio.txt
@@ -0,0 +1,26 @@
1* Allwinner A10 MDIO Ethernet Controller interface
2
3Required properties:
4- compatible: should be "allwinner,sun4i-mdio".
5- reg: address and length of the register set for the device.
6
7Optional properties:
8- phy-supply: phandle to a regulator if the PHY needs one
9
10Example at the SoC level:
11mdio@01c0b080 {
12 compatible = "allwinner,sun4i-mdio";
13 reg = <0x01c0b080 0x14>;
14 #address-cells = <1>;
15 #size-cells = <0>;
16};
17
18And at the board level:
19
20mdio@01c0b080 {
21 phy-supply = <&reg_emac_3v3>;
22
23 phy0: ethernet-phy@0 {
24 reg = <0>;
25 };
26};
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 1e11f2bfd9ce..3a316b30089f 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -144,6 +144,16 @@ config MDIO_OCTEON
144 144
145 If in doubt, say Y. 145 If in doubt, say Y.
146 146
147config MDIO_SUN4I
148 tristate "Allwinner sun4i MDIO interface support"
149 depends on ARCH_SUNXI
150 select REGULATOR
151 select REGULATOR_FIXED_VOLTAGE
152 help
153 This driver supports the MDIO interface found in the network
154 interface units of the Allwinner SoC that have an EMAC (A10,
155 A12, A10s, etc.)
156
147config MDIO_BUS_MUX 157config MDIO_BUS_MUX
148 tristate 158 tristate
149 depends on OF_MDIO 159 depends on OF_MDIO
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 9645e389a58d..23a2ab2e847e 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_AMD_PHY) += amd.o
30obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o 30obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o
31obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o 31obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
32obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o 32obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
33obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
diff --git a/drivers/net/phy/mdio-sun4i.c b/drivers/net/phy/mdio-sun4i.c
new file mode 100644
index 000000000000..61d3f4ebf52e
--- /dev/null
+++ b/drivers/net/phy/mdio-sun4i.c
@@ -0,0 +1,194 @@
1/*
2 * Allwinner EMAC MDIO interface driver
3 *
4 * Copyright 2012-2013 Stefan Roese <sr@denx.de>
5 * Copyright 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
6 *
7 * Based on the Linux driver provided by Allwinner:
8 * Copyright (C) 1997 Sten Wang
9 *
10 * This file is licensed under the terms of the GNU General Public
11 * License version 2. This program is licensed "as is" without any
12 * warranty of any kind, whether express or implied.
13 */
14
15#include <linux/delay.h>
16#include <linux/init.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/mutex.h>
20#include <linux/of_address.h>
21#include <linux/of_mdio.h>
22#include <linux/phy.h>
23#include <linux/platform_device.h>
24#include <linux/regulator/consumer.h>
25
26#define EMAC_MAC_MCMD_REG (0x00)
27#define EMAC_MAC_MADR_REG (0x04)
28#define EMAC_MAC_MWTD_REG (0x08)
29#define EMAC_MAC_MRDD_REG (0x0c)
30#define EMAC_MAC_MIND_REG (0x10)
31#define EMAC_MAC_SSRR_REG (0x14)
32
33#define MDIO_TIMEOUT (msecs_to_jiffies(100))
34
35struct sun4i_mdio_data {
36 void __iomem *membase;
37 struct regulator *regulator;
38};
39
40static int sun4i_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
41{
42 struct sun4i_mdio_data *data = bus->priv;
43 unsigned long start_jiffies;
44 int value;
45
46 /* issue the phy address and reg */
47 writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);
48 /* pull up the phy io line */
49 writel(0x1, data->membase + EMAC_MAC_MCMD_REG);
50
51 /* Wait read complete */
52 start_jiffies = jiffies;
53 while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {
54 if (time_after(start_jiffies,
55 start_jiffies + MDIO_TIMEOUT))
56 return -ETIMEDOUT;
57 msleep(1);
58 }
59
60 /* push down the phy io line */
61 writel(0x0, data->membase + EMAC_MAC_MCMD_REG);
62 /* and read data */
63 value = readl(data->membase + EMAC_MAC_MRDD_REG);
64
65 return value;
66}
67
68static int sun4i_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
69 u16 value)
70{
71 struct sun4i_mdio_data *data = bus->priv;
72 unsigned long start_jiffies;
73
74 /* issue the phy address and reg */
75 writel((mii_id << 8) | regnum, data->membase + EMAC_MAC_MADR_REG);
76 /* pull up the phy io line */
77 writel(0x1, data->membase + EMAC_MAC_MCMD_REG);
78
79 /* Wait read complete */
80 start_jiffies = jiffies;
81 while (readl(data->membase + EMAC_MAC_MIND_REG) & 0x1) {
82 if (time_after(start_jiffies,
83 start_jiffies + MDIO_TIMEOUT))
84 return -ETIMEDOUT;
85 msleep(1);
86 }
87
88 /* push down the phy io line */
89 writel(0x0, data->membase + EMAC_MAC_MCMD_REG);
90 /* and write data */
91 writel(value, data->membase + EMAC_MAC_MWTD_REG);
92
93 return 0;
94}
95
96static int sun4i_mdio_reset(struct mii_bus *bus)
97{
98 return 0;
99}
100
101static int sun4i_mdio_probe(struct platform_device *pdev)
102{
103 struct device_node *np = pdev->dev.of_node;
104 struct mii_bus *bus;
105 struct sun4i_mdio_data *data;
106 int ret, i;
107
108 bus = mdiobus_alloc_size(sizeof(*data));
109 if (!bus)
110 return -ENOMEM;
111
112 bus->name = "sun4i_mii_bus";
113 bus->read = &sun4i_mdio_read;
114 bus->write = &sun4i_mdio_write;
115 bus->reset = &sun4i_mdio_reset;
116 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
117 bus->parent = &pdev->dev;
118
119 bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
120 if (!bus->irq) {
121 ret = -ENOMEM;
122 goto err_out_free_mdiobus;
123 }
124
125 for (i = 0; i < PHY_MAX_ADDR; i++)
126 bus->irq[i] = PHY_POLL;
127
128 data = bus->priv;
129 data->membase = of_iomap(np, 0);
130 if (!data->membase) {
131 ret = -ENOMEM;
132 goto err_out_free_mdio_irq;
133 }
134
135 data->regulator = devm_regulator_get(&pdev->dev, "phy");
136 if (IS_ERR(data->regulator)) {
137 if (PTR_ERR(data->regulator) == -EPROBE_DEFER)
138 return -EPROBE_DEFER;
139
140 dev_info(&pdev->dev, "no regulator found\n");
141 } else {
142 ret = regulator_enable(data->regulator);
143 if (ret)
144 goto err_out_free_mdio_irq;
145 }
146
147 ret = of_mdiobus_register(bus, np);
148 if (ret < 0)
149 goto err_out_disable_regulator;
150
151 platform_set_drvdata(pdev, bus);
152
153 return 0;
154
155err_out_disable_regulator:
156 regulator_disable(data->regulator);
157err_out_free_mdio_irq:
158 kfree(bus->irq);
159err_out_free_mdiobus:
160 mdiobus_free(bus);
161 return ret;
162}
163
164static int sun4i_mdio_remove(struct platform_device *pdev)
165{
166 struct mii_bus *bus = platform_get_drvdata(pdev);
167
168 mdiobus_unregister(bus);
169 kfree(bus->irq);
170 mdiobus_free(bus);
171
172 return 0;
173}
174
175static const struct of_device_id sun4i_mdio_dt_ids[] = {
176 { .compatible = "allwinner,sun4i-mdio" },
177 { }
178};
179MODULE_DEVICE_TABLE(of, sun4i_mdio_dt_ids);
180
181static struct platform_driver sun4i_mdio_driver = {
182 .probe = sun4i_mdio_probe,
183 .remove = sun4i_mdio_remove,
184 .driver = {
185 .name = "sun4i-mdio",
186 .of_match_table = sun4i_mdio_dt_ids,
187 },
188};
189
190module_platform_driver(sun4i_mdio_driver);
191
192MODULE_DESCRIPTION("Allwinner EMAC MDIO interface driver");
193MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
194MODULE_LICENSE("GPL");