Skip to content

Commit

Permalink
net: dsa: mv88e6xxx: Poll when no interrupt defined
Browse files Browse the repository at this point in the history
Not all boards have the interrupt output from the switch connected to
a GPIO line. In such cases, phylib has to poll the internal PHYs,
rather than receive an interrupt when there is a change in the link
state. phylib polls once per second, and per PHY reads around 4
words. With a switch typically having 4 internal PHYs, this means 16
MDIO transactions per second.

Rather than performing this phylib level polling, have the driver poll
the interrupt status register. If the status register indicates an
interrupt condition processing of interrupts in the same way as if a
GPIO was used.

Polling 10 times a second places less load on the MDIO bus. But rather
than taking on average 0.5s to detect a link change, it takes less
than 0.05s. Additionally, other interrupts, such as the watchdog, ATU
and VTU violations will be reported.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Andrew Lunn authored and David S. Miller committed Feb 26, 2018
1 parent 65b53bf commit 294d711
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 43 deletions.
146 changes: 103 additions & 43 deletions drivers/net/dsa/mv88e6xxx/chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -280,6 +279,13 @@ static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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:
Expand All @@ -411,6 +415,62 @@ static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
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;
Expand Down Expand Up @@ -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);

if (err)
goto out;

if (chip->info->g2_irqs > 0) {
err = mv88e6xxx_g2_irq_setup(chip);
if (err)
goto out_g1_irq;
}
else
err = mv88e6xxx_irq_poll_setup(chip);
mutex_unlock(&chip->reg_lock);

err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
if (err)
goto out_g2_irq;
if (err)
goto out;

err = mv88e6xxx_g1_vtu_prob_irq_setup(chip);
if (chip->info->g2_irqs > 0) {
err = mv88e6xxx_g2_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;
Expand All @@ -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;
}
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/dsa/mv88e6xxx/chip.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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];
Expand Down

0 comments on commit 294d711

Please sign in to comment.