xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision f6791424)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Marvell 88E6xxx SERDES manipulation, via SMI bus
4  *
5  * Copyright (c) 2008 Marvell Semiconductor
6  *
7  * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
8  */
9 
10 #include <linux/interrupt.h>
11 #include <linux/irqdomain.h>
12 #include <linux/mii.h>
13 
14 #include "chip.h"
15 #include "global2.h"
16 #include "phy.h"
17 #include "port.h"
18 #include "serdes.h"
19 
mv88e6352_serdes_read(struct mv88e6xxx_chip * chip,int reg,u16 * val)20 static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
21 				 u16 *val)
22 {
23 	return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
24 				       MV88E6352_SERDES_PAGE_FIBER,
25 				       reg, val);
26 }
27 
mv88e6352_serdes_write(struct mv88e6xxx_chip * chip,int reg,u16 val)28 static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
29 				  u16 val)
30 {
31 	return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
32 					MV88E6352_SERDES_PAGE_FIBER,
33 					reg, val);
34 }
35 
mv88e6390_serdes_read(struct mv88e6xxx_chip * chip,int lane,int device,int reg,u16 * val)36 static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
37 				 int lane, int device, int reg, u16 *val)
38 {
39 	return mv88e6xxx_phy_read_c45(chip, lane, device, reg, val);
40 }
41 
mv88e6xxx_pcs_decode_state(struct device * dev,u16 bmsr,u16 lpa,u16 status,struct phylink_link_state * state)42 int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
43 			       u16 status, struct phylink_link_state *state)
44 {
45 	state->link = false;
46 
47 	/* If the BMSR reports that the link had failed, report this to
48 	 * phylink.
49 	 */
50 	if (!(bmsr & BMSR_LSTATUS))
51 		return 0;
52 
53 	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
54 	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
55 
56 	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
57 		/* The Spped and Duplex Resolved register is 1 if AN is enabled
58 		 * and complete, or if AN is disabled. So with disabled AN we
59 		 * still get here on link up.
60 		 */
61 		state->duplex = status &
62 				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
63 			                         DUPLEX_FULL : DUPLEX_HALF;
64 
65 		if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
66 			state->pause |= MLO_PAUSE_TX;
67 		if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
68 			state->pause |= MLO_PAUSE_RX;
69 
70 		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
71 		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
72 			if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
73 				state->speed = SPEED_2500;
74 			else
75 				state->speed = SPEED_1000;
76 			break;
77 		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
78 			state->speed = SPEED_100;
79 			break;
80 		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
81 			state->speed = SPEED_10;
82 			break;
83 		default:
84 			dev_err(dev, "invalid PHY speed\n");
85 			return -EINVAL;
86 		}
87 	} else if (state->link &&
88 		   state->interface != PHY_INTERFACE_MODE_SGMII) {
89 		/* If Speed and Duplex Resolved register is 0 and link is up, it
90 		 * means that AN was enabled, but link partner had it disabled
91 		 * and the PHY invoked the Auto-Negotiation Bypass feature and
92 		 * linked anyway.
93 		 */
94 		state->duplex = DUPLEX_FULL;
95 		if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
96 			state->speed = SPEED_2500;
97 		else
98 			state->speed = SPEED_1000;
99 	} else {
100 		state->link = false;
101 	}
102 
103 	if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
104 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
105 				       ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
106 	else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
107 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
108 				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
109 
110 	return 0;
111 }
112 
113 struct mv88e6352_serdes_hw_stat {
114 	char string[ETH_GSTRING_LEN];
115 	int sizeof_stat;
116 	int reg;
117 };
118 
119 static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
120 	{ "serdes_fibre_rx_error", 16, 21 },
121 	{ "serdes_PRBS_error", 32, 24 },
122 };
123 
mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip * chip,int port)124 int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
125 {
126 	int err;
127 
128 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
129 	if (err <= 0)
130 		return err;
131 
132 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
133 }
134 
mv88e6352_serdes_get_strings(struct mv88e6xxx_chip * chip,int port,uint8_t * data)135 int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
136 				 int port, uint8_t *data)
137 {
138 	struct mv88e6352_serdes_hw_stat *stat;
139 	int err, i;
140 
141 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
142 	if (err <= 0)
143 		return err;
144 
145 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
146 		stat = &mv88e6352_serdes_hw_stats[i];
147 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
148 		       ETH_GSTRING_LEN);
149 	}
150 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
151 }
152 
mv88e6352_serdes_get_stat(struct mv88e6xxx_chip * chip,struct mv88e6352_serdes_hw_stat * stat)153 static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
154 					  struct mv88e6352_serdes_hw_stat *stat)
155 {
156 	u64 val = 0;
157 	u16 reg;
158 	int err;
159 
160 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
161 	if (err) {
162 		dev_err(chip->dev, "failed to read statistic\n");
163 		return 0;
164 	}
165 
166 	val = reg;
167 
168 	if (stat->sizeof_stat == 32) {
169 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
170 		if (err) {
171 			dev_err(chip->dev, "failed to read statistic\n");
172 			return 0;
173 		}
174 		val = val << 16 | reg;
175 	}
176 
177 	return val;
178 }
179 
mv88e6352_serdes_get_stats(struct mv88e6xxx_chip * chip,int port,uint64_t * data)180 size_t mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
181 				  uint64_t *data)
182 {
183 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
184 	struct mv88e6352_serdes_hw_stat *stat;
185 	int i, err;
186 	u64 value;
187 
188 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
189 	if (err <= 0)
190 		return 0;
191 
192 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
193 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
194 
195 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
196 		stat = &mv88e6352_serdes_hw_stats[i];
197 		value = mv88e6352_serdes_get_stat(chip, stat);
198 		mv88e6xxx_port->serdes_stats[i] += value;
199 		data[i] = mv88e6xxx_port->serdes_stats[i];
200 	}
201 
202 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
203 }
204 
mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip * chip,int port)205 unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
206 {
207 	return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ);
208 }
209 
mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip * chip,int port)210 int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
211 {
212 	int err;
213 
214 	mv88e6xxx_reg_lock(chip);
215 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
216 	mv88e6xxx_reg_unlock(chip);
217 	if (err <= 0)
218 		return err;
219 
220 	return 32 * sizeof(u16);
221 }
222 
mv88e6352_serdes_get_regs(struct mv88e6xxx_chip * chip,int port,void * _p)223 void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
224 {
225 	u16 *p = _p;
226 	u16 reg;
227 	int err;
228 	int i;
229 
230 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
231 	if (err <= 0)
232 		return;
233 
234 	for (i = 0 ; i < 32; i++) {
235 		err = mv88e6352_serdes_read(chip, i, &reg);
236 		if (!err)
237 			p[i] = reg;
238 	}
239 }
240 
mv88e6341_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)241 int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
242 {
243 	u8 cmode = chip->ports[port].cmode;
244 	int lane = -ENODEV;
245 
246 	switch (port) {
247 	case 5:
248 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
249 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
250 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
251 			lane = MV88E6341_PORT5_LANE;
252 		break;
253 	}
254 
255 	return lane;
256 }
257 
mv88e6390_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)258 int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
259 {
260 	u8 cmode = chip->ports[port].cmode;
261 	int lane = -ENODEV;
262 
263 	switch (port) {
264 	case 9:
265 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
266 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
267 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
268 			lane = MV88E6390_PORT9_LANE0;
269 		break;
270 	case 10:
271 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
272 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
273 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
274 			lane = MV88E6390_PORT10_LANE0;
275 		break;
276 	}
277 
278 	return lane;
279 }
280 
mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)281 int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
282 {
283 	u8 cmode_port = chip->ports[port].cmode;
284 	u8 cmode_port10 = chip->ports[10].cmode;
285 	u8 cmode_port9 = chip->ports[9].cmode;
286 	int lane = -ENODEV;
287 
288 	switch (port) {
289 	case 2:
290 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
291 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
292 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
293 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
294 				lane = MV88E6390_PORT9_LANE1;
295 		break;
296 	case 3:
297 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
298 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
299 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
300 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
301 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
302 				lane = MV88E6390_PORT9_LANE2;
303 		break;
304 	case 4:
305 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
306 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
307 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
308 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
309 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
310 				lane = MV88E6390_PORT9_LANE3;
311 		break;
312 	case 5:
313 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
314 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
315 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
316 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
317 				lane = MV88E6390_PORT10_LANE1;
318 		break;
319 	case 6:
320 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
321 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
322 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
323 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
324 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
325 				lane = MV88E6390_PORT10_LANE2;
326 		break;
327 	case 7:
328 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
329 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
330 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
331 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
332 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
333 				lane = MV88E6390_PORT10_LANE3;
334 		break;
335 	case 9:
336 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
337 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
338 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
339 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
340 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
341 			lane = MV88E6390_PORT9_LANE0;
342 		break;
343 	case 10:
344 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
345 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
346 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
347 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
348 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
349 			lane = MV88E6390_PORT10_LANE0;
350 		break;
351 	}
352 
353 	return lane;
354 }
355 
356 /* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address
357  * a port is using else Returns -ENODEV.
358  */
mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)359 int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
360 {
361 	u8 cmode = chip->ports[port].cmode;
362 	int lane = -ENODEV;
363 
364 	if (port != 0 && port != 9 && port != 10)
365 		return -EOPNOTSUPP;
366 
367 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
368 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
369 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
370 	    cmode == MV88E6393X_PORT_STS_CMODE_5GBASER ||
371 	    cmode == MV88E6393X_PORT_STS_CMODE_10GBASER ||
372 	    cmode == MV88E6393X_PORT_STS_CMODE_USXGMII)
373 		lane = port;
374 
375 	return lane;
376 }
377 
378 struct mv88e6390_serdes_hw_stat {
379 	char string[ETH_GSTRING_LEN];
380 	int reg;
381 };
382 
383 static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = {
384 	{ "serdes_rx_pkts", 0xf021 },
385 	{ "serdes_rx_bytes", 0xf024 },
386 	{ "serdes_rx_pkts_error", 0xf027 },
387 };
388 
mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip * chip,int port)389 int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
390 {
391 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
392 		return 0;
393 
394 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
395 }
396 
mv88e6390_serdes_get_strings(struct mv88e6xxx_chip * chip,int port,uint8_t * data)397 int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip,
398 				 int port, uint8_t *data)
399 {
400 	struct mv88e6390_serdes_hw_stat *stat;
401 	int i;
402 
403 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
404 		return 0;
405 
406 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
407 		stat = &mv88e6390_serdes_hw_stats[i];
408 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
409 		       ETH_GSTRING_LEN);
410 	}
411 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
412 }
413 
mv88e6390_serdes_get_stat(struct mv88e6xxx_chip * chip,int lane,struct mv88e6390_serdes_hw_stat * stat)414 static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane,
415 					  struct mv88e6390_serdes_hw_stat *stat)
416 {
417 	u16 reg[3];
418 	int err, i;
419 
420 	for (i = 0; i < 3; i++) {
421 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
422 					    stat->reg + i, &reg[i]);
423 		if (err) {
424 			dev_err(chip->dev, "failed to read statistic\n");
425 			return 0;
426 		}
427 	}
428 
429 	return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32);
430 }
431 
mv88e6390_serdes_get_stats(struct mv88e6xxx_chip * chip,int port,uint64_t * data)432 size_t mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
433 				  uint64_t *data)
434 {
435 	struct mv88e6390_serdes_hw_stat *stat;
436 	int lane;
437 	int i;
438 
439 	lane = mv88e6xxx_serdes_get_lane(chip, port);
440 	if (lane < 0)
441 		return 0;
442 
443 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
444 		stat = &mv88e6390_serdes_hw_stats[i];
445 		data[i] = mv88e6390_serdes_get_stat(chip, lane, stat);
446 	}
447 
448 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
449 }
450 
mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip * chip,int port)451 unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
452 {
453 	return irq_find_mapping(chip->g2_irq.domain, port);
454 }
455 
456 static const u16 mv88e6390_serdes_regs[] = {
457 	/* SERDES common registers */
458 	0xf00a, 0xf00b, 0xf00c,
459 	0xf010, 0xf011, 0xf012, 0xf013,
460 	0xf016, 0xf017, 0xf018,
461 	0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
462 	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
463 	0xf028, 0xf029,
464 	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
465 	0xf038, 0xf039,
466 	/* SGMII */
467 	0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
468 	0x2008,
469 	0x200f,
470 	0xa000, 0xa001, 0xa002, 0xa003,
471 	/* 10Gbase-X */
472 	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
473 	0x1008,
474 	0x100e, 0x100f,
475 	0x1018, 0x1019,
476 	0x9000, 0x9001, 0x9002, 0x9003, 0x9004,
477 	0x9006,
478 	0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016,
479 	/* 10Gbase-R */
480 	0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
481 	0x1028, 0x1029, 0x102a, 0x102b,
482 };
483 
mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip * chip,int port)484 int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
485 {
486 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
487 		return 0;
488 
489 	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
490 }
491 
mv88e6390_serdes_get_regs(struct mv88e6xxx_chip * chip,int port,void * _p)492 void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
493 {
494 	u16 *p = _p;
495 	int lane;
496 	u16 reg;
497 	int err;
498 	int i;
499 
500 	lane = mv88e6xxx_serdes_get_lane(chip, port);
501 	if (lane < 0)
502 		return;
503 
504 	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
505 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
506 					    mv88e6390_serdes_regs[i], &reg);
507 		if (!err)
508 			p[i] = reg;
509 	}
510 }
511 
512 static const int mv88e6352_serdes_p2p_to_reg[] = {
513 	/* Index of value in microvolts corresponds to the register value */
514 	14000, 112000, 210000, 308000, 406000, 504000, 602000, 700000,
515 };
516 
mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip * chip,int port,int val)517 int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port,
518 				      int val)
519 {
520 	bool found = false;
521 	u16 ctrl, reg;
522 	int err;
523 	int i;
524 
525 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
526 	if (err <= 0)
527 		return err;
528 
529 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_p2p_to_reg); ++i) {
530 		if (mv88e6352_serdes_p2p_to_reg[i] == val) {
531 			reg = i;
532 			found = true;
533 			break;
534 		}
535 	}
536 
537 	if (!found)
538 		return -EINVAL;
539 
540 	err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_SPEC_CTRL2, &ctrl);
541 	if (err)
542 		return err;
543 
544 	ctrl &= ~MV88E6352_SERDES_OUT_AMP_MASK;
545 	ctrl |= reg;
546 
547 	return mv88e6352_serdes_write(chip, MV88E6352_SERDES_SPEC_CTRL2, ctrl);
548 }
549