16d91782fSAndrew Lunn /* 26d91782fSAndrew Lunn * Marvell 88E6xxx SERDES manipulation, via SMI bus 36d91782fSAndrew Lunn * 46d91782fSAndrew Lunn * Copyright (c) 2008 Marvell Semiconductor 56d91782fSAndrew Lunn * 66d91782fSAndrew Lunn * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> 76d91782fSAndrew Lunn * 86d91782fSAndrew Lunn * This program is free software; you can redistribute it and/or modify 96d91782fSAndrew Lunn * it under the terms of the GNU General Public License as published by 106d91782fSAndrew Lunn * the Free Software Foundation; either version 2 of the License, or 116d91782fSAndrew Lunn * (at your option) any later version. 126d91782fSAndrew Lunn */ 136d91782fSAndrew Lunn 146d91782fSAndrew Lunn #include <linux/mii.h> 156d91782fSAndrew Lunn 164d5f2ba7SVivien Didelot #include "chip.h" 176335e9f2SAndrew Lunn #include "global2.h" 186d91782fSAndrew Lunn #include "phy.h" 196d91782fSAndrew Lunn #include "port.h" 206d91782fSAndrew Lunn #include "serdes.h" 216d91782fSAndrew Lunn 226d91782fSAndrew Lunn static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg, 236d91782fSAndrew Lunn u16 *val) 246d91782fSAndrew Lunn { 256d91782fSAndrew Lunn return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, 266d91782fSAndrew Lunn MV88E6352_SERDES_PAGE_FIBER, 276d91782fSAndrew Lunn reg, val); 286d91782fSAndrew Lunn } 296d91782fSAndrew Lunn 306d91782fSAndrew Lunn static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, 316d91782fSAndrew Lunn u16 val) 326d91782fSAndrew Lunn { 336d91782fSAndrew Lunn return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, 346d91782fSAndrew Lunn MV88E6352_SERDES_PAGE_FIBER, 356d91782fSAndrew Lunn reg, val); 366d91782fSAndrew Lunn } 376d91782fSAndrew Lunn 38e6891c76SAndrew Lunn static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip, 39e6891c76SAndrew Lunn int lane, int device, int reg, u16 *val) 40e6891c76SAndrew Lunn { 41e6891c76SAndrew Lunn int reg_c45 = MII_ADDR_C45 | device << 16 | reg; 42e6891c76SAndrew Lunn 43e6891c76SAndrew Lunn return mv88e6xxx_phy_read(chip, lane, reg_c45, val); 44e6891c76SAndrew Lunn } 45e6891c76SAndrew Lunn 46e6891c76SAndrew Lunn static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip, 47e6891c76SAndrew Lunn int lane, int device, int reg, u16 val) 48e6891c76SAndrew Lunn { 49e6891c76SAndrew Lunn int reg_c45 = MII_ADDR_C45 | device << 16 | reg; 50e6891c76SAndrew Lunn 51e6891c76SAndrew Lunn return mv88e6xxx_phy_write(chip, lane, reg_c45, val); 52e6891c76SAndrew Lunn } 53e6891c76SAndrew Lunn 546d91782fSAndrew Lunn static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) 556d91782fSAndrew Lunn { 566d91782fSAndrew Lunn u16 val, new_val; 576d91782fSAndrew Lunn int err; 586d91782fSAndrew Lunn 596d91782fSAndrew Lunn err = mv88e6352_serdes_read(chip, MII_BMCR, &val); 606d91782fSAndrew Lunn if (err) 616d91782fSAndrew Lunn return err; 626d91782fSAndrew Lunn 636d91782fSAndrew Lunn if (on) 646d91782fSAndrew Lunn new_val = val & ~BMCR_PDOWN; 656d91782fSAndrew Lunn else 666d91782fSAndrew Lunn new_val = val | BMCR_PDOWN; 676d91782fSAndrew Lunn 686d91782fSAndrew Lunn if (val != new_val) 696d91782fSAndrew Lunn err = mv88e6352_serdes_write(chip, MII_BMCR, new_val); 706d91782fSAndrew Lunn 716d91782fSAndrew Lunn return err; 726d91782fSAndrew Lunn } 736d91782fSAndrew Lunn 74eb755c3fSAndrew Lunn static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) 756d91782fSAndrew Lunn { 766d91782fSAndrew Lunn u8 cmode; 77eb755c3fSAndrew Lunn int err; 786d91782fSAndrew Lunn 796d91782fSAndrew Lunn err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 80eb755c3fSAndrew Lunn if (err) { 81eb755c3fSAndrew Lunn dev_err(chip->dev, "failed to read cmode\n"); 82b1312b85SFengguang Wu return false; 83eb755c3fSAndrew Lunn } 846d91782fSAndrew Lunn 855f83dc93SVivien Didelot if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || 865f83dc93SVivien Didelot (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || 87eb755c3fSAndrew Lunn (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) 88b1312b85SFengguang Wu return true; 89eb755c3fSAndrew Lunn 90b1312b85SFengguang Wu return false; 91eb755c3fSAndrew Lunn } 92eb755c3fSAndrew Lunn 93eb755c3fSAndrew Lunn int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 94eb755c3fSAndrew Lunn { 95eb755c3fSAndrew Lunn int err; 96eb755c3fSAndrew Lunn 97eb755c3fSAndrew Lunn if (mv88e6352_port_has_serdes(chip, port)) { 986d91782fSAndrew Lunn err = mv88e6352_serdes_power_set(chip, on); 996d91782fSAndrew Lunn if (err < 0) 1006d91782fSAndrew Lunn return err; 1016d91782fSAndrew Lunn } 1026d91782fSAndrew Lunn 1036d91782fSAndrew Lunn return 0; 1046d91782fSAndrew Lunn } 1056335e9f2SAndrew Lunn 106cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat { 107cda9f4aaSAndrew Lunn char string[ETH_GSTRING_LEN]; 108cda9f4aaSAndrew Lunn int sizeof_stat; 109cda9f4aaSAndrew Lunn int reg; 110cda9f4aaSAndrew Lunn }; 111cda9f4aaSAndrew Lunn 112cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { 113cda9f4aaSAndrew Lunn { "serdes_fibre_rx_error", 16, 21 }, 114cda9f4aaSAndrew Lunn { "serdes_PRBS_error", 32, 24 }, 115cda9f4aaSAndrew Lunn }; 116cda9f4aaSAndrew Lunn 117cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) 118cda9f4aaSAndrew Lunn { 119cda9f4aaSAndrew Lunn if (mv88e6352_port_has_serdes(chip, port)) 120cda9f4aaSAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 121cda9f4aaSAndrew Lunn 122cda9f4aaSAndrew Lunn return 0; 123cda9f4aaSAndrew Lunn } 124cda9f4aaSAndrew Lunn 12565f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, 126cda9f4aaSAndrew Lunn int port, uint8_t *data) 127cda9f4aaSAndrew Lunn { 128cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat; 129cda9f4aaSAndrew Lunn int i; 130cda9f4aaSAndrew Lunn 131cda9f4aaSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 13265f60e45SAndrew Lunn return 0; 133cda9f4aaSAndrew Lunn 134cda9f4aaSAndrew Lunn for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 135cda9f4aaSAndrew Lunn stat = &mv88e6352_serdes_hw_stats[i]; 136cda9f4aaSAndrew Lunn memcpy(data + i * ETH_GSTRING_LEN, stat->string, 137cda9f4aaSAndrew Lunn ETH_GSTRING_LEN); 138cda9f4aaSAndrew Lunn } 13965f60e45SAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 140cda9f4aaSAndrew Lunn } 141cda9f4aaSAndrew Lunn 142cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, 143cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat) 144cda9f4aaSAndrew Lunn { 145cda9f4aaSAndrew Lunn u64 val = 0; 146cda9f4aaSAndrew Lunn u16 reg; 147cda9f4aaSAndrew Lunn int err; 148cda9f4aaSAndrew Lunn 149cda9f4aaSAndrew Lunn err = mv88e6352_serdes_read(chip, stat->reg, ®); 150cda9f4aaSAndrew Lunn if (err) { 151cda9f4aaSAndrew Lunn dev_err(chip->dev, "failed to read statistic\n"); 152cda9f4aaSAndrew Lunn return 0; 153cda9f4aaSAndrew Lunn } 154cda9f4aaSAndrew Lunn 155cda9f4aaSAndrew Lunn val = reg; 156cda9f4aaSAndrew Lunn 157cda9f4aaSAndrew Lunn if (stat->sizeof_stat == 32) { 158cda9f4aaSAndrew Lunn err = mv88e6352_serdes_read(chip, stat->reg + 1, ®); 159cda9f4aaSAndrew Lunn if (err) { 160cda9f4aaSAndrew Lunn dev_err(chip->dev, "failed to read statistic\n"); 161cda9f4aaSAndrew Lunn return 0; 162cda9f4aaSAndrew Lunn } 163cda9f4aaSAndrew Lunn val = val << 16 | reg; 164cda9f4aaSAndrew Lunn } 165cda9f4aaSAndrew Lunn 166cda9f4aaSAndrew Lunn return val; 167cda9f4aaSAndrew Lunn } 168cda9f4aaSAndrew Lunn 16965f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, 170cda9f4aaSAndrew Lunn uint64_t *data) 171cda9f4aaSAndrew Lunn { 172cda9f4aaSAndrew Lunn struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; 173cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat *stat; 174cda9f4aaSAndrew Lunn u64 value; 175cda9f4aaSAndrew Lunn int i; 176cda9f4aaSAndrew Lunn 177cda9f4aaSAndrew Lunn if (!mv88e6352_port_has_serdes(chip, port)) 17865f60e45SAndrew Lunn return 0; 179cda9f4aaSAndrew Lunn 180cda9f4aaSAndrew Lunn BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > 181cda9f4aaSAndrew Lunn ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); 182cda9f4aaSAndrew Lunn 183cda9f4aaSAndrew Lunn for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 184cda9f4aaSAndrew Lunn stat = &mv88e6352_serdes_hw_stats[i]; 185cda9f4aaSAndrew Lunn value = mv88e6352_serdes_get_stat(chip, stat); 186cda9f4aaSAndrew Lunn mv88e6xxx_port->serdes_stats[i] += value; 187cda9f4aaSAndrew Lunn data[i] = mv88e6xxx_port->serdes_stats[i]; 188cda9f4aaSAndrew Lunn } 18965f60e45SAndrew Lunn 19065f60e45SAndrew Lunn return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 191cda9f4aaSAndrew Lunn } 192cda9f4aaSAndrew Lunn 19307ffbd74SAndrew Lunn /* Return the SERDES lane address a port is using. Only Ports 9 and 10 19407ffbd74SAndrew Lunn * have SERDES lanes. Returns -ENODEV if a port does not have a lane. 19507ffbd74SAndrew Lunn */ 19607ffbd74SAndrew Lunn static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) 19707ffbd74SAndrew Lunn { 19807ffbd74SAndrew Lunn u8 cmode; 19907ffbd74SAndrew Lunn int err; 20007ffbd74SAndrew Lunn 20107ffbd74SAndrew Lunn err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 20207ffbd74SAndrew Lunn if (err) 20307ffbd74SAndrew Lunn return err; 20407ffbd74SAndrew Lunn 20507ffbd74SAndrew Lunn switch (port) { 20607ffbd74SAndrew Lunn case 9: 20707ffbd74SAndrew Lunn if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 20807ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 20907ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 21007ffbd74SAndrew Lunn return MV88E6390_PORT9_LANE0; 21107ffbd74SAndrew Lunn return -ENODEV; 21207ffbd74SAndrew Lunn case 10: 21307ffbd74SAndrew Lunn if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 21407ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 21507ffbd74SAndrew Lunn cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 21607ffbd74SAndrew Lunn return MV88E6390_PORT10_LANE0; 21707ffbd74SAndrew Lunn return -ENODEV; 21807ffbd74SAndrew Lunn default: 21907ffbd74SAndrew Lunn return -ENODEV; 22007ffbd74SAndrew Lunn } 22107ffbd74SAndrew Lunn } 22207ffbd74SAndrew Lunn 223a8c01c0dSAndrew Lunn /* Return the SERDES lane address a port is using. Ports 9 and 10 can 224a8c01c0dSAndrew Lunn * use multiple lanes. If so, return the first lane the port uses. 225a8c01c0dSAndrew Lunn * Returns -ENODEV if a port does not have a lane. 226a8c01c0dSAndrew Lunn */ 22707ffbd74SAndrew Lunn static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port) 228a8c01c0dSAndrew Lunn { 229a8c01c0dSAndrew Lunn u8 cmode_port9, cmode_port10, cmode_port; 230a8c01c0dSAndrew Lunn int err; 231a8c01c0dSAndrew Lunn 232a8c01c0dSAndrew Lunn err = mv88e6xxx_port_get_cmode(chip, 9, &cmode_port9); 233a8c01c0dSAndrew Lunn if (err) 234a8c01c0dSAndrew Lunn return err; 235a8c01c0dSAndrew Lunn 236a8c01c0dSAndrew Lunn err = mv88e6xxx_port_get_cmode(chip, 10, &cmode_port10); 237a8c01c0dSAndrew Lunn if (err) 238a8c01c0dSAndrew Lunn return err; 239a8c01c0dSAndrew Lunn 240a8c01c0dSAndrew Lunn err = mv88e6xxx_port_get_cmode(chip, port, &cmode_port); 241a8c01c0dSAndrew Lunn if (err) 242a8c01c0dSAndrew Lunn return err; 243a8c01c0dSAndrew Lunn 244a8c01c0dSAndrew Lunn switch (port) { 245a8c01c0dSAndrew Lunn case 2: 246a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 247a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 248a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 249a8c01c0dSAndrew Lunn if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) 250a8c01c0dSAndrew Lunn return MV88E6390_PORT9_LANE1; 251a8c01c0dSAndrew Lunn return -ENODEV; 252a8c01c0dSAndrew Lunn case 3: 253a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 254a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 255a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 256a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 257a8c01c0dSAndrew Lunn if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) 258a8c01c0dSAndrew Lunn return MV88E6390_PORT9_LANE2; 259a8c01c0dSAndrew Lunn return -ENODEV; 260a8c01c0dSAndrew Lunn case 4: 261a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 262a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 263a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 264a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 265a8c01c0dSAndrew Lunn if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) 266a8c01c0dSAndrew Lunn return MV88E6390_PORT9_LANE3; 267a8c01c0dSAndrew Lunn return -ENODEV; 268a8c01c0dSAndrew Lunn case 5: 269a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 270a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 271a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 272a8c01c0dSAndrew Lunn if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) 273a8c01c0dSAndrew Lunn return MV88E6390_PORT10_LANE1; 274a8c01c0dSAndrew Lunn return -ENODEV; 275a8c01c0dSAndrew Lunn case 6: 276a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 277a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 278a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 279a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 280a8c01c0dSAndrew Lunn if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) 281a8c01c0dSAndrew Lunn return MV88E6390_PORT10_LANE2; 282a8c01c0dSAndrew Lunn return -ENODEV; 283a8c01c0dSAndrew Lunn case 7: 284a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 285a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 286a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 287a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 288a8c01c0dSAndrew Lunn if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) 289a8c01c0dSAndrew Lunn return MV88E6390_PORT10_LANE3; 290a8c01c0dSAndrew Lunn return -ENODEV; 291a8c01c0dSAndrew Lunn case 9: 292a8c01c0dSAndrew Lunn if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 293a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII || 294a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 295a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI || 296a8c01c0dSAndrew Lunn cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 297a8c01c0dSAndrew Lunn return MV88E6390_PORT9_LANE0; 298a8c01c0dSAndrew Lunn return -ENODEV; 299a8c01c0dSAndrew Lunn case 10: 300a8c01c0dSAndrew Lunn if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 301a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII || 302a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX || 303a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI || 304a8c01c0dSAndrew Lunn cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) 305a8c01c0dSAndrew Lunn return MV88E6390_PORT10_LANE0; 306a8c01c0dSAndrew Lunn return -ENODEV; 307a8c01c0dSAndrew Lunn default: 308a8c01c0dSAndrew Lunn return -ENODEV; 309a8c01c0dSAndrew Lunn } 310a8c01c0dSAndrew Lunn } 311a8c01c0dSAndrew Lunn 3126335e9f2SAndrew Lunn /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 31323ef57d8SAndrew Lunn static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane, 31423ef57d8SAndrew Lunn bool on) 3156335e9f2SAndrew Lunn { 3166335e9f2SAndrew Lunn u16 val, new_val; 3176335e9f2SAndrew Lunn int err; 3186335e9f2SAndrew Lunn 319e6891c76SAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 320e6891c76SAndrew Lunn MV88E6390_PCS_CONTROL_1, &val); 321e6891c76SAndrew Lunn 3226335e9f2SAndrew Lunn if (err) 3236335e9f2SAndrew Lunn return err; 3246335e9f2SAndrew Lunn 3256335e9f2SAndrew Lunn if (on) 3266335e9f2SAndrew Lunn new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | 3276335e9f2SAndrew Lunn MV88E6390_PCS_CONTROL_1_LOOPBACK | 3286335e9f2SAndrew Lunn MV88E6390_PCS_CONTROL_1_PDOWN); 3296335e9f2SAndrew Lunn else 3306335e9f2SAndrew Lunn new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; 3316335e9f2SAndrew Lunn 3326335e9f2SAndrew Lunn if (val != new_val) 333e6891c76SAndrew Lunn err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 334e6891c76SAndrew Lunn MV88E6390_PCS_CONTROL_1, new_val); 3356335e9f2SAndrew Lunn 3366335e9f2SAndrew Lunn return err; 3376335e9f2SAndrew Lunn } 3386335e9f2SAndrew Lunn 339a8c01c0dSAndrew Lunn /* Set the power on/off for SGMII and 1000Base-X */ 34023ef57d8SAndrew Lunn static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane, 3416335e9f2SAndrew Lunn bool on) 3426335e9f2SAndrew Lunn { 3436335e9f2SAndrew Lunn u16 val, new_val; 3446335e9f2SAndrew Lunn int err; 3456335e9f2SAndrew Lunn 346e6891c76SAndrew Lunn err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS, 347e6891c76SAndrew Lunn MV88E6390_SGMII_CONTROL, &val); 3486335e9f2SAndrew Lunn if (err) 3496335e9f2SAndrew Lunn return err; 3506335e9f2SAndrew Lunn 3516335e9f2SAndrew Lunn if (on) 3526335e9f2SAndrew Lunn new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | 3536335e9f2SAndrew Lunn MV88E6390_SGMII_CONTROL_LOOPBACK | 3546335e9f2SAndrew Lunn MV88E6390_SGMII_CONTROL_PDOWN); 3556335e9f2SAndrew Lunn else 3566335e9f2SAndrew Lunn new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; 3576335e9f2SAndrew Lunn 3586335e9f2SAndrew Lunn if (val != new_val) 359e6891c76SAndrew Lunn err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS, 360e6891c76SAndrew Lunn MV88E6390_SGMII_CONTROL, new_val); 3616335e9f2SAndrew Lunn 3626335e9f2SAndrew Lunn return err; 3636335e9f2SAndrew Lunn } 3646335e9f2SAndrew Lunn 365a8c01c0dSAndrew Lunn static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port, 366a8c01c0dSAndrew Lunn int lane, bool on) 3676335e9f2SAndrew Lunn { 3686335e9f2SAndrew Lunn u8 cmode; 3696335e9f2SAndrew Lunn int err; 3706335e9f2SAndrew Lunn 3716335e9f2SAndrew Lunn err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 3726335e9f2SAndrew Lunn if (err) 37364b2f726SDan Carpenter return err; 3746335e9f2SAndrew Lunn 375a8c01c0dSAndrew Lunn switch (cmode) { 376a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_SGMII: 377a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 37823ef57d8SAndrew Lunn return mv88e6390_serdes_power_sgmii(chip, lane, on); 379a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_XAUI: 380a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_RXAUI: 381a8c01c0dSAndrew Lunn case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 38223ef57d8SAndrew Lunn return mv88e6390_serdes_power_10g(chip, lane, on); 383a8c01c0dSAndrew Lunn } 384a8c01c0dSAndrew Lunn 385a8c01c0dSAndrew Lunn return 0; 386a8c01c0dSAndrew Lunn } 387a8c01c0dSAndrew Lunn 388a8c01c0dSAndrew Lunn int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 389a8c01c0dSAndrew Lunn { 390a8c01c0dSAndrew Lunn int lane; 391a8c01c0dSAndrew Lunn 392a8c01c0dSAndrew Lunn lane = mv88e6390_serdes_get_lane(chip, port); 393a8c01c0dSAndrew Lunn if (lane == -ENODEV) 394a8c01c0dSAndrew Lunn return 0; 395a8c01c0dSAndrew Lunn 396a8c01c0dSAndrew Lunn if (lane < 0) 397a8c01c0dSAndrew Lunn return lane; 398a8c01c0dSAndrew Lunn 3996335e9f2SAndrew Lunn switch (port) { 40007ffbd74SAndrew Lunn case 9 ... 10: 40107ffbd74SAndrew Lunn return mv88e6390_serdes_power_lane(chip, port, lane, on); 40207ffbd74SAndrew Lunn } 40307ffbd74SAndrew Lunn 40407ffbd74SAndrew Lunn return 0; 40507ffbd74SAndrew Lunn } 40607ffbd74SAndrew Lunn 40707ffbd74SAndrew Lunn int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 40807ffbd74SAndrew Lunn { 40907ffbd74SAndrew Lunn int lane; 41007ffbd74SAndrew Lunn 41107ffbd74SAndrew Lunn lane = mv88e6390x_serdes_get_lane(chip, port); 41207ffbd74SAndrew Lunn if (lane == -ENODEV) 41307ffbd74SAndrew Lunn return 0; 41407ffbd74SAndrew Lunn 41507ffbd74SAndrew Lunn if (lane < 0) 41607ffbd74SAndrew Lunn return lane; 41707ffbd74SAndrew Lunn 41807ffbd74SAndrew Lunn switch (port) { 419a8c01c0dSAndrew Lunn case 2 ... 4: 420a8c01c0dSAndrew Lunn case 5 ... 7: 421a8c01c0dSAndrew Lunn case 9 ... 10: 422a8c01c0dSAndrew Lunn return mv88e6390_serdes_power_lane(chip, port, lane, on); 4236335e9f2SAndrew Lunn } 4246335e9f2SAndrew Lunn 4256335e9f2SAndrew Lunn return 0; 4266335e9f2SAndrew Lunn } 4275bafeb6eSMarek Behún 4285bafeb6eSMarek Behún int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 4295bafeb6eSMarek Behún { 4305bafeb6eSMarek Behún int err; 4315bafeb6eSMarek Behún u8 cmode; 4325bafeb6eSMarek Behún 4335bafeb6eSMarek Behún if (port != 5) 4345bafeb6eSMarek Behún return 0; 4355bafeb6eSMarek Behún 4365bafeb6eSMarek Behún err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 4375bafeb6eSMarek Behún if (err) 4385bafeb6eSMarek Behún return err; 4395bafeb6eSMarek Behún 4405bafeb6eSMarek Behún if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 4415bafeb6eSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_SGMII || 4425bafeb6eSMarek Behún cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) 44323ef57d8SAndrew Lunn return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES, 44423ef57d8SAndrew Lunn on); 4455bafeb6eSMarek Behún 4465bafeb6eSMarek Behún return 0; 4475bafeb6eSMarek Behún } 448