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, ®);
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, ®);
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, ®);
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, ®[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], ®);
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