int err;
 
        mutex_lock(&chip->reg_lock);
+
        err = mv88e6xxx_serdes_power(chip, port, true);
+
+       if (!err && chip->info->ops->serdes_irq_setup)
+               err = chip->info->ops->serdes_irq_setup(chip, port);
+
        mutex_unlock(&chip->reg_lock);
 
        return err;
        struct mv88e6xxx_chip *chip = ds->priv;
 
        mutex_lock(&chip->reg_lock);
+
+       if (chip->info->ops->serdes_irq_free)
+               chip->info->ops->serdes_irq_free(chip, port);
+
        if (mv88e6xxx_serdes_power(chip, port, false))
                dev_err(chip->dev, "failed to power off SERDES\n");
+
        mutex_unlock(&chip->reg_lock);
 }
 
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .phylink_validate = mv88e6390_phylink_validate,
 };
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390x_serdes_power,
+       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .phylink_validate = mv88e6390x_phylink_validate,
 };
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390_serdes_irq_free,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
        .phylink_validate = mv88e6390_phylink_validate,
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
+       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
        .vtu_getnext = mv88e6390_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390x_serdes_power,
+       .serdes_irq_setup = mv88e6390_serdes_irq_setup,
+       .serdes_irq_free = mv88e6390_serdes_irq_free,
        .gpio_ops = &mv88e6352_gpio_ops,
        .avb_ops = &mv88e6390_avb_ops,
        .ptp_ops = &mv88e6352_ptp_ops,
 
        u64 vtu_member_violation;
        u64 vtu_miss_violation;
        u8 cmode;
+       int serdes_irq;
 };
 
 struct mv88e6xxx_chip {
        /* Power on/off a SERDES interface */
        int (*serdes_power)(struct mv88e6xxx_chip *chip, int port, bool on);
 
+       /* SERDES interrupt handling */
+       int (*serdes_irq_setup)(struct mv88e6xxx_chip *chip, int port);
+       void (*serdes_irq_free)(struct mv88e6xxx_chip *chip, int port);
+
        /* Statistics from the SERDES interface */
        int (*serdes_get_sset_count)(struct mv88e6xxx_chip *chip, int port);
        int (*serdes_get_strings)(struct mv88e6xxx_chip *chip,  int port,
 
  * (at your option) any later version.
  */
 
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
 #include <linux/mii.h>
 
 #include "chip.h"
        return 0;
 }
 
+static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
+                                           int port, int lane)
+{
+       struct dsa_switch *ds = chip->ds;
+       u16 status;
+       bool up;
+
+       mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                             MV88E6390_SGMII_STATUS, &status);
+
+       /* Status must be read twice in order to give the current link
+        * status. Otherwise the change in link status since the last
+        * read of the register is returned.
+        */
+       mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                             MV88E6390_SGMII_STATUS, &status);
+       up = status & MV88E6390_SGMII_STATUS_LINK;
+
+       dsa_port_phylink_mac_change(ds, port, up);
+}
+
+static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
+                                            int lane)
+{
+       return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+                                     MV88E6390_SGMII_INT_ENABLE,
+                                     MV88E6390_SGMII_INT_LINK_DOWN |
+                                     MV88E6390_SGMII_INT_LINK_UP);
+}
+
+static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip,
+                                             int lane)
+{
+       return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+                                     MV88E6390_SGMII_INT_ENABLE, 0);
+}
+
+int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
+                               int lane)
+{
+       u8 cmode = chip->ports[port].cmode;
+       int err = 0;
+
+       switch (cmode) {
+       case MV88E6XXX_PORT_STS_CMODE_SGMII:
+       case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
+       case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+               err = mv88e6390_serdes_irq_enable_sgmii(chip, lane);
+       }
+
+       return err;
+}
+
+int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
+                                int lane)
+{
+       u8 cmode = chip->ports[port].cmode;
+       int err = 0;
+
+       switch (cmode) {
+       case MV88E6XXX_PORT_STS_CMODE_SGMII:
+       case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
+       case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+               err = mv88e6390_serdes_irq_disable_sgmii(chip, lane);
+       }
+
+       return err;
+}
+
+static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
+                                            int lane, u16 *status)
+{
+       int err;
+
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_INT_STATUS, status);
+
+       return err;
+}
+
+static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
+{
+       struct mv88e6xxx_port *port = dev_id;
+       struct mv88e6xxx_chip *chip = port->chip;
+       irqreturn_t ret = IRQ_NONE;
+       u8 cmode = port->cmode;
+       u16 status;
+       int lane;
+       int err;
+
+       lane = mv88e6390x_serdes_get_lane(chip, port->port);
+
+       mutex_lock(&chip->reg_lock);
+
+       switch (cmode) {
+       case MV88E6XXX_PORT_STS_CMODE_SGMII:
+       case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
+       case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
+               err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
+               if (err)
+                       goto out;
+               if (status && (MV88E6390_SGMII_INT_LINK_DOWN ||
+                              MV88E6390_SGMII_INT_LINK_UP)) {
+                       ret = IRQ_HANDLED;
+                       mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane);
+               }
+       }
+out:
+       mutex_unlock(&chip->reg_lock);
+
+       return ret;
+}
+
+int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
+{
+       int lane;
+       int err;
+
+       /* Only support ports 9 and 10 at the moment */
+       if (port < 9)
+               return 0;
+
+       lane = mv88e6390x_serdes_get_lane(chip, port);
+
+       if (lane == -ENODEV)
+               return 0;
+
+       if (lane < 0)
+               return lane;
+
+       chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
+                                                       port);
+       if (chip->ports[port].serdes_irq < 0) {
+               dev_err(chip->dev, "Unable to map SERDES irq: %d\n",
+                       chip->ports[port].serdes_irq);
+               return chip->ports[port].serdes_irq;
+       }
+
+       /* Requesting the IRQ will trigger irq callbacks. So we cannot
+        * hold the reg_lock.
+        */
+       mutex_unlock(&chip->reg_lock);
+       err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
+                                  mv88e6390_serdes_thread_fn,
+                                  IRQF_ONESHOT, "mv88e6xxx-serdes",
+                                  &chip->ports[port]);
+       mutex_lock(&chip->reg_lock);
+
+       if (err) {
+               dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
+                       err);
+               return err;
+       }
+
+       return mv88e6390_serdes_irq_enable(chip, port, lane);
+}
+
+void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
+{
+       int lane = mv88e6390x_serdes_get_lane(chip, port);
+
+       if (port < 9)
+               return;
+
+       if (lane < 0)
+               return;
+
+       mv88e6390_serdes_irq_disable(chip, port, lane);
+
+       /* Freeing the IRQ will trigger irq callbacks. So we cannot
+        * hold the reg_lock.
+        */
+       mutex_unlock(&chip->reg_lock);
+       free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
+       mutex_lock(&chip->reg_lock);
+}
+
 int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
 {
        u8 cmode = chip->ports[port].cmode;
 
 #define MV88E6390_SGMII_CONTROL_RESET          BIT(15)
 #define MV88E6390_SGMII_CONTROL_LOOPBACK       BIT(14)
 #define MV88E6390_SGMII_CONTROL_PDOWN          BIT(11)
+#define MV88E6390_SGMII_STATUS         0x2001
+#define MV88E6390_SGMII_STATUS_AN_DONE         BIT(5)
+#define MV88E6390_SGMII_STATUS_REMOTE_FAULT    BIT(4)
+#define MV88E6390_SGMII_STATUS_LINK            BIT(2)
+#define MV88E6390_SGMII_INT_ENABLE     0xa001
+#define MV88E6390_SGMII_INT_SPEED_CHANGE       BIT(14)
+#define MV88E6390_SGMII_INT_DUPLEX_CHANGE      BIT(13)
+#define MV88E6390_SGMII_INT_PAGE_RX            BIT(12)
+#define MV88E6390_SGMII_INT_AN_COMPLETE                BIT(11)
+#define MV88E6390_SGMII_INT_LINK_DOWN          BIT(10)
+#define MV88E6390_SGMII_INT_LINK_UP            BIT(9)
+#define MV88E6390_SGMII_INT_SYMBOL_ERROR       BIT(8)
+#define MV88E6390_SGMII_INT_FALSE_CARRIER      BIT(7)
+#define MV88E6390_SGMII_INT_STATUS     0xa002
 
 int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
 int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on);
+int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port);
+void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port);
 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port);
 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
                                 int port, uint8_t *data);