1 /* 2 * Marvell 88E6xxx SERDES manipulation, via SMI bus 3 * 4 * Copyright (c) 2008 Marvell Semiconductor 5 * 6 * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 */ 13 14 #include <linux/mii.h> 15 16 #include "chip.h" 17 #include "global2.h" 18 #include "phy.h" 19 #include "port.h" 20 #include "serdes.h" 21 22 static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg, 23 u16 *val) 24 { 25 return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES, 26 MV88E6352_SERDES_PAGE_FIBER, 27 reg, val); 28 } 29 30 static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg, 31 u16 val) 32 { 33 return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES, 34 MV88E6352_SERDES_PAGE_FIBER, 35 reg, val); 36 } 37 38 static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on) 39 { 40 u16 val, new_val; 41 int err; 42 43 err = mv88e6352_serdes_read(chip, MII_BMCR, &val); 44 if (err) 45 return err; 46 47 if (on) 48 new_val = val & ~BMCR_PDOWN; 49 else 50 new_val = val | BMCR_PDOWN; 51 52 if (val != new_val) 53 err = mv88e6352_serdes_write(chip, MII_BMCR, new_val); 54 55 return err; 56 } 57 58 static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port) 59 { 60 u8 cmode; 61 int err; 62 63 err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 64 if (err) { 65 dev_err(chip->dev, "failed to read cmode\n"); 66 return 0; 67 } 68 69 if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || 70 (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || 71 (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) 72 return 1; 73 74 return 0; 75 } 76 77 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 78 { 79 int err; 80 81 if (mv88e6352_port_has_serdes(chip, port)) { 82 err = mv88e6352_serdes_power_set(chip, on); 83 if (err < 0) 84 return err; 85 } 86 87 return 0; 88 } 89 90 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 91 static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) 92 { 93 u16 val, new_val; 94 int reg_c45; 95 int err; 96 97 reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | 98 MV88E6390_PCS_CONTROL_1; 99 err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); 100 if (err) 101 return err; 102 103 if (on) 104 new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | 105 MV88E6390_PCS_CONTROL_1_LOOPBACK | 106 MV88E6390_PCS_CONTROL_1_PDOWN); 107 else 108 new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; 109 110 if (val != new_val) 111 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); 112 113 return err; 114 } 115 116 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 117 static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, 118 bool on) 119 { 120 u16 val, new_val; 121 int reg_c45; 122 int err; 123 124 reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | 125 MV88E6390_SGMII_CONTROL; 126 err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); 127 if (err) 128 return err; 129 130 if (on) 131 new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | 132 MV88E6390_SGMII_CONTROL_LOOPBACK | 133 MV88E6390_SGMII_CONTROL_PDOWN); 134 else 135 new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; 136 137 if (val != new_val) 138 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); 139 140 return err; 141 } 142 143 static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode, 144 int port_donor, int lane, bool rxaui, bool on) 145 { 146 int err; 147 u8 cmode_donor; 148 149 err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor); 150 if (err) 151 return err; 152 153 switch (cmode_donor) { 154 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 155 if (!rxaui) 156 break; 157 /* Fall through */ 158 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 159 case MV88E6XXX_PORT_STS_CMODE_SGMII: 160 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 161 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 162 cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) 163 return mv88e6390_serdes_sgmii(chip, lane, on); 164 } 165 return 0; 166 } 167 168 static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, 169 bool on) 170 { 171 switch (cmode) { 172 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 173 case MV88E6XXX_PORT_STS_CMODE_SGMII: 174 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on); 175 case MV88E6XXX_PORT_STS_CMODE_XAUI: 176 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 177 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 178 return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); 179 } 180 181 return 0; 182 } 183 184 static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, 185 bool on) 186 { 187 switch (cmode) { 188 case MV88E6XXX_PORT_STS_CMODE_SGMII: 189 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on); 190 case MV88E6XXX_PORT_STS_CMODE_XAUI: 191 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 192 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 193 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 194 return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on); 195 } 196 197 return 0; 198 } 199 200 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 201 { 202 u8 cmode; 203 int err; 204 205 err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 206 if (err) 207 return err; 208 209 switch (port) { 210 case 2: 211 return mv88e6390_serdes_lower(chip, cmode, 9, 212 MV88E6390_PORT9_LANE1, 213 false, on); 214 case 3: 215 return mv88e6390_serdes_lower(chip, cmode, 9, 216 MV88E6390_PORT9_LANE2, 217 true, on); 218 case 4: 219 return mv88e6390_serdes_lower(chip, cmode, 9, 220 MV88E6390_PORT9_LANE3, 221 true, on); 222 case 5: 223 return mv88e6390_serdes_lower(chip, cmode, 10, 224 MV88E6390_PORT10_LANE1, 225 false, on); 226 case 6: 227 return mv88e6390_serdes_lower(chip, cmode, 10, 228 MV88E6390_PORT10_LANE2, 229 true, on); 230 case 7: 231 return mv88e6390_serdes_lower(chip, cmode, 10, 232 MV88E6390_PORT10_LANE3, 233 true, on); 234 case 9: 235 return mv88e6390_serdes_port9(chip, cmode, on); 236 case 10: 237 return mv88e6390_serdes_port10(chip, cmode, on); 238 } 239 240 return 0; 241 } 242