|
@@ -18,11 +18,15 @@
|
|
|
#include <linux/etherdevice.h>
|
|
|
#include <linux/ethtool.h>
|
|
|
#include <linux/if_bridge.h>
|
|
|
+#include <linux/interrupt.h>
|
|
|
+#include <linux/irq.h>
|
|
|
+#include <linux/irqdomain.h>
|
|
|
#include <linux/jiffies.h>
|
|
|
#include <linux/list.h>
|
|
|
#include <linux/mdio.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <linux/of_device.h>
|
|
|
+#include <linux/of_irq.h>
|
|
|
#include <linux/of_mdio.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/gpio/consumer.h>
|
|
@@ -323,6 +327,164 @@ static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
|
|
|
reg, val);
|
|
|
}
|
|
|
|
|
|
+static void mv88e6xxx_g1_irq_mask(struct irq_data *d)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
+ unsigned int n = d->hwirq;
|
|
|
+
|
|
|
+ chip->g1_irq.masked |= (1 << n);
|
|
|
+}
|
|
|
+
|
|
|
+static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
+ unsigned int n = d->hwirq;
|
|
|
+
|
|
|
+ chip->g1_irq.masked &= ~(1 << n);
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_chip *chip = dev_id;
|
|
|
+ unsigned int nhandled = 0;
|
|
|
+ unsigned int sub_irq;
|
|
|
+ unsigned int n;
|
|
|
+ u16 reg;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ mutex_lock(&chip->reg_lock);
|
|
|
+ err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®);
|
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ for (n = 0; n < chip->g1_irq.nirqs; ++n) {
|
|
|
+ if (reg & (1 << n)) {
|
|
|
+ sub_irq = irq_find_mapping(chip->g1_irq.domain, n);
|
|
|
+ handle_nested_irq(sub_irq);
|
|
|
+ ++nhandled;
|
|
|
+ }
|
|
|
+ }
|
|
|
+out:
|
|
|
+ return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
|
|
|
+}
|
|
|
+
|
|
|
+static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
+
|
|
|
+ mutex_lock(&chip->reg_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
|
|
|
+ u16 mask = GENMASK(chip->g1_irq.nirqs, 0);
|
|
|
+ u16 reg;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ reg &= ~mask;
|
|
|
+ reg |= (~chip->g1_irq.masked & mask);
|
|
|
+
|
|
|
+ err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+out:
|
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static struct irq_chip mv88e6xxx_g1_irq_chip = {
|
|
|
+ .name = "mv88e6xxx-g1",
|
|
|
+ .irq_mask = mv88e6xxx_g1_irq_mask,
|
|
|
+ .irq_unmask = mv88e6xxx_g1_irq_unmask,
|
|
|
+ .irq_bus_lock = mv88e6xxx_g1_irq_bus_lock,
|
|
|
+ .irq_bus_sync_unlock = mv88e6xxx_g1_irq_bus_sync_unlock,
|
|
|
+};
|
|
|
+
|
|
|
+static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d,
|
|
|
+ unsigned int irq,
|
|
|
+ irq_hw_number_t hwirq)
|
|
|
+{
|
|
|
+ struct mv88e6xxx_chip *chip = d->host_data;
|
|
|
+
|
|
|
+ irq_set_chip_data(irq, d->host_data);
|
|
|
+ irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq);
|
|
|
+ irq_set_noprobe(irq);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
|
|
|
+ .map = mv88e6xxx_g1_irq_domain_map,
|
|
|
+ .xlate = irq_domain_xlate_twocell,
|
|
|
+};
|
|
|
+
|
|
|
+static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
|
|
|
+{
|
|
|
+ int irq, virq;
|
|
|
+
|
|
|
+ for (irq = 0; irq < 16; irq++) {
|
|
|
+ virq = irq_find_mapping(chip->g2_irq.domain, irq);
|
|
|
+ irq_dispose_mapping(virq);
|
|
|
+ }
|
|
|
+
|
|
|
+ irq_domain_remove(chip->g2_irq.domain);
|
|
|
+}
|
|
|
+
|
|
|
+static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
|
|
|
+{
|
|
|
+ int err, irq;
|
|
|
+ u16 reg;
|
|
|
+
|
|
|
+ chip->g1_irq.nirqs = chip->info->g1_irqs;
|
|
|
+ chip->g1_irq.domain = irq_domain_add_simple(
|
|
|
+ NULL, chip->g1_irq.nirqs, 0,
|
|
|
+ &mv88e6xxx_g1_irq_domain_ops, chip);
|
|
|
+ if (!chip->g1_irq.domain)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ for (irq = 0; irq < chip->g1_irq.nirqs; irq++)
|
|
|
+ irq_create_mapping(chip->g1_irq.domain, irq);
|
|
|
+
|
|
|
+ chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
|
|
|
+ chip->g1_irq.masked = ~0;
|
|
|
+
|
|
|
+ err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ reg &= ~GENMASK(chip->g1_irq.nirqs, 0);
|
|
|
+
|
|
|
+ err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* Reading the interrupt status clears (most of) them */
|
|
|
+ err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, ®);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ 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;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+out:
|
|
|
+ mv88e6xxx_g1_irq_free(chip);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
|
|
|
{
|
|
|
int i;
|
|
@@ -2770,7 +2932,11 @@ static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
|
|
|
/* Enable the PHY Polling Unit if present, don't discard any packets,
|
|
|
* and mask all interrupt sources.
|
|
|
*/
|
|
|
- reg = 0;
|
|
|
+ err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, ®);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ reg &= ~GLOBAL_CONTROL_PPU_ENABLE;
|
|
|
if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
|
|
|
mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
|
|
|
reg |= GLOBAL_CONTROL_PPU_ENABLE;
|
|
@@ -2875,10 +3041,6 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
|
|
|
|
|
|
mutex_lock(&chip->reg_lock);
|
|
|
|
|
|
- err = mv88e6xxx_switch_reset(chip);
|
|
|
- if (err)
|
|
|
- goto unlock;
|
|
|
-
|
|
|
/* Setup Switch Port Registers */
|
|
|
for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
|
|
|
err = mv88e6xxx_setup_port(chip, i);
|
|
@@ -3326,6 +3488,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 8,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6097,
|
|
|
.ops = &mv88e6085_ops,
|
|
|
},
|
|
@@ -3339,6 +3502,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 8,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6095,
|
|
|
.ops = &mv88e6095_ops,
|
|
|
},
|
|
@@ -3352,6 +3516,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
|
|
|
.ops = &mv88e6123_ops,
|
|
|
},
|
|
@@ -3365,6 +3530,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
|
|
|
.ops = &mv88e6131_ops,
|
|
|
},
|
|
@@ -3378,6 +3544,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
|
|
|
.ops = &mv88e6161_ops,
|
|
|
},
|
|
@@ -3391,6 +3558,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6165,
|
|
|
.ops = &mv88e6165_ops,
|
|
|
},
|
|
@@ -3404,6 +3572,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
|
|
.ops = &mv88e6171_ops,
|
|
|
},
|
|
@@ -3417,6 +3586,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
|
|
.ops = &mv88e6172_ops,
|
|
|
},
|
|
@@ -3430,6 +3600,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
|
|
.ops = &mv88e6175_ops,
|
|
|
},
|
|
@@ -3443,6 +3614,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
|
|
.ops = &mv88e6176_ops,
|
|
|
},
|
|
@@ -3456,6 +3628,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 8,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6185,
|
|
|
.ops = &mv88e6185_ops,
|
|
|
},
|
|
@@ -3469,6 +3642,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
|
|
.ops = &mv88e6240_ops,
|
|
|
},
|
|
@@ -3482,6 +3656,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 8,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
|
|
|
.ops = &mv88e6320_ops,
|
|
|
},
|
|
@@ -3495,6 +3670,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 8,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6320,
|
|
|
.ops = &mv88e6321_ops,
|
|
|
},
|
|
@@ -3508,6 +3684,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
|
|
.ops = &mv88e6350_ops,
|
|
|
},
|
|
@@ -3521,6 +3698,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6351,
|
|
|
.ops = &mv88e6351_ops,
|
|
|
},
|
|
@@ -3534,6 +3712,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
|
|
|
.port_base_addr = 0x10,
|
|
|
.global1_addr = 0x1b,
|
|
|
.age_time_coeff = 15000,
|
|
|
+ .g1_irqs = 9,
|
|
|
.flags = MV88E6XXX_FLAGS_FAMILY_6352,
|
|
|
.ops = &mv88e6352_ops,
|
|
|
},
|
|
@@ -3667,6 +3846,12 @@ static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
|
|
|
if (err)
|
|
|
goto free;
|
|
|
|
|
|
+ mutex_lock(&chip->reg_lock);
|
|
|
+ err = mv88e6xxx_switch_reset(chip);
|
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
|
+ if (err)
|
|
|
+ goto free;
|
|
|
+
|
|
|
mv88e6xxx_phy_init(chip);
|
|
|
|
|
|
err = mv88e6xxx_mdio_register(chip, NULL);
|
|
@@ -3837,17 +4022,56 @@ static int mv88e6xxx_probe(struct mdio_device *mdiodev)
|
|
|
!of_property_read_u32(np, "eeprom-length", &eeprom_len))
|
|
|
chip->eeprom_len = eeprom_len;
|
|
|
|
|
|
+ mutex_lock(&chip->reg_lock);
|
|
|
+ err = mv88e6xxx_switch_reset(chip);
|
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ chip->irq = of_irq_get(np, 0);
|
|
|
+ if (chip->irq == -EPROBE_DEFER) {
|
|
|
+ err = chip->irq;
|
|
|
+ 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);
|
|
|
+ err = mv88e6xxx_g1_irq_setup(chip);
|
|
|
+ mutex_unlock(&chip->reg_lock);
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
|
|
|
+ err = mv88e6xxx_g2_irq_setup(chip);
|
|
|
+ if (err)
|
|
|
+ goto out_g1_irq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
err = mv88e6xxx_mdio_register(chip, np);
|
|
|
if (err)
|
|
|
- return err;
|
|
|
+ goto out_g2_irq;
|
|
|
|
|
|
err = mv88e6xxx_register_switch(chip, np);
|
|
|
- if (err) {
|
|
|
- mv88e6xxx_mdio_unregister(chip);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ if (err)
|
|
|
+ goto out_mdio;
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+out_mdio:
|
|
|
+ mv88e6xxx_mdio_unregister(chip);
|
|
|
+out_g2_irq:
|
|
|
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
|
|
|
+ mv88e6xxx_g2_irq_free(chip);
|
|
|
+out_g1_irq:
|
|
|
+ mv88e6xxx_g1_irq_free(chip);
|
|
|
+out:
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
static void mv88e6xxx_remove(struct mdio_device *mdiodev)
|
|
@@ -3858,6 +4082,10 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
|
|
|
mv88e6xxx_phy_destroy(chip);
|
|
|
mv88e6xxx_unregister_switch(chip);
|
|
|
mv88e6xxx_mdio_unregister(chip);
|
|
|
+
|
|
|
+ if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
|
|
|
+ mv88e6xxx_g2_irq_free(chip);
|
|
|
+ mv88e6xxx_g1_irq_free(chip);
|
|
|
}
|
|
|
|
|
|
static const struct of_device_id mv88e6xxx_of_match[] = {
|