aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Fainelli <florian@openwrt.org>2013-03-21 23:39:27 -0400
committerDavid S. Miller <davem@davemloft.net>2013-03-22 10:25:15 -0400
commit2ec985213864cb64c45dc0284d7316142eefb5d4 (patch)
tree376fb2e4758ab5e7a75f8ec935cf2f24074f671f
parent3712b71769578fd39481ce02e1e8cea3c4f8370f (diff)
net: mvmdio: enhance driver to support SMI error/done interrupts
This patch enhances the "mvmdio" to support a SMI error/done interrupt line which can be used along with a wait queue instead of doing busy-waiting on the registers. This is a feature which is available in the mv643xx_eth SMI code and thus reduces again the gap between the two. Signed-off-by: Florian Fainelli <florian@openwrt.org> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/devicetree/bindings/net/marvell-orion-mdio.txt3
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c98
2 files changed, 83 insertions, 18 deletions
diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
index 34e7aafa321c..052b5f28a624 100644
--- a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
+++ b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
@@ -9,6 +9,9 @@ Required properties:
9- compatible: "marvell,orion-mdio" 9- compatible: "marvell,orion-mdio"
10- reg: address and length of the SMI register 10- reg: address and length of the SMI register
11 11
12Optional properties:
13- interrupts: interrupt line number for the SMI error/done interrupt
14
12The child nodes of the MDIO driver are the individual PHY devices 15The child nodes of the MDIO driver are the individual PHY devices
13connected to this MDIO bus. They must have a "reg" property given the 16connected to this MDIO bus. They must have a "reg" property given the
14PHY address on the MDIO bus. 17PHY address on the MDIO bus.
diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c
index 3e2711d22451..3472574602b2 100644
--- a/drivers/net/ethernet/marvell/mvmdio.c
+++ b/drivers/net/ethernet/marvell/mvmdio.c
@@ -24,10 +24,13 @@
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/mutex.h> 25#include <linux/mutex.h>
26#include <linux/phy.h> 26#include <linux/phy.h>
27#include <linux/interrupt.h>
27#include <linux/platform_device.h> 28#include <linux/platform_device.h>
28#include <linux/delay.h> 29#include <linux/delay.h>
29#include <linux/io.h> 30#include <linux/io.h>
30#include <linux/of_mdio.h> 31#include <linux/of_mdio.h>
32#include <linux/sched.h>
33#include <linux/wait.h>
31 34
32#define MVMDIO_SMI_DATA_SHIFT 0 35#define MVMDIO_SMI_DATA_SHIFT 0
33#define MVMDIO_SMI_PHY_ADDR_SHIFT 16 36#define MVMDIO_SMI_PHY_ADDR_SHIFT 16
@@ -36,33 +39,58 @@
36#define MVMDIO_SMI_WRITE_OPERATION 0 39#define MVMDIO_SMI_WRITE_OPERATION 0
37#define MVMDIO_SMI_READ_VALID BIT(27) 40#define MVMDIO_SMI_READ_VALID BIT(27)
38#define MVMDIO_SMI_BUSY BIT(28) 41#define MVMDIO_SMI_BUSY BIT(28)
42#define MVMDIO_ERR_INT_CAUSE 0x007C
43#define MVMDIO_ERR_INT_SMI_DONE 0x00000010
44#define MVMDIO_ERR_INT_MASK 0x0080
39 45
40struct orion_mdio_dev { 46struct orion_mdio_dev {
41 struct mutex lock; 47 struct mutex lock;
42 void __iomem *regs; 48 void __iomem *regs;
49 /*
50 * If we have access to the error interrupt pin (which is
51 * somewhat misnamed as it not only reflects internal errors
52 * but also reflects SMI completion), use that to wait for
53 * SMI access completion instead of polling the SMI busy bit.
54 */
55 int err_interrupt;
56 wait_queue_head_t smi_busy_wait;
43}; 57};
44 58
59static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev)
60{
61 return !(readl(dev->regs) & MVMDIO_SMI_BUSY);
62}
63
45/* Wait for the SMI unit to be ready for another operation 64/* Wait for the SMI unit to be ready for another operation
46 */ 65 */
47static int orion_mdio_wait_ready(struct mii_bus *bus) 66static int orion_mdio_wait_ready(struct mii_bus *bus)
48{ 67{
49 struct orion_mdio_dev *dev = bus->priv; 68 struct orion_mdio_dev *dev = bus->priv;
50 int count; 69 int count;
51 u32 val;
52 70
53 count = 0; 71 if (dev->err_interrupt <= 0) {
54 while (1) { 72 count = 0;
55 val = readl(dev->regs); 73 while (1) {
56 if (!(val & MVMDIO_SMI_BUSY)) 74 if (orion_mdio_smi_is_done(dev))
57 break; 75 break;
58 76
59 if (count > 100) { 77 if (count > 100) {
60 dev_err(bus->parent, "Timeout: SMI busy for too long\n"); 78 dev_err(bus->parent,
61 return -ETIMEDOUT; 79 "Timeout: SMI busy for too long\n");
62 } 80 return -ETIMEDOUT;
81 }
63 82
64 udelay(10); 83 udelay(10);
65 count++; 84 count++;
85 }
86 } else {
87 if (!orion_mdio_smi_is_done(dev)) {
88 wait_event_timeout(dev->smi_busy_wait,
89 orion_mdio_smi_is_done(dev),
90 msecs_to_jiffies(100));
91 if (!orion_mdio_smi_is_done(dev))
92 return -ETIMEDOUT;
93 }
66 } 94 }
67 95
68 return 0; 96 return 0;
@@ -141,6 +169,21 @@ static int orion_mdio_reset(struct mii_bus *bus)
141 return 0; 169 return 0;
142} 170}
143 171
172static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id)
173{
174 struct orion_mdio_dev *dev = dev_id;
175
176 if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) &
177 MVMDIO_ERR_INT_SMI_DONE) {
178 writel(~MVMDIO_ERR_INT_SMI_DONE,
179 dev->regs + MVMDIO_ERR_INT_CAUSE);
180 wake_up(&dev->smi_busy_wait);
181 return IRQ_HANDLED;
182 }
183
184 return IRQ_NONE;
185}
186
144static int orion_mdio_probe(struct platform_device *pdev) 187static int orion_mdio_probe(struct platform_device *pdev)
145{ 188{
146 struct resource *r; 189 struct resource *r;
@@ -181,9 +224,22 @@ static int orion_mdio_probe(struct platform_device *pdev)
181 dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 224 dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
182 if (!dev->regs) { 225 if (!dev->regs) {
183 dev_err(&pdev->dev, "Unable to remap SMI register\n"); 226 dev_err(&pdev->dev, "Unable to remap SMI register\n");
184 kfree(bus->irq); 227 ret = -ENODEV;
185 mdiobus_free(bus); 228 goto out_mdio;
186 return -ENODEV; 229 }
230
231 init_waitqueue_head(&dev->smi_busy_wait);
232
233 dev->err_interrupt = platform_get_irq(pdev, 0);
234 if (dev->err_interrupt != -ENXIO) {
235 ret = devm_request_irq(&pdev->dev, dev->err_interrupt,
236 orion_mdio_err_irq,
237 IRQF_SHARED, pdev->name, dev);
238 if (ret)
239 goto out_mdio;
240
241 writel(MVMDIO_ERR_INT_SMI_DONE,
242 dev->regs + MVMDIO_ERR_INT_MASK);
187 } 243 }
188 244
189 mutex_init(&dev->lock); 245 mutex_init(&dev->lock);
@@ -194,19 +250,25 @@ static int orion_mdio_probe(struct platform_device *pdev)
194 ret = mdiobus_register(bus); 250 ret = mdiobus_register(bus);
195 if (ret < 0) { 251 if (ret < 0) {
196 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); 252 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
197 kfree(bus->irq); 253 goto out_mdio;
198 mdiobus_free(bus);
199 return ret;
200 } 254 }
201 255
202 platform_set_drvdata(pdev, bus); 256 platform_set_drvdata(pdev, bus);
203 257
204 return 0; 258 return 0;
259
260out_mdio:
261 kfree(bus->irq);
262 mdiobus_free(bus);
263 return ret;
205} 264}
206 265
207static int orion_mdio_remove(struct platform_device *pdev) 266static int orion_mdio_remove(struct platform_device *pdev)
208{ 267{
209 struct mii_bus *bus = platform_get_drvdata(pdev); 268 struct mii_bus *bus = platform_get_drvdata(pdev);
269 struct orion_mdio_dev *dev = bus->priv;
270
271 writel(0, dev->regs + MVMDIO_ERR_INT_MASK);
210 mdiobus_unregister(bus); 272 mdiobus_unregister(bus);
211 kfree(bus->irq); 273 kfree(bus->irq);
212 mdiobus_free(bus); 274 mdiobus_free(bus);