diff options
Diffstat (limited to 'drivers/net/dsa/mv88e6xxx/global2.c')
-rw-r--r-- | drivers/net/dsa/mv88e6xxx/global2.c | 137 |
1 files changed, 136 insertions, 1 deletions
diff --git a/drivers/net/dsa/mv88e6xxx/global2.c b/drivers/net/dsa/mv88e6xxx/global2.c index 50e4e0be4227..8f15bc7b1f5f 100644 --- a/drivers/net/dsa/mv88e6xxx/global2.c +++ b/drivers/net/dsa/mv88e6xxx/global2.c @@ -649,6 +649,139 @@ int mv88e6xxx_g2_smi_phy_write(struct mv88e6xxx_chip *chip, return mv88e6xxx_g2_smi_phy_write_c22(chip, addr, reg, val, external); } +static int mv88e6097_watchdog_action(struct mv88e6xxx_chip *chip, int irq) +{ + u16 reg; + + mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); + + dev_info(chip->dev, "Watchdog event: 0x%04x", reg); + + return IRQ_HANDLED; +} + +static void mv88e6097_watchdog_free(struct mv88e6xxx_chip *chip) +{ + u16 reg; + + mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); + + reg &= ~(GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE | + GLOBAL2_WDOG_CONTROL_QC_ENABLE); + + mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, reg); +} + +static int mv88e6097_watchdog_setup(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, + GLOBAL2_WDOG_CONTROL_EGRESS_ENABLE | + GLOBAL2_WDOG_CONTROL_QC_ENABLE | + GLOBAL2_WDOG_CONTROL_SWRESET); +} + +const struct mv88e6xxx_irq_ops mv88e6097_watchdog_ops = { + .irq_action = mv88e6097_watchdog_action, + .irq_setup = mv88e6097_watchdog_setup, + .irq_free = mv88e6097_watchdog_free, +}; + +static int mv88e6390_watchdog_setup(struct mv88e6xxx_chip *chip) +{ + return mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL, + GLOBAL2_WDOG_INT_ENABLE | + GLOBAL2_WDOG_CUT_THROUGH | + GLOBAL2_WDOG_QUEUE_CONTROLLER | + GLOBAL2_WDOG_EGRESS | + GLOBAL2_WDOG_FORCE_IRQ); +} + +static int mv88e6390_watchdog_action(struct mv88e6xxx_chip *chip, int irq) +{ + int err; + u16 reg; + + mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_EVENT); + err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); + + dev_info(chip->dev, "Watchdog event: 0x%04x", + reg & GLOBAL2_WDOG_DATA_MASK); + + mv88e6xxx_g2_write(chip, GLOBAL2_WDOG_CONTROL, GLOBAL2_WDOG_HISTORY); + err = mv88e6xxx_g2_read(chip, GLOBAL2_WDOG_CONTROL, ®); + + dev_info(chip->dev, "Watchdog history: 0x%04x", + reg & GLOBAL2_WDOG_DATA_MASK); + + /* Trigger a software reset to try to recover the switch */ + if (chip->info->ops->reset) + chip->info->ops->reset(chip); + + mv88e6390_watchdog_setup(chip); + + return IRQ_HANDLED; +} + +static void mv88e6390_watchdog_free(struct mv88e6xxx_chip *chip) +{ + mv88e6xxx_g2_update(chip, GLOBAL2_WDOG_CONTROL, + GLOBAL2_WDOG_INT_ENABLE); +} + +const struct mv88e6xxx_irq_ops mv88e6390_watchdog_ops = { + .irq_action = mv88e6390_watchdog_action, + .irq_setup = mv88e6390_watchdog_setup, + .irq_free = mv88e6390_watchdog_free, +}; + +static irqreturn_t mv88e6xxx_g2_watchdog_thread_fn(int irq, void *dev_id) +{ + struct mv88e6xxx_chip *chip = dev_id; + irqreturn_t ret = IRQ_NONE; + + mutex_lock(&chip->reg_lock); + if (chip->info->ops->watchdog_ops->irq_action) + ret = chip->info->ops->watchdog_ops->irq_action(chip, irq); + mutex_unlock(&chip->reg_lock); + + return ret; +} + +static void mv88e6xxx_g2_watchdog_free(struct mv88e6xxx_chip *chip) +{ + mutex_lock(&chip->reg_lock); + if (chip->info->ops->watchdog_ops->irq_free) + chip->info->ops->watchdog_ops->irq_free(chip); + mutex_unlock(&chip->reg_lock); + + free_irq(chip->watchdog_irq, chip); + irq_dispose_mapping(chip->watchdog_irq); +} + +static int mv88e6xxx_g2_watchdog_setup(struct mv88e6xxx_chip *chip) +{ + int err; + + chip->watchdog_irq = irq_find_mapping(chip->g2_irq.domain, + GLOBAL2_INT_SOURCE_WATCHDOG); + if (chip->watchdog_irq < 0) + return chip->watchdog_irq; + + err = request_threaded_irq(chip->watchdog_irq, NULL, + mv88e6xxx_g2_watchdog_thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + "mv88e6xxx-watchdog", chip); + if (err) + return err; + + mutex_lock(&chip->reg_lock); + if (chip->info->ops->watchdog_ops->irq_setup) + err = chip->info->ops->watchdog_ops->irq_setup(chip); + mutex_unlock(&chip->reg_lock); + + return err; +} + static void mv88e6xxx_g2_irq_mask(struct irq_data *d) { struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d); @@ -737,6 +870,8 @@ void mv88e6xxx_g2_irq_free(struct mv88e6xxx_chip *chip) { int irq, virq; + mv88e6xxx_g2_watchdog_free(chip); + free_irq(chip->device_irq, chip); irq_dispose_mapping(chip->device_irq); @@ -779,7 +914,7 @@ int mv88e6xxx_g2_irq_setup(struct mv88e6xxx_chip *chip) if (err) goto out; - return 0; + return mv88e6xxx_g2_watchdog_setup(chip); out: for (irq = 0; irq < 16; irq++) { |