xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision a8c01c0d)
16d91782fSAndrew Lunn /*
26d91782fSAndrew Lunn  * Marvell 88E6xxx SERDES manipulation, via SMI bus
36d91782fSAndrew Lunn  *
46d91782fSAndrew Lunn  * Copyright (c) 2008 Marvell Semiconductor
56d91782fSAndrew Lunn  *
66d91782fSAndrew Lunn  * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
76d91782fSAndrew Lunn  *
86d91782fSAndrew Lunn  * This program is free software; you can redistribute it and/or modify
96d91782fSAndrew Lunn  * it under the terms of the GNU General Public License as published by
106d91782fSAndrew Lunn  * the Free Software Foundation; either version 2 of the License, or
116d91782fSAndrew Lunn  * (at your option) any later version.
126d91782fSAndrew Lunn  */
136d91782fSAndrew Lunn 
146d91782fSAndrew Lunn #include <linux/mii.h>
156d91782fSAndrew Lunn 
164d5f2ba7SVivien Didelot #include "chip.h"
176335e9f2SAndrew Lunn #include "global2.h"
186d91782fSAndrew Lunn #include "phy.h"
196d91782fSAndrew Lunn #include "port.h"
206d91782fSAndrew Lunn #include "serdes.h"
216d91782fSAndrew Lunn 
226d91782fSAndrew Lunn static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
236d91782fSAndrew Lunn 				 u16 *val)
246d91782fSAndrew Lunn {
256d91782fSAndrew Lunn 	return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
266d91782fSAndrew Lunn 				       MV88E6352_SERDES_PAGE_FIBER,
276d91782fSAndrew Lunn 				       reg, val);
286d91782fSAndrew Lunn }
296d91782fSAndrew Lunn 
306d91782fSAndrew Lunn static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
316d91782fSAndrew Lunn 				  u16 val)
326d91782fSAndrew Lunn {
336d91782fSAndrew Lunn 	return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
346d91782fSAndrew Lunn 					MV88E6352_SERDES_PAGE_FIBER,
356d91782fSAndrew Lunn 					reg, val);
366d91782fSAndrew Lunn }
376d91782fSAndrew Lunn 
386d91782fSAndrew Lunn static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
396d91782fSAndrew Lunn {
406d91782fSAndrew Lunn 	u16 val, new_val;
416d91782fSAndrew Lunn 	int err;
426d91782fSAndrew Lunn 
436d91782fSAndrew Lunn 	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
446d91782fSAndrew Lunn 	if (err)
456d91782fSAndrew Lunn 		return err;
466d91782fSAndrew Lunn 
476d91782fSAndrew Lunn 	if (on)
486d91782fSAndrew Lunn 		new_val = val & ~BMCR_PDOWN;
496d91782fSAndrew Lunn 	else
506d91782fSAndrew Lunn 		new_val = val | BMCR_PDOWN;
516d91782fSAndrew Lunn 
526d91782fSAndrew Lunn 	if (val != new_val)
536d91782fSAndrew Lunn 		err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
546d91782fSAndrew Lunn 
556d91782fSAndrew Lunn 	return err;
566d91782fSAndrew Lunn }
576d91782fSAndrew Lunn 
58eb755c3fSAndrew Lunn static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
596d91782fSAndrew Lunn {
606d91782fSAndrew Lunn 	u8 cmode;
61eb755c3fSAndrew Lunn 	int err;
626d91782fSAndrew Lunn 
636d91782fSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
64eb755c3fSAndrew Lunn 	if (err) {
65eb755c3fSAndrew Lunn 		dev_err(chip->dev, "failed to read cmode\n");
66b1312b85SFengguang Wu 		return false;
67eb755c3fSAndrew Lunn 	}
686d91782fSAndrew Lunn 
695f83dc93SVivien Didelot 	if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
705f83dc93SVivien Didelot 	    (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
71eb755c3fSAndrew Lunn 	    (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
72b1312b85SFengguang Wu 		return true;
73eb755c3fSAndrew Lunn 
74b1312b85SFengguang Wu 	return false;
75eb755c3fSAndrew Lunn }
76eb755c3fSAndrew Lunn 
77eb755c3fSAndrew Lunn int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
78eb755c3fSAndrew Lunn {
79eb755c3fSAndrew Lunn 	int err;
80eb755c3fSAndrew Lunn 
81eb755c3fSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port)) {
826d91782fSAndrew Lunn 		err = mv88e6352_serdes_power_set(chip, on);
836d91782fSAndrew Lunn 		if (err < 0)
846d91782fSAndrew Lunn 			return err;
856d91782fSAndrew Lunn 	}
866d91782fSAndrew Lunn 
876d91782fSAndrew Lunn 	return 0;
886d91782fSAndrew Lunn }
896335e9f2SAndrew Lunn 
90cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat {
91cda9f4aaSAndrew Lunn 	char string[ETH_GSTRING_LEN];
92cda9f4aaSAndrew Lunn 	int sizeof_stat;
93cda9f4aaSAndrew Lunn 	int reg;
94cda9f4aaSAndrew Lunn };
95cda9f4aaSAndrew Lunn 
96cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
97cda9f4aaSAndrew Lunn 	{ "serdes_fibre_rx_error", 16, 21 },
98cda9f4aaSAndrew Lunn 	{ "serdes_PRBS_error", 32, 24 },
99cda9f4aaSAndrew Lunn };
100cda9f4aaSAndrew Lunn 
101cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
102cda9f4aaSAndrew Lunn {
103cda9f4aaSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port))
104cda9f4aaSAndrew Lunn 		return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
105cda9f4aaSAndrew Lunn 
106cda9f4aaSAndrew Lunn 	return 0;
107cda9f4aaSAndrew Lunn }
108cda9f4aaSAndrew Lunn 
10965f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
110cda9f4aaSAndrew Lunn 				 int port, uint8_t *data)
111cda9f4aaSAndrew Lunn {
112cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
113cda9f4aaSAndrew Lunn 	int i;
114cda9f4aaSAndrew Lunn 
115cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
11665f60e45SAndrew Lunn 		return 0;
117cda9f4aaSAndrew Lunn 
118cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
119cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
120cda9f4aaSAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
121cda9f4aaSAndrew Lunn 		       ETH_GSTRING_LEN);
122cda9f4aaSAndrew Lunn 	}
12365f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
124cda9f4aaSAndrew Lunn }
125cda9f4aaSAndrew Lunn 
126cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
127cda9f4aaSAndrew Lunn 					  struct mv88e6352_serdes_hw_stat *stat)
128cda9f4aaSAndrew Lunn {
129cda9f4aaSAndrew Lunn 	u64 val = 0;
130cda9f4aaSAndrew Lunn 	u16 reg;
131cda9f4aaSAndrew Lunn 	int err;
132cda9f4aaSAndrew Lunn 
133cda9f4aaSAndrew Lunn 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
134cda9f4aaSAndrew Lunn 	if (err) {
135cda9f4aaSAndrew Lunn 		dev_err(chip->dev, "failed to read statistic\n");
136cda9f4aaSAndrew Lunn 		return 0;
137cda9f4aaSAndrew Lunn 	}
138cda9f4aaSAndrew Lunn 
139cda9f4aaSAndrew Lunn 	val = reg;
140cda9f4aaSAndrew Lunn 
141cda9f4aaSAndrew Lunn 	if (stat->sizeof_stat == 32) {
142cda9f4aaSAndrew Lunn 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
143cda9f4aaSAndrew Lunn 		if (err) {
144cda9f4aaSAndrew Lunn 			dev_err(chip->dev, "failed to read statistic\n");
145cda9f4aaSAndrew Lunn 			return 0;
146cda9f4aaSAndrew Lunn 		}
147cda9f4aaSAndrew Lunn 		val = val << 16 | reg;
148cda9f4aaSAndrew Lunn 	}
149cda9f4aaSAndrew Lunn 
150cda9f4aaSAndrew Lunn 	return val;
151cda9f4aaSAndrew Lunn }
152cda9f4aaSAndrew Lunn 
15365f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
154cda9f4aaSAndrew Lunn 			       uint64_t *data)
155cda9f4aaSAndrew Lunn {
156cda9f4aaSAndrew Lunn 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
157cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
158cda9f4aaSAndrew Lunn 	u64 value;
159cda9f4aaSAndrew Lunn 	int i;
160cda9f4aaSAndrew Lunn 
161cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
16265f60e45SAndrew Lunn 		return 0;
163cda9f4aaSAndrew Lunn 
164cda9f4aaSAndrew Lunn 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
165cda9f4aaSAndrew Lunn 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
166cda9f4aaSAndrew Lunn 
167cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
168cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
169cda9f4aaSAndrew Lunn 		value = mv88e6352_serdes_get_stat(chip, stat);
170cda9f4aaSAndrew Lunn 		mv88e6xxx_port->serdes_stats[i] += value;
171cda9f4aaSAndrew Lunn 		data[i] = mv88e6xxx_port->serdes_stats[i];
172cda9f4aaSAndrew Lunn 	}
17365f60e45SAndrew Lunn 
17465f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
175cda9f4aaSAndrew Lunn }
176cda9f4aaSAndrew Lunn 
177a8c01c0dSAndrew Lunn /* Return the SERDES lane address a port is using. Ports 9 and 10 can
178a8c01c0dSAndrew Lunn  * use multiple lanes. If so, return the first lane the port uses.
179a8c01c0dSAndrew Lunn  * Returns -ENODEV if a port does not have a lane.
180a8c01c0dSAndrew Lunn  */
181a8c01c0dSAndrew Lunn static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
182a8c01c0dSAndrew Lunn {
183a8c01c0dSAndrew Lunn 	u8 cmode_port9, cmode_port10, cmode_port;
184a8c01c0dSAndrew Lunn 	int err;
185a8c01c0dSAndrew Lunn 
186a8c01c0dSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, 9, &cmode_port9);
187a8c01c0dSAndrew Lunn 	if (err)
188a8c01c0dSAndrew Lunn 		return err;
189a8c01c0dSAndrew Lunn 
190a8c01c0dSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, 10, &cmode_port10);
191a8c01c0dSAndrew Lunn 	if (err)
192a8c01c0dSAndrew Lunn 		return err;
193a8c01c0dSAndrew Lunn 
194a8c01c0dSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode_port);
195a8c01c0dSAndrew Lunn 	if (err)
196a8c01c0dSAndrew Lunn 		return err;
197a8c01c0dSAndrew Lunn 
198a8c01c0dSAndrew Lunn 	switch (port) {
199a8c01c0dSAndrew Lunn 	case 2:
200a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
201a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
202a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
203a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
204a8c01c0dSAndrew Lunn 				return MV88E6390_PORT9_LANE1;
205a8c01c0dSAndrew Lunn 		return -ENODEV;
206a8c01c0dSAndrew Lunn 	case 3:
207a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
208a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
209a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
210a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
211a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
212a8c01c0dSAndrew Lunn 				return MV88E6390_PORT9_LANE2;
213a8c01c0dSAndrew Lunn 		return -ENODEV;
214a8c01c0dSAndrew Lunn 	case 4:
215a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
216a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
217a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
218a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
219a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
220a8c01c0dSAndrew Lunn 				return MV88E6390_PORT9_LANE3;
221a8c01c0dSAndrew Lunn 		return -ENODEV;
222a8c01c0dSAndrew Lunn 	case 5:
223a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
224a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
225a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
226a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
227a8c01c0dSAndrew Lunn 				return MV88E6390_PORT10_LANE1;
228a8c01c0dSAndrew Lunn 		return -ENODEV;
229a8c01c0dSAndrew Lunn 	case 6:
230a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
231a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
232a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
233a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
234a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
235a8c01c0dSAndrew Lunn 				return MV88E6390_PORT10_LANE2;
236a8c01c0dSAndrew Lunn 		return -ENODEV;
237a8c01c0dSAndrew Lunn 	case 7:
238a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
239a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
240a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
241a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
242a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
243a8c01c0dSAndrew Lunn 				return MV88E6390_PORT10_LANE3;
244a8c01c0dSAndrew Lunn 		return -ENODEV;
245a8c01c0dSAndrew Lunn 	case 9:
246a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
247a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
248a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
249a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
250a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
251a8c01c0dSAndrew Lunn 			return MV88E6390_PORT9_LANE0;
252a8c01c0dSAndrew Lunn 		return -ENODEV;
253a8c01c0dSAndrew Lunn 	case 10:
254a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
255a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
256a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
257a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
258a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
259a8c01c0dSAndrew Lunn 			return MV88E6390_PORT10_LANE0;
260a8c01c0dSAndrew Lunn 		return -ENODEV;
261a8c01c0dSAndrew Lunn 	default:
262a8c01c0dSAndrew Lunn 		return -ENODEV;
263a8c01c0dSAndrew Lunn 	}
264a8c01c0dSAndrew Lunn }
265a8c01c0dSAndrew Lunn 
2666335e9f2SAndrew Lunn /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
267a8c01c0dSAndrew Lunn static int mv88e6390_serdes_10g(struct mv88e6xxx_chip *chip, int lane, bool on)
2686335e9f2SAndrew Lunn {
2696335e9f2SAndrew Lunn 	u16 val, new_val;
2706335e9f2SAndrew Lunn 	int reg_c45;
2716335e9f2SAndrew Lunn 	int err;
2726335e9f2SAndrew Lunn 
2736335e9f2SAndrew Lunn 	reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
2746335e9f2SAndrew Lunn 		MV88E6390_PCS_CONTROL_1;
275a8c01c0dSAndrew Lunn 	err = mv88e6xxx_phy_read(chip, lane, reg_c45, &val);
2766335e9f2SAndrew Lunn 	if (err)
2776335e9f2SAndrew Lunn 		return err;
2786335e9f2SAndrew Lunn 
2796335e9f2SAndrew Lunn 	if (on)
2806335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
2816335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_LOOPBACK |
2826335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_PDOWN);
2836335e9f2SAndrew Lunn 	else
2846335e9f2SAndrew Lunn 		new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
2856335e9f2SAndrew Lunn 
2866335e9f2SAndrew Lunn 	if (val != new_val)
287a8c01c0dSAndrew Lunn 		err = mv88e6xxx_phy_write(chip, lane, reg_c45, new_val);
2886335e9f2SAndrew Lunn 
2896335e9f2SAndrew Lunn 	return err;
2906335e9f2SAndrew Lunn }
2916335e9f2SAndrew Lunn 
292a8c01c0dSAndrew Lunn /* Set the power on/off for SGMII and 1000Base-X */
293a8c01c0dSAndrew Lunn static int mv88e6390_serdes_sgmii(struct mv88e6xxx_chip *chip, int lane,
2946335e9f2SAndrew Lunn 				  bool on)
2956335e9f2SAndrew Lunn {
2966335e9f2SAndrew Lunn 	u16 val, new_val;
2976335e9f2SAndrew Lunn 	int reg_c45;
2986335e9f2SAndrew Lunn 	int err;
2996335e9f2SAndrew Lunn 
3006335e9f2SAndrew Lunn 	reg_c45 = MII_ADDR_C45 | MV88E6390_SERDES_DEVICE |
3016335e9f2SAndrew Lunn 		MV88E6390_SGMII_CONTROL;
302a8c01c0dSAndrew Lunn 	err = mv88e6xxx_phy_read(chip, lane, reg_c45, &val);
3036335e9f2SAndrew Lunn 	if (err)
3046335e9f2SAndrew Lunn 		return err;
3056335e9f2SAndrew Lunn 
3066335e9f2SAndrew Lunn 	if (on)
3076335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
3086335e9f2SAndrew Lunn 				  MV88E6390_SGMII_CONTROL_LOOPBACK |
3096335e9f2SAndrew Lunn 				  MV88E6390_SGMII_CONTROL_PDOWN);
3106335e9f2SAndrew Lunn 	else
3116335e9f2SAndrew Lunn 		new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
3126335e9f2SAndrew Lunn 
3136335e9f2SAndrew Lunn 	if (val != new_val)
314a8c01c0dSAndrew Lunn 		err = mv88e6xxx_phy_write(chip, lane, reg_c45, new_val);
3156335e9f2SAndrew Lunn 
3166335e9f2SAndrew Lunn 	return err;
3176335e9f2SAndrew Lunn }
3186335e9f2SAndrew Lunn 
319a8c01c0dSAndrew Lunn static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port,
320a8c01c0dSAndrew Lunn 				       int lane, bool on)
3216335e9f2SAndrew Lunn {
3226335e9f2SAndrew Lunn 	u8 cmode;
3236335e9f2SAndrew Lunn 	int err;
3246335e9f2SAndrew Lunn 
3256335e9f2SAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
3266335e9f2SAndrew Lunn 	if (err)
32764b2f726SDan Carpenter 		return err;
3286335e9f2SAndrew Lunn 
329a8c01c0dSAndrew Lunn 	switch (cmode) {
330a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
331a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
332a8c01c0dSAndrew Lunn 		return mv88e6390_serdes_sgmii(chip, lane, on);
333a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_XAUI:
334a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
335a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
336a8c01c0dSAndrew Lunn 		return mv88e6390_serdes_10g(chip, lane, on);
337a8c01c0dSAndrew Lunn 	}
338a8c01c0dSAndrew Lunn 
339a8c01c0dSAndrew Lunn 	return 0;
340a8c01c0dSAndrew Lunn }
341a8c01c0dSAndrew Lunn 
342a8c01c0dSAndrew Lunn int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
343a8c01c0dSAndrew Lunn {
344a8c01c0dSAndrew Lunn 	int lane;
345a8c01c0dSAndrew Lunn 
346a8c01c0dSAndrew Lunn 	lane = mv88e6390_serdes_get_lane(chip, port);
347a8c01c0dSAndrew Lunn 	if (lane == -ENODEV)
348a8c01c0dSAndrew Lunn 		return 0;
349a8c01c0dSAndrew Lunn 
350a8c01c0dSAndrew Lunn 	if (lane < 0)
351a8c01c0dSAndrew Lunn 		return lane;
352a8c01c0dSAndrew Lunn 
3536335e9f2SAndrew Lunn 	switch (port) {
354a8c01c0dSAndrew Lunn 	case 2 ... 4:
355a8c01c0dSAndrew Lunn 	case 5 ... 7:
356a8c01c0dSAndrew Lunn 	case 9 ... 10:
357a8c01c0dSAndrew Lunn 		return mv88e6390_serdes_power_lane(chip, port, lane, on);
3586335e9f2SAndrew Lunn 	}
3596335e9f2SAndrew Lunn 
3606335e9f2SAndrew Lunn 	return 0;
3616335e9f2SAndrew Lunn }
3625bafeb6eSMarek Behún 
3635bafeb6eSMarek Behún int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
3645bafeb6eSMarek Behún {
3655bafeb6eSMarek Behún 	int err;
3665bafeb6eSMarek Behún 	u8 cmode;
3675bafeb6eSMarek Behún 
3685bafeb6eSMarek Behún 	if (port != 5)
3695bafeb6eSMarek Behún 		return 0;
3705bafeb6eSMarek Behún 
3715bafeb6eSMarek Behún 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
3725bafeb6eSMarek Behún 	if (err)
3735bafeb6eSMarek Behún 		return err;
3745bafeb6eSMarek Behún 
3755bafeb6eSMarek Behún 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
3765bafeb6eSMarek Behún 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
3775bafeb6eSMarek Behún 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
3785bafeb6eSMarek Behún 		return mv88e6390_serdes_sgmii(chip, MV88E6341_ADDR_SERDES, on);
3795bafeb6eSMarek Behún 
3805bafeb6eSMarek Behún 	return 0;
3815bafeb6eSMarek Behún }
382