xref: /openbmc/linux/drivers/net/dsa/mv88e6xxx/chip.c (revision e28def33)
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;
416dc30c35bSAndrew Lunn 
417dc30c35bSAndrew Lunn 	for (irq = 0; irq < 16; irq++) {
418dc30c35bSAndrew Lunn 		virq = irq_find_mapping(chip->g2_irq.domain, irq);
419dc30c35bSAndrew Lunn 		irq_dispose_mapping(virq);
420dc30c35bSAndrew Lunn 	}
421dc30c35bSAndrew Lunn 
422dc30c35bSAndrew Lunn 	irq_domain_remove(chip->g2_irq.domain);
423dc30c35bSAndrew Lunn }
424dc30c35bSAndrew Lunn 
425dc30c35bSAndrew Lunn static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
426dc30c35bSAndrew Lunn {
427dc30c35bSAndrew Lunn 	int err, irq;
428dc30c35bSAndrew Lunn 	u16 reg;
429dc30c35bSAndrew Lunn 
430dc30c35bSAndrew Lunn 	chip->g1_irq.nirqs = chip->info->g1_irqs;
431dc30c35bSAndrew Lunn 	chip->g1_irq.domain = irq_domain_add_simple(
432dc30c35bSAndrew Lunn 		NULL, chip->g1_irq.nirqs, 0,
433dc30c35bSAndrew Lunn 		&mv88e6xxx_g1_irq_domain_ops, chip);
434dc30c35bSAndrew Lunn 	if (!chip->g1_irq.domain)
435dc30c35bSAndrew Lunn 		return -ENOMEM;
436dc30c35bSAndrew Lunn 
437dc30c35bSAndrew Lunn 	for (irq = 0; irq < chip->g1_irq.nirqs; irq++)
438dc30c35bSAndrew Lunn 		irq_create_mapping(chip->g1_irq.domain, irq);
439dc30c35bSAndrew Lunn 
440dc30c35bSAndrew Lunn 	chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
441dc30c35bSAndrew Lunn 	chip->g1_irq.masked = ~0;
442dc30c35bSAndrew Lunn 
443dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
444dc30c35bSAndrew Lunn 	if (err)
445dc30c35bSAndrew Lunn 		goto out;
446dc30c35bSAndrew Lunn 
447dc30c35bSAndrew Lunn 	reg &= ~GENMASK(chip->g1_irq.nirqs, 0);
448dc30c35bSAndrew Lunn 
449dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
450dc30c35bSAndrew Lunn 	if (err)
451dc30c35bSAndrew Lunn 		goto out;
452dc30c35bSAndrew Lunn 
453dc30c35bSAndrew Lunn 	/* Reading the interrupt status clears (most of) them */
454dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
455dc30c35bSAndrew Lunn 	if (err)
456dc30c35bSAndrew Lunn 		goto out;
457dc30c35bSAndrew Lunn 
458dc30c35bSAndrew Lunn 	err = request_threaded_irq(chip->irq, NULL,
459dc30c35bSAndrew Lunn 				   mv88e6xxx_g1_irq_thread_fn,
460dc30c35bSAndrew Lunn 				   IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
461dc30c35bSAndrew Lunn 				   dev_name(chip->dev), chip);
462dc30c35bSAndrew Lunn 	if (err)
463dc30c35bSAndrew Lunn 		goto out;
464dc30c35bSAndrew Lunn 
465dc30c35bSAndrew Lunn 	return 0;
466dc30c35bSAndrew Lunn 
467dc30c35bSAndrew Lunn out:
468dc30c35bSAndrew Lunn 	mv88e6xxx_g1_irq_free(chip);
469dc30c35bSAndrew Lunn 
470dc30c35bSAndrew Lunn 	return err;
471dc30c35bSAndrew Lunn }
472dc30c35bSAndrew Lunn 
473ec561276SVivien Didelot int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
4742d79af6eSVivien Didelot {
4756441e669SAndrew Lunn 	int i;
4762d79af6eSVivien Didelot 
4776441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
4782d79af6eSVivien Didelot 		u16 val;
4792d79af6eSVivien Didelot 		int err;
4802d79af6eSVivien Didelot 
4812d79af6eSVivien Didelot 		err = mv88e6xxx_read(chip, addr, reg, &val);
4822d79af6eSVivien Didelot 		if (err)
4832d79af6eSVivien Didelot 			return err;
4842d79af6eSVivien Didelot 
4852d79af6eSVivien Didelot 		if (!(val & mask))
4862d79af6eSVivien Didelot 			return 0;
4872d79af6eSVivien Didelot 
4882d79af6eSVivien Didelot 		usleep_range(1000, 2000);
4892d79af6eSVivien Didelot 	}
4902d79af6eSVivien Didelot 
49130853553SAndrew Lunn 	dev_err(chip->dev, "Timeout while waiting for switch\n");
4922d79af6eSVivien Didelot 	return -ETIMEDOUT;
4932d79af6eSVivien Didelot }
4942d79af6eSVivien Didelot 
495f22ab641SVivien Didelot /* Indirect write to single pointer-data register with an Update bit */
496ec561276SVivien Didelot int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
497f22ab641SVivien Didelot {
498f22ab641SVivien Didelot 	u16 val;
4990f02b4f7SAndrew Lunn 	int err;
500f22ab641SVivien Didelot 
501f22ab641SVivien Didelot 	/* Wait until the previous operation is completed */
5020f02b4f7SAndrew Lunn 	err = mv88e6xxx_wait(chip, addr, reg, BIT(15));
503f22ab641SVivien Didelot 	if (err)
504f22ab641SVivien Didelot 		return err;
505f22ab641SVivien Didelot 
506f22ab641SVivien Didelot 	/* Set the Update bit to trigger a write operation */
507f22ab641SVivien Didelot 	val = BIT(15) | update;
508f22ab641SVivien Didelot 
509f22ab641SVivien Didelot 	return mv88e6xxx_write(chip, addr, reg, val);
510f22ab641SVivien Didelot }
511f22ab641SVivien Didelot 
512a935c052SVivien Didelot static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
513fad09c73SVivien Didelot {
514fad09c73SVivien Didelot 	u16 val;
515a935c052SVivien Didelot 	int i, err;
516fad09c73SVivien Didelot 
517a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
518fad09c73SVivien Didelot 	if (err)
519fad09c73SVivien Didelot 		return err;
520fad09c73SVivien Didelot 
521a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
522a935c052SVivien Didelot 				 val & ~GLOBAL_CONTROL_PPU_ENABLE);
523a935c052SVivien Didelot 	if (err)
524a935c052SVivien Didelot 		return err;
525fad09c73SVivien Didelot 
5266441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
527a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
528a935c052SVivien Didelot 		if (err)
529a935c052SVivien Didelot 			return err;
530fad09c73SVivien Didelot 
531fad09c73SVivien Didelot 		usleep_range(1000, 2000);
532a935c052SVivien Didelot 		if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING)
533fad09c73SVivien Didelot 			return 0;
534fad09c73SVivien Didelot 	}
535fad09c73SVivien Didelot 
536fad09c73SVivien Didelot 	return -ETIMEDOUT;
537fad09c73SVivien Didelot }
538fad09c73SVivien Didelot 
539fad09c73SVivien Didelot static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
540fad09c73SVivien Didelot {
541a935c052SVivien Didelot 	u16 val;
542a935c052SVivien Didelot 	int i, err;
543fad09c73SVivien Didelot 
544a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
545a935c052SVivien Didelot 	if (err)
546a935c052SVivien Didelot 		return err;
547fad09c73SVivien Didelot 
548a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
549a935c052SVivien Didelot 				 val | GLOBAL_CONTROL_PPU_ENABLE);
550fad09c73SVivien Didelot 	if (err)
551fad09c73SVivien Didelot 		return err;
552fad09c73SVivien Didelot 
5536441e669SAndrew Lunn 	for (i = 0; i < 16; i++) {
554a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
555a935c052SVivien Didelot 		if (err)
556a935c052SVivien Didelot 			return err;
557fad09c73SVivien Didelot 
558fad09c73SVivien Didelot 		usleep_range(1000, 2000);
559a935c052SVivien Didelot 		if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING)
560fad09c73SVivien Didelot 			return 0;
561fad09c73SVivien Didelot 	}
562fad09c73SVivien Didelot 
563fad09c73SVivien Didelot 	return -ETIMEDOUT;
564fad09c73SVivien Didelot }
565fad09c73SVivien Didelot 
566fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
567fad09c73SVivien Didelot {
568fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
569fad09c73SVivien Didelot 
570fad09c73SVivien Didelot 	chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
571fad09c73SVivien Didelot 
572fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
573fad09c73SVivien Didelot 
574fad09c73SVivien Didelot 	if (mutex_trylock(&chip->ppu_mutex)) {
575fad09c73SVivien Didelot 		if (mv88e6xxx_ppu_enable(chip) == 0)
576fad09c73SVivien Didelot 			chip->ppu_disabled = 0;
577fad09c73SVivien Didelot 		mutex_unlock(&chip->ppu_mutex);
578fad09c73SVivien Didelot 	}
579fad09c73SVivien Didelot 
580fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
581fad09c73SVivien Didelot }
582fad09c73SVivien Didelot 
583fad09c73SVivien Didelot static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
584fad09c73SVivien Didelot {
585fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip = (void *)_ps;
586fad09c73SVivien Didelot 
587fad09c73SVivien Didelot 	schedule_work(&chip->ppu_work);
588fad09c73SVivien Didelot }
589fad09c73SVivien Didelot 
590fad09c73SVivien Didelot static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
591fad09c73SVivien Didelot {
592fad09c73SVivien Didelot 	int ret;
593fad09c73SVivien Didelot 
594fad09c73SVivien Didelot 	mutex_lock(&chip->ppu_mutex);
595fad09c73SVivien Didelot 
596fad09c73SVivien Didelot 	/* If the PHY polling unit is enabled, disable it so that
597fad09c73SVivien Didelot 	 * we can access the PHY registers.  If it was already
598fad09c73SVivien Didelot 	 * disabled, cancel the timer that is going to re-enable
599fad09c73SVivien Didelot 	 * it.
600fad09c73SVivien Didelot 	 */
601fad09c73SVivien Didelot 	if (!chip->ppu_disabled) {
602fad09c73SVivien Didelot 		ret = mv88e6xxx_ppu_disable(chip);
603fad09c73SVivien Didelot 		if (ret < 0) {
604fad09c73SVivien Didelot 			mutex_unlock(&chip->ppu_mutex);
605fad09c73SVivien Didelot 			return ret;
606fad09c73SVivien Didelot 		}
607fad09c73SVivien Didelot 		chip->ppu_disabled = 1;
608fad09c73SVivien Didelot 	} else {
609fad09c73SVivien Didelot 		del_timer(&chip->ppu_timer);
610fad09c73SVivien Didelot 		ret = 0;
611fad09c73SVivien Didelot 	}
612fad09c73SVivien Didelot 
613fad09c73SVivien Didelot 	return ret;
614fad09c73SVivien Didelot }
615fad09c73SVivien Didelot 
616fad09c73SVivien Didelot static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
617fad09c73SVivien Didelot {
618fad09c73SVivien Didelot 	/* Schedule a timer to re-enable the PHY polling unit. */
619fad09c73SVivien Didelot 	mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
620fad09c73SVivien Didelot 	mutex_unlock(&chip->ppu_mutex);
621fad09c73SVivien Didelot }
622fad09c73SVivien Didelot 
623fad09c73SVivien Didelot static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
624fad09c73SVivien Didelot {
625fad09c73SVivien Didelot 	mutex_init(&chip->ppu_mutex);
626fad09c73SVivien Didelot 	INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
62768497a87SWei Yongjun 	setup_timer(&chip->ppu_timer, mv88e6xxx_ppu_reenable_timer,
62868497a87SWei Yongjun 		    (unsigned long)chip);
629fad09c73SVivien Didelot }
630fad09c73SVivien Didelot 
631930188ceSAndrew Lunn static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip)
632930188ceSAndrew Lunn {
633930188ceSAndrew Lunn 	del_timer_sync(&chip->ppu_timer);
634930188ceSAndrew Lunn }
635930188ceSAndrew Lunn 
636e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
637e57e5e77SVivien Didelot 				  int reg, u16 *val)
638fad09c73SVivien Didelot {
639e57e5e77SVivien Didelot 	int err;
640fad09c73SVivien Didelot 
641e57e5e77SVivien Didelot 	err = mv88e6xxx_ppu_access_get(chip);
642e57e5e77SVivien Didelot 	if (!err) {
643e57e5e77SVivien Didelot 		err = mv88e6xxx_read(chip, addr, reg, val);
644fad09c73SVivien Didelot 		mv88e6xxx_ppu_access_put(chip);
645fad09c73SVivien Didelot 	}
646fad09c73SVivien Didelot 
647e57e5e77SVivien Didelot 	return err;
648fad09c73SVivien Didelot }
649fad09c73SVivien Didelot 
650e57e5e77SVivien Didelot static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
651e57e5e77SVivien Didelot 				   int reg, u16 val)
652fad09c73SVivien Didelot {
653e57e5e77SVivien Didelot 	int err;
654fad09c73SVivien Didelot 
655e57e5e77SVivien Didelot 	err = mv88e6xxx_ppu_access_get(chip);
656e57e5e77SVivien Didelot 	if (!err) {
657e57e5e77SVivien Didelot 		err = mv88e6xxx_write(chip, addr, reg, val);
658fad09c73SVivien Didelot 		mv88e6xxx_ppu_access_put(chip);
659fad09c73SVivien Didelot 	}
660fad09c73SVivien Didelot 
661e57e5e77SVivien Didelot 	return err;
662fad09c73SVivien Didelot }
663fad09c73SVivien Didelot 
664fad09c73SVivien Didelot static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
665fad09c73SVivien Didelot {
666fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6065;
667fad09c73SVivien Didelot }
668fad09c73SVivien Didelot 
669fad09c73SVivien Didelot static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
670fad09c73SVivien Didelot {
671fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6095;
672fad09c73SVivien Didelot }
673fad09c73SVivien Didelot 
674fad09c73SVivien Didelot static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
675fad09c73SVivien Didelot {
676fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6097;
677fad09c73SVivien Didelot }
678fad09c73SVivien Didelot 
679fad09c73SVivien Didelot static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
680fad09c73SVivien Didelot {
681fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6165;
682fad09c73SVivien Didelot }
683fad09c73SVivien Didelot 
684fad09c73SVivien Didelot static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
685fad09c73SVivien Didelot {
686fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6185;
687fad09c73SVivien Didelot }
688fad09c73SVivien Didelot 
689fad09c73SVivien Didelot static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
690fad09c73SVivien Didelot {
691fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6320;
692fad09c73SVivien Didelot }
693fad09c73SVivien Didelot 
694fad09c73SVivien Didelot static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
695fad09c73SVivien Didelot {
696fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6351;
697fad09c73SVivien Didelot }
698fad09c73SVivien Didelot 
699fad09c73SVivien Didelot static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
700fad09c73SVivien Didelot {
701fad09c73SVivien Didelot 	return chip->info->family == MV88E6XXX_FAMILY_6352;
702fad09c73SVivien Didelot }
703fad09c73SVivien Didelot 
704fad09c73SVivien Didelot /* We expect the switch to perform auto negotiation if there is a real
705fad09c73SVivien Didelot  * phy. However, in the case of a fixed link phy, we force the port
706fad09c73SVivien Didelot  * settings from the fixed link settings.
707fad09c73SVivien Didelot  */
708fad09c73SVivien Didelot static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
709fad09c73SVivien Didelot 				  struct phy_device *phydev)
710fad09c73SVivien Didelot {
71104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
7120e7b9925SAndrew Lunn 	u16 reg;
7130e7b9925SAndrew Lunn 	int err;
714fad09c73SVivien Didelot 
715fad09c73SVivien Didelot 	if (!phy_is_pseudo_fixed_link(phydev))
716fad09c73SVivien Didelot 		return;
717fad09c73SVivien Didelot 
718fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
719fad09c73SVivien Didelot 
7200e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
7210e7b9925SAndrew Lunn 	if (err)
722fad09c73SVivien Didelot 		goto out;
723fad09c73SVivien Didelot 
7240e7b9925SAndrew Lunn 	reg &= ~(PORT_PCS_CTRL_LINK_UP |
725fad09c73SVivien Didelot 		 PORT_PCS_CTRL_FORCE_LINK |
726fad09c73SVivien Didelot 		 PORT_PCS_CTRL_DUPLEX_FULL |
727fad09c73SVivien Didelot 		 PORT_PCS_CTRL_FORCE_DUPLEX |
728fad09c73SVivien Didelot 		 PORT_PCS_CTRL_UNFORCED);
729fad09c73SVivien Didelot 
730fad09c73SVivien Didelot 	reg |= PORT_PCS_CTRL_FORCE_LINK;
731fad09c73SVivien Didelot 	if (phydev->link)
732fad09c73SVivien Didelot 		reg |= PORT_PCS_CTRL_LINK_UP;
733fad09c73SVivien Didelot 
734fad09c73SVivien Didelot 	if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
735fad09c73SVivien Didelot 		goto out;
736fad09c73SVivien Didelot 
737fad09c73SVivien Didelot 	switch (phydev->speed) {
738fad09c73SVivien Didelot 	case SPEED_1000:
739fad09c73SVivien Didelot 		reg |= PORT_PCS_CTRL_1000;
740fad09c73SVivien Didelot 		break;
741fad09c73SVivien Didelot 	case SPEED_100:
742fad09c73SVivien Didelot 		reg |= PORT_PCS_CTRL_100;
743fad09c73SVivien Didelot 		break;
744fad09c73SVivien Didelot 	case SPEED_10:
745fad09c73SVivien Didelot 		reg |= PORT_PCS_CTRL_10;
746fad09c73SVivien Didelot 		break;
747fad09c73SVivien Didelot 	default:
748fad09c73SVivien Didelot 		pr_info("Unknown speed");
749fad09c73SVivien Didelot 		goto out;
750fad09c73SVivien Didelot 	}
751fad09c73SVivien Didelot 
752fad09c73SVivien Didelot 	reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
753fad09c73SVivien Didelot 	if (phydev->duplex == DUPLEX_FULL)
754fad09c73SVivien Didelot 		reg |= PORT_PCS_CTRL_DUPLEX_FULL;
755fad09c73SVivien Didelot 
756fad09c73SVivien Didelot 	if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
757370b4ffbSVivien Didelot 	    (port >= mv88e6xxx_num_ports(chip) - 2)) {
758fad09c73SVivien Didelot 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
759fad09c73SVivien Didelot 			reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
760fad09c73SVivien Didelot 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
761fad09c73SVivien Didelot 			reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
762fad09c73SVivien Didelot 		if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
763fad09c73SVivien Didelot 			reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
764fad09c73SVivien Didelot 				PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
765fad09c73SVivien Didelot 	}
7660e7b9925SAndrew Lunn 	mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
767fad09c73SVivien Didelot 
768fad09c73SVivien Didelot out:
769fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
770fad09c73SVivien Didelot }
771fad09c73SVivien Didelot 
772fad09c73SVivien Didelot static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
773fad09c73SVivien Didelot {
774a935c052SVivien Didelot 	u16 val;
775a935c052SVivien Didelot 	int i, err;
776fad09c73SVivien Didelot 
777fad09c73SVivien Didelot 	for (i = 0; i < 10; i++) {
778a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val);
779a935c052SVivien Didelot 		if ((val & GLOBAL_STATS_OP_BUSY) == 0)
780fad09c73SVivien Didelot 			return 0;
781fad09c73SVivien Didelot 	}
782fad09c73SVivien Didelot 
783fad09c73SVivien Didelot 	return -ETIMEDOUT;
784fad09c73SVivien Didelot }
785fad09c73SVivien Didelot 
786fad09c73SVivien Didelot static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
787fad09c73SVivien Didelot {
788a935c052SVivien Didelot 	int err;
789fad09c73SVivien Didelot 
790fad09c73SVivien Didelot 	if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
791fad09c73SVivien Didelot 		port = (port + 1) << 5;
792fad09c73SVivien Didelot 
793fad09c73SVivien Didelot 	/* Snapshot the hardware statistics counters for this port. */
794a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
795fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_CAPTURE_PORT |
796fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_HIST_RX_TX | port);
797a935c052SVivien Didelot 	if (err)
798a935c052SVivien Didelot 		return err;
799fad09c73SVivien Didelot 
800fad09c73SVivien Didelot 	/* Wait for the snapshotting to complete. */
801a935c052SVivien Didelot 	return _mv88e6xxx_stats_wait(chip);
802fad09c73SVivien Didelot }
803fad09c73SVivien Didelot 
804fad09c73SVivien Didelot static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
805fad09c73SVivien Didelot 				  int stat, u32 *val)
806fad09c73SVivien Didelot {
807a935c052SVivien Didelot 	u32 value;
808a935c052SVivien Didelot 	u16 reg;
809a935c052SVivien Didelot 	int err;
810fad09c73SVivien Didelot 
811fad09c73SVivien Didelot 	*val = 0;
812fad09c73SVivien Didelot 
813a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
814fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_READ_CAPTURED |
815fad09c73SVivien Didelot 				 GLOBAL_STATS_OP_HIST_RX_TX | stat);
816a935c052SVivien Didelot 	if (err)
817fad09c73SVivien Didelot 		return;
818fad09c73SVivien Didelot 
819a935c052SVivien Didelot 	err = _mv88e6xxx_stats_wait(chip);
820a935c052SVivien Didelot 	if (err)
821fad09c73SVivien Didelot 		return;
822fad09c73SVivien Didelot 
823a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, &reg);
824a935c052SVivien Didelot 	if (err)
825fad09c73SVivien Didelot 		return;
826fad09c73SVivien Didelot 
827a935c052SVivien Didelot 	value = reg << 16;
828fad09c73SVivien Didelot 
829a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, &reg);
830a935c052SVivien Didelot 	if (err)
831fad09c73SVivien Didelot 		return;
832fad09c73SVivien Didelot 
833a935c052SVivien Didelot 	*val = value | reg;
834fad09c73SVivien Didelot }
835fad09c73SVivien Didelot 
836fad09c73SVivien Didelot static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
837fad09c73SVivien Didelot 	{ "in_good_octets",	8, 0x00, BANK0, },
838fad09c73SVivien Didelot 	{ "in_bad_octets",	4, 0x02, BANK0, },
839fad09c73SVivien Didelot 	{ "in_unicast",		4, 0x04, BANK0, },
840fad09c73SVivien Didelot 	{ "in_broadcasts",	4, 0x06, BANK0, },
841fad09c73SVivien Didelot 	{ "in_multicasts",	4, 0x07, BANK0, },
842fad09c73SVivien Didelot 	{ "in_pause",		4, 0x16, BANK0, },
843fad09c73SVivien Didelot 	{ "in_undersize",	4, 0x18, BANK0, },
844fad09c73SVivien Didelot 	{ "in_fragments",	4, 0x19, BANK0, },
845fad09c73SVivien Didelot 	{ "in_oversize",	4, 0x1a, BANK0, },
846fad09c73SVivien Didelot 	{ "in_jabber",		4, 0x1b, BANK0, },
847fad09c73SVivien Didelot 	{ "in_rx_error",	4, 0x1c, BANK0, },
848fad09c73SVivien Didelot 	{ "in_fcs_error",	4, 0x1d, BANK0, },
849fad09c73SVivien Didelot 	{ "out_octets",		8, 0x0e, BANK0, },
850fad09c73SVivien Didelot 	{ "out_unicast",	4, 0x10, BANK0, },
851fad09c73SVivien Didelot 	{ "out_broadcasts",	4, 0x13, BANK0, },
852fad09c73SVivien Didelot 	{ "out_multicasts",	4, 0x12, BANK0, },
853fad09c73SVivien Didelot 	{ "out_pause",		4, 0x15, BANK0, },
854fad09c73SVivien Didelot 	{ "excessive",		4, 0x11, BANK0, },
855fad09c73SVivien Didelot 	{ "collisions",		4, 0x1e, BANK0, },
856fad09c73SVivien Didelot 	{ "deferred",		4, 0x05, BANK0, },
857fad09c73SVivien Didelot 	{ "single",		4, 0x14, BANK0, },
858fad09c73SVivien Didelot 	{ "multiple",		4, 0x17, BANK0, },
859fad09c73SVivien Didelot 	{ "out_fcs_error",	4, 0x03, BANK0, },
860fad09c73SVivien Didelot 	{ "late",		4, 0x1f, BANK0, },
861fad09c73SVivien Didelot 	{ "hist_64bytes",	4, 0x08, BANK0, },
862fad09c73SVivien Didelot 	{ "hist_65_127bytes",	4, 0x09, BANK0, },
863fad09c73SVivien Didelot 	{ "hist_128_255bytes",	4, 0x0a, BANK0, },
864fad09c73SVivien Didelot 	{ "hist_256_511bytes",	4, 0x0b, BANK0, },
865fad09c73SVivien Didelot 	{ "hist_512_1023bytes", 4, 0x0c, BANK0, },
866fad09c73SVivien Didelot 	{ "hist_1024_max_bytes", 4, 0x0d, BANK0, },
867fad09c73SVivien Didelot 	{ "sw_in_discards",	4, 0x10, PORT, },
868fad09c73SVivien Didelot 	{ "sw_in_filtered",	2, 0x12, PORT, },
869fad09c73SVivien Didelot 	{ "sw_out_filtered",	2, 0x13, PORT, },
870fad09c73SVivien Didelot 	{ "in_discards",	4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
871fad09c73SVivien Didelot 	{ "in_filtered",	4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
872fad09c73SVivien Didelot 	{ "in_accepted",	4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
873fad09c73SVivien Didelot 	{ "in_bad_accepted",	4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
874fad09c73SVivien Didelot 	{ "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
875fad09c73SVivien Didelot 	{ "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
876fad09c73SVivien Didelot 	{ "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
877fad09c73SVivien Didelot 	{ "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
878fad09c73SVivien Didelot 	{ "tcam_counter_0",	4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
879fad09c73SVivien Didelot 	{ "tcam_counter_1",	4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
880fad09c73SVivien Didelot 	{ "tcam_counter_2",	4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
881fad09c73SVivien Didelot 	{ "tcam_counter_3",	4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
882fad09c73SVivien Didelot 	{ "in_da_unknown",	4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
883fad09c73SVivien Didelot 	{ "in_management",	4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
884fad09c73SVivien Didelot 	{ "out_queue_0",	4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
885fad09c73SVivien Didelot 	{ "out_queue_1",	4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
886fad09c73SVivien Didelot 	{ "out_queue_2",	4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
887fad09c73SVivien Didelot 	{ "out_queue_3",	4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
888fad09c73SVivien Didelot 	{ "out_queue_4",	4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
889fad09c73SVivien Didelot 	{ "out_queue_5",	4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
890fad09c73SVivien Didelot 	{ "out_queue_6",	4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
891fad09c73SVivien Didelot 	{ "out_queue_7",	4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
892fad09c73SVivien Didelot 	{ "out_cut_through",	4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
893fad09c73SVivien Didelot 	{ "out_octets_a",	4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
894fad09c73SVivien Didelot 	{ "out_octets_b",	4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
895fad09c73SVivien Didelot 	{ "out_management",	4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
896fad09c73SVivien Didelot };
897fad09c73SVivien Didelot 
898fad09c73SVivien Didelot static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
899fad09c73SVivien Didelot 			       struct mv88e6xxx_hw_stat *stat)
900fad09c73SVivien Didelot {
901fad09c73SVivien Didelot 	switch (stat->type) {
902fad09c73SVivien Didelot 	case BANK0:
903fad09c73SVivien Didelot 		return true;
904fad09c73SVivien Didelot 	case BANK1:
905fad09c73SVivien Didelot 		return mv88e6xxx_6320_family(chip);
906fad09c73SVivien Didelot 	case PORT:
907fad09c73SVivien Didelot 		return mv88e6xxx_6095_family(chip) ||
908fad09c73SVivien Didelot 			mv88e6xxx_6185_family(chip) ||
909fad09c73SVivien Didelot 			mv88e6xxx_6097_family(chip) ||
910fad09c73SVivien Didelot 			mv88e6xxx_6165_family(chip) ||
911fad09c73SVivien Didelot 			mv88e6xxx_6351_family(chip) ||
912fad09c73SVivien Didelot 			mv88e6xxx_6352_family(chip);
913fad09c73SVivien Didelot 	}
914fad09c73SVivien Didelot 	return false;
915fad09c73SVivien Didelot }
916fad09c73SVivien Didelot 
917fad09c73SVivien Didelot static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
918fad09c73SVivien Didelot 					    struct mv88e6xxx_hw_stat *s,
919fad09c73SVivien Didelot 					    int port)
920fad09c73SVivien Didelot {
921fad09c73SVivien Didelot 	u32 low;
922fad09c73SVivien Didelot 	u32 high = 0;
9230e7b9925SAndrew Lunn 	int err;
9240e7b9925SAndrew Lunn 	u16 reg;
925fad09c73SVivien Didelot 	u64 value;
926fad09c73SVivien Didelot 
927fad09c73SVivien Didelot 	switch (s->type) {
928fad09c73SVivien Didelot 	case PORT:
9290e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, s->reg, &reg);
9300e7b9925SAndrew Lunn 		if (err)
931fad09c73SVivien Didelot 			return UINT64_MAX;
932fad09c73SVivien Didelot 
9330e7b9925SAndrew Lunn 		low = reg;
934fad09c73SVivien Didelot 		if (s->sizeof_stat == 4) {
9350e7b9925SAndrew Lunn 			err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
9360e7b9925SAndrew Lunn 			if (err)
937fad09c73SVivien Didelot 				return UINT64_MAX;
9380e7b9925SAndrew Lunn 			high = reg;
939fad09c73SVivien Didelot 		}
940fad09c73SVivien Didelot 		break;
941fad09c73SVivien Didelot 	case BANK0:
942fad09c73SVivien Didelot 	case BANK1:
943fad09c73SVivien Didelot 		_mv88e6xxx_stats_read(chip, s->reg, &low);
944fad09c73SVivien Didelot 		if (s->sizeof_stat == 8)
945fad09c73SVivien Didelot 			_mv88e6xxx_stats_read(chip, s->reg + 1, &high);
946fad09c73SVivien Didelot 	}
947fad09c73SVivien Didelot 	value = (((u64)high) << 16) | low;
948fad09c73SVivien Didelot 	return value;
949fad09c73SVivien Didelot }
950fad09c73SVivien Didelot 
951fad09c73SVivien Didelot static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
952fad09c73SVivien Didelot 				  uint8_t *data)
953fad09c73SVivien Didelot {
95404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
955fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
956fad09c73SVivien Didelot 	int i, j;
957fad09c73SVivien Didelot 
958fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
959fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
960fad09c73SVivien Didelot 		if (mv88e6xxx_has_stat(chip, stat)) {
961fad09c73SVivien Didelot 			memcpy(data + j * ETH_GSTRING_LEN, stat->string,
962fad09c73SVivien Didelot 			       ETH_GSTRING_LEN);
963fad09c73SVivien Didelot 			j++;
964fad09c73SVivien Didelot 		}
965fad09c73SVivien Didelot 	}
966fad09c73SVivien Didelot }
967fad09c73SVivien Didelot 
968fad09c73SVivien Didelot static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
969fad09c73SVivien Didelot {
97004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
971fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
972fad09c73SVivien Didelot 	int i, j;
973fad09c73SVivien Didelot 
974fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
975fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
976fad09c73SVivien Didelot 		if (mv88e6xxx_has_stat(chip, stat))
977fad09c73SVivien Didelot 			j++;
978fad09c73SVivien Didelot 	}
979fad09c73SVivien Didelot 	return j;
980fad09c73SVivien Didelot }
981fad09c73SVivien Didelot 
982fad09c73SVivien Didelot static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
983fad09c73SVivien Didelot 					uint64_t *data)
984fad09c73SVivien Didelot {
98504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
986fad09c73SVivien Didelot 	struct mv88e6xxx_hw_stat *stat;
987fad09c73SVivien Didelot 	int ret;
988fad09c73SVivien Didelot 	int i, j;
989fad09c73SVivien Didelot 
990fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
991fad09c73SVivien Didelot 
992fad09c73SVivien Didelot 	ret = _mv88e6xxx_stats_snapshot(chip, port);
993fad09c73SVivien Didelot 	if (ret < 0) {
994fad09c73SVivien Didelot 		mutex_unlock(&chip->reg_lock);
995fad09c73SVivien Didelot 		return;
996fad09c73SVivien Didelot 	}
997fad09c73SVivien Didelot 	for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
998fad09c73SVivien Didelot 		stat = &mv88e6xxx_hw_stats[i];
999fad09c73SVivien Didelot 		if (mv88e6xxx_has_stat(chip, stat)) {
1000fad09c73SVivien Didelot 			data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
1001fad09c73SVivien Didelot 			j++;
1002fad09c73SVivien Didelot 		}
1003fad09c73SVivien Didelot 	}
1004fad09c73SVivien Didelot 
1005fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1006fad09c73SVivien Didelot }
1007fad09c73SVivien Didelot 
1008fad09c73SVivien Didelot static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
1009fad09c73SVivien Didelot {
1010fad09c73SVivien Didelot 	return 32 * sizeof(u16);
1011fad09c73SVivien Didelot }
1012fad09c73SVivien Didelot 
1013fad09c73SVivien Didelot static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
1014fad09c73SVivien Didelot 			       struct ethtool_regs *regs, void *_p)
1015fad09c73SVivien Didelot {
101604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10170e7b9925SAndrew Lunn 	int err;
10180e7b9925SAndrew Lunn 	u16 reg;
1019fad09c73SVivien Didelot 	u16 *p = _p;
1020fad09c73SVivien Didelot 	int i;
1021fad09c73SVivien Didelot 
1022fad09c73SVivien Didelot 	regs->version = 0;
1023fad09c73SVivien Didelot 
1024fad09c73SVivien Didelot 	memset(p, 0xff, 32 * sizeof(u16));
1025fad09c73SVivien Didelot 
1026fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1027fad09c73SVivien Didelot 
1028fad09c73SVivien Didelot 	for (i = 0; i < 32; i++) {
1029fad09c73SVivien Didelot 
10300e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, i, &reg);
10310e7b9925SAndrew Lunn 		if (!err)
10320e7b9925SAndrew Lunn 			p[i] = reg;
1033fad09c73SVivien Didelot 	}
1034fad09c73SVivien Didelot 
1035fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1036fad09c73SVivien Didelot }
1037fad09c73SVivien Didelot 
1038fad09c73SVivien Didelot static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
1039fad09c73SVivien Didelot {
1040a935c052SVivien Didelot 	return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
1041fad09c73SVivien Didelot }
1042fad09c73SVivien Didelot 
1043fad09c73SVivien Didelot static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1044fad09c73SVivien Didelot 			     struct ethtool_eee *e)
1045fad09c73SVivien Didelot {
104604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10479c93829cSVivien Didelot 	u16 reg;
10489c93829cSVivien Didelot 	int err;
1049fad09c73SVivien Didelot 
1050fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
1051fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1052fad09c73SVivien Didelot 
1053fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1054fad09c73SVivien Didelot 
10559c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
10569c93829cSVivien Didelot 	if (err)
1057fad09c73SVivien Didelot 		goto out;
1058fad09c73SVivien Didelot 
1059fad09c73SVivien Didelot 	e->eee_enabled = !!(reg & 0x0200);
1060fad09c73SVivien Didelot 	e->tx_lpi_enabled = !!(reg & 0x0100);
1061fad09c73SVivien Didelot 
10620e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
10639c93829cSVivien Didelot 	if (err)
1064fad09c73SVivien Didelot 		goto out;
1065fad09c73SVivien Didelot 
1066fad09c73SVivien Didelot 	e->eee_active = !!(reg & PORT_STATUS_EEE);
1067fad09c73SVivien Didelot out:
1068fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
10699c93829cSVivien Didelot 
10709c93829cSVivien Didelot 	return err;
1071fad09c73SVivien Didelot }
1072fad09c73SVivien Didelot 
1073fad09c73SVivien Didelot static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1074fad09c73SVivien Didelot 			     struct phy_device *phydev, struct ethtool_eee *e)
1075fad09c73SVivien Didelot {
107604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
10779c93829cSVivien Didelot 	u16 reg;
10789c93829cSVivien Didelot 	int err;
1079fad09c73SVivien Didelot 
1080fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
1081fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1082fad09c73SVivien Didelot 
1083fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1084fad09c73SVivien Didelot 
10859c93829cSVivien Didelot 	err = mv88e6xxx_phy_read(chip, port, 16, &reg);
10869c93829cSVivien Didelot 	if (err)
1087fad09c73SVivien Didelot 		goto out;
1088fad09c73SVivien Didelot 
10899c93829cSVivien Didelot 	reg &= ~0x0300;
1090fad09c73SVivien Didelot 	if (e->eee_enabled)
1091fad09c73SVivien Didelot 		reg |= 0x0200;
1092fad09c73SVivien Didelot 	if (e->tx_lpi_enabled)
1093fad09c73SVivien Didelot 		reg |= 0x0100;
1094fad09c73SVivien Didelot 
10959c93829cSVivien Didelot 	err = mv88e6xxx_phy_write(chip, port, 16, reg);
1096fad09c73SVivien Didelot out:
1097fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1098fad09c73SVivien Didelot 
10999c93829cSVivien Didelot 	return err;
1100fad09c73SVivien Didelot }
1101fad09c73SVivien Didelot 
1102fad09c73SVivien Didelot static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
1103fad09c73SVivien Didelot {
1104a935c052SVivien Didelot 	u16 val;
1105a935c052SVivien Didelot 	int err;
1106fad09c73SVivien Didelot 
11076dc10bbcSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) {
1108a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid);
1109a935c052SVivien Didelot 		if (err)
1110a935c052SVivien Didelot 			return err;
1111fad09c73SVivien Didelot 	} else if (mv88e6xxx_num_databases(chip) == 256) {
1112fad09c73SVivien Didelot 		/* ATU DBNum[7:4] are located in ATU Control 15:12 */
1113a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
1114a935c052SVivien Didelot 		if (err)
1115a935c052SVivien Didelot 			return err;
1116fad09c73SVivien Didelot 
1117a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
1118a935c052SVivien Didelot 					 (val & 0xfff) | ((fid << 8) & 0xf000));
1119a935c052SVivien Didelot 		if (err)
1120a935c052SVivien Didelot 			return err;
1121fad09c73SVivien Didelot 
1122fad09c73SVivien Didelot 		/* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1123fad09c73SVivien Didelot 		cmd |= fid & 0xf;
1124fad09c73SVivien Didelot 	}
1125fad09c73SVivien Didelot 
1126a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd);
1127a935c052SVivien Didelot 	if (err)
1128a935c052SVivien Didelot 		return err;
1129fad09c73SVivien Didelot 
1130fad09c73SVivien Didelot 	return _mv88e6xxx_atu_wait(chip);
1131fad09c73SVivien Didelot }
1132fad09c73SVivien Didelot 
1133fad09c73SVivien Didelot static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
1134fad09c73SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry)
1135fad09c73SVivien Didelot {
1136fad09c73SVivien Didelot 	u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1137fad09c73SVivien Didelot 
1138fad09c73SVivien Didelot 	if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1139fad09c73SVivien Didelot 		unsigned int mask, shift;
1140fad09c73SVivien Didelot 
1141fad09c73SVivien Didelot 		if (entry->trunk) {
1142fad09c73SVivien Didelot 			data |= GLOBAL_ATU_DATA_TRUNK;
1143fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1144fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1145fad09c73SVivien Didelot 		} else {
1146fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1147fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1148fad09c73SVivien Didelot 		}
1149fad09c73SVivien Didelot 
1150fad09c73SVivien Didelot 		data |= (entry->portv_trunkid << shift) & mask;
1151fad09c73SVivien Didelot 	}
1152fad09c73SVivien Didelot 
1153a935c052SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
1154fad09c73SVivien Didelot }
1155fad09c73SVivien Didelot 
1156fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
1157fad09c73SVivien Didelot 				     struct mv88e6xxx_atu_entry *entry,
1158fad09c73SVivien Didelot 				     bool static_too)
1159fad09c73SVivien Didelot {
1160fad09c73SVivien Didelot 	int op;
1161fad09c73SVivien Didelot 	int err;
1162fad09c73SVivien Didelot 
1163fad09c73SVivien Didelot 	err = _mv88e6xxx_atu_wait(chip);
1164fad09c73SVivien Didelot 	if (err)
1165fad09c73SVivien Didelot 		return err;
1166fad09c73SVivien Didelot 
1167fad09c73SVivien Didelot 	err = _mv88e6xxx_atu_data_write(chip, entry);
1168fad09c73SVivien Didelot 	if (err)
1169fad09c73SVivien Didelot 		return err;
1170fad09c73SVivien Didelot 
1171fad09c73SVivien Didelot 	if (entry->fid) {
1172fad09c73SVivien Didelot 		op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1173fad09c73SVivien Didelot 			GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1174fad09c73SVivien Didelot 	} else {
1175fad09c73SVivien Didelot 		op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1176fad09c73SVivien Didelot 			GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1177fad09c73SVivien Didelot 	}
1178fad09c73SVivien Didelot 
1179fad09c73SVivien Didelot 	return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
1180fad09c73SVivien Didelot }
1181fad09c73SVivien Didelot 
1182fad09c73SVivien Didelot static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
1183fad09c73SVivien Didelot 				u16 fid, bool static_too)
1184fad09c73SVivien Didelot {
1185fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
1186fad09c73SVivien Didelot 		.fid = fid,
1187fad09c73SVivien Didelot 		.state = 0, /* EntryState bits must be 0 */
1188fad09c73SVivien Didelot 	};
1189fad09c73SVivien Didelot 
1190fad09c73SVivien Didelot 	return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
1191fad09c73SVivien Didelot }
1192fad09c73SVivien Didelot 
1193fad09c73SVivien Didelot static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
1194fad09c73SVivien Didelot 			       int from_port, int to_port, bool static_too)
1195fad09c73SVivien Didelot {
1196fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry entry = {
1197fad09c73SVivien Didelot 		.trunk = false,
1198fad09c73SVivien Didelot 		.fid = fid,
1199fad09c73SVivien Didelot 	};
1200fad09c73SVivien Didelot 
1201fad09c73SVivien Didelot 	/* EntryState bits must be 0xF */
1202fad09c73SVivien Didelot 	entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1203fad09c73SVivien Didelot 
1204fad09c73SVivien Didelot 	/* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1205fad09c73SVivien Didelot 	entry.portv_trunkid = (to_port & 0x0f) << 4;
1206fad09c73SVivien Didelot 	entry.portv_trunkid |= from_port & 0x0f;
1207fad09c73SVivien Didelot 
1208fad09c73SVivien Didelot 	return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
1209fad09c73SVivien Didelot }
1210fad09c73SVivien Didelot 
1211fad09c73SVivien Didelot static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
1212fad09c73SVivien Didelot 				 int port, bool static_too)
1213fad09c73SVivien Didelot {
1214fad09c73SVivien Didelot 	/* Destination port 0xF means remove the entries */
1215fad09c73SVivien Didelot 	return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
1216fad09c73SVivien Didelot }
1217fad09c73SVivien Didelot 
1218fad09c73SVivien Didelot static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
1219fad09c73SVivien Didelot {
1220fad09c73SVivien Didelot 	struct net_device *bridge = chip->ports[port].bridge_dev;
1221370b4ffbSVivien Didelot 	const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;
1222fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1223fad09c73SVivien Didelot 	u16 output_ports = 0;
12240e7b9925SAndrew Lunn 	u16 reg;
12250e7b9925SAndrew Lunn 	int err;
1226fad09c73SVivien Didelot 	int i;
1227fad09c73SVivien Didelot 
1228fad09c73SVivien Didelot 	/* allow CPU port or DSA link(s) to send frames to every port */
1229fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1230fad09c73SVivien Didelot 		output_ports = mask;
1231fad09c73SVivien Didelot 	} else {
1232370b4ffbSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1233fad09c73SVivien Didelot 			/* allow sending frames to every group member */
1234fad09c73SVivien Didelot 			if (bridge && chip->ports[i].bridge_dev == bridge)
1235fad09c73SVivien Didelot 				output_ports |= BIT(i);
1236fad09c73SVivien Didelot 
1237fad09c73SVivien Didelot 			/* allow sending frames to CPU port and DSA link(s) */
1238fad09c73SVivien Didelot 			if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1239fad09c73SVivien Didelot 				output_ports |= BIT(i);
1240fad09c73SVivien Didelot 		}
1241fad09c73SVivien Didelot 	}
1242fad09c73SVivien Didelot 
1243fad09c73SVivien Didelot 	/* prevent frames from going back out of the port they came in on */
1244fad09c73SVivien Didelot 	output_ports &= ~BIT(port);
1245fad09c73SVivien Didelot 
12460e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
12470e7b9925SAndrew Lunn 	if (err)
12480e7b9925SAndrew Lunn 		return err;
1249fad09c73SVivien Didelot 
1250fad09c73SVivien Didelot 	reg &= ~mask;
1251fad09c73SVivien Didelot 	reg |= output_ports & mask;
1252fad09c73SVivien Didelot 
12530e7b9925SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
1254fad09c73SVivien Didelot }
1255fad09c73SVivien Didelot 
1256fad09c73SVivien Didelot static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1257fad09c73SVivien Didelot 					 u8 state)
1258fad09c73SVivien Didelot {
125904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1260fad09c73SVivien Didelot 	int stp_state;
1261fad09c73SVivien Didelot 	int err;
1262fad09c73SVivien Didelot 
1263fad09c73SVivien Didelot 	switch (state) {
1264fad09c73SVivien Didelot 	case BR_STATE_DISABLED:
1265fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_DISABLED;
1266fad09c73SVivien Didelot 		break;
1267fad09c73SVivien Didelot 	case BR_STATE_BLOCKING:
1268fad09c73SVivien Didelot 	case BR_STATE_LISTENING:
1269fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_BLOCKING;
1270fad09c73SVivien Didelot 		break;
1271fad09c73SVivien Didelot 	case BR_STATE_LEARNING:
1272fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_LEARNING;
1273fad09c73SVivien Didelot 		break;
1274fad09c73SVivien Didelot 	case BR_STATE_FORWARDING:
1275fad09c73SVivien Didelot 	default:
1276fad09c73SVivien Didelot 		stp_state = PORT_CONTROL_STATE_FORWARDING;
1277fad09c73SVivien Didelot 		break;
1278fad09c73SVivien Didelot 	}
1279fad09c73SVivien Didelot 
1280fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1281e28def33SVivien Didelot 	err = mv88e6xxx_port_set_state(chip, port, stp_state);
1282fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1283fad09c73SVivien Didelot 
1284fad09c73SVivien Didelot 	if (err)
1285e28def33SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to update state\n");
1286fad09c73SVivien Didelot }
1287fad09c73SVivien Didelot 
1288749efcb8SVivien Didelot static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
1289749efcb8SVivien Didelot {
1290749efcb8SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1291749efcb8SVivien Didelot 	int err;
1292749efcb8SVivien Didelot 
1293749efcb8SVivien Didelot 	mutex_lock(&chip->reg_lock);
1294749efcb8SVivien Didelot 	err = _mv88e6xxx_atu_remove(chip, 0, port, false);
1295749efcb8SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1296749efcb8SVivien Didelot 
1297749efcb8SVivien Didelot 	if (err)
1298749efcb8SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
1299749efcb8SVivien Didelot }
1300749efcb8SVivien Didelot 
1301fad09c73SVivien Didelot static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
1302fad09c73SVivien Didelot 				u16 *new, u16 *old)
1303fad09c73SVivien Didelot {
1304fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
13050e7b9925SAndrew Lunn 	u16 pvid, reg;
13060e7b9925SAndrew Lunn 	int err;
1307fad09c73SVivien Didelot 
13080e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
13090e7b9925SAndrew Lunn 	if (err)
13100e7b9925SAndrew Lunn 		return err;
1311fad09c73SVivien Didelot 
13120e7b9925SAndrew Lunn 	pvid = reg & PORT_DEFAULT_VLAN_MASK;
1313fad09c73SVivien Didelot 
1314fad09c73SVivien Didelot 	if (new) {
13150e7b9925SAndrew Lunn 		reg &= ~PORT_DEFAULT_VLAN_MASK;
13160e7b9925SAndrew Lunn 		reg |= *new & PORT_DEFAULT_VLAN_MASK;
1317fad09c73SVivien Didelot 
13180e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
13190e7b9925SAndrew Lunn 		if (err)
13200e7b9925SAndrew Lunn 			return err;
1321fad09c73SVivien Didelot 
1322fad09c73SVivien Didelot 		netdev_dbg(ds->ports[port].netdev,
1323fad09c73SVivien Didelot 			   "DefaultVID %d (was %d)\n", *new, pvid);
1324fad09c73SVivien Didelot 	}
1325fad09c73SVivien Didelot 
1326fad09c73SVivien Didelot 	if (old)
1327fad09c73SVivien Didelot 		*old = pvid;
1328fad09c73SVivien Didelot 
1329fad09c73SVivien Didelot 	return 0;
1330fad09c73SVivien Didelot }
1331fad09c73SVivien Didelot 
1332fad09c73SVivien Didelot static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
1333fad09c73SVivien Didelot 				    int port, u16 *pvid)
1334fad09c73SVivien Didelot {
1335fad09c73SVivien Didelot 	return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
1336fad09c73SVivien Didelot }
1337fad09c73SVivien Didelot 
1338fad09c73SVivien Didelot static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
1339fad09c73SVivien Didelot 				    int port, u16 pvid)
1340fad09c73SVivien Didelot {
1341fad09c73SVivien Didelot 	return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
1342fad09c73SVivien Didelot }
1343fad09c73SVivien Didelot 
1344fad09c73SVivien Didelot static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
1345fad09c73SVivien Didelot {
1346a935c052SVivien Didelot 	return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
1347fad09c73SVivien Didelot }
1348fad09c73SVivien Didelot 
1349fad09c73SVivien Didelot static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
1350fad09c73SVivien Didelot {
1351a935c052SVivien Didelot 	int err;
1352fad09c73SVivien Didelot 
1353a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
1354a935c052SVivien Didelot 	if (err)
1355a935c052SVivien Didelot 		return err;
1356fad09c73SVivien Didelot 
1357fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_wait(chip);
1358fad09c73SVivien Didelot }
1359fad09c73SVivien Didelot 
1360fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
1361fad09c73SVivien Didelot {
1362fad09c73SVivien Didelot 	int ret;
1363fad09c73SVivien Didelot 
1364fad09c73SVivien Didelot 	ret = _mv88e6xxx_vtu_wait(chip);
1365fad09c73SVivien Didelot 	if (ret < 0)
1366fad09c73SVivien Didelot 		return ret;
1367fad09c73SVivien Didelot 
1368fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
1369fad09c73SVivien Didelot }
1370fad09c73SVivien Didelot 
1371fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
1372b4e47c0fSVivien Didelot 					struct mv88e6xxx_vtu_entry *entry,
1373fad09c73SVivien Didelot 					unsigned int nibble_offset)
1374fad09c73SVivien Didelot {
1375fad09c73SVivien Didelot 	u16 regs[3];
1376a935c052SVivien Didelot 	int i, err;
1377fad09c73SVivien Didelot 
1378fad09c73SVivien Didelot 	for (i = 0; i < 3; ++i) {
1379a935c052SVivien Didelot 		u16 *reg = &regs[i];
1380fad09c73SVivien Didelot 
1381a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
1382a935c052SVivien Didelot 		if (err)
1383a935c052SVivien Didelot 			return err;
1384fad09c73SVivien Didelot 	}
1385fad09c73SVivien Didelot 
1386370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1387fad09c73SVivien Didelot 		unsigned int shift = (i % 4) * 4 + nibble_offset;
1388fad09c73SVivien Didelot 		u16 reg = regs[i / 4];
1389fad09c73SVivien Didelot 
1390fad09c73SVivien Didelot 		entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1391fad09c73SVivien Didelot 	}
1392fad09c73SVivien Didelot 
1393fad09c73SVivien Didelot 	return 0;
1394fad09c73SVivien Didelot }
1395fad09c73SVivien Didelot 
1396fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
1397b4e47c0fSVivien Didelot 				   struct mv88e6xxx_vtu_entry *entry)
1398fad09c73SVivien Didelot {
1399fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
1400fad09c73SVivien Didelot }
1401fad09c73SVivien Didelot 
1402fad09c73SVivien Didelot static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
1403b4e47c0fSVivien Didelot 				   struct mv88e6xxx_vtu_entry *entry)
1404fad09c73SVivien Didelot {
1405fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
1406fad09c73SVivien Didelot }
1407fad09c73SVivien Didelot 
1408fad09c73SVivien Didelot static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
1409b4e47c0fSVivien Didelot 					 struct mv88e6xxx_vtu_entry *entry,
1410fad09c73SVivien Didelot 					 unsigned int nibble_offset)
1411fad09c73SVivien Didelot {
1412fad09c73SVivien Didelot 	u16 regs[3] = { 0 };
1413a935c052SVivien Didelot 	int i, err;
1414fad09c73SVivien Didelot 
1415370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1416fad09c73SVivien Didelot 		unsigned int shift = (i % 4) * 4 + nibble_offset;
1417fad09c73SVivien Didelot 		u8 data = entry->data[i];
1418fad09c73SVivien Didelot 
1419fad09c73SVivien Didelot 		regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1420fad09c73SVivien Didelot 	}
1421fad09c73SVivien Didelot 
1422fad09c73SVivien Didelot 	for (i = 0; i < 3; ++i) {
1423a935c052SVivien Didelot 		u16 reg = regs[i];
1424a935c052SVivien Didelot 
1425a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
1426a935c052SVivien Didelot 		if (err)
1427a935c052SVivien Didelot 			return err;
1428fad09c73SVivien Didelot 	}
1429fad09c73SVivien Didelot 
1430fad09c73SVivien Didelot 	return 0;
1431fad09c73SVivien Didelot }
1432fad09c73SVivien Didelot 
1433fad09c73SVivien Didelot static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
1434b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1435fad09c73SVivien Didelot {
1436fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
1437fad09c73SVivien Didelot }
1438fad09c73SVivien Didelot 
1439fad09c73SVivien Didelot static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
1440b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1441fad09c73SVivien Didelot {
1442fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
1443fad09c73SVivien Didelot }
1444fad09c73SVivien Didelot 
1445fad09c73SVivien Didelot static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
1446fad09c73SVivien Didelot {
1447a935c052SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID,
1448fad09c73SVivien Didelot 				  vid & GLOBAL_VTU_VID_MASK);
1449fad09c73SVivien Didelot }
1450fad09c73SVivien Didelot 
1451fad09c73SVivien Didelot static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
1452b4e47c0fSVivien Didelot 				  struct mv88e6xxx_vtu_entry *entry)
1453fad09c73SVivien Didelot {
1454b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry next = { 0 };
1455a935c052SVivien Didelot 	u16 val;
1456a935c052SVivien Didelot 	int err;
1457fad09c73SVivien Didelot 
1458a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1459a935c052SVivien Didelot 	if (err)
1460a935c052SVivien Didelot 		return err;
1461fad09c73SVivien Didelot 
1462a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
1463a935c052SVivien Didelot 	if (err)
1464a935c052SVivien Didelot 		return err;
1465fad09c73SVivien Didelot 
1466a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
1467a935c052SVivien Didelot 	if (err)
1468a935c052SVivien Didelot 		return err;
1469fad09c73SVivien Didelot 
1470a935c052SVivien Didelot 	next.vid = val & GLOBAL_VTU_VID_MASK;
1471a935c052SVivien Didelot 	next.valid = !!(val & GLOBAL_VTU_VID_VALID);
1472fad09c73SVivien Didelot 
1473fad09c73SVivien Didelot 	if (next.valid) {
1474a935c052SVivien Didelot 		err = mv88e6xxx_vtu_data_read(chip, &next);
1475a935c052SVivien Didelot 		if (err)
1476a935c052SVivien Didelot 			return err;
1477fad09c73SVivien Didelot 
14786dc10bbcSVivien Didelot 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
1479a935c052SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
1480a935c052SVivien Didelot 			if (err)
1481a935c052SVivien Didelot 				return err;
1482fad09c73SVivien Didelot 
1483a935c052SVivien Didelot 			next.fid = val & GLOBAL_VTU_FID_MASK;
1484fad09c73SVivien Didelot 		} else if (mv88e6xxx_num_databases(chip) == 256) {
1485fad09c73SVivien Didelot 			/* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1486fad09c73SVivien Didelot 			 * VTU DBNum[3:0] are located in VTU Operation 3:0
1487fad09c73SVivien Didelot 			 */
1488a935c052SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
1489a935c052SVivien Didelot 			if (err)
1490a935c052SVivien Didelot 				return err;
1491fad09c73SVivien Didelot 
1492a935c052SVivien Didelot 			next.fid = (val & 0xf00) >> 4;
1493a935c052SVivien Didelot 			next.fid |= val & 0xf;
1494fad09c73SVivien Didelot 		}
1495fad09c73SVivien Didelot 
1496fad09c73SVivien Didelot 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1497a935c052SVivien Didelot 			err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
1498a935c052SVivien Didelot 			if (err)
1499a935c052SVivien Didelot 				return err;
1500fad09c73SVivien Didelot 
1501a935c052SVivien Didelot 			next.sid = val & GLOBAL_VTU_SID_MASK;
1502fad09c73SVivien Didelot 		}
1503fad09c73SVivien Didelot 	}
1504fad09c73SVivien Didelot 
1505fad09c73SVivien Didelot 	*entry = next;
1506fad09c73SVivien Didelot 	return 0;
1507fad09c73SVivien Didelot }
1508fad09c73SVivien Didelot 
1509fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1510fad09c73SVivien Didelot 				    struct switchdev_obj_port_vlan *vlan,
1511fad09c73SVivien Didelot 				    int (*cb)(struct switchdev_obj *obj))
1512fad09c73SVivien Didelot {
151304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1514b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry next;
1515fad09c73SVivien Didelot 	u16 pvid;
1516fad09c73SVivien Didelot 	int err;
1517fad09c73SVivien Didelot 
1518fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1519fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1520fad09c73SVivien Didelot 
1521fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1522fad09c73SVivien Didelot 
1523fad09c73SVivien Didelot 	err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
1524fad09c73SVivien Didelot 	if (err)
1525fad09c73SVivien Didelot 		goto unlock;
1526fad09c73SVivien Didelot 
1527fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
1528fad09c73SVivien Didelot 	if (err)
1529fad09c73SVivien Didelot 		goto unlock;
1530fad09c73SVivien Didelot 
1531fad09c73SVivien Didelot 	do {
1532fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &next);
1533fad09c73SVivien Didelot 		if (err)
1534fad09c73SVivien Didelot 			break;
1535fad09c73SVivien Didelot 
1536fad09c73SVivien Didelot 		if (!next.valid)
1537fad09c73SVivien Didelot 			break;
1538fad09c73SVivien Didelot 
1539fad09c73SVivien Didelot 		if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1540fad09c73SVivien Didelot 			continue;
1541fad09c73SVivien Didelot 
1542fad09c73SVivien Didelot 		/* reinit and dump this VLAN obj */
1543fad09c73SVivien Didelot 		vlan->vid_begin = next.vid;
1544fad09c73SVivien Didelot 		vlan->vid_end = next.vid;
1545fad09c73SVivien Didelot 		vlan->flags = 0;
1546fad09c73SVivien Didelot 
1547fad09c73SVivien Didelot 		if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1548fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1549fad09c73SVivien Didelot 
1550fad09c73SVivien Didelot 		if (next.vid == pvid)
1551fad09c73SVivien Didelot 			vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1552fad09c73SVivien Didelot 
1553fad09c73SVivien Didelot 		err = cb(&vlan->obj);
1554fad09c73SVivien Didelot 		if (err)
1555fad09c73SVivien Didelot 			break;
1556fad09c73SVivien Didelot 	} while (next.vid < GLOBAL_VTU_VID_MASK);
1557fad09c73SVivien Didelot 
1558fad09c73SVivien Didelot unlock:
1559fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1560fad09c73SVivien Didelot 
1561fad09c73SVivien Didelot 	return err;
1562fad09c73SVivien Didelot }
1563fad09c73SVivien Didelot 
1564fad09c73SVivien Didelot static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
1565b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1566fad09c73SVivien Didelot {
1567fad09c73SVivien Didelot 	u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
1568fad09c73SVivien Didelot 	u16 reg = 0;
1569a935c052SVivien Didelot 	int err;
1570fad09c73SVivien Didelot 
1571a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1572a935c052SVivien Didelot 	if (err)
1573a935c052SVivien Didelot 		return err;
1574fad09c73SVivien Didelot 
1575fad09c73SVivien Didelot 	if (!entry->valid)
1576fad09c73SVivien Didelot 		goto loadpurge;
1577fad09c73SVivien Didelot 
1578fad09c73SVivien Didelot 	/* Write port member tags */
1579a935c052SVivien Didelot 	err = mv88e6xxx_vtu_data_write(chip, entry);
1580a935c052SVivien Didelot 	if (err)
1581a935c052SVivien Didelot 		return err;
1582fad09c73SVivien Didelot 
1583fad09c73SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
1584fad09c73SVivien Didelot 		reg = entry->sid & GLOBAL_VTU_SID_MASK;
1585a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
1586a935c052SVivien Didelot 		if (err)
1587a935c052SVivien Didelot 			return err;
1588fad09c73SVivien Didelot 	}
1589fad09c73SVivien Didelot 
15906dc10bbcSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
1591fad09c73SVivien Didelot 		reg = entry->fid & GLOBAL_VTU_FID_MASK;
1592a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg);
1593a935c052SVivien Didelot 		if (err)
1594a935c052SVivien Didelot 			return err;
1595fad09c73SVivien Didelot 	} else if (mv88e6xxx_num_databases(chip) == 256) {
1596fad09c73SVivien Didelot 		/* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1597fad09c73SVivien Didelot 		 * VTU DBNum[3:0] are located in VTU Operation 3:0
1598fad09c73SVivien Didelot 		 */
1599fad09c73SVivien Didelot 		op |= (entry->fid & 0xf0) << 8;
1600fad09c73SVivien Didelot 		op |= entry->fid & 0xf;
1601fad09c73SVivien Didelot 	}
1602fad09c73SVivien Didelot 
1603fad09c73SVivien Didelot 	reg = GLOBAL_VTU_VID_VALID;
1604fad09c73SVivien Didelot loadpurge:
1605fad09c73SVivien Didelot 	reg |= entry->vid & GLOBAL_VTU_VID_MASK;
1606a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
1607a935c052SVivien Didelot 	if (err)
1608a935c052SVivien Didelot 		return err;
1609fad09c73SVivien Didelot 
1610fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_cmd(chip, op);
1611fad09c73SVivien Didelot }
1612fad09c73SVivien Didelot 
1613fad09c73SVivien Didelot static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
1614b4e47c0fSVivien Didelot 				  struct mv88e6xxx_vtu_entry *entry)
1615fad09c73SVivien Didelot {
1616b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry next = { 0 };
1617a935c052SVivien Didelot 	u16 val;
1618a935c052SVivien Didelot 	int err;
1619fad09c73SVivien Didelot 
1620a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1621a935c052SVivien Didelot 	if (err)
1622a935c052SVivien Didelot 		return err;
1623fad09c73SVivien Didelot 
1624a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID,
1625fad09c73SVivien Didelot 				 sid & GLOBAL_VTU_SID_MASK);
1626a935c052SVivien Didelot 	if (err)
1627a935c052SVivien Didelot 		return err;
1628fad09c73SVivien Didelot 
1629a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
1630a935c052SVivien Didelot 	if (err)
1631a935c052SVivien Didelot 		return err;
1632fad09c73SVivien Didelot 
1633a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
1634a935c052SVivien Didelot 	if (err)
1635a935c052SVivien Didelot 		return err;
1636fad09c73SVivien Didelot 
1637a935c052SVivien Didelot 	next.sid = val & GLOBAL_VTU_SID_MASK;
1638fad09c73SVivien Didelot 
1639a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
1640a935c052SVivien Didelot 	if (err)
1641a935c052SVivien Didelot 		return err;
1642fad09c73SVivien Didelot 
1643a935c052SVivien Didelot 	next.valid = !!(val & GLOBAL_VTU_VID_VALID);
1644fad09c73SVivien Didelot 
1645fad09c73SVivien Didelot 	if (next.valid) {
1646a935c052SVivien Didelot 		err = mv88e6xxx_stu_data_read(chip, &next);
1647a935c052SVivien Didelot 		if (err)
1648a935c052SVivien Didelot 			return err;
1649fad09c73SVivien Didelot 	}
1650fad09c73SVivien Didelot 
1651fad09c73SVivien Didelot 	*entry = next;
1652fad09c73SVivien Didelot 	return 0;
1653fad09c73SVivien Didelot }
1654fad09c73SVivien Didelot 
1655fad09c73SVivien Didelot static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
1656b4e47c0fSVivien Didelot 				    struct mv88e6xxx_vtu_entry *entry)
1657fad09c73SVivien Didelot {
1658fad09c73SVivien Didelot 	u16 reg = 0;
1659a935c052SVivien Didelot 	int err;
1660fad09c73SVivien Didelot 
1661a935c052SVivien Didelot 	err = _mv88e6xxx_vtu_wait(chip);
1662a935c052SVivien Didelot 	if (err)
1663a935c052SVivien Didelot 		return err;
1664fad09c73SVivien Didelot 
1665fad09c73SVivien Didelot 	if (!entry->valid)
1666fad09c73SVivien Didelot 		goto loadpurge;
1667fad09c73SVivien Didelot 
1668fad09c73SVivien Didelot 	/* Write port states */
1669a935c052SVivien Didelot 	err = mv88e6xxx_stu_data_write(chip, entry);
1670a935c052SVivien Didelot 	if (err)
1671a935c052SVivien Didelot 		return err;
1672fad09c73SVivien Didelot 
1673fad09c73SVivien Didelot 	reg = GLOBAL_VTU_VID_VALID;
1674fad09c73SVivien Didelot loadpurge:
1675a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
1676a935c052SVivien Didelot 	if (err)
1677a935c052SVivien Didelot 		return err;
1678fad09c73SVivien Didelot 
1679fad09c73SVivien Didelot 	reg = entry->sid & GLOBAL_VTU_SID_MASK;
1680a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
1681a935c052SVivien Didelot 	if (err)
1682a935c052SVivien Didelot 		return err;
1683fad09c73SVivien Didelot 
1684fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
1685fad09c73SVivien Didelot }
1686fad09c73SVivien Didelot 
1687fad09c73SVivien Didelot static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
1688fad09c73SVivien Didelot 			       u16 *new, u16 *old)
1689fad09c73SVivien Didelot {
1690fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1691fad09c73SVivien Didelot 	u16 upper_mask;
1692fad09c73SVivien Didelot 	u16 fid;
16930e7b9925SAndrew Lunn 	u16 reg;
16940e7b9925SAndrew Lunn 	int err;
1695fad09c73SVivien Didelot 
1696fad09c73SVivien Didelot 	if (mv88e6xxx_num_databases(chip) == 4096)
1697fad09c73SVivien Didelot 		upper_mask = 0xff;
1698fad09c73SVivien Didelot 	else if (mv88e6xxx_num_databases(chip) == 256)
1699fad09c73SVivien Didelot 		upper_mask = 0xf;
1700fad09c73SVivien Didelot 	else
1701fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1702fad09c73SVivien Didelot 
1703fad09c73SVivien Didelot 	/* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
17040e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
17050e7b9925SAndrew Lunn 	if (err)
17060e7b9925SAndrew Lunn 		return err;
1707fad09c73SVivien Didelot 
17080e7b9925SAndrew Lunn 	fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1709fad09c73SVivien Didelot 
1710fad09c73SVivien Didelot 	if (new) {
17110e7b9925SAndrew Lunn 		reg &= ~PORT_BASE_VLAN_FID_3_0_MASK;
17120e7b9925SAndrew Lunn 		reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1713fad09c73SVivien Didelot 
17140e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
17150e7b9925SAndrew Lunn 		if (err)
17160e7b9925SAndrew Lunn 			return err;
1717fad09c73SVivien Didelot 	}
1718fad09c73SVivien Didelot 
1719fad09c73SVivien Didelot 	/* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
17200e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
17210e7b9925SAndrew Lunn 	if (err)
17220e7b9925SAndrew Lunn 		return err;
1723fad09c73SVivien Didelot 
17240e7b9925SAndrew Lunn 	fid |= (reg & upper_mask) << 4;
1725fad09c73SVivien Didelot 
1726fad09c73SVivien Didelot 	if (new) {
17270e7b9925SAndrew Lunn 		reg &= ~upper_mask;
17280e7b9925SAndrew Lunn 		reg |= (*new >> 4) & upper_mask;
1729fad09c73SVivien Didelot 
17300e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
17310e7b9925SAndrew Lunn 		if (err)
17320e7b9925SAndrew Lunn 			return err;
1733fad09c73SVivien Didelot 
1734fad09c73SVivien Didelot 		netdev_dbg(ds->ports[port].netdev,
1735fad09c73SVivien Didelot 			   "FID %d (was %d)\n", *new, fid);
1736fad09c73SVivien Didelot 	}
1737fad09c73SVivien Didelot 
1738fad09c73SVivien Didelot 	if (old)
1739fad09c73SVivien Didelot 		*old = fid;
1740fad09c73SVivien Didelot 
1741fad09c73SVivien Didelot 	return 0;
1742fad09c73SVivien Didelot }
1743fad09c73SVivien Didelot 
1744fad09c73SVivien Didelot static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
1745fad09c73SVivien Didelot 				   int port, u16 *fid)
1746fad09c73SVivien Didelot {
1747fad09c73SVivien Didelot 	return _mv88e6xxx_port_fid(chip, port, NULL, fid);
1748fad09c73SVivien Didelot }
1749fad09c73SVivien Didelot 
1750fad09c73SVivien Didelot static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
1751fad09c73SVivien Didelot 				   int port, u16 fid)
1752fad09c73SVivien Didelot {
1753fad09c73SVivien Didelot 	return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
1754fad09c73SVivien Didelot }
1755fad09c73SVivien Didelot 
1756fad09c73SVivien Didelot static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
1757fad09c73SVivien Didelot {
1758fad09c73SVivien Didelot 	DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1759b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1760fad09c73SVivien Didelot 	int i, err;
1761fad09c73SVivien Didelot 
1762fad09c73SVivien Didelot 	bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1763fad09c73SVivien Didelot 
1764fad09c73SVivien Didelot 	/* Set every FID bit used by the (un)bridged ports */
1765370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1766fad09c73SVivien Didelot 		err = _mv88e6xxx_port_fid_get(chip, i, fid);
1767fad09c73SVivien Didelot 		if (err)
1768fad09c73SVivien Didelot 			return err;
1769fad09c73SVivien Didelot 
1770fad09c73SVivien Didelot 		set_bit(*fid, fid_bitmap);
1771fad09c73SVivien Didelot 	}
1772fad09c73SVivien Didelot 
1773fad09c73SVivien Didelot 	/* Set every FID bit used by the VLAN entries */
1774fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
1775fad09c73SVivien Didelot 	if (err)
1776fad09c73SVivien Didelot 		return err;
1777fad09c73SVivien Didelot 
1778fad09c73SVivien Didelot 	do {
1779fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &vlan);
1780fad09c73SVivien Didelot 		if (err)
1781fad09c73SVivien Didelot 			return err;
1782fad09c73SVivien Didelot 
1783fad09c73SVivien Didelot 		if (!vlan.valid)
1784fad09c73SVivien Didelot 			break;
1785fad09c73SVivien Didelot 
1786fad09c73SVivien Didelot 		set_bit(vlan.fid, fid_bitmap);
1787fad09c73SVivien Didelot 	} while (vlan.vid < GLOBAL_VTU_VID_MASK);
1788fad09c73SVivien Didelot 
1789fad09c73SVivien Didelot 	/* The reset value 0x000 is used to indicate that multiple address
1790fad09c73SVivien Didelot 	 * databases are not needed. Return the next positive available.
1791fad09c73SVivien Didelot 	 */
1792fad09c73SVivien Didelot 	*fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
1793fad09c73SVivien Didelot 	if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
1794fad09c73SVivien Didelot 		return -ENOSPC;
1795fad09c73SVivien Didelot 
1796fad09c73SVivien Didelot 	/* Clear the database */
1797fad09c73SVivien Didelot 	return _mv88e6xxx_atu_flush(chip, *fid, true);
1798fad09c73SVivien Didelot }
1799fad09c73SVivien Didelot 
1800fad09c73SVivien Didelot static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
1801b4e47c0fSVivien Didelot 			      struct mv88e6xxx_vtu_entry *entry)
1802fad09c73SVivien Didelot {
1803fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
1804b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
1805fad09c73SVivien Didelot 		.valid = true,
1806fad09c73SVivien Didelot 		.vid = vid,
1807fad09c73SVivien Didelot 	};
1808fad09c73SVivien Didelot 	int i, err;
1809fad09c73SVivien Didelot 
1810fad09c73SVivien Didelot 	err = _mv88e6xxx_fid_new(chip, &vlan.fid);
1811fad09c73SVivien Didelot 	if (err)
1812fad09c73SVivien Didelot 		return err;
1813fad09c73SVivien Didelot 
1814fad09c73SVivien Didelot 	/* exclude all ports except the CPU and DSA ports */
1815370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
1816fad09c73SVivien Didelot 		vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1817fad09c73SVivien Didelot 			? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1818fad09c73SVivien Didelot 			: GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1819fad09c73SVivien Didelot 
1820fad09c73SVivien Didelot 	if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
1821fad09c73SVivien Didelot 	    mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
1822b4e47c0fSVivien Didelot 		struct mv88e6xxx_vtu_entry vstp;
1823fad09c73SVivien Didelot 
1824fad09c73SVivien Didelot 		/* Adding a VTU entry requires a valid STU entry. As VSTP is not
1825fad09c73SVivien Didelot 		 * implemented, only one STU entry is needed to cover all VTU
1826fad09c73SVivien Didelot 		 * entries. Thus, validate the SID 0.
1827fad09c73SVivien Didelot 		 */
1828fad09c73SVivien Didelot 		vlan.sid = 0;
1829fad09c73SVivien Didelot 		err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
1830fad09c73SVivien Didelot 		if (err)
1831fad09c73SVivien Didelot 			return err;
1832fad09c73SVivien Didelot 
1833fad09c73SVivien Didelot 		if (vstp.sid != vlan.sid || !vstp.valid) {
1834fad09c73SVivien Didelot 			memset(&vstp, 0, sizeof(vstp));
1835fad09c73SVivien Didelot 			vstp.valid = true;
1836fad09c73SVivien Didelot 			vstp.sid = vlan.sid;
1837fad09c73SVivien Didelot 
1838fad09c73SVivien Didelot 			err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
1839fad09c73SVivien Didelot 			if (err)
1840fad09c73SVivien Didelot 				return err;
1841fad09c73SVivien Didelot 		}
1842fad09c73SVivien Didelot 	}
1843fad09c73SVivien Didelot 
1844fad09c73SVivien Didelot 	*entry = vlan;
1845fad09c73SVivien Didelot 	return 0;
1846fad09c73SVivien Didelot }
1847fad09c73SVivien Didelot 
1848fad09c73SVivien Didelot static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
1849b4e47c0fSVivien Didelot 			      struct mv88e6xxx_vtu_entry *entry, bool creat)
1850fad09c73SVivien Didelot {
1851fad09c73SVivien Didelot 	int err;
1852fad09c73SVivien Didelot 
1853fad09c73SVivien Didelot 	if (!vid)
1854fad09c73SVivien Didelot 		return -EINVAL;
1855fad09c73SVivien Didelot 
1856fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
1857fad09c73SVivien Didelot 	if (err)
1858fad09c73SVivien Didelot 		return err;
1859fad09c73SVivien Didelot 
1860fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_getnext(chip, entry);
1861fad09c73SVivien Didelot 	if (err)
1862fad09c73SVivien Didelot 		return err;
1863fad09c73SVivien Didelot 
1864fad09c73SVivien Didelot 	if (entry->vid != vid || !entry->valid) {
1865fad09c73SVivien Didelot 		if (!creat)
1866fad09c73SVivien Didelot 			return -EOPNOTSUPP;
1867fad09c73SVivien Didelot 		/* -ENOENT would've been more appropriate, but switchdev expects
1868fad09c73SVivien Didelot 		 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1869fad09c73SVivien Didelot 		 */
1870fad09c73SVivien Didelot 
1871fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_new(chip, vid, entry);
1872fad09c73SVivien Didelot 	}
1873fad09c73SVivien Didelot 
1874fad09c73SVivien Didelot 	return err;
1875fad09c73SVivien Didelot }
1876fad09c73SVivien Didelot 
1877fad09c73SVivien Didelot static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1878fad09c73SVivien Didelot 					u16 vid_begin, u16 vid_end)
1879fad09c73SVivien Didelot {
188004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1881b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
1882fad09c73SVivien Didelot 	int i, err;
1883fad09c73SVivien Didelot 
1884fad09c73SVivien Didelot 	if (!vid_begin)
1885fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1886fad09c73SVivien Didelot 
1887fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1888fad09c73SVivien Didelot 
1889fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
1890fad09c73SVivien Didelot 	if (err)
1891fad09c73SVivien Didelot 		goto unlock;
1892fad09c73SVivien Didelot 
1893fad09c73SVivien Didelot 	do {
1894fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &vlan);
1895fad09c73SVivien Didelot 		if (err)
1896fad09c73SVivien Didelot 			goto unlock;
1897fad09c73SVivien Didelot 
1898fad09c73SVivien Didelot 		if (!vlan.valid)
1899fad09c73SVivien Didelot 			break;
1900fad09c73SVivien Didelot 
1901fad09c73SVivien Didelot 		if (vlan.vid > vid_end)
1902fad09c73SVivien Didelot 			break;
1903fad09c73SVivien Didelot 
1904370b4ffbSVivien Didelot 		for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
1905fad09c73SVivien Didelot 			if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1906fad09c73SVivien Didelot 				continue;
1907fad09c73SVivien Didelot 
1908fad09c73SVivien Didelot 			if (vlan.data[i] ==
1909fad09c73SVivien Didelot 			    GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1910fad09c73SVivien Didelot 				continue;
1911fad09c73SVivien Didelot 
1912fad09c73SVivien Didelot 			if (chip->ports[i].bridge_dev ==
1913fad09c73SVivien Didelot 			    chip->ports[port].bridge_dev)
1914fad09c73SVivien Didelot 				break; /* same bridge, check next VLAN */
1915fad09c73SVivien Didelot 
1916fad09c73SVivien Didelot 			netdev_warn(ds->ports[port].netdev,
1917fad09c73SVivien Didelot 				    "hardware VLAN %d already used by %s\n",
1918fad09c73SVivien Didelot 				    vlan.vid,
1919fad09c73SVivien Didelot 				    netdev_name(chip->ports[i].bridge_dev));
1920fad09c73SVivien Didelot 			err = -EOPNOTSUPP;
1921fad09c73SVivien Didelot 			goto unlock;
1922fad09c73SVivien Didelot 		}
1923fad09c73SVivien Didelot 	} while (vlan.vid < vid_end);
1924fad09c73SVivien Didelot 
1925fad09c73SVivien Didelot unlock:
1926fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1927fad09c73SVivien Didelot 
1928fad09c73SVivien Didelot 	return err;
1929fad09c73SVivien Didelot }
1930fad09c73SVivien Didelot 
1931fad09c73SVivien Didelot static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1932fad09c73SVivien Didelot 	[PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1933fad09c73SVivien Didelot 	[PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1934fad09c73SVivien Didelot 	[PORT_CONTROL_2_8021Q_CHECK] = "Check",
1935fad09c73SVivien Didelot 	[PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1936fad09c73SVivien Didelot };
1937fad09c73SVivien Didelot 
1938fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1939fad09c73SVivien Didelot 					 bool vlan_filtering)
1940fad09c73SVivien Didelot {
194104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1942fad09c73SVivien Didelot 	u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1943fad09c73SVivien Didelot 		PORT_CONTROL_2_8021Q_DISABLED;
19440e7b9925SAndrew Lunn 	u16 reg;
19450e7b9925SAndrew Lunn 	int err;
1946fad09c73SVivien Didelot 
1947fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1948fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1949fad09c73SVivien Didelot 
1950fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
1951fad09c73SVivien Didelot 
19520e7b9925SAndrew Lunn 	err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
19530e7b9925SAndrew Lunn 	if (err)
1954fad09c73SVivien Didelot 		goto unlock;
1955fad09c73SVivien Didelot 
19560e7b9925SAndrew Lunn 	old = reg & PORT_CONTROL_2_8021Q_MASK;
1957fad09c73SVivien Didelot 
1958fad09c73SVivien Didelot 	if (new != old) {
19590e7b9925SAndrew Lunn 		reg &= ~PORT_CONTROL_2_8021Q_MASK;
19600e7b9925SAndrew Lunn 		reg |= new & PORT_CONTROL_2_8021Q_MASK;
1961fad09c73SVivien Didelot 
19620e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
19630e7b9925SAndrew Lunn 		if (err)
1964fad09c73SVivien Didelot 			goto unlock;
1965fad09c73SVivien Didelot 
1966fad09c73SVivien Didelot 		netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
1967fad09c73SVivien Didelot 			   mv88e6xxx_port_8021q_mode_names[new],
1968fad09c73SVivien Didelot 			   mv88e6xxx_port_8021q_mode_names[old]);
1969fad09c73SVivien Didelot 	}
1970fad09c73SVivien Didelot 
19710e7b9925SAndrew Lunn 	err = 0;
1972fad09c73SVivien Didelot unlock:
1973fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
1974fad09c73SVivien Didelot 
19750e7b9925SAndrew Lunn 	return err;
1976fad09c73SVivien Didelot }
1977fad09c73SVivien Didelot 
1978fad09c73SVivien Didelot static int
1979fad09c73SVivien Didelot mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1980fad09c73SVivien Didelot 			    const struct switchdev_obj_port_vlan *vlan,
1981fad09c73SVivien Didelot 			    struct switchdev_trans *trans)
1982fad09c73SVivien Didelot {
198304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
1984fad09c73SVivien Didelot 	int err;
1985fad09c73SVivien Didelot 
1986fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
1987fad09c73SVivien Didelot 		return -EOPNOTSUPP;
1988fad09c73SVivien Didelot 
1989fad09c73SVivien Didelot 	/* If the requested port doesn't belong to the same bridge as the VLAN
1990fad09c73SVivien Didelot 	 * members, do not support it (yet) and fallback to software VLAN.
1991fad09c73SVivien Didelot 	 */
1992fad09c73SVivien Didelot 	err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1993fad09c73SVivien Didelot 					   vlan->vid_end);
1994fad09c73SVivien Didelot 	if (err)
1995fad09c73SVivien Didelot 		return err;
1996fad09c73SVivien Didelot 
1997fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
1998fad09c73SVivien Didelot 	 * so skip the prepare phase.
1999fad09c73SVivien Didelot 	 */
2000fad09c73SVivien Didelot 	return 0;
2001fad09c73SVivien Didelot }
2002fad09c73SVivien Didelot 
2003fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
2004fad09c73SVivien Didelot 				    u16 vid, bool untagged)
2005fad09c73SVivien Didelot {
2006b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
2007fad09c73SVivien Didelot 	int err;
2008fad09c73SVivien Didelot 
2009fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
2010fad09c73SVivien Didelot 	if (err)
2011fad09c73SVivien Didelot 		return err;
2012fad09c73SVivien Didelot 
2013fad09c73SVivien Didelot 	vlan.data[port] = untagged ?
2014fad09c73SVivien Didelot 		GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2015fad09c73SVivien Didelot 		GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2016fad09c73SVivien Didelot 
2017fad09c73SVivien Didelot 	return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
2018fad09c73SVivien Didelot }
2019fad09c73SVivien Didelot 
2020fad09c73SVivien Didelot static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2021fad09c73SVivien Didelot 				    const struct switchdev_obj_port_vlan *vlan,
2022fad09c73SVivien Didelot 				    struct switchdev_trans *trans)
2023fad09c73SVivien Didelot {
202404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2025fad09c73SVivien Didelot 	bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2026fad09c73SVivien Didelot 	bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2027fad09c73SVivien Didelot 	u16 vid;
2028fad09c73SVivien Didelot 
2029fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
2030fad09c73SVivien Didelot 		return;
2031fad09c73SVivien Didelot 
2032fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2033fad09c73SVivien Didelot 
2034fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
2035fad09c73SVivien Didelot 		if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
2036fad09c73SVivien Didelot 			netdev_err(ds->ports[port].netdev,
2037fad09c73SVivien Didelot 				   "failed to add VLAN %d%c\n",
2038fad09c73SVivien Didelot 				   vid, untagged ? 'u' : 't');
2039fad09c73SVivien Didelot 
2040fad09c73SVivien Didelot 	if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
2041fad09c73SVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
2042fad09c73SVivien Didelot 			   vlan->vid_end);
2043fad09c73SVivien Didelot 
2044fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2045fad09c73SVivien Didelot }
2046fad09c73SVivien Didelot 
2047fad09c73SVivien Didelot static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
2048fad09c73SVivien Didelot 				    int port, u16 vid)
2049fad09c73SVivien Didelot {
2050fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
2051b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
2052fad09c73SVivien Didelot 	int i, err;
2053fad09c73SVivien Didelot 
2054fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
2055fad09c73SVivien Didelot 	if (err)
2056fad09c73SVivien Didelot 		return err;
2057fad09c73SVivien Didelot 
2058fad09c73SVivien Didelot 	/* Tell switchdev if this VLAN is handled in software */
2059fad09c73SVivien Didelot 	if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2060fad09c73SVivien Didelot 		return -EOPNOTSUPP;
2061fad09c73SVivien Didelot 
2062fad09c73SVivien Didelot 	vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2063fad09c73SVivien Didelot 
2064fad09c73SVivien Didelot 	/* keep the VLAN unless all ports are excluded */
2065fad09c73SVivien Didelot 	vlan.valid = false;
2066370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
2067fad09c73SVivien Didelot 		if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
2068fad09c73SVivien Didelot 			continue;
2069fad09c73SVivien Didelot 
2070fad09c73SVivien Didelot 		if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
2071fad09c73SVivien Didelot 			vlan.valid = true;
2072fad09c73SVivien Didelot 			break;
2073fad09c73SVivien Didelot 		}
2074fad09c73SVivien Didelot 	}
2075fad09c73SVivien Didelot 
2076fad09c73SVivien Didelot 	err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
2077fad09c73SVivien Didelot 	if (err)
2078fad09c73SVivien Didelot 		return err;
2079fad09c73SVivien Didelot 
2080fad09c73SVivien Didelot 	return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
2081fad09c73SVivien Didelot }
2082fad09c73SVivien Didelot 
2083fad09c73SVivien Didelot static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2084fad09c73SVivien Didelot 				   const struct switchdev_obj_port_vlan *vlan)
2085fad09c73SVivien Didelot {
208604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2087fad09c73SVivien Didelot 	u16 pvid, vid;
2088fad09c73SVivien Didelot 	int err = 0;
2089fad09c73SVivien Didelot 
2090fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
2091fad09c73SVivien Didelot 		return -EOPNOTSUPP;
2092fad09c73SVivien Didelot 
2093fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2094fad09c73SVivien Didelot 
2095fad09c73SVivien Didelot 	err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
2096fad09c73SVivien Didelot 	if (err)
2097fad09c73SVivien Didelot 		goto unlock;
2098fad09c73SVivien Didelot 
2099fad09c73SVivien Didelot 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
2100fad09c73SVivien Didelot 		err = _mv88e6xxx_port_vlan_del(chip, port, vid);
2101fad09c73SVivien Didelot 		if (err)
2102fad09c73SVivien Didelot 			goto unlock;
2103fad09c73SVivien Didelot 
2104fad09c73SVivien Didelot 		if (vid == pvid) {
2105fad09c73SVivien Didelot 			err = _mv88e6xxx_port_pvid_set(chip, port, 0);
2106fad09c73SVivien Didelot 			if (err)
2107fad09c73SVivien Didelot 				goto unlock;
2108fad09c73SVivien Didelot 		}
2109fad09c73SVivien Didelot 	}
2110fad09c73SVivien Didelot 
2111fad09c73SVivien Didelot unlock:
2112fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2113fad09c73SVivien Didelot 
2114fad09c73SVivien Didelot 	return err;
2115fad09c73SVivien Didelot }
2116fad09c73SVivien Didelot 
2117fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
2118fad09c73SVivien Didelot 				    const unsigned char *addr)
2119fad09c73SVivien Didelot {
2120a935c052SVivien Didelot 	int i, err;
2121fad09c73SVivien Didelot 
2122fad09c73SVivien Didelot 	for (i = 0; i < 3; i++) {
2123a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i,
2124fad09c73SVivien Didelot 					 (addr[i * 2] << 8) | addr[i * 2 + 1]);
2125a935c052SVivien Didelot 		if (err)
2126a935c052SVivien Didelot 			return err;
2127fad09c73SVivien Didelot 	}
2128fad09c73SVivien Didelot 
2129fad09c73SVivien Didelot 	return 0;
2130fad09c73SVivien Didelot }
2131fad09c73SVivien Didelot 
2132fad09c73SVivien Didelot static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
2133fad09c73SVivien Didelot 				   unsigned char *addr)
2134fad09c73SVivien Didelot {
2135a935c052SVivien Didelot 	u16 val;
2136a935c052SVivien Didelot 	int i, err;
2137fad09c73SVivien Didelot 
2138fad09c73SVivien Didelot 	for (i = 0; i < 3; i++) {
2139a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
2140a935c052SVivien Didelot 		if (err)
2141a935c052SVivien Didelot 			return err;
2142a935c052SVivien Didelot 
2143a935c052SVivien Didelot 		addr[i * 2] = val >> 8;
2144a935c052SVivien Didelot 		addr[i * 2 + 1] = val & 0xff;
2145fad09c73SVivien Didelot 	}
2146fad09c73SVivien Didelot 
2147fad09c73SVivien Didelot 	return 0;
2148fad09c73SVivien Didelot }
2149fad09c73SVivien Didelot 
2150fad09c73SVivien Didelot static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
2151fad09c73SVivien Didelot 			       struct mv88e6xxx_atu_entry *entry)
2152fad09c73SVivien Didelot {
2153fad09c73SVivien Didelot 	int ret;
2154fad09c73SVivien Didelot 
2155fad09c73SVivien Didelot 	ret = _mv88e6xxx_atu_wait(chip);
2156fad09c73SVivien Didelot 	if (ret < 0)
2157fad09c73SVivien Didelot 		return ret;
2158fad09c73SVivien Didelot 
2159fad09c73SVivien Didelot 	ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
2160fad09c73SVivien Didelot 	if (ret < 0)
2161fad09c73SVivien Didelot 		return ret;
2162fad09c73SVivien Didelot 
2163fad09c73SVivien Didelot 	ret = _mv88e6xxx_atu_data_write(chip, entry);
2164fad09c73SVivien Didelot 	if (ret < 0)
2165fad09c73SVivien Didelot 		return ret;
2166fad09c73SVivien Didelot 
2167fad09c73SVivien Didelot 	return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
2168fad09c73SVivien Didelot }
2169fad09c73SVivien Didelot 
217088472939SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
217188472939SVivien Didelot 				  struct mv88e6xxx_atu_entry *entry);
217288472939SVivien Didelot 
217388472939SVivien Didelot static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid,
217488472939SVivien Didelot 			     const u8 *addr, struct mv88e6xxx_atu_entry *entry)
217588472939SVivien Didelot {
217688472939SVivien Didelot 	struct mv88e6xxx_atu_entry next;
217788472939SVivien Didelot 	int err;
217888472939SVivien Didelot 
217988472939SVivien Didelot 	eth_broadcast_addr(next.mac);
218088472939SVivien Didelot 
218188472939SVivien Didelot 	err = _mv88e6xxx_atu_mac_write(chip, next.mac);
218288472939SVivien Didelot 	if (err)
218388472939SVivien Didelot 		return err;
218488472939SVivien Didelot 
218588472939SVivien Didelot 	do {
218688472939SVivien Didelot 		err = _mv88e6xxx_atu_getnext(chip, fid, &next);
218788472939SVivien Didelot 		if (err)
218888472939SVivien Didelot 			return err;
218988472939SVivien Didelot 
219088472939SVivien Didelot 		if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
219188472939SVivien Didelot 			break;
219288472939SVivien Didelot 
219388472939SVivien Didelot 		if (ether_addr_equal(next.mac, addr)) {
219488472939SVivien Didelot 			*entry = next;
219588472939SVivien Didelot 			return 0;
219688472939SVivien Didelot 		}
219788472939SVivien Didelot 	} while (!is_broadcast_ether_addr(next.mac));
219888472939SVivien Didelot 
219988472939SVivien Didelot 	memset(entry, 0, sizeof(*entry));
220088472939SVivien Didelot 	entry->fid = fid;
220188472939SVivien Didelot 	ether_addr_copy(entry->mac, addr);
220288472939SVivien Didelot 
220388472939SVivien Didelot 	return 0;
220488472939SVivien Didelot }
220588472939SVivien Didelot 
220683dabd1fSVivien Didelot static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
2207fad09c73SVivien Didelot 					const unsigned char *addr, u16 vid,
2208fad09c73SVivien Didelot 					u8 state)
2209fad09c73SVivien Didelot {
2210b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan;
221188472939SVivien Didelot 	struct mv88e6xxx_atu_entry entry;
2212fad09c73SVivien Didelot 	int err;
2213fad09c73SVivien Didelot 
2214fad09c73SVivien Didelot 	/* Null VLAN ID corresponds to the port private database */
2215fad09c73SVivien Didelot 	if (vid == 0)
2216fad09c73SVivien Didelot 		err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
2217fad09c73SVivien Didelot 	else
2218fad09c73SVivien Didelot 		err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
2219fad09c73SVivien Didelot 	if (err)
2220fad09c73SVivien Didelot 		return err;
2221fad09c73SVivien Didelot 
222288472939SVivien Didelot 	err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry);
222388472939SVivien Didelot 	if (err)
222488472939SVivien Didelot 		return err;
222588472939SVivien Didelot 
222688472939SVivien Didelot 	/* Purge the ATU entry only if no port is using it anymore */
222788472939SVivien Didelot 	if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
222888472939SVivien Didelot 		entry.portv_trunkid &= ~BIT(port);
222988472939SVivien Didelot 		if (!entry.portv_trunkid)
223088472939SVivien Didelot 			entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
223188472939SVivien Didelot 	} else {
223288472939SVivien Didelot 		entry.portv_trunkid |= BIT(port);
2233fad09c73SVivien Didelot 		entry.state = state;
2234fad09c73SVivien Didelot 	}
2235fad09c73SVivien Didelot 
2236fad09c73SVivien Didelot 	return _mv88e6xxx_atu_load(chip, &entry);
2237fad09c73SVivien Didelot }
2238fad09c73SVivien Didelot 
2239fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2240fad09c73SVivien Didelot 				      const struct switchdev_obj_port_fdb *fdb,
2241fad09c73SVivien Didelot 				      struct switchdev_trans *trans)
2242fad09c73SVivien Didelot {
2243fad09c73SVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
2244fad09c73SVivien Didelot 	 * so skip the prepare phase.
2245fad09c73SVivien Didelot 	 */
2246fad09c73SVivien Didelot 	return 0;
2247fad09c73SVivien Didelot }
2248fad09c73SVivien Didelot 
2249fad09c73SVivien Didelot static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2250fad09c73SVivien Didelot 				   const struct switchdev_obj_port_fdb *fdb,
2251fad09c73SVivien Didelot 				   struct switchdev_trans *trans)
2252fad09c73SVivien Didelot {
225304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2254fad09c73SVivien Didelot 
2255fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
225683dabd1fSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
225783dabd1fSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_UC_STATIC))
225883dabd1fSVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
2259fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2260fad09c73SVivien Didelot }
2261fad09c73SVivien Didelot 
2262fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2263fad09c73SVivien Didelot 				  const struct switchdev_obj_port_fdb *fdb)
2264fad09c73SVivien Didelot {
226504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
226683dabd1fSVivien Didelot 	int err;
2267fad09c73SVivien Didelot 
2268fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
226983dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
2270fad09c73SVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
2271fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2272fad09c73SVivien Didelot 
227383dabd1fSVivien Didelot 	return err;
2274fad09c73SVivien Didelot }
2275fad09c73SVivien Didelot 
2276fad09c73SVivien Didelot static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
2277fad09c73SVivien Didelot 				  struct mv88e6xxx_atu_entry *entry)
2278fad09c73SVivien Didelot {
2279fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry next = { 0 };
2280a935c052SVivien Didelot 	u16 val;
2281a935c052SVivien Didelot 	int err;
2282fad09c73SVivien Didelot 
2283fad09c73SVivien Didelot 	next.fid = fid;
2284fad09c73SVivien Didelot 
2285a935c052SVivien Didelot 	err = _mv88e6xxx_atu_wait(chip);
2286a935c052SVivien Didelot 	if (err)
2287a935c052SVivien Didelot 		return err;
2288fad09c73SVivien Didelot 
2289a935c052SVivien Didelot 	err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
2290a935c052SVivien Didelot 	if (err)
2291a935c052SVivien Didelot 		return err;
2292fad09c73SVivien Didelot 
2293a935c052SVivien Didelot 	err = _mv88e6xxx_atu_mac_read(chip, next.mac);
2294a935c052SVivien Didelot 	if (err)
2295a935c052SVivien Didelot 		return err;
2296fad09c73SVivien Didelot 
2297a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
2298a935c052SVivien Didelot 	if (err)
2299a935c052SVivien Didelot 		return err;
2300fad09c73SVivien Didelot 
2301a935c052SVivien Didelot 	next.state = val & GLOBAL_ATU_DATA_STATE_MASK;
2302fad09c73SVivien Didelot 	if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2303fad09c73SVivien Didelot 		unsigned int mask, shift;
2304fad09c73SVivien Didelot 
2305a935c052SVivien Didelot 		if (val & GLOBAL_ATU_DATA_TRUNK) {
2306fad09c73SVivien Didelot 			next.trunk = true;
2307fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2308fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2309fad09c73SVivien Didelot 		} else {
2310fad09c73SVivien Didelot 			next.trunk = false;
2311fad09c73SVivien Didelot 			mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2312fad09c73SVivien Didelot 			shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2313fad09c73SVivien Didelot 		}
2314fad09c73SVivien Didelot 
2315a935c052SVivien Didelot 		next.portv_trunkid = (val & mask) >> shift;
2316fad09c73SVivien Didelot 	}
2317fad09c73SVivien Didelot 
2318fad09c73SVivien Didelot 	*entry = next;
2319fad09c73SVivien Didelot 	return 0;
2320fad09c73SVivien Didelot }
2321fad09c73SVivien Didelot 
232283dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
2323fad09c73SVivien Didelot 				      u16 fid, u16 vid, int port,
232483dabd1fSVivien Didelot 				      struct switchdev_obj *obj,
2325fad09c73SVivien Didelot 				      int (*cb)(struct switchdev_obj *obj))
2326fad09c73SVivien Didelot {
2327fad09c73SVivien Didelot 	struct mv88e6xxx_atu_entry addr = {
2328fad09c73SVivien Didelot 		.mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2329fad09c73SVivien Didelot 	};
2330fad09c73SVivien Didelot 	int err;
2331fad09c73SVivien Didelot 
2332fad09c73SVivien Didelot 	err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
2333fad09c73SVivien Didelot 	if (err)
2334fad09c73SVivien Didelot 		return err;
2335fad09c73SVivien Didelot 
2336fad09c73SVivien Didelot 	do {
2337fad09c73SVivien Didelot 		err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
2338fad09c73SVivien Didelot 		if (err)
233983dabd1fSVivien Didelot 			return err;
2340fad09c73SVivien Didelot 
2341fad09c73SVivien Didelot 		if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2342fad09c73SVivien Didelot 			break;
2343fad09c73SVivien Didelot 
234483dabd1fSVivien Didelot 		if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
234583dabd1fSVivien Didelot 			continue;
2346fad09c73SVivien Didelot 
234783dabd1fSVivien Didelot 		if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
234883dabd1fSVivien Didelot 			struct switchdev_obj_port_fdb *fdb;
234983dabd1fSVivien Didelot 
235083dabd1fSVivien Didelot 			if (!is_unicast_ether_addr(addr.mac))
235183dabd1fSVivien Didelot 				continue;
235283dabd1fSVivien Didelot 
235383dabd1fSVivien Didelot 			fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
2354fad09c73SVivien Didelot 			fdb->vid = vid;
2355fad09c73SVivien Didelot 			ether_addr_copy(fdb->addr, addr.mac);
235683dabd1fSVivien Didelot 			if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
235783dabd1fSVivien Didelot 				fdb->ndm_state = NUD_NOARP;
235883dabd1fSVivien Didelot 			else
235983dabd1fSVivien Didelot 				fdb->ndm_state = NUD_REACHABLE;
23607df8fbddSVivien Didelot 		} else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
23617df8fbddSVivien Didelot 			struct switchdev_obj_port_mdb *mdb;
23627df8fbddSVivien Didelot 
23637df8fbddSVivien Didelot 			if (!is_multicast_ether_addr(addr.mac))
23647df8fbddSVivien Didelot 				continue;
23657df8fbddSVivien Didelot 
23667df8fbddSVivien Didelot 			mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
23677df8fbddSVivien Didelot 			mdb->vid = vid;
23687df8fbddSVivien Didelot 			ether_addr_copy(mdb->addr, addr.mac);
236983dabd1fSVivien Didelot 		} else {
237083dabd1fSVivien Didelot 			return -EOPNOTSUPP;
2371fad09c73SVivien Didelot 		}
237283dabd1fSVivien Didelot 
237383dabd1fSVivien Didelot 		err = cb(obj);
237483dabd1fSVivien Didelot 		if (err)
237583dabd1fSVivien Didelot 			return err;
2376fad09c73SVivien Didelot 	} while (!is_broadcast_ether_addr(addr.mac));
2377fad09c73SVivien Didelot 
2378fad09c73SVivien Didelot 	return err;
2379fad09c73SVivien Didelot }
2380fad09c73SVivien Didelot 
238183dabd1fSVivien Didelot static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
238283dabd1fSVivien Didelot 				  struct switchdev_obj *obj,
238383dabd1fSVivien Didelot 				  int (*cb)(struct switchdev_obj *obj))
238483dabd1fSVivien Didelot {
2385b4e47c0fSVivien Didelot 	struct mv88e6xxx_vtu_entry vlan = {
238683dabd1fSVivien Didelot 		.vid = GLOBAL_VTU_VID_MASK, /* all ones */
238783dabd1fSVivien Didelot 	};
238883dabd1fSVivien Didelot 	u16 fid;
238983dabd1fSVivien Didelot 	int err;
239083dabd1fSVivien Didelot 
239183dabd1fSVivien Didelot 	/* Dump port's default Filtering Information Database (VLAN ID 0) */
239283dabd1fSVivien Didelot 	err = _mv88e6xxx_port_fid_get(chip, port, &fid);
239383dabd1fSVivien Didelot 	if (err)
239483dabd1fSVivien Didelot 		return err;
239583dabd1fSVivien Didelot 
239683dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
239783dabd1fSVivien Didelot 	if (err)
239883dabd1fSVivien Didelot 		return err;
239983dabd1fSVivien Didelot 
240083dabd1fSVivien Didelot 	/* Dump VLANs' Filtering Information Databases */
240183dabd1fSVivien Didelot 	err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
240283dabd1fSVivien Didelot 	if (err)
240383dabd1fSVivien Didelot 		return err;
240483dabd1fSVivien Didelot 
240583dabd1fSVivien Didelot 	do {
240683dabd1fSVivien Didelot 		err = _mv88e6xxx_vtu_getnext(chip, &vlan);
240783dabd1fSVivien Didelot 		if (err)
240883dabd1fSVivien Didelot 			return err;
240983dabd1fSVivien Didelot 
241083dabd1fSVivien Didelot 		if (!vlan.valid)
241183dabd1fSVivien Didelot 			break;
241283dabd1fSVivien Didelot 
241383dabd1fSVivien Didelot 		err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
241483dabd1fSVivien Didelot 						 obj, cb);
241583dabd1fSVivien Didelot 		if (err)
241683dabd1fSVivien Didelot 			return err;
241783dabd1fSVivien Didelot 	} while (vlan.vid < GLOBAL_VTU_VID_MASK);
241883dabd1fSVivien Didelot 
241983dabd1fSVivien Didelot 	return err;
242083dabd1fSVivien Didelot }
242183dabd1fSVivien Didelot 
2422fad09c73SVivien Didelot static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2423fad09c73SVivien Didelot 				   struct switchdev_obj_port_fdb *fdb,
2424fad09c73SVivien Didelot 				   int (*cb)(struct switchdev_obj *obj))
2425fad09c73SVivien Didelot {
242604bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2427fad09c73SVivien Didelot 	int err;
2428fad09c73SVivien Didelot 
2429fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
243083dabd1fSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
2431fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2432fad09c73SVivien Didelot 
2433fad09c73SVivien Didelot 	return err;
2434fad09c73SVivien Didelot }
2435fad09c73SVivien Didelot 
2436fad09c73SVivien Didelot static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2437fad09c73SVivien Didelot 				      struct net_device *bridge)
2438fad09c73SVivien Didelot {
243904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2440fad09c73SVivien Didelot 	int i, err = 0;
2441fad09c73SVivien Didelot 
2442fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2443fad09c73SVivien Didelot 
2444fad09c73SVivien Didelot 	/* Assign the bridge and remap each port's VLANTable */
2445fad09c73SVivien Didelot 	chip->ports[port].bridge_dev = bridge;
2446fad09c73SVivien Didelot 
2447370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
2448fad09c73SVivien Didelot 		if (chip->ports[i].bridge_dev == bridge) {
2449fad09c73SVivien Didelot 			err = _mv88e6xxx_port_based_vlan_map(chip, i);
2450fad09c73SVivien Didelot 			if (err)
2451fad09c73SVivien Didelot 				break;
2452fad09c73SVivien Didelot 		}
2453fad09c73SVivien Didelot 	}
2454fad09c73SVivien Didelot 
2455fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2456fad09c73SVivien Didelot 
2457fad09c73SVivien Didelot 	return err;
2458fad09c73SVivien Didelot }
2459fad09c73SVivien Didelot 
2460fad09c73SVivien Didelot static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
2461fad09c73SVivien Didelot {
246204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2463fad09c73SVivien Didelot 	struct net_device *bridge = chip->ports[port].bridge_dev;
2464fad09c73SVivien Didelot 	int i;
2465fad09c73SVivien Didelot 
2466fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2467fad09c73SVivien Didelot 
2468fad09c73SVivien Didelot 	/* Unassign the bridge and remap each port's VLANTable */
2469fad09c73SVivien Didelot 	chip->ports[port].bridge_dev = NULL;
2470fad09c73SVivien Didelot 
2471370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
2472fad09c73SVivien Didelot 		if (i == port || chip->ports[i].bridge_dev == bridge)
2473fad09c73SVivien Didelot 			if (_mv88e6xxx_port_based_vlan_map(chip, i))
2474fad09c73SVivien Didelot 				netdev_warn(ds->ports[i].netdev,
2475fad09c73SVivien Didelot 					    "failed to remap\n");
2476fad09c73SVivien Didelot 
2477fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
2478fad09c73SVivien Didelot }
2479fad09c73SVivien Didelot 
2480fad09c73SVivien Didelot static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
2481fad09c73SVivien Didelot {
2482fad09c73SVivien Didelot 	bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
2483fad09c73SVivien Didelot 	u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
2484fad09c73SVivien Didelot 	struct gpio_desc *gpiod = chip->reset;
2485fad09c73SVivien Didelot 	unsigned long timeout;
24860e7b9925SAndrew Lunn 	u16 reg;
2487a935c052SVivien Didelot 	int err;
2488fad09c73SVivien Didelot 	int i;
2489fad09c73SVivien Didelot 
2490fad09c73SVivien Didelot 	/* Set all ports to the disabled state. */
2491370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
2492e28def33SVivien Didelot 		err = mv88e6xxx_port_set_state(chip, i,
2493e28def33SVivien Didelot 					       PORT_CONTROL_STATE_DISABLED);
24940e7b9925SAndrew Lunn 		if (err)
24950e7b9925SAndrew Lunn 			return err;
2496fad09c73SVivien Didelot 	}
2497fad09c73SVivien Didelot 
2498fad09c73SVivien Didelot 	/* Wait for transmit queues to drain. */
2499fad09c73SVivien Didelot 	usleep_range(2000, 4000);
2500fad09c73SVivien Didelot 
2501fad09c73SVivien Didelot 	/* If there is a gpio connected to the reset pin, toggle it */
2502fad09c73SVivien Didelot 	if (gpiod) {
2503fad09c73SVivien Didelot 		gpiod_set_value_cansleep(gpiod, 1);
2504fad09c73SVivien Didelot 		usleep_range(10000, 20000);
2505fad09c73SVivien Didelot 		gpiod_set_value_cansleep(gpiod, 0);
2506fad09c73SVivien Didelot 		usleep_range(10000, 20000);
2507fad09c73SVivien Didelot 	}
2508fad09c73SVivien Didelot 
2509fad09c73SVivien Didelot 	/* Reset the switch. Keep the PPU active if requested. The PPU
2510fad09c73SVivien Didelot 	 * needs to be active to support indirect phy register access
2511fad09c73SVivien Didelot 	 * through global registers 0x18 and 0x19.
2512fad09c73SVivien Didelot 	 */
2513fad09c73SVivien Didelot 	if (ppu_active)
2514a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, 0x04, 0xc000);
2515fad09c73SVivien Didelot 	else
2516a935c052SVivien Didelot 		err = mv88e6xxx_g1_write(chip, 0x04, 0xc400);
25170e7b9925SAndrew Lunn 	if (err)
25180e7b9925SAndrew Lunn 		return err;
2519fad09c73SVivien Didelot 
2520fad09c73SVivien Didelot 	/* Wait up to one second for reset to complete. */
2521fad09c73SVivien Didelot 	timeout = jiffies + 1 * HZ;
2522fad09c73SVivien Didelot 	while (time_before(jiffies, timeout)) {
2523a935c052SVivien Didelot 		err = mv88e6xxx_g1_read(chip, 0x00, &reg);
2524a935c052SVivien Didelot 		if (err)
2525a935c052SVivien Didelot 			return err;
2526fad09c73SVivien Didelot 
2527a935c052SVivien Didelot 		if ((reg & is_reset) == is_reset)
2528fad09c73SVivien Didelot 			break;
2529fad09c73SVivien Didelot 		usleep_range(1000, 2000);
2530fad09c73SVivien Didelot 	}
2531fad09c73SVivien Didelot 	if (time_after(jiffies, timeout))
25320e7b9925SAndrew Lunn 		err = -ETIMEDOUT;
2533fad09c73SVivien Didelot 	else
25340e7b9925SAndrew Lunn 		err = 0;
2535fad09c73SVivien Didelot 
25360e7b9925SAndrew Lunn 	return err;
2537fad09c73SVivien Didelot }
2538fad09c73SVivien Didelot 
253909cb7dfdSVivien Didelot static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
2540fad09c73SVivien Didelot {
254109cb7dfdSVivien Didelot 	u16 val;
254209cb7dfdSVivien Didelot 	int err;
2543fad09c73SVivien Didelot 
254409cb7dfdSVivien Didelot 	/* Clear Power Down bit */
254509cb7dfdSVivien Didelot 	err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
254609cb7dfdSVivien Didelot 	if (err)
254709cb7dfdSVivien Didelot 		return err;
2548fad09c73SVivien Didelot 
254909cb7dfdSVivien Didelot 	if (val & BMCR_PDOWN) {
255009cb7dfdSVivien Didelot 		val &= ~BMCR_PDOWN;
255109cb7dfdSVivien Didelot 		err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
2552fad09c73SVivien Didelot 	}
2553fad09c73SVivien Didelot 
255409cb7dfdSVivien Didelot 	return err;
2555fad09c73SVivien Didelot }
2556fad09c73SVivien Didelot 
2557fad09c73SVivien Didelot static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
2558fad09c73SVivien Didelot {
2559fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
25600e7b9925SAndrew Lunn 	int err;
2561fad09c73SVivien Didelot 	u16 reg;
2562fad09c73SVivien Didelot 
2563fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2564fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2565fad09c73SVivien Didelot 	    mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2566fad09c73SVivien Didelot 	    mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
2567fad09c73SVivien Didelot 		/* MAC Forcing register: don't force link, speed,
2568fad09c73SVivien Didelot 		 * duplex or flow control state to any particular
2569fad09c73SVivien Didelot 		 * values on physical ports, but force the CPU port
2570fad09c73SVivien Didelot 		 * and all DSA ports to their maximum bandwidth and
2571fad09c73SVivien Didelot 		 * full duplex.
2572fad09c73SVivien Didelot 		 */
25730e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
2574fad09c73SVivien Didelot 		if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
2575fad09c73SVivien Didelot 			reg &= ~PORT_PCS_CTRL_UNFORCED;
2576fad09c73SVivien Didelot 			reg |= PORT_PCS_CTRL_FORCE_LINK |
2577fad09c73SVivien Didelot 				PORT_PCS_CTRL_LINK_UP |
2578fad09c73SVivien Didelot 				PORT_PCS_CTRL_DUPLEX_FULL |
2579fad09c73SVivien Didelot 				PORT_PCS_CTRL_FORCE_DUPLEX;
2580fad09c73SVivien Didelot 			if (mv88e6xxx_6065_family(chip))
2581fad09c73SVivien Didelot 				reg |= PORT_PCS_CTRL_100;
2582fad09c73SVivien Didelot 			else
2583fad09c73SVivien Didelot 				reg |= PORT_PCS_CTRL_1000;
2584fad09c73SVivien Didelot 		} else {
2585fad09c73SVivien Didelot 			reg |= PORT_PCS_CTRL_UNFORCED;
2586fad09c73SVivien Didelot 		}
2587fad09c73SVivien Didelot 
25880e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
25890e7b9925SAndrew Lunn 		if (err)
25900e7b9925SAndrew Lunn 			return err;
2591fad09c73SVivien Didelot 	}
2592fad09c73SVivien Didelot 
2593fad09c73SVivien Didelot 	/* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2594fad09c73SVivien Didelot 	 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2595fad09c73SVivien Didelot 	 * tunneling, determine priority by looking at 802.1p and IP
2596fad09c73SVivien Didelot 	 * priority fields (IP prio has precedence), and set STP state
2597fad09c73SVivien Didelot 	 * to Forwarding.
2598fad09c73SVivien Didelot 	 *
2599fad09c73SVivien Didelot 	 * If this is the CPU link, use DSA or EDSA tagging depending
2600fad09c73SVivien Didelot 	 * on which tagging mode was configured.
2601fad09c73SVivien Didelot 	 *
2602fad09c73SVivien Didelot 	 * If this is a link to another switch, use DSA tagging mode.
2603fad09c73SVivien Didelot 	 *
2604fad09c73SVivien Didelot 	 * If this is the upstream port for this switch, enable
2605fad09c73SVivien Didelot 	 * forwarding of unknown unicasts and multicasts.
2606fad09c73SVivien Didelot 	 */
2607fad09c73SVivien Didelot 	reg = 0;
2608fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2609fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2610fad09c73SVivien Didelot 	    mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2611fad09c73SVivien Didelot 	    mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
2612fad09c73SVivien Didelot 		reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2613fad09c73SVivien Didelot 		PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2614fad09c73SVivien Didelot 		PORT_CONTROL_STATE_FORWARDING;
2615fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port)) {
26162bbb33beSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
2617fad09c73SVivien Didelot 			reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2618fad09c73SVivien Didelot 				PORT_CONTROL_FORWARD_UNKNOWN_MC;
26192bbb33beSAndrew Lunn 		else
26202bbb33beSAndrew Lunn 			reg |= PORT_CONTROL_DSA_TAG;
2621f027e0ccSJamie Lentin 		reg |= PORT_CONTROL_EGRESS_ADD_TAG |
2622f027e0ccSJamie Lentin 			PORT_CONTROL_FORWARD_UNKNOWN;
2623fad09c73SVivien Didelot 	}
2624fad09c73SVivien Didelot 	if (dsa_is_dsa_port(ds, port)) {
2625fad09c73SVivien Didelot 		if (mv88e6xxx_6095_family(chip) ||
2626fad09c73SVivien Didelot 		    mv88e6xxx_6185_family(chip))
2627fad09c73SVivien Didelot 			reg |= PORT_CONTROL_DSA_TAG;
2628fad09c73SVivien Didelot 		if (mv88e6xxx_6352_family(chip) ||
2629fad09c73SVivien Didelot 		    mv88e6xxx_6351_family(chip) ||
2630fad09c73SVivien Didelot 		    mv88e6xxx_6165_family(chip) ||
2631fad09c73SVivien Didelot 		    mv88e6xxx_6097_family(chip) ||
2632fad09c73SVivien Didelot 		    mv88e6xxx_6320_family(chip)) {
2633fad09c73SVivien Didelot 			reg |= PORT_CONTROL_FRAME_MODE_DSA;
2634fad09c73SVivien Didelot 		}
2635fad09c73SVivien Didelot 
2636fad09c73SVivien Didelot 		if (port == dsa_upstream_port(ds))
2637fad09c73SVivien Didelot 			reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2638fad09c73SVivien Didelot 				PORT_CONTROL_FORWARD_UNKNOWN_MC;
2639fad09c73SVivien Didelot 	}
2640fad09c73SVivien Didelot 	if (reg) {
26410e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
26420e7b9925SAndrew Lunn 		if (err)
26430e7b9925SAndrew Lunn 			return err;
2644fad09c73SVivien Didelot 	}
2645fad09c73SVivien Didelot 
2646fad09c73SVivien Didelot 	/* If this port is connected to a SerDes, make sure the SerDes is not
2647fad09c73SVivien Didelot 	 * powered down.
2648fad09c73SVivien Didelot 	 */
264909cb7dfdSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
26500e7b9925SAndrew Lunn 		err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
26510e7b9925SAndrew Lunn 		if (err)
26520e7b9925SAndrew Lunn 			return err;
26530e7b9925SAndrew Lunn 		reg &= PORT_STATUS_CMODE_MASK;
26540e7b9925SAndrew Lunn 		if ((reg == PORT_STATUS_CMODE_100BASE_X) ||
26550e7b9925SAndrew Lunn 		    (reg == PORT_STATUS_CMODE_1000BASE_X) ||
26560e7b9925SAndrew Lunn 		    (reg == PORT_STATUS_CMODE_SGMII)) {
26570e7b9925SAndrew Lunn 			err = mv88e6xxx_serdes_power_on(chip);
26580e7b9925SAndrew Lunn 			if (err < 0)
26590e7b9925SAndrew Lunn 				return err;
2660fad09c73SVivien Didelot 		}
2661fad09c73SVivien Didelot 	}
2662fad09c73SVivien Didelot 
2663fad09c73SVivien Didelot 	/* Port Control 2: don't force a good FCS, set the maximum frame size to
2664fad09c73SVivien Didelot 	 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
2665fad09c73SVivien Didelot 	 * untagged frames on this port, do a destination address lookup on all
2666fad09c73SVivien Didelot 	 * received packets as usual, disable ARP mirroring and don't send a
2667fad09c73SVivien Didelot 	 * copy of all transmitted/received frames on this port to the CPU.
2668fad09c73SVivien Didelot 	 */
2669fad09c73SVivien Didelot 	reg = 0;
2670fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2671fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2672fad09c73SVivien Didelot 	    mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2673fad09c73SVivien Didelot 	    mv88e6xxx_6185_family(chip))
2674fad09c73SVivien Didelot 		reg = PORT_CONTROL_2_MAP_DA;
2675fad09c73SVivien Didelot 
2676fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2677fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
2678fad09c73SVivien Didelot 		reg |= PORT_CONTROL_2_JUMBO_10240;
2679fad09c73SVivien Didelot 
2680fad09c73SVivien Didelot 	if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
2681fad09c73SVivien Didelot 		/* Set the upstream port this port should use */
2682fad09c73SVivien Didelot 		reg |= dsa_upstream_port(ds);
2683fad09c73SVivien Didelot 		/* enable forwarding of unknown multicast addresses to
2684fad09c73SVivien Didelot 		 * the upstream port
2685fad09c73SVivien Didelot 		 */
2686fad09c73SVivien Didelot 		if (port == dsa_upstream_port(ds))
2687fad09c73SVivien Didelot 			reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2688fad09c73SVivien Didelot 	}
2689fad09c73SVivien Didelot 
2690fad09c73SVivien Didelot 	reg |= PORT_CONTROL_2_8021Q_DISABLED;
2691fad09c73SVivien Didelot 
2692fad09c73SVivien Didelot 	if (reg) {
26930e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
26940e7b9925SAndrew Lunn 		if (err)
26950e7b9925SAndrew Lunn 			return err;
2696fad09c73SVivien Didelot 	}
2697fad09c73SVivien Didelot 
2698fad09c73SVivien Didelot 	/* Port Association Vector: when learning source addresses
2699fad09c73SVivien Didelot 	 * of packets, add the address to the address database using
2700fad09c73SVivien Didelot 	 * a port bitmap that has only the bit for this port set and
2701fad09c73SVivien Didelot 	 * the other bits clear.
2702fad09c73SVivien Didelot 	 */
2703fad09c73SVivien Didelot 	reg = 1 << port;
2704fad09c73SVivien Didelot 	/* Disable learning for CPU port */
2705fad09c73SVivien Didelot 	if (dsa_is_cpu_port(ds, port))
2706fad09c73SVivien Didelot 		reg = 0;
2707fad09c73SVivien Didelot 
27080e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
27090e7b9925SAndrew Lunn 	if (err)
27100e7b9925SAndrew Lunn 		return err;
2711fad09c73SVivien Didelot 
2712fad09c73SVivien Didelot 	/* Egress rate control 2: disable egress rate control. */
27130e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
27140e7b9925SAndrew Lunn 	if (err)
27150e7b9925SAndrew Lunn 		return err;
2716fad09c73SVivien Didelot 
2717fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2718fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2719fad09c73SVivien Didelot 	    mv88e6xxx_6320_family(chip)) {
2720fad09c73SVivien Didelot 		/* Do not limit the period of time that this port can
2721fad09c73SVivien Didelot 		 * be paused for by the remote end or the period of
2722fad09c73SVivien Didelot 		 * time that this port can pause the remote end.
2723fad09c73SVivien Didelot 		 */
27240e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
27250e7b9925SAndrew Lunn 		if (err)
27260e7b9925SAndrew Lunn 			return err;
2727fad09c73SVivien Didelot 
2728fad09c73SVivien Didelot 		/* Port ATU control: disable limiting the number of
2729fad09c73SVivien Didelot 		 * address database entries that this port is allowed
2730fad09c73SVivien Didelot 		 * to use.
2731fad09c73SVivien Didelot 		 */
27320e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL,
27330e7b9925SAndrew Lunn 					   0x0000);
2734fad09c73SVivien Didelot 		/* Priority Override: disable DA, SA and VTU priority
2735fad09c73SVivien Didelot 		 * override.
2736fad09c73SVivien Didelot 		 */
27370e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE,
27380e7b9925SAndrew Lunn 					   0x0000);
27390e7b9925SAndrew Lunn 		if (err)
27400e7b9925SAndrew Lunn 			return err;
2741fad09c73SVivien Didelot 
2742fad09c73SVivien Didelot 		/* Port Ethertype: use the Ethertype DSA Ethertype
2743fad09c73SVivien Didelot 		 * value.
2744fad09c73SVivien Didelot 		 */
27452bbb33beSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) {
27460e7b9925SAndrew Lunn 			err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE,
27470e7b9925SAndrew Lunn 						   ETH_P_EDSA);
27480e7b9925SAndrew Lunn 			if (err)
27490e7b9925SAndrew Lunn 				return err;
27502bbb33beSAndrew Lunn 		}
27512bbb33beSAndrew Lunn 
2752fad09c73SVivien Didelot 		/* Tag Remap: use an identity 802.1p prio -> switch
2753fad09c73SVivien Didelot 		 * prio mapping.
2754fad09c73SVivien Didelot 		 */
27550e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123,
27560e7b9925SAndrew Lunn 					   0x3210);
27570e7b9925SAndrew Lunn 		if (err)
27580e7b9925SAndrew Lunn 			return err;
2759fad09c73SVivien Didelot 
2760fad09c73SVivien Didelot 		/* Tag Remap 2: use an identity 802.1p prio -> switch
2761fad09c73SVivien Didelot 		 * prio mapping.
2762fad09c73SVivien Didelot 		 */
27630e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567,
27640e7b9925SAndrew Lunn 					   0x7654);
27650e7b9925SAndrew Lunn 		if (err)
27660e7b9925SAndrew Lunn 			return err;
2767fad09c73SVivien Didelot 	}
2768fad09c73SVivien Didelot 
27691bc261faSJamie Lentin 	/* Rate Control: disable ingress rate limiting. */
2770fad09c73SVivien Didelot 	if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2771fad09c73SVivien Didelot 	    mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2772fad09c73SVivien Didelot 	    mv88e6xxx_6320_family(chip)) {
27730e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
27740e7b9925SAndrew Lunn 					   0x0001);
27750e7b9925SAndrew Lunn 		if (err)
27760e7b9925SAndrew Lunn 			return err;
27771bc261faSJamie Lentin 	} else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) {
27780e7b9925SAndrew Lunn 		err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
27790e7b9925SAndrew Lunn 					   0x0000);
27800e7b9925SAndrew Lunn 		if (err)
27810e7b9925SAndrew Lunn 			return err;
2782fad09c73SVivien Didelot 	}
2783fad09c73SVivien Didelot 
2784fad09c73SVivien Didelot 	/* Port Control 1: disable trunking, disable sending
2785fad09c73SVivien Didelot 	 * learning messages to this port.
2786fad09c73SVivien Didelot 	 */
27870e7b9925SAndrew Lunn 	err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000);
27880e7b9925SAndrew Lunn 	if (err)
27890e7b9925SAndrew Lunn 		return err;
2790fad09c73SVivien Didelot 
2791fad09c73SVivien Didelot 	/* Port based VLAN map: give each port the same default address
2792fad09c73SVivien Didelot 	 * database, and allow bidirectional communication between the
2793fad09c73SVivien Didelot 	 * CPU and DSA port(s), and the other ports.
2794fad09c73SVivien Didelot 	 */
27950e7b9925SAndrew Lunn 	err = _mv88e6xxx_port_fid_set(chip, port, 0);
27960e7b9925SAndrew Lunn 	if (err)
27970e7b9925SAndrew Lunn 		return err;
2798fad09c73SVivien Didelot 
27990e7b9925SAndrew Lunn 	err = _mv88e6xxx_port_based_vlan_map(chip, port);
28000e7b9925SAndrew Lunn 	if (err)
28010e7b9925SAndrew Lunn 		return err;
2802fad09c73SVivien Didelot 
2803fad09c73SVivien Didelot 	/* Default VLAN ID and priority: don't set a default VLAN
2804fad09c73SVivien Didelot 	 * ID, and set the default packet priority to zero.
2805fad09c73SVivien Didelot 	 */
28060e7b9925SAndrew Lunn 	return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
2807fad09c73SVivien Didelot }
2808fad09c73SVivien Didelot 
2809aa0938c6SWei Yongjun static int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
28103b4caa1bSVivien Didelot {
28113b4caa1bSVivien Didelot 	int err;
28123b4caa1bSVivien Didelot 
2813a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
28143b4caa1bSVivien Didelot 	if (err)
28153b4caa1bSVivien Didelot 		return err;
28163b4caa1bSVivien Didelot 
2817a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
28183b4caa1bSVivien Didelot 	if (err)
28193b4caa1bSVivien Didelot 		return err;
28203b4caa1bSVivien Didelot 
2821a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
2822a935c052SVivien Didelot 	if (err)
2823a935c052SVivien Didelot 		return err;
2824a935c052SVivien Didelot 
2825a935c052SVivien Didelot 	return 0;
28263b4caa1bSVivien Didelot }
28273b4caa1bSVivien Didelot 
2828acddbd21SVivien Didelot static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
2829acddbd21SVivien Didelot 				     unsigned int msecs)
2830acddbd21SVivien Didelot {
2831acddbd21SVivien Didelot 	const unsigned int coeff = chip->info->age_time_coeff;
2832acddbd21SVivien Didelot 	const unsigned int min = 0x01 * coeff;
2833acddbd21SVivien Didelot 	const unsigned int max = 0xff * coeff;
2834acddbd21SVivien Didelot 	u8 age_time;
2835acddbd21SVivien Didelot 	u16 val;
2836acddbd21SVivien Didelot 	int err;
2837acddbd21SVivien Didelot 
2838acddbd21SVivien Didelot 	if (msecs < min || msecs > max)
2839acddbd21SVivien Didelot 		return -ERANGE;
2840acddbd21SVivien Didelot 
2841acddbd21SVivien Didelot 	/* Round to nearest multiple of coeff */
2842acddbd21SVivien Didelot 	age_time = (msecs + coeff / 2) / coeff;
2843acddbd21SVivien Didelot 
2844a935c052SVivien Didelot 	err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
2845acddbd21SVivien Didelot 	if (err)
2846acddbd21SVivien Didelot 		return err;
2847acddbd21SVivien Didelot 
2848acddbd21SVivien Didelot 	/* AgeTime is 11:4 bits */
2849acddbd21SVivien Didelot 	val &= ~0xff0;
2850acddbd21SVivien Didelot 	val |= age_time << 4;
2851acddbd21SVivien Didelot 
2852a935c052SVivien Didelot 	return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
2853acddbd21SVivien Didelot }
2854acddbd21SVivien Didelot 
28552cfcd964SVivien Didelot static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
28562cfcd964SVivien Didelot 				     unsigned int ageing_time)
28572cfcd964SVivien Didelot {
285804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
28592cfcd964SVivien Didelot 	int err;
28602cfcd964SVivien Didelot 
28612cfcd964SVivien Didelot 	mutex_lock(&chip->reg_lock);
28622cfcd964SVivien Didelot 	err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
28632cfcd964SVivien Didelot 	mutex_unlock(&chip->reg_lock);
28642cfcd964SVivien Didelot 
28652cfcd964SVivien Didelot 	return err;
28662cfcd964SVivien Didelot }
28672cfcd964SVivien Didelot 
28689729934cSVivien Didelot static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
2869fad09c73SVivien Didelot {
2870fad09c73SVivien Didelot 	struct dsa_switch *ds = chip->ds;
2871fad09c73SVivien Didelot 	u32 upstream_port = dsa_upstream_port(ds);
2872fad09c73SVivien Didelot 	u16 reg;
2873fad09c73SVivien Didelot 	int err;
2874fad09c73SVivien Didelot 
2875fad09c73SVivien Didelot 	/* Enable the PHY Polling Unit if present, don't discard any packets,
2876fad09c73SVivien Didelot 	 * and mask all interrupt sources.
2877fad09c73SVivien Didelot 	 */
2878dc30c35bSAndrew Lunn 	err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
2879dc30c35bSAndrew Lunn 	if (err < 0)
2880dc30c35bSAndrew Lunn 		return err;
2881dc30c35bSAndrew Lunn 
2882dc30c35bSAndrew Lunn 	reg &= ~GLOBAL_CONTROL_PPU_ENABLE;
2883fad09c73SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
2884fad09c73SVivien Didelot 	    mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
2885fad09c73SVivien Didelot 		reg |= GLOBAL_CONTROL_PPU_ENABLE;
2886fad09c73SVivien Didelot 
2887a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
2888fad09c73SVivien Didelot 	if (err)
2889fad09c73SVivien Didelot 		return err;
2890fad09c73SVivien Didelot 
2891fad09c73SVivien Didelot 	/* Configure the upstream port, and configure it as the port to which
2892fad09c73SVivien Didelot 	 * ingress and egress and ARP monitor frames are to be sent.
2893fad09c73SVivien Didelot 	 */
2894fad09c73SVivien Didelot 	reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2895fad09c73SVivien Didelot 		upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2896fad09c73SVivien Didelot 		upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2897a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
2898fad09c73SVivien Didelot 	if (err)
2899fad09c73SVivien Didelot 		return err;
2900fad09c73SVivien Didelot 
2901fad09c73SVivien Didelot 	/* Disable remote management, and set the switch's DSA device number. */
2902a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
2903fad09c73SVivien Didelot 				 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2904fad09c73SVivien Didelot 				 (ds->index & 0x1f));
2905fad09c73SVivien Didelot 	if (err)
2906fad09c73SVivien Didelot 		return err;
2907fad09c73SVivien Didelot 
2908acddbd21SVivien Didelot 	/* Clear all the VTU and STU entries */
2909acddbd21SVivien Didelot 	err = _mv88e6xxx_vtu_stu_flush(chip);
2910acddbd21SVivien Didelot 	if (err < 0)
2911acddbd21SVivien Didelot 		return err;
2912acddbd21SVivien Didelot 
2913fad09c73SVivien Didelot 	/* Set the default address aging time to 5 minutes, and
2914fad09c73SVivien Didelot 	 * enable address learn messages to be sent to all message
2915fad09c73SVivien Didelot 	 * ports.
2916fad09c73SVivien Didelot 	 */
2917a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
2918acddbd21SVivien Didelot 				 GLOBAL_ATU_CONTROL_LEARN2ALL);
2919fad09c73SVivien Didelot 	if (err)
2920fad09c73SVivien Didelot 		return err;
2921fad09c73SVivien Didelot 
2922acddbd21SVivien Didelot 	err = mv88e6xxx_g1_set_age_time(chip, 300000);
2923acddbd21SVivien Didelot 	if (err)
29249729934cSVivien Didelot 		return err;
29259729934cSVivien Didelot 
29269729934cSVivien Didelot 	/* Clear all ATU entries */
29279729934cSVivien Didelot 	err = _mv88e6xxx_atu_flush(chip, 0, true);
29289729934cSVivien Didelot 	if (err)
29299729934cSVivien Didelot 		return err;
29309729934cSVivien Didelot 
2931fad09c73SVivien Didelot 	/* Configure the IP ToS mapping registers. */
2932a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
2933fad09c73SVivien Didelot 	if (err)
2934fad09c73SVivien Didelot 		return err;
2935a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
2936fad09c73SVivien Didelot 	if (err)
2937fad09c73SVivien Didelot 		return err;
2938a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
2939fad09c73SVivien Didelot 	if (err)
2940fad09c73SVivien Didelot 		return err;
2941a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
2942fad09c73SVivien Didelot 	if (err)
2943fad09c73SVivien Didelot 		return err;
2944a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
2945fad09c73SVivien Didelot 	if (err)
2946fad09c73SVivien Didelot 		return err;
2947a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
2948fad09c73SVivien Didelot 	if (err)
2949fad09c73SVivien Didelot 		return err;
2950a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
2951fad09c73SVivien Didelot 	if (err)
2952fad09c73SVivien Didelot 		return err;
2953a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
2954fad09c73SVivien Didelot 	if (err)
2955fad09c73SVivien Didelot 		return err;
2956fad09c73SVivien Didelot 
2957fad09c73SVivien Didelot 	/* Configure the IEEE 802.1p priority mapping register. */
2958a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
2959fad09c73SVivien Didelot 	if (err)
2960fad09c73SVivien Didelot 		return err;
2961fad09c73SVivien Didelot 
29629729934cSVivien Didelot 	/* Clear the statistics counters for all ports */
2963a935c052SVivien Didelot 	err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
29649729934cSVivien Didelot 				 GLOBAL_STATS_OP_FLUSH_ALL);
29659729934cSVivien Didelot 	if (err)
29669729934cSVivien Didelot 		return err;
29679729934cSVivien Didelot 
29689729934cSVivien Didelot 	/* Wait for the flush to complete. */
29699729934cSVivien Didelot 	err = _mv88e6xxx_stats_wait(chip);
29709729934cSVivien Didelot 	if (err)
29719729934cSVivien Didelot 		return err;
29729729934cSVivien Didelot 
29739729934cSVivien Didelot 	return 0;
29749729934cSVivien Didelot }
29759729934cSVivien Didelot 
2976fad09c73SVivien Didelot static int mv88e6xxx_setup(struct dsa_switch *ds)
2977fad09c73SVivien Didelot {
297804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
2979fad09c73SVivien Didelot 	int err;
2980fad09c73SVivien Didelot 	int i;
2981fad09c73SVivien Didelot 
2982fad09c73SVivien Didelot 	chip->ds = ds;
2983fad09c73SVivien Didelot 	ds->slave_mii_bus = chip->mdio_bus;
2984fad09c73SVivien Didelot 
2985fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
2986fad09c73SVivien Didelot 
29879729934cSVivien Didelot 	/* Setup Switch Port Registers */
2988370b4ffbSVivien Didelot 	for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
29899729934cSVivien Didelot 		err = mv88e6xxx_setup_port(chip, i);
29909729934cSVivien Didelot 		if (err)
29919729934cSVivien Didelot 			goto unlock;
29929729934cSVivien Didelot 	}
29939729934cSVivien Didelot 
29949729934cSVivien Didelot 	/* Setup Switch Global 1 Registers */
29959729934cSVivien Didelot 	err = mv88e6xxx_g1_setup(chip);
2996fad09c73SVivien Didelot 	if (err)
2997fad09c73SVivien Didelot 		goto unlock;
2998fad09c73SVivien Didelot 
29999729934cSVivien Didelot 	/* Setup Switch Global 2 Registers */
30009729934cSVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
30019729934cSVivien Didelot 		err = mv88e6xxx_g2_setup(chip);
3002fad09c73SVivien Didelot 		if (err)
3003fad09c73SVivien Didelot 			goto unlock;
3004fad09c73SVivien Didelot 	}
3005fad09c73SVivien Didelot 
3006fad09c73SVivien Didelot unlock:
3007fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3008fad09c73SVivien Didelot 
3009fad09c73SVivien Didelot 	return err;
3010fad09c73SVivien Didelot }
3011fad09c73SVivien Didelot 
30123b4caa1bSVivien Didelot static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
30133b4caa1bSVivien Didelot {
301404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
30153b4caa1bSVivien Didelot 	int err;
30163b4caa1bSVivien Didelot 
3017b073d4e2SVivien Didelot 	if (!chip->info->ops->set_switch_mac)
3018b073d4e2SVivien Didelot 		return -EOPNOTSUPP;
3019b073d4e2SVivien Didelot 
30203b4caa1bSVivien Didelot 	mutex_lock(&chip->reg_lock);
3021b073d4e2SVivien Didelot 	err = chip->info->ops->set_switch_mac(chip, addr);
30223b4caa1bSVivien Didelot 	mutex_unlock(&chip->reg_lock);
30233b4caa1bSVivien Didelot 
30243b4caa1bSVivien Didelot 	return err;
30253b4caa1bSVivien Didelot }
30263b4caa1bSVivien Didelot 
3027e57e5e77SVivien Didelot static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
3028fad09c73SVivien Didelot {
3029fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip = bus->priv;
3030e57e5e77SVivien Didelot 	u16 val;
3031e57e5e77SVivien Didelot 	int err;
3032fad09c73SVivien Didelot 
3033370b4ffbSVivien Didelot 	if (phy >= mv88e6xxx_num_ports(chip))
3034fad09c73SVivien Didelot 		return 0xffff;
3035fad09c73SVivien Didelot 
3036fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
3037e57e5e77SVivien Didelot 	err = mv88e6xxx_phy_read(chip, phy, reg, &val);
3038fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3039e57e5e77SVivien Didelot 
3040e57e5e77SVivien Didelot 	return err ? err : val;
3041fad09c73SVivien Didelot }
3042fad09c73SVivien Didelot 
3043e57e5e77SVivien Didelot static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
3044fad09c73SVivien Didelot {
3045fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip = bus->priv;
3046e57e5e77SVivien Didelot 	int err;
3047fad09c73SVivien Didelot 
3048370b4ffbSVivien Didelot 	if (phy >= mv88e6xxx_num_ports(chip))
3049fad09c73SVivien Didelot 		return 0xffff;
3050fad09c73SVivien Didelot 
3051fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
3052e57e5e77SVivien Didelot 	err = mv88e6xxx_phy_write(chip, phy, reg, val);
3053fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3054e57e5e77SVivien Didelot 
3055e57e5e77SVivien Didelot 	return err;
3056fad09c73SVivien Didelot }
3057fad09c73SVivien Didelot 
3058fad09c73SVivien Didelot static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
3059fad09c73SVivien Didelot 				   struct device_node *np)
3060fad09c73SVivien Didelot {
3061fad09c73SVivien Didelot 	static int index;
3062fad09c73SVivien Didelot 	struct mii_bus *bus;
3063fad09c73SVivien Didelot 	int err;
3064fad09c73SVivien Didelot 
3065fad09c73SVivien Didelot 	if (np)
3066fad09c73SVivien Didelot 		chip->mdio_np = of_get_child_by_name(np, "mdio");
3067fad09c73SVivien Didelot 
3068fad09c73SVivien Didelot 	bus = devm_mdiobus_alloc(chip->dev);
3069fad09c73SVivien Didelot 	if (!bus)
3070fad09c73SVivien Didelot 		return -ENOMEM;
3071fad09c73SVivien Didelot 
3072fad09c73SVivien Didelot 	bus->priv = (void *)chip;
3073fad09c73SVivien Didelot 	if (np) {
3074fad09c73SVivien Didelot 		bus->name = np->full_name;
3075fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3076fad09c73SVivien Didelot 	} else {
3077fad09c73SVivien Didelot 		bus->name = "mv88e6xxx SMI";
3078fad09c73SVivien Didelot 		snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3079fad09c73SVivien Didelot 	}
3080fad09c73SVivien Didelot 
3081fad09c73SVivien Didelot 	bus->read = mv88e6xxx_mdio_read;
3082fad09c73SVivien Didelot 	bus->write = mv88e6xxx_mdio_write;
3083fad09c73SVivien Didelot 	bus->parent = chip->dev;
3084fad09c73SVivien Didelot 
3085fad09c73SVivien Didelot 	if (chip->mdio_np)
3086fad09c73SVivien Didelot 		err = of_mdiobus_register(bus, chip->mdio_np);
3087fad09c73SVivien Didelot 	else
3088fad09c73SVivien Didelot 		err = mdiobus_register(bus);
3089fad09c73SVivien Didelot 	if (err) {
3090fad09c73SVivien Didelot 		dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
3091fad09c73SVivien Didelot 		goto out;
3092fad09c73SVivien Didelot 	}
3093fad09c73SVivien Didelot 	chip->mdio_bus = bus;
3094fad09c73SVivien Didelot 
3095fad09c73SVivien Didelot 	return 0;
3096fad09c73SVivien Didelot 
3097fad09c73SVivien Didelot out:
3098fad09c73SVivien Didelot 	if (chip->mdio_np)
3099fad09c73SVivien Didelot 		of_node_put(chip->mdio_np);
3100fad09c73SVivien Didelot 
3101fad09c73SVivien Didelot 	return err;
3102fad09c73SVivien Didelot }
3103fad09c73SVivien Didelot 
3104fad09c73SVivien Didelot static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
3105fad09c73SVivien Didelot 
3106fad09c73SVivien Didelot {
3107fad09c73SVivien Didelot 	struct mii_bus *bus = chip->mdio_bus;
3108fad09c73SVivien Didelot 
3109fad09c73SVivien Didelot 	mdiobus_unregister(bus);
3110fad09c73SVivien Didelot 
3111fad09c73SVivien Didelot 	if (chip->mdio_np)
3112fad09c73SVivien Didelot 		of_node_put(chip->mdio_np);
3113fad09c73SVivien Didelot }
3114fad09c73SVivien Didelot 
3115fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON
3116fad09c73SVivien Didelot 
3117fad09c73SVivien Didelot static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3118fad09c73SVivien Didelot {
311904bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
31209c93829cSVivien Didelot 	u16 val;
3121fad09c73SVivien Didelot 	int ret;
3122fad09c73SVivien Didelot 
3123fad09c73SVivien Didelot 	*temp = 0;
3124fad09c73SVivien Didelot 
3125fad09c73SVivien Didelot 	mutex_lock(&chip->reg_lock);
3126fad09c73SVivien Didelot 
31279c93829cSVivien Didelot 	ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
3128fad09c73SVivien Didelot 	if (ret < 0)
3129fad09c73SVivien Didelot 		goto error;
3130fad09c73SVivien Didelot 
3131fad09c73SVivien Didelot 	/* Enable temperature sensor */
31329c93829cSVivien Didelot 	ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
3133fad09c73SVivien Didelot 	if (ret < 0)
3134fad09c73SVivien Didelot 		goto error;
3135fad09c73SVivien Didelot 
31369c93829cSVivien Didelot 	ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
3137fad09c73SVivien Didelot 	if (ret < 0)
3138fad09c73SVivien Didelot 		goto error;
3139fad09c73SVivien Didelot 
3140fad09c73SVivien Didelot 	/* Wait for temperature to stabilize */
3141fad09c73SVivien Didelot 	usleep_range(10000, 12000);
3142fad09c73SVivien Didelot 
31439c93829cSVivien Didelot 	ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
31449c93829cSVivien Didelot 	if (ret < 0)
3145fad09c73SVivien Didelot 		goto error;
3146fad09c73SVivien Didelot 
3147fad09c73SVivien Didelot 	/* Disable temperature sensor */
31489c93829cSVivien Didelot 	ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
3149fad09c73SVivien Didelot 	if (ret < 0)
3150fad09c73SVivien Didelot 		goto error;
3151fad09c73SVivien Didelot 
3152fad09c73SVivien Didelot 	*temp = ((val & 0x1f) - 5) * 5;
3153fad09c73SVivien Didelot 
3154fad09c73SVivien Didelot error:
31559c93829cSVivien Didelot 	mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
3156fad09c73SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3157fad09c73SVivien Didelot 	return ret;
3158fad09c73SVivien Didelot }
3159fad09c73SVivien Didelot 
3160fad09c73SVivien Didelot static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3161fad09c73SVivien Didelot {
316204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3163fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
31649c93829cSVivien Didelot 	u16 val;
3165fad09c73SVivien Didelot 	int ret;
3166fad09c73SVivien Didelot 
3167fad09c73SVivien Didelot 	*temp = 0;
3168fad09c73SVivien Didelot 
31699c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
31709c93829cSVivien Didelot 	ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
31719c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
3172fad09c73SVivien Didelot 	if (ret < 0)
3173fad09c73SVivien Didelot 		return ret;
3174fad09c73SVivien Didelot 
31759c93829cSVivien Didelot 	*temp = (val & 0xff) - 25;
3176fad09c73SVivien Didelot 
3177fad09c73SVivien Didelot 	return 0;
3178fad09c73SVivien Didelot }
3179fad09c73SVivien Didelot 
3180fad09c73SVivien Didelot static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
3181fad09c73SVivien Didelot {
318204bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3183fad09c73SVivien Didelot 
3184fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
3185fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3186fad09c73SVivien Didelot 
3187fad09c73SVivien Didelot 	if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
3188fad09c73SVivien Didelot 		return mv88e63xx_get_temp(ds, temp);
3189fad09c73SVivien Didelot 
3190fad09c73SVivien Didelot 	return mv88e61xx_get_temp(ds, temp);
3191fad09c73SVivien Didelot }
3192fad09c73SVivien Didelot 
3193fad09c73SVivien Didelot static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
3194fad09c73SVivien Didelot {
319504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3196fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
31979c93829cSVivien Didelot 	u16 val;
3198fad09c73SVivien Didelot 	int ret;
3199fad09c73SVivien Didelot 
3200fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
3201fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3202fad09c73SVivien Didelot 
3203fad09c73SVivien Didelot 	*temp = 0;
3204fad09c73SVivien Didelot 
32059c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
32069c93829cSVivien Didelot 	ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
32079c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
3208fad09c73SVivien Didelot 	if (ret < 0)
3209fad09c73SVivien Didelot 		return ret;
3210fad09c73SVivien Didelot 
32119c93829cSVivien Didelot 	*temp = (((val >> 8) & 0x1f) * 5) - 25;
3212fad09c73SVivien Didelot 
3213fad09c73SVivien Didelot 	return 0;
3214fad09c73SVivien Didelot }
3215fad09c73SVivien Didelot 
3216fad09c73SVivien Didelot static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
3217fad09c73SVivien Didelot {
321804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3219fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
32209c93829cSVivien Didelot 	u16 val;
32219c93829cSVivien Didelot 	int err;
3222fad09c73SVivien Didelot 
3223fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
3224fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3225fad09c73SVivien Didelot 
32269c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
32279c93829cSVivien Didelot 	err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
32289c93829cSVivien Didelot 	if (err)
32299c93829cSVivien Didelot 		goto unlock;
3230fad09c73SVivien Didelot 	temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
32319c93829cSVivien Didelot 	err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
32329c93829cSVivien Didelot 				       (val & 0xe0ff) | (temp << 8));
32339c93829cSVivien Didelot unlock:
32349c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
32359c93829cSVivien Didelot 
32369c93829cSVivien Didelot 	return err;
3237fad09c73SVivien Didelot }
3238fad09c73SVivien Didelot 
3239fad09c73SVivien Didelot static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
3240fad09c73SVivien Didelot {
324104bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3242fad09c73SVivien Didelot 	int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
32439c93829cSVivien Didelot 	u16 val;
3244fad09c73SVivien Didelot 	int ret;
3245fad09c73SVivien Didelot 
3246fad09c73SVivien Didelot 	if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
3247fad09c73SVivien Didelot 		return -EOPNOTSUPP;
3248fad09c73SVivien Didelot 
3249fad09c73SVivien Didelot 	*alarm = false;
3250fad09c73SVivien Didelot 
32519c93829cSVivien Didelot 	mutex_lock(&chip->reg_lock);
32529c93829cSVivien Didelot 	ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
32539c93829cSVivien Didelot 	mutex_unlock(&chip->reg_lock);
3254fad09c73SVivien Didelot 	if (ret < 0)
3255fad09c73SVivien Didelot 		return ret;
3256fad09c73SVivien Didelot 
32579c93829cSVivien Didelot 	*alarm = !!(val & 0x40);
3258fad09c73SVivien Didelot 
3259fad09c73SVivien Didelot 	return 0;
3260fad09c73SVivien Didelot }
3261fad09c73SVivien Didelot #endif /* CONFIG_NET_DSA_HWMON */
3262fad09c73SVivien Didelot 
3263855b1932SVivien Didelot static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
3264855b1932SVivien Didelot {
326504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3266855b1932SVivien Didelot 
3267855b1932SVivien Didelot 	return chip->eeprom_len;
3268855b1932SVivien Didelot }
3269855b1932SVivien Didelot 
3270855b1932SVivien Didelot static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
3271855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
3272855b1932SVivien Didelot {
327304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3274855b1932SVivien Didelot 	int err;
3275855b1932SVivien Didelot 
3276ee4dc2e7SVivien Didelot 	if (!chip->info->ops->get_eeprom)
3277ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
3278ee4dc2e7SVivien Didelot 
3279855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
3280ee4dc2e7SVivien Didelot 	err = chip->info->ops->get_eeprom(chip, eeprom, data);
3281855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3282855b1932SVivien Didelot 
3283855b1932SVivien Didelot 	if (err)
3284855b1932SVivien Didelot 		return err;
3285855b1932SVivien Didelot 
3286855b1932SVivien Didelot 	eeprom->magic = 0xc3ec4951;
3287855b1932SVivien Didelot 
3288855b1932SVivien Didelot 	return 0;
3289855b1932SVivien Didelot }
3290855b1932SVivien Didelot 
3291855b1932SVivien Didelot static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
3292855b1932SVivien Didelot 				struct ethtool_eeprom *eeprom, u8 *data)
3293855b1932SVivien Didelot {
329404bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
3295855b1932SVivien Didelot 	int err;
3296855b1932SVivien Didelot 
3297ee4dc2e7SVivien Didelot 	if (!chip->info->ops->set_eeprom)
3298ee4dc2e7SVivien Didelot 		return -EOPNOTSUPP;
3299ee4dc2e7SVivien Didelot 
3300855b1932SVivien Didelot 	if (eeprom->magic != 0xc3ec4951)
3301855b1932SVivien Didelot 		return -EINVAL;
3302855b1932SVivien Didelot 
3303855b1932SVivien Didelot 	mutex_lock(&chip->reg_lock);
3304ee4dc2e7SVivien Didelot 	err = chip->info->ops->set_eeprom(chip, eeprom, data);
3305855b1932SVivien Didelot 	mutex_unlock(&chip->reg_lock);
3306855b1932SVivien Didelot 
3307855b1932SVivien Didelot 	return err;
3308855b1932SVivien Didelot }
3309855b1932SVivien Didelot 
3310b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6085_ops = {
3311b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3312b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3313b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
3314b3469dd8SVivien Didelot };
3315b3469dd8SVivien Didelot 
3316b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6095_ops = {
3317b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3318b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3319b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
3320b3469dd8SVivien Didelot };
3321b3469dd8SVivien Didelot 
3322b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6123_ops = {
3323b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3324b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_read,
3325b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_write,
3326b3469dd8SVivien Didelot };
3327b3469dd8SVivien Didelot 
3328b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6131_ops = {
3329b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3330b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3331b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
3332b3469dd8SVivien Didelot };
3333b3469dd8SVivien Didelot 
3334b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6161_ops = {
3335b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3336b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_read,
3337b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_write,
3338b3469dd8SVivien Didelot };
3339b3469dd8SVivien Didelot 
3340b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6165_ops = {
3341b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3342b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_read,
3343b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_write,
3344b3469dd8SVivien Didelot };
3345b3469dd8SVivien Didelot 
3346b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6171_ops = {
3347b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3348b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3349b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3350b3469dd8SVivien Didelot };
3351b3469dd8SVivien Didelot 
3352b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6172_ops = {
3353ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3354ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3355b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3356b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3357b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3358b3469dd8SVivien Didelot };
3359b3469dd8SVivien Didelot 
3360b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6175_ops = {
3361b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3362b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3363b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3364b3469dd8SVivien Didelot };
3365b3469dd8SVivien Didelot 
3366b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6176_ops = {
3367ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3368ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3369b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3370b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3371b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3372b3469dd8SVivien Didelot };
3373b3469dd8SVivien Didelot 
3374b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6185_ops = {
3375b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g1_set_switch_mac,
3376b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_phy_ppu_read,
3377b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_phy_ppu_write,
3378b3469dd8SVivien Didelot };
3379b3469dd8SVivien Didelot 
3380b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6240_ops = {
3381ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3382ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3383b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3384b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3385b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3386b3469dd8SVivien Didelot };
3387b3469dd8SVivien Didelot 
3388b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6320_ops = {
3389ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3390ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3391b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3392b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3393b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3394b3469dd8SVivien Didelot };
3395b3469dd8SVivien Didelot 
3396b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6321_ops = {
3397ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3398ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3399b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3400b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3401b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3402b3469dd8SVivien Didelot };
3403b3469dd8SVivien Didelot 
3404b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6350_ops = {
3405b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3406b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3407b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3408b3469dd8SVivien Didelot };
3409b3469dd8SVivien Didelot 
3410b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6351_ops = {
3411b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3412b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3413b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3414b3469dd8SVivien Didelot };
3415b3469dd8SVivien Didelot 
3416b3469dd8SVivien Didelot static const struct mv88e6xxx_ops mv88e6352_ops = {
3417ee4dc2e7SVivien Didelot 	.get_eeprom = mv88e6xxx_g2_get_eeprom16,
3418ee4dc2e7SVivien Didelot 	.set_eeprom = mv88e6xxx_g2_set_eeprom16,
3419b073d4e2SVivien Didelot 	.set_switch_mac = mv88e6xxx_g2_set_switch_mac,
3420b3469dd8SVivien Didelot 	.phy_read = mv88e6xxx_g2_smi_phy_read,
3421b3469dd8SVivien Didelot 	.phy_write = mv88e6xxx_g2_smi_phy_write,
3422b3469dd8SVivien Didelot };
3423b3469dd8SVivien Didelot 
3424fad09c73SVivien Didelot static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3425fad09c73SVivien Didelot 	[MV88E6085] = {
3426fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3427fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6097,
3428fad09c73SVivien Didelot 		.name = "Marvell 88E6085",
3429fad09c73SVivien Didelot 		.num_databases = 4096,
3430fad09c73SVivien Didelot 		.num_ports = 10,
3431fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3432a935c052SVivien Didelot 		.global1_addr = 0x1b,
3433acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3434dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3435fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6097,
3436b3469dd8SVivien Didelot 		.ops = &mv88e6085_ops,
3437fad09c73SVivien Didelot 	},
3438fad09c73SVivien Didelot 
3439fad09c73SVivien Didelot 	[MV88E6095] = {
3440fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3441fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6095,
3442fad09c73SVivien Didelot 		.name = "Marvell 88E6095/88E6095F",
3443fad09c73SVivien Didelot 		.num_databases = 256,
3444fad09c73SVivien Didelot 		.num_ports = 11,
3445fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3446a935c052SVivien Didelot 		.global1_addr = 0x1b,
3447acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3448dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3449fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6095,
3450b3469dd8SVivien Didelot 		.ops = &mv88e6095_ops,
3451fad09c73SVivien Didelot 	},
3452fad09c73SVivien Didelot 
3453fad09c73SVivien Didelot 	[MV88E6123] = {
3454fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3455fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3456fad09c73SVivien Didelot 		.name = "Marvell 88E6123",
3457fad09c73SVivien Didelot 		.num_databases = 4096,
3458fad09c73SVivien Didelot 		.num_ports = 3,
3459fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3460a935c052SVivien Didelot 		.global1_addr = 0x1b,
3461acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3462dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3463fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3464b3469dd8SVivien Didelot 		.ops = &mv88e6123_ops,
3465fad09c73SVivien Didelot 	},
3466fad09c73SVivien Didelot 
3467fad09c73SVivien Didelot 	[MV88E6131] = {
3468fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3469fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3470fad09c73SVivien Didelot 		.name = "Marvell 88E6131",
3471fad09c73SVivien Didelot 		.num_databases = 256,
3472fad09c73SVivien Didelot 		.num_ports = 8,
3473fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3474a935c052SVivien Didelot 		.global1_addr = 0x1b,
3475acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3476dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3477fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3478b3469dd8SVivien Didelot 		.ops = &mv88e6131_ops,
3479fad09c73SVivien Didelot 	},
3480fad09c73SVivien Didelot 
3481fad09c73SVivien Didelot 	[MV88E6161] = {
3482fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3483fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3484fad09c73SVivien Didelot 		.name = "Marvell 88E6161",
3485fad09c73SVivien Didelot 		.num_databases = 4096,
3486fad09c73SVivien Didelot 		.num_ports = 6,
3487fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3488a935c052SVivien Didelot 		.global1_addr = 0x1b,
3489acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3490dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3491fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3492b3469dd8SVivien Didelot 		.ops = &mv88e6161_ops,
3493fad09c73SVivien Didelot 	},
3494fad09c73SVivien Didelot 
3495fad09c73SVivien Didelot 	[MV88E6165] = {
3496fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3497fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6165,
3498fad09c73SVivien Didelot 		.name = "Marvell 88E6165",
3499fad09c73SVivien Didelot 		.num_databases = 4096,
3500fad09c73SVivien Didelot 		.num_ports = 6,
3501fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3502a935c052SVivien Didelot 		.global1_addr = 0x1b,
3503acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3504dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3505fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6165,
3506b3469dd8SVivien Didelot 		.ops = &mv88e6165_ops,
3507fad09c73SVivien Didelot 	},
3508fad09c73SVivien Didelot 
3509fad09c73SVivien Didelot 	[MV88E6171] = {
3510fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3511fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3512fad09c73SVivien Didelot 		.name = "Marvell 88E6171",
3513fad09c73SVivien Didelot 		.num_databases = 4096,
3514fad09c73SVivien Didelot 		.num_ports = 7,
3515fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3516a935c052SVivien Didelot 		.global1_addr = 0x1b,
3517acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3518dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3519fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3520b3469dd8SVivien Didelot 		.ops = &mv88e6171_ops,
3521fad09c73SVivien Didelot 	},
3522fad09c73SVivien Didelot 
3523fad09c73SVivien Didelot 	[MV88E6172] = {
3524fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3525fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3526fad09c73SVivien Didelot 		.name = "Marvell 88E6172",
3527fad09c73SVivien Didelot 		.num_databases = 4096,
3528fad09c73SVivien Didelot 		.num_ports = 7,
3529fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3530a935c052SVivien Didelot 		.global1_addr = 0x1b,
3531acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3532dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3533fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3534b3469dd8SVivien Didelot 		.ops = &mv88e6172_ops,
3535fad09c73SVivien Didelot 	},
3536fad09c73SVivien Didelot 
3537fad09c73SVivien Didelot 	[MV88E6175] = {
3538fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3539fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3540fad09c73SVivien Didelot 		.name = "Marvell 88E6175",
3541fad09c73SVivien Didelot 		.num_databases = 4096,
3542fad09c73SVivien Didelot 		.num_ports = 7,
3543fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3544a935c052SVivien Didelot 		.global1_addr = 0x1b,
3545acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3546dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3547fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3548b3469dd8SVivien Didelot 		.ops = &mv88e6175_ops,
3549fad09c73SVivien Didelot 	},
3550fad09c73SVivien Didelot 
3551fad09c73SVivien Didelot 	[MV88E6176] = {
3552fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3553fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3554fad09c73SVivien Didelot 		.name = "Marvell 88E6176",
3555fad09c73SVivien Didelot 		.num_databases = 4096,
3556fad09c73SVivien Didelot 		.num_ports = 7,
3557fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3558a935c052SVivien Didelot 		.global1_addr = 0x1b,
3559acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3560dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3561fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3562b3469dd8SVivien Didelot 		.ops = &mv88e6176_ops,
3563fad09c73SVivien Didelot 	},
3564fad09c73SVivien Didelot 
3565fad09c73SVivien Didelot 	[MV88E6185] = {
3566fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3567fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6185,
3568fad09c73SVivien Didelot 		.name = "Marvell 88E6185",
3569fad09c73SVivien Didelot 		.num_databases = 256,
3570fad09c73SVivien Didelot 		.num_ports = 10,
3571fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3572a935c052SVivien Didelot 		.global1_addr = 0x1b,
3573acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3574dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3575fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6185,
3576b3469dd8SVivien Didelot 		.ops = &mv88e6185_ops,
3577fad09c73SVivien Didelot 	},
3578fad09c73SVivien Didelot 
3579fad09c73SVivien Didelot 	[MV88E6240] = {
3580fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3581fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3582fad09c73SVivien Didelot 		.name = "Marvell 88E6240",
3583fad09c73SVivien Didelot 		.num_databases = 4096,
3584fad09c73SVivien Didelot 		.num_ports = 7,
3585fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3586a935c052SVivien Didelot 		.global1_addr = 0x1b,
3587acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3588dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3589fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3590b3469dd8SVivien Didelot 		.ops = &mv88e6240_ops,
3591fad09c73SVivien Didelot 	},
3592fad09c73SVivien Didelot 
3593fad09c73SVivien Didelot 	[MV88E6320] = {
3594fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3595fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3596fad09c73SVivien Didelot 		.name = "Marvell 88E6320",
3597fad09c73SVivien Didelot 		.num_databases = 4096,
3598fad09c73SVivien Didelot 		.num_ports = 7,
3599fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3600a935c052SVivien Didelot 		.global1_addr = 0x1b,
3601acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3602dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3603fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3604b3469dd8SVivien Didelot 		.ops = &mv88e6320_ops,
3605fad09c73SVivien Didelot 	},
3606fad09c73SVivien Didelot 
3607fad09c73SVivien Didelot 	[MV88E6321] = {
3608fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3609fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6320,
3610fad09c73SVivien Didelot 		.name = "Marvell 88E6321",
3611fad09c73SVivien Didelot 		.num_databases = 4096,
3612fad09c73SVivien Didelot 		.num_ports = 7,
3613fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3614a935c052SVivien Didelot 		.global1_addr = 0x1b,
3615acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3616dc30c35bSAndrew Lunn 		.g1_irqs = 8,
3617fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6320,
3618b3469dd8SVivien Didelot 		.ops = &mv88e6321_ops,
3619fad09c73SVivien Didelot 	},
3620fad09c73SVivien Didelot 
3621fad09c73SVivien Didelot 	[MV88E6350] = {
3622fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3623fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3624fad09c73SVivien Didelot 		.name = "Marvell 88E6350",
3625fad09c73SVivien Didelot 		.num_databases = 4096,
3626fad09c73SVivien Didelot 		.num_ports = 7,
3627fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3628a935c052SVivien Didelot 		.global1_addr = 0x1b,
3629acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3630dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3631fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3632b3469dd8SVivien Didelot 		.ops = &mv88e6350_ops,
3633fad09c73SVivien Didelot 	},
3634fad09c73SVivien Didelot 
3635fad09c73SVivien Didelot 	[MV88E6351] = {
3636fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3637fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6351,
3638fad09c73SVivien Didelot 		.name = "Marvell 88E6351",
3639fad09c73SVivien Didelot 		.num_databases = 4096,
3640fad09c73SVivien Didelot 		.num_ports = 7,
3641fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3642a935c052SVivien Didelot 		.global1_addr = 0x1b,
3643acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3644dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3645fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6351,
3646b3469dd8SVivien Didelot 		.ops = &mv88e6351_ops,
3647fad09c73SVivien Didelot 	},
3648fad09c73SVivien Didelot 
3649fad09c73SVivien Didelot 	[MV88E6352] = {
3650fad09c73SVivien Didelot 		.prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3651fad09c73SVivien Didelot 		.family = MV88E6XXX_FAMILY_6352,
3652fad09c73SVivien Didelot 		.name = "Marvell 88E6352",
3653fad09c73SVivien Didelot 		.num_databases = 4096,
3654fad09c73SVivien Didelot 		.num_ports = 7,
3655fad09c73SVivien Didelot 		.port_base_addr = 0x10,
3656a935c052SVivien Didelot 		.global1_addr = 0x1b,
3657acddbd21SVivien Didelot 		.age_time_coeff = 15000,
3658dc30c35bSAndrew Lunn 		.g1_irqs = 9,
3659fad09c73SVivien Didelot 		.flags = MV88E6XXX_FLAGS_FAMILY_6352,
3660b3469dd8SVivien Didelot 		.ops = &mv88e6352_ops,
3661fad09c73SVivien Didelot 	},
3662fad09c73SVivien Didelot };
3663fad09c73SVivien Didelot 
3664fad09c73SVivien Didelot static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
3665fad09c73SVivien Didelot {
3666fad09c73SVivien Didelot 	int i;
3667fad09c73SVivien Didelot 
3668fad09c73SVivien Didelot 	for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3669fad09c73SVivien Didelot 		if (mv88e6xxx_table[i].prod_num == prod_num)
3670fad09c73SVivien Didelot 			return &mv88e6xxx_table[i];
3671fad09c73SVivien Didelot 
3672fad09c73SVivien Didelot 	return NULL;
3673fad09c73SVivien Didelot }
3674fad09c73SVivien Didelot 
3675fad09c73SVivien Didelot static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
3676fad09c73SVivien Didelot {
3677fad09c73SVivien Didelot 	const struct mv88e6xxx_info *info;
36788f6345b2SVivien Didelot 	unsigned int prod_num, rev;
36798f6345b2SVivien Didelot 	u16 id;
36808f6345b2SVivien Didelot 	int err;
3681fad09c73SVivien Didelot 
36828f6345b2SVivien Didelot 	mutex_lock(&chip->reg_lock);
36838f6345b2SVivien Didelot 	err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
36848f6345b2SVivien Didelot 	mutex_unlock(&chip->reg_lock);
36858f6345b2SVivien Didelot 	if (err)
36868f6345b2SVivien Didelot 		return err;
3687fad09c73SVivien Didelot 
3688fad09c73SVivien Didelot 	prod_num = (id & 0xfff0) >> 4;
3689fad09c73SVivien Didelot 	rev = id & 0x000f;
3690fad09c73SVivien Didelot 
3691fad09c73SVivien Didelot 	info = mv88e6xxx_lookup_info(prod_num);
3692fad09c73SVivien Didelot 	if (!info)
3693fad09c73SVivien Didelot 		return -ENODEV;
3694fad09c73SVivien Didelot 
3695fad09c73SVivien Didelot 	/* Update the compatible info with the probed one */
3696fad09c73SVivien Didelot 	chip->info = info;
3697fad09c73SVivien Didelot 
3698ca070c10SVivien Didelot 	err = mv88e6xxx_g2_require(chip);
3699ca070c10SVivien Didelot 	if (err)
3700ca070c10SVivien Didelot 		return err;
3701ca070c10SVivien Didelot 
3702fad09c73SVivien Didelot 	dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3703fad09c73SVivien Didelot 		 chip->info->prod_num, chip->info->name, rev);
3704fad09c73SVivien Didelot 
3705fad09c73SVivien Didelot 	return 0;
3706fad09c73SVivien Didelot }
3707fad09c73SVivien Didelot 
3708fad09c73SVivien Didelot static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
3709fad09c73SVivien Didelot {
3710fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3711fad09c73SVivien Didelot 
3712fad09c73SVivien Didelot 	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3713fad09c73SVivien Didelot 	if (!chip)
3714fad09c73SVivien Didelot 		return NULL;
3715fad09c73SVivien Didelot 
3716fad09c73SVivien Didelot 	chip->dev = dev;
3717fad09c73SVivien Didelot 
3718fad09c73SVivien Didelot 	mutex_init(&chip->reg_lock);
3719fad09c73SVivien Didelot 
3720fad09c73SVivien Didelot 	return chip;
3721fad09c73SVivien Didelot }
3722fad09c73SVivien Didelot 
3723e57e5e77SVivien Didelot static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
3724e57e5e77SVivien Didelot {
3725b3469dd8SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3726e57e5e77SVivien Didelot 		mv88e6xxx_ppu_state_init(chip);
3727e57e5e77SVivien Didelot }
3728e57e5e77SVivien Didelot 
3729930188ceSAndrew Lunn static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
3730930188ceSAndrew Lunn {
3731b3469dd8SVivien Didelot 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
3732930188ceSAndrew Lunn 		mv88e6xxx_ppu_state_destroy(chip);
3733930188ceSAndrew Lunn }
3734930188ceSAndrew Lunn 
3735fad09c73SVivien Didelot static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
3736fad09c73SVivien Didelot 			      struct mii_bus *bus, int sw_addr)
3737fad09c73SVivien Didelot {
3738fad09c73SVivien Didelot 	/* ADDR[0] pin is unavailable externally and considered zero */
3739fad09c73SVivien Didelot 	if (sw_addr & 0x1)
3740fad09c73SVivien Didelot 		return -EINVAL;
3741fad09c73SVivien Didelot 
3742fad09c73SVivien Didelot 	if (sw_addr == 0)
3743fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
3744a0ffff24SVivien Didelot 	else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
3745fad09c73SVivien Didelot 		chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
3746fad09c73SVivien Didelot 	else
3747fad09c73SVivien Didelot 		return -EINVAL;
3748fad09c73SVivien Didelot 
3749fad09c73SVivien Didelot 	chip->bus = bus;
3750fad09c73SVivien Didelot 	chip->sw_addr = sw_addr;
3751fad09c73SVivien Didelot 
3752fad09c73SVivien Didelot 	return 0;
3753fad09c73SVivien Didelot }
3754fad09c73SVivien Didelot 
37557b314362SAndrew Lunn static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
37567b314362SAndrew Lunn {
375704bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
37582bbb33beSAndrew Lunn 
37592bbb33beSAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
37607b314362SAndrew Lunn 		return DSA_TAG_PROTO_EDSA;
37612bbb33beSAndrew Lunn 
37622bbb33beSAndrew Lunn 	return DSA_TAG_PROTO_DSA;
37637b314362SAndrew Lunn }
37647b314362SAndrew Lunn 
3765fad09c73SVivien Didelot static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3766fad09c73SVivien Didelot 				       struct device *host_dev, int sw_addr,
3767fad09c73SVivien Didelot 				       void **priv)
3768fad09c73SVivien Didelot {
3769fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3770fad09c73SVivien Didelot 	struct mii_bus *bus;
3771fad09c73SVivien Didelot 	int err;
3772fad09c73SVivien Didelot 
3773fad09c73SVivien Didelot 	bus = dsa_host_dev_to_mii_bus(host_dev);
3774fad09c73SVivien Didelot 	if (!bus)
3775fad09c73SVivien Didelot 		return NULL;
3776fad09c73SVivien Didelot 
3777fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dsa_dev);
3778fad09c73SVivien Didelot 	if (!chip)
3779fad09c73SVivien Didelot 		return NULL;
3780fad09c73SVivien Didelot 
3781fad09c73SVivien Didelot 	/* Legacy SMI probing will only support chips similar to 88E6085 */
3782fad09c73SVivien Didelot 	chip->info = &mv88e6xxx_table[MV88E6085];
3783fad09c73SVivien Didelot 
3784fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, bus, sw_addr);
3785fad09c73SVivien Didelot 	if (err)
3786fad09c73SVivien Didelot 		goto free;
3787fad09c73SVivien Didelot 
3788fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3789fad09c73SVivien Didelot 	if (err)
3790fad09c73SVivien Didelot 		goto free;
3791fad09c73SVivien Didelot 
3792dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3793dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3794dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3795dc30c35bSAndrew Lunn 	if (err)
3796dc30c35bSAndrew Lunn 		goto free;
3797dc30c35bSAndrew Lunn 
3798e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3799e57e5e77SVivien Didelot 
3800fad09c73SVivien Didelot 	err = mv88e6xxx_mdio_register(chip, NULL);
3801fad09c73SVivien Didelot 	if (err)
3802fad09c73SVivien Didelot 		goto free;
3803fad09c73SVivien Didelot 
3804fad09c73SVivien Didelot 	*priv = chip;
3805fad09c73SVivien Didelot 
3806fad09c73SVivien Didelot 	return chip->info->name;
3807fad09c73SVivien Didelot free:
3808fad09c73SVivien Didelot 	devm_kfree(dsa_dev, chip);
3809fad09c73SVivien Didelot 
3810fad09c73SVivien Didelot 	return NULL;
3811fad09c73SVivien Didelot }
3812fad09c73SVivien Didelot 
38137df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
38147df8fbddSVivien Didelot 				      const struct switchdev_obj_port_mdb *mdb,
38157df8fbddSVivien Didelot 				      struct switchdev_trans *trans)
38167df8fbddSVivien Didelot {
38177df8fbddSVivien Didelot 	/* We don't need any dynamic resource from the kernel (yet),
38187df8fbddSVivien Didelot 	 * so skip the prepare phase.
38197df8fbddSVivien Didelot 	 */
38207df8fbddSVivien Didelot 
38217df8fbddSVivien Didelot 	return 0;
38227df8fbddSVivien Didelot }
38237df8fbddSVivien Didelot 
38247df8fbddSVivien Didelot static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
38257df8fbddSVivien Didelot 				   const struct switchdev_obj_port_mdb *mdb,
38267df8fbddSVivien Didelot 				   struct switchdev_trans *trans)
38277df8fbddSVivien Didelot {
382804bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
38297df8fbddSVivien Didelot 
38307df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
38317df8fbddSVivien Didelot 	if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
38327df8fbddSVivien Didelot 					 GLOBAL_ATU_DATA_STATE_MC_STATIC))
38337df8fbddSVivien Didelot 		netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
38347df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
38357df8fbddSVivien Didelot }
38367df8fbddSVivien Didelot 
38377df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
38387df8fbddSVivien Didelot 				  const struct switchdev_obj_port_mdb *mdb)
38397df8fbddSVivien Didelot {
384004bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
38417df8fbddSVivien Didelot 	int err;
38427df8fbddSVivien Didelot 
38437df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
38447df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
38457df8fbddSVivien Didelot 					   GLOBAL_ATU_DATA_STATE_UNUSED);
38467df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
38477df8fbddSVivien Didelot 
38487df8fbddSVivien Didelot 	return err;
38497df8fbddSVivien Didelot }
38507df8fbddSVivien Didelot 
38517df8fbddSVivien Didelot static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
38527df8fbddSVivien Didelot 				   struct switchdev_obj_port_mdb *mdb,
38537df8fbddSVivien Didelot 				   int (*cb)(struct switchdev_obj *obj))
38547df8fbddSVivien Didelot {
385504bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
38567df8fbddSVivien Didelot 	int err;
38577df8fbddSVivien Didelot 
38587df8fbddSVivien Didelot 	mutex_lock(&chip->reg_lock);
38597df8fbddSVivien Didelot 	err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
38607df8fbddSVivien Didelot 	mutex_unlock(&chip->reg_lock);
38617df8fbddSVivien Didelot 
38627df8fbddSVivien Didelot 	return err;
38637df8fbddSVivien Didelot }
38647df8fbddSVivien Didelot 
38659d490b4eSVivien Didelot static struct dsa_switch_ops mv88e6xxx_switch_ops = {
3866fad09c73SVivien Didelot 	.probe			= mv88e6xxx_drv_probe,
38677b314362SAndrew Lunn 	.get_tag_protocol	= mv88e6xxx_get_tag_protocol,
3868fad09c73SVivien Didelot 	.setup			= mv88e6xxx_setup,
3869fad09c73SVivien Didelot 	.set_addr		= mv88e6xxx_set_addr,
3870fad09c73SVivien Didelot 	.adjust_link		= mv88e6xxx_adjust_link,
3871fad09c73SVivien Didelot 	.get_strings		= mv88e6xxx_get_strings,
3872fad09c73SVivien Didelot 	.get_ethtool_stats	= mv88e6xxx_get_ethtool_stats,
3873fad09c73SVivien Didelot 	.get_sset_count		= mv88e6xxx_get_sset_count,
3874fad09c73SVivien Didelot 	.set_eee		= mv88e6xxx_set_eee,
3875fad09c73SVivien Didelot 	.get_eee		= mv88e6xxx_get_eee,
3876fad09c73SVivien Didelot #ifdef CONFIG_NET_DSA_HWMON
3877fad09c73SVivien Didelot 	.get_temp		= mv88e6xxx_get_temp,
3878fad09c73SVivien Didelot 	.get_temp_limit		= mv88e6xxx_get_temp_limit,
3879fad09c73SVivien Didelot 	.set_temp_limit		= mv88e6xxx_set_temp_limit,
3880fad09c73SVivien Didelot 	.get_temp_alarm		= mv88e6xxx_get_temp_alarm,
3881fad09c73SVivien Didelot #endif
3882fad09c73SVivien Didelot 	.get_eeprom_len		= mv88e6xxx_get_eeprom_len,
3883fad09c73SVivien Didelot 	.get_eeprom		= mv88e6xxx_get_eeprom,
3884fad09c73SVivien Didelot 	.set_eeprom		= mv88e6xxx_set_eeprom,
3885fad09c73SVivien Didelot 	.get_regs_len		= mv88e6xxx_get_regs_len,
3886fad09c73SVivien Didelot 	.get_regs		= mv88e6xxx_get_regs,
38872cfcd964SVivien Didelot 	.set_ageing_time	= mv88e6xxx_set_ageing_time,
3888fad09c73SVivien Didelot 	.port_bridge_join	= mv88e6xxx_port_bridge_join,
3889fad09c73SVivien Didelot 	.port_bridge_leave	= mv88e6xxx_port_bridge_leave,
3890fad09c73SVivien Didelot 	.port_stp_state_set	= mv88e6xxx_port_stp_state_set,
3891749efcb8SVivien Didelot 	.port_fast_age		= mv88e6xxx_port_fast_age,
3892fad09c73SVivien Didelot 	.port_vlan_filtering	= mv88e6xxx_port_vlan_filtering,
3893fad09c73SVivien Didelot 	.port_vlan_prepare	= mv88e6xxx_port_vlan_prepare,
3894fad09c73SVivien Didelot 	.port_vlan_add		= mv88e6xxx_port_vlan_add,
3895fad09c73SVivien Didelot 	.port_vlan_del		= mv88e6xxx_port_vlan_del,
3896fad09c73SVivien Didelot 	.port_vlan_dump		= mv88e6xxx_port_vlan_dump,
3897fad09c73SVivien Didelot 	.port_fdb_prepare       = mv88e6xxx_port_fdb_prepare,
3898fad09c73SVivien Didelot 	.port_fdb_add           = mv88e6xxx_port_fdb_add,
3899fad09c73SVivien Didelot 	.port_fdb_del           = mv88e6xxx_port_fdb_del,
3900fad09c73SVivien Didelot 	.port_fdb_dump          = mv88e6xxx_port_fdb_dump,
39017df8fbddSVivien Didelot 	.port_mdb_prepare       = mv88e6xxx_port_mdb_prepare,
39027df8fbddSVivien Didelot 	.port_mdb_add           = mv88e6xxx_port_mdb_add,
39037df8fbddSVivien Didelot 	.port_mdb_del           = mv88e6xxx_port_mdb_del,
39047df8fbddSVivien Didelot 	.port_mdb_dump          = mv88e6xxx_port_mdb_dump,
3905fad09c73SVivien Didelot };
3906fad09c73SVivien Didelot 
3907fad09c73SVivien Didelot static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
3908fad09c73SVivien Didelot 				     struct device_node *np)
3909fad09c73SVivien Didelot {
3910fad09c73SVivien Didelot 	struct device *dev = chip->dev;
3911fad09c73SVivien Didelot 	struct dsa_switch *ds;
3912fad09c73SVivien Didelot 
3913fad09c73SVivien Didelot 	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3914fad09c73SVivien Didelot 	if (!ds)
3915fad09c73SVivien Didelot 		return -ENOMEM;
3916fad09c73SVivien Didelot 
3917fad09c73SVivien Didelot 	ds->dev = dev;
3918fad09c73SVivien Didelot 	ds->priv = chip;
39199d490b4eSVivien Didelot 	ds->ops = &mv88e6xxx_switch_ops;
3920fad09c73SVivien Didelot 
3921fad09c73SVivien Didelot 	dev_set_drvdata(dev, ds);
3922fad09c73SVivien Didelot 
3923fad09c73SVivien Didelot 	return dsa_register_switch(ds, np);
3924fad09c73SVivien Didelot }
3925fad09c73SVivien Didelot 
3926fad09c73SVivien Didelot static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
3927fad09c73SVivien Didelot {
3928fad09c73SVivien Didelot 	dsa_unregister_switch(chip->ds);
3929fad09c73SVivien Didelot }
3930fad09c73SVivien Didelot 
3931fad09c73SVivien Didelot static int mv88e6xxx_probe(struct mdio_device *mdiodev)
3932fad09c73SVivien Didelot {
3933fad09c73SVivien Didelot 	struct device *dev = &mdiodev->dev;
3934fad09c73SVivien Didelot 	struct device_node *np = dev->of_node;
3935fad09c73SVivien Didelot 	const struct mv88e6xxx_info *compat_info;
3936fad09c73SVivien Didelot 	struct mv88e6xxx_chip *chip;
3937fad09c73SVivien Didelot 	u32 eeprom_len;
3938fad09c73SVivien Didelot 	int err;
3939fad09c73SVivien Didelot 
3940fad09c73SVivien Didelot 	compat_info = of_device_get_match_data(dev);
3941fad09c73SVivien Didelot 	if (!compat_info)
3942fad09c73SVivien Didelot 		return -EINVAL;
3943fad09c73SVivien Didelot 
3944fad09c73SVivien Didelot 	chip = mv88e6xxx_alloc_chip(dev);
3945fad09c73SVivien Didelot 	if (!chip)
3946fad09c73SVivien Didelot 		return -ENOMEM;
3947fad09c73SVivien Didelot 
3948fad09c73SVivien Didelot 	chip->info = compat_info;
3949fad09c73SVivien Didelot 
3950fad09c73SVivien Didelot 	err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
3951fad09c73SVivien Didelot 	if (err)
3952fad09c73SVivien Didelot 		return err;
3953fad09c73SVivien Didelot 
3954fad09c73SVivien Didelot 	err = mv88e6xxx_detect(chip);
3955fad09c73SVivien Didelot 	if (err)
3956fad09c73SVivien Didelot 		return err;
3957fad09c73SVivien Didelot 
3958e57e5e77SVivien Didelot 	mv88e6xxx_phy_init(chip);
3959e57e5e77SVivien Didelot 
3960fad09c73SVivien Didelot 	chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
3961fad09c73SVivien Didelot 	if (IS_ERR(chip->reset))
3962fad09c73SVivien Didelot 		return PTR_ERR(chip->reset);
3963fad09c73SVivien Didelot 
3964ee4dc2e7SVivien Didelot 	if (chip->info->ops->get_eeprom &&
3965fad09c73SVivien Didelot 	    !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3966fad09c73SVivien Didelot 		chip->eeprom_len = eeprom_len;
3967fad09c73SVivien Didelot 
3968dc30c35bSAndrew Lunn 	mutex_lock(&chip->reg_lock);
3969dc30c35bSAndrew Lunn 	err = mv88e6xxx_switch_reset(chip);
3970dc30c35bSAndrew Lunn 	mutex_unlock(&chip->reg_lock);
3971fad09c73SVivien Didelot 	if (err)
3972dc30c35bSAndrew Lunn 		goto out;
3973fad09c73SVivien Didelot 
3974dc30c35bSAndrew Lunn 	chip->irq = of_irq_get(np, 0);
3975dc30c35bSAndrew Lunn 	if (chip->irq == -EPROBE_DEFER) {
3976dc30c35bSAndrew Lunn 		err = chip->irq;
3977dc30c35bSAndrew Lunn 		goto out;
3978fad09c73SVivien Didelot 	}
3979fad09c73SVivien Didelot 
3980dc30c35bSAndrew Lunn 	if (chip->irq > 0) {
3981dc30c35bSAndrew Lunn 		/* Has to be performed before the MDIO bus is created,
3982dc30c35bSAndrew Lunn 		 * because the PHYs will link there interrupts to these
3983dc30c35bSAndrew Lunn 		 * interrupt controllers
3984dc30c35bSAndrew Lunn 		 */
3985dc30c35bSAndrew Lunn 		mutex_lock(&chip->reg_lock);
3986dc30c35bSAndrew Lunn 		err = mv88e6xxx_g1_irq_setup(chip);
3987dc30c35bSAndrew Lunn 		mutex_unlock(&chip->reg_lock);
3988dc30c35bSAndrew Lunn 
3989dc30c35bSAndrew Lunn 		if (err)
3990dc30c35bSAndrew Lunn 			goto out;
3991dc30c35bSAndrew Lunn 
3992dc30c35bSAndrew Lunn 		if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
3993dc30c35bSAndrew Lunn 			err = mv88e6xxx_g2_irq_setup(chip);
3994dc30c35bSAndrew Lunn 			if (err)
3995dc30c35bSAndrew Lunn 				goto out_g1_irq;
3996dc30c35bSAndrew Lunn 		}
3997dc30c35bSAndrew Lunn 	}
3998dc30c35bSAndrew Lunn 
3999dc30c35bSAndrew Lunn 	err = mv88e6xxx_mdio_register(chip, np);
4000dc30c35bSAndrew Lunn 	if (err)
4001dc30c35bSAndrew Lunn 		goto out_g2_irq;
4002dc30c35bSAndrew Lunn 
4003dc30c35bSAndrew Lunn 	err = mv88e6xxx_register_switch(chip, np);
4004dc30c35bSAndrew Lunn 	if (err)
4005dc30c35bSAndrew Lunn 		goto out_mdio;
4006dc30c35bSAndrew Lunn 
4007fad09c73SVivien Didelot 	return 0;
4008dc30c35bSAndrew Lunn 
4009dc30c35bSAndrew Lunn out_mdio:
4010dc30c35bSAndrew Lunn 	mv88e6xxx_mdio_unregister(chip);
4011dc30c35bSAndrew Lunn out_g2_irq:
4012dc30c35bSAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
4013dc30c35bSAndrew Lunn 		mv88e6xxx_g2_irq_free(chip);
4014dc30c35bSAndrew Lunn out_g1_irq:
4015dc30c35bSAndrew Lunn 	mv88e6xxx_g1_irq_free(chip);
4016dc30c35bSAndrew Lunn out:
4017dc30c35bSAndrew Lunn 	return err;
4018fad09c73SVivien Didelot }
4019fad09c73SVivien Didelot 
4020fad09c73SVivien Didelot static void mv88e6xxx_remove(struct mdio_device *mdiodev)
4021fad09c73SVivien Didelot {
4022fad09c73SVivien Didelot 	struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
402304bed143SVivien Didelot 	struct mv88e6xxx_chip *chip = ds->priv;
4024fad09c73SVivien Didelot 
4025930188ceSAndrew Lunn 	mv88e6xxx_phy_destroy(chip);
4026fad09c73SVivien Didelot 	mv88e6xxx_unregister_switch(chip);
4027fad09c73SVivien Didelot 	mv88e6xxx_mdio_unregister(chip);
4028dc30c35bSAndrew Lunn 
4029dc30c35bSAndrew Lunn 	if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
4030dc30c35bSAndrew Lunn 		mv88e6xxx_g2_irq_free(chip);
4031dc30c35bSAndrew Lunn 	mv88e6xxx_g1_irq_free(chip);
4032fad09c73SVivien Didelot }
4033fad09c73SVivien Didelot 
4034fad09c73SVivien Didelot static const struct of_device_id mv88e6xxx_of_match[] = {
4035fad09c73SVivien Didelot 	{
4036fad09c73SVivien Didelot 		.compatible = "marvell,mv88e6085",
4037fad09c73SVivien Didelot 		.data = &mv88e6xxx_table[MV88E6085],
4038fad09c73SVivien Didelot 	},
4039fad09c73SVivien Didelot 	{ /* sentinel */ },
4040fad09c73SVivien Didelot };
4041fad09c73SVivien Didelot 
4042fad09c73SVivien Didelot MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
4043fad09c73SVivien Didelot 
4044fad09c73SVivien Didelot static struct mdio_driver mv88e6xxx_driver = {
4045fad09c73SVivien Didelot 	.probe	= mv88e6xxx_probe,
4046fad09c73SVivien Didelot 	.remove = mv88e6xxx_remove,
4047fad09c73SVivien Didelot 	.mdiodrv.driver = {
4048fad09c73SVivien Didelot 		.name = "mv88e6085",
4049fad09c73SVivien Didelot 		.of_match_table = mv88e6xxx_of_match,
4050fad09c73SVivien Didelot 	},
4051fad09c73SVivien Didelot };
4052fad09c73SVivien Didelot 
4053fad09c73SVivien Didelot static int __init mv88e6xxx_init(void)
4054fad09c73SVivien Didelot {
40559d490b4eSVivien Didelot 	register_switch_driver(&mv88e6xxx_switch_ops);
4056fad09c73SVivien Didelot 	return mdio_driver_register(&mv88e6xxx_driver);
4057fad09c73SVivien Didelot }
4058fad09c73SVivien Didelot module_init(mv88e6xxx_init);
4059fad09c73SVivien Didelot 
4060fad09c73SVivien Didelot static void __exit mv88e6xxx_cleanup(void)
4061fad09c73SVivien Didelot {
4062fad09c73SVivien Didelot 	mdio_driver_unregister(&mv88e6xxx_driver);
40639d490b4eSVivien Didelot 	unregister_switch_driver(&mv88e6xxx_switch_ops);
4064fad09c73SVivien Didelot }
4065fad09c73SVivien Didelot module_exit(mv88e6xxx_cleanup);
4066fad09c73SVivien Didelot 
4067fad09c73SVivien Didelot MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4068fad09c73SVivien Didelot MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4069fad09c73SVivien Didelot MODULE_LICENSE("GPL");
4070