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 false; 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 true; 73 74 return false; 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 struct mv88e6352_serdes_hw_stat { 91 char string[ETH_GSTRING_LEN]; 92 int sizeof_stat; 93 int reg; 94 }; 95 96 static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = { 97 { "serdes_fibre_rx_error", 16, 21 }, 98 { "serdes_PRBS_error", 32, 24 }, 99 }; 100 101 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port) 102 { 103 if (mv88e6352_port_has_serdes(chip, port)) 104 return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 105 106 return 0; 107 } 108 109 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip, 110 int port, uint8_t *data) 111 { 112 struct mv88e6352_serdes_hw_stat *stat; 113 int i; 114 115 if (!mv88e6352_port_has_serdes(chip, port)) 116 return 0; 117 118 for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 119 stat = &mv88e6352_serdes_hw_stats[i]; 120 memcpy(data + i * ETH_GSTRING_LEN, stat->string, 121 ETH_GSTRING_LEN); 122 } 123 return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 124 } 125 126 static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip, 127 struct mv88e6352_serdes_hw_stat *stat) 128 { 129 u64 val = 0; 130 u16 reg; 131 int err; 132 133 err = mv88e6352_serdes_read(chip, stat->reg, ®); 134 if (err) { 135 dev_err(chip->dev, "failed to read statistic\n"); 136 return 0; 137 } 138 139 val = reg; 140 141 if (stat->sizeof_stat == 32) { 142 err = mv88e6352_serdes_read(chip, stat->reg + 1, ®); 143 if (err) { 144 dev_err(chip->dev, "failed to read statistic\n"); 145 return 0; 146 } 147 val = val << 16 | reg; 148 } 149 150 return val; 151 } 152 153 int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port, 154 uint64_t *data) 155 { 156 struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port]; 157 struct mv88e6352_serdes_hw_stat *stat; 158 u64 value; 159 int i; 160 161 if (!mv88e6352_port_has_serdes(chip, port)) 162 return 0; 163 164 BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) > 165 ARRAY_SIZE(mv88e6xxx_port->serdes_stats)); 166 167 for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) { 168 stat = &mv88e6352_serdes_hw_stats[i]; 169 value = mv88e6352_serdes_get_stat(chip, stat); 170 mv88e6xxx_port->serdes_stats[i] += value; 171 data[i] = mv88e6xxx_port->serdes_stats[i]; 172 } 173 174 return ARRAY_SIZE(mv88e6352_serdes_hw_stats); 175 } 176 177 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 178 static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) 179 { 180 u16 val, new_val; 181 int reg_c45; 182 int err; 183 184 reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | 185 MV88E6390_PCS_CONTROL_1; 186 err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); 187 if (err) 188 return err; 189 190 if (on) 191 new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | 192 MV88E6390_PCS_CONTROL_1_LOOPBACK | 193 MV88E6390_PCS_CONTROL_1_PDOWN); 194 else 195 new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; 196 197 if (val != new_val) 198 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); 199 200 return err; 201 } 202 203 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 204 static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, 205 bool on) 206 { 207 u16 val, new_val; 208 int reg_c45; 209 int err; 210 211 reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | 212 MV88E6390_SGMII_CONTROL; 213 err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); 214 if (err) 215 return err; 216 217 if (on) 218 new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | 219 MV88E6390_SGMII_CONTROL_LOOPBACK | 220 MV88E6390_SGMII_CONTROL_PDOWN); 221 else 222 new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; 223 224 if (val != new_val) 225 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); 226 227 return err; 228 } 229 230 static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode, 231 int port_donor, int lane, bool rxaui, bool on) 232 { 233 int err; 234 u8 cmode_donor; 235 236 err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor); 237 if (err) 238 return err; 239 240 switch (cmode_donor) { 241 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 242 if (!rxaui) 243 break; 244 /* Fall through */ 245 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 246 case MV88E6XXX_PORT_STS_CMODE_SGMII: 247 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 248 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 249 cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) 250 return mv88e6390_serdes_sgmii(chip, lane, on); 251 } 252 return 0; 253 } 254 255 static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, 256 bool on) 257 { 258 switch (cmode) { 259 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 260 case MV88E6XXX_PORT_STS_CMODE_SGMII: 261 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on); 262 case MV88E6XXX_PORT_STS_CMODE_XAUI: 263 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 264 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 265 return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); 266 } 267 268 return 0; 269 } 270 271 static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, 272 bool on) 273 { 274 switch (cmode) { 275 case MV88E6XXX_PORT_STS_CMODE_SGMII: 276 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on); 277 case MV88E6XXX_PORT_STS_CMODE_XAUI: 278 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 279 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 280 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 281 return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on); 282 } 283 284 return 0; 285 } 286 287 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 288 { 289 u8 cmode; 290 int err; 291 292 err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 293 if (err) 294 return err; 295 296 switch (port) { 297 case 2: 298 return mv88e6390_serdes_lower(chip, cmode, 9, 299 MV88E6390_PORT9_LANE1, 300 false, on); 301 case 3: 302 return mv88e6390_serdes_lower(chip, cmode, 9, 303 MV88E6390_PORT9_LANE2, 304 true, on); 305 case 4: 306 return mv88e6390_serdes_lower(chip, cmode, 9, 307 MV88E6390_PORT9_LANE3, 308 true, on); 309 case 5: 310 return mv88e6390_serdes_lower(chip, cmode, 10, 311 MV88E6390_PORT10_LANE1, 312 false, on); 313 case 6: 314 return mv88e6390_serdes_lower(chip, cmode, 10, 315 MV88E6390_PORT10_LANE2, 316 true, on); 317 case 7: 318 return mv88e6390_serdes_lower(chip, cmode, 10, 319 MV88E6390_PORT10_LANE3, 320 true, on); 321 case 9: 322 return mv88e6390_serdes_port9(chip, cmode, on); 323 case 10: 324 return mv88e6390_serdes_port10(chip, cmode, on); 325 } 326 327 return 0; 328 } 329