xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision f6791424)
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 
mv88e6352_serdes_read(struct mv88e6xxx_chip * chip,int reg,u16 * val)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 
mv88e6352_serdes_write(struct mv88e6xxx_chip * chip,int reg,u16 val)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 
mv88e6390_serdes_read(struct mv88e6xxx_chip * chip,int lane,int device,int reg,u16 * val)36e6891c76SAndrew Lunn static int mv88e6390_serdes_read(struct mv88e6xxx_chip *chip,
37e6891c76SAndrew Lunn 				 int lane, int device, int reg, u16 *val)
38e6891c76SAndrew Lunn {
39743a19e3SAndrew Lunn 	return mv88e6xxx_phy_read_c45(chip, lane, device, reg, val);
40e6891c76SAndrew Lunn }
41e6891c76SAndrew Lunn 
mv88e6xxx_pcs_decode_state(struct device * dev,u16 bmsr,u16 lpa,u16 status,struct phylink_link_state * state)4205407b0eSRussell King (Oracle) int mv88e6xxx_pcs_decode_state(struct device *dev, u16 bmsr, u16 lpa,
4305407b0eSRussell King (Oracle) 			       u16 status, struct phylink_link_state *state)
44a5a6858bSRussell King {
45b4d78731SRussell King (Oracle) 	state->link = false;
46b4d78731SRussell King (Oracle) 
47b4d78731SRussell King (Oracle) 	/* If the BMSR reports that the link had failed, report this to
48b4d78731SRussell King (Oracle) 	 * phylink.
49b4d78731SRussell King (Oracle) 	 */
50b4d78731SRussell King (Oracle) 	if (!(bmsr & BMSR_LSTATUS))
51b4d78731SRussell King (Oracle) 		return 0;
52b4d78731SRussell King (Oracle) 
53a5a6858bSRussell King 	state->link = !!(status & MV88E6390_SGMII_PHY_STATUS_LINK);
5447e96930SMarek Behún 	state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
55ede359d8SMarek Behún 
56ede359d8SMarek Behún 	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
57ede359d8SMarek Behún 		/* The Spped and Duplex Resolved register is 1 if AN is enabled
58ede359d8SMarek Behún 		 * and complete, or if AN is disabled. So with disabled AN we
5947e96930SMarek Behún 		 * still get here on link up.
60ede359d8SMarek Behún 		 */
61a5a6858bSRussell King 		state->duplex = status &
62a5a6858bSRussell King 				MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
63a5a6858bSRussell King 			                         DUPLEX_FULL : DUPLEX_HALF;
64a5a6858bSRussell King 
65a5a6858bSRussell King 		if (status & MV88E6390_SGMII_PHY_STATUS_TX_PAUSE)
66a5a6858bSRussell King 			state->pause |= MLO_PAUSE_TX;
67a5a6858bSRussell King 		if (status & MV88E6390_SGMII_PHY_STATUS_RX_PAUSE)
68a5a6858bSRussell King 			state->pause |= MLO_PAUSE_RX;
69a5a6858bSRussell King 
70a5a6858bSRussell King 		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
71a5a6858bSRussell King 		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
72a5a6858bSRussell King 			if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
73a5a6858bSRussell King 				state->speed = SPEED_2500;
74a5a6858bSRussell King 			else
75a5a6858bSRussell King 				state->speed = SPEED_1000;
76a5a6858bSRussell King 			break;
77a5a6858bSRussell King 		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
78a5a6858bSRussell King 			state->speed = SPEED_100;
79a5a6858bSRussell King 			break;
80a5a6858bSRussell King 		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
81a5a6858bSRussell King 			state->speed = SPEED_10;
82a5a6858bSRussell King 			break;
83a5a6858bSRussell King 		default:
8405407b0eSRussell King (Oracle) 			dev_err(dev, "invalid PHY speed\n");
85a5a6858bSRussell King 			return -EINVAL;
86a5a6858bSRussell King 		}
87ede359d8SMarek Behún 	} else if (state->link &&
88ede359d8SMarek Behún 		   state->interface != PHY_INTERFACE_MODE_SGMII) {
89ede359d8SMarek Behún 		/* If Speed and Duplex Resolved register is 0 and link is up, it
90ede359d8SMarek Behún 		 * means that AN was enabled, but link partner had it disabled
91ede359d8SMarek Behún 		 * and the PHY invoked the Auto-Negotiation Bypass feature and
92ede359d8SMarek Behún 		 * linked anyway.
93ede359d8SMarek Behún 		 */
94ede359d8SMarek Behún 		state->duplex = DUPLEX_FULL;
95ede359d8SMarek Behún 		if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
96ede359d8SMarek Behún 			state->speed = SPEED_2500;
97ede359d8SMarek Behún 		else
98ede359d8SMarek Behún 			state->speed = SPEED_1000;
99a5a6858bSRussell King 	} else {
100a5a6858bSRussell King 		state->link = false;
101a5a6858bSRussell King 	}
102a5a6858bSRussell King 
103a5a6858bSRussell King 	if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
104a5a6858bSRussell King 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
105a5a6858bSRussell King 				       ETHTOOL_LINK_MODE_2500baseX_Full_BIT);
106a5a6858bSRussell King 	else if (state->interface == PHY_INTERFACE_MODE_1000BASEX)
107a5a6858bSRussell King 		mii_lpa_mod_linkmode_x(state->lp_advertising, lpa,
108a5a6858bSRussell King 				       ETHTOOL_LINK_MODE_1000baseX_Full_BIT);
109a5a6858bSRussell King 
110a5a6858bSRussell King 	return 0;
111a5a6858bSRussell King }
112a5a6858bSRussell King 
113cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat {
114cda9f4aaSAndrew Lunn 	char string[ETH_GSTRING_LEN];
115cda9f4aaSAndrew Lunn 	int sizeof_stat;
116cda9f4aaSAndrew Lunn 	int reg;
117cda9f4aaSAndrew Lunn };
118cda9f4aaSAndrew Lunn 
119cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
120cda9f4aaSAndrew Lunn 	{ "serdes_fibre_rx_error", 16, 21 },
121cda9f4aaSAndrew Lunn 	{ "serdes_PRBS_error", 32, 24 },
122cda9f4aaSAndrew Lunn };
123cda9f4aaSAndrew Lunn 
mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip * chip,int port)124cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
125cda9f4aaSAndrew Lunn {
1267f7d32bcSRussell King (Oracle) 	int err;
127cda9f4aaSAndrew Lunn 
1287f7d32bcSRussell King (Oracle) 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
1297f7d32bcSRussell King (Oracle) 	if (err <= 0)
1307f7d32bcSRussell King (Oracle) 		return err;
1317f7d32bcSRussell King (Oracle) 
1327f7d32bcSRussell King (Oracle) 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
133cda9f4aaSAndrew Lunn }
134cda9f4aaSAndrew Lunn 
mv88e6352_serdes_get_strings(struct mv88e6xxx_chip * chip,int port,uint8_t * data)13565f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
136cda9f4aaSAndrew Lunn 				 int port, uint8_t *data)
137cda9f4aaSAndrew Lunn {
138cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
1397f7d32bcSRussell King (Oracle) 	int err, i;
140cda9f4aaSAndrew Lunn 
1417f7d32bcSRussell King (Oracle) 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
1427f7d32bcSRussell King (Oracle) 	if (err <= 0)
1437f7d32bcSRussell King (Oracle) 		return err;
144cda9f4aaSAndrew Lunn 
145cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
146cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
147cda9f4aaSAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
148cda9f4aaSAndrew Lunn 		       ETH_GSTRING_LEN);
149cda9f4aaSAndrew Lunn 	}
15065f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
151cda9f4aaSAndrew Lunn }
152cda9f4aaSAndrew Lunn 
mv88e6352_serdes_get_stat(struct mv88e6xxx_chip * chip,struct mv88e6352_serdes_hw_stat * stat)153cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
154cda9f4aaSAndrew Lunn 					  struct mv88e6352_serdes_hw_stat *stat)
155cda9f4aaSAndrew Lunn {
156cda9f4aaSAndrew Lunn 	u64 val = 0;
157cda9f4aaSAndrew Lunn 	u16 reg;
158cda9f4aaSAndrew Lunn 	int err;
159cda9f4aaSAndrew Lunn 
160cda9f4aaSAndrew Lunn 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
161cda9f4aaSAndrew Lunn 	if (err) {
162cda9f4aaSAndrew Lunn 		dev_err(chip->dev, "failed to read statistic\n");
163cda9f4aaSAndrew Lunn 		return 0;
164cda9f4aaSAndrew Lunn 	}
165cda9f4aaSAndrew Lunn 
166cda9f4aaSAndrew Lunn 	val = reg;
167cda9f4aaSAndrew Lunn 
168cda9f4aaSAndrew Lunn 	if (stat->sizeof_stat == 32) {
169cda9f4aaSAndrew Lunn 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
170cda9f4aaSAndrew Lunn 		if (err) {
171cda9f4aaSAndrew Lunn 			dev_err(chip->dev, "failed to read statistic\n");
172cda9f4aaSAndrew Lunn 			return 0;
173cda9f4aaSAndrew Lunn 		}
174cda9f4aaSAndrew Lunn 		val = val << 16 | reg;
175cda9f4aaSAndrew Lunn 	}
176cda9f4aaSAndrew Lunn 
177cda9f4aaSAndrew Lunn 	return val;
178cda9f4aaSAndrew Lunn }
179cda9f4aaSAndrew Lunn 
mv88e6352_serdes_get_stats(struct mv88e6xxx_chip * chip,int port,uint64_t * data)180*f6791424STobias Waldekranz size_t mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
181cda9f4aaSAndrew Lunn 				  uint64_t *data)
182cda9f4aaSAndrew Lunn {
183cda9f4aaSAndrew Lunn 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
184cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
1857f7d32bcSRussell King (Oracle) 	int i, err;
186cda9f4aaSAndrew Lunn 	u64 value;
187cda9f4aaSAndrew Lunn 
1887f7d32bcSRussell King (Oracle) 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
1897f7d32bcSRussell King (Oracle) 	if (err <= 0)
190*f6791424STobias Waldekranz 		return 0;
191cda9f4aaSAndrew Lunn 
192cda9f4aaSAndrew Lunn 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
193cda9f4aaSAndrew Lunn 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
194cda9f4aaSAndrew Lunn 
195cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
196cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
197cda9f4aaSAndrew Lunn 		value = mv88e6352_serdes_get_stat(chip, stat);
198cda9f4aaSAndrew Lunn 		mv88e6xxx_port->serdes_stats[i] += value;
199cda9f4aaSAndrew Lunn 		data[i] = mv88e6xxx_port->serdes_stats[i];
200cda9f4aaSAndrew Lunn 	}
20165f60e45SAndrew Lunn 
20265f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
203cda9f4aaSAndrew Lunn }
204cda9f4aaSAndrew Lunn 
mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip * chip,int port)2054241ef52SVivien Didelot unsigned int mv88e6352_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
2064241ef52SVivien Didelot {
2074241ef52SVivien Didelot 	return irq_find_mapping(chip->g2_irq.domain, MV88E6352_SERDES_IRQ);
2084241ef52SVivien Didelot }
2094241ef52SVivien Didelot 
mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip * chip,int port)210d3f88a24SAndrew Lunn int mv88e6352_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
211d3f88a24SAndrew Lunn {
2127f7d32bcSRussell King (Oracle) 	int err;
2137f7d32bcSRussell King (Oracle) 
2147f7d32bcSRussell King (Oracle) 	mv88e6xxx_reg_lock(chip);
2157f7d32bcSRussell King (Oracle) 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
2167f7d32bcSRussell King (Oracle) 	mv88e6xxx_reg_unlock(chip);
2177f7d32bcSRussell King (Oracle) 	if (err <= 0)
2187f7d32bcSRussell King (Oracle) 		return err;
219d3f88a24SAndrew Lunn 
220d3f88a24SAndrew Lunn 	return 32 * sizeof(u16);
221d3f88a24SAndrew Lunn }
222d3f88a24SAndrew Lunn 
mv88e6352_serdes_get_regs(struct mv88e6xxx_chip * chip,int port,void * _p)223d3f88a24SAndrew Lunn void mv88e6352_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
224d3f88a24SAndrew Lunn {
225d3f88a24SAndrew Lunn 	u16 *p = _p;
226d3f88a24SAndrew Lunn 	u16 reg;
2270fd5d79eSChris Packham 	int err;
228d3f88a24SAndrew Lunn 	int i;
229d3f88a24SAndrew Lunn 
2307f7d32bcSRussell King (Oracle) 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
2317f7d32bcSRussell King (Oracle) 	if (err <= 0)
232d3f88a24SAndrew Lunn 		return;
233d3f88a24SAndrew Lunn 
234d3f88a24SAndrew Lunn 	for (i = 0 ; i < 32; i++) {
2350fd5d79eSChris Packham 		err = mv88e6352_serdes_read(chip, i, &reg);
2360fd5d79eSChris Packham 		if (!err)
237d3f88a24SAndrew Lunn 			p[i] = reg;
238d3f88a24SAndrew Lunn 	}
239d3f88a24SAndrew Lunn }
240d3f88a24SAndrew Lunn 
mv88e6341_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)241193c5b26SPavana Sharma int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
242d3cf7d8fSMarek Behún {
243d3cf7d8fSMarek Behún 	u8 cmode = chip->ports[port].cmode;
244193c5b26SPavana Sharma 	int lane = -ENODEV;
245d3cf7d8fSMarek Behún 
2465122d4ecSVivien Didelot 	switch (port) {
2475122d4ecSVivien Didelot 	case 5:
2483bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
249d3cf7d8fSMarek Behún 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
2505122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
2515122d4ecSVivien Didelot 			lane = MV88E6341_PORT5_LANE;
2525122d4ecSVivien Didelot 		break;
253d3cf7d8fSMarek Behún 	}
254d3cf7d8fSMarek Behún 
2555122d4ecSVivien Didelot 	return lane;
256d3cf7d8fSMarek Behún }
257d3cf7d8fSMarek Behún 
mv88e6390_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)258193c5b26SPavana Sharma int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
25907ffbd74SAndrew Lunn {
2602d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
261193c5b26SPavana Sharma 	int lane = -ENODEV;
26207ffbd74SAndrew Lunn 
26307ffbd74SAndrew Lunn 	switch (port) {
26407ffbd74SAndrew Lunn 	case 9:
2653bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
26607ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
2675122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
2685122d4ecSVivien Didelot 			lane = MV88E6390_PORT9_LANE0;
26917deaf5cSMarek Behún 		break;
27007ffbd74SAndrew Lunn 	case 10:
2713bbb8867SMarek Behún 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
27207ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
2735122d4ecSVivien Didelot 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
2745122d4ecSVivien Didelot 			lane = MV88E6390_PORT10_LANE0;
27517deaf5cSMarek Behún 		break;
27607ffbd74SAndrew Lunn 	}
27707ffbd74SAndrew Lunn 
2785122d4ecSVivien Didelot 	return lane;
27917deaf5cSMarek Behún }
28017deaf5cSMarek Behún 
mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)281193c5b26SPavana Sharma int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
282a8c01c0dSAndrew Lunn {
2835122d4ecSVivien Didelot 	u8 cmode_port = chip->ports[port].cmode;
2845122d4ecSVivien Didelot 	u8 cmode_port10 = chip->ports[10].cmode;
2855122d4ecSVivien Didelot 	u8 cmode_port9 = chip->ports[9].cmode;
286193c5b26SPavana Sharma 	int lane = -ENODEV;
287a8c01c0dSAndrew Lunn 
288a8c01c0dSAndrew Lunn 	switch (port) {
289a8c01c0dSAndrew Lunn 	case 2:
2903bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
291a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
2925122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
2935122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
2945122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE1;
29517deaf5cSMarek Behún 		break;
296a8c01c0dSAndrew Lunn 	case 3:
2973bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
298a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
299a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
3005122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
3015122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
3025122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE2;
30317deaf5cSMarek Behún 		break;
304a8c01c0dSAndrew Lunn 	case 4:
3053bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
306a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
307a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
3085122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
3095122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
3105122d4ecSVivien Didelot 				lane = MV88E6390_PORT9_LANE3;
31117deaf5cSMarek Behún 		break;
312a8c01c0dSAndrew Lunn 	case 5:
3133bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
314a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
3155122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
3165122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
3175122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE1;
31817deaf5cSMarek Behún 		break;
319a8c01c0dSAndrew Lunn 	case 6:
3203bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
321a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
322a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
3235122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
3245122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
3255122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE2;
32617deaf5cSMarek Behún 		break;
327a8c01c0dSAndrew Lunn 	case 7:
3283bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
329a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
330a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
3315122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
3325122d4ecSVivien Didelot 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASEX)
3335122d4ecSVivien Didelot 				lane = MV88E6390_PORT10_LANE3;
33417deaf5cSMarek Behún 		break;
335a8c01c0dSAndrew Lunn 	case 9:
3363bbb8867SMarek Behún 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
337a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
338a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
339a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
3405122d4ecSVivien Didelot 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
3415122d4ecSVivien Didelot 			lane = MV88E6390_PORT9_LANE0;
34217deaf5cSMarek Behún 		break;
343a8c01c0dSAndrew Lunn 	case 10:
3443bbb8867SMarek Behún 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
345a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
346a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
347a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
3485122d4ecSVivien Didelot 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI)
3495122d4ecSVivien Didelot 			lane = MV88E6390_PORT10_LANE0;
35017deaf5cSMarek Behún 		break;
35117deaf5cSMarek Behún 	}
35217deaf5cSMarek Behún 
3535122d4ecSVivien Didelot 	return lane;
354a8c01c0dSAndrew Lunn }
355a8c01c0dSAndrew Lunn 
356de776d0dSPavana Sharma /* Only Ports 0, 9 and 10 have SERDES lanes. Return the SERDES lane address
357de776d0dSPavana Sharma  * a port is using else Returns -ENODEV.
358de776d0dSPavana Sharma  */
mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip * chip,int port)359de776d0dSPavana Sharma int mv88e6393x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port)
360de776d0dSPavana Sharma {
361de776d0dSPavana Sharma 	u8 cmode = chip->ports[port].cmode;
362de776d0dSPavana Sharma 	int lane = -ENODEV;
363de776d0dSPavana Sharma 
364de776d0dSPavana Sharma 	if (port != 0 && port != 9 && port != 10)
365de776d0dSPavana Sharma 		return -EOPNOTSUPP;
366de776d0dSPavana Sharma 
367de776d0dSPavana Sharma 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASEX ||
368de776d0dSPavana Sharma 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
369de776d0dSPavana Sharma 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
370de776d0dSPavana Sharma 	    cmode == MV88E6393X_PORT_STS_CMODE_5GBASER ||
3714a562127SMichal Smulski 	    cmode == MV88E6393X_PORT_STS_CMODE_10GBASER ||
3724a562127SMichal Smulski 	    cmode == MV88E6393X_PORT_STS_CMODE_USXGMII)
373de776d0dSPavana Sharma 		lane = port;
374de776d0dSPavana Sharma 
375de776d0dSPavana Sharma 	return lane;
376de776d0dSPavana Sharma }
377de776d0dSPavana Sharma 
3780df95287SNikita Yushchenko struct mv88e6390_serdes_hw_stat {
3790df95287SNikita Yushchenko 	char string[ETH_GSTRING_LEN];
3800df95287SNikita Yushchenko 	int reg;
3810df95287SNikita Yushchenko };
3820df95287SNikita Yushchenko 
3830df95287SNikita Yushchenko static struct mv88e6390_serdes_hw_stat mv88e6390_serdes_hw_stats[] = {
3840df95287SNikita Yushchenko 	{ "serdes_rx_pkts", 0xf021 },
3850df95287SNikita Yushchenko 	{ "serdes_rx_bytes", 0xf024 },
3860df95287SNikita Yushchenko 	{ "serdes_rx_pkts_error", 0xf027 },
3870df95287SNikita Yushchenko };
3880df95287SNikita Yushchenko 
mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip * chip,int port)3890df95287SNikita Yushchenko int mv88e6390_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
3900df95287SNikita Yushchenko {
391a03b98d6SMarek Behún 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
3920df95287SNikita Yushchenko 		return 0;
3930df95287SNikita Yushchenko 
3940df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
3950df95287SNikita Yushchenko }
3960df95287SNikita Yushchenko 
mv88e6390_serdes_get_strings(struct mv88e6xxx_chip * chip,int port,uint8_t * data)3970df95287SNikita Yushchenko int mv88e6390_serdes_get_strings(struct mv88e6xxx_chip *chip,
3980df95287SNikita Yushchenko 				 int port, uint8_t *data)
3990df95287SNikita Yushchenko {
4000df95287SNikita Yushchenko 	struct mv88e6390_serdes_hw_stat *stat;
4010df95287SNikita Yushchenko 	int i;
4020df95287SNikita Yushchenko 
403a03b98d6SMarek Behún 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
4040df95287SNikita Yushchenko 		return 0;
4050df95287SNikita Yushchenko 
4060df95287SNikita Yushchenko 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
4070df95287SNikita Yushchenko 		stat = &mv88e6390_serdes_hw_stats[i];
4080df95287SNikita Yushchenko 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
4090df95287SNikita Yushchenko 		       ETH_GSTRING_LEN);
4100df95287SNikita Yushchenko 	}
4110df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
4120df95287SNikita Yushchenko }
4130df95287SNikita Yushchenko 
mv88e6390_serdes_get_stat(struct mv88e6xxx_chip * chip,int lane,struct mv88e6390_serdes_hw_stat * stat)4140df95287SNikita Yushchenko static uint64_t mv88e6390_serdes_get_stat(struct mv88e6xxx_chip *chip, int lane,
4150df95287SNikita Yushchenko 					  struct mv88e6390_serdes_hw_stat *stat)
4160df95287SNikita Yushchenko {
4170df95287SNikita Yushchenko 	u16 reg[3];
4180df95287SNikita Yushchenko 	int err, i;
4190df95287SNikita Yushchenko 
4200df95287SNikita Yushchenko 	for (i = 0; i < 3; i++) {
4210df95287SNikita Yushchenko 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
4220df95287SNikita Yushchenko 					    stat->reg + i, &reg[i]);
4230df95287SNikita Yushchenko 		if (err) {
4240df95287SNikita Yushchenko 			dev_err(chip->dev, "failed to read statistic\n");
4250df95287SNikita Yushchenko 			return 0;
4260df95287SNikita Yushchenko 		}
4270df95287SNikita Yushchenko 	}
4280df95287SNikita Yushchenko 
4290df95287SNikita Yushchenko 	return reg[0] | ((u64)reg[1] << 16) | ((u64)reg[2] << 32);
4300df95287SNikita Yushchenko }
4310df95287SNikita Yushchenko 
mv88e6390_serdes_get_stats(struct mv88e6xxx_chip * chip,int port,uint64_t * data)432*f6791424STobias Waldekranz size_t mv88e6390_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
4330df95287SNikita Yushchenko 				  uint64_t *data)
4340df95287SNikita Yushchenko {
4350df95287SNikita Yushchenko 	struct mv88e6390_serdes_hw_stat *stat;
4360df95287SNikita Yushchenko 	int lane;
4370df95287SNikita Yushchenko 	int i;
4380df95287SNikita Yushchenko 
439a03b98d6SMarek Behún 	lane = mv88e6xxx_serdes_get_lane(chip, port);
440193c5b26SPavana Sharma 	if (lane < 0)
4410df95287SNikita Yushchenko 		return 0;
4420df95287SNikita Yushchenko 
4430df95287SNikita Yushchenko 	for (i = 0; i < ARRAY_SIZE(mv88e6390_serdes_hw_stats); i++) {
4440df95287SNikita Yushchenko 		stat = &mv88e6390_serdes_hw_stats[i];
4450df95287SNikita Yushchenko 		data[i] = mv88e6390_serdes_get_stat(chip, lane, stat);
4460df95287SNikita Yushchenko 	}
4470df95287SNikita Yushchenko 
4480df95287SNikita Yushchenko 	return ARRAY_SIZE(mv88e6390_serdes_hw_stats);
4490df95287SNikita Yushchenko }
4500df95287SNikita Yushchenko 
mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip * chip,int port)4514241ef52SVivien Didelot unsigned int mv88e6390_serdes_irq_mapping(struct mv88e6xxx_chip *chip, int port)
4524241ef52SVivien Didelot {
4534241ef52SVivien Didelot 	return irq_find_mapping(chip->g2_irq.domain, port);
4544241ef52SVivien Didelot }
455bf3504ceSAndrew Lunn 
456bf3504ceSAndrew Lunn static const u16 mv88e6390_serdes_regs[] = {
457bf3504ceSAndrew Lunn 	/* SERDES common registers */
458bf3504ceSAndrew Lunn 	0xf00a, 0xf00b, 0xf00c,
459bf3504ceSAndrew Lunn 	0xf010, 0xf011, 0xf012, 0xf013,
460bf3504ceSAndrew Lunn 	0xf016, 0xf017, 0xf018,
461bf3504ceSAndrew Lunn 	0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
462bf3504ceSAndrew Lunn 	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
463bf3504ceSAndrew Lunn 	0xf028, 0xf029,
464bf3504ceSAndrew Lunn 	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
465bf3504ceSAndrew Lunn 	0xf038, 0xf039,
466bf3504ceSAndrew Lunn 	/* SGMII */
467bf3504ceSAndrew Lunn 	0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
468bf3504ceSAndrew Lunn 	0x2008,
469bf3504ceSAndrew Lunn 	0x200f,
470bf3504ceSAndrew Lunn 	0xa000, 0xa001, 0xa002, 0xa003,
471bf3504ceSAndrew Lunn 	/* 10Gbase-X */
472bf3504ceSAndrew Lunn 	0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
473bf3504ceSAndrew Lunn 	0x1008,
474bf3504ceSAndrew Lunn 	0x100e, 0x100f,
475bf3504ceSAndrew Lunn 	0x1018, 0x1019,
476bf3504ceSAndrew Lunn 	0x9000, 0x9001, 0x9002, 0x9003, 0x9004,
477bf3504ceSAndrew Lunn 	0x9006,
478bf3504ceSAndrew Lunn 	0x9010, 0x9011, 0x9012, 0x9013, 0x9014, 0x9015, 0x9016,
479bf3504ceSAndrew Lunn 	/* 10Gbase-R */
480bf3504ceSAndrew Lunn 	0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
481bf3504ceSAndrew Lunn 	0x1028, 0x1029, 0x102a, 0x102b,
482bf3504ceSAndrew Lunn };
483bf3504ceSAndrew Lunn 
mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip * chip,int port)484bf3504ceSAndrew Lunn int mv88e6390_serdes_get_regs_len(struct mv88e6xxx_chip *chip, int port)
485bf3504ceSAndrew Lunn {
486193c5b26SPavana Sharma 	if (mv88e6xxx_serdes_get_lane(chip, port) < 0)
487bf3504ceSAndrew Lunn 		return 0;
488bf3504ceSAndrew Lunn 
489bf3504ceSAndrew Lunn 	return ARRAY_SIZE(mv88e6390_serdes_regs) * sizeof(u16);
490bf3504ceSAndrew Lunn }
491bf3504ceSAndrew Lunn 
mv88e6390_serdes_get_regs(struct mv88e6xxx_chip * chip,int port,void * _p)492bf3504ceSAndrew Lunn void mv88e6390_serdes_get_regs(struct mv88e6xxx_chip *chip, int port, void *_p)
493bf3504ceSAndrew Lunn {
494bf3504ceSAndrew Lunn 	u16 *p = _p;
495bf3504ceSAndrew Lunn 	int lane;
496bf3504ceSAndrew Lunn 	u16 reg;
4970fd5d79eSChris Packham 	int err;
498bf3504ceSAndrew Lunn 	int i;
499bf3504ceSAndrew Lunn 
500bf3504ceSAndrew Lunn 	lane = mv88e6xxx_serdes_get_lane(chip, port);
501193c5b26SPavana Sharma 	if (lane < 0)
502bf3504ceSAndrew Lunn 		return;
503bf3504ceSAndrew Lunn 
504bf3504ceSAndrew Lunn 	for (i = 0 ; i < ARRAY_SIZE(mv88e6390_serdes_regs); i++) {
5050fd5d79eSChris Packham 		err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
506bf3504ceSAndrew Lunn 					    mv88e6390_serdes_regs[i], &reg);
5070fd5d79eSChris Packham 		if (!err)
508bf3504ceSAndrew Lunn 			p[i] = reg;
509bf3504ceSAndrew Lunn 	}
510bf3504ceSAndrew Lunn }
511de776d0dSPavana Sharma 
512926eae60SHolger Brunck static const int mv88e6352_serdes_p2p_to_reg[] = {
513926eae60SHolger Brunck 	/* Index of value in microvolts corresponds to the register value */
514926eae60SHolger Brunck 	14000, 112000, 210000, 308000, 406000, 504000, 602000, 700000,
515926eae60SHolger Brunck };
516926eae60SHolger Brunck 
mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip * chip,int port,int val)517926eae60SHolger Brunck int mv88e6352_serdes_set_tx_amplitude(struct mv88e6xxx_chip *chip, int port,
518926eae60SHolger Brunck 				      int val)
519926eae60SHolger Brunck {
520926eae60SHolger Brunck 	bool found = false;
521926eae60SHolger Brunck 	u16 ctrl, reg;
522926eae60SHolger Brunck 	int err;
523926eae60SHolger Brunck 	int i;
524926eae60SHolger Brunck 
525926eae60SHolger Brunck 	err = mv88e6352_g2_scratch_port_has_serdes(chip, port);
526926eae60SHolger Brunck 	if (err <= 0)
527926eae60SHolger Brunck 		return err;
528926eae60SHolger Brunck 
529926eae60SHolger Brunck 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_p2p_to_reg); ++i) {
530926eae60SHolger Brunck 		if (mv88e6352_serdes_p2p_to_reg[i] == val) {
531926eae60SHolger Brunck 			reg = i;
532926eae60SHolger Brunck 			found = true;
533926eae60SHolger Brunck 			break;
534926eae60SHolger Brunck 		}
535926eae60SHolger Brunck 	}
536926eae60SHolger Brunck 
537926eae60SHolger Brunck 	if (!found)
538926eae60SHolger Brunck 		return -EINVAL;
539926eae60SHolger Brunck 
540926eae60SHolger Brunck 	err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_SPEC_CTRL2, &ctrl);
541926eae60SHolger Brunck 	if (err)
542926eae60SHolger Brunck 		return err;
543926eae60SHolger Brunck 
544926eae60SHolger Brunck 	ctrl &= ~MV88E6352_SERDES_OUT_AMP_MASK;
545926eae60SHolger Brunck 	ctrl |= reg;
546926eae60SHolger Brunck 
547926eae60SHolger Brunck 	return mv88e6352_serdes_write(chip, MV88E6352_SERDES_SPEC_CTRL2, ctrl);
548926eae60SHolger Brunck }
549