xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision a03b98d6)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26d91782fSAndrew Lunn /*
36d91782fSAndrew Lunn  * Marvell 88E6xxx SERDES manipulation, via SMI bus
46d91782fSAndrew Lunn  *
56d91782fSAndrew Lunn  * Copyright (c) 2008 Marvell Semiconductor
66d91782fSAndrew Lunn  *
76d91782fSAndrew Lunn  * Copyright (c) 2017 Andrew Lunn <andrew@lunn.ch>
86d91782fSAndrew Lunn  */
96d91782fSAndrew Lunn 
10efd1ba6aSAndrew Lunn #include <linux/interrupt.h>
11efd1ba6aSAndrew Lunn #include <linux/irqdomain.h>
126d91782fSAndrew Lunn #include <linux/mii.h>
136d91782fSAndrew Lunn 
144d5f2ba7SVivien Didelot #include "chip.h"
156335e9f2SAndrew Lunn #include "global2.h"
166d91782fSAndrew Lunn #include "phy.h"
176d91782fSAndrew Lunn #include "port.h"
186d91782fSAndrew Lunn #include "serdes.h"
196d91782fSAndrew Lunn 
206d91782fSAndrew Lunn static int mv88e6352_serdes_read(struct mv88e6xxx_chip *chip, int reg,
216d91782fSAndrew Lunn 				 u16 *val)
226d91782fSAndrew Lunn {
236d91782fSAndrew Lunn 	return mv88e6xxx_phy_page_read(chip, MV88E6352_ADDR_SERDES,
246d91782fSAndrew Lunn 				       MV88E6352_SERDES_PAGE_FIBER,
256d91782fSAndrew Lunn 				       reg, val);
266d91782fSAndrew Lunn }
276d91782fSAndrew Lunn 
286d91782fSAndrew Lunn static int mv88e6352_serdes_write(struct mv88e6xxx_chip *chip, int reg,
296d91782fSAndrew Lunn 				  u16 val)
306d91782fSAndrew Lunn {
316d91782fSAndrew Lunn 	return mv88e6xxx_phy_page_write(chip, MV88E6352_ADDR_SERDES,
326d91782fSAndrew Lunn 					MV88E6352_SERDES_PAGE_FIBER,
336d91782fSAndrew Lunn 					reg, val);
346d91782fSAndrew Lunn }
356d91782fSAndrew Lunn 
36e6891c76SAndrew Lunn static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
37e6891c76SAndrew Lunn 				 int lane, int device, int reg, u16 *val)
38e6891c76SAndrew Lunn {
39e6891c76SAndrew Lunn 	int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
40e6891c76SAndrew Lunn 
41e6891c76SAndrew Lunn 	return mv88e6xxx_phy_read(chip, lane, reg_c45, val);
42e6891c76SAndrew Lunn }
43e6891c76SAndrew Lunn 
44e6891c76SAndrew Lunn static int mv88e6390_serdes_write(struct mv88e6xxx_chip *chip,
45e6891c76SAndrew Lunn 				  int lane, int device, int reg, u16 val)
46e6891c76SAndrew Lunn {
47e6891c76SAndrew Lunn 	int reg_c45 = MII_ADDR_C45 | device << 16 | reg;
48e6891c76SAndrew Lunn 
49e6891c76SAndrew Lunn 	return mv88e6xxx_phy_write(chip, lane, reg_c45, val);
50e6891c76SAndrew Lunn }
51e6891c76SAndrew Lunn 
52a5a6858bSRussell King static int mv88e6xxx_serdes_pcs_get_state(struct mv88e6xxx_chip *chip,
53a5a6858bSRussell King 					  u16 status, u16 lpa,
54a5a6858bSRussell King 					  struct phylink_link_state *state)
55a5a6858bSRussell King {
56a5a6858bSRussell King 	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
57a5a6858bSRussell King 		state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
58a5a6858bSRussell King 		state->duplex = status &
59a5a6858bSRussell King 				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
60a5a6858bSRussell King 			                         DUPLEX_FULL : DUPLEX_HALF;
61a5a6858bSRussell King 
62a5a6858bSRussell King 		if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
63a5a6858bSRussell King 			state->pause |= MLO_PAUSE_TX;
64a5a6858bSRussell King 		if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
65a5a6858bSRussell King 			state->pause |= MLO_PAUSE_RX;
66a5a6858bSRussell King 
67a5a6858bSRussell King 		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
68a5a6858bSRussell King 		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
69a5a6858bSRussell King 			if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
70a5a6858bSRussell King 				state->speed = SPEED_2500;
71a5a6858bSRussell King 			else
72a5a6858bSRussell King 				state->speed = SPEED_1000;
73a5a6858bSRussell King 			break;
74a5a6858bSRussell King 		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
75a5a6858bSRussell King 			state->speed = SPEED_100;
76a5a6858bSRussell King 			break;
77a5a6858bSRussell King 		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
78a5a6858bSRussell King 			state->speed = SPEED_10;
79a5a6858bSRussell King 			break;
80a5a6858bSRussell King 		default:
81a5a6858bSRussell King 			dev_err(chip->dev, "invalid PHY speed\n");
82a5a6858bSRussell King 			return -EINVAL;
83a5a6858bSRussell King 		}
84a5a6858bSRussell King 	} else {
85a5a6858bSRussell King 		state->link = false;
86a5a6858bSRussell King 	}
87a5a6858bSRussell King 
88a5a6858bSRussell King 	if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
89a5a6858bSRussell King 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
90a5a6858bSRussell King 				       ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
91a5a6858bSRussell King 	else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
92a5a6858bSRussell King 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
93a5a6858bSRussell King 				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
94a5a6858bSRussell King 
95a5a6858bSRussell King 	return 0;
96a5a6858bSRussell King }
97a5a6858bSRussell King 
98193c5b26SPavana Sharma int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
99dc272f60SVivien Didelot 			   bool up)
1006d91782fSAndrew Lunn {
1016d91782fSAndrew Lunn 	u16 val, new_val;
1026d91782fSAndrew Lunn 	int err;
1036d91782fSAndrew Lunn 
1046d91782fSAndrew Lunn 	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
1056d91782fSAndrew Lunn 	if (err)
1066d91782fSAndrew Lunn 		return err;
1076d91782fSAndrew Lunn 
108dc272f60SVivien Didelot 	if (up)
1096d91782fSAndrew Lunn 		new_val = val & ~BMCR_PDOWN;
1106d91782fSAndrew Lunn 	else
1116d91782fSAndrew Lunn 		new_val = val | BMCR_PDOWN;
1126d91782fSAndrew Lunn 
1136d91782fSAndrew Lunn 	if (val != new_val)
1146d91782fSAndrew Lunn 		err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
1156d91782fSAndrew Lunn 
1166d91782fSAndrew Lunn 	return err;
1176d91782fSAndrew Lunn }
1186d91782fSAndrew Lunn 
119a5a6858bSRussell King int mv88e6352_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
120193c5b26SPavana Sharma 				int lane, unsigned int mode,
121a5a6858bSRussell King 				phy_interface_t interface,
122a5a6858bSRussell King 				const unsigned long *advertise)
123a5a6858bSRussell King {
124a5a6858bSRussell King 	u16 adv, bmcr, val;
125a5a6858bSRussell King 	bool changed;
126a5a6858bSRussell King 	int err;
127a5a6858bSRussell King 
128a5a6858bSRussell King 	switch (interface) {
129a5a6858bSRussell King 	case PHY_INTERFACE_MODE_SGMII:
130a5a6858bSRussell King 		adv = 0x0001;
131a5a6858bSRussell King 		break;
132a5a6858bSRussell King 
133a5a6858bSRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
134a5a6858bSRussell King 		adv = linkmode_adv_to_mii_adv_x(advertise,
135a5a6858bSRussell King 					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
136a5a6858bSRussell King 		break;
137a5a6858bSRussell King 
138a5a6858bSRussell King 	default:
139a5a6858bSRussell King 		return 0;
140a5a6858bSRussell King 	}
141a5a6858bSRussell King 
142a5a6858bSRussell King 	err = mv88e6352_serdes_read(chip, MII_ADVERTISE, &val);
143a5a6858bSRussell King 	if (err)
144a5a6858bSRussell King 		return err;
145a5a6858bSRussell King 
146a5a6858bSRussell King 	changed = val != adv;
147a5a6858bSRussell King 	if (changed) {
148a5a6858bSRussell King 		err = mv88e6352_serdes_write(chip, MII_ADVERTISE, adv);
149a5a6858bSRussell King 		if (err)
150a5a6858bSRussell King 			return err;
151a5a6858bSRussell King 	}
152a5a6858bSRussell King 
153a5a6858bSRussell King 	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
154a5a6858bSRussell King 	if (err)
155a5a6858bSRussell King 		return err;
156a5a6858bSRussell King 
157a5a6858bSRussell King 	if (phylink_autoneg_inband(mode))
158a5a6858bSRussell King 		bmcr = val | BMCR_ANENABLE;
159a5a6858bSRussell King 	else
160a5a6858bSRussell King 		bmcr = val & ~BMCR_ANENABLE;
161a5a6858bSRussell King 
162a5a6858bSRussell King 	if (bmcr == val)
163a5a6858bSRussell King 		return changed;
164a5a6858bSRussell King 
165a5a6858bSRussell King 	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
166a5a6858bSRussell King }
167a5a6858bSRussell King 
168a5a6858bSRussell King int mv88e6352_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
169193c5b26SPavana Sharma 				   int lane, struct phylink_link_state *state)
170a5a6858bSRussell King {
171a5a6858bSRussell King 	u16 lpa, status;
172a5a6858bSRussell King 	int err;
173a5a6858bSRussell King 
174a5a6858bSRussell King 	err = mv88e6352_serdes_read(chip, 0x11, &status);
175a5a6858bSRussell King 	if (err) {
176a5a6858bSRussell King 		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
177a5a6858bSRussell King 		return err;
178a5a6858bSRussell King 	}
179a5a6858bSRussell King 
180a5a6858bSRussell King 	err = mv88e6352_serdes_read(chip, MII_LPA, &lpa);
181a5a6858bSRussell King 	if (err) {
182a5a6858bSRussell King 		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
183a5a6858bSRussell King 		return err;
184a5a6858bSRussell King 	}
185a5a6858bSRussell King 
186a5a6858bSRussell King 	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
187a5a6858bSRussell King }
188a5a6858bSRussell King 
189a5a6858bSRussell King int mv88e6352_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
190193c5b26SPavana Sharma 				    int lane)
191a5a6858bSRussell King {
192a5a6858bSRussell King 	u16 bmcr;
193a5a6858bSRussell King 	int err;
194a5a6858bSRussell King 
195a5a6858bSRussell King 	err = mv88e6352_serdes_read(chip, MII_BMCR, &bmcr);
196a5a6858bSRussell King 	if (err)
197a5a6858bSRussell King 		return err;
198a5a6858bSRussell King 
199a5a6858bSRussell King 	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr | BMCR_ANRESTART);
200a5a6858bSRussell King }
201a5a6858bSRussell King 
202a5a6858bSRussell King int mv88e6352_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
203193c5b26SPavana Sharma 				 int lane, int speed, int duplex)
204a5a6858bSRussell King {
205a5a6858bSRussell King 	u16 val, bmcr;
206a5a6858bSRussell King 	int err;
207a5a6858bSRussell King 
208a5a6858bSRussell King 	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
209a5a6858bSRussell King 	if (err)
210a5a6858bSRussell King 		return err;
211a5a6858bSRussell King 
212a5a6858bSRussell King 	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
213a5a6858bSRussell King 	switch (speed) {
214a5a6858bSRussell King 	case SPEED_1000:
215a5a6858bSRussell King 		bmcr |= BMCR_SPEED1000;
216a5a6858bSRussell King 		break;
217a5a6858bSRussell King 	case SPEED_100:
218a5a6858bSRussell King 		bmcr |= BMCR_SPEED100;
219a5a6858bSRussell King 		break;
220a5a6858bSRussell King 	case SPEED_10:
221a5a6858bSRussell King 		break;
222a5a6858bSRussell King 	}
223a5a6858bSRussell King 
224a5a6858bSRussell King 	if (duplex == DUPLEX_FULL)
225a5a6858bSRussell King 		bmcr |= BMCR_FULLDPLX;
226a5a6858bSRussell King 
227a5a6858bSRussell King 	if (bmcr == val)
228a5a6858bSRussell King 		return 0;
229a5a6858bSRussell King 
230a5a6858bSRussell King 	return mv88e6352_serdes_write(chip, MII_BMCR, bmcr);
231a5a6858bSRussell King }
232a5a6858bSRussell King 
233193c5b26SPavana Sharma int mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
2346d91782fSAndrew Lunn {
2352d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
236193c5b26SPavana Sharma 	int lane = -ENODEV;
2376d91782fSAndrew Lunn 
2383bbb8867SMarek Behún 	if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASEX) ||
2393bbb8867SMarek Behún 	    (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX) ||
240eb755c3fSAndrew Lunn 	    (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
2419db4a725SVivien Didelot 		lane = 0xff; /* Unused */
2429db4a725SVivien Didelot 
2439db4a725SVivien Didelot 	return lane;
2449db4a725SVivien Didelot }
2459db4a725SVivien Didelot 
2469db4a725SVivien Didelot static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
2479db4a725SVivien Didelot {
248193c5b26SPavana Sharma 	if (mv88e6xxx_serdes_get_lane(chip, port) >= 0)
249b1312b85SFengguang Wu 		return true;
250eb755c3fSAndrew Lunn 
251b1312b85SFengguang Wu 	return false;
252eb755c3fSAndrew Lunn }
253eb755c3fSAndrew Lunn 
254cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat {
255cda9f4aaSAndrew Lunn 	char string[ETH_GSTRING_LEN];
256cda9f4aaSAndrew Lunn 	int sizeof_stat;
257cda9f4aaSAndrew Lunn 	int reg;
258cda9f4aaSAndrew Lunn };
259cda9f4aaSAndrew Lunn 
260cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
261cda9f4aaSAndrew Lunn 	{ "serdes_fibre_rx_error", 16, 21 },
262cda9f4aaSAndrew Lunn 	{ "serdes_PRBS_error", 32, 24 },
263cda9f4aaSAndrew Lunn };
264cda9f4aaSAndrew Lunn 
265cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
266cda9f4aaSAndrew Lunn {
267cda9f4aaSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port))
268cda9f4aaSAndrew Lunn 		return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
269cda9f4aaSAndrew Lunn 
270cda9f4aaSAndrew Lunn 	return 0;
271cda9f4aaSAndrew Lunn }
272cda9f4aaSAndrew Lunn 
27365f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
274cda9f4aaSAndrew Lunn 				 int port, uint8_t *data)
275cda9f4aaSAndrew Lunn {
276cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
277cda9f4aaSAndrew Lunn 	int i;
278cda9f4aaSAndrew Lunn 
279cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
28065f60e45SAndrew Lunn 		return 0;
281cda9f4aaSAndrew Lunn 
282cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
283cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
284cda9f4aaSAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
285cda9f4aaSAndrew Lunn 		       ETH_GSTRING_LEN);
286cda9f4aaSAndrew Lunn 	}
28765f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
288cda9f4aaSAndrew Lunn }
289cda9f4aaSAndrew Lunn 
290cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
291cda9f4aaSAndrew Lunn 					  struct mv88e6352_serdes_hw_stat *stat)
292cda9f4aaSAndrew Lunn {
293cda9f4aaSAndrew Lunn 	u64 val = 0;
294cda9f4aaSAndrew Lunn 	u16 reg;
295cda9f4aaSAndrew Lunn 	int err;
296cda9f4aaSAndrew Lunn 
297cda9f4aaSAndrew Lunn 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
298cda9f4aaSAndrew Lunn 	if (err) {
299cda9f4aaSAndrew Lunn 		dev_err(chip->dev, "failed to read statistic\n");
300cda9f4aaSAndrew Lunn 		return 0;
301cda9f4aaSAndrew Lunn 	}
302cda9f4aaSAndrew Lunn 
303cda9f4aaSAndrew Lunn 	val = reg;
304cda9f4aaSAndrew Lunn 
305cda9f4aaSAndrew Lunn 	if (stat->sizeof_stat == 32) {
306cda9f4aaSAndrew Lunn 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
307cda9f4aaSAndrew Lunn 		if (err) {
308cda9f4aaSAndrew Lunn 			dev_err(chip->dev, "failed to read statistic\n");
309cda9f4aaSAndrew Lunn 			return 0;
310cda9f4aaSAndrew Lunn 		}
311cda9f4aaSAndrew Lunn 		val = val << 16 | reg;
312cda9f4aaSAndrew Lunn 	}
313cda9f4aaSAndrew Lunn 
314cda9f4aaSAndrew Lunn 	return val;
315cda9f4aaSAndrew Lunn }
316cda9f4aaSAndrew Lunn 
31765f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
318cda9f4aaSAndrew Lunn 			       uint64_t *data)
319cda9f4aaSAndrew Lunn {
320cda9f4aaSAndrew Lunn 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
321cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
322cda9f4aaSAndrew Lunn 	u64 value;
323cda9f4aaSAndrew Lunn 	int i;
324cda9f4aaSAndrew Lunn 
325cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
32665f60e45SAndrew Lunn 		return 0;
327cda9f4aaSAndrew Lunn 
328cda9f4aaSAndrew Lunn 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
329cda9f4aaSAndrew Lunn 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
330cda9f4aaSAndrew Lunn 
331cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
332cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
333cda9f4aaSAndrew Lunn 		value = mv88e6352_serdes_get_stat(chip, stat);
334cda9f4aaSAndrew Lunn 		mv88e6xxx_port->serdes_stats[i] += value;
335cda9f4aaSAndrew Lunn 		data[i] = mv88e6xxx_port->serdes_stats[i];
336cda9f4aaSAndrew Lunn 	}
33765f60e45SAndrew Lunn 
33865f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
339cda9f4aaSAndrew Lunn }
340cda9f4aaSAndrew Lunn 
3414382172fSAndrew Lunn static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
3424382172fSAndrew Lunn {
3437e0e6243SRussell King 	u16 bmsr;
344946bc250SVivien Didelot 	int err;
3454382172fSAndrew Lunn 
3467e0e6243SRussell King 	/* If the link has dropped, we want to know about it. */
3477e0e6243SRussell King 	err = mv88e6352_serdes_read(chip, MII_BMSR, &bmsr);
3487e0e6243SRussell King 	if (err) {
3497e0e6243SRussell King 		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
350946bc250SVivien Didelot 		return;
3517e0e6243SRussell King 	}
3524382172fSAndrew Lunn 
3537e0e6243SRussell King 	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
3544382172fSAndrew Lunn }
3554382172fSAndrew Lunn 
356907b9b9fSVivien Didelot irqreturn_t mv88e6352_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
357193c5b26SPavana Sharma 					int lane)
358907b9b9fSVivien Didelot {
359907b9b9fSVivien Didelot 	irqreturn_t ret = IRQ_NONE;
360907b9b9fSVivien Didelot 	u16 status;
361907b9b9fSVivien Didelot 	int err;
362907b9b9fSVivien Didelot 
363907b9b9fSVivien Didelot 	err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status);
364907b9b9fSVivien Didelot 	if (err)
365907b9b9fSVivien Didelot 		return ret;
366907b9b9fSVivien Didelot 
367907b9b9fSVivien Didelot 	if (status & MV88E6352_SERDES_INT_LINK_CHANGE) {
368907b9b9fSVivien Didelot 		ret = IRQ_HANDLED;
369907b9b9fSVivien Didelot 		mv88e6352_serdes_irq_link(chip, port);
370907b9b9fSVivien Didelot 	}
371907b9b9fSVivien Didelot 
372907b9b9fSVivien Didelot 	return ret;
373907b9b9fSVivien Didelot }
374907b9b9fSVivien Didelot 
375193c5b26SPavana Sharma int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
37661a46b41SVivien Didelot 				bool enable)
3774382172fSAndrew Lunn {
37861a46b41SVivien Didelot 	u16 val = 0;
3794382172fSAndrew Lunn 
38061a46b41SVivien Didelot 	if (enable)
38161a46b41SVivien Didelot 		val |= MV88E6352_SERDES_INT_LINK_CHANGE;
38261a46b41SVivien Didelot 
38361a46b41SVivien Didelot 	return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, val);
3844382172fSAndrew Lunn }
3854382172fSAndrew Lunn 
3864241ef52SVivien Didelot unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
3874241ef52SVivien Didelot {
3884241ef52SVivien Didelot 	return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ);
3894241ef52SVivien Didelot }
3904241ef52SVivien Didelot 
391d3f88a24SAndrew Lunn int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
392d3f88a24SAndrew Lunn {
393d3f88a24SAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
394d3f88a24SAndrew Lunn 		return 0;
395d3f88a24SAndrew Lunn 
396d3f88a24SAndrew Lunn 	return 32 * sizeof(u16);
397d3f88a24SAndrew Lunn }
398d3f88a24SAndrew Lunn 
399d3f88a24SAndrew Lunn void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
400d3f88a24SAndrew Lunn {
401d3f88a24SAndrew Lunn 	u16 *p = _p;
402d3f88a24SAndrew Lunn 	u16 reg;
4030fd5d79eSChris Packham 	int err;
404d3f88a24SAndrew Lunn 	int i;
405d3f88a24SAndrew Lunn 
406d3f88a24SAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
407d3f88a24SAndrew Lunn 		return;
408d3f88a24SAndrew Lunn 
409d3f88a24SAndrew Lunn 	for (i = 0 ; i < 32; i++) {
4100fd5d79eSChris Packham 		err = mv88e6352_serdes_read(chip, i, &reg);
4110fd5d79eSChris Packham 		if (!err)
412d3f88a24SAndrew Lunn 			p[i] = reg;
413d3f88a24SAndrew Lunn 	}
414d3f88a24SAndrew Lunn }
415d3f88a24SAndrew Lunn 
416193c5b26SPavana Sharma int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
417d3cf7d8fSMarek Behún {
418d3cf7d8fSMarek Behún 	u8 cmode = chip->ports[port].cmode;
419193c5b26SPavana Sharma 	int lane = -ENODEV;
420d3cf7d8fSMarek Behún 
4215122d4ecSVivien Didelot 	switch (port) {
4225122d4ecSVivien Didelot 	case 5:
4233bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
424d3cf7d8fSMarek Behún 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4255122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
4265122d4ecSVivien Didelot 			lane = MV88E6341_PORT5_LANE;
4275122d4ecSVivien Didelot 		break;
428d3cf7d8fSMarek Behún 	}
429d3cf7d8fSMarek Behún 
4305122d4ecSVivien Didelot 	return lane;
431d3cf7d8fSMarek Behún }
432d3cf7d8fSMarek Behún 
433193c5b26SPavana Sharma int mv88e6185_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
434f5be107cSChris Packham 			   bool up)
435f5be107cSChris Packham {
436f5be107cSChris Packham 	/* The serdes power can't be controlled on this switch chip but we need
437f5be107cSChris Packham 	 * to supply this function to avoid returning -EOPNOTSUPP in
438f5be107cSChris Packham 	 * mv88e6xxx_serdes_power_up/mv88e6xxx_serdes_power_down
439f5be107cSChris Packham 	 */
440f5be107cSChris Packham 	return 0;
441f5be107cSChris Packham }
442f5be107cSChris Packham 
443193c5b26SPavana Sharma int mv88e6185_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
444f5be107cSChris Packham {
445f5be107cSChris Packham 	/* There are no configurable serdes lanes on this switch chip but we
4466066234aSTobias Waldekranz 	 * need to return a non-negative lane number so that callers of
447f5be107cSChris Packham 	 * mv88e6xxx_serdes_get_lane() know this is a serdes port.
448f5be107cSChris Packham 	 */
449f5be107cSChris Packham 	switch (chip->ports[port].cmode) {
450f5be107cSChris Packham 	case MV88E6185_PORT_STS_CMODE_SERDES:
451f5be107cSChris Packham 	case MV88E6185_PORT_STS_CMODE_1000BASE_X:
452f5be107cSChris Packham 		return 0;
4536066234aSTobias Waldekranz 	default:
4546066234aSTobias Waldekranz 		return -ENODEV;
455f5be107cSChris Packham 	}
456f5be107cSChris Packham }
457f5be107cSChris Packham 
458f5be107cSChris Packham int mv88e6185_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
459193c5b26SPavana Sharma 				   int lane, struct phylink_link_state *state)
460f5be107cSChris Packham {
461f5be107cSChris Packham 	int err;
462f5be107cSChris Packham 	u16 status;
463f5be107cSChris Packham 
464f5be107cSChris Packham 	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
465f5be107cSChris Packham 	if (err)
466f5be107cSChris Packham 		return err;
467f5be107cSChris Packham 
468f5be107cSChris Packham 	state->link = !!(status & MV88E6XXX_PORT_STS_LINK);
469f5be107cSChris Packham 
470f5be107cSChris Packham 	if (state->link) {
471f5be107cSChris Packham 		state->duplex = status & MV88E6XXX_PORT_STS_DUPLEX ? DUPLEX_FULL : DUPLEX_HALF;
472f5be107cSChris Packham 
473f5be107cSChris Packham 		switch (status &  MV88E6XXX_PORT_STS_SPEED_MASK) {
474f5be107cSChris Packham 		case MV88E6XXX_PORT_STS_SPEED_1000:
475f5be107cSChris Packham 			state->speed = SPEED_1000;
476f5be107cSChris Packham 			break;
477f5be107cSChris Packham 		case MV88E6XXX_PORT_STS_SPEED_100:
478f5be107cSChris Packham 			state->speed = SPEED_100;
479f5be107cSChris Packham 			break;
480f5be107cSChris Packham 		case MV88E6XXX_PORT_STS_SPEED_10:
481f5be107cSChris Packham 			state->speed = SPEED_10;
482f5be107cSChris Packham 			break;
483f5be107cSChris Packham 		default:
484f5be107cSChris Packham 			dev_err(chip->dev, "invalid PHY speed\n");
485f5be107cSChris Packham 			return -EINVAL;
486f5be107cSChris Packham 		}
487f5be107cSChris Packham 	} else {
488f5be107cSChris Packham 		state->duplex = DUPLEX_UNKNOWN;
489f5be107cSChris Packham 		state->speed = SPEED_UNKNOWN;
490f5be107cSChris Packham 	}
491f5be107cSChris Packham 
492f5be107cSChris Packham 	return 0;
493f5be107cSChris Packham }
494f5be107cSChris Packham 
495193c5b26SPavana Sharma int mv88e6097_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
4965c19bc8bSChris Packham 				bool enable)
4975c19bc8bSChris Packham {
4985c19bc8bSChris Packham 	u8 cmode = chip->ports[port].cmode;
4995c19bc8bSChris Packham 
5005c19bc8bSChris Packham 	/* The serdes interrupts are enabled in the G2_INT_MASK register. We
5015c19bc8bSChris Packham 	 * need to return 0 to avoid returning -EOPNOTSUPP in
5025c19bc8bSChris Packham 	 * mv88e6xxx_serdes_irq_enable/mv88e6xxx_serdes_irq_disable
5035c19bc8bSChris Packham 	 */
5045c19bc8bSChris Packham 	switch (cmode) {
5055c19bc8bSChris Packham 	case MV88E6185_PORT_STS_CMODE_SERDES:
5065c19bc8bSChris Packham 	case MV88E6185_PORT_STS_CMODE_1000BASE_X:
5075c19bc8bSChris Packham 		return 0;
5085c19bc8bSChris Packham 	}
5095c19bc8bSChris Packham 
5105c19bc8bSChris Packham 	return -EOPNOTSUPP;
5115c19bc8bSChris Packham }
5125c19bc8bSChris Packham 
5135c19bc8bSChris Packham static void mv88e6097_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
5145c19bc8bSChris Packham {
5155c19bc8bSChris Packham 	u16 status;
5165c19bc8bSChris Packham 	int err;
5175c19bc8bSChris Packham 
5185c19bc8bSChris Packham 	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &status);
5195c19bc8bSChris Packham 	if (err) {
5205c19bc8bSChris Packham 		dev_err(chip->dev, "can't read port status: %d\n", err);
5215c19bc8bSChris Packham 		return;
5225c19bc8bSChris Packham 	}
5235c19bc8bSChris Packham 
5245c19bc8bSChris Packham 	dsa_port_phylink_mac_change(chip->ds, port, !!(status & MV88E6XXX_PORT_STS_LINK));
5255c19bc8bSChris Packham }
5265c19bc8bSChris Packham 
5275c19bc8bSChris Packham irqreturn_t mv88e6097_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
528193c5b26SPavana Sharma 					int lane)
5295c19bc8bSChris Packham {
5305c19bc8bSChris Packham 	u8 cmode = chip->ports[port].cmode;
5315c19bc8bSChris Packham 
5325c19bc8bSChris Packham 	switch (cmode) {
5335c19bc8bSChris Packham 	case MV88E6185_PORT_STS_CMODE_SERDES:
5345c19bc8bSChris Packham 	case MV88E6185_PORT_STS_CMODE_1000BASE_X:
5355c19bc8bSChris Packham 		mv88e6097_serdes_irq_link(chip, port);
5365c19bc8bSChris Packham 		return IRQ_HANDLED;
5375c19bc8bSChris Packham 	}
5385c19bc8bSChris Packham 
5395c19bc8bSChris Packham 	return IRQ_NONE;
5405c19bc8bSChris Packham }
5415c19bc8bSChris Packham 
542193c5b26SPavana Sharma int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
54307ffbd74SAndrew Lunn {
5442d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
545193c5b26SPavana Sharma 	int lane = -ENODEV;
54607ffbd74SAndrew Lunn 
54707ffbd74SAndrew Lunn 	switch (port) {
54807ffbd74SAndrew Lunn 	case 9:
5493bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
55007ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
5515122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
5525122d4ecSVivien Didelot 			lane = MV88E6390_PORT9_LANE0;
55317deaf5cSMarek Behún 		break;
55407ffbd74SAndrew Lunn 	case 10:
5553bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
55607ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
5575122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
5585122d4ecSVivien Didelot 			lane = MV88E6390_PORT10_LANE0;
55917deaf5cSMarek Behún 		break;
56007ffbd74SAndrew Lunn 	}
56107ffbd74SAndrew Lunn 
5625122d4ecSVivien Didelot 	return lane;
56317deaf5cSMarek Behún }
56417deaf5cSMarek Behún 
565193c5b26SPavana Sharma int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
566a8c01c0dSAndrew Lunn {
5675122d4ecSVivien Didelot 	u8 cmode_port = chip->ports[port].cmode;
5685122d4ecSVivien Didelot 	u8 cmode_port10 = chip->ports[10].cmode;
5695122d4ecSVivien Didelot 	u8 cmode_port9 = chip->ports[9].cmode;
570193c5b26SPavana Sharma 	int lane = -ENODEV;
571a8c01c0dSAndrew Lunn 
572a8c01c0dSAndrew Lunn 	switch (port) {
573a8c01c0dSAndrew Lunn 	case 2:
5743bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
575a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
5765122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
5775122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
5785122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE1;
57917deaf5cSMarek Behún 		break;
580a8c01c0dSAndrew Lunn 	case 3:
5813bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
582a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
583a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
5845122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
5855122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
5865122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE2;
58717deaf5cSMarek Behún 		break;
588a8c01c0dSAndrew Lunn 	case 4:
5893bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
590a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
591a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
5925122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
5935122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
5945122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE3;
59517deaf5cSMarek Behún 		break;
596a8c01c0dSAndrew Lunn 	case 5:
5973bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
598a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
5995122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
6005122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
6015122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE1;
60217deaf5cSMarek Behún 		break;
603a8c01c0dSAndrew Lunn 	case 6:
6043bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
605a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
606a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
6075122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
6085122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
6095122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE2;
61017deaf5cSMarek Behún 		break;
611a8c01c0dSAndrew Lunn 	case 7:
6123bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
613a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
614a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
6155122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
6165122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
6175122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE3;
61817deaf5cSMarek Behún 		break;
619a8c01c0dSAndrew Lunn 	case 9:
6203bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
621a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
622a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
623a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
6245122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
6255122d4ecSVivien Didelot 			lane = MV88E6390_PORT9_LANE0;
62617deaf5cSMarek Behún 		break;
627a8c01c0dSAndrew Lunn 	case 10:
6283bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
629a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
630a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
631a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
6325122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
6335122d4ecSVivien Didelot 			lane = MV88E6390_PORT10_LANE0;
63417deaf5cSMarek Behún 		break;
63517deaf5cSMarek Behún 	}
63617deaf5cSMarek Behún 
6375122d4ecSVivien Didelot 	return lane;
638a8c01c0dSAndrew Lunn }
639a8c01c0dSAndrew Lunn 
640de776d0dSPavana Sharma /* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address
641de776d0dSPavana Sharma  * a port is using else Returns -ENODEV.
642de776d0dSPavana Sharma  */
643de776d0dSPavana Sharma int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
644de776d0dSPavana Sharma {
645de776d0dSPavana Sharma 	u8 cmode = chip->ports[port].cmode;
646de776d0dSPavana Sharma 	int lane = -ENODEV;
647de776d0dSPavana Sharma 
648de776d0dSPavana Sharma 	if (port != 0 && port != 9 && port != 10)
649de776d0dSPavana Sharma 		return -EOPNOTSUPP;
650de776d0dSPavana Sharma 
651de776d0dSPavana Sharma 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
652de776d0dSPavana Sharma 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
653de776d0dSPavana Sharma 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
654de776d0dSPavana Sharma 	    cmode == MV88E6393X_PORT_STS_CMODE_5GBASER ||
655de776d0dSPavana Sharma 	    cmode == MV88E6393X_PORT_STS_CMODE_10GBASER)
656de776d0dSPavana Sharma 		lane = port;
657de776d0dSPavana Sharma 
658de776d0dSPavana Sharma 	return lane;
659de776d0dSPavana Sharma }
660de776d0dSPavana Sharma 
661dc272f60SVivien Didelot /* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */
662193c5b26SPavana Sharma static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, int lane,
663dc272f60SVivien Didelot 				      bool up)
6646335e9f2SAndrew Lunn {
6656335e9f2SAndrew Lunn 	u16 val, new_val;
6666335e9f2SAndrew Lunn 	int err;
6676335e9f2SAndrew Lunn 
668e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
669bf604bc9SRussell King 				    MV88E6390_10G_CTRL1, &val);
670e6891c76SAndrew Lunn 
6716335e9f2SAndrew Lunn 	if (err)
6726335e9f2SAndrew Lunn 		return err;
6736335e9f2SAndrew Lunn 
674dc272f60SVivien Didelot 	if (up)
675bf604bc9SRussell King 		new_val = val & ~(MDIO_CTRL1_RESET |
676bf604bc9SRussell King 				  MDIO_PCS_CTRL1_LOOPBACK |
677bf604bc9SRussell King 				  MDIO_CTRL1_LPOWER);
6786335e9f2SAndrew Lunn 	else
679bf604bc9SRussell King 		new_val = val | MDIO_CTRL1_LPOWER;
6806335e9f2SAndrew Lunn 
6816335e9f2SAndrew Lunn 	if (val != new_val)
682e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
683bf604bc9SRussell King 					     MV88E6390_10G_CTRL1, new_val);
6846335e9f2SAndrew Lunn 
6856335e9f2SAndrew Lunn 	return err;
6866335e9f2SAndrew Lunn }
6876335e9f2SAndrew Lunn 
688dc272f60SVivien Didelot /* Set power up/down for SGMII and 1000Base-X */
689193c5b26SPavana Sharma static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, int lane,
690dc272f60SVivien Didelot 					bool up)
6916335e9f2SAndrew Lunn {
6926335e9f2SAndrew Lunn 	u16 val, new_val;
6936335e9f2SAndrew Lunn 	int err;
6946335e9f2SAndrew Lunn 
695e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
6964c8b7350SRussell King 				    MV88E6390_SGMII_BMCR, &val);
6976335e9f2SAndrew Lunn 	if (err)
6986335e9f2SAndrew Lunn 		return err;
6996335e9f2SAndrew Lunn 
700dc272f60SVivien Didelot 	if (up)
7014c8b7350SRussell King 		new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN);
7026335e9f2SAndrew Lunn 	else
7034c8b7350SRussell King 		new_val = val | BMCR_PDOWN;
7046335e9f2SAndrew Lunn 
7056335e9f2SAndrew Lunn 	if (val != new_val)
706e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
7074c8b7350SRussell King 					     MV88E6390_SGMII_BMCR, new_val);
7086335e9f2SAndrew Lunn 
7096335e9f2SAndrew Lunn 	return err;
7106335e9f2SAndrew Lunn }
7116335e9f2SAndrew Lunn 
7120df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat {
7130df95287SNikita Yushchenko 	char string[ETH_GSTRING_LEN];
7140df95287SNikita Yushchenko 	int reg;
7150df95287SNikita Yushchenko };
7160df95287SNikita Yushchenko 
7170df95287SNikita Yushchenko static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = {
7180df95287SNikita Yushchenko 	{ "serdes_rx_pkts", 0xf021 },
7190df95287SNikita Yushchenko 	{ "serdes_rx_bytes", 0xf024 },
7200df95287SNikita Yushchenko 	{ "serdes_rx_pkts_error", 0xf027 },
7210df95287SNikita Yushchenko };
7220df95287SNikita Yushchenko 
7230df95287SNikita Yushchenko int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
7240df95287SNikita Yushchenko {
725*a03b98d6SMarek Behún 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
7260df95287SNikita Yushchenko 		return 0;
7270df95287SNikita Yushchenko 
7280df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
7290df95287SNikita Yushchenko }
7300df95287SNikita Yushchenko 
7310df95287SNikita Yushchenko int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip,
7320df95287SNikita Yushchenko 				 int port, uint8_t *data)
7330df95287SNikita Yushchenko {
7340df95287SNikita Yushchenko 	struct mv88e6390_serdes_hw_stat *stat;
7350df95287SNikita Yushchenko 	int i;
7360df95287SNikita Yushchenko 
737*a03b98d6SMarek Behún 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
7380df95287SNikita Yushchenko 		return 0;
7390df95287SNikita Yushchenko 
7400df95287SNikita Yushchenko 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
7410df95287SNikita Yushchenko 		stat = &mv88e6390_serdes_hw_stats[i];
7420df95287SNikita Yushchenko 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
7430df95287SNikita Yushchenko 		       ETH_GSTRING_LEN);
7440df95287SNikita Yushchenko 	}
7450df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
7460df95287SNikita Yushchenko }
7470df95287SNikita Yushchenko 
7480df95287SNikita Yushchenko static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane,
7490df95287SNikita Yushchenko 					  struct mv88e6390_serdes_hw_stat *stat)
7500df95287SNikita Yushchenko {
7510df95287SNikita Yushchenko 	u16 reg[3];
7520df95287SNikita Yushchenko 	int err, i;
7530df95287SNikita Yushchenko 
7540df95287SNikita Yushchenko 	for (i = 0; i < 3; i++) {
7550df95287SNikita Yushchenko 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
7560df95287SNikita Yushchenko 					    stat->reg + i, &reg[i]);
7570df95287SNikita Yushchenko 		if (err) {
7580df95287SNikita Yushchenko 			dev_err(chip->dev, "failed to read statistic\n");
7590df95287SNikita Yushchenko 			return 0;
7600df95287SNikita Yushchenko 		}
7610df95287SNikita Yushchenko 	}
7620df95287SNikita Yushchenko 
7630df95287SNikita Yushchenko 	return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32);
7640df95287SNikita Yushchenko }
7650df95287SNikita Yushchenko 
7660df95287SNikita Yushchenko int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
7670df95287SNikita Yushchenko 			       uint64_t *data)
7680df95287SNikita Yushchenko {
7690df95287SNikita Yushchenko 	struct mv88e6390_serdes_hw_stat *stat;
7700df95287SNikita Yushchenko 	int lane;
7710df95287SNikita Yushchenko 	int i;
7720df95287SNikita Yushchenko 
773*a03b98d6SMarek Behún 	lane = mv88e6xxx_serdes_get_lane(chip, port);
774193c5b26SPavana Sharma 	if (lane < 0)
7750df95287SNikita Yushchenko 		return 0;
7760df95287SNikita Yushchenko 
7770df95287SNikita Yushchenko 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
7780df95287SNikita Yushchenko 		stat = &mv88e6390_serdes_hw_stats[i];
7790df95287SNikita Yushchenko 		data[i] = mv88e6390_serdes_get_stat(chip, lane, stat);
7800df95287SNikita Yushchenko 	}
7810df95287SNikita Yushchenko 
7820df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
7830df95287SNikita Yushchenko }
7840df95287SNikita Yushchenko 
785193c5b26SPavana Sharma static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, int lane)
7860df95287SNikita Yushchenko {
7870df95287SNikita Yushchenko 	u16 reg;
7880df95287SNikita Yushchenko 	int err;
7890df95287SNikita Yushchenko 
7900df95287SNikita Yushchenko 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
7910df95287SNikita Yushchenko 				    MV88E6390_PG_CONTROL, &reg);
7920df95287SNikita Yushchenko 	if (err)
7930df95287SNikita Yushchenko 		return err;
7940df95287SNikita Yushchenko 
7950df95287SNikita Yushchenko 	reg |= MV88E6390_PG_CONTROL_ENABLE_PC;
7960df95287SNikita Yushchenko 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
7970df95287SNikita Yushchenko 				      MV88E6390_PG_CONTROL, reg);
7980df95287SNikita Yushchenko }
7990df95287SNikita Yushchenko 
800193c5b26SPavana Sharma int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
801dc272f60SVivien Didelot 			   bool up)
8026335e9f2SAndrew Lunn {
8032d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
8040df95287SNikita Yushchenko 	int err = 0;
8056335e9f2SAndrew Lunn 
806a8c01c0dSAndrew Lunn 	switch (cmode) {
807a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
8083bbb8867SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
809f8236a08SAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
8100df95287SNikita Yushchenko 		err = mv88e6390_serdes_power_sgmii(chip, lane, up);
8110df95287SNikita Yushchenko 		break;
812a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_XAUI:
813a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
8140df95287SNikita Yushchenko 		err = mv88e6390_serdes_power_10g(chip, lane, up);
8150df95287SNikita Yushchenko 		break;
816a8c01c0dSAndrew Lunn 	}
817a8c01c0dSAndrew Lunn 
8180df95287SNikita Yushchenko 	if (!err && up)
8190df95287SNikita Yushchenko 		err = mv88e6390_serdes_enable_checker(chip, lane);
8200df95287SNikita Yushchenko 
8210df95287SNikita Yushchenko 	return err;
822a8c01c0dSAndrew Lunn }
823a8c01c0dSAndrew Lunn 
824a5a6858bSRussell King int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
825193c5b26SPavana Sharma 				int lane, unsigned int mode,
826a5a6858bSRussell King 				phy_interface_t interface,
827a5a6858bSRussell King 				const unsigned long *advertise)
828a5a6858bSRussell King {
829a5a6858bSRussell King 	u16 val, bmcr, adv;
830a5a6858bSRussell King 	bool changed;
831a5a6858bSRussell King 	int err;
832a5a6858bSRussell King 
833a5a6858bSRussell King 	switch (interface) {
834a5a6858bSRussell King 	case PHY_INTERFACE_MODE_SGMII:
835a5a6858bSRussell King 		adv = 0x0001;
836a5a6858bSRussell King 		break;
837a5a6858bSRussell King 
838a5a6858bSRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
839a5a6858bSRussell King 		adv = linkmode_adv_to_mii_adv_x(advertise,
840a5a6858bSRussell King 					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
841a5a6858bSRussell King 		break;
842a5a6858bSRussell King 
843a5a6858bSRussell King 	case PHY_INTERFACE_MODE_2500BASEX:
844a5a6858bSRussell King 		adv = linkmode_adv_to_mii_adv_x(advertise,
845a5a6858bSRussell King 					ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
846a5a6858bSRussell King 		break;
847a5a6858bSRussell King 
848a5a6858bSRussell King 	default:
849a5a6858bSRussell King 		return 0;
850a5a6858bSRussell King 	}
851a5a6858bSRussell King 
852a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
853a5a6858bSRussell King 				    MV88E6390_SGMII_ADVERTISE, &val);
854a5a6858bSRussell King 	if (err)
855a5a6858bSRussell King 		return err;
856a5a6858bSRussell King 
857a5a6858bSRussell King 	changed = val != adv;
858a5a6858bSRussell King 	if (changed) {
859a5a6858bSRussell King 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
860a5a6858bSRussell King 					     MV88E6390_SGMII_ADVERTISE, adv);
861a5a6858bSRussell King 		if (err)
862a5a6858bSRussell King 			return err;
863a5a6858bSRussell King 	}
864a5a6858bSRussell King 
865a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
866a5a6858bSRussell King 				    MV88E6390_SGMII_BMCR, &val);
867a5a6858bSRussell King 	if (err)
868a5a6858bSRussell King 		return err;
869a5a6858bSRussell King 
870a5a6858bSRussell King 	if (phylink_autoneg_inband(mode))
871a5a6858bSRussell King 		bmcr = val | BMCR_ANENABLE;
872a5a6858bSRussell King 	else
873a5a6858bSRussell King 		bmcr = val & ~BMCR_ANENABLE;
874a5a6858bSRussell King 
875a5a6858bSRussell King 	/* setting ANENABLE triggers a restart of negotiation */
876a5a6858bSRussell King 	if (bmcr == val)
877a5a6858bSRussell King 		return changed;
878a5a6858bSRussell King 
879a5a6858bSRussell King 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
880a5a6858bSRussell King 				      MV88E6390_SGMII_BMCR, bmcr);
881a5a6858bSRussell King }
882a5a6858bSRussell King 
8837019bba4SRussell King static int mv88e6390_serdes_pcs_get_state_sgmii(struct mv88e6xxx_chip *chip,
884193c5b26SPavana Sharma 	int port, int lane, struct phylink_link_state *state)
885a5a6858bSRussell King {
886a5a6858bSRussell King 	u16 lpa, status;
887a5a6858bSRussell King 	int err;
888a5a6858bSRussell King 
889a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
890a5a6858bSRussell King 				    MV88E6390_SGMII_PHY_STATUS, &status);
891a5a6858bSRussell King 	if (err) {
892a5a6858bSRussell King 		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
893a5a6858bSRussell King 		return err;
894a5a6858bSRussell King 	}
895a5a6858bSRussell King 
896a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
897a5a6858bSRussell King 				    MV88E6390_SGMII_LPA, &lpa);
898a5a6858bSRussell King 	if (err) {
899a5a6858bSRussell King 		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
900a5a6858bSRussell King 		return err;
901a5a6858bSRussell King 	}
902a5a6858bSRussell King 
903a5a6858bSRussell King 	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
904a5a6858bSRussell King }
905a5a6858bSRussell King 
9067019bba4SRussell King static int mv88e6390_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
907193c5b26SPavana Sharma 	int port, int lane, struct phylink_link_state *state)
9087019bba4SRussell King {
9097019bba4SRussell King 	u16 status;
9107019bba4SRussell King 	int err;
9117019bba4SRussell King 
9127019bba4SRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
9137019bba4SRussell King 				    MV88E6390_10G_STAT1, &status);
9147019bba4SRussell King 	if (err)
9157019bba4SRussell King 		return err;
9167019bba4SRussell King 
9177019bba4SRussell King 	state->link = !!(status & MDIO_STAT1_LSTATUS);
9187019bba4SRussell King 	if (state->link) {
9197019bba4SRussell King 		state->speed = SPEED_10000;
9207019bba4SRussell King 		state->duplex = DUPLEX_FULL;
9217019bba4SRussell King 	}
9227019bba4SRussell King 
9237019bba4SRussell King 	return 0;
9247019bba4SRussell King }
9257019bba4SRussell King 
926de776d0dSPavana Sharma static int mv88e6393x_serdes_pcs_get_state_10g(struct mv88e6xxx_chip *chip,
927de776d0dSPavana Sharma 					       int port, int lane,
928de776d0dSPavana Sharma 					       struct phylink_link_state *state)
929de776d0dSPavana Sharma {
930de776d0dSPavana Sharma 	u16 status;
931de776d0dSPavana Sharma 	int err;
932de776d0dSPavana Sharma 
933de776d0dSPavana Sharma 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
934de776d0dSPavana Sharma 				    MV88E6390_10G_STAT1, &status);
935de776d0dSPavana Sharma 	if (err)
936de776d0dSPavana Sharma 		return err;
937de776d0dSPavana Sharma 
938de776d0dSPavana Sharma 	state->link = !!(status & MDIO_STAT1_LSTATUS);
939de776d0dSPavana Sharma 	if (state->link) {
940de776d0dSPavana Sharma 		if (state->interface == PHY_INTERFACE_MODE_5GBASER)
941de776d0dSPavana Sharma 			state->speed = SPEED_5000;
942de776d0dSPavana Sharma 		else
943de776d0dSPavana Sharma 			state->speed = SPEED_10000;
944de776d0dSPavana Sharma 		state->duplex = DUPLEX_FULL;
945de776d0dSPavana Sharma 	}
946de776d0dSPavana Sharma 
947de776d0dSPavana Sharma 	return 0;
948de776d0dSPavana Sharma }
949de776d0dSPavana Sharma 
9507019bba4SRussell King int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
951193c5b26SPavana Sharma 				   int lane, struct phylink_link_state *state)
9527019bba4SRussell King {
9537019bba4SRussell King 	switch (state->interface) {
9547019bba4SRussell King 	case PHY_INTERFACE_MODE_SGMII:
9557019bba4SRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
9567019bba4SRussell King 	case PHY_INTERFACE_MODE_2500BASEX:
9577019bba4SRussell King 		return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane,
9587019bba4SRussell King 							    state);
9597019bba4SRussell King 	case PHY_INTERFACE_MODE_XAUI:
9607019bba4SRussell King 	case PHY_INTERFACE_MODE_RXAUI:
9617019bba4SRussell King 		return mv88e6390_serdes_pcs_get_state_10g(chip, port, lane,
9627019bba4SRussell King 							  state);
9637019bba4SRussell King 
9647019bba4SRussell King 	default:
9657019bba4SRussell King 		return -EOPNOTSUPP;
9667019bba4SRussell King 	}
9677019bba4SRussell King }
9687019bba4SRussell King 
969de776d0dSPavana Sharma int mv88e6393x_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
970de776d0dSPavana Sharma 				    int lane, struct phylink_link_state *state)
971de776d0dSPavana Sharma {
972de776d0dSPavana Sharma 	switch (state->interface) {
973de776d0dSPavana Sharma 	case PHY_INTERFACE_MODE_SGMII:
974de776d0dSPavana Sharma 	case PHY_INTERFACE_MODE_1000BASEX:
975de776d0dSPavana Sharma 	case PHY_INTERFACE_MODE_2500BASEX:
976de776d0dSPavana Sharma 		return mv88e6390_serdes_pcs_get_state_sgmii(chip, port, lane,
977de776d0dSPavana Sharma 							    state);
978de776d0dSPavana Sharma 	case PHY_INTERFACE_MODE_5GBASER:
979de776d0dSPavana Sharma 	case PHY_INTERFACE_MODE_10GBASER:
980de776d0dSPavana Sharma 		return mv88e6393x_serdes_pcs_get_state_10g(chip, port, lane,
981de776d0dSPavana Sharma 							   state);
982de776d0dSPavana Sharma 
983de776d0dSPavana Sharma 	default:
984de776d0dSPavana Sharma 		return -EOPNOTSUPP;
985de776d0dSPavana Sharma 	}
986de776d0dSPavana Sharma }
987de776d0dSPavana Sharma 
988a5a6858bSRussell King int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
989193c5b26SPavana Sharma 				    int lane)
990a5a6858bSRussell King {
991a5a6858bSRussell King 	u16 bmcr;
992a5a6858bSRussell King 	int err;
993a5a6858bSRussell King 
994a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
995a5a6858bSRussell King 				    MV88E6390_SGMII_BMCR, &bmcr);
996a5a6858bSRussell King 	if (err)
997a5a6858bSRussell King 		return err;
998a5a6858bSRussell King 
999a5a6858bSRussell King 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
1000a5a6858bSRussell King 				      MV88E6390_SGMII_BMCR,
1001a5a6858bSRussell King 				      bmcr | BMCR_ANRESTART);
1002a5a6858bSRussell King }
1003a5a6858bSRussell King 
1004a5a6858bSRussell King int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
1005193c5b26SPavana Sharma 				 int lane, int speed, int duplex)
1006a5a6858bSRussell King {
1007a5a6858bSRussell King 	u16 val, bmcr;
1008a5a6858bSRussell King 	int err;
1009a5a6858bSRussell King 
1010a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1011a5a6858bSRussell King 				    MV88E6390_SGMII_BMCR, &val);
1012a5a6858bSRussell King 	if (err)
1013a5a6858bSRussell King 		return err;
1014a5a6858bSRussell King 
1015a5a6858bSRussell King 	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
1016a5a6858bSRussell King 	switch (speed) {
1017a5a6858bSRussell King 	case SPEED_2500:
1018a5a6858bSRussell King 	case SPEED_1000:
1019a5a6858bSRussell King 		bmcr |= BMCR_SPEED1000;
1020a5a6858bSRussell King 		break;
1021a5a6858bSRussell King 	case SPEED_100:
1022a5a6858bSRussell King 		bmcr |= BMCR_SPEED100;
1023a5a6858bSRussell King 		break;
1024a5a6858bSRussell King 	case SPEED_10:
1025a5a6858bSRussell King 		break;
1026a5a6858bSRussell King 	}
1027a5a6858bSRussell King 
1028a5a6858bSRussell King 	if (duplex == DUPLEX_FULL)
1029a5a6858bSRussell King 		bmcr |= BMCR_FULLDPLX;
1030a5a6858bSRussell King 
1031a5a6858bSRussell King 	if (bmcr == val)
1032a5a6858bSRussell King 		return 0;
1033a5a6858bSRussell King 
1034a5a6858bSRussell King 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
1035a5a6858bSRussell King 				      MV88E6390_SGMII_BMCR, bmcr);
1036a5a6858bSRussell King }
1037a5a6858bSRussell King 
1038efd1ba6aSAndrew Lunn static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
1039193c5b26SPavana Sharma 					    int port, int lane)
1040efd1ba6aSAndrew Lunn {
10417e0e6243SRussell King 	u16 bmsr;
1042a5a6858bSRussell King 	int err;
1043efd1ba6aSAndrew Lunn 
10447e0e6243SRussell King 	/* If the link has dropped, we want to know about it. */
104572d8b4fdSHeiner Kallweit 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
10467e0e6243SRussell King 				    MV88E6390_SGMII_BMSR, &bmsr);
104772d8b4fdSHeiner Kallweit 	if (err) {
10487e0e6243SRussell King 		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
104972d8b4fdSHeiner Kallweit 		return;
105072d8b4fdSHeiner Kallweit 	}
1051efd1ba6aSAndrew Lunn 
10527e0e6243SRussell King 	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
1053efd1ba6aSAndrew Lunn }
1054efd1ba6aSAndrew Lunn 
1055de776d0dSPavana Sharma static void mv88e6393x_serdes_irq_link_10g(struct mv88e6xxx_chip *chip,
1056de776d0dSPavana Sharma 					   int port, u8 lane)
1057de776d0dSPavana Sharma {
1058de776d0dSPavana Sharma 	u16 status;
1059de776d0dSPavana Sharma 	int err;
1060de776d0dSPavana Sharma 
1061de776d0dSPavana Sharma 	/* If the link has dropped, we want to know about it. */
1062de776d0dSPavana Sharma 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1063de776d0dSPavana Sharma 				    MV88E6390_10G_STAT1, &status);
1064de776d0dSPavana Sharma 	if (err) {
1065de776d0dSPavana Sharma 		dev_err(chip->dev, "can't read Serdes STAT1: %d\n", err);
1066de776d0dSPavana Sharma 		return;
1067de776d0dSPavana Sharma 	}
1068de776d0dSPavana Sharma 
1069de776d0dSPavana Sharma 	dsa_port_phylink_mac_change(chip->ds, port, !!(status & MDIO_STAT1_LSTATUS));
1070de776d0dSPavana Sharma }
1071de776d0dSPavana Sharma 
1072efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
1073193c5b26SPavana Sharma 					     int lane, bool enable)
1074efd1ba6aSAndrew Lunn {
107561a46b41SVivien Didelot 	u16 val = 0;
107661a46b41SVivien Didelot 
107761a46b41SVivien Didelot 	if (enable)
107861a46b41SVivien Didelot 		val |= MV88E6390_SGMII_INT_LINK_DOWN |
107961a46b41SVivien Didelot 			MV88E6390_SGMII_INT_LINK_UP;
108061a46b41SVivien Didelot 
1081efd1ba6aSAndrew Lunn 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
108261a46b41SVivien Didelot 				      MV88E6390_SGMII_INT_ENABLE, val);
1083efd1ba6aSAndrew Lunn }
1084efd1ba6aSAndrew Lunn 
1085193c5b26SPavana Sharma int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, int lane,
108661a46b41SVivien Didelot 				bool enable)
1087efd1ba6aSAndrew Lunn {
1088efd1ba6aSAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
1089efd1ba6aSAndrew Lunn 
1090efd1ba6aSAndrew Lunn 	switch (cmode) {
1091efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
10923bbb8867SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
1093efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
109461a46b41SVivien Didelot 		return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable);
1095efd1ba6aSAndrew Lunn 	}
1096efd1ba6aSAndrew Lunn 
109761a46b41SVivien Didelot 	return 0;
1098efd1ba6aSAndrew Lunn }
1099efd1ba6aSAndrew Lunn 
1100efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
1101193c5b26SPavana Sharma 					     int lane, u16 *status)
1102efd1ba6aSAndrew Lunn {
1103efd1ba6aSAndrew Lunn 	int err;
1104efd1ba6aSAndrew Lunn 
1105efd1ba6aSAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1106efd1ba6aSAndrew Lunn 				    MV88E6390_SGMII_INT_STATUS, status);
1107efd1ba6aSAndrew Lunn 
1108efd1ba6aSAndrew Lunn 	return err;
1109efd1ba6aSAndrew Lunn }
1110efd1ba6aSAndrew Lunn 
1111de776d0dSPavana Sharma static int mv88e6393x_serdes_irq_enable_10g(struct mv88e6xxx_chip *chip,
1112de776d0dSPavana Sharma 					    u8 lane, bool enable)
1113de776d0dSPavana Sharma {
1114de776d0dSPavana Sharma 	u16 val = 0;
1115de776d0dSPavana Sharma 
1116de776d0dSPavana Sharma 	if (enable)
1117de776d0dSPavana Sharma 		val |= MV88E6393X_10G_INT_LINK_CHANGE;
1118de776d0dSPavana Sharma 
1119de776d0dSPavana Sharma 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
1120de776d0dSPavana Sharma 				      MV88E6393X_10G_INT_ENABLE, val);
1121de776d0dSPavana Sharma }
1122de776d0dSPavana Sharma 
1123de776d0dSPavana Sharma int mv88e6393x_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
1124de776d0dSPavana Sharma 				 int lane, bool enable)
1125de776d0dSPavana Sharma {
1126de776d0dSPavana Sharma 	u8 cmode = chip->ports[port].cmode;
1127de776d0dSPavana Sharma 
1128de776d0dSPavana Sharma 	switch (cmode) {
1129de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
1130de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
1131de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
1132de776d0dSPavana Sharma 		return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable);
1133de776d0dSPavana Sharma 	case MV88E6393X_PORT_STS_CMODE_5GBASER:
1134de776d0dSPavana Sharma 	case MV88E6393X_PORT_STS_CMODE_10GBASER:
1135de776d0dSPavana Sharma 		return mv88e6393x_serdes_irq_enable_10g(chip, lane, enable);
1136de776d0dSPavana Sharma 	}
1137de776d0dSPavana Sharma 
1138de776d0dSPavana Sharma 	return 0;
1139de776d0dSPavana Sharma }
1140de776d0dSPavana Sharma 
1141de776d0dSPavana Sharma static int mv88e6393x_serdes_irq_status_10g(struct mv88e6xxx_chip *chip,
1142de776d0dSPavana Sharma 					    u8 lane, u16 *status)
1143de776d0dSPavana Sharma {
1144de776d0dSPavana Sharma 	int err;
1145de776d0dSPavana Sharma 
1146de776d0dSPavana Sharma 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1147de776d0dSPavana Sharma 				    MV88E6393X_10G_INT_STATUS, status);
1148de776d0dSPavana Sharma 
1149de776d0dSPavana Sharma 	return err;
1150de776d0dSPavana Sharma }
1151de776d0dSPavana Sharma 
1152de776d0dSPavana Sharma irqreturn_t mv88e6393x_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
1153de776d0dSPavana Sharma 					 int lane)
1154de776d0dSPavana Sharma {
1155de776d0dSPavana Sharma 	u8 cmode = chip->ports[port].cmode;
1156de776d0dSPavana Sharma 	irqreturn_t ret = IRQ_NONE;
1157de776d0dSPavana Sharma 	u16 status;
1158de776d0dSPavana Sharma 	int err;
1159de776d0dSPavana Sharma 
1160de776d0dSPavana Sharma 	switch (cmode) {
1161de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
1162de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
1163de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
1164de776d0dSPavana Sharma 		err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
1165de776d0dSPavana Sharma 		if (err)
1166de776d0dSPavana Sharma 			return ret;
1167de776d0dSPavana Sharma 		if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
1168de776d0dSPavana Sharma 			      MV88E6390_SGMII_INT_LINK_UP)) {
1169de776d0dSPavana Sharma 			ret = IRQ_HANDLED;
1170de776d0dSPavana Sharma 			mv88e6390_serdes_irq_link_sgmii(chip, port, lane);
1171de776d0dSPavana Sharma 		}
1172de776d0dSPavana Sharma 		break;
1173de776d0dSPavana Sharma 	case MV88E6393X_PORT_STS_CMODE_5GBASER:
1174de776d0dSPavana Sharma 	case MV88E6393X_PORT_STS_CMODE_10GBASER:
1175de776d0dSPavana Sharma 		err = mv88e6393x_serdes_irq_status_10g(chip, lane, &status);
1176de776d0dSPavana Sharma 		if (err)
1177de776d0dSPavana Sharma 			return err;
1178de776d0dSPavana Sharma 		if (status & MV88E6393X_10G_INT_LINK_CHANGE) {
1179de776d0dSPavana Sharma 			ret = IRQ_HANDLED;
1180de776d0dSPavana Sharma 			mv88e6393x_serdes_irq_link_10g(chip, port, lane);
1181de776d0dSPavana Sharma 		}
1182de776d0dSPavana Sharma 		break;
1183de776d0dSPavana Sharma 	}
1184de776d0dSPavana Sharma 
1185de776d0dSPavana Sharma 	return ret;
1186de776d0dSPavana Sharma }
1187de776d0dSPavana Sharma 
1188907b9b9fSVivien Didelot irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
1189193c5b26SPavana Sharma 					int lane)
1190efd1ba6aSAndrew Lunn {
1191907b9b9fSVivien Didelot 	u8 cmode = chip->ports[port].cmode;
1192efd1ba6aSAndrew Lunn 	irqreturn_t ret = IRQ_NONE;
1193efd1ba6aSAndrew Lunn 	u16 status;
1194efd1ba6aSAndrew Lunn 	int err;
1195b98f0f53SVivien Didelot 
1196efd1ba6aSAndrew Lunn 	switch (cmode) {
1197efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
11983bbb8867SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
1199efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
1200efd1ba6aSAndrew Lunn 		err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
1201efd1ba6aSAndrew Lunn 		if (err)
1202907b9b9fSVivien Didelot 			return ret;
12036feddb49SDan Carpenter 		if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
1204efd1ba6aSAndrew Lunn 			      MV88E6390_SGMII_INT_LINK_UP)) {
1205efd1ba6aSAndrew Lunn 			ret = IRQ_HANDLED;
1206907b9b9fSVivien Didelot 			mv88e6390_serdes_irq_link_sgmii(chip, port, lane);
1207efd1ba6aSAndrew Lunn 		}
1208efd1ba6aSAndrew Lunn 	}
1209907b9b9fSVivien Didelot 
1210907b9b9fSVivien Didelot 	return ret;
1211907b9b9fSVivien Didelot }
1212907b9b9fSVivien Didelot 
12134241ef52SVivien Didelot unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
12144241ef52SVivien Didelot {
12154241ef52SVivien Didelot 	return irq_find_mapping(chip->g2_irq.domain, port);
12164241ef52SVivien Didelot }
1217bf3504ceSAndrew Lunn 
1218bf3504ceSAndrew Lunn static const u16 mv88e6390_serdes_regs[] = {
1219bf3504ceSAndrew Lunn 	/* SERDES common registers */
1220bf3504ceSAndrew Lunn 	0xf00a, 0xf00b, 0xf00c,
1221bf3504ceSAndrew Lunn 	0xf010, 0xf011, 0xf012, 0xf013,
1222bf3504ceSAndrew Lunn 	0xf016, 0xf017, 0xf018,
1223bf3504ceSAndrew Lunn 	0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
1224bf3504ceSAndrew Lunn 	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
1225bf3504ceSAndrew Lunn 	0xf028, 0xf029,
1226bf3504ceSAndrew Lunn 	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
1227bf3504ceSAndrew Lunn 	0xf038, 0xf039,
1228bf3504ceSAndrew Lunn 	/* SGMII */
1229bf3504ceSAndrew Lunn 	0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
1230bf3504ceSAndrew Lunn 	0x2008,
1231bf3504ceSAndrew Lunn 	0x200f,
1232bf3504ceSAndrew Lunn 	0xa000, 0xa001, 0xa002, 0xa003,
1233bf3504ceSAndrew Lunn 	/* 10Gbase-X */
1234bf3504ceSAndrew Lunn 	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
1235bf3504ceSAndrew Lunn 	0x1008,
1236bf3504ceSAndrew Lunn 	0x100e, 0x100f,
1237bf3504ceSAndrew Lunn 	0x1018, 0x1019,
1238bf3504ceSAndrew Lunn 	0x9000, 0x9001, 0x9002, 0x9003, 0x9004,
1239bf3504ceSAndrew Lunn 	0x9006,
1240bf3504ceSAndrew Lunn 	0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016,
1241bf3504ceSAndrew Lunn 	/* 10Gbase-R */
1242bf3504ceSAndrew Lunn 	0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
1243bf3504ceSAndrew Lunn 	0x1028, 0x1029, 0x102a, 0x102b,
1244bf3504ceSAndrew Lunn };
1245bf3504ceSAndrew Lunn 
1246bf3504ceSAndrew Lunn int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
1247bf3504ceSAndrew Lunn {
1248193c5b26SPavana Sharma 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
1249bf3504ceSAndrew Lunn 		return 0;
1250bf3504ceSAndrew Lunn 
1251bf3504ceSAndrew Lunn 	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
1252bf3504ceSAndrew Lunn }
1253bf3504ceSAndrew Lunn 
1254bf3504ceSAndrew Lunn void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
1255bf3504ceSAndrew Lunn {
1256bf3504ceSAndrew Lunn 	u16 *p = _p;
1257bf3504ceSAndrew Lunn 	int lane;
1258bf3504ceSAndrew Lunn 	u16 reg;
12590fd5d79eSChris Packham 	int err;
1260bf3504ceSAndrew Lunn 	int i;
1261bf3504ceSAndrew Lunn 
1262bf3504ceSAndrew Lunn 	lane = mv88e6xxx_serdes_get_lane(chip, port);
1263193c5b26SPavana Sharma 	if (lane < 0)
1264bf3504ceSAndrew Lunn 		return;
1265bf3504ceSAndrew Lunn 
1266bf3504ceSAndrew Lunn 	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
12670fd5d79eSChris Packham 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1268bf3504ceSAndrew Lunn 					    mv88e6390_serdes_regs[i], &reg);
12690fd5d79eSChris Packham 		if (!err)
1270bf3504ceSAndrew Lunn 			p[i] = reg;
1271bf3504ceSAndrew Lunn 	}
1272bf3504ceSAndrew Lunn }
1273de776d0dSPavana Sharma 
1274de776d0dSPavana Sharma static int mv88e6393x_serdes_port_errata(struct mv88e6xxx_chip *chip, int lane)
1275de776d0dSPavana Sharma {
1276de776d0dSPavana Sharma 	u16 reg, pcs;
1277de776d0dSPavana Sharma 	int err;
1278de776d0dSPavana Sharma 
1279de776d0dSPavana Sharma 	/* mv88e6393x family errata 4.6:
1280de776d0dSPavana Sharma 	 * Cannot clear PwrDn bit on SERDES on port 0 if device is configured
1281de776d0dSPavana Sharma 	 * CPU_MGD mode or P0_mode is configured for [x]MII.
1282de776d0dSPavana Sharma 	 * Workaround: Set Port0 SERDES register 4.F002 bit 5=0 and bit 15=1.
1283de776d0dSPavana Sharma 	 *
1284de776d0dSPavana Sharma 	 * It seems that after this workaround the SERDES is automatically
1285de776d0dSPavana Sharma 	 * powered up (the bit is cleared), so power it down.
1286de776d0dSPavana Sharma 	 */
1287de776d0dSPavana Sharma 	if (lane == MV88E6393X_PORT0_LANE) {
1288de776d0dSPavana Sharma 		err = mv88e6390_serdes_read(chip, MV88E6393X_PORT0_LANE,
1289de776d0dSPavana Sharma 					    MDIO_MMD_PHYXS,
1290de776d0dSPavana Sharma 					    MV88E6393X_SERDES_POC, &reg);
1291de776d0dSPavana Sharma 		if (err)
1292de776d0dSPavana Sharma 			return err;
1293de776d0dSPavana Sharma 
1294de776d0dSPavana Sharma 		reg &= ~MV88E6393X_SERDES_POC_PDOWN;
1295de776d0dSPavana Sharma 		reg |= MV88E6393X_SERDES_POC_RESET;
1296de776d0dSPavana Sharma 
1297de776d0dSPavana Sharma 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
1298de776d0dSPavana Sharma 					     MV88E6393X_SERDES_POC, reg);
1299de776d0dSPavana Sharma 		if (err)
1300de776d0dSPavana Sharma 			return err;
1301de776d0dSPavana Sharma 
1302de776d0dSPavana Sharma 		err = mv88e6390_serdes_power_sgmii(chip, lane, false);
1303de776d0dSPavana Sharma 		if (err)
1304de776d0dSPavana Sharma 			return err;
1305de776d0dSPavana Sharma 	}
1306de776d0dSPavana Sharma 
1307de776d0dSPavana Sharma 	/* mv88e6393x family errata 4.8:
1308de776d0dSPavana Sharma 	 * When a SERDES port is operating in 1000BASE-X or SGMII mode link may
1309de776d0dSPavana Sharma 	 * not come up after hardware reset or software reset of SERDES core.
1310de776d0dSPavana Sharma 	 * Workaround is to write SERDES register 4.F074.14=1 for only those
1311de776d0dSPavana Sharma 	 * modes and 0 in all other modes.
1312de776d0dSPavana Sharma 	 */
1313de776d0dSPavana Sharma 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1314de776d0dSPavana Sharma 				    MV88E6393X_SERDES_POC, &pcs);
1315de776d0dSPavana Sharma 	if (err)
1316de776d0dSPavana Sharma 		return err;
1317de776d0dSPavana Sharma 
1318de776d0dSPavana Sharma 	pcs &= MV88E6393X_SERDES_POC_PCS_MASK;
1319de776d0dSPavana Sharma 
1320de776d0dSPavana Sharma 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
1321de776d0dSPavana Sharma 				    MV88E6393X_ERRATA_4_8_REG, &reg);
1322de776d0dSPavana Sharma 	if (err)
1323de776d0dSPavana Sharma 		return err;
1324de776d0dSPavana Sharma 
1325de776d0dSPavana Sharma 	if (pcs == MV88E6393X_SERDES_POC_PCS_1000BASEX ||
1326de776d0dSPavana Sharma 	    pcs == MV88E6393X_SERDES_POC_PCS_SGMII_PHY ||
1327de776d0dSPavana Sharma 	    pcs == MV88E6393X_SERDES_POC_PCS_SGMII_MAC)
1328de776d0dSPavana Sharma 		reg |= MV88E6393X_ERRATA_4_8_BIT;
1329de776d0dSPavana Sharma 	else
1330de776d0dSPavana Sharma 		reg &= ~MV88E6393X_ERRATA_4_8_BIT;
1331de776d0dSPavana Sharma 
1332de776d0dSPavana Sharma 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
1333de776d0dSPavana Sharma 				      MV88E6393X_ERRATA_4_8_REG, reg);
1334de776d0dSPavana Sharma }
1335de776d0dSPavana Sharma 
1336de776d0dSPavana Sharma int mv88e6393x_serdes_setup_errata(struct mv88e6xxx_chip *chip)
1337de776d0dSPavana Sharma {
1338de776d0dSPavana Sharma 	int err;
1339de776d0dSPavana Sharma 
1340de776d0dSPavana Sharma 	err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT0_LANE);
1341de776d0dSPavana Sharma 	if (err)
1342de776d0dSPavana Sharma 		return err;
1343de776d0dSPavana Sharma 
1344de776d0dSPavana Sharma 	err = mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT9_LANE);
1345de776d0dSPavana Sharma 	if (err)
1346de776d0dSPavana Sharma 		return err;
1347de776d0dSPavana Sharma 
1348de776d0dSPavana Sharma 	return mv88e6393x_serdes_port_errata(chip, MV88E6393X_PORT10_LANE);
1349de776d0dSPavana Sharma }
1350de776d0dSPavana Sharma 
1351de776d0dSPavana Sharma int mv88e6393x_serdes_power(struct mv88e6xxx_chip *chip, int port, int lane,
1352de776d0dSPavana Sharma 			    bool on)
1353de776d0dSPavana Sharma {
1354de776d0dSPavana Sharma 	u8 cmode = chip->ports[port].cmode;
1355de776d0dSPavana Sharma 
1356de776d0dSPavana Sharma 	if (port != 0 && port != 9 && port != 10)
1357de776d0dSPavana Sharma 		return -EOPNOTSUPP;
1358de776d0dSPavana Sharma 
1359de776d0dSPavana Sharma 	switch (cmode) {
1360de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
1361de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
1362de776d0dSPavana Sharma 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
1363de776d0dSPavana Sharma 		return mv88e6390_serdes_power_sgmii(chip, lane, on);
1364de776d0dSPavana Sharma 	case MV88E6393X_PORT_STS_CMODE_5GBASER:
1365de776d0dSPavana Sharma 	case MV88E6393X_PORT_STS_CMODE_10GBASER:
1366de776d0dSPavana Sharma 		return mv88e6390_serdes_power_10g(chip, lane, on);
1367de776d0dSPavana Sharma 	}
1368de776d0dSPavana Sharma 
1369de776d0dSPavana Sharma 	return 0;
1370de776d0dSPavana Sharma }
1371