diff options
author | Florian Fainelli <florian@openwrt.org> | 2013-03-21 23:39:27 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-22 10:25:15 -0400 |
commit | 2ec985213864cb64c45dc0284d7316142eefb5d4 (patch) | |
tree | 376fb2e4758ab5e7a75f8ec935cf2f24074f671f | |
parent | 3712b71769578fd39481ce02e1e8cea3c4f8370f (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.txt | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/marvell/mvmdio.c | 98 |
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 | ||
12 | Optional properties: | ||
13 | - interrupts: interrupt line number for the SMI error/done interrupt | ||
14 | |||
12 | The child nodes of the MDIO driver are the individual PHY devices | 15 | The child nodes of the MDIO driver are the individual PHY devices |
13 | connected to this MDIO bus. They must have a "reg" property given the | 16 | connected to this MDIO bus. They must have a "reg" property given the |
14 | PHY address on the MDIO bus. | 17 | PHY 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 | ||
40 | struct orion_mdio_dev { | 46 | struct 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 | ||
59 | static 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 | */ |
47 | static int orion_mdio_wait_ready(struct mii_bus *bus) | 66 | static 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 | ||
172 | static 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 | |||
144 | static int orion_mdio_probe(struct platform_device *pdev) | 187 | static 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 | |||
260 | out_mdio: | ||
261 | kfree(bus->irq); | ||
262 | mdiobus_free(bus); | ||
263 | return ret; | ||
205 | } | 264 | } |
206 | 265 | ||
207 | static int orion_mdio_remove(struct platform_device *pdev) | 266 | static 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); |