xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/serdes.c (revision d3cf7d8f)
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 
526d91782fSAndrew Lunn static int mv88e6352_serdes_power_set(struct mv88e6xxx_chip *chip, bool on)
536d91782fSAndrew Lunn {
546d91782fSAndrew Lunn 	u16 val, new_val;
556d91782fSAndrew Lunn 	int err;
566d91782fSAndrew Lunn 
576d91782fSAndrew Lunn 	err = mv88e6352_serdes_read(chip, MII_BMCR, &val);
586d91782fSAndrew Lunn 	if (err)
596d91782fSAndrew Lunn 		return err;
606d91782fSAndrew Lunn 
616d91782fSAndrew Lunn 	if (on)
626d91782fSAndrew Lunn 		new_val = val & ~BMCR_PDOWN;
636d91782fSAndrew Lunn 	else
646d91782fSAndrew Lunn 		new_val = val | BMCR_PDOWN;
656d91782fSAndrew Lunn 
666d91782fSAndrew Lunn 	if (val != new_val)
676d91782fSAndrew Lunn 		err = mv88e6352_serdes_write(chip, MII_BMCR, new_val);
686d91782fSAndrew Lunn 
696d91782fSAndrew Lunn 	return err;
706d91782fSAndrew Lunn }
716d91782fSAndrew Lunn 
72eb755c3fSAndrew Lunn static bool mv88e6352_port_has_serdes(struct mv88e6xxx_chip *chip, int port)
736d91782fSAndrew Lunn {
742d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
756d91782fSAndrew Lunn 
765f83dc93SVivien Didelot 	if ((cmode == MV88E6XXX_PORT_STS_CMODE_100BASE_X) ||
775f83dc93SVivien Didelot 	    (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) ||
78eb755c3fSAndrew Lunn 	    (cmode == MV88E6XXX_PORT_STS_CMODE_SGMII))
79b1312b85SFengguang Wu 		return true;
80eb755c3fSAndrew Lunn 
81b1312b85SFengguang Wu 	return false;
82eb755c3fSAndrew Lunn }
83eb755c3fSAndrew Lunn 
84eb755c3fSAndrew Lunn int mv88e6352_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
85eb755c3fSAndrew Lunn {
86eb755c3fSAndrew Lunn 	int err;
87eb755c3fSAndrew Lunn 
88eb755c3fSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port)) {
896d91782fSAndrew Lunn 		err = mv88e6352_serdes_power_set(chip, on);
906d91782fSAndrew Lunn 		if (err < 0)
916d91782fSAndrew Lunn 			return err;
926d91782fSAndrew Lunn 	}
936d91782fSAndrew Lunn 
946d91782fSAndrew Lunn 	return 0;
956d91782fSAndrew Lunn }
966335e9f2SAndrew Lunn 
97cda9f4aaSAndrew Lunn struct mv88e6352_serdes_hw_stat {
98cda9f4aaSAndrew Lunn 	char string[ETH_GSTRING_LEN];
99cda9f4aaSAndrew Lunn 	int sizeof_stat;
100cda9f4aaSAndrew Lunn 	int reg;
101cda9f4aaSAndrew Lunn };
102cda9f4aaSAndrew Lunn 
103cda9f4aaSAndrew Lunn static struct mv88e6352_serdes_hw_stat mv88e6352_serdes_hw_stats[] = {
104cda9f4aaSAndrew Lunn 	{ "serdes_fibre_rx_error", 16, 21 },
105cda9f4aaSAndrew Lunn 	{ "serdes_PRBS_error", 32, 24 },
106cda9f4aaSAndrew Lunn };
107cda9f4aaSAndrew Lunn 
108cda9f4aaSAndrew Lunn int mv88e6352_serdes_get_sset_count(struct mv88e6xxx_chip *chip, int port)
109cda9f4aaSAndrew Lunn {
110cda9f4aaSAndrew Lunn 	if (mv88e6352_port_has_serdes(chip, port))
111cda9f4aaSAndrew Lunn 		return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
112cda9f4aaSAndrew Lunn 
113cda9f4aaSAndrew Lunn 	return 0;
114cda9f4aaSAndrew Lunn }
115cda9f4aaSAndrew Lunn 
11665f60e45SAndrew Lunn int mv88e6352_serdes_get_strings(struct mv88e6xxx_chip *chip,
117cda9f4aaSAndrew Lunn 				 int port, uint8_t *data)
118cda9f4aaSAndrew Lunn {
119cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
120cda9f4aaSAndrew Lunn 	int i;
121cda9f4aaSAndrew Lunn 
122cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
12365f60e45SAndrew Lunn 		return 0;
124cda9f4aaSAndrew Lunn 
125cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
126cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
127cda9f4aaSAndrew Lunn 		memcpy(data + i * ETH_GSTRING_LEN, stat->string,
128cda9f4aaSAndrew Lunn 		       ETH_GSTRING_LEN);
129cda9f4aaSAndrew Lunn 	}
13065f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
131cda9f4aaSAndrew Lunn }
132cda9f4aaSAndrew Lunn 
133cda9f4aaSAndrew Lunn static uint64_t mv88e6352_serdes_get_stat(struct mv88e6xxx_chip *chip,
134cda9f4aaSAndrew Lunn 					  struct mv88e6352_serdes_hw_stat *stat)
135cda9f4aaSAndrew Lunn {
136cda9f4aaSAndrew Lunn 	u64 val = 0;
137cda9f4aaSAndrew Lunn 	u16 reg;
138cda9f4aaSAndrew Lunn 	int err;
139cda9f4aaSAndrew Lunn 
140cda9f4aaSAndrew Lunn 	err = mv88e6352_serdes_read(chip, stat->reg, &reg);
141cda9f4aaSAndrew Lunn 	if (err) {
142cda9f4aaSAndrew Lunn 		dev_err(chip->dev, "failed to read statistic\n");
143cda9f4aaSAndrew Lunn 		return 0;
144cda9f4aaSAndrew Lunn 	}
145cda9f4aaSAndrew Lunn 
146cda9f4aaSAndrew Lunn 	val = reg;
147cda9f4aaSAndrew Lunn 
148cda9f4aaSAndrew Lunn 	if (stat->sizeof_stat == 32) {
149cda9f4aaSAndrew Lunn 		err = mv88e6352_serdes_read(chip, stat->reg + 1, &reg);
150cda9f4aaSAndrew Lunn 		if (err) {
151cda9f4aaSAndrew Lunn 			dev_err(chip->dev, "failed to read statistic\n");
152cda9f4aaSAndrew Lunn 			return 0;
153cda9f4aaSAndrew Lunn 		}
154cda9f4aaSAndrew Lunn 		val = val << 16 | reg;
155cda9f4aaSAndrew Lunn 	}
156cda9f4aaSAndrew Lunn 
157cda9f4aaSAndrew Lunn 	return val;
158cda9f4aaSAndrew Lunn }
159cda9f4aaSAndrew Lunn 
16065f60e45SAndrew Lunn int mv88e6352_serdes_get_stats(struct mv88e6xxx_chip *chip, int port,
161cda9f4aaSAndrew Lunn 			       uint64_t *data)
162cda9f4aaSAndrew Lunn {
163cda9f4aaSAndrew Lunn 	struct mv88e6xxx_port *mv88e6xxx_port = &chip->ports[port];
164cda9f4aaSAndrew Lunn 	struct mv88e6352_serdes_hw_stat *stat;
165cda9f4aaSAndrew Lunn 	u64 value;
166cda9f4aaSAndrew Lunn 	int i;
167cda9f4aaSAndrew Lunn 
168cda9f4aaSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
16965f60e45SAndrew Lunn 		return 0;
170cda9f4aaSAndrew Lunn 
171cda9f4aaSAndrew Lunn 	BUILD_BUG_ON(ARRAY_SIZE(mv88e6352_serdes_hw_stats) >
172cda9f4aaSAndrew Lunn 		     ARRAY_SIZE(mv88e6xxx_port->serdes_stats));
173cda9f4aaSAndrew Lunn 
174cda9f4aaSAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(mv88e6352_serdes_hw_stats); i++) {
175cda9f4aaSAndrew Lunn 		stat = &mv88e6352_serdes_hw_stats[i];
176cda9f4aaSAndrew Lunn 		value = mv88e6352_serdes_get_stat(chip, stat);
177cda9f4aaSAndrew Lunn 		mv88e6xxx_port->serdes_stats[i] += value;
178cda9f4aaSAndrew Lunn 		data[i] = mv88e6xxx_port->serdes_stats[i];
179cda9f4aaSAndrew Lunn 	}
18065f60e45SAndrew Lunn 
18165f60e45SAndrew Lunn 	return ARRAY_SIZE(mv88e6352_serdes_hw_stats);
182cda9f4aaSAndrew Lunn }
183cda9f4aaSAndrew Lunn 
1844382172fSAndrew Lunn static void mv88e6352_serdes_irq_link(struct mv88e6xxx_chip *chip, int port)
1854382172fSAndrew Lunn {
1864382172fSAndrew Lunn 	struct dsa_switch *ds = chip->ds;
1874382172fSAndrew Lunn 	u16 status;
1884382172fSAndrew Lunn 	bool up;
1894382172fSAndrew Lunn 
1904382172fSAndrew Lunn 	mv88e6352_serdes_read(chip, MII_BMSR, &status);
1914382172fSAndrew Lunn 
1924382172fSAndrew Lunn 	/* Status must be read twice in order to give the current link
1934382172fSAndrew Lunn 	 * status. Otherwise the change in link status since the last
1944382172fSAndrew Lunn 	 * read of the register is returned.
1954382172fSAndrew Lunn 	 */
1964382172fSAndrew Lunn 	mv88e6352_serdes_read(chip, MII_BMSR, &status);
1974382172fSAndrew Lunn 
1984382172fSAndrew Lunn 	up = status & BMSR_LSTATUS;
1994382172fSAndrew Lunn 
2004382172fSAndrew Lunn 	dsa_port_phylink_mac_change(ds, port, up);
2014382172fSAndrew Lunn }
2024382172fSAndrew Lunn 
2034382172fSAndrew Lunn static irqreturn_t mv88e6352_serdes_thread_fn(int irq, void *dev_id)
2044382172fSAndrew Lunn {
2054382172fSAndrew Lunn 	struct mv88e6xxx_port *port = dev_id;
2064382172fSAndrew Lunn 	struct mv88e6xxx_chip *chip = port->chip;
2074382172fSAndrew Lunn 	irqreturn_t ret = IRQ_NONE;
2084382172fSAndrew Lunn 	u16 status;
2094382172fSAndrew Lunn 	int err;
2104382172fSAndrew Lunn 
211c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
2124382172fSAndrew Lunn 
2134382172fSAndrew Lunn 	err = mv88e6352_serdes_read(chip, MV88E6352_SERDES_INT_STATUS, &status);
2144382172fSAndrew Lunn 	if (err)
2154382172fSAndrew Lunn 		goto out;
2164382172fSAndrew Lunn 
2174382172fSAndrew Lunn 	if (status & MV88E6352_SERDES_INT_LINK_CHANGE) {
2184382172fSAndrew Lunn 		ret = IRQ_HANDLED;
2194382172fSAndrew Lunn 		mv88e6352_serdes_irq_link(chip, port->port);
2204382172fSAndrew Lunn 	}
2214382172fSAndrew Lunn out:
222c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
2234382172fSAndrew Lunn 
2244382172fSAndrew Lunn 	return ret;
2254382172fSAndrew Lunn }
2264382172fSAndrew Lunn 
2274382172fSAndrew Lunn static int mv88e6352_serdes_irq_enable(struct mv88e6xxx_chip *chip)
2284382172fSAndrew Lunn {
2294382172fSAndrew Lunn 	return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE,
2304382172fSAndrew Lunn 				      MV88E6352_SERDES_INT_LINK_CHANGE);
2314382172fSAndrew Lunn }
2324382172fSAndrew Lunn 
2334382172fSAndrew Lunn static int mv88e6352_serdes_irq_disable(struct mv88e6xxx_chip *chip)
2344382172fSAndrew Lunn {
2354382172fSAndrew Lunn 	return mv88e6352_serdes_write(chip, MV88E6352_SERDES_INT_ENABLE, 0);
2364382172fSAndrew Lunn }
2374382172fSAndrew Lunn 
2384382172fSAndrew Lunn int mv88e6352_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
2394382172fSAndrew Lunn {
2404382172fSAndrew Lunn 	int err;
2414382172fSAndrew Lunn 
2424382172fSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
2434382172fSAndrew Lunn 		return 0;
2444382172fSAndrew Lunn 
2454382172fSAndrew Lunn 	chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
2464382172fSAndrew Lunn 							MV88E6352_SERDES_IRQ);
2474382172fSAndrew Lunn 	if (chip->ports[port].serdes_irq < 0) {
2484382172fSAndrew Lunn 		dev_err(chip->dev, "Unable to map SERDES irq: %d\n",
2494382172fSAndrew Lunn 			chip->ports[port].serdes_irq);
2504382172fSAndrew Lunn 		return chip->ports[port].serdes_irq;
2514382172fSAndrew Lunn 	}
2524382172fSAndrew Lunn 
2534382172fSAndrew Lunn 	/* Requesting the IRQ will trigger irq callbacks. So we cannot
2544382172fSAndrew Lunn 	 * hold the reg_lock.
2554382172fSAndrew Lunn 	 */
256c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
2574382172fSAndrew Lunn 	err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
2584382172fSAndrew Lunn 				   mv88e6352_serdes_thread_fn,
2594382172fSAndrew Lunn 				   IRQF_ONESHOT, "mv88e6xxx-serdes",
2604382172fSAndrew Lunn 				   &chip->ports[port]);
261c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
2624382172fSAndrew Lunn 
2634382172fSAndrew Lunn 	if (err) {
2644382172fSAndrew Lunn 		dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
2654382172fSAndrew Lunn 			err);
2664382172fSAndrew Lunn 		return err;
2674382172fSAndrew Lunn 	}
2684382172fSAndrew Lunn 
2694382172fSAndrew Lunn 	return mv88e6352_serdes_irq_enable(chip);
2704382172fSAndrew Lunn }
2714382172fSAndrew Lunn 
2724382172fSAndrew Lunn void mv88e6352_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
2734382172fSAndrew Lunn {
2744382172fSAndrew Lunn 	if (!mv88e6352_port_has_serdes(chip, port))
2754382172fSAndrew Lunn 		return;
2764382172fSAndrew Lunn 
2774382172fSAndrew Lunn 	mv88e6352_serdes_irq_disable(chip);
2784382172fSAndrew Lunn 
2794382172fSAndrew Lunn 	/* Freeing the IRQ will trigger irq callbacks. So we cannot
2804382172fSAndrew Lunn 	 * hold the reg_lock.
2814382172fSAndrew Lunn 	 */
282c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
2834382172fSAndrew Lunn 	free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
284c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
2854382172fSAndrew Lunn 
2864382172fSAndrew Lunn 	chip->ports[port].serdes_irq = 0;
2874382172fSAndrew Lunn }
2884382172fSAndrew Lunn 
289d3cf7d8fSMarek Behún int mv88e6341_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane)
290d3cf7d8fSMarek Behún {
291d3cf7d8fSMarek Behún 	u8 cmode = chip->ports[port].cmode;
292d3cf7d8fSMarek Behún 
293d3cf7d8fSMarek Behún 	if (port != 5)
294d3cf7d8fSMarek Behún 		return -ENODEV;
295d3cf7d8fSMarek Behún 
296d3cf7d8fSMarek Behún 	if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
297d3cf7d8fSMarek Behún 	    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
298d3cf7d8fSMarek Behún 	    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
299d3cf7d8fSMarek Behún 		*lane = MV88E6341_PORT5_LANE;
300d3cf7d8fSMarek Behún 		return 0;
301d3cf7d8fSMarek Behún 	}
302d3cf7d8fSMarek Behún 
303d3cf7d8fSMarek Behún 	return -ENODEV;
304d3cf7d8fSMarek Behún }
305d3cf7d8fSMarek Behún 
30617deaf5cSMarek Behún int mv88e6390_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane)
30707ffbd74SAndrew Lunn {
3082d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
30907ffbd74SAndrew Lunn 
31007ffbd74SAndrew Lunn 	switch (port) {
31107ffbd74SAndrew Lunn 	case 9:
31207ffbd74SAndrew Lunn 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
31307ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
31417deaf5cSMarek Behún 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
31517deaf5cSMarek Behún 			*lane = MV88E6390_PORT9_LANE0;
31617deaf5cSMarek Behún 			return 0;
31717deaf5cSMarek Behún 		}
31817deaf5cSMarek Behún 		break;
31907ffbd74SAndrew Lunn 	case 10:
32007ffbd74SAndrew Lunn 		if (cmode == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
32107ffbd74SAndrew Lunn 		    cmode == MV88E6XXX_PORT_STS_CMODE_SGMII ||
32217deaf5cSMarek Behún 		    cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
32317deaf5cSMarek Behún 			*lane = MV88E6390_PORT10_LANE0;
32417deaf5cSMarek Behún 			return 0;
32507ffbd74SAndrew Lunn 		}
32617deaf5cSMarek Behún 		break;
32717deaf5cSMarek Behún 	default:
32817deaf5cSMarek Behún 		break;
32907ffbd74SAndrew Lunn 	}
33007ffbd74SAndrew Lunn 
33117deaf5cSMarek Behún 	return -ENODEV;
33217deaf5cSMarek Behún }
33317deaf5cSMarek Behún 
33417deaf5cSMarek Behún int mv88e6390x_serdes_get_lane(struct mv88e6xxx_chip *chip, int port, u8 *lane)
335a8c01c0dSAndrew Lunn {
336a8c01c0dSAndrew Lunn 	u8 cmode_port9, cmode_port10, cmode_port;
337a8c01c0dSAndrew Lunn 
3382d2e1dd2SAndrew Lunn 	cmode_port9 = chip->ports[9].cmode;
3392d2e1dd2SAndrew Lunn 	cmode_port10 = chip->ports[10].cmode;
3402d2e1dd2SAndrew Lunn 	cmode_port = chip->ports[port].cmode;
341a8c01c0dSAndrew Lunn 
342a8c01c0dSAndrew Lunn 	switch (port) {
343a8c01c0dSAndrew Lunn 	case 2:
344a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
345a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
34617deaf5cSMarek Behún 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
34717deaf5cSMarek Behún 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) {
34817deaf5cSMarek Behún 				*lane = MV88E6390_PORT9_LANE1;
34917deaf5cSMarek Behún 				return 0;
35017deaf5cSMarek Behún 			}
35117deaf5cSMarek Behún 		}
35217deaf5cSMarek Behún 		break;
353a8c01c0dSAndrew Lunn 	case 3:
354a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
355a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
356a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
35717deaf5cSMarek Behún 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
35817deaf5cSMarek Behún 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) {
35917deaf5cSMarek Behún 				*lane = MV88E6390_PORT9_LANE2;
36017deaf5cSMarek Behún 				return 0;
36117deaf5cSMarek Behún 			}
36217deaf5cSMarek Behún 		}
36317deaf5cSMarek Behún 		break;
364a8c01c0dSAndrew Lunn 	case 4:
365a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
366a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
367a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
36817deaf5cSMarek Behún 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
36917deaf5cSMarek Behún 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) {
37017deaf5cSMarek Behún 				*lane = MV88E6390_PORT9_LANE3;
37117deaf5cSMarek Behún 				return 0;
37217deaf5cSMarek Behún 			}
37317deaf5cSMarek Behún 		}
37417deaf5cSMarek Behún 		break;
375a8c01c0dSAndrew Lunn 	case 5:
376a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
377a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
37817deaf5cSMarek Behún 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX) {
37917deaf5cSMarek Behún 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) {
38017deaf5cSMarek Behún 				*lane = MV88E6390_PORT10_LANE1;
38117deaf5cSMarek Behún 				return 0;
38217deaf5cSMarek Behún 			}
38317deaf5cSMarek Behún 		}
38417deaf5cSMarek Behún 		break;
385a8c01c0dSAndrew Lunn 	case 6:
386a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
387a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
388a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
38917deaf5cSMarek Behún 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
39017deaf5cSMarek Behún 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) {
39117deaf5cSMarek Behún 				*lane = MV88E6390_PORT10_LANE2;
39217deaf5cSMarek Behún 				return 0;
39317deaf5cSMarek Behún 			}
39417deaf5cSMarek Behún 		}
39517deaf5cSMarek Behún 		break;
396a8c01c0dSAndrew Lunn 	case 7:
397a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
398a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
399a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
40017deaf5cSMarek Behún 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
40117deaf5cSMarek Behún 			if (cmode_port == MV88E6XXX_PORT_STS_CMODE_1000BASE_X) {
40217deaf5cSMarek Behún 				*lane = MV88E6390_PORT10_LANE3;
40317deaf5cSMarek Behún 				return 0;
40417deaf5cSMarek Behún 			}
40517deaf5cSMarek Behún 		}
40617deaf5cSMarek Behún 		break;
407a8c01c0dSAndrew Lunn 	case 9:
408a8c01c0dSAndrew Lunn 		if (cmode_port9 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
409a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
410a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
411a8c01c0dSAndrew Lunn 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
41217deaf5cSMarek Behún 		    cmode_port9 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
41317deaf5cSMarek Behún 			*lane = MV88E6390_PORT9_LANE0;
41417deaf5cSMarek Behún 			return 0;
41517deaf5cSMarek Behún 		}
41617deaf5cSMarek Behún 		break;
417a8c01c0dSAndrew Lunn 	case 10:
418a8c01c0dSAndrew Lunn 		if (cmode_port10 == MV88E6XXX_PORT_STS_CMODE_1000BASE_X ||
419a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_SGMII ||
420a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_2500BASEX ||
421a8c01c0dSAndrew Lunn 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_XAUI ||
42217deaf5cSMarek Behún 		    cmode_port10 == MV88E6XXX_PORT_STS_CMODE_RXAUI) {
42317deaf5cSMarek Behún 			*lane = MV88E6390_PORT10_LANE0;
42417deaf5cSMarek Behún 			return 0;
425a8c01c0dSAndrew Lunn 		}
42617deaf5cSMarek Behún 		break;
42717deaf5cSMarek Behún 	default:
42817deaf5cSMarek Behún 		break;
42917deaf5cSMarek Behún 	}
43017deaf5cSMarek Behún 
43117deaf5cSMarek Behún 	return -ENODEV;
432a8c01c0dSAndrew Lunn }
433a8c01c0dSAndrew Lunn 
4346335e9f2SAndrew Lunn /* Set the power on/off for 10GBASE-R and 10GBASE-X4/X2 */
43517deaf5cSMarek Behún static int mv88e6390_serdes_power_10g(struct mv88e6xxx_chip *chip, u8 lane,
43623ef57d8SAndrew Lunn 				      bool on)
4376335e9f2SAndrew Lunn {
4386335e9f2SAndrew Lunn 	u16 val, new_val;
4396335e9f2SAndrew Lunn 	int err;
4406335e9f2SAndrew Lunn 
441e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
442e6891c76SAndrew Lunn 				    MV88E6390_PCS_CONTROL_1, &val);
443e6891c76SAndrew Lunn 
4446335e9f2SAndrew Lunn 	if (err)
4456335e9f2SAndrew Lunn 		return err;
4466335e9f2SAndrew Lunn 
4476335e9f2SAndrew Lunn 	if (on)
4486335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_PCS_CONTROL_1_RESET |
4496335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_LOOPBACK |
4506335e9f2SAndrew Lunn 				  MV88E6390_PCS_CONTROL_1_PDOWN);
4516335e9f2SAndrew Lunn 	else
4526335e9f2SAndrew Lunn 		new_val = val | MV88E6390_PCS_CONTROL_1_PDOWN;
4536335e9f2SAndrew Lunn 
4546335e9f2SAndrew Lunn 	if (val != new_val)
455e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
456e6891c76SAndrew Lunn 					     MV88E6390_PCS_CONTROL_1, new_val);
4576335e9f2SAndrew Lunn 
4586335e9f2SAndrew Lunn 	return err;
4596335e9f2SAndrew Lunn }
4606335e9f2SAndrew Lunn 
461a8c01c0dSAndrew Lunn /* Set the power on/off for SGMII and 1000Base-X */
46217deaf5cSMarek Behún static int mv88e6390_serdes_power_sgmii(struct mv88e6xxx_chip *chip, u8 lane,
4636335e9f2SAndrew Lunn 					bool on)
4646335e9f2SAndrew Lunn {
4656335e9f2SAndrew Lunn 	u16 val, new_val;
4666335e9f2SAndrew Lunn 	int err;
4676335e9f2SAndrew Lunn 
468e6891c76SAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
469e6891c76SAndrew Lunn 				    MV88E6390_SGMII_CONTROL, &val);
4706335e9f2SAndrew Lunn 	if (err)
4716335e9f2SAndrew Lunn 		return err;
4726335e9f2SAndrew Lunn 
4736335e9f2SAndrew Lunn 	if (on)
4746335e9f2SAndrew Lunn 		new_val = val & ~(MV88E6390_SGMII_CONTROL_RESET |
4756335e9f2SAndrew Lunn 				  MV88E6390_SGMII_CONTROL_LOOPBACK |
4766335e9f2SAndrew Lunn 				  MV88E6390_SGMII_CONTROL_PDOWN);
4776335e9f2SAndrew Lunn 	else
4786335e9f2SAndrew Lunn 		new_val = val | MV88E6390_SGMII_CONTROL_PDOWN;
4796335e9f2SAndrew Lunn 
4806335e9f2SAndrew Lunn 	if (val != new_val)
481e6891c76SAndrew Lunn 		err = mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
482e6891c76SAndrew Lunn 					     MV88E6390_SGMII_CONTROL, new_val);
4836335e9f2SAndrew Lunn 
4846335e9f2SAndrew Lunn 	return err;
4856335e9f2SAndrew Lunn }
4866335e9f2SAndrew Lunn 
487d3cf7d8fSMarek Behún int mv88e6390_serdes_power(struct mv88e6xxx_chip *chip, int port, bool on)
4886335e9f2SAndrew Lunn {
4892d2e1dd2SAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
490d3cf7d8fSMarek Behún 	u8 lane;
491d3cf7d8fSMarek Behún 	int err;
492d3cf7d8fSMarek Behún 
493d3cf7d8fSMarek Behún 	err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
494d3cf7d8fSMarek Behún 	if (err) {
495d3cf7d8fSMarek Behún 		if (err == -ENODEV)
496d3cf7d8fSMarek Behún 			err = 0;
497d3cf7d8fSMarek Behún 		return err;
498d3cf7d8fSMarek Behún 	}
4996335e9f2SAndrew Lunn 
500a8c01c0dSAndrew Lunn 	switch (cmode) {
501a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
502a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
503f8236a08SAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
50423ef57d8SAndrew Lunn 		return mv88e6390_serdes_power_sgmii(chip, lane, on);
505a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_XAUI:
506a8c01c0dSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_RXAUI:
50723ef57d8SAndrew Lunn 		return mv88e6390_serdes_power_10g(chip, lane, on);
508a8c01c0dSAndrew Lunn 	}
509a8c01c0dSAndrew Lunn 
510a8c01c0dSAndrew Lunn 	return 0;
511a8c01c0dSAndrew Lunn }
512a8c01c0dSAndrew Lunn 
513efd1ba6aSAndrew Lunn static void mv88e6390_serdes_irq_link_sgmii(struct mv88e6xxx_chip *chip,
51417deaf5cSMarek Behún 					    int port, u8 lane)
515efd1ba6aSAndrew Lunn {
5164e6da796SMarek Behún 	u8 cmode = chip->ports[port].cmode;
517efd1ba6aSAndrew Lunn 	struct dsa_switch *ds = chip->ds;
51872d8b4fdSHeiner Kallweit 	int duplex = DUPLEX_UNKNOWN;
51972d8b4fdSHeiner Kallweit 	int speed = SPEED_UNKNOWN;
5204e6da796SMarek Behún 	phy_interface_t mode;
52172d8b4fdSHeiner Kallweit 	int link, err;
522efd1ba6aSAndrew Lunn 	u16 status;
523efd1ba6aSAndrew Lunn 
52472d8b4fdSHeiner Kallweit 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
52572d8b4fdSHeiner Kallweit 				    MV88E6390_SGMII_PHY_STATUS, &status);
52672d8b4fdSHeiner Kallweit 	if (err) {
52772d8b4fdSHeiner Kallweit 		dev_err(chip->dev, "can't read SGMII PHY status: %d\n", err);
52872d8b4fdSHeiner Kallweit 		return;
52972d8b4fdSHeiner Kallweit 	}
530efd1ba6aSAndrew Lunn 
53172d8b4fdSHeiner Kallweit 	link = status & MV88E6390_SGMII_PHY_STATUS_LINK ?
53272d8b4fdSHeiner Kallweit 	       LINK_FORCED_UP : LINK_FORCED_DOWN;
533efd1ba6aSAndrew Lunn 
53472d8b4fdSHeiner Kallweit 	if (status & MV88E6390_SGMII_PHY_STATUS_SPD_DPL_VALID) {
53572d8b4fdSHeiner Kallweit 		duplex = status & MV88E6390_SGMII_PHY_STATUS_DUPLEX_FULL ?
53672d8b4fdSHeiner Kallweit 			 DUPLEX_FULL : DUPLEX_HALF;
53772d8b4fdSHeiner Kallweit 
53872d8b4fdSHeiner Kallweit 		switch (status & MV88E6390_SGMII_PHY_STATUS_SPEED_MASK) {
53972d8b4fdSHeiner Kallweit 		case MV88E6390_SGMII_PHY_STATUS_SPEED_1000:
5404e6da796SMarek Behún 			if (cmode == MV88E6XXX_PORT_STS_CMODE_2500BASEX)
5414e6da796SMarek Behún 				speed = SPEED_2500;
5424e6da796SMarek Behún 			else
54372d8b4fdSHeiner Kallweit 				speed = SPEED_1000;
54472d8b4fdSHeiner Kallweit 			break;
54572d8b4fdSHeiner Kallweit 		case MV88E6390_SGMII_PHY_STATUS_SPEED_100:
54672d8b4fdSHeiner Kallweit 			speed = SPEED_100;
54772d8b4fdSHeiner Kallweit 			break;
54872d8b4fdSHeiner Kallweit 		case MV88E6390_SGMII_PHY_STATUS_SPEED_10:
54972d8b4fdSHeiner Kallweit 			speed = SPEED_10;
55072d8b4fdSHeiner Kallweit 			break;
55172d8b4fdSHeiner Kallweit 		default:
55272d8b4fdSHeiner Kallweit 			dev_err(chip->dev, "invalid PHY speed\n");
55372d8b4fdSHeiner Kallweit 			return;
55472d8b4fdSHeiner Kallweit 		}
55572d8b4fdSHeiner Kallweit 	}
55672d8b4fdSHeiner Kallweit 
5574e6da796SMarek Behún 	switch (cmode) {
5584e6da796SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
5594e6da796SMarek Behún 		mode = PHY_INTERFACE_MODE_SGMII;
5604e6da796SMarek Behún 		break;
5614e6da796SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
5624e6da796SMarek Behún 		mode = PHY_INTERFACE_MODE_1000BASEX;
5634e6da796SMarek Behún 		break;
5644e6da796SMarek Behún 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
5654e6da796SMarek Behún 		mode = PHY_INTERFACE_MODE_2500BASEX;
5664e6da796SMarek Behún 		break;
5674e6da796SMarek Behún 	default:
5684e6da796SMarek Behún 		mode = PHY_INTERFACE_MODE_NA;
5694e6da796SMarek Behún 	}
5704e6da796SMarek Behún 
57172d8b4fdSHeiner Kallweit 	err = mv88e6xxx_port_setup_mac(chip, port, link, speed, duplex,
5724e6da796SMarek Behún 				       PAUSE_OFF, mode);
57372d8b4fdSHeiner Kallweit 	if (err)
57472d8b4fdSHeiner Kallweit 		dev_err(chip->dev, "can't propagate PHY settings to MAC: %d\n",
57572d8b4fdSHeiner Kallweit 			err);
57672d8b4fdSHeiner Kallweit 	else
57772d8b4fdSHeiner Kallweit 		dsa_port_phylink_mac_change(ds, port, link == LINK_FORCED_UP);
578efd1ba6aSAndrew Lunn }
579efd1ba6aSAndrew Lunn 
580efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_enable_sgmii(struct mv88e6xxx_chip *chip,
58117deaf5cSMarek Behún 					     u8 lane)
582efd1ba6aSAndrew Lunn {
583efd1ba6aSAndrew Lunn 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
584efd1ba6aSAndrew Lunn 				      MV88E6390_SGMII_INT_ENABLE,
585efd1ba6aSAndrew Lunn 				      MV88E6390_SGMII_INT_LINK_DOWN |
586efd1ba6aSAndrew Lunn 				      MV88E6390_SGMII_INT_LINK_UP);
587efd1ba6aSAndrew Lunn }
588efd1ba6aSAndrew Lunn 
589efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_disable_sgmii(struct mv88e6xxx_chip *chip,
59017deaf5cSMarek Behún 					      u8 lane)
591efd1ba6aSAndrew Lunn {
592efd1ba6aSAndrew Lunn 	return mv88e6390_serdes_write(chip, lane, MDIO_MMD_PHYXS,
593efd1ba6aSAndrew Lunn 				      MV88E6390_SGMII_INT_ENABLE, 0);
594efd1ba6aSAndrew Lunn }
595efd1ba6aSAndrew Lunn 
596efd1ba6aSAndrew Lunn int mv88e6390_serdes_irq_enable(struct mv88e6xxx_chip *chip, int port,
59717deaf5cSMarek Behún 				u8 lane)
598efd1ba6aSAndrew Lunn {
599efd1ba6aSAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
600efd1ba6aSAndrew Lunn 	int err = 0;
601efd1ba6aSAndrew Lunn 
602efd1ba6aSAndrew Lunn 	switch (cmode) {
603efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
604efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
605efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
606efd1ba6aSAndrew Lunn 		err = mv88e6390_serdes_irq_enable_sgmii(chip, lane);
607efd1ba6aSAndrew Lunn 	}
608efd1ba6aSAndrew Lunn 
609efd1ba6aSAndrew Lunn 	return err;
610efd1ba6aSAndrew Lunn }
611efd1ba6aSAndrew Lunn 
612efd1ba6aSAndrew Lunn int mv88e6390_serdes_irq_disable(struct mv88e6xxx_chip *chip, int port,
61317deaf5cSMarek Behún 				 u8 lane)
614efd1ba6aSAndrew Lunn {
615efd1ba6aSAndrew Lunn 	u8 cmode = chip->ports[port].cmode;
616efd1ba6aSAndrew Lunn 	int err = 0;
617efd1ba6aSAndrew Lunn 
618efd1ba6aSAndrew Lunn 	switch (cmode) {
619efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
620efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
621efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
622efd1ba6aSAndrew Lunn 		err = mv88e6390_serdes_irq_disable_sgmii(chip, lane);
623efd1ba6aSAndrew Lunn 	}
624efd1ba6aSAndrew Lunn 
625efd1ba6aSAndrew Lunn 	return err;
626efd1ba6aSAndrew Lunn }
627efd1ba6aSAndrew Lunn 
628efd1ba6aSAndrew Lunn static int mv88e6390_serdes_irq_status_sgmii(struct mv88e6xxx_chip *chip,
62917deaf5cSMarek Behún 					     u8 lane, u16 *status)
630efd1ba6aSAndrew Lunn {
631efd1ba6aSAndrew Lunn 	int err;
632efd1ba6aSAndrew Lunn 
633efd1ba6aSAndrew Lunn 	err = mv88e6390_serdes_read(chip, lane, MDIO_MMD_PHYXS,
634efd1ba6aSAndrew Lunn 				    MV88E6390_SGMII_INT_STATUS, status);
635efd1ba6aSAndrew Lunn 
636efd1ba6aSAndrew Lunn 	return err;
637efd1ba6aSAndrew Lunn }
638efd1ba6aSAndrew Lunn 
639efd1ba6aSAndrew Lunn static irqreturn_t mv88e6390_serdes_thread_fn(int irq, void *dev_id)
640efd1ba6aSAndrew Lunn {
641efd1ba6aSAndrew Lunn 	struct mv88e6xxx_port *port = dev_id;
642efd1ba6aSAndrew Lunn 	struct mv88e6xxx_chip *chip = port->chip;
643efd1ba6aSAndrew Lunn 	irqreturn_t ret = IRQ_NONE;
644efd1ba6aSAndrew Lunn 	u8 cmode = port->cmode;
645efd1ba6aSAndrew Lunn 	u16 status;
646efd1ba6aSAndrew Lunn 	int err;
64717deaf5cSMarek Behún 	u8 lane;
648efd1ba6aSAndrew Lunn 
64917deaf5cSMarek Behún 	mv88e6xxx_serdes_get_lane(chip, port->port, &lane);
650efd1ba6aSAndrew Lunn 
651c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
652efd1ba6aSAndrew Lunn 
653efd1ba6aSAndrew Lunn 	switch (cmode) {
654efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_SGMII:
655efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_1000BASE_X:
656efd1ba6aSAndrew Lunn 	case MV88E6XXX_PORT_STS_CMODE_2500BASEX:
657efd1ba6aSAndrew Lunn 		err = mv88e6390_serdes_irq_status_sgmii(chip, lane, &status);
658efd1ba6aSAndrew Lunn 		if (err)
659efd1ba6aSAndrew Lunn 			goto out;
6606feddb49SDan Carpenter 		if (status & (MV88E6390_SGMII_INT_LINK_DOWN |
661efd1ba6aSAndrew Lunn 			      MV88E6390_SGMII_INT_LINK_UP)) {
662efd1ba6aSAndrew Lunn 			ret = IRQ_HANDLED;
663efd1ba6aSAndrew Lunn 			mv88e6390_serdes_irq_link_sgmii(chip, port->port, lane);
664efd1ba6aSAndrew Lunn 		}
665efd1ba6aSAndrew Lunn 	}
666efd1ba6aSAndrew Lunn out:
667c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
668efd1ba6aSAndrew Lunn 
669efd1ba6aSAndrew Lunn 	return ret;
670efd1ba6aSAndrew Lunn }
671efd1ba6aSAndrew Lunn 
672d3cf7d8fSMarek Behún int mv88e6390_serdes_irq_setup(struct mv88e6xxx_chip *chip, int port)
673efd1ba6aSAndrew Lunn {
674efd1ba6aSAndrew Lunn 	int err;
67517deaf5cSMarek Behún 	u8 lane;
676efd1ba6aSAndrew Lunn 
67717deaf5cSMarek Behún 	err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
67817deaf5cSMarek Behún 	if (err) {
67917deaf5cSMarek Behún 		if (err == -ENODEV)
68017deaf5cSMarek Behún 			err = 0;
68117deaf5cSMarek Behún 		return err;
68217deaf5cSMarek Behún 	}
683efd1ba6aSAndrew Lunn 
684efd1ba6aSAndrew Lunn 	chip->ports[port].serdes_irq = irq_find_mapping(chip->g2_irq.domain,
685efd1ba6aSAndrew Lunn 							port);
686efd1ba6aSAndrew Lunn 	if (chip->ports[port].serdes_irq < 0) {
687efd1ba6aSAndrew Lunn 		dev_err(chip->dev, "Unable to map SERDES irq: %d\n",
688efd1ba6aSAndrew Lunn 			chip->ports[port].serdes_irq);
689efd1ba6aSAndrew Lunn 		return chip->ports[port].serdes_irq;
690efd1ba6aSAndrew Lunn 	}
691efd1ba6aSAndrew Lunn 
692efd1ba6aSAndrew Lunn 	/* Requesting the IRQ will trigger irq callbacks. So we cannot
693efd1ba6aSAndrew Lunn 	 * hold the reg_lock.
694efd1ba6aSAndrew Lunn 	 */
695c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
696efd1ba6aSAndrew Lunn 	err = request_threaded_irq(chip->ports[port].serdes_irq, NULL,
697efd1ba6aSAndrew Lunn 				   mv88e6390_serdes_thread_fn,
698efd1ba6aSAndrew Lunn 				   IRQF_ONESHOT, "mv88e6xxx-serdes",
699efd1ba6aSAndrew Lunn 				   &chip->ports[port]);
700c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
701efd1ba6aSAndrew Lunn 
702efd1ba6aSAndrew Lunn 	if (err) {
703efd1ba6aSAndrew Lunn 		dev_err(chip->dev, "Unable to request SERDES interrupt: %d\n",
704efd1ba6aSAndrew Lunn 			err);
705efd1ba6aSAndrew Lunn 		return err;
706efd1ba6aSAndrew Lunn 	}
707efd1ba6aSAndrew Lunn 
708efd1ba6aSAndrew Lunn 	return mv88e6390_serdes_irq_enable(chip, port, lane);
709efd1ba6aSAndrew Lunn }
710efd1ba6aSAndrew Lunn 
711d3cf7d8fSMarek Behún void mv88e6390_serdes_irq_free(struct mv88e6xxx_chip *chip, int port)
712efd1ba6aSAndrew Lunn {
71317deaf5cSMarek Behún 	int err;
71417deaf5cSMarek Behún 	u8 lane;
715efd1ba6aSAndrew Lunn 
71617deaf5cSMarek Behún 	err = mv88e6xxx_serdes_get_lane(chip, port, &lane);
71717deaf5cSMarek Behún 	if (err) {
71817deaf5cSMarek Behún 		if (err != -ENODEV)
71917deaf5cSMarek Behún 			dev_err(chip->dev, "Unable to free SERDES irq: %d\n",
72017deaf5cSMarek Behún 				err);
721efd1ba6aSAndrew Lunn 		return;
72217deaf5cSMarek Behún 	}
723efd1ba6aSAndrew Lunn 
724efd1ba6aSAndrew Lunn 	mv88e6390_serdes_irq_disable(chip, port, lane);
725efd1ba6aSAndrew Lunn 
726efd1ba6aSAndrew Lunn 	/* Freeing the IRQ will trigger irq callbacks. So we cannot
727efd1ba6aSAndrew Lunn 	 * hold the reg_lock.
728efd1ba6aSAndrew Lunn 	 */
729c9acece0SRasmus Villemoes 	mv88e6xxx_reg_unlock(chip);
730efd1ba6aSAndrew Lunn 	free_irq(chip->ports[port].serdes_irq, &chip->ports[port]);
731c9acece0SRasmus Villemoes 	mv88e6xxx_reg_lock(chip);
732734447d4SAndrew Lunn 
733734447d4SAndrew Lunn 	chip->ports[port].serdes_irq = 0;
734efd1ba6aSAndrew Lunn }
735