xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision cda9f4aa)
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 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, &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, &reg);
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