xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/chip.c (revision b7929fb3)
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 
364d5f2ba7SVivien Didelot #include "chip.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))
492774439e5SVivien Didelot 		dev_err(chip->dev, "p%d: failed to restore MAC's link\n", port);
493d78343d2SVivien Didelot 
494d78343d2SVivien Didelot 	return err;
495d78343d2SVivien Didelot }
496d78343d2SVivien Didelot 
497fad09c73SVivien Didelot /* We expect the switch to perform auto negotiation if there is a real
498fad09c73SVivien Didelot  * phy. However, in the case of a fixed link phy, we force the port
499fad09c73SVivien Didelot  * settings from the fixed link settings.
500fad09c73SVivien Didelot  */
501fad09c73SVivien Didelot static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
502fad09c73SVivien Didelot 				  struct phy_device *phydev)
503fad09c73SVivien Didelot {
50404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
5050e7b9925SAndrew Lunn 	int err;
506fad09c73SVivien Didelot 
507fad09c73SVivien Didelot 	if (!phy_is_pseudo_fixed_link(phydev))
508fad09c73SVivien Didelot 		return;
509fad09c73SVivien Didelot 
510fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
511d78343d2SVivien Didelot 	err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed,
512d78343d2SVivien Didelot 				       phydev->duplex, phydev->interface);
513fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
514d78343d2SVivien Didelot 
515d78343d2SVivien Didelot 	if (err && err != -EOPNOTSUPP)
516774439e5SVivien Didelot 		dev_err(ds->dev, "p%d: failed to configure MAC\n", port);
517fad09c73SVivien Didelot }
518fad09c73SVivien Didelot 
519a605a0feSAndrew Lunn static int mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
520fad09c73SVivien Didelot {
521a605a0feSAndrew Lunn 	if (!chip->info->ops->stats_snapshot)
522a605a0feSAndrew Lunn 		return -EOPNOTSUPP;
523fad09c73SVivien Didelot 
524a605a0feSAndrew Lunn 	return chip->info->ops->stats_snapshot(chip, port);
525fad09c73SVivien Didelot }
526fad09c73SVivien Didelot 
527fad09c73SVivien Didelot static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
528dfafe449SAndrew Lunn 	{ "in_good_octets",		8, 0x00, STATS_TYPE_BANK0, },
529dfafe449SAndrew Lunn 	{ "in_bad_octets",		4, 0x02, STATS_TYPE_BANK0, },
530dfafe449SAndrew Lunn 	{ "in_unicast",			4, 0x04, STATS_TYPE_BANK0, },
531dfafe449SAndrew Lunn 	{ "in_broadcasts",		4, 0x06, STATS_TYPE_BANK0, },
532dfafe449SAndrew Lunn 	{ "in_multicasts",		4, 0x07, STATS_TYPE_BANK0, },
533dfafe449SAndrew Lunn 	{ "in_pause",			4, 0x16, STATS_TYPE_BANK0, },
534dfafe449SAndrew Lunn 	{ "in_undersize",		4, 0x18, STATS_TYPE_BANK0, },
535dfafe449SAndrew Lunn 	{ "in_fragments",		4, 0x19, STATS_TYPE_BANK0, },
536dfafe449SAndrew Lunn 	{ "in_oversize",		4, 0x1a, STATS_TYPE_BANK0, },
537dfafe449SAndrew Lunn 	{ "in_jabber",			4, 0x1b, STATS_TYPE_BANK0, },
538dfafe449SAndrew Lunn 	{ "in_rx_error",		4, 0x1c, STATS_TYPE_BANK0, },
539dfafe449SAndrew Lunn 	{ "in_fcs_error",		4, 0x1d, STATS_TYPE_BANK0, },
540dfafe449SAndrew Lunn 	{ "out_octets",			8, 0x0e, STATS_TYPE_BANK0, },
541dfafe449SAndrew Lunn 	{ "out_unicast",		4, 0x10, STATS_TYPE_BANK0, },
542dfafe449SAndrew Lunn 	{ "out_broadcasts",		4, 0x13, STATS_TYPE_BANK0, },
543dfafe449SAndrew Lunn 	{ "out_multicasts",		4, 0x12, STATS_TYPE_BANK0, },
544dfafe449SAndrew Lunn 	{ "out_pause",			4, 0x15, STATS_TYPE_BANK0, },
545dfafe449SAndrew Lunn 	{ "excessive",			4, 0x11, STATS_TYPE_BANK0, },
546dfafe449SAndrew Lunn 	{ "collisions",			4, 0x1e, STATS_TYPE_BANK0, },
547dfafe449SAndrew Lunn 	{ "deferred",			4, 0x05, STATS_TYPE_BANK0, },
548dfafe449SAndrew Lunn 	{ "single",			4, 0x14, STATS_TYPE_BANK0, },
549dfafe449SAndrew Lunn 	{ "multiple",			4, 0x17, STATS_TYPE_BANK0, },
550dfafe449SAndrew Lunn 	{ "out_fcs_error",		4, 0x03, STATS_TYPE_BANK0, },
551dfafe449SAndrew Lunn 	{ "late",			4, 0x1f, STATS_TYPE_BANK0, },
552dfafe449SAndrew Lunn 	{ "hist_64bytes",		4, 0x08, STATS_TYPE_BANK0, },
553dfafe449SAndrew Lunn 	{ "hist_65_127bytes",		4, 0x09, STATS_TYPE_BANK0, },
554dfafe449SAndrew Lunn 	{ "hist_128_255bytes",		4, 0x0a, STATS_TYPE_BANK0, },
555dfafe449SAndrew Lunn 	{ "hist_256_511bytes",		4, 0x0b, STATS_TYPE_BANK0, },
556dfafe449SAndrew Lunn 	{ "hist_512_1023bytes",		4, 0x0c, STATS_TYPE_BANK0, },
557dfafe449SAndrew Lunn 	{ "hist_1024_max_bytes",	4, 0x0d, STATS_TYPE_BANK0, },
558dfafe449SAndrew Lunn 	{ "sw_in_discards",		4, 0x10, STATS_TYPE_PORT, },
559dfafe449SAndrew Lunn 	{ "sw_in_filtered",		2, 0x12, STATS_TYPE_PORT, },
560dfafe449SAndrew Lunn 	{ "sw_out_filtered",		2, 0x13, STATS_TYPE_PORT, },
561dfafe449SAndrew Lunn 	{ "in_discards",		4, 0x00, STATS_TYPE_BANK1, },
562dfafe449SAndrew Lunn 	{ "in_filtered",		4, 0x01, STATS_TYPE_BANK1, },
563dfafe449SAndrew Lunn 	{ "in_accepted",		4, 0x02, STATS_TYPE_BANK1, },
564dfafe449SAndrew Lunn 	{ "in_bad_accepted",		4, 0x03, STATS_TYPE_BANK1, },
565dfafe449SAndrew Lunn 	{ "in_good_avb_class_a",	4, 0x04, STATS_TYPE_BANK1, },
566dfafe449SAndrew Lunn 	{ "in_good_avb_class_b",	4, 0x05, STATS_TYPE_BANK1, },
567dfafe449SAndrew Lunn 	{ "in_bad_avb_class_a",		4, 0x06, STATS_TYPE_BANK1, },
568dfafe449SAndrew Lunn 	{ "in_bad_avb_class_b",		4, 0x07, STATS_TYPE_BANK1, },
569dfafe449SAndrew Lunn 	{ "tcam_counter_0",		4, 0x08, STATS_TYPE_BANK1, },
570dfafe449SAndrew Lunn 	{ "tcam_counter_1",		4, 0x09, STATS_TYPE_BANK1, },
571dfafe449SAndrew Lunn 	{ "tcam_counter_2",		4, 0x0a, STATS_TYPE_BANK1, },
572dfafe449SAndrew Lunn 	{ "tcam_counter_3",		4, 0x0b, STATS_TYPE_BANK1, },
573dfafe449SAndrew Lunn 	{ "in_da_unknown",		4, 0x0e, STATS_TYPE_BANK1, },
574dfafe449SAndrew Lunn 	{ "in_management",		4, 0x0f, STATS_TYPE_BANK1, },
575dfafe449SAndrew Lunn 	{ "out_queue_0",		4, 0x10, STATS_TYPE_BANK1, },
576dfafe449SAndrew Lunn 	{ "out_queue_1",		4, 0x11, STATS_TYPE_BANK1, },
577dfafe449SAndrew Lunn 	{ "out_queue_2",		4, 0x12, STATS_TYPE_BANK1, },
578dfafe449SAndrew Lunn 	{ "out_queue_3",		4, 0x13, STATS_TYPE_BANK1, },
579dfafe449SAndrew Lunn 	{ "out_queue_4",		4, 0x14, STATS_TYPE_BANK1, },
580dfafe449SAndrew Lunn 	{ "out_queue_5",		4, 0x15, STATS_TYPE_BANK1, },
581dfafe449SAndrew Lunn 	{ "out_queue_6",		4, 0x16, STATS_TYPE_BANK1, },
582dfafe449SAndrew Lunn 	{ "out_queue_7",		4, 0x17, STATS_TYPE_BANK1, },
583dfafe449SAndrew Lunn 	{ "out_cut_through",		4, 0x18, STATS_TYPE_BANK1, },
584dfafe449SAndrew Lunn 	{ "out_octets_a",		4, 0x1a, STATS_TYPE_BANK1, },
585dfafe449SAndrew Lunn 	{ "out_octets_b",		4, 0x1b, STATS_TYPE_BANK1, },
586dfafe449SAndrew Lunn 	{ "out_management",		4, 0x1f, STATS_TYPE_BANK1, },
587fad09c73SVivien Didelot };
588fad09c73SVivien Didelot 
589fad09c73SVivien Didelot static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
590fad09c73SVivien Didelot 					    struct mv88e6xxx_hw_stat *s,
591e0d8b615SAndrew Lunn 					    int port, u16 bank1_select,
592e0d8b615SAndrew Lunn 					    u16 histogram)
593fad09c73SVivien Didelot {
594fad09c73SVivien Didelot 	u32 low;
595fad09c73SVivien Didelot 	u32 high = 0;
596dfafe449SAndrew Lunn 	u16 reg = 0;
5970e7b9925SAndrew Lunn 	int err;
598fad09c73SVivien Didelot 	u64 value;
599fad09c73SVivien Didelot 
600fad09c73SVivien Didelot 	switch (s->type) {
601dfafe449SAndrew Lunn 	case STATS_TYPE_PORT:
6020e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, s->reg, &reg);
6030e7b9925SAndrew Lunn 		if (err)
604fad09c73SVivien Didelot 			return UINT64_MAX;
605fad09c73SVivien Didelot 
6060e7b9925SAndrew Lunn 		low = reg;
607fad09c73SVivien Didelot 		if (s->sizeof_stat == 4) {
6080e7b9925SAndrew Lunn 			err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
6090e7b9925SAndrew Lunn 			if (err)
610fad09c73SVivien Didelot 				return UINT64_MAX;
6110e7b9925SAndrew Lunn 			high = reg;
612fad09c73SVivien Didelot 		}
613fad09c73SVivien Didelot 		break;
614dfafe449SAndrew Lunn 	case STATS_TYPE_BANK1:
615e0d8b615SAndrew Lunn 		reg = bank1_select;
616dfafe449SAndrew Lunn 		/* fall through */
617dfafe449SAndrew Lunn 	case STATS_TYPE_BANK0:
618e0d8b615SAndrew Lunn 		reg |= s->reg | histogram;
6197f9ef3afSAndrew Lunn 		mv88e6xxx_g1_stats_read(chip, reg, &low);
620fad09c73SVivien Didelot 		if (s->sizeof_stat == 8)
6217f9ef3afSAndrew Lunn 			mv88e6xxx_g1_stats_read(chip, reg + 1, &high);
6229fc3e4dcSGustavo A. R. Silva 		break;
6239fc3e4dcSGustavo A. R. Silva 	default:
6249fc3e4dcSGustavo A. R. Silva 		return UINT64_MAX;
625fad09c73SVivien Didelot 	}
626fad09c73SVivien Didelot 	value = (((u64)high) << 16) | low;
627fad09c73SVivien Didelot 	return value;
628fad09c73SVivien Didelot }
629fad09c73SVivien Didelot 
630dfafe449SAndrew Lunn static void mv88e6xxx_stats_get_strings(struct mv88e6xxx_chip *chip,
631dfafe449SAndrew Lunn 					uint8_t *data, int types)
632fad09c73SVivien Didelot {
633fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
634fad09c73SVivien Didelot 	int i, j;
635fad09c73SVivien Didelot 
636fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
637fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
638dfafe449SAndrew Lunn 		if (stat->type & types) {
639fad09c73SVivien Didelot 			memcpy(data + j * ETH_GSTRING_LEN, stat->string,
640fad09c73SVivien Didelot 			       ETH_GSTRING_LEN);
641fad09c73SVivien Didelot 			j++;
642fad09c73SVivien Didelot 		}
643fad09c73SVivien Didelot 	}
644fad09c73SVivien Didelot }
645fad09c73SVivien Didelot 
646dfafe449SAndrew Lunn static void mv88e6095_stats_get_strings(struct mv88e6xxx_chip *chip,
647dfafe449SAndrew Lunn 					uint8_t *data)
648dfafe449SAndrew Lunn {
649dfafe449SAndrew Lunn 	mv88e6xxx_stats_get_strings(chip, data,
650dfafe449SAndrew Lunn 				    STATS_TYPE_BANK0 | STATS_TYPE_PORT);
651dfafe449SAndrew Lunn }
652dfafe449SAndrew Lunn 
653dfafe449SAndrew Lunn static void mv88e6320_stats_get_strings(struct mv88e6xxx_chip *chip,
654dfafe449SAndrew Lunn 					uint8_t *data)
655dfafe449SAndrew Lunn {
656dfafe449SAndrew Lunn 	mv88e6xxx_stats_get_strings(chip, data,
657dfafe449SAndrew Lunn 				    STATS_TYPE_BANK0 | STATS_TYPE_BANK1);
658dfafe449SAndrew Lunn }
659dfafe449SAndrew Lunn 
660dfafe449SAndrew Lunn static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
661dfafe449SAndrew Lunn 				  uint8_t *data)
662fad09c73SVivien Didelot {
66304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
664dfafe449SAndrew Lunn 
665dfafe449SAndrew Lunn 	if (chip->info->ops->stats_get_strings)
666dfafe449SAndrew Lunn 		chip->info->ops->stats_get_strings(chip, data);
667dfafe449SAndrew Lunn }
668dfafe449SAndrew Lunn 
669dfafe449SAndrew Lunn static int mv88e6xxx_stats_get_sset_count(struct mv88e6xxx_chip *chip,
670dfafe449SAndrew Lunn 					  int types)
671dfafe449SAndrew Lunn {
672fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
673fad09c73SVivien Didelot 	int i, j;
674fad09c73SVivien Didelot 
675fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
676fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
677dfafe449SAndrew Lunn 		if (stat->type & types)
678fad09c73SVivien Didelot 			j++;
679fad09c73SVivien Didelot 	}
680fad09c73SVivien Didelot 	return j;
681fad09c73SVivien Didelot }
682fad09c73SVivien Didelot 
683dfafe449SAndrew Lunn static int mv88e6095_stats_get_sset_count(struct mv88e6xxx_chip *chip)
684dfafe449SAndrew Lunn {
685dfafe449SAndrew Lunn 	return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 |
686dfafe449SAndrew Lunn 					      STATS_TYPE_PORT);
687dfafe449SAndrew Lunn }
688dfafe449SAndrew Lunn 
689dfafe449SAndrew Lunn static int mv88e6320_stats_get_sset_count(struct mv88e6xxx_chip *chip)
690dfafe449SAndrew Lunn {
691dfafe449SAndrew Lunn 	return mv88e6xxx_stats_get_sset_count(chip, STATS_TYPE_BANK0 |
692dfafe449SAndrew Lunn 					      STATS_TYPE_BANK1);
693dfafe449SAndrew Lunn }
694dfafe449SAndrew Lunn 
695dfafe449SAndrew Lunn static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
696dfafe449SAndrew Lunn {
697dfafe449SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
698dfafe449SAndrew Lunn 
699dfafe449SAndrew Lunn 	if (chip->info->ops->stats_get_sset_count)
700dfafe449SAndrew Lunn 		return chip->info->ops->stats_get_sset_count(chip);
701dfafe449SAndrew Lunn 
702dfafe449SAndrew Lunn 	return 0;
703dfafe449SAndrew Lunn }
704dfafe449SAndrew Lunn 
705052f947fSAndrew Lunn static void mv88e6xxx_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
706e0d8b615SAndrew Lunn 				      uint64_t *data, int types,
707e0d8b615SAndrew Lunn 				      u16 bank1_select, u16 histogram)
708052f947fSAndrew Lunn {
709052f947fSAndrew Lunn 	struct mv88e6xxx_hw_stat *stat;
710052f947fSAndrew Lunn 	int i, j;
711052f947fSAndrew Lunn 
712052f947fSAndrew Lunn 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
713052f947fSAndrew Lunn 		stat = &mv88e6xxx_hw_stats[i];
714052f947fSAndrew Lunn 		if (stat->type & types) {
715e0d8b615SAndrew Lunn 			data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port,
716e0d8b615SAndrew Lunn 							      bank1_select,
717e0d8b615SAndrew Lunn 							      histogram);
718052f947fSAndrew Lunn 			j++;
719052f947fSAndrew Lunn 		}
720052f947fSAndrew Lunn 	}
721052f947fSAndrew Lunn }
722052f947fSAndrew Lunn 
723052f947fSAndrew Lunn static void mv88e6095_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
724052f947fSAndrew Lunn 				      uint64_t *data)
725052f947fSAndrew Lunn {
726052f947fSAndrew Lunn 	return mv88e6xxx_stats_get_stats(chip, port, data,
727e0d8b615SAndrew Lunn 					 STATS_TYPE_BANK0 | STATS_TYPE_PORT,
728e0d8b615SAndrew Lunn 					 0, GLOBAL_STATS_OP_HIST_RX_TX);
729052f947fSAndrew Lunn }
730052f947fSAndrew Lunn 
731052f947fSAndrew Lunn static void mv88e6320_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
732052f947fSAndrew Lunn 				      uint64_t *data)
733052f947fSAndrew Lunn {
734052f947fSAndrew Lunn 	return mv88e6xxx_stats_get_stats(chip, port, data,
735e0d8b615SAndrew Lunn 					 STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
736e0d8b615SAndrew Lunn 					 GLOBAL_STATS_OP_BANK_1_BIT_9,
737e0d8b615SAndrew Lunn 					 GLOBAL_STATS_OP_HIST_RX_TX);
738e0d8b615SAndrew Lunn }
739e0d8b615SAndrew Lunn 
740e0d8b615SAndrew Lunn static void mv88e6390_stats_get_stats(struct mv88e6xxx_chip *chip, int port,
741e0d8b615SAndrew Lunn 				      uint64_t *data)
742e0d8b615SAndrew Lunn {
743e0d8b615SAndrew Lunn 	return mv88e6xxx_stats_get_stats(chip, port, data,
744e0d8b615SAndrew Lunn 					 STATS_TYPE_BANK0 | STATS_TYPE_BANK1,
745e0d8b615SAndrew Lunn 					 GLOBAL_STATS_OP_BANK_1_BIT_10, 0);
746052f947fSAndrew Lunn }
747052f947fSAndrew Lunn 
748052f947fSAndrew Lunn static void mv88e6xxx_get_stats(struct mv88e6xxx_chip *chip, int port,
749052f947fSAndrew Lunn 				uint64_t *data)
750052f947fSAndrew Lunn {
751052f947fSAndrew Lunn 	if (chip->info->ops->stats_get_stats)
752052f947fSAndrew Lunn 		chip->info->ops->stats_get_stats(chip, port, data);
753052f947fSAndrew Lunn }
754052f947fSAndrew Lunn 
755fad09c73SVivien Didelot static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
756fad09c73SVivien Didelot 					uint64_t *data)
757fad09c73SVivien Didelot {
75804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
759fad09c73SVivien Didelot 	int ret;
760fad09c73SVivien Didelot 
761fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
762fad09c73SVivien Didelot 
763a605a0feSAndrew Lunn 	ret = mv88e6xxx_stats_snapshot(chip, port);
764fad09c73SVivien Didelot 	if (ret < 0) {
765fad09c73SVivien Didelot 		mutex_unlock(&chip->reg_lock);
766fad09c73SVivien Didelot 		return;
767fad09c73SVivien Didelot 	}
768052f947fSAndrew Lunn 
769052f947fSAndrew Lunn 	mv88e6xxx_get_stats(chip, port, data);
770fad09c73SVivien Didelot 
771fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
772fad09c73SVivien Didelot }
773fad09c73SVivien Didelot 
774de227387SAndrew Lunn static int mv88e6xxx_stats_set_histogram(struct mv88e6xxx_chip *chip)
775de227387SAndrew Lunn {
776de227387SAndrew Lunn 	if (chip->info->ops->stats_set_histogram)
777de227387SAndrew Lunn 		return chip->info->ops->stats_set_histogram(chip);
778de227387SAndrew Lunn 
779de227387SAndrew Lunn 	return 0;
780de227387SAndrew Lunn }
781de227387SAndrew Lunn 
782fad09c73SVivien Didelot static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
783fad09c73SVivien Didelot {
784fad09c73SVivien Didelot 	return 32 * sizeof(u16);
785fad09c73SVivien Didelot }
786fad09c73SVivien Didelot 
787fad09c73SVivien Didelot static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
788fad09c73SVivien Didelot 			       struct ethtool_regs *regs, void *_p)
789fad09c73SVivien Didelot {
79004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
7910e7b9925SAndrew Lunn 	int err;
7920e7b9925SAndrew Lunn 	u16 reg;
793fad09c73SVivien Didelot 	u16 *p = _p;
794fad09c73SVivien Didelot 	int i;
795fad09c73SVivien Didelot 
796fad09c73SVivien Didelot 	regs->version = 0;
797fad09c73SVivien Didelot 
798fad09c73SVivien Didelot 	memset(p, 0xff, 32 * sizeof(u16));
799fad09c73SVivien Didelot 
800fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
801fad09c73SVivien Didelot 
802fad09c73SVivien Didelot 	for (i = 0; i < 32; i++) {
803fad09c73SVivien Didelot 
8040e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, i, &reg);
8050e7b9925SAndrew Lunn 		if (!err)
8060e7b9925SAndrew Lunn 			p[i] = reg;
807fad09c73SVivien Didelot 	}
808fad09c73SVivien Didelot 
809fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
810fad09c73SVivien Didelot }
811fad09c73SVivien Didelot 
812fad09c73SVivien Didelot static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
813fad09c73SVivien Didelot 			     struct ethtool_eee *e)
814fad09c73SVivien Didelot {
81504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
8169c93829cSVivien Didelot 	u16 reg;
8179c93829cSVivien Didelot 	int err;
818fad09c73SVivien Didelot 
819fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
820fad09c73SVivien Didelot 		return -EOPNOTSUPP;
821fad09c73SVivien Didelot 
822fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
823fad09c73SVivien Didelot 
8249c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
8259c93829cSVivien Didelot 	if (err)
826fad09c73SVivien Didelot 		goto out;
827fad09c73SVivien Didelot 
828fad09c73SVivien Didelot 	e->eee_enabled = !!(reg & 0x0200);
829fad09c73SVivien Didelot 	e->tx_lpi_enabled = !!(reg & 0x0100);
830fad09c73SVivien Didelot 
8315f83dc93SVivien Didelot 	err = mv88e6xxx_port_read(chip, port, MV88E6XXX_PORT_STS, &reg);
8329c93829cSVivien Didelot 	if (err)
833fad09c73SVivien Didelot 		goto out;
834fad09c73SVivien Didelot 
8355f83dc93SVivien Didelot 	e->eee_active = !!(reg & MV88E6352_PORT_STS_EEE);
836fad09c73SVivien Didelot out:
837fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
8389c93829cSVivien Didelot 
8399c93829cSVivien Didelot 	return err;
840fad09c73SVivien Didelot }
841fad09c73SVivien Didelot 
842fad09c73SVivien Didelot static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
843fad09c73SVivien Didelot 			     struct phy_device *phydev, struct ethtool_eee *e)
844fad09c73SVivien Didelot {
84504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
8469c93829cSVivien Didelot 	u16 reg;
8479c93829cSVivien Didelot 	int err;
848fad09c73SVivien Didelot 
849fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
850fad09c73SVivien Didelot 		return -EOPNOTSUPP;
851fad09c73SVivien Didelot 
852fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
853fad09c73SVivien Didelot 
8549c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
8559c93829cSVivien Didelot 	if (err)
856fad09c73SVivien Didelot 		goto out;
857fad09c73SVivien Didelot 
8589c93829cSVivien Didelot 	reg &= ~0x0300;
859fad09c73SVivien Didelot 	if (e->eee_enabled)
860fad09c73SVivien Didelot 		reg |= 0x0200;
861fad09c73SVivien Didelot 	if (e->tx_lpi_enabled)
862fad09c73SVivien Didelot 		reg |= 0x0100;
863fad09c73SVivien Didelot 
8649c93829cSVivien Didelot 	err = mv88e6xxx_phy_write(chip, port, 16, reg);
865fad09c73SVivien Didelot out:
866fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
867fad09c73SVivien Didelot 
8689c93829cSVivien Didelot 	return err;
869fad09c73SVivien Didelot }
870fad09c73SVivien Didelot 
871e5887a2aSVivien Didelot static u16 mv88e6xxx_port_vlan(struct mv88e6xxx_chip *chip, int dev, int port)
872fad09c73SVivien Didelot {
873e5887a2aSVivien Didelot 	struct dsa_switch *ds = NULL;
874e5887a2aSVivien Didelot 	struct net_device *br;
875e5887a2aSVivien Didelot 	u16 pvlan;
876fad09c73SVivien Didelot 	int i;
877fad09c73SVivien Didelot 
878e5887a2aSVivien Didelot 	if (dev < DSA_MAX_SWITCHES)
879e5887a2aSVivien Didelot 		ds = chip->ds->dst->ds[dev];
880fad09c73SVivien Didelot 
881e5887a2aSVivien Didelot 	/* Prevent frames from unknown switch or port */
882e5887a2aSVivien Didelot 	if (!ds || port >= ds->num_ports)
883e5887a2aSVivien Didelot 		return 0;
884e5887a2aSVivien Didelot 
885e5887a2aSVivien Didelot 	/* Frames from DSA links and CPU ports can egress any local port */
886e5887a2aSVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
887e5887a2aSVivien Didelot 		return mv88e6xxx_port_mask(chip);
888e5887a2aSVivien Didelot 
889e5887a2aSVivien Didelot 	br = ds->ports[port].bridge_dev;
890e5887a2aSVivien Didelot 	pvlan = 0;
891e5887a2aSVivien Didelot 
892e5887a2aSVivien Didelot 	/* Frames from user ports can egress any local DSA links and CPU ports,
893e5887a2aSVivien Didelot 	 * as well as any local member of their bridge group.
894e5887a2aSVivien Didelot 	 */
895e5887a2aSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
896e5887a2aSVivien Didelot 		if (dsa_is_cpu_port(chip->ds, i) ||
897e5887a2aSVivien Didelot 		    dsa_is_dsa_port(chip->ds, i) ||
898e5887a2aSVivien Didelot 		    (br && chip->ds->ports[i].bridge_dev == br))
899e5887a2aSVivien Didelot 			pvlan |= BIT(i);
900e5887a2aSVivien Didelot 
901e5887a2aSVivien Didelot 	return pvlan;
902fad09c73SVivien Didelot }
903e5887a2aSVivien Didelot 
904240ea3efSVivien Didelot static int mv88e6xxx_port_vlan_map(struct mv88e6xxx_chip *chip, int port)
905e5887a2aSVivien Didelot {
906e5887a2aSVivien Didelot 	u16 output_ports = mv88e6xxx_port_vlan(chip, chip->ds->index, port);
907fad09c73SVivien Didelot 
908fad09c73SVivien Didelot 	/* prevent frames from going back out of the port they came in on */
909fad09c73SVivien Didelot 	output_ports &= ~BIT(port);
910fad09c73SVivien Didelot 
9115a7921f4SVivien Didelot 	return mv88e6xxx_port_set_vlan_map(chip, port, output_ports);
912fad09c73SVivien Didelot }
913fad09c73SVivien Didelot 
914fad09c73SVivien Didelot static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
915fad09c73SVivien Didelot 					 u8 state)
916fad09c73SVivien Didelot {
91704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
918fad09c73SVivien Didelot 	int err;
919fad09c73SVivien Didelot 
920fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
921f894c29cSVivien Didelot 	err = mv88e6xxx_port_set_state(chip, port, state);
922fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
923fad09c73SVivien Didelot 
924fad09c73SVivien Didelot 	if (err)
925774439e5SVivien Didelot 		dev_err(ds->dev, "p%d: failed to update state\n", port);
926fad09c73SVivien Didelot }
927fad09c73SVivien Didelot 
928a2ac29d2SVivien Didelot static int mv88e6xxx_atu_setup(struct mv88e6xxx_chip *chip)
929a2ac29d2SVivien Didelot {
930c3a7d4adSVivien Didelot 	int err;
931c3a7d4adSVivien Didelot 
932daefc943SVivien Didelot 	err = mv88e6xxx_g1_atu_flush(chip, 0, true);
933daefc943SVivien Didelot 	if (err)
934daefc943SVivien Didelot 		return err;
935daefc943SVivien Didelot 
936c3a7d4adSVivien Didelot 	err = mv88e6xxx_g1_atu_set_learn2all(chip, true);
937c3a7d4adSVivien Didelot 	if (err)
938c3a7d4adSVivien Didelot 		return err;
939c3a7d4adSVivien Didelot 
940a2ac29d2SVivien Didelot 	return mv88e6xxx_g1_atu_set_age_time(chip, 300000);
941a2ac29d2SVivien Didelot }
942a2ac29d2SVivien Didelot 
94317a1594eSVivien Didelot static int mv88e6xxx_pvt_map(struct mv88e6xxx_chip *chip, int dev, int port)
94417a1594eSVivien Didelot {
94517a1594eSVivien Didelot 	u16 pvlan = 0;
94617a1594eSVivien Didelot 
94717a1594eSVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
94817a1594eSVivien Didelot 		return -EOPNOTSUPP;
94917a1594eSVivien Didelot 
95017a1594eSVivien Didelot 	/* Skip the local source device, which uses in-chip port VLAN */
95117a1594eSVivien Didelot 	if (dev != chip->ds->index)
952aec5ac88SVivien Didelot 		pvlan = mv88e6xxx_port_vlan(chip, dev, port);
95317a1594eSVivien Didelot 
95417a1594eSVivien Didelot 	return mv88e6xxx_g2_pvt_write(chip, dev, port, pvlan);
95517a1594eSVivien Didelot }
95617a1594eSVivien Didelot 
95781228996SVivien Didelot static int mv88e6xxx_pvt_setup(struct mv88e6xxx_chip *chip)
95881228996SVivien Didelot {
95917a1594eSVivien Didelot 	int dev, port;
96017a1594eSVivien Didelot 	int err;
96117a1594eSVivien Didelot 
96281228996SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
96381228996SVivien Didelot 		return 0;
96481228996SVivien Didelot 
96581228996SVivien Didelot 	/* Clear 5 Bit Port for usage with Marvell Link Street devices:
96681228996SVivien Didelot 	 * use 4 bits for the Src_Port/Src_Trunk and 5 bits for the Src_Dev.
96781228996SVivien Didelot 	 */
96817a1594eSVivien Didelot 	err = mv88e6xxx_g2_misc_4_bit_port(chip);
96917a1594eSVivien Didelot 	if (err)
97017a1594eSVivien Didelot 		return err;
97117a1594eSVivien Didelot 
97217a1594eSVivien Didelot 	for (dev = 0; dev < MV88E6XXX_MAX_PVT_SWITCHES; ++dev) {
97317a1594eSVivien Didelot 		for (port = 0; port < MV88E6XXX_MAX_PVT_PORTS; ++port) {
97417a1594eSVivien Didelot 			err = mv88e6xxx_pvt_map(chip, dev, port);
97517a1594eSVivien Didelot 			if (err)
97617a1594eSVivien Didelot 				return err;
97717a1594eSVivien Didelot 		}
97817a1594eSVivien Didelot 	}
97917a1594eSVivien Didelot 
98017a1594eSVivien Didelot 	return 0;
98181228996SVivien Didelot }
98281228996SVivien Didelot 
983749efcb8SVivien Didelot static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
984749efcb8SVivien Didelot {
985749efcb8SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
986749efcb8SVivien Didelot 	int err;
987749efcb8SVivien Didelot 
988749efcb8SVivien Didelot 	mutex_lock(&chip->reg_lock);
989e606ca36SVivien Didelot 	err = mv88e6xxx_g1_atu_remove(chip, 0, port, false);
990749efcb8SVivien Didelot 	mutex_unlock(&chip->reg_lock);
991749efcb8SVivien Didelot 
992749efcb8SVivien Didelot 	if (err)
993774439e5SVivien Didelot 		dev_err(ds->dev, "p%d: failed to flush ATU\n", port);
994749efcb8SVivien Didelot }
995749efcb8SVivien Didelot 
996b486d7c9SVivien Didelot static int mv88e6xxx_vtu_setup(struct mv88e6xxx_chip *chip)
997b486d7c9SVivien Didelot {
998b486d7c9SVivien Didelot 	if (!chip->info->max_vid)
999b486d7c9SVivien Didelot 		return 0;
1000b486d7c9SVivien Didelot 
1001b486d7c9SVivien Didelot 	return mv88e6xxx_g1_vtu_flush(chip);
1002b486d7c9SVivien Didelot }
1003b486d7c9SVivien Didelot 
1004f1394b78SVivien Didelot static int mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
1005f1394b78SVivien Didelot 				 struct mv88e6xxx_vtu_entry *entry)
1006f1394b78SVivien Didelot {
1007f1394b78SVivien Didelot 	if (!chip->info->ops->vtu_getnext)
1008f1394b78SVivien Didelot 		return -EOPNOTSUPP;
1009f1394b78SVivien Didelot 
1010f1394b78SVivien Didelot 	return chip->info->ops->vtu_getnext(chip, entry);
1011f1394b78SVivien Didelot }
1012f1394b78SVivien Didelot 
10130ad5daf6SVivien Didelot static int mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
10140ad5daf6SVivien Didelot 				   struct mv88e6xxx_vtu_entry *entry)
10150ad5daf6SVivien Didelot {
10160ad5daf6SVivien Didelot 	if (!chip->info->ops->vtu_loadpurge)
10170ad5daf6SVivien Didelot 		return -EOPNOTSUPP;
10180ad5daf6SVivien Didelot 
10190ad5daf6SVivien Didelot 	return chip->info->ops->vtu_loadpurge(chip, entry);
10200ad5daf6SVivien Didelot }
10210ad5daf6SVivien Didelot 
1022fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1023fad09c73SVivien Didelot 				    struct switchdev_obj_port_vlan *vlan,
1024438ff537SVivien Didelot 				    switchdev_obj_dump_cb_t *cb)
1025fad09c73SVivien Didelot {
102604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10273afb4bdeSVivien Didelot 	struct mv88e6xxx_vtu_entry next = {
10283afb4bdeSVivien Didelot 		.vid = chip->info->max_vid,
10293afb4bdeSVivien Didelot 	};
1030fad09c73SVivien Didelot 	u16 pvid;
1031fad09c73SVivien Didelot 	int err;
1032fad09c73SVivien Didelot 
10333cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1034fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1035fad09c73SVivien Didelot 
1036fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1037fad09c73SVivien Didelot 
103877064f37SVivien Didelot 	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
1039fad09c73SVivien Didelot 	if (err)
1040fad09c73SVivien Didelot 		goto unlock;
1041fad09c73SVivien Didelot 
1042fad09c73SVivien Didelot 	do {
1043f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &next);
1044fad09c73SVivien Didelot 		if (err)
1045fad09c73SVivien Didelot 			break;
1046fad09c73SVivien Didelot 
1047fad09c73SVivien Didelot 		if (!next.valid)
1048fad09c73SVivien Didelot 			break;
1049fad09c73SVivien Didelot 
1050bd00e053SVivien Didelot 		if (next.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1051fad09c73SVivien Didelot 			continue;
1052fad09c73SVivien Didelot 
1053fad09c73SVivien Didelot 		/* reinit and dump this VLAN obj */
1054fad09c73SVivien Didelot 		vlan->vid_begin = next.vid;
1055fad09c73SVivien Didelot 		vlan->vid_end = next.vid;
1056fad09c73SVivien Didelot 		vlan->flags = 0;
1057fad09c73SVivien Didelot 
1058bd00e053SVivien Didelot 		if (next.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1059fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1060fad09c73SVivien Didelot 
1061fad09c73SVivien Didelot 		if (next.vid == pvid)
1062fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1063fad09c73SVivien Didelot 
1064fad09c73SVivien Didelot 		err = cb(&vlan->obj);
1065fad09c73SVivien Didelot 		if (err)
1066fad09c73SVivien Didelot 			break;
10673cf3c846SVivien Didelot 	} while (next.vid < chip->info->max_vid);
1068fad09c73SVivien Didelot 
1069fad09c73SVivien Didelot unlock:
1070fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1071fad09c73SVivien Didelot 
1072fad09c73SVivien Didelot 	return err;
1073fad09c73SVivien Didelot }
1074fad09c73SVivien Didelot 
1075d7f435f9SVivien Didelot static int mv88e6xxx_atu_new(struct mv88e6xxx_chip *chip, u16 *fid)
1076fad09c73SVivien Didelot {
1077fad09c73SVivien Didelot 	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
10783afb4bdeSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
10793afb4bdeSVivien Didelot 		.vid = chip->info->max_vid,
10803afb4bdeSVivien Didelot 	};
1081fad09c73SVivien Didelot 	int i, err;
1082fad09c73SVivien Didelot 
1083fad09c73SVivien Didelot 	bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1084fad09c73SVivien Didelot 
1085fad09c73SVivien Didelot 	/* Set every FID bit used by the (un)bridged ports */
1086370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1087b4e48c50SVivien Didelot 		err = mv88e6xxx_port_get_fid(chip, i, fid);
1088fad09c73SVivien Didelot 		if (err)
1089fad09c73SVivien Didelot 			return err;
1090fad09c73SVivien Didelot 
1091fad09c73SVivien Didelot 		set_bit(*fid, fid_bitmap);
1092fad09c73SVivien Didelot 	}
1093fad09c73SVivien Didelot 
1094fad09c73SVivien Didelot 	/* Set every FID bit used by the VLAN entries */
1095fad09c73SVivien Didelot 	do {
1096f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &vlan);
1097fad09c73SVivien Didelot 		if (err)
1098fad09c73SVivien Didelot 			return err;
1099fad09c73SVivien Didelot 
1100fad09c73SVivien Didelot 		if (!vlan.valid)
1101fad09c73SVivien Didelot 			break;
1102fad09c73SVivien Didelot 
1103fad09c73SVivien Didelot 		set_bit(vlan.fid, fid_bitmap);
11043cf3c846SVivien Didelot 	} while (vlan.vid < chip->info->max_vid);
1105fad09c73SVivien Didelot 
1106fad09c73SVivien Didelot 	/* The reset value 0x000 is used to indicate that multiple address
1107fad09c73SVivien Didelot 	 * databases are not needed. Return the next positive available.
1108fad09c73SVivien Didelot 	 */
1109fad09c73SVivien Didelot 	*fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
1110fad09c73SVivien Didelot 	if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
1111fad09c73SVivien Didelot 		return -ENOSPC;
1112fad09c73SVivien Didelot 
1113fad09c73SVivien Didelot 	/* Clear the database */
1114daefc943SVivien Didelot 	return mv88e6xxx_g1_atu_flush(chip, *fid, true);
1115fad09c73SVivien Didelot }
1116fad09c73SVivien Didelot 
1117567aa59aSVivien Didelot static int mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
1118567aa59aSVivien Didelot 			     struct mv88e6xxx_vtu_entry *entry, bool new)
1119fad09c73SVivien Didelot {
1120fad09c73SVivien Didelot 	int err;
1121fad09c73SVivien Didelot 
1122fad09c73SVivien Didelot 	if (!vid)
1123fad09c73SVivien Didelot 		return -EINVAL;
1124fad09c73SVivien Didelot 
11253afb4bdeSVivien Didelot 	entry->vid = vid - 1;
11263afb4bdeSVivien Didelot 	entry->valid = false;
1127fad09c73SVivien Didelot 
1128f1394b78SVivien Didelot 	err = mv88e6xxx_vtu_getnext(chip, entry);
1129fad09c73SVivien Didelot 	if (err)
1130fad09c73SVivien Didelot 		return err;
1131fad09c73SVivien Didelot 
1132567aa59aSVivien Didelot 	if (entry->vid == vid && entry->valid)
1133567aa59aSVivien Didelot 		return 0;
1134fad09c73SVivien Didelot 
1135567aa59aSVivien Didelot 	if (new) {
1136567aa59aSVivien Didelot 		int i;
1137567aa59aSVivien Didelot 
1138567aa59aSVivien Didelot 		/* Initialize a fresh VLAN entry */
1139567aa59aSVivien Didelot 		memset(entry, 0, sizeof(*entry));
1140567aa59aSVivien Didelot 		entry->valid = true;
1141567aa59aSVivien Didelot 		entry->vid = vid;
1142567aa59aSVivien Didelot 
1143553a768dSVivien Didelot 		/* Exclude all ports */
1144567aa59aSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
1145553a768dSVivien Didelot 			entry->member[i] =
1146553a768dSVivien Didelot 				GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1147567aa59aSVivien Didelot 
1148567aa59aSVivien Didelot 		return mv88e6xxx_atu_new(chip, &entry->fid);
1149fad09c73SVivien Didelot 	}
1150fad09c73SVivien Didelot 
1151567aa59aSVivien Didelot 	/* switchdev expects -EOPNOTSUPP to honor software VLANs */
1152567aa59aSVivien Didelot 	return -EOPNOTSUPP;
1153fad09c73SVivien Didelot }
1154fad09c73SVivien Didelot 
1155fad09c73SVivien Didelot static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1156fad09c73SVivien Didelot 					u16 vid_begin, u16 vid_end)
1157fad09c73SVivien Didelot {
115804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
11593afb4bdeSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
11603afb4bdeSVivien Didelot 		.vid = vid_begin - 1,
11613afb4bdeSVivien Didelot 	};
1162fad09c73SVivien Didelot 	int i, err;
1163fad09c73SVivien Didelot 
1164fad09c73SVivien Didelot 	if (!vid_begin)
1165fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1166fad09c73SVivien Didelot 
1167fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1168fad09c73SVivien Didelot 
1169fad09c73SVivien Didelot 	do {
1170f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &vlan);
1171fad09c73SVivien Didelot 		if (err)
1172fad09c73SVivien Didelot 			goto unlock;
1173fad09c73SVivien Didelot 
1174fad09c73SVivien Didelot 		if (!vlan.valid)
1175fad09c73SVivien Didelot 			break;
1176fad09c73SVivien Didelot 
1177fad09c73SVivien Didelot 		if (vlan.vid > vid_end)
1178fad09c73SVivien Didelot 			break;
1179fad09c73SVivien Didelot 
1180370b4ffbSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1181fad09c73SVivien Didelot 			if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1182fad09c73SVivien Didelot 				continue;
1183fad09c73SVivien Didelot 
118466e2809dSAndrew Lunn 			if (!ds->ports[port].netdev)
118566e2809dSAndrew Lunn 				continue;
118666e2809dSAndrew Lunn 
1187bd00e053SVivien Didelot 			if (vlan.member[i] ==
1188fad09c73SVivien Didelot 			    GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1189fad09c73SVivien Didelot 				continue;
1190fad09c73SVivien Didelot 
1191fae8a25eSVivien Didelot 			if (ds->ports[i].bridge_dev ==
1192fae8a25eSVivien Didelot 			    ds->ports[port].bridge_dev)
1193fad09c73SVivien Didelot 				break; /* same bridge, check next VLAN */
1194fad09c73SVivien Didelot 
1195fae8a25eSVivien Didelot 			if (!ds->ports[i].bridge_dev)
119666e2809dSAndrew Lunn 				continue;
119766e2809dSAndrew Lunn 
1198774439e5SVivien Didelot 			dev_err(ds->dev, "p%d: hw VLAN %d already used by %s\n",
1199774439e5SVivien Didelot 				port, vlan.vid,
1200fae8a25eSVivien Didelot 				netdev_name(ds->ports[i].bridge_dev));
1201fad09c73SVivien Didelot 			err = -EOPNOTSUPP;
1202fad09c73SVivien Didelot 			goto unlock;
1203fad09c73SVivien Didelot 		}
1204fad09c73SVivien Didelot 	} while (vlan.vid < vid_end);
1205fad09c73SVivien Didelot 
1206fad09c73SVivien Didelot unlock:
1207fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1208fad09c73SVivien Didelot 
1209fad09c73SVivien Didelot 	return err;
1210fad09c73SVivien Didelot }
1211fad09c73SVivien Didelot 
1212fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1213fad09c73SVivien Didelot 					 bool vlan_filtering)
1214fad09c73SVivien Didelot {
121504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1216385a0995SVivien Didelot 	u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1217fad09c73SVivien Didelot 		PORT_CONTROL_2_8021Q_DISABLED;
12180e7b9925SAndrew Lunn 	int err;
1219fad09c73SVivien Didelot 
12203cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1221fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1222fad09c73SVivien Didelot 
1223fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1224385a0995SVivien Didelot 	err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
1225fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1226fad09c73SVivien Didelot 
12270e7b9925SAndrew Lunn 	return err;
1228fad09c73SVivien Didelot }
1229fad09c73SVivien Didelot 
1230fad09c73SVivien Didelot static int
1231fad09c73SVivien Didelot mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1232fad09c73SVivien Didelot 			    const struct switchdev_obj_port_vlan *vlan,
1233fad09c73SVivien Didelot 			    struct switchdev_trans *trans)
1234fad09c73SVivien Didelot {
123504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1236fad09c73SVivien Didelot 	int err;
1237fad09c73SVivien Didelot 
12383cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1239fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1240fad09c73SVivien Didelot 
1241fad09c73SVivien Didelot 	/* If the requested port doesn't belong to the same bridge as the VLAN
1242fad09c73SVivien Didelot 	 * members, do not support it (yet) and fallback to software VLAN.
1243fad09c73SVivien Didelot 	 */
1244fad09c73SVivien Didelot 	err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1245fad09c73SVivien Didelot 					   vlan->vid_end);
1246fad09c73SVivien Didelot 	if (err)
1247fad09c73SVivien Didelot 		return err;
1248fad09c73SVivien Didelot 
1249fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
1250fad09c73SVivien Didelot 	 * so skip the prepare phase.
1251fad09c73SVivien Didelot 	 */
1252fad09c73SVivien Didelot 	return 0;
1253fad09c73SVivien Didelot }
1254fad09c73SVivien Didelot 
1255fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
1256c91498e1SVivien Didelot 				    u16 vid, u8 member)
1257fad09c73SVivien Didelot {
1258b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1259fad09c73SVivien Didelot 	int err;
1260fad09c73SVivien Didelot 
1261567aa59aSVivien Didelot 	err = mv88e6xxx_vtu_get(chip, vid, &vlan, true);
1262fad09c73SVivien Didelot 	if (err)
1263fad09c73SVivien Didelot 		return err;
1264fad09c73SVivien Didelot 
1265c91498e1SVivien Didelot 	vlan.member[port] = member;
1266fad09c73SVivien Didelot 
12670ad5daf6SVivien Didelot 	return mv88e6xxx_vtu_loadpurge(chip, &vlan);
1268fad09c73SVivien Didelot }
1269fad09c73SVivien Didelot 
1270fad09c73SVivien Didelot static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1271fad09c73SVivien Didelot 				    const struct switchdev_obj_port_vlan *vlan,
1272fad09c73SVivien Didelot 				    struct switchdev_trans *trans)
1273fad09c73SVivien Didelot {
127404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1275fad09c73SVivien Didelot 	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1276fad09c73SVivien Didelot 	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1277c91498e1SVivien Didelot 	u8 member;
1278fad09c73SVivien Didelot 	u16 vid;
1279fad09c73SVivien Didelot 
12803cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1281fad09c73SVivien Didelot 		return;
1282fad09c73SVivien Didelot 
1283c91498e1SVivien Didelot 	if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
1284c91498e1SVivien Didelot 		member = GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED;
1285c91498e1SVivien Didelot 	else if (untagged)
1286c91498e1SVivien Didelot 		member = GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED;
1287c91498e1SVivien Didelot 	else
1288c91498e1SVivien Didelot 		member = GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1289c91498e1SVivien Didelot 
1290fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1291fad09c73SVivien Didelot 
1292fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
1293c91498e1SVivien Didelot 		if (_mv88e6xxx_port_vlan_add(chip, port, vid, member))
1294774439e5SVivien Didelot 			dev_err(ds->dev, "p%d: failed to add VLAN %d%c\n", port,
1295fad09c73SVivien Didelot 				vid, untagged ? 'u' : 't');
1296fad09c73SVivien Didelot 
129777064f37SVivien Didelot 	if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
1298774439e5SVivien Didelot 		dev_err(ds->dev, "p%d: failed to set PVID %d\n", port,
1299fad09c73SVivien Didelot 			vlan->vid_end);
1300fad09c73SVivien Didelot 
1301fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1302fad09c73SVivien Didelot }
1303fad09c73SVivien Didelot 
1304fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
1305fad09c73SVivien Didelot 				    int port, u16 vid)
1306fad09c73SVivien Didelot {
1307b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1308fad09c73SVivien Didelot 	int i, err;
1309fad09c73SVivien Didelot 
1310567aa59aSVivien Didelot 	err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
1311fad09c73SVivien Didelot 	if (err)
1312fad09c73SVivien Didelot 		return err;
1313fad09c73SVivien Didelot 
1314fad09c73SVivien Didelot 	/* Tell switchdev if this VLAN is handled in software */
1315bd00e053SVivien Didelot 	if (vlan.member[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1316fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1317fad09c73SVivien Didelot 
1318bd00e053SVivien Didelot 	vlan.member[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1319fad09c73SVivien Didelot 
1320fad09c73SVivien Didelot 	/* keep the VLAN unless all ports are excluded */
1321fad09c73SVivien Didelot 	vlan.valid = false;
1322370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1323bd00e053SVivien Didelot 		if (vlan.member[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
1324fad09c73SVivien Didelot 			vlan.valid = true;
1325fad09c73SVivien Didelot 			break;
1326fad09c73SVivien Didelot 		}
1327fad09c73SVivien Didelot 	}
1328fad09c73SVivien Didelot 
13290ad5daf6SVivien Didelot 	err = mv88e6xxx_vtu_loadpurge(chip, &vlan);
1330fad09c73SVivien Didelot 	if (err)
1331fad09c73SVivien Didelot 		return err;
1332fad09c73SVivien Didelot 
1333e606ca36SVivien Didelot 	return mv88e6xxx_g1_atu_remove(chip, vlan.fid, port, false);
1334fad09c73SVivien Didelot }
1335fad09c73SVivien Didelot 
1336fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1337fad09c73SVivien Didelot 				   const struct switchdev_obj_port_vlan *vlan)
1338fad09c73SVivien Didelot {
133904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1340fad09c73SVivien Didelot 	u16 pvid, vid;
1341fad09c73SVivien Didelot 	int err = 0;
1342fad09c73SVivien Didelot 
13433cf3c846SVivien Didelot 	if (!chip->info->max_vid)
1344fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1345fad09c73SVivien Didelot 
1346fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1347fad09c73SVivien Didelot 
134877064f37SVivien Didelot 	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
1349fad09c73SVivien Didelot 	if (err)
1350fad09c73SVivien Didelot 		goto unlock;
1351fad09c73SVivien Didelot 
1352fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
1353fad09c73SVivien Didelot 		err = _mv88e6xxx_port_vlan_del(chip, port, vid);
1354fad09c73SVivien Didelot 		if (err)
1355fad09c73SVivien Didelot 			goto unlock;
1356fad09c73SVivien Didelot 
1357fad09c73SVivien Didelot 		if (vid == pvid) {
135877064f37SVivien Didelot 			err = mv88e6xxx_port_set_pvid(chip, port, 0);
1359fad09c73SVivien Didelot 			if (err)
1360fad09c73SVivien Didelot 				goto unlock;
1361fad09c73SVivien Didelot 		}
1362fad09c73SVivien Didelot 	}
1363fad09c73SVivien Didelot 
1364fad09c73SVivien Didelot unlock:
1365fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1366fad09c73SVivien Didelot 
1367fad09c73SVivien Didelot 	return err;
1368fad09c73SVivien Didelot }
1369fad09c73SVivien Didelot 
137083dabd1fSVivien Didelot static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
1371fad09c73SVivien Didelot 					const unsigned char *addr, u16 vid,
1372fad09c73SVivien Didelot 					u8 state)
1373fad09c73SVivien Didelot {
1374b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
137588472939SVivien Didelot 	struct mv88e6xxx_atu_entry entry;
1376fad09c73SVivien Didelot 	int err;
1377fad09c73SVivien Didelot 
1378fad09c73SVivien Didelot 	/* Null VLAN ID corresponds to the port private database */
1379fad09c73SVivien Didelot 	if (vid == 0)
1380b4e48c50SVivien Didelot 		err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
1381fad09c73SVivien Didelot 	else
1382567aa59aSVivien Didelot 		err = mv88e6xxx_vtu_get(chip, vid, &vlan, false);
1383fad09c73SVivien Didelot 	if (err)
1384fad09c73SVivien Didelot 		return err;
1385fad09c73SVivien Didelot 
1386dabc1a96SVivien Didelot 	entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
1387dabc1a96SVivien Didelot 	ether_addr_copy(entry.mac, addr);
1388dabc1a96SVivien Didelot 	eth_addr_dec(entry.mac);
1389dabc1a96SVivien Didelot 
1390dabc1a96SVivien Didelot 	err = mv88e6xxx_g1_atu_getnext(chip, vlan.fid, &entry);
139188472939SVivien Didelot 	if (err)
139288472939SVivien Didelot 		return err;
139388472939SVivien Didelot 
1394dabc1a96SVivien Didelot 	/* Initialize a fresh ATU entry if it isn't found */
1395dabc1a96SVivien Didelot 	if (entry.state == GLOBAL_ATU_DATA_STATE_UNUSED ||
1396dabc1a96SVivien Didelot 	    !ether_addr_equal(entry.mac, addr)) {
1397dabc1a96SVivien Didelot 		memset(&entry, 0, sizeof(entry));
1398dabc1a96SVivien Didelot 		ether_addr_copy(entry.mac, addr);
1399dabc1a96SVivien Didelot 	}
1400dabc1a96SVivien Didelot 
140188472939SVivien Didelot 	/* Purge the ATU entry only if no port is using it anymore */
140288472939SVivien Didelot 	if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
140301bd96c8SVivien Didelot 		entry.portvec &= ~BIT(port);
140401bd96c8SVivien Didelot 		if (!entry.portvec)
140588472939SVivien Didelot 			entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
140688472939SVivien Didelot 	} else {
140701bd96c8SVivien Didelot 		entry.portvec |= BIT(port);
1408fad09c73SVivien Didelot 		entry.state = state;
1409fad09c73SVivien Didelot 	}
1410fad09c73SVivien Didelot 
14119c13c026SVivien Didelot 	return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry);
1412fad09c73SVivien Didelot }
1413fad09c73SVivien Didelot 
1414fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
1415fad09c73SVivien Didelot 				      const struct switchdev_obj_port_fdb *fdb,
1416fad09c73SVivien Didelot 				      struct switchdev_trans *trans)
1417fad09c73SVivien Didelot {
1418fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
1419fad09c73SVivien Didelot 	 * so skip the prepare phase.
1420fad09c73SVivien Didelot 	 */
1421fad09c73SVivien Didelot 	return 0;
1422fad09c73SVivien Didelot }
1423fad09c73SVivien Didelot 
1424fad09c73SVivien Didelot static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
1425fad09c73SVivien Didelot 				   const struct switchdev_obj_port_fdb *fdb,
1426fad09c73SVivien Didelot 				   struct switchdev_trans *trans)
1427fad09c73SVivien Didelot {
142804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1429fad09c73SVivien Didelot 
1430fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
143183dabd1fSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
143283dabd1fSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_UC_STATIC))
1433774439e5SVivien Didelot 		dev_err(ds->dev, "p%d: failed to load unicast MAC address\n",
1434774439e5SVivien Didelot 			port);
1435fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1436fad09c73SVivien Didelot }
1437fad09c73SVivien Didelot 
1438fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
1439fad09c73SVivien Didelot 				  const struct switchdev_obj_port_fdb *fdb)
1440fad09c73SVivien Didelot {
144104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
144283dabd1fSVivien Didelot 	int err;
1443fad09c73SVivien Didelot 
1444fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
144583dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
1446fad09c73SVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
1447fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1448fad09c73SVivien Didelot 
144983dabd1fSVivien Didelot 	return err;
1450fad09c73SVivien Didelot }
1451fad09c73SVivien Didelot 
145283dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
1453fad09c73SVivien Didelot 				      u16 fid, u16 vid, int port,
145483dabd1fSVivien Didelot 				      struct switchdev_obj *obj,
1455438ff537SVivien Didelot 				      switchdev_obj_dump_cb_t *cb)
1456fad09c73SVivien Didelot {
1457dabc1a96SVivien Didelot 	struct mv88e6xxx_atu_entry addr;
1458fad09c73SVivien Didelot 	int err;
1459fad09c73SVivien Didelot 
1460dabc1a96SVivien Didelot 	addr.state = GLOBAL_ATU_DATA_STATE_UNUSED;
1461dabc1a96SVivien Didelot 	eth_broadcast_addr(addr.mac);
1462fad09c73SVivien Didelot 
1463fad09c73SVivien Didelot 	do {
1464dabc1a96SVivien Didelot 		err = mv88e6xxx_g1_atu_getnext(chip, fid, &addr);
1465fad09c73SVivien Didelot 		if (err)
146683dabd1fSVivien Didelot 			return err;
1467fad09c73SVivien Didelot 
1468fad09c73SVivien Didelot 		if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
1469fad09c73SVivien Didelot 			break;
1470fad09c73SVivien Didelot 
147101bd96c8SVivien Didelot 		if (addr.trunk || (addr.portvec & BIT(port)) == 0)
147283dabd1fSVivien Didelot 			continue;
1473fad09c73SVivien Didelot 
147483dabd1fSVivien Didelot 		if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
147583dabd1fSVivien Didelot 			struct switchdev_obj_port_fdb *fdb;
147683dabd1fSVivien Didelot 
147783dabd1fSVivien Didelot 			if (!is_unicast_ether_addr(addr.mac))
147883dabd1fSVivien Didelot 				continue;
147983dabd1fSVivien Didelot 
148083dabd1fSVivien Didelot 			fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
1481fad09c73SVivien Didelot 			fdb->vid = vid;
1482fad09c73SVivien Didelot 			ether_addr_copy(fdb->addr, addr.mac);
148383dabd1fSVivien Didelot 			if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
148483dabd1fSVivien Didelot 				fdb->ndm_state = NUD_NOARP;
148583dabd1fSVivien Didelot 			else
148683dabd1fSVivien Didelot 				fdb->ndm_state = NUD_REACHABLE;
14877df8fbddSVivien Didelot 		} else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
14887df8fbddSVivien Didelot 			struct switchdev_obj_port_mdb *mdb;
14897df8fbddSVivien Didelot 
14907df8fbddSVivien Didelot 			if (!is_multicast_ether_addr(addr.mac))
14917df8fbddSVivien Didelot 				continue;
14927df8fbddSVivien Didelot 
14937df8fbddSVivien Didelot 			mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
14947df8fbddSVivien Didelot 			mdb->vid = vid;
14957df8fbddSVivien Didelot 			ether_addr_copy(mdb->addr, addr.mac);
149683dabd1fSVivien Didelot 		} else {
149783dabd1fSVivien Didelot 			return -EOPNOTSUPP;
1498fad09c73SVivien Didelot 		}
149983dabd1fSVivien Didelot 
150083dabd1fSVivien Didelot 		err = cb(obj);
150183dabd1fSVivien Didelot 		if (err)
150283dabd1fSVivien Didelot 			return err;
1503fad09c73SVivien Didelot 	} while (!is_broadcast_ether_addr(addr.mac));
1504fad09c73SVivien Didelot 
1505fad09c73SVivien Didelot 	return err;
1506fad09c73SVivien Didelot }
1507fad09c73SVivien Didelot 
150883dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
150983dabd1fSVivien Didelot 				  struct switchdev_obj *obj,
1510438ff537SVivien Didelot 				  switchdev_obj_dump_cb_t *cb)
151183dabd1fSVivien Didelot {
1512b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
15133cf3c846SVivien Didelot 		.vid = chip->info->max_vid,
151483dabd1fSVivien Didelot 	};
151583dabd1fSVivien Didelot 	u16 fid;
151683dabd1fSVivien Didelot 	int err;
151783dabd1fSVivien Didelot 
151883dabd1fSVivien Didelot 	/* Dump port's default Filtering Information Database (VLAN ID 0) */
1519b4e48c50SVivien Didelot 	err = mv88e6xxx_port_get_fid(chip, port, &fid);
152083dabd1fSVivien Didelot 	if (err)
152183dabd1fSVivien Didelot 		return err;
152283dabd1fSVivien Didelot 
152383dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
152483dabd1fSVivien Didelot 	if (err)
152583dabd1fSVivien Didelot 		return err;
152683dabd1fSVivien Didelot 
152783dabd1fSVivien Didelot 	/* Dump VLANs' Filtering Information Databases */
152883dabd1fSVivien Didelot 	do {
1529f1394b78SVivien Didelot 		err = mv88e6xxx_vtu_getnext(chip, &vlan);
153083dabd1fSVivien Didelot 		if (err)
153183dabd1fSVivien Didelot 			return err;
153283dabd1fSVivien Didelot 
153383dabd1fSVivien Didelot 		if (!vlan.valid)
153483dabd1fSVivien Didelot 			break;
153583dabd1fSVivien Didelot 
153683dabd1fSVivien Didelot 		err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
153783dabd1fSVivien Didelot 						 obj, cb);
153883dabd1fSVivien Didelot 		if (err)
153983dabd1fSVivien Didelot 			return err;
15403cf3c846SVivien Didelot 	} while (vlan.vid < chip->info->max_vid);
154183dabd1fSVivien Didelot 
154283dabd1fSVivien Didelot 	return err;
154383dabd1fSVivien Didelot }
154483dabd1fSVivien Didelot 
1545fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
1546fad09c73SVivien Didelot 				   struct switchdev_obj_port_fdb *fdb,
1547438ff537SVivien Didelot 				   switchdev_obj_dump_cb_t *cb)
1548fad09c73SVivien Didelot {
154904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1550fad09c73SVivien Didelot 	int err;
1551fad09c73SVivien Didelot 
1552fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
155383dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
1554fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1555fad09c73SVivien Didelot 
1556fad09c73SVivien Didelot 	return err;
1557fad09c73SVivien Didelot }
1558fad09c73SVivien Didelot 
1559240ea3efSVivien Didelot static int mv88e6xxx_bridge_map(struct mv88e6xxx_chip *chip,
1560240ea3efSVivien Didelot 				struct net_device *br)
1561240ea3efSVivien Didelot {
1562e96a6e02SVivien Didelot 	struct dsa_switch *ds;
1563240ea3efSVivien Didelot 	int port;
1564e96a6e02SVivien Didelot 	int dev;
1565240ea3efSVivien Didelot 	int err;
1566240ea3efSVivien Didelot 
1567240ea3efSVivien Didelot 	/* Remap the Port VLAN of each local bridge group member */
1568240ea3efSVivien Didelot 	for (port = 0; port < mv88e6xxx_num_ports(chip); ++port) {
1569240ea3efSVivien Didelot 		if (chip->ds->ports[port].bridge_dev == br) {
1570240ea3efSVivien Didelot 			err = mv88e6xxx_port_vlan_map(chip, port);
1571240ea3efSVivien Didelot 			if (err)
1572240ea3efSVivien Didelot 				return err;
1573240ea3efSVivien Didelot 		}
1574240ea3efSVivien Didelot 	}
1575240ea3efSVivien Didelot 
1576e96a6e02SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
1577e96a6e02SVivien Didelot 		return 0;
1578e96a6e02SVivien Didelot 
1579e96a6e02SVivien Didelot 	/* Remap the Port VLAN of each cross-chip bridge group member */
1580e96a6e02SVivien Didelot 	for (dev = 0; dev < DSA_MAX_SWITCHES; ++dev) {
1581e96a6e02SVivien Didelot 		ds = chip->ds->dst->ds[dev];
1582e96a6e02SVivien Didelot 		if (!ds)
1583e96a6e02SVivien Didelot 			break;
1584e96a6e02SVivien Didelot 
1585e96a6e02SVivien Didelot 		for (port = 0; port < ds->num_ports; ++port) {
1586e96a6e02SVivien Didelot 			if (ds->ports[port].bridge_dev == br) {
1587e96a6e02SVivien Didelot 				err = mv88e6xxx_pvt_map(chip, dev, port);
1588e96a6e02SVivien Didelot 				if (err)
1589e96a6e02SVivien Didelot 					return err;
1590e96a6e02SVivien Didelot 			}
1591e96a6e02SVivien Didelot 		}
1592e96a6e02SVivien Didelot 	}
1593e96a6e02SVivien Didelot 
1594240ea3efSVivien Didelot 	return 0;
1595240ea3efSVivien Didelot }
1596240ea3efSVivien Didelot 
1597fad09c73SVivien Didelot static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
1598fae8a25eSVivien Didelot 				      struct net_device *br)
1599fad09c73SVivien Didelot {
160004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1601240ea3efSVivien Didelot 	int err;
1602fad09c73SVivien Didelot 
1603fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1604240ea3efSVivien Didelot 	err = mv88e6xxx_bridge_map(chip, br);
1605fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1606fad09c73SVivien Didelot 
1607fad09c73SVivien Didelot 	return err;
1608fad09c73SVivien Didelot }
1609fad09c73SVivien Didelot 
1610f123f2fbSVivien Didelot static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port,
1611f123f2fbSVivien Didelot 					struct net_device *br)
1612fad09c73SVivien Didelot {
161304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1614fad09c73SVivien Didelot 
1615fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1616240ea3efSVivien Didelot 	if (mv88e6xxx_bridge_map(chip, br) ||
1617240ea3efSVivien Didelot 	    mv88e6xxx_port_vlan_map(chip, port))
1618240ea3efSVivien Didelot 		dev_err(ds->dev, "failed to remap in-chip Port VLAN\n");
1619fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1620fad09c73SVivien Didelot }
1621fad09c73SVivien Didelot 
1622aec5ac88SVivien Didelot static int mv88e6xxx_crosschip_bridge_join(struct dsa_switch *ds, int dev,
1623aec5ac88SVivien Didelot 					   int port, struct net_device *br)
1624aec5ac88SVivien Didelot {
1625aec5ac88SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1626aec5ac88SVivien Didelot 	int err;
1627aec5ac88SVivien Didelot 
1628aec5ac88SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
1629aec5ac88SVivien Didelot 		return 0;
1630aec5ac88SVivien Didelot 
1631aec5ac88SVivien Didelot 	mutex_lock(&chip->reg_lock);
1632aec5ac88SVivien Didelot 	err = mv88e6xxx_pvt_map(chip, dev, port);
1633aec5ac88SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1634aec5ac88SVivien Didelot 
1635aec5ac88SVivien Didelot 	return err;
1636aec5ac88SVivien Didelot }
1637aec5ac88SVivien Didelot 
1638aec5ac88SVivien Didelot static void mv88e6xxx_crosschip_bridge_leave(struct dsa_switch *ds, int dev,
1639aec5ac88SVivien Didelot 					     int port, struct net_device *br)
1640aec5ac88SVivien Didelot {
1641aec5ac88SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1642aec5ac88SVivien Didelot 
1643aec5ac88SVivien Didelot 	if (!mv88e6xxx_has_pvt(chip))
1644aec5ac88SVivien Didelot 		return;
1645aec5ac88SVivien Didelot 
1646aec5ac88SVivien Didelot 	mutex_lock(&chip->reg_lock);
1647aec5ac88SVivien Didelot 	if (mv88e6xxx_pvt_map(chip, dev, port))
1648aec5ac88SVivien Didelot 		dev_err(ds->dev, "failed to remap cross-chip Port VLAN\n");
1649aec5ac88SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1650aec5ac88SVivien Didelot }
1651aec5ac88SVivien Didelot 
165217e708baSVivien Didelot static int mv88e6xxx_software_reset(struct mv88e6xxx_chip *chip)
165317e708baSVivien Didelot {
165417e708baSVivien Didelot 	if (chip->info->ops->reset)
165517e708baSVivien Didelot 		return chip->info->ops->reset(chip);
165617e708baSVivien Didelot 
165717e708baSVivien Didelot 	return 0;
165817e708baSVivien Didelot }
165917e708baSVivien Didelot 
1660309eca6dSVivien Didelot static void mv88e6xxx_hardware_reset(struct mv88e6xxx_chip *chip)
1661309eca6dSVivien Didelot {
1662309eca6dSVivien Didelot 	struct gpio_desc *gpiod = chip->reset;
1663309eca6dSVivien Didelot 
1664309eca6dSVivien Didelot 	/* If there is a GPIO connected to the reset pin, toggle it */
1665309eca6dSVivien Didelot 	if (gpiod) {
1666309eca6dSVivien Didelot 		gpiod_set_value_cansleep(gpiod, 1);
1667309eca6dSVivien Didelot 		usleep_range(10000, 20000);
1668309eca6dSVivien Didelot 		gpiod_set_value_cansleep(gpiod, 0);
1669309eca6dSVivien Didelot 		usleep_range(10000, 20000);
1670309eca6dSVivien Didelot 	}
1671309eca6dSVivien Didelot }
1672309eca6dSVivien Didelot 
16734ac4b5a6SVivien Didelot static int mv88e6xxx_disable_ports(struct mv88e6xxx_chip *chip)
16744ac4b5a6SVivien Didelot {
16754ac4b5a6SVivien Didelot 	int i, err;
16764ac4b5a6SVivien Didelot 
16774ac4b5a6SVivien Didelot 	/* Set all ports to the Disabled state */
16784ac4b5a6SVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
1679f894c29cSVivien Didelot 		err = mv88e6xxx_port_set_state(chip, i, BR_STATE_DISABLED);
16804ac4b5a6SVivien Didelot 		if (err)
16814ac4b5a6SVivien Didelot 			return err;
16824ac4b5a6SVivien Didelot 	}
16834ac4b5a6SVivien Didelot 
16844ac4b5a6SVivien Didelot 	/* Wait for transmit queues to drain,
16854ac4b5a6SVivien Didelot 	 * i.e. 2ms for a maximum frame to be transmitted at 10 Mbps.
16864ac4b5a6SVivien Didelot 	 */
16874ac4b5a6SVivien Didelot 	usleep_range(2000, 4000);
16884ac4b5a6SVivien Didelot 
16894ac4b5a6SVivien Didelot 	return 0;
16904ac4b5a6SVivien Didelot }
16914ac4b5a6SVivien Didelot 
1692fad09c73SVivien Didelot static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
1693fad09c73SVivien Didelot {
1694a935c052SVivien Didelot 	int err;
1695fad09c73SVivien Didelot 
16964ac4b5a6SVivien Didelot 	err = mv88e6xxx_disable_ports(chip);
16970e7b9925SAndrew Lunn 	if (err)
16980e7b9925SAndrew Lunn 		return err;
1699fad09c73SVivien Didelot 
1700309eca6dSVivien Didelot 	mv88e6xxx_hardware_reset(chip);
1701fad09c73SVivien Didelot 
170217e708baSVivien Didelot 	return mv88e6xxx_software_reset(chip);
1703fad09c73SVivien Didelot }
1704fad09c73SVivien Didelot 
17054314557cSVivien Didelot static int mv88e6xxx_set_port_mode(struct mv88e6xxx_chip *chip, int port,
170631bef4e9SVivien Didelot 				   enum mv88e6xxx_frame_mode frame,
170731bef4e9SVivien Didelot 				   enum mv88e6xxx_egress_mode egress, u16 etype)
170856995cbcSAndrew Lunn {
170956995cbcSAndrew Lunn 	int err;
171056995cbcSAndrew Lunn 
17114314557cSVivien Didelot 	if (!chip->info->ops->port_set_frame_mode)
17124314557cSVivien Didelot 		return -EOPNOTSUPP;
17134314557cSVivien Didelot 
17144314557cSVivien Didelot 	err = mv88e6xxx_port_set_egress_mode(chip, port, egress);
171556995cbcSAndrew Lunn 	if (err)
171656995cbcSAndrew Lunn 		return err;
171756995cbcSAndrew Lunn 
17184314557cSVivien Didelot 	err = chip->info->ops->port_set_frame_mode(chip, port, frame);
17194314557cSVivien Didelot 	if (err)
17204314557cSVivien Didelot 		return err;
17214314557cSVivien Didelot 
17224314557cSVivien Didelot 	if (chip->info->ops->port_set_ether_type)
17234314557cSVivien Didelot 		return chip->info->ops->port_set_ether_type(chip, port, etype);
17244314557cSVivien Didelot 
17254314557cSVivien Didelot 	return 0;
17264314557cSVivien Didelot }
17274314557cSVivien Didelot 
17284314557cSVivien Didelot static int mv88e6xxx_set_port_mode_normal(struct mv88e6xxx_chip *chip, int port)
17294314557cSVivien Didelot {
17304314557cSVivien Didelot 	return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_NORMAL,
173131bef4e9SVivien Didelot 				       MV88E6XXX_EGRESS_MODE_UNMODIFIED,
17324314557cSVivien Didelot 				       PORT_ETH_TYPE_DEFAULT);
17334314557cSVivien Didelot }
17344314557cSVivien Didelot 
17354314557cSVivien Didelot static int mv88e6xxx_set_port_mode_dsa(struct mv88e6xxx_chip *chip, int port)
17364314557cSVivien Didelot {
17374314557cSVivien Didelot 	return mv88e6xxx_set_port_mode(chip, port, MV88E6XXX_FRAME_MODE_DSA,
173831bef4e9SVivien Didelot 				       MV88E6XXX_EGRESS_MODE_UNMODIFIED,
17394314557cSVivien Didelot 				       PORT_ETH_TYPE_DEFAULT);
17404314557cSVivien Didelot }
17414314557cSVivien Didelot 
17424314557cSVivien Didelot static int mv88e6xxx_set_port_mode_edsa(struct mv88e6xxx_chip *chip, int port)
17434314557cSVivien Didelot {
17444314557cSVivien Didelot 	return mv88e6xxx_set_port_mode(chip, port,
17454314557cSVivien Didelot 				       MV88E6XXX_FRAME_MODE_ETHERTYPE,
174631bef4e9SVivien Didelot 				       MV88E6XXX_EGRESS_MODE_ETHERTYPE,
174731bef4e9SVivien Didelot 				       ETH_P_EDSA);
17484314557cSVivien Didelot }
17494314557cSVivien Didelot 
17504314557cSVivien Didelot static int mv88e6xxx_setup_port_mode(struct mv88e6xxx_chip *chip, int port)
17514314557cSVivien Didelot {
17524314557cSVivien Didelot 	if (dsa_is_dsa_port(chip->ds, port))
17534314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_dsa(chip, port);
17544314557cSVivien Didelot 
17554314557cSVivien Didelot 	if (dsa_is_normal_port(chip->ds, port))
17564314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_normal(chip, port);
17574314557cSVivien Didelot 
17584314557cSVivien Didelot 	/* Setup CPU port mode depending on its supported tag format */
17594314557cSVivien Didelot 	if (chip->info->tag_protocol == DSA_TAG_PROTO_DSA)
17604314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_dsa(chip, port);
17614314557cSVivien Didelot 
17624314557cSVivien Didelot 	if (chip->info->tag_protocol == DSA_TAG_PROTO_EDSA)
17634314557cSVivien Didelot 		return mv88e6xxx_set_port_mode_edsa(chip, port);
17644314557cSVivien Didelot 
17654314557cSVivien Didelot 	return -EINVAL;
17664314557cSVivien Didelot }
17674314557cSVivien Didelot 
1768ea698f4fSVivien Didelot static int mv88e6xxx_setup_message_port(struct mv88e6xxx_chip *chip, int port)
1769ea698f4fSVivien Didelot {
1770ea698f4fSVivien Didelot 	bool message = dsa_is_dsa_port(chip->ds, port);
1771ea698f4fSVivien Didelot 
1772ea698f4fSVivien Didelot 	return mv88e6xxx_port_set_message_port(chip, port, message);
1773ea698f4fSVivien Didelot }
1774ea698f4fSVivien Didelot 
1775601aeed3SVivien Didelot static int mv88e6xxx_setup_egress_floods(struct mv88e6xxx_chip *chip, int port)
1776601aeed3SVivien Didelot {
1777601aeed3SVivien Didelot 	bool flood = port == dsa_upstream_port(chip->ds);
1778601aeed3SVivien Didelot 
1779601aeed3SVivien Didelot 	/* Upstream ports flood frames with unknown unicast or multicast DA */
1780601aeed3SVivien Didelot 	if (chip->info->ops->port_set_egress_floods)
1781601aeed3SVivien Didelot 		return chip->info->ops->port_set_egress_floods(chip, port,
1782601aeed3SVivien Didelot 							       flood, flood);
1783601aeed3SVivien Didelot 
1784601aeed3SVivien Didelot 	return 0;
1785601aeed3SVivien Didelot }
1786601aeed3SVivien Didelot 
17876d91782fSAndrew Lunn static int mv88e6xxx_serdes_power(struct mv88e6xxx_chip *chip, int port,
17886d91782fSAndrew Lunn 				  bool on)
17896d91782fSAndrew Lunn {
1790523a8904SVivien Didelot 	if (chip->info->ops->serdes_power)
1791523a8904SVivien Didelot 		return chip->info->ops->serdes_power(chip, port, on);
17926d91782fSAndrew Lunn 
1793523a8904SVivien Didelot 	return 0;
17946d91782fSAndrew Lunn }
17956d91782fSAndrew Lunn 
1796fad09c73SVivien Didelot static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
1797fad09c73SVivien Didelot {
1798fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
17990e7b9925SAndrew Lunn 	int err;
1800fad09c73SVivien Didelot 	u16 reg;
1801fad09c73SVivien Didelot 
1802d78343d2SVivien Didelot 	/* MAC Forcing register: don't force link, speed, duplex or flow control
1803d78343d2SVivien Didelot 	 * state to any particular values on physical ports, but force the CPU
1804d78343d2SVivien Didelot 	 * port and all DSA ports to their maximum bandwidth and full duplex.
1805fad09c73SVivien Didelot 	 */
1806d78343d2SVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
1807d78343d2SVivien Didelot 		err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP,
1808d78343d2SVivien Didelot 					       SPEED_MAX, DUPLEX_FULL,
1809d78343d2SVivien Didelot 					       PHY_INTERFACE_MODE_NA);
1810fad09c73SVivien Didelot 	else
1811d78343d2SVivien Didelot 		err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED,
1812d78343d2SVivien Didelot 					       SPEED_UNFORCED, DUPLEX_UNFORCED,
1813d78343d2SVivien Didelot 					       PHY_INTERFACE_MODE_NA);
18140e7b9925SAndrew Lunn 	if (err)
18150e7b9925SAndrew Lunn 		return err;
1816fad09c73SVivien Didelot 
1817fad09c73SVivien Didelot 	/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
1818fad09c73SVivien Didelot 	 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
1819fad09c73SVivien Didelot 	 * tunneling, determine priority by looking at 802.1p and IP
1820fad09c73SVivien Didelot 	 * priority fields (IP prio has precedence), and set STP state
1821fad09c73SVivien Didelot 	 * to Forwarding.
1822fad09c73SVivien Didelot 	 *
1823fad09c73SVivien Didelot 	 * If this is the CPU link, use DSA or EDSA tagging depending
1824fad09c73SVivien Didelot 	 * on which tagging mode was configured.
1825fad09c73SVivien Didelot 	 *
1826fad09c73SVivien Didelot 	 * If this is a link to another switch, use DSA tagging mode.
1827fad09c73SVivien Didelot 	 *
1828fad09c73SVivien Didelot 	 * If this is the upstream port for this switch, enable
1829fad09c73SVivien Didelot 	 * forwarding of unknown unicasts and multicasts.
1830fad09c73SVivien Didelot 	 */
1831a89b433bSVivien Didelot 	reg = MV88E6XXX_PORT_CTL0_IGMP_MLD_SNOOP |
1832a89b433bSVivien Didelot 		MV88E6185_PORT_CTL0_USE_TAG | MV88E6185_PORT_CTL0_USE_IP |
1833a89b433bSVivien Didelot 		MV88E6XXX_PORT_CTL0_STATE_FORWARDING;
1834a89b433bSVivien Didelot 	err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_CTL0, reg);
18350e7b9925SAndrew Lunn 	if (err)
18360e7b9925SAndrew Lunn 		return err;
183756995cbcSAndrew Lunn 
1838601aeed3SVivien Didelot 	err = mv88e6xxx_setup_port_mode(chip, port);
183956995cbcSAndrew Lunn 	if (err)
184056995cbcSAndrew Lunn 		return err;
1841fad09c73SVivien Didelot 
1842601aeed3SVivien Didelot 	err = mv88e6xxx_setup_egress_floods(chip, port);
18434314557cSVivien Didelot 	if (err)
18444314557cSVivien Didelot 		return err;
18454314557cSVivien Didelot 
184604aca993SAndrew Lunn 	/* Enable the SERDES interface for DSA and CPU ports. Normal
184704aca993SAndrew Lunn 	 * ports SERDES are enabled when the port is enabled, thus
184804aca993SAndrew Lunn 	 * saving a bit of power.
1849fad09c73SVivien Didelot 	 */
185004aca993SAndrew Lunn 	if ((dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) {
18516d91782fSAndrew Lunn 		err = mv88e6xxx_serdes_power(chip, port, true);
18520e7b9925SAndrew Lunn 		if (err)
18530e7b9925SAndrew Lunn 			return err;
185404aca993SAndrew Lunn 	}
1855fad09c73SVivien Didelot 
1856fad09c73SVivien Didelot 	/* Port Control 2: don't force a good FCS, set the maximum frame size to
1857fad09c73SVivien Didelot 	 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
1858fad09c73SVivien Didelot 	 * untagged frames on this port, do a destination address lookup on all
1859fad09c73SVivien Didelot 	 * received packets as usual, disable ARP mirroring and don't send a
1860fad09c73SVivien Didelot 	 * copy of all transmitted/received frames on this port to the CPU.
1861fad09c73SVivien Didelot 	 */
1862a23b2961SAndrew Lunn 	err = mv88e6xxx_port_set_map_da(chip, port);
1863a23b2961SAndrew Lunn 	if (err)
1864a23b2961SAndrew Lunn 		return err;
1865a23b2961SAndrew Lunn 
1866fad09c73SVivien Didelot 	reg = 0;
1867a23b2961SAndrew Lunn 	if (chip->info->ops->port_set_upstream_port) {
1868a23b2961SAndrew Lunn 		err = chip->info->ops->port_set_upstream_port(
1869a23b2961SAndrew Lunn 			chip, port, dsa_upstream_port(ds));
18700e7b9925SAndrew Lunn 		if (err)
18710e7b9925SAndrew Lunn 			return err;
1872fad09c73SVivien Didelot 	}
1873fad09c73SVivien Didelot 
1874a23b2961SAndrew Lunn 	err = mv88e6xxx_port_set_8021q_mode(chip, port,
1875a23b2961SAndrew Lunn 					    PORT_CONTROL_2_8021Q_DISABLED);
1876a23b2961SAndrew Lunn 	if (err)
1877a23b2961SAndrew Lunn 		return err;
1878a23b2961SAndrew Lunn 
1879cd782656SVivien Didelot 	if (chip->info->ops->port_set_jumbo_size) {
1880cd782656SVivien Didelot 		err = chip->info->ops->port_set_jumbo_size(chip, port, 10240);
18815f436666SAndrew Lunn 		if (err)
18825f436666SAndrew Lunn 			return err;
18835f436666SAndrew Lunn 	}
18845f436666SAndrew Lunn 
1885fad09c73SVivien Didelot 	/* Port Association Vector: when learning source addresses
1886fad09c73SVivien Didelot 	 * of packets, add the address to the address database using
1887fad09c73SVivien Didelot 	 * a port bitmap that has only the bit for this port set and
1888fad09c73SVivien Didelot 	 * the other bits clear.
1889fad09c73SVivien Didelot 	 */
1890fad09c73SVivien Didelot 	reg = 1 << port;
1891fad09c73SVivien Didelot 	/* Disable learning for CPU port */
1892fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port))
1893fad09c73SVivien Didelot 		reg = 0;
1894fad09c73SVivien Didelot 
18950e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
18960e7b9925SAndrew Lunn 	if (err)
18970e7b9925SAndrew Lunn 		return err;
1898fad09c73SVivien Didelot 
1899fad09c73SVivien Didelot 	/* Egress rate control 2: disable egress rate control. */
19000e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
19010e7b9925SAndrew Lunn 	if (err)
19020e7b9925SAndrew Lunn 		return err;
1903fad09c73SVivien Didelot 
19040898432cSVivien Didelot 	if (chip->info->ops->port_pause_limit) {
19050898432cSVivien Didelot 		err = chip->info->ops->port_pause_limit(chip, port, 0, 0);
1906b35d322aSAndrew Lunn 		if (err)
1907b35d322aSAndrew Lunn 			return err;
1908b35d322aSAndrew Lunn 	}
1909b35d322aSAndrew Lunn 
1910c8c94891SVivien Didelot 	if (chip->info->ops->port_disable_learn_limit) {
1911c8c94891SVivien Didelot 		err = chip->info->ops->port_disable_learn_limit(chip, port);
1912c8c94891SVivien Didelot 		if (err)
1913c8c94891SVivien Didelot 			return err;
1914c8c94891SVivien Didelot 	}
1915c8c94891SVivien Didelot 
19169dbfb4e1SVivien Didelot 	if (chip->info->ops->port_disable_pri_override) {
19179dbfb4e1SVivien Didelot 		err = chip->info->ops->port_disable_pri_override(chip, port);
19180e7b9925SAndrew Lunn 		if (err)
19190e7b9925SAndrew Lunn 			return err;
1920ef0a7318SAndrew Lunn 	}
19212bbb33beSAndrew Lunn 
1922ef0a7318SAndrew Lunn 	if (chip->info->ops->port_tag_remap) {
1923ef0a7318SAndrew Lunn 		err = chip->info->ops->port_tag_remap(chip, port);
19240e7b9925SAndrew Lunn 		if (err)
19250e7b9925SAndrew Lunn 			return err;
1926fad09c73SVivien Didelot 	}
1927fad09c73SVivien Didelot 
1928ef70b111SAndrew Lunn 	if (chip->info->ops->port_egress_rate_limiting) {
1929ef70b111SAndrew Lunn 		err = chip->info->ops->port_egress_rate_limiting(chip, port);
19300e7b9925SAndrew Lunn 		if (err)
19310e7b9925SAndrew Lunn 			return err;
1932fad09c73SVivien Didelot 	}
1933fad09c73SVivien Didelot 
1934ea698f4fSVivien Didelot 	err = mv88e6xxx_setup_message_port(chip, port);
19350e7b9925SAndrew Lunn 	if (err)
19360e7b9925SAndrew Lunn 		return err;
1937fad09c73SVivien Didelot 
1938fad09c73SVivien Didelot 	/* Port based VLAN map: give each port the same default address
1939fad09c73SVivien Didelot 	 * database, and allow bidirectional communication between the
1940fad09c73SVivien Didelot 	 * CPU and DSA port(s), and the other ports.
1941fad09c73SVivien Didelot 	 */
1942b4e48c50SVivien Didelot 	err = mv88e6xxx_port_set_fid(chip, port, 0);
19430e7b9925SAndrew Lunn 	if (err)
19440e7b9925SAndrew Lunn 		return err;
1945fad09c73SVivien Didelot 
1946240ea3efSVivien Didelot 	err = mv88e6xxx_port_vlan_map(chip, port);
19470e7b9925SAndrew Lunn 	if (err)
19480e7b9925SAndrew Lunn 		return err;
1949fad09c73SVivien Didelot 
1950fad09c73SVivien Didelot 	/* Default VLAN ID and priority: don't set a default VLAN
1951fad09c73SVivien Didelot 	 * ID, and set the default packet priority to zero.
1952fad09c73SVivien Didelot 	 */
1953b7929fb3SVivien Didelot 	return mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_DEFAULT_VLAN, 0);
1954fad09c73SVivien Didelot }
1955fad09c73SVivien Didelot 
195604aca993SAndrew Lunn static int mv88e6xxx_port_enable(struct dsa_switch *ds, int port,
195704aca993SAndrew Lunn 				 struct phy_device *phydev)
195804aca993SAndrew Lunn {
195904aca993SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
1960523a8904SVivien Didelot 	int err;
196104aca993SAndrew Lunn 
196204aca993SAndrew Lunn 	mutex_lock(&chip->reg_lock);
1963523a8904SVivien Didelot 	err = mv88e6xxx_serdes_power(chip, port, true);
196404aca993SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
196504aca993SAndrew Lunn 
196604aca993SAndrew Lunn 	return err;
196704aca993SAndrew Lunn }
196804aca993SAndrew Lunn 
196904aca993SAndrew Lunn static void mv88e6xxx_port_disable(struct dsa_switch *ds, int port,
197004aca993SAndrew Lunn 				   struct phy_device *phydev)
197104aca993SAndrew Lunn {
197204aca993SAndrew Lunn 	struct mv88e6xxx_chip *chip = ds->priv;
197304aca993SAndrew Lunn 
197404aca993SAndrew Lunn 	mutex_lock(&chip->reg_lock);
1975523a8904SVivien Didelot 	if (mv88e6xxx_serdes_power(chip, port, false))
1976523a8904SVivien Didelot 		dev_err(chip->dev, "failed to power off SERDES\n");
197704aca993SAndrew Lunn 	mutex_unlock(&chip->reg_lock);
197804aca993SAndrew Lunn }
197904aca993SAndrew Lunn 
1980aa0938c6SWei Yongjun static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
19813b4caa1bSVivien Didelot {
19823b4caa1bSVivien Didelot 	int err;
19833b4caa1bSVivien Didelot 
1984a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
19853b4caa1bSVivien Didelot 	if (err)
19863b4caa1bSVivien Didelot 		return err;
19873b4caa1bSVivien Didelot 
1988a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
19893b4caa1bSVivien Didelot 	if (err)
19903b4caa1bSVivien Didelot 		return err;
19913b4caa1bSVivien Didelot 
1992a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
1993a935c052SVivien Didelot 	if (err)
1994a935c052SVivien Didelot 		return err;
1995a935c052SVivien Didelot 
1996a935c052SVivien Didelot 	return 0;
19973b4caa1bSVivien Didelot }
19983b4caa1bSVivien Didelot 
19992cfcd964SVivien Didelot static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
20002cfcd964SVivien Didelot 				     unsigned int ageing_time)
20012cfcd964SVivien Didelot {
200204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
20032cfcd964SVivien Didelot 	int err;
20042cfcd964SVivien Didelot 
20052cfcd964SVivien Didelot 	mutex_lock(&chip->reg_lock);
2006720c6343SVivien Didelot 	err = mv88e6xxx_g1_atu_set_age_time(chip, ageing_time);
20072cfcd964SVivien Didelot 	mutex_unlock(&chip->reg_lock);
20082cfcd964SVivien Didelot 
20092cfcd964SVivien Didelot 	return err;
20102cfcd964SVivien Didelot }
20112cfcd964SVivien Didelot 
20129729934cSVivien Didelot static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
2013fad09c73SVivien Didelot {
2014fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
2015fad09c73SVivien Didelot 	u32 upstream_port = dsa_upstream_port(ds);
2016fad09c73SVivien Didelot 	int err;
2017fad09c73SVivien Didelot 
2018fa8d1179SVivien Didelot 	if (chip->info->ops->set_cpu_port) {
2019fa8d1179SVivien Didelot 		err = chip->info->ops->set_cpu_port(chip, upstream_port);
2020fad09c73SVivien Didelot 		if (err)
2021fad09c73SVivien Didelot 			return err;
202233641994SAndrew Lunn 	}
202333641994SAndrew Lunn 
2024fa8d1179SVivien Didelot 	if (chip->info->ops->set_egress_port) {
2025fa8d1179SVivien Didelot 		err = chip->info->ops->set_egress_port(chip, upstream_port);
202633641994SAndrew Lunn 		if (err)
202733641994SAndrew Lunn 			return err;
202833641994SAndrew Lunn 	}
2029fad09c73SVivien Didelot 
2030fad09c73SVivien Didelot 	/* Disable remote management, and set the switch's DSA device number. */
2031a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
2032fad09c73SVivien Didelot 				 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2033fad09c73SVivien Didelot 				 (ds->index & 0x1f));
2034fad09c73SVivien Didelot 	if (err)
2035fad09c73SVivien Didelot 		return err;
2036fad09c73SVivien Didelot 
2037fad09c73SVivien Didelot 	/* Configure the IP ToS mapping registers. */
2038a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
2039fad09c73SVivien Didelot 	if (err)
2040fad09c73SVivien Didelot 		return err;
2041a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
2042fad09c73SVivien Didelot 	if (err)
2043fad09c73SVivien Didelot 		return err;
2044a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
2045fad09c73SVivien Didelot 	if (err)
2046fad09c73SVivien Didelot 		return err;
2047a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
2048fad09c73SVivien Didelot 	if (err)
2049fad09c73SVivien Didelot 		return err;
2050a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
2051fad09c73SVivien Didelot 	if (err)
2052fad09c73SVivien Didelot 		return err;
2053a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
2054fad09c73SVivien Didelot 	if (err)
2055fad09c73SVivien Didelot 		return err;
2056a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
2057fad09c73SVivien Didelot 	if (err)
2058fad09c73SVivien Didelot 		return err;
2059a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
2060fad09c73SVivien Didelot 	if (err)
2061fad09c73SVivien Didelot 		return err;
2062fad09c73SVivien Didelot 
2063fad09c73SVivien Didelot 	/* Configure the IEEE 802.1p priority mapping register. */
2064a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
2065fad09c73SVivien Didelot 	if (err)
2066fad09c73SVivien Didelot 		return err;
2067fad09c73SVivien Didelot 
2068de227387SAndrew Lunn 	/* Initialize the statistics unit */
2069de227387SAndrew Lunn 	err = mv88e6xxx_stats_set_histogram(chip);
2070de227387SAndrew Lunn 	if (err)
2071de227387SAndrew Lunn 		return err;
2072de227387SAndrew Lunn 
20739729934cSVivien Didelot 	/* Clear the statistics counters for all ports */
2074a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
20759729934cSVivien Didelot 				 GLOBAL_STATS_OP_FLUSH_ALL);
20769729934cSVivien Didelot 	if (err)
20779729934cSVivien Didelot 		return err;
20789729934cSVivien Didelot 
20799729934cSVivien Didelot 	/* Wait for the flush to complete. */
20807f9ef3afSAndrew Lunn 	err = mv88e6xxx_g1_stats_wait(chip);
20819729934cSVivien Didelot 	if (err)
20829729934cSVivien Didelot 		return err;
20839729934cSVivien Didelot 
20849729934cSVivien Didelot 	return 0;
20859729934cSVivien Didelot }
20869729934cSVivien Didelot 
2087fad09c73SVivien Didelot static int mv88e6xxx_setup(struct dsa_switch *ds)
2088fad09c73SVivien Didelot {
208904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2090fad09c73SVivien Didelot 	int err;
2091fad09c73SVivien Didelot 	int i;
2092fad09c73SVivien Didelot 
2093fad09c73SVivien Didelot 	chip->ds = ds;
2094a3c53be5SAndrew Lunn 	ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip);
2095fad09c73SVivien Didelot 
2096fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2097fad09c73SVivien Didelot 
20989729934cSVivien Didelot 	/* Setup Switch Port Registers */
2099370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
21009729934cSVivien Didelot 		err = mv88e6xxx_setup_port(chip, i);
21019729934cSVivien Didelot 		if (err)
21029729934cSVivien Didelot 			goto unlock;
21039729934cSVivien Didelot 	}
21049729934cSVivien Didelot 
21059729934cSVivien Didelot 	/* Setup Switch Global 1 Registers */
21069729934cSVivien Didelot 	err = mv88e6xxx_g1_setup(chip);
2107fad09c73SVivien Didelot 	if (err)
2108fad09c73SVivien Didelot 		goto unlock;
2109fad09c73SVivien Didelot 
21109729934cSVivien Didelot 	/* Setup Switch Global 2 Registers */
21119729934cSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
21129729934cSVivien Didelot 		err = mv88e6xxx_g2_setup(chip);
2113fad09c73SVivien Didelot 		if (err)
2114fad09c73SVivien Didelot 			goto unlock;
2115fad09c73SVivien Didelot 	}
2116fad09c73SVivien Didelot 
21171b17aedfSVivien Didelot 	err = mv88e6xxx_phy_setup(chip);
21181b17aedfSVivien Didelot 	if (err)
21191b17aedfSVivien Didelot 		goto unlock;
21201b17aedfSVivien Didelot 
2121b486d7c9SVivien Didelot 	err = mv88e6xxx_vtu_setup(chip);
2122b486d7c9SVivien Didelot 	if (err)
2123b486d7c9SVivien Didelot 		goto unlock;
2124b486d7c9SVivien Didelot 
212581228996SVivien Didelot 	err = mv88e6xxx_pvt_setup(chip);
212681228996SVivien Didelot 	if (err)
212781228996SVivien Didelot 		goto unlock;
212881228996SVivien Didelot 
2129a2ac29d2SVivien Didelot 	err = mv88e6xxx_atu_setup(chip);
2130a2ac29d2SVivien Didelot 	if (err)
2131a2ac29d2SVivien Didelot 		goto unlock;
2132a2ac29d2SVivien Didelot 
21336e55f698SAndrew Lunn 	/* Some generations have the configuration of sending reserved
21346e55f698SAndrew Lunn 	 * management frames to the CPU in global2, others in
21356e55f698SAndrew Lunn 	 * global1. Hence it does not fit the two setup functions
21366e55f698SAndrew Lunn 	 * above.
21376e55f698SAndrew Lunn 	 */
21386e55f698SAndrew Lunn 	if (chip->info->ops->mgmt_rsvd2cpu) {
21396e55f698SAndrew Lunn 		err = chip->info->ops->mgmt_rsvd2cpu(chip);
21406e55f698SAndrew Lunn 		if (err)
21416e55f698SAndrew Lunn 			goto unlock;
21426e55f698SAndrew Lunn 	}
21436e55f698SAndrew Lunn 
2144fad09c73SVivien Didelot unlock:
2145fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2146fad09c73SVivien Didelot 
2147fad09c73SVivien Didelot 	return err;
2148fad09c73SVivien Didelot }
2149fad09c73SVivien Didelot 
21503b4caa1bSVivien Didelot static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
21513b4caa1bSVivien Didelot {
215204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
21533b4caa1bSVivien Didelot 	int err;
21543b4caa1bSVivien Didelot 
2155b073d4e2SVivien Didelot 	if (!chip->info->ops->set_switch_mac)
2156b073d4e2SVivien Didelot 		return -EOPNOTSUPP;
2157b073d4e2SVivien Didelot 
21583b4caa1bSVivien Didelot 	mutex_lock(&chip->reg_lock);
2159b073d4e2SVivien Didelot 	err = chip->info->ops->set_switch_mac(chip, addr);
21603b4caa1bSVivien Didelot 	mutex_unlock(&chip->reg_lock);
21613b4caa1bSVivien Didelot 
21623b4caa1bSVivien Didelot 	return err;
21633b4caa1bSVivien Didelot }
21643b4caa1bSVivien Didelot 
2165e57e5e77SVivien Didelot static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
2166fad09c73SVivien Didelot {
21670dd12d54SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
21680dd12d54SAndrew Lunn 	struct mv88e6xxx_chip *chip = mdio_bus->chip;
2169e57e5e77SVivien Didelot 	u16 val;
2170e57e5e77SVivien Didelot 	int err;
2171fad09c73SVivien Didelot 
2172ee26a228SAndrew Lunn 	if (!chip->info->ops->phy_read)
2173ee26a228SAndrew Lunn 		return -EOPNOTSUPP;
2174ee26a228SAndrew Lunn 
2175fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2176ee26a228SAndrew Lunn 	err = chip->info->ops->phy_read(chip, bus, phy, reg, &val);
2177fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2178e57e5e77SVivien Didelot 
2179da9f3301SAndrew Lunn 	if (reg == MII_PHYSID2) {
2180da9f3301SAndrew Lunn 		/* Some internal PHYS don't have a model number.  Use
2181da9f3301SAndrew Lunn 		 * the mv88e6390 family model number instead.
2182da9f3301SAndrew Lunn 		 */
2183da9f3301SAndrew Lunn 		if (!(val & 0x3f0))
2184107fcc10SVivien Didelot 			val |= MV88E6XXX_PORT_SWITCH_ID_PROD_6390 >> 4;
2185da9f3301SAndrew Lunn 	}
2186da9f3301SAndrew Lunn 
2187e57e5e77SVivien Didelot 	return err ? err : val;
2188fad09c73SVivien Didelot }
2189fad09c73SVivien Didelot 
2190e57e5e77SVivien Didelot static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
2191fad09c73SVivien Didelot {
21920dd12d54SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus = bus->priv;
21930dd12d54SAndrew Lunn 	struct mv88e6xxx_chip *chip = mdio_bus->chip;
2194e57e5e77SVivien Didelot 	int err;
2195fad09c73SVivien Didelot 
2196ee26a228SAndrew Lunn 	if (!chip->info->ops->phy_write)
2197ee26a228SAndrew Lunn 		return -EOPNOTSUPP;
2198ee26a228SAndrew Lunn 
2199fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2200ee26a228SAndrew Lunn 	err = chip->info->ops->phy_write(chip, bus, phy, reg, val);
2201fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2202e57e5e77SVivien Didelot 
2203e57e5e77SVivien Didelot 	return err;
2204fad09c73SVivien Didelot }
2205fad09c73SVivien Didelot 
2206fad09c73SVivien Didelot static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
2207a3c53be5SAndrew Lunn 				   struct device_node *np,
2208a3c53be5SAndrew Lunn 				   bool external)
2209fad09c73SVivien Didelot {
2210fad09c73SVivien Didelot 	static int index;
22110dd12d54SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus;
2212fad09c73SVivien Didelot 	struct mii_bus *bus;
2213fad09c73SVivien Didelot 	int err;
2214fad09c73SVivien Didelot 
22150dd12d54SAndrew Lunn 	bus = devm_mdiobus_alloc_size(chip->dev, sizeof(*mdio_bus));
2216fad09c73SVivien Didelot 	if (!bus)
2217fad09c73SVivien Didelot 		return -ENOMEM;
2218fad09c73SVivien Didelot 
22190dd12d54SAndrew Lunn 	mdio_bus = bus->priv;
2220a3c53be5SAndrew Lunn 	mdio_bus->bus = bus;
22210dd12d54SAndrew Lunn 	mdio_bus->chip = chip;
2222a3c53be5SAndrew Lunn 	INIT_LIST_HEAD(&mdio_bus->list);
2223a3c53be5SAndrew Lunn 	mdio_bus->external = external;
22240dd12d54SAndrew Lunn 
2225fad09c73SVivien Didelot 	if (np) {
2226fad09c73SVivien Didelot 		bus->name = np->full_name;
2227fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
2228fad09c73SVivien Didelot 	} else {
2229fad09c73SVivien Didelot 		bus->name = "mv88e6xxx SMI";
2230fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
2231fad09c73SVivien Didelot 	}
2232fad09c73SVivien Didelot 
2233fad09c73SVivien Didelot 	bus->read = mv88e6xxx_mdio_read;
2234fad09c73SVivien Didelot 	bus->write = mv88e6xxx_mdio_write;
2235fad09c73SVivien Didelot 	bus->parent = chip->dev;
2236fad09c73SVivien Didelot 
2237a3c53be5SAndrew Lunn 	if (np)
2238a3c53be5SAndrew Lunn 		err = of_mdiobus_register(bus, np);
2239fad09c73SVivien Didelot 	else
2240fad09c73SVivien Didelot 		err = mdiobus_register(bus);
2241fad09c73SVivien Didelot 	if (err) {
2242fad09c73SVivien Didelot 		dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
2243fad09c73SVivien Didelot 		return err;
2244fad09c73SVivien Didelot 	}
2245fad09c73SVivien Didelot 
2246a3c53be5SAndrew Lunn 	if (external)
2247a3c53be5SAndrew Lunn 		list_add_tail(&mdio_bus->list, &chip->mdios);
2248a3c53be5SAndrew Lunn 	else
2249a3c53be5SAndrew Lunn 		list_add(&mdio_bus->list, &chip->mdios);
2250a3c53be5SAndrew Lunn 
2251a3c53be5SAndrew Lunn 	return 0;
2252a3c53be5SAndrew Lunn }
2253a3c53be5SAndrew Lunn 
2254a3c53be5SAndrew Lunn static const struct of_device_id mv88e6xxx_mdio_external_match[] = {
2255a3c53be5SAndrew Lunn 	{ .compatible = "marvell,mv88e6xxx-mdio-external",
2256a3c53be5SAndrew Lunn 	  .data = (void *)true },
2257a3c53be5SAndrew Lunn 	{ },
2258a3c53be5SAndrew Lunn };
2259a3c53be5SAndrew Lunn 
2260a3c53be5SAndrew Lunn static int mv88e6xxx_mdios_register(struct mv88e6xxx_chip *chip,
2261a3c53be5SAndrew Lunn 				    struct device_node *np)
2262a3c53be5SAndrew Lunn {
2263a3c53be5SAndrew Lunn 	const struct of_device_id *match;
2264a3c53be5SAndrew Lunn 	struct device_node *child;
2265a3c53be5SAndrew Lunn 	int err;
2266a3c53be5SAndrew Lunn 
2267a3c53be5SAndrew Lunn 	/* Always register one mdio bus for the internal/default mdio
2268a3c53be5SAndrew Lunn 	 * bus. This maybe represented in the device tree, but is
2269a3c53be5SAndrew Lunn 	 * optional.
2270a3c53be5SAndrew Lunn 	 */
2271a3c53be5SAndrew Lunn 	child = of_get_child_by_name(np, "mdio");
2272a3c53be5SAndrew Lunn 	err = mv88e6xxx_mdio_register(chip, child, false);
2273a3c53be5SAndrew Lunn 	if (err)
2274a3c53be5SAndrew Lunn 		return err;
2275a3c53be5SAndrew Lunn 
2276a3c53be5SAndrew Lunn 	/* Walk the device tree, and see if there are any other nodes
2277a3c53be5SAndrew Lunn 	 * which say they are compatible with the external mdio
2278a3c53be5SAndrew Lunn 	 * bus.
2279a3c53be5SAndrew Lunn 	 */
2280a3c53be5SAndrew Lunn 	for_each_available_child_of_node(np, child) {
2281a3c53be5SAndrew Lunn 		match = of_match_node(mv88e6xxx_mdio_external_match, child);
2282a3c53be5SAndrew Lunn 		if (match) {
2283a3c53be5SAndrew Lunn 			err = mv88e6xxx_mdio_register(chip, child, true);
2284a3c53be5SAndrew Lunn 			if (err)
2285a3c53be5SAndrew Lunn 				return err;
2286a3c53be5SAndrew Lunn 		}
2287a3c53be5SAndrew Lunn 	}
2288a3c53be5SAndrew Lunn 
2289a3c53be5SAndrew Lunn 	return 0;
2290a3c53be5SAndrew Lunn }
2291a3c53be5SAndrew Lunn 
2292a3c53be5SAndrew Lunn static void mv88e6xxx_mdios_unregister(struct mv88e6xxx_chip *chip)
2293fad09c73SVivien Didelot 
2294fad09c73SVivien Didelot {
2295a3c53be5SAndrew Lunn 	struct mv88e6xxx_mdio_bus *mdio_bus;
2296a3c53be5SAndrew Lunn 	struct mii_bus *bus;
2297a3c53be5SAndrew Lunn 
2298a3c53be5SAndrew Lunn 	list_for_each_entry(mdio_bus, &chip->mdios, list) {
2299a3c53be5SAndrew Lunn 		bus = mdio_bus->bus;
2300fad09c73SVivien Didelot 
2301fad09c73SVivien Didelot 		mdiobus_unregister(bus);
2302a3c53be5SAndrew Lunn 	}
2303fad09c73SVivien Didelot }
2304fad09c73SVivien Didelot 
2305855b1932SVivien Didelot static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
2306855b1932SVivien Didelot {
230704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2308855b1932SVivien Didelot 
2309855b1932SVivien Didelot 	return chip->eeprom_len;
2310855b1932SVivien Didelot }
2311855b1932SVivien Didelot 
2312855b1932SVivien Didelot static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
2313855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
2314855b1932SVivien Didelot {
231504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2316855b1932SVivien Didelot 	int err;
2317855b1932SVivien Didelot 
2318ee4dc2e7SVivien Didelot 	if (!chip->info->ops->get_eeprom)
2319ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
2320ee4dc2e7SVivien Didelot 
2321855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
2322ee4dc2e7SVivien Didelot 	err = chip->info->ops->get_eeprom(chip, eeprom, data);
2323855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2324855b1932SVivien Didelot 
2325855b1932SVivien Didelot 	if (err)
2326855b1932SVivien Didelot 		return err;
2327855b1932SVivien Didelot 
2328855b1932SVivien Didelot 	eeprom->magic = 0xc3ec4951;
2329855b1932SVivien Didelot 
2330855b1932SVivien Didelot 	return 0;
2331855b1932SVivien Didelot }
2332855b1932SVivien Didelot 
2333855b1932SVivien Didelot static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
2334855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
2335855b1932SVivien Didelot {
233604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2337855b1932SVivien Didelot 	int err;
2338855b1932SVivien Didelot 
2339ee4dc2e7SVivien Didelot 	if (!chip->info->ops->set_eeprom)
2340ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
2341ee4dc2e7SVivien Didelot 
2342855b1932SVivien Didelot 	if (eeprom->magic != 0xc3ec4951)
2343855b1932SVivien Didelot 		return -EINVAL;
2344855b1932SVivien Didelot 
2345855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
2346ee4dc2e7SVivien Didelot 	err = chip->info->ops->set_eeprom(chip, eeprom, data);
2347855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2348855b1932SVivien Didelot 
2349855b1932SVivien Didelot 	return err;
2350855b1932SVivien Didelot }
2351855b1932SVivien Didelot 
2352b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6085_ops = {
23534b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6097 */
2354b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
23557e20cfb5SVivien Didelot 	.phy_read = mv88e6185_phy_ppu_read,
23567e20cfb5SVivien Didelot 	.phy_write = mv88e6185_phy_ppu_write,
235708ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
23587f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
235996a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2360ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
236156995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2362601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
236356995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2364ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
23650898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2366c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
23679dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2368a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2369dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2370dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2371052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2372fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2373fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2374fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
23756e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2376a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2377a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
237817e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2379f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
23800ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2381b3469dd8SVivien Didelot };
2382b3469dd8SVivien Didelot 
2383b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6095_ops = {
23844b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6095 */
2385b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
23867e20cfb5SVivien Didelot 	.phy_read = mv88e6185_phy_ppu_read,
23877e20cfb5SVivien Didelot 	.phy_write = mv88e6185_phy_ppu_write,
238808ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
23897f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
239096a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
239156995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6085_port_set_frame_mode,
2392601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6185_port_set_egress_floods,
2393a23b2961SAndrew Lunn 	.port_set_upstream_port = mv88e6095_port_set_upstream_port,
2394a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2395dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2396dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2397052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
23986e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2399a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2400a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
240117e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2402f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
24030ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2404b3469dd8SVivien Didelot };
2405b3469dd8SVivien Didelot 
24067d381a02SStefan Eichenberger static const struct mv88e6xxx_ops mv88e6097_ops = {
240715da3cc8SStefan Eichenberger 	/* MV88E6XXX_FAMILY_6097 */
24087d381a02SStefan Eichenberger 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
24097d381a02SStefan Eichenberger 	.phy_read = mv88e6xxx_g2_smi_phy_read,
24107d381a02SStefan Eichenberger 	.phy_write = mv88e6xxx_g2_smi_phy_write,
24117d381a02SStefan Eichenberger 	.port_set_link = mv88e6xxx_port_set_link,
24127d381a02SStefan Eichenberger 	.port_set_duplex = mv88e6xxx_port_set_duplex,
24137d381a02SStefan Eichenberger 	.port_set_speed = mv88e6185_port_set_speed,
2414ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
241556995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2416601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
241756995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2418cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2419ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting,
24200898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2421c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
24229dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
24237d381a02SStefan Eichenberger 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
24247d381a02SStefan Eichenberger 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
24257d381a02SStefan Eichenberger 	.stats_get_strings = mv88e6095_stats_get_strings,
24267d381a02SStefan Eichenberger 	.stats_get_stats = mv88e6095_stats_get_stats,
2427fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2428fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
242991eaa475SVolodymyr Bendiuga 	.watchdog_ops = &mv88e6097_watchdog_ops,
24306e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
243117e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2432f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
24330ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
24347d381a02SStefan Eichenberger };
24357d381a02SStefan Eichenberger 
2436b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6123_ops = {
24374b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6165 */
2438b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2439ec8378bbSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2440ec8378bbSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
244108ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
24427f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
244396a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
244456995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6085_port_set_frame_mode,
2445601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
2446c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
24479dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
24480ac64c39SAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2449dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2450dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2451052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2452fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2453fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2454fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
24556e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
245617e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2457f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
24580ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2459b3469dd8SVivien Didelot };
2460b3469dd8SVivien Didelot 
2461b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6131_ops = {
24624b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6185 */
2463b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
24647e20cfb5SVivien Didelot 	.phy_read = mv88e6185_phy_ppu_read,
24657e20cfb5SVivien Didelot 	.phy_write = mv88e6185_phy_ppu_write,
246608ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
24677f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
246896a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2469ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
247056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2471601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6185_port_set_egress_floods,
247256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2473a23b2961SAndrew Lunn 	.port_set_upstream_port = mv88e6095_port_set_upstream_port,
2474cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2475ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
24760898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2477a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2478dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2479dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2480052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2481fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2482fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2483fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
24846e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2485a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2486a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
248717e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2488f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
24890ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2490b3469dd8SVivien Didelot };
2491b3469dd8SVivien Didelot 
2492990e27b0SVivien Didelot static const struct mv88e6xxx_ops mv88e6141_ops = {
2493990e27b0SVivien Didelot 	/* MV88E6XXX_FAMILY_6341 */
2494990e27b0SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
2495990e27b0SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
2496990e27b0SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2497990e27b0SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2498990e27b0SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
2499990e27b0SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
2500990e27b0SVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2501990e27b0SVivien Didelot 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
2502990e27b0SVivien Didelot 	.port_set_speed = mv88e6390_port_set_speed,
2503990e27b0SVivien Didelot 	.port_tag_remap = mv88e6095_port_tag_remap,
2504990e27b0SVivien Didelot 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2505990e27b0SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
2506990e27b0SVivien Didelot 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2507cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2508990e27b0SVivien Didelot 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
25090898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2510990e27b0SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
2511990e27b0SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2512990e27b0SVivien Didelot 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2513990e27b0SVivien Didelot 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2514990e27b0SVivien Didelot 	.stats_get_strings = mv88e6320_stats_get_strings,
2515990e27b0SVivien Didelot 	.stats_get_stats = mv88e6390_stats_get_stats,
2516fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
2517fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
2518990e27b0SVivien Didelot 	.watchdog_ops = &mv88e6390_watchdog_ops,
2519990e27b0SVivien Didelot 	.mgmt_rsvd2cpu =  mv88e6390_g1_mgmt_rsvd2cpu,
2520990e27b0SVivien Didelot 	.reset = mv88e6352_g1_reset,
2521f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
25220ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2523990e27b0SVivien Didelot };
2524990e27b0SVivien Didelot 
2525b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6161_ops = {
25264b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6165 */
2527b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2528ec8378bbSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2529ec8378bbSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
253008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
25317f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
253296a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2533ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
253456995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2535601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
253656995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2537cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2538ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
25390898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2540c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
25419dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
25420ac64c39SAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2543dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2544dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2545052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2546fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2547fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2548fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
25496e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
255017e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2551f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
25520ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2553b3469dd8SVivien Didelot };
2554b3469dd8SVivien Didelot 
2555b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6165_ops = {
25564b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6165 */
2557b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2558efb3e74dSAndrew Lunn 	.phy_read = mv88e6165_phy_read,
2559efb3e74dSAndrew Lunn 	.phy_write = mv88e6165_phy_write,
256008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
25617f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
256296a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2563c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
25649dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2565a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2566dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2567dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2568052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2569fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2570fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2571fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
25726e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
257317e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2574f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
25750ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2576b3469dd8SVivien Didelot };
2577b3469dd8SVivien Didelot 
2578b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6171_ops = {
25794b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
2580b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2581b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2582b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
258308ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
25847f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
258594d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
258696a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2587ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
258856995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2589601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
259056995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2591cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2592ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
25930898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2594c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
25959dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2596a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2597dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2598dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2599052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2600fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2601fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2602fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
26036e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
260417e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2605f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
26060ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2607b3469dd8SVivien Didelot };
2608b3469dd8SVivien Didelot 
2609b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6172_ops = {
26104b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
2611ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2612ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2613b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2614b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2615b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
261608ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
26177f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2618a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
261996a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
2620ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
262156995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2622601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
262356995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2624cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2625ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
26260898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2627c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
26289dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2629a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2630dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2631dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2632052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2633fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2634fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2635fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
26366e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
263717e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2638f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
26390ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
26406d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
2641b3469dd8SVivien Didelot };
2642b3469dd8SVivien Didelot 
2643b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6175_ops = {
26444b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
2645b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2646b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2647b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
264808ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
26497f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
265094d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
265196a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2652ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
265356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2654601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
265556995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2656cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2657ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
26580898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2659c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
26609dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2661a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2662dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2663dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2664052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2665fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2666fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2667fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
26686e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
266917e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2670f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
26710ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
2672b3469dd8SVivien Didelot };
2673b3469dd8SVivien Didelot 
2674b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6176_ops = {
26754b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
2676ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2677ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2678b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2679b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2680b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
268108ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
26827f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2683a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
268496a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
2685ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
268656995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2687601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
268856995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2689cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2690ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
26910898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2692c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
26939dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2694a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2695dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2696dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2697052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2698fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2699fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2700fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
27016e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
270217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2703f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
27040ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
27056d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
2706b3469dd8SVivien Didelot };
2707b3469dd8SVivien Didelot 
2708b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6185_ops = {
27094b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6185 */
2710b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
27117e20cfb5SVivien Didelot 	.phy_read = mv88e6185_phy_ppu_read,
27127e20cfb5SVivien Didelot 	.phy_write = mv88e6185_phy_ppu_write,
271308ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
27147f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
271596a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
271656995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6085_port_set_frame_mode,
2717601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6185_port_set_egress_floods,
2718ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6095_port_egress_rate_limiting,
2719a23b2961SAndrew Lunn 	.port_set_upstream_port = mv88e6095_port_set_upstream_port,
2720a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6xxx_g1_stats_snapshot,
2721dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2722dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2723052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2724fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2725fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2726fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
27276e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
2728a199d8b6SVivien Didelot 	.ppu_enable = mv88e6185_g1_ppu_enable,
2729a199d8b6SVivien Didelot 	.ppu_disable = mv88e6185_g1_ppu_disable,
273017e708baSVivien Didelot 	.reset = mv88e6185_g1_reset,
2731f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
27320ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2733b3469dd8SVivien Didelot };
2734b3469dd8SVivien Didelot 
27351a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6190_ops = {
27364b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
273798fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
273898fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
27391a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
27401a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
27411a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
27421a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
27431a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
27441a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
27451a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
2746ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
274756995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2748601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
274956995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
27500898432cSVivien Didelot 	.port_pause_limit = mv88e6390_port_pause_limit,
2751c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
27529dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
275379523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2754de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2755dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2756dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2757e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
2758fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
2759fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
276061303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
27616e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
276217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2763931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2764931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
27656335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
27661a3b39ecSAndrew Lunn };
27671a3b39ecSAndrew Lunn 
27681a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6190x_ops = {
27694b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
277098fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
277198fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
27721a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
27731a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
27741a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
27751a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
27761a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
27771a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
27781a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390x_port_set_speed,
2779ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
278056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2781601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
278256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
27830898432cSVivien Didelot 	.port_pause_limit = mv88e6390_port_pause_limit,
2784c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
27859dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
278679523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2787de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2788dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2789dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2790e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
2791fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
2792fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
279361303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
27946e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
279517e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2796931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2797931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
27986335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
27991a3b39ecSAndrew Lunn };
28001a3b39ecSAndrew Lunn 
28011a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6191_ops = {
28024b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
280398fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
280498fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
28051a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
28061a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
28071a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
28081a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
28091a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
28101a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
28111a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
2812ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
281356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2814601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
281556995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
28160898432cSVivien Didelot 	.port_pause_limit = mv88e6390_port_pause_limit,
2817c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
28189dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
281979523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2820de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2821dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2822dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2823e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
2824fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
2825fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
282661303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
28276e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
282817e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2829931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2830931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
28316335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
28321a3b39ecSAndrew Lunn };
28331a3b39ecSAndrew Lunn 
2834b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6240_ops = {
28354b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
2836ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2837ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2838b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2839b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2840b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
284108ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
28427f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
2843a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
284496a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
2845ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
284656995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2847601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
284856995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2849cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2850ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
28510898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2852c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
28539dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2854a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2855dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
2856dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
2857052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
2858fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2859fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
2860fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
28616e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
286217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2863f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
28640ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
28656d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
2866b3469dd8SVivien Didelot };
2867b3469dd8SVivien Didelot 
28681a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6290_ops = {
28694b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
287098fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
287198fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
28721a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
28731a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
28741a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
28751a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
28761a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
28771a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
28781a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
2879ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
288056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2881601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
288256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
28830898432cSVivien Didelot 	.port_pause_limit = mv88e6390_port_pause_limit,
2884f39908d3SAndrew Lunn 	.port_set_cmode = mv88e6390x_port_set_cmode,
2885c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
28869dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
288779523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
2888de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
2889dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2890dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2891e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
2892fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
2893fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
289461303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
28956e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
289617e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2897931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
2898931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
28996335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
29001a3b39ecSAndrew Lunn };
29011a3b39ecSAndrew Lunn 
2902b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6320_ops = {
29034b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6320 */
2904ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2905ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2906b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2907b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2908b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
290908ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
29107f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
291196a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2912ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
291356995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2914601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
291556995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2916cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2917ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
29180898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2919c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
29209dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2921a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2922dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2923dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2924052f947fSAndrew Lunn 	.stats_get_stats = mv88e6320_stats_get_stats,
2925fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2926fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
29276e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
292817e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2929f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
29300ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2931b3469dd8SVivien Didelot };
2932b3469dd8SVivien Didelot 
2933b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6321_ops = {
29344b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6321 */
2935ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
2936ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
2937b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2938b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
2939b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
294008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
29417f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
294296a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
2943ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
294456995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
2945601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
294656995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2947cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
2948ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
29490898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
2950c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
29519dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
2952a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
2953dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
2954dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
2955052f947fSAndrew Lunn 	.stats_get_stats = mv88e6320_stats_get_stats,
2956fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
2957fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
295817e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
2959f1394b78SVivien Didelot 	.vtu_getnext = mv88e6185_g1_vtu_getnext,
29600ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6185_g1_vtu_loadpurge,
2961b3469dd8SVivien Didelot };
2962b3469dd8SVivien Didelot 
296316e329aeSVivien Didelot static const struct mv88e6xxx_ops mv88e6341_ops = {
296416e329aeSVivien Didelot 	/* MV88E6XXX_FAMILY_6341 */
296516e329aeSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
296616e329aeSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
296716e329aeSVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
296816e329aeSVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
296916e329aeSVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
297016e329aeSVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
297116e329aeSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
297216e329aeSVivien Didelot 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
297316e329aeSVivien Didelot 	.port_set_speed = mv88e6390_port_set_speed,
297416e329aeSVivien Didelot 	.port_tag_remap = mv88e6095_port_tag_remap,
297516e329aeSVivien Didelot 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
297616e329aeSVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
297716e329aeSVivien Didelot 	.port_set_ether_type = mv88e6351_port_set_ether_type,
2978cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
297916e329aeSVivien Didelot 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
29800898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
298116e329aeSVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
298216e329aeSVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
298316e329aeSVivien Didelot 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
298416e329aeSVivien Didelot 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
298516e329aeSVivien Didelot 	.stats_get_strings = mv88e6320_stats_get_strings,
298616e329aeSVivien Didelot 	.stats_get_stats = mv88e6390_stats_get_stats,
2987fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
2988fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
298916e329aeSVivien Didelot 	.watchdog_ops = &mv88e6390_watchdog_ops,
299016e329aeSVivien Didelot 	.mgmt_rsvd2cpu =  mv88e6390_g1_mgmt_rsvd2cpu,
299116e329aeSVivien Didelot 	.reset = mv88e6352_g1_reset,
2992f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
29930ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
299416e329aeSVivien Didelot };
299516e329aeSVivien Didelot 
2996b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6350_ops = {
29974b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
2998b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
2999b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3000b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
300108ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
30027f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
300394d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
300496a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3005ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
300656995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3007601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
300856995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
3009cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
3010ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
30110898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
3012c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
30139dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
3014a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
3015dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
3016dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
3017052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
3018fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
3019fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
3020fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
30216e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
302217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3023f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
30240ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
3025b3469dd8SVivien Didelot };
3026b3469dd8SVivien Didelot 
3027b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6351_ops = {
30284b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6351 */
3029b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3030b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3031b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
303208ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
30337f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
303494d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
303596a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3036ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
303756995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3038601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
303956995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
3040cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
3041ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
30420898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
3043c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
30449dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
3045a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
3046dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
3047dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
3048052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
3049fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
3050fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
3051fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
30526e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
305317e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3054f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
30550ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
3056b3469dd8SVivien Didelot };
3057b3469dd8SVivien Didelot 
3058b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6352_ops = {
30594b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6352 */
3060ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3061ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3062b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3063b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3064b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
306508ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
30667f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
3067a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
306896a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
3069ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6095_port_tag_remap,
307056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3071601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
307256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
3073cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
3074ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
30750898432cSVivien Didelot 	.port_pause_limit = mv88e6097_port_pause_limit,
3076c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
30779dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
3078a605a0feSAndrew Lunn 	.stats_snapshot = mv88e6320_g1_stats_snapshot,
3079dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6095_stats_get_sset_count,
3080dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6095_stats_get_strings,
3081052f947fSAndrew Lunn 	.stats_get_stats = mv88e6095_stats_get_stats,
3082fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6095_g1_set_cpu_port,
3083fa8d1179SVivien Didelot 	.set_egress_port = mv88e6095_g1_set_egress_port,
3084fcd25166SAndrew Lunn 	.watchdog_ops = &mv88e6097_watchdog_ops,
30856e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6095_g2_mgmt_rsvd2cpu,
308617e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3087f1394b78SVivien Didelot 	.vtu_getnext = mv88e6352_g1_vtu_getnext,
30880ad5daf6SVivien Didelot 	.vtu_loadpurge = mv88e6352_g1_vtu_loadpurge,
30896d91782fSAndrew Lunn 	.serdes_power = mv88e6352_serdes_power,
3090b3469dd8SVivien Didelot };
3091b3469dd8SVivien Didelot 
30921a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6390_ops = {
30934b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
309498fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
309598fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
30961a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
30971a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
30981a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
30991a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
31001a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
31011a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
31021a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390_port_set_speed,
3103ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
310456995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3105601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
310656995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
3107cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
3108ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
31090898432cSVivien Didelot 	.port_pause_limit = mv88e6390_port_pause_limit,
3110f39908d3SAndrew Lunn 	.port_set_cmode = mv88e6390x_port_set_cmode,
3111c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
31129dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
311379523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
3114de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
3115dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
3116dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
3117e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
3118fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
3119fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
312061303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
31216e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
312217e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3123931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
3124931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
31256335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
31261a3b39ecSAndrew Lunn };
31271a3b39ecSAndrew Lunn 
31281a3b39ecSAndrew Lunn static const struct mv88e6xxx_ops mv88e6390x_ops = {
31294b325d8cSAndrew Lunn 	/* MV88E6XXX_FAMILY_6390 */
313098fc3c6fSVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom8,
313198fc3c6fSVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom8,
31321a3b39ecSAndrew Lunn 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
31331a3b39ecSAndrew Lunn 	.phy_read = mv88e6xxx_g2_smi_phy_read,
31341a3b39ecSAndrew Lunn 	.phy_write = mv88e6xxx_g2_smi_phy_write,
31351a3b39ecSAndrew Lunn 	.port_set_link = mv88e6xxx_port_set_link,
31361a3b39ecSAndrew Lunn 	.port_set_duplex = mv88e6xxx_port_set_duplex,
31371a3b39ecSAndrew Lunn 	.port_set_rgmii_delay = mv88e6390_port_set_rgmii_delay,
31381a3b39ecSAndrew Lunn 	.port_set_speed = mv88e6390x_port_set_speed,
3139ef0a7318SAndrew Lunn 	.port_tag_remap = mv88e6390_port_tag_remap,
314056995cbcSAndrew Lunn 	.port_set_frame_mode = mv88e6351_port_set_frame_mode,
3141601aeed3SVivien Didelot 	.port_set_egress_floods = mv88e6352_port_set_egress_floods,
314256995cbcSAndrew Lunn 	.port_set_ether_type = mv88e6351_port_set_ether_type,
3143cd782656SVivien Didelot 	.port_set_jumbo_size = mv88e6165_port_set_jumbo_size,
3144ef70b111SAndrew Lunn 	.port_egress_rate_limiting = mv88e6097_port_egress_rate_limiting,
31450898432cSVivien Didelot 	.port_pause_limit = mv88e6390_port_pause_limit,
3146c8c94891SVivien Didelot 	.port_disable_learn_limit = mv88e6xxx_port_disable_learn_limit,
31479dbfb4e1SVivien Didelot 	.port_disable_pri_override = mv88e6xxx_port_disable_pri_override,
314879523473SAndrew Lunn 	.stats_snapshot = mv88e6390_g1_stats_snapshot,
3149de227387SAndrew Lunn 	.stats_set_histogram = mv88e6390_g1_stats_set_histogram,
3150dfafe449SAndrew Lunn 	.stats_get_sset_count = mv88e6320_stats_get_sset_count,
3151dfafe449SAndrew Lunn 	.stats_get_strings = mv88e6320_stats_get_strings,
3152e0d8b615SAndrew Lunn 	.stats_get_stats = mv88e6390_stats_get_stats,
3153fa8d1179SVivien Didelot 	.set_cpu_port = mv88e6390_g1_set_cpu_port,
3154fa8d1179SVivien Didelot 	.set_egress_port = mv88e6390_g1_set_egress_port,
315561303736SAndrew Lunn 	.watchdog_ops = &mv88e6390_watchdog_ops,
31566e55f698SAndrew Lunn 	.mgmt_rsvd2cpu = mv88e6390_g1_mgmt_rsvd2cpu,
315717e708baSVivien Didelot 	.reset = mv88e6352_g1_reset,
3158931d1822SVivien Didelot 	.vtu_getnext = mv88e6390_g1_vtu_getnext,
3159931d1822SVivien Didelot 	.vtu_loadpurge = mv88e6390_g1_vtu_loadpurge,
31606335e9f2SAndrew Lunn 	.serdes_power = mv88e6390_serdes_power,
31611a3b39ecSAndrew Lunn };
31621a3b39ecSAndrew Lunn 
3163fad09c73SVivien Didelot static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3164fad09c73SVivien Didelot 	[MV88E6085] = {
3165107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6085,
3166fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6097,
3167fad09c73SVivien Didelot 		.name = "Marvell 88E6085",
3168fad09c73SVivien Didelot 		.num_databases = 4096,
3169fad09c73SVivien Didelot 		.num_ports = 10,
31703cf3c846SVivien Didelot 		.max_vid = 4095,
3171fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3172a935c052SVivien Didelot 		.global1_addr = 0x1b,
3173acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3174dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3175e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3176f3645652SVivien Didelot 		.pvt = true,
3177443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3178fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
3179b3469dd8SVivien Didelot 		.ops = &mv88e6085_ops,
3180fad09c73SVivien Didelot 	},
3181fad09c73SVivien Didelot 
3182fad09c73SVivien Didelot 	[MV88E6095] = {
3183107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6095,
3184fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6095,
3185fad09c73SVivien Didelot 		.name = "Marvell 88E6095/88E6095F",
3186fad09c73SVivien Didelot 		.num_databases = 256,
3187fad09c73SVivien Didelot 		.num_ports = 11,
31883cf3c846SVivien Didelot 		.max_vid = 4095,
3189fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3190a935c052SVivien Didelot 		.global1_addr = 0x1b,
3191acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3192dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3193e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3194443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3195fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6095,
3196b3469dd8SVivien Didelot 		.ops = &mv88e6095_ops,
3197fad09c73SVivien Didelot 	},
3198fad09c73SVivien Didelot 
31997d381a02SStefan Eichenberger 	[MV88E6097] = {
3200107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6097,
32017d381a02SStefan Eichenberger 		.family = MV88E6XXX_FAMILY_6097,
32027d381a02SStefan Eichenberger 		.name = "Marvell 88E6097/88E6097F",
32037d381a02SStefan Eichenberger 		.num_databases = 4096,
32047d381a02SStefan Eichenberger 		.num_ports = 11,
32053cf3c846SVivien Didelot 		.max_vid = 4095,
32067d381a02SStefan Eichenberger 		.port_base_addr = 0x10,
32077d381a02SStefan Eichenberger 		.global1_addr = 0x1b,
32087d381a02SStefan Eichenberger 		.age_time_coeff = 15000,
3209c534178bSStefan Eichenberger 		.g1_irqs = 8,
3210e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3211f3645652SVivien Didelot 		.pvt = true,
32122bfcfcd3SStefan Eichenberger 		.tag_protocol = DSA_TAG_PROTO_EDSA,
32137d381a02SStefan Eichenberger 		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
32147d381a02SStefan Eichenberger 		.ops = &mv88e6097_ops,
32157d381a02SStefan Eichenberger 	},
32167d381a02SStefan Eichenberger 
3217fad09c73SVivien Didelot 	[MV88E6123] = {
3218107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6123,
3219fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3220fad09c73SVivien Didelot 		.name = "Marvell 88E6123",
3221fad09c73SVivien Didelot 		.num_databases = 4096,
3222fad09c73SVivien Didelot 		.num_ports = 3,
32233cf3c846SVivien Didelot 		.max_vid = 4095,
3224fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3225a935c052SVivien Didelot 		.global1_addr = 0x1b,
3226acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3227dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3228e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3229f3645652SVivien Didelot 		.pvt = true,
32305ebe31d7SAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3231fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3232b3469dd8SVivien Didelot 		.ops = &mv88e6123_ops,
3233fad09c73SVivien Didelot 	},
3234fad09c73SVivien Didelot 
3235fad09c73SVivien Didelot 	[MV88E6131] = {
3236107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6131,
3237fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3238fad09c73SVivien Didelot 		.name = "Marvell 88E6131",
3239fad09c73SVivien Didelot 		.num_databases = 256,
3240fad09c73SVivien Didelot 		.num_ports = 8,
32413cf3c846SVivien Didelot 		.max_vid = 4095,
3242fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3243a935c052SVivien Didelot 		.global1_addr = 0x1b,
3244acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3245dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3246e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3247443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3248fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3249b3469dd8SVivien Didelot 		.ops = &mv88e6131_ops,
3250fad09c73SVivien Didelot 	},
3251fad09c73SVivien Didelot 
3252990e27b0SVivien Didelot 	[MV88E6141] = {
3253107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6141,
3254990e27b0SVivien Didelot 		.family = MV88E6XXX_FAMILY_6341,
3255990e27b0SVivien Didelot 		.name = "Marvell 88E6341",
3256990e27b0SVivien Didelot 		.num_databases = 4096,
3257990e27b0SVivien Didelot 		.num_ports = 6,
32583cf3c846SVivien Didelot 		.max_vid = 4095,
3259990e27b0SVivien Didelot 		.port_base_addr = 0x10,
3260990e27b0SVivien Didelot 		.global1_addr = 0x1b,
3261990e27b0SVivien Didelot 		.age_time_coeff = 3750,
3262990e27b0SVivien Didelot 		.atu_move_port_mask = 0x1f,
3263f3645652SVivien Didelot 		.pvt = true,
3264990e27b0SVivien Didelot 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3265990e27b0SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6341,
3266990e27b0SVivien Didelot 		.ops = &mv88e6141_ops,
3267990e27b0SVivien Didelot 	},
3268990e27b0SVivien Didelot 
3269fad09c73SVivien Didelot 	[MV88E6161] = {
3270107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6161,
3271fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3272fad09c73SVivien Didelot 		.name = "Marvell 88E6161",
3273fad09c73SVivien Didelot 		.num_databases = 4096,
3274fad09c73SVivien Didelot 		.num_ports = 6,
32753cf3c846SVivien Didelot 		.max_vid = 4095,
3276fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3277a935c052SVivien Didelot 		.global1_addr = 0x1b,
3278acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3279dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3280e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3281f3645652SVivien Didelot 		.pvt = true,
32825ebe31d7SAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3283fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3284b3469dd8SVivien Didelot 		.ops = &mv88e6161_ops,
3285fad09c73SVivien Didelot 	},
3286fad09c73SVivien Didelot 
3287fad09c73SVivien Didelot 	[MV88E6165] = {
3288107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6165,
3289fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3290fad09c73SVivien Didelot 		.name = "Marvell 88E6165",
3291fad09c73SVivien Didelot 		.num_databases = 4096,
3292fad09c73SVivien Didelot 		.num_ports = 6,
32933cf3c846SVivien Didelot 		.max_vid = 4095,
3294fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3295a935c052SVivien Didelot 		.global1_addr = 0x1b,
3296acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3297dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3298e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3299f3645652SVivien Didelot 		.pvt = true,
3300443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3301fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3302b3469dd8SVivien Didelot 		.ops = &mv88e6165_ops,
3303fad09c73SVivien Didelot 	},
3304fad09c73SVivien Didelot 
3305fad09c73SVivien Didelot 	[MV88E6171] = {
3306107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6171,
3307fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3308fad09c73SVivien Didelot 		.name = "Marvell 88E6171",
3309fad09c73SVivien Didelot 		.num_databases = 4096,
3310fad09c73SVivien Didelot 		.num_ports = 7,
33113cf3c846SVivien Didelot 		.max_vid = 4095,
3312fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3313a935c052SVivien Didelot 		.global1_addr = 0x1b,
3314acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3315dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3316e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3317f3645652SVivien Didelot 		.pvt = true,
3318443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3319fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3320b3469dd8SVivien Didelot 		.ops = &mv88e6171_ops,
3321fad09c73SVivien Didelot 	},
3322fad09c73SVivien Didelot 
3323fad09c73SVivien Didelot 	[MV88E6172] = {
3324107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6172,
3325fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3326fad09c73SVivien Didelot 		.name = "Marvell 88E6172",
3327fad09c73SVivien Didelot 		.num_databases = 4096,
3328fad09c73SVivien Didelot 		.num_ports = 7,
33293cf3c846SVivien Didelot 		.max_vid = 4095,
3330fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3331a935c052SVivien Didelot 		.global1_addr = 0x1b,
3332acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3333dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3334e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3335f3645652SVivien Didelot 		.pvt = true,
3336443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3337fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3338b3469dd8SVivien Didelot 		.ops = &mv88e6172_ops,
3339fad09c73SVivien Didelot 	},
3340fad09c73SVivien Didelot 
3341fad09c73SVivien Didelot 	[MV88E6175] = {
3342107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6175,
3343fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3344fad09c73SVivien Didelot 		.name = "Marvell 88E6175",
3345fad09c73SVivien Didelot 		.num_databases = 4096,
3346fad09c73SVivien Didelot 		.num_ports = 7,
33473cf3c846SVivien Didelot 		.max_vid = 4095,
3348fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3349a935c052SVivien Didelot 		.global1_addr = 0x1b,
3350acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3351dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3352e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3353f3645652SVivien Didelot 		.pvt = true,
3354443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3355fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3356b3469dd8SVivien Didelot 		.ops = &mv88e6175_ops,
3357fad09c73SVivien Didelot 	},
3358fad09c73SVivien Didelot 
3359fad09c73SVivien Didelot 	[MV88E6176] = {
3360107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6176,
3361fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3362fad09c73SVivien Didelot 		.name = "Marvell 88E6176",
3363fad09c73SVivien Didelot 		.num_databases = 4096,
3364fad09c73SVivien Didelot 		.num_ports = 7,
33653cf3c846SVivien Didelot 		.max_vid = 4095,
3366fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3367a935c052SVivien Didelot 		.global1_addr = 0x1b,
3368acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3369dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3370e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3371f3645652SVivien Didelot 		.pvt = true,
3372443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3373fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3374b3469dd8SVivien Didelot 		.ops = &mv88e6176_ops,
3375fad09c73SVivien Didelot 	},
3376fad09c73SVivien Didelot 
3377fad09c73SVivien Didelot 	[MV88E6185] = {
3378107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6185,
3379fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3380fad09c73SVivien Didelot 		.name = "Marvell 88E6185",
3381fad09c73SVivien Didelot 		.num_databases = 256,
3382fad09c73SVivien Didelot 		.num_ports = 10,
33833cf3c846SVivien Didelot 		.max_vid = 4095,
3384fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3385a935c052SVivien Didelot 		.global1_addr = 0x1b,
3386acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3387dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3388e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3389443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3390fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3391b3469dd8SVivien Didelot 		.ops = &mv88e6185_ops,
3392fad09c73SVivien Didelot 	},
3393fad09c73SVivien Didelot 
33941a3b39ecSAndrew Lunn 	[MV88E6190] = {
3395107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190,
33961a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
33971a3b39ecSAndrew Lunn 		.name = "Marvell 88E6190",
33981a3b39ecSAndrew Lunn 		.num_databases = 4096,
33991a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3400931d1822SVivien Didelot 		.max_vid = 8191,
34011a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34021a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3403443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
3404b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
34051a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3406f3645652SVivien Didelot 		.pvt = true,
3407e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
34081a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34091a3b39ecSAndrew Lunn 		.ops = &mv88e6190_ops,
34101a3b39ecSAndrew Lunn 	},
34111a3b39ecSAndrew Lunn 
34121a3b39ecSAndrew Lunn 	[MV88E6190X] = {
3413107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6190X,
34141a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34151a3b39ecSAndrew Lunn 		.name = "Marvell 88E6190X",
34161a3b39ecSAndrew Lunn 		.num_databases = 4096,
34171a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3418931d1822SVivien Didelot 		.max_vid = 8191,
34191a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34201a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3421b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
34221a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3423e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3424f3645652SVivien Didelot 		.pvt = true,
3425443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
34261a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34271a3b39ecSAndrew Lunn 		.ops = &mv88e6190x_ops,
34281a3b39ecSAndrew Lunn 	},
34291a3b39ecSAndrew Lunn 
34301a3b39ecSAndrew Lunn 	[MV88E6191] = {
3431107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6191,
34321a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34331a3b39ecSAndrew Lunn 		.name = "Marvell 88E6191",
34341a3b39ecSAndrew Lunn 		.num_databases = 4096,
34351a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3436931d1822SVivien Didelot 		.max_vid = 8191,
34371a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34381a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3439b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
3440443d5a1bSAndrew Lunn 		.g1_irqs = 9,
3441e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3442f3645652SVivien Didelot 		.pvt = true,
3443443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
34441a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34452cf4cefbSVivien Didelot 		.ops = &mv88e6191_ops,
34461a3b39ecSAndrew Lunn 	},
34471a3b39ecSAndrew Lunn 
3448fad09c73SVivien Didelot 	[MV88E6240] = {
3449107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6240,
3450fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3451fad09c73SVivien Didelot 		.name = "Marvell 88E6240",
3452fad09c73SVivien Didelot 		.num_databases = 4096,
3453fad09c73SVivien Didelot 		.num_ports = 7,
34543cf3c846SVivien Didelot 		.max_vid = 4095,
3455fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3456a935c052SVivien Didelot 		.global1_addr = 0x1b,
3457acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3458dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3459e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3460f3645652SVivien Didelot 		.pvt = true,
3461443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3462fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3463b3469dd8SVivien Didelot 		.ops = &mv88e6240_ops,
3464fad09c73SVivien Didelot 	},
3465fad09c73SVivien Didelot 
34661a3b39ecSAndrew Lunn 	[MV88E6290] = {
3467107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6290,
34681a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
34691a3b39ecSAndrew Lunn 		.name = "Marvell 88E6290",
34701a3b39ecSAndrew Lunn 		.num_databases = 4096,
34711a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3472931d1822SVivien Didelot 		.max_vid = 8191,
34731a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
34741a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3475b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
34761a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3477e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3478f3645652SVivien Didelot 		.pvt = true,
3479443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
34801a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
34811a3b39ecSAndrew Lunn 		.ops = &mv88e6290_ops,
34821a3b39ecSAndrew Lunn 	},
34831a3b39ecSAndrew Lunn 
3484fad09c73SVivien Didelot 	[MV88E6320] = {
3485107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6320,
3486fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3487fad09c73SVivien Didelot 		.name = "Marvell 88E6320",
3488fad09c73SVivien Didelot 		.num_databases = 4096,
3489fad09c73SVivien Didelot 		.num_ports = 7,
34903cf3c846SVivien Didelot 		.max_vid = 4095,
3491fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3492a935c052SVivien Didelot 		.global1_addr = 0x1b,
3493acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3494dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3495e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3496f3645652SVivien Didelot 		.pvt = true,
3497443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3498fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3499b3469dd8SVivien Didelot 		.ops = &mv88e6320_ops,
3500fad09c73SVivien Didelot 	},
3501fad09c73SVivien Didelot 
3502fad09c73SVivien Didelot 	[MV88E6321] = {
3503107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6321,
3504fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3505fad09c73SVivien Didelot 		.name = "Marvell 88E6321",
3506fad09c73SVivien Didelot 		.num_databases = 4096,
3507fad09c73SVivien Didelot 		.num_ports = 7,
35083cf3c846SVivien Didelot 		.max_vid = 4095,
3509fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3510a935c052SVivien Didelot 		.global1_addr = 0x1b,
3511acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3512dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3513e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3514443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3515fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3516b3469dd8SVivien Didelot 		.ops = &mv88e6321_ops,
3517fad09c73SVivien Didelot 	},
3518fad09c73SVivien Didelot 
3519a75961d0SGregory CLEMENT 	[MV88E6341] = {
3520107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6341,
3521a75961d0SGregory CLEMENT 		.family = MV88E6XXX_FAMILY_6341,
3522a75961d0SGregory CLEMENT 		.name = "Marvell 88E6341",
3523a75961d0SGregory CLEMENT 		.num_databases = 4096,
3524a75961d0SGregory CLEMENT 		.num_ports = 6,
35253cf3c846SVivien Didelot 		.max_vid = 4095,
3526a75961d0SGregory CLEMENT 		.port_base_addr = 0x10,
3527a75961d0SGregory CLEMENT 		.global1_addr = 0x1b,
3528a75961d0SGregory CLEMENT 		.age_time_coeff = 3750,
3529e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3530f3645652SVivien Didelot 		.pvt = true,
3531a75961d0SGregory CLEMENT 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3532a75961d0SGregory CLEMENT 		.flags = MV88E6XXX_FLAGS_FAMILY_6341,
3533a75961d0SGregory CLEMENT 		.ops = &mv88e6341_ops,
3534a75961d0SGregory CLEMENT 	},
3535a75961d0SGregory CLEMENT 
3536fad09c73SVivien Didelot 	[MV88E6350] = {
3537107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6350,
3538fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3539fad09c73SVivien Didelot 		.name = "Marvell 88E6350",
3540fad09c73SVivien Didelot 		.num_databases = 4096,
3541fad09c73SVivien Didelot 		.num_ports = 7,
35423cf3c846SVivien Didelot 		.max_vid = 4095,
3543fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3544a935c052SVivien Didelot 		.global1_addr = 0x1b,
3545acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3546dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3547e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3548f3645652SVivien Didelot 		.pvt = true,
3549443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3550fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3551b3469dd8SVivien Didelot 		.ops = &mv88e6350_ops,
3552fad09c73SVivien Didelot 	},
3553fad09c73SVivien Didelot 
3554fad09c73SVivien Didelot 	[MV88E6351] = {
3555107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6351,
3556fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3557fad09c73SVivien Didelot 		.name = "Marvell 88E6351",
3558fad09c73SVivien Didelot 		.num_databases = 4096,
3559fad09c73SVivien Didelot 		.num_ports = 7,
35603cf3c846SVivien Didelot 		.max_vid = 4095,
3561fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3562a935c052SVivien Didelot 		.global1_addr = 0x1b,
3563acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3564dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3565e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3566f3645652SVivien Didelot 		.pvt = true,
3567443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3568fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3569b3469dd8SVivien Didelot 		.ops = &mv88e6351_ops,
3570fad09c73SVivien Didelot 	},
3571fad09c73SVivien Didelot 
3572fad09c73SVivien Didelot 	[MV88E6352] = {
3573107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6352,
3574fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3575fad09c73SVivien Didelot 		.name = "Marvell 88E6352",
3576fad09c73SVivien Didelot 		.num_databases = 4096,
3577fad09c73SVivien Didelot 		.num_ports = 7,
35783cf3c846SVivien Didelot 		.max_vid = 4095,
3579fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3580a935c052SVivien Didelot 		.global1_addr = 0x1b,
3581acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3582dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3583e606ca36SVivien Didelot 		.atu_move_port_mask = 0xf,
3584f3645652SVivien Didelot 		.pvt = true,
3585443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_EDSA,
3586fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3587b3469dd8SVivien Didelot 		.ops = &mv88e6352_ops,
3588fad09c73SVivien Didelot 	},
35891a3b39ecSAndrew Lunn 	[MV88E6390] = {
3590107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390,
35911a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
35921a3b39ecSAndrew Lunn 		.name = "Marvell 88E6390",
35931a3b39ecSAndrew Lunn 		.num_databases = 4096,
35941a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3595931d1822SVivien Didelot 		.max_vid = 8191,
35961a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
35971a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3598b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
35991a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3600e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3601f3645652SVivien Didelot 		.pvt = true,
3602443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
36031a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
36041a3b39ecSAndrew Lunn 		.ops = &mv88e6390_ops,
36051a3b39ecSAndrew Lunn 	},
36061a3b39ecSAndrew Lunn 	[MV88E6390X] = {
3607107fcc10SVivien Didelot 		.prod_num = MV88E6XXX_PORT_SWITCH_ID_PROD_6390X,
36081a3b39ecSAndrew Lunn 		.family = MV88E6XXX_FAMILY_6390,
36091a3b39ecSAndrew Lunn 		.name = "Marvell 88E6390X",
36101a3b39ecSAndrew Lunn 		.num_databases = 4096,
36111a3b39ecSAndrew Lunn 		.num_ports = 11,	/* 10 + Z80 */
3612931d1822SVivien Didelot 		.max_vid = 8191,
36131a3b39ecSAndrew Lunn 		.port_base_addr = 0x0,
36141a3b39ecSAndrew Lunn 		.global1_addr = 0x1b,
3615b91e055cSAndrew Lunn 		.age_time_coeff = 3750,
36161a3b39ecSAndrew Lunn 		.g1_irqs = 9,
3617e606ca36SVivien Didelot 		.atu_move_port_mask = 0x1f,
3618f3645652SVivien Didelot 		.pvt = true,
3619443d5a1bSAndrew Lunn 		.tag_protocol = DSA_TAG_PROTO_DSA,
36201a3b39ecSAndrew Lunn 		.flags = MV88E6XXX_FLAGS_FAMILY_6390,
36211a3b39ecSAndrew Lunn 		.ops = &mv88e6390x_ops,
36221a3b39ecSAndrew Lunn 	},
3623fad09c73SVivien Didelot };
3624fad09c73SVivien Didelot 
3625fad09c73SVivien Didelot static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
3626fad09c73SVivien Didelot {
3627fad09c73SVivien Didelot 	int i;
3628fad09c73SVivien Didelot 
3629fad09c73SVivien Didelot 	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3630fad09c73SVivien Didelot 		if (mv88e6xxx_table[i].prod_num == prod_num)
3631fad09c73SVivien Didelot 			return &mv88e6xxx_table[i];
3632fad09c73SVivien Didelot 
3633fad09c73SVivien Didelot 	return NULL;
3634fad09c73SVivien Didelot }
3635fad09c73SVivien Didelot 
3636fad09c73SVivien Didelot static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
3637fad09c73SVivien Didelot {
3638fad09c73SVivien Didelot 	const struct mv88e6xxx_info *info;
36398f6345b2SVivien Didelot 	unsigned int prod_num, rev;
36408f6345b2SVivien Didelot 	u16 id;
36418f6345b2SVivien Didelot 	int err;
3642fad09c73SVivien Didelot 
36438f6345b2SVivien Didelot 	mutex_lock(&chip->reg_lock);
3644107fcc10SVivien Didelot 	err = mv88e6xxx_port_read(chip, 0, MV88E6XXX_PORT_SWITCH_ID, &id);
36458f6345b2SVivien Didelot 	mutex_unlock(&chip->reg_lock);
36468f6345b2SVivien Didelot 	if (err)
36478f6345b2SVivien Didelot 		return err;
3648fad09c73SVivien Didelot 
3649107fcc10SVivien Didelot 	prod_num = id & MV88E6XXX_PORT_SWITCH_ID_PROD_MASK;
3650107fcc10SVivien Didelot 	rev = id & MV88E6XXX_PORT_SWITCH_ID_REV_MASK;
3651fad09c73SVivien Didelot 
3652fad09c73SVivien Didelot 	info = mv88e6xxx_lookup_info(prod_num);
3653fad09c73SVivien Didelot 	if (!info)
3654fad09c73SVivien Didelot 		return -ENODEV;
3655fad09c73SVivien Didelot 
3656fad09c73SVivien Didelot 	/* Update the compatible info with the probed one */
3657fad09c73SVivien Didelot 	chip->info = info;
3658fad09c73SVivien Didelot 
3659ca070c10SVivien Didelot 	err = mv88e6xxx_g2_require(chip);
3660ca070c10SVivien Didelot 	if (err)
3661ca070c10SVivien Didelot 		return err;
3662ca070c10SVivien Didelot 
3663fad09c73SVivien Didelot 	dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3664fad09c73SVivien Didelot 		 chip->info->prod_num, chip->info->name, rev);
3665fad09c73SVivien Didelot 
3666fad09c73SVivien Didelot 	return 0;
3667fad09c73SVivien Didelot }
3668fad09c73SVivien Didelot 
3669fad09c73SVivien Didelot static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
3670fad09c73SVivien Didelot {
3671fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3672fad09c73SVivien Didelot 
3673fad09c73SVivien Didelot 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3674fad09c73SVivien Didelot 	if (!chip)
3675fad09c73SVivien Didelot 		return NULL;
3676fad09c73SVivien Didelot 
3677fad09c73SVivien Didelot 	chip->dev = dev;
3678fad09c73SVivien Didelot 
3679fad09c73SVivien Didelot 	mutex_init(&chip->reg_lock);
3680a3c53be5SAndrew Lunn 	INIT_LIST_HEAD(&chip->mdios);
3681fad09c73SVivien Didelot 
3682fad09c73SVivien Didelot 	return chip;
3683fad09c73SVivien Didelot }
3684fad09c73SVivien Didelot 
3685fad09c73SVivien Didelot static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
3686fad09c73SVivien Didelot 			      struct mii_bus *bus, int sw_addr)
3687fad09c73SVivien Didelot {
3688fad09c73SVivien Didelot 	if (sw_addr == 0)
3689fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
3690a0ffff24SVivien Didelot 	else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
3691fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
3692fad09c73SVivien Didelot 	else
3693fad09c73SVivien Didelot 		return -EINVAL;
3694fad09c73SVivien Didelot 
3695fad09c73SVivien Didelot 	chip->bus = bus;
3696fad09c73SVivien Didelot 	chip->sw_addr = sw_addr;
3697fad09c73SVivien Didelot 
3698fad09c73SVivien Didelot 	return 0;
3699fad09c73SVivien Didelot }
3700fad09c73SVivien Didelot 
37017b314362SAndrew Lunn static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
37027b314362SAndrew Lunn {
370304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37042bbb33beSAndrew Lunn 
3705443d5a1bSAndrew Lunn 	return chip->info->tag_protocol;
37067b314362SAndrew Lunn }
37077b314362SAndrew Lunn 
3708fad09c73SVivien Didelot static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3709fad09c73SVivien Didelot 				       struct device *host_dev, int sw_addr,
3710fad09c73SVivien Didelot 				       void **priv)
3711fad09c73SVivien Didelot {
3712fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3713fad09c73SVivien Didelot 	struct mii_bus *bus;
3714fad09c73SVivien Didelot 	int err;
3715fad09c73SVivien Didelot 
3716fad09c73SVivien Didelot 	bus = dsa_host_dev_to_mii_bus(host_dev);
3717fad09c73SVivien Didelot 	if (!bus)
3718fad09c73SVivien Didelot 		return NULL;
3719fad09c73SVivien Didelot 
3720fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dsa_dev);
3721fad09c73SVivien Didelot 	if (!chip)
3722fad09c73SVivien Didelot 		return NULL;
3723fad09c73SVivien Didelot 
3724fad09c73SVivien Didelot 	/* Legacy SMI probing will only support chips similar to 88E6085 */
3725fad09c73SVivien Didelot 	chip->info = &mv88e6xxx_table[MV88E6085];
3726fad09c73SVivien Didelot 
3727fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, bus, sw_addr);
3728fad09c73SVivien Didelot 	if (err)
3729fad09c73SVivien Didelot 		goto free;
3730fad09c73SVivien Didelot 
3731fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3732fad09c73SVivien Didelot 	if (err)
3733fad09c73SVivien Didelot 		goto free;
3734fad09c73SVivien Didelot 
3735dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3736dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3737dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3738dc30c35bSAndrew Lunn 	if (err)
3739dc30c35bSAndrew Lunn 		goto free;
3740dc30c35bSAndrew Lunn 
3741e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3742e57e5e77SVivien Didelot 
3743a3c53be5SAndrew Lunn 	err = mv88e6xxx_mdios_register(chip, NULL);
3744fad09c73SVivien Didelot 	if (err)
3745fad09c73SVivien Didelot 		goto free;
3746fad09c73SVivien Didelot 
3747fad09c73SVivien Didelot 	*priv = chip;
3748fad09c73SVivien Didelot 
3749fad09c73SVivien Didelot 	return chip->info->name;
3750fad09c73SVivien Didelot free:
3751fad09c73SVivien Didelot 	devm_kfree(dsa_dev, chip);
3752fad09c73SVivien Didelot 
3753fad09c73SVivien Didelot 	return NULL;
3754fad09c73SVivien Didelot }
3755fad09c73SVivien Didelot 
37567df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
37577df8fbddSVivien Didelot 				      const struct switchdev_obj_port_mdb *mdb,
37587df8fbddSVivien Didelot 				      struct switchdev_trans *trans)
37597df8fbddSVivien Didelot {
37607df8fbddSVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
37617df8fbddSVivien Didelot 	 * so skip the prepare phase.
37627df8fbddSVivien Didelot 	 */
37637df8fbddSVivien Didelot 
37647df8fbddSVivien Didelot 	return 0;
37657df8fbddSVivien Didelot }
37667df8fbddSVivien Didelot 
37677df8fbddSVivien Didelot static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
37687df8fbddSVivien Didelot 				   const struct switchdev_obj_port_mdb *mdb,
37697df8fbddSVivien Didelot 				   struct switchdev_trans *trans)
37707df8fbddSVivien Didelot {
377104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37727df8fbddSVivien Didelot 
37737df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
37747df8fbddSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
37757df8fbddSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_MC_STATIC))
3776774439e5SVivien Didelot 		dev_err(ds->dev, "p%d: failed to load multicast MAC address\n",
3777774439e5SVivien Didelot 			port);
37787df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
37797df8fbddSVivien Didelot }
37807df8fbddSVivien Didelot 
37817df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
37827df8fbddSVivien Didelot 				  const struct switchdev_obj_port_mdb *mdb)
37837df8fbddSVivien Didelot {
378404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37857df8fbddSVivien Didelot 	int err;
37867df8fbddSVivien Didelot 
37877df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
37887df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
37897df8fbddSVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
37907df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
37917df8fbddSVivien Didelot 
37927df8fbddSVivien Didelot 	return err;
37937df8fbddSVivien Didelot }
37947df8fbddSVivien Didelot 
37957df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
37967df8fbddSVivien Didelot 				   struct switchdev_obj_port_mdb *mdb,
3797438ff537SVivien Didelot 				   switchdev_obj_dump_cb_t *cb)
37987df8fbddSVivien Didelot {
379904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
38007df8fbddSVivien Didelot 	int err;
38017df8fbddSVivien Didelot 
38027df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
38037df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
38047df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
38057df8fbddSVivien Didelot 
38067df8fbddSVivien Didelot 	return err;
38077df8fbddSVivien Didelot }
38087df8fbddSVivien Didelot 
3809a82f67afSFlorian Fainelli static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
3810fad09c73SVivien Didelot 	.probe			= mv88e6xxx_drv_probe,
38117b314362SAndrew Lunn 	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
3812fad09c73SVivien Didelot 	.setup			= mv88e6xxx_setup,
3813fad09c73SVivien Didelot 	.set_addr		= mv88e6xxx_set_addr,
3814fad09c73SVivien Didelot 	.adjust_link		= mv88e6xxx_adjust_link,
3815fad09c73SVivien Didelot 	.get_strings		= mv88e6xxx_get_strings,
3816fad09c73SVivien Didelot 	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
3817fad09c73SVivien Didelot 	.get_sset_count		= mv88e6xxx_get_sset_count,
381804aca993SAndrew Lunn 	.port_enable		= mv88e6xxx_port_enable,
381904aca993SAndrew Lunn 	.port_disable		= mv88e6xxx_port_disable,
3820fad09c73SVivien Didelot 	.set_eee		= mv88e6xxx_set_eee,
3821fad09c73SVivien Didelot 	.get_eee		= mv88e6xxx_get_eee,
3822fad09c73SVivien Didelot 	.get_eeprom_len		= mv88e6xxx_get_eeprom_len,
3823fad09c73SVivien Didelot 	.get_eeprom		= mv88e6xxx_get_eeprom,
3824fad09c73SVivien Didelot 	.set_eeprom		= mv88e6xxx_set_eeprom,
3825fad09c73SVivien Didelot 	.get_regs_len		= mv88e6xxx_get_regs_len,
3826fad09c73SVivien Didelot 	.get_regs		= mv88e6xxx_get_regs,
38272cfcd964SVivien Didelot 	.set_ageing_time	= mv88e6xxx_set_ageing_time,
3828fad09c73SVivien Didelot 	.port_bridge_join	= mv88e6xxx_port_bridge_join,
3829fad09c73SVivien Didelot 	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
3830fad09c73SVivien Didelot 	.port_stp_state_set	= mv88e6xxx_port_stp_state_set,
3831749efcb8SVivien Didelot 	.port_fast_age		= mv88e6xxx_port_fast_age,
3832fad09c73SVivien Didelot 	.port_vlan_filtering	= mv88e6xxx_port_vlan_filtering,
3833fad09c73SVivien Didelot 	.port_vlan_prepare	= mv88e6xxx_port_vlan_prepare,
3834fad09c73SVivien Didelot 	.port_vlan_add		= mv88e6xxx_port_vlan_add,
3835fad09c73SVivien Didelot 	.port_vlan_del		= mv88e6xxx_port_vlan_del,
3836fad09c73SVivien Didelot 	.port_vlan_dump		= mv88e6xxx_port_vlan_dump,
3837fad09c73SVivien Didelot 	.port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
3838fad09c73SVivien Didelot 	.port_fdb_add           = mv88e6xxx_port_fdb_add,
3839fad09c73SVivien Didelot 	.port_fdb_del           = mv88e6xxx_port_fdb_del,
3840fad09c73SVivien Didelot 	.port_fdb_dump          = mv88e6xxx_port_fdb_dump,
38417df8fbddSVivien Didelot 	.port_mdb_prepare       = mv88e6xxx_port_mdb_prepare,
38427df8fbddSVivien Didelot 	.port_mdb_add           = mv88e6xxx_port_mdb_add,
38437df8fbddSVivien Didelot 	.port_mdb_del           = mv88e6xxx_port_mdb_del,
38447df8fbddSVivien Didelot 	.port_mdb_dump          = mv88e6xxx_port_mdb_dump,
3845aec5ac88SVivien Didelot 	.crosschip_bridge_join	= mv88e6xxx_crosschip_bridge_join,
3846aec5ac88SVivien Didelot 	.crosschip_bridge_leave	= mv88e6xxx_crosschip_bridge_leave,
3847fad09c73SVivien Didelot };
3848fad09c73SVivien Didelot 
3849ab3d408dSFlorian Fainelli static struct dsa_switch_driver mv88e6xxx_switch_drv = {
3850ab3d408dSFlorian Fainelli 	.ops			= &mv88e6xxx_switch_ops,
3851ab3d408dSFlorian Fainelli };
3852ab3d408dSFlorian Fainelli 
385355ed0ce0SFlorian Fainelli static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip)
3854fad09c73SVivien Didelot {
3855fad09c73SVivien Didelot 	struct device *dev = chip->dev;
3856fad09c73SVivien Didelot 	struct dsa_switch *ds;
3857fad09c73SVivien Didelot 
385873b1204dSVivien Didelot 	ds = dsa_switch_alloc(dev, mv88e6xxx_num_ports(chip));
3859fad09c73SVivien Didelot 	if (!ds)
3860fad09c73SVivien Didelot 		return -ENOMEM;
3861fad09c73SVivien Didelot 
3862fad09c73SVivien Didelot 	ds->priv = chip;
38639d490b4eSVivien Didelot 	ds->ops = &mv88e6xxx_switch_ops;
38649ff74f24SVivien Didelot 	ds->ageing_time_min = chip->info->age_time_coeff;
38659ff74f24SVivien Didelot 	ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX;
3866fad09c73SVivien Didelot 
3867fad09c73SVivien Didelot 	dev_set_drvdata(dev, ds);
3868fad09c73SVivien Didelot 
386923c9ee49SVivien Didelot 	return dsa_register_switch(ds);
3870fad09c73SVivien Didelot }
3871fad09c73SVivien Didelot 
3872fad09c73SVivien Didelot static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
3873fad09c73SVivien Didelot {
3874fad09c73SVivien Didelot 	dsa_unregister_switch(chip->ds);
3875fad09c73SVivien Didelot }
3876fad09c73SVivien Didelot 
3877fad09c73SVivien Didelot static int mv88e6xxx_probe(struct mdio_device *mdiodev)
3878fad09c73SVivien Didelot {
3879fad09c73SVivien Didelot 	struct device *dev = &mdiodev->dev;
3880fad09c73SVivien Didelot 	struct device_node *np = dev->of_node;
3881fad09c73SVivien Didelot 	const struct mv88e6xxx_info *compat_info;
3882fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3883fad09c73SVivien Didelot 	u32 eeprom_len;
3884fad09c73SVivien Didelot 	int err;
3885fad09c73SVivien Didelot 
3886fad09c73SVivien Didelot 	compat_info = of_device_get_match_data(dev);
3887fad09c73SVivien Didelot 	if (!compat_info)
3888fad09c73SVivien Didelot 		return -EINVAL;
3889fad09c73SVivien Didelot 
3890fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dev);
3891fad09c73SVivien Didelot 	if (!chip)
3892fad09c73SVivien Didelot 		return -ENOMEM;
3893fad09c73SVivien Didelot 
3894fad09c73SVivien Didelot 	chip->info = compat_info;
3895fad09c73SVivien Didelot 
3896fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
3897fad09c73SVivien Didelot 	if (err)
3898fad09c73SVivien Didelot 		return err;
3899fad09c73SVivien Didelot 
3900b4308f04SAndrew Lunn 	chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
3901b4308f04SAndrew Lunn 	if (IS_ERR(chip->reset))
3902b4308f04SAndrew Lunn 		return PTR_ERR(chip->reset);
3903b4308f04SAndrew Lunn 
3904fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3905fad09c73SVivien Didelot 	if (err)
3906fad09c73SVivien Didelot 		return err;
3907fad09c73SVivien Didelot 
3908e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3909e57e5e77SVivien Didelot 
3910ee4dc2e7SVivien Didelot 	if (chip->info->ops->get_eeprom &&
3911fad09c73SVivien Didelot 	    !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3912fad09c73SVivien Didelot 		chip->eeprom_len = eeprom_len;
3913fad09c73SVivien Didelot 
3914dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3915dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3916dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3917fad09c73SVivien Didelot 	if (err)
3918dc30c35bSAndrew Lunn 		goto out;
3919fad09c73SVivien Didelot 
3920dc30c35bSAndrew Lunn 	chip->irq = of_irq_get(np, 0);
3921dc30c35bSAndrew Lunn 	if (chip->irq == -EPROBE_DEFER) {
3922dc30c35bSAndrew Lunn 		err = chip->irq;
3923dc30c35bSAndrew Lunn 		goto out;
3924fad09c73SVivien Didelot 	}
3925fad09c73SVivien Didelot 
3926dc30c35bSAndrew Lunn 	if (chip->irq > 0) {
3927dc30c35bSAndrew Lunn 		/* Has to be performed before the MDIO bus is created,
3928dc30c35bSAndrew Lunn 		 * because the PHYs will link there interrupts to these
3929dc30c35bSAndrew Lunn 		 * interrupt controllers
3930dc30c35bSAndrew Lunn 		 */
3931dc30c35bSAndrew Lunn 		mutex_lock(&chip->reg_lock);
3932dc30c35bSAndrew Lunn 		err = mv88e6xxx_g1_irq_setup(chip);
3933dc30c35bSAndrew Lunn 		mutex_unlock(&chip->reg_lock);
3934dc30c35bSAndrew Lunn 
3935dc30c35bSAndrew Lunn 		if (err)
3936dc30c35bSAndrew Lunn 			goto out;
3937dc30c35bSAndrew Lunn 
3938dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
3939dc30c35bSAndrew Lunn 			err = mv88e6xxx_g2_irq_setup(chip);
3940dc30c35bSAndrew Lunn 			if (err)
3941dc30c35bSAndrew Lunn 				goto out_g1_irq;
3942dc30c35bSAndrew Lunn 		}
3943dc30c35bSAndrew Lunn 	}
3944dc30c35bSAndrew Lunn 
3945a3c53be5SAndrew Lunn 	err = mv88e6xxx_mdios_register(chip, np);
3946dc30c35bSAndrew Lunn 	if (err)
3947dc30c35bSAndrew Lunn 		goto out_g2_irq;
3948dc30c35bSAndrew Lunn 
394955ed0ce0SFlorian Fainelli 	err = mv88e6xxx_register_switch(chip);
3950dc30c35bSAndrew Lunn 	if (err)
3951dc30c35bSAndrew Lunn 		goto out_mdio;
3952dc30c35bSAndrew Lunn 
3953fad09c73SVivien Didelot 	return 0;
3954dc30c35bSAndrew Lunn 
3955dc30c35bSAndrew Lunn out_mdio:
3956a3c53be5SAndrew Lunn 	mv88e6xxx_mdios_unregister(chip);
3957dc30c35bSAndrew Lunn out_g2_irq:
395846712644SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0)
3959dc30c35bSAndrew Lunn 		mv88e6xxx_g2_irq_free(chip);
3960dc30c35bSAndrew Lunn out_g1_irq:
396161f7c3f8SAndrew Lunn 	if (chip->irq > 0) {
396261f7c3f8SAndrew Lunn 		mutex_lock(&chip->reg_lock);
3963dc30c35bSAndrew Lunn 		mv88e6xxx_g1_irq_free(chip);
396461f7c3f8SAndrew Lunn 		mutex_unlock(&chip->reg_lock);
396561f7c3f8SAndrew Lunn 	}
3966dc30c35bSAndrew Lunn out:
3967dc30c35bSAndrew Lunn 	return err;
3968fad09c73SVivien Didelot }
3969fad09c73SVivien Didelot 
3970fad09c73SVivien Didelot static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3971fad09c73SVivien Didelot {
3972fad09c73SVivien Didelot 	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
397304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3974fad09c73SVivien Didelot 
3975930188ceSAndrew Lunn 	mv88e6xxx_phy_destroy(chip);
3976fad09c73SVivien Didelot 	mv88e6xxx_unregister_switch(chip);
3977a3c53be5SAndrew Lunn 	mv88e6xxx_mdios_unregister(chip);
3978dc30c35bSAndrew Lunn 
397946712644SAndrew Lunn 	if (chip->irq > 0) {
3980dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
3981dc30c35bSAndrew Lunn 			mv88e6xxx_g2_irq_free(chip);
3982dc30c35bSAndrew Lunn 		mv88e6xxx_g1_irq_free(chip);
3983fad09c73SVivien Didelot 	}
398446712644SAndrew Lunn }
3985fad09c73SVivien Didelot 
3986fad09c73SVivien Didelot static const struct of_device_id mv88e6xxx_of_match[] = {
3987fad09c73SVivien Didelot 	{
3988fad09c73SVivien Didelot 		.compatible = "marvell,mv88e6085",
3989fad09c73SVivien Didelot 		.data = &mv88e6xxx_table[MV88E6085],
3990fad09c73SVivien Didelot 	},
39911a3b39ecSAndrew Lunn 	{
39921a3b39ecSAndrew Lunn 		.compatible = "marvell,mv88e6190",
39931a3b39ecSAndrew Lunn 		.data = &mv88e6xxx_table[MV88E6190],
39941a3b39ecSAndrew Lunn 	},
3995fad09c73SVivien Didelot 	{ /* sentinel */ },
3996fad09c73SVivien Didelot };
3997fad09c73SVivien Didelot 
3998fad09c73SVivien Didelot MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3999fad09c73SVivien Didelot 
4000fad09c73SVivien Didelot static struct mdio_driver mv88e6xxx_driver = {
4001fad09c73SVivien Didelot 	.probe	= mv88e6xxx_probe,
4002fad09c73SVivien Didelot 	.remove = mv88e6xxx_remove,
4003fad09c73SVivien Didelot 	.mdiodrv.driver = {
4004fad09c73SVivien Didelot 		.name = "mv88e6085",
4005fad09c73SVivien Didelot 		.of_match_table = mv88e6xxx_of_match,
4006fad09c73SVivien Didelot 	},
4007fad09c73SVivien Didelot };
4008fad09c73SVivien Didelot 
4009fad09c73SVivien Didelot static int __init mv88e6xxx_init(void)
4010fad09c73SVivien Didelot {
4011ab3d408dSFlorian Fainelli 	register_switch_driver(&mv88e6xxx_switch_drv);
4012fad09c73SVivien Didelot 	return mdio_driver_register(&mv88e6xxx_driver);
4013fad09c73SVivien Didelot }
4014fad09c73SVivien Didelot module_init(mv88e6xxx_init);
4015fad09c73SVivien Didelot 
4016fad09c73SVivien Didelot static void __exit mv88e6xxx_cleanup(void)
4017fad09c73SVivien Didelot {
4018fad09c73SVivien Didelot 	mdio_driver_unregister(&mv88e6xxx_driver);
4019ab3d408dSFlorian Fainelli 	unregister_switch_driver(&mv88e6xxx_switch_drv);
4020fad09c73SVivien Didelot }
4021fad09c73SVivien Didelot module_exit(mv88e6xxx_cleanup);
4022fad09c73SVivien Didelot 
4023fad09c73SVivien Didelot MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4024fad09c73SVivien Didelot MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4025fad09c73SVivien Didelot MODULE_LICENSE("GPL");
4026