Merge branch 'mv88e6xxx-Poll-when-no-interrupt-defined'

Andrew Lunn says:

====================
mv88e6xxx: Poll when no interrupt defined

Not all boards using the mv88e6xxx switches have the interrupt output
connected to a GPIO. On these boards phylib has to poll the PHYs,
rather than use interrupts. Have the driver poll the interrupt status
register, which is more efficient than having phylib do it. And it
enables other switch interrupts to be services.

The Armada 370RD is such a board without a interrupt GPIO. Now that
interrupts work, wire up the PHYs to make use if them.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2018-02-26 11:28:11 -05:00
commit 25ab47ef48
3 changed files with 139 additions and 44 deletions

View File

@ -56,6 +56,7 @@
/dts-v1/;
#include <dt-bindings/input/input.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/gpio/gpio.h>
#include "armada-370.dtsi"
@ -243,6 +244,8 @@
#address-cells = <1>;
#size-cells = <0>;
reg = <0x10>;
interrupt-controller;
#interrupt-cells = <2>;
ports {
#address-cells = <1>;
@ -278,6 +281,35 @@
};
};
};
mdio {
#address-cells = <1>;
#size-cells = <0>;
switchphy0: switchphy@0 {
reg = <0>;
interrupt-parent = <&switch>;
interrupts = <0 IRQ_TYPE_LEVEL_HIGH>;
};
switchphy1: switchphy@1 {
reg = <1>;
interrupt-parent = <&switch>;
interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
};
switchphy2: switchphy@2 {
reg = <2>;
interrupt-parent = <&switch>;
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
};
switchphy3: switchphy@3 {
reg = <3>;
interrupt-parent = <&switch>;
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
};
};
};
};

View File

@ -253,9 +253,8 @@ static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
chip->g1_irq.masked &= ~(1 << n);
}
static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
static irqreturn_t mv88e6xxx_g1_irq_thread_work(struct mv88e6xxx_chip *chip)
{
struct mv88e6xxx_chip *chip = dev_id;
unsigned int nhandled = 0;
unsigned int sub_irq;
unsigned int n;
@ -280,6 +279,13 @@ out:
return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
}
static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
{
struct mv88e6xxx_chip *chip = dev_id;
return mv88e6xxx_g1_irq_thread_work(chip);
}
static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
{
struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
@ -335,7 +341,7 @@ static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
.xlate = irq_domain_xlate_twocell,
};
static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
static void mv88e6xxx_g1_irq_free_common(struct mv88e6xxx_chip *chip)
{
int irq, virq;
u16 mask;
@ -344,8 +350,6 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
mask &= ~GENMASK(chip->g1_irq.nirqs, 0);
mv88e6xxx_g1_write(chip, MV88E6XXX_G1_CTL1, mask);
free_irq(chip->irq, chip);
for (irq = 0; irq < chip->g1_irq.nirqs; irq++) {
virq = irq_find_mapping(chip->g1_irq.domain, irq);
irq_dispose_mapping(virq);
@ -354,7 +358,14 @@ static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
irq_domain_remove(chip->g1_irq.domain);
}
static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
{
mv88e6xxx_g1_irq_free(chip);
free_irq(chip->irq, chip);
}
static int mv88e6xxx_g1_irq_setup_common(struct mv88e6xxx_chip *chip)
{
int err, irq, virq;
u16 reg, mask;
@ -387,13 +398,6 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
if (err)
goto out_disable;
err = request_threaded_irq(chip->irq, NULL,
mv88e6xxx_g1_irq_thread_fn,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
dev_name(chip->dev), chip);
if (err)
goto out_disable;
return 0;
out_disable:
@ -411,6 +415,62 @@ out_mapping:
return err;
}
static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
{
int err;
err = mv88e6xxx_g1_irq_setup_common(chip);
if (err)
return err;
err = request_threaded_irq(chip->irq, NULL,
mv88e6xxx_g1_irq_thread_fn,
IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
dev_name(chip->dev), chip);
if (err)
mv88e6xxx_g1_irq_free_common(chip);
return err;
}
static void mv88e6xxx_irq_poll(struct kthread_work *work)
{
struct mv88e6xxx_chip *chip = container_of(work,
struct mv88e6xxx_chip,
irq_poll_work.work);
mv88e6xxx_g1_irq_thread_work(chip);
kthread_queue_delayed_work(chip->kworker, &chip->irq_poll_work,
msecs_to_jiffies(100));
}
static int mv88e6xxx_irq_poll_setup(struct mv88e6xxx_chip *chip)
{
int err;
err = mv88e6xxx_g1_irq_setup_common(chip);
if (err)
return err;
kthread_init_delayed_work(&chip->irq_poll_work,
mv88e6xxx_irq_poll);
chip->kworker = kthread_create_worker(0, dev_name(chip->dev));
if (IS_ERR(chip->kworker))
return PTR_ERR(chip->kworker);
kthread_queue_delayed_work(chip->kworker, &chip->irq_poll_work,
msecs_to_jiffies(100));
return 0;
}
static void mv88e6xxx_irq_poll_free(struct mv88e6xxx_chip *chip)
{
kthread_cancel_delayed_work_sync(&chip->irq_poll_work);
kthread_destroy_worker(chip->kworker);
}
int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
{
int i;
@ -4034,33 +4094,34 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
goto out;
}
if (chip->irq > 0) {
/* Has to be performed before the MDIO bus is created,
* because the PHYs will link there interrupts to these
* interrupt controllers
*/
mutex_lock(&chip->reg_lock);
/* Has to be performed before the MDIO bus is created, because
* the PHYs will link there interrupts to these interrupt
* controllers
*/
mutex_lock(&chip->reg_lock);
if (chip->irq > 0)
err = mv88e6xxx_g1_irq_setup(chip);
mutex_unlock(&chip->reg_lock);
else
err = mv88e6xxx_irq_poll_setup(chip);
mutex_unlock(&chip->reg_lock);
if (err)
goto out;
if (chip->info->g2_irqs > 0) {
err = mv88e6xxx_g2_irq_setup(chip);
if (err)
goto out;
if (chip->info->g2_irqs > 0) {
err = mv88e6xxx_g2_irq_setup(chip);
if (err)
goto out_g1_irq;
}
err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
if (err)
goto out_g2_irq;
err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
if (err)
goto out_g1_atu_prob_irq;
goto out_g1_irq;
}
err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
if (err)
goto out_g2_irq;
err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
if (err)
goto out_g1_atu_prob_irq;
err = mv88e6xxx_mdios_register(chip, np);
if (err)
goto out_g1_vtu_prob_irq;
@ -4074,20 +4135,19 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
out_mdio:
mv88e6xxx_mdios_unregister(chip);
out_g1_vtu_prob_irq:
if (chip->irq > 0)
mv88e6xxx_g1_vtu_prob_irq_free(chip);
mv88e6xxx_g1_vtu_prob_irq_free(chip);
out_g1_atu_prob_irq:
if (chip->irq > 0)
mv88e6xxx_g1_atu_prob_irq_free(chip);
mv88e6xxx_g1_atu_prob_irq_free(chip);
out_g2_irq:
if (chip->info->g2_irqs > 0 && chip->irq > 0)
if (chip->info->g2_irqs > 0)
mv88e6xxx_g2_irq_free(chip);
out_g1_irq:
if (chip->irq > 0) {
mutex_lock(&chip->reg_lock);
mutex_lock(&chip->reg_lock);
if (chip->irq > 0)
mv88e6xxx_g1_irq_free(chip);
mutex_unlock(&chip->reg_lock);
}
else
mv88e6xxx_irq_poll_free(chip);
mutex_unlock(&chip->reg_lock);
out:
return err;
}

View File

@ -15,6 +15,7 @@
#include <linux/if_vlan.h>
#include <linux/irq.h>
#include <linux/gpio/consumer.h>
#include <linux/kthread.h>
#include <linux/phy.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
@ -245,6 +246,8 @@ struct mv88e6xxx_chip {
int watchdog_irq;
int atu_prob_irq;
int vtu_prob_irq;
struct kthread_worker *kworker;
struct kthread_delayed_work irq_poll_work;
/* GPIO resources */
u8 gpio_data[2];