xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/chip.c (revision 1b17aedf)
1fad09c73SVivien Didelot /*
2fad09c73SVivien Didelot  * Marvell 88e6xxx Ethernet switch single-chip support
3fad09c73SVivien Didelot  *
4fad09c73SVivien Didelot  * Copyright (c) 2008 Marvell Semiconductor
5fad09c73SVivien Didelot  *
6fad09c73SVivien Didelot  * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
7fad09c73SVivien Didelot  *
84333d619SVivien Didelot  * Copyright (c) 2016-2017 Savoir-faire Linux Inc.
94333d619SVivien Didelot  *	Vivien Didelot <vivien.didelot@savoirfairelinux.com>
104333d619SVivien Didelot  *
11fad09c73SVivien Didelot  * This program is free software; you can redistribute it and/or modify
12fad09c73SVivien Didelot  * it under the terms of the GNU General Public License as published by
13fad09c73SVivien Didelot  * the Free Software Foundation; either version 2 of the License, or
14fad09c73SVivien Didelot  * (at your option) any later version.
15fad09c73SVivien Didelot  */
16fad09c73SVivien Didelot 
17fad09c73SVivien Didelot #include <linux/delay.h>
18fad09c73SVivien Didelot #include <linux/etherdevice.h>
19fad09c73SVivien Didelot #include <linux/ethtool.h>
20fad09c73SVivien Didelot #include <linux/if_bridge.h>
21dc30c35bSAndrew Lunn #include <linux/interrupt.h>
22dc30c35bSAndrew Lunn #include <linux/irq.h>
23dc30c35bSAndrew Lunn #include <linux/irqdomain.h>
24fad09c73SVivien Didelot #include <linux/jiffies.h>
25fad09c73SVivien Didelot #include <linux/list.h>
26fad09c73SVivien Didelot #include <linux/mdio.h>
27fad09c73SVivien Didelot #include <linux/module.h>
28fad09c73SVivien Didelot #include <linux/of_device.h>
29dc30c35bSAndrew Lunn #include <linux/of_irq.h>
30fad09c73SVivien Didelot #include <linux/of_mdio.h>
31fad09c73SVivien Didelot #include <linux/netdevice.h>
32fad09c73SVivien Didelot #include <linux/gpio/consumer.h>
33fad09c73SVivien Didelot #include <linux/phy.h>
34fad09c73SVivien Didelot #include <net/dsa.h>
35ec561276SVivien Didelot 
36fad09c73SVivien Didelot #include "mv88e6xxx.h"
37a935c052SVivien Didelot #include "global1.h"
38ec561276SVivien Didelot #include "global2.h"
3910fa5bfcSAndrew Lunn #include "phy.h"
4018abed21SVivien Didelot #include "port.h"
416d91782fSAndrew Lunn #include "serdes.h"
42fad09c73SVivien Didelot 
43fad09c73SVivien Didelot static void assert_reg_lock(struct mv88e6xxx_chip *chip)
44fad09c73SVivien Didelot {
45fad09c73SVivien Didelot 	if (unlikely(!mutex_is_locked(&chip->reg_lock))) {
46fad09c73SVivien Didelot 		dev_err(chip->dev, "Switch registers lock not held!\n");
47fad09c73SVivien Didelot 		dump_stack();
48fad09c73SVivien Didelot 	}
49fad09c73SVivien Didelot }
50fad09c73SVivien Didelot 
51fad09c73SVivien Didelot /* The switch ADDR[4:1] configuration pins define the chip SMI device address
52fad09c73SVivien Didelot  * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
53fad09c73SVivien Didelot  *
54fad09c73SVivien Didelot  * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
55fad09c73SVivien Didelot  * is the only device connected to the SMI master. In this mode it responds to
56fad09c73SVivien Didelot  * all 32 possible SMI addresses, and thus maps directly the internal devices.
57fad09c73SVivien Didelot  *
58fad09c73SVivien Didelot  * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
59fad09c73SVivien Didelot  * multiple devices to share the SMI interface. In this mode it responds to only
60fad09c73SVivien Didelot  * 2 registers, used to indirectly access the internal SMI devices.
61fad09c73SVivien Didelot  */
62fad09c73SVivien Didelot 
63fad09c73SVivien Didelot static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
64fad09c73SVivien Didelot 			      int addr, int reg, u16 *val)
65fad09c73SVivien Didelot {
66fad09c73SVivien Didelot 	if (!chip->smi_ops)
67fad09c73SVivien Didelot 		return -EOPNOTSUPP;
68fad09c73SVivien Didelot 
69fad09c73SVivien Didelot 	return chip->smi_ops->read(chip, addr, reg, val);
70fad09c73SVivien Didelot }
71fad09c73SVivien Didelot 
72fad09c73SVivien Didelot static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
73fad09c73SVivien Didelot 			       int addr, int reg, u16 val)
74fad09c73SVivien Didelot {
75fad09c73SVivien Didelot 	if (!chip->smi_ops)
76fad09c73SVivien Didelot 		return -EOPNOTSUPP;
77fad09c73SVivien Didelot 
78fad09c73SVivien Didelot 	return chip->smi_ops->write(chip, addr, reg, val);
79fad09c73SVivien Didelot }
80fad09c73SVivien Didelot 
81fad09c73SVivien Didelot static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
82fad09c73SVivien Didelot 					  int addr, int reg, u16 *val)
83fad09c73SVivien Didelot {
84fad09c73SVivien Didelot 	int ret;
85fad09c73SVivien Didelot 
86fad09c73SVivien Didelot 	ret = mdiobus_read_nested(chip->bus, addr, reg);
87fad09c73SVivien Didelot 	if (ret < 0)
88fad09c73SVivien Didelot 		return ret;
89fad09c73SVivien Didelot 
90fad09c73SVivien Didelot 	*val = ret & 0xffff;
91fad09c73SVivien Didelot 
92fad09c73SVivien Didelot 	return 0;
93fad09c73SVivien Didelot }
94fad09c73SVivien Didelot 
95fad09c73SVivien Didelot static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
96fad09c73SVivien Didelot 					   int addr, int reg, u16 val)
97fad09c73SVivien Didelot {
98fad09c73SVivien Didelot 	int ret;
99fad09c73SVivien Didelot 
100fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, addr, reg, val);
101fad09c73SVivien Didelot 	if (ret < 0)
102fad09c73SVivien Didelot 		return ret;
103fad09c73SVivien Didelot 
104fad09c73SVivien Didelot 	return 0;
105fad09c73SVivien Didelot }
106fad09c73SVivien Didelot 
107c08026abSVivien Didelot static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = {
108fad09c73SVivien Didelot 	.read = mv88e6xxx_smi_single_chip_read,
109fad09c73SVivien Didelot 	.write = mv88e6xxx_smi_single_chip_write,
110fad09c73SVivien Didelot };
111fad09c73SVivien Didelot 
112fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
113fad09c73SVivien Didelot {
114fad09c73SVivien Didelot 	int ret;
115fad09c73SVivien Didelot 	int i;
116fad09c73SVivien Didelot 
117fad09c73SVivien Didelot 	for (i = 0; i < 16; i++) {
118fad09c73SVivien Didelot 		ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
119fad09c73SVivien Didelot 		if (ret < 0)
120fad09c73SVivien Didelot 			return ret;
121fad09c73SVivien Didelot 
122fad09c73SVivien Didelot 		if ((ret & SMI_CMD_BUSY) == 0)
123fad09c73SVivien Didelot 			return 0;
124fad09c73SVivien Didelot 	}
125fad09c73SVivien Didelot 
126fad09c73SVivien Didelot 	return -ETIMEDOUT;
127fad09c73SVivien Didelot }
128fad09c73SVivien Didelot 
129fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
130fad09c73SVivien Didelot 					 int addr, int reg, u16 *val)
131fad09c73SVivien Didelot {
132fad09c73SVivien Didelot 	int ret;
133fad09c73SVivien Didelot 
134fad09c73SVivien Didelot 	/* Wait for the bus to become free. */
135fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
136fad09c73SVivien Didelot 	if (ret < 0)
137fad09c73SVivien Didelot 		return ret;
138fad09c73SVivien Didelot 
139fad09c73SVivien Didelot 	/* Transmit the read command. */
140fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
141fad09c73SVivien Didelot 				   SMI_CMD_OP_22_READ | (addr << 5) | reg);
142fad09c73SVivien Didelot 	if (ret < 0)
143fad09c73SVivien Didelot 		return ret;
144fad09c73SVivien Didelot 
145fad09c73SVivien Didelot 	/* Wait for the read command to complete. */
146fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
147fad09c73SVivien Didelot 	if (ret < 0)
148fad09c73SVivien Didelot 		return ret;
149fad09c73SVivien Didelot 
150fad09c73SVivien Didelot 	/* Read the data. */
151fad09c73SVivien Didelot 	ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
152fad09c73SVivien Didelot 	if (ret < 0)
153fad09c73SVivien Didelot 		return ret;
154fad09c73SVivien Didelot 
155fad09c73SVivien Didelot 	*val = ret & 0xffff;
156fad09c73SVivien Didelot 
157fad09c73SVivien Didelot 	return 0;
158fad09c73SVivien Didelot }
159fad09c73SVivien Didelot 
160fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
161fad09c73SVivien Didelot 					  int addr, int reg, u16 val)
162fad09c73SVivien Didelot {
163fad09c73SVivien Didelot 	int ret;
164fad09c73SVivien Didelot 
165fad09c73SVivien Didelot 	/* Wait for the bus to become free. */
166fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
167fad09c73SVivien Didelot 	if (ret < 0)
168fad09c73SVivien Didelot 		return ret;
169fad09c73SVivien Didelot 
170fad09c73SVivien Didelot 	/* Transmit the data to write. */
171fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
172fad09c73SVivien Didelot 	if (ret < 0)
173fad09c73SVivien Didelot 		return ret;
174fad09c73SVivien Didelot 
175fad09c73SVivien Didelot 	/* Transmit the write command. */
176fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
177fad09c73SVivien Didelot 				   SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
178fad09c73SVivien Didelot 	if (ret < 0)
179fad09c73SVivien Didelot 		return ret;
180fad09c73SVivien Didelot 
181fad09c73SVivien Didelot 	/* Wait for the write command to complete. */
182fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
183fad09c73SVivien Didelot 	if (ret < 0)
184fad09c73SVivien Didelot 		return ret;
185fad09c73SVivien Didelot 
186fad09c73SVivien Didelot 	return 0;
187fad09c73SVivien Didelot }
188fad09c73SVivien Didelot 
189c08026abSVivien Didelot static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = {
190fad09c73SVivien Didelot 	.read = mv88e6xxx_smi_multi_chip_read,
191fad09c73SVivien Didelot 	.write = mv88e6xxx_smi_multi_chip_write,
192fad09c73SVivien Didelot };
193fad09c73SVivien Didelot 
194ec561276SVivien Didelot int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
195fad09c73SVivien Didelot {
196fad09c73SVivien Didelot 	int err;
197fad09c73SVivien Didelot 
198fad09c73SVivien Didelot 	assert_reg_lock(chip);
199fad09c73SVivien Didelot 
200fad09c73SVivien Didelot 	err = mv88e6xxx_smi_read(chip, addr, reg, val);
201fad09c73SVivien Didelot 	if (err)
202fad09c73SVivien Didelot 		return err;
203fad09c73SVivien Didelot 
204fad09c73SVivien Didelot 	dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
205fad09c73SVivien Didelot 		addr, reg, *val);
206fad09c73SVivien Didelot 
207fad09c73SVivien Didelot 	return 0;
208fad09c73SVivien Didelot }
209fad09c73SVivien Didelot 
210ec561276SVivien Didelot int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
211fad09c73SVivien Didelot {
212fad09c73SVivien Didelot 	int err;
213fad09c73SVivien Didelot 
214fad09c73SVivien Didelot 	assert_reg_lock(chip);
215fad09c73SVivien Didelot 
216fad09c73SVivien Didelot 	err = mv88e6xxx_smi_write(chip, addr, reg, val);
217fad09c73SVivien Didelot 	if (err)
218fad09c73SVivien Didelot 		return err;
219fad09c73SVivien Didelot 
220fad09c73SVivien Didelot 	dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
221fad09c73SVivien Didelot 		addr, reg, val);
222fad09c73SVivien Didelot 
223fad09c73SVivien Didelot 	return 0;
224fad09c73SVivien Didelot }
225fad09c73SVivien Didelot 
22610fa5bfcSAndrew Lunn struct mii_bus *mv88e6xxx_default_mdio_bus(struct mv88e6xxx_chip *chip)
227a3c53be5SAndrew Lunn {
228a3c53be5SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus;
229a3c53be5SAndrew Lunn 
230a3c53be5SAndrew Lunn 	mdio_bus = list_first_entry(&chip->mdios, struct mv88e6xxx_mdio_bus,
231a3c53be5SAndrew Lunn 				    list);
232a3c53be5SAndrew Lunn 	if (!mdio_bus)
233a3c53be5SAndrew Lunn 		return NULL;
234a3c53be5SAndrew Lunn 
235a3c53be5SAndrew Lunn 	return mdio_bus->bus;
236a3c53be5SAndrew Lunn }
237a3c53be5SAndrew Lunn 
238dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_mask(struct irq_data *d)
239dc30c35bSAndrew Lunn {
240dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
241dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
242dc30c35bSAndrew Lunn 
243dc30c35bSAndrew Lunn 	chip->g1_irq.masked |= (1 << n);
244dc30c35bSAndrew Lunn }
245dc30c35bSAndrew Lunn 
246dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
247dc30c35bSAndrew Lunn {
248dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
249dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
250dc30c35bSAndrew Lunn 
251dc30c35bSAndrew Lunn 	chip->g1_irq.masked &= ~(1 << n);
252dc30c35bSAndrew Lunn }
253dc30c35bSAndrew Lunn 
254dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
255dc30c35bSAndrew Lunn {
256dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
257dc30c35bSAndrew Lunn 	unsigned int nhandled = 0;
258dc30c35bSAndrew Lunn 	unsigned int sub_irq;
259dc30c35bSAndrew Lunn 	unsigned int n;
260dc30c35bSAndrew Lunn 	u16 reg;
261dc30c35bSAndrew Lunn 	int err;
262dc30c35bSAndrew Lunn 
263dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
264dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
265dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
266dc30c35bSAndrew Lunn 
267dc30c35bSAndrew Lunn 	if (err)
268dc30c35bSAndrew Lunn 		goto out;
269dc30c35bSAndrew Lunn 
270dc30c35bSAndrew Lunn 	for (n = 0; n < chip->g1_irq.nirqs; ++n) {
271dc30c35bSAndrew Lunn 		if (reg & (1 << n)) {
272dc30c35bSAndrew Lunn 			sub_irq = irq_find_mapping(chip->g1_irq.domain, n);
273dc30c35bSAndrew Lunn 			handle_nested_irq(sub_irq);
274dc30c35bSAndrew Lunn 			++nhandled;
275dc30c35bSAndrew Lunn 		}
276dc30c35bSAndrew Lunn 	}
277dc30c35bSAndrew Lunn out:
278dc30c35bSAndrew Lunn 	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
279dc30c35bSAndrew Lunn }
280dc30c35bSAndrew Lunn 
281dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
282dc30c35bSAndrew Lunn {
283dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
284dc30c35bSAndrew Lunn 
285dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
286dc30c35bSAndrew Lunn }
287dc30c35bSAndrew Lunn 
288dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d)
289dc30c35bSAndrew Lunn {
290dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
291dc30c35bSAndrew Lunn 	u16 mask = GENMASK(chip->g1_irq.nirqs, 0);
292dc30c35bSAndrew Lunn 	u16 reg;
293dc30c35bSAndrew Lunn 	int err;
294dc30c35bSAndrew Lunn 
295dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
296dc30c35bSAndrew Lunn 	if (err)
297dc30c35bSAndrew Lunn 		goto out;
298dc30c35bSAndrew Lunn 
299dc30c35bSAndrew Lunn 	reg &= ~mask;
300dc30c35bSAndrew Lunn 	reg |= (~chip->g1_irq.masked & mask);
301dc30c35bSAndrew Lunn 
302dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
303dc30c35bSAndrew Lunn 	if (err)
304dc30c35bSAndrew Lunn 		goto out;
305dc30c35bSAndrew Lunn 
306dc30c35bSAndrew Lunn out:
307dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
308dc30c35bSAndrew Lunn }
309dc30c35bSAndrew Lunn 
310dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g1_irq_chip = {
311dc30c35bSAndrew Lunn 	.name			= "mv88e6xxx-g1",
312dc30c35bSAndrew Lunn 	.irq_mask		= mv88e6xxx_g1_irq_mask,
313dc30c35bSAndrew Lunn 	.irq_unmask		= mv88e6xxx_g1_irq_unmask,
314dc30c35bSAndrew Lunn 	.irq_bus_lock		= mv88e6xxx_g1_irq_bus_lock,
315dc30c35bSAndrew Lunn 	.irq_bus_sync_unlock	= mv88e6xxx_g1_irq_bus_sync_unlock,
316dc30c35bSAndrew Lunn };
317dc30c35bSAndrew Lunn 
318dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d,
319dc30c35bSAndrew Lunn 				       unsigned int irq,
320dc30c35bSAndrew Lunn 				       irq_hw_number_t hwirq)
321dc30c35bSAndrew Lunn {
322dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = d->host_data;
323dc30c35bSAndrew Lunn 
324dc30c35bSAndrew Lunn 	irq_set_chip_data(irq, d->host_data);
325dc30c35bSAndrew Lunn 	irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq);
326dc30c35bSAndrew Lunn 	irq_set_noprobe(irq);
327dc30c35bSAndrew Lunn 
328dc30c35bSAndrew Lunn 	return 0;
329dc30c35bSAndrew Lunn }
330dc30c35bSAndrew Lunn 
331dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
332dc30c35bSAndrew Lunn 	.map	= mv88e6xxx_g1_irq_domain_map,
333dc30c35bSAndrew Lunn 	.xlate	= irq_domain_xlate_twocell,
334dc30c35bSAndrew Lunn };
335dc30c35bSAndrew Lunn 
336dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
337dc30c35bSAndrew Lunn {
338dc30c35bSAndrew Lunn 	int irq, virq;
3393460a577SAndrew Lunn 	u16 mask;
3403460a577SAndrew Lunn 
3413460a577SAndrew Lunn 	mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask);
3423460a577SAndrew Lunn 	mask |= GENMASK(chip->g1_irq.nirqs, 0);
3433460a577SAndrew Lunn 	mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
3443460a577SAndrew Lunn 
3453460a577SAndrew Lunn 	free_irq(chip->irq, chip);
346dc30c35bSAndrew Lunn 
3475edef2f2SAndreas Färber 	for (irq = 0; irq < chip->g1_irq.nirqs; irq++) {
348a3db3d3aSAndrew Lunn 		virq = irq_find_mapping(chip->g1_irq.domain, irq);
349dc30c35bSAndrew Lunn 		irq_dispose_mapping(virq);
350dc30c35bSAndrew Lunn 	}
351dc30c35bSAndrew Lunn 
352a3db3d3aSAndrew Lunn 	irq_domain_remove(chip->g1_irq.domain);
353dc30c35bSAndrew Lunn }
354dc30c35bSAndrew Lunn 
355dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
356dc30c35bSAndrew Lunn {
3573dd0ef05SAndrew Lunn 	int err, irq, virq;
3583dd0ef05SAndrew Lunn 	u16 reg, mask;
359dc30c35bSAndrew Lunn 
360dc30c35bSAndrew Lunn 	chip->g1_irq.nirqs = chip->info->g1_irqs;
361dc30c35bSAndrew Lunn 	chip->g1_irq.domain = irq_domain_add_simple(
362dc30c35bSAndrew Lunn 		NULL, chip->g1_irq.nirqs, 0,
363dc30c35bSAndrew Lunn 		&mv88e6xxx_g1_irq_domain_ops, chip);
364dc30c35bSAndrew Lunn 	if (!chip->g1_irq.domain)
365dc30c35bSAndrew Lunn 		return -ENOMEM;
366dc30c35bSAndrew Lunn 
367dc30c35bSAndrew Lunn 	for (irq = 0; irq < chip->g1_irq.nirqs; irq++)
368dc30c35bSAndrew Lunn 		irq_create_mapping(chip->g1_irq.domain, irq);
369dc30c35bSAndrew Lunn 
370dc30c35bSAndrew Lunn 	chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
371dc30c35bSAndrew Lunn 	chip->g1_irq.masked = ~0;
372dc30c35bSAndrew Lunn 
3733dd0ef05SAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask);
374dc30c35bSAndrew Lunn 	if (err)
3753dd0ef05SAndrew Lunn 		goto out_mapping;
376dc30c35bSAndrew Lunn 
3773dd0ef05SAndrew Lunn 	mask &= ~GENMASK(chip->g1_irq.nirqs, 0);
378dc30c35bSAndrew Lunn 
3793dd0ef05SAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
380dc30c35bSAndrew Lunn 	if (err)
3813dd0ef05SAndrew Lunn 		goto out_disable;
382dc30c35bSAndrew Lunn 
383dc30c35bSAndrew Lunn 	/* Reading the interrupt status clears (most of) them */
384dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
385dc30c35bSAndrew Lunn 	if (err)
3863dd0ef05SAndrew Lunn 		goto out_disable;
387dc30c35bSAndrew Lunn 
388dc30c35bSAndrew Lunn 	err = request_threaded_irq(chip->irq, NULL,
389dc30c35bSAndrew Lunn 				   mv88e6xxx_g1_irq_thread_fn,
390dc30c35bSAndrew Lunn 				   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
391dc30c35bSAndrew Lunn 				   dev_name(chip->dev), chip);
392dc30c35bSAndrew Lunn 	if (err)
3933dd0ef05SAndrew Lunn 		goto out_disable;
394dc30c35bSAndrew Lunn 
395dc30c35bSAndrew Lunn 	return 0;
396dc30c35bSAndrew Lunn 
3973dd0ef05SAndrew Lunn out_disable:
3983dd0ef05SAndrew Lunn 	mask |= GENMASK(chip->g1_irq.nirqs, 0);
3993dd0ef05SAndrew Lunn 	mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
4003dd0ef05SAndrew Lunn 
4013dd0ef05SAndrew Lunn out_mapping:
4023dd0ef05SAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
4033dd0ef05SAndrew Lunn 		virq = irq_find_mapping(chip->g1_irq.domain, irq);
4043dd0ef05SAndrew Lunn 		irq_dispose_mapping(virq);
4053dd0ef05SAndrew Lunn 	}
4063dd0ef05SAndrew Lunn 
4073dd0ef05SAndrew Lunn 	irq_domain_remove(chip->g1_irq.domain);
408dc30c35bSAndrew Lunn 
409dc30c35bSAndrew Lunn 	return err;
410dc30c35bSAndrew Lunn }
411dc30c35bSAndrew Lunn 
412ec561276SVivien Didelot int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
4132d79af6eSVivien Didelot {
4146441e669SAndrew Lunn 	int i;
4152d79af6eSVivien Didelot 
4166441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
4172d79af6eSVivien Didelot 		u16 val;
4182d79af6eSVivien Didelot 		int err;
4192d79af6eSVivien Didelot 
4202d79af6eSVivien Didelot 		err = mv88e6xxx_read(chip, addr, reg, &val);
4212d79af6eSVivien Didelot 		if (err)
4222d79af6eSVivien Didelot 			return err;
4232d79af6eSVivien Didelot 
4242d79af6eSVivien Didelot 		if (!(val & mask))
4252d79af6eSVivien Didelot 			return 0;
4262d79af6eSVivien Didelot 
4272d79af6eSVivien Didelot 		usleep_range(1000, 2000);
4282d79af6eSVivien Didelot 	}
4292d79af6eSVivien Didelot 
43030853553SAndrew Lunn 	dev_err(chip->dev, "Timeout while waiting for switch\n");
4312d79af6eSVivien Didelot 	return -ETIMEDOUT;
4322d79af6eSVivien Didelot }
4332d79af6eSVivien Didelot 
434f22ab641SVivien Didelot /* Indirect write to single pointer-data register with an Update bit */
435ec561276SVivien Didelot int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
436f22ab641SVivien Didelot {
437f22ab641SVivien Didelot 	u16 val;
4380f02b4f7SAndrew Lunn 	int err;
439f22ab641SVivien Didelot 
440f22ab641SVivien Didelot 	/* Wait until the previous operation is completed */
4410f02b4f7SAndrew Lunn 	err = mv88e6xxx_wait(chip, addr, reg, BIT(15));
442f22ab641SVivien Didelot 	if (err)
443f22ab641SVivien Didelot 		return err;
444f22ab641SVivien Didelot 
445f22ab641SVivien Didelot 	/* Set the Update bit to trigger a write operation */
446f22ab641SVivien Didelot 	val = BIT(15) | update;
447f22ab641SVivien Didelot 
448f22ab641SVivien Didelot 	return mv88e6xxx_write(chip, addr, reg, val);
449f22ab641SVivien Didelot }
450f22ab641SVivien Didelot 
451d78343d2SVivien Didelot static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
452d78343d2SVivien Didelot 				    int link, int speed, int duplex,
453d78343d2SVivien Didelot 				    phy_interface_t mode)
454d78343d2SVivien Didelot {
455d78343d2SVivien Didelot 	int err;
456d78343d2SVivien Didelot 
457d78343d2SVivien Didelot 	if (!chip->info->ops->port_set_link)
458d78343d2SVivien Didelot 		return 0;
459d78343d2SVivien Didelot 
460d78343d2SVivien Didelot 	/* Port's MAC control must not be changed unless the link is down */
461d78343d2SVivien Didelot 	err = chip->info->ops->port_set_link(chip, port, 0);
462d78343d2SVivien Didelot 	if (err)
463d78343d2SVivien Didelot 		return err;
464d78343d2SVivien Didelot 
465d78343d2SVivien Didelot 	if (chip->info->ops->port_set_speed) {
466d78343d2SVivien Didelot 		err = chip->info->ops->port_set_speed(chip, port, speed);
467d78343d2SVivien Didelot 		if (err && err != -EOPNOTSUPP)
468d78343d2SVivien Didelot 			goto restore_link;
469d78343d2SVivien Didelot 	}
470d78343d2SVivien Didelot 
471d78343d2SVivien Didelot 	if (chip->info->ops->port_set_duplex) {
472d78343d2SVivien Didelot 		err = chip->info->ops->port_set_duplex(chip, port, duplex);
473d78343d2SVivien Didelot 		if (err && err != -EOPNOTSUPP)
474d78343d2SVivien Didelot 			goto restore_link;
475d78343d2SVivien Didelot 	}
476d78343d2SVivien Didelot 
477d78343d2SVivien Didelot 	if (chip->info->ops->port_set_rgmii_delay) {
478d78343d2SVivien Didelot 		err = chip->info->ops->port_set_rgmii_delay(chip, port, mode);
479d78343d2SVivien Didelot 		if (err && err != -EOPNOTSUPP)
480d78343d2SVivien Didelot 			goto restore_link;
481d78343d2SVivien Didelot 	}
482d78343d2SVivien Didelot 
483f39908d3SAndrew Lunn 	if (chip->info->ops->port_set_cmode) {
484f39908d3SAndrew Lunn 		err = chip->info->ops->port_set_cmode(chip, port, mode);
485f39908d3SAndrew Lunn 		if (err && err != -EOPNOTSUPP)
486f39908d3SAndrew Lunn 			goto restore_link;
487f39908d3SAndrew Lunn 	}
488f39908d3SAndrew Lunn 
489d78343d2SVivien Didelot 	err = 0;
490d78343d2SVivien Didelot restore_link:
491d78343d2SVivien Didelot 	if (chip->info->ops->port_set_link(chip, port, link))
492d78343d2SVivien Didelot 		netdev_err(chip->ds->ports[port].netdev,
493d78343d2SVivien Didelot 			   "failed to restore MAC's link\n");
494d78343d2SVivien Didelot 
495d78343d2SVivien Didelot 	return err;
496d78343d2SVivien Didelot }
497d78343d2SVivien Didelot 
498fad09c73SVivien Didelot /* We expect the switch to perform auto negotiation if there is a real
499fad09c73SVivien Didelot  * phy. However, in the case of a fixed link phy, we force the port
500fad09c73SVivien Didelot  * settings from the fixed link settings.
501fad09c73SVivien Didelot  */
502fad09c73SVivien Didelot static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
503fad09c73SVivien Didelot 				  struct phy_device *phydev)
504fad09c73SVivien Didelot {
50504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
5060e7b9925SAndrew Lunn 	int err;
507fad09c73SVivien Didelot 
508fad09c73SVivien Didelot 	if (!phy_is_pseudo_fixed_link(phydev))
509fad09c73SVivien Didelot 		return;
510fad09c73SVivien Didelot 
511fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
512d78343d2SVivien Didelot 	err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed,
513d78343d2SVivien Didelot 				       phydev->duplex, phydev->interface);
514fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
515d78343d2SVivien Didelot 
516d78343d2SVivien Didelot 	if (err && err != -EOPNOTSUPP)
517d78343d2SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to configure MAC\n");
518fad09c73SVivien Didelot }
519fad09c73SVivien Didelot 
520a605a0feSAndrew Lunn static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
521fad09c73SVivien Didelot {
522a605a0feSAndrew Lunn 	if (!chip->info->ops->stats_snapshot)
523a605a0feSAndrew Lunn 		return -EOPNOTSUPP;
524fad09c73SVivien Didelot 
525a605a0feSAndrew Lunn 	return chip->info->ops->stats_snapshot(chip, port);
526fad09c73SVivien Didelot }
527fad09c73SVivien Didelot 
528fad09c73SVivien Didelot static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
529dfafe449SAndrew Lunn 	{ "in_good_octets",		8, 0x00, STATS_TYPE_BANK0, },
530dfafe449SAndrew Lunn 	{ "in_bad_octets",		4, 0x02, STATS_TYPE_BANK0, },
531dfafe449SAndrew Lunn 	{ "in_unicast",			4, 0x04, STATS_TYPE_BANK0, },
532dfafe449SAndrew Lunn 	{ "in_broadcasts",		4, 0x06, STATS_TYPE_BANK0, },
533dfafe449SAndrew Lunn 	{ "in_multicasts",		4, 0x07, STATS_TYPE_BANK0, },
534dfafe449SAndrew Lunn 	{ "in_pause",			4, 0x16, STATS_TYPE_BANK0, },
535dfafe449SAndrew Lunn 	{ "in_undersize",		4, 0x18, STATS_TYPE_BANK0, },
536dfafe449SAndrew Lunn 	{ "in_fragments",		4, 0x19, STATS_TYPE_BANK0, },
537dfafe449SAndrew Lunn 	{ "in_oversize",		4, 0x1a, STATS_TYPE_BANK0, },
538dfafe449SAndrew Lunn 	{ "in_jabber",			4, 0x1b, STATS_TYPE_BANK0, },
539dfafe449SAndrew Lunn 	{ "in_rx_error",		4, 0x1c, STATS_TYPE_BANK0, },
540dfafe449SAndrew Lunn 	{ "in_fcs_error",		4, 0x1d, STATS_TYPE_BANK0, },
541dfafe449SAndrew Lunn 	{ "out_octets",			8, 0x0e, STATS_TYPE_BANK0, },
542dfafe449SAndrew Lunn 	{ "out_unicast",		4, 0x10, STATS_TYPE_BANK0, },
543dfafe449SAndrew Lunn 	{ "out_broadcasts",		4, 0x13, STATS_TYPE_BANK0, },
544dfafe449SAndrew Lunn 	{ "out_multicasts",		4, 0x12, STATS_TYPE_BANK0, },
545dfafe449SAndrew Lunn 	{ "out_pause",			4, 0x15, STATS_TYPE_BANK0, },
546dfafe449SAndrew Lunn 	{ "excessive",			4, 0x11, STATS_TYPE_BANK0, },
547dfafe449SAndrew Lunn 	{ "collisions",			4, 0x1e, STATS_TYPE_BANK0, },
548dfafe449SAndrew Lunn 	{ "deferred",			4, 0x05, STATS_TYPE_BANK0, },
549dfafe449SAndrew Lunn 	{ "single",			4, 0x14, STATS_TYPE_BANK0, },
550dfafe449SAndrew Lunn 	{ "multiple",			4, 0x17, STATS_TYPE_BANK0, },
551dfafe449SAndrew Lunn 	{ "out_fcs_error",		4, 0x03, STATS_TYPE_BANK0, },
552dfafe449SAndrew Lunn 	{ "late",			4, 0x1f, STATS_TYPE_BANK0, },
553dfafe449SAndrew Lunn 	{ "hist_64bytes",		4, 0x08, STATS_TYPE_BANK0, },
554dfafe449SAndrew Lunn 	{ "hist_65_127bytes",		4, 0x09, STATS_TYPE_BANK0, },
555dfafe449SAndrew Lunn 	{ "hist_128_255bytes",		4, 0x0a, STATS_TYPE_BANK0, },
556dfafe449SAndrew Lunn 	{ "hist_256_511bytes",		4, 0x0b, STATS_TYPE_BANK0, },
557dfafe449SAndrew Lunn 	{ "hist_512_1023bytes",		4, 0x0c, STATS_TYPE_BANK0, },
558dfafe449SAndrew Lunn 	{ "hist_1024_max_bytes",	4, 0x0d, STATS_TYPE_BANK0, },
559dfafe449SAndrew Lunn 	{ "sw_in_discards",		4, 0x10, STATS_TYPE_PORT, },
560dfafe449SAndrew Lunn 	{ "sw_in_filtered",		2, 0x12, STATS_TYPE_PORT, },
561dfafe449SAndrew Lunn 	{ "sw_out_filtered",		2, 0x13, STATS_TYPE_PORT, },
562dfafe449SAndrew Lunn 	{ "in_discards",		4, 0x00, STATS_TYPE_BANK1, },
563dfafe449SAndrew Lunn 	{ "in_filtered",		4, 0x01, STATS_TYPE_BANK1, },
564dfafe449SAndrew Lunn 	{ "in_accepted",		4, 0x02, STATS_TYPE_BANK1, },
565dfafe449SAndrew Lunn 	{ "in_bad_accepted",		4, 0x03, STATS_TYPE_BANK1, },
566dfafe449SAndrew Lunn 	{ "in_good_avb_class_a",	4, 0x04, STATS_TYPE_BANK1, },
567dfafe449SAndrew Lunn 	{ "in_good_avb_class_b",	4, 0x05, STATS_TYPE_BANK1, },
568dfafe449SAndrew Lunn 	{ "in_bad_avb_class_a",		4, 0x06, STATS_TYPE_BANK1, },
569dfafe449SAndrew Lunn 	{ "in_bad_avb_class_b",		4, 0x07, STATS_TYPE_BANK1, },
570dfafe449SAndrew Lunn 	{ "tcam_counter_0",		4, 0x08, STATS_TYPE_BANK1, },
571dfafe449SAndrew Lunn 	{ "tcam_counter_1",		4, 0x09, STATS_TYPE_BANK1, },
572dfafe449SAndrew Lunn 	{ "tcam_counter_2",		4, 0x0a, STATS_TYPE_BANK1, },
573dfafe449SAndrew Lunn 	{ "tcam_counter_3",		4, 0x0b, STATS_TYPE_BANK1, },
574dfafe449SAndrew Lunn 	{ "in_da_unknown",		4, 0x0e, STATS_TYPE_BANK1, },
575dfafe449SAndrew Lunn 	{ "in_management",		4, 0x0f, STATS_TYPE_BANK1, },
576dfafe449SAndrew Lunn 	{ "out_queue_0",		4, 0x10, STATS_TYPE_BANK1, },
577dfafe449SAndrew Lunn 	{ "out_queue_1",		4, 0x11, STATS_TYPE_BANK1, },
578dfafe449SAndrew Lunn 	{ "out_queue_2",		4, 0x12, STATS_TYPE_BANK1, },
579dfafe449SAndrew Lunn 	{ "out_queue_3",		4, 0x13, STATS_TYPE_BANK1, },
580dfafe449SAndrew Lunn 	{ "out_queue_4",		4, 0x14, STATS_TYPE_BANK1, },
581dfafe449SAndrew Lunn 	{ "out_queue_5",		4, 0x15, STATS_TYPE_BANK1, },
582dfafe449SAndrew Lunn 	{ "out_queue_6",		4, 0x16, STATS_TYPE_BANK1, },
583dfafe449SAndrew Lunn 	{ "out_queue_7",		4, 0x17, STATS_TYPE_BANK1, },
584dfafe449SAndrew Lunn 	{ "out_cut_through",		4, 0x18, STATS_TYPE_BANK1, },
585dfafe449SAndrew Lunn 	{ "out_octets_a",		4, 0x1a, STATS_TYPE_BANK1, },
586dfafe449SAndrew Lunn 	{ "out_octets_b",		4, 0x1b, STATS_TYPE_BANK1, },
587dfafe449SAndrew Lunn 	{ "out_management",		4, 0x1f, STATS_TYPE_BANK1, },
588fad09c73SVivien Didelot };
589fad09c73SVivien Didelot 
590fad09c73SVivien Didelot static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
591fad09c73SVivien Didelot 					    struct mv88e6xxx_hw_stat *s,
592e0d8b615SAndrew Lunn 					    int port, u16 bank1_select,
593e0d8b615SAndrew Lunn 					    u16 histogram)
594fad09c73SVivien Didelot {
595fad09c73SVivien Didelot 	u32 low;
596fad09c73SVivien Didelot 	u32 high = 0;
597dfafe449SAndrew Lunn 	u16 reg = 0;
5980e7b9925SAndrew Lunn 	int err;
599fad09c73SVivien Didelot 	u64 value;
600fad09c73SVivien Didelot 
601fad09c73SVivien Didelot 	switch (s->type) {
602dfafe449SAndrew Lunn 	case STATS_TYPE_PORT:
6030e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, s->reg, &reg);
6040e7b9925SAndrew Lunn 		if (err)
605fad09c73SVivien Didelot 			return UINT64_MAX;
606fad09c73SVivien Didelot 
6070e7b9925SAndrew Lunn 		low = reg;
608fad09c73SVivien Didelot 		if (s->sizeof_stat == 4) {
6090e7b9925SAndrew Lunn 			err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
6100e7b9925SAndrew Lunn 			if (err)
611fad09c73SVivien Didelot 				return UINT64_MAX;
6120e7b9925SAndrew Lunn 			high = reg;
613fad09c73SVivien Didelot 		}
614fad09c73SVivien Didelot 		break;
615dfafe449SAndrew Lunn 	case STATS_TYPE_BANK1:
616e0d8b615SAndrew Lunn 		reg = bank1_select;
617dfafe449SAndrew Lunn 		/* fall through */
618dfafe449SAndrew Lunn 	case STATS_TYPE_BANK0:
619e0d8b615SAndrew Lunn 		reg |= s->reg | histogram;
6207f9ef3afSAndrew Lunn 		mv88e6xxx_g1_stats_read(chip, reg, &low);
621fad09c73SVivien Didelot 		if (s->sizeof_stat == 8)
6227f9ef3afSAndrew Lunn 			mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
6239fc3e4dcSGustavo A. R. Silva 		break;
6249fc3e4dcSGustavo A. R. Silva 	default:
6259fc3e4dcSGustavo A. R. Silva 		return UINT64_MAX;
626fad09c73SVivien Didelot 	}
627fad09c73SVivien Didelot 	value = (((u64)high) << 16) | low;
628fad09c73SVivien Didelot 	return value;
629fad09c73SVivien Didelot }
630fad09c73SVivien Didelot 
631dfafe449SAndrew Lunn static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
632dfafe449SAndrew Lunn 					uint8_t *data, int types)
633fad09c73SVivien Didelot {
634fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
635fad09c73SVivien Didelot 	int i, j;
636fad09c73SVivien Didelot 
637fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
638fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
639dfafe449SAndrew Lunn 		if (stat->type & types) {
640fad09c73SVivien Didelot 			memcpy(data + j * ETH_GSTRING_LEN, stat->string,
641fad09c73SVivien Didelot 			       ETH_GSTRING_LEN);
642fad09c73SVivien Didelot 			j++;
643fad09c73SVivien Didelot 		}
644fad09c73SVivien Didelot 	}
645fad09c73SVivien Didelot }
646fad09c73SVivien Didelot 
647dfafe449SAndrew Lunn static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
648dfafe449SAndrew Lunn 					uint8_t *data)
649dfafe449SAndrew Lunn {
650dfafe449SAndrew Lunn 	mv88e6xxx_stats_get_strings(chip, data,
651dfafe449SAndrew Lunn 				    STATS_TYPE_BANK0 | STATS_TYPE_PORT);
652dfafe449SAndrew Lunn }
653dfafe449SAndrew Lunn 
654dfafe449SAndrew Lunn static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
655dfafe449SAndrew Lunn 					uint8_t *data)
656dfafe449SAndrew Lunn {
657dfafe449SAndrew Lunn 	mv88e6xxx_stats_get_strings(chip, data,
658dfafe449SAndrew Lunn 				    STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
659dfafe449SAndrew Lunn }
660dfafe449SAndrew Lunn 
661dfafe449SAndrew Lunn static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
662dfafe449SAndrew Lunn 				  uint8_t *data)
663fad09c73SVivien Didelot {
66404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
665dfafe449SAndrew Lunn 
666dfafe449SAndrew Lunn 	if (chip->info->ops->stats_get_strings)
667dfafe449SAndrew Lunn 		chip->info->ops->stats_get_strings(chip, data);
668dfafe449SAndrew Lunn }
669dfafe449SAndrew Lunn 
670dfafe449SAndrew Lunn static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip,
671dfafe449SAndrew Lunn 					  int types)
672dfafe449SAndrew Lunn {
673fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
674fad09c73SVivien Didelot 	int i, j;
675fad09c73SVivien Didelot 
676fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
677fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
678dfafe449SAndrew Lunn 		if (stat->type & types)
679fad09c73SVivien Didelot 			j++;
680fad09c73SVivien Didelot 	}
681fad09c73SVivien Didelot 	return j;
682fad09c73SVivien Didelot }
683fad09c73SVivien Didelot 
684dfafe449SAndrew Lunn static int mv88e6095_stats_get_sset_count(struct mv88e6xxx_chip *chip)
685dfafe449SAndrew Lunn {
686dfafe449SAndrew Lunn 	return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 |
687dfafe449SAndrew Lunn 					      STATS_TYPE_PORT);
688dfafe449SAndrew Lunn }
689dfafe449SAndrew Lunn 
690dfafe449SAndrew Lunn static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip)
691dfafe449SAndrew Lunn {
692dfafe449SAndrew Lunn 	return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 |
693dfafe449SAndrew Lunn 					      STATS_TYPE_BANK1);
694dfafe449SAndrew Lunn }
695dfafe449SAndrew Lunn 
696dfafe449SAndrew Lunn static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
697dfafe449SAndrew Lunn {
698dfafe449SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
699dfafe449SAndrew Lunn 
700dfafe449SAndrew Lunn 	if (chip->info->ops->stats_get_sset_count)
701dfafe449SAndrew Lunn 		return chip->info->ops->stats_get_sset_count(chip);
702dfafe449SAndrew Lunn 
703dfafe449SAndrew Lunn 	return 0;
704dfafe449SAndrew Lunn }
705dfafe449SAndrew Lunn 
706052f947fSAndrew Lunn static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
707e0d8b615SAndrew Lunn 				      uint64_t *data, int types,
708e0d8b615SAndrew Lunn 				      u16 bank1_select, u16 histogram)
709052f947fSAndrew Lunn {
710052f947fSAndrew Lunn 	struct mv88e6xxx_hw_stat *stat;
711052f947fSAndrew Lunn 	int i, j;
712052f947fSAndrew Lunn 
713052f947fSAndrew Lunn 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
714052f947fSAndrew Lunn 		stat = &mv88e6xxx_hw_stats[i];
715052f947fSAndrew Lunn 		if (stat->type & types) {
716e0d8b615SAndrew Lunn 			data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
717e0d8b615SAndrew Lunn 							      bank1_select,
718e0d8b615SAndrew Lunn 							      histogram);
719052f947fSAndrew Lunn 			j++;
720052f947fSAndrew Lunn 		}
721052f947fSAndrew Lunn 	}
722052f947fSAndrew Lunn }
723052f947fSAndrew Lunn 
724052f947fSAndrew Lunn static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
725052f947fSAndrew Lunn 				      uint64_t *data)
726052f947fSAndrew Lunn {
727052f947fSAndrew Lunn 	return mv88e6xxx_stats_get_stats(chip, port, data,
728e0d8b615SAndrew Lunn 					 STATS_TYPE_BANK0 | STATS_TYPE_PORT,
729e0d8b615SAndrew Lunn 					 0, GLOBAL_STATS_OP_HIST_RX_TX);
730052f947fSAndrew Lunn }
731052f947fSAndrew Lunn 
732052f947fSAndrew Lunn static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
733052f947fSAndrew Lunn 				      uint64_t *data)
734052f947fSAndrew Lunn {
735052f947fSAndrew Lunn 	return mv88e6xxx_stats_get_stats(chip, port, data,
736e0d8b615SAndrew Lunn 					 STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
737e0d8b615SAndrew Lunn 					 GLOBAL_STATS_OP_BANK_1_BIT_9,
738e0d8b615SAndrew Lunn 					 GLOBAL_STATS_OP_HIST_RX_TX);
739e0d8b615SAndrew Lunn }
740e0d8b615SAndrew Lunn 
741e0d8b615SAndrew Lunn static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
742e0d8b615SAndrew Lunn 				      uint64_t *data)
743e0d8b615SAndrew Lunn {
744e0d8b615SAndrew Lunn 	return mv88e6xxx_stats_get_stats(chip, port, data,
745e0d8b615SAndrew Lunn 					 STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
746e0d8b615SAndrew Lunn 					 GLOBAL_STATS_OP_BANK_1_BIT_10, 0);
747052f947fSAndrew Lunn }
748052f947fSAndrew Lunn 
749052f947fSAndrew Lunn static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
750052f947fSAndrew Lunn 				uint64_t *data)
751052f947fSAndrew Lunn {
752052f947fSAndrew Lunn 	if (chip->info->ops->stats_get_stats)
753052f947fSAndrew Lunn 		chip->info->ops->stats_get_stats(chip, port, data);
754052f947fSAndrew Lunn }
755052f947fSAndrew Lunn 
756fad09c73SVivien Didelot static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
757fad09c73SVivien Didelot 					uint64_t *data)
758fad09c73SVivien Didelot {
75904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
760fad09c73SVivien Didelot 	int ret;
761fad09c73SVivien Didelot 
762fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
763fad09c73SVivien Didelot 
764a605a0feSAndrew Lunn 	ret = mv88e6xxx_stats_snapshot(chip, port);
765fad09c73SVivien Didelot 	if (ret < 0) {
766fad09c73SVivien Didelot 		mutex_unlock(&chip->reg_lock);
767fad09c73SVivien Didelot 		return;
768fad09c73SVivien Didelot 	}
769052f947fSAndrew Lunn 
770052f947fSAndrew Lunn 	mv88e6xxx_get_stats(chip, port, data);
771fad09c73SVivien Didelot 
772fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
773fad09c73SVivien Didelot }
774fad09c73SVivien Didelot 
775de227387SAndrew Lunn static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip)
776de227387SAndrew Lunn {
777de227387SAndrew Lunn 	if (chip->info->ops->stats_set_histogram)
778de227387SAndrew Lunn 		return chip->info->ops->stats_set_histogram(chip);
779de227387SAndrew Lunn 
780de227387SAndrew Lunn 	return 0;
781de227387SAndrew Lunn }
782de227387SAndrew Lunn 
783fad09c73SVivien Didelot static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
784fad09c73SVivien Didelot {
785fad09c73SVivien Didelot 	return 32 * sizeof(u16);
786fad09c73SVivien Didelot }
787fad09c73SVivien Didelot 
788fad09c73SVivien Didelot static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
789fad09c73SVivien Didelot 			       struct ethtool_regs *regs, void *_p)
790fad09c73SVivien Didelot {
79104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
7920e7b9925SAndrew Lunn 	int err;
7930e7b9925SAndrew Lunn 	u16 reg;
794fad09c73SVivien Didelot 	u16 *p = _p;
795fad09c73SVivien Didelot 	int i;
796fad09c73SVivien Didelot 
797fad09c73SVivien Didelot 	regs->version = 0;
798fad09c73SVivien Didelot 
799fad09c73SVivien Didelot 	memset(p, 0xff, 32 * sizeof(u16));
800fad09c73SVivien Didelot 
801fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
802fad09c73SVivien Didelot 
803fad09c73SVivien Didelot 	for (i = 0; i < 32; i++) {
804fad09c73SVivien Didelot 
8050e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, i, &reg);
8060e7b9925SAndrew Lunn 		if (!err)
8070e7b9925SAndrew Lunn 			p[i] = reg;
808fad09c73SVivien Didelot 	}
809fad09c73SVivien Didelot 
810fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
811fad09c73SVivien Didelot }
812fad09c73SVivien Didelot 
813fad09c73SVivien Didelot static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
814fad09c73SVivien Didelot 			     struct ethtool_eee *e)
815fad09c73SVivien Didelot {
81604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
8179c93829cSVivien Didelot 	u16 reg;
8189c93829cSVivien Didelot 	int err;
819fad09c73SVivien Didelot 
820fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
821fad09c73SVivien Didelot 		return -EOPNOTSUPP;
822fad09c73SVivien Didelot 
823fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
824fad09c73SVivien Didelot 
8259c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
8269c93829cSVivien Didelot 	if (err)
827fad09c73SVivien Didelot 		goto out;
828fad09c73SVivien Didelot 
829fad09c73SVivien Didelot 	e->eee_enabled = !!(reg & 0x0200);
830fad09c73SVivien Didelot 	e->tx_lpi_enabled = !!(reg & 0x0100);
831fad09c73SVivien Didelot 
8320e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
8339c93829cSVivien Didelot 	if (err)
834fad09c73SVivien Didelot 		goto out;
835fad09c73SVivien Didelot 
836fad09c73SVivien Didelot 	e->eee_active = !!(reg & PORT_STATUS_EEE);
837fad09c73SVivien Didelot out:
838fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
8399c93829cSVivien Didelot 
8409c93829cSVivien Didelot 	return err;
841fad09c73SVivien Didelot }
842fad09c73SVivien Didelot 
843fad09c73SVivien Didelot static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
844fad09c73SVivien Didelot 			     struct phy_device *phydev, struct ethtool_eee *e)
845fad09c73SVivien Didelot {
84604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
8479c93829cSVivien Didelot 	u16 reg;
8489c93829cSVivien Didelot 	int err;
849fad09c73SVivien Didelot 
850fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
851fad09c73SVivien Didelot 		return -EOPNOTSUPP;
852fad09c73SVivien Didelot 
853fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
854fad09c73SVivien Didelot 
8559c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
8569c93829cSVivien Didelot 	if (err)
857fad09c73SVivien Didelot 		goto out;
858fad09c73SVivien Didelot 
8599c93829cSVivien Didelot 	reg &= ~0x0300;
860fad09c73SVivien Didelot 	if (e->eee_enabled)
861fad09c73SVivien Didelot 		reg |= 0x0200;
862fad09c73SVivien Didelot 	if (e->tx_lpi_enabled)
863fad09c73SVivien Didelot 		reg |= 0x0100;
864fad09c73SVivien Didelot 
8659c93829cSVivien Didelot 	err = mv88e6xxx_phy_write(chip, port, 16, reg);
866fad09c73SVivien Didelot out:
867fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
868fad09c73SVivien Didelot 
8699c93829cSVivien Didelot 	return err;
870fad09c73SVivien Didelot }
871fad09c73SVivien Didelot 
872e5887a2aSVivien Didelot static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)
873fad09c73SVivien Didelot {
874e5887a2aSVivien Didelot 	struct dsa_switch *ds = NULL;
875e5887a2aSVivien Didelot 	struct net_device *br;
876e5887a2aSVivien Didelot 	u16 pvlan;
877fad09c73SVivien Didelot 	int i;
878fad09c73SVivien Didelot 
879e5887a2aSVivien Didelot 	if (dev < DSA_MAX_SWITCHES)
880e5887a2aSVivien Didelot 		ds = chip->ds->dst->ds[dev];
881fad09c73SVivien Didelot 
882e5887a2aSVivien Didelot 	/* Prevent frames from unknown switch or port */
883e5887a2aSVivien Didelot 	if (!ds || port >= ds->num_ports)
884e5887a2aSVivien Didelot 		return 0;
885e5887a2aSVivien Didelot 
886e5887a2aSVivien Didelot 	/* Frames from DSA links and CPU ports can egress any local port */
887e5887a2aSVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
888e5887a2aSVivien Didelot 		return mv88e6xxx_port_mask(chip);
889e5887a2aSVivien Didelot 
890e5887a2aSVivien Didelot 	br = ds->ports[port].bridge_dev;
891e5887a2aSVivien Didelot 	pvlan = 0;
892e5887a2aSVivien Didelot 
893e5887a2aSVivien Didelot 	/* Frames from user ports can egress any local DSA links and CPU ports,
894e5887a2aSVivien Didelot 	 * as well as any local member of their bridge group.
895e5887a2aSVivien Didelot 	 */
896e5887a2aSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
897e5887a2aSVivien Didelot 		if (dsa_is_cpu_port(chip->ds, i) ||
898e5887a2aSVivien Didelot 		    dsa_is_dsa_port(chip->ds, i) ||
899e5887a2aSVivien Didelot 		    (br && chip->ds->ports[i].bridge_dev == br))
900e5887a2aSVivien Didelot 			pvlan |= BIT(i);
901e5887a2aSVivien Didelot 
902e5887a2aSVivien Didelot 	return pvlan;
903fad09c73SVivien Didelot }
904e5887a2aSVivien Didelot 
905240ea3efSVivien Didelot static int mv88e6xxx_port_vlan_map(struct mv88e6xxx_chip *chip, int port)
906e5887a2aSVivien Didelot {
907e5887a2aSVivien Didelot 	u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port);
908fad09c73SVivien Didelot 
909fad09c73SVivien Didelot 	/* prevent frames from going back out of the port they came in on */
910fad09c73SVivien Didelot 	output_ports &= ~BIT(port);
911fad09c73SVivien Didelot 
9125a7921f4SVivien Didelot 	return mv88e6xxx_port_set_vlan_map(chip, port, output_ports);
913fad09c73SVivien Didelot }
914fad09c73SVivien Didelot 
915fad09c73SVivien Didelot static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
916fad09c73SVivien Didelot 					 u8 state)
917fad09c73SVivien Didelot {
91804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
919fad09c73SVivien Didelot 	int stp_state;
920fad09c73SVivien Didelot 	int err;
921fad09c73SVivien Didelot 
922fad09c73SVivien Didelot 	switch (state) {
923fad09c73SVivien Didelot 	case BR_STATE_DISABLED:
924fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_DISABLED;
925fad09c73SVivien Didelot 		break;
926fad09c73SVivien Didelot 	case BR_STATE_BLOCKING:
927fad09c73SVivien Didelot 	case BR_STATE_LISTENING:
928fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_BLOCKING;
929fad09c73SVivien Didelot 		break;
930fad09c73SVivien Didelot 	case BR_STATE_LEARNING:
931fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_LEARNING;
932fad09c73SVivien Didelot 		break;
933fad09c73SVivien Didelot 	case BR_STATE_FORWARDING:
934fad09c73SVivien Didelot 	default:
935fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_FORWARDING;
936fad09c73SVivien Didelot 		break;
937fad09c73SVivien Didelot 	}
938fad09c73SVivien Didelot 
939fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
940e28def33SVivien Didelot 	err = mv88e6xxx_port_set_state(chip, port, stp_state);
941fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
942fad09c73SVivien Didelot 
943fad09c73SVivien Didelot 	if (err)
944e28def33SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to update state\n");
945fad09c73SVivien Didelot }
946fad09c73SVivien Didelot 
947a2ac29d2SVivien Didelot static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip)
948a2ac29d2SVivien Didelot {
949c3a7d4adSVivien Didelot 	int err;
950c3a7d4adSVivien Didelot 
951daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_flush(chip, 0, true);
952daefc943SVivien Didelot 	if (err)
953daefc943SVivien Didelot 		return err;
954daefc943SVivien Didelot 
955c3a7d4adSVivien Didelot 	err = mv88e6xxx_g1_atu_set_learn2all(chip, true);
956c3a7d4adSVivien Didelot 	if (err)
957c3a7d4adSVivien Didelot 		return err;
958c3a7d4adSVivien Didelot 
959a2ac29d2SVivien Didelot 	return mv88e6xxx_g1_atu_set_age_time(chip, 300000);
960a2ac29d2SVivien Didelot }
961a2ac29d2SVivien Didelot 
96217a1594eSVivien Didelot static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
96317a1594eSVivien Didelot {
96417a1594eSVivien Didelot 	u16 pvlan = 0;
96517a1594eSVivien Didelot 
96617a1594eSVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
96717a1594eSVivien Didelot 		return -EOPNOTSUPP;
96817a1594eSVivien Didelot 
96917a1594eSVivien Didelot 	/* Skip the local source device, which uses in-chip port VLAN */
97017a1594eSVivien Didelot 	if (dev != chip->ds->index)
971aec5ac88SVivien Didelot 		pvlan = mv88e6xxx_port_vlan(chip, dev, port);
97217a1594eSVivien Didelot 
97317a1594eSVivien Didelot 	return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
97417a1594eSVivien Didelot }
97517a1594eSVivien Didelot 
97681228996SVivien Didelot static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
97781228996SVivien Didelot {
97817a1594eSVivien Didelot 	int dev, port;
97917a1594eSVivien Didelot 	int err;
98017a1594eSVivien Didelot 
98181228996SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
98281228996SVivien Didelot 		return 0;
98381228996SVivien Didelot 
98481228996SVivien Didelot 	/* Clear 5 Bit Port for usage with Marvell Link Street devices:
98581228996SVivien Didelot 	 * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev.
98681228996SVivien Didelot 	 */
98717a1594eSVivien Didelot 	err = mv88e6xxx_g2_misc_4_bit_port(chip);
98817a1594eSVivien Didelot 	if (err)
98917a1594eSVivien Didelot 		return err;
99017a1594eSVivien Didelot 
99117a1594eSVivien Didelot 	for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; ++dev) {
99217a1594eSVivien Didelot 		for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; ++port) {
99317a1594eSVivien Didelot 			err = mv88e6xxx_pvt_map(chip, dev, port);
99417a1594eSVivien Didelot 			if (err)
99517a1594eSVivien Didelot 				return err;
99617a1594eSVivien Didelot 		}
99717a1594eSVivien Didelot 	}
99817a1594eSVivien Didelot 
99917a1594eSVivien Didelot 	return 0;
100081228996SVivien Didelot }
100181228996SVivien Didelot 
1002749efcb8SVivien Didelot static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
1003749efcb8SVivien Didelot {
1004749efcb8SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1005749efcb8SVivien Didelot 	int err;
1006749efcb8SVivien Didelot 
1007749efcb8SVivien Didelot 	mutex_lock(&chip->reg_lock);
1008e606ca36SVivien Didelot 	err = mv88e6xxx_g1_atu_remove(chip, 0, port, false);
1009749efcb8SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1010749efcb8SVivien Didelot 
1011749efcb8SVivien Didelot 	if (err)
1012749efcb8SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
1013749efcb8SVivien Didelot }
1014749efcb8SVivien Didelot 
1015b486d7c9SVivien Didelot static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
1016b486d7c9SVivien Didelot {
1017b486d7c9SVivien Didelot 	if (!chip->info->max_vid)
1018b486d7c9SVivien Didelot 		return 0;
1019b486d7c9SVivien Didelot 
1020b486d7c9SVivien Didelot 	return mv88e6xxx_g1_vtu_flush(chip);
1021b486d7c9SVivien Didelot }
1022b486d7c9SVivien Didelot 
1023f1394b78SVivien Didelot static int mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
1024f1394b78SVivien Didelot 				 struct mv88e6xxx_vtu_entry *entry)
1025f1394b78SVivien Didelot {
1026f1394b78SVivien Didelot 	if (!chip->info->ops->vtu_getnext)
1027f1394b78SVivien Didelot 		return -EOPNOTSUPP;
1028f1394b78SVivien Didelot 
1029f1394b78SVivien Didelot 	return chip->info->ops->vtu_getnext(chip, entry);
1030f1394b78SVivien Didelot }
1031f1394b78SVivien Didelot 
10320ad5daf6SVivien Didelot static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
10330ad5daf6SVivien Didelot 				   struct mv88e6xxx_vtu_entry *entry)
10340ad5daf6SVivien Didelot {
10350ad5daf6SVivien Didelot 	if (!chip->info->ops->vtu_loadpurge)
10360ad5daf6SVivien Didelot 		return -EOPNOTSUPP;
10370ad5daf6SVivien Didelot 
10380ad5daf6SVivien Didelot 	return chip->info->ops->vtu_loadpurge(chip, entry);
10390ad5daf6SVivien Didelot }
10400ad5daf6SVivien Didelot 
1041fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1042fad09c73SVivien Didelot 				    struct switchdev_obj_port_vlan *vlan,
1043438ff537SVivien Didelot 				    switchdev_obj_dump_cb_t *cb)
1044fad09c73SVivien Didelot {
104504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10463afb4bdeSVivien Didelot 	struct mv88e6xxx_vtu_entry next = {
10473afb4bdeSVivien Didelot 		.vid = chip->info->max_vid,
10483afb4bdeSVivien Didelot 	};
1049fad09c73SVivien Didelot 	u16 pvid;
1050fad09c73SVivien Didelot 	int err;
1051fad09c73SVivien Didelot 
10523cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1053fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1054fad09c73SVivien Didelot 
1055fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1056fad09c73SVivien Didelot 
105777064f37SVivien Didelot 	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
1058fad09c73SVivien Didelot 	if (err)
1059fad09c73SVivien Didelot 		goto unlock;
1060fad09c73SVivien Didelot 
1061fad09c73SVivien Didelot 	do {
1062f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &next);
1063fad09c73SVivien Didelot 		if (err)
1064fad09c73SVivien Didelot 			break;
1065fad09c73SVivien Didelot 
1066fad09c73SVivien Didelot 		if (!next.valid)
1067fad09c73SVivien Didelot 			break;
1068fad09c73SVivien Didelot 
1069bd00e053SVivien Didelot 		if (next.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1070fad09c73SVivien Didelot 			continue;
1071fad09c73SVivien Didelot 
1072fad09c73SVivien Didelot 		/* reinit and dump this VLAN obj */
1073fad09c73SVivien Didelot 		vlan->vid_begin = next.vid;
1074fad09c73SVivien Didelot 		vlan->vid_end = next.vid;
1075fad09c73SVivien Didelot 		vlan->flags = 0;
1076fad09c73SVivien Didelot 
1077bd00e053SVivien Didelot 		if (next.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1078fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1079fad09c73SVivien Didelot 
1080fad09c73SVivien Didelot 		if (next.vid == pvid)
1081fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1082fad09c73SVivien Didelot 
1083fad09c73SVivien Didelot 		err = cb(&vlan->obj);
1084fad09c73SVivien Didelot 		if (err)
1085fad09c73SVivien Didelot 			break;
10863cf3c846SVivien Didelot 	} while (next.vid < chip->info->max_vid);
1087fad09c73SVivien Didelot 
1088fad09c73SVivien Didelot unlock:
1089fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1090fad09c73SVivien Didelot 
1091fad09c73SVivien Didelot 	return err;
1092fad09c73SVivien Didelot }
1093fad09c73SVivien Didelot 
1094d7f435f9SVivien Didelot static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
1095fad09c73SVivien Didelot {
1096fad09c73SVivien Didelot 	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
10973afb4bdeSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
10983afb4bdeSVivien Didelot 		.vid = chip->info->max_vid,
10993afb4bdeSVivien Didelot 	};
1100fad09c73SVivien Didelot 	int i, err;
1101fad09c73SVivien Didelot 
1102fad09c73SVivien Didelot 	bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1103fad09c73SVivien Didelot 
1104fad09c73SVivien Didelot 	/* Set every FID bit used by the (un)bridged ports */
1105370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1106b4e48c50SVivien Didelot 		err = mv88e6xxx_port_get_fid(chip, i, fid);
1107fad09c73SVivien Didelot 		if (err)
1108fad09c73SVivien Didelot 			return err;
1109fad09c73SVivien Didelot 
1110fad09c73SVivien Didelot 		set_bit(*fid, fid_bitmap);
1111fad09c73SVivien Didelot 	}
1112fad09c73SVivien Didelot 
1113fad09c73SVivien Didelot 	/* Set every FID bit used by the VLAN entries */
1114fad09c73SVivien Didelot 	do {
1115f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &vlan);
1116fad09c73SVivien Didelot 		if (err)
1117fad09c73SVivien Didelot 			return err;
1118fad09c73SVivien Didelot 
1119fad09c73SVivien Didelot 		if (!vlan.valid)
1120fad09c73SVivien Didelot 			break;
1121fad09c73SVivien Didelot 
1122fad09c73SVivien Didelot 		set_bit(vlan.fid, fid_bitmap);
11233cf3c846SVivien Didelot 	} while (vlan.vid < chip->info->max_vid);
1124fad09c73SVivien Didelot 
1125fad09c73SVivien Didelot 	/* The reset value 0x000 is used to indicate that multiple address
1126fad09c73SVivien Didelot 	 * databases are not needed. Return the next positive available.
1127fad09c73SVivien Didelot 	 */
1128fad09c73SVivien Didelot 	*fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
1129fad09c73SVivien Didelot 	if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
1130fad09c73SVivien Didelot 		return -ENOSPC;
1131fad09c73SVivien Didelot 
1132fad09c73SVivien Didelot 	/* Clear the database */
1133daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_flush(chip, *fid, true);
1134fad09c73SVivien Didelot }
1135fad09c73SVivien Didelot 
1136567aa59aSVivien Didelot static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
1137567aa59aSVivien Didelot 			     struct mv88e6xxx_vtu_entry *entry, bool new)
1138fad09c73SVivien Didelot {
1139fad09c73SVivien Didelot 	int err;
1140fad09c73SVivien Didelot 
1141fad09c73SVivien Didelot 	if (!vid)
1142fad09c73SVivien Didelot 		return -EINVAL;
1143fad09c73SVivien Didelot 
11443afb4bdeSVivien Didelot 	entry->vid = vid - 1;
11453afb4bdeSVivien Didelot 	entry->valid = false;
1146fad09c73SVivien Didelot 
1147f1394b78SVivien Didelot 	err = mv88e6xxx_vtu_getnext(chip, entry);
1148fad09c73SVivien Didelot 	if (err)
1149fad09c73SVivien Didelot 		return err;
1150fad09c73SVivien Didelot 
1151567aa59aSVivien Didelot 	if (entry->vid == vid && entry->valid)
1152567aa59aSVivien Didelot 		return 0;
1153fad09c73SVivien Didelot 
1154567aa59aSVivien Didelot 	if (new) {
1155567aa59aSVivien Didelot 		int i;
1156567aa59aSVivien Didelot 
1157567aa59aSVivien Didelot 		/* Initialize a fresh VLAN entry */
1158567aa59aSVivien Didelot 		memset(entry, 0, sizeof(*entry));
1159567aa59aSVivien Didelot 		entry->valid = true;
1160567aa59aSVivien Didelot 		entry->vid = vid;
1161567aa59aSVivien Didelot 
1162567aa59aSVivien Didelot 		/* Include only CPU and DSA ports */
1163567aa59aSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
1164567aa59aSVivien Didelot 			entry->member[i] = dsa_is_normal_port(chip->ds, i) ?
1165567aa59aSVivien Didelot 				GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER :
1166567aa59aSVivien Didelot 				GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED;
1167567aa59aSVivien Didelot 
1168567aa59aSVivien Didelot 		return mv88e6xxx_atu_new(chip, &entry->fid);
1169fad09c73SVivien Didelot 	}
1170fad09c73SVivien Didelot 
1171567aa59aSVivien Didelot 	/* switchdev expects -EOPNOTSUPP to honor software VLANs */
1172567aa59aSVivien Didelot 	return -EOPNOTSUPP;
1173fad09c73SVivien Didelot }
1174fad09c73SVivien Didelot 
1175fad09c73SVivien Didelot static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1176fad09c73SVivien Didelot 					u16 vid_begin, u16 vid_end)
1177fad09c73SVivien Didelot {
117804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
11793afb4bdeSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
11803afb4bdeSVivien Didelot 		.vid = vid_begin - 1,
11813afb4bdeSVivien Didelot 	};
1182fad09c73SVivien Didelot 	int i, err;
1183fad09c73SVivien Didelot 
1184fad09c73SVivien Didelot 	if (!vid_begin)
1185fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1186fad09c73SVivien Didelot 
1187fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1188fad09c73SVivien Didelot 
1189fad09c73SVivien Didelot 	do {
1190f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &vlan);
1191fad09c73SVivien Didelot 		if (err)
1192fad09c73SVivien Didelot 			goto unlock;
1193fad09c73SVivien Didelot 
1194fad09c73SVivien Didelot 		if (!vlan.valid)
1195fad09c73SVivien Didelot 			break;
1196fad09c73SVivien Didelot 
1197fad09c73SVivien Didelot 		if (vlan.vid > vid_end)
1198fad09c73SVivien Didelot 			break;
1199fad09c73SVivien Didelot 
1200370b4ffbSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1201fad09c73SVivien Didelot 			if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1202fad09c73SVivien Didelot 				continue;
1203fad09c73SVivien Didelot 
120466e2809dSAndrew Lunn 			if (!ds->ports[port].netdev)
120566e2809dSAndrew Lunn 				continue;
120666e2809dSAndrew Lunn 
1207bd00e053SVivien Didelot 			if (vlan.member[i] ==
1208fad09c73SVivien Didelot 			    GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1209fad09c73SVivien Didelot 				continue;
1210fad09c73SVivien Didelot 
1211fae8a25eSVivien Didelot 			if (ds->ports[i].bridge_dev ==
1212fae8a25eSVivien Didelot 			    ds->ports[port].bridge_dev)
1213fad09c73SVivien Didelot 				break; /* same bridge, check next VLAN */
1214fad09c73SVivien Didelot 
1215fae8a25eSVivien Didelot 			if (!ds->ports[i].bridge_dev)
121666e2809dSAndrew Lunn 				continue;
121766e2809dSAndrew Lunn 
1218fad09c73SVivien Didelot 			netdev_warn(ds->ports[port].netdev,
1219fad09c73SVivien Didelot 				    "hardware VLAN %d already used by %s\n",
1220fad09c73SVivien Didelot 				    vlan.vid,
1221fae8a25eSVivien Didelot 				    netdev_name(ds->ports[i].bridge_dev));
1222fad09c73SVivien Didelot 			err = -EOPNOTSUPP;
1223fad09c73SVivien Didelot 			goto unlock;
1224fad09c73SVivien Didelot 		}
1225fad09c73SVivien Didelot 	} while (vlan.vid < vid_end);
1226fad09c73SVivien Didelot 
1227fad09c73SVivien Didelot unlock:
1228fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1229fad09c73SVivien Didelot 
1230fad09c73SVivien Didelot 	return err;
1231fad09c73SVivien Didelot }
1232fad09c73SVivien Didelot 
1233fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1234fad09c73SVivien Didelot 					 bool vlan_filtering)
1235fad09c73SVivien Didelot {
123604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1237385a0995SVivien Didelot 	u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1238fad09c73SVivien Didelot 		PORT_CONTROL_2_8021Q_DISABLED;
12390e7b9925SAndrew Lunn 	int err;
1240fad09c73SVivien Didelot 
12413cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1242fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1243fad09c73SVivien Didelot 
1244fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1245385a0995SVivien Didelot 	err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
1246fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1247fad09c73SVivien Didelot 
12480e7b9925SAndrew Lunn 	return err;
1249fad09c73SVivien Didelot }
1250fad09c73SVivien Didelot 
1251fad09c73SVivien Didelot static int
1252fad09c73SVivien Didelot mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1253fad09c73SVivien Didelot 			    const struct switchdev_obj_port_vlan *vlan,
1254fad09c73SVivien Didelot 			    struct switchdev_trans *trans)
1255fad09c73SVivien Didelot {
125604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1257fad09c73SVivien Didelot 	int err;
1258fad09c73SVivien Didelot 
12593cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1260fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1261fad09c73SVivien Didelot 
1262fad09c73SVivien Didelot 	/* If the requested port doesn't belong to the same bridge as the VLAN
1263fad09c73SVivien Didelot 	 * members, do not support it (yet) and fallback to software VLAN.
1264fad09c73SVivien Didelot 	 */
1265fad09c73SVivien Didelot 	err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1266fad09c73SVivien Didelot 					   vlan->vid_end);
1267fad09c73SVivien Didelot 	if (err)
1268fad09c73SVivien Didelot 		return err;
1269fad09c73SVivien Didelot 
1270fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
1271fad09c73SVivien Didelot 	 * so skip the prepare phase.
1272fad09c73SVivien Didelot 	 */
1273fad09c73SVivien Didelot 	return 0;
1274fad09c73SVivien Didelot }
1275fad09c73SVivien Didelot 
1276fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
1277fad09c73SVivien Didelot 				    u16 vid, bool untagged)
1278fad09c73SVivien Didelot {
1279b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1280fad09c73SVivien Didelot 	int err;
1281fad09c73SVivien Didelot 
1282567aa59aSVivien Didelot 	err = mv88e6xxx_vtu_get(chip, vid, &vlan, true);
1283fad09c73SVivien Didelot 	if (err)
1284fad09c73SVivien Didelot 		return err;
1285fad09c73SVivien Didelot 
1286bd00e053SVivien Didelot 	vlan.member[port] = untagged ?
1287fad09c73SVivien Didelot 		GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1288fad09c73SVivien Didelot 		GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1289fad09c73SVivien Didelot 
12900ad5daf6SVivien Didelot 	return mv88e6xxx_vtu_loadpurge(chip, &vlan);
1291fad09c73SVivien Didelot }
1292fad09c73SVivien Didelot 
1293fad09c73SVivien Didelot static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1294fad09c73SVivien Didelot 				    const struct switchdev_obj_port_vlan *vlan,
1295fad09c73SVivien Didelot 				    struct switchdev_trans *trans)
1296fad09c73SVivien Didelot {
129704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1298fad09c73SVivien Didelot 	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1299fad09c73SVivien Didelot 	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1300fad09c73SVivien Didelot 	u16 vid;
1301fad09c73SVivien Didelot 
13023cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1303fad09c73SVivien Didelot 		return;
1304fad09c73SVivien Didelot 
1305fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1306fad09c73SVivien Didelot 
1307fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
1308fad09c73SVivien Didelot 		if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
1309fad09c73SVivien Didelot 			netdev_err(ds->ports[port].netdev,
1310fad09c73SVivien Didelot 				   "failed to add VLAN %d%c\n",
1311fad09c73SVivien Didelot 				   vid, untagged ? 'u' : 't');
1312fad09c73SVivien Didelot 
131377064f37SVivien Didelot 	if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
1314fad09c73SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
1315fad09c73SVivien Didelot 			   vlan->vid_end);
1316fad09c73SVivien Didelot 
1317fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1318fad09c73SVivien Didelot }
1319fad09c73SVivien Didelot 
1320fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
1321fad09c73SVivien Didelot 				    int port, u16 vid)
1322fad09c73SVivien Didelot {
1323fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1324b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1325fad09c73SVivien Didelot 	int i, err;
1326fad09c73SVivien Didelot 
1327567aa59aSVivien Didelot 	err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
1328fad09c73SVivien Didelot 	if (err)
1329fad09c73SVivien Didelot 		return err;
1330fad09c73SVivien Didelot 
1331fad09c73SVivien Didelot 	/* Tell switchdev if this VLAN is handled in software */
1332bd00e053SVivien Didelot 	if (vlan.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1333fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1334fad09c73SVivien Didelot 
1335bd00e053SVivien Didelot 	vlan.member[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1336fad09c73SVivien Didelot 
1337fad09c73SVivien Didelot 	/* keep the VLAN unless all ports are excluded */
1338fad09c73SVivien Didelot 	vlan.valid = false;
1339370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1340fad09c73SVivien Didelot 		if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1341fad09c73SVivien Didelot 			continue;
1342fad09c73SVivien Didelot 
1343bd00e053SVivien Didelot 		if (vlan.member[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
1344fad09c73SVivien Didelot 			vlan.valid = true;
1345fad09c73SVivien Didelot 			break;
1346fad09c73SVivien Didelot 		}
1347fad09c73SVivien Didelot 	}
1348fad09c73SVivien Didelot 
13490ad5daf6SVivien Didelot 	err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
1350fad09c73SVivien Didelot 	if (err)
1351fad09c73SVivien Didelot 		return err;
1352fad09c73SVivien Didelot 
1353e606ca36SVivien Didelot 	return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false);
1354fad09c73SVivien Didelot }
1355fad09c73SVivien Didelot 
1356fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1357fad09c73SVivien Didelot 				   const struct switchdev_obj_port_vlan *vlan)
1358fad09c73SVivien Didelot {
135904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1360fad09c73SVivien Didelot 	u16 pvid, vid;
1361fad09c73SVivien Didelot 	int err = 0;
1362fad09c73SVivien Didelot 
13633cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1364fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1365fad09c73SVivien Didelot 
1366fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1367fad09c73SVivien Didelot 
136877064f37SVivien Didelot 	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
1369fad09c73SVivien Didelot 	if (err)
1370fad09c73SVivien Didelot 		goto unlock;
1371fad09c73SVivien Didelot 
1372fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
1373fad09c73SVivien Didelot 		err = _mv88e6xxx_port_vlan_del(chip, port, vid);
1374fad09c73SVivien Didelot 		if (err)
1375fad09c73SVivien Didelot 			goto unlock;
1376fad09c73SVivien Didelot 
1377fad09c73SVivien Didelot 		if (vid == pvid) {
137877064f37SVivien Didelot 			err = mv88e6xxx_port_set_pvid(chip, port, 0);
1379fad09c73SVivien Didelot 			if (err)
1380fad09c73SVivien Didelot 				goto unlock;
1381fad09c73SVivien Didelot 		}
1382fad09c73SVivien Didelot 	}
1383fad09c73SVivien Didelot 
1384fad09c73SVivien Didelot unlock:
1385fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1386fad09c73SVivien Didelot 
1387fad09c73SVivien Didelot 	return err;
1388fad09c73SVivien Didelot }
1389fad09c73SVivien Didelot 
139083dabd1fSVivien Didelot static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
1391fad09c73SVivien Didelot 					const unsigned char *addr, u16 vid,
1392fad09c73SVivien Didelot 					u8 state)
1393fad09c73SVivien Didelot {
1394b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
139588472939SVivien Didelot 	struct mv88e6xxx_atu_entry entry;
1396fad09c73SVivien Didelot 	int err;
1397fad09c73SVivien Didelot 
1398fad09c73SVivien Didelot 	/* Null VLAN ID corresponds to the port private database */
1399fad09c73SVivien Didelot 	if (vid == 0)
1400b4e48c50SVivien Didelot 		err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
1401fad09c73SVivien Didelot 	else
1402567aa59aSVivien Didelot 		err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
1403fad09c73SVivien Didelot 	if (err)
1404fad09c73SVivien Didelot 		return err;
1405fad09c73SVivien Didelot 
1406dabc1a96SVivien Didelot 	entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
1407dabc1a96SVivien Didelot 	ether_addr_copy(entry.mac, addr);
1408dabc1a96SVivien Didelot 	eth_addr_dec(entry.mac);
1409dabc1a96SVivien Didelot 
1410dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry);
141188472939SVivien Didelot 	if (err)
141288472939SVivien Didelot 		return err;
141388472939SVivien Didelot 
1414dabc1a96SVivien Didelot 	/* Initialize a fresh ATU entry if it isn't found */
1415dabc1a96SVivien Didelot 	if (entry.state == GLOBAL_ATU_DATA_STATE_UNUSED ||
1416dabc1a96SVivien Didelot 	    !ether_addr_equal(entry.mac, addr)) {
1417dabc1a96SVivien Didelot 		memset(&entry, 0, sizeof(entry));
1418dabc1a96SVivien Didelot 		ether_addr_copy(entry.mac, addr);
1419dabc1a96SVivien Didelot 	}
1420dabc1a96SVivien Didelot 
142188472939SVivien Didelot 	/* Purge the ATU entry only if no port is using it anymore */
142288472939SVivien Didelot 	if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
142301bd96c8SVivien Didelot 		entry.portvec &= ~BIT(port);
142401bd96c8SVivien Didelot 		if (!entry.portvec)
142588472939SVivien Didelot 			entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
142688472939SVivien Didelot 	} else {
142701bd96c8SVivien Didelot 		entry.portvec |= BIT(port);
1428fad09c73SVivien Didelot 		entry.state = state;
1429fad09c73SVivien Didelot 	}
1430fad09c73SVivien Didelot 
14319c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
1432fad09c73SVivien Didelot }
1433fad09c73SVivien Didelot 
1434fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
1435fad09c73SVivien Didelot 				      const struct switchdev_obj_port_fdb *fdb,
1436fad09c73SVivien Didelot 				      struct switchdev_trans *trans)
1437fad09c73SVivien Didelot {
1438fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
1439fad09c73SVivien Didelot 	 * so skip the prepare phase.
1440fad09c73SVivien Didelot 	 */
1441fad09c73SVivien Didelot 	return 0;
1442fad09c73SVivien Didelot }
1443fad09c73SVivien Didelot 
1444fad09c73SVivien Didelot static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
1445fad09c73SVivien Didelot 				   const struct switchdev_obj_port_fdb *fdb,
1446fad09c73SVivien Didelot 				   struct switchdev_trans *trans)
1447fad09c73SVivien Didelot {
144804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1449fad09c73SVivien Didelot 
1450fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
145183dabd1fSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
145283dabd1fSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_UC_STATIC))
145383dabd1fSVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
1454fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1455fad09c73SVivien Didelot }
1456fad09c73SVivien Didelot 
1457fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
1458fad09c73SVivien Didelot 				  const struct switchdev_obj_port_fdb *fdb)
1459fad09c73SVivien Didelot {
146004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
146183dabd1fSVivien Didelot 	int err;
1462fad09c73SVivien Didelot 
1463fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
146483dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
1465fad09c73SVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
1466fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1467fad09c73SVivien Didelot 
146883dabd1fSVivien Didelot 	return err;
1469fad09c73SVivien Didelot }
1470fad09c73SVivien Didelot 
147183dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
1472fad09c73SVivien Didelot 				      u16 fid, u16 vid, int port,
147383dabd1fSVivien Didelot 				      struct switchdev_obj *obj,
1474438ff537SVivien Didelot 				      switchdev_obj_dump_cb_t *cb)
1475fad09c73SVivien Didelot {
1476dabc1a96SVivien Didelot 	struct mv88e6xxx_atu_entry addr;
1477fad09c73SVivien Didelot 	int err;
1478fad09c73SVivien Didelot 
1479dabc1a96SVivien Didelot 	addr.state = GLOBAL_ATU_DATA_STATE_UNUSED;
1480dabc1a96SVivien Didelot 	eth_broadcast_addr(addr.mac);
1481fad09c73SVivien Didelot 
1482fad09c73SVivien Didelot 	do {
1483dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
1484fad09c73SVivien Didelot 		if (err)
148583dabd1fSVivien Didelot 			return err;
1486fad09c73SVivien Didelot 
1487fad09c73SVivien Didelot 		if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
1488fad09c73SVivien Didelot 			break;
1489fad09c73SVivien Didelot 
149001bd96c8SVivien Didelot 		if (addr.trunk || (addr.portvec & BIT(port)) == 0)
149183dabd1fSVivien Didelot 			continue;
1492fad09c73SVivien Didelot 
149383dabd1fSVivien Didelot 		if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
149483dabd1fSVivien Didelot 			struct switchdev_obj_port_fdb *fdb;
149583dabd1fSVivien Didelot 
149683dabd1fSVivien Didelot 			if (!is_unicast_ether_addr(addr.mac))
149783dabd1fSVivien Didelot 				continue;
149883dabd1fSVivien Didelot 
149983dabd1fSVivien Didelot 			fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
1500fad09c73SVivien Didelot 			fdb->vid = vid;
1501fad09c73SVivien Didelot 			ether_addr_copy(fdb->addr, addr.mac);
150283dabd1fSVivien Didelot 			if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
150383dabd1fSVivien Didelot 				fdb->ndm_state = NUD_NOARP;
150483dabd1fSVivien Didelot 			else
150583dabd1fSVivien Didelot 				fdb->ndm_state = NUD_REACHABLE;
15067df8fbddSVivien Didelot 		} else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
15077df8fbddSVivien Didelot 			struct switchdev_obj_port_mdb *mdb;
15087df8fbddSVivien Didelot 
15097df8fbddSVivien Didelot 			if (!is_multicast_ether_addr(addr.mac))
15107df8fbddSVivien Didelot 				continue;
15117df8fbddSVivien Didelot 
15127df8fbddSVivien Didelot 			mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
15137df8fbddSVivien Didelot 			mdb->vid = vid;
15147df8fbddSVivien Didelot 			ether_addr_copy(mdb->addr, addr.mac);
151583dabd1fSVivien Didelot 		} else {
151683dabd1fSVivien Didelot 			return -EOPNOTSUPP;
1517fad09c73SVivien Didelot 		}
151883dabd1fSVivien Didelot 
151983dabd1fSVivien Didelot 		err = cb(obj);
152083dabd1fSVivien Didelot 		if (err)
152183dabd1fSVivien Didelot 			return err;
1522fad09c73SVivien Didelot 	} while (!is_broadcast_ether_addr(addr.mac));
1523fad09c73SVivien Didelot 
1524fad09c73SVivien Didelot 	return err;
1525fad09c73SVivien Didelot }
1526fad09c73SVivien Didelot 
152783dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
152883dabd1fSVivien Didelot 				  struct switchdev_obj *obj,
1529438ff537SVivien Didelot 				  switchdev_obj_dump_cb_t *cb)
153083dabd1fSVivien Didelot {
1531b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
15323cf3c846SVivien Didelot 		.vid = chip->info->max_vid,
153383dabd1fSVivien Didelot 	};
153483dabd1fSVivien Didelot 	u16 fid;
153583dabd1fSVivien Didelot 	int err;
153683dabd1fSVivien Didelot 
153783dabd1fSVivien Didelot 	/* Dump port's default Filtering Information Database (VLAN ID 0) */
1538b4e48c50SVivien Didelot 	err = mv88e6xxx_port_get_fid(chip, port, &fid);
153983dabd1fSVivien Didelot 	if (err)
154083dabd1fSVivien Didelot 		return err;
154183dabd1fSVivien Didelot 
154283dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
154383dabd1fSVivien Didelot 	if (err)
154483dabd1fSVivien Didelot 		return err;
154583dabd1fSVivien Didelot 
154683dabd1fSVivien Didelot 	/* Dump VLANs' Filtering Information Databases */
154783dabd1fSVivien Didelot 	do {
1548f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &vlan);
154983dabd1fSVivien Didelot 		if (err)
155083dabd1fSVivien Didelot 			return err;
155183dabd1fSVivien Didelot 
155283dabd1fSVivien Didelot 		if (!vlan.valid)
155383dabd1fSVivien Didelot 			break;
155483dabd1fSVivien Didelot 
155583dabd1fSVivien Didelot 		err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
155683dabd1fSVivien Didelot 						 obj, cb);
155783dabd1fSVivien Didelot 		if (err)
155883dabd1fSVivien Didelot 			return err;
15593cf3c846SVivien Didelot 	} while (vlan.vid < chip->info->max_vid);
156083dabd1fSVivien Didelot 
156183dabd1fSVivien Didelot 	return err;
156283dabd1fSVivien Didelot }
156383dabd1fSVivien Didelot 
1564fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
1565fad09c73SVivien Didelot 				   struct switchdev_obj_port_fdb *fdb,
1566438ff537SVivien Didelot 				   switchdev_obj_dump_cb_t *cb)
1567fad09c73SVivien Didelot {
156804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1569fad09c73SVivien Didelot 	int err;
1570fad09c73SVivien Didelot 
1571fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
157283dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
1573fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1574fad09c73SVivien Didelot 
1575fad09c73SVivien Didelot 	return err;
1576fad09c73SVivien Didelot }
1577fad09c73SVivien Didelot 
1578240ea3efSVivien Didelot static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
1579240ea3efSVivien Didelot 				struct net_device *br)
1580240ea3efSVivien Didelot {
1581e96a6e02SVivien Didelot 	struct dsa_switch *ds;
1582240ea3efSVivien Didelot 	int port;
1583e96a6e02SVivien Didelot 	int dev;
1584240ea3efSVivien Didelot 	int err;
1585240ea3efSVivien Didelot 
1586240ea3efSVivien Didelot 	/* Remap the Port VLAN of each local bridge group member */
1587240ea3efSVivien Didelot 	for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
1588240ea3efSVivien Didelot 		if (chip->ds->ports[port].bridge_dev == br) {
1589240ea3efSVivien Didelot 			err = mv88e6xxx_port_vlan_map(chip, port);
1590240ea3efSVivien Didelot 			if (err)
1591240ea3efSVivien Didelot 				return err;
1592240ea3efSVivien Didelot 		}
1593240ea3efSVivien Didelot 	}
1594240ea3efSVivien Didelot 
1595e96a6e02SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
1596e96a6e02SVivien Didelot 		return 0;
1597e96a6e02SVivien Didelot 
1598e96a6e02SVivien Didelot 	/* Remap the Port VLAN of each cross-chip bridge group member */
1599e96a6e02SVivien Didelot 	for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) {
1600e96a6e02SVivien Didelot 		ds = chip->ds->dst->ds[dev];
1601e96a6e02SVivien Didelot 		if (!ds)
1602e96a6e02SVivien Didelot 			break;
1603e96a6e02SVivien Didelot 
1604e96a6e02SVivien Didelot 		for (port = 0; port < ds->num_ports; ++port) {
1605e96a6e02SVivien Didelot 			if (ds->ports[port].bridge_dev == br) {
1606e96a6e02SVivien Didelot 				err = mv88e6xxx_pvt_map(chip, dev, port);
1607e96a6e02SVivien Didelot 				if (err)
1608e96a6e02SVivien Didelot 					return err;
1609e96a6e02SVivien Didelot 			}
1610e96a6e02SVivien Didelot 		}
1611e96a6e02SVivien Didelot 	}
1612e96a6e02SVivien Didelot 
1613240ea3efSVivien Didelot 	return 0;
1614240ea3efSVivien Didelot }
1615240ea3efSVivien Didelot 
1616fad09c73SVivien Didelot static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
1617fae8a25eSVivien Didelot 				      struct net_device *br)
1618fad09c73SVivien Didelot {
161904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1620240ea3efSVivien Didelot 	int err;
1621fad09c73SVivien Didelot 
1622fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1623240ea3efSVivien Didelot 	err = mv88e6xxx_bridge_map(chip, br);
1624fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1625fad09c73SVivien Didelot 
1626fad09c73SVivien Didelot 	return err;
1627fad09c73SVivien Didelot }
1628fad09c73SVivien Didelot 
1629f123f2fbSVivien Didelot static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
1630f123f2fbSVivien Didelot 					struct net_device *br)
1631fad09c73SVivien Didelot {
163204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1633fad09c73SVivien Didelot 
1634fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1635240ea3efSVivien Didelot 	if (mv88e6xxx_bridge_map(chip, br) ||
1636240ea3efSVivien Didelot 	    mv88e6xxx_port_vlan_map(chip, port))
1637240ea3efSVivien Didelot 		dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");
1638fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1639fad09c73SVivien Didelot }
1640fad09c73SVivien Didelot 
1641aec5ac88SVivien Didelot static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev,
1642aec5ac88SVivien Didelot 					   int port, struct net_device *br)
1643aec5ac88SVivien Didelot {
1644aec5ac88SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1645aec5ac88SVivien Didelot 	int err;
1646aec5ac88SVivien Didelot 
1647aec5ac88SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
1648aec5ac88SVivien Didelot 		return 0;
1649aec5ac88SVivien Didelot 
1650aec5ac88SVivien Didelot 	mutex_lock(&chip->reg_lock);
1651aec5ac88SVivien Didelot 	err = mv88e6xxx_pvt_map(chip, dev, port);
1652aec5ac88SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1653aec5ac88SVivien Didelot 
1654aec5ac88SVivien Didelot 	return err;
1655aec5ac88SVivien Didelot }
1656aec5ac88SVivien Didelot 
1657aec5ac88SVivien Didelot static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev,
1658aec5ac88SVivien Didelot 					     int port, struct net_device *br)
1659aec5ac88SVivien Didelot {
1660aec5ac88SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1661aec5ac88SVivien Didelot 
1662aec5ac88SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
1663aec5ac88SVivien Didelot 		return;
1664aec5ac88SVivien Didelot 
1665aec5ac88SVivien Didelot 	mutex_lock(&chip->reg_lock);
1666aec5ac88SVivien Didelot 	if (mv88e6xxx_pvt_map(chip, dev, port))
1667aec5ac88SVivien Didelot 		dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n");
1668aec5ac88SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1669aec5ac88SVivien Didelot }
1670aec5ac88SVivien Didelot 
167117e708baSVivien Didelot static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip)
167217e708baSVivien Didelot {
167317e708baSVivien Didelot 	if (chip->info->ops->reset)
167417e708baSVivien Didelot 		return chip->info->ops->reset(chip);
167517e708baSVivien Didelot 
167617e708baSVivien Didelot 	return 0;
167717e708baSVivien Didelot }
167817e708baSVivien Didelot 
1679309eca6dSVivien Didelot static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip)
1680309eca6dSVivien Didelot {
1681309eca6dSVivien Didelot 	struct gpio_desc *gpiod = chip->reset;
1682309eca6dSVivien Didelot 
1683309eca6dSVivien Didelot 	/* If there is a GPIO connected to the reset pin, toggle it */
1684309eca6dSVivien Didelot 	if (gpiod) {
1685309eca6dSVivien Didelot 		gpiod_set_value_cansleep(gpiod, 1);
1686309eca6dSVivien Didelot 		usleep_range(10000, 20000);
1687309eca6dSVivien Didelot 		gpiod_set_value_cansleep(gpiod, 0);
1688309eca6dSVivien Didelot 		usleep_range(10000, 20000);
1689309eca6dSVivien Didelot 	}
1690309eca6dSVivien Didelot }
1691309eca6dSVivien Didelot 
16924ac4b5a6SVivien Didelot static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip)
16934ac4b5a6SVivien Didelot {
16944ac4b5a6SVivien Didelot 	int i, err;
16954ac4b5a6SVivien Didelot 
16964ac4b5a6SVivien Didelot 	/* Set all ports to the Disabled state */
16974ac4b5a6SVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
16984ac4b5a6SVivien Didelot 		err = mv88e6xxx_port_set_state(chip, i,
16994ac4b5a6SVivien Didelot 					       PORT_CONTROL_STATE_DISABLED);
17004ac4b5a6SVivien Didelot 		if (err)
17014ac4b5a6SVivien Didelot 			return err;
17024ac4b5a6SVivien Didelot 	}
17034ac4b5a6SVivien Didelot 
17044ac4b5a6SVivien Didelot 	/* Wait for transmit queues to drain,
17054ac4b5a6SVivien Didelot 	 * i.e. 2ms for a maximum frame to be transmitted at 10 Mbps.
17064ac4b5a6SVivien Didelot 	 */
17074ac4b5a6SVivien Didelot 	usleep_range(2000, 4000);
17084ac4b5a6SVivien Didelot 
17094ac4b5a6SVivien Didelot 	return 0;
17104ac4b5a6SVivien Didelot }
17114ac4b5a6SVivien Didelot 
1712fad09c73SVivien Didelot static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
1713fad09c73SVivien Didelot {
1714a935c052SVivien Didelot 	int err;
1715fad09c73SVivien Didelot 
17164ac4b5a6SVivien Didelot 	err = mv88e6xxx_disable_ports(chip);
17170e7b9925SAndrew Lunn 	if (err)
17180e7b9925SAndrew Lunn 		return err;
1719fad09c73SVivien Didelot 
1720309eca6dSVivien Didelot 	mv88e6xxx_hardware_reset(chip);
1721fad09c73SVivien Didelot 
172217e708baSVivien Didelot 	return mv88e6xxx_software_reset(chip);
1723fad09c73SVivien Didelot }
1724fad09c73SVivien Didelot 
17254314557cSVivien Didelot static int mv88e6xxx_set_port_mode(struct mv88e6xxx_chip *chip, int port,
17264314557cSVivien Didelot 				   enum mv88e6xxx_frame_mode frame, u16 egress,
17274314557cSVivien Didelot 				   u16 etype)
172856995cbcSAndrew Lunn {
172956995cbcSAndrew Lunn 	int err;
173056995cbcSAndrew Lunn 
17314314557cSVivien Didelot 	if (!chip->info->ops->port_set_frame_mode)
17324314557cSVivien Didelot 		return -EOPNOTSUPP;
17334314557cSVivien Didelot 
17344314557cSVivien Didelot 	err = mv88e6xxx_port_set_egress_mode(chip, port, egress);
173556995cbcSAndrew Lunn 	if (err)
173656995cbcSAndrew Lunn 		return err;
173756995cbcSAndrew Lunn 
17384314557cSVivien Didelot 	err = chip->info->ops->port_set_frame_mode(chip, port, frame);
17394314557cSVivien Didelot 	if (err)
17404314557cSVivien Didelot 		return err;
17414314557cSVivien Didelot 
17424314557cSVivien Didelot 	if (chip->info->ops->port_set_ether_type)
17434314557cSVivien Didelot 		return chip->info->ops->port_set_ether_type(chip, port, etype);
17444314557cSVivien Didelot 
17454314557cSVivien Didelot 	return 0;
17464314557cSVivien Didelot }
17474314557cSVivien Didelot 
17484314557cSVivien Didelot static int mv88e6xxx_set_port_mode_normal(struct mv88e6xxx_chip *chip, int port)
17494314557cSVivien Didelot {
17504314557cSVivien Didelot 	return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_NORMAL,
17514314557cSVivien Didelot 				       PORT_CONTROL_EGRESS_UNMODIFIED,
17524314557cSVivien Didelot 				       PORT_ETH_TYPE_DEFAULT);
17534314557cSVivien Didelot }
17544314557cSVivien Didelot 
17554314557cSVivien Didelot static int mv88e6xxx_set_port_mode_dsa(struct mv88e6xxx_chip *chip, int port)
17564314557cSVivien Didelot {
17574314557cSVivien Didelot 	return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_DSA,
17584314557cSVivien Didelot 				       PORT_CONTROL_EGRESS_UNMODIFIED,
17594314557cSVivien Didelot 				       PORT_ETH_TYPE_DEFAULT);
17604314557cSVivien Didelot }
17614314557cSVivien Didelot 
17624314557cSVivien Didelot static int mv88e6xxx_set_port_mode_edsa(struct mv88e6xxx_chip *chip, int port)
17634314557cSVivien Didelot {
17644314557cSVivien Didelot 	return mv88e6xxx_set_port_mode(chip, port,
17654314557cSVivien Didelot 				       MV88E6XXX_FRAME_MODE_ETHERTYPE,
17664314557cSVivien Didelot 				       PORT_CONTROL_EGRESS_ADD_TAG, ETH_P_EDSA);
17674314557cSVivien Didelot }
17684314557cSVivien Didelot 
17694314557cSVivien Didelot static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port)
17704314557cSVivien Didelot {
17714314557cSVivien Didelot 	if (dsa_is_dsa_port(chip->ds, port))
17724314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_dsa(chip, port);
17734314557cSVivien Didelot 
17744314557cSVivien Didelot 	if (dsa_is_normal_port(chip->ds, port))
17754314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_normal(chip, port);
17764314557cSVivien Didelot 
17774314557cSVivien Didelot 	/* Setup CPU port mode depending on its supported tag format */
17784314557cSVivien Didelot 	if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA)
17794314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_dsa(chip, port);
17804314557cSVivien Didelot 
17814314557cSVivien Didelot 	if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA)
17824314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_edsa(chip, port);
17834314557cSVivien Didelot 
17844314557cSVivien Didelot 	return -EINVAL;
17854314557cSVivien Didelot }
17864314557cSVivien Didelot 
1787ea698f4fSVivien Didelot static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port)
1788ea698f4fSVivien Didelot {
1789ea698f4fSVivien Didelot 	bool message = dsa_is_dsa_port(chip->ds, port);
1790ea698f4fSVivien Didelot 
1791ea698f4fSVivien Didelot 	return mv88e6xxx_port_set_message_port(chip, port, message);
1792ea698f4fSVivien Didelot }
1793ea698f4fSVivien Didelot 
1794601aeed3SVivien Didelot static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port)
1795601aeed3SVivien Didelot {
1796601aeed3SVivien Didelot 	bool flood = port == dsa_upstream_port(chip->ds);
1797601aeed3SVivien Didelot 
1798601aeed3SVivien Didelot 	/* Upstream ports flood frames with unknown unicast or multicast DA */
1799601aeed3SVivien Didelot 	if (chip->info->ops->port_set_egress_floods)
1800601aeed3SVivien Didelot 		return chip->info->ops->port_set_egress_floods(chip, port,
1801601aeed3SVivien Didelot 							       flood, flood);
1802601aeed3SVivien Didelot 
1803601aeed3SVivien Didelot 	return 0;
1804601aeed3SVivien Didelot }
1805601aeed3SVivien Didelot 
18066d91782fSAndrew Lunn static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
18076d91782fSAndrew Lunn 				  bool on)
18086d91782fSAndrew Lunn {
1809523a8904SVivien Didelot 	if (chip->info->ops->serdes_power)
1810523a8904SVivien Didelot 		return chip->info->ops->serdes_power(chip, port, on);
18116d91782fSAndrew Lunn 
1812523a8904SVivien Didelot 	return 0;
18136d91782fSAndrew Lunn }
18146d91782fSAndrew Lunn 
1815fad09c73SVivien Didelot static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
1816fad09c73SVivien Didelot {
1817fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
18180e7b9925SAndrew Lunn 	int err;
1819fad09c73SVivien Didelot 	u16 reg;
1820fad09c73SVivien Didelot 
1821d78343d2SVivien Didelot 	/* MAC Forcing register: don't force link, speed, duplex or flow control
1822d78343d2SVivien Didelot 	 * state to any particular values on physical ports, but force the CPU
1823d78343d2SVivien Didelot 	 * port and all DSA ports to their maximum bandwidth and full duplex.
1824fad09c73SVivien Didelot 	 */
1825d78343d2SVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
1826d78343d2SVivien Didelot 		err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP,
1827d78343d2SVivien Didelot 					       SPEED_MAX, DUPLEX_FULL,
1828d78343d2SVivien Didelot 					       PHY_INTERFACE_MODE_NA);
1829fad09c73SVivien Didelot 	else
1830d78343d2SVivien Didelot 		err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED,
1831d78343d2SVivien Didelot 					       SPEED_UNFORCED, DUPLEX_UNFORCED,
1832d78343d2SVivien Didelot 					       PHY_INTERFACE_MODE_NA);
18330e7b9925SAndrew Lunn 	if (err)
18340e7b9925SAndrew Lunn 		return err;
1835fad09c73SVivien Didelot 
1836fad09c73SVivien Didelot 	/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
1837fad09c73SVivien Didelot 	 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
1838fad09c73SVivien Didelot 	 * tunneling, determine priority by looking at 802.1p and IP
1839fad09c73SVivien Didelot 	 * priority fields (IP prio has precedence), and set STP state
1840fad09c73SVivien Didelot 	 * to Forwarding.
1841fad09c73SVivien Didelot 	 *
1842fad09c73SVivien Didelot 	 * If this is the CPU link, use DSA or EDSA tagging depending
1843fad09c73SVivien Didelot 	 * on which tagging mode was configured.
1844fad09c73SVivien Didelot 	 *
1845fad09c73SVivien Didelot 	 * If this is a link to another switch, use DSA tagging mode.
1846fad09c73SVivien Didelot 	 *
1847fad09c73SVivien Didelot 	 * If this is the upstream port for this switch, enable
1848fad09c73SVivien Didelot 	 * forwarding of unknown unicasts and multicasts.
1849fad09c73SVivien Didelot 	 */
1850fad09c73SVivien Didelot 	reg = PORT_CONTROL_IGMP_MLD_SNOOP |
1851fad09c73SVivien Didelot 		PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
1852fad09c73SVivien Didelot 		PORT_CONTROL_STATE_FORWARDING;
18530e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
18540e7b9925SAndrew Lunn 	if (err)
18550e7b9925SAndrew Lunn 		return err;
185656995cbcSAndrew Lunn 
1857601aeed3SVivien Didelot 	err = mv88e6xxx_setup_port_mode(chip, port);
185856995cbcSAndrew Lunn 	if (err)
185956995cbcSAndrew Lunn 		return err;
1860fad09c73SVivien Didelot 
1861601aeed3SVivien Didelot 	err = mv88e6xxx_setup_egress_floods(chip, port);
18624314557cSVivien Didelot 	if (err)
18634314557cSVivien Didelot 		return err;
18644314557cSVivien Didelot 
186504aca993SAndrew Lunn 	/* Enable the SERDES interface for DSA and CPU ports. Normal
186604aca993SAndrew Lunn 	 * ports SERDES are enabled when the port is enabled, thus
186704aca993SAndrew Lunn 	 * saving a bit of power.
1868fad09c73SVivien Didelot 	 */
186904aca993SAndrew Lunn 	if ((dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) {
18706d91782fSAndrew Lunn 		err = mv88e6xxx_serdes_power(chip, port, true);
18710e7b9925SAndrew Lunn 		if (err)
18720e7b9925SAndrew Lunn 			return err;
187304aca993SAndrew Lunn 	}
1874fad09c73SVivien Didelot 
1875fad09c73SVivien Didelot 	/* Port Control 2: don't force a good FCS, set the maximum frame size to
1876fad09c73SVivien Didelot 	 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
1877fad09c73SVivien Didelot 	 * untagged frames on this port, do a destination address lookup on all
1878fad09c73SVivien Didelot 	 * received packets as usual, disable ARP mirroring and don't send a
1879fad09c73SVivien Didelot 	 * copy of all transmitted/received frames on this port to the CPU.
1880fad09c73SVivien Didelot 	 */
1881a23b2961SAndrew Lunn 	err = mv88e6xxx_port_set_map_da(chip, port);
1882a23b2961SAndrew Lunn 	if (err)
1883a23b2961SAndrew Lunn 		return err;
1884a23b2961SAndrew Lunn 
1885fad09c73SVivien Didelot 	reg = 0;
1886a23b2961SAndrew Lunn 	if (chip->info->ops->port_set_upstream_port) {
1887a23b2961SAndrew Lunn 		err = chip->info->ops->port_set_upstream_port(
1888a23b2961SAndrew Lunn 			chip, port, dsa_upstream_port(ds));
18890e7b9925SAndrew Lunn 		if (err)
18900e7b9925SAndrew Lunn 			return err;
1891fad09c73SVivien Didelot 	}
1892fad09c73SVivien Didelot 
1893a23b2961SAndrew Lunn 	err = mv88e6xxx_port_set_8021q_mode(chip, port,
1894a23b2961SAndrew Lunn 					    PORT_CONTROL_2_8021Q_DISABLED);
1895a23b2961SAndrew Lunn 	if (err)
1896a23b2961SAndrew Lunn 		return err;
1897a23b2961SAndrew Lunn 
18985f436666SAndrew Lunn 	if (chip->info->ops->port_jumbo_config) {
18995f436666SAndrew Lunn 		err = chip->info->ops->port_jumbo_config(chip, port);
19005f436666SAndrew Lunn 		if (err)
19015f436666SAndrew Lunn 			return err;
19025f436666SAndrew Lunn 	}
19035f436666SAndrew Lunn 
1904fad09c73SVivien Didelot 	/* Port Association Vector: when learning source addresses
1905fad09c73SVivien Didelot 	 * of packets, add the address to the address database using
1906fad09c73SVivien Didelot 	 * a port bitmap that has only the bit for this port set and
1907fad09c73SVivien Didelot 	 * the other bits clear.
1908fad09c73SVivien Didelot 	 */
1909fad09c73SVivien Didelot 	reg = 1 << port;
1910fad09c73SVivien Didelot 	/* Disable learning for CPU port */
1911fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port))
1912fad09c73SVivien Didelot 		reg = 0;
1913fad09c73SVivien Didelot 
19140e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
19150e7b9925SAndrew Lunn 	if (err)
19160e7b9925SAndrew Lunn 		return err;
1917fad09c73SVivien Didelot 
1918fad09c73SVivien Didelot 	/* Egress rate control 2: disable egress rate control. */
19190e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
19200e7b9925SAndrew Lunn 	if (err)
19210e7b9925SAndrew Lunn 		return err;
1922fad09c73SVivien Didelot 
1923b35d322aSAndrew Lunn 	if (chip->info->ops->port_pause_config) {
1924b35d322aSAndrew Lunn 		err = chip->info->ops->port_pause_config(chip, port);
1925b35d322aSAndrew Lunn 		if (err)
1926b35d322aSAndrew Lunn 			return err;
1927b35d322aSAndrew Lunn 	}
1928b35d322aSAndrew Lunn 
1929c8c94891SVivien Didelot 	if (chip->info->ops->port_disable_learn_limit) {
1930c8c94891SVivien Didelot 		err = chip->info->ops->port_disable_learn_limit(chip, port);
1931c8c94891SVivien Didelot 		if (err)
1932c8c94891SVivien Didelot 			return err;
1933c8c94891SVivien Didelot 	}
1934c8c94891SVivien Didelot 
19359dbfb4e1SVivien Didelot 	if (chip->info->ops->port_disable_pri_override) {
19369dbfb4e1SVivien Didelot 		err = chip->info->ops->port_disable_pri_override(chip, port);
19370e7b9925SAndrew Lunn 		if (err)
19380e7b9925SAndrew Lunn 			return err;
1939ef0a7318SAndrew Lunn 	}
19402bbb33beSAndrew Lunn 
1941ef0a7318SAndrew Lunn 	if (chip->info->ops->port_tag_remap) {
1942ef0a7318SAndrew Lunn 		err = chip->info->ops->port_tag_remap(chip, port);
19430e7b9925SAndrew Lunn 		if (err)
19440e7b9925SAndrew Lunn 			return err;
1945fad09c73SVivien Didelot 	}
1946fad09c73SVivien Didelot 
1947ef70b111SAndrew Lunn 	if (chip->info->ops->port_egress_rate_limiting) {
1948ef70b111SAndrew Lunn 		err = chip->info->ops->port_egress_rate_limiting(chip, port);
19490e7b9925SAndrew Lunn 		if (err)
19500e7b9925SAndrew Lunn 			return err;
1951fad09c73SVivien Didelot 	}
1952fad09c73SVivien Didelot 
1953ea698f4fSVivien Didelot 	err = mv88e6xxx_setup_message_port(chip, port);
19540e7b9925SAndrew Lunn 	if (err)
19550e7b9925SAndrew Lunn 		return err;
1956fad09c73SVivien Didelot 
1957fad09c73SVivien Didelot 	/* Port based VLAN map: give each port the same default address
1958fad09c73SVivien Didelot 	 * database, and allow bidirectional communication between the
1959fad09c73SVivien Didelot 	 * CPU and DSA port(s), and the other ports.
1960fad09c73SVivien Didelot 	 */
1961b4e48c50SVivien Didelot 	err = mv88e6xxx_port_set_fid(chip, port, 0);
19620e7b9925SAndrew Lunn 	if (err)
19630e7b9925SAndrew Lunn 		return err;
1964fad09c73SVivien Didelot 
1965240ea3efSVivien Didelot 	err = mv88e6xxx_port_vlan_map(chip, port);
19660e7b9925SAndrew Lunn 	if (err)
19670e7b9925SAndrew Lunn 		return err;
1968fad09c73SVivien Didelot 
1969fad09c73SVivien Didelot 	/* Default VLAN ID and priority: don't set a default VLAN
1970fad09c73SVivien Didelot 	 * ID, and set the default packet priority to zero.
1971fad09c73SVivien Didelot 	 */
19720e7b9925SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
1973fad09c73SVivien Didelot }
1974fad09c73SVivien Didelot 
197504aca993SAndrew Lunn static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port,
197604aca993SAndrew Lunn 				 struct phy_device *phydev)
197704aca993SAndrew Lunn {
197804aca993SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
1979523a8904SVivien Didelot 	int err;
198004aca993SAndrew Lunn 
198104aca993SAndrew Lunn 	mutex_lock(&chip->reg_lock);
1982523a8904SVivien Didelot 	err = mv88e6xxx_serdes_power(chip, port, true);
198304aca993SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
198404aca993SAndrew Lunn 
198504aca993SAndrew Lunn 	return err;
198604aca993SAndrew Lunn }
198704aca993SAndrew Lunn 
198804aca993SAndrew Lunn static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port,
198904aca993SAndrew Lunn 				   struct phy_device *phydev)
199004aca993SAndrew Lunn {
199104aca993SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
199204aca993SAndrew Lunn 
199304aca993SAndrew Lunn 	mutex_lock(&chip->reg_lock);
1994523a8904SVivien Didelot 	if (mv88e6xxx_serdes_power(chip, port, false))
1995523a8904SVivien Didelot 		dev_err(chip->dev, "failed to power off SERDES\n");
199604aca993SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
199704aca993SAndrew Lunn }
199804aca993SAndrew Lunn 
1999aa0938c6SWei Yongjun static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
20003b4caa1bSVivien Didelot {
20013b4caa1bSVivien Didelot 	int err;
20023b4caa1bSVivien Didelot 
2003a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
20043b4caa1bSVivien Didelot 	if (err)
20053b4caa1bSVivien Didelot 		return err;
20063b4caa1bSVivien Didelot 
2007a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
20083b4caa1bSVivien Didelot 	if (err)
20093b4caa1bSVivien Didelot 		return err;
20103b4caa1bSVivien Didelot 
2011a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
2012a935c052SVivien Didelot 	if (err)
2013a935c052SVivien Didelot 		return err;
2014a935c052SVivien Didelot 
2015a935c052SVivien Didelot 	return 0;
20163b4caa1bSVivien Didelot }
20173b4caa1bSVivien Didelot 
20182cfcd964SVivien Didelot static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
20192cfcd964SVivien Didelot 				     unsigned int ageing_time)
20202cfcd964SVivien Didelot {
202104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
20222cfcd964SVivien Didelot 	int err;
20232cfcd964SVivien Didelot 
20242cfcd964SVivien Didelot 	mutex_lock(&chip->reg_lock);
2025720c6343SVivien Didelot 	err = mv88e6xxx_g1_atu_set_age_time(chip, ageing_time);
20262cfcd964SVivien Didelot 	mutex_unlock(&chip->reg_lock);
20272cfcd964SVivien Didelot 
20282cfcd964SVivien Didelot 	return err;
20292cfcd964SVivien Didelot }
20302cfcd964SVivien Didelot 
20319729934cSVivien Didelot static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
2032fad09c73SVivien Didelot {
2033fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
2034fad09c73SVivien Didelot 	u32 upstream_port = dsa_upstream_port(ds);
2035fad09c73SVivien Didelot 	int err;
2036fad09c73SVivien Didelot 
203733641994SAndrew Lunn 	if (chip->info->ops->g1_set_cpu_port) {
203833641994SAndrew Lunn 		err = chip->info->ops->g1_set_cpu_port(chip, upstream_port);
2039fad09c73SVivien Didelot 		if (err)
2040fad09c73SVivien Didelot 			return err;
204133641994SAndrew Lunn 	}
204233641994SAndrew Lunn 
204333641994SAndrew Lunn 	if (chip->info->ops->g1_set_egress_port) {
204433641994SAndrew Lunn 		err = chip->info->ops->g1_set_egress_port(chip, upstream_port);
204533641994SAndrew Lunn 		if (err)
204633641994SAndrew Lunn 			return err;
204733641994SAndrew Lunn 	}
2048fad09c73SVivien Didelot 
2049fad09c73SVivien Didelot 	/* Disable remote management, and set the switch's DSA device number. */
2050a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
2051fad09c73SVivien Didelot 				 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2052fad09c73SVivien Didelot 				 (ds->index & 0x1f));
2053fad09c73SVivien Didelot 	if (err)
2054fad09c73SVivien Didelot 		return err;
2055fad09c73SVivien Didelot 
2056fad09c73SVivien Didelot 	/* Configure the IP ToS mapping registers. */
2057a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
2058fad09c73SVivien Didelot 	if (err)
2059fad09c73SVivien Didelot 		return err;
2060a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
2061fad09c73SVivien Didelot 	if (err)
2062fad09c73SVivien Didelot 		return err;
2063a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
2064fad09c73SVivien Didelot 	if (err)
2065fad09c73SVivien Didelot 		return err;
2066a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
2067fad09c73SVivien Didelot 	if (err)
2068fad09c73SVivien Didelot 		return err;
2069a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
2070fad09c73SVivien Didelot 	if (err)
2071fad09c73SVivien Didelot 		return err;
2072a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
2073fad09c73SVivien Didelot 	if (err)
2074fad09c73SVivien Didelot 		return err;
2075a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
2076fad09c73SVivien Didelot 	if (err)
2077fad09c73SVivien Didelot 		return err;
2078a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
2079fad09c73SVivien Didelot 	if (err)
2080fad09c73SVivien Didelot 		return err;
2081fad09c73SVivien Didelot 
2082fad09c73SVivien Didelot 	/* Configure the IEEE 802.1p priority mapping register. */
2083a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
2084fad09c73SVivien Didelot 	if (err)
2085fad09c73SVivien Didelot 		return err;
2086fad09c73SVivien Didelot 
2087de227387SAndrew Lunn 	/* Initialize the statistics unit */
2088de227387SAndrew Lunn 	err = mv88e6xxx_stats_set_histogram(chip);
2089de227387SAndrew Lunn 	if (err)
2090de227387SAndrew Lunn 		return err;
2091de227387SAndrew Lunn 
20929729934cSVivien Didelot 	/* Clear the statistics counters for all ports */
2093a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
20949729934cSVivien Didelot 				 GLOBAL_STATS_OP_FLUSH_ALL);
20959729934cSVivien Didelot 	if (err)
20969729934cSVivien Didelot 		return err;
20979729934cSVivien Didelot 
20989729934cSVivien Didelot 	/* Wait for the flush to complete. */
20997f9ef3afSAndrew Lunn 	err = mv88e6xxx_g1_stats_wait(chip);
21009729934cSVivien Didelot 	if (err)
21019729934cSVivien Didelot 		return err;
21029729934cSVivien Didelot 
21039729934cSVivien Didelot 	return 0;
21049729934cSVivien Didelot }
21059729934cSVivien Didelot 
2106fad09c73SVivien Didelot static int mv88e6xxx_setup(struct dsa_switch *ds)
2107fad09c73SVivien Didelot {
210804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2109fad09c73SVivien Didelot 	int err;
2110fad09c73SVivien Didelot 	int i;
2111fad09c73SVivien Didelot 
2112fad09c73SVivien Didelot 	chip->ds = ds;
2113a3c53be5SAndrew Lunn 	ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip);
2114fad09c73SVivien Didelot 
2115fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2116fad09c73SVivien Didelot 
21179729934cSVivien Didelot 	/* Setup Switch Port Registers */
2118370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
21199729934cSVivien Didelot 		err = mv88e6xxx_setup_port(chip, i);
21209729934cSVivien Didelot 		if (err)
21219729934cSVivien Didelot 			goto unlock;
21229729934cSVivien Didelot 	}
21239729934cSVivien Didelot 
21249729934cSVivien Didelot 	/* Setup Switch Global 1 Registers */
21259729934cSVivien Didelot 	err = mv88e6xxx_g1_setup(chip);
2126fad09c73SVivien Didelot 	if (err)
2127fad09c73SVivien Didelot 		goto unlock;
2128fad09c73SVivien Didelot 
21299729934cSVivien Didelot 	/* Setup Switch Global 2 Registers */
21309729934cSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
21319729934cSVivien Didelot 		err = mv88e6xxx_g2_setup(chip);
2132fad09c73SVivien Didelot 		if (err)
2133fad09c73SVivien Didelot 			goto unlock;
2134fad09c73SVivien Didelot 	}
2135fad09c73SVivien Didelot 
21361b17aedfSVivien Didelot 	err = mv88e6xxx_phy_setup(chip);
21371b17aedfSVivien Didelot 	if (err)
21381b17aedfSVivien Didelot 		goto unlock;
21391b17aedfSVivien Didelot 
2140b486d7c9SVivien Didelot 	err = mv88e6xxx_vtu_setup(chip);
2141b486d7c9SVivien Didelot 	if (err)
2142b486d7c9SVivien Didelot 		goto unlock;
2143b486d7c9SVivien Didelot 
214481228996SVivien Didelot 	err = mv88e6xxx_pvt_setup(chip);
214581228996SVivien Didelot 	if (err)
214681228996SVivien Didelot 		goto unlock;
214781228996SVivien Didelot 
2148a2ac29d2SVivien Didelot 	err = mv88e6xxx_atu_setup(chip);
2149a2ac29d2SVivien Didelot 	if (err)
2150a2ac29d2SVivien Didelot 		goto unlock;
2151a2ac29d2SVivien Didelot 
21526e55f698SAndrew Lunn 	/* Some generations have the configuration of sending reserved
21536e55f698SAndrew Lunn 	 * management frames to the CPU in global2, others in
21546e55f698SAndrew Lunn 	 * global1. Hence it does not fit the two setup functions
21556e55f698SAndrew Lunn 	 * above.
21566e55f698SAndrew Lunn 	 */
21576e55f698SAndrew Lunn 	if (chip->info->ops->mgmt_rsvd2cpu) {
21586e55f698SAndrew Lunn 		err = chip->info->ops->mgmt_rsvd2cpu(chip);
21596e55f698SAndrew Lunn 		if (err)
21606e55f698SAndrew Lunn 			goto unlock;
21616e55f698SAndrew Lunn 	}
21626e55f698SAndrew Lunn 
2163fad09c73SVivien Didelot unlock:
2164fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2165fad09c73SVivien Didelot 
2166fad09c73SVivien Didelot 	return err;
2167fad09c73SVivien Didelot }
2168fad09c73SVivien Didelot 
21693b4caa1bSVivien Didelot static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
21703b4caa1bSVivien Didelot {
217104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
21723b4caa1bSVivien Didelot 	int err;
21733b4caa1bSVivien Didelot 
2174b073d4e2SVivien Didelot 	if (!chip->info->ops->set_switch_mac)
2175b073d4e2SVivien Didelot 		return -EOPNOTSUPP;
2176b073d4e2SVivien Didelot 
21773b4caa1bSVivien Didelot 	mutex_lock(&chip->reg_lock);
2178b073d4e2SVivien Didelot 	err = chip->info->ops->set_switch_mac(chip, addr);
21793b4caa1bSVivien Didelot 	mutex_unlock(&chip->reg_lock);
21803b4caa1bSVivien Didelot 
21813b4caa1bSVivien Didelot 	return err;
21823b4caa1bSVivien Didelot }
21833b4caa1bSVivien Didelot 
2184e57e5e77SVivien Didelot static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
2185fad09c73SVivien Didelot {
21860dd12d54SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
21870dd12d54SAndrew Lunn 	struct mv88e6xxx_chip *chip = mdio_bus->chip;
2188e57e5e77SVivien Didelot 	u16 val;
2189e57e5e77SVivien Didelot 	int err;
2190fad09c73SVivien Didelot 
2191ee26a228SAndrew Lunn 	if (!chip->info->ops->phy_read)
2192ee26a228SAndrew Lunn 		return -EOPNOTSUPP;
2193ee26a228SAndrew Lunn 
2194fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2195ee26a228SAndrew Lunn 	err = chip->info->ops->phy_read(chip, bus, phy, reg, &val);
2196fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2197e57e5e77SVivien Didelot 
2198da9f3301SAndrew Lunn 	if (reg == MII_PHYSID2) {
2199da9f3301SAndrew Lunn 		/* Some internal PHYS don't have a model number.  Use
2200da9f3301SAndrew Lunn 		 * the mv88e6390 family model number instead.
2201da9f3301SAndrew Lunn 		 */
2202da9f3301SAndrew Lunn 		if (!(val & 0x3f0))
2203da9f3301SAndrew Lunn 			val |= PORT_SWITCH_ID_PROD_NUM_6390;
2204da9f3301SAndrew Lunn 	}
2205da9f3301SAndrew Lunn 
2206e57e5e77SVivien Didelot 	return err ? err : val;
2207fad09c73SVivien Didelot }
2208fad09c73SVivien Didelot 
2209e57e5e77SVivien Didelot static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
2210fad09c73SVivien Didelot {
22110dd12d54SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
22120dd12d54SAndrew Lunn 	struct mv88e6xxx_chip *chip = mdio_bus->chip;
2213e57e5e77SVivien Didelot 	int err;
2214fad09c73SVivien Didelot 
2215ee26a228SAndrew Lunn 	if (!chip->info->ops->phy_write)
2216ee26a228SAndrew Lunn 		return -EOPNOTSUPP;
2217ee26a228SAndrew Lunn 
2218fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2219ee26a228SAndrew Lunn 	err = chip->info->ops->phy_write(chip, bus, phy, reg, val);
2220fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2221e57e5e77SVivien Didelot 
2222e57e5e77SVivien Didelot 	return err;
2223fad09c73SVivien Didelot }
2224fad09c73SVivien Didelot 
2225fad09c73SVivien Didelot static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
2226a3c53be5SAndrew Lunn 				   struct device_node *np,
2227a3c53be5SAndrew Lunn 				   bool external)
2228fad09c73SVivien Didelot {
2229fad09c73SVivien Didelot 	static int index;
22300dd12d54SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus;
2231fad09c73SVivien Didelot 	struct mii_bus *bus;
2232fad09c73SVivien Didelot 	int err;
2233fad09c73SVivien Didelot 
22340dd12d54SAndrew Lunn 	bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
2235fad09c73SVivien Didelot 	if (!bus)
2236fad09c73SVivien Didelot 		return -ENOMEM;
2237fad09c73SVivien Didelot 
22380dd12d54SAndrew Lunn 	mdio_bus = bus->priv;
2239a3c53be5SAndrew Lunn 	mdio_bus->bus = bus;
22400dd12d54SAndrew Lunn 	mdio_bus->chip = chip;
2241a3c53be5SAndrew Lunn 	INIT_LIST_HEAD(&mdio_bus->list);
2242a3c53be5SAndrew Lunn 	mdio_bus->external = external;
22430dd12d54SAndrew Lunn 
2244fad09c73SVivien Didelot 	if (np) {
2245fad09c73SVivien Didelot 		bus->name = np->full_name;
2246fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
2247fad09c73SVivien Didelot 	} else {
2248fad09c73SVivien Didelot 		bus->name = "mv88e6xxx SMI";
2249fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
2250fad09c73SVivien Didelot 	}
2251fad09c73SVivien Didelot 
2252fad09c73SVivien Didelot 	bus->read = mv88e6xxx_mdio_read;
2253fad09c73SVivien Didelot 	bus->write = mv88e6xxx_mdio_write;
2254fad09c73SVivien Didelot 	bus->parent = chip->dev;
2255fad09c73SVivien Didelot 
2256a3c53be5SAndrew Lunn 	if (np)
2257a3c53be5SAndrew Lunn 		err = of_mdiobus_register(bus, np);
2258fad09c73SVivien Didelot 	else
2259fad09c73SVivien Didelot 		err = mdiobus_register(bus);
2260fad09c73SVivien Didelot 	if (err) {
2261fad09c73SVivien Didelot 		dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
2262fad09c73SVivien Didelot 		return err;
2263fad09c73SVivien Didelot 	}
2264fad09c73SVivien Didelot 
2265a3c53be5SAndrew Lunn 	if (external)
2266a3c53be5SAndrew Lunn 		list_add_tail(&mdio_bus->list, &chip->mdios);
2267a3c53be5SAndrew Lunn 	else
2268a3c53be5SAndrew Lunn 		list_add(&mdio_bus->list, &chip->mdios);
2269a3c53be5SAndrew Lunn 
2270a3c53be5SAndrew Lunn 	return 0;
2271a3c53be5SAndrew Lunn }
2272a3c53be5SAndrew Lunn 
2273a3c53be5SAndrew Lunn static const struct of_device_id mv88e6xxx_mdio_external_match[] = {
2274a3c53be5SAndrew Lunn 	{ .compatible = "marvell,mv88e6xxx-mdio-external",
2275a3c53be5SAndrew Lunn 	  .data = (void *)true },
2276a3c53be5SAndrew Lunn 	{ },
2277a3c53be5SAndrew Lunn };
2278a3c53be5SAndrew Lunn 
2279a3c53be5SAndrew Lunn static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,
2280a3c53be5SAndrew Lunn 				    struct device_node *np)
2281a3c53be5SAndrew Lunn {
2282a3c53be5SAndrew Lunn 	const struct of_device_id *match;
2283a3c53be5SAndrew Lunn 	struct device_node *child;
2284a3c53be5SAndrew Lunn 	int err;
2285a3c53be5SAndrew Lunn 
2286a3c53be5SAndrew Lunn 	/* Always register one mdio bus for the internal/default mdio
2287a3c53be5SAndrew Lunn 	 * bus. This maybe represented in the device tree, but is
2288a3c53be5SAndrew Lunn 	 * optional.
2289a3c53be5SAndrew Lunn 	 */
2290a3c53be5SAndrew Lunn 	child = of_get_child_by_name(np, "mdio");
2291a3c53be5SAndrew Lunn 	err = mv88e6xxx_mdio_register(chip, child, false);
2292a3c53be5SAndrew Lunn 	if (err)
2293a3c53be5SAndrew Lunn 		return err;
2294a3c53be5SAndrew Lunn 
2295a3c53be5SAndrew Lunn 	/* Walk the device tree, and see if there are any other nodes
2296a3c53be5SAndrew Lunn 	 * which say they are compatible with the external mdio
2297a3c53be5SAndrew Lunn 	 * bus.
2298a3c53be5SAndrew Lunn 	 */
2299a3c53be5SAndrew Lunn 	for_each_available_child_of_node(np, child) {
2300a3c53be5SAndrew Lunn 		match = of_match_node(mv88e6xxx_mdio_external_match, child);
2301a3c53be5SAndrew Lunn 		if (match) {
2302a3c53be5SAndrew Lunn 			err = mv88e6xxx_mdio_register(chip, child, true);
2303a3c53be5SAndrew Lunn 			if (err)
2304a3c53be5SAndrew Lunn 				return err;
2305a3c53be5SAndrew Lunn 		}
2306a3c53be5SAndrew Lunn 	}
2307a3c53be5SAndrew Lunn 
2308a3c53be5SAndrew Lunn 	return 0;
2309a3c53be5SAndrew Lunn }
2310a3c53be5SAndrew Lunn 
2311a3c53be5SAndrew Lunn static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
2312fad09c73SVivien Didelot 
2313fad09c73SVivien Didelot {
2314a3c53be5SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus;
2315a3c53be5SAndrew Lunn 	struct mii_bus *bus;
2316a3c53be5SAndrew Lunn 
2317a3c53be5SAndrew Lunn 	list_for_each_entry(mdio_bus, &chip->mdios, list) {
2318a3c53be5SAndrew Lunn 		bus = mdio_bus->bus;
2319fad09c73SVivien Didelot 
2320fad09c73SVivien Didelot 		mdiobus_unregister(bus);
2321a3c53be5SAndrew Lunn 	}
2322fad09c73SVivien Didelot }
2323fad09c73SVivien Didelot 
2324855b1932SVivien Didelot static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
2325855b1932SVivien Didelot {
232604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2327855b1932SVivien Didelot 
2328855b1932SVivien Didelot 	return chip->eeprom_len;
2329855b1932SVivien Didelot }
2330855b1932SVivien Didelot 
2331855b1932SVivien Didelot static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
2332855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
2333855b1932SVivien Didelot {
233404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2335855b1932SVivien Didelot 	int err;
2336855b1932SVivien Didelot 
2337ee4dc2e7SVivien Didelot 	if (!chip->info->ops->get_eeprom)
2338ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
2339ee4dc2e7SVivien Didelot 
2340855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
2341ee4dc2e7SVivien Didelot 	err = chip->info->ops->get_eeprom(chip, eeprom, data);
2342855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2343855b1932SVivien Didelot 
2344855b1932SVivien Didelot 	if (err)
2345855b1932SVivien Didelot 		return err;
2346855b1932SVivien Didelot 
2347855b1932SVivien Didelot 	eeprom->magic = 0xc3ec4951;
2348855b1932SVivien Didelot 
2349855b1932SVivien Didelot 	return 0;
2350855b1932SVivien Didelot }
2351855b1932SVivien Didelot 
2352855b1932SVivien Didelot static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
2353855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
2354855b1932SVivien Didelot {
235504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2356855b1932SVivien Didelot 	int err;
2357855b1932SVivien Didelot 
2358ee4dc2e7SVivien Didelot 	if (!chip->info->ops->set_eeprom)
2359ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
2360ee4dc2e7SVivien Didelot 
2361855b1932SVivien Didelot 	if (eeprom->magic != 0xc3ec4951)
2362855b1932SVivien Didelot 		return -EINVAL;
2363855b1932SVivien Didelot 
2364855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
2365ee4dc2e7SVivien Didelot 	err = chip->info->ops->set_eeprom(chip, eeprom, data);
2366855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2367855b1932SVivien Didelot 
2368855b1932SVivien Didelot 	return err;
2369855b1932SVivien Didelot }
2370855b1932SVivien Didelot 
2371b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6085_ops = {
23724b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6097 */
2373b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
2374b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
2375b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
237608ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
23777f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
237896a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2379ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
238056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2381601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
238256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2383ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2384b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2385c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
23869dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2387a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2388dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2389dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2390052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
239133641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
239233641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2393fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
23946e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2395a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2396a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
239717e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2398f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
23990ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2400b3469dd8SVivien Didelot };
2401b3469dd8SVivien Didelot 
2402b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6095_ops = {
24034b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6095 */
2404b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
2405b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
2406b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
240708ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
24087f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
240996a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
241056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6085_port_set_frame_mode,
2411601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6185_port_set_egress_floods,
2412a23b2961SAndrew Lunn 	.port_set_upstream_port = mv88e6095_port_set_upstream_port,
2413a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2414dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2415dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2416052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
24176e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2418a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2419a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
242017e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2421f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
24220ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2423b3469dd8SVivien Didelot };
2424b3469dd8SVivien Didelot 
24257d381a02SStefan Eichenberger static const struct mv88e6xxx_ops mv88e6097_ops = {
242615da3cc8SStefan Eichenberger 	/* MV88E6XXX_FAMILY_6097 */
24277d381a02SStefan Eichenberger 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
24287d381a02SStefan Eichenberger 	.phy_read = mv88e6xxx_g2_smi_phy_read,
24297d381a02SStefan Eichenberger 	.phy_write = mv88e6xxx_g2_smi_phy_write,
24307d381a02SStefan Eichenberger 	.port_set_link = mv88e6xxx_port_set_link,
24317d381a02SStefan Eichenberger 	.port_set_duplex = mv88e6xxx_port_set_duplex,
24327d381a02SStefan Eichenberger 	.port_set_speed = mv88e6185_port_set_speed,
2433ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
243456995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2435601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
243656995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
24375f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2438ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting,
2439b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2440c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
24419dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
24427d381a02SStefan Eichenberger 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
24437d381a02SStefan Eichenberger 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
24447d381a02SStefan Eichenberger 	.stats_get_strings = mv88e6095_stats_get_strings,
24457d381a02SStefan Eichenberger 	.stats_get_stats = mv88e6095_stats_get_stats,
244633641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
244733641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
244891eaa475SVolodymyr Bendiuga 	.watchdog_ops = &mv88e6097_watchdog_ops,
24496e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
245017e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2451f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
24520ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
24537d381a02SStefan Eichenberger };
24547d381a02SStefan Eichenberger 
2455b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6123_ops = {
24564b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6165 */
2457b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2458efb3e74dSAndrew Lunn 	.phy_read = mv88e6165_phy_read,
2459efb3e74dSAndrew Lunn 	.phy_write = mv88e6165_phy_write,
246008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
24617f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
246296a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
246356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6085_port_set_frame_mode,
2464601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
2465c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
24669dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2467a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2468dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2469dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2470052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
247133641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
247233641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2473fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
24746e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
247517e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2476f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
24770ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2478b3469dd8SVivien Didelot };
2479b3469dd8SVivien Didelot 
2480b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6131_ops = {
24814b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6185 */
2482b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
2483b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
2484b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
248508ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
24867f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
248796a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2488ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
248956995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2490601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6185_port_set_egress_floods,
249156995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2492a23b2961SAndrew Lunn 	.port_set_upstream_port = mv88e6095_port_set_upstream_port,
24935f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2494ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2495b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2496a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2497dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2498dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2499052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
250033641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
250133641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2502fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
25036e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2504a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2505a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
250617e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2507f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
25080ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2509b3469dd8SVivien Didelot };
2510b3469dd8SVivien Didelot 
2511990e27b0SVivien Didelot static const struct mv88e6xxx_ops mv88e6141_ops = {
2512990e27b0SVivien Didelot 	/* MV88E6XXX_FAMILY_6341 */
2513990e27b0SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
2514990e27b0SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
2515990e27b0SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2516990e27b0SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2517990e27b0SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
2518990e27b0SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
2519990e27b0SVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2520990e27b0SVivien Didelot 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
2521990e27b0SVivien Didelot 	.port_set_speed = mv88e6390_port_set_speed,
2522990e27b0SVivien Didelot 	.port_tag_remap = mv88e6095_port_tag_remap,
2523990e27b0SVivien Didelot 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2524990e27b0SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
2525990e27b0SVivien Didelot 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2526990e27b0SVivien Didelot 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2527990e27b0SVivien Didelot 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2528990e27b0SVivien Didelot 	.port_pause_config = mv88e6097_port_pause_config,
2529990e27b0SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
2530990e27b0SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2531990e27b0SVivien Didelot 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2532990e27b0SVivien Didelot 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2533990e27b0SVivien Didelot 	.stats_get_strings = mv88e6320_stats_get_strings,
2534990e27b0SVivien Didelot 	.stats_get_stats = mv88e6390_stats_get_stats,
2535990e27b0SVivien Didelot 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
2536990e27b0SVivien Didelot 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
2537990e27b0SVivien Didelot 	.watchdog_ops = &mv88e6390_watchdog_ops,
2538990e27b0SVivien Didelot 	.mgmt_rsvd2cpu =  mv88e6390_g1_mgmt_rsvd2cpu,
2539990e27b0SVivien Didelot 	.reset = mv88e6352_g1_reset,
2540f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
25410ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2542990e27b0SVivien Didelot };
2543990e27b0SVivien Didelot 
2544b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6161_ops = {
25454b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6165 */
2546b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2547efb3e74dSAndrew Lunn 	.phy_read = mv88e6165_phy_read,
2548efb3e74dSAndrew Lunn 	.phy_write = mv88e6165_phy_write,
254908ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
25507f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
255196a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2552ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
255356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2554601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
255556995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
25565f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2557ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2558b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2559c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
25609dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2561a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2562dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2563dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2564052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
256533641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
256633641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2567fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
25686e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
256917e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2570f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
25710ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2572b3469dd8SVivien Didelot };
2573b3469dd8SVivien Didelot 
2574b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6165_ops = {
25754b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6165 */
2576b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2577efb3e74dSAndrew Lunn 	.phy_read = mv88e6165_phy_read,
2578efb3e74dSAndrew Lunn 	.phy_write = mv88e6165_phy_write,
257908ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
25807f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
258196a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2582c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
25839dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2584a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2585dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2586dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2587052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
258833641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
258933641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2590fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
25916e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
259217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2593f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
25940ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2595b3469dd8SVivien Didelot };
2596b3469dd8SVivien Didelot 
2597b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6171_ops = {
25984b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
2599b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2600b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2601b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
260208ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
26037f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
260494d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
260596a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2606ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
260756995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2608601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
260956995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
26105f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2611ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2612b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2613c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
26149dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2615a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2616dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2617dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2618052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
261933641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
262033641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2621fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
26226e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
262317e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2624f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
26250ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2626b3469dd8SVivien Didelot };
2627b3469dd8SVivien Didelot 
2628b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6172_ops = {
26294b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
2630ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2631ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2632b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2633b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2634b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
263508ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
26367f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2637a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
263896a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
2639ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
264056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2641601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
264256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
26435f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2644ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2645b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2646c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
26479dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2648a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2649dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2650dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2651052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
265233641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
265333641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2654fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
26556e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
265617e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2657f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
26580ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
26596d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
2660b3469dd8SVivien Didelot };
2661b3469dd8SVivien Didelot 
2662b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6175_ops = {
26634b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
2664b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2665b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2666b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
266708ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
26687f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
266994d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
267096a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2671ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
267256995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2673601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
267456995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
26755f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2676ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2677b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2678c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
26799dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2680a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2681dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2682dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2683052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
268433641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
268533641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2686fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
26876e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
268817e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2689f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
26900ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2691b3469dd8SVivien Didelot };
2692b3469dd8SVivien Didelot 
2693b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6176_ops = {
26944b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
2695ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2696ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2697b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2698b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2699b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
270008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
27017f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2702a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
270396a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
2704ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
270556995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2706601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
270756995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
27085f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2709ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2710b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2711c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
27129dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2713a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2714dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2715dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2716052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
271733641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
271833641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2719fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
27206e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
272117e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2722f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
27230ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
27246d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
2725b3469dd8SVivien Didelot };
2726b3469dd8SVivien Didelot 
2727b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6185_ops = {
27284b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6185 */
2729b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
2730b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
2731b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
273208ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
27337f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
273496a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
273556995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6085_port_set_frame_mode,
2736601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6185_port_set_egress_floods,
2737ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting,
2738a23b2961SAndrew Lunn 	.port_set_upstream_port = mv88e6095_port_set_upstream_port,
2739a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2740dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2741dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2742052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
274333641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
274433641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2745fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
27466e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2747a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2748a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
274917e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2750f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
27510ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2752b3469dd8SVivien Didelot };
2753b3469dd8SVivien Didelot 
27541a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6190_ops = {
27554b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
275698fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
275798fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
27581a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
27591a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
27601a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
27611a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
27621a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
27631a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
27641a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
2765ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
276656995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2767601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
276856995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
27693ce0e65eSAndrew Lunn 	.port_pause_config = mv88e6390_port_pause_config,
2770c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
27719dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
277279523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2773de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2774dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2775dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2776e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
277733641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
277833641994SAndrew Lunn 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
277961303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
27806e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
278117e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2782931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2783931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
27846335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
27851a3b39ecSAndrew Lunn };
27861a3b39ecSAndrew Lunn 
27871a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6190x_ops = {
27884b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
278998fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
279098fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
27911a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
27921a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
27931a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
27941a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
27951a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
27961a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
27971a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390x_port_set_speed,
2798ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
279956995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2800601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
280156995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
28023ce0e65eSAndrew Lunn 	.port_pause_config = mv88e6390_port_pause_config,
2803c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
28049dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
280579523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2806de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2807dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2808dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2809e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
281033641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
281133641994SAndrew Lunn 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
281261303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
28136e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
281417e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2815931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2816931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
28176335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
28181a3b39ecSAndrew Lunn };
28191a3b39ecSAndrew Lunn 
28201a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6191_ops = {
28214b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
282298fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
282398fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
28241a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
28251a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
28261a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
28271a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
28281a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
28291a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
28301a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
2831ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
283256995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2833601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
283456995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
28353ce0e65eSAndrew Lunn 	.port_pause_config = mv88e6390_port_pause_config,
2836c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
28379dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
283879523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2839de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2840dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2841dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2842e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
284333641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
284433641994SAndrew Lunn 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
284561303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
28466e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
284717e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2848931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2849931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
28506335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
28511a3b39ecSAndrew Lunn };
28521a3b39ecSAndrew Lunn 
2853b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6240_ops = {
28544b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
2855ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2856ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2857b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2858b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2859b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
286008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
28617f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2862a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
286396a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
2864ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
286556995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2866601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
286756995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
28685f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2869ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2870b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2871c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
28729dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2873a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2874dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2875dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2876052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
287733641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
287833641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
2879fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
28806e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
288117e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2882f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
28830ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
28846d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
2885b3469dd8SVivien Didelot };
2886b3469dd8SVivien Didelot 
28871a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6290_ops = {
28884b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
288998fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
289098fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
28911a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
28921a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
28931a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
28941a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
28951a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
28961a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
28971a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
2898ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
289956995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2900601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
290156995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
29023ce0e65eSAndrew Lunn 	.port_pause_config = mv88e6390_port_pause_config,
2903f39908d3SAndrew Lunn 	.port_set_cmode = mv88e6390x_port_set_cmode,
2904c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
29059dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
290679523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2907de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2908dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2909dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2910e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
291133641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
291233641994SAndrew Lunn 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
291361303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
29146e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
291517e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2916931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2917931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
29186335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
29191a3b39ecSAndrew Lunn };
29201a3b39ecSAndrew Lunn 
2921b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6320_ops = {
29224b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6320 */
2923ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2924ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2925b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2926b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2927b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
292808ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
29297f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
293096a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2931ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
293256995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2933601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
293456995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
29355f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2936ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2937b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2938c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
29399dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2940a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2941dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2942dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2943052f947fSAndrew Lunn 	.stats_get_stats = mv88e6320_stats_get_stats,
294433641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
294533641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
29466e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
294717e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2948f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
29490ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2950b3469dd8SVivien Didelot };
2951b3469dd8SVivien Didelot 
2952b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6321_ops = {
29534b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6321 */
2954ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2955ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2956b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2957b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2958b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
295908ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
29607f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
296196a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2962ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
296356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2964601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
296556995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
29665f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
2967ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
2968b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
2969c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
29709dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2971a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2972dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2973dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2974052f947fSAndrew Lunn 	.stats_get_stats = mv88e6320_stats_get_stats,
297533641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
297633641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
297717e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2978f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
29790ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2980b3469dd8SVivien Didelot };
2981b3469dd8SVivien Didelot 
298216e329aeSVivien Didelot static const struct mv88e6xxx_ops mv88e6341_ops = {
298316e329aeSVivien Didelot 	/* MV88E6XXX_FAMILY_6341 */
298416e329aeSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
298516e329aeSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
298616e329aeSVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
298716e329aeSVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
298816e329aeSVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
298916e329aeSVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
299016e329aeSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
299116e329aeSVivien Didelot 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
299216e329aeSVivien Didelot 	.port_set_speed = mv88e6390_port_set_speed,
299316e329aeSVivien Didelot 	.port_tag_remap = mv88e6095_port_tag_remap,
299416e329aeSVivien Didelot 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
299516e329aeSVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
299616e329aeSVivien Didelot 	.port_set_ether_type = mv88e6351_port_set_ether_type,
299716e329aeSVivien Didelot 	.port_jumbo_config = mv88e6165_port_jumbo_config,
299816e329aeSVivien Didelot 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
299916e329aeSVivien Didelot 	.port_pause_config = mv88e6097_port_pause_config,
300016e329aeSVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
300116e329aeSVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
300216e329aeSVivien Didelot 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
300316e329aeSVivien Didelot 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
300416e329aeSVivien Didelot 	.stats_get_strings = mv88e6320_stats_get_strings,
300516e329aeSVivien Didelot 	.stats_get_stats = mv88e6390_stats_get_stats,
300616e329aeSVivien Didelot 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
300716e329aeSVivien Didelot 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
300816e329aeSVivien Didelot 	.watchdog_ops = &mv88e6390_watchdog_ops,
300916e329aeSVivien Didelot 	.mgmt_rsvd2cpu =  mv88e6390_g1_mgmt_rsvd2cpu,
301016e329aeSVivien Didelot 	.reset = mv88e6352_g1_reset,
3011f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
30120ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
301316e329aeSVivien Didelot };
301416e329aeSVivien Didelot 
3015b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6350_ops = {
30164b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
3017b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3018b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3019b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
302008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
30217f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
302294d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
302396a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3024ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
302556995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3026601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
302756995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
30285f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
3029ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
3030b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
3031c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
30329dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
3033a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
3034dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
3035dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
3036052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
303733641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
303833641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
3039fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
30406e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
304117e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3042f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
30430ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
3044b3469dd8SVivien Didelot };
3045b3469dd8SVivien Didelot 
3046b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6351_ops = {
30474b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
3048b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3049b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3050b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
305108ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
30527f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
305394d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
305496a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3055ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
305656995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3057601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
305856995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
30595f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
3060ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
3061b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
3062c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
30639dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
3064a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
3065dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
3066dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
3067052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
306833641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
306933641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
3070fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
30716e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
307217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3073f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
30740ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
3075b3469dd8SVivien Didelot };
3076b3469dd8SVivien Didelot 
3077b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6352_ops = {
30784b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
3079ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3080ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3081b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3082b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3083b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
308408ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
30857f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
3086a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
308796a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
3088ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
308956995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3090601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
309156995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
30925f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
3093ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
3094b35d322aSAndrew Lunn 	.port_pause_config = mv88e6097_port_pause_config,
3095c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
30969dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
3097a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
3098dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
3099dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
3100052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
310133641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6095_g1_set_cpu_port,
310233641994SAndrew Lunn 	.g1_set_egress_port = mv88e6095_g1_set_egress_port,
3103fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
31046e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
310517e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3106f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
31070ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
31086d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
3109b3469dd8SVivien Didelot };
3110b3469dd8SVivien Didelot 
31111a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6390_ops = {
31124b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
311398fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
311498fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
31151a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
31161a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
31171a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
31181a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
31191a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
31201a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
31211a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
3122ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
312356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3124601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
312556995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
31265f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
3127ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
31283ce0e65eSAndrew Lunn 	.port_pause_config = mv88e6390_port_pause_config,
3129f39908d3SAndrew Lunn 	.port_set_cmode = mv88e6390x_port_set_cmode,
3130c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
31319dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
313279523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
3133de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
3134dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
3135dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
3136e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
313733641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
313833641994SAndrew Lunn 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
313961303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
31406e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
314117e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3142931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
3143931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
31446335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
31451a3b39ecSAndrew Lunn };
31461a3b39ecSAndrew Lunn 
31471a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6390x_ops = {
31484b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
314998fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
315098fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
31511a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
31521a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
31531a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
31541a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
31551a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
31561a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
31571a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390x_port_set_speed,
3158ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
315956995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3160601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
316156995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
31625f436666SAndrew Lunn 	.port_jumbo_config = mv88e6165_port_jumbo_config,
3163ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
31643ce0e65eSAndrew Lunn 	.port_pause_config = mv88e6390_port_pause_config,
3165c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
31669dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
316779523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
3168de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
3169dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
3170dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
3171e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
317233641994SAndrew Lunn 	.g1_set_cpu_port = mv88e6390_g1_set_cpu_port,
317333641994SAndrew Lunn 	.g1_set_egress_port = mv88e6390_g1_set_egress_port,
317461303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
31756e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
317617e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3177931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
3178931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
31796335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
31801a3b39ecSAndrew Lunn };
31811a3b39ecSAndrew Lunn 
3182fad09c73SVivien Didelot static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3183fad09c73SVivien Didelot 	[MV88E6085] = {
3184fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3185fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6097,
3186fad09c73SVivien Didelot 		.name = "Marvell 88E6085",
3187fad09c73SVivien Didelot 		.num_databases = 4096,
3188fad09c73SVivien Didelot 		.num_ports = 10,
31893cf3c846SVivien Didelot 		.max_vid = 4095,
3190fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3191a935c052SVivien Didelot 		.global1_addr = 0x1b,
3192acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3193dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3194e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3195f3645652SVivien Didelot 		.pvt = true,
3196443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3197fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
3198b3469dd8SVivien Didelot 		.ops = &mv88e6085_ops,
3199fad09c73SVivien Didelot 	},
3200fad09c73SVivien Didelot 
3201fad09c73SVivien Didelot 	[MV88E6095] = {
3202fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3203fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6095,
3204fad09c73SVivien Didelot 		.name = "Marvell 88E6095/88E6095F",
3205fad09c73SVivien Didelot 		.num_databases = 256,
3206fad09c73SVivien Didelot 		.num_ports = 11,
32073cf3c846SVivien Didelot 		.max_vid = 4095,
3208fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3209a935c052SVivien Didelot 		.global1_addr = 0x1b,
3210acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3211dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3212e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3213443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3214fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6095,
3215b3469dd8SVivien Didelot 		.ops = &mv88e6095_ops,
3216fad09c73SVivien Didelot 	},
3217fad09c73SVivien Didelot 
32187d381a02SStefan Eichenberger 	[MV88E6097] = {
32197d381a02SStefan Eichenberger 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6097,
32207d381a02SStefan Eichenberger 		.family = MV88E6XXX_FAMILY_6097,
32217d381a02SStefan Eichenberger 		.name = "Marvell 88E6097/88E6097F",
32227d381a02SStefan Eichenberger 		.num_databases = 4096,
32237d381a02SStefan Eichenberger 		.num_ports = 11,
32243cf3c846SVivien Didelot 		.max_vid = 4095,
32257d381a02SStefan Eichenberger 		.port_base_addr = 0x10,
32267d381a02SStefan Eichenberger 		.global1_addr = 0x1b,
32277d381a02SStefan Eichenberger 		.age_time_coeff = 15000,
3228c534178bSStefan Eichenberger 		.g1_irqs = 8,
3229e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3230f3645652SVivien Didelot 		.pvt = true,
32312bfcfcd3SStefan Eichenberger 		.tag_protocol = DSA_TAG_PROTO_EDSA,
32327d381a02SStefan Eichenberger 		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
32337d381a02SStefan Eichenberger 		.ops = &mv88e6097_ops,
32347d381a02SStefan Eichenberger 	},
32357d381a02SStefan Eichenberger 
3236fad09c73SVivien Didelot 	[MV88E6123] = {
3237fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3238fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3239fad09c73SVivien Didelot 		.name = "Marvell 88E6123",
3240fad09c73SVivien Didelot 		.num_databases = 4096,
3241fad09c73SVivien Didelot 		.num_ports = 3,
32423cf3c846SVivien Didelot 		.max_vid = 4095,
3243fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3244a935c052SVivien Didelot 		.global1_addr = 0x1b,
3245acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3246dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3247e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3248f3645652SVivien Didelot 		.pvt = true,
3249443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3250fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3251b3469dd8SVivien Didelot 		.ops = &mv88e6123_ops,
3252fad09c73SVivien Didelot 	},
3253fad09c73SVivien Didelot 
3254fad09c73SVivien Didelot 	[MV88E6131] = {
3255fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3256fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3257fad09c73SVivien Didelot 		.name = "Marvell 88E6131",
3258fad09c73SVivien Didelot 		.num_databases = 256,
3259fad09c73SVivien Didelot 		.num_ports = 8,
32603cf3c846SVivien Didelot 		.max_vid = 4095,
3261fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3262a935c052SVivien Didelot 		.global1_addr = 0x1b,
3263acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3264dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3265e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3266443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3267fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3268b3469dd8SVivien Didelot 		.ops = &mv88e6131_ops,
3269fad09c73SVivien Didelot 	},
3270fad09c73SVivien Didelot 
3271990e27b0SVivien Didelot 	[MV88E6141] = {
3272990e27b0SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6141,
3273990e27b0SVivien Didelot 		.family = MV88E6XXX_FAMILY_6341,
3274990e27b0SVivien Didelot 		.name = "Marvell 88E6341",
3275990e27b0SVivien Didelot 		.num_databases = 4096,
3276990e27b0SVivien Didelot 		.num_ports = 6,
32773cf3c846SVivien Didelot 		.max_vid = 4095,
3278990e27b0SVivien Didelot 		.port_base_addr = 0x10,
3279990e27b0SVivien Didelot 		.global1_addr = 0x1b,
3280990e27b0SVivien Didelot 		.age_time_coeff = 3750,
3281990e27b0SVivien Didelot 		.atu_move_port_mask = 0x1f,
3282f3645652SVivien Didelot 		.pvt = true,
3283990e27b0SVivien Didelot 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3284990e27b0SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6341,
3285990e27b0SVivien Didelot 		.ops = &mv88e6141_ops,
3286990e27b0SVivien Didelot 	},
3287990e27b0SVivien Didelot 
3288fad09c73SVivien Didelot 	[MV88E6161] = {
3289fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3290fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3291fad09c73SVivien Didelot 		.name = "Marvell 88E6161",
3292fad09c73SVivien Didelot 		.num_databases = 4096,
3293fad09c73SVivien Didelot 		.num_ports = 6,
32943cf3c846SVivien Didelot 		.max_vid = 4095,
3295fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3296a935c052SVivien Didelot 		.global1_addr = 0x1b,
3297acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3298dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3299e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3300f3645652SVivien Didelot 		.pvt = true,
3301443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3302fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3303b3469dd8SVivien Didelot 		.ops = &mv88e6161_ops,
3304fad09c73SVivien Didelot 	},
3305fad09c73SVivien Didelot 
3306fad09c73SVivien Didelot 	[MV88E6165] = {
3307fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3308fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3309fad09c73SVivien Didelot 		.name = "Marvell 88E6165",
3310fad09c73SVivien Didelot 		.num_databases = 4096,
3311fad09c73SVivien Didelot 		.num_ports = 6,
33123cf3c846SVivien Didelot 		.max_vid = 4095,
3313fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3314a935c052SVivien Didelot 		.global1_addr = 0x1b,
3315acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3316dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3317e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3318f3645652SVivien Didelot 		.pvt = true,
3319443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3320fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3321b3469dd8SVivien Didelot 		.ops = &mv88e6165_ops,
3322fad09c73SVivien Didelot 	},
3323fad09c73SVivien Didelot 
3324fad09c73SVivien Didelot 	[MV88E6171] = {
3325fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3326fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3327fad09c73SVivien Didelot 		.name = "Marvell 88E6171",
3328fad09c73SVivien Didelot 		.num_databases = 4096,
3329fad09c73SVivien Didelot 		.num_ports = 7,
33303cf3c846SVivien Didelot 		.max_vid = 4095,
3331fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3332a935c052SVivien Didelot 		.global1_addr = 0x1b,
3333acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3334dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3335e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3336f3645652SVivien Didelot 		.pvt = true,
3337443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3338fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3339b3469dd8SVivien Didelot 		.ops = &mv88e6171_ops,
3340fad09c73SVivien Didelot 	},
3341fad09c73SVivien Didelot 
3342fad09c73SVivien Didelot 	[MV88E6172] = {
3343fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3344fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3345fad09c73SVivien Didelot 		.name = "Marvell 88E6172",
3346fad09c73SVivien Didelot 		.num_databases = 4096,
3347fad09c73SVivien Didelot 		.num_ports = 7,
33483cf3c846SVivien Didelot 		.max_vid = 4095,
3349fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3350a935c052SVivien Didelot 		.global1_addr = 0x1b,
3351acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3352dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3353e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3354f3645652SVivien Didelot 		.pvt = true,
3355443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3356fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3357b3469dd8SVivien Didelot 		.ops = &mv88e6172_ops,
3358fad09c73SVivien Didelot 	},
3359fad09c73SVivien Didelot 
3360fad09c73SVivien Didelot 	[MV88E6175] = {
3361fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3362fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3363fad09c73SVivien Didelot 		.name = "Marvell 88E6175",
3364fad09c73SVivien Didelot 		.num_databases = 4096,
3365fad09c73SVivien Didelot 		.num_ports = 7,
33663cf3c846SVivien Didelot 		.max_vid = 4095,
3367fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3368a935c052SVivien Didelot 		.global1_addr = 0x1b,
3369acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3370dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3371e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3372f3645652SVivien Didelot 		.pvt = true,
3373443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3374fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3375b3469dd8SVivien Didelot 		.ops = &mv88e6175_ops,
3376fad09c73SVivien Didelot 	},
3377fad09c73SVivien Didelot 
3378fad09c73SVivien Didelot 	[MV88E6176] = {
3379fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3380fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3381fad09c73SVivien Didelot 		.name = "Marvell 88E6176",
3382fad09c73SVivien Didelot 		.num_databases = 4096,
3383fad09c73SVivien Didelot 		.num_ports = 7,
33843cf3c846SVivien Didelot 		.max_vid = 4095,
3385fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3386a935c052SVivien Didelot 		.global1_addr = 0x1b,
3387acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3388dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3389e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3390f3645652SVivien Didelot 		.pvt = true,
3391443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3392fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3393b3469dd8SVivien Didelot 		.ops = &mv88e6176_ops,
3394fad09c73SVivien Didelot 	},
3395fad09c73SVivien Didelot 
3396fad09c73SVivien Didelot 	[MV88E6185] = {
3397fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3398fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3399fad09c73SVivien Didelot 		.name = "Marvell 88E6185",
3400fad09c73SVivien Didelot 		.num_databases = 256,
3401fad09c73SVivien Didelot 		.num_ports = 10,
34023cf3c846SVivien Didelot 		.max_vid = 4095,
3403fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3404a935c052SVivien Didelot 		.global1_addr = 0x1b,
3405acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3406dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3407e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3408443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3409fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3410b3469dd8SVivien Didelot 		.ops = &mv88e6185_ops,
3411fad09c73SVivien Didelot 	},
3412fad09c73SVivien Didelot 
34131a3b39ecSAndrew Lunn 	[MV88E6190] = {
34141a3b39ecSAndrew Lunn 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6190,
34151a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34161a3b39ecSAndrew Lunn 		.name = "Marvell 88E6190",
34171a3b39ecSAndrew Lunn 		.num_databases = 4096,
34181a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3419931d1822SVivien Didelot 		.max_vid = 8191,
34201a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34211a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3422443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3423b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
34241a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3425f3645652SVivien Didelot 		.pvt = true,
3426e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
34271a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34281a3b39ecSAndrew Lunn 		.ops = &mv88e6190_ops,
34291a3b39ecSAndrew Lunn 	},
34301a3b39ecSAndrew Lunn 
34311a3b39ecSAndrew Lunn 	[MV88E6190X] = {
34321a3b39ecSAndrew Lunn 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6190X,
34331a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34341a3b39ecSAndrew Lunn 		.name = "Marvell 88E6190X",
34351a3b39ecSAndrew Lunn 		.num_databases = 4096,
34361a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3437931d1822SVivien Didelot 		.max_vid = 8191,
34381a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34391a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3440b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
34411a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3442e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3443f3645652SVivien Didelot 		.pvt = true,
3444443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
34451a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34461a3b39ecSAndrew Lunn 		.ops = &mv88e6190x_ops,
34471a3b39ecSAndrew Lunn 	},
34481a3b39ecSAndrew Lunn 
34491a3b39ecSAndrew Lunn 	[MV88E6191] = {
34501a3b39ecSAndrew Lunn 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6191,
34511a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34521a3b39ecSAndrew Lunn 		.name = "Marvell 88E6191",
34531a3b39ecSAndrew Lunn 		.num_databases = 4096,
34541a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3455931d1822SVivien Didelot 		.max_vid = 8191,
34561a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34571a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3458b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
3459443d5a1bSAndrew Lunn 		.g1_irqs = 9,
3460e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3461f3645652SVivien Didelot 		.pvt = true,
3462443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
34631a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34642cf4cefbSVivien Didelot 		.ops = &mv88e6191_ops,
34651a3b39ecSAndrew Lunn 	},
34661a3b39ecSAndrew Lunn 
3467fad09c73SVivien Didelot 	[MV88E6240] = {
3468fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3469fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3470fad09c73SVivien Didelot 		.name = "Marvell 88E6240",
3471fad09c73SVivien Didelot 		.num_databases = 4096,
3472fad09c73SVivien Didelot 		.num_ports = 7,
34733cf3c846SVivien Didelot 		.max_vid = 4095,
3474fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3475a935c052SVivien Didelot 		.global1_addr = 0x1b,
3476acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3477dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3478e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3479f3645652SVivien Didelot 		.pvt = true,
3480443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3481fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3482b3469dd8SVivien Didelot 		.ops = &mv88e6240_ops,
3483fad09c73SVivien Didelot 	},
3484fad09c73SVivien Didelot 
34851a3b39ecSAndrew Lunn 	[MV88E6290] = {
34861a3b39ecSAndrew Lunn 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6290,
34871a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34881a3b39ecSAndrew Lunn 		.name = "Marvell 88E6290",
34891a3b39ecSAndrew Lunn 		.num_databases = 4096,
34901a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3491931d1822SVivien Didelot 		.max_vid = 8191,
34921a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34931a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3494b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
34951a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3496e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3497f3645652SVivien Didelot 		.pvt = true,
3498443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
34991a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
35001a3b39ecSAndrew Lunn 		.ops = &mv88e6290_ops,
35011a3b39ecSAndrew Lunn 	},
35021a3b39ecSAndrew Lunn 
3503fad09c73SVivien Didelot 	[MV88E6320] = {
3504fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3505fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3506fad09c73SVivien Didelot 		.name = "Marvell 88E6320",
3507fad09c73SVivien Didelot 		.num_databases = 4096,
3508fad09c73SVivien Didelot 		.num_ports = 7,
35093cf3c846SVivien Didelot 		.max_vid = 4095,
3510fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3511a935c052SVivien Didelot 		.global1_addr = 0x1b,
3512acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3513dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3514e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3515f3645652SVivien Didelot 		.pvt = true,
3516443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3517fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3518b3469dd8SVivien Didelot 		.ops = &mv88e6320_ops,
3519fad09c73SVivien Didelot 	},
3520fad09c73SVivien Didelot 
3521fad09c73SVivien Didelot 	[MV88E6321] = {
3522fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3523fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3524fad09c73SVivien Didelot 		.name = "Marvell 88E6321",
3525fad09c73SVivien Didelot 		.num_databases = 4096,
3526fad09c73SVivien Didelot 		.num_ports = 7,
35273cf3c846SVivien Didelot 		.max_vid = 4095,
3528fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3529a935c052SVivien Didelot 		.global1_addr = 0x1b,
3530acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3531dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3532e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3533443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3534fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3535b3469dd8SVivien Didelot 		.ops = &mv88e6321_ops,
3536fad09c73SVivien Didelot 	},
3537fad09c73SVivien Didelot 
3538a75961d0SGregory CLEMENT 	[MV88E6341] = {
3539a75961d0SGregory CLEMENT 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6341,
3540a75961d0SGregory CLEMENT 		.family = MV88E6XXX_FAMILY_6341,
3541a75961d0SGregory CLEMENT 		.name = "Marvell 88E6341",
3542a75961d0SGregory CLEMENT 		.num_databases = 4096,
3543a75961d0SGregory CLEMENT 		.num_ports = 6,
35443cf3c846SVivien Didelot 		.max_vid = 4095,
3545a75961d0SGregory CLEMENT 		.port_base_addr = 0x10,
3546a75961d0SGregory CLEMENT 		.global1_addr = 0x1b,
3547a75961d0SGregory CLEMENT 		.age_time_coeff = 3750,
3548e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3549f3645652SVivien Didelot 		.pvt = true,
3550a75961d0SGregory CLEMENT 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3551a75961d0SGregory CLEMENT 		.flags = MV88E6XXX_FLAGS_FAMILY_6341,
3552a75961d0SGregory CLEMENT 		.ops = &mv88e6341_ops,
3553a75961d0SGregory CLEMENT 	},
3554a75961d0SGregory CLEMENT 
3555fad09c73SVivien Didelot 	[MV88E6350] = {
3556fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3557fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3558fad09c73SVivien Didelot 		.name = "Marvell 88E6350",
3559fad09c73SVivien Didelot 		.num_databases = 4096,
3560fad09c73SVivien Didelot 		.num_ports = 7,
35613cf3c846SVivien Didelot 		.max_vid = 4095,
3562fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3563a935c052SVivien Didelot 		.global1_addr = 0x1b,
3564acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3565dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3566e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3567f3645652SVivien Didelot 		.pvt = true,
3568443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3569fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3570b3469dd8SVivien Didelot 		.ops = &mv88e6350_ops,
3571fad09c73SVivien Didelot 	},
3572fad09c73SVivien Didelot 
3573fad09c73SVivien Didelot 	[MV88E6351] = {
3574fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3575fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3576fad09c73SVivien Didelot 		.name = "Marvell 88E6351",
3577fad09c73SVivien Didelot 		.num_databases = 4096,
3578fad09c73SVivien Didelot 		.num_ports = 7,
35793cf3c846SVivien Didelot 		.max_vid = 4095,
3580fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3581a935c052SVivien Didelot 		.global1_addr = 0x1b,
3582acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3583dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3584e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3585f3645652SVivien Didelot 		.pvt = true,
3586443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3587fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3588b3469dd8SVivien Didelot 		.ops = &mv88e6351_ops,
3589fad09c73SVivien Didelot 	},
3590fad09c73SVivien Didelot 
3591fad09c73SVivien Didelot 	[MV88E6352] = {
3592fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3593fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3594fad09c73SVivien Didelot 		.name = "Marvell 88E6352",
3595fad09c73SVivien Didelot 		.num_databases = 4096,
3596fad09c73SVivien Didelot 		.num_ports = 7,
35973cf3c846SVivien Didelot 		.max_vid = 4095,
3598fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3599a935c052SVivien Didelot 		.global1_addr = 0x1b,
3600acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3601dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3602e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3603f3645652SVivien Didelot 		.pvt = true,
3604443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3605fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3606b3469dd8SVivien Didelot 		.ops = &mv88e6352_ops,
3607fad09c73SVivien Didelot 	},
36081a3b39ecSAndrew Lunn 	[MV88E6390] = {
36091a3b39ecSAndrew Lunn 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6390,
36101a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
36111a3b39ecSAndrew Lunn 		.name = "Marvell 88E6390",
36121a3b39ecSAndrew Lunn 		.num_databases = 4096,
36131a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3614931d1822SVivien Didelot 		.max_vid = 8191,
36151a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
36161a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3617b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
36181a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3619e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3620f3645652SVivien Didelot 		.pvt = true,
3621443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
36221a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
36231a3b39ecSAndrew Lunn 		.ops = &mv88e6390_ops,
36241a3b39ecSAndrew Lunn 	},
36251a3b39ecSAndrew Lunn 	[MV88E6390X] = {
36261a3b39ecSAndrew Lunn 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6390X,
36271a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
36281a3b39ecSAndrew Lunn 		.name = "Marvell 88E6390X",
36291a3b39ecSAndrew Lunn 		.num_databases = 4096,
36301a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3631931d1822SVivien Didelot 		.max_vid = 8191,
36321a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
36331a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3634b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
36351a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3636e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3637f3645652SVivien Didelot 		.pvt = true,
3638443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
36391a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
36401a3b39ecSAndrew Lunn 		.ops = &mv88e6390x_ops,
36411a3b39ecSAndrew Lunn 	},
3642fad09c73SVivien Didelot };
3643fad09c73SVivien Didelot 
3644fad09c73SVivien Didelot static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
3645fad09c73SVivien Didelot {
3646fad09c73SVivien Didelot 	int i;
3647fad09c73SVivien Didelot 
3648fad09c73SVivien Didelot 	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3649fad09c73SVivien Didelot 		if (mv88e6xxx_table[i].prod_num == prod_num)
3650fad09c73SVivien Didelot 			return &mv88e6xxx_table[i];
3651fad09c73SVivien Didelot 
3652fad09c73SVivien Didelot 	return NULL;
3653fad09c73SVivien Didelot }
3654fad09c73SVivien Didelot 
3655fad09c73SVivien Didelot static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
3656fad09c73SVivien Didelot {
3657fad09c73SVivien Didelot 	const struct mv88e6xxx_info *info;
36588f6345b2SVivien Didelot 	unsigned int prod_num, rev;
36598f6345b2SVivien Didelot 	u16 id;
36608f6345b2SVivien Didelot 	int err;
3661fad09c73SVivien Didelot 
36628f6345b2SVivien Didelot 	mutex_lock(&chip->reg_lock);
36638f6345b2SVivien Didelot 	err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
36648f6345b2SVivien Didelot 	mutex_unlock(&chip->reg_lock);
36658f6345b2SVivien Didelot 	if (err)
36668f6345b2SVivien Didelot 		return err;
3667fad09c73SVivien Didelot 
3668fad09c73SVivien Didelot 	prod_num = (id & 0xfff0) >> 4;
3669fad09c73SVivien Didelot 	rev = id & 0x000f;
3670fad09c73SVivien Didelot 
3671fad09c73SVivien Didelot 	info = mv88e6xxx_lookup_info(prod_num);
3672fad09c73SVivien Didelot 	if (!info)
3673fad09c73SVivien Didelot 		return -ENODEV;
3674fad09c73SVivien Didelot 
3675fad09c73SVivien Didelot 	/* Update the compatible info with the probed one */
3676fad09c73SVivien Didelot 	chip->info = info;
3677fad09c73SVivien Didelot 
3678ca070c10SVivien Didelot 	err = mv88e6xxx_g2_require(chip);
3679ca070c10SVivien Didelot 	if (err)
3680ca070c10SVivien Didelot 		return err;
3681ca070c10SVivien Didelot 
3682fad09c73SVivien Didelot 	dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3683fad09c73SVivien Didelot 		 chip->info->prod_num, chip->info->name, rev);
3684fad09c73SVivien Didelot 
3685fad09c73SVivien Didelot 	return 0;
3686fad09c73SVivien Didelot }
3687fad09c73SVivien Didelot 
3688fad09c73SVivien Didelot static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
3689fad09c73SVivien Didelot {
3690fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3691fad09c73SVivien Didelot 
3692fad09c73SVivien Didelot 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3693fad09c73SVivien Didelot 	if (!chip)
3694fad09c73SVivien Didelot 		return NULL;
3695fad09c73SVivien Didelot 
3696fad09c73SVivien Didelot 	chip->dev = dev;
3697fad09c73SVivien Didelot 
3698fad09c73SVivien Didelot 	mutex_init(&chip->reg_lock);
3699a3c53be5SAndrew Lunn 	INIT_LIST_HEAD(&chip->mdios);
3700fad09c73SVivien Didelot 
3701fad09c73SVivien Didelot 	return chip;
3702fad09c73SVivien Didelot }
3703fad09c73SVivien Didelot 
3704fad09c73SVivien Didelot static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
3705fad09c73SVivien Didelot 			      struct mii_bus *bus, int sw_addr)
3706fad09c73SVivien Didelot {
3707fad09c73SVivien Didelot 	if (sw_addr == 0)
3708fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
3709a0ffff24SVivien Didelot 	else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
3710fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
3711fad09c73SVivien Didelot 	else
3712fad09c73SVivien Didelot 		return -EINVAL;
3713fad09c73SVivien Didelot 
3714fad09c73SVivien Didelot 	chip->bus = bus;
3715fad09c73SVivien Didelot 	chip->sw_addr = sw_addr;
3716fad09c73SVivien Didelot 
3717fad09c73SVivien Didelot 	return 0;
3718fad09c73SVivien Didelot }
3719fad09c73SVivien Didelot 
37207b314362SAndrew Lunn static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
37217b314362SAndrew Lunn {
372204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37232bbb33beSAndrew Lunn 
3724443d5a1bSAndrew Lunn 	return chip->info->tag_protocol;
37257b314362SAndrew Lunn }
37267b314362SAndrew Lunn 
3727fad09c73SVivien Didelot static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3728fad09c73SVivien Didelot 				       struct device *host_dev, int sw_addr,
3729fad09c73SVivien Didelot 				       void **priv)
3730fad09c73SVivien Didelot {
3731fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3732fad09c73SVivien Didelot 	struct mii_bus *bus;
3733fad09c73SVivien Didelot 	int err;
3734fad09c73SVivien Didelot 
3735fad09c73SVivien Didelot 	bus = dsa_host_dev_to_mii_bus(host_dev);
3736fad09c73SVivien Didelot 	if (!bus)
3737fad09c73SVivien Didelot 		return NULL;
3738fad09c73SVivien Didelot 
3739fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dsa_dev);
3740fad09c73SVivien Didelot 	if (!chip)
3741fad09c73SVivien Didelot 		return NULL;
3742fad09c73SVivien Didelot 
3743fad09c73SVivien Didelot 	/* Legacy SMI probing will only support chips similar to 88E6085 */
3744fad09c73SVivien Didelot 	chip->info = &mv88e6xxx_table[MV88E6085];
3745fad09c73SVivien Didelot 
3746fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, bus, sw_addr);
3747fad09c73SVivien Didelot 	if (err)
3748fad09c73SVivien Didelot 		goto free;
3749fad09c73SVivien Didelot 
3750fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3751fad09c73SVivien Didelot 	if (err)
3752fad09c73SVivien Didelot 		goto free;
3753fad09c73SVivien Didelot 
3754dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3755dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3756dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3757dc30c35bSAndrew Lunn 	if (err)
3758dc30c35bSAndrew Lunn 		goto free;
3759dc30c35bSAndrew Lunn 
3760e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3761e57e5e77SVivien Didelot 
3762a3c53be5SAndrew Lunn 	err = mv88e6xxx_mdios_register(chip, NULL);
3763fad09c73SVivien Didelot 	if (err)
3764fad09c73SVivien Didelot 		goto free;
3765fad09c73SVivien Didelot 
3766fad09c73SVivien Didelot 	*priv = chip;
3767fad09c73SVivien Didelot 
3768fad09c73SVivien Didelot 	return chip->info->name;
3769fad09c73SVivien Didelot free:
3770fad09c73SVivien Didelot 	devm_kfree(dsa_dev, chip);
3771fad09c73SVivien Didelot 
3772fad09c73SVivien Didelot 	return NULL;
3773fad09c73SVivien Didelot }
3774fad09c73SVivien Didelot 
37757df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
37767df8fbddSVivien Didelot 				      const struct switchdev_obj_port_mdb *mdb,
37777df8fbddSVivien Didelot 				      struct switchdev_trans *trans)
37787df8fbddSVivien Didelot {
37797df8fbddSVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
37807df8fbddSVivien Didelot 	 * so skip the prepare phase.
37817df8fbddSVivien Didelot 	 */
37827df8fbddSVivien Didelot 
37837df8fbddSVivien Didelot 	return 0;
37847df8fbddSVivien Didelot }
37857df8fbddSVivien Didelot 
37867df8fbddSVivien Didelot static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
37877df8fbddSVivien Didelot 				   const struct switchdev_obj_port_mdb *mdb,
37887df8fbddSVivien Didelot 				   struct switchdev_trans *trans)
37897df8fbddSVivien Didelot {
379004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37917df8fbddSVivien Didelot 
37927df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
37937df8fbddSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
37947df8fbddSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_MC_STATIC))
37957df8fbddSVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
37967df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
37977df8fbddSVivien Didelot }
37987df8fbddSVivien Didelot 
37997df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
38007df8fbddSVivien Didelot 				  const struct switchdev_obj_port_mdb *mdb)
38017df8fbddSVivien Didelot {
380204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
38037df8fbddSVivien Didelot 	int err;
38047df8fbddSVivien Didelot 
38057df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
38067df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
38077df8fbddSVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
38087df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
38097df8fbddSVivien Didelot 
38107df8fbddSVivien Didelot 	return err;
38117df8fbddSVivien Didelot }
38127df8fbddSVivien Didelot 
38137df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
38147df8fbddSVivien Didelot 				   struct switchdev_obj_port_mdb *mdb,
3815438ff537SVivien Didelot 				   switchdev_obj_dump_cb_t *cb)
38167df8fbddSVivien Didelot {
381704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
38187df8fbddSVivien Didelot 	int err;
38197df8fbddSVivien Didelot 
38207df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
38217df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
38227df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
38237df8fbddSVivien Didelot 
38247df8fbddSVivien Didelot 	return err;
38257df8fbddSVivien Didelot }
38267df8fbddSVivien Didelot 
3827a82f67afSFlorian Fainelli static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
3828fad09c73SVivien Didelot 	.probe			= mv88e6xxx_drv_probe,
38297b314362SAndrew Lunn 	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
3830fad09c73SVivien Didelot 	.setup			= mv88e6xxx_setup,
3831fad09c73SVivien Didelot 	.set_addr		= mv88e6xxx_set_addr,
3832fad09c73SVivien Didelot 	.adjust_link		= mv88e6xxx_adjust_link,
3833fad09c73SVivien Didelot 	.get_strings		= mv88e6xxx_get_strings,
3834fad09c73SVivien Didelot 	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
3835fad09c73SVivien Didelot 	.get_sset_count		= mv88e6xxx_get_sset_count,
383604aca993SAndrew Lunn 	.port_enable		= mv88e6xxx_port_enable,
383704aca993SAndrew Lunn 	.port_disable		= mv88e6xxx_port_disable,
3838fad09c73SVivien Didelot 	.set_eee		= mv88e6xxx_set_eee,
3839fad09c73SVivien Didelot 	.get_eee		= mv88e6xxx_get_eee,
3840fad09c73SVivien Didelot 	.get_eeprom_len		= mv88e6xxx_get_eeprom_len,
3841fad09c73SVivien Didelot 	.get_eeprom		= mv88e6xxx_get_eeprom,
3842fad09c73SVivien Didelot 	.set_eeprom		= mv88e6xxx_set_eeprom,
3843fad09c73SVivien Didelot 	.get_regs_len		= mv88e6xxx_get_regs_len,
3844fad09c73SVivien Didelot 	.get_regs		= mv88e6xxx_get_regs,
38452cfcd964SVivien Didelot 	.set_ageing_time	= mv88e6xxx_set_ageing_time,
3846fad09c73SVivien Didelot 	.port_bridge_join	= mv88e6xxx_port_bridge_join,
3847fad09c73SVivien Didelot 	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
3848fad09c73SVivien Didelot 	.port_stp_state_set	= mv88e6xxx_port_stp_state_set,
3849749efcb8SVivien Didelot 	.port_fast_age		= mv88e6xxx_port_fast_age,
3850fad09c73SVivien Didelot 	.port_vlan_filtering	= mv88e6xxx_port_vlan_filtering,
3851fad09c73SVivien Didelot 	.port_vlan_prepare	= mv88e6xxx_port_vlan_prepare,
3852fad09c73SVivien Didelot 	.port_vlan_add		= mv88e6xxx_port_vlan_add,
3853fad09c73SVivien Didelot 	.port_vlan_del		= mv88e6xxx_port_vlan_del,
3854fad09c73SVivien Didelot 	.port_vlan_dump		= mv88e6xxx_port_vlan_dump,
3855fad09c73SVivien Didelot 	.port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
3856fad09c73SVivien Didelot 	.port_fdb_add           = mv88e6xxx_port_fdb_add,
3857fad09c73SVivien Didelot 	.port_fdb_del           = mv88e6xxx_port_fdb_del,
3858fad09c73SVivien Didelot 	.port_fdb_dump          = mv88e6xxx_port_fdb_dump,
38597df8fbddSVivien Didelot 	.port_mdb_prepare       = mv88e6xxx_port_mdb_prepare,
38607df8fbddSVivien Didelot 	.port_mdb_add           = mv88e6xxx_port_mdb_add,
38617df8fbddSVivien Didelot 	.port_mdb_del           = mv88e6xxx_port_mdb_del,
38627df8fbddSVivien Didelot 	.port_mdb_dump          = mv88e6xxx_port_mdb_dump,
3863aec5ac88SVivien Didelot 	.crosschip_bridge_join	= mv88e6xxx_crosschip_bridge_join,
3864aec5ac88SVivien Didelot 	.crosschip_bridge_leave	= mv88e6xxx_crosschip_bridge_leave,
3865fad09c73SVivien Didelot };
3866fad09c73SVivien Didelot 
3867ab3d408dSFlorian Fainelli static struct dsa_switch_driver mv88e6xxx_switch_drv = {
3868ab3d408dSFlorian Fainelli 	.ops			= &mv88e6xxx_switch_ops,
3869ab3d408dSFlorian Fainelli };
3870ab3d408dSFlorian Fainelli 
387155ed0ce0SFlorian Fainelli static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
3872fad09c73SVivien Didelot {
3873fad09c73SVivien Didelot 	struct device *dev = chip->dev;
3874fad09c73SVivien Didelot 	struct dsa_switch *ds;
3875fad09c73SVivien Didelot 
387673b1204dSVivien Didelot 	ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip));
3877fad09c73SVivien Didelot 	if (!ds)
3878fad09c73SVivien Didelot 		return -ENOMEM;
3879fad09c73SVivien Didelot 
3880fad09c73SVivien Didelot 	ds->priv = chip;
38819d490b4eSVivien Didelot 	ds->ops = &mv88e6xxx_switch_ops;
38829ff74f24SVivien Didelot 	ds->ageing_time_min = chip->info->age_time_coeff;
38839ff74f24SVivien Didelot 	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
3884fad09c73SVivien Didelot 
3885fad09c73SVivien Didelot 	dev_set_drvdata(dev, ds);
3886fad09c73SVivien Didelot 
388755ed0ce0SFlorian Fainelli 	return dsa_register_switch(ds, dev);
3888fad09c73SVivien Didelot }
3889fad09c73SVivien Didelot 
3890fad09c73SVivien Didelot static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
3891fad09c73SVivien Didelot {
3892fad09c73SVivien Didelot 	dsa_unregister_switch(chip->ds);
3893fad09c73SVivien Didelot }
3894fad09c73SVivien Didelot 
3895fad09c73SVivien Didelot static int mv88e6xxx_probe(struct mdio_device *mdiodev)
3896fad09c73SVivien Didelot {
3897fad09c73SVivien Didelot 	struct device *dev = &mdiodev->dev;
3898fad09c73SVivien Didelot 	struct device_node *np = dev->of_node;
3899fad09c73SVivien Didelot 	const struct mv88e6xxx_info *compat_info;
3900fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3901fad09c73SVivien Didelot 	u32 eeprom_len;
3902fad09c73SVivien Didelot 	int err;
3903fad09c73SVivien Didelot 
3904fad09c73SVivien Didelot 	compat_info = of_device_get_match_data(dev);
3905fad09c73SVivien Didelot 	if (!compat_info)
3906fad09c73SVivien Didelot 		return -EINVAL;
3907fad09c73SVivien Didelot 
3908fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dev);
3909fad09c73SVivien Didelot 	if (!chip)
3910fad09c73SVivien Didelot 		return -ENOMEM;
3911fad09c73SVivien Didelot 
3912fad09c73SVivien Didelot 	chip->info = compat_info;
3913fad09c73SVivien Didelot 
3914fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
3915fad09c73SVivien Didelot 	if (err)
3916fad09c73SVivien Didelot 		return err;
3917fad09c73SVivien Didelot 
3918b4308f04SAndrew Lunn 	chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
3919b4308f04SAndrew Lunn 	if (IS_ERR(chip->reset))
3920b4308f04SAndrew Lunn 		return PTR_ERR(chip->reset);
3921b4308f04SAndrew Lunn 
3922fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3923fad09c73SVivien Didelot 	if (err)
3924fad09c73SVivien Didelot 		return err;
3925fad09c73SVivien Didelot 
3926e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3927e57e5e77SVivien Didelot 
3928ee4dc2e7SVivien Didelot 	if (chip->info->ops->get_eeprom &&
3929fad09c73SVivien Didelot 	    !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3930fad09c73SVivien Didelot 		chip->eeprom_len = eeprom_len;
3931fad09c73SVivien Didelot 
3932dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3933dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3934dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3935fad09c73SVivien Didelot 	if (err)
3936dc30c35bSAndrew Lunn 		goto out;
3937fad09c73SVivien Didelot 
3938dc30c35bSAndrew Lunn 	chip->irq = of_irq_get(np, 0);
3939dc30c35bSAndrew Lunn 	if (chip->irq == -EPROBE_DEFER) {
3940dc30c35bSAndrew Lunn 		err = chip->irq;
3941dc30c35bSAndrew Lunn 		goto out;
3942fad09c73SVivien Didelot 	}
3943fad09c73SVivien Didelot 
3944dc30c35bSAndrew Lunn 	if (chip->irq > 0) {
3945dc30c35bSAndrew Lunn 		/* Has to be performed before the MDIO bus is created,
3946dc30c35bSAndrew Lunn 		 * because the PHYs will link there interrupts to these
3947dc30c35bSAndrew Lunn 		 * interrupt controllers
3948dc30c35bSAndrew Lunn 		 */
3949dc30c35bSAndrew Lunn 		mutex_lock(&chip->reg_lock);
3950dc30c35bSAndrew Lunn 		err = mv88e6xxx_g1_irq_setup(chip);
3951dc30c35bSAndrew Lunn 		mutex_unlock(&chip->reg_lock);
3952dc30c35bSAndrew Lunn 
3953dc30c35bSAndrew Lunn 		if (err)
3954dc30c35bSAndrew Lunn 			goto out;
3955dc30c35bSAndrew Lunn 
3956dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
3957dc30c35bSAndrew Lunn 			err = mv88e6xxx_g2_irq_setup(chip);
3958dc30c35bSAndrew Lunn 			if (err)
3959dc30c35bSAndrew Lunn 				goto out_g1_irq;
3960dc30c35bSAndrew Lunn 		}
3961dc30c35bSAndrew Lunn 	}
3962dc30c35bSAndrew Lunn 
3963a3c53be5SAndrew Lunn 	err = mv88e6xxx_mdios_register(chip, np);
3964dc30c35bSAndrew Lunn 	if (err)
3965dc30c35bSAndrew Lunn 		goto out_g2_irq;
3966dc30c35bSAndrew Lunn 
396755ed0ce0SFlorian Fainelli 	err = mv88e6xxx_register_switch(chip);
3968dc30c35bSAndrew Lunn 	if (err)
3969dc30c35bSAndrew Lunn 		goto out_mdio;
3970dc30c35bSAndrew Lunn 
3971fad09c73SVivien Didelot 	return 0;
3972dc30c35bSAndrew Lunn 
3973dc30c35bSAndrew Lunn out_mdio:
3974a3c53be5SAndrew Lunn 	mv88e6xxx_mdios_unregister(chip);
3975dc30c35bSAndrew Lunn out_g2_irq:
397646712644SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0)
3977dc30c35bSAndrew Lunn 		mv88e6xxx_g2_irq_free(chip);
3978dc30c35bSAndrew Lunn out_g1_irq:
397961f7c3f8SAndrew Lunn 	if (chip->irq > 0) {
398061f7c3f8SAndrew Lunn 		mutex_lock(&chip->reg_lock);
3981dc30c35bSAndrew Lunn 		mv88e6xxx_g1_irq_free(chip);
398261f7c3f8SAndrew Lunn 		mutex_unlock(&chip->reg_lock);
398361f7c3f8SAndrew Lunn 	}
3984dc30c35bSAndrew Lunn out:
3985dc30c35bSAndrew Lunn 	return err;
3986fad09c73SVivien Didelot }
3987fad09c73SVivien Didelot 
3988fad09c73SVivien Didelot static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3989fad09c73SVivien Didelot {
3990fad09c73SVivien Didelot 	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
399104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3992fad09c73SVivien Didelot 
3993930188ceSAndrew Lunn 	mv88e6xxx_phy_destroy(chip);
3994fad09c73SVivien Didelot 	mv88e6xxx_unregister_switch(chip);
3995a3c53be5SAndrew Lunn 	mv88e6xxx_mdios_unregister(chip);
3996dc30c35bSAndrew Lunn 
399746712644SAndrew Lunn 	if (chip->irq > 0) {
3998dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
3999dc30c35bSAndrew Lunn 			mv88e6xxx_g2_irq_free(chip);
4000dc30c35bSAndrew Lunn 		mv88e6xxx_g1_irq_free(chip);
4001fad09c73SVivien Didelot 	}
400246712644SAndrew Lunn }
4003fad09c73SVivien Didelot 
4004fad09c73SVivien Didelot static const struct of_device_id mv88e6xxx_of_match[] = {
4005fad09c73SVivien Didelot 	{
4006fad09c73SVivien Didelot 		.compatible = "marvell,mv88e6085",
4007fad09c73SVivien Didelot 		.data = &mv88e6xxx_table[MV88E6085],
4008fad09c73SVivien Didelot 	},
40091a3b39ecSAndrew Lunn 	{
40101a3b39ecSAndrew Lunn 		.compatible = "marvell,mv88e6190",
40111a3b39ecSAndrew Lunn 		.data = &mv88e6xxx_table[MV88E6190],
40121a3b39ecSAndrew Lunn 	},
4013fad09c73SVivien Didelot 	{ /* sentinel */ },
4014fad09c73SVivien Didelot };
4015fad09c73SVivien Didelot 
4016fad09c73SVivien Didelot MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
4017fad09c73SVivien Didelot 
4018fad09c73SVivien Didelot static struct mdio_driver mv88e6xxx_driver = {
4019fad09c73SVivien Didelot 	.probe	= mv88e6xxx_probe,
4020fad09c73SVivien Didelot 	.remove = mv88e6xxx_remove,
4021fad09c73SVivien Didelot 	.mdiodrv.driver = {
4022fad09c73SVivien Didelot 		.name = "mv88e6085",
4023fad09c73SVivien Didelot 		.of_match_table = mv88e6xxx_of_match,
4024fad09c73SVivien Didelot 	},
4025fad09c73SVivien Didelot };
4026fad09c73SVivien Didelot 
4027fad09c73SVivien Didelot static int __init mv88e6xxx_init(void)
4028fad09c73SVivien Didelot {
4029ab3d408dSFlorian Fainelli 	register_switch_driver(&mv88e6xxx_switch_drv);
4030fad09c73SVivien Didelot 	return mdio_driver_register(&mv88e6xxx_driver);
4031fad09c73SVivien Didelot }
4032fad09c73SVivien Didelot module_init(mv88e6xxx_init);
4033fad09c73SVivien Didelot 
4034fad09c73SVivien Didelot static void __exit mv88e6xxx_cleanup(void)
4035fad09c73SVivien Didelot {
4036fad09c73SVivien Didelot 	mdio_driver_unregister(&mv88e6xxx_driver);
4037ab3d408dSFlorian Fainelli 	unregister_switch_driver(&mv88e6xxx_switch_drv);
4038fad09c73SVivien Didelot }
4039fad09c73SVivien Didelot module_exit(mv88e6xxx_cleanup);
4040fad09c73SVivien Didelot 
4041fad09c73SVivien Didelot MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4042fad09c73SVivien Didelot MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4043fad09c73SVivien Didelot MODULE_LICENSE("GPL");
4044