Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Florian Fainelli and committed by
David S. Miller
2ec98521 3712b717

+83 -18
+3
Documentation/devicetree/bindings/net/marvell-orion-mdio.txt
··· 9 9 - compatible: "marvell,orion-mdio" 10 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 15 The child nodes of the MDIO driver are the individual PHY devices 13 16 connected to this MDIO bus. They must have a "reg" property given the 14 17 PHY address on the MDIO bus.
+80 -18
drivers/net/ethernet/marvell/mvmdio.c
··· 24 24 #include <linux/module.h> 25 25 #include <linux/mutex.h> 26 26 #include <linux/phy.h> 27 + #include <linux/interrupt.h> 27 28 #include <linux/platform_device.h> 28 29 #include <linux/delay.h> 29 30 #include <linux/io.h> 30 31 #include <linux/of_mdio.h> 32 + #include <linux/sched.h> 33 + #include <linux/wait.h> 31 34 32 35 #define MVMDIO_SMI_DATA_SHIFT 0 33 36 #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 ··· 39 36 #define MVMDIO_SMI_WRITE_OPERATION 0 40 37 #define MVMDIO_SMI_READ_VALID BIT(27) 41 38 #define MVMDIO_SMI_BUSY BIT(28) 39 + #define MVMDIO_ERR_INT_CAUSE 0x007C 40 + #define MVMDIO_ERR_INT_SMI_DONE 0x00000010 41 + #define MVMDIO_ERR_INT_MASK 0x0080 42 42 43 43 struct orion_mdio_dev { 44 44 struct mutex lock; 45 45 void __iomem *regs; 46 + /* 47 + * If we have access to the error interrupt pin (which is 48 + * somewhat misnamed as it not only reflects internal errors 49 + * but also reflects SMI completion), use that to wait for 50 + * SMI access completion instead of polling the SMI busy bit. 51 + */ 52 + int err_interrupt; 53 + wait_queue_head_t smi_busy_wait; 46 54 }; 55 + 56 + static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) 57 + { 58 + return !(readl(dev->regs) & MVMDIO_SMI_BUSY); 59 + } 47 60 48 61 /* Wait for the SMI unit to be ready for another operation 49 62 */ ··· 67 48 { 68 49 struct orion_mdio_dev *dev = bus->priv; 69 50 int count; 70 - u32 val; 71 51 72 - count = 0; 73 - while (1) { 74 - val = readl(dev->regs); 75 - if (!(val & MVMDIO_SMI_BUSY)) 76 - break; 52 + if (dev->err_interrupt <= 0) { 53 + count = 0; 54 + while (1) { 55 + if (orion_mdio_smi_is_done(dev)) 56 + break; 77 57 78 - if (count > 100) { 79 - dev_err(bus->parent, "Timeout: SMI busy for too long\n"); 80 - return -ETIMEDOUT; 58 + if (count > 100) { 59 + dev_err(bus->parent, 60 + "Timeout: SMI busy for too long\n"); 61 + return -ETIMEDOUT; 62 + } 63 + 64 + udelay(10); 65 + count++; 81 66 } 82 - 83 - udelay(10); 84 - count++; 67 + } else { 68 + if (!orion_mdio_smi_is_done(dev)) { 69 + wait_event_timeout(dev->smi_busy_wait, 70 + orion_mdio_smi_is_done(dev), 71 + msecs_to_jiffies(100)); 72 + if (!orion_mdio_smi_is_done(dev)) 73 + return -ETIMEDOUT; 74 + } 85 75 } 86 76 87 77 return 0; ··· 169 141 return 0; 170 142 } 171 143 144 + static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) 145 + { 146 + struct orion_mdio_dev *dev = dev_id; 147 + 148 + if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) & 149 + MVMDIO_ERR_INT_SMI_DONE) { 150 + writel(~MVMDIO_ERR_INT_SMI_DONE, 151 + dev->regs + MVMDIO_ERR_INT_CAUSE); 152 + wake_up(&dev->smi_busy_wait); 153 + return IRQ_HANDLED; 154 + } 155 + 156 + return IRQ_NONE; 157 + } 158 + 172 159 static int orion_mdio_probe(struct platform_device *pdev) 173 160 { 174 161 struct resource *r; ··· 224 181 dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r)); 225 182 if (!dev->regs) { 226 183 dev_err(&pdev->dev, "Unable to remap SMI register\n"); 227 - kfree(bus->irq); 228 - mdiobus_free(bus); 229 - return -ENODEV; 184 + ret = -ENODEV; 185 + goto out_mdio; 186 + } 187 + 188 + init_waitqueue_head(&dev->smi_busy_wait); 189 + 190 + dev->err_interrupt = platform_get_irq(pdev, 0); 191 + if (dev->err_interrupt != -ENXIO) { 192 + ret = devm_request_irq(&pdev->dev, dev->err_interrupt, 193 + orion_mdio_err_irq, 194 + IRQF_SHARED, pdev->name, dev); 195 + if (ret) 196 + goto out_mdio; 197 + 198 + writel(MVMDIO_ERR_INT_SMI_DONE, 199 + dev->regs + MVMDIO_ERR_INT_MASK); 230 200 } 231 201 232 202 mutex_init(&dev->lock); ··· 250 194 ret = mdiobus_register(bus); 251 195 if (ret < 0) { 252 196 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret); 253 - kfree(bus->irq); 254 - mdiobus_free(bus); 255 - return ret; 197 + goto out_mdio; 256 198 } 257 199 258 200 platform_set_drvdata(pdev, bus); 259 201 260 202 return 0; 203 + 204 + out_mdio: 205 + kfree(bus->irq); 206 + mdiobus_free(bus); 207 + return ret; 261 208 } 262 209 263 210 static int orion_mdio_remove(struct platform_device *pdev) 264 211 { 265 212 struct mii_bus *bus = platform_get_drvdata(pdev); 213 + struct orion_mdio_dev *dev = bus->priv; 214 + 215 + writel(0, dev->regs + MVMDIO_ERR_INT_MASK); 266 216 mdiobus_unregister(bus); 267 217 kfree(bus->irq); 268 218 mdiobus_free(bus);