xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision eb755c3f)
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