diff options
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.c | 9 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/chip.h | 1 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1.h | 8 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global1_vtu.c | 74 |
4 files changed, 90 insertions, 2 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 97d8bc94cee7..54cb00a27408 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -3980,11 +3980,15 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) 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_atu_prob_irq; + goto out_g1_vtu_prob_irq; err = mv88e6xxx_register_switch(chip); if (err) @@ -3994,6 +3998,8 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev) out_mdio: mv88e6xxx_mdios_unregister(chip); +out_g1_vtu_prob_irq: + mv88e6xxx_g1_vtu_prob_irq_free(chip); out_g1_atu_prob_irq: mv88e6xxx_g1_atu_prob_irq_free(chip); out_g2_irq: @@ -4019,6 +4025,7 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) mv88e6xxx_mdios_unregister(chip); if (chip->irq > 0) { + mv88e6xxx_g1_vtu_prob_irq_free(chip); mv88e6xxx_g1_atu_prob_irq_free(chip); if (chip->info->g2_irqs > 0) mv88e6xxx_g2_irq_free(chip); diff --git a/drivers/net/dsa/mv88e6xxx/chip.h b/drivers/net/dsa/mv88e6xxx/chip.h index 4dc4a11c6e16..3dba6e90adcf 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.h +++ b/drivers/net/dsa/mv88e6xxx/chip.h @@ -208,6 +208,7 @@ struct mv88e6xxx_chip { int device_irq; int watchdog_irq; int atu_prob_irq; + int vtu_prob_irq; }; struct mv88e6xxx_bus_ops { diff --git a/drivers/net/dsa/mv88e6xxx/global1.h b/drivers/net/dsa/mv88e6xxx/global1.h index 7afa8072fe91..6aee7316fea6 100644 --- a/drivers/net/dsa/mv88e6xxx/global1.h +++ b/drivers/net/dsa/mv88e6xxx/global1.h @@ -29,7 +29,7 @@ #define MV88E6XXX_G1_STS_IRQ_AVB 8 #define MV88E6XXX_G1_STS_IRQ_DEVICE 7 #define MV88E6XXX_G1_STS_IRQ_STATS 6 -#define MV88E6XXX_G1_STS_IRQ_VTU_PROBLEM 5 +#define MV88E6XXX_G1_STS_IRQ_VTU_PROB 5 #define MV88E6XXX_G1_STS_IRQ_VTU_DONE 4 #define MV88E6XXX_G1_STS_IRQ_ATU_PROB 3 #define MV88E6XXX_G1_STS_IRQ_ATU_DONE 2 @@ -82,6 +82,10 @@ #define MV88E6XXX_G1_VTU_OP_VTU_GET_NEXT 0x4000 #define MV88E6XXX_G1_VTU_OP_STU_LOAD_PURGE 0x5000 #define MV88E6XXX_G1_VTU_OP_STU_GET_NEXT 0x6000 +#define MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION 0x7000 +#define MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION BIT(6) +#define MV88E6XXX_G1_VTU_OP_MISS_VIOLATION BIT(5) +#define MV88E6XXX_G1_VTU_OP_SPID_MASK 0xf /* Offset 0x06: VTU VID Register */ #define MV88E6XXX_G1_VTU_VID 0x06 @@ -275,5 +279,7 @@ int mv88e6390_g1_vtu_getnext(struct mv88e6xxx_chip *chip, int mv88e6390_g1_vtu_loadpurge(struct mv88e6xxx_chip *chip, struct mv88e6xxx_vtu_entry *entry); int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip); +int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip); +void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip); #endif /* _MV88E6XXX_GLOBAL1_H */ diff --git a/drivers/net/dsa/mv88e6xxx/global1_vtu.c b/drivers/net/dsa/mv88e6xxx/global1_vtu.c index 8c8a0ec3d6e9..53d58a01484a 100644 --- a/drivers/net/dsa/mv88e6xxx/global1_vtu.c +++ b/drivers/net/dsa/mv88e6xxx/global1_vtu.c @@ -11,6 +11,9 @@ * (at your option) any later version. */ +#include <linux/interrupt.h> +#include <linux/irqdomain.h> + #include "chip.h" #include "global1.h" @@ -513,3 +516,74 @@ int mv88e6xxx_g1_vtu_flush(struct mv88e6xxx_chip *chip) return mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_FLUSH_ALL); } + +static irqreturn_t mv88e6xxx_g1_vtu_prob_irq_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_chip *chip = dev_id; + struct mv88e6xxx_vtu_entry entry; + int spid; + int err; + u16 val; + + mutex_lock(&chip->reg_lock); + + err = mv88e6xxx_g1_vtu_op(chip, MV88E6XXX_G1_VTU_OP_GET_CLR_VIOLATION); + if (err) + goto out; + + err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_VTU_OP, &val); + if (err) + goto out; + + err = mv88e6xxx_g1_vtu_vid_read(chip, &entry); + if (err) + goto out; + + mutex_unlock(&chip->reg_lock); + + spid = val & MV88E6XXX_G1_VTU_OP_SPID_MASK; + + if (val & MV88E6XXX_G1_VTU_OP_MEMBER_VIOLATION) { + dev_err_ratelimited(chip->dev, "VTU member violation for vid %d, source port %d\n", + entry.vid, spid); + } + + if (val & MV88E6XXX_G1_VTU_OP_MISS_VIOLATION) + dev_err_ratelimited(chip->dev, "VTU miss violation for vid %d, source port %d\n", + entry.vid, spid); + + return IRQ_HANDLED; + +out: + mutex_unlock(&chip->reg_lock); + + dev_err(chip->dev, "VTU problem: error %d while handling interrupt\n", + err); + + return IRQ_HANDLED; +} + +int mv88e6xxx_g1_vtu_prob_irq_setup(struct mv88e6xxx_chip *chip) +{ + int err; + + chip->vtu_prob_irq = irq_find_mapping(chip->g1_irq.domain, + MV88E6XXX_G1_STS_IRQ_VTU_PROB); + if (chip->vtu_prob_irq < 0) + return chip->device_irq; + + err = request_threaded_irq(chip->vtu_prob_irq, NULL, + mv88e6xxx_g1_vtu_prob_irq_thread_fn, + IRQF_ONESHOT, "mv88e6xxx-g1-vtu-prob", + chip); + if (err) + irq_dispose_mapping(chip->vtu_prob_irq); + + return err; +} + +void mv88e6xxx_g1_vtu_prob_irq_free(struct mv88e6xxx_chip *chip) +{ + free_irq(chip->vtu_prob_irq, chip); + irq_dispose_mapping(chip->vtu_prob_irq); +} |