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 |
8 | * the MDIO bus). This driver is currently used by the mvneta and |
9 | * mv643xx_eth drivers. |
10 | * |
11 | * Copyright (C) 2012 Marvell |
12 | * |
13 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> |
14 | * |
15 | * This file is licensed under the terms of the GNU General Public |
16 | * License version 2. This program is licensed "as is" without any |
17 | * warranty of any kind, whether express or implied. |
18 | */ |
19 | |
20 | #include <linux/acpi.h> |
21 | #include <linux/acpi_mdio.h> |
22 | #include <linux/clk.h> |
23 | #include <linux/delay.h> |
24 | #include <linux/interrupt.h> |
25 | #include <linux/io.h> |
26 | #include <linux/iopoll.h> |
27 | #include <linux/kernel.h> |
28 | #include <linux/mod_devicetable.h> |
29 | #include <linux/module.h> |
30 | #include <linux/of_mdio.h> |
31 | #include <linux/phy.h> |
32 | #include <linux/platform_device.h> |
33 | #include <linux/sched.h> |
34 | #include <linux/wait.h> |
35 | |
36 | #define MVMDIO_SMI_DATA_SHIFT 0 |
37 | #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 |
38 | #define MVMDIO_SMI_PHY_REG_SHIFT 21 |
39 | #define MVMDIO_SMI_READ_OPERATION BIT(26) |
40 | #define MVMDIO_SMI_WRITE_OPERATION 0 |
41 | #define MVMDIO_SMI_READ_VALID BIT(27) |
42 | #define MVMDIO_SMI_BUSY BIT(28) |
43 | #define MVMDIO_ERR_INT_CAUSE 0x007C |
44 | #define MVMDIO_ERR_INT_SMI_DONE 0x00000010 |
45 | #define MVMDIO_ERR_INT_MASK 0x0080 |
46 | |
47 | #define MVMDIO_XSMI_MGNT_REG 0x0 |
48 | #define MVMDIO_XSMI_PHYADDR_SHIFT 16 |
49 | #define MVMDIO_XSMI_DEVADDR_SHIFT 21 |
50 | #define MVMDIO_XSMI_WRITE_OPERATION (0x5 << 26) |
51 | #define MVMDIO_XSMI_READ_OPERATION (0x7 << 26) |
52 | #define MVMDIO_XSMI_READ_VALID BIT(29) |
53 | #define MVMDIO_XSMI_BUSY BIT(30) |
54 | #define MVMDIO_XSMI_ADDR_REG 0x8 |
55 | |
56 | #define MVMDIO_XSMI_CFG_REG 0xc |
57 | #define MVMDIO_XSMI_CLKDIV_MASK 0x3 |
58 | #define MVMDIO_XSMI_CLKDIV_256 0x0 |
59 | #define MVMDIO_XSMI_CLKDIV_64 0x1 |
60 | #define MVMDIO_XSMI_CLKDIV_32 0x2 |
61 | #define MVMDIO_XSMI_CLKDIV_8 0x3 |
62 | |
63 | /* |
64 | * SMI Timeout measurements: |
65 | * - Kirkwood 88F6281 (Globalscale Dreamplug): 45us to 95us (Interrupt) |
66 | * - Armada 370 (Globalscale Mirabox): 41us to 43us (Polled) |
67 | */ |
68 | #define MVMDIO_SMI_TIMEOUT 1000 /* 1000us = 1ms */ |
69 | |
70 | struct orion_mdio_dev { |
71 | void __iomem *regs; |
72 | struct clk *clk[4]; |
73 | /* |
74 | * If we have access to the error interrupt pin (which is |
75 | * somewhat misnamed as it not only reflects internal errors |
76 | * but also reflects SMI completion), use that to wait for |
77 | * SMI access completion instead of polling the SMI busy bit. |
78 | */ |
79 | int err_interrupt; |
80 | wait_queue_head_t smi_busy_wait; |
81 | }; |
82 | |
83 | enum orion_mdio_bus_type { |
84 | BUS_TYPE_SMI, |
85 | BUS_TYPE_XSMI |
86 | }; |
87 | |
88 | struct orion_mdio_ops { |
89 | int (*is_done)(struct orion_mdio_dev *); |
90 | }; |
91 | |
92 | /* Wait for the SMI unit to be ready for another operation |
93 | */ |
94 | static int orion_mdio_wait_ready(const struct orion_mdio_ops *ops, |
95 | struct mii_bus *bus) |
96 | { |
97 | struct orion_mdio_dev *dev = bus->priv; |
98 | unsigned long timeout; |
99 | int done; |
100 | |
101 | if (dev->err_interrupt <= 0) { |
102 | if (!read_poll_timeout_atomic(ops->is_done, done, done, 2, |
103 | MVMDIO_SMI_TIMEOUT, false, dev)) |
104 | return 0; |
105 | } else { |
106 | /* wait_event_timeout does not guarantee a delay of at |
107 | * least one whole jiffie, so timeout must be no less |
108 | * than two. |
109 | */ |
110 | timeout = max(usecs_to_jiffies(MVMDIO_SMI_TIMEOUT), 2); |
111 | |
112 | if (wait_event_timeout(dev->smi_busy_wait, |
113 | ops->is_done(dev), timeout)) |
114 | return 0; |
115 | } |
116 | |
117 | dev_err(bus->parent, "Timeout: SMI busy for too long\n" ); |
118 | return -ETIMEDOUT; |
119 | } |
120 | |
121 | static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) |
122 | { |
123 | return !(readl(addr: dev->regs) & MVMDIO_SMI_BUSY); |
124 | } |
125 | |
126 | static const struct orion_mdio_ops orion_mdio_smi_ops = { |
127 | .is_done = orion_mdio_smi_is_done, |
128 | }; |
129 | |
130 | static int orion_mdio_smi_read(struct mii_bus *bus, int mii_id, |
131 | int regnum) |
132 | { |
133 | struct orion_mdio_dev *dev = bus->priv; |
134 | u32 val; |
135 | int ret; |
136 | |
137 | ret = orion_mdio_wait_ready(ops: &orion_mdio_smi_ops, bus); |
138 | if (ret < 0) |
139 | return ret; |
140 | |
141 | writel(val: ((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | |
142 | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | |
143 | MVMDIO_SMI_READ_OPERATION), |
144 | addr: dev->regs); |
145 | |
146 | ret = orion_mdio_wait_ready(ops: &orion_mdio_smi_ops, bus); |
147 | if (ret < 0) |
148 | return ret; |
149 | |
150 | val = readl(addr: dev->regs); |
151 | if (!(val & MVMDIO_SMI_READ_VALID)) { |
152 | dev_err(bus->parent, "SMI bus read not valid\n" ); |
153 | return -ENODEV; |
154 | } |
155 | |
156 | return val & GENMASK(15, 0); |
157 | } |
158 | |
159 | static int orion_mdio_smi_write(struct mii_bus *bus, int mii_id, |
160 | int regnum, u16 value) |
161 | { |
162 | struct orion_mdio_dev *dev = bus->priv; |
163 | int ret; |
164 | |
165 | ret = orion_mdio_wait_ready(ops: &orion_mdio_smi_ops, bus); |
166 | if (ret < 0) |
167 | return ret; |
168 | |
169 | writel(val: ((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) | |
170 | (regnum << MVMDIO_SMI_PHY_REG_SHIFT) | |
171 | MVMDIO_SMI_WRITE_OPERATION | |
172 | (value << MVMDIO_SMI_DATA_SHIFT)), |
173 | addr: dev->regs); |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static int orion_mdio_xsmi_is_done(struct orion_mdio_dev *dev) |
179 | { |
180 | return !(readl(addr: dev->regs + MVMDIO_XSMI_MGNT_REG) & MVMDIO_XSMI_BUSY); |
181 | } |
182 | |
183 | static const struct orion_mdio_ops orion_mdio_xsmi_ops = { |
184 | .is_done = orion_mdio_xsmi_is_done, |
185 | }; |
186 | |
187 | static int orion_mdio_xsmi_read_c45(struct mii_bus *bus, int mii_id, |
188 | int dev_addr, int regnum) |
189 | { |
190 | struct orion_mdio_dev *dev = bus->priv; |
191 | int ret; |
192 | |
193 | ret = orion_mdio_wait_ready(ops: &orion_mdio_xsmi_ops, bus); |
194 | if (ret < 0) |
195 | return ret; |
196 | |
197 | writel(val: regnum, addr: dev->regs + MVMDIO_XSMI_ADDR_REG); |
198 | writel(val: (mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | |
199 | (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | |
200 | MVMDIO_XSMI_READ_OPERATION, |
201 | addr: dev->regs + MVMDIO_XSMI_MGNT_REG); |
202 | |
203 | ret = orion_mdio_wait_ready(ops: &orion_mdio_xsmi_ops, bus); |
204 | if (ret < 0) |
205 | return ret; |
206 | |
207 | if (!(readl(addr: dev->regs + MVMDIO_XSMI_MGNT_REG) & |
208 | MVMDIO_XSMI_READ_VALID)) { |
209 | dev_err(bus->parent, "XSMI bus read not valid\n" ); |
210 | return -ENODEV; |
211 | } |
212 | |
213 | return readl(addr: dev->regs + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0); |
214 | } |
215 | |
216 | static int orion_mdio_xsmi_write_c45(struct mii_bus *bus, int mii_id, |
217 | int dev_addr, int regnum, u16 value) |
218 | { |
219 | struct orion_mdio_dev *dev = bus->priv; |
220 | int ret; |
221 | |
222 | ret = orion_mdio_wait_ready(ops: &orion_mdio_xsmi_ops, bus); |
223 | if (ret < 0) |
224 | return ret; |
225 | |
226 | writel(val: regnum, addr: dev->regs + MVMDIO_XSMI_ADDR_REG); |
227 | writel(val: (mii_id << MVMDIO_XSMI_PHYADDR_SHIFT) | |
228 | (dev_addr << MVMDIO_XSMI_DEVADDR_SHIFT) | |
229 | MVMDIO_XSMI_WRITE_OPERATION | value, |
230 | addr: dev->regs + MVMDIO_XSMI_MGNT_REG); |
231 | |
232 | return 0; |
233 | } |
234 | |
235 | static void orion_mdio_xsmi_set_mdc_freq(struct mii_bus *bus) |
236 | { |
237 | struct orion_mdio_dev *dev = bus->priv; |
238 | struct clk *mg_core; |
239 | u32 div, freq, cfg; |
240 | |
241 | if (device_property_read_u32(dev: bus->parent, propname: "clock-frequency" , val: &freq)) |
242 | return; |
243 | |
244 | mg_core = of_clk_get_by_name(np: bus->parent->of_node, name: "mg_core_clk" ); |
245 | if (IS_ERR(ptr: mg_core)) { |
246 | dev_err(bus->parent, |
247 | "MG core clock unknown, not changing MDC frequency" ); |
248 | return; |
249 | } |
250 | |
251 | div = clk_get_rate(clk: mg_core) / (freq + 1) + 1; |
252 | clk_put(clk: mg_core); |
253 | |
254 | if (div <= 8) |
255 | div = MVMDIO_XSMI_CLKDIV_8; |
256 | else if (div <= 32) |
257 | div = MVMDIO_XSMI_CLKDIV_32; |
258 | else if (div <= 64) |
259 | div = MVMDIO_XSMI_CLKDIV_64; |
260 | else |
261 | div = MVMDIO_XSMI_CLKDIV_256; |
262 | |
263 | cfg = readl(addr: dev->regs + MVMDIO_XSMI_CFG_REG); |
264 | cfg &= ~MVMDIO_XSMI_CLKDIV_MASK; |
265 | cfg |= div; |
266 | writel(val: cfg, addr: dev->regs + MVMDIO_XSMI_CFG_REG); |
267 | } |
268 | |
269 | static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) |
270 | { |
271 | struct orion_mdio_dev *dev = dev_id; |
272 | |
273 | if (readl(addr: dev->regs + MVMDIO_ERR_INT_CAUSE) & |
274 | MVMDIO_ERR_INT_SMI_DONE) { |
275 | writel(val: ~MVMDIO_ERR_INT_SMI_DONE, |
276 | addr: dev->regs + MVMDIO_ERR_INT_CAUSE); |
277 | wake_up(&dev->smi_busy_wait); |
278 | return IRQ_HANDLED; |
279 | } |
280 | |
281 | return IRQ_NONE; |
282 | } |
283 | |
284 | static int orion_mdio_probe(struct platform_device *pdev) |
285 | { |
286 | enum orion_mdio_bus_type type; |
287 | struct resource *r; |
288 | struct mii_bus *bus; |
289 | struct orion_mdio_dev *dev; |
290 | int i, ret; |
291 | |
292 | type = (uintptr_t)device_get_match_data(dev: &pdev->dev); |
293 | |
294 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
295 | if (!r) { |
296 | dev_err(&pdev->dev, "No SMI register address given\n" ); |
297 | return -ENODEV; |
298 | } |
299 | |
300 | bus = devm_mdiobus_alloc_size(dev: &pdev->dev, |
301 | sizeof_priv: sizeof(struct orion_mdio_dev)); |
302 | if (!bus) |
303 | return -ENOMEM; |
304 | |
305 | switch (type) { |
306 | case BUS_TYPE_SMI: |
307 | bus->read = orion_mdio_smi_read; |
308 | bus->write = orion_mdio_smi_write; |
309 | break; |
310 | case BUS_TYPE_XSMI: |
311 | bus->read_c45 = orion_mdio_xsmi_read_c45; |
312 | bus->write_c45 = orion_mdio_xsmi_write_c45; |
313 | break; |
314 | } |
315 | |
316 | bus->name = "orion_mdio_bus" ; |
317 | snprintf(buf: bus->id, MII_BUS_ID_SIZE, fmt: "%s-mii" , |
318 | dev_name(dev: &pdev->dev)); |
319 | bus->parent = &pdev->dev; |
320 | |
321 | dev = bus->priv; |
322 | dev->regs = devm_ioremap(dev: &pdev->dev, offset: r->start, size: resource_size(res: r)); |
323 | if (!dev->regs) { |
324 | dev_err(&pdev->dev, "Unable to remap SMI register\n" ); |
325 | return -ENODEV; |
326 | } |
327 | |
328 | init_waitqueue_head(&dev->smi_busy_wait); |
329 | |
330 | if (pdev->dev.of_node) { |
331 | for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { |
332 | dev->clk[i] = of_clk_get(np: pdev->dev.of_node, index: i); |
333 | if (PTR_ERR(ptr: dev->clk[i]) == -EPROBE_DEFER) { |
334 | ret = -EPROBE_DEFER; |
335 | goto out_clk; |
336 | } |
337 | if (IS_ERR(ptr: dev->clk[i])) |
338 | break; |
339 | clk_prepare_enable(clk: dev->clk[i]); |
340 | } |
341 | |
342 | if (!IS_ERR(ptr: of_clk_get(np: pdev->dev.of_node, |
343 | ARRAY_SIZE(dev->clk)))) |
344 | dev_warn(&pdev->dev, |
345 | "unsupported number of clocks, limiting to the first " |
346 | __stringify(ARRAY_SIZE(dev->clk)) "\n" ); |
347 | |
348 | if (type == BUS_TYPE_XSMI) |
349 | orion_mdio_xsmi_set_mdc_freq(bus); |
350 | } else { |
351 | dev->clk[0] = clk_get(dev: &pdev->dev, NULL); |
352 | if (PTR_ERR(ptr: dev->clk[0]) == -EPROBE_DEFER) { |
353 | ret = -EPROBE_DEFER; |
354 | goto out_clk; |
355 | } |
356 | if (!IS_ERR(ptr: dev->clk[0])) |
357 | clk_prepare_enable(clk: dev->clk[0]); |
358 | } |
359 | |
360 | |
361 | dev->err_interrupt = platform_get_irq_optional(pdev, 0); |
362 | if (dev->err_interrupt > 0 && |
363 | resource_size(res: r) < MVMDIO_ERR_INT_MASK + 4) { |
364 | dev_err(&pdev->dev, |
365 | "disabling interrupt, resource size is too small\n" ); |
366 | dev->err_interrupt = 0; |
367 | } |
368 | if (dev->err_interrupt > 0) { |
369 | ret = devm_request_irq(dev: &pdev->dev, irq: dev->err_interrupt, |
370 | handler: orion_mdio_err_irq, |
371 | IRQF_SHARED, devname: pdev->name, dev_id: dev); |
372 | if (ret) |
373 | goto out_mdio; |
374 | |
375 | writel(MVMDIO_ERR_INT_SMI_DONE, |
376 | addr: dev->regs + MVMDIO_ERR_INT_MASK); |
377 | |
378 | } else if (dev->err_interrupt == -EPROBE_DEFER) { |
379 | ret = -EPROBE_DEFER; |
380 | goto out_mdio; |
381 | } |
382 | |
383 | /* For the platforms not supporting DT/ACPI fall-back |
384 | * to mdiobus_register via of_mdiobus_register. |
385 | */ |
386 | if (is_acpi_node(fwnode: pdev->dev.fwnode)) |
387 | ret = acpi_mdiobus_register(mdio: bus, handle: pdev->dev.fwnode); |
388 | else |
389 | ret = of_mdiobus_register(mdio: bus, np: pdev->dev.of_node); |
390 | if (ret < 0) { |
391 | dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n" , ret); |
392 | goto out_mdio; |
393 | } |
394 | |
395 | platform_set_drvdata(pdev, data: bus); |
396 | |
397 | return 0; |
398 | |
399 | out_mdio: |
400 | if (dev->err_interrupt > 0) |
401 | writel(val: 0, addr: dev->regs + MVMDIO_ERR_INT_MASK); |
402 | |
403 | out_clk: |
404 | for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { |
405 | if (IS_ERR(ptr: dev->clk[i])) |
406 | break; |
407 | clk_disable_unprepare(clk: dev->clk[i]); |
408 | clk_put(clk: dev->clk[i]); |
409 | } |
410 | |
411 | return ret; |
412 | } |
413 | |
414 | static void orion_mdio_remove(struct platform_device *pdev) |
415 | { |
416 | struct mii_bus *bus = platform_get_drvdata(pdev); |
417 | struct orion_mdio_dev *dev = bus->priv; |
418 | int i; |
419 | |
420 | if (dev->err_interrupt > 0) |
421 | writel(val: 0, addr: dev->regs + MVMDIO_ERR_INT_MASK); |
422 | mdiobus_unregister(bus); |
423 | |
424 | for (i = 0; i < ARRAY_SIZE(dev->clk); i++) { |
425 | if (IS_ERR(ptr: dev->clk[i])) |
426 | break; |
427 | clk_disable_unprepare(clk: dev->clk[i]); |
428 | clk_put(clk: dev->clk[i]); |
429 | } |
430 | } |
431 | |
432 | static const struct of_device_id orion_mdio_match[] = { |
433 | { .compatible = "marvell,orion-mdio" , .data = (void *)BUS_TYPE_SMI }, |
434 | { .compatible = "marvell,xmdio" , .data = (void *)BUS_TYPE_XSMI }, |
435 | { } |
436 | }; |
437 | MODULE_DEVICE_TABLE(of, orion_mdio_match); |
438 | |
439 | #ifdef CONFIG_ACPI |
440 | static const struct acpi_device_id orion_mdio_acpi_match[] = { |
441 | { "MRVL0100" , BUS_TYPE_SMI }, |
442 | { "MRVL0101" , BUS_TYPE_XSMI }, |
443 | { }, |
444 | }; |
445 | MODULE_DEVICE_TABLE(acpi, orion_mdio_acpi_match); |
446 | #endif |
447 | |
448 | static struct platform_driver orion_mdio_driver = { |
449 | .probe = orion_mdio_probe, |
450 | .remove_new = orion_mdio_remove, |
451 | .driver = { |
452 | .name = "orion-mdio" , |
453 | .of_match_table = orion_mdio_match, |
454 | .acpi_match_table = ACPI_PTR(orion_mdio_acpi_match), |
455 | }, |
456 | }; |
457 | |
458 | module_platform_driver(orion_mdio_driver); |
459 | |
460 | MODULE_DESCRIPTION("Marvell MDIO interface driver" ); |
461 | MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>" ); |
462 | MODULE_LICENSE("GPL" ); |
463 | MODULE_ALIAS("platform:orion-mdio" ); |
464 | |