diff options
-rw-r--r-- | Documentation/devicetree/bindings/net/marvell-orion-mdio.txt | 10 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvmdio.c | 214 |
2 files changed, 164 insertions, 60 deletions
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt index ccdabdcc8618..42cd81090a2c 100644 --- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt +++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt | |||
@@ -1,12 +1,14 @@ | |||
1 | * Marvell MDIO Ethernet Controller interface | 1 | * Marvell MDIO Ethernet Controller interface |
2 | 2 | ||
3 | The Ethernet controllers of the Marvel Kirkwood, Dove, Orion5x, | 3 | The Ethernet controllers of the Marvel Kirkwood, Dove, Orion5x, |
4 | MV78xx0, Armada 370 and Armada XP have an identical unit that provides | 4 | MV78xx0, Armada 370, Armada XP, Armada 7k and Armada 8k have an |
5 | an interface with the MDIO bus. This driver handles this MDIO | 5 | identical unit that provides an interface with the MDIO bus. |
6 | interface. | 6 | Additionally, Armada 7k and Armada 8k has a second unit which |
7 | provides an interface with the xMDIO bus. This driver handles | ||
8 | these interfaces. | ||
7 | 9 | ||
8 | Required properties: | 10 | Required properties: |
9 | - compatible: "marvell,orion-mdio" | 11 | - compatible: "marvell,orion-mdio" or "marvell,xmdio" |
10 | - reg: address and length of the MDIO registers. When an interrupt is | 12 | - reg: address and length of the MDIO registers. When an interrupt is |
11 | not present, the length is the size of the SMI register (4 bytes) | 13 | not present, the length is the size of the SMI register (4 bytes) |
12 | otherwise it must be 0x84 bytes to cover the interrupt control | 14 | otherwise it must be 0x84 bytes to cover the interrupt control |
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 90a60b98c28e..c9798210fa0f 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c | |||
@@ -17,41 +17,52 @@ | |||
17 | * warranty of any kind, whether express or implied. | 17 | * warranty of any kind, whether express or implied. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/clk.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/io.h> | ||
20 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
21 | #include <linux/module.h> | 25 | #include <linux/module.h> |
22 | #include <linux/mutex.h> | 26 | #include <linux/of_device.h> |
27 | #include <linux/of_mdio.h> | ||
23 | #include <linux/phy.h> | 28 | #include <linux/phy.h> |
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/platform_device.h> | 29 | #include <linux/platform_device.h> |
26 | #include <linux/delay.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/clk.h> | ||
29 | #include <linux/of_mdio.h> | ||
30 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
31 | #include <linux/wait.h> | 31 | #include <linux/wait.h> |
32 | 32 | ||
33 | #define MVMDIO_SMI_DATA_SHIFT 0 | 33 | #define MVMDIO_SMI_DATA_SHIFT 0 |
34 | #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 | 34 | #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 |
35 | #define MVMDIO_SMI_PHY_REG_SHIFT 21 | 35 | #define MVMDIO_SMI_PHY_REG_SHIFT 21 |
36 | #define MVMDIO_SMI_READ_OPERATION BIT(26) | 36 | #define MVMDIO_SMI_READ_OPERATION BIT(26) |
37 | #define MVMDIO_SMI_WRITE_OPERATION 0 | 37 | #define MVMDIO_SMI_WRITE_OPERATION 0 |
38 | #define MVMDIO_SMI_READ_VALID BIT(27) | 38 | #define MVMDIO_SMI_READ_VALID BIT(27) |
39 | #define MVMDIO_SMI_BUSY BIT(28) | 39 | #define MVMDIO_SMI_BUSY BIT(28) |
40 | #define MVMDIO_ERR_INT_CAUSE 0x007C | 40 | #define MVMDIO_ERR_INT_CAUSE 0x007C |
41 | #define MVMDIO_ERR_INT_SMI_DONE 0x00000010 | 41 | #define MVMDIO_ERR_INT_SMI_DONE 0x00000010 |
42 | #define MVMDIO_ERR_INT_MASK 0x0080 | 42 | #define MVMDIO_ERR_INT_MASK 0x0080 |
43 | |||
44 | #define MVMDIO_XSMI_MGNT_REG 0x0 | ||
45 | #define MVMDIO_XSMI_PHYADDR_SHIFT 16 | ||
46 | #define MVMDIO_XSMI_DEVADDR_SHIFT 21 | ||
47 | #define MVMDIO_XSMI_WRITE_OPERATION (0x5 << 26) | ||
48 | #define MVMDIO_XSMI_READ_OPERATION (0x7 << 26) | ||
49 | #define MVMDIO_XSMI_READ_VALID BIT(29) | ||
50 | #define MVMDIO_XSMI_BUSY BIT(30) | ||
51 | #define MVMDIO_XSMI_ADDR_REG 0x8 | ||
43 | 52 | ||
44 | /* | 53 | /* |
45 | * SMI Timeout measurements: | 54 | * SMI Timeout measurements: |
46 | * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt) | 55 | * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt) |
47 | * - Armada 370 (Globalscale Mirabox): 41us to 43us (Polled) | 56 | * - Armada 370 (Globalscale Mirabox): 41us to 43us (Polled) |
48 | */ | 57 | */ |
49 | #define MVMDIO_SMI_TIMEOUT 1000 /* 1000us = 1ms */ | 58 | #define MVMDIO_SMI_TIMEOUT 1000 /* 1000us = 1ms */ |
50 | #define MVMDIO_SMI_POLL_INTERVAL_MIN 45 | 59 | #define MVMDIO_SMI_POLL_INTERVAL_MIN 45 |
51 | #define MVMDIO_SMI_POLL_INTERVAL_MAX 55 | 60 | #define MVMDIO_SMI_POLL_INTERVAL_MAX 55 |
61 | |||
62 | #define MVMDIO_XSMI_POLL_INTERVAL_MIN 150 | ||
63 | #define MVMDIO_XSMI_POLL_INTERVAL_MAX 160 | ||
52 | 64 | ||
53 | struct orion_mdio_dev { | 65 | struct orion_mdio_dev { |
54 | struct mutex lock; | ||
55 | void __iomem *regs; | 66 | void __iomem *regs; |
56 | struct clk *clk[3]; | 67 | struct clk *clk[3]; |
57 | /* | 68 | /* |
@@ -64,14 +75,21 @@ struct orion_mdio_dev { | |||
64 | wait_queue_head_t smi_busy_wait; | 75 | wait_queue_head_t smi_busy_wait; |
65 | }; | 76 | }; |
66 | 77 | ||
67 | static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) | 78 | enum orion_mdio_bus_type { |
68 | { | 79 | BUS_TYPE_SMI, |
69 | return !(readl(dev->regs) & MVMDIO_SMI_BUSY); | 80 | BUS_TYPE_XSMI |
70 | } | 81 | }; |
82 | |||
83 | struct orion_mdio_ops { | ||
84 | int (*is_done)(struct orion_mdio_dev *); | ||
85 | unsigned int poll_interval_min; | ||
86 | unsigned int poll_interval_max; | ||
87 | }; | ||
71 | 88 | ||
72 | /* Wait for the SMI unit to be ready for another operation | 89 | /* Wait for the SMI unit to be ready for another operation |
73 | */ | 90 | */ |
74 | static int orion_mdio_wait_ready(struct mii_bus *bus) | 91 | static int orion_mdio_wait_ready(const struct orion_mdio_ops *ops, |
92 | struct mii_bus *bus) | ||
75 | { | 93 | { |
76 | struct orion_mdio_dev *dev = bus->priv; | 94 | struct orion_mdio_dev *dev = bus->priv; |
77 | unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT); | 95 | unsigned long timeout = usecs_to_jiffies(MVMDIO_SMI_TIMEOUT); |
@@ -79,14 +97,14 @@ static int orion_mdio_wait_ready(struct mii_bus *bus) | |||
79 | int timedout = 0; | 97 | int timedout = 0; |
80 | 98 | ||
81 | while (1) { | 99 | while (1) { |
82 | if (orion_mdio_smi_is_done(dev)) | 100 | if (ops->is_done(dev)) |
83 | return 0; | 101 | return 0; |
84 | else if (timedout) | 102 | else if (timedout) |
85 | break; | 103 | break; |
86 | 104 | ||
87 | if (dev->err_interrupt <= 0) { | 105 | if (dev->err_interrupt <= 0) { |
88 | usleep_range(MVMDIO_SMI_POLL_INTERVAL_MIN, | 106 | usleep_range(ops->poll_interval_min, |
89 | MVMDIO_SMI_POLL_INTERVAL_MAX); | 107 | ops->poll_interval_max); |
90 | 108 | ||
91 | if (time_is_before_jiffies(end)) | 109 | if (time_is_before_jiffies(end)) |
92 | ++timedout; | 110 | ++timedout; |
@@ -98,8 +116,7 @@ static int orion_mdio_wait_ready(struct mii_bus *bus) | |||
98 | if (timeout < 2) | 116 | if (timeout < 2) |
99 | timeout = 2; | 117 | timeout = 2; |
100 | wait_event_timeout(dev->smi_busy_wait, | 118 | wait_event_timeout(dev->smi_busy_wait, |
101 | orion_mdio_smi_is_done(dev), | 119 | ops->is_done(dev), timeout); |
102 | timeout); | ||
103 | 120 | ||
104 | ++timedout; | 121 | ++timedout; |
105 | } | 122 | } |
@@ -109,52 +126,61 @@ static int orion_mdio_wait_ready(struct mii_bus *bus) | |||
109 | return -ETIMEDOUT; | 126 | return -ETIMEDOUT; |
110 | } | 127 | } |
111 | 128 | ||
112 | static int orion_mdio_read(struct mii_bus *bus, int mii_id, | 129 | static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) |
113 | int regnum) | 130 | { |
131 | return !(readl(dev->regs) & MVMDIO_SMI_BUSY); | ||
132 | } | ||
133 | |||
134 | static const struct orion_mdio_ops orion_mdio_smi_ops = { | ||
135 | .is_done = orion_mdio_smi_is_done, | ||
136 | .poll_interval_min = MVMDIO_SMI_POLL_INTERVAL_MIN, | ||
137 | .poll_interval_max = MVMDIO_SMI_POLL_INTERVAL_MAX, | ||
138 | }; | ||
139 | |||
140 | static int orion_mdio_smi_read(struct mii_bus *bus, int mii_id, | ||
141 | int regnum) | ||
114 | { | 142 | { |
115 | struct orion_mdio_dev *dev = bus->priv; | 143 | struct orion_mdio_dev *dev = bus->priv; |
116 | u32 val; | 144 | u32 val; |
117 | int ret; | 145 | int ret; |
118 | 146 | ||
119 | mutex_lock(&dev->lock); | 147 | if (regnum & MII_ADDR_C45) |
148 | return -EOPNOTSUPP; | ||
120 | 149 | ||
121 | ret = orion_mdio_wait_ready(bus); | 150 | ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); |
122 | if (ret < 0) | 151 | if (ret < 0) |
123 | goto out; | 152 | return ret; |
124 | 153 | ||
125 | writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | | 154 | writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | |
126 | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | | 155 | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | |
127 | MVMDIO_SMI_READ_OPERATION), | 156 | MVMDIO_SMI_READ_OPERATION), |
128 | dev->regs); | 157 | dev->regs); |
129 | 158 | ||
130 | ret = orion_mdio_wait_ready(bus); | 159 | ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); |
131 | if (ret < 0) | 160 | if (ret < 0) |
132 | goto out; | 161 | return ret; |
133 | 162 | ||
134 | val = readl(dev->regs); | 163 | val = readl(dev->regs); |
135 | if (!(val & MVMDIO_SMI_READ_VALID)) { | 164 | if (!(val & MVMDIO_SMI_READ_VALID)) { |
136 | dev_err(bus->parent, "SMI bus read not valid\n"); | 165 | dev_err(bus->parent, "SMI bus read not valid\n"); |
137 | ret = -ENODEV; | 166 | return -ENODEV; |
138 | goto out; | ||
139 | } | 167 | } |
140 | 168 | ||
141 | ret = val & 0xFFFF; | 169 | return val & GENMASK(15, 0); |
142 | out: | ||
143 | mutex_unlock(&dev->lock); | ||
144 | return ret; | ||
145 | } | 170 | } |
146 | 171 | ||
147 | static int orion_mdio_write(struct mii_bus *bus, int mii_id, | 172 | static int orion_mdio_smi_write(struct mii_bus *bus, int mii_id, |
148 | int regnum, u16 value) | 173 | int regnum, u16 value) |
149 | { | 174 | { |
150 | struct orion_mdio_dev *dev = bus->priv; | 175 | struct orion_mdio_dev *dev = bus->priv; |
151 | int ret; | 176 | int ret; |
152 | 177 | ||
153 | mutex_lock(&dev->lock); | 178 | if (regnum & MII_ADDR_C45) |
179 | return -EOPNOTSUPP; | ||
154 | 180 | ||
155 | ret = orion_mdio_wait_ready(bus); | 181 | ret = orion_mdio_wait_ready(&orion_mdio_smi_ops, bus); |
156 | if (ret < 0) | 182 | if (ret < 0) |
157 | goto out; | 183 | return ret; |
158 | 184 | ||
159 | writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | | 185 | writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | |
160 | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | | 186 | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | |
@@ -162,9 +188,74 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id, | |||
162 | (value << MVMDIO_SMI_DATA_SHIFT)), | 188 | (value << MVMDIO_SMI_DATA_SHIFT)), |
163 | dev->regs); | 189 | dev->regs); |
164 | 190 | ||
165 | out: | 191 | return 0; |
166 | mutex_unlock(&dev->lock); | 192 | } |
167 | return ret; | 193 | |
194 | static int orion_mdio_xsmi_is_done(struct orion_mdio_dev *dev) | ||
195 | { | ||
196 | return !(readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & MVMDIO_XSMI_BUSY); | ||
197 | } | ||
198 | |||
199 | static const struct orion_mdio_ops orion_mdio_xsmi_ops = { | ||
200 | .is_done = orion_mdio_xsmi_is_done, | ||
201 | .poll_interval_min = MVMDIO_XSMI_POLL_INTERVAL_MIN, | ||
202 | .poll_interval_max = MVMDIO_XSMI_POLL_INTERVAL_MAX, | ||
203 | }; | ||
204 | |||
205 | static int orion_mdio_xsmi_read(struct mii_bus *bus, int mii_id, | ||
206 | int regnum) | ||
207 | { | ||
208 | struct orion_mdio_dev *dev = bus->priv; | ||
209 | u16 dev_addr = (regnum >> 16) & GENMASK(4, 0); | ||
210 | int ret; | ||
211 | |||
212 | if (!(regnum & MII_ADDR_C45)) | ||
213 | return -EOPNOTSUPP; | ||
214 | |||
215 | ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); | ||
216 | if (ret < 0) | ||
217 | return ret; | ||
218 | |||
219 | writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); | ||
220 | writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | | ||
221 | (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | | ||
222 | MVMDIO_XSMI_READ_OPERATION, | ||
223 | dev->regs + MVMDIO_XSMI_MGNT_REG); | ||
224 | |||
225 | ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); | ||
226 | if (ret < 0) | ||
227 | return ret; | ||
228 | |||
229 | if (!(readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & | ||
230 | MVMDIO_XSMI_READ_VALID)) { | ||
231 | dev_err(bus->parent, "XSMI bus read not valid\n"); | ||
232 | return -ENODEV; | ||
233 | } | ||
234 | |||
235 | return readl(dev->regs + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0); | ||
236 | } | ||
237 | |||
238 | static int orion_mdio_xsmi_write(struct mii_bus *bus, int mii_id, | ||
239 | int regnum, u16 value) | ||
240 | { | ||
241 | struct orion_mdio_dev *dev = bus->priv; | ||
242 | u16 dev_addr = (regnum >> 16) & GENMASK(4, 0); | ||
243 | int ret; | ||
244 | |||
245 | if (!(regnum & MII_ADDR_C45)) | ||
246 | return -EOPNOTSUPP; | ||
247 | |||
248 | ret = orion_mdio_wait_ready(&orion_mdio_xsmi_ops, bus); | ||
249 | if (ret < 0) | ||
250 | return ret; | ||
251 | |||
252 | writel(regnum & GENMASK(15, 0), dev->regs + MVMDIO_XSMI_ADDR_REG); | ||
253 | writel((mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | | ||
254 | (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | | ||
255 | MVMDIO_XSMI_WRITE_OPERATION | value, | ||
256 | dev->regs + MVMDIO_XSMI_MGNT_REG); | ||
257 | |||
258 | return 0; | ||
168 | } | 259 | } |
169 | 260 | ||
170 | static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) | 261 | static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) |
@@ -184,11 +275,14 @@ static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) | |||
184 | 275 | ||
185 | static int orion_mdio_probe(struct platform_device *pdev) | 276 | static int orion_mdio_probe(struct platform_device *pdev) |
186 | { | 277 | { |
278 | enum orion_mdio_bus_type type; | ||
187 | struct resource *r; | 279 | struct resource *r; |
188 | struct mii_bus *bus; | 280 | struct mii_bus *bus; |
189 | struct orion_mdio_dev *dev; | 281 | struct orion_mdio_dev *dev; |
190 | int i, ret; | 282 | int i, ret; |
191 | 283 | ||
284 | type = (enum orion_mdio_bus_type)of_device_get_match_data(&pdev->dev); | ||
285 | |||
192 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 286 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
193 | if (!r) { | 287 | if (!r) { |
194 | dev_err(&pdev->dev, "No SMI register address given\n"); | 288 | dev_err(&pdev->dev, "No SMI register address given\n"); |
@@ -200,9 +294,18 @@ static int orion_mdio_probe(struct platform_device *pdev) | |||
200 | if (!bus) | 294 | if (!bus) |
201 | return -ENOMEM; | 295 | return -ENOMEM; |
202 | 296 | ||
297 | switch (type) { | ||
298 | case BUS_TYPE_SMI: | ||
299 | bus->read = orion_mdio_smi_read; | ||
300 | bus->write = orion_mdio_smi_write; | ||
301 | break; | ||
302 | case BUS_TYPE_XSMI: | ||
303 | bus->read = orion_mdio_xsmi_read; | ||
304 | bus->write = orion_mdio_xsmi_write; | ||
305 | break; | ||
306 | } | ||
307 | |||
203 | bus->name = "orion_mdio_bus"; | 308 | bus->name = "orion_mdio_bus"; |
204 | bus->read = orion_mdio_read; | ||
205 | bus->write = orion_mdio_write; | ||
206 | snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", | 309 | snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", |
207 | dev_name(&pdev->dev)); | 310 | dev_name(&pdev->dev)); |
208 | bus->parent = &pdev->dev; | 311 | bus->parent = &pdev->dev; |
@@ -244,8 +347,6 @@ static int orion_mdio_probe(struct platform_device *pdev) | |||
244 | return -EPROBE_DEFER; | 347 | return -EPROBE_DEFER; |
245 | } | 348 | } |
246 | 349 | ||
247 | mutex_init(&dev->lock); | ||
248 | |||
249 | if (pdev->dev.of_node) | 350 | if (pdev->dev.of_node) |
250 | ret = of_mdiobus_register(bus, pdev->dev.of_node); | 351 | ret = of_mdiobus_register(bus, pdev->dev.of_node); |
251 | else | 352 | else |
@@ -294,7 +395,8 @@ static int orion_mdio_remove(struct platform_device *pdev) | |||
294 | } | 395 | } |
295 | 396 | ||
296 | static const struct of_device_id orion_mdio_match[] = { | 397 | static const struct of_device_id orion_mdio_match[] = { |
297 | { .compatible = "marvell,orion-mdio" }, | 398 | { .compatible = "marvell,orion-mdio", .data = (void *)BUS_TYPE_SMI }, |
399 | { .compatible = "marvell,xmdio", .data = (void *)BUS_TYPE_XSMI }, | ||
298 | { } | 400 | { } |
299 | }; | 401 | }; |
300 | MODULE_DEVICE_TABLE(of, orion_mdio_match); | 402 | MODULE_DEVICE_TABLE(of, orion_mdio_match); |