Re: [PATCH 3/4 v2] net: mvmdio: enhance driver to support SMI error/done interrupts
On Thu, Mar 14, 2013 at 07:08:34PM +0100, Florian Fainelli wrote: > + if (dev->err_interrupt == NO_IRQ) { ... > + init_waitqueue_head(>smi_busy_wait); > + > + dev->err_interrupt = platform_get_irq(pdev, 0); > + if (dev->err_interrupt != -ENXIO) { ... > + } else > + dev->err_interrupt = NO_IRQ; FYI, NO_IRQ is not supposed to be used anymore (we're supposed to be removing it). platform_get_irq() returns negative numbers for failure, so why not test for < 0 in both the above tests, or maybe <= 0 (as IRQ0 is also not supposed to be valid.)? -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 3/4 v2] net: mvmdio: enhance driver to support SMI error/done interrupts
On Thu, Mar 14, 2013 at 07:08:34PM +0100, Florian Fainelli wrote: + if (dev-err_interrupt == NO_IRQ) { ... + init_waitqueue_head(dev-smi_busy_wait); + + dev-err_interrupt = platform_get_irq(pdev, 0); + if (dev-err_interrupt != -ENXIO) { ... + } else + dev-err_interrupt = NO_IRQ; FYI, NO_IRQ is not supposed to be used anymore (we're supposed to be removing it). platform_get_irq() returns negative numbers for failure, so why not test for 0 in both the above tests, or maybe = 0 (as IRQ0 is also not supposed to be valid.)? -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 3/4 v2] 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 --- Chances since v1: - always use orion_smi_is_done() helper - make interrupt/non-interrupt code path entirely independant .../devicetree/bindings/net/marvell-orion-mdio.txt |3 + drivers/net/ethernet/marvell/mvmdio.c | 83 +--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt index 34e7aaf..052b5f2 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: - compatible: "marvell,orion-mdio" - reg: address and length of the SMI register +Optional properties: +- interrupts: interrupt line number for the SMI error/done interrupt + The child nodes of the MDIO driver are the individual PHY devices connected to this MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus. diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 595deea..7ac83de 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -24,9 +24,12 @@ #include #include #include +#include #include #include #include +#include +#include #define MVMDIO_SMI_DATA_SHIFT 0 #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 @@ -35,33 +38,58 @@ #define MVMDIO_SMI_WRITE_OPERATION 0 #define MVMDIO_SMI_READ_VALID BIT(27) #define MVMDIO_SMI_BUSYBIT(28) +#define MVMDIO_ERR_INT_CAUSE 0x007C +#define MVMDIO_ERR_INT_SMI_DONE 0x0010 +#define MVMDIO_ERR_INT_MASK 0x0080 struct orion_mdio_dev { struct mutex lock; void __iomem *regs; + /* +* If we have access to the error interrupt pin (which is +* somewhat misnamed as it not only reflects internal errors +* but also reflects SMI completion), use that to wait for +* SMI access completion instead of polling the SMI busy bit. +*/ + int err_interrupt; + wait_queue_head_t smi_busy_wait; }; +static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) +{ + return !(readl(dev->regs) & MVMDIO_SMI_BUSY); +} + /* Wait for the SMI unit to be ready for another operation */ static int orion_mdio_wait_ready(struct mii_bus *bus) { struct orion_mdio_dev *dev = bus->priv; int count; - u32 val; - count = 0; - while (1) { - val = readl(dev->regs); - if (!(val & MVMDIO_SMI_BUSY)) - break; + if (dev->err_interrupt == NO_IRQ) { + count = 0; + while (1) { + if (orion_mdio_smi_is_done(dev)) + break; - if (count > 100) { - dev_err(bus->parent, "Timeout: SMI busy for too long\n"); - return -ETIMEDOUT; - } + if (count > 100) { + dev_err(bus->parent, + "Timeout: SMI busy for too long\n"); + return -ETIMEDOUT; + } - udelay(10); - count++; + udelay(10); + count++; + } + } else { + if (!orion_mdio_smi_is_done(dev)) { + wait_event_timeout(dev->smi_busy_wait, + orion_mdio_smi_is_done(dev), + msecs_to_jiffies(100)); + if (!orion_mdio_smi_is_done(dev)) + return -ETIMEDOUT; + } } return 0; @@ -140,6 +168,21 @@ static int orion_mdio_reset(struct mii_bus *bus) return 0; } +static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) +{ + struct orion_mdio_dev *dev = dev_id; + + if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) & + MVMDIO_ERR_INT_SMI_DONE) { + writel(~MVMDIO_ERR_INT_SMI_DONE, + dev->regs + MVMDIO_ERR_INT_CAUSE); + wake_up(>smi_busy_wait); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + static int orion_mdio_probe(struct platform_device *pdev) { struct resource *r; @@ -185,6 +228,19 @@ static int orion_mdio_probe(struct platform_device *pdev) return -ENODEV; } + init_waitqueue_head(>smi_busy_wait); + +
[PATCH 3/4 v2] 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 flor...@openwrt.org --- Chances since v1: - always use orion_smi_is_done() helper - make interrupt/non-interrupt code path entirely independant .../devicetree/bindings/net/marvell-orion-mdio.txt |3 + drivers/net/ethernet/marvell/mvmdio.c | 83 +--- 2 files changed, 74 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt b/Documentation/devicetree/bindings/net/marvell-orion-mdio.txt index 34e7aaf..052b5f2 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: - compatible: marvell,orion-mdio - reg: address and length of the SMI register +Optional properties: +- interrupts: interrupt line number for the SMI error/done interrupt + The child nodes of the MDIO driver are the individual PHY devices connected to this MDIO bus. They must have a reg property given the PHY address on the MDIO bus. diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 595deea..7ac83de 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -24,9 +24,12 @@ #include linux/module.h #include linux/mutex.h #include linux/phy.h +#include linux/interrupt.h #include linux/platform_device.h #include linux/delay.h #include linux/io.h +#include linux/sched.h +#include linux/wait.h #define MVMDIO_SMI_DATA_SHIFT 0 #define MVMDIO_SMI_PHY_ADDR_SHIFT 16 @@ -35,33 +38,58 @@ #define MVMDIO_SMI_WRITE_OPERATION 0 #define MVMDIO_SMI_READ_VALID BIT(27) #define MVMDIO_SMI_BUSYBIT(28) +#define MVMDIO_ERR_INT_CAUSE 0x007C +#define MVMDIO_ERR_INT_SMI_DONE 0x0010 +#define MVMDIO_ERR_INT_MASK 0x0080 struct orion_mdio_dev { struct mutex lock; void __iomem *regs; + /* +* If we have access to the error interrupt pin (which is +* somewhat misnamed as it not only reflects internal errors +* but also reflects SMI completion), use that to wait for +* SMI access completion instead of polling the SMI busy bit. +*/ + int err_interrupt; + wait_queue_head_t smi_busy_wait; }; +static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev) +{ + return !(readl(dev-regs) MVMDIO_SMI_BUSY); +} + /* Wait for the SMI unit to be ready for another operation */ static int orion_mdio_wait_ready(struct mii_bus *bus) { struct orion_mdio_dev *dev = bus-priv; int count; - u32 val; - count = 0; - while (1) { - val = readl(dev-regs); - if (!(val MVMDIO_SMI_BUSY)) - break; + if (dev-err_interrupt == NO_IRQ) { + count = 0; + while (1) { + if (orion_mdio_smi_is_done(dev)) + break; - if (count 100) { - dev_err(bus-parent, Timeout: SMI busy for too long\n); - return -ETIMEDOUT; - } + if (count 100) { + dev_err(bus-parent, + Timeout: SMI busy for too long\n); + return -ETIMEDOUT; + } - udelay(10); - count++; + udelay(10); + count++; + } + } else { + if (!orion_mdio_smi_is_done(dev)) { + wait_event_timeout(dev-smi_busy_wait, + orion_mdio_smi_is_done(dev), + msecs_to_jiffies(100)); + if (!orion_mdio_smi_is_done(dev)) + return -ETIMEDOUT; + } } return 0; @@ -140,6 +168,21 @@ static int orion_mdio_reset(struct mii_bus *bus) return 0; } +static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id) +{ + struct orion_mdio_dev *dev = dev_id; + + if (readl(dev-regs + MVMDIO_ERR_INT_CAUSE) + MVMDIO_ERR_INT_SMI_DONE) { + writel(~MVMDIO_ERR_INT_SMI_DONE, + dev-regs + MVMDIO_ERR_INT_CAUSE); + wake_up(dev-smi_busy_wait); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + static int orion_mdio_probe(struct platform_device *pdev) { struct resource *r; @@ -185,6 +228,19 @@ static int orion_mdio_probe(struct