net: dsa: mv88e6xxx: Decode ATU problem interrupt
authorAndrew Lunn <andrew@lunn.ch>
Sun, 14 Jan 2018 01:32:44 +0000 (02:32 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 14 Jan 2018 17:08:45 +0000 (12:08 -0500)
When there is a problem with the ATU, an interrupt can be
generated. Trap this interrupt and decode the registers to determine
what the problem was, then log the error.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/chip.c
drivers/net/dsa/mv88e6xxx/chip.h
drivers/net/dsa/mv88e6xxx/global1.h
drivers/net/dsa/mv88e6xxx/global1_atu.c

index fc512c98f2f86759393802f87e4efd20a8a591ed..97d8bc94cee79ec1ba5c3e038cb091a99cf4ca06 100644 (file)
@@ -3976,11 +3976,15 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
                        if (err)
                                goto out_g1_irq;
                }
+
+               err = mv88e6xxx_g1_atu_prob_irq_setup(chip);
+               if (err)
+                       goto out_g2_irq;
        }
 
        err = mv88e6xxx_mdios_register(chip, np);
        if (err)
-               goto out_g2_irq;
+               goto out_g1_atu_prob_irq;
 
        err = mv88e6xxx_register_switch(chip);
        if (err)
@@ -3990,6 +3994,8 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
 
 out_mdio:
        mv88e6xxx_mdios_unregister(chip);
+out_g1_atu_prob_irq:
+       mv88e6xxx_g1_atu_prob_irq_free(chip);
 out_g2_irq:
        if (chip->info->g2_irqs > 0 && chip->irq > 0)
                mv88e6xxx_g2_irq_free(chip);
@@ -4013,6 +4019,7 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
        mv88e6xxx_mdios_unregister(chip);
 
        if (chip->irq > 0) {
+               mv88e6xxx_g1_atu_prob_irq_free(chip);
                if (chip->info->g2_irqs > 0)
                        mv88e6xxx_g2_irq_free(chip);
                mutex_lock(&chip->reg_lock);
index 334f6f7544ba4568a5a59924d6b656e39ef005d3..4dc4a11c6e16444f0bab0d907af59d7f86b3cef1 100644 (file)
@@ -207,6 +207,7 @@ struct mv88e6xxx_chip {
        int irq;
        int device_irq;
        int watchdog_irq;
+       int atu_prob_irq;
 };
 
 struct mv88e6xxx_bus_ops {
index b0dc7518b47fd355636b9acdda688c3c5601f62a..7afa8072fe91f896cb039d70c451cdf34406ac2e 100644 (file)
@@ -31,7 +31,7 @@
 #define MV88E6XXX_G1_STS_IRQ_STATS                     6
 #define MV88E6XXX_G1_STS_IRQ_VTU_PROBLEM               5
 #define MV88E6XXX_G1_STS_IRQ_VTU_DONE                  4
-#define MV88E6XXX_G1_STS_IRQ_ATU_PROBLEM               3
+#define MV88E6XXX_G1_STS_IRQ_ATU_PROB                  3
 #define MV88E6XXX_G1_STS_IRQ_ATU_DONE                  2
 #define MV88E6XXX_G1_STS_IRQ_TCAM_DONE                 1
 #define MV88E6XXX_G1_STS_IRQ_EEPROM_DONE               0
 #define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_ALL_DB          0x5000
 #define MV88E6XXX_G1_ATU_OP_FLUSH_MOVE_NON_STATIC_DB   0x6000
 #define MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION          0x7000
+#define MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION          BIT(7)
+#define MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION           BIT(6)
+#define MV88E6XXX_G1_ATU_OP_MISS_VIOLTATION            BIT(5)
+#define MV88E6XXX_G1_ATU_OP_FULL_VIOLATION             BIT(4)
 
 /* Offset 0x0C: ATU Data Register */
 #define MV88E6XXX_G1_ATU_DATA                          0x0c
@@ -255,6 +259,8 @@ int mv88e6xxx_g1_atu_loadpurge(struct mv88e6xxx_chip *chip, u16 fid,
 int mv88e6xxx_g1_atu_flush(struct mv88e6xxx_chip *chip, u16 fid, bool all);
 int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
                            bool all);
+int mv88e6xxx_g1_atu_prob_irq_setup(struct mv88e6xxx_chip *chip);
+void mv88e6xxx_g1_atu_prob_irq_free(struct mv88e6xxx_chip *chip);
 
 int mv88e6185_g1_vtu_getnext(struct mv88e6xxx_chip *chip,
                             struct mv88e6xxx_vtu_entry *entry);
index efeef4b014421dad2430c1d7f3282b68fe5a9efa..b97de9d363372f0e05919f9ee25558ed1cbd53f2 100644 (file)
@@ -9,6 +9,8 @@
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  */
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 
 #include "chip.h"
 #include "global1.h"
@@ -307,3 +309,88 @@ int mv88e6xxx_g1_atu_remove(struct mv88e6xxx_chip *chip, u16 fid, int port,
 
        return mv88e6xxx_g1_atu_move(chip, fid, from_port, to_port, all);
 }
+
+static irqreturn_t mv88e6xxx_g1_atu_prob_irq_thread_fn(int irq, void *dev_id)
+{
+       struct mv88e6xxx_chip *chip = dev_id;
+       struct mv88e6xxx_atu_entry entry;
+       int err;
+       u16 val;
+
+       mutex_lock(&chip->reg_lock);
+
+       err = mv88e6xxx_g1_atu_op(chip, 0,
+                                 MV88E6XXX_G1_ATU_OP_GET_CLR_VIOLATION);
+       if (err)
+               goto out;
+
+       err = mv88e6xxx_g1_read(chip, MV88E6XXX_G1_ATU_OP, &val);
+       if (err)
+               goto out;
+
+       err = mv88e6xxx_g1_atu_data_read(chip, &entry);
+       if (err)
+               goto out;
+
+       err = mv88e6xxx_g1_atu_mac_read(chip, &entry);
+       if (err)
+               goto out;
+
+       mutex_unlock(&chip->reg_lock);
+
+       if (val & MV88E6XXX_G1_ATU_OP_AGE_OUT_VIOLATION) {
+               dev_err_ratelimited(chip->dev,
+                                   "ATU age out violation for %pM\n",
+                                   entry.mac);
+       }
+
+       if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION) {
+               dev_err_ratelimited(chip->dev,
+                                   "ATU member violation for %pM portvec %x\n",
+                                   entry.mac, entry.portvec);
+       }
+
+       if (val & MV88E6XXX_G1_ATU_OP_MEMBER_VIOLATION)
+               dev_err_ratelimited(chip->dev,
+                                   "ATU miss violation for %pM portvec %x\n",
+                                   entry.mac, entry.portvec);
+
+       if (val & MV88E6XXX_G1_ATU_OP_FULL_VIOLATION)
+               dev_err_ratelimited(chip->dev,
+                                   "ATU full violation for %pM portvec %x\n",
+                                   entry.mac, entry.portvec);
+
+       return IRQ_HANDLED;
+
+out:
+       mutex_unlock(&chip->reg_lock);
+
+       dev_err(chip->dev, "ATU problem: error %d while handling interrupt\n",
+               err);
+       return IRQ_HANDLED;
+}
+
+int mv88e6xxx_g1_atu_prob_irq_setup(struct mv88e6xxx_chip *chip)
+{
+       int err;
+
+       chip->atu_prob_irq = irq_find_mapping(chip->g1_irq.domain,
+                                             MV88E6XXX_G1_STS_IRQ_ATU_PROB);
+       if (chip->atu_prob_irq < 0)
+               return chip->device_irq;
+
+       err = request_threaded_irq(chip->atu_prob_irq, NULL,
+                                  mv88e6xxx_g1_atu_prob_irq_thread_fn,
+                                  IRQF_ONESHOT, "mv88e6xxx-g1-atu-prob",
+                                  chip);
+       if (err)
+               irq_dispose_mapping(chip->atu_prob_irq);
+
+       return err;
+}
+
+void mv88e6xxx_g1_atu_prob_irq_free(struct mv88e6xxx_chip *chip)
+{
+       free_irq(chip->atu_prob_irq, chip);
+       irq_dispose_mapping(chip->atu_prob_irq);
+}