12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 26d91782fSAndrew Lunn /* 36d91782fSAndrew Lunn * Marvell 88E6xxx SERDES manipulation, via SMI bus 46d91782fSAndrew Lunn * 56d91782fSAndrew Lunn * Copyright (c) 2008 Marvell Semiconductor 66d91782fSAndrew Lunn * 76d91782fSAndrew Lunn * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> 86d91782fSAndrew Lunn */ 96d91782fSAndrew Lunn 10efd1ba6aSAndrew Lunn #include <linux/interrupt.h> 11efd1ba6aSAndrew Lunn #include <linux/irqdomain.h> 126d91782fSAndrew Lunn #include <linux/mii.h> 136d91782fSAndrew Lunn 144d5f2ba7SVivien Didelot #include "chip.h" 156335e9f2SAndrew Lunn #include "global2.h" 166d91782fSAndrew Lunn #include "phy.h" 176d91782fSAndrew Lunn #include "port.h" 186d91782fSAndrew Lunn #include "serdes.h" 196d91782fSAndrew Lunn 206d91782fSAndrew Lunn static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg, 216d91782fSAndrew Lunn u16 *val) 226d91782fSAndrew Lunn { 236d91782fSAndrew Lunn return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, 246d91782fSAndrew Lunn MV88E6352_SERDES_PAGE_FIBER, 256d91782fSAndrew Lunn reg, val); 266d91782fSAndrew Lunn } 276d91782fSAndrew Lunn 286d91782fSAndrew Lunn static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, 296d91782fSAndrew Lunn u16 val) 306d91782fSAndrew Lunn { 316d91782fSAndrew Lunn return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, 326d91782fSAndrew Lunn MV88E6352_SERDES_PAGE_FIBER, 336d91782fSAndrew Lunn reg, val); 346d91782fSAndrew Lunn } 356d91782fSAndrew Lunn 36e6891c76SAndrew Lunn static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, 37e6891c76SAndrew Lunn int lane, int device, int reg, u16 *val) 38e6891c76SAndrew Lunn { 39e6891c76SAndrew Lunn int reg_c45 = MII_ADDR_C45 | device << 16 | reg; 40e6891c76SAndrew Lunn 41e6891c76SAndrew Lunn return mv88e6xxx_phy_read(chip, lane, reg_c45, val); 42e6891c76SAndrew Lunn } 43e6891c76SAndrew Lunn 44e6891c76SAndrew Lunn static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, 45e6891c76SAndrew Lunn int lane, int device, int reg, u16 val) 46e6891c76SAndrew Lunn { 47e6891c76SAndrew Lunn int reg_c45 = MII_ADDR_C45 | device << 16 | reg; 48e6891c76SAndrew Lunn 49e6891c76SAndrew Lunn return mv88e6xxx_phy_write(chip, lane, reg_c45, val); 50e6891c76SAndrew Lunn } 51e6891c76SAndrew Lunn 52a5a6858bSRussell King static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, 53a5a6858bSRussell King u16 status, u16 lpa, 54a5a6858bSRussell King struct phylink_link_state *state) 55a5a6858bSRussell King { 56a5a6858bSRussell King if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) { 57a5a6858bSRussell King state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK); 58a5a6858bSRussell King state->duplex = status & 59a5a6858bSRussell King MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ? 60a5a6858bSRussell King DUPLEX_FULL : DUPLEX_HALF; 61a5a6858bSRussell King 62a5a6858bSRussell King if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE) 63a5a6858bSRussell King state->pause |= MLO_PAUSE_TX; 64a5a6858bSRussell King if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE) 65a5a6858bSRussell King state->pause |= MLO_PAUSE_RX; 66a5a6858bSRussell King 67a5a6858bSRussell King switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) { 68a5a6858bSRussell King case MV88E6390_SGMII_PHY_STATUS_SPEED_1000: 69a5a6858bSRussell King if (state->interface == PHY_INTERFACE_MODE_2500BASEX) 70a5a6858bSRussell King state->speed = SPEED_2500; 71a5a6858bSRussell King else 72a5a6858bSRussell King state->speed = SPEED_1000; 73a5a6858bSRussell King break; 74a5a6858bSRussell King case MV88E6390_SGMII_PHY_STATUS_SPEED_100: 75a5a6858bSRussell King state->speed = SPEED_100; 76a5a6858bSRussell King break; 77a5a6858bSRussell King case MV88E6390_SGMII_PHY_STATUS_SPEED_10: 78a5a6858bSRussell King state->speed = SPEED_10; 79a5a6858bSRussell King break; 80a5a6858bSRussell King default: 81a5a6858bSRussell King dev_err(chip->dev, "invalid PHY speed\n"); 82a5a6858bSRussell King return -EINVAL; 83a5a6858bSRussell King } 84a5a6858bSRussell King } else { 85a5a6858bSRussell King state->link = false; 86a5a6858bSRussell King } 87a5a6858bSRussell King 88a5a6858bSRussell King if (state->interface == PHY_INTERFACE_MODE_2500BASEX) 89a5a6858bSRussell King mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, 90a5a6858bSRussell King ETHTOOL_LINK_MODE_2500baseX_Full_BIT); 91a5a6858bSRussell King else if (state->interface == PHY_INTERFACE_MODE_1000BASEX) 92a5a6858bSRussell King mii_lpa_mod_linkmode_x(state->lp_advertising, lpa, 93a5a6858bSRussell King ETHTOOL_LINK_MODE_1000baseX_Full_BIT); 94a5a6858bSRussell King 95a5a6858bSRussell King return 0; 96a5a6858bSRussell King } 97a5a6858bSRussell King 98dc272f60SVivien Didelot int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, 99dc272f60SVivien Didelot bool up) 1006d91782fSAndrew Lunn { 1016d91782fSAndrew Lunn u16 val, new_val; 1026d91782fSAndrew Lunn int err; 1036d91782fSAndrew Lunn 1046d91782fSAndrew Lunn err = mv88e6352_serdes_read(chip, MII_BMCR, &val); 1056d91782fSAndrew Lunn if (err) 1066d91782fSAndrew Lunn return err; 1076d91782fSAndrew Lunn 108dc272f60SVivien Didelot if (up) 1096d91782fSAndrew Lunn new_val = val & ~BMCR_PDOWN; 1106d91782fSAndrew Lunn else 1116d91782fSAndrew Lunn new_val = val | BMCR_PDOWN; 1126d91782fSAndrew Lunn 1136d91782fSAndrew Lunn if (val != new_val) 1146d91782fSAndrew Lunn err = mv88e6352_serdes_write(chip, MII_BMCR, new_val); 1156d91782fSAndrew Lunn 1166d91782fSAndrew Lunn return err; 1176d91782fSAndrew Lunn } 1186d91782fSAndrew Lunn 119a5a6858bSRussell King int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, 120a5a6858bSRussell King u8 lane, unsigned int mode, 121a5a6858bSRussell King phy_interface_t interface, 122a5a6858bSRussell King const unsigned long *advertise) 123a5a6858bSRussell King { 124a5a6858bSRussell King u16 adv, bmcr, val; 125a5a6858bSRussell King bool changed; 126a5a6858bSRussell King int err; 127a5a6858bSRussell King 128a5a6858bSRussell King switch (interface) { 129a5a6858bSRussell King case PHY_INTERFACE_MODE_SGMII: 130a5a6858bSRussell King adv = 0x0001; 131a5a6858bSRussell King break; 132a5a6858bSRussell King 133a5a6858bSRussell King case PHY_INTERFACE_MODE_1000BASEX: 134a5a6858bSRussell King adv = linkmode_adv_to_mii_adv_x(advertise, 135a5a6858bSRussell King ETHTOOL_LINK_MODE_1000baseX_Full_BIT); 136a5a6858bSRussell King break; 137a5a6858bSRussell King 138a5a6858bSRussell King default: 139a5a6858bSRussell King return 0; 140a5a6858bSRussell King } 141a5a6858bSRussell King 142a5a6858bSRussell King err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val); 143a5a6858bSRussell King if (err) 144a5a6858bSRussell King return err; 145a5a6858bSRussell King 146a5a6858bSRussell King changed = val != adv; 147a5a6858bSRussell King if (changed) { 148a5a6858bSRussell King err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv); 149a5a6858bSRussell King if (err) 150a5a6858bSRussell King return err; 151a5a6858bSRussell King } 152a5a6858bSRussell King 153a5a6858bSRussell King err = mv88e6352_serdes_read(chip, MII_BMCR, &val); 154a5a6858bSRussell King if (err) 155a5a6858bSRussell King return err; 156a5a6858bSRussell King 157a5a6858bSRussell King if (phylink_autoneg_inband(mode)) 158a5a6858bSRussell King bmcr = val | BMCR_ANENABLE; 159a5a6858bSRussell King else 160a5a6858bSRussell King bmcr = val & ~BMCR_ANENABLE; 161a5a6858bSRussell King 162a5a6858bSRussell King if (bmcr == val) 163a5a6858bSRussell King return changed; 164a5a6858bSRussell King 165a5a6858bSRussell King return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); 166a5a6858bSRussell King } 167a5a6858bSRussell King 168a5a6858bSRussell King int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, 169a5a6858bSRussell King u8 lane, struct phylink_link_state *state) 170a5a6858bSRussell King { 171a5a6858bSRussell King u16 lpa, status; 172a5a6858bSRussell King int err; 173a5a6858bSRussell King 174a5a6858bSRussell King err = mv88e6352_serdes_read(chip, 0x11, &status); 175a5a6858bSRussell King if (err) { 176a5a6858bSRussell King dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); 177a5a6858bSRussell King return err; 178a5a6858bSRussell King } 179a5a6858bSRussell King 180a5a6858bSRussell King err = mv88e6352_serdes_read(chip, MII_LPA, &lpa); 181a5a6858bSRussell King if (err) { 182a5a6858bSRussell King dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); 183a5a6858bSRussell King return err; 184a5a6858bSRussell King } 185a5a6858bSRussell King 186a5a6858bSRussell King return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state); 187a5a6858bSRussell King } 188a5a6858bSRussell King 189a5a6858bSRussell King int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, 190a5a6858bSRussell King u8 lane) 191a5a6858bSRussell King { 192a5a6858bSRussell King u16 bmcr; 193a5a6858bSRussell King int err; 194a5a6858bSRussell King 195a5a6858bSRussell King err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr); 196a5a6858bSRussell King if (err) 197a5a6858bSRussell King return err; 198a5a6858bSRussell King 199a5a6858bSRussell King return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART); 200a5a6858bSRussell King } 201a5a6858bSRussell King 202a5a6858bSRussell King int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, 203a5a6858bSRussell King u8 lane, int speed, int duplex) 204a5a6858bSRussell King { 205a5a6858bSRussell King u16 val, bmcr; 206a5a6858bSRussell King int err; 207a5a6858bSRussell King 208a5a6858bSRussell King err = mv88e6352_serdes_read(chip, MII_BMCR, &val); 209a5a6858bSRussell King if (err) 210a5a6858bSRussell King return err; 211a5a6858bSRussell King 212a5a6858bSRussell King bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); 213a5a6858bSRussell King switch (speed) { 214a5a6858bSRussell King case SPEED_1000: 215a5a6858bSRussell King bmcr |= BMCR_SPEED1000; 216a5a6858bSRussell King break; 217a5a6858bSRussell King case SPEED_100: 218a5a6858bSRussell King bmcr |= BMCR_SPEED100; 219a5a6858bSRussell King break; 220a5a6858bSRussell King case SPEED_10: 221a5a6858bSRussell King break; 222a5a6858bSRussell King } 223a5a6858bSRussell King 224a5a6858bSRussell King if (duplex == DUPLEX_FULL) 225a5a6858bSRussell King bmcr |= BMCR_FULLDPLX; 226a5a6858bSRussell King 227a5a6858bSRussell King if (bmcr == val) 228a5a6858bSRussell King return 0; 229a5a6858bSRussell King 230a5a6858bSRussell King return mv88e6352_serdes_write(chip, MII_BMCR, bmcr); 231a5a6858bSRussell King } 232a5a6858bSRussell King 2339db4a725SVivien Didelot u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) 2346d91782fSAndrew Lunn { 2352d2e1dd2SAndrew Lunn u8 cmode = chip->ports[port].cmode; 2369db4a725SVivien Didelot u8 lane = 0; 2376d91782fSAndrew Lunn 2383bbb8867SMarek Behún if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) || 2393bbb8867SMarek Behún (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) || 240eb755c3fSAndrew Lunn (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) 2419db4a725SVivien Didelot lane = 0xff; /* Unused */ 2429db4a725SVivien Didelot 2439db4a725SVivien Didelot return lane; 2449db4a725SVivien Didelot } 2459db4a725SVivien Didelot 2469db4a725SVivien Didelot static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) 2479db4a725SVivien Didelot { 2489db4a725SVivien Didelot if (mv88e6xxx_serdes_get_lane(chip, port)) 249b1312b85SFengguang Wu return true; 250eb755c3fSAndrew Lunn 251b1312b85SFengguang Wu return false; 252eb755c3fSAndrew Lunn } 253eb755c3fSAndrew Lunn 254cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat { 255cda9f4aaSAndrew Lunn char string[ETH_GSTRING_LEN]; 256cda9f4aaSAndrew Lunn int sizeof_stat; 257cda9f4aaSAndrew Lunn int reg; 258cda9f4aaSAndrew Lunn }; 259cda9f4aaSAndrew Lunn 260cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { 261cda9f4aaSAndrew Lunn { "serdes_fibre_rx_error", 16, 21 }, 262cda9f4aaSAndrew Lunn { "serdes_PRBS_error", 32, 24 }, 263cda9f4aaSAndrew Lunn }; 264cda9f4aaSAndrew Lunn 265cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) 266cda9f4aaSAndrew Lunn { 267cda9f4aaSAndrew Lunn if (mv88e6352_port_has_serdes(chip, port)) 268cda9f4aaSAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 269cda9f4aaSAndrew Lunn 270cda9f4aaSAndrew Lunn return 0; 271cda9f4aaSAndrew Lunn } 272cda9f4aaSAndrew Lunn 27365f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, 274cda9f4aaSAndrew Lunn int port, uint8_t *data) 275cda9f4aaSAndrew Lunn { 276cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat; 277cda9f4aaSAndrew Lunn int i; 278cda9f4aaSAndrew Lunn 279cda9f4aaSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 28065f60e45SAndrew Lunn return 0; 281cda9f4aaSAndrew Lunn 282cda9f4aaSAndrew Lunn for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 283cda9f4aaSAndrew Lunn stat = &mv88e6352_serdes_hw_stats[i]; 284cda9f4aaSAndrew Lunn memcpy(data + i * ETH_GSTRING_LEN, stat->string, 285cda9f4aaSAndrew Lunn ETH_GSTRING_LEN); 286cda9f4aaSAndrew Lunn } 28765f60e45SAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 288cda9f4aaSAndrew Lunn } 289cda9f4aaSAndrew Lunn 290cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, 291cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat) 292cda9f4aaSAndrew Lunn { 293cda9f4aaSAndrew Lunn u64 val = 0; 294cda9f4aaSAndrew Lunn u16 reg; 295cda9f4aaSAndrew Lunn int err; 296cda9f4aaSAndrew Lunn 297cda9f4aaSAndrew Lunn err = mv88e6352_serdes_read(chip, stat->reg, ®); 298cda9f4aaSAndrew Lunn if (err) { 299cda9f4aaSAndrew Lunn dev_err(chip->dev, "failed to read statistic\n"); 300cda9f4aaSAndrew Lunn return 0; 301cda9f4aaSAndrew Lunn } 302cda9f4aaSAndrew Lunn 303cda9f4aaSAndrew Lunn val = reg; 304cda9f4aaSAndrew Lunn 305cda9f4aaSAndrew Lunn if (stat->sizeof_stat == 32) { 306cda9f4aaSAndrew Lunn err = mv88e6352_serdes_read(chip, stat->reg + 1, ®); 307cda9f4aaSAndrew Lunn if (err) { 308cda9f4aaSAndrew Lunn dev_err(chip->dev, "failed to read statistic\n"); 309cda9f4aaSAndrew Lunn return 0; 310cda9f4aaSAndrew Lunn } 311cda9f4aaSAndrew Lunn val = val << 16 | reg; 312cda9f4aaSAndrew Lunn } 313cda9f4aaSAndrew Lunn 314cda9f4aaSAndrew Lunn return val; 315cda9f4aaSAndrew Lunn } 316cda9f4aaSAndrew Lunn 31765f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, 318cda9f4aaSAndrew Lunn uint64_t *data) 319cda9f4aaSAndrew Lunn { 320cda9f4aaSAndrew Lunn struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; 321cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat; 322cda9f4aaSAndrew Lunn u64 value; 323cda9f4aaSAndrew Lunn int i; 324cda9f4aaSAndrew Lunn 325cda9f4aaSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 32665f60e45SAndrew Lunn return 0; 327cda9f4aaSAndrew Lunn 328cda9f4aaSAndrew Lunn BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > 329cda9f4aaSAndrew Lunn ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); 330cda9f4aaSAndrew Lunn 331cda9f4aaSAndrew Lunn for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 332cda9f4aaSAndrew Lunn stat = &mv88e6352_serdes_hw_stats[i]; 333cda9f4aaSAndrew Lunn value = mv88e6352_serdes_get_stat(chip, stat); 334cda9f4aaSAndrew Lunn mv88e6xxx_port->serdes_stats[i] += value; 335cda9f4aaSAndrew Lunn data[i] = mv88e6xxx_port->serdes_stats[i]; 336cda9f4aaSAndrew Lunn } 33765f60e45SAndrew Lunn 33865f60e45SAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 339cda9f4aaSAndrew Lunn } 340cda9f4aaSAndrew Lunn 3414382172fSAndrew Lunn static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port) 3424382172fSAndrew Lunn { 3437e0e6243SRussell King u16 bmsr; 344946bc250SVivien Didelot int err; 3454382172fSAndrew Lunn 3467e0e6243SRussell King /* If the link has dropped, we want to know about it. */ 3477e0e6243SRussell King err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr); 3487e0e6243SRussell King if (err) { 3497e0e6243SRussell King dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); 350946bc250SVivien Didelot return; 3517e0e6243SRussell King } 3524382172fSAndrew Lunn 3537e0e6243SRussell King dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); 3544382172fSAndrew Lunn } 3554382172fSAndrew Lunn 356907b9b9fSVivien Didelot irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, 357907b9b9fSVivien Didelot u8 lane) 358907b9b9fSVivien Didelot { 359907b9b9fSVivien Didelot irqreturn_t ret = IRQ_NONE; 360907b9b9fSVivien Didelot u16 status; 361907b9b9fSVivien Didelot int err; 362907b9b9fSVivien Didelot 363907b9b9fSVivien Didelot err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status); 364907b9b9fSVivien Didelot if (err) 365907b9b9fSVivien Didelot return ret; 366907b9b9fSVivien Didelot 367907b9b9fSVivien Didelot if (status & MV88E6352_SERDES_INT_LINK_CHANGE) { 368907b9b9fSVivien Didelot ret = IRQ_HANDLED; 369907b9b9fSVivien Didelot mv88e6352_serdes_irq_link(chip, port); 370907b9b9fSVivien Didelot } 371907b9b9fSVivien Didelot 372907b9b9fSVivien Didelot return ret; 373907b9b9fSVivien Didelot } 374907b9b9fSVivien Didelot 37561a46b41SVivien Didelot int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, 37661a46b41SVivien Didelot bool enable) 3774382172fSAndrew Lunn { 37861a46b41SVivien Didelot u16 val = 0; 3794382172fSAndrew Lunn 38061a46b41SVivien Didelot if (enable) 38161a46b41SVivien Didelot val |= MV88E6352_SERDES_INT_LINK_CHANGE; 38261a46b41SVivien Didelot 38361a46b41SVivien Didelot return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, val); 3844382172fSAndrew Lunn } 3854382172fSAndrew Lunn 3864241ef52SVivien Didelot unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) 3874241ef52SVivien Didelot { 3884241ef52SVivien Didelot return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ); 3894241ef52SVivien Didelot } 3904241ef52SVivien Didelot 391d3f88a24SAndrew Lunn int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) 392d3f88a24SAndrew Lunn { 393d3f88a24SAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 394d3f88a24SAndrew Lunn return 0; 395d3f88a24SAndrew Lunn 396d3f88a24SAndrew Lunn return 32 * sizeof(u16); 397d3f88a24SAndrew Lunn } 398d3f88a24SAndrew Lunn 399d3f88a24SAndrew Lunn void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) 400d3f88a24SAndrew Lunn { 401d3f88a24SAndrew Lunn u16 *p = _p; 402d3f88a24SAndrew Lunn u16 reg; 403d3f88a24SAndrew Lunn int i; 404d3f88a24SAndrew Lunn 405d3f88a24SAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 406d3f88a24SAndrew Lunn return; 407d3f88a24SAndrew Lunn 408d3f88a24SAndrew Lunn for (i = 0 ; i < 32; i++) { 409d3f88a24SAndrew Lunn mv88e6352_serdes_read(chip, i, ®); 410d3f88a24SAndrew Lunn p[i] = reg; 411d3f88a24SAndrew Lunn } 412d3f88a24SAndrew Lunn } 413d3f88a24SAndrew Lunn 4145122d4ecSVivien Didelot u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) 415d3cf7d8fSMarek Behún { 416d3cf7d8fSMarek Behún u8 cmode = chip->ports[port].cmode; 4175122d4ecSVivien Didelot u8 lane = 0; 418d3cf7d8fSMarek Behún 4195122d4ecSVivien Didelot switch (port) { 4205122d4ecSVivien Didelot case 5: 4213bbb8867SMarek Behún if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 422d3cf7d8fSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 4235122d4ecSVivien Didelot cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 4245122d4ecSVivien Didelot lane = MV88E6341_PORT5_LANE; 4255122d4ecSVivien Didelot break; 426d3cf7d8fSMarek Behún } 427d3cf7d8fSMarek Behún 4285122d4ecSVivien Didelot return lane; 429d3cf7d8fSMarek Behún } 430d3cf7d8fSMarek Behún 4315122d4ecSVivien Didelot u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) 43207ffbd74SAndrew Lunn { 4332d2e1dd2SAndrew Lunn u8 cmode = chip->ports[port].cmode; 4345122d4ecSVivien Didelot u8 lane = 0; 43507ffbd74SAndrew Lunn 43607ffbd74SAndrew Lunn switch (port) { 43707ffbd74SAndrew Lunn case 9: 4383bbb8867SMarek Behún if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 43907ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 4405122d4ecSVivien Didelot cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 4415122d4ecSVivien Didelot lane = MV88E6390_PORT9_LANE0; 44217deaf5cSMarek Behún break; 44307ffbd74SAndrew Lunn case 10: 4443bbb8867SMarek Behún if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 44507ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 4465122d4ecSVivien Didelot cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 4475122d4ecSVivien Didelot lane = MV88E6390_PORT10_LANE0; 44817deaf5cSMarek Behún break; 44907ffbd74SAndrew Lunn } 45007ffbd74SAndrew Lunn 4515122d4ecSVivien Didelot return lane; 45217deaf5cSMarek Behún } 45317deaf5cSMarek Behún 4545122d4ecSVivien Didelot u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) 455a8c01c0dSAndrew Lunn { 4565122d4ecSVivien Didelot u8 cmode_port = chip->ports[port].cmode; 4575122d4ecSVivien Didelot u8 cmode_port10 = chip->ports[10].cmode; 4585122d4ecSVivien Didelot u8 cmode_port9 = chip->ports[9].cmode; 4595122d4ecSVivien Didelot u8 lane = 0; 460a8c01c0dSAndrew Lunn 461a8c01c0dSAndrew Lunn switch (port) { 462a8c01c0dSAndrew Lunn case 2: 4633bbb8867SMarek Behún if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 464a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 4655122d4ecSVivien Didelot cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 4665122d4ecSVivien Didelot if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) 4675122d4ecSVivien Didelot lane = MV88E6390_PORT9_LANE1; 46817deaf5cSMarek Behún break; 469a8c01c0dSAndrew Lunn case 3: 4703bbb8867SMarek Behún if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 471a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 472a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 4735122d4ecSVivien Didelot cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 4745122d4ecSVivien Didelot if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) 4755122d4ecSVivien Didelot lane = MV88E6390_PORT9_LANE2; 47617deaf5cSMarek Behún break; 477a8c01c0dSAndrew Lunn case 4: 4783bbb8867SMarek Behún if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 479a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 480a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 4815122d4ecSVivien Didelot cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 4825122d4ecSVivien Didelot if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) 4835122d4ecSVivien Didelot lane = MV88E6390_PORT9_LANE3; 48417deaf5cSMarek Behún break; 485a8c01c0dSAndrew Lunn case 5: 4863bbb8867SMarek Behún if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 487a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 4885122d4ecSVivien Didelot cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 4895122d4ecSVivien Didelot if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) 4905122d4ecSVivien Didelot lane = MV88E6390_PORT10_LANE1; 49117deaf5cSMarek Behún break; 492a8c01c0dSAndrew Lunn case 6: 4933bbb8867SMarek Behún if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 494a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 495a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 4965122d4ecSVivien Didelot cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 4975122d4ecSVivien Didelot if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) 4985122d4ecSVivien Didelot lane = MV88E6390_PORT10_LANE2; 49917deaf5cSMarek Behún break; 500a8c01c0dSAndrew Lunn case 7: 5013bbb8867SMarek Behún if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 502a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 503a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 5045122d4ecSVivien Didelot cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 5055122d4ecSVivien Didelot if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX) 5065122d4ecSVivien Didelot lane = MV88E6390_PORT10_LANE3; 50717deaf5cSMarek Behún break; 508a8c01c0dSAndrew Lunn case 9: 5093bbb8867SMarek Behún if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 510a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 511a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 512a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || 5135122d4ecSVivien Didelot cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 5145122d4ecSVivien Didelot lane = MV88E6390_PORT9_LANE0; 51517deaf5cSMarek Behún break; 516a8c01c0dSAndrew Lunn case 10: 5173bbb8867SMarek Behún if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX || 518a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 519a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 520a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || 5215122d4ecSVivien Didelot cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 5225122d4ecSVivien Didelot lane = MV88E6390_PORT10_LANE0; 52317deaf5cSMarek Behún break; 52417deaf5cSMarek Behún } 52517deaf5cSMarek Behún 5265122d4ecSVivien Didelot return lane; 527a8c01c0dSAndrew Lunn } 528a8c01c0dSAndrew Lunn 529dc272f60SVivien Didelot /* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */ 53017deaf5cSMarek Behún static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane, 531dc272f60SVivien Didelot bool up) 5326335e9f2SAndrew Lunn { 5336335e9f2SAndrew Lunn u16 val, new_val; 5346335e9f2SAndrew Lunn int err; 5356335e9f2SAndrew Lunn 536e6891c76SAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 537bf604bc9SRussell King MV88E6390_10G_CTRL1, &val); 538e6891c76SAndrew Lunn 5396335e9f2SAndrew Lunn if (err) 5406335e9f2SAndrew Lunn return err; 5416335e9f2SAndrew Lunn 542dc272f60SVivien Didelot if (up) 543bf604bc9SRussell King new_val = val & ~(MDIO_CTRL1_RESET | 544bf604bc9SRussell King MDIO_PCS_CTRL1_LOOPBACK | 545bf604bc9SRussell King MDIO_CTRL1_LPOWER); 5466335e9f2SAndrew Lunn else 547bf604bc9SRussell King new_val = val | MDIO_CTRL1_LPOWER; 5486335e9f2SAndrew Lunn 5496335e9f2SAndrew Lunn if (val != new_val) 550e6891c76SAndrew Lunn err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 551bf604bc9SRussell King MV88E6390_10G_CTRL1, new_val); 5526335e9f2SAndrew Lunn 5536335e9f2SAndrew Lunn return err; 5546335e9f2SAndrew Lunn } 5556335e9f2SAndrew Lunn 556dc272f60SVivien Didelot /* Set power up/down for SGMII and 1000Base-X */ 55717deaf5cSMarek Behún static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane, 558dc272f60SVivien Didelot bool up) 5596335e9f2SAndrew Lunn { 5606335e9f2SAndrew Lunn u16 val, new_val; 5616335e9f2SAndrew Lunn int err; 5626335e9f2SAndrew Lunn 563e6891c76SAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 5644c8b7350SRussell King MV88E6390_SGMII_BMCR, &val); 5656335e9f2SAndrew Lunn if (err) 5666335e9f2SAndrew Lunn return err; 5676335e9f2SAndrew Lunn 568dc272f60SVivien Didelot if (up) 5694c8b7350SRussell King new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN); 5706335e9f2SAndrew Lunn else 5714c8b7350SRussell King new_val = val | BMCR_PDOWN; 5726335e9f2SAndrew Lunn 5736335e9f2SAndrew Lunn if (val != new_val) 574e6891c76SAndrew Lunn err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 5754c8b7350SRussell King MV88E6390_SGMII_BMCR, new_val); 5766335e9f2SAndrew Lunn 5776335e9f2SAndrew Lunn return err; 5786335e9f2SAndrew Lunn } 5796335e9f2SAndrew Lunn 5800df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat { 5810df95287SNikita Yushchenko char string[ETH_GSTRING_LEN]; 5820df95287SNikita Yushchenko int reg; 5830df95287SNikita Yushchenko }; 5840df95287SNikita Yushchenko 5850df95287SNikita Yushchenko static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = { 5860df95287SNikita Yushchenko { "serdes_rx_pkts", 0xf021 }, 5870df95287SNikita Yushchenko { "serdes_rx_bytes", 0xf024 }, 5880df95287SNikita Yushchenko { "serdes_rx_pkts_error", 0xf027 }, 5890df95287SNikita Yushchenko }; 5900df95287SNikita Yushchenko 5910df95287SNikita Yushchenko int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) 5920df95287SNikita Yushchenko { 5930df95287SNikita Yushchenko if (mv88e6390_serdes_get_lane(chip, port) == 0) 5940df95287SNikita Yushchenko return 0; 5950df95287SNikita Yushchenko 5960df95287SNikita Yushchenko return ARRAY_SIZE(mv88e6390_serdes_hw_stats); 5970df95287SNikita Yushchenko } 5980df95287SNikita Yushchenko 5990df95287SNikita Yushchenko int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip, 6000df95287SNikita Yushchenko int port, uint8_t *data) 6010df95287SNikita Yushchenko { 6020df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat *stat; 6030df95287SNikita Yushchenko int i; 6040df95287SNikita Yushchenko 6050df95287SNikita Yushchenko if (mv88e6390_serdes_get_lane(chip, port) == 0) 6060df95287SNikita Yushchenko return 0; 6070df95287SNikita Yushchenko 6080df95287SNikita Yushchenko for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { 6090df95287SNikita Yushchenko stat = &mv88e6390_serdes_hw_stats[i]; 6100df95287SNikita Yushchenko memcpy(data + i * ETH_GSTRING_LEN, stat->string, 6110df95287SNikita Yushchenko ETH_GSTRING_LEN); 6120df95287SNikita Yushchenko } 6130df95287SNikita Yushchenko return ARRAY_SIZE(mv88e6390_serdes_hw_stats); 6140df95287SNikita Yushchenko } 6150df95287SNikita Yushchenko 6160df95287SNikita Yushchenko static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane, 6170df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat *stat) 6180df95287SNikita Yushchenko { 6190df95287SNikita Yushchenko u16 reg[3]; 6200df95287SNikita Yushchenko int err, i; 6210df95287SNikita Yushchenko 6220df95287SNikita Yushchenko for (i = 0; i < 3; i++) { 6230df95287SNikita Yushchenko err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 6240df95287SNikita Yushchenko stat->reg + i, ®[i]); 6250df95287SNikita Yushchenko if (err) { 6260df95287SNikita Yushchenko dev_err(chip->dev, "failed to read statistic\n"); 6270df95287SNikita Yushchenko return 0; 6280df95287SNikita Yushchenko } 6290df95287SNikita Yushchenko } 6300df95287SNikita Yushchenko 6310df95287SNikita Yushchenko return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32); 6320df95287SNikita Yushchenko } 6330df95287SNikita Yushchenko 6340df95287SNikita Yushchenko int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, 6350df95287SNikita Yushchenko uint64_t *data) 6360df95287SNikita Yushchenko { 6370df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat *stat; 6380df95287SNikita Yushchenko int lane; 6390df95287SNikita Yushchenko int i; 6400df95287SNikita Yushchenko 6410df95287SNikita Yushchenko lane = mv88e6390_serdes_get_lane(chip, port); 6420df95287SNikita Yushchenko if (lane == 0) 6430df95287SNikita Yushchenko return 0; 6440df95287SNikita Yushchenko 6450df95287SNikita Yushchenko for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) { 6460df95287SNikita Yushchenko stat = &mv88e6390_serdes_hw_stats[i]; 6470df95287SNikita Yushchenko data[i] = mv88e6390_serdes_get_stat(chip, lane, stat); 6480df95287SNikita Yushchenko } 6490df95287SNikita Yushchenko 6500df95287SNikita Yushchenko return ARRAY_SIZE(mv88e6390_serdes_hw_stats); 6510df95287SNikita Yushchenko } 6520df95287SNikita Yushchenko 6530df95287SNikita Yushchenko static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane) 6540df95287SNikita Yushchenko { 6550df95287SNikita Yushchenko u16 reg; 6560df95287SNikita Yushchenko int err; 6570df95287SNikita Yushchenko 6580df95287SNikita Yushchenko err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 6590df95287SNikita Yushchenko MV88E6390_PG_CONTROL, ®); 6600df95287SNikita Yushchenko if (err) 6610df95287SNikita Yushchenko return err; 6620df95287SNikita Yushchenko 6630df95287SNikita Yushchenko reg |= MV88E6390_PG_CONTROL_ENABLE_PC; 6640df95287SNikita Yushchenko return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 6650df95287SNikita Yushchenko MV88E6390_PG_CONTROL, reg); 6660df95287SNikita Yushchenko } 6670df95287SNikita Yushchenko 668dc272f60SVivien Didelot int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane, 669dc272f60SVivien Didelot bool up) 6706335e9f2SAndrew Lunn { 6712d2e1dd2SAndrew Lunn u8 cmode = chip->ports[port].cmode; 6720df95287SNikita Yushchenko int err = 0; 6736335e9f2SAndrew Lunn 674a8c01c0dSAndrew Lunn switch (cmode) { 675a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 6763bbb8867SMarek Behún case MV88E6XXX_PORT_STS_CMODE_1000BASEX: 677f8236a08SAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 6780df95287SNikita Yushchenko err = mv88e6390_serdes_power_sgmii(chip, lane, up); 6790df95287SNikita Yushchenko break; 680a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_XAUI: 681a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_RXAUI: 6820df95287SNikita Yushchenko err = mv88e6390_serdes_power_10g(chip, lane, up); 6830df95287SNikita Yushchenko break; 684a8c01c0dSAndrew Lunn } 685a8c01c0dSAndrew Lunn 6860df95287SNikita Yushchenko if (!err && up) 6870df95287SNikita Yushchenko err = mv88e6390_serdes_enable_checker(chip, lane); 6880df95287SNikita Yushchenko 6890df95287SNikita Yushchenko return err; 690a8c01c0dSAndrew Lunn } 691a8c01c0dSAndrew Lunn 692a5a6858bSRussell King int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port, 693a5a6858bSRussell King u8 lane, unsigned int mode, 694a5a6858bSRussell King phy_interface_t interface, 695a5a6858bSRussell King const unsigned long *advertise) 696a5a6858bSRussell King { 697a5a6858bSRussell King u16 val, bmcr, adv; 698a5a6858bSRussell King bool changed; 699a5a6858bSRussell King int err; 700a5a6858bSRussell King 701a5a6858bSRussell King switch (interface) { 702a5a6858bSRussell King case PHY_INTERFACE_MODE_SGMII: 703a5a6858bSRussell King adv = 0x0001; 704a5a6858bSRussell King break; 705a5a6858bSRussell King 706a5a6858bSRussell King case PHY_INTERFACE_MODE_1000BASEX: 707a5a6858bSRussell King adv = linkmode_adv_to_mii_adv_x(advertise, 708a5a6858bSRussell King ETHTOOL_LINK_MODE_1000baseX_Full_BIT); 709a5a6858bSRussell King break; 710a5a6858bSRussell King 711a5a6858bSRussell King case PHY_INTERFACE_MODE_2500BASEX: 712a5a6858bSRussell King adv = linkmode_adv_to_mii_adv_x(advertise, 713a5a6858bSRussell King ETHTOOL_LINK_MODE_2500baseX_Full_BIT); 714a5a6858bSRussell King break; 715a5a6858bSRussell King 716a5a6858bSRussell King default: 717a5a6858bSRussell King return 0; 718a5a6858bSRussell King } 719a5a6858bSRussell King 720a5a6858bSRussell King err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 721a5a6858bSRussell King MV88E6390_SGMII_ADVERTISE, &val); 722a5a6858bSRussell King if (err) 723a5a6858bSRussell King return err; 724a5a6858bSRussell King 725a5a6858bSRussell King changed = val != adv; 726a5a6858bSRussell King if (changed) { 727a5a6858bSRussell King err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 728a5a6858bSRussell King MV88E6390_SGMII_ADVERTISE, adv); 729a5a6858bSRussell King if (err) 730a5a6858bSRussell King return err; 731a5a6858bSRussell King } 732a5a6858bSRussell King 733a5a6858bSRussell King err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 734a5a6858bSRussell King MV88E6390_SGMII_BMCR, &val); 735a5a6858bSRussell King if (err) 736a5a6858bSRussell King return err; 737a5a6858bSRussell King 738a5a6858bSRussell King if (phylink_autoneg_inband(mode)) 739a5a6858bSRussell King bmcr = val | BMCR_ANENABLE; 740a5a6858bSRussell King else 741a5a6858bSRussell King bmcr = val & ~BMCR_ANENABLE; 742a5a6858bSRussell King 743a5a6858bSRussell King /* setting ANENABLE triggers a restart of negotiation */ 744a5a6858bSRussell King if (bmcr == val) 745a5a6858bSRussell King return changed; 746a5a6858bSRussell King 747a5a6858bSRussell King return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 748a5a6858bSRussell King MV88E6390_SGMII_BMCR, bmcr); 749a5a6858bSRussell King } 750a5a6858bSRussell King 751a5a6858bSRussell King int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port, 752a5a6858bSRussell King u8 lane, struct phylink_link_state *state) 753a5a6858bSRussell King { 754a5a6858bSRussell King u16 lpa, status; 755a5a6858bSRussell King int err; 756a5a6858bSRussell King 757a5a6858bSRussell King err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 758a5a6858bSRussell King MV88E6390_SGMII_PHY_STATUS, &status); 759a5a6858bSRussell King if (err) { 760a5a6858bSRussell King dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err); 761a5a6858bSRussell King return err; 762a5a6858bSRussell King } 763a5a6858bSRussell King 764a5a6858bSRussell King err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 765a5a6858bSRussell King MV88E6390_SGMII_LPA, &lpa); 766a5a6858bSRussell King if (err) { 767a5a6858bSRussell King dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err); 768a5a6858bSRussell King return err; 769a5a6858bSRussell King } 770a5a6858bSRussell King 771a5a6858bSRussell King return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state); 772a5a6858bSRussell King } 773a5a6858bSRussell King 774a5a6858bSRussell King int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port, 775a5a6858bSRussell King u8 lane) 776a5a6858bSRussell King { 777a5a6858bSRussell King u16 bmcr; 778a5a6858bSRussell King int err; 779a5a6858bSRussell King 780a5a6858bSRussell King err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 781a5a6858bSRussell King MV88E6390_SGMII_BMCR, &bmcr); 782a5a6858bSRussell King if (err) 783a5a6858bSRussell King return err; 784a5a6858bSRussell King 785a5a6858bSRussell King return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 786a5a6858bSRussell King MV88E6390_SGMII_BMCR, 787a5a6858bSRussell King bmcr | BMCR_ANRESTART); 788a5a6858bSRussell King } 789a5a6858bSRussell King 790a5a6858bSRussell King int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port, 791a5a6858bSRussell King u8 lane, int speed, int duplex) 792a5a6858bSRussell King { 793a5a6858bSRussell King u16 val, bmcr; 794a5a6858bSRussell King int err; 795a5a6858bSRussell King 796a5a6858bSRussell King err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 797a5a6858bSRussell King MV88E6390_SGMII_BMCR, &val); 798a5a6858bSRussell King if (err) 799a5a6858bSRussell King return err; 800a5a6858bSRussell King 801a5a6858bSRussell King bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000); 802a5a6858bSRussell King switch (speed) { 803a5a6858bSRussell King case SPEED_2500: 804a5a6858bSRussell King case SPEED_1000: 805a5a6858bSRussell King bmcr |= BMCR_SPEED1000; 806a5a6858bSRussell King break; 807a5a6858bSRussell King case SPEED_100: 808a5a6858bSRussell King bmcr |= BMCR_SPEED100; 809a5a6858bSRussell King break; 810a5a6858bSRussell King case SPEED_10: 811a5a6858bSRussell King break; 812a5a6858bSRussell King } 813a5a6858bSRussell King 814a5a6858bSRussell King if (duplex == DUPLEX_FULL) 815a5a6858bSRussell King bmcr |= BMCR_FULLDPLX; 816a5a6858bSRussell King 817a5a6858bSRussell King if (bmcr == val) 818a5a6858bSRussell King return 0; 819a5a6858bSRussell King 820a5a6858bSRussell King return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 821a5a6858bSRussell King MV88E6390_SGMII_BMCR, bmcr); 822a5a6858bSRussell King } 823a5a6858bSRussell King 824efd1ba6aSAndrew Lunn static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip, 82517deaf5cSMarek Behún int port, u8 lane) 826efd1ba6aSAndrew Lunn { 8277e0e6243SRussell King u16 bmsr; 828a5a6858bSRussell King int err; 829efd1ba6aSAndrew Lunn 8307e0e6243SRussell King /* If the link has dropped, we want to know about it. */ 83172d8b4fdSHeiner Kallweit err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 8327e0e6243SRussell King MV88E6390_SGMII_BMSR, &bmsr); 83372d8b4fdSHeiner Kallweit if (err) { 8347e0e6243SRussell King dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err); 83572d8b4fdSHeiner Kallweit return; 83672d8b4fdSHeiner Kallweit } 837efd1ba6aSAndrew Lunn 8387e0e6243SRussell King dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS)); 839efd1ba6aSAndrew Lunn } 840efd1ba6aSAndrew Lunn 841efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip, 84261a46b41SVivien Didelot u8 lane, bool enable) 843efd1ba6aSAndrew Lunn { 84461a46b41SVivien Didelot u16 val = 0; 84561a46b41SVivien Didelot 84661a46b41SVivien Didelot if (enable) 84761a46b41SVivien Didelot val |= MV88E6390_SGMII_INT_LINK_DOWN | 84861a46b41SVivien Didelot MV88E6390_SGMII_INT_LINK_UP; 84961a46b41SVivien Didelot 850efd1ba6aSAndrew Lunn return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 85161a46b41SVivien Didelot MV88E6390_SGMII_INT_ENABLE, val); 852efd1ba6aSAndrew Lunn } 853efd1ba6aSAndrew Lunn 85461a46b41SVivien Didelot int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane, 85561a46b41SVivien Didelot bool enable) 856efd1ba6aSAndrew Lunn { 857efd1ba6aSAndrew Lunn u8 cmode = chip->ports[port].cmode; 858efd1ba6aSAndrew Lunn 859efd1ba6aSAndrew Lunn switch (cmode) { 860efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 8613bbb8867SMarek Behún case MV88E6XXX_PORT_STS_CMODE_1000BASEX: 862efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 86361a46b41SVivien Didelot return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable); 864efd1ba6aSAndrew Lunn } 865efd1ba6aSAndrew Lunn 86661a46b41SVivien Didelot return 0; 867efd1ba6aSAndrew Lunn } 868efd1ba6aSAndrew Lunn 869efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip, 87017deaf5cSMarek Behún u8 lane, u16 *status) 871efd1ba6aSAndrew Lunn { 872efd1ba6aSAndrew Lunn int err; 873efd1ba6aSAndrew Lunn 874efd1ba6aSAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 875efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_STATUS, status); 876efd1ba6aSAndrew Lunn 877efd1ba6aSAndrew Lunn return err; 878efd1ba6aSAndrew Lunn } 879efd1ba6aSAndrew Lunn 880907b9b9fSVivien Didelot irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port, 881907b9b9fSVivien Didelot u8 lane) 882efd1ba6aSAndrew Lunn { 883907b9b9fSVivien Didelot u8 cmode = chip->ports[port].cmode; 884efd1ba6aSAndrew Lunn irqreturn_t ret = IRQ_NONE; 885efd1ba6aSAndrew Lunn u16 status; 886efd1ba6aSAndrew Lunn int err; 887b98f0f53SVivien Didelot 888efd1ba6aSAndrew Lunn switch (cmode) { 889efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 8903bbb8867SMarek Behún case MV88E6XXX_PORT_STS_CMODE_1000BASEX: 891efd1ba6aSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 892efd1ba6aSAndrew Lunn err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status); 893efd1ba6aSAndrew Lunn if (err) 894907b9b9fSVivien Didelot return ret; 8956feddb49SDan Carpenter if (status & (MV88E6390_SGMII_INT_LINK_DOWN | 896efd1ba6aSAndrew Lunn MV88E6390_SGMII_INT_LINK_UP)) { 897efd1ba6aSAndrew Lunn ret = IRQ_HANDLED; 898907b9b9fSVivien Didelot mv88e6390_serdes_irq_link_sgmii(chip, port, lane); 899efd1ba6aSAndrew Lunn } 900efd1ba6aSAndrew Lunn } 901907b9b9fSVivien Didelot 902907b9b9fSVivien Didelot return ret; 903907b9b9fSVivien Didelot } 904907b9b9fSVivien Didelot 9054241ef52SVivien Didelot unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port) 9064241ef52SVivien Didelot { 9074241ef52SVivien Didelot return irq_find_mapping(chip->g2_irq.domain, port); 9084241ef52SVivien Didelot } 909bf3504ceSAndrew Lunn 910bf3504ceSAndrew Lunn static const u16 mv88e6390_serdes_regs[] = { 911bf3504ceSAndrew Lunn /* SERDES common registers */ 912bf3504ceSAndrew Lunn 0xf00a, 0xf00b, 0xf00c, 913bf3504ceSAndrew Lunn 0xf010, 0xf011, 0xf012, 0xf013, 914bf3504ceSAndrew Lunn 0xf016, 0xf017, 0xf018, 915bf3504ceSAndrew Lunn 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f, 916bf3504ceSAndrew Lunn 0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027, 917bf3504ceSAndrew Lunn 0xf028, 0xf029, 918bf3504ceSAndrew Lunn 0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037, 919bf3504ceSAndrew Lunn 0xf038, 0xf039, 920bf3504ceSAndrew Lunn /* SGMII */ 921bf3504ceSAndrew Lunn 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 922bf3504ceSAndrew Lunn 0x2008, 923bf3504ceSAndrew Lunn 0x200f, 924bf3504ceSAndrew Lunn 0xa000, 0xa001, 0xa002, 0xa003, 925bf3504ceSAndrew Lunn /* 10Gbase-X */ 926bf3504ceSAndrew Lunn 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007, 927bf3504ceSAndrew Lunn 0x1008, 928bf3504ceSAndrew Lunn 0x100e, 0x100f, 929bf3504ceSAndrew Lunn 0x1018, 0x1019, 930bf3504ceSAndrew Lunn 0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 931bf3504ceSAndrew Lunn 0x9006, 932bf3504ceSAndrew Lunn 0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016, 933bf3504ceSAndrew Lunn /* 10Gbase-R */ 934bf3504ceSAndrew Lunn 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027, 935bf3504ceSAndrew Lunn 0x1028, 0x1029, 0x102a, 0x102b, 936bf3504ceSAndrew Lunn }; 937bf3504ceSAndrew Lunn 938bf3504ceSAndrew Lunn int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port) 939bf3504ceSAndrew Lunn { 940bf3504ceSAndrew Lunn if (mv88e6xxx_serdes_get_lane(chip, port) == 0) 941bf3504ceSAndrew Lunn return 0; 942bf3504ceSAndrew Lunn 943bf3504ceSAndrew Lunn return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16); 944bf3504ceSAndrew Lunn } 945bf3504ceSAndrew Lunn 946bf3504ceSAndrew Lunn void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p) 947bf3504ceSAndrew Lunn { 948bf3504ceSAndrew Lunn u16 *p = _p; 949bf3504ceSAndrew Lunn int lane; 950bf3504ceSAndrew Lunn u16 reg; 951bf3504ceSAndrew Lunn int i; 952bf3504ceSAndrew Lunn 953bf3504ceSAndrew Lunn lane = mv88e6xxx_serdes_get_lane(chip, port); 954bf3504ceSAndrew Lunn if (lane == 0) 955bf3504ceSAndrew Lunn return; 956bf3504ceSAndrew Lunn 957bf3504ceSAndrew Lunn for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) { 958bf3504ceSAndrew Lunn mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 959bf3504ceSAndrew Lunn mv88e6390_serdes_regs[i], ®); 960bf3504ceSAndrew Lunn p[i] = reg; 961bf3504ceSAndrew Lunn } 962bf3504ceSAndrew Lunn } 963