xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision 7e0e6243)
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 
98dc272f60SVivien Didelot int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 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,
120a5a6858bSRussell King 				u8 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,
169a5a6858bSRussell King 				   u8 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,
190a5a6858bSRussell King 				    u8 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,
203a5a6858bSRussell King 				 u8 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 
2339db4a725SVivien Didelot u8 mv88e6352_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
2346d91782fSAndrew Lunn {
2352d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
2369db4a725SVivien Didelot 	u8 lane = 0;
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 {
2489db4a725SVivien Didelot 	if (mv88e6xxx_serdes_get_lane(chip, port))
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,
357907b9b9fSVivien Didelot 					u8 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 
37561a46b41SVivien Didelot int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 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;
403d3f88a24SAndrew Lunn 	int i;
404d3f88a24SAndrew Lunn 
405d3f88a24SAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
406d3f88a24SAndrew Lunn 		return;
407d3f88a24SAndrew Lunn 
408d3f88a24SAndrew Lunn 	for (i = 0 ; i < 32; i++) {
409d3f88a24SAndrew Lunn 		mv88e6352_serdes_read(chip, i, &reg);
410d3f88a24SAndrew Lunn 		p[i] = reg;
411d3f88a24SAndrew Lunn 	}
412d3f88a24SAndrew Lunn }
413d3f88a24SAndrew Lunn 
4145122d4ecSVivien Didelot u8 mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
415d3cf7d8fSMarek Behún {
416d3cf7d8fSMarek Behún 	u8 cmode = chip->ports[port].cmode;
4175122d4ecSVivien Didelot 	u8 lane = 0;
418d3cf7d8fSMarek Behún 
4195122d4ecSVivien Didelot 	switch (port) {
4205122d4ecSVivien Didelot 	case 5:
4213bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
422d3cf7d8fSMarek Behún 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4235122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
4245122d4ecSVivien Didelot 			lane = MV88E6341_PORT5_LANE;
4255122d4ecSVivien Didelot 		break;
426d3cf7d8fSMarek Behún 	}
427d3cf7d8fSMarek Behún 
4285122d4ecSVivien Didelot 	return lane;
429d3cf7d8fSMarek Behún }
430d3cf7d8fSMarek Behún 
4315122d4ecSVivien Didelot u8 mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
43207ffbd74SAndrew Lunn {
4332d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
4345122d4ecSVivien Didelot 	u8 lane = 0;
43507ffbd74SAndrew Lunn 
43607ffbd74SAndrew Lunn 	switch (port) {
43707ffbd74SAndrew Lunn 	case 9:
4383bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
43907ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4405122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
4415122d4ecSVivien Didelot 			lane = MV88E6390_PORT9_LANE0;
44217deaf5cSMarek Behún 		break;
44307ffbd74SAndrew Lunn 	case 10:
4443bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
44507ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4465122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
4475122d4ecSVivien Didelot 			lane = MV88E6390_PORT10_LANE0;
44817deaf5cSMarek Behún 		break;
44907ffbd74SAndrew Lunn 	}
45007ffbd74SAndrew Lunn 
4515122d4ecSVivien Didelot 	return lane;
45217deaf5cSMarek Behún }
45317deaf5cSMarek Behún 
4545122d4ecSVivien Didelot u8 mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
455a8c01c0dSAndrew Lunn {
4565122d4ecSVivien Didelot 	u8 cmode_port = chip->ports[port].cmode;
4575122d4ecSVivien Didelot 	u8 cmode_port10 = chip->ports[10].cmode;
4585122d4ecSVivien Didelot 	u8 cmode_port9 = chip->ports[9].cmode;
4595122d4ecSVivien Didelot 	u8 lane = 0;
460a8c01c0dSAndrew Lunn 
461a8c01c0dSAndrew Lunn 	switch (port) {
462a8c01c0dSAndrew Lunn 	case 2:
4633bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
464a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4655122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
4665122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
4675122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE1;
46817deaf5cSMarek Behún 		break;
469a8c01c0dSAndrew Lunn 	case 3:
4703bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
471a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
472a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
4735122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
4745122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
4755122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE2;
47617deaf5cSMarek Behún 		break;
477a8c01c0dSAndrew Lunn 	case 4:
4783bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
479a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
480a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
4815122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
4825122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
4835122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE3;
48417deaf5cSMarek Behún 		break;
485a8c01c0dSAndrew Lunn 	case 5:
4863bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
487a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
4885122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
4895122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
4905122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE1;
49117deaf5cSMarek Behún 		break;
492a8c01c0dSAndrew Lunn 	case 6:
4933bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
494a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
495a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
4965122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
4975122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
4985122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE2;
49917deaf5cSMarek Behún 		break;
500a8c01c0dSAndrew Lunn 	case 7:
5013bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
502a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
503a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
5045122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
5055122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
5065122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE3;
50717deaf5cSMarek Behún 		break;
508a8c01c0dSAndrew Lunn 	case 9:
5093bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
510a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
511a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
512a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
5135122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
5145122d4ecSVivien Didelot 			lane = MV88E6390_PORT9_LANE0;
51517deaf5cSMarek Behún 		break;
516a8c01c0dSAndrew Lunn 	case 10:
5173bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
518a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
519a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
520a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
5215122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
5225122d4ecSVivien Didelot 			lane = MV88E6390_PORT10_LANE0;
52317deaf5cSMarek Behún 		break;
52417deaf5cSMarek Behún 	}
52517deaf5cSMarek Behún 
5265122d4ecSVivien Didelot 	return lane;
527a8c01c0dSAndrew Lunn }
528a8c01c0dSAndrew Lunn 
529dc272f60SVivien Didelot /* Set power up/down for 10GBASE-R and 10GBASE-X4/X2 */
53017deaf5cSMarek Behún static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
531dc272f60SVivien Didelot 				      bool up)
5326335e9f2SAndrew Lunn {
5336335e9f2SAndrew Lunn 	u16 val, new_val;
5346335e9f2SAndrew Lunn 	int err;
5356335e9f2SAndrew Lunn 
536e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
537e6891c76SAndrew Lunn 				    MV88E6390_PCS_CONTROL_1, &val);
538e6891c76SAndrew Lunn 
5396335e9f2SAndrew Lunn 	if (err)
5406335e9f2SAndrew Lunn 		return err;
5416335e9f2SAndrew Lunn 
542dc272f60SVivien Didelot 	if (up)
5436335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
5446335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_LOOPBACK |
5456335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_PDOWN);
5466335e9f2SAndrew Lunn 	else
5476335e9f2SAndrew Lunn 		new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
5486335e9f2SAndrew Lunn 
5496335e9f2SAndrew Lunn 	if (val != new_val)
550e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
551e6891c76SAndrew Lunn 					     MV88E6390_PCS_CONTROL_1, new_val);
5526335e9f2SAndrew Lunn 
5536335e9f2SAndrew Lunn 	return err;
5546335e9f2SAndrew Lunn }
5556335e9f2SAndrew Lunn 
556dc272f60SVivien Didelot /* Set power up/down for SGMII and 1000Base-X */
55717deaf5cSMarek Behún static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
558dc272f60SVivien Didelot 					bool up)
5596335e9f2SAndrew Lunn {
5606335e9f2SAndrew Lunn 	u16 val, new_val;
5616335e9f2SAndrew Lunn 	int err;
5626335e9f2SAndrew Lunn 
563e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
5644c8b7350SRussell King 				    MV88E6390_SGMII_BMCR, &val);
5656335e9f2SAndrew Lunn 	if (err)
5666335e9f2SAndrew Lunn 		return err;
5676335e9f2SAndrew Lunn 
568dc272f60SVivien Didelot 	if (up)
5694c8b7350SRussell King 		new_val = val & ~(BMCR_RESET | BMCR_LOOPBACK | BMCR_PDOWN);
5706335e9f2SAndrew Lunn 	else
5714c8b7350SRussell King 		new_val = val | BMCR_PDOWN;
5726335e9f2SAndrew Lunn 
5736335e9f2SAndrew Lunn 	if (val != new_val)
574e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
5754c8b7350SRussell King 					     MV88E6390_SGMII_BMCR, new_val);
5766335e9f2SAndrew Lunn 
5776335e9f2SAndrew Lunn 	return err;
5786335e9f2SAndrew Lunn }
5796335e9f2SAndrew Lunn 
5800df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat {
5810df95287SNikita Yushchenko 	char string[ETH_GSTRING_LEN];
5820df95287SNikita Yushchenko 	int reg;
5830df95287SNikita Yushchenko };
5840df95287SNikita Yushchenko 
5850df95287SNikita Yushchenko static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = {
5860df95287SNikita Yushchenko 	{ "serdes_rx_pkts", 0xf021 },
5870df95287SNikita Yushchenko 	{ "serdes_rx_bytes", 0xf024 },
5880df95287SNikita Yushchenko 	{ "serdes_rx_pkts_error", 0xf027 },
5890df95287SNikita Yushchenko };
5900df95287SNikita Yushchenko 
5910df95287SNikita Yushchenko int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
5920df95287SNikita Yushchenko {
5930df95287SNikita Yushchenko 	if (mv88e6390_serdes_get_lane(chip, port) == 0)
5940df95287SNikita Yushchenko 		return 0;
5950df95287SNikita Yushchenko 
5960df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
5970df95287SNikita Yushchenko }
5980df95287SNikita Yushchenko 
5990df95287SNikita Yushchenko int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip,
6000df95287SNikita Yushchenko 				 int port, uint8_t *data)
6010df95287SNikita Yushchenko {
6020df95287SNikita Yushchenko 	struct mv88e6390_serdes_hw_stat *stat;
6030df95287SNikita Yushchenko 	int i;
6040df95287SNikita Yushchenko 
6050df95287SNikita Yushchenko 	if (mv88e6390_serdes_get_lane(chip, port) == 0)
6060df95287SNikita Yushchenko 		return 0;
6070df95287SNikita Yushchenko 
6080df95287SNikita Yushchenko 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
6090df95287SNikita Yushchenko 		stat = &mv88e6390_serdes_hw_stats[i];
6100df95287SNikita Yushchenko 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
6110df95287SNikita Yushchenko 		       ETH_GSTRING_LEN);
6120df95287SNikita Yushchenko 	}
6130df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
6140df95287SNikita Yushchenko }
6150df95287SNikita Yushchenko 
6160df95287SNikita Yushchenko static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane,
6170df95287SNikita Yushchenko 					  struct mv88e6390_serdes_hw_stat *stat)
6180df95287SNikita Yushchenko {
6190df95287SNikita Yushchenko 	u16 reg[3];
6200df95287SNikita Yushchenko 	int err, i;
6210df95287SNikita Yushchenko 
6220df95287SNikita Yushchenko 	for (i = 0; i < 3; i++) {
6230df95287SNikita Yushchenko 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
6240df95287SNikita Yushchenko 					    stat->reg + i, &reg[i]);
6250df95287SNikita Yushchenko 		if (err) {
6260df95287SNikita Yushchenko 			dev_err(chip->dev, "failed to read statistic\n");
6270df95287SNikita Yushchenko 			return 0;
6280df95287SNikita Yushchenko 		}
6290df95287SNikita Yushchenko 	}
6300df95287SNikita Yushchenko 
6310df95287SNikita Yushchenko 	return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32);
6320df95287SNikita Yushchenko }
6330df95287SNikita Yushchenko 
6340df95287SNikita Yushchenko int mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
6350df95287SNikita Yushchenko 			       uint64_t *data)
6360df95287SNikita Yushchenko {
6370df95287SNikita Yushchenko 	struct mv88e6390_serdes_hw_stat *stat;
6380df95287SNikita Yushchenko 	int lane;
6390df95287SNikita Yushchenko 	int i;
6400df95287SNikita Yushchenko 
6410df95287SNikita Yushchenko 	lane = mv88e6390_serdes_get_lane(chip, port);
6420df95287SNikita Yushchenko 	if (lane == 0)
6430df95287SNikita Yushchenko 		return 0;
6440df95287SNikita Yushchenko 
6450df95287SNikita Yushchenko 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
6460df95287SNikita Yushchenko 		stat = &mv88e6390_serdes_hw_stats[i];
6470df95287SNikita Yushchenko 		data[i] = mv88e6390_serdes_get_stat(chip, lane, stat);
6480df95287SNikita Yushchenko 	}
6490df95287SNikita Yushchenko 
6500df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
6510df95287SNikita Yushchenko }
6520df95287SNikita Yushchenko 
6530df95287SNikita Yushchenko static int mv88e6390_serdes_enable_checker(struct mv88e6xxx_chip *chip, u8 lane)
6540df95287SNikita Yushchenko {
6550df95287SNikita Yushchenko 	u16 reg;
6560df95287SNikita Yushchenko 	int err;
6570df95287SNikita Yushchenko 
6580df95287SNikita Yushchenko 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
6590df95287SNikita Yushchenko 				    MV88E6390_PG_CONTROL, &reg);
6600df95287SNikita Yushchenko 	if (err)
6610df95287SNikita Yushchenko 		return err;
6620df95287SNikita Yushchenko 
6630df95287SNikita Yushchenko 	reg |= MV88E6390_PG_CONTROL_ENABLE_PC;
6640df95287SNikita Yushchenko 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
6650df95287SNikita Yushchenko 				      MV88E6390_PG_CONTROL, reg);
6660df95287SNikita Yushchenko }
6670df95287SNikita Yushchenko 
668dc272f60SVivien Didelot int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, u8 lane,
669dc272f60SVivien Didelot 			   bool up)
6706335e9f2SAndrew Lunn {
6712d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
6720df95287SNikita Yushchenko 	int err = 0;
6736335e9f2SAndrew Lunn 
674a8c01c0dSAndrew Lunn 	switch (cmode) {
675a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
6763bbb8867SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
677f8236a08SAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
6780df95287SNikita Yushchenko 		err = mv88e6390_serdes_power_sgmii(chip, lane, up);
6790df95287SNikita Yushchenko 		break;
680a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_XAUI:
681a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
6820df95287SNikita Yushchenko 		err = mv88e6390_serdes_power_10g(chip, lane, up);
6830df95287SNikita Yushchenko 		break;
684a8c01c0dSAndrew Lunn 	}
685a8c01c0dSAndrew Lunn 
6860df95287SNikita Yushchenko 	if (!err && up)
6870df95287SNikita Yushchenko 		err = mv88e6390_serdes_enable_checker(chip, lane);
6880df95287SNikita Yushchenko 
6890df95287SNikita Yushchenko 	return err;
690a8c01c0dSAndrew Lunn }
691a8c01c0dSAndrew Lunn 
692a5a6858bSRussell King int mv88e6390_serdes_pcs_config(struct mv88e6xxx_chip *chip, int port,
693a5a6858bSRussell King 				u8 lane, unsigned int mode,
694a5a6858bSRussell King 				phy_interface_t interface,
695a5a6858bSRussell King 				const unsigned long *advertise)
696a5a6858bSRussell King {
697a5a6858bSRussell King 	u16 val, bmcr, adv;
698a5a6858bSRussell King 	bool changed;
699a5a6858bSRussell King 	int err;
700a5a6858bSRussell King 
701a5a6858bSRussell King 	switch (interface) {
702a5a6858bSRussell King 	case PHY_INTERFACE_MODE_SGMII:
703a5a6858bSRussell King 		adv = 0x0001;
704a5a6858bSRussell King 		break;
705a5a6858bSRussell King 
706a5a6858bSRussell King 	case PHY_INTERFACE_MODE_1000BASEX:
707a5a6858bSRussell King 		adv = linkmode_adv_to_mii_adv_x(advertise,
708a5a6858bSRussell King 					ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
709a5a6858bSRussell King 		break;
710a5a6858bSRussell King 
711a5a6858bSRussell King 	case PHY_INTERFACE_MODE_2500BASEX:
712a5a6858bSRussell King 		adv = linkmode_adv_to_mii_adv_x(advertise,
713a5a6858bSRussell King 					ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
714a5a6858bSRussell King 		break;
715a5a6858bSRussell King 
716a5a6858bSRussell King 	default:
717a5a6858bSRussell King 		return 0;
718a5a6858bSRussell King 	}
719a5a6858bSRussell King 
720a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
721a5a6858bSRussell King 				    MV88E6390_SGMII_ADVERTISE, &val);
722a5a6858bSRussell King 	if (err)
723a5a6858bSRussell King 		return err;
724a5a6858bSRussell King 
725a5a6858bSRussell King 	changed = val != adv;
726a5a6858bSRussell King 	if (changed) {
727a5a6858bSRussell King 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
728a5a6858bSRussell King 					     MV88E6390_SGMII_ADVERTISE, adv);
729a5a6858bSRussell King 		if (err)
730a5a6858bSRussell King 			return err;
731a5a6858bSRussell King 	}
732a5a6858bSRussell King 
733a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
734a5a6858bSRussell King 				    MV88E6390_SGMII_BMCR, &val);
735a5a6858bSRussell King 	if (err)
736a5a6858bSRussell King 		return err;
737a5a6858bSRussell King 
738a5a6858bSRussell King 	if (phylink_autoneg_inband(mode))
739a5a6858bSRussell King 		bmcr = val | BMCR_ANENABLE;
740a5a6858bSRussell King 	else
741a5a6858bSRussell King 		bmcr = val & ~BMCR_ANENABLE;
742a5a6858bSRussell King 
743a5a6858bSRussell King 	/* setting ANENABLE triggers a restart of negotiation */
744a5a6858bSRussell King 	if (bmcr == val)
745a5a6858bSRussell King 		return changed;
746a5a6858bSRussell King 
747a5a6858bSRussell King 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
748a5a6858bSRussell King 				      MV88E6390_SGMII_BMCR, bmcr);
749a5a6858bSRussell King }
750a5a6858bSRussell King 
751a5a6858bSRussell King int mv88e6390_serdes_pcs_get_state(struct mv88e6xxx_chip *chip, int port,
752a5a6858bSRussell King 				   u8 lane, struct phylink_link_state *state)
753a5a6858bSRussell King {
754a5a6858bSRussell King 	u16 lpa, status;
755a5a6858bSRussell King 	int err;
756a5a6858bSRussell King 
757a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
758a5a6858bSRussell King 				    MV88E6390_SGMII_PHY_STATUS, &status);
759a5a6858bSRussell King 	if (err) {
760a5a6858bSRussell King 		dev_err(chip->dev, "can't read Serdes PHY status: %d\n", err);
761a5a6858bSRussell King 		return err;
762a5a6858bSRussell King 	}
763a5a6858bSRussell King 
764a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
765a5a6858bSRussell King 				    MV88E6390_SGMII_LPA, &lpa);
766a5a6858bSRussell King 	if (err) {
767a5a6858bSRussell King 		dev_err(chip->dev, "can't read Serdes PHY LPA: %d\n", err);
768a5a6858bSRussell King 		return err;
769a5a6858bSRussell King 	}
770a5a6858bSRussell King 
771a5a6858bSRussell King 	return mv88e6xxx_serdes_pcs_get_state(chip, status, lpa, state);
772a5a6858bSRussell King }
773a5a6858bSRussell King 
774a5a6858bSRussell King int mv88e6390_serdes_pcs_an_restart(struct mv88e6xxx_chip *chip, int port,
775a5a6858bSRussell King 				    u8 lane)
776a5a6858bSRussell King {
777a5a6858bSRussell King 	u16 bmcr;
778a5a6858bSRussell King 	int err;
779a5a6858bSRussell King 
780a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
781a5a6858bSRussell King 				    MV88E6390_SGMII_BMCR, &bmcr);
782a5a6858bSRussell King 	if (err)
783a5a6858bSRussell King 		return err;
784a5a6858bSRussell King 
785a5a6858bSRussell King 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
786a5a6858bSRussell King 				      MV88E6390_SGMII_BMCR,
787a5a6858bSRussell King 				      bmcr | BMCR_ANRESTART);
788a5a6858bSRussell King }
789a5a6858bSRussell King 
790a5a6858bSRussell King int mv88e6390_serdes_pcs_link_up(struct mv88e6xxx_chip *chip, int port,
791a5a6858bSRussell King 				 u8 lane, int speed, int duplex)
792a5a6858bSRussell King {
793a5a6858bSRussell King 	u16 val, bmcr;
794a5a6858bSRussell King 	int err;
795a5a6858bSRussell King 
796a5a6858bSRussell King 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
797a5a6858bSRussell King 				    MV88E6390_SGMII_BMCR, &val);
798a5a6858bSRussell King 	if (err)
799a5a6858bSRussell King 		return err;
800a5a6858bSRussell King 
801a5a6858bSRussell King 	bmcr = val & ~(BMCR_SPEED100 | BMCR_FULLDPLX | BMCR_SPEED1000);
802a5a6858bSRussell King 	switch (speed) {
803a5a6858bSRussell King 	case SPEED_2500:
804a5a6858bSRussell King 	case SPEED_1000:
805a5a6858bSRussell King 		bmcr |= BMCR_SPEED1000;
806a5a6858bSRussell King 		break;
807a5a6858bSRussell King 	case SPEED_100:
808a5a6858bSRussell King 		bmcr |= BMCR_SPEED100;
809a5a6858bSRussell King 		break;
810a5a6858bSRussell King 	case SPEED_10:
811a5a6858bSRussell King 		break;
812a5a6858bSRussell King 	}
813a5a6858bSRussell King 
814a5a6858bSRussell King 	if (duplex == DUPLEX_FULL)
815a5a6858bSRussell King 		bmcr |= BMCR_FULLDPLX;
816a5a6858bSRussell King 
817a5a6858bSRussell King 	if (bmcr == val)
818a5a6858bSRussell King 		return 0;
819a5a6858bSRussell King 
820a5a6858bSRussell King 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
821a5a6858bSRussell King 				      MV88E6390_SGMII_BMCR, bmcr);
822a5a6858bSRussell King }
823a5a6858bSRussell King 
824efd1ba6aSAndrew Lunn static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
82517deaf5cSMarek Behún 					    int port, u8 lane)
826efd1ba6aSAndrew Lunn {
8277e0e6243SRussell King 	u16 bmsr;
828a5a6858bSRussell King 	int err;
829efd1ba6aSAndrew Lunn 
8307e0e6243SRussell King 	/* If the link has dropped, we want to know about it. */
83172d8b4fdSHeiner Kallweit 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
8327e0e6243SRussell King 				    MV88E6390_SGMII_BMSR, &bmsr);
83372d8b4fdSHeiner Kallweit 	if (err) {
8347e0e6243SRussell King 		dev_err(chip->dev, "can't read Serdes BMSR: %d\n", err);
83572d8b4fdSHeiner Kallweit 		return;
83672d8b4fdSHeiner Kallweit 	}
837efd1ba6aSAndrew Lunn 
8387e0e6243SRussell King 	dsa_port_phylink_mac_change(chip->ds, port, !!(bmsr & BMSR_LSTATUS));
839efd1ba6aSAndrew Lunn }
840efd1ba6aSAndrew Lunn 
841efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
84261a46b41SVivien Didelot 					     u8 lane, bool enable)
843efd1ba6aSAndrew Lunn {
84461a46b41SVivien Didelot 	u16 val = 0;
84561a46b41SVivien Didelot 
84661a46b41SVivien Didelot 	if (enable)
84761a46b41SVivien Didelot 		val |= MV88E6390_SGMII_INT_LINK_DOWN |
84861a46b41SVivien Didelot 			MV88E6390_SGMII_INT_LINK_UP;
84961a46b41SVivien Didelot 
850efd1ba6aSAndrew Lunn 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
85161a46b41SVivien Didelot 				      MV88E6390_SGMII_INT_ENABLE, val);
852efd1ba6aSAndrew Lunn }
853efd1ba6aSAndrew Lunn 
85461a46b41SVivien Didelot int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port, u8 lane,
85561a46b41SVivien Didelot 				bool enable)
856efd1ba6aSAndrew Lunn {
857efd1ba6aSAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
858efd1ba6aSAndrew Lunn 
859efd1ba6aSAndrew Lunn 	switch (cmode) {
860efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
8613bbb8867SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
862efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
86361a46b41SVivien Didelot 		return mv88e6390_serdes_irq_enable_sgmii(chip, lane, enable);
864efd1ba6aSAndrew Lunn 	}
865efd1ba6aSAndrew Lunn 
86661a46b41SVivien Didelot 	return 0;
867efd1ba6aSAndrew Lunn }
868efd1ba6aSAndrew Lunn 
869efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
87017deaf5cSMarek Behún 					     u8 lane, u16 *status)
871efd1ba6aSAndrew Lunn {
872efd1ba6aSAndrew Lunn 	int err;
873efd1ba6aSAndrew Lunn 
874efd1ba6aSAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
875efd1ba6aSAndrew Lunn 				    MV88E6390_SGMII_INT_STATUS, status);
876efd1ba6aSAndrew Lunn 
877efd1ba6aSAndrew Lunn 	return err;
878efd1ba6aSAndrew Lunn }
879efd1ba6aSAndrew Lunn 
880907b9b9fSVivien Didelot irqreturn_t mv88e6390_serdes_irq_status(struct mv88e6xxx_chip *chip, int port,
881907b9b9fSVivien Didelot 					u8 lane)
882efd1ba6aSAndrew Lunn {
883907b9b9fSVivien Didelot 	u8 cmode = chip->ports[port].cmode;
884efd1ba6aSAndrew Lunn 	irqreturn_t ret = IRQ_NONE;
885efd1ba6aSAndrew Lunn 	u16 status;
886efd1ba6aSAndrew Lunn 	int err;
887b98f0f53SVivien Didelot 
888efd1ba6aSAndrew Lunn 	switch (cmode) {
889efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
8903bbb8867SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASEX:
891efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
892efd1ba6aSAndrew Lunn 		err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
893efd1ba6aSAndrew Lunn 		if (err)
894907b9b9fSVivien Didelot 			return ret;
8956feddb49SDan Carpenter 		if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
896efd1ba6aSAndrew Lunn 			      MV88E6390_SGMII_INT_LINK_UP)) {
897efd1ba6aSAndrew Lunn 			ret = IRQ_HANDLED;
898907b9b9fSVivien Didelot 			mv88e6390_serdes_irq_link_sgmii(chip, port, lane);
899efd1ba6aSAndrew Lunn 		}
900efd1ba6aSAndrew Lunn 	}
901907b9b9fSVivien Didelot 
902907b9b9fSVivien Didelot 	return ret;
903907b9b9fSVivien Didelot }
904907b9b9fSVivien Didelot 
9054241ef52SVivien Didelot unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
9064241ef52SVivien Didelot {
9074241ef52SVivien Didelot 	return irq_find_mapping(chip->g2_irq.domain, port);
9084241ef52SVivien Didelot }
909bf3504ceSAndrew Lunn 
910bf3504ceSAndrew Lunn static const u16 mv88e6390_serdes_regs[] = {
911bf3504ceSAndrew Lunn 	/* SERDES common registers */
912bf3504ceSAndrew Lunn 	0xf00a, 0xf00b, 0xf00c,
913bf3504ceSAndrew Lunn 	0xf010, 0xf011, 0xf012, 0xf013,
914bf3504ceSAndrew Lunn 	0xf016, 0xf017, 0xf018,
915bf3504ceSAndrew Lunn 	0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
916bf3504ceSAndrew Lunn 	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
917bf3504ceSAndrew Lunn 	0xf028, 0xf029,
918bf3504ceSAndrew Lunn 	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
919bf3504ceSAndrew Lunn 	0xf038, 0xf039,
920bf3504ceSAndrew Lunn 	/* SGMII */
921bf3504ceSAndrew Lunn 	0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
922bf3504ceSAndrew Lunn 	0x2008,
923bf3504ceSAndrew Lunn 	0x200f,
924bf3504ceSAndrew Lunn 	0xa000, 0xa001, 0xa002, 0xa003,
925bf3504ceSAndrew Lunn 	/* 10Gbase-X */
926bf3504ceSAndrew Lunn 	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
927bf3504ceSAndrew Lunn 	0x1008,
928bf3504ceSAndrew Lunn 	0x100e, 0x100f,
929bf3504ceSAndrew Lunn 	0x1018, 0x1019,
930bf3504ceSAndrew Lunn 	0x9000, 0x9001, 0x9002, 0x9003, 0x9004,
931bf3504ceSAndrew Lunn 	0x9006,
932bf3504ceSAndrew Lunn 	0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016,
933bf3504ceSAndrew Lunn 	/* 10Gbase-R */
934bf3504ceSAndrew Lunn 	0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
935bf3504ceSAndrew Lunn 	0x1028, 0x1029, 0x102a, 0x102b,
936bf3504ceSAndrew Lunn };
937bf3504ceSAndrew Lunn 
938bf3504ceSAndrew Lunn int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
939bf3504ceSAndrew Lunn {
940bf3504ceSAndrew Lunn 	if (mv88e6xxx_serdes_get_lane(chip, port) == 0)
941bf3504ceSAndrew Lunn 		return 0;
942bf3504ceSAndrew Lunn 
943bf3504ceSAndrew Lunn 	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
944bf3504ceSAndrew Lunn }
945bf3504ceSAndrew Lunn 
946bf3504ceSAndrew Lunn void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
947bf3504ceSAndrew Lunn {
948bf3504ceSAndrew Lunn 	u16 *p = _p;
949bf3504ceSAndrew Lunn 	int lane;
950bf3504ceSAndrew Lunn 	u16 reg;
951bf3504ceSAndrew Lunn 	int i;
952bf3504ceSAndrew Lunn 
953bf3504ceSAndrew Lunn 	lane = mv88e6xxx_serdes_get_lane(chip, port);
954bf3504ceSAndrew Lunn 	if (lane == 0)
955bf3504ceSAndrew Lunn 		return;
956bf3504ceSAndrew Lunn 
957bf3504ceSAndrew Lunn 	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
958bf3504ceSAndrew Lunn 		mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
959bf3504ceSAndrew Lunn 				      mv88e6390_serdes_regs[i], &reg);
960bf3504ceSAndrew Lunn 		p[i] = reg;
961bf3504ceSAndrew Lunn 	}
962bf3504ceSAndrew Lunn }
963