xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/chip.c (revision 61f7c3f8)
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) 2015 CMC Electronics, Inc.
7fad09c73SVivien Didelot  *	Added support for VLAN Table Unit operations
8fad09c73SVivien Didelot  *
9fad09c73SVivien Didelot  * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
10fad09c73SVivien 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>
35fad09c73SVivien Didelot #include <net/switchdev.h>
36ec561276SVivien Didelot 
37fad09c73SVivien Didelot #include "mv88e6xxx.h"
38a935c052SVivien Didelot #include "global1.h"
39ec561276SVivien Didelot #include "global2.h"
4018abed21SVivien Didelot #include "port.h"
41fad09c73SVivien Didelot 
42fad09c73SVivien Didelot static void assert_reg_lock(struct mv88e6xxx_chip *chip)
43fad09c73SVivien Didelot {
44fad09c73SVivien Didelot 	if (unlikely(!mutex_is_locked(&chip->reg_lock))) {
45fad09c73SVivien Didelot 		dev_err(chip->dev, "Switch registers lock not held!\n");
46fad09c73SVivien Didelot 		dump_stack();
47fad09c73SVivien Didelot 	}
48fad09c73SVivien Didelot }
49fad09c73SVivien Didelot 
50fad09c73SVivien Didelot /* The switch ADDR[4:1] configuration pins define the chip SMI device address
51fad09c73SVivien Didelot  * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
52fad09c73SVivien Didelot  *
53fad09c73SVivien Didelot  * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
54fad09c73SVivien Didelot  * is the only device connected to the SMI master. In this mode it responds to
55fad09c73SVivien Didelot  * all 32 possible SMI addresses, and thus maps directly the internal devices.
56fad09c73SVivien Didelot  *
57fad09c73SVivien Didelot  * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
58fad09c73SVivien Didelot  * multiple devices to share the SMI interface. In this mode it responds to only
59fad09c73SVivien Didelot  * 2 registers, used to indirectly access the internal SMI devices.
60fad09c73SVivien Didelot  */
61fad09c73SVivien Didelot 
62fad09c73SVivien Didelot static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
63fad09c73SVivien Didelot 			      int addr, int reg, u16 *val)
64fad09c73SVivien Didelot {
65fad09c73SVivien Didelot 	if (!chip->smi_ops)
66fad09c73SVivien Didelot 		return -EOPNOTSUPP;
67fad09c73SVivien Didelot 
68fad09c73SVivien Didelot 	return chip->smi_ops->read(chip, addr, reg, val);
69fad09c73SVivien Didelot }
70fad09c73SVivien Didelot 
71fad09c73SVivien Didelot static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
72fad09c73SVivien Didelot 			       int addr, int reg, u16 val)
73fad09c73SVivien Didelot {
74fad09c73SVivien Didelot 	if (!chip->smi_ops)
75fad09c73SVivien Didelot 		return -EOPNOTSUPP;
76fad09c73SVivien Didelot 
77fad09c73SVivien Didelot 	return chip->smi_ops->write(chip, addr, reg, val);
78fad09c73SVivien Didelot }
79fad09c73SVivien Didelot 
80fad09c73SVivien Didelot static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
81fad09c73SVivien Didelot 					  int addr, int reg, u16 *val)
82fad09c73SVivien Didelot {
83fad09c73SVivien Didelot 	int ret;
84fad09c73SVivien Didelot 
85fad09c73SVivien Didelot 	ret = mdiobus_read_nested(chip->bus, addr, reg);
86fad09c73SVivien Didelot 	if (ret < 0)
87fad09c73SVivien Didelot 		return ret;
88fad09c73SVivien Didelot 
89fad09c73SVivien Didelot 	*val = ret & 0xffff;
90fad09c73SVivien Didelot 
91fad09c73SVivien Didelot 	return 0;
92fad09c73SVivien Didelot }
93fad09c73SVivien Didelot 
94fad09c73SVivien Didelot static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
95fad09c73SVivien Didelot 					   int addr, int reg, u16 val)
96fad09c73SVivien Didelot {
97fad09c73SVivien Didelot 	int ret;
98fad09c73SVivien Didelot 
99fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, addr, reg, val);
100fad09c73SVivien Didelot 	if (ret < 0)
101fad09c73SVivien Didelot 		return ret;
102fad09c73SVivien Didelot 
103fad09c73SVivien Didelot 	return 0;
104fad09c73SVivien Didelot }
105fad09c73SVivien Didelot 
106c08026abSVivien Didelot static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = {
107fad09c73SVivien Didelot 	.read = mv88e6xxx_smi_single_chip_read,
108fad09c73SVivien Didelot 	.write = mv88e6xxx_smi_single_chip_write,
109fad09c73SVivien Didelot };
110fad09c73SVivien Didelot 
111fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
112fad09c73SVivien Didelot {
113fad09c73SVivien Didelot 	int ret;
114fad09c73SVivien Didelot 	int i;
115fad09c73SVivien Didelot 
116fad09c73SVivien Didelot 	for (i = 0; i < 16; i++) {
117fad09c73SVivien Didelot 		ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
118fad09c73SVivien Didelot 		if (ret < 0)
119fad09c73SVivien Didelot 			return ret;
120fad09c73SVivien Didelot 
121fad09c73SVivien Didelot 		if ((ret & SMI_CMD_BUSY) == 0)
122fad09c73SVivien Didelot 			return 0;
123fad09c73SVivien Didelot 	}
124fad09c73SVivien Didelot 
125fad09c73SVivien Didelot 	return -ETIMEDOUT;
126fad09c73SVivien Didelot }
127fad09c73SVivien Didelot 
128fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
129fad09c73SVivien Didelot 					 int addr, int reg, u16 *val)
130fad09c73SVivien Didelot {
131fad09c73SVivien Didelot 	int ret;
132fad09c73SVivien Didelot 
133fad09c73SVivien Didelot 	/* Wait for the bus to become free. */
134fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
135fad09c73SVivien Didelot 	if (ret < 0)
136fad09c73SVivien Didelot 		return ret;
137fad09c73SVivien Didelot 
138fad09c73SVivien Didelot 	/* Transmit the read command. */
139fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
140fad09c73SVivien Didelot 				   SMI_CMD_OP_22_READ | (addr << 5) | reg);
141fad09c73SVivien Didelot 	if (ret < 0)
142fad09c73SVivien Didelot 		return ret;
143fad09c73SVivien Didelot 
144fad09c73SVivien Didelot 	/* Wait for the read command to complete. */
145fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
146fad09c73SVivien Didelot 	if (ret < 0)
147fad09c73SVivien Didelot 		return ret;
148fad09c73SVivien Didelot 
149fad09c73SVivien Didelot 	/* Read the data. */
150fad09c73SVivien Didelot 	ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
151fad09c73SVivien Didelot 	if (ret < 0)
152fad09c73SVivien Didelot 		return ret;
153fad09c73SVivien Didelot 
154fad09c73SVivien Didelot 	*val = ret & 0xffff;
155fad09c73SVivien Didelot 
156fad09c73SVivien Didelot 	return 0;
157fad09c73SVivien Didelot }
158fad09c73SVivien Didelot 
159fad09c73SVivien Didelot static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
160fad09c73SVivien Didelot 					  int addr, int reg, u16 val)
161fad09c73SVivien Didelot {
162fad09c73SVivien Didelot 	int ret;
163fad09c73SVivien Didelot 
164fad09c73SVivien Didelot 	/* Wait for the bus to become free. */
165fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
166fad09c73SVivien Didelot 	if (ret < 0)
167fad09c73SVivien Didelot 		return ret;
168fad09c73SVivien Didelot 
169fad09c73SVivien Didelot 	/* Transmit the data to write. */
170fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
171fad09c73SVivien Didelot 	if (ret < 0)
172fad09c73SVivien Didelot 		return ret;
173fad09c73SVivien Didelot 
174fad09c73SVivien Didelot 	/* Transmit the write command. */
175fad09c73SVivien Didelot 	ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
176fad09c73SVivien Didelot 				   SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
177fad09c73SVivien Didelot 	if (ret < 0)
178fad09c73SVivien Didelot 		return ret;
179fad09c73SVivien Didelot 
180fad09c73SVivien Didelot 	/* Wait for the write command to complete. */
181fad09c73SVivien Didelot 	ret = mv88e6xxx_smi_multi_chip_wait(chip);
182fad09c73SVivien Didelot 	if (ret < 0)
183fad09c73SVivien Didelot 		return ret;
184fad09c73SVivien Didelot 
185fad09c73SVivien Didelot 	return 0;
186fad09c73SVivien Didelot }
187fad09c73SVivien Didelot 
188c08026abSVivien Didelot static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = {
189fad09c73SVivien Didelot 	.read = mv88e6xxx_smi_multi_chip_read,
190fad09c73SVivien Didelot 	.write = mv88e6xxx_smi_multi_chip_write,
191fad09c73SVivien Didelot };
192fad09c73SVivien Didelot 
193ec561276SVivien Didelot int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
194fad09c73SVivien Didelot {
195fad09c73SVivien Didelot 	int err;
196fad09c73SVivien Didelot 
197fad09c73SVivien Didelot 	assert_reg_lock(chip);
198fad09c73SVivien Didelot 
199fad09c73SVivien Didelot 	err = mv88e6xxx_smi_read(chip, addr, reg, val);
200fad09c73SVivien Didelot 	if (err)
201fad09c73SVivien Didelot 		return err;
202fad09c73SVivien Didelot 
203fad09c73SVivien Didelot 	dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
204fad09c73SVivien Didelot 		addr, reg, *val);
205fad09c73SVivien Didelot 
206fad09c73SVivien Didelot 	return 0;
207fad09c73SVivien Didelot }
208fad09c73SVivien Didelot 
209ec561276SVivien Didelot int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
210fad09c73SVivien Didelot {
211fad09c73SVivien Didelot 	int err;
212fad09c73SVivien Didelot 
213fad09c73SVivien Didelot 	assert_reg_lock(chip);
214fad09c73SVivien Didelot 
215fad09c73SVivien Didelot 	err = mv88e6xxx_smi_write(chip, addr, reg, val);
216fad09c73SVivien Didelot 	if (err)
217fad09c73SVivien Didelot 		return err;
218fad09c73SVivien Didelot 
219fad09c73SVivien Didelot 	dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
220fad09c73SVivien Didelot 		addr, reg, val);
221fad09c73SVivien Didelot 
222fad09c73SVivien Didelot 	return 0;
223fad09c73SVivien Didelot }
224fad09c73SVivien Didelot 
225e57e5e77SVivien Didelot static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
226e57e5e77SVivien Didelot 			      int reg, u16 *val)
227e57e5e77SVivien Didelot {
228e57e5e77SVivien Didelot 	int addr = phy; /* PHY devices addresses start at 0x0 */
229e57e5e77SVivien Didelot 
230b3469dd8SVivien Didelot 	if (!chip->info->ops->phy_read)
231e57e5e77SVivien Didelot 		return -EOPNOTSUPP;
232e57e5e77SVivien Didelot 
233b3469dd8SVivien Didelot 	return chip->info->ops->phy_read(chip, addr, reg, val);
234e57e5e77SVivien Didelot }
235e57e5e77SVivien Didelot 
236e57e5e77SVivien Didelot static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
237e57e5e77SVivien Didelot 			       int reg, u16 val)
238e57e5e77SVivien Didelot {
239e57e5e77SVivien Didelot 	int addr = phy; /* PHY devices addresses start at 0x0 */
240e57e5e77SVivien Didelot 
241b3469dd8SVivien Didelot 	if (!chip->info->ops->phy_write)
242e57e5e77SVivien Didelot 		return -EOPNOTSUPP;
243e57e5e77SVivien Didelot 
244b3469dd8SVivien Didelot 	return chip->info->ops->phy_write(chip, addr, reg, val);
245e57e5e77SVivien Didelot }
246e57e5e77SVivien Didelot 
24709cb7dfdSVivien Didelot static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
24809cb7dfdSVivien Didelot {
24909cb7dfdSVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE))
25009cb7dfdSVivien Didelot 		return -EOPNOTSUPP;
25109cb7dfdSVivien Didelot 
25209cb7dfdSVivien Didelot 	return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
25309cb7dfdSVivien Didelot }
25409cb7dfdSVivien Didelot 
25509cb7dfdSVivien Didelot static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
25609cb7dfdSVivien Didelot {
25709cb7dfdSVivien Didelot 	int err;
25809cb7dfdSVivien Didelot 
25909cb7dfdSVivien Didelot 	/* Restore PHY page Copper 0x0 for access via the registered MDIO bus */
26009cb7dfdSVivien Didelot 	err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER);
26109cb7dfdSVivien Didelot 	if (unlikely(err)) {
26209cb7dfdSVivien Didelot 		dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n",
26309cb7dfdSVivien Didelot 			phy, err);
26409cb7dfdSVivien Didelot 	}
26509cb7dfdSVivien Didelot }
26609cb7dfdSVivien Didelot 
26709cb7dfdSVivien Didelot static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
26809cb7dfdSVivien Didelot 				   u8 page, int reg, u16 *val)
26909cb7dfdSVivien Didelot {
27009cb7dfdSVivien Didelot 	int err;
27109cb7dfdSVivien Didelot 
27209cb7dfdSVivien Didelot 	/* There is no paging for registers 22 */
27309cb7dfdSVivien Didelot 	if (reg == PHY_PAGE)
27409cb7dfdSVivien Didelot 		return -EINVAL;
27509cb7dfdSVivien Didelot 
27609cb7dfdSVivien Didelot 	err = mv88e6xxx_phy_page_get(chip, phy, page);
27709cb7dfdSVivien Didelot 	if (!err) {
27809cb7dfdSVivien Didelot 		err = mv88e6xxx_phy_read(chip, phy, reg, val);
27909cb7dfdSVivien Didelot 		mv88e6xxx_phy_page_put(chip, phy);
28009cb7dfdSVivien Didelot 	}
28109cb7dfdSVivien Didelot 
28209cb7dfdSVivien Didelot 	return err;
28309cb7dfdSVivien Didelot }
28409cb7dfdSVivien Didelot 
28509cb7dfdSVivien Didelot static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
28609cb7dfdSVivien Didelot 				    u8 page, int reg, u16 val)
28709cb7dfdSVivien Didelot {
28809cb7dfdSVivien Didelot 	int err;
28909cb7dfdSVivien Didelot 
29009cb7dfdSVivien Didelot 	/* There is no paging for registers 22 */
29109cb7dfdSVivien Didelot 	if (reg == PHY_PAGE)
29209cb7dfdSVivien Didelot 		return -EINVAL;
29309cb7dfdSVivien Didelot 
29409cb7dfdSVivien Didelot 	err = mv88e6xxx_phy_page_get(chip, phy, page);
29509cb7dfdSVivien Didelot 	if (!err) {
29609cb7dfdSVivien Didelot 		err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
29709cb7dfdSVivien Didelot 		mv88e6xxx_phy_page_put(chip, phy);
29809cb7dfdSVivien Didelot 	}
29909cb7dfdSVivien Didelot 
30009cb7dfdSVivien Didelot 	return err;
30109cb7dfdSVivien Didelot }
30209cb7dfdSVivien Didelot 
30309cb7dfdSVivien Didelot static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
30409cb7dfdSVivien Didelot {
30509cb7dfdSVivien Didelot 	return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
30609cb7dfdSVivien Didelot 				       reg, val);
30709cb7dfdSVivien Didelot }
30809cb7dfdSVivien Didelot 
30909cb7dfdSVivien Didelot static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
31009cb7dfdSVivien Didelot {
31109cb7dfdSVivien Didelot 	return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
31209cb7dfdSVivien Didelot 					reg, val);
31309cb7dfdSVivien Didelot }
31409cb7dfdSVivien Didelot 
315dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_mask(struct irq_data *d)
316dc30c35bSAndrew Lunn {
317dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
318dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
319dc30c35bSAndrew Lunn 
320dc30c35bSAndrew Lunn 	chip->g1_irq.masked |= (1 << n);
321dc30c35bSAndrew Lunn }
322dc30c35bSAndrew Lunn 
323dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
324dc30c35bSAndrew Lunn {
325dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
326dc30c35bSAndrew Lunn 	unsigned int n = d->hwirq;
327dc30c35bSAndrew Lunn 
328dc30c35bSAndrew Lunn 	chip->g1_irq.masked &= ~(1 << n);
329dc30c35bSAndrew Lunn }
330dc30c35bSAndrew Lunn 
331dc30c35bSAndrew Lunn static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
332dc30c35bSAndrew Lunn {
333dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = dev_id;
334dc30c35bSAndrew Lunn 	unsigned int nhandled = 0;
335dc30c35bSAndrew Lunn 	unsigned int sub_irq;
336dc30c35bSAndrew Lunn 	unsigned int n;
337dc30c35bSAndrew Lunn 	u16 reg;
338dc30c35bSAndrew Lunn 	int err;
339dc30c35bSAndrew Lunn 
340dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
341dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
342dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
343dc30c35bSAndrew Lunn 
344dc30c35bSAndrew Lunn 	if (err)
345dc30c35bSAndrew Lunn 		goto out;
346dc30c35bSAndrew Lunn 
347dc30c35bSAndrew Lunn 	for (n = 0; n < chip->g1_irq.nirqs; ++n) {
348dc30c35bSAndrew Lunn 		if (reg & (1 << n)) {
349dc30c35bSAndrew Lunn 			sub_irq = irq_find_mapping(chip->g1_irq.domain, n);
350dc30c35bSAndrew Lunn 			handle_nested_irq(sub_irq);
351dc30c35bSAndrew Lunn 			++nhandled;
352dc30c35bSAndrew Lunn 		}
353dc30c35bSAndrew Lunn 	}
354dc30c35bSAndrew Lunn out:
355dc30c35bSAndrew Lunn 	return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
356dc30c35bSAndrew Lunn }
357dc30c35bSAndrew Lunn 
358dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
359dc30c35bSAndrew Lunn {
360dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
361dc30c35bSAndrew Lunn 
362dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
363dc30c35bSAndrew Lunn }
364dc30c35bSAndrew Lunn 
365dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d)
366dc30c35bSAndrew Lunn {
367dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
368dc30c35bSAndrew Lunn 	u16 mask = GENMASK(chip->g1_irq.nirqs, 0);
369dc30c35bSAndrew Lunn 	u16 reg;
370dc30c35bSAndrew Lunn 	int err;
371dc30c35bSAndrew Lunn 
372dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
373dc30c35bSAndrew Lunn 	if (err)
374dc30c35bSAndrew Lunn 		goto out;
375dc30c35bSAndrew Lunn 
376dc30c35bSAndrew Lunn 	reg &= ~mask;
377dc30c35bSAndrew Lunn 	reg |= (~chip->g1_irq.masked & mask);
378dc30c35bSAndrew Lunn 
379dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
380dc30c35bSAndrew Lunn 	if (err)
381dc30c35bSAndrew Lunn 		goto out;
382dc30c35bSAndrew Lunn 
383dc30c35bSAndrew Lunn out:
384dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
385dc30c35bSAndrew Lunn }
386dc30c35bSAndrew Lunn 
387dc30c35bSAndrew Lunn static struct irq_chip mv88e6xxx_g1_irq_chip = {
388dc30c35bSAndrew Lunn 	.name			= "mv88e6xxx-g1",
389dc30c35bSAndrew Lunn 	.irq_mask		= mv88e6xxx_g1_irq_mask,
390dc30c35bSAndrew Lunn 	.irq_unmask		= mv88e6xxx_g1_irq_unmask,
391dc30c35bSAndrew Lunn 	.irq_bus_lock		= mv88e6xxx_g1_irq_bus_lock,
392dc30c35bSAndrew Lunn 	.irq_bus_sync_unlock	= mv88e6xxx_g1_irq_bus_sync_unlock,
393dc30c35bSAndrew Lunn };
394dc30c35bSAndrew Lunn 
395dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d,
396dc30c35bSAndrew Lunn 				       unsigned int irq,
397dc30c35bSAndrew Lunn 				       irq_hw_number_t hwirq)
398dc30c35bSAndrew Lunn {
399dc30c35bSAndrew Lunn 	struct mv88e6xxx_chip *chip = d->host_data;
400dc30c35bSAndrew Lunn 
401dc30c35bSAndrew Lunn 	irq_set_chip_data(irq, d->host_data);
402dc30c35bSAndrew Lunn 	irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq);
403dc30c35bSAndrew Lunn 	irq_set_noprobe(irq);
404dc30c35bSAndrew Lunn 
405dc30c35bSAndrew Lunn 	return 0;
406dc30c35bSAndrew Lunn }
407dc30c35bSAndrew Lunn 
408dc30c35bSAndrew Lunn static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
409dc30c35bSAndrew Lunn 	.map	= mv88e6xxx_g1_irq_domain_map,
410dc30c35bSAndrew Lunn 	.xlate	= irq_domain_xlate_twocell,
411dc30c35bSAndrew Lunn };
412dc30c35bSAndrew Lunn 
413dc30c35bSAndrew Lunn static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
414dc30c35bSAndrew Lunn {
415dc30c35bSAndrew Lunn 	int irq, virq;
4163460a577SAndrew Lunn 	u16 mask;
4173460a577SAndrew Lunn 
4183460a577SAndrew Lunn 	mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask);
4193460a577SAndrew Lunn 	mask |= GENMASK(chip->g1_irq.nirqs, 0);
4203460a577SAndrew Lunn 	mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
4213460a577SAndrew Lunn 
4223460a577SAndrew Lunn 	free_irq(chip->irq, chip);
423dc30c35bSAndrew Lunn 
424dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
425a3db3d3aSAndrew Lunn 		virq = irq_find_mapping(chip->g1_irq.domain, irq);
426dc30c35bSAndrew Lunn 		irq_dispose_mapping(virq);
427dc30c35bSAndrew Lunn 	}
428dc30c35bSAndrew Lunn 
429a3db3d3aSAndrew Lunn 	irq_domain_remove(chip->g1_irq.domain);
430dc30c35bSAndrew Lunn }
431dc30c35bSAndrew Lunn 
432dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
433dc30c35bSAndrew Lunn {
4343dd0ef05SAndrew Lunn 	int err, irq, virq;
4353dd0ef05SAndrew Lunn 	u16 reg, mask;
436dc30c35bSAndrew Lunn 
437dc30c35bSAndrew Lunn 	chip->g1_irq.nirqs = chip->info->g1_irqs;
438dc30c35bSAndrew Lunn 	chip->g1_irq.domain = irq_domain_add_simple(
439dc30c35bSAndrew Lunn 		NULL, chip->g1_irq.nirqs, 0,
440dc30c35bSAndrew Lunn 		&mv88e6xxx_g1_irq_domain_ops, chip);
441dc30c35bSAndrew Lunn 	if (!chip->g1_irq.domain)
442dc30c35bSAndrew Lunn 		return -ENOMEM;
443dc30c35bSAndrew Lunn 
444dc30c35bSAndrew Lunn 	for (irq = 0; irq < chip->g1_irq.nirqs; irq++)
445dc30c35bSAndrew Lunn 		irq_create_mapping(chip->g1_irq.domain, irq);
446dc30c35bSAndrew Lunn 
447dc30c35bSAndrew Lunn 	chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
448dc30c35bSAndrew Lunn 	chip->g1_irq.masked = ~0;
449dc30c35bSAndrew Lunn 
4503dd0ef05SAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &mask);
451dc30c35bSAndrew Lunn 	if (err)
4523dd0ef05SAndrew Lunn 		goto out_mapping;
453dc30c35bSAndrew Lunn 
4543dd0ef05SAndrew Lunn 	mask &= ~GENMASK(chip->g1_irq.nirqs, 0);
455dc30c35bSAndrew Lunn 
4563dd0ef05SAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
457dc30c35bSAndrew Lunn 	if (err)
4583dd0ef05SAndrew Lunn 		goto out_disable;
459dc30c35bSAndrew Lunn 
460dc30c35bSAndrew Lunn 	/* Reading the interrupt status clears (most of) them */
461dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
462dc30c35bSAndrew Lunn 	if (err)
4633dd0ef05SAndrew Lunn 		goto out_disable;
464dc30c35bSAndrew Lunn 
465dc30c35bSAndrew Lunn 	err = request_threaded_irq(chip->irq, NULL,
466dc30c35bSAndrew Lunn 				   mv88e6xxx_g1_irq_thread_fn,
467dc30c35bSAndrew Lunn 				   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
468dc30c35bSAndrew Lunn 				   dev_name(chip->dev), chip);
469dc30c35bSAndrew Lunn 	if (err)
4703dd0ef05SAndrew Lunn 		goto out_disable;
471dc30c35bSAndrew Lunn 
472dc30c35bSAndrew Lunn 	return 0;
473dc30c35bSAndrew Lunn 
4743dd0ef05SAndrew Lunn out_disable:
4753dd0ef05SAndrew Lunn 	mask |= GENMASK(chip->g1_irq.nirqs, 0);
4763dd0ef05SAndrew Lunn 	mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, mask);
4773dd0ef05SAndrew Lunn 
4783dd0ef05SAndrew Lunn out_mapping:
4793dd0ef05SAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
4803dd0ef05SAndrew Lunn 		virq = irq_find_mapping(chip->g1_irq.domain, irq);
4813dd0ef05SAndrew Lunn 		irq_dispose_mapping(virq);
4823dd0ef05SAndrew Lunn 	}
4833dd0ef05SAndrew Lunn 
4843dd0ef05SAndrew Lunn 	irq_domain_remove(chip->g1_irq.domain);
485dc30c35bSAndrew Lunn 
486dc30c35bSAndrew Lunn 	return err;
487dc30c35bSAndrew Lunn }
488dc30c35bSAndrew Lunn 
489ec561276SVivien Didelot int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
4902d79af6eSVivien Didelot {
4916441e669SAndrew Lunn 	int i;
4922d79af6eSVivien Didelot 
4936441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
4942d79af6eSVivien Didelot 		u16 val;
4952d79af6eSVivien Didelot 		int err;
4962d79af6eSVivien Didelot 
4972d79af6eSVivien Didelot 		err = mv88e6xxx_read(chip, addr, reg, &val);
4982d79af6eSVivien Didelot 		if (err)
4992d79af6eSVivien Didelot 			return err;
5002d79af6eSVivien Didelot 
5012d79af6eSVivien Didelot 		if (!(val & mask))
5022d79af6eSVivien Didelot 			return 0;
5032d79af6eSVivien Didelot 
5042d79af6eSVivien Didelot 		usleep_range(1000, 2000);
5052d79af6eSVivien Didelot 	}
5062d79af6eSVivien Didelot 
50730853553SAndrew Lunn 	dev_err(chip->dev, "Timeout while waiting for switch\n");
5082d79af6eSVivien Didelot 	return -ETIMEDOUT;
5092d79af6eSVivien Didelot }
5102d79af6eSVivien Didelot 
511f22ab641SVivien Didelot /* Indirect write to single pointer-data register with an Update bit */
512ec561276SVivien Didelot int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
513f22ab641SVivien Didelot {
514f22ab641SVivien Didelot 	u16 val;
5150f02b4f7SAndrew Lunn 	int err;
516f22ab641SVivien Didelot 
517f22ab641SVivien Didelot 	/* Wait until the previous operation is completed */
5180f02b4f7SAndrew Lunn 	err = mv88e6xxx_wait(chip, addr, reg, BIT(15));
519f22ab641SVivien Didelot 	if (err)
520f22ab641SVivien Didelot 		return err;
521f22ab641SVivien Didelot 
522f22ab641SVivien Didelot 	/* Set the Update bit to trigger a write operation */
523f22ab641SVivien Didelot 	val = BIT(15) | update;
524f22ab641SVivien Didelot 
525f22ab641SVivien Didelot 	return mv88e6xxx_write(chip, addr, reg, val);
526f22ab641SVivien Didelot }
527f22ab641SVivien Didelot 
528a935c052SVivien Didelot static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
529fad09c73SVivien Didelot {
530fad09c73SVivien Didelot 	u16 val;
531a935c052SVivien Didelot 	int i, err;
532fad09c73SVivien Didelot 
533a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
534fad09c73SVivien Didelot 	if (err)
535fad09c73SVivien Didelot 		return err;
536fad09c73SVivien Didelot 
537a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
538a935c052SVivien Didelot 				 val & ~GLOBAL_CONTROL_PPU_ENABLE);
539a935c052SVivien Didelot 	if (err)
540a935c052SVivien Didelot 		return err;
541fad09c73SVivien Didelot 
5426441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
543a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
544a935c052SVivien Didelot 		if (err)
545a935c052SVivien Didelot 			return err;
546fad09c73SVivien Didelot 
547fad09c73SVivien Didelot 		usleep_range(1000, 2000);
548a935c052SVivien Didelot 		if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING)
549fad09c73SVivien Didelot 			return 0;
550fad09c73SVivien Didelot 	}
551fad09c73SVivien Didelot 
552fad09c73SVivien Didelot 	return -ETIMEDOUT;
553fad09c73SVivien Didelot }
554fad09c73SVivien Didelot 
555fad09c73SVivien Didelot static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
556fad09c73SVivien Didelot {
557a935c052SVivien Didelot 	u16 val;
558a935c052SVivien Didelot 	int i, err;
559fad09c73SVivien Didelot 
560a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
561a935c052SVivien Didelot 	if (err)
562a935c052SVivien Didelot 		return err;
563fad09c73SVivien Didelot 
564a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
565a935c052SVivien Didelot 				 val | GLOBAL_CONTROL_PPU_ENABLE);
566fad09c73SVivien Didelot 	if (err)
567fad09c73SVivien Didelot 		return err;
568fad09c73SVivien Didelot 
5696441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
570a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
571a935c052SVivien Didelot 		if (err)
572a935c052SVivien Didelot 			return err;
573fad09c73SVivien Didelot 
574fad09c73SVivien Didelot 		usleep_range(1000, 2000);
575a935c052SVivien Didelot 		if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING)
576fad09c73SVivien Didelot 			return 0;
577fad09c73SVivien Didelot 	}
578fad09c73SVivien Didelot 
579fad09c73SVivien Didelot 	return -ETIMEDOUT;
580fad09c73SVivien Didelot }
581fad09c73SVivien Didelot 
582fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
583fad09c73SVivien Didelot {
584fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
585fad09c73SVivien Didelot 
586fad09c73SVivien Didelot 	chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
587fad09c73SVivien Didelot 
588fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
589fad09c73SVivien Didelot 
590fad09c73SVivien Didelot 	if (mutex_trylock(&chip->ppu_mutex)) {
591fad09c73SVivien Didelot 		if (mv88e6xxx_ppu_enable(chip) == 0)
592fad09c73SVivien Didelot 			chip->ppu_disabled = 0;
593fad09c73SVivien Didelot 		mutex_unlock(&chip->ppu_mutex);
594fad09c73SVivien Didelot 	}
595fad09c73SVivien Didelot 
596fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
597fad09c73SVivien Didelot }
598fad09c73SVivien Didelot 
599fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
600fad09c73SVivien Didelot {
601fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip = (void *)_ps;
602fad09c73SVivien Didelot 
603fad09c73SVivien Didelot 	schedule_work(&chip->ppu_work);
604fad09c73SVivien Didelot }
605fad09c73SVivien Didelot 
606fad09c73SVivien Didelot static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
607fad09c73SVivien Didelot {
608fad09c73SVivien Didelot 	int ret;
609fad09c73SVivien Didelot 
610fad09c73SVivien Didelot 	mutex_lock(&chip->ppu_mutex);
611fad09c73SVivien Didelot 
612fad09c73SVivien Didelot 	/* If the PHY polling unit is enabled, disable it so that
613fad09c73SVivien Didelot 	 * we can access the PHY registers.  If it was already
614fad09c73SVivien Didelot 	 * disabled, cancel the timer that is going to re-enable
615fad09c73SVivien Didelot 	 * it.
616fad09c73SVivien Didelot 	 */
617fad09c73SVivien Didelot 	if (!chip->ppu_disabled) {
618fad09c73SVivien Didelot 		ret = mv88e6xxx_ppu_disable(chip);
619fad09c73SVivien Didelot 		if (ret < 0) {
620fad09c73SVivien Didelot 			mutex_unlock(&chip->ppu_mutex);
621fad09c73SVivien Didelot 			return ret;
622fad09c73SVivien Didelot 		}
623fad09c73SVivien Didelot 		chip->ppu_disabled = 1;
624fad09c73SVivien Didelot 	} else {
625fad09c73SVivien Didelot 		del_timer(&chip->ppu_timer);
626fad09c73SVivien Didelot 		ret = 0;
627fad09c73SVivien Didelot 	}
628fad09c73SVivien Didelot 
629fad09c73SVivien Didelot 	return ret;
630fad09c73SVivien Didelot }
631fad09c73SVivien Didelot 
632fad09c73SVivien Didelot static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
633fad09c73SVivien Didelot {
634fad09c73SVivien Didelot 	/* Schedule a timer to re-enable the PHY polling unit. */
635fad09c73SVivien Didelot 	mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
636fad09c73SVivien Didelot 	mutex_unlock(&chip->ppu_mutex);
637fad09c73SVivien Didelot }
638fad09c73SVivien Didelot 
639fad09c73SVivien Didelot static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
640fad09c73SVivien Didelot {
641fad09c73SVivien Didelot 	mutex_init(&chip->ppu_mutex);
642fad09c73SVivien Didelot 	INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
64368497a87SWei Yongjun 	setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer,
64468497a87SWei Yongjun 		    (unsigned long)chip);
645fad09c73SVivien Didelot }
646fad09c73SVivien Didelot 
647930188ceSAndrew Lunn static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip)
648930188ceSAndrew Lunn {
649930188ceSAndrew Lunn 	del_timer_sync(&chip->ppu_timer);
650930188ceSAndrew Lunn }
651930188ceSAndrew Lunn 
652e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
653e57e5e77SVivien Didelot 				  int reg, u16 *val)
654fad09c73SVivien Didelot {
655e57e5e77SVivien Didelot 	int err;
656fad09c73SVivien Didelot 
657e57e5e77SVivien Didelot 	err = mv88e6xxx_ppu_access_get(chip);
658e57e5e77SVivien Didelot 	if (!err) {
659e57e5e77SVivien Didelot 		err = mv88e6xxx_read(chip, addr, reg, val);
660fad09c73SVivien Didelot 		mv88e6xxx_ppu_access_put(chip);
661fad09c73SVivien Didelot 	}
662fad09c73SVivien Didelot 
663e57e5e77SVivien Didelot 	return err;
664fad09c73SVivien Didelot }
665fad09c73SVivien Didelot 
666e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
667e57e5e77SVivien Didelot 				   int reg, u16 val)
668fad09c73SVivien Didelot {
669e57e5e77SVivien Didelot 	int err;
670fad09c73SVivien Didelot 
671e57e5e77SVivien Didelot 	err = mv88e6xxx_ppu_access_get(chip);
672e57e5e77SVivien Didelot 	if (!err) {
673e57e5e77SVivien Didelot 		err = mv88e6xxx_write(chip, addr, reg, val);
674fad09c73SVivien Didelot 		mv88e6xxx_ppu_access_put(chip);
675fad09c73SVivien Didelot 	}
676fad09c73SVivien Didelot 
677e57e5e77SVivien Didelot 	return err;
678fad09c73SVivien Didelot }
679fad09c73SVivien Didelot 
680fad09c73SVivien Didelot static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
681fad09c73SVivien Didelot {
682fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6065;
683fad09c73SVivien Didelot }
684fad09c73SVivien Didelot 
685fad09c73SVivien Didelot static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
686fad09c73SVivien Didelot {
687fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6095;
688fad09c73SVivien Didelot }
689fad09c73SVivien Didelot 
690fad09c73SVivien Didelot static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
691fad09c73SVivien Didelot {
692fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6097;
693fad09c73SVivien Didelot }
694fad09c73SVivien Didelot 
695fad09c73SVivien Didelot static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
696fad09c73SVivien Didelot {
697fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6165;
698fad09c73SVivien Didelot }
699fad09c73SVivien Didelot 
700fad09c73SVivien Didelot static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
701fad09c73SVivien Didelot {
702fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6185;
703fad09c73SVivien Didelot }
704fad09c73SVivien Didelot 
705fad09c73SVivien Didelot static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
706fad09c73SVivien Didelot {
707fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6320;
708fad09c73SVivien Didelot }
709fad09c73SVivien Didelot 
710fad09c73SVivien Didelot static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
711fad09c73SVivien Didelot {
712fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6351;
713fad09c73SVivien Didelot }
714fad09c73SVivien Didelot 
715fad09c73SVivien Didelot static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
716fad09c73SVivien Didelot {
717fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6352;
718fad09c73SVivien Didelot }
719fad09c73SVivien Didelot 
720d78343d2SVivien Didelot static int mv88e6xxx_port_setup_mac(struct mv88e6xxx_chip *chip, int port,
721d78343d2SVivien Didelot 				    int link, int speed, int duplex,
722d78343d2SVivien Didelot 				    phy_interface_t mode)
723d78343d2SVivien Didelot {
724d78343d2SVivien Didelot 	int err;
725d78343d2SVivien Didelot 
726d78343d2SVivien Didelot 	if (!chip->info->ops->port_set_link)
727d78343d2SVivien Didelot 		return 0;
728d78343d2SVivien Didelot 
729d78343d2SVivien Didelot 	/* Port's MAC control must not be changed unless the link is down */
730d78343d2SVivien Didelot 	err = chip->info->ops->port_set_link(chip, port, 0);
731d78343d2SVivien Didelot 	if (err)
732d78343d2SVivien Didelot 		return err;
733d78343d2SVivien Didelot 
734d78343d2SVivien Didelot 	if (chip->info->ops->port_set_speed) {
735d78343d2SVivien Didelot 		err = chip->info->ops->port_set_speed(chip, port, speed);
736d78343d2SVivien Didelot 		if (err && err != -EOPNOTSUPP)
737d78343d2SVivien Didelot 			goto restore_link;
738d78343d2SVivien Didelot 	}
739d78343d2SVivien Didelot 
740d78343d2SVivien Didelot 	if (chip->info->ops->port_set_duplex) {
741d78343d2SVivien Didelot 		err = chip->info->ops->port_set_duplex(chip, port, duplex);
742d78343d2SVivien Didelot 		if (err && err != -EOPNOTSUPP)
743d78343d2SVivien Didelot 			goto restore_link;
744d78343d2SVivien Didelot 	}
745d78343d2SVivien Didelot 
746d78343d2SVivien Didelot 	if (chip->info->ops->port_set_rgmii_delay) {
747d78343d2SVivien Didelot 		err = chip->info->ops->port_set_rgmii_delay(chip, port, mode);
748d78343d2SVivien Didelot 		if (err && err != -EOPNOTSUPP)
749d78343d2SVivien Didelot 			goto restore_link;
750d78343d2SVivien Didelot 	}
751d78343d2SVivien Didelot 
752d78343d2SVivien Didelot 	err = 0;
753d78343d2SVivien Didelot restore_link:
754d78343d2SVivien Didelot 	if (chip->info->ops->port_set_link(chip, port, link))
755d78343d2SVivien Didelot 		netdev_err(chip->ds->ports[port].netdev,
756d78343d2SVivien Didelot 			   "failed to restore MAC's link\n");
757d78343d2SVivien Didelot 
758d78343d2SVivien Didelot 	return err;
759d78343d2SVivien Didelot }
760d78343d2SVivien Didelot 
761fad09c73SVivien Didelot /* We expect the switch to perform auto negotiation if there is a real
762fad09c73SVivien Didelot  * phy. However, in the case of a fixed link phy, we force the port
763fad09c73SVivien Didelot  * settings from the fixed link settings.
764fad09c73SVivien Didelot  */
765fad09c73SVivien Didelot static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
766fad09c73SVivien Didelot 				  struct phy_device *phydev)
767fad09c73SVivien Didelot {
76804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
7690e7b9925SAndrew Lunn 	int err;
770fad09c73SVivien Didelot 
771fad09c73SVivien Didelot 	if (!phy_is_pseudo_fixed_link(phydev))
772fad09c73SVivien Didelot 		return;
773fad09c73SVivien Didelot 
774fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
775d78343d2SVivien Didelot 	err = mv88e6xxx_port_setup_mac(chip, port, phydev->link, phydev->speed,
776d78343d2SVivien Didelot 				       phydev->duplex, phydev->interface);
777fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
778d78343d2SVivien Didelot 
779d78343d2SVivien Didelot 	if (err && err != -EOPNOTSUPP)
780d78343d2SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to configure MAC\n");
781fad09c73SVivien Didelot }
782fad09c73SVivien Didelot 
783fad09c73SVivien Didelot static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
784fad09c73SVivien Didelot {
785a935c052SVivien Didelot 	u16 val;
786a935c052SVivien Didelot 	int i, err;
787fad09c73SVivien Didelot 
788fad09c73SVivien Didelot 	for (i = 0; i < 10; i++) {
789a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val);
790a935c052SVivien Didelot 		if ((val & GLOBAL_STATS_OP_BUSY) == 0)
791fad09c73SVivien Didelot 			return 0;
792fad09c73SVivien Didelot 	}
793fad09c73SVivien Didelot 
794fad09c73SVivien Didelot 	return -ETIMEDOUT;
795fad09c73SVivien Didelot }
796fad09c73SVivien Didelot 
797fad09c73SVivien Didelot static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
798fad09c73SVivien Didelot {
799a935c052SVivien Didelot 	int err;
800fad09c73SVivien Didelot 
801fad09c73SVivien Didelot 	if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
802fad09c73SVivien Didelot 		port = (port + 1) << 5;
803fad09c73SVivien Didelot 
804fad09c73SVivien Didelot 	/* Snapshot the hardware statistics counters for this port. */
805a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
806fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_CAPTURE_PORT |
807fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_HIST_RX_TX | port);
808a935c052SVivien Didelot 	if (err)
809a935c052SVivien Didelot 		return err;
810fad09c73SVivien Didelot 
811fad09c73SVivien Didelot 	/* Wait for the snapshotting to complete. */
812a935c052SVivien Didelot 	return _mv88e6xxx_stats_wait(chip);
813fad09c73SVivien Didelot }
814fad09c73SVivien Didelot 
815fad09c73SVivien Didelot static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
816fad09c73SVivien Didelot 				  int stat, u32 *val)
817fad09c73SVivien Didelot {
818a935c052SVivien Didelot 	u32 value;
819a935c052SVivien Didelot 	u16 reg;
820a935c052SVivien Didelot 	int err;
821fad09c73SVivien Didelot 
822fad09c73SVivien Didelot 	*val = 0;
823fad09c73SVivien Didelot 
824a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
825fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_READ_CAPTURED |
826fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_HIST_RX_TX | stat);
827a935c052SVivien Didelot 	if (err)
828fad09c73SVivien Didelot 		return;
829fad09c73SVivien Didelot 
830a935c052SVivien Didelot 	err = _mv88e6xxx_stats_wait(chip);
831a935c052SVivien Didelot 	if (err)
832fad09c73SVivien Didelot 		return;
833fad09c73SVivien Didelot 
834a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, &reg);
835a935c052SVivien Didelot 	if (err)
836fad09c73SVivien Didelot 		return;
837fad09c73SVivien Didelot 
838a935c052SVivien Didelot 	value = reg << 16;
839fad09c73SVivien Didelot 
840a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, &reg);
841a935c052SVivien Didelot 	if (err)
842fad09c73SVivien Didelot 		return;
843fad09c73SVivien Didelot 
844a935c052SVivien Didelot 	*val = value | reg;
845fad09c73SVivien Didelot }
846fad09c73SVivien Didelot 
847fad09c73SVivien Didelot static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
848fad09c73SVivien Didelot 	{ "in_good_octets",	8, 0x00, BANK0, },
849fad09c73SVivien Didelot 	{ "in_bad_octets",	4, 0x02, BANK0, },
850fad09c73SVivien Didelot 	{ "in_unicast",		4, 0x04, BANK0, },
851fad09c73SVivien Didelot 	{ "in_broadcasts",	4, 0x06, BANK0, },
852fad09c73SVivien Didelot 	{ "in_multicasts",	4, 0x07, BANK0, },
853fad09c73SVivien Didelot 	{ "in_pause",		4, 0x16, BANK0, },
854fad09c73SVivien Didelot 	{ "in_undersize",	4, 0x18, BANK0, },
855fad09c73SVivien Didelot 	{ "in_fragments",	4, 0x19, BANK0, },
856fad09c73SVivien Didelot 	{ "in_oversize",	4, 0x1a, BANK0, },
857fad09c73SVivien Didelot 	{ "in_jabber",		4, 0x1b, BANK0, },
858fad09c73SVivien Didelot 	{ "in_rx_error",	4, 0x1c, BANK0, },
859fad09c73SVivien Didelot 	{ "in_fcs_error",	4, 0x1d, BANK0, },
860fad09c73SVivien Didelot 	{ "out_octets",		8, 0x0e, BANK0, },
861fad09c73SVivien Didelot 	{ "out_unicast",	4, 0x10, BANK0, },
862fad09c73SVivien Didelot 	{ "out_broadcasts",	4, 0x13, BANK0, },
863fad09c73SVivien Didelot 	{ "out_multicasts",	4, 0x12, BANK0, },
864fad09c73SVivien Didelot 	{ "out_pause",		4, 0x15, BANK0, },
865fad09c73SVivien Didelot 	{ "excessive",		4, 0x11, BANK0, },
866fad09c73SVivien Didelot 	{ "collisions",		4, 0x1e, BANK0, },
867fad09c73SVivien Didelot 	{ "deferred",		4, 0x05, BANK0, },
868fad09c73SVivien Didelot 	{ "single",		4, 0x14, BANK0, },
869fad09c73SVivien Didelot 	{ "multiple",		4, 0x17, BANK0, },
870fad09c73SVivien Didelot 	{ "out_fcs_error",	4, 0x03, BANK0, },
871fad09c73SVivien Didelot 	{ "late",		4, 0x1f, BANK0, },
872fad09c73SVivien Didelot 	{ "hist_64bytes",	4, 0x08, BANK0, },
873fad09c73SVivien Didelot 	{ "hist_65_127bytes",	4, 0x09, BANK0, },
874fad09c73SVivien Didelot 	{ "hist_128_255bytes",	4, 0x0a, BANK0, },
875fad09c73SVivien Didelot 	{ "hist_256_511bytes",	4, 0x0b, BANK0, },
876fad09c73SVivien Didelot 	{ "hist_512_1023bytes", 4, 0x0c, BANK0, },
877fad09c73SVivien Didelot 	{ "hist_1024_max_bytes", 4, 0x0d, BANK0, },
878fad09c73SVivien Didelot 	{ "sw_in_discards",	4, 0x10, PORT, },
879fad09c73SVivien Didelot 	{ "sw_in_filtered",	2, 0x12, PORT, },
880fad09c73SVivien Didelot 	{ "sw_out_filtered",	2, 0x13, PORT, },
881fad09c73SVivien Didelot 	{ "in_discards",	4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
882fad09c73SVivien Didelot 	{ "in_filtered",	4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
883fad09c73SVivien Didelot 	{ "in_accepted",	4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
884fad09c73SVivien Didelot 	{ "in_bad_accepted",	4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
885fad09c73SVivien Didelot 	{ "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
886fad09c73SVivien Didelot 	{ "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
887fad09c73SVivien Didelot 	{ "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
888fad09c73SVivien Didelot 	{ "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
889fad09c73SVivien Didelot 	{ "tcam_counter_0",	4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
890fad09c73SVivien Didelot 	{ "tcam_counter_1",	4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
891fad09c73SVivien Didelot 	{ "tcam_counter_2",	4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
892fad09c73SVivien Didelot 	{ "tcam_counter_3",	4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
893fad09c73SVivien Didelot 	{ "in_da_unknown",	4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
894fad09c73SVivien Didelot 	{ "in_management",	4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
895fad09c73SVivien Didelot 	{ "out_queue_0",	4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
896fad09c73SVivien Didelot 	{ "out_queue_1",	4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
897fad09c73SVivien Didelot 	{ "out_queue_2",	4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
898fad09c73SVivien Didelot 	{ "out_queue_3",	4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
899fad09c73SVivien Didelot 	{ "out_queue_4",	4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
900fad09c73SVivien Didelot 	{ "out_queue_5",	4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
901fad09c73SVivien Didelot 	{ "out_queue_6",	4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
902fad09c73SVivien Didelot 	{ "out_queue_7",	4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
903fad09c73SVivien Didelot 	{ "out_cut_through",	4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
904fad09c73SVivien Didelot 	{ "out_octets_a",	4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
905fad09c73SVivien Didelot 	{ "out_octets_b",	4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
906fad09c73SVivien Didelot 	{ "out_management",	4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
907fad09c73SVivien Didelot };
908fad09c73SVivien Didelot 
909fad09c73SVivien Didelot static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
910fad09c73SVivien Didelot 			       struct mv88e6xxx_hw_stat *stat)
911fad09c73SVivien Didelot {
912fad09c73SVivien Didelot 	switch (stat->type) {
913fad09c73SVivien Didelot 	case BANK0:
914fad09c73SVivien Didelot 		return true;
915fad09c73SVivien Didelot 	case BANK1:
916fad09c73SVivien Didelot 		return mv88e6xxx_6320_family(chip);
917fad09c73SVivien Didelot 	case PORT:
918fad09c73SVivien Didelot 		return mv88e6xxx_6095_family(chip) ||
919fad09c73SVivien Didelot 			mv88e6xxx_6185_family(chip) ||
920fad09c73SVivien Didelot 			mv88e6xxx_6097_family(chip) ||
921fad09c73SVivien Didelot 			mv88e6xxx_6165_family(chip) ||
922fad09c73SVivien Didelot 			mv88e6xxx_6351_family(chip) ||
923fad09c73SVivien Didelot 			mv88e6xxx_6352_family(chip);
924fad09c73SVivien Didelot 	}
925fad09c73SVivien Didelot 	return false;
926fad09c73SVivien Didelot }
927fad09c73SVivien Didelot 
928fad09c73SVivien Didelot static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
929fad09c73SVivien Didelot 					    struct mv88e6xxx_hw_stat *s,
930fad09c73SVivien Didelot 					    int port)
931fad09c73SVivien Didelot {
932fad09c73SVivien Didelot 	u32 low;
933fad09c73SVivien Didelot 	u32 high = 0;
9340e7b9925SAndrew Lunn 	int err;
9350e7b9925SAndrew Lunn 	u16 reg;
936fad09c73SVivien Didelot 	u64 value;
937fad09c73SVivien Didelot 
938fad09c73SVivien Didelot 	switch (s->type) {
939fad09c73SVivien Didelot 	case PORT:
9400e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, s->reg, &reg);
9410e7b9925SAndrew Lunn 		if (err)
942fad09c73SVivien Didelot 			return UINT64_MAX;
943fad09c73SVivien Didelot 
9440e7b9925SAndrew Lunn 		low = reg;
945fad09c73SVivien Didelot 		if (s->sizeof_stat == 4) {
9460e7b9925SAndrew Lunn 			err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
9470e7b9925SAndrew Lunn 			if (err)
948fad09c73SVivien Didelot 				return UINT64_MAX;
9490e7b9925SAndrew Lunn 			high = reg;
950fad09c73SVivien Didelot 		}
951fad09c73SVivien Didelot 		break;
952fad09c73SVivien Didelot 	case BANK0:
953fad09c73SVivien Didelot 	case BANK1:
954fad09c73SVivien Didelot 		_mv88e6xxx_stats_read(chip, s->reg, &low);
955fad09c73SVivien Didelot 		if (s->sizeof_stat == 8)
956fad09c73SVivien Didelot 			_mv88e6xxx_stats_read(chip, s->reg + 1, &high);
957fad09c73SVivien Didelot 	}
958fad09c73SVivien Didelot 	value = (((u64)high) << 16) | low;
959fad09c73SVivien Didelot 	return value;
960fad09c73SVivien Didelot }
961fad09c73SVivien Didelot 
962fad09c73SVivien Didelot static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
963fad09c73SVivien Didelot 				  uint8_t *data)
964fad09c73SVivien Didelot {
96504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
966fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
967fad09c73SVivien Didelot 	int i, j;
968fad09c73SVivien Didelot 
969fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
970fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
971fad09c73SVivien Didelot 		if (mv88e6xxx_has_stat(chip, stat)) {
972fad09c73SVivien Didelot 			memcpy(data + j * ETH_GSTRING_LEN, stat->string,
973fad09c73SVivien Didelot 			       ETH_GSTRING_LEN);
974fad09c73SVivien Didelot 			j++;
975fad09c73SVivien Didelot 		}
976fad09c73SVivien Didelot 	}
977fad09c73SVivien Didelot }
978fad09c73SVivien Didelot 
979fad09c73SVivien Didelot static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
980fad09c73SVivien Didelot {
98104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
982fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
983fad09c73SVivien Didelot 	int i, j;
984fad09c73SVivien Didelot 
985fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
986fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
987fad09c73SVivien Didelot 		if (mv88e6xxx_has_stat(chip, stat))
988fad09c73SVivien Didelot 			j++;
989fad09c73SVivien Didelot 	}
990fad09c73SVivien Didelot 	return j;
991fad09c73SVivien Didelot }
992fad09c73SVivien Didelot 
993fad09c73SVivien Didelot static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
994fad09c73SVivien Didelot 					uint64_t *data)
995fad09c73SVivien Didelot {
99604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
997fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
998fad09c73SVivien Didelot 	int ret;
999fad09c73SVivien Didelot 	int i, j;
1000fad09c73SVivien Didelot 
1001fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1002fad09c73SVivien Didelot 
1003fad09c73SVivien Didelot 	ret = _mv88e6xxx_stats_snapshot(chip, port);
1004fad09c73SVivien Didelot 	if (ret < 0) {
1005fad09c73SVivien Didelot 		mutex_unlock(&chip->reg_lock);
1006fad09c73SVivien Didelot 		return;
1007fad09c73SVivien Didelot 	}
1008fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
1009fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
1010fad09c73SVivien Didelot 		if (mv88e6xxx_has_stat(chip, stat)) {
1011fad09c73SVivien Didelot 			data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
1012fad09c73SVivien Didelot 			j++;
1013fad09c73SVivien Didelot 		}
1014fad09c73SVivien Didelot 	}
1015fad09c73SVivien Didelot 
1016fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1017fad09c73SVivien Didelot }
1018fad09c73SVivien Didelot 
1019fad09c73SVivien Didelot static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
1020fad09c73SVivien Didelot {
1021fad09c73SVivien Didelot 	return 32 * sizeof(u16);
1022fad09c73SVivien Didelot }
1023fad09c73SVivien Didelot 
1024fad09c73SVivien Didelot static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
1025fad09c73SVivien Didelot 			       struct ethtool_regs *regs, void *_p)
1026fad09c73SVivien Didelot {
102704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10280e7b9925SAndrew Lunn 	int err;
10290e7b9925SAndrew Lunn 	u16 reg;
1030fad09c73SVivien Didelot 	u16 *p = _p;
1031fad09c73SVivien Didelot 	int i;
1032fad09c73SVivien Didelot 
1033fad09c73SVivien Didelot 	regs->version = 0;
1034fad09c73SVivien Didelot 
1035fad09c73SVivien Didelot 	memset(p, 0xff, 32 * sizeof(u16));
1036fad09c73SVivien Didelot 
1037fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1038fad09c73SVivien Didelot 
1039fad09c73SVivien Didelot 	for (i = 0; i < 32; i++) {
1040fad09c73SVivien Didelot 
10410e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, i, &reg);
10420e7b9925SAndrew Lunn 		if (!err)
10430e7b9925SAndrew Lunn 			p[i] = reg;
1044fad09c73SVivien Didelot 	}
1045fad09c73SVivien Didelot 
1046fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1047fad09c73SVivien Didelot }
1048fad09c73SVivien Didelot 
1049fad09c73SVivien Didelot static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
1050fad09c73SVivien Didelot {
1051a935c052SVivien Didelot 	return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
1052fad09c73SVivien Didelot }
1053fad09c73SVivien Didelot 
1054fad09c73SVivien Didelot static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1055fad09c73SVivien Didelot 			     struct ethtool_eee *e)
1056fad09c73SVivien Didelot {
105704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10589c93829cSVivien Didelot 	u16 reg;
10599c93829cSVivien Didelot 	int err;
1060fad09c73SVivien Didelot 
1061fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
1062fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1063fad09c73SVivien Didelot 
1064fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1065fad09c73SVivien Didelot 
10669c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
10679c93829cSVivien Didelot 	if (err)
1068fad09c73SVivien Didelot 		goto out;
1069fad09c73SVivien Didelot 
1070fad09c73SVivien Didelot 	e->eee_enabled = !!(reg & 0x0200);
1071fad09c73SVivien Didelot 	e->tx_lpi_enabled = !!(reg & 0x0100);
1072fad09c73SVivien Didelot 
10730e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
10749c93829cSVivien Didelot 	if (err)
1075fad09c73SVivien Didelot 		goto out;
1076fad09c73SVivien Didelot 
1077fad09c73SVivien Didelot 	e->eee_active = !!(reg & PORT_STATUS_EEE);
1078fad09c73SVivien Didelot out:
1079fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
10809c93829cSVivien Didelot 
10819c93829cSVivien Didelot 	return err;
1082fad09c73SVivien Didelot }
1083fad09c73SVivien Didelot 
1084fad09c73SVivien Didelot static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1085fad09c73SVivien Didelot 			     struct phy_device *phydev, struct ethtool_eee *e)
1086fad09c73SVivien Didelot {
108704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10889c93829cSVivien Didelot 	u16 reg;
10899c93829cSVivien Didelot 	int err;
1090fad09c73SVivien Didelot 
1091fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
1092fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1093fad09c73SVivien Didelot 
1094fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1095fad09c73SVivien Didelot 
10969c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
10979c93829cSVivien Didelot 	if (err)
1098fad09c73SVivien Didelot 		goto out;
1099fad09c73SVivien Didelot 
11009c93829cSVivien Didelot 	reg &= ~0x0300;
1101fad09c73SVivien Didelot 	if (e->eee_enabled)
1102fad09c73SVivien Didelot 		reg |= 0x0200;
1103fad09c73SVivien Didelot 	if (e->tx_lpi_enabled)
1104fad09c73SVivien Didelot 		reg |= 0x0100;
1105fad09c73SVivien Didelot 
11069c93829cSVivien Didelot 	err = mv88e6xxx_phy_write(chip, port, 16, reg);
1107fad09c73SVivien Didelot out:
1108fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1109fad09c73SVivien Didelot 
11109c93829cSVivien Didelot 	return err;
1111fad09c73SVivien Didelot }
1112fad09c73SVivien Didelot 
1113fad09c73SVivien Didelot static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
1114fad09c73SVivien Didelot {
1115a935c052SVivien Didelot 	u16 val;
1116a935c052SVivien Didelot 	int err;
1117fad09c73SVivien Didelot 
11186dc10bbcSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) {
1119a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid);
1120a935c052SVivien Didelot 		if (err)
1121a935c052SVivien Didelot 			return err;
1122fad09c73SVivien Didelot 	} else if (mv88e6xxx_num_databases(chip) == 256) {
1123fad09c73SVivien Didelot 		/* ATU DBNum[7:4] are located in ATU Control 15:12 */
1124a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
1125a935c052SVivien Didelot 		if (err)
1126a935c052SVivien Didelot 			return err;
1127fad09c73SVivien Didelot 
1128a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
1129a935c052SVivien Didelot 					 (val & 0xfff) | ((fid << 8) & 0xf000));
1130a935c052SVivien Didelot 		if (err)
1131a935c052SVivien Didelot 			return err;
1132fad09c73SVivien Didelot 
1133fad09c73SVivien Didelot 		/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1134fad09c73SVivien Didelot 		cmd |= fid & 0xf;
1135fad09c73SVivien Didelot 	}
1136fad09c73SVivien Didelot 
1137a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd);
1138a935c052SVivien Didelot 	if (err)
1139a935c052SVivien Didelot 		return err;
1140fad09c73SVivien Didelot 
1141fad09c73SVivien Didelot 	return _mv88e6xxx_atu_wait(chip);
1142fad09c73SVivien Didelot }
1143fad09c73SVivien Didelot 
1144fad09c73SVivien Didelot static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
1145fad09c73SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry)
1146fad09c73SVivien Didelot {
1147fad09c73SVivien Didelot 	u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1148fad09c73SVivien Didelot 
1149fad09c73SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1150fad09c73SVivien Didelot 		unsigned int mask, shift;
1151fad09c73SVivien Didelot 
1152fad09c73SVivien Didelot 		if (entry->trunk) {
1153fad09c73SVivien Didelot 			data |= GLOBAL_ATU_DATA_TRUNK;
1154fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1155fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1156fad09c73SVivien Didelot 		} else {
1157fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1158fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1159fad09c73SVivien Didelot 		}
1160fad09c73SVivien Didelot 
1161fad09c73SVivien Didelot 		data |= (entry->portv_trunkid << shift) & mask;
1162fad09c73SVivien Didelot 	}
1163fad09c73SVivien Didelot 
1164a935c052SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
1165fad09c73SVivien Didelot }
1166fad09c73SVivien Didelot 
1167fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
1168fad09c73SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry,
1169fad09c73SVivien Didelot 				     bool static_too)
1170fad09c73SVivien Didelot {
1171fad09c73SVivien Didelot 	int op;
1172fad09c73SVivien Didelot 	int err;
1173fad09c73SVivien Didelot 
1174fad09c73SVivien Didelot 	err = _mv88e6xxx_atu_wait(chip);
1175fad09c73SVivien Didelot 	if (err)
1176fad09c73SVivien Didelot 		return err;
1177fad09c73SVivien Didelot 
1178fad09c73SVivien Didelot 	err = _mv88e6xxx_atu_data_write(chip, entry);
1179fad09c73SVivien Didelot 	if (err)
1180fad09c73SVivien Didelot 		return err;
1181fad09c73SVivien Didelot 
1182fad09c73SVivien Didelot 	if (entry->fid) {
1183fad09c73SVivien Didelot 		op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1184fad09c73SVivien Didelot 			GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1185fad09c73SVivien Didelot 	} else {
1186fad09c73SVivien Didelot 		op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1187fad09c73SVivien Didelot 			GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1188fad09c73SVivien Didelot 	}
1189fad09c73SVivien Didelot 
1190fad09c73SVivien Didelot 	return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
1191fad09c73SVivien Didelot }
1192fad09c73SVivien Didelot 
1193fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
1194fad09c73SVivien Didelot 				u16 fid, bool static_too)
1195fad09c73SVivien Didelot {
1196fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
1197fad09c73SVivien Didelot 		.fid = fid,
1198fad09c73SVivien Didelot 		.state = 0, /* EntryState bits must be 0 */
1199fad09c73SVivien Didelot 	};
1200fad09c73SVivien Didelot 
1201fad09c73SVivien Didelot 	return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
1202fad09c73SVivien Didelot }
1203fad09c73SVivien Didelot 
1204fad09c73SVivien Didelot static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
1205fad09c73SVivien Didelot 			       int from_port, int to_port, bool static_too)
1206fad09c73SVivien Didelot {
1207fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
1208fad09c73SVivien Didelot 		.trunk = false,
1209fad09c73SVivien Didelot 		.fid = fid,
1210fad09c73SVivien Didelot 	};
1211fad09c73SVivien Didelot 
1212fad09c73SVivien Didelot 	/* EntryState bits must be 0xF */
1213fad09c73SVivien Didelot 	entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1214fad09c73SVivien Didelot 
1215fad09c73SVivien Didelot 	/* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1216fad09c73SVivien Didelot 	entry.portv_trunkid = (to_port & 0x0f) << 4;
1217fad09c73SVivien Didelot 	entry.portv_trunkid |= from_port & 0x0f;
1218fad09c73SVivien Didelot 
1219fad09c73SVivien Didelot 	return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
1220fad09c73SVivien Didelot }
1221fad09c73SVivien Didelot 
1222fad09c73SVivien Didelot static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
1223fad09c73SVivien Didelot 				 int port, bool static_too)
1224fad09c73SVivien Didelot {
1225fad09c73SVivien Didelot 	/* Destination port 0xF means remove the entries */
1226fad09c73SVivien Didelot 	return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
1227fad09c73SVivien Didelot }
1228fad09c73SVivien Didelot 
1229fad09c73SVivien Didelot static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
1230fad09c73SVivien Didelot {
1231fad09c73SVivien Didelot 	struct net_device *bridge = chip->ports[port].bridge_dev;
1232fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1233fad09c73SVivien Didelot 	u16 output_ports = 0;
1234fad09c73SVivien Didelot 	int i;
1235fad09c73SVivien Didelot 
1236fad09c73SVivien Didelot 	/* allow CPU port or DSA link(s) to send frames to every port */
1237fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
12385a7921f4SVivien Didelot 		output_ports = ~0;
1239fad09c73SVivien Didelot 	} else {
1240370b4ffbSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1241fad09c73SVivien Didelot 			/* allow sending frames to every group member */
1242fad09c73SVivien Didelot 			if (bridge && chip->ports[i].bridge_dev == bridge)
1243fad09c73SVivien Didelot 				output_ports |= BIT(i);
1244fad09c73SVivien Didelot 
1245fad09c73SVivien Didelot 			/* allow sending frames to CPU port and DSA link(s) */
1246fad09c73SVivien Didelot 			if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1247fad09c73SVivien Didelot 				output_ports |= BIT(i);
1248fad09c73SVivien Didelot 		}
1249fad09c73SVivien Didelot 	}
1250fad09c73SVivien Didelot 
1251fad09c73SVivien Didelot 	/* prevent frames from going back out of the port they came in on */
1252fad09c73SVivien Didelot 	output_ports &= ~BIT(port);
1253fad09c73SVivien Didelot 
12545a7921f4SVivien Didelot 	return mv88e6xxx_port_set_vlan_map(chip, port, output_ports);
1255fad09c73SVivien Didelot }
1256fad09c73SVivien Didelot 
1257fad09c73SVivien Didelot static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1258fad09c73SVivien Didelot 					 u8 state)
1259fad09c73SVivien Didelot {
126004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1261fad09c73SVivien Didelot 	int stp_state;
1262fad09c73SVivien Didelot 	int err;
1263fad09c73SVivien Didelot 
1264fad09c73SVivien Didelot 	switch (state) {
1265fad09c73SVivien Didelot 	case BR_STATE_DISABLED:
1266fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_DISABLED;
1267fad09c73SVivien Didelot 		break;
1268fad09c73SVivien Didelot 	case BR_STATE_BLOCKING:
1269fad09c73SVivien Didelot 	case BR_STATE_LISTENING:
1270fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_BLOCKING;
1271fad09c73SVivien Didelot 		break;
1272fad09c73SVivien Didelot 	case BR_STATE_LEARNING:
1273fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_LEARNING;
1274fad09c73SVivien Didelot 		break;
1275fad09c73SVivien Didelot 	case BR_STATE_FORWARDING:
1276fad09c73SVivien Didelot 	default:
1277fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_FORWARDING;
1278fad09c73SVivien Didelot 		break;
1279fad09c73SVivien Didelot 	}
1280fad09c73SVivien Didelot 
1281fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1282e28def33SVivien Didelot 	err = mv88e6xxx_port_set_state(chip, port, stp_state);
1283fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1284fad09c73SVivien Didelot 
1285fad09c73SVivien Didelot 	if (err)
1286e28def33SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to update state\n");
1287fad09c73SVivien Didelot }
1288fad09c73SVivien Didelot 
1289749efcb8SVivien Didelot static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
1290749efcb8SVivien Didelot {
1291749efcb8SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1292749efcb8SVivien Didelot 	int err;
1293749efcb8SVivien Didelot 
1294749efcb8SVivien Didelot 	mutex_lock(&chip->reg_lock);
1295749efcb8SVivien Didelot 	err = _mv88e6xxx_atu_remove(chip, 0, port, false);
1296749efcb8SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1297749efcb8SVivien Didelot 
1298749efcb8SVivien Didelot 	if (err)
1299749efcb8SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
1300749efcb8SVivien Didelot }
1301749efcb8SVivien Didelot 
1302fad09c73SVivien Didelot static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
1303fad09c73SVivien Didelot {
1304a935c052SVivien Didelot 	return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
1305fad09c73SVivien Didelot }
1306fad09c73SVivien Didelot 
1307fad09c73SVivien Didelot static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
1308fad09c73SVivien Didelot {
1309a935c052SVivien Didelot 	int err;
1310fad09c73SVivien Didelot 
1311a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
1312a935c052SVivien Didelot 	if (err)
1313a935c052SVivien Didelot 		return err;
1314fad09c73SVivien Didelot 
1315fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_wait(chip);
1316fad09c73SVivien Didelot }
1317fad09c73SVivien Didelot 
1318fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
1319fad09c73SVivien Didelot {
1320fad09c73SVivien Didelot 	int ret;
1321fad09c73SVivien Didelot 
1322fad09c73SVivien Didelot 	ret = _mv88e6xxx_vtu_wait(chip);
1323fad09c73SVivien Didelot 	if (ret < 0)
1324fad09c73SVivien Didelot 		return ret;
1325fad09c73SVivien Didelot 
1326fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
1327fad09c73SVivien Didelot }
1328fad09c73SVivien Didelot 
1329fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
1330b4e47c0fSVivien Didelot 					struct mv88e6xxx_vtu_entry *entry,
1331fad09c73SVivien Didelot 					unsigned int nibble_offset)
1332fad09c73SVivien Didelot {
1333fad09c73SVivien Didelot 	u16 regs[3];
1334a935c052SVivien Didelot 	int i, err;
1335fad09c73SVivien Didelot 
1336fad09c73SVivien Didelot 	for (i = 0; i < 3; ++i) {
1337a935c052SVivien Didelot 		u16 *reg = &regs[i];
1338fad09c73SVivien Didelot 
1339a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
1340a935c052SVivien Didelot 		if (err)
1341a935c052SVivien Didelot 			return err;
1342fad09c73SVivien Didelot 	}
1343fad09c73SVivien Didelot 
1344370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1345fad09c73SVivien Didelot 		unsigned int shift = (i % 4) * 4 + nibble_offset;
1346fad09c73SVivien Didelot 		u16 reg = regs[i / 4];
1347fad09c73SVivien Didelot 
1348fad09c73SVivien Didelot 		entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1349fad09c73SVivien Didelot 	}
1350fad09c73SVivien Didelot 
1351fad09c73SVivien Didelot 	return 0;
1352fad09c73SVivien Didelot }
1353fad09c73SVivien Didelot 
1354fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
1355b4e47c0fSVivien Didelot 				   struct mv88e6xxx_vtu_entry *entry)
1356fad09c73SVivien Didelot {
1357fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
1358fad09c73SVivien Didelot }
1359fad09c73SVivien Didelot 
1360fad09c73SVivien Didelot static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
1361b4e47c0fSVivien Didelot 				   struct mv88e6xxx_vtu_entry *entry)
1362fad09c73SVivien Didelot {
1363fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
1364fad09c73SVivien Didelot }
1365fad09c73SVivien Didelot 
1366fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
1367b4e47c0fSVivien Didelot 					 struct mv88e6xxx_vtu_entry *entry,
1368fad09c73SVivien Didelot 					 unsigned int nibble_offset)
1369fad09c73SVivien Didelot {
1370fad09c73SVivien Didelot 	u16 regs[3] = { 0 };
1371a935c052SVivien Didelot 	int i, err;
1372fad09c73SVivien Didelot 
1373370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1374fad09c73SVivien Didelot 		unsigned int shift = (i % 4) * 4 + nibble_offset;
1375fad09c73SVivien Didelot 		u8 data = entry->data[i];
1376fad09c73SVivien Didelot 
1377fad09c73SVivien Didelot 		regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1378fad09c73SVivien Didelot 	}
1379fad09c73SVivien Didelot 
1380fad09c73SVivien Didelot 	for (i = 0; i < 3; ++i) {
1381a935c052SVivien Didelot 		u16 reg = regs[i];
1382a935c052SVivien Didelot 
1383a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
1384a935c052SVivien Didelot 		if (err)
1385a935c052SVivien Didelot 			return err;
1386fad09c73SVivien Didelot 	}
1387fad09c73SVivien Didelot 
1388fad09c73SVivien Didelot 	return 0;
1389fad09c73SVivien Didelot }
1390fad09c73SVivien Didelot 
1391fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
1392b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1393fad09c73SVivien Didelot {
1394fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
1395fad09c73SVivien Didelot }
1396fad09c73SVivien Didelot 
1397fad09c73SVivien Didelot static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
1398b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1399fad09c73SVivien Didelot {
1400fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
1401fad09c73SVivien Didelot }
1402fad09c73SVivien Didelot 
1403fad09c73SVivien Didelot static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
1404fad09c73SVivien Didelot {
1405a935c052SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID,
1406fad09c73SVivien Didelot 				  vid & GLOBAL_VTU_VID_MASK);
1407fad09c73SVivien Didelot }
1408fad09c73SVivien Didelot 
1409fad09c73SVivien Didelot static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
1410b4e47c0fSVivien Didelot 				  struct mv88e6xxx_vtu_entry *entry)
1411fad09c73SVivien Didelot {
1412b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry next = { 0 };
1413a935c052SVivien Didelot 	u16 val;
1414a935c052SVivien Didelot 	int err;
1415fad09c73SVivien Didelot 
1416a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1417a935c052SVivien Didelot 	if (err)
1418a935c052SVivien Didelot 		return err;
1419fad09c73SVivien Didelot 
1420a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
1421a935c052SVivien Didelot 	if (err)
1422a935c052SVivien Didelot 		return err;
1423fad09c73SVivien Didelot 
1424a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
1425a935c052SVivien Didelot 	if (err)
1426a935c052SVivien Didelot 		return err;
1427fad09c73SVivien Didelot 
1428a935c052SVivien Didelot 	next.vid = val & GLOBAL_VTU_VID_MASK;
1429a935c052SVivien Didelot 	next.valid = !!(val & GLOBAL_VTU_VID_VALID);
1430fad09c73SVivien Didelot 
1431fad09c73SVivien Didelot 	if (next.valid) {
1432a935c052SVivien Didelot 		err = mv88e6xxx_vtu_data_read(chip, &next);
1433a935c052SVivien Didelot 		if (err)
1434a935c052SVivien Didelot 			return err;
1435fad09c73SVivien Didelot 
14366dc10bbcSVivien Didelot 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
1437a935c052SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
1438a935c052SVivien Didelot 			if (err)
1439a935c052SVivien Didelot 				return err;
1440fad09c73SVivien Didelot 
1441a935c052SVivien Didelot 			next.fid = val & GLOBAL_VTU_FID_MASK;
1442fad09c73SVivien Didelot 		} else if (mv88e6xxx_num_databases(chip) == 256) {
1443fad09c73SVivien Didelot 			/* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1444fad09c73SVivien Didelot 			 * VTU DBNum[3:0] are located in VTU Operation 3:0
1445fad09c73SVivien Didelot 			 */
1446a935c052SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
1447a935c052SVivien Didelot 			if (err)
1448a935c052SVivien Didelot 				return err;
1449fad09c73SVivien Didelot 
1450a935c052SVivien Didelot 			next.fid = (val & 0xf00) >> 4;
1451a935c052SVivien Didelot 			next.fid |= val & 0xf;
1452fad09c73SVivien Didelot 		}
1453fad09c73SVivien Didelot 
1454fad09c73SVivien Didelot 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1455a935c052SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
1456a935c052SVivien Didelot 			if (err)
1457a935c052SVivien Didelot 				return err;
1458fad09c73SVivien Didelot 
1459a935c052SVivien Didelot 			next.sid = val & GLOBAL_VTU_SID_MASK;
1460fad09c73SVivien Didelot 		}
1461fad09c73SVivien Didelot 	}
1462fad09c73SVivien Didelot 
1463fad09c73SVivien Didelot 	*entry = next;
1464fad09c73SVivien Didelot 	return 0;
1465fad09c73SVivien Didelot }
1466fad09c73SVivien Didelot 
1467fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1468fad09c73SVivien Didelot 				    struct switchdev_obj_port_vlan *vlan,
1469fad09c73SVivien Didelot 				    int (*cb)(struct switchdev_obj *obj))
1470fad09c73SVivien Didelot {
147104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1472b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry next;
1473fad09c73SVivien Didelot 	u16 pvid;
1474fad09c73SVivien Didelot 	int err;
1475fad09c73SVivien Didelot 
1476fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1477fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1478fad09c73SVivien Didelot 
1479fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1480fad09c73SVivien Didelot 
148177064f37SVivien Didelot 	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
1482fad09c73SVivien Didelot 	if (err)
1483fad09c73SVivien Didelot 		goto unlock;
1484fad09c73SVivien Didelot 
1485fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
1486fad09c73SVivien Didelot 	if (err)
1487fad09c73SVivien Didelot 		goto unlock;
1488fad09c73SVivien Didelot 
1489fad09c73SVivien Didelot 	do {
1490fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &next);
1491fad09c73SVivien Didelot 		if (err)
1492fad09c73SVivien Didelot 			break;
1493fad09c73SVivien Didelot 
1494fad09c73SVivien Didelot 		if (!next.valid)
1495fad09c73SVivien Didelot 			break;
1496fad09c73SVivien Didelot 
1497fad09c73SVivien Didelot 		if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1498fad09c73SVivien Didelot 			continue;
1499fad09c73SVivien Didelot 
1500fad09c73SVivien Didelot 		/* reinit and dump this VLAN obj */
1501fad09c73SVivien Didelot 		vlan->vid_begin = next.vid;
1502fad09c73SVivien Didelot 		vlan->vid_end = next.vid;
1503fad09c73SVivien Didelot 		vlan->flags = 0;
1504fad09c73SVivien Didelot 
1505fad09c73SVivien Didelot 		if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1506fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1507fad09c73SVivien Didelot 
1508fad09c73SVivien Didelot 		if (next.vid == pvid)
1509fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1510fad09c73SVivien Didelot 
1511fad09c73SVivien Didelot 		err = cb(&vlan->obj);
1512fad09c73SVivien Didelot 		if (err)
1513fad09c73SVivien Didelot 			break;
1514fad09c73SVivien Didelot 	} while (next.vid < GLOBAL_VTU_VID_MASK);
1515fad09c73SVivien Didelot 
1516fad09c73SVivien Didelot unlock:
1517fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1518fad09c73SVivien Didelot 
1519fad09c73SVivien Didelot 	return err;
1520fad09c73SVivien Didelot }
1521fad09c73SVivien Didelot 
1522fad09c73SVivien Didelot static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
1523b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1524fad09c73SVivien Didelot {
1525fad09c73SVivien Didelot 	u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
1526fad09c73SVivien Didelot 	u16 reg = 0;
1527a935c052SVivien Didelot 	int err;
1528fad09c73SVivien Didelot 
1529a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1530a935c052SVivien Didelot 	if (err)
1531a935c052SVivien Didelot 		return err;
1532fad09c73SVivien Didelot 
1533fad09c73SVivien Didelot 	if (!entry->valid)
1534fad09c73SVivien Didelot 		goto loadpurge;
1535fad09c73SVivien Didelot 
1536fad09c73SVivien Didelot 	/* Write port member tags */
1537a935c052SVivien Didelot 	err = mv88e6xxx_vtu_data_write(chip, entry);
1538a935c052SVivien Didelot 	if (err)
1539a935c052SVivien Didelot 		return err;
1540fad09c73SVivien Didelot 
1541fad09c73SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1542fad09c73SVivien Didelot 		reg = entry->sid & GLOBAL_VTU_SID_MASK;
1543a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
1544a935c052SVivien Didelot 		if (err)
1545a935c052SVivien Didelot 			return err;
1546fad09c73SVivien Didelot 	}
1547fad09c73SVivien Didelot 
15486dc10bbcSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
1549fad09c73SVivien Didelot 		reg = entry->fid & GLOBAL_VTU_FID_MASK;
1550a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg);
1551a935c052SVivien Didelot 		if (err)
1552a935c052SVivien Didelot 			return err;
1553fad09c73SVivien Didelot 	} else if (mv88e6xxx_num_databases(chip) == 256) {
1554fad09c73SVivien Didelot 		/* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1555fad09c73SVivien Didelot 		 * VTU DBNum[3:0] are located in VTU Operation 3:0
1556fad09c73SVivien Didelot 		 */
1557fad09c73SVivien Didelot 		op |= (entry->fid & 0xf0) << 8;
1558fad09c73SVivien Didelot 		op |= entry->fid & 0xf;
1559fad09c73SVivien Didelot 	}
1560fad09c73SVivien Didelot 
1561fad09c73SVivien Didelot 	reg = GLOBAL_VTU_VID_VALID;
1562fad09c73SVivien Didelot loadpurge:
1563fad09c73SVivien Didelot 	reg |= entry->vid & GLOBAL_VTU_VID_MASK;
1564a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
1565a935c052SVivien Didelot 	if (err)
1566a935c052SVivien Didelot 		return err;
1567fad09c73SVivien Didelot 
1568fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_cmd(chip, op);
1569fad09c73SVivien Didelot }
1570fad09c73SVivien Didelot 
1571fad09c73SVivien Didelot static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
1572b4e47c0fSVivien Didelot 				  struct mv88e6xxx_vtu_entry *entry)
1573fad09c73SVivien Didelot {
1574b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry next = { 0 };
1575a935c052SVivien Didelot 	u16 val;
1576a935c052SVivien Didelot 	int err;
1577fad09c73SVivien Didelot 
1578a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1579a935c052SVivien Didelot 	if (err)
1580a935c052SVivien Didelot 		return err;
1581fad09c73SVivien Didelot 
1582a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID,
1583fad09c73SVivien Didelot 				 sid & GLOBAL_VTU_SID_MASK);
1584a935c052SVivien Didelot 	if (err)
1585a935c052SVivien Didelot 		return err;
1586fad09c73SVivien Didelot 
1587a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
1588a935c052SVivien Didelot 	if (err)
1589a935c052SVivien Didelot 		return err;
1590fad09c73SVivien Didelot 
1591a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
1592a935c052SVivien Didelot 	if (err)
1593a935c052SVivien Didelot 		return err;
1594fad09c73SVivien Didelot 
1595a935c052SVivien Didelot 	next.sid = val & GLOBAL_VTU_SID_MASK;
1596fad09c73SVivien Didelot 
1597a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
1598a935c052SVivien Didelot 	if (err)
1599a935c052SVivien Didelot 		return err;
1600fad09c73SVivien Didelot 
1601a935c052SVivien Didelot 	next.valid = !!(val & GLOBAL_VTU_VID_VALID);
1602fad09c73SVivien Didelot 
1603fad09c73SVivien Didelot 	if (next.valid) {
1604a935c052SVivien Didelot 		err = mv88e6xxx_stu_data_read(chip, &next);
1605a935c052SVivien Didelot 		if (err)
1606a935c052SVivien Didelot 			return err;
1607fad09c73SVivien Didelot 	}
1608fad09c73SVivien Didelot 
1609fad09c73SVivien Didelot 	*entry = next;
1610fad09c73SVivien Didelot 	return 0;
1611fad09c73SVivien Didelot }
1612fad09c73SVivien Didelot 
1613fad09c73SVivien Didelot static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
1614b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1615fad09c73SVivien Didelot {
1616fad09c73SVivien Didelot 	u16 reg = 0;
1617a935c052SVivien Didelot 	int err;
1618fad09c73SVivien Didelot 
1619a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1620a935c052SVivien Didelot 	if (err)
1621a935c052SVivien Didelot 		return err;
1622fad09c73SVivien Didelot 
1623fad09c73SVivien Didelot 	if (!entry->valid)
1624fad09c73SVivien Didelot 		goto loadpurge;
1625fad09c73SVivien Didelot 
1626fad09c73SVivien Didelot 	/* Write port states */
1627a935c052SVivien Didelot 	err = mv88e6xxx_stu_data_write(chip, entry);
1628a935c052SVivien Didelot 	if (err)
1629a935c052SVivien Didelot 		return err;
1630fad09c73SVivien Didelot 
1631fad09c73SVivien Didelot 	reg = GLOBAL_VTU_VID_VALID;
1632fad09c73SVivien Didelot loadpurge:
1633a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
1634a935c052SVivien Didelot 	if (err)
1635a935c052SVivien Didelot 		return err;
1636fad09c73SVivien Didelot 
1637fad09c73SVivien Didelot 	reg = entry->sid & GLOBAL_VTU_SID_MASK;
1638a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
1639a935c052SVivien Didelot 	if (err)
1640a935c052SVivien Didelot 		return err;
1641fad09c73SVivien Didelot 
1642fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
1643fad09c73SVivien Didelot }
1644fad09c73SVivien Didelot 
1645fad09c73SVivien Didelot static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
1646fad09c73SVivien Didelot {
1647fad09c73SVivien Didelot 	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1648b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1649fad09c73SVivien Didelot 	int i, err;
1650fad09c73SVivien Didelot 
1651fad09c73SVivien Didelot 	bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1652fad09c73SVivien Didelot 
1653fad09c73SVivien Didelot 	/* Set every FID bit used by the (un)bridged ports */
1654370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1655b4e48c50SVivien Didelot 		err = mv88e6xxx_port_get_fid(chip, i, fid);
1656fad09c73SVivien Didelot 		if (err)
1657fad09c73SVivien Didelot 			return err;
1658fad09c73SVivien Didelot 
1659fad09c73SVivien Didelot 		set_bit(*fid, fid_bitmap);
1660fad09c73SVivien Didelot 	}
1661fad09c73SVivien Didelot 
1662fad09c73SVivien Didelot 	/* Set every FID bit used by the VLAN entries */
1663fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
1664fad09c73SVivien Didelot 	if (err)
1665fad09c73SVivien Didelot 		return err;
1666fad09c73SVivien Didelot 
1667fad09c73SVivien Didelot 	do {
1668fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &vlan);
1669fad09c73SVivien Didelot 		if (err)
1670fad09c73SVivien Didelot 			return err;
1671fad09c73SVivien Didelot 
1672fad09c73SVivien Didelot 		if (!vlan.valid)
1673fad09c73SVivien Didelot 			break;
1674fad09c73SVivien Didelot 
1675fad09c73SVivien Didelot 		set_bit(vlan.fid, fid_bitmap);
1676fad09c73SVivien Didelot 	} while (vlan.vid < GLOBAL_VTU_VID_MASK);
1677fad09c73SVivien Didelot 
1678fad09c73SVivien Didelot 	/* The reset value 0x000 is used to indicate that multiple address
1679fad09c73SVivien Didelot 	 * databases are not needed. Return the next positive available.
1680fad09c73SVivien Didelot 	 */
1681fad09c73SVivien Didelot 	*fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
1682fad09c73SVivien Didelot 	if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
1683fad09c73SVivien Didelot 		return -ENOSPC;
1684fad09c73SVivien Didelot 
1685fad09c73SVivien Didelot 	/* Clear the database */
1686fad09c73SVivien Didelot 	return _mv88e6xxx_atu_flush(chip, *fid, true);
1687fad09c73SVivien Didelot }
1688fad09c73SVivien Didelot 
1689fad09c73SVivien Didelot static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
1690b4e47c0fSVivien Didelot 			      struct mv88e6xxx_vtu_entry *entry)
1691fad09c73SVivien Didelot {
1692fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1693b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
1694fad09c73SVivien Didelot 		.valid = true,
1695fad09c73SVivien Didelot 		.vid = vid,
1696fad09c73SVivien Didelot 	};
1697fad09c73SVivien Didelot 	int i, err;
1698fad09c73SVivien Didelot 
1699fad09c73SVivien Didelot 	err = _mv88e6xxx_fid_new(chip, &vlan.fid);
1700fad09c73SVivien Didelot 	if (err)
1701fad09c73SVivien Didelot 		return err;
1702fad09c73SVivien Didelot 
1703fad09c73SVivien Didelot 	/* exclude all ports except the CPU and DSA ports */
1704370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
1705fad09c73SVivien Didelot 		vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1706fad09c73SVivien Didelot 			? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1707fad09c73SVivien Didelot 			: GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1708fad09c73SVivien Didelot 
1709fad09c73SVivien Didelot 	if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
1710fad09c73SVivien Didelot 	    mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
1711b4e47c0fSVivien Didelot 		struct mv88e6xxx_vtu_entry vstp;
1712fad09c73SVivien Didelot 
1713fad09c73SVivien Didelot 		/* Adding a VTU entry requires a valid STU entry. As VSTP is not
1714fad09c73SVivien Didelot 		 * implemented, only one STU entry is needed to cover all VTU
1715fad09c73SVivien Didelot 		 * entries. Thus, validate the SID 0.
1716fad09c73SVivien Didelot 		 */
1717fad09c73SVivien Didelot 		vlan.sid = 0;
1718fad09c73SVivien Didelot 		err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
1719fad09c73SVivien Didelot 		if (err)
1720fad09c73SVivien Didelot 			return err;
1721fad09c73SVivien Didelot 
1722fad09c73SVivien Didelot 		if (vstp.sid != vlan.sid || !vstp.valid) {
1723fad09c73SVivien Didelot 			memset(&vstp, 0, sizeof(vstp));
1724fad09c73SVivien Didelot 			vstp.valid = true;
1725fad09c73SVivien Didelot 			vstp.sid = vlan.sid;
1726fad09c73SVivien Didelot 
1727fad09c73SVivien Didelot 			err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
1728fad09c73SVivien Didelot 			if (err)
1729fad09c73SVivien Didelot 				return err;
1730fad09c73SVivien Didelot 		}
1731fad09c73SVivien Didelot 	}
1732fad09c73SVivien Didelot 
1733fad09c73SVivien Didelot 	*entry = vlan;
1734fad09c73SVivien Didelot 	return 0;
1735fad09c73SVivien Didelot }
1736fad09c73SVivien Didelot 
1737fad09c73SVivien Didelot static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
1738b4e47c0fSVivien Didelot 			      struct mv88e6xxx_vtu_entry *entry, bool creat)
1739fad09c73SVivien Didelot {
1740fad09c73SVivien Didelot 	int err;
1741fad09c73SVivien Didelot 
1742fad09c73SVivien Didelot 	if (!vid)
1743fad09c73SVivien Didelot 		return -EINVAL;
1744fad09c73SVivien Didelot 
1745fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
1746fad09c73SVivien Didelot 	if (err)
1747fad09c73SVivien Didelot 		return err;
1748fad09c73SVivien Didelot 
1749fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_getnext(chip, entry);
1750fad09c73SVivien Didelot 	if (err)
1751fad09c73SVivien Didelot 		return err;
1752fad09c73SVivien Didelot 
1753fad09c73SVivien Didelot 	if (entry->vid != vid || !entry->valid) {
1754fad09c73SVivien Didelot 		if (!creat)
1755fad09c73SVivien Didelot 			return -EOPNOTSUPP;
1756fad09c73SVivien Didelot 		/* -ENOENT would've been more appropriate, but switchdev expects
1757fad09c73SVivien Didelot 		 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1758fad09c73SVivien Didelot 		 */
1759fad09c73SVivien Didelot 
1760fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_new(chip, vid, entry);
1761fad09c73SVivien Didelot 	}
1762fad09c73SVivien Didelot 
1763fad09c73SVivien Didelot 	return err;
1764fad09c73SVivien Didelot }
1765fad09c73SVivien Didelot 
1766fad09c73SVivien Didelot static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1767fad09c73SVivien Didelot 					u16 vid_begin, u16 vid_end)
1768fad09c73SVivien Didelot {
176904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1770b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1771fad09c73SVivien Didelot 	int i, err;
1772fad09c73SVivien Didelot 
1773fad09c73SVivien Didelot 	if (!vid_begin)
1774fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1775fad09c73SVivien Didelot 
1776fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1777fad09c73SVivien Didelot 
1778fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
1779fad09c73SVivien Didelot 	if (err)
1780fad09c73SVivien Didelot 		goto unlock;
1781fad09c73SVivien Didelot 
1782fad09c73SVivien Didelot 	do {
1783fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &vlan);
1784fad09c73SVivien Didelot 		if (err)
1785fad09c73SVivien Didelot 			goto unlock;
1786fad09c73SVivien Didelot 
1787fad09c73SVivien Didelot 		if (!vlan.valid)
1788fad09c73SVivien Didelot 			break;
1789fad09c73SVivien Didelot 
1790fad09c73SVivien Didelot 		if (vlan.vid > vid_end)
1791fad09c73SVivien Didelot 			break;
1792fad09c73SVivien Didelot 
1793370b4ffbSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1794fad09c73SVivien Didelot 			if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1795fad09c73SVivien Didelot 				continue;
1796fad09c73SVivien Didelot 
1797fad09c73SVivien Didelot 			if (vlan.data[i] ==
1798fad09c73SVivien Didelot 			    GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1799fad09c73SVivien Didelot 				continue;
1800fad09c73SVivien Didelot 
1801fad09c73SVivien Didelot 			if (chip->ports[i].bridge_dev ==
1802fad09c73SVivien Didelot 			    chip->ports[port].bridge_dev)
1803fad09c73SVivien Didelot 				break; /* same bridge, check next VLAN */
1804fad09c73SVivien Didelot 
1805fad09c73SVivien Didelot 			netdev_warn(ds->ports[port].netdev,
1806fad09c73SVivien Didelot 				    "hardware VLAN %d already used by %s\n",
1807fad09c73SVivien Didelot 				    vlan.vid,
1808fad09c73SVivien Didelot 				    netdev_name(chip->ports[i].bridge_dev));
1809fad09c73SVivien Didelot 			err = -EOPNOTSUPP;
1810fad09c73SVivien Didelot 			goto unlock;
1811fad09c73SVivien Didelot 		}
1812fad09c73SVivien Didelot 	} while (vlan.vid < vid_end);
1813fad09c73SVivien Didelot 
1814fad09c73SVivien Didelot unlock:
1815fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1816fad09c73SVivien Didelot 
1817fad09c73SVivien Didelot 	return err;
1818fad09c73SVivien Didelot }
1819fad09c73SVivien Didelot 
1820fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1821fad09c73SVivien Didelot 					 bool vlan_filtering)
1822fad09c73SVivien Didelot {
182304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1824385a0995SVivien Didelot 	u16 mode = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1825fad09c73SVivien Didelot 		PORT_CONTROL_2_8021Q_DISABLED;
18260e7b9925SAndrew Lunn 	int err;
1827fad09c73SVivien Didelot 
1828fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1829fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1830fad09c73SVivien Didelot 
1831fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1832385a0995SVivien Didelot 	err = mv88e6xxx_port_set_8021q_mode(chip, port, mode);
1833fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1834fad09c73SVivien Didelot 
18350e7b9925SAndrew Lunn 	return err;
1836fad09c73SVivien Didelot }
1837fad09c73SVivien Didelot 
1838fad09c73SVivien Didelot static int
1839fad09c73SVivien Didelot mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1840fad09c73SVivien Didelot 			    const struct switchdev_obj_port_vlan *vlan,
1841fad09c73SVivien Didelot 			    struct switchdev_trans *trans)
1842fad09c73SVivien Didelot {
184304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1844fad09c73SVivien Didelot 	int err;
1845fad09c73SVivien Didelot 
1846fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1847fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1848fad09c73SVivien Didelot 
1849fad09c73SVivien Didelot 	/* If the requested port doesn't belong to the same bridge as the VLAN
1850fad09c73SVivien Didelot 	 * members, do not support it (yet) and fallback to software VLAN.
1851fad09c73SVivien Didelot 	 */
1852fad09c73SVivien Didelot 	err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1853fad09c73SVivien Didelot 					   vlan->vid_end);
1854fad09c73SVivien Didelot 	if (err)
1855fad09c73SVivien Didelot 		return err;
1856fad09c73SVivien Didelot 
1857fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
1858fad09c73SVivien Didelot 	 * so skip the prepare phase.
1859fad09c73SVivien Didelot 	 */
1860fad09c73SVivien Didelot 	return 0;
1861fad09c73SVivien Didelot }
1862fad09c73SVivien Didelot 
1863fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
1864fad09c73SVivien Didelot 				    u16 vid, bool untagged)
1865fad09c73SVivien Didelot {
1866b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1867fad09c73SVivien Didelot 	int err;
1868fad09c73SVivien Didelot 
1869fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
1870fad09c73SVivien Didelot 	if (err)
1871fad09c73SVivien Didelot 		return err;
1872fad09c73SVivien Didelot 
1873fad09c73SVivien Didelot 	vlan.data[port] = untagged ?
1874fad09c73SVivien Didelot 		GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1875fad09c73SVivien Didelot 		GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1876fad09c73SVivien Didelot 
1877fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
1878fad09c73SVivien Didelot }
1879fad09c73SVivien Didelot 
1880fad09c73SVivien Didelot static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1881fad09c73SVivien Didelot 				    const struct switchdev_obj_port_vlan *vlan,
1882fad09c73SVivien Didelot 				    struct switchdev_trans *trans)
1883fad09c73SVivien Didelot {
188404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1885fad09c73SVivien Didelot 	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1886fad09c73SVivien Didelot 	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1887fad09c73SVivien Didelot 	u16 vid;
1888fad09c73SVivien Didelot 
1889fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1890fad09c73SVivien Didelot 		return;
1891fad09c73SVivien Didelot 
1892fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1893fad09c73SVivien Didelot 
1894fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
1895fad09c73SVivien Didelot 		if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
1896fad09c73SVivien Didelot 			netdev_err(ds->ports[port].netdev,
1897fad09c73SVivien Didelot 				   "failed to add VLAN %d%c\n",
1898fad09c73SVivien Didelot 				   vid, untagged ? 'u' : 't');
1899fad09c73SVivien Didelot 
190077064f37SVivien Didelot 	if (pvid && mv88e6xxx_port_set_pvid(chip, port, vlan->vid_end))
1901fad09c73SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
1902fad09c73SVivien Didelot 			   vlan->vid_end);
1903fad09c73SVivien Didelot 
1904fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1905fad09c73SVivien Didelot }
1906fad09c73SVivien Didelot 
1907fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
1908fad09c73SVivien Didelot 				    int port, u16 vid)
1909fad09c73SVivien Didelot {
1910fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1911b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1912fad09c73SVivien Didelot 	int i, err;
1913fad09c73SVivien Didelot 
1914fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
1915fad09c73SVivien Didelot 	if (err)
1916fad09c73SVivien Didelot 		return err;
1917fad09c73SVivien Didelot 
1918fad09c73SVivien Didelot 	/* Tell switchdev if this VLAN is handled in software */
1919fad09c73SVivien Didelot 	if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1920fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1921fad09c73SVivien Didelot 
1922fad09c73SVivien Didelot 	vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1923fad09c73SVivien Didelot 
1924fad09c73SVivien Didelot 	/* keep the VLAN unless all ports are excluded */
1925fad09c73SVivien Didelot 	vlan.valid = false;
1926370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1927fad09c73SVivien Didelot 		if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1928fad09c73SVivien Didelot 			continue;
1929fad09c73SVivien Didelot 
1930fad09c73SVivien Didelot 		if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
1931fad09c73SVivien Didelot 			vlan.valid = true;
1932fad09c73SVivien Didelot 			break;
1933fad09c73SVivien Didelot 		}
1934fad09c73SVivien Didelot 	}
1935fad09c73SVivien Didelot 
1936fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
1937fad09c73SVivien Didelot 	if (err)
1938fad09c73SVivien Didelot 		return err;
1939fad09c73SVivien Didelot 
1940fad09c73SVivien Didelot 	return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
1941fad09c73SVivien Didelot }
1942fad09c73SVivien Didelot 
1943fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1944fad09c73SVivien Didelot 				   const struct switchdev_obj_port_vlan *vlan)
1945fad09c73SVivien Didelot {
194604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1947fad09c73SVivien Didelot 	u16 pvid, vid;
1948fad09c73SVivien Didelot 	int err = 0;
1949fad09c73SVivien Didelot 
1950fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1951fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1952fad09c73SVivien Didelot 
1953fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1954fad09c73SVivien Didelot 
195577064f37SVivien Didelot 	err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
1956fad09c73SVivien Didelot 	if (err)
1957fad09c73SVivien Didelot 		goto unlock;
1958fad09c73SVivien Didelot 
1959fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
1960fad09c73SVivien Didelot 		err = _mv88e6xxx_port_vlan_del(chip, port, vid);
1961fad09c73SVivien Didelot 		if (err)
1962fad09c73SVivien Didelot 			goto unlock;
1963fad09c73SVivien Didelot 
1964fad09c73SVivien Didelot 		if (vid == pvid) {
196577064f37SVivien Didelot 			err = mv88e6xxx_port_set_pvid(chip, port, 0);
1966fad09c73SVivien Didelot 			if (err)
1967fad09c73SVivien Didelot 				goto unlock;
1968fad09c73SVivien Didelot 		}
1969fad09c73SVivien Didelot 	}
1970fad09c73SVivien Didelot 
1971fad09c73SVivien Didelot unlock:
1972fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1973fad09c73SVivien Didelot 
1974fad09c73SVivien Didelot 	return err;
1975fad09c73SVivien Didelot }
1976fad09c73SVivien Didelot 
1977fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
1978fad09c73SVivien Didelot 				    const unsigned char *addr)
1979fad09c73SVivien Didelot {
1980a935c052SVivien Didelot 	int i, err;
1981fad09c73SVivien Didelot 
1982fad09c73SVivien Didelot 	for (i = 0; i < 3; i++) {
1983a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i,
1984fad09c73SVivien Didelot 					 (addr[i * 2] << 8) | addr[i * 2 + 1]);
1985a935c052SVivien Didelot 		if (err)
1986a935c052SVivien Didelot 			return err;
1987fad09c73SVivien Didelot 	}
1988fad09c73SVivien Didelot 
1989fad09c73SVivien Didelot 	return 0;
1990fad09c73SVivien Didelot }
1991fad09c73SVivien Didelot 
1992fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
1993fad09c73SVivien Didelot 				   unsigned char *addr)
1994fad09c73SVivien Didelot {
1995a935c052SVivien Didelot 	u16 val;
1996a935c052SVivien Didelot 	int i, err;
1997fad09c73SVivien Didelot 
1998fad09c73SVivien Didelot 	for (i = 0; i < 3; i++) {
1999a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
2000a935c052SVivien Didelot 		if (err)
2001a935c052SVivien Didelot 			return err;
2002a935c052SVivien Didelot 
2003a935c052SVivien Didelot 		addr[i * 2] = val >> 8;
2004a935c052SVivien Didelot 		addr[i * 2 + 1] = val & 0xff;
2005fad09c73SVivien Didelot 	}
2006fad09c73SVivien Didelot 
2007fad09c73SVivien Didelot 	return 0;
2008fad09c73SVivien Didelot }
2009fad09c73SVivien Didelot 
2010fad09c73SVivien Didelot static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
2011fad09c73SVivien Didelot 			       struct mv88e6xxx_atu_entry *entry)
2012fad09c73SVivien Didelot {
2013fad09c73SVivien Didelot 	int ret;
2014fad09c73SVivien Didelot 
2015fad09c73SVivien Didelot 	ret = _mv88e6xxx_atu_wait(chip);
2016fad09c73SVivien Didelot 	if (ret < 0)
2017fad09c73SVivien Didelot 		return ret;
2018fad09c73SVivien Didelot 
2019fad09c73SVivien Didelot 	ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
2020fad09c73SVivien Didelot 	if (ret < 0)
2021fad09c73SVivien Didelot 		return ret;
2022fad09c73SVivien Didelot 
2023fad09c73SVivien Didelot 	ret = _mv88e6xxx_atu_data_write(chip, entry);
2024fad09c73SVivien Didelot 	if (ret < 0)
2025fad09c73SVivien Didelot 		return ret;
2026fad09c73SVivien Didelot 
2027fad09c73SVivien Didelot 	return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
2028fad09c73SVivien Didelot }
2029fad09c73SVivien Didelot 
203088472939SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
203188472939SVivien Didelot 				  struct mv88e6xxx_atu_entry *entry);
203288472939SVivien Didelot 
203388472939SVivien Didelot static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid,
203488472939SVivien Didelot 			     const u8 *addr, struct mv88e6xxx_atu_entry *entry)
203588472939SVivien Didelot {
203688472939SVivien Didelot 	struct mv88e6xxx_atu_entry next;
203788472939SVivien Didelot 	int err;
203888472939SVivien Didelot 
203988472939SVivien Didelot 	eth_broadcast_addr(next.mac);
204088472939SVivien Didelot 
204188472939SVivien Didelot 	err = _mv88e6xxx_atu_mac_write(chip, next.mac);
204288472939SVivien Didelot 	if (err)
204388472939SVivien Didelot 		return err;
204488472939SVivien Didelot 
204588472939SVivien Didelot 	do {
204688472939SVivien Didelot 		err = _mv88e6xxx_atu_getnext(chip, fid, &next);
204788472939SVivien Didelot 		if (err)
204888472939SVivien Didelot 			return err;
204988472939SVivien Didelot 
205088472939SVivien Didelot 		if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
205188472939SVivien Didelot 			break;
205288472939SVivien Didelot 
205388472939SVivien Didelot 		if (ether_addr_equal(next.mac, addr)) {
205488472939SVivien Didelot 			*entry = next;
205588472939SVivien Didelot 			return 0;
205688472939SVivien Didelot 		}
205788472939SVivien Didelot 	} while (!is_broadcast_ether_addr(next.mac));
205888472939SVivien Didelot 
205988472939SVivien Didelot 	memset(entry, 0, sizeof(*entry));
206088472939SVivien Didelot 	entry->fid = fid;
206188472939SVivien Didelot 	ether_addr_copy(entry->mac, addr);
206288472939SVivien Didelot 
206388472939SVivien Didelot 	return 0;
206488472939SVivien Didelot }
206588472939SVivien Didelot 
206683dabd1fSVivien Didelot static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
2067fad09c73SVivien Didelot 					const unsigned char *addr, u16 vid,
2068fad09c73SVivien Didelot 					u8 state)
2069fad09c73SVivien Didelot {
2070b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
207188472939SVivien Didelot 	struct mv88e6xxx_atu_entry entry;
2072fad09c73SVivien Didelot 	int err;
2073fad09c73SVivien Didelot 
2074fad09c73SVivien Didelot 	/* Null VLAN ID corresponds to the port private database */
2075fad09c73SVivien Didelot 	if (vid == 0)
2076b4e48c50SVivien Didelot 		err = mv88e6xxx_port_get_fid(chip, port, &vlan.fid);
2077fad09c73SVivien Didelot 	else
2078fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
2079fad09c73SVivien Didelot 	if (err)
2080fad09c73SVivien Didelot 		return err;
2081fad09c73SVivien Didelot 
208288472939SVivien Didelot 	err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry);
208388472939SVivien Didelot 	if (err)
208488472939SVivien Didelot 		return err;
208588472939SVivien Didelot 
208688472939SVivien Didelot 	/* Purge the ATU entry only if no port is using it anymore */
208788472939SVivien Didelot 	if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
208888472939SVivien Didelot 		entry.portv_trunkid &= ~BIT(port);
208988472939SVivien Didelot 		if (!entry.portv_trunkid)
209088472939SVivien Didelot 			entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
209188472939SVivien Didelot 	} else {
209288472939SVivien Didelot 		entry.portv_trunkid |= BIT(port);
2093fad09c73SVivien Didelot 		entry.state = state;
2094fad09c73SVivien Didelot 	}
2095fad09c73SVivien Didelot 
2096fad09c73SVivien Didelot 	return _mv88e6xxx_atu_load(chip, &entry);
2097fad09c73SVivien Didelot }
2098fad09c73SVivien Didelot 
2099fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2100fad09c73SVivien Didelot 				      const struct switchdev_obj_port_fdb *fdb,
2101fad09c73SVivien Didelot 				      struct switchdev_trans *trans)
2102fad09c73SVivien Didelot {
2103fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
2104fad09c73SVivien Didelot 	 * so skip the prepare phase.
2105fad09c73SVivien Didelot 	 */
2106fad09c73SVivien Didelot 	return 0;
2107fad09c73SVivien Didelot }
2108fad09c73SVivien Didelot 
2109fad09c73SVivien Didelot static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2110fad09c73SVivien Didelot 				   const struct switchdev_obj_port_fdb *fdb,
2111fad09c73SVivien Didelot 				   struct switchdev_trans *trans)
2112fad09c73SVivien Didelot {
211304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2114fad09c73SVivien Didelot 
2115fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
211683dabd1fSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
211783dabd1fSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_UC_STATIC))
211883dabd1fSVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
2119fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2120fad09c73SVivien Didelot }
2121fad09c73SVivien Didelot 
2122fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2123fad09c73SVivien Didelot 				  const struct switchdev_obj_port_fdb *fdb)
2124fad09c73SVivien Didelot {
212504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
212683dabd1fSVivien Didelot 	int err;
2127fad09c73SVivien Didelot 
2128fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
212983dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
2130fad09c73SVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
2131fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2132fad09c73SVivien Didelot 
213383dabd1fSVivien Didelot 	return err;
2134fad09c73SVivien Didelot }
2135fad09c73SVivien Didelot 
2136fad09c73SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
2137fad09c73SVivien Didelot 				  struct mv88e6xxx_atu_entry *entry)
2138fad09c73SVivien Didelot {
2139fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry next = { 0 };
2140a935c052SVivien Didelot 	u16 val;
2141a935c052SVivien Didelot 	int err;
2142fad09c73SVivien Didelot 
2143fad09c73SVivien Didelot 	next.fid = fid;
2144fad09c73SVivien Didelot 
2145a935c052SVivien Didelot 	err = _mv88e6xxx_atu_wait(chip);
2146a935c052SVivien Didelot 	if (err)
2147a935c052SVivien Didelot 		return err;
2148fad09c73SVivien Didelot 
2149a935c052SVivien Didelot 	err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
2150a935c052SVivien Didelot 	if (err)
2151a935c052SVivien Didelot 		return err;
2152fad09c73SVivien Didelot 
2153a935c052SVivien Didelot 	err = _mv88e6xxx_atu_mac_read(chip, next.mac);
2154a935c052SVivien Didelot 	if (err)
2155a935c052SVivien Didelot 		return err;
2156fad09c73SVivien Didelot 
2157a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
2158a935c052SVivien Didelot 	if (err)
2159a935c052SVivien Didelot 		return err;
2160fad09c73SVivien Didelot 
2161a935c052SVivien Didelot 	next.state = val & GLOBAL_ATU_DATA_STATE_MASK;
2162fad09c73SVivien Didelot 	if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2163fad09c73SVivien Didelot 		unsigned int mask, shift;
2164fad09c73SVivien Didelot 
2165a935c052SVivien Didelot 		if (val & GLOBAL_ATU_DATA_TRUNK) {
2166fad09c73SVivien Didelot 			next.trunk = true;
2167fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2168fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2169fad09c73SVivien Didelot 		} else {
2170fad09c73SVivien Didelot 			next.trunk = false;
2171fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2172fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2173fad09c73SVivien Didelot 		}
2174fad09c73SVivien Didelot 
2175a935c052SVivien Didelot 		next.portv_trunkid = (val & mask) >> shift;
2176fad09c73SVivien Didelot 	}
2177fad09c73SVivien Didelot 
2178fad09c73SVivien Didelot 	*entry = next;
2179fad09c73SVivien Didelot 	return 0;
2180fad09c73SVivien Didelot }
2181fad09c73SVivien Didelot 
218283dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
2183fad09c73SVivien Didelot 				      u16 fid, u16 vid, int port,
218483dabd1fSVivien Didelot 				      struct switchdev_obj *obj,
2185fad09c73SVivien Didelot 				      int (*cb)(struct switchdev_obj *obj))
2186fad09c73SVivien Didelot {
2187fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry addr = {
2188fad09c73SVivien Didelot 		.mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2189fad09c73SVivien Didelot 	};
2190fad09c73SVivien Didelot 	int err;
2191fad09c73SVivien Didelot 
2192fad09c73SVivien Didelot 	err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
2193fad09c73SVivien Didelot 	if (err)
2194fad09c73SVivien Didelot 		return err;
2195fad09c73SVivien Didelot 
2196fad09c73SVivien Didelot 	do {
2197fad09c73SVivien Didelot 		err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
2198fad09c73SVivien Didelot 		if (err)
219983dabd1fSVivien Didelot 			return err;
2200fad09c73SVivien Didelot 
2201fad09c73SVivien Didelot 		if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2202fad09c73SVivien Didelot 			break;
2203fad09c73SVivien Didelot 
220483dabd1fSVivien Didelot 		if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
220583dabd1fSVivien Didelot 			continue;
2206fad09c73SVivien Didelot 
220783dabd1fSVivien Didelot 		if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
220883dabd1fSVivien Didelot 			struct switchdev_obj_port_fdb *fdb;
220983dabd1fSVivien Didelot 
221083dabd1fSVivien Didelot 			if (!is_unicast_ether_addr(addr.mac))
221183dabd1fSVivien Didelot 				continue;
221283dabd1fSVivien Didelot 
221383dabd1fSVivien Didelot 			fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
2214fad09c73SVivien Didelot 			fdb->vid = vid;
2215fad09c73SVivien Didelot 			ether_addr_copy(fdb->addr, addr.mac);
221683dabd1fSVivien Didelot 			if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
221783dabd1fSVivien Didelot 				fdb->ndm_state = NUD_NOARP;
221883dabd1fSVivien Didelot 			else
221983dabd1fSVivien Didelot 				fdb->ndm_state = NUD_REACHABLE;
22207df8fbddSVivien Didelot 		} else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
22217df8fbddSVivien Didelot 			struct switchdev_obj_port_mdb *mdb;
22227df8fbddSVivien Didelot 
22237df8fbddSVivien Didelot 			if (!is_multicast_ether_addr(addr.mac))
22247df8fbddSVivien Didelot 				continue;
22257df8fbddSVivien Didelot 
22267df8fbddSVivien Didelot 			mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
22277df8fbddSVivien Didelot 			mdb->vid = vid;
22287df8fbddSVivien Didelot 			ether_addr_copy(mdb->addr, addr.mac);
222983dabd1fSVivien Didelot 		} else {
223083dabd1fSVivien Didelot 			return -EOPNOTSUPP;
2231fad09c73SVivien Didelot 		}
223283dabd1fSVivien Didelot 
223383dabd1fSVivien Didelot 		err = cb(obj);
223483dabd1fSVivien Didelot 		if (err)
223583dabd1fSVivien Didelot 			return err;
2236fad09c73SVivien Didelot 	} while (!is_broadcast_ether_addr(addr.mac));
2237fad09c73SVivien Didelot 
2238fad09c73SVivien Didelot 	return err;
2239fad09c73SVivien Didelot }
2240fad09c73SVivien Didelot 
224183dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
224283dabd1fSVivien Didelot 				  struct switchdev_obj *obj,
224383dabd1fSVivien Didelot 				  int (*cb)(struct switchdev_obj *obj))
224483dabd1fSVivien Didelot {
2245b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
224683dabd1fSVivien Didelot 		.vid = GLOBAL_VTU_VID_MASK, /* all ones */
224783dabd1fSVivien Didelot 	};
224883dabd1fSVivien Didelot 	u16 fid;
224983dabd1fSVivien Didelot 	int err;
225083dabd1fSVivien Didelot 
225183dabd1fSVivien Didelot 	/* Dump port's default Filtering Information Database (VLAN ID 0) */
2252b4e48c50SVivien Didelot 	err = mv88e6xxx_port_get_fid(chip, port, &fid);
225383dabd1fSVivien Didelot 	if (err)
225483dabd1fSVivien Didelot 		return err;
225583dabd1fSVivien Didelot 
225683dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
225783dabd1fSVivien Didelot 	if (err)
225883dabd1fSVivien Didelot 		return err;
225983dabd1fSVivien Didelot 
226083dabd1fSVivien Didelot 	/* Dump VLANs' Filtering Information Databases */
226183dabd1fSVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
226283dabd1fSVivien Didelot 	if (err)
226383dabd1fSVivien Didelot 		return err;
226483dabd1fSVivien Didelot 
226583dabd1fSVivien Didelot 	do {
226683dabd1fSVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &vlan);
226783dabd1fSVivien Didelot 		if (err)
226883dabd1fSVivien Didelot 			return err;
226983dabd1fSVivien Didelot 
227083dabd1fSVivien Didelot 		if (!vlan.valid)
227183dabd1fSVivien Didelot 			break;
227283dabd1fSVivien Didelot 
227383dabd1fSVivien Didelot 		err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
227483dabd1fSVivien Didelot 						 obj, cb);
227583dabd1fSVivien Didelot 		if (err)
227683dabd1fSVivien Didelot 			return err;
227783dabd1fSVivien Didelot 	} while (vlan.vid < GLOBAL_VTU_VID_MASK);
227883dabd1fSVivien Didelot 
227983dabd1fSVivien Didelot 	return err;
228083dabd1fSVivien Didelot }
228183dabd1fSVivien Didelot 
2282fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2283fad09c73SVivien Didelot 				   struct switchdev_obj_port_fdb *fdb,
2284fad09c73SVivien Didelot 				   int (*cb)(struct switchdev_obj *obj))
2285fad09c73SVivien Didelot {
228604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2287fad09c73SVivien Didelot 	int err;
2288fad09c73SVivien Didelot 
2289fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
229083dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
2291fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2292fad09c73SVivien Didelot 
2293fad09c73SVivien Didelot 	return err;
2294fad09c73SVivien Didelot }
2295fad09c73SVivien Didelot 
2296fad09c73SVivien Didelot static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2297fad09c73SVivien Didelot 				      struct net_device *bridge)
2298fad09c73SVivien Didelot {
229904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2300fad09c73SVivien Didelot 	int i, err = 0;
2301fad09c73SVivien Didelot 
2302fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2303fad09c73SVivien Didelot 
2304fad09c73SVivien Didelot 	/* Assign the bridge and remap each port's VLANTable */
2305fad09c73SVivien Didelot 	chip->ports[port].bridge_dev = bridge;
2306fad09c73SVivien Didelot 
2307370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
2308fad09c73SVivien Didelot 		if (chip->ports[i].bridge_dev == bridge) {
2309fad09c73SVivien Didelot 			err = _mv88e6xxx_port_based_vlan_map(chip, i);
2310fad09c73SVivien Didelot 			if (err)
2311fad09c73SVivien Didelot 				break;
2312fad09c73SVivien Didelot 		}
2313fad09c73SVivien Didelot 	}
2314fad09c73SVivien Didelot 
2315fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2316fad09c73SVivien Didelot 
2317fad09c73SVivien Didelot 	return err;
2318fad09c73SVivien Didelot }
2319fad09c73SVivien Didelot 
2320fad09c73SVivien Didelot static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
2321fad09c73SVivien Didelot {
232204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2323fad09c73SVivien Didelot 	struct net_device *bridge = chip->ports[port].bridge_dev;
2324fad09c73SVivien Didelot 	int i;
2325fad09c73SVivien Didelot 
2326fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2327fad09c73SVivien Didelot 
2328fad09c73SVivien Didelot 	/* Unassign the bridge and remap each port's VLANTable */
2329fad09c73SVivien Didelot 	chip->ports[port].bridge_dev = NULL;
2330fad09c73SVivien Didelot 
2331370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
2332fad09c73SVivien Didelot 		if (i == port || chip->ports[i].bridge_dev == bridge)
2333fad09c73SVivien Didelot 			if (_mv88e6xxx_port_based_vlan_map(chip, i))
2334fad09c73SVivien Didelot 				netdev_warn(ds->ports[i].netdev,
2335fad09c73SVivien Didelot 					    "failed to remap\n");
2336fad09c73SVivien Didelot 
2337fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2338fad09c73SVivien Didelot }
2339fad09c73SVivien Didelot 
2340fad09c73SVivien Didelot static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
2341fad09c73SVivien Didelot {
2342fad09c73SVivien Didelot 	bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
2343fad09c73SVivien Didelot 	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
2344fad09c73SVivien Didelot 	struct gpio_desc *gpiod = chip->reset;
2345fad09c73SVivien Didelot 	unsigned long timeout;
23460e7b9925SAndrew Lunn 	u16 reg;
2347a935c052SVivien Didelot 	int err;
2348fad09c73SVivien Didelot 	int i;
2349fad09c73SVivien Didelot 
2350fad09c73SVivien Didelot 	/* Set all ports to the disabled state. */
2351370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
2352e28def33SVivien Didelot 		err = mv88e6xxx_port_set_state(chip, i,
2353e28def33SVivien Didelot 					       PORT_CONTROL_STATE_DISABLED);
23540e7b9925SAndrew Lunn 		if (err)
23550e7b9925SAndrew Lunn 			return err;
2356fad09c73SVivien Didelot 	}
2357fad09c73SVivien Didelot 
2358fad09c73SVivien Didelot 	/* Wait for transmit queues to drain. */
2359fad09c73SVivien Didelot 	usleep_range(2000, 4000);
2360fad09c73SVivien Didelot 
2361fad09c73SVivien Didelot 	/* If there is a gpio connected to the reset pin, toggle it */
2362fad09c73SVivien Didelot 	if (gpiod) {
2363fad09c73SVivien Didelot 		gpiod_set_value_cansleep(gpiod, 1);
2364fad09c73SVivien Didelot 		usleep_range(10000, 20000);
2365fad09c73SVivien Didelot 		gpiod_set_value_cansleep(gpiod, 0);
2366fad09c73SVivien Didelot 		usleep_range(10000, 20000);
2367fad09c73SVivien Didelot 	}
2368fad09c73SVivien Didelot 
2369fad09c73SVivien Didelot 	/* Reset the switch. Keep the PPU active if requested. The PPU
2370fad09c73SVivien Didelot 	 * needs to be active to support indirect phy register access
2371fad09c73SVivien Didelot 	 * through global registers 0x18 and 0x19.
2372fad09c73SVivien Didelot 	 */
2373fad09c73SVivien Didelot 	if (ppu_active)
2374a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, 0x04, 0xc000);
2375fad09c73SVivien Didelot 	else
2376a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, 0x04, 0xc400);
23770e7b9925SAndrew Lunn 	if (err)
23780e7b9925SAndrew Lunn 		return err;
2379fad09c73SVivien Didelot 
2380fad09c73SVivien Didelot 	/* Wait up to one second for reset to complete. */
2381fad09c73SVivien Didelot 	timeout = jiffies + 1 * HZ;
2382fad09c73SVivien Didelot 	while (time_before(jiffies, timeout)) {
2383a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, 0x00, &reg);
2384a935c052SVivien Didelot 		if (err)
2385a935c052SVivien Didelot 			return err;
2386fad09c73SVivien Didelot 
2387a935c052SVivien Didelot 		if ((reg & is_reset) == is_reset)
2388fad09c73SVivien Didelot 			break;
2389fad09c73SVivien Didelot 		usleep_range(1000, 2000);
2390fad09c73SVivien Didelot 	}
2391fad09c73SVivien Didelot 	if (time_after(jiffies, timeout))
23920e7b9925SAndrew Lunn 		err = -ETIMEDOUT;
2393fad09c73SVivien Didelot 	else
23940e7b9925SAndrew Lunn 		err = 0;
2395fad09c73SVivien Didelot 
23960e7b9925SAndrew Lunn 	return err;
2397fad09c73SVivien Didelot }
2398fad09c73SVivien Didelot 
239909cb7dfdSVivien Didelot static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
2400fad09c73SVivien Didelot {
240109cb7dfdSVivien Didelot 	u16 val;
240209cb7dfdSVivien Didelot 	int err;
2403fad09c73SVivien Didelot 
240409cb7dfdSVivien Didelot 	/* Clear Power Down bit */
240509cb7dfdSVivien Didelot 	err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
240609cb7dfdSVivien Didelot 	if (err)
240709cb7dfdSVivien Didelot 		return err;
2408fad09c73SVivien Didelot 
240909cb7dfdSVivien Didelot 	if (val & BMCR_PDOWN) {
241009cb7dfdSVivien Didelot 		val &= ~BMCR_PDOWN;
241109cb7dfdSVivien Didelot 		err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
2412fad09c73SVivien Didelot 	}
2413fad09c73SVivien Didelot 
241409cb7dfdSVivien Didelot 	return err;
2415fad09c73SVivien Didelot }
2416fad09c73SVivien Didelot 
2417fad09c73SVivien Didelot static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
2418fad09c73SVivien Didelot {
2419fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
24200e7b9925SAndrew Lunn 	int err;
2421fad09c73SVivien Didelot 	u16 reg;
2422fad09c73SVivien Didelot 
2423d78343d2SVivien Didelot 	/* MAC Forcing register: don't force link, speed, duplex or flow control
2424d78343d2SVivien Didelot 	 * state to any particular values on physical ports, but force the CPU
2425d78343d2SVivien Didelot 	 * port and all DSA ports to their maximum bandwidth and full duplex.
2426fad09c73SVivien Didelot 	 */
2427d78343d2SVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
2428d78343d2SVivien Didelot 		err = mv88e6xxx_port_setup_mac(chip, port, LINK_FORCED_UP,
2429d78343d2SVivien Didelot 					       SPEED_MAX, DUPLEX_FULL,
2430d78343d2SVivien Didelot 					       PHY_INTERFACE_MODE_NA);
2431fad09c73SVivien Didelot 	else
2432d78343d2SVivien Didelot 		err = mv88e6xxx_port_setup_mac(chip, port, LINK_UNFORCED,
2433d78343d2SVivien Didelot 					       SPEED_UNFORCED, DUPLEX_UNFORCED,
2434d78343d2SVivien Didelot 					       PHY_INTERFACE_MODE_NA);
24350e7b9925SAndrew Lunn 	if (err)
24360e7b9925SAndrew Lunn 		return err;
2437fad09c73SVivien Didelot 
2438fad09c73SVivien Didelot 	/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2439fad09c73SVivien Didelot 	 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2440fad09c73SVivien Didelot 	 * tunneling, determine priority by looking at 802.1p and IP
2441fad09c73SVivien Didelot 	 * priority fields (IP prio has precedence), and set STP state
2442fad09c73SVivien Didelot 	 * to Forwarding.
2443fad09c73SVivien Didelot 	 *
2444fad09c73SVivien Didelot 	 * If this is the CPU link, use DSA or EDSA tagging depending
2445fad09c73SVivien Didelot 	 * on which tagging mode was configured.
2446fad09c73SVivien Didelot 	 *
2447fad09c73SVivien Didelot 	 * If this is a link to another switch, use DSA tagging mode.
2448fad09c73SVivien Didelot 	 *
2449fad09c73SVivien Didelot 	 * If this is the upstream port for this switch, enable
2450fad09c73SVivien Didelot 	 * forwarding of unknown unicasts and multicasts.
2451fad09c73SVivien Didelot 	 */
2452fad09c73SVivien Didelot 	reg = 0;
2453fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2454fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2455fad09c73SVivien Didelot 	    mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2456fad09c73SVivien Didelot 	    mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
2457fad09c73SVivien Didelot 		reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2458fad09c73SVivien Didelot 		PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2459fad09c73SVivien Didelot 		PORT_CONTROL_STATE_FORWARDING;
2460fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port)) {
24612bbb33beSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
2462fad09c73SVivien Didelot 			reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2463fad09c73SVivien Didelot 				PORT_CONTROL_FORWARD_UNKNOWN_MC;
24642bbb33beSAndrew Lunn 		else
24652bbb33beSAndrew Lunn 			reg |= PORT_CONTROL_DSA_TAG;
2466f027e0ccSJamie Lentin 		reg |= PORT_CONTROL_EGRESS_ADD_TAG |
2467f027e0ccSJamie Lentin 			PORT_CONTROL_FORWARD_UNKNOWN;
2468fad09c73SVivien Didelot 	}
2469fad09c73SVivien Didelot 	if (dsa_is_dsa_port(ds, port)) {
2470fad09c73SVivien Didelot 		if (mv88e6xxx_6095_family(chip) ||
2471fad09c73SVivien Didelot 		    mv88e6xxx_6185_family(chip))
2472fad09c73SVivien Didelot 			reg |= PORT_CONTROL_DSA_TAG;
2473fad09c73SVivien Didelot 		if (mv88e6xxx_6352_family(chip) ||
2474fad09c73SVivien Didelot 		    mv88e6xxx_6351_family(chip) ||
2475fad09c73SVivien Didelot 		    mv88e6xxx_6165_family(chip) ||
2476fad09c73SVivien Didelot 		    mv88e6xxx_6097_family(chip) ||
2477fad09c73SVivien Didelot 		    mv88e6xxx_6320_family(chip)) {
2478fad09c73SVivien Didelot 			reg |= PORT_CONTROL_FRAME_MODE_DSA;
2479fad09c73SVivien Didelot 		}
2480fad09c73SVivien Didelot 
2481fad09c73SVivien Didelot 		if (port == dsa_upstream_port(ds))
2482fad09c73SVivien Didelot 			reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2483fad09c73SVivien Didelot 				PORT_CONTROL_FORWARD_UNKNOWN_MC;
2484fad09c73SVivien Didelot 	}
2485fad09c73SVivien Didelot 	if (reg) {
24860e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
24870e7b9925SAndrew Lunn 		if (err)
24880e7b9925SAndrew Lunn 			return err;
2489fad09c73SVivien Didelot 	}
2490fad09c73SVivien Didelot 
2491fad09c73SVivien Didelot 	/* If this port is connected to a SerDes, make sure the SerDes is not
2492fad09c73SVivien Didelot 	 * powered down.
2493fad09c73SVivien Didelot 	 */
249409cb7dfdSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
24950e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
24960e7b9925SAndrew Lunn 		if (err)
24970e7b9925SAndrew Lunn 			return err;
24980e7b9925SAndrew Lunn 		reg &= PORT_STATUS_CMODE_MASK;
24990e7b9925SAndrew Lunn 		if ((reg == PORT_STATUS_CMODE_100BASE_X) ||
25000e7b9925SAndrew Lunn 		    (reg == PORT_STATUS_CMODE_1000BASE_X) ||
25010e7b9925SAndrew Lunn 		    (reg == PORT_STATUS_CMODE_SGMII)) {
25020e7b9925SAndrew Lunn 			err = mv88e6xxx_serdes_power_on(chip);
25030e7b9925SAndrew Lunn 			if (err < 0)
25040e7b9925SAndrew Lunn 				return err;
2505fad09c73SVivien Didelot 		}
2506fad09c73SVivien Didelot 	}
2507fad09c73SVivien Didelot 
2508fad09c73SVivien Didelot 	/* Port Control 2: don't force a good FCS, set the maximum frame size to
2509fad09c73SVivien Didelot 	 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
2510fad09c73SVivien Didelot 	 * untagged frames on this port, do a destination address lookup on all
2511fad09c73SVivien Didelot 	 * received packets as usual, disable ARP mirroring and don't send a
2512fad09c73SVivien Didelot 	 * copy of all transmitted/received frames on this port to the CPU.
2513fad09c73SVivien Didelot 	 */
2514fad09c73SVivien Didelot 	reg = 0;
2515fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2516fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2517fad09c73SVivien Didelot 	    mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2518fad09c73SVivien Didelot 	    mv88e6xxx_6185_family(chip))
2519fad09c73SVivien Didelot 		reg = PORT_CONTROL_2_MAP_DA;
2520fad09c73SVivien Didelot 
2521fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2522fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
2523fad09c73SVivien Didelot 		reg |= PORT_CONTROL_2_JUMBO_10240;
2524fad09c73SVivien Didelot 
2525fad09c73SVivien Didelot 	if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
2526fad09c73SVivien Didelot 		/* Set the upstream port this port should use */
2527fad09c73SVivien Didelot 		reg |= dsa_upstream_port(ds);
2528fad09c73SVivien Didelot 		/* enable forwarding of unknown multicast addresses to
2529fad09c73SVivien Didelot 		 * the upstream port
2530fad09c73SVivien Didelot 		 */
2531fad09c73SVivien Didelot 		if (port == dsa_upstream_port(ds))
2532fad09c73SVivien Didelot 			reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2533fad09c73SVivien Didelot 	}
2534fad09c73SVivien Didelot 
2535fad09c73SVivien Didelot 	reg |= PORT_CONTROL_2_8021Q_DISABLED;
2536fad09c73SVivien Didelot 
2537fad09c73SVivien Didelot 	if (reg) {
25380e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
25390e7b9925SAndrew Lunn 		if (err)
25400e7b9925SAndrew Lunn 			return err;
2541fad09c73SVivien Didelot 	}
2542fad09c73SVivien Didelot 
2543fad09c73SVivien Didelot 	/* Port Association Vector: when learning source addresses
2544fad09c73SVivien Didelot 	 * of packets, add the address to the address database using
2545fad09c73SVivien Didelot 	 * a port bitmap that has only the bit for this port set and
2546fad09c73SVivien Didelot 	 * the other bits clear.
2547fad09c73SVivien Didelot 	 */
2548fad09c73SVivien Didelot 	reg = 1 << port;
2549fad09c73SVivien Didelot 	/* Disable learning for CPU port */
2550fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port))
2551fad09c73SVivien Didelot 		reg = 0;
2552fad09c73SVivien Didelot 
25530e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
25540e7b9925SAndrew Lunn 	if (err)
25550e7b9925SAndrew Lunn 		return err;
2556fad09c73SVivien Didelot 
2557fad09c73SVivien Didelot 	/* Egress rate control 2: disable egress rate control. */
25580e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
25590e7b9925SAndrew Lunn 	if (err)
25600e7b9925SAndrew Lunn 		return err;
2561fad09c73SVivien Didelot 
2562fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2563fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2564fad09c73SVivien Didelot 	    mv88e6xxx_6320_family(chip)) {
2565fad09c73SVivien Didelot 		/* Do not limit the period of time that this port can
2566fad09c73SVivien Didelot 		 * be paused for by the remote end or the period of
2567fad09c73SVivien Didelot 		 * time that this port can pause the remote end.
2568fad09c73SVivien Didelot 		 */
25690e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
25700e7b9925SAndrew Lunn 		if (err)
25710e7b9925SAndrew Lunn 			return err;
2572fad09c73SVivien Didelot 
2573fad09c73SVivien Didelot 		/* Port ATU control: disable limiting the number of
2574fad09c73SVivien Didelot 		 * address database entries that this port is allowed
2575fad09c73SVivien Didelot 		 * to use.
2576fad09c73SVivien Didelot 		 */
25770e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL,
25780e7b9925SAndrew Lunn 					   0x0000);
2579fad09c73SVivien Didelot 		/* Priority Override: disable DA, SA and VTU priority
2580fad09c73SVivien Didelot 		 * override.
2581fad09c73SVivien Didelot 		 */
25820e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE,
25830e7b9925SAndrew Lunn 					   0x0000);
25840e7b9925SAndrew Lunn 		if (err)
25850e7b9925SAndrew Lunn 			return err;
2586fad09c73SVivien Didelot 
2587fad09c73SVivien Didelot 		/* Port Ethertype: use the Ethertype DSA Ethertype
2588fad09c73SVivien Didelot 		 * value.
2589fad09c73SVivien Didelot 		 */
25902bbb33beSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) {
25910e7b9925SAndrew Lunn 			err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE,
25920e7b9925SAndrew Lunn 						   ETH_P_EDSA);
25930e7b9925SAndrew Lunn 			if (err)
25940e7b9925SAndrew Lunn 				return err;
25952bbb33beSAndrew Lunn 		}
25962bbb33beSAndrew Lunn 
2597fad09c73SVivien Didelot 		/* Tag Remap: use an identity 802.1p prio -> switch
2598fad09c73SVivien Didelot 		 * prio mapping.
2599fad09c73SVivien Didelot 		 */
26000e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123,
26010e7b9925SAndrew Lunn 					   0x3210);
26020e7b9925SAndrew Lunn 		if (err)
26030e7b9925SAndrew Lunn 			return err;
2604fad09c73SVivien Didelot 
2605fad09c73SVivien Didelot 		/* Tag Remap 2: use an identity 802.1p prio -> switch
2606fad09c73SVivien Didelot 		 * prio mapping.
2607fad09c73SVivien Didelot 		 */
26080e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567,
26090e7b9925SAndrew Lunn 					   0x7654);
26100e7b9925SAndrew Lunn 		if (err)
26110e7b9925SAndrew Lunn 			return err;
2612fad09c73SVivien Didelot 	}
2613fad09c73SVivien Didelot 
26141bc261faSJamie Lentin 	/* Rate Control: disable ingress rate limiting. */
2615fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2616fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2617fad09c73SVivien Didelot 	    mv88e6xxx_6320_family(chip)) {
26180e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
26190e7b9925SAndrew Lunn 					   0x0001);
26200e7b9925SAndrew Lunn 		if (err)
26210e7b9925SAndrew Lunn 			return err;
26221bc261faSJamie Lentin 	} else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) {
26230e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
26240e7b9925SAndrew Lunn 					   0x0000);
26250e7b9925SAndrew Lunn 		if (err)
26260e7b9925SAndrew Lunn 			return err;
2627fad09c73SVivien Didelot 	}
2628fad09c73SVivien Didelot 
2629fad09c73SVivien Didelot 	/* Port Control 1: disable trunking, disable sending
2630fad09c73SVivien Didelot 	 * learning messages to this port.
2631fad09c73SVivien Didelot 	 */
26320e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000);
26330e7b9925SAndrew Lunn 	if (err)
26340e7b9925SAndrew Lunn 		return err;
2635fad09c73SVivien Didelot 
2636fad09c73SVivien Didelot 	/* Port based VLAN map: give each port the same default address
2637fad09c73SVivien Didelot 	 * database, and allow bidirectional communication between the
2638fad09c73SVivien Didelot 	 * CPU and DSA port(s), and the other ports.
2639fad09c73SVivien Didelot 	 */
2640b4e48c50SVivien Didelot 	err = mv88e6xxx_port_set_fid(chip, port, 0);
26410e7b9925SAndrew Lunn 	if (err)
26420e7b9925SAndrew Lunn 		return err;
2643fad09c73SVivien Didelot 
26440e7b9925SAndrew Lunn 	err = _mv88e6xxx_port_based_vlan_map(chip, port);
26450e7b9925SAndrew Lunn 	if (err)
26460e7b9925SAndrew Lunn 		return err;
2647fad09c73SVivien Didelot 
2648fad09c73SVivien Didelot 	/* Default VLAN ID and priority: don't set a default VLAN
2649fad09c73SVivien Didelot 	 * ID, and set the default packet priority to zero.
2650fad09c73SVivien Didelot 	 */
26510e7b9925SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
2652fad09c73SVivien Didelot }
2653fad09c73SVivien Didelot 
2654aa0938c6SWei Yongjun static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
26553b4caa1bSVivien Didelot {
26563b4caa1bSVivien Didelot 	int err;
26573b4caa1bSVivien Didelot 
2658a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
26593b4caa1bSVivien Didelot 	if (err)
26603b4caa1bSVivien Didelot 		return err;
26613b4caa1bSVivien Didelot 
2662a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
26633b4caa1bSVivien Didelot 	if (err)
26643b4caa1bSVivien Didelot 		return err;
26653b4caa1bSVivien Didelot 
2666a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
2667a935c052SVivien Didelot 	if (err)
2668a935c052SVivien Didelot 		return err;
2669a935c052SVivien Didelot 
2670a935c052SVivien Didelot 	return 0;
26713b4caa1bSVivien Didelot }
26723b4caa1bSVivien Didelot 
2673acddbd21SVivien Didelot static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
2674acddbd21SVivien Didelot 				     unsigned int msecs)
2675acddbd21SVivien Didelot {
2676acddbd21SVivien Didelot 	const unsigned int coeff = chip->info->age_time_coeff;
2677acddbd21SVivien Didelot 	const unsigned int min = 0x01 * coeff;
2678acddbd21SVivien Didelot 	const unsigned int max = 0xff * coeff;
2679acddbd21SVivien Didelot 	u8 age_time;
2680acddbd21SVivien Didelot 	u16 val;
2681acddbd21SVivien Didelot 	int err;
2682acddbd21SVivien Didelot 
2683acddbd21SVivien Didelot 	if (msecs < min || msecs > max)
2684acddbd21SVivien Didelot 		return -ERANGE;
2685acddbd21SVivien Didelot 
2686acddbd21SVivien Didelot 	/* Round to nearest multiple of coeff */
2687acddbd21SVivien Didelot 	age_time = (msecs + coeff / 2) / coeff;
2688acddbd21SVivien Didelot 
2689a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
2690acddbd21SVivien Didelot 	if (err)
2691acddbd21SVivien Didelot 		return err;
2692acddbd21SVivien Didelot 
2693acddbd21SVivien Didelot 	/* AgeTime is 11:4 bits */
2694acddbd21SVivien Didelot 	val &= ~0xff0;
2695acddbd21SVivien Didelot 	val |= age_time << 4;
2696acddbd21SVivien Didelot 
2697a935c052SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
2698acddbd21SVivien Didelot }
2699acddbd21SVivien Didelot 
27002cfcd964SVivien Didelot static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
27012cfcd964SVivien Didelot 				     unsigned int ageing_time)
27022cfcd964SVivien Didelot {
270304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
27042cfcd964SVivien Didelot 	int err;
27052cfcd964SVivien Didelot 
27062cfcd964SVivien Didelot 	mutex_lock(&chip->reg_lock);
27072cfcd964SVivien Didelot 	err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
27082cfcd964SVivien Didelot 	mutex_unlock(&chip->reg_lock);
27092cfcd964SVivien Didelot 
27102cfcd964SVivien Didelot 	return err;
27112cfcd964SVivien Didelot }
27122cfcd964SVivien Didelot 
27139729934cSVivien Didelot static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
2714fad09c73SVivien Didelot {
2715fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
2716fad09c73SVivien Didelot 	u32 upstream_port = dsa_upstream_port(ds);
2717fad09c73SVivien Didelot 	u16 reg;
2718fad09c73SVivien Didelot 	int err;
2719fad09c73SVivien Didelot 
2720fad09c73SVivien Didelot 	/* Enable the PHY Polling Unit if present, don't discard any packets,
2721fad09c73SVivien Didelot 	 * and mask all interrupt sources.
2722fad09c73SVivien Didelot 	 */
2723dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
2724dc30c35bSAndrew Lunn 	if (err < 0)
2725dc30c35bSAndrew Lunn 		return err;
2726dc30c35bSAndrew Lunn 
2727dc30c35bSAndrew Lunn 	reg &= ~GLOBAL_CONTROL_PPU_ENABLE;
2728fad09c73SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
2729fad09c73SVivien Didelot 	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
2730fad09c73SVivien Didelot 		reg |= GLOBAL_CONTROL_PPU_ENABLE;
2731fad09c73SVivien Didelot 
2732a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
2733fad09c73SVivien Didelot 	if (err)
2734fad09c73SVivien Didelot 		return err;
2735fad09c73SVivien Didelot 
2736fad09c73SVivien Didelot 	/* Configure the upstream port, and configure it as the port to which
2737fad09c73SVivien Didelot 	 * ingress and egress and ARP monitor frames are to be sent.
2738fad09c73SVivien Didelot 	 */
2739fad09c73SVivien Didelot 	reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2740fad09c73SVivien Didelot 		upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2741fad09c73SVivien Didelot 		upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2742a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
2743fad09c73SVivien Didelot 	if (err)
2744fad09c73SVivien Didelot 		return err;
2745fad09c73SVivien Didelot 
2746fad09c73SVivien Didelot 	/* Disable remote management, and set the switch's DSA device number. */
2747a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
2748fad09c73SVivien Didelot 				 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2749fad09c73SVivien Didelot 				 (ds->index & 0x1f));
2750fad09c73SVivien Didelot 	if (err)
2751fad09c73SVivien Didelot 		return err;
2752fad09c73SVivien Didelot 
2753acddbd21SVivien Didelot 	/* Clear all the VTU and STU entries */
2754acddbd21SVivien Didelot 	err = _mv88e6xxx_vtu_stu_flush(chip);
2755acddbd21SVivien Didelot 	if (err < 0)
2756acddbd21SVivien Didelot 		return err;
2757acddbd21SVivien Didelot 
2758fad09c73SVivien Didelot 	/* Set the default address aging time to 5 minutes, and
2759fad09c73SVivien Didelot 	 * enable address learn messages to be sent to all message
2760fad09c73SVivien Didelot 	 * ports.
2761fad09c73SVivien Didelot 	 */
2762a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
2763acddbd21SVivien Didelot 				 GLOBAL_ATU_CONTROL_LEARN2ALL);
2764fad09c73SVivien Didelot 	if (err)
2765fad09c73SVivien Didelot 		return err;
2766fad09c73SVivien Didelot 
2767acddbd21SVivien Didelot 	err = mv88e6xxx_g1_set_age_time(chip, 300000);
2768acddbd21SVivien Didelot 	if (err)
27699729934cSVivien Didelot 		return err;
27709729934cSVivien Didelot 
27719729934cSVivien Didelot 	/* Clear all ATU entries */
27729729934cSVivien Didelot 	err = _mv88e6xxx_atu_flush(chip, 0, true);
27739729934cSVivien Didelot 	if (err)
27749729934cSVivien Didelot 		return err;
27759729934cSVivien Didelot 
2776fad09c73SVivien Didelot 	/* Configure the IP ToS mapping registers. */
2777a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
2778fad09c73SVivien Didelot 	if (err)
2779fad09c73SVivien Didelot 		return err;
2780a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
2781fad09c73SVivien Didelot 	if (err)
2782fad09c73SVivien Didelot 		return err;
2783a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
2784fad09c73SVivien Didelot 	if (err)
2785fad09c73SVivien Didelot 		return err;
2786a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
2787fad09c73SVivien Didelot 	if (err)
2788fad09c73SVivien Didelot 		return err;
2789a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
2790fad09c73SVivien Didelot 	if (err)
2791fad09c73SVivien Didelot 		return err;
2792a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
2793fad09c73SVivien Didelot 	if (err)
2794fad09c73SVivien Didelot 		return err;
2795a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
2796fad09c73SVivien Didelot 	if (err)
2797fad09c73SVivien Didelot 		return err;
2798a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
2799fad09c73SVivien Didelot 	if (err)
2800fad09c73SVivien Didelot 		return err;
2801fad09c73SVivien Didelot 
2802fad09c73SVivien Didelot 	/* Configure the IEEE 802.1p priority mapping register. */
2803a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
2804fad09c73SVivien Didelot 	if (err)
2805fad09c73SVivien Didelot 		return err;
2806fad09c73SVivien Didelot 
28079729934cSVivien Didelot 	/* Clear the statistics counters for all ports */
2808a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
28099729934cSVivien Didelot 				 GLOBAL_STATS_OP_FLUSH_ALL);
28109729934cSVivien Didelot 	if (err)
28119729934cSVivien Didelot 		return err;
28129729934cSVivien Didelot 
28139729934cSVivien Didelot 	/* Wait for the flush to complete. */
28149729934cSVivien Didelot 	err = _mv88e6xxx_stats_wait(chip);
28159729934cSVivien Didelot 	if (err)
28169729934cSVivien Didelot 		return err;
28179729934cSVivien Didelot 
28189729934cSVivien Didelot 	return 0;
28199729934cSVivien Didelot }
28209729934cSVivien Didelot 
2821fad09c73SVivien Didelot static int mv88e6xxx_setup(struct dsa_switch *ds)
2822fad09c73SVivien Didelot {
282304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2824fad09c73SVivien Didelot 	int err;
2825fad09c73SVivien Didelot 	int i;
2826fad09c73SVivien Didelot 
2827fad09c73SVivien Didelot 	chip->ds = ds;
2828fad09c73SVivien Didelot 	ds->slave_mii_bus = chip->mdio_bus;
2829fad09c73SVivien Didelot 
2830fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2831fad09c73SVivien Didelot 
28329729934cSVivien Didelot 	/* Setup Switch Port Registers */
2833370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
28349729934cSVivien Didelot 		err = mv88e6xxx_setup_port(chip, i);
28359729934cSVivien Didelot 		if (err)
28369729934cSVivien Didelot 			goto unlock;
28379729934cSVivien Didelot 	}
28389729934cSVivien Didelot 
28399729934cSVivien Didelot 	/* Setup Switch Global 1 Registers */
28409729934cSVivien Didelot 	err = mv88e6xxx_g1_setup(chip);
2841fad09c73SVivien Didelot 	if (err)
2842fad09c73SVivien Didelot 		goto unlock;
2843fad09c73SVivien Didelot 
28449729934cSVivien Didelot 	/* Setup Switch Global 2 Registers */
28459729934cSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
28469729934cSVivien Didelot 		err = mv88e6xxx_g2_setup(chip);
2847fad09c73SVivien Didelot 		if (err)
2848fad09c73SVivien Didelot 			goto unlock;
2849fad09c73SVivien Didelot 	}
2850fad09c73SVivien Didelot 
2851fad09c73SVivien Didelot unlock:
2852fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2853fad09c73SVivien Didelot 
2854fad09c73SVivien Didelot 	return err;
2855fad09c73SVivien Didelot }
2856fad09c73SVivien Didelot 
28573b4caa1bSVivien Didelot static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
28583b4caa1bSVivien Didelot {
285904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
28603b4caa1bSVivien Didelot 	int err;
28613b4caa1bSVivien Didelot 
2862b073d4e2SVivien Didelot 	if (!chip->info->ops->set_switch_mac)
2863b073d4e2SVivien Didelot 		return -EOPNOTSUPP;
2864b073d4e2SVivien Didelot 
28653b4caa1bSVivien Didelot 	mutex_lock(&chip->reg_lock);
2866b073d4e2SVivien Didelot 	err = chip->info->ops->set_switch_mac(chip, addr);
28673b4caa1bSVivien Didelot 	mutex_unlock(&chip->reg_lock);
28683b4caa1bSVivien Didelot 
28693b4caa1bSVivien Didelot 	return err;
28703b4caa1bSVivien Didelot }
28713b4caa1bSVivien Didelot 
2872e57e5e77SVivien Didelot static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
2873fad09c73SVivien Didelot {
2874fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip = bus->priv;
2875e57e5e77SVivien Didelot 	u16 val;
2876e57e5e77SVivien Didelot 	int err;
2877fad09c73SVivien Didelot 
2878370b4ffbSVivien Didelot 	if (phy >= mv88e6xxx_num_ports(chip))
2879fad09c73SVivien Didelot 		return 0xffff;
2880fad09c73SVivien Didelot 
2881fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2882e57e5e77SVivien Didelot 	err = mv88e6xxx_phy_read(chip, phy, reg, &val);
2883fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2884e57e5e77SVivien Didelot 
2885e57e5e77SVivien Didelot 	return err ? err : val;
2886fad09c73SVivien Didelot }
2887fad09c73SVivien Didelot 
2888e57e5e77SVivien Didelot static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
2889fad09c73SVivien Didelot {
2890fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip = bus->priv;
2891e57e5e77SVivien Didelot 	int err;
2892fad09c73SVivien Didelot 
2893370b4ffbSVivien Didelot 	if (phy >= mv88e6xxx_num_ports(chip))
2894fad09c73SVivien Didelot 		return 0xffff;
2895fad09c73SVivien Didelot 
2896fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2897e57e5e77SVivien Didelot 	err = mv88e6xxx_phy_write(chip, phy, reg, val);
2898fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2899e57e5e77SVivien Didelot 
2900e57e5e77SVivien Didelot 	return err;
2901fad09c73SVivien Didelot }
2902fad09c73SVivien Didelot 
2903fad09c73SVivien Didelot static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
2904fad09c73SVivien Didelot 				   struct device_node *np)
2905fad09c73SVivien Didelot {
2906fad09c73SVivien Didelot 	static int index;
2907fad09c73SVivien Didelot 	struct mii_bus *bus;
2908fad09c73SVivien Didelot 	int err;
2909fad09c73SVivien Didelot 
2910fad09c73SVivien Didelot 	if (np)
2911fad09c73SVivien Didelot 		chip->mdio_np = of_get_child_by_name(np, "mdio");
2912fad09c73SVivien Didelot 
2913fad09c73SVivien Didelot 	bus = devm_mdiobus_alloc(chip->dev);
2914fad09c73SVivien Didelot 	if (!bus)
2915fad09c73SVivien Didelot 		return -ENOMEM;
2916fad09c73SVivien Didelot 
2917fad09c73SVivien Didelot 	bus->priv = (void *)chip;
2918fad09c73SVivien Didelot 	if (np) {
2919fad09c73SVivien Didelot 		bus->name = np->full_name;
2920fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
2921fad09c73SVivien Didelot 	} else {
2922fad09c73SVivien Didelot 		bus->name = "mv88e6xxx SMI";
2923fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
2924fad09c73SVivien Didelot 	}
2925fad09c73SVivien Didelot 
2926fad09c73SVivien Didelot 	bus->read = mv88e6xxx_mdio_read;
2927fad09c73SVivien Didelot 	bus->write = mv88e6xxx_mdio_write;
2928fad09c73SVivien Didelot 	bus->parent = chip->dev;
2929fad09c73SVivien Didelot 
2930fad09c73SVivien Didelot 	if (chip->mdio_np)
2931fad09c73SVivien Didelot 		err = of_mdiobus_register(bus, chip->mdio_np);
2932fad09c73SVivien Didelot 	else
2933fad09c73SVivien Didelot 		err = mdiobus_register(bus);
2934fad09c73SVivien Didelot 	if (err) {
2935fad09c73SVivien Didelot 		dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
2936fad09c73SVivien Didelot 		goto out;
2937fad09c73SVivien Didelot 	}
2938fad09c73SVivien Didelot 	chip->mdio_bus = bus;
2939fad09c73SVivien Didelot 
2940fad09c73SVivien Didelot 	return 0;
2941fad09c73SVivien Didelot 
2942fad09c73SVivien Didelot out:
2943fad09c73SVivien Didelot 	if (chip->mdio_np)
2944fad09c73SVivien Didelot 		of_node_put(chip->mdio_np);
2945fad09c73SVivien Didelot 
2946fad09c73SVivien Didelot 	return err;
2947fad09c73SVivien Didelot }
2948fad09c73SVivien Didelot 
2949fad09c73SVivien Didelot static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
2950fad09c73SVivien Didelot 
2951fad09c73SVivien Didelot {
2952fad09c73SVivien Didelot 	struct mii_bus *bus = chip->mdio_bus;
2953fad09c73SVivien Didelot 
2954fad09c73SVivien Didelot 	mdiobus_unregister(bus);
2955fad09c73SVivien Didelot 
2956fad09c73SVivien Didelot 	if (chip->mdio_np)
2957fad09c73SVivien Didelot 		of_node_put(chip->mdio_np);
2958fad09c73SVivien Didelot }
2959fad09c73SVivien Didelot 
2960fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON
2961fad09c73SVivien Didelot 
2962fad09c73SVivien Didelot static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
2963fad09c73SVivien Didelot {
296404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
29659c93829cSVivien Didelot 	u16 val;
2966fad09c73SVivien Didelot 	int ret;
2967fad09c73SVivien Didelot 
2968fad09c73SVivien Didelot 	*temp = 0;
2969fad09c73SVivien Didelot 
2970fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2971fad09c73SVivien Didelot 
29729c93829cSVivien Didelot 	ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
2973fad09c73SVivien Didelot 	if (ret < 0)
2974fad09c73SVivien Didelot 		goto error;
2975fad09c73SVivien Didelot 
2976fad09c73SVivien Didelot 	/* Enable temperature sensor */
29779c93829cSVivien Didelot 	ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
2978fad09c73SVivien Didelot 	if (ret < 0)
2979fad09c73SVivien Didelot 		goto error;
2980fad09c73SVivien Didelot 
29819c93829cSVivien Didelot 	ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
2982fad09c73SVivien Didelot 	if (ret < 0)
2983fad09c73SVivien Didelot 		goto error;
2984fad09c73SVivien Didelot 
2985fad09c73SVivien Didelot 	/* Wait for temperature to stabilize */
2986fad09c73SVivien Didelot 	usleep_range(10000, 12000);
2987fad09c73SVivien Didelot 
29889c93829cSVivien Didelot 	ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
29899c93829cSVivien Didelot 	if (ret < 0)
2990fad09c73SVivien Didelot 		goto error;
2991fad09c73SVivien Didelot 
2992fad09c73SVivien Didelot 	/* Disable temperature sensor */
29939c93829cSVivien Didelot 	ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
2994fad09c73SVivien Didelot 	if (ret < 0)
2995fad09c73SVivien Didelot 		goto error;
2996fad09c73SVivien Didelot 
2997fad09c73SVivien Didelot 	*temp = ((val & 0x1f) - 5) * 5;
2998fad09c73SVivien Didelot 
2999fad09c73SVivien Didelot error:
30009c93829cSVivien Didelot 	mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
3001fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3002fad09c73SVivien Didelot 	return ret;
3003fad09c73SVivien Didelot }
3004fad09c73SVivien Didelot 
3005fad09c73SVivien Didelot static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3006fad09c73SVivien Didelot {
300704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3008fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
30099c93829cSVivien Didelot 	u16 val;
3010fad09c73SVivien Didelot 	int ret;
3011fad09c73SVivien Didelot 
3012fad09c73SVivien Didelot 	*temp = 0;
3013fad09c73SVivien Didelot 
30149c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
30159c93829cSVivien Didelot 	ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
30169c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
3017fad09c73SVivien Didelot 	if (ret < 0)
3018fad09c73SVivien Didelot 		return ret;
3019fad09c73SVivien Didelot 
30209c93829cSVivien Didelot 	*temp = (val & 0xff) - 25;
3021fad09c73SVivien Didelot 
3022fad09c73SVivien Didelot 	return 0;
3023fad09c73SVivien Didelot }
3024fad09c73SVivien Didelot 
3025fad09c73SVivien Didelot static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
3026fad09c73SVivien Didelot {
302704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3028fad09c73SVivien Didelot 
3029fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
3030fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3031fad09c73SVivien Didelot 
3032fad09c73SVivien Didelot 	if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
3033fad09c73SVivien Didelot 		return mv88e63xx_get_temp(ds, temp);
3034fad09c73SVivien Didelot 
3035fad09c73SVivien Didelot 	return mv88e61xx_get_temp(ds, temp);
3036fad09c73SVivien Didelot }
3037fad09c73SVivien Didelot 
3038fad09c73SVivien Didelot static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
3039fad09c73SVivien Didelot {
304004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3041fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
30429c93829cSVivien Didelot 	u16 val;
3043fad09c73SVivien Didelot 	int ret;
3044fad09c73SVivien Didelot 
3045fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
3046fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3047fad09c73SVivien Didelot 
3048fad09c73SVivien Didelot 	*temp = 0;
3049fad09c73SVivien Didelot 
30509c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
30519c93829cSVivien Didelot 	ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
30529c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
3053fad09c73SVivien Didelot 	if (ret < 0)
3054fad09c73SVivien Didelot 		return ret;
3055fad09c73SVivien Didelot 
30569c93829cSVivien Didelot 	*temp = (((val >> 8) & 0x1f) * 5) - 25;
3057fad09c73SVivien Didelot 
3058fad09c73SVivien Didelot 	return 0;
3059fad09c73SVivien Didelot }
3060fad09c73SVivien Didelot 
3061fad09c73SVivien Didelot static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
3062fad09c73SVivien Didelot {
306304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3064fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
30659c93829cSVivien Didelot 	u16 val;
30669c93829cSVivien Didelot 	int err;
3067fad09c73SVivien Didelot 
3068fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
3069fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3070fad09c73SVivien Didelot 
30719c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
30729c93829cSVivien Didelot 	err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
30739c93829cSVivien Didelot 	if (err)
30749c93829cSVivien Didelot 		goto unlock;
3075fad09c73SVivien Didelot 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
30769c93829cSVivien Didelot 	err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
30779c93829cSVivien Didelot 				       (val & 0xe0ff) | (temp << 8));
30789c93829cSVivien Didelot unlock:
30799c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
30809c93829cSVivien Didelot 
30819c93829cSVivien Didelot 	return err;
3082fad09c73SVivien Didelot }
3083fad09c73SVivien Didelot 
3084fad09c73SVivien Didelot static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
3085fad09c73SVivien Didelot {
308604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3087fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
30889c93829cSVivien Didelot 	u16 val;
3089fad09c73SVivien Didelot 	int ret;
3090fad09c73SVivien Didelot 
3091fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
3092fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3093fad09c73SVivien Didelot 
3094fad09c73SVivien Didelot 	*alarm = false;
3095fad09c73SVivien Didelot 
30969c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
30979c93829cSVivien Didelot 	ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
30989c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
3099fad09c73SVivien Didelot 	if (ret < 0)
3100fad09c73SVivien Didelot 		return ret;
3101fad09c73SVivien Didelot 
31029c93829cSVivien Didelot 	*alarm = !!(val & 0x40);
3103fad09c73SVivien Didelot 
3104fad09c73SVivien Didelot 	return 0;
3105fad09c73SVivien Didelot }
3106fad09c73SVivien Didelot #endif /* CONFIG_NET_DSA_HWMON */
3107fad09c73SVivien Didelot 
3108855b1932SVivien Didelot static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
3109855b1932SVivien Didelot {
311004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3111855b1932SVivien Didelot 
3112855b1932SVivien Didelot 	return chip->eeprom_len;
3113855b1932SVivien Didelot }
3114855b1932SVivien Didelot 
3115855b1932SVivien Didelot static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
3116855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
3117855b1932SVivien Didelot {
311804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3119855b1932SVivien Didelot 	int err;
3120855b1932SVivien Didelot 
3121ee4dc2e7SVivien Didelot 	if (!chip->info->ops->get_eeprom)
3122ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
3123ee4dc2e7SVivien Didelot 
3124855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
3125ee4dc2e7SVivien Didelot 	err = chip->info->ops->get_eeprom(chip, eeprom, data);
3126855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3127855b1932SVivien Didelot 
3128855b1932SVivien Didelot 	if (err)
3129855b1932SVivien Didelot 		return err;
3130855b1932SVivien Didelot 
3131855b1932SVivien Didelot 	eeprom->magic = 0xc3ec4951;
3132855b1932SVivien Didelot 
3133855b1932SVivien Didelot 	return 0;
3134855b1932SVivien Didelot }
3135855b1932SVivien Didelot 
3136855b1932SVivien Didelot static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
3137855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
3138855b1932SVivien Didelot {
313904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3140855b1932SVivien Didelot 	int err;
3141855b1932SVivien Didelot 
3142ee4dc2e7SVivien Didelot 	if (!chip->info->ops->set_eeprom)
3143ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
3144ee4dc2e7SVivien Didelot 
3145855b1932SVivien Didelot 	if (eeprom->magic != 0xc3ec4951)
3146855b1932SVivien Didelot 		return -EINVAL;
3147855b1932SVivien Didelot 
3148855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
3149ee4dc2e7SVivien Didelot 	err = chip->info->ops->set_eeprom(chip, eeprom, data);
3150855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3151855b1932SVivien Didelot 
3152855b1932SVivien Didelot 	return err;
3153855b1932SVivien Didelot }
3154855b1932SVivien Didelot 
3155b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6085_ops = {
3156b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3157b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3158b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
315908ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
31607f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
316196a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3162b3469dd8SVivien Didelot };
3163b3469dd8SVivien Didelot 
3164b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6095_ops = {
3165b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3166b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3167b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
316808ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
31697f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
317096a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3171b3469dd8SVivien Didelot };
3172b3469dd8SVivien Didelot 
3173b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6123_ops = {
3174b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3175b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_read,
3176b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_write,
317708ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
31787f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
317996a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3180b3469dd8SVivien Didelot };
3181b3469dd8SVivien Didelot 
3182b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6131_ops = {
3183b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3184b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3185b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
318608ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
31877f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
318896a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3189b3469dd8SVivien Didelot };
3190b3469dd8SVivien Didelot 
3191b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6161_ops = {
3192b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3193b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_read,
3194b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_write,
319508ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
31967f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
319796a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3198b3469dd8SVivien Didelot };
3199b3469dd8SVivien Didelot 
3200b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6165_ops = {
3201b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3202b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_read,
3203b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_write,
320408ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32057f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
320696a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3207b3469dd8SVivien Didelot };
3208b3469dd8SVivien Didelot 
3209b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6171_ops = {
3210b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3211b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3212b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
321308ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32147f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
321594d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
321696a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3217b3469dd8SVivien Didelot };
3218b3469dd8SVivien Didelot 
3219b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6172_ops = {
3220ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3221ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3222b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3223b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3224b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
322508ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32267f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
3227a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
322896a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
3229b3469dd8SVivien Didelot };
3230b3469dd8SVivien Didelot 
3231b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6175_ops = {
3232b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3233b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3234b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
323508ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32367f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
323794d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
323896a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3239b3469dd8SVivien Didelot };
3240b3469dd8SVivien Didelot 
3241b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6176_ops = {
3242ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3243ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3244b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3245b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3246b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
324708ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32487f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
3249a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
325096a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
3251b3469dd8SVivien Didelot };
3252b3469dd8SVivien Didelot 
3253b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6185_ops = {
3254b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3255b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3256b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
325708ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32587f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
325996a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3260b3469dd8SVivien Didelot };
3261b3469dd8SVivien Didelot 
3262b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6240_ops = {
3263ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3264ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3265b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3266b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3267b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
326808ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32697f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
3270a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
327196a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
3272b3469dd8SVivien Didelot };
3273b3469dd8SVivien Didelot 
3274b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6320_ops = {
3275ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3276ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3277b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3278b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3279b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
328008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32817f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
328296a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3283b3469dd8SVivien Didelot };
3284b3469dd8SVivien Didelot 
3285b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6321_ops = {
3286ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3287ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3288b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3289b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3290b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
329108ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
32927f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
329396a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3294b3469dd8SVivien Didelot };
3295b3469dd8SVivien Didelot 
3296b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6350_ops = {
3297b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3298b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3299b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
330008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
33017f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
330294d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
330396a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3304b3469dd8SVivien Didelot };
3305b3469dd8SVivien Didelot 
3306b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6351_ops = {
3307b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3308b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3309b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
331008ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
33117f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
331294d66ae6SAndrew Lunn 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
331396a2b40cSVivien Didelot 	.port_set_speed = mv88e6185_port_set_speed,
3314b3469dd8SVivien Didelot };
3315b3469dd8SVivien Didelot 
3316b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6352_ops = {
3317ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3318ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3319b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3320b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3321b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
332208ef7f10SVivien Didelot 	.port_set_link = mv88e6xxx_port_set_link,
33237f1ae07bSVivien Didelot 	.port_set_duplex = mv88e6xxx_port_set_duplex,
3324a0a0f622SVivien Didelot 	.port_set_rgmii_delay = mv88e6352_port_set_rgmii_delay,
332596a2b40cSVivien Didelot 	.port_set_speed = mv88e6352_port_set_speed,
3326b3469dd8SVivien Didelot };
3327b3469dd8SVivien Didelot 
3328fad09c73SVivien Didelot static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3329fad09c73SVivien Didelot 	[MV88E6085] = {
3330fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3331fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6097,
3332fad09c73SVivien Didelot 		.name = "Marvell 88E6085",
3333fad09c73SVivien Didelot 		.num_databases = 4096,
3334fad09c73SVivien Didelot 		.num_ports = 10,
3335fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3336a935c052SVivien Didelot 		.global1_addr = 0x1b,
3337acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3338dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3339fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
3340b3469dd8SVivien Didelot 		.ops = &mv88e6085_ops,
3341fad09c73SVivien Didelot 	},
3342fad09c73SVivien Didelot 
3343fad09c73SVivien Didelot 	[MV88E6095] = {
3344fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3345fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6095,
3346fad09c73SVivien Didelot 		.name = "Marvell 88E6095/88E6095F",
3347fad09c73SVivien Didelot 		.num_databases = 256,
3348fad09c73SVivien Didelot 		.num_ports = 11,
3349fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3350a935c052SVivien Didelot 		.global1_addr = 0x1b,
3351acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3352dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3353fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6095,
3354b3469dd8SVivien Didelot 		.ops = &mv88e6095_ops,
3355fad09c73SVivien Didelot 	},
3356fad09c73SVivien Didelot 
3357fad09c73SVivien Didelot 	[MV88E6123] = {
3358fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3359fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3360fad09c73SVivien Didelot 		.name = "Marvell 88E6123",
3361fad09c73SVivien Didelot 		.num_databases = 4096,
3362fad09c73SVivien Didelot 		.num_ports = 3,
3363fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3364a935c052SVivien Didelot 		.global1_addr = 0x1b,
3365acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3366dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3367fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3368b3469dd8SVivien Didelot 		.ops = &mv88e6123_ops,
3369fad09c73SVivien Didelot 	},
3370fad09c73SVivien Didelot 
3371fad09c73SVivien Didelot 	[MV88E6131] = {
3372fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3373fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3374fad09c73SVivien Didelot 		.name = "Marvell 88E6131",
3375fad09c73SVivien Didelot 		.num_databases = 256,
3376fad09c73SVivien Didelot 		.num_ports = 8,
3377fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3378a935c052SVivien Didelot 		.global1_addr = 0x1b,
3379acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3380dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3381fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3382b3469dd8SVivien Didelot 		.ops = &mv88e6131_ops,
3383fad09c73SVivien Didelot 	},
3384fad09c73SVivien Didelot 
3385fad09c73SVivien Didelot 	[MV88E6161] = {
3386fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3387fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3388fad09c73SVivien Didelot 		.name = "Marvell 88E6161",
3389fad09c73SVivien Didelot 		.num_databases = 4096,
3390fad09c73SVivien Didelot 		.num_ports = 6,
3391fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3392a935c052SVivien Didelot 		.global1_addr = 0x1b,
3393acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3394dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3395fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3396b3469dd8SVivien Didelot 		.ops = &mv88e6161_ops,
3397fad09c73SVivien Didelot 	},
3398fad09c73SVivien Didelot 
3399fad09c73SVivien Didelot 	[MV88E6165] = {
3400fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3401fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3402fad09c73SVivien Didelot 		.name = "Marvell 88E6165",
3403fad09c73SVivien Didelot 		.num_databases = 4096,
3404fad09c73SVivien Didelot 		.num_ports = 6,
3405fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3406a935c052SVivien Didelot 		.global1_addr = 0x1b,
3407acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3408dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3409fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3410b3469dd8SVivien Didelot 		.ops = &mv88e6165_ops,
3411fad09c73SVivien Didelot 	},
3412fad09c73SVivien Didelot 
3413fad09c73SVivien Didelot 	[MV88E6171] = {
3414fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3415fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3416fad09c73SVivien Didelot 		.name = "Marvell 88E6171",
3417fad09c73SVivien Didelot 		.num_databases = 4096,
3418fad09c73SVivien Didelot 		.num_ports = 7,
3419fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3420a935c052SVivien Didelot 		.global1_addr = 0x1b,
3421acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3422dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3423fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3424b3469dd8SVivien Didelot 		.ops = &mv88e6171_ops,
3425fad09c73SVivien Didelot 	},
3426fad09c73SVivien Didelot 
3427fad09c73SVivien Didelot 	[MV88E6172] = {
3428fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3429fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3430fad09c73SVivien Didelot 		.name = "Marvell 88E6172",
3431fad09c73SVivien Didelot 		.num_databases = 4096,
3432fad09c73SVivien Didelot 		.num_ports = 7,
3433fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3434a935c052SVivien Didelot 		.global1_addr = 0x1b,
3435acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3436dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3437fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3438b3469dd8SVivien Didelot 		.ops = &mv88e6172_ops,
3439fad09c73SVivien Didelot 	},
3440fad09c73SVivien Didelot 
3441fad09c73SVivien Didelot 	[MV88E6175] = {
3442fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3443fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3444fad09c73SVivien Didelot 		.name = "Marvell 88E6175",
3445fad09c73SVivien Didelot 		.num_databases = 4096,
3446fad09c73SVivien Didelot 		.num_ports = 7,
3447fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3448a935c052SVivien Didelot 		.global1_addr = 0x1b,
3449acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3450dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3451fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3452b3469dd8SVivien Didelot 		.ops = &mv88e6175_ops,
3453fad09c73SVivien Didelot 	},
3454fad09c73SVivien Didelot 
3455fad09c73SVivien Didelot 	[MV88E6176] = {
3456fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3457fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3458fad09c73SVivien Didelot 		.name = "Marvell 88E6176",
3459fad09c73SVivien Didelot 		.num_databases = 4096,
3460fad09c73SVivien Didelot 		.num_ports = 7,
3461fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3462a935c052SVivien Didelot 		.global1_addr = 0x1b,
3463acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3464dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3465fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3466b3469dd8SVivien Didelot 		.ops = &mv88e6176_ops,
3467fad09c73SVivien Didelot 	},
3468fad09c73SVivien Didelot 
3469fad09c73SVivien Didelot 	[MV88E6185] = {
3470fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3471fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3472fad09c73SVivien Didelot 		.name = "Marvell 88E6185",
3473fad09c73SVivien Didelot 		.num_databases = 256,
3474fad09c73SVivien Didelot 		.num_ports = 10,
3475fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3476a935c052SVivien Didelot 		.global1_addr = 0x1b,
3477acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3478dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3479fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3480b3469dd8SVivien Didelot 		.ops = &mv88e6185_ops,
3481fad09c73SVivien Didelot 	},
3482fad09c73SVivien Didelot 
3483fad09c73SVivien Didelot 	[MV88E6240] = {
3484fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3485fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3486fad09c73SVivien Didelot 		.name = "Marvell 88E6240",
3487fad09c73SVivien Didelot 		.num_databases = 4096,
3488fad09c73SVivien Didelot 		.num_ports = 7,
3489fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3490a935c052SVivien Didelot 		.global1_addr = 0x1b,
3491acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3492dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3493fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3494b3469dd8SVivien Didelot 		.ops = &mv88e6240_ops,
3495fad09c73SVivien Didelot 	},
3496fad09c73SVivien Didelot 
3497fad09c73SVivien Didelot 	[MV88E6320] = {
3498fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3499fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3500fad09c73SVivien Didelot 		.name = "Marvell 88E6320",
3501fad09c73SVivien Didelot 		.num_databases = 4096,
3502fad09c73SVivien Didelot 		.num_ports = 7,
3503fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3504a935c052SVivien Didelot 		.global1_addr = 0x1b,
3505acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3506dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3507fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3508b3469dd8SVivien Didelot 		.ops = &mv88e6320_ops,
3509fad09c73SVivien Didelot 	},
3510fad09c73SVivien Didelot 
3511fad09c73SVivien Didelot 	[MV88E6321] = {
3512fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3513fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3514fad09c73SVivien Didelot 		.name = "Marvell 88E6321",
3515fad09c73SVivien Didelot 		.num_databases = 4096,
3516fad09c73SVivien Didelot 		.num_ports = 7,
3517fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3518a935c052SVivien Didelot 		.global1_addr = 0x1b,
3519acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3520dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3521fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3522b3469dd8SVivien Didelot 		.ops = &mv88e6321_ops,
3523fad09c73SVivien Didelot 	},
3524fad09c73SVivien Didelot 
3525fad09c73SVivien Didelot 	[MV88E6350] = {
3526fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3527fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3528fad09c73SVivien Didelot 		.name = "Marvell 88E6350",
3529fad09c73SVivien Didelot 		.num_databases = 4096,
3530fad09c73SVivien Didelot 		.num_ports = 7,
3531fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3532a935c052SVivien Didelot 		.global1_addr = 0x1b,
3533acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3534dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3535fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3536b3469dd8SVivien Didelot 		.ops = &mv88e6350_ops,
3537fad09c73SVivien Didelot 	},
3538fad09c73SVivien Didelot 
3539fad09c73SVivien Didelot 	[MV88E6351] = {
3540fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3541fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3542fad09c73SVivien Didelot 		.name = "Marvell 88E6351",
3543fad09c73SVivien Didelot 		.num_databases = 4096,
3544fad09c73SVivien Didelot 		.num_ports = 7,
3545fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3546a935c052SVivien Didelot 		.global1_addr = 0x1b,
3547acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3548dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3549fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3550b3469dd8SVivien Didelot 		.ops = &mv88e6351_ops,
3551fad09c73SVivien Didelot 	},
3552fad09c73SVivien Didelot 
3553fad09c73SVivien Didelot 	[MV88E6352] = {
3554fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3555fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3556fad09c73SVivien Didelot 		.name = "Marvell 88E6352",
3557fad09c73SVivien Didelot 		.num_databases = 4096,
3558fad09c73SVivien Didelot 		.num_ports = 7,
3559fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3560a935c052SVivien Didelot 		.global1_addr = 0x1b,
3561acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3562dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3563fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3564b3469dd8SVivien Didelot 		.ops = &mv88e6352_ops,
3565fad09c73SVivien Didelot 	},
3566fad09c73SVivien Didelot };
3567fad09c73SVivien Didelot 
3568fad09c73SVivien Didelot static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
3569fad09c73SVivien Didelot {
3570fad09c73SVivien Didelot 	int i;
3571fad09c73SVivien Didelot 
3572fad09c73SVivien Didelot 	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3573fad09c73SVivien Didelot 		if (mv88e6xxx_table[i].prod_num == prod_num)
3574fad09c73SVivien Didelot 			return &mv88e6xxx_table[i];
3575fad09c73SVivien Didelot 
3576fad09c73SVivien Didelot 	return NULL;
3577fad09c73SVivien Didelot }
3578fad09c73SVivien Didelot 
3579fad09c73SVivien Didelot static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
3580fad09c73SVivien Didelot {
3581fad09c73SVivien Didelot 	const struct mv88e6xxx_info *info;
35828f6345b2SVivien Didelot 	unsigned int prod_num, rev;
35838f6345b2SVivien Didelot 	u16 id;
35848f6345b2SVivien Didelot 	int err;
3585fad09c73SVivien Didelot 
35868f6345b2SVivien Didelot 	mutex_lock(&chip->reg_lock);
35878f6345b2SVivien Didelot 	err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
35888f6345b2SVivien Didelot 	mutex_unlock(&chip->reg_lock);
35898f6345b2SVivien Didelot 	if (err)
35908f6345b2SVivien Didelot 		return err;
3591fad09c73SVivien Didelot 
3592fad09c73SVivien Didelot 	prod_num = (id & 0xfff0) >> 4;
3593fad09c73SVivien Didelot 	rev = id & 0x000f;
3594fad09c73SVivien Didelot 
3595fad09c73SVivien Didelot 	info = mv88e6xxx_lookup_info(prod_num);
3596fad09c73SVivien Didelot 	if (!info)
3597fad09c73SVivien Didelot 		return -ENODEV;
3598fad09c73SVivien Didelot 
3599fad09c73SVivien Didelot 	/* Update the compatible info with the probed one */
3600fad09c73SVivien Didelot 	chip->info = info;
3601fad09c73SVivien Didelot 
3602ca070c10SVivien Didelot 	err = mv88e6xxx_g2_require(chip);
3603ca070c10SVivien Didelot 	if (err)
3604ca070c10SVivien Didelot 		return err;
3605ca070c10SVivien Didelot 
3606fad09c73SVivien Didelot 	dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3607fad09c73SVivien Didelot 		 chip->info->prod_num, chip->info->name, rev);
3608fad09c73SVivien Didelot 
3609fad09c73SVivien Didelot 	return 0;
3610fad09c73SVivien Didelot }
3611fad09c73SVivien Didelot 
3612fad09c73SVivien Didelot static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
3613fad09c73SVivien Didelot {
3614fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3615fad09c73SVivien Didelot 
3616fad09c73SVivien Didelot 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3617fad09c73SVivien Didelot 	if (!chip)
3618fad09c73SVivien Didelot 		return NULL;
3619fad09c73SVivien Didelot 
3620fad09c73SVivien Didelot 	chip->dev = dev;
3621fad09c73SVivien Didelot 
3622fad09c73SVivien Didelot 	mutex_init(&chip->reg_lock);
3623fad09c73SVivien Didelot 
3624fad09c73SVivien Didelot 	return chip;
3625fad09c73SVivien Didelot }
3626fad09c73SVivien Didelot 
3627e57e5e77SVivien Didelot static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
3628e57e5e77SVivien Didelot {
3629b3469dd8SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3630e57e5e77SVivien Didelot 		mv88e6xxx_ppu_state_init(chip);
3631e57e5e77SVivien Didelot }
3632e57e5e77SVivien Didelot 
3633930188ceSAndrew Lunn static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
3634930188ceSAndrew Lunn {
3635b3469dd8SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3636930188ceSAndrew Lunn 		mv88e6xxx_ppu_state_destroy(chip);
3637930188ceSAndrew Lunn }
3638930188ceSAndrew Lunn 
3639fad09c73SVivien Didelot static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
3640fad09c73SVivien Didelot 			      struct mii_bus *bus, int sw_addr)
3641fad09c73SVivien Didelot {
3642fad09c73SVivien Didelot 	/* ADDR[0] pin is unavailable externally and considered zero */
3643fad09c73SVivien Didelot 	if (sw_addr & 0x1)
3644fad09c73SVivien Didelot 		return -EINVAL;
3645fad09c73SVivien Didelot 
3646fad09c73SVivien Didelot 	if (sw_addr == 0)
3647fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
3648a0ffff24SVivien Didelot 	else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
3649fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
3650fad09c73SVivien Didelot 	else
3651fad09c73SVivien Didelot 		return -EINVAL;
3652fad09c73SVivien Didelot 
3653fad09c73SVivien Didelot 	chip->bus = bus;
3654fad09c73SVivien Didelot 	chip->sw_addr = sw_addr;
3655fad09c73SVivien Didelot 
3656fad09c73SVivien Didelot 	return 0;
3657fad09c73SVivien Didelot }
3658fad09c73SVivien Didelot 
36597b314362SAndrew Lunn static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
36607b314362SAndrew Lunn {
366104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
36622bbb33beSAndrew Lunn 
36632bbb33beSAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
36647b314362SAndrew Lunn 		return DSA_TAG_PROTO_EDSA;
36652bbb33beSAndrew Lunn 
36662bbb33beSAndrew Lunn 	return DSA_TAG_PROTO_DSA;
36677b314362SAndrew Lunn }
36687b314362SAndrew Lunn 
3669fad09c73SVivien Didelot static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3670fad09c73SVivien Didelot 				       struct device *host_dev, int sw_addr,
3671fad09c73SVivien Didelot 				       void **priv)
3672fad09c73SVivien Didelot {
3673fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3674fad09c73SVivien Didelot 	struct mii_bus *bus;
3675fad09c73SVivien Didelot 	int err;
3676fad09c73SVivien Didelot 
3677fad09c73SVivien Didelot 	bus = dsa_host_dev_to_mii_bus(host_dev);
3678fad09c73SVivien Didelot 	if (!bus)
3679fad09c73SVivien Didelot 		return NULL;
3680fad09c73SVivien Didelot 
3681fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dsa_dev);
3682fad09c73SVivien Didelot 	if (!chip)
3683fad09c73SVivien Didelot 		return NULL;
3684fad09c73SVivien Didelot 
3685fad09c73SVivien Didelot 	/* Legacy SMI probing will only support chips similar to 88E6085 */
3686fad09c73SVivien Didelot 	chip->info = &mv88e6xxx_table[MV88E6085];
3687fad09c73SVivien Didelot 
3688fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, bus, sw_addr);
3689fad09c73SVivien Didelot 	if (err)
3690fad09c73SVivien Didelot 		goto free;
3691fad09c73SVivien Didelot 
3692fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3693fad09c73SVivien Didelot 	if (err)
3694fad09c73SVivien Didelot 		goto free;
3695fad09c73SVivien Didelot 
3696dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3697dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3698dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3699dc30c35bSAndrew Lunn 	if (err)
3700dc30c35bSAndrew Lunn 		goto free;
3701dc30c35bSAndrew Lunn 
3702e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3703e57e5e77SVivien Didelot 
3704fad09c73SVivien Didelot 	err = mv88e6xxx_mdio_register(chip, NULL);
3705fad09c73SVivien Didelot 	if (err)
3706fad09c73SVivien Didelot 		goto free;
3707fad09c73SVivien Didelot 
3708fad09c73SVivien Didelot 	*priv = chip;
3709fad09c73SVivien Didelot 
3710fad09c73SVivien Didelot 	return chip->info->name;
3711fad09c73SVivien Didelot free:
3712fad09c73SVivien Didelot 	devm_kfree(dsa_dev, chip);
3713fad09c73SVivien Didelot 
3714fad09c73SVivien Didelot 	return NULL;
3715fad09c73SVivien Didelot }
3716fad09c73SVivien Didelot 
37177df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
37187df8fbddSVivien Didelot 				      const struct switchdev_obj_port_mdb *mdb,
37197df8fbddSVivien Didelot 				      struct switchdev_trans *trans)
37207df8fbddSVivien Didelot {
37217df8fbddSVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
37227df8fbddSVivien Didelot 	 * so skip the prepare phase.
37237df8fbddSVivien Didelot 	 */
37247df8fbddSVivien Didelot 
37257df8fbddSVivien Didelot 	return 0;
37267df8fbddSVivien Didelot }
37277df8fbddSVivien Didelot 
37287df8fbddSVivien Didelot static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
37297df8fbddSVivien Didelot 				   const struct switchdev_obj_port_mdb *mdb,
37307df8fbddSVivien Didelot 				   struct switchdev_trans *trans)
37317df8fbddSVivien Didelot {
373204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37337df8fbddSVivien Didelot 
37347df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
37357df8fbddSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
37367df8fbddSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_MC_STATIC))
37377df8fbddSVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
37387df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
37397df8fbddSVivien Didelot }
37407df8fbddSVivien Didelot 
37417df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
37427df8fbddSVivien Didelot 				  const struct switchdev_obj_port_mdb *mdb)
37437df8fbddSVivien Didelot {
374404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37457df8fbddSVivien Didelot 	int err;
37467df8fbddSVivien Didelot 
37477df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
37487df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
37497df8fbddSVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
37507df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
37517df8fbddSVivien Didelot 
37527df8fbddSVivien Didelot 	return err;
37537df8fbddSVivien Didelot }
37547df8fbddSVivien Didelot 
37557df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
37567df8fbddSVivien Didelot 				   struct switchdev_obj_port_mdb *mdb,
37577df8fbddSVivien Didelot 				   int (*cb)(struct switchdev_obj *obj))
37587df8fbddSVivien Didelot {
375904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37607df8fbddSVivien Didelot 	int err;
37617df8fbddSVivien Didelot 
37627df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
37637df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
37647df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
37657df8fbddSVivien Didelot 
37667df8fbddSVivien Didelot 	return err;
37677df8fbddSVivien Didelot }
37687df8fbddSVivien Didelot 
37699d490b4eSVivien Didelot static struct dsa_switch_ops mv88e6xxx_switch_ops = {
3770fad09c73SVivien Didelot 	.probe			= mv88e6xxx_drv_probe,
37717b314362SAndrew Lunn 	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
3772fad09c73SVivien Didelot 	.setup			= mv88e6xxx_setup,
3773fad09c73SVivien Didelot 	.set_addr		= mv88e6xxx_set_addr,
3774fad09c73SVivien Didelot 	.adjust_link		= mv88e6xxx_adjust_link,
3775fad09c73SVivien Didelot 	.get_strings		= mv88e6xxx_get_strings,
3776fad09c73SVivien Didelot 	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
3777fad09c73SVivien Didelot 	.get_sset_count		= mv88e6xxx_get_sset_count,
3778fad09c73SVivien Didelot 	.set_eee		= mv88e6xxx_set_eee,
3779fad09c73SVivien Didelot 	.get_eee		= mv88e6xxx_get_eee,
3780fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON
3781fad09c73SVivien Didelot 	.get_temp		= mv88e6xxx_get_temp,
3782fad09c73SVivien Didelot 	.get_temp_limit		= mv88e6xxx_get_temp_limit,
3783fad09c73SVivien Didelot 	.set_temp_limit		= mv88e6xxx_set_temp_limit,
3784fad09c73SVivien Didelot 	.get_temp_alarm		= mv88e6xxx_get_temp_alarm,
3785fad09c73SVivien Didelot #endif
3786fad09c73SVivien Didelot 	.get_eeprom_len		= mv88e6xxx_get_eeprom_len,
3787fad09c73SVivien Didelot 	.get_eeprom		= mv88e6xxx_get_eeprom,
3788fad09c73SVivien Didelot 	.set_eeprom		= mv88e6xxx_set_eeprom,
3789fad09c73SVivien Didelot 	.get_regs_len		= mv88e6xxx_get_regs_len,
3790fad09c73SVivien Didelot 	.get_regs		= mv88e6xxx_get_regs,
37912cfcd964SVivien Didelot 	.set_ageing_time	= mv88e6xxx_set_ageing_time,
3792fad09c73SVivien Didelot 	.port_bridge_join	= mv88e6xxx_port_bridge_join,
3793fad09c73SVivien Didelot 	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
3794fad09c73SVivien Didelot 	.port_stp_state_set	= mv88e6xxx_port_stp_state_set,
3795749efcb8SVivien Didelot 	.port_fast_age		= mv88e6xxx_port_fast_age,
3796fad09c73SVivien Didelot 	.port_vlan_filtering	= mv88e6xxx_port_vlan_filtering,
3797fad09c73SVivien Didelot 	.port_vlan_prepare	= mv88e6xxx_port_vlan_prepare,
3798fad09c73SVivien Didelot 	.port_vlan_add		= mv88e6xxx_port_vlan_add,
3799fad09c73SVivien Didelot 	.port_vlan_del		= mv88e6xxx_port_vlan_del,
3800fad09c73SVivien Didelot 	.port_vlan_dump		= mv88e6xxx_port_vlan_dump,
3801fad09c73SVivien Didelot 	.port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
3802fad09c73SVivien Didelot 	.port_fdb_add           = mv88e6xxx_port_fdb_add,
3803fad09c73SVivien Didelot 	.port_fdb_del           = mv88e6xxx_port_fdb_del,
3804fad09c73SVivien Didelot 	.port_fdb_dump          = mv88e6xxx_port_fdb_dump,
38057df8fbddSVivien Didelot 	.port_mdb_prepare       = mv88e6xxx_port_mdb_prepare,
38067df8fbddSVivien Didelot 	.port_mdb_add           = mv88e6xxx_port_mdb_add,
38077df8fbddSVivien Didelot 	.port_mdb_del           = mv88e6xxx_port_mdb_del,
38087df8fbddSVivien Didelot 	.port_mdb_dump          = mv88e6xxx_port_mdb_dump,
3809fad09c73SVivien Didelot };
3810fad09c73SVivien Didelot 
3811fad09c73SVivien Didelot static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
3812fad09c73SVivien Didelot 				     struct device_node *np)
3813fad09c73SVivien Didelot {
3814fad09c73SVivien Didelot 	struct device *dev = chip->dev;
3815fad09c73SVivien Didelot 	struct dsa_switch *ds;
3816fad09c73SVivien Didelot 
3817fad09c73SVivien Didelot 	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3818fad09c73SVivien Didelot 	if (!ds)
3819fad09c73SVivien Didelot 		return -ENOMEM;
3820fad09c73SVivien Didelot 
3821fad09c73SVivien Didelot 	ds->dev = dev;
3822fad09c73SVivien Didelot 	ds->priv = chip;
38239d490b4eSVivien Didelot 	ds->ops = &mv88e6xxx_switch_ops;
3824fad09c73SVivien Didelot 
3825fad09c73SVivien Didelot 	dev_set_drvdata(dev, ds);
3826fad09c73SVivien Didelot 
3827fad09c73SVivien Didelot 	return dsa_register_switch(ds, np);
3828fad09c73SVivien Didelot }
3829fad09c73SVivien Didelot 
3830fad09c73SVivien Didelot static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
3831fad09c73SVivien Didelot {
3832fad09c73SVivien Didelot 	dsa_unregister_switch(chip->ds);
3833fad09c73SVivien Didelot }
3834fad09c73SVivien Didelot 
3835fad09c73SVivien Didelot static int mv88e6xxx_probe(struct mdio_device *mdiodev)
3836fad09c73SVivien Didelot {
3837fad09c73SVivien Didelot 	struct device *dev = &mdiodev->dev;
3838fad09c73SVivien Didelot 	struct device_node *np = dev->of_node;
3839fad09c73SVivien Didelot 	const struct mv88e6xxx_info *compat_info;
3840fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3841fad09c73SVivien Didelot 	u32 eeprom_len;
3842fad09c73SVivien Didelot 	int err;
3843fad09c73SVivien Didelot 
3844fad09c73SVivien Didelot 	compat_info = of_device_get_match_data(dev);
3845fad09c73SVivien Didelot 	if (!compat_info)
3846fad09c73SVivien Didelot 		return -EINVAL;
3847fad09c73SVivien Didelot 
3848fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dev);
3849fad09c73SVivien Didelot 	if (!chip)
3850fad09c73SVivien Didelot 		return -ENOMEM;
3851fad09c73SVivien Didelot 
3852fad09c73SVivien Didelot 	chip->info = compat_info;
3853fad09c73SVivien Didelot 
3854fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
3855fad09c73SVivien Didelot 	if (err)
3856fad09c73SVivien Didelot 		return err;
3857fad09c73SVivien Didelot 
3858fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3859fad09c73SVivien Didelot 	if (err)
3860fad09c73SVivien Didelot 		return err;
3861fad09c73SVivien Didelot 
3862e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3863e57e5e77SVivien Didelot 
3864fad09c73SVivien Didelot 	chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
3865fad09c73SVivien Didelot 	if (IS_ERR(chip->reset))
3866fad09c73SVivien Didelot 		return PTR_ERR(chip->reset);
3867fad09c73SVivien Didelot 
3868ee4dc2e7SVivien Didelot 	if (chip->info->ops->get_eeprom &&
3869fad09c73SVivien Didelot 	    !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3870fad09c73SVivien Didelot 		chip->eeprom_len = eeprom_len;
3871fad09c73SVivien Didelot 
3872dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3873dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3874dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3875fad09c73SVivien Didelot 	if (err)
3876dc30c35bSAndrew Lunn 		goto out;
3877fad09c73SVivien Didelot 
3878dc30c35bSAndrew Lunn 	chip->irq = of_irq_get(np, 0);
3879dc30c35bSAndrew Lunn 	if (chip->irq == -EPROBE_DEFER) {
3880dc30c35bSAndrew Lunn 		err = chip->irq;
3881dc30c35bSAndrew Lunn 		goto out;
3882fad09c73SVivien Didelot 	}
3883fad09c73SVivien Didelot 
3884dc30c35bSAndrew Lunn 	if (chip->irq > 0) {
3885dc30c35bSAndrew Lunn 		/* Has to be performed before the MDIO bus is created,
3886dc30c35bSAndrew Lunn 		 * because the PHYs will link there interrupts to these
3887dc30c35bSAndrew Lunn 		 * interrupt controllers
3888dc30c35bSAndrew Lunn 		 */
3889dc30c35bSAndrew Lunn 		mutex_lock(&chip->reg_lock);
3890dc30c35bSAndrew Lunn 		err = mv88e6xxx_g1_irq_setup(chip);
3891dc30c35bSAndrew Lunn 		mutex_unlock(&chip->reg_lock);
3892dc30c35bSAndrew Lunn 
3893dc30c35bSAndrew Lunn 		if (err)
3894dc30c35bSAndrew Lunn 			goto out;
3895dc30c35bSAndrew Lunn 
3896dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
3897dc30c35bSAndrew Lunn 			err = mv88e6xxx_g2_irq_setup(chip);
3898dc30c35bSAndrew Lunn 			if (err)
3899dc30c35bSAndrew Lunn 				goto out_g1_irq;
3900dc30c35bSAndrew Lunn 		}
3901dc30c35bSAndrew Lunn 	}
3902dc30c35bSAndrew Lunn 
3903dc30c35bSAndrew Lunn 	err = mv88e6xxx_mdio_register(chip, np);
3904dc30c35bSAndrew Lunn 	if (err)
3905dc30c35bSAndrew Lunn 		goto out_g2_irq;
3906dc30c35bSAndrew Lunn 
3907dc30c35bSAndrew Lunn 	err = mv88e6xxx_register_switch(chip, np);
3908dc30c35bSAndrew Lunn 	if (err)
3909dc30c35bSAndrew Lunn 		goto out_mdio;
3910dc30c35bSAndrew Lunn 
3911fad09c73SVivien Didelot 	return 0;
3912dc30c35bSAndrew Lunn 
3913dc30c35bSAndrew Lunn out_mdio:
3914dc30c35bSAndrew Lunn 	mv88e6xxx_mdio_unregister(chip);
3915dc30c35bSAndrew Lunn out_g2_irq:
391646712644SAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT) && chip->irq > 0)
3917dc30c35bSAndrew Lunn 		mv88e6xxx_g2_irq_free(chip);
3918dc30c35bSAndrew Lunn out_g1_irq:
391961f7c3f8SAndrew Lunn 	if (chip->irq > 0) {
392061f7c3f8SAndrew Lunn 		mutex_lock(&chip->reg_lock);
3921dc30c35bSAndrew Lunn 		mv88e6xxx_g1_irq_free(chip);
392261f7c3f8SAndrew Lunn 		mutex_unlock(&chip->reg_lock);
392361f7c3f8SAndrew Lunn 	}
3924dc30c35bSAndrew Lunn out:
3925dc30c35bSAndrew Lunn 	return err;
3926fad09c73SVivien Didelot }
3927fad09c73SVivien Didelot 
3928fad09c73SVivien Didelot static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3929fad09c73SVivien Didelot {
3930fad09c73SVivien Didelot 	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
393104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3932fad09c73SVivien Didelot 
3933930188ceSAndrew Lunn 	mv88e6xxx_phy_destroy(chip);
3934fad09c73SVivien Didelot 	mv88e6xxx_unregister_switch(chip);
3935fad09c73SVivien Didelot 	mv88e6xxx_mdio_unregister(chip);
3936dc30c35bSAndrew Lunn 
393746712644SAndrew Lunn 	if (chip->irq > 0) {
3938dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
3939dc30c35bSAndrew Lunn 			mv88e6xxx_g2_irq_free(chip);
3940dc30c35bSAndrew Lunn 		mv88e6xxx_g1_irq_free(chip);
3941fad09c73SVivien Didelot 	}
394246712644SAndrew Lunn }
3943fad09c73SVivien Didelot 
3944fad09c73SVivien Didelot static const struct of_device_id mv88e6xxx_of_match[] = {
3945fad09c73SVivien Didelot 	{
3946fad09c73SVivien Didelot 		.compatible = "marvell,mv88e6085",
3947fad09c73SVivien Didelot 		.data = &mv88e6xxx_table[MV88E6085],
3948fad09c73SVivien Didelot 	},
3949fad09c73SVivien Didelot 	{ /* sentinel */ },
3950fad09c73SVivien Didelot };
3951fad09c73SVivien Didelot 
3952fad09c73SVivien Didelot MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3953fad09c73SVivien Didelot 
3954fad09c73SVivien Didelot static struct mdio_driver mv88e6xxx_driver = {
3955fad09c73SVivien Didelot 	.probe	= mv88e6xxx_probe,
3956fad09c73SVivien Didelot 	.remove = mv88e6xxx_remove,
3957fad09c73SVivien Didelot 	.mdiodrv.driver = {
3958fad09c73SVivien Didelot 		.name = "mv88e6085",
3959fad09c73SVivien Didelot 		.of_match_table = mv88e6xxx_of_match,
3960fad09c73SVivien Didelot 	},
3961fad09c73SVivien Didelot };
3962fad09c73SVivien Didelot 
3963fad09c73SVivien Didelot static int __init mv88e6xxx_init(void)
3964fad09c73SVivien Didelot {
39659d490b4eSVivien Didelot 	register_switch_driver(&mv88e6xxx_switch_ops);
3966fad09c73SVivien Didelot 	return mdio_driver_register(&mv88e6xxx_driver);
3967fad09c73SVivien Didelot }
3968fad09c73SVivien Didelot module_init(mv88e6xxx_init);
3969fad09c73SVivien Didelot 
3970fad09c73SVivien Didelot static void __exit mv88e6xxx_cleanup(void)
3971fad09c73SVivien Didelot {
3972fad09c73SVivien Didelot 	mdio_driver_unregister(&mv88e6xxx_driver);
39739d490b4eSVivien Didelot 	unregister_switch_driver(&mv88e6xxx_switch_ops);
3974fad09c73SVivien Didelot }
3975fad09c73SVivien Didelot module_exit(mv88e6xxx_cleanup);
3976fad09c73SVivien Didelot 
3977fad09c73SVivien Didelot MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3978fad09c73SVivien Didelot MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3979fad09c73SVivien Didelot MODULE_LICENSE("GPL");
3980