]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - drivers/net/dsa/mv88e6xxx/chip.c
net: dsa: mv88e6xxx: Add MDIO interrupts for internal PHYs
[mirror_ubuntu-jammy-kernel.git] / drivers / net / dsa / mv88e6xxx / chip.c
index eb328bade225187ffc745573cfff5741af66e46d..fe46b40195fae1503fff871952d34370bf4053a6 100644 (file)
 #include "chip.h"
 #include "global1.h"
 #include "global2.h"
+#include "hwtstamp.h"
 #include "phy.h"
 #include "port.h"
+#include "ptp.h"
 #include "serdes.h"
 
 static void assert_reg_lock(struct mv88e6xxx_chip *chip)
@@ -251,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;
@@ -278,6 +279,13 @@ out:
        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);
@@ -333,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;
@@ -342,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);
@@ -352,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_common(chip);
+
+       free_irq(chip->irq, chip);
+}
+
+static int mv88e6xxx_g1_irq_setup_common(struct mv88e6xxx_chip *chip)
 {
        int err, irq, virq;
        u16 reg, mask;
@@ -385,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:
@@ -409,6 +415,62 @@ out_mapping:
        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;
@@ -604,7 +666,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
                        return UINT64_MAX;
 
                low = reg;
-               if (s->sizeof_stat == 4) {
+               if (s->size == 4) {
                        err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
                        if (err)
                                return UINT64_MAX;
@@ -617,7 +679,7 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
        case STATS_TYPE_BANK0:
                reg |= s->reg | histogram;
                mv88e6xxx_g1_stats_read(chip, reg, &low);
-               if (s->sizeof_stat == 8)
+               if (s->size == 8)
                        mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
                break;
        default:
@@ -627,8 +689,8 @@ static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
        return value;
 }
 
-static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
-                                       uint8_t *data, int types)
+static int mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
+                                      uint8_t *data, int types)
 {
        struct mv88e6xxx_hw_stat *stat;
        int i, j;
@@ -641,29 +703,41 @@ static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
                        j++;
                }
        }
+
+       return j;
 }
 
-static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
-                                       uint8_t *data)
+static int mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
+                                      uint8_t *data)
 {
-       mv88e6xxx_stats_get_strings(chip, data,
-                                   STATS_TYPE_BANK0 | STATS_TYPE_PORT);
+       return mv88e6xxx_stats_get_strings(chip, data,
+                                          STATS_TYPE_BANK0 | STATS_TYPE_PORT);
 }
 
-static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
-                                       uint8_t *data)
+static int mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
+                                      uint8_t *data)
 {
-       mv88e6xxx_stats_get_strings(chip, data,
-                                   STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
+       return mv88e6xxx_stats_get_strings(chip, data,
+                                          STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
 }
 
 static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
                                  uint8_t *data)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
+       int count = 0;
+
+       mutex_lock(&chip->reg_lock);
 
        if (chip->info->ops->stats_get_strings)
-               chip->info->ops->stats_get_strings(chip, data);
+               count = chip->info->ops->stats_get_strings(chip, data);
+
+       if (chip->info->ops->serdes_get_strings) {
+               data += count * ETH_GSTRING_LEN;
+               chip->info->ops->serdes_get_strings(chip, port, data);
+       }
+
+       mutex_unlock(&chip->reg_lock);
 }
 
 static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip,
@@ -692,19 +766,34 @@ static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip)
                                              STATS_TYPE_BANK1);
 }
 
-static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
+static int mv88e6xxx_get_sset_count(struct dsa_switch *ds, int port)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
+       int serdes_count = 0;
+       int count = 0;
 
+       mutex_lock(&chip->reg_lock);
        if (chip->info->ops->stats_get_sset_count)
-               return chip->info->ops->stats_get_sset_count(chip);
+               count = chip->info->ops->stats_get_sset_count(chip);
+       if (count < 0)
+               goto out;
 
-       return 0;
+       if (chip->info->ops->serdes_get_sset_count)
+               serdes_count = chip->info->ops->serdes_get_sset_count(chip,
+                                                                     port);
+       if (serdes_count < 0)
+               count = serdes_count;
+       else
+               count += serdes_count;
+out:
+       mutex_unlock(&chip->reg_lock);
+
+       return count;
 }
 
-static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
-                                     uint64_t *data, int types,
-                                     u16 bank1_select, u16 histogram)
+static int mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+                                    uint64_t *data, int types,
+                                    u16 bank1_select, u16 histogram)
 {
        struct mv88e6xxx_hw_stat *stat;
        int i, j;
@@ -712,24 +801,28 @@ static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
        for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
                stat = &mv88e6xxx_hw_stats[i];
                if (stat->type & types) {
+                       mutex_lock(&chip->reg_lock);
                        data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
                                                              bank1_select,
                                                              histogram);
+                       mutex_unlock(&chip->reg_lock);
+
                        j++;
                }
        }
+       return j;
 }
 
-static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
-                                     uint64_t *data)
+static int mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+                                    uint64_t *data)
 {
        return mv88e6xxx_stats_get_stats(chip, port, data,
                                         STATS_TYPE_BANK0 | STATS_TYPE_PORT,
                                         0, MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
 }
 
-static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
-                                     uint64_t *data)
+static int mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+                                    uint64_t *data)
 {
        return mv88e6xxx_stats_get_stats(chip, port, data,
                                         STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
@@ -737,8 +830,8 @@ static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
                                         MV88E6XXX_G1_STATS_OP_HIST_RX_TX);
 }
 
-static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
-                                     uint64_t *data)
+static int mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
+                                    uint64_t *data)
 {
        return mv88e6xxx_stats_get_stats(chip, port, data,
                                         STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
@@ -749,8 +842,17 @@ static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
 static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
                                uint64_t *data)
 {
+       int count = 0;
+
        if (chip->info->ops->stats_get_stats)
-               chip->info->ops->stats_get_stats(chip, port, data);
+               count = chip->info->ops->stats_get_stats(chip, port, data);
+
+       if (chip->info->ops->serdes_get_stats) {
+               data += count;
+               mutex_lock(&chip->reg_lock);
+               chip->info->ops->serdes_get_stats(chip, port, data);
+               mutex_unlock(&chip->reg_lock);
+       }
 }
 
 static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
@@ -762,14 +864,13 @@ static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
        mutex_lock(&chip->reg_lock);
 
        ret = mv88e6xxx_stats_snapshot(chip, port);
-       if (ret < 0) {
-               mutex_unlock(&chip->reg_lock);
+       mutex_unlock(&chip->reg_lock);
+
+       if (ret < 0)
                return;
-       }
 
        mv88e6xxx_get_stats(chip, port, data);
 
-       mutex_unlock(&chip->reg_lock);
 }
 
 static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip)
@@ -1433,7 +1534,9 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
        eth_broadcast_addr(addr.mac);
 
        do {
+               mutex_lock(&chip->reg_lock);
                err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
+               mutex_unlock(&chip->reg_lock);
                if (err)
                        return err;
 
@@ -1466,7 +1569,10 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
        int err;
 
        /* Dump port's default Filtering Information Database (VLAN ID 0) */
+       mutex_lock(&chip->reg_lock);
        err = mv88e6xxx_port_get_fid(chip, port, &fid);
+       mutex_unlock(&chip->reg_lock);
+
        if (err)
                return err;
 
@@ -1476,7 +1582,9 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
 
        /* Dump VLANs' Filtering Information Databases */
        do {
+               mutex_lock(&chip->reg_lock);
                err = mv88e6xxx_vtu_getnext(chip, &vlan);
+               mutex_unlock(&chip->reg_lock);
                if (err)
                        return err;
 
@@ -1496,13 +1604,8 @@ static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
                                   dsa_fdb_dump_cb_t *cb, void *data)
 {
        struct mv88e6xxx_chip *chip = ds->priv;
-       int err;
-
-       mutex_lock(&chip->reg_lock);
-       err = mv88e6xxx_port_db_dump(chip, port, cb, data);
-       mutex_unlock(&chip->reg_lock);
 
-       return err;
+       return mv88e6xxx_port_db_dump(chip, port, cb, data);
 }
 
 static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
@@ -2092,6 +2195,17 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
        if (err)
                goto unlock;
 
+       /* Setup PTP Hardware Clock and timestamping */
+       if (chip->info->ptp_support) {
+               err = mv88e6xxx_ptp_setup(chip);
+               if (err)
+                       goto unlock;
+
+               err = mv88e6xxx_hwtstamp_setup(chip);
+               if (err)
+                       goto unlock;
+       }
+
 unlock:
        mutex_unlock(&chip->reg_lock);
 
@@ -2148,6 +2262,15 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
        struct mii_bus *bus;
        int err;
 
+       if (external) {
+               mutex_lock(&chip->reg_lock);
+               err = mv88e6xxx_g2_scratch_gpio_set_smi(chip, true);
+               mutex_unlock(&chip->reg_lock);
+
+               if (err)
+                       return err;
+       }
+
        bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
        if (!bus)
                return -ENOMEM;
@@ -2170,12 +2293,19 @@ static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
        bus->write = mv88e6xxx_mdio_write;
        bus->parent = chip->dev;
 
+       if (!external) {
+               err = mv88e6xxx_g2_irq_mdio_setup(chip, bus);
+               if (err)
+                       return err;
+       }
+
        if (np)
                err = of_mdiobus_register(bus, np);
        else
                err = mdiobus_register(bus);
        if (err) {
                dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
+               mv88e6xxx_g2_irq_mdio_free(chip, bus);
                return err;
        }
 
@@ -2202,6 +2332,9 @@ static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
        list_for_each_entry(mdio_bus, &chip->mdios, list) {
                bus = mdio_bus->bus;
 
+               if (!mdio_bus->external)
+                       mv88e6xxx_g2_irq_mdio_free(chip, bus);
+
                mdiobus_unregister(bus);
        }
 }
@@ -2472,6 +2605,7 @@ static const struct mv88e6xxx_ops mv88e6141_ops = {
        .reset = mv88e6352_g1_reset,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+       .gpio_ops = &mv88e6352_gpio_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6161_ops = {
@@ -2602,6 +2736,7 @@ static const struct mv88e6xxx_ops mv88e6172_ops = {
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_power = mv88e6352_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6175_ops = {
@@ -2673,6 +2808,7 @@ static const struct mv88e6xxx_ops mv88e6176_ops = {
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_power = mv88e6352_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6185_ops = {
@@ -2736,6 +2872,7 @@ static const struct mv88e6xxx_ops mv88e6190_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6190x_ops = {
@@ -2771,6 +2908,7 @@ static const struct mv88e6xxx_ops mv88e6190x_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6191_ops = {
@@ -2843,6 +2981,8 @@ static const struct mv88e6xxx_ops mv88e6240_ops = {
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_power = mv88e6352_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6352_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6290_ops = {
@@ -2879,6 +3019,8 @@ static const struct mv88e6xxx_ops mv88e6290_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6390_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6320_ops = {
@@ -2913,6 +3055,8 @@ static const struct mv88e6xxx_ops mv88e6320_ops = {
        .reset = mv88e6352_g1_reset,
        .vtu_getnext = mv88e6185_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6352_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6321_ops = {
@@ -2945,6 +3089,8 @@ static const struct mv88e6xxx_ops mv88e6321_ops = {
        .reset = mv88e6352_g1_reset,
        .vtu_getnext = mv88e6185_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6352_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6341_ops = {
@@ -2981,6 +3127,8 @@ static const struct mv88e6xxx_ops mv88e6341_ops = {
        .reset = mv88e6352_g1_reset,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6390_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6350_ops = {
@@ -3049,6 +3197,7 @@ static const struct mv88e6xxx_ops mv88e6351_ops = {
        .reset = mv88e6352_g1_reset,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
+       .avb_ops = &mv88e6352_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6352_ops = {
@@ -3086,6 +3235,11 @@ static const struct mv88e6xxx_ops mv88e6352_ops = {
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_power = mv88e6352_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6352_avb_ops,
+       .serdes_get_sset_count = mv88e6352_serdes_get_sset_count,
+       .serdes_get_strings = mv88e6352_serdes_get_strings,
+       .serdes_get_stats = mv88e6352_serdes_get_stats,
 };
 
 static const struct mv88e6xxx_ops mv88e6390_ops = {
@@ -3124,6 +3278,8 @@ static const struct mv88e6xxx_ops mv88e6390_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6390_avb_ops,
 };
 
 static const struct mv88e6xxx_ops mv88e6390x_ops = {
@@ -3162,6 +3318,8 @@ static const struct mv88e6xxx_ops mv88e6390x_ops = {
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .gpio_ops = &mv88e6352_gpio_ops,
+       .avb_ops = &mv88e6390_avb_ops,
 };
 
 static const struct mv88e6xxx_info mv88e6xxx_table[] = {
@@ -3171,6 +3329,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6085",
                .num_databases = 4096,
                .num_ports = 10,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3191,6 +3350,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6095/88E6095F",
                .num_databases = 256,
                .num_ports = 11,
+               .num_internal_phys = 0,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3209,6 +3369,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6097/88E6097F",
                .num_databases = 4096,
                .num_ports = 11,
+               .num_internal_phys = 8,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3229,6 +3390,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6123",
                .num_databases = 4096,
                .num_ports = 3,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3249,6 +3411,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6131",
                .num_databases = 256,
                .num_ports = 8,
+               .num_internal_phys = 0,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3267,12 +3430,15 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6341",
                .num_databases = 4096,
                .num_ports = 6,
+               .num_internal_phys = 5,
+               .num_gpio = 11,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
                .global2_addr = 0x1c,
                .age_time_coeff = 3750,
                .atu_move_port_mask = 0x1f,
+               .g1_irqs = 9,
                .g2_irqs = 10,
                .pvt = true,
                .multi_chip = true,
@@ -3286,6 +3452,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6161",
                .num_databases = 4096,
                .num_ports = 6,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3306,6 +3473,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6165",
                .num_databases = 4096,
                .num_ports = 6,
+               .num_internal_phys = 0,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3326,6 +3494,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6171",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3346,6 +3515,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6172",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
+               .num_gpio = 15,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3366,6 +3537,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6175",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3386,6 +3558,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6176",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
+               .num_gpio = 15,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3406,6 +3580,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6185",
                .num_databases = 256,
                .num_ports = 10,
+               .num_internal_phys = 0,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3424,6 +3599,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6190",
                .num_databases = 4096,
                .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 11,
+               .num_gpio = 16,
                .max_vid = 8191,
                .port_base_addr = 0x0,
                .global1_addr = 0x1b,
@@ -3444,6 +3621,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6190X",
                .num_databases = 4096,
                .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 11,
+               .num_gpio = 16,
                .max_vid = 8191,
                .port_base_addr = 0x0,
                .global1_addr = 0x1b,
@@ -3464,6 +3643,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6191",
                .num_databases = 4096,
                .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 11,
                .max_vid = 8191,
                .port_base_addr = 0x0,
                .global1_addr = 0x1b,
@@ -3475,6 +3655,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
                .ops = &mv88e6191_ops,
        },
 
@@ -3484,6 +3665,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6240",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
+               .num_gpio = 15,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3495,6 +3678,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_EDSA,
+               .ptp_support = true,
                .ops = &mv88e6240_ops,
        },
 
@@ -3504,6 +3688,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6290",
                .num_databases = 4096,
                .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 11,
+               .num_gpio = 16,
                .max_vid = 8191,
                .port_base_addr = 0x0,
                .global1_addr = 0x1b,
@@ -3515,6 +3701,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
                .ops = &mv88e6290_ops,
        },
 
@@ -3524,16 +3711,20 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6320",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
+               .num_gpio = 15,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
                .global2_addr = 0x1c,
                .age_time_coeff = 15000,
                .g1_irqs = 8,
+               .g2_irqs = 10,
                .atu_move_port_mask = 0xf,
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_EDSA,
+               .ptp_support = true,
                .ops = &mv88e6320_ops,
        },
 
@@ -3543,15 +3734,19 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6321",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
+               .num_gpio = 15,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
                .global2_addr = 0x1c,
                .age_time_coeff = 15000,
                .g1_irqs = 8,
+               .g2_irqs = 10,
                .atu_move_port_mask = 0xf,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_EDSA,
+               .ptp_support = true,
                .ops = &mv88e6321_ops,
        },
 
@@ -3560,17 +3755,21 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .family = MV88E6XXX_FAMILY_6341,
                .name = "Marvell 88E6341",
                .num_databases = 4096,
+               .num_internal_phys = 5,
                .num_ports = 6,
+               .num_gpio = 11,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
                .global2_addr = 0x1c,
                .age_time_coeff = 3750,
                .atu_move_port_mask = 0x1f,
+               .g1_irqs = 9,
                .g2_irqs = 10,
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_EDSA,
+               .ptp_support = true,
                .ops = &mv88e6341_ops,
        },
 
@@ -3580,6 +3779,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6350",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3600,6 +3800,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6351",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3620,6 +3821,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6352",
                .num_databases = 4096,
                .num_ports = 7,
+               .num_internal_phys = 5,
+               .num_gpio = 15,
                .max_vid = 4095,
                .port_base_addr = 0x10,
                .global1_addr = 0x1b,
@@ -3631,6 +3834,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_EDSA,
+               .ptp_support = true,
                .ops = &mv88e6352_ops,
        },
        [MV88E6390] = {
@@ -3639,6 +3843,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6390",
                .num_databases = 4096,
                .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 11,
+               .num_gpio = 16,
                .max_vid = 8191,
                .port_base_addr = 0x0,
                .global1_addr = 0x1b,
@@ -3650,6 +3856,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
                .ops = &mv88e6390_ops,
        },
        [MV88E6390X] = {
@@ -3658,6 +3865,8 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .name = "Marvell 88E6390X",
                .num_databases = 4096,
                .num_ports = 11,        /* 10 + Z80 */
+               .num_internal_phys = 11,
+               .num_gpio = 16,
                .max_vid = 8191,
                .port_base_addr = 0x0,
                .global1_addr = 0x1b,
@@ -3669,6 +3878,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
                .pvt = true,
                .multi_chip = true,
                .tag_protocol = DSA_TAG_PROTO_DSA,
+               .ptp_support = true,
                .ops = &mv88e6390x_ops,
        },
 };
@@ -3880,6 +4090,11 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
        .port_mdb_del           = mv88e6xxx_port_mdb_del,
        .crosschip_bridge_join  = mv88e6xxx_crosschip_bridge_join,
        .crosschip_bridge_leave = mv88e6xxx_crosschip_bridge_leave,
+       .port_hwtstamp_set      = mv88e6xxx_port_hwtstamp_set,
+       .port_hwtstamp_get      = mv88e6xxx_port_hwtstamp_get,
+       .port_txtstamp          = mv88e6xxx_port_txtstamp,
+       .port_rxtstamp          = mv88e6xxx_port_rxtstamp,
+       .get_ts_info            = mv88e6xxx_get_ts_info,
 };
 
 static struct dsa_switch_driver mv88e6xxx_switch_drv = {
@@ -3959,33 +4174,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;
@@ -3999,20 +4215,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;
 }
@@ -4022,19 +4237,27 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
        struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
        struct mv88e6xxx_chip *chip = ds->priv;
 
+       if (chip->info->ptp_support) {
+               mv88e6xxx_hwtstamp_free(chip);
+               mv88e6xxx_ptp_free(chip);
+       }
+
        mv88e6xxx_phy_destroy(chip);
        mv88e6xxx_unregister_switch(chip);
        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);
-               mutex_lock(&chip->reg_lock);
+       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);
+
+       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);
 }
 
 static const struct of_device_id mv88e6xxx_of_match[] = {