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