xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision e6891c76)
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 
38e6891c76SAndrew Lunn static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
39e6891c76SAndrew Lunn 				 int lane, int device, int reg, u16 *val)
40e6891c76SAndrew Lunn {
41e6891c76SAndrew Lunn 	int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
42e6891c76SAndrew Lunn 
43e6891c76SAndrew Lunn 	return mv88e6xxx_phy_read(chip, lane, reg_c45, val);
44e6891c76SAndrew Lunn }
45e6891c76SAndrew Lunn 
46e6891c76SAndrew Lunn static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
47e6891c76SAndrew Lunn 				  int lane, int device, int reg, u16 val)
48e6891c76SAndrew Lunn {
49e6891c76SAndrew Lunn 	int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
50e6891c76SAndrew Lunn 
51e6891c76SAndrew Lunn 	return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
52e6891c76SAndrew Lunn }
53e6891c76SAndrew Lunn 
546d91782fSAndrew Lunn static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
556d91782fSAndrew Lunn {
566d91782fSAndrew Lunn 	u16 val, new_val;
576d91782fSAndrew Lunn 	int err;
586d91782fSAndrew Lunn 
596d91782fSAndrew Lunn 	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
606d91782fSAndrew Lunn 	if (err)
616d91782fSAndrew Lunn 		return err;
626d91782fSAndrew Lunn 
636d91782fSAndrew Lunn 	if (on)
646d91782fSAndrew Lunn 		new_val = val & ~BMCR_PDOWN;
656d91782fSAndrew Lunn 	else
666d91782fSAndrew Lunn 		new_val = val | BMCR_PDOWN;
676d91782fSAndrew Lunn 
686d91782fSAndrew Lunn 	if (val != new_val)
696d91782fSAndrew Lunn 		err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
706d91782fSAndrew Lunn 
716d91782fSAndrew Lunn 	return err;
726d91782fSAndrew Lunn }
736d91782fSAndrew Lunn 
74eb755c3fSAndrew Lunn static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
756d91782fSAndrew Lunn {
766d91782fSAndrew Lunn 	u8 cmode;
77eb755c3fSAndrew Lunn 	int err;
786d91782fSAndrew Lunn 
796d91782fSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
80eb755c3fSAndrew Lunn 	if (err) {
81eb755c3fSAndrew Lunn 		dev_err(chip->dev, "failed to read cmode\n");
82b1312b85SFengguang Wu 		return false;
83eb755c3fSAndrew Lunn 	}
846d91782fSAndrew Lunn 
855f83dc93SVivien Didelot 	if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
865f83dc93SVivien Didelot 	    (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
87eb755c3fSAndrew Lunn 	    (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
88b1312b85SFengguang Wu 		return true;
89eb755c3fSAndrew Lunn 
90b1312b85SFengguang Wu 	return false;
91eb755c3fSAndrew Lunn }
92eb755c3fSAndrew Lunn 
93eb755c3fSAndrew Lunn int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
94eb755c3fSAndrew Lunn {
95eb755c3fSAndrew Lunn 	int err;
96eb755c3fSAndrew Lunn 
97eb755c3fSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port)) {
986d91782fSAndrew Lunn 		err = mv88e6352_serdes_power_set(chip, on);
996d91782fSAndrew Lunn 		if (err < 0)
1006d91782fSAndrew Lunn 			return err;
1016d91782fSAndrew Lunn 	}
1026d91782fSAndrew Lunn 
1036d91782fSAndrew Lunn 	return 0;
1046d91782fSAndrew Lunn }
1056335e9f2SAndrew Lunn 
106cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat {
107cda9f4aaSAndrew Lunn 	char string[ETH_GSTRING_LEN];
108cda9f4aaSAndrew Lunn 	int sizeof_stat;
109cda9f4aaSAndrew Lunn 	int reg;
110cda9f4aaSAndrew Lunn };
111cda9f4aaSAndrew Lunn 
112cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
113cda9f4aaSAndrew Lunn 	{ "serdes_fibre_rx_error", 16, 21 },
114cda9f4aaSAndrew Lunn 	{ "serdes_PRBS_error", 32, 24 },
115cda9f4aaSAndrew Lunn };
116cda9f4aaSAndrew Lunn 
117cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
118cda9f4aaSAndrew Lunn {
119cda9f4aaSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port))
120cda9f4aaSAndrew Lunn 		return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
121cda9f4aaSAndrew Lunn 
122cda9f4aaSAndrew Lunn 	return 0;
123cda9f4aaSAndrew Lunn }
124cda9f4aaSAndrew Lunn 
12565f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
126cda9f4aaSAndrew Lunn 				 int port, uint8_t *data)
127cda9f4aaSAndrew Lunn {
128cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
129cda9f4aaSAndrew Lunn 	int i;
130cda9f4aaSAndrew Lunn 
131cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
13265f60e45SAndrew Lunn 		return 0;
133cda9f4aaSAndrew Lunn 
134cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
135cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
136cda9f4aaSAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
137cda9f4aaSAndrew Lunn 		       ETH_GSTRING_LEN);
138cda9f4aaSAndrew Lunn 	}
13965f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
140cda9f4aaSAndrew Lunn }
141cda9f4aaSAndrew Lunn 
142cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
143cda9f4aaSAndrew Lunn 					  struct mv88e6352_serdes_hw_stat *stat)
144cda9f4aaSAndrew Lunn {
145cda9f4aaSAndrew Lunn 	u64 val = 0;
146cda9f4aaSAndrew Lunn 	u16 reg;
147cda9f4aaSAndrew Lunn 	int err;
148cda9f4aaSAndrew Lunn 
149cda9f4aaSAndrew Lunn 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
150cda9f4aaSAndrew Lunn 	if (err) {
151cda9f4aaSAndrew Lunn 		dev_err(chip->dev, "failed to read statistic\n");
152cda9f4aaSAndrew Lunn 		return 0;
153cda9f4aaSAndrew Lunn 	}
154cda9f4aaSAndrew Lunn 
155cda9f4aaSAndrew Lunn 	val = reg;
156cda9f4aaSAndrew Lunn 
157cda9f4aaSAndrew Lunn 	if (stat->sizeof_stat == 32) {
158cda9f4aaSAndrew Lunn 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
159cda9f4aaSAndrew Lunn 		if (err) {
160cda9f4aaSAndrew Lunn 			dev_err(chip->dev, "failed to read statistic\n");
161cda9f4aaSAndrew Lunn 			return 0;
162cda9f4aaSAndrew Lunn 		}
163cda9f4aaSAndrew Lunn 		val = val << 16 | reg;
164cda9f4aaSAndrew Lunn 	}
165cda9f4aaSAndrew Lunn 
166cda9f4aaSAndrew Lunn 	return val;
167cda9f4aaSAndrew Lunn }
168cda9f4aaSAndrew Lunn 
16965f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
170cda9f4aaSAndrew Lunn 			       uint64_t *data)
171cda9f4aaSAndrew Lunn {
172cda9f4aaSAndrew Lunn 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
173cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
174cda9f4aaSAndrew Lunn 	u64 value;
175cda9f4aaSAndrew Lunn 	int i;
176cda9f4aaSAndrew Lunn 
177cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
17865f60e45SAndrew Lunn 		return 0;
179cda9f4aaSAndrew Lunn 
180cda9f4aaSAndrew Lunn 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
181cda9f4aaSAndrew Lunn 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
182cda9f4aaSAndrew Lunn 
183cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
184cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
185cda9f4aaSAndrew Lunn 		value = mv88e6352_serdes_get_stat(chip, stat);
186cda9f4aaSAndrew Lunn 		mv88e6xxx_port->serdes_stats[i] += value;
187cda9f4aaSAndrew Lunn 		data[i] = mv88e6xxx_port->serdes_stats[i];
188cda9f4aaSAndrew Lunn 	}
18965f60e45SAndrew Lunn 
19065f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
191cda9f4aaSAndrew Lunn }
192cda9f4aaSAndrew Lunn 
19307ffbd74SAndrew Lunn /* Return the SERDES lane address a port is using. Only Ports 9 and 10
19407ffbd74SAndrew Lunn  * have SERDES lanes. Returns -ENODEV if a port does not have a lane.
19507ffbd74SAndrew Lunn  */
19607ffbd74SAndrew Lunn static int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
19707ffbd74SAndrew Lunn {
19807ffbd74SAndrew Lunn 	u8 cmode;
19907ffbd74SAndrew Lunn 	int err;
20007ffbd74SAndrew Lunn 
20107ffbd74SAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
20207ffbd74SAndrew Lunn 	if (err)
20307ffbd74SAndrew Lunn 		return err;
20407ffbd74SAndrew Lunn 
20507ffbd74SAndrew Lunn 	switch (port) {
20607ffbd74SAndrew Lunn 	case 9:
20707ffbd74SAndrew Lunn 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
20807ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
20907ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
21007ffbd74SAndrew Lunn 			return MV88E6390_PORT9_LANE0;
21107ffbd74SAndrew Lunn 		return -ENODEV;
21207ffbd74SAndrew Lunn 	case 10:
21307ffbd74SAndrew Lunn 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
21407ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
21507ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
21607ffbd74SAndrew Lunn 			return MV88E6390_PORT10_LANE0;
21707ffbd74SAndrew Lunn 		return -ENODEV;
21807ffbd74SAndrew Lunn 	default:
21907ffbd74SAndrew Lunn 		return -ENODEV;
22007ffbd74SAndrew Lunn 	}
22107ffbd74SAndrew Lunn }
22207ffbd74SAndrew Lunn 
223a8c01c0dSAndrew Lunn /* Return the SERDES lane address a port is using. Ports 9 and 10 can
224a8c01c0dSAndrew Lunn  * use multiple lanes. If so, return the first lane the port uses.
225a8c01c0dSAndrew Lunn  * Returns -ENODEV if a port does not have a lane.
226a8c01c0dSAndrew Lunn  */
22707ffbd74SAndrew Lunn static int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
228a8c01c0dSAndrew Lunn {
229a8c01c0dSAndrew Lunn 	u8 cmode_port9, cmode_port10, cmode_port;
230a8c01c0dSAndrew Lunn 	int err;
231a8c01c0dSAndrew Lunn 
232a8c01c0dSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, 9, &cmode_port9);
233a8c01c0dSAndrew Lunn 	if (err)
234a8c01c0dSAndrew Lunn 		return err;
235a8c01c0dSAndrew Lunn 
236a8c01c0dSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, 10, &cmode_port10);
237a8c01c0dSAndrew Lunn 	if (err)
238a8c01c0dSAndrew Lunn 		return err;
239a8c01c0dSAndrew Lunn 
240a8c01c0dSAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode_port);
241a8c01c0dSAndrew Lunn 	if (err)
242a8c01c0dSAndrew Lunn 		return err;
243a8c01c0dSAndrew Lunn 
244a8c01c0dSAndrew Lunn 	switch (port) {
245a8c01c0dSAndrew Lunn 	case 2:
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 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
250a8c01c0dSAndrew Lunn 				return MV88E6390_PORT9_LANE1;
251a8c01c0dSAndrew Lunn 		return -ENODEV;
252a8c01c0dSAndrew Lunn 	case 3:
253a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
254a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
255a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
256a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
257a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
258a8c01c0dSAndrew Lunn 				return MV88E6390_PORT9_LANE2;
259a8c01c0dSAndrew Lunn 		return -ENODEV;
260a8c01c0dSAndrew Lunn 	case 4:
261a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
262a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
263a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
264a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
265a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
266a8c01c0dSAndrew Lunn 				return MV88E6390_PORT9_LANE3;
267a8c01c0dSAndrew Lunn 		return -ENODEV;
268a8c01c0dSAndrew Lunn 	case 5:
269a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
270a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
271a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
272a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
273a8c01c0dSAndrew Lunn 				return MV88E6390_PORT10_LANE1;
274a8c01c0dSAndrew Lunn 		return -ENODEV;
275a8c01c0dSAndrew Lunn 	case 6:
276a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
277a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
278a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
279a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
280a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
281a8c01c0dSAndrew Lunn 				return MV88E6390_PORT10_LANE2;
282a8c01c0dSAndrew Lunn 		return -ENODEV;
283a8c01c0dSAndrew Lunn 	case 7:
284a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
285a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
286a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
287a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
288a8c01c0dSAndrew Lunn 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X)
289a8c01c0dSAndrew Lunn 				return MV88E6390_PORT10_LANE3;
290a8c01c0dSAndrew Lunn 		return -ENODEV;
291a8c01c0dSAndrew Lunn 	case 9:
292a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
293a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
294a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
295a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
296a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
297a8c01c0dSAndrew Lunn 			return MV88E6390_PORT9_LANE0;
298a8c01c0dSAndrew Lunn 		return -ENODEV;
299a8c01c0dSAndrew Lunn 	case 10:
300a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
301a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
302a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
303a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
304a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
305a8c01c0dSAndrew Lunn 			return MV88E6390_PORT10_LANE0;
306a8c01c0dSAndrew Lunn 		return -ENODEV;
307a8c01c0dSAndrew Lunn 	default:
308a8c01c0dSAndrew Lunn 		return -ENODEV;
309a8c01c0dSAndrew Lunn 	}
310a8c01c0dSAndrew Lunn }
311a8c01c0dSAndrew Lunn 
3126335e9f2SAndrew Lunn /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
31323ef57d8SAndrew Lunn static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
31423ef57d8SAndrew Lunn 				      bool on)
3156335e9f2SAndrew Lunn {
3166335e9f2SAndrew Lunn 	u16 val, new_val;
3176335e9f2SAndrew Lunn 	int err;
3186335e9f2SAndrew Lunn 
319e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
320e6891c76SAndrew Lunn 				    MV88E6390_PCS_CONTROL_1, &val);
321e6891c76SAndrew Lunn 
3226335e9f2SAndrew Lunn 	if (err)
3236335e9f2SAndrew Lunn 		return err;
3246335e9f2SAndrew Lunn 
3256335e9f2SAndrew Lunn 	if (on)
3266335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
3276335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_LOOPBACK |
3286335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_PDOWN);
3296335e9f2SAndrew Lunn 	else
3306335e9f2SAndrew Lunn 		new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
3316335e9f2SAndrew Lunn 
3326335e9f2SAndrew Lunn 	if (val != new_val)
333e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
334e6891c76SAndrew Lunn 					     MV88E6390_PCS_CONTROL_1, new_val);
3356335e9f2SAndrew Lunn 
3366335e9f2SAndrew Lunn 	return err;
3376335e9f2SAndrew Lunn }
3386335e9f2SAndrew Lunn 
339a8c01c0dSAndrew Lunn /* Set the power on/off for SGMII and 1000Base-X */
34023ef57d8SAndrew Lunn static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
3416335e9f2SAndrew Lunn 					bool on)
3426335e9f2SAndrew Lunn {
3436335e9f2SAndrew Lunn 	u16 val, new_val;
3446335e9f2SAndrew Lunn 	int err;
3456335e9f2SAndrew Lunn 
346e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
347e6891c76SAndrew Lunn 				    MV88E6390_SGMII_CONTROL, &val);
3486335e9f2SAndrew Lunn 	if (err)
3496335e9f2SAndrew Lunn 		return err;
3506335e9f2SAndrew Lunn 
3516335e9f2SAndrew Lunn 	if (on)
3526335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
3536335e9f2SAndrew Lunn 				  MV88E6390_SGMII_CONTROL_LOOPBACK |
3546335e9f2SAndrew Lunn 				  MV88E6390_SGMII_CONTROL_PDOWN);
3556335e9f2SAndrew Lunn 	else
3566335e9f2SAndrew Lunn 		new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
3576335e9f2SAndrew Lunn 
3586335e9f2SAndrew Lunn 	if (val != new_val)
359e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
360e6891c76SAndrew Lunn 					     MV88E6390_SGMII_CONTROL, new_val);
3616335e9f2SAndrew Lunn 
3626335e9f2SAndrew Lunn 	return err;
3636335e9f2SAndrew Lunn }
3646335e9f2SAndrew Lunn 
365a8c01c0dSAndrew Lunn static int mv88e6390_serdes_power_lane(struct mv88e6xxx_chip *chip, int port,
366a8c01c0dSAndrew Lunn 				       int lane, bool on)
3676335e9f2SAndrew Lunn {
3686335e9f2SAndrew Lunn 	u8 cmode;
3696335e9f2SAndrew Lunn 	int err;
3706335e9f2SAndrew Lunn 
3716335e9f2SAndrew Lunn 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
3726335e9f2SAndrew Lunn 	if (err)
37364b2f726SDan Carpenter 		return err;
3746335e9f2SAndrew Lunn 
375a8c01c0dSAndrew Lunn 	switch (cmode) {
376a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
377a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
37823ef57d8SAndrew Lunn 		return mv88e6390_serdes_power_sgmii(chip, lane, on);
379a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_XAUI:
380a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
381a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
38223ef57d8SAndrew Lunn 		return mv88e6390_serdes_power_10g(chip, lane, on);
383a8c01c0dSAndrew Lunn 	}
384a8c01c0dSAndrew Lunn 
385a8c01c0dSAndrew Lunn 	return 0;
386a8c01c0dSAndrew Lunn }
387a8c01c0dSAndrew Lunn 
388a8c01c0dSAndrew Lunn int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
389a8c01c0dSAndrew Lunn {
390a8c01c0dSAndrew Lunn 	int lane;
391a8c01c0dSAndrew Lunn 
392a8c01c0dSAndrew Lunn 	lane = mv88e6390_serdes_get_lane(chip, port);
393a8c01c0dSAndrew Lunn 	if (lane == -ENODEV)
394a8c01c0dSAndrew Lunn 		return 0;
395a8c01c0dSAndrew Lunn 
396a8c01c0dSAndrew Lunn 	if (lane < 0)
397a8c01c0dSAndrew Lunn 		return lane;
398a8c01c0dSAndrew Lunn 
3996335e9f2SAndrew Lunn 	switch (port) {
40007ffbd74SAndrew Lunn 	case 9 ... 10:
40107ffbd74SAndrew Lunn 		return mv88e6390_serdes_power_lane(chip, port, lane, on);
40207ffbd74SAndrew Lunn 	}
40307ffbd74SAndrew Lunn 
40407ffbd74SAndrew Lunn 	return 0;
40507ffbd74SAndrew Lunn }
40607ffbd74SAndrew Lunn 
40707ffbd74SAndrew Lunn int mv88e6390x_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
40807ffbd74SAndrew Lunn {
40907ffbd74SAndrew Lunn 	int lane;
41007ffbd74SAndrew Lunn 
41107ffbd74SAndrew Lunn 	lane = mv88e6390x_serdes_get_lane(chip, port);
41207ffbd74SAndrew Lunn 	if (lane == -ENODEV)
41307ffbd74SAndrew Lunn 		return 0;
41407ffbd74SAndrew Lunn 
41507ffbd74SAndrew Lunn 	if (lane < 0)
41607ffbd74SAndrew Lunn 		return lane;
41707ffbd74SAndrew Lunn 
41807ffbd74SAndrew Lunn 	switch (port) {
419a8c01c0dSAndrew Lunn 	case 2 ... 4:
420a8c01c0dSAndrew Lunn 	case 5 ... 7:
421a8c01c0dSAndrew Lunn 	case 9 ... 10:
422a8c01c0dSAndrew Lunn 		return mv88e6390_serdes_power_lane(chip, port, lane, on);
4236335e9f2SAndrew Lunn 	}
4246335e9f2SAndrew Lunn 
4256335e9f2SAndrew Lunn 	return 0;
4266335e9f2SAndrew Lunn }
4275bafeb6eSMarek Behún 
4285bafeb6eSMarek Behún int mv88e6341_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
4295bafeb6eSMarek Behún {
4305bafeb6eSMarek Behún 	int err;
4315bafeb6eSMarek Behún 	u8 cmode;
4325bafeb6eSMarek Behún 
4335bafeb6eSMarek Behún 	if (port != 5)
4345bafeb6eSMarek Behún 		return 0;
4355bafeb6eSMarek Behún 
4365bafeb6eSMarek Behún 	err = mv88e6xxx_port_get_cmode(chip, port, &cmode);
4375bafeb6eSMarek Behún 	if (err)
4385bafeb6eSMarek Behún 		return err;
4395bafeb6eSMarek Behún 
4405bafeb6eSMarek Behún 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
4415bafeb6eSMarek Behún 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4425bafeb6eSMarek Behún 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
44323ef57d8SAndrew Lunn 		return mv88e6390_serdes_power_sgmii(chip, MV88E6341_ADDR_SERDES,
44423ef57d8SAndrew Lunn 						    on);
4455bafeb6eSMarek Behún 
4465bafeb6eSMarek Behún 	return 0;
4475bafeb6eSMarek Behún }
448