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 int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 59 { 60 int err; 61 u8 cmode; 62 63 err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 64 if (err) 65 return err; 66 67 if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) || 68 (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) || 69 (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII)) { 70 err = mv88e6352_serdes_power_set(chip, on); 71 if (err < 0) 72 return err; 73 } 74 75 return 0; 76 } 77 78 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 79 static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int addr, bool on) 80 { 81 u16 val, new_val; 82 int reg_c45; 83 int err; 84 85 reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | 86 MV88E6390_PCS_CONTROL_1; 87 err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); 88 if (err) 89 return err; 90 91 if (on) 92 new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET | 93 MV88E6390_PCS_CONTROL_1_LOOPBACK | 94 MV88E6390_PCS_CONTROL_1_PDOWN); 95 else 96 new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN; 97 98 if (val != new_val) 99 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); 100 101 return err; 102 } 103 104 /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */ 105 static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int addr, 106 bool on) 107 { 108 u16 val, new_val; 109 int reg_c45; 110 int err; 111 112 reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE | 113 MV88E6390_SGMII_CONTROL; 114 err = mv88e6xxx_phy_read(chip, addr, reg_c45, &val); 115 if (err) 116 return err; 117 118 if (on) 119 new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET | 120 MV88E6390_SGMII_CONTROL_LOOPBACK | 121 MV88E6390_SGMII_CONTROL_PDOWN); 122 else 123 new_val = val | MV88E6390_SGMII_CONTROL_PDOWN; 124 125 if (val != new_val) 126 err = mv88e6xxx_phy_write(chip, addr, reg_c45, new_val); 127 128 return err; 129 } 130 131 static int mv88e6390_serdes_lower(struct mv88e6xxx_chip *chip, u8 cmode, 132 int port_donor, int lane, bool rxaui, bool on) 133 { 134 int err; 135 u8 cmode_donor; 136 137 err = mv88e6xxx_port_get_cmode(chip, port_donor, &cmode_donor); 138 if (err) 139 return err; 140 141 switch (cmode_donor) { 142 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 143 if (!rxaui) 144 break; 145 /* Fall through */ 146 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 147 case MV88E6XXX_PORT_STS_CMODE_SGMII: 148 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 149 if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X || 150 cmode == MV88E6XXX_PORT_STS_CMODE_SGMII) 151 return mv88e6390_serdes_sgmii(chip, lane, on); 152 } 153 return 0; 154 } 155 156 static int mv88e6390_serdes_port9(struct mv88e6xxx_chip *chip, u8 cmode, 157 bool on) 158 { 159 switch (cmode) { 160 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 161 case MV88E6XXX_PORT_STS_CMODE_SGMII: 162 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT9_LANE0, on); 163 case MV88E6XXX_PORT_STS_CMODE_XAUI: 164 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 165 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 166 return mv88e6390_serdes_10g(chip, MV88E6390_PORT9_LANE0, on); 167 } 168 169 return 0; 170 } 171 172 static int mv88e6390_serdes_port10(struct mv88e6xxx_chip *chip, u8 cmode, 173 bool on) 174 { 175 switch (cmode) { 176 case MV88E6XXX_PORT_STS_CMODE_SGMII: 177 return mv88e6390_serdes_sgmii(chip, MV88E6390_PORT10_LANE0, on); 178 case MV88E6XXX_PORT_STS_CMODE_XAUI: 179 case MV88E6XXX_PORT_STS_CMODE_RXAUI: 180 case MV88E6XXX_PORT_STS_CMODE_1000BASE_X: 181 case MV88E6XXX_PORT_STS_CMODE_2500BASEX: 182 return mv88e6390_serdes_10g(chip, MV88E6390_PORT10_LANE0, on); 183 } 184 185 return 0; 186 } 187 188 int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on) 189 { 190 u8 cmode; 191 int err; 192 193 err = mv88e6xxx_port_get_cmode(chip, port, &cmode); 194 if (err) 195 return err; 196 197 switch (port) { 198 case 2: 199 return mv88e6390_serdes_lower(chip, cmode, 9, 200 MV88E6390_PORT9_LANE1, 201 false, on); 202 case 3: 203 return mv88e6390_serdes_lower(chip, cmode, 9, 204 MV88E6390_PORT9_LANE2, 205 true, on); 206 case 4: 207 return mv88e6390_serdes_lower(chip, cmode, 9, 208 MV88E6390_PORT9_LANE3, 209 true, on); 210 case 5: 211 return mv88e6390_serdes_lower(chip, cmode, 10, 212 MV88E6390_PORT10_LANE1, 213 false, on); 214 case 6: 215 return mv88e6390_serdes_lower(chip, cmode, 10, 216 MV88E6390_PORT10_LANE2, 217 true, on); 218 case 7: 219 return mv88e6390_serdes_lower(chip, cmode, 10, 220 MV88E6390_PORT10_LANE3, 221 true, on); 222 case 9: 223 return mv88e6390_serdes_port9(chip, cmode, on); 224 case 10: 225 return mv88e6390_serdes_port10(chip, cmode, on); 226 } 227 228 return 0; 229 } 230