return 0;
 }
 
-int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port, int link,
-                            int speed, int duplex, int pause,
-                            phy_interface_t mode)
+static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
+                                   int link, int speed, int duplex, int pause,
+                                   phy_interface_t mode)
 {
        struct phylink_link_state state;
        int err;
        return port < chip->info->num_internal_phys;
 }
 
+static int mv88e6xxx_serdes_pcs_get_state(struct dsa_switch *ds, int port,
+                                         struct phylink_link_state *state)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       u8 lane;
+       int err;
+
+       mv88e6xxx_reg_lock(chip);
+       lane = mv88e6xxx_serdes_get_lane(chip, port);
+       if (lane && chip->info->ops->serdes_pcs_get_state)
+               err = chip->info->ops->serdes_pcs_get_state(chip, port, lane,
+                                                           state);
+       else
+               err = -EOPNOTSUPP;
+       mv88e6xxx_reg_unlock(chip);
+
+       return err;
+}
+
+static int mv88e6xxx_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+                                      unsigned int mode,
+                                      phy_interface_t interface,
+                                      const unsigned long *advertise)
+{
+       const struct mv88e6xxx_ops *ops = chip->info->ops;
+       u8 lane;
+
+       if (ops->serdes_pcs_config) {
+               lane = mv88e6xxx_serdes_get_lane(chip, port);
+               if (lane)
+                       return ops->serdes_pcs_config(chip, port, lane, mode,
+                                                     interface, advertise);
+       }
+
+       return 0;
+}
+
+static void mv88e6xxx_serdes_pcs_an_restart(struct dsa_switch *ds, int port)
+{
+       struct mv88e6xxx_chip *chip = ds->priv;
+       const struct mv88e6xxx_ops *ops;
+       int err = 0;
+       u8 lane;
+
+       ops = chip->info->ops;
+
+       if (ops->serdes_pcs_an_restart) {
+               mv88e6xxx_reg_lock(chip);
+               lane = mv88e6xxx_serdes_get_lane(chip, port);
+               if (lane)
+                       err = ops->serdes_pcs_an_restart(chip, port, lane);
+               mv88e6xxx_reg_unlock(chip);
+
+               if (err)
+                       dev_err(ds->dev, "p%d: failed to restart AN\n", port);
+       }
+}
+
+static int mv88e6xxx_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+                                       unsigned int mode,
+                                       int speed, int duplex)
+{
+       const struct mv88e6xxx_ops *ops = chip->info->ops;
+       u8 lane;
+
+       if (!phylink_autoneg_inband(mode) && ops->serdes_pcs_link_up) {
+               lane = mv88e6xxx_serdes_get_lane(chip, port);
+               if (lane)
+                       return ops->serdes_pcs_link_up(chip, port, lane,
+                                                      speed, duplex);
+       }
+
+       return 0;
+}
+
 static void mv88e6065_phylink_validate(struct mv88e6xxx_chip *chip, int port,
                                       unsigned long *mask,
                                       struct phylink_link_state *state)
        phylink_helper_basex_speed(state);
 }
 
-static int mv88e6xxx_link_state(struct dsa_switch *ds, int port,
-                               struct phylink_link_state *state)
-{
-       struct mv88e6xxx_chip *chip = ds->priv;
-       int err;
-
-       mv88e6xxx_reg_lock(chip);
-       if (chip->info->ops->port_link_state)
-               err = chip->info->ops->port_link_state(chip, port, state);
-       else
-               err = -EOPNOTSUPP;
-       mv88e6xxx_reg_unlock(chip);
-
-       return err;
-}
-
 static void mv88e6xxx_mac_config(struct dsa_switch *ds, int port,
                                 unsigned int mode,
                                 const struct phylink_link_state *state)
         * gets in the way.
         */
        err = mv88e6xxx_port_config_interface(chip, port, state->interface);
+       if (err && err != -EOPNOTSUPP)
+               goto err_unlock;
+
+       err = mv88e6xxx_serdes_pcs_config(chip, port, mode, state->interface,
+                                         state->advertising);
+       /* FIXME: we should restart negotiation if something changed - which
+        * is something we get if we convert to using phylinks PCS operations.
+        */
+       if (err > 0)
+               err = 0;
+
+err_unlock:
        mv88e6xxx_reg_unlock(chip);
 
        if (err && err != -EOPNOTSUPP)
                /* FIXME: for an automedia port, should we force the link
                 * down here - what if the link comes up due to "other" media
                 * while we're bringing the port up, how is the exclusivity
-                * handled in the Marvell hardware? E.g. port 4 on 88E6532
+                * handled in the Marvell hardware? E.g. port 2 on 88E6390
                 * shared between internal PHY and Serdes.
                 */
+               err = mv88e6xxx_serdes_pcs_link_up(chip, port, mode, speed,
+                                                  duplex);
+               if (err)
+                       goto error;
+
                if (ops->port_set_speed) {
                        err = ops->port_set_speed(chip, port, speed);
                        if (err && err != -EOPNOTSUPP)
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6341_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_get_lane = mv88e6352_serdes_get_lane,
+       .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6352_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
        .serdes_power = mv88e6352_serdes_power,
        .serdes_get_regs_len = mv88e6352_serdes_get_regs_len,
        .serdes_get_regs = mv88e6352_serdes_get_regs,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_get_lane = mv88e6352_serdes_get_lane,
+       .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6352_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
        .serdes_power = mv88e6352_serdes_power,
        .serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6352_serdes_irq_enable,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6390_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6390x_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6390_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_get_lane = mv88e6352_serdes_get_lane,
+       .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6352_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
        .serdes_power = mv88e6352_serdes_power,
        .serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6352_serdes_irq_enable,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6390_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6341_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_getnext = mv88e6352_g1_vtu_getnext,
        .vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
        .serdes_get_lane = mv88e6352_serdes_get_lane,
+       .serdes_pcs_get_state = mv88e6352_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6352_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6352_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6352_serdes_pcs_link_up,
        .serdes_power = mv88e6352_serdes_power,
        .serdes_irq_mapping = mv88e6352_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6352_serdes_irq_enable,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6390_serdes_get_lane,
+       /* Check status register pause & lpa register */
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
        .serdes_power = mv88e6390_serdes_power,
        .serdes_get_lane = mv88e6390x_serdes_get_lane,
+       .serdes_pcs_get_state = mv88e6390_serdes_pcs_get_state,
+       .serdes_pcs_config = mv88e6390_serdes_pcs_config,
+       .serdes_pcs_an_restart = mv88e6390_serdes_pcs_an_restart,
+       .serdes_pcs_link_up = mv88e6390_serdes_pcs_link_up,
        .serdes_irq_mapping = mv88e6390_serdes_irq_mapping,
        .serdes_irq_enable = mv88e6390_serdes_irq_enable,
        .serdes_irq_status = mv88e6390_serdes_irq_status,
        .setup                  = mv88e6xxx_setup,
        .teardown               = mv88e6xxx_teardown,
        .phylink_validate       = mv88e6xxx_validate,
-       .phylink_mac_link_state = mv88e6xxx_link_state,
+       .phylink_mac_link_state = mv88e6xxx_serdes_pcs_get_state,
        .phylink_mac_config     = mv88e6xxx_mac_config,
+       .phylink_mac_an_restart = mv88e6xxx_serdes_pcs_an_restart,
        .phylink_mac_link_down  = mv88e6xxx_mac_link_down,
        .phylink_mac_link_up    = mv88e6xxx_mac_link_up,
        .get_strings            = mv88e6xxx_get_strings,
 
        return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
 }
 
+static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
+                                         u16 status, u16 lpa,
+                                         struct phylink_link_state *state)
+{
+       if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
+               state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
+               state->duplex = status &
+                               MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
+                                                DUPLEX_FULL : DUPLEX_HALF;
+
+               if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
+                       state->pause |= MLO_PAUSE_TX;
+               if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
+                       state->pause |= MLO_PAUSE_RX;
+
+               switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
+               case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
+                       if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+                               state->speed = SPEED_2500;
+                       else
+                               state->speed = SPEED_1000;
+                       break;
+               case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
+                       state->speed = SPEED_100;
+                       break;
+               case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
+                       state->speed = SPEED_10;
+                       break;
+               default:
+                       dev_err(chip->dev, "invalid PHY speed\n");
+                       return -EINVAL;
+               }
+       } else {
+               state->link = false;
+       }
+
+       if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
+               mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
+                                      ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
+       else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
+               mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
+                                      ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
+
+       return 0;
+}
+
 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
                           bool up)
 {
        return err;
 }
 
+int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+                               u8 lane, unsigned int mode,
+                               phy_interface_t interface,
+                               const unsigned long *advertise)
+{
+       u16 adv, bmcr, val;
+       bool changed;
+       int err;
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+               adv = 0x0001;
+               break;
+
+       case PHY_INTERFACE_MODE_1000BASEX:
+               adv = linkmode_adv_to_mii_adv_x(advertise,
+                                       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
+               break;
+
+       default:
+               return 0;
+       }
+
+       err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val);
+       if (err)
+               return err;
+
+       changed = val != adv;
+       if (changed) {
+               err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv);
+               if (err)
+                       return err;
+       }
+
+       err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
+       if (err)
+               return err;
+
+       if (phylink_autoneg_inband(mode))
+               bmcr = val | BMCR_ANENABLE;
+       else
+               bmcr = val & ~BMCR_ANENABLE;
+
+       if (bmcr == val)
+               return changed;
+
+       return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
+}
+
+int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+                                  u8 lane, struct phylink_link_state *state)
+{
+       u16 lpa, status;
+       int err;
+
+       err = mv88e6352_serdes_read(chip, 0x11, &status);
+       if (err) {
+               dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
+               return err;
+       }
+
+       err = mv88e6352_serdes_read(chip, MII_LPA, &lpa);
+       if (err) {
+               dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
+               return err;
+       }
+
+       return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
+}
+
+int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+                                   u8 lane)
+{
+       u16 bmcr;
+       int err;
+
+       err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr);
+       if (err)
+               return err;
+
+       return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART);
+}
+
+int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+                                u8 lane, int speed, int duplex)
+{
+       u16 val, bmcr;
+       int err;
+
+       err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
+       if (err)
+               return err;
+
+       bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
+       switch (speed) {
+       case SPEED_1000:
+               bmcr |= BMCR_SPEED1000;
+               break;
+       case SPEED_100:
+               bmcr |= BMCR_SPEED100;
+               break;
+       case SPEED_10:
+               break;
+       }
+
+       if (duplex == DUPLEX_FULL)
+               bmcr |= BMCR_FULLDPLX;
+
+       if (bmcr == val)
+               return 0;
+
+       return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
+}
+
 u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
 {
        u8 cmode = chip->ports[port].cmode;
        return err;
 }
 
-static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
-                                           int port, u8 lane)
+int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
+                               u8 lane, unsigned int mode,
+                               phy_interface_t interface,
+                               const unsigned long *advertise)
 {
-       u8 cmode = chip->ports[port].cmode;
-       struct dsa_switch *ds = chip->ds;
-       int duplex = DUPLEX_UNKNOWN;
-       int speed = SPEED_UNKNOWN;
-       phy_interface_t mode;
-       int link, err;
-       u16 status;
+       u16 val, bmcr, adv;
+       bool changed;
+       int err;
+
+       switch (interface) {
+       case PHY_INTERFACE_MODE_SGMII:
+               adv = 0x0001;
+               break;
+
+       case PHY_INTERFACE_MODE_1000BASEX:
+               adv = linkmode_adv_to_mii_adv_x(advertise,
+                                       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
+               break;
+
+       case PHY_INTERFACE_MODE_2500BASEX:
+               adv = linkmode_adv_to_mii_adv_x(advertise,
+                                       ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
+               break;
+
+       default:
+               return 0;
+       }
+
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_ADVERTISE, &val);
+       if (err)
+               return err;
+
+       changed = val != adv;
+       if (changed) {
+               err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+                                            MV88E6390_SGMII_ADVERTISE, adv);
+               if (err)
+                       return err;
+       }
+
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_BMCR, &val);
+       if (err)
+               return err;
+
+       if (phylink_autoneg_inband(mode))
+               bmcr = val | BMCR_ANENABLE;
+       else
+               bmcr = val & ~BMCR_ANENABLE;
+
+       /* setting ANENABLE triggers a restart of negotiation */
+       if (bmcr == val)
+               return changed;
+
+       return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+                                     MV88E6390_SGMII_BMCR, bmcr);
+}
+
+int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
+                                  u8 lane, struct phylink_link_state *state)
+{
+       u16 lpa, status;
+       int err;
 
        err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
                                    MV88E6390_SGMII_PHY_STATUS, &status);
        if (err) {
-               dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err);
-               return;
+               dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
+               return err;
        }
 
-       link = status & MV88E6390_SGMII_PHY_STATUS_LINK ?
-              LINK_FORCED_UP : LINK_FORCED_DOWN;
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_LPA, &lpa);
+       if (err) {
+               dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
+               return err;
+       }
 
-       if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
-               duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
-                        DUPLEX_FULL : DUPLEX_HALF;
+       return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
+}
 
-               switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
-               case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
-                       if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
-                               speed = SPEED_2500;
-                       else
-                               speed = SPEED_1000;
-                       break;
-               case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
-                       speed = SPEED_100;
-                       break;
-               case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
-                       speed = SPEED_10;
-                       break;
-               default:
-                       dev_err(chip->dev, "invalid PHY speed\n");
-                       return;
-               }
-       }
+int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
+                                   u8 lane)
+{
+       u16 bmcr;
+       int err;
 
-       switch (cmode) {
-       case MV88E6XXX_PORT_STS_CMODE_SGMII:
-               mode = PHY_INTERFACE_MODE_SGMII;
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_BMCR, &bmcr);
+       if (err)
+               return err;
+
+       return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+                                     MV88E6390_SGMII_BMCR,
+                                     bmcr | BMCR_ANRESTART);
+}
+
+int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
+                                u8 lane, int speed, int duplex)
+{
+       u16 val, bmcr;
+       int err;
+
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_BMCR, &val);
+       if (err)
+               return err;
+
+       bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
+       switch (speed) {
+       case SPEED_2500:
+       case SPEED_1000:
+               bmcr |= BMCR_SPEED1000;
                break;
-       case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
-               mode = PHY_INTERFACE_MODE_1000BASEX;
+       case SPEED_100:
+               bmcr |= BMCR_SPEED100;
                break;
-       case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
-               mode = PHY_INTERFACE_MODE_2500BASEX;
+       case SPEED_10:
                break;
-       default:
-               mode = PHY_INTERFACE_MODE_NA;
        }
 
-       err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex,
-                                      PAUSE_OFF, mode);
-       if (err)
-               dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n",
-                       err);
-       else
-               dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP);
+       if (duplex == DUPLEX_FULL)
+               bmcr |= BMCR_FULLDPLX;
+
+       if (bmcr == val)
+               return 0;
+
+       return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
+                                     MV88E6390_SGMII_BMCR, bmcr);
+}
+
+static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
+                                           int port, u8 lane)
+{
+       u16 status;
+       int err;
+
+       err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
+                                   MV88E6390_SGMII_PHY_STATUS, &status);
+       if (err) {
+               dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err);
+               return;
+       }
+
+       dsa_port_phylink_mac_change(chip->ds, port,
+                               !!(status & MV88E6390_SGMII_PHY_STATUS_LINK));
 }
 
 static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,