aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/marvell/mvmdio.c
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 /drivers/net/ethernet/marvell/mvmdio.c
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>
Diffstat (limited to 'drivers/net/ethernet/marvell/mvmdio.c')
-rw-r--r--drivers/net/ethernet/marvell/mvmdio.c98
1 files changed, 80 insertions, 18 deletions
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);