blob: ac032977b16c1c28396ea8d4ee77da8c9e659bb2 [file] [log] [blame]
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001/*
Vivien Didelot0d3cd4b2016-06-21 12:28:19 -04002 * Marvell 88e6xxx Ethernet switch single-chip support
3 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00004 * Copyright (c) 2008 Marvell Semiconductor
5 *
Vivien Didelotb8fee952015-08-13 12:52:19 -04006 * Copyright (c) 2015 CMC Electronics, Inc.
7 * Added support for VLAN Table Unit operations
8 *
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02009 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
10 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 */
16
Barry Grussling19b2f972013-01-08 16:05:54 +000017#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070018#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020019#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070020#include <linux/if_bridge.h>
Andrew Lunndc30c352016-10-16 19:56:49 +020021#include <linux/interrupt.h>
22#include <linux/irq.h>
23#include <linux/irqdomain.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000024#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000025#include <linux/list.h>
Andrew Lunn14c7b3c2016-05-10 23:27:21 +020026#include <linux/mdio.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000027#include <linux/module.h>
Vivien Didelotcaac8542016-06-20 13:14:09 -040028#include <linux/of_device.h>
Andrew Lunndc30c352016-10-16 19:56:49 +020029#include <linux/of_irq.h>
Andrew Lunnb516d452016-06-04 21:17:06 +020030#include <linux/of_mdio.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000031#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010032#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000033#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000034#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040035#include <net/switchdev.h>
Vivien Didelotec561272016-09-02 14:45:33 -040036
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000037#include "mv88e6xxx.h"
Vivien Didelota935c052016-09-29 12:21:53 -040038#include "global1.h"
Vivien Didelotec561272016-09-02 14:45:33 -040039#include "global2.h"
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000040
Vivien Didelotfad09c72016-06-21 12:28:20 -040041static void assert_reg_lock(struct mv88e6xxx_chip *chip)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040042{
Vivien Didelotfad09c72016-06-21 12:28:20 -040043 if (unlikely(!mutex_is_locked(&chip->reg_lock))) {
44 dev_err(chip->dev, "Switch registers lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040045 dump_stack();
46 }
47}
48
Vivien Didelot914b32f2016-06-20 13:14:11 -040049/* The switch ADDR[4:1] configuration pins define the chip SMI device address
50 * (ADDR[0] is always zero, thus only even SMI addresses can be strapped).
51 *
52 * When ADDR is all zero, the chip uses Single-chip Addressing Mode, assuming it
53 * is the only device connected to the SMI master. In this mode it responds to
54 * all 32 possible SMI addresses, and thus maps directly the internal devices.
55 *
56 * When ADDR is non-zero, the chip uses Multi-chip Addressing Mode, allowing
57 * multiple devices to share the SMI interface. In this mode it responds to only
58 * 2 registers, used to indirectly access the internal SMI devices.
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000059 */
Vivien Didelot914b32f2016-06-20 13:14:11 -040060
Vivien Didelotfad09c72016-06-21 12:28:20 -040061static int mv88e6xxx_smi_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040062 int addr, int reg, u16 *val)
63{
Vivien Didelotfad09c72016-06-21 12:28:20 -040064 if (!chip->smi_ops)
Vivien Didelot914b32f2016-06-20 13:14:11 -040065 return -EOPNOTSUPP;
66
Vivien Didelotfad09c72016-06-21 12:28:20 -040067 return chip->smi_ops->read(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040068}
69
Vivien Didelotfad09c72016-06-21 12:28:20 -040070static int mv88e6xxx_smi_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040071 int addr, int reg, u16 val)
72{
Vivien Didelotfad09c72016-06-21 12:28:20 -040073 if (!chip->smi_ops)
Vivien Didelot914b32f2016-06-20 13:14:11 -040074 return -EOPNOTSUPP;
75
Vivien Didelotfad09c72016-06-21 12:28:20 -040076 return chip->smi_ops->write(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040077}
78
Vivien Didelotfad09c72016-06-21 12:28:20 -040079static int mv88e6xxx_smi_single_chip_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040080 int addr, int reg, u16 *val)
81{
82 int ret;
83
Vivien Didelotfad09c72016-06-21 12:28:20 -040084 ret = mdiobus_read_nested(chip->bus, addr, reg);
Vivien Didelot914b32f2016-06-20 13:14:11 -040085 if (ret < 0)
86 return ret;
87
88 *val = ret & 0xffff;
89
90 return 0;
91}
92
Vivien Didelotfad09c72016-06-21 12:28:20 -040093static int mv88e6xxx_smi_single_chip_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -040094 int addr, int reg, u16 val)
95{
96 int ret;
97
Vivien Didelotfad09c72016-06-21 12:28:20 -040098 ret = mdiobus_write_nested(chip->bus, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -040099 if (ret < 0)
100 return ret;
101
102 return 0;
103}
104
Vivien Didelotc08026a2016-09-29 12:21:59 -0400105static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_single_chip_ops = {
Vivien Didelot914b32f2016-06-20 13:14:11 -0400106 .read = mv88e6xxx_smi_single_chip_read,
107 .write = mv88e6xxx_smi_single_chip_write,
108};
109
Vivien Didelotfad09c72016-06-21 12:28:20 -0400110static int mv88e6xxx_smi_multi_chip_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000111{
112 int ret;
113 int i;
114
115 for (i = 0; i < 16; i++) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400116 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000117 if (ret < 0)
118 return ret;
119
Andrew Lunncca8b132015-04-02 04:06:39 +0200120 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000121 return 0;
122 }
123
124 return -ETIMEDOUT;
125}
126
Vivien Didelotfad09c72016-06-21 12:28:20 -0400127static int mv88e6xxx_smi_multi_chip_read(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400128 int addr, int reg, u16 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000129{
130 int ret;
131
Barry Grussling3675c8d2013-01-08 16:05:53 +0000132 /* Wait for the bus to become free. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400133 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000134 if (ret < 0)
135 return ret;
136
Barry Grussling3675c8d2013-01-08 16:05:53 +0000137 /* Transmit the read command. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400138 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
Neil Armstrong6e899e62015-10-22 10:37:53 +0200139 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000140 if (ret < 0)
141 return ret;
142
Barry Grussling3675c8d2013-01-08 16:05:53 +0000143 /* Wait for the read command to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400144 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000145 if (ret < 0)
146 return ret;
147
Barry Grussling3675c8d2013-01-08 16:05:53 +0000148 /* Read the data. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400149 ret = mdiobus_read_nested(chip->bus, chip->sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000150 if (ret < 0)
151 return ret;
152
Vivien Didelot914b32f2016-06-20 13:14:11 -0400153 *val = ret & 0xffff;
154
155 return 0;
156}
157
Vivien Didelotfad09c72016-06-21 12:28:20 -0400158static int mv88e6xxx_smi_multi_chip_write(struct mv88e6xxx_chip *chip,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400159 int addr, int reg, u16 val)
160{
161 int ret;
162
163 /* Wait for the bus to become free. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400164 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400165 if (ret < 0)
166 return ret;
167
168 /* Transmit the data to write. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400169 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_DATA, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400170 if (ret < 0)
171 return ret;
172
173 /* Transmit the write command. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400174 ret = mdiobus_write_nested(chip->bus, chip->sw_addr, SMI_CMD,
Vivien Didelot914b32f2016-06-20 13:14:11 -0400175 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
176 if (ret < 0)
177 return ret;
178
179 /* Wait for the write command to complete. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400180 ret = mv88e6xxx_smi_multi_chip_wait(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400181 if (ret < 0)
182 return ret;
183
184 return 0;
185}
186
Vivien Didelotc08026a2016-09-29 12:21:59 -0400187static const struct mv88e6xxx_bus_ops mv88e6xxx_smi_multi_chip_ops = {
Vivien Didelot914b32f2016-06-20 13:14:11 -0400188 .read = mv88e6xxx_smi_multi_chip_read,
189 .write = mv88e6xxx_smi_multi_chip_write,
190};
191
Vivien Didelotec561272016-09-02 14:45:33 -0400192int mv88e6xxx_read(struct mv88e6xxx_chip *chip, int addr, int reg, u16 *val)
Vivien Didelot914b32f2016-06-20 13:14:11 -0400193{
194 int err;
195
Vivien Didelotfad09c72016-06-21 12:28:20 -0400196 assert_reg_lock(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400197
Vivien Didelotfad09c72016-06-21 12:28:20 -0400198 err = mv88e6xxx_smi_read(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400199 if (err)
200 return err;
201
Vivien Didelotfad09c72016-06-21 12:28:20 -0400202 dev_dbg(chip->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelot914b32f2016-06-20 13:14:11 -0400203 addr, reg, *val);
204
205 return 0;
206}
207
Vivien Didelotec561272016-09-02 14:45:33 -0400208int mv88e6xxx_write(struct mv88e6xxx_chip *chip, int addr, int reg, u16 val)
Vivien Didelot914b32f2016-06-20 13:14:11 -0400209{
210 int err;
211
Vivien Didelotfad09c72016-06-21 12:28:20 -0400212 assert_reg_lock(chip);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400213
Vivien Didelotfad09c72016-06-21 12:28:20 -0400214 err = mv88e6xxx_smi_write(chip, addr, reg, val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400215 if (err)
216 return err;
217
Vivien Didelotfad09c72016-06-21 12:28:20 -0400218 dev_dbg(chip->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelot914b32f2016-06-20 13:14:11 -0400219 addr, reg, val);
220
221 return 0;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000222}
223
Wei Yongjunb3f5bf62016-09-25 15:43:02 +0000224static int mv88e6xxx_port_read(struct mv88e6xxx_chip *chip, int port, int reg,
225 u16 *val)
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200226{
227 int addr = chip->info->port_base_addr + port;
228
229 return mv88e6xxx_read(chip, addr, reg, val);
230}
231
Wei Yongjunb3f5bf62016-09-25 15:43:02 +0000232static int mv88e6xxx_port_write(struct mv88e6xxx_chip *chip, int port, int reg,
233 u16 val)
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200234{
235 int addr = chip->info->port_base_addr + port;
236
237 return mv88e6xxx_write(chip, addr, reg, val);
238}
239
Vivien Didelote57e5e72016-08-15 17:19:00 -0400240static int mv88e6xxx_phy_read(struct mv88e6xxx_chip *chip, int phy,
241 int reg, u16 *val)
242{
243 int addr = phy; /* PHY devices addresses start at 0x0 */
244
Vivien Didelotb3469dd2016-09-29 12:22:00 -0400245 if (!chip->info->ops->phy_read)
Vivien Didelote57e5e72016-08-15 17:19:00 -0400246 return -EOPNOTSUPP;
247
Vivien Didelotb3469dd2016-09-29 12:22:00 -0400248 return chip->info->ops->phy_read(chip, addr, reg, val);
Vivien Didelote57e5e72016-08-15 17:19:00 -0400249}
250
251static int mv88e6xxx_phy_write(struct mv88e6xxx_chip *chip, int phy,
252 int reg, u16 val)
253{
254 int addr = phy; /* PHY devices addresses start at 0x0 */
255
Vivien Didelotb3469dd2016-09-29 12:22:00 -0400256 if (!chip->info->ops->phy_write)
Vivien Didelote57e5e72016-08-15 17:19:00 -0400257 return -EOPNOTSUPP;
258
Vivien Didelotb3469dd2016-09-29 12:22:00 -0400259 return chip->info->ops->phy_write(chip, addr, reg, val);
Vivien Didelote57e5e72016-08-15 17:19:00 -0400260}
261
Vivien Didelot09cb7df2016-08-15 17:19:01 -0400262static int mv88e6xxx_phy_page_get(struct mv88e6xxx_chip *chip, int phy, u8 page)
263{
264 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_PHY_PAGE))
265 return -EOPNOTSUPP;
266
267 return mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
268}
269
270static void mv88e6xxx_phy_page_put(struct mv88e6xxx_chip *chip, int phy)
271{
272 int err;
273
274 /* Restore PHY page Copper 0x0 for access via the registered MDIO bus */
275 err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, PHY_PAGE_COPPER);
276 if (unlikely(err)) {
277 dev_err(chip->dev, "failed to restore PHY %d page Copper (%d)\n",
278 phy, err);
279 }
280}
281
282static int mv88e6xxx_phy_page_read(struct mv88e6xxx_chip *chip, int phy,
283 u8 page, int reg, u16 *val)
284{
285 int err;
286
287 /* There is no paging for registers 22 */
288 if (reg == PHY_PAGE)
289 return -EINVAL;
290
291 err = mv88e6xxx_phy_page_get(chip, phy, page);
292 if (!err) {
293 err = mv88e6xxx_phy_read(chip, phy, reg, val);
294 mv88e6xxx_phy_page_put(chip, phy);
295 }
296
297 return err;
298}
299
300static int mv88e6xxx_phy_page_write(struct mv88e6xxx_chip *chip, int phy,
301 u8 page, int reg, u16 val)
302{
303 int err;
304
305 /* There is no paging for registers 22 */
306 if (reg == PHY_PAGE)
307 return -EINVAL;
308
309 err = mv88e6xxx_phy_page_get(chip, phy, page);
310 if (!err) {
311 err = mv88e6xxx_phy_write(chip, phy, PHY_PAGE, page);
312 mv88e6xxx_phy_page_put(chip, phy);
313 }
314
315 return err;
316}
317
318static int mv88e6xxx_serdes_read(struct mv88e6xxx_chip *chip, int reg, u16 *val)
319{
320 return mv88e6xxx_phy_page_read(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
321 reg, val);
322}
323
324static int mv88e6xxx_serdes_write(struct mv88e6xxx_chip *chip, int reg, u16 val)
325{
326 return mv88e6xxx_phy_page_write(chip, ADDR_SERDES, SERDES_PAGE_FIBER,
327 reg, val);
328}
329
Andrew Lunndc30c352016-10-16 19:56:49 +0200330static void mv88e6xxx_g1_irq_mask(struct irq_data *d)
331{
332 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
333 unsigned int n = d->hwirq;
334
335 chip->g1_irq.masked |= (1 << n);
336}
337
338static void mv88e6xxx_g1_irq_unmask(struct irq_data *d)
339{
340 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
341 unsigned int n = d->hwirq;
342
343 chip->g1_irq.masked &= ~(1 << n);
344}
345
346static irqreturn_t mv88e6xxx_g1_irq_thread_fn(int irq, void *dev_id)
347{
348 struct mv88e6xxx_chip *chip = dev_id;
349 unsigned int nhandled = 0;
350 unsigned int sub_irq;
351 unsigned int n;
352 u16 reg;
353 int err;
354
355 mutex_lock(&chip->reg_lock);
356 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
357 mutex_unlock(&chip->reg_lock);
358
359 if (err)
360 goto out;
361
362 for (n = 0; n < chip->g1_irq.nirqs; ++n) {
363 if (reg & (1 << n)) {
364 sub_irq = irq_find_mapping(chip->g1_irq.domain, n);
365 handle_nested_irq(sub_irq);
366 ++nhandled;
367 }
368 }
369out:
370 return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE);
371}
372
373static void mv88e6xxx_g1_irq_bus_lock(struct irq_data *d)
374{
375 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
376
377 mutex_lock(&chip->reg_lock);
378}
379
380static void mv88e6xxx_g1_irq_bus_sync_unlock(struct irq_data *d)
381{
382 struct mv88e6xxx_chip *chip = irq_data_get_irq_chip_data(d);
383 u16 mask = GENMASK(chip->g1_irq.nirqs, 0);
384 u16 reg;
385 int err;
386
387 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
388 if (err)
389 goto out;
390
391 reg &= ~mask;
392 reg |= (~chip->g1_irq.masked & mask);
393
394 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
395 if (err)
396 goto out;
397
398out:
399 mutex_unlock(&chip->reg_lock);
400}
401
402static struct irq_chip mv88e6xxx_g1_irq_chip = {
403 .name = "mv88e6xxx-g1",
404 .irq_mask = mv88e6xxx_g1_irq_mask,
405 .irq_unmask = mv88e6xxx_g1_irq_unmask,
406 .irq_bus_lock = mv88e6xxx_g1_irq_bus_lock,
407 .irq_bus_sync_unlock = mv88e6xxx_g1_irq_bus_sync_unlock,
408};
409
410static int mv88e6xxx_g1_irq_domain_map(struct irq_domain *d,
411 unsigned int irq,
412 irq_hw_number_t hwirq)
413{
414 struct mv88e6xxx_chip *chip = d->host_data;
415
416 irq_set_chip_data(irq, d->host_data);
417 irq_set_chip_and_handler(irq, &chip->g1_irq.chip, handle_level_irq);
418 irq_set_noprobe(irq);
419
420 return 0;
421}
422
423static const struct irq_domain_ops mv88e6xxx_g1_irq_domain_ops = {
424 .map = mv88e6xxx_g1_irq_domain_map,
425 .xlate = irq_domain_xlate_twocell,
426};
427
428static void mv88e6xxx_g1_irq_free(struct mv88e6xxx_chip *chip)
429{
430 int irq, virq;
431
432 for (irq = 0; irq < 16; irq++) {
433 virq = irq_find_mapping(chip->g2_irq.domain, irq);
434 irq_dispose_mapping(virq);
435 }
436
437 irq_domain_remove(chip->g2_irq.domain);
438}
439
440static int mv88e6xxx_g1_irq_setup(struct mv88e6xxx_chip *chip)
441{
442 int err, irq;
443 u16 reg;
444
445 chip->g1_irq.nirqs = chip->info->g1_irqs;
446 chip->g1_irq.domain = irq_domain_add_simple(
447 NULL, chip->g1_irq.nirqs, 0,
448 &mv88e6xxx_g1_irq_domain_ops, chip);
449 if (!chip->g1_irq.domain)
450 return -ENOMEM;
451
452 for (irq = 0; irq < chip->g1_irq.nirqs; irq++)
453 irq_create_mapping(chip->g1_irq.domain, irq);
454
455 chip->g1_irq.chip = mv88e6xxx_g1_irq_chip;
456 chip->g1_irq.masked = ~0;
457
458 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
459 if (err)
460 goto out;
461
462 reg &= ~GENMASK(chip->g1_irq.nirqs, 0);
463
464 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
465 if (err)
466 goto out;
467
468 /* Reading the interrupt status clears (most of) them */
469 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &reg);
470 if (err)
471 goto out;
472
473 err = request_threaded_irq(chip->irq, NULL,
474 mv88e6xxx_g1_irq_thread_fn,
475 IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
476 dev_name(chip->dev), chip);
477 if (err)
478 goto out;
479
480 return 0;
481
482out:
483 mv88e6xxx_g1_irq_free(chip);
484
485 return err;
486}
487
Vivien Didelotec561272016-09-02 14:45:33 -0400488int mv88e6xxx_wait(struct mv88e6xxx_chip *chip, int addr, int reg, u16 mask)
Vivien Didelot2d79af62016-08-15 17:18:57 -0400489{
Andrew Lunn6441e6692016-08-19 00:01:55 +0200490 int i;
Vivien Didelot2d79af62016-08-15 17:18:57 -0400491
Andrew Lunn6441e6692016-08-19 00:01:55 +0200492 for (i = 0; i < 16; i++) {
Vivien Didelot2d79af62016-08-15 17:18:57 -0400493 u16 val;
494 int err;
495
496 err = mv88e6xxx_read(chip, addr, reg, &val);
497 if (err)
498 return err;
499
500 if (!(val & mask))
501 return 0;
502
503 usleep_range(1000, 2000);
504 }
505
Andrew Lunn30853552016-08-19 00:01:57 +0200506 dev_err(chip->dev, "Timeout while waiting for switch\n");
Vivien Didelot2d79af62016-08-15 17:18:57 -0400507 return -ETIMEDOUT;
508}
509
Vivien Didelotf22ab642016-07-18 20:45:31 -0400510/* Indirect write to single pointer-data register with an Update bit */
Vivien Didelotec561272016-09-02 14:45:33 -0400511int mv88e6xxx_update(struct mv88e6xxx_chip *chip, int addr, int reg, u16 update)
Vivien Didelotf22ab642016-07-18 20:45:31 -0400512{
513 u16 val;
Andrew Lunn0f02b4f2016-08-19 00:01:56 +0200514 int err;
Vivien Didelotf22ab642016-07-18 20:45:31 -0400515
516 /* Wait until the previous operation is completed */
Andrew Lunn0f02b4f2016-08-19 00:01:56 +0200517 err = mv88e6xxx_wait(chip, addr, reg, BIT(15));
518 if (err)
519 return err;
Vivien Didelotf22ab642016-07-18 20:45:31 -0400520
521 /* Set the Update bit to trigger a write operation */
522 val = BIT(15) | update;
523
524 return mv88e6xxx_write(chip, addr, reg, val);
525}
526
Vivien Didelota935c052016-09-29 12:21:53 -0400527static int mv88e6xxx_ppu_disable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000528{
Vivien Didelot914b32f2016-06-20 13:14:11 -0400529 u16 val;
Vivien Didelota935c052016-09-29 12:21:53 -0400530 int i, err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000531
Vivien Didelota935c052016-09-29 12:21:53 -0400532 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
Vivien Didelot914b32f2016-06-20 13:14:11 -0400533 if (err)
534 return err;
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400535
Vivien Didelota935c052016-09-29 12:21:53 -0400536 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
537 val & ~GLOBAL_CONTROL_PPU_ENABLE);
538 if (err)
539 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000540
Andrew Lunn6441e6692016-08-19 00:01:55 +0200541 for (i = 0; i < 16; i++) {
Vivien Didelota935c052016-09-29 12:21:53 -0400542 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
543 if (err)
544 return err;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200545
Barry Grussling19b2f972013-01-08 16:05:54 +0000546 usleep_range(1000, 2000);
Vivien Didelota935c052016-09-29 12:21:53 -0400547 if ((val & GLOBAL_STATUS_PPU_MASK) != GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000548 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000549 }
550
551 return -ETIMEDOUT;
552}
553
Vivien Didelotfad09c72016-06-21 12:28:20 -0400554static int mv88e6xxx_ppu_enable(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000555{
Vivien Didelota935c052016-09-29 12:21:53 -0400556 u16 val;
557 int i, err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000558
Vivien Didelota935c052016-09-29 12:21:53 -0400559 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &val);
560 if (err)
561 return err;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200562
Vivien Didelota935c052016-09-29 12:21:53 -0400563 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL,
564 val | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200565 if (err)
566 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000567
Andrew Lunn6441e6692016-08-19 00:01:55 +0200568 for (i = 0; i < 16; i++) {
Vivien Didelota935c052016-09-29 12:21:53 -0400569 err = mv88e6xxx_g1_read(chip, GLOBAL_STATUS, &val);
570 if (err)
571 return err;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200572
Barry Grussling19b2f972013-01-08 16:05:54 +0000573 usleep_range(1000, 2000);
Vivien Didelota935c052016-09-29 12:21:53 -0400574 if ((val & GLOBAL_STATUS_PPU_MASK) == GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000575 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000576 }
577
578 return -ETIMEDOUT;
579}
580
581static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
582{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400583 struct mv88e6xxx_chip *chip;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000584
Vivien Didelotfad09c72016-06-21 12:28:20 -0400585 chip = container_of(ugly, struct mv88e6xxx_chip, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200586
Vivien Didelotfad09c72016-06-21 12:28:20 -0400587 mutex_lock(&chip->reg_lock);
Vivien Didelot762eb672016-06-04 21:16:54 +0200588
Vivien Didelotfad09c72016-06-21 12:28:20 -0400589 if (mutex_trylock(&chip->ppu_mutex)) {
590 if (mv88e6xxx_ppu_enable(chip) == 0)
591 chip->ppu_disabled = 0;
592 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000593 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200594
Vivien Didelotfad09c72016-06-21 12:28:20 -0400595 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000596}
597
598static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
599{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400600 struct mv88e6xxx_chip *chip = (void *)_ps;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000601
Vivien Didelotfad09c72016-06-21 12:28:20 -0400602 schedule_work(&chip->ppu_work);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000603}
604
Vivien Didelotfad09c72016-06-21 12:28:20 -0400605static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000606{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000607 int ret;
608
Vivien Didelotfad09c72016-06-21 12:28:20 -0400609 mutex_lock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000610
Barry Grussling3675c8d2013-01-08 16:05:53 +0000611 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000612 * we can access the PHY registers. If it was already
613 * disabled, cancel the timer that is going to re-enable
614 * it.
615 */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400616 if (!chip->ppu_disabled) {
617 ret = mv88e6xxx_ppu_disable(chip);
Barry Grussling85686582013-01-08 16:05:56 +0000618 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400619 mutex_unlock(&chip->ppu_mutex);
Barry Grussling85686582013-01-08 16:05:56 +0000620 return ret;
621 }
Vivien Didelotfad09c72016-06-21 12:28:20 -0400622 chip->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000623 } else {
Vivien Didelotfad09c72016-06-21 12:28:20 -0400624 del_timer(&chip->ppu_timer);
Barry Grussling85686582013-01-08 16:05:56 +0000625 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000626 }
627
628 return ret;
629}
630
Vivien Didelotfad09c72016-06-21 12:28:20 -0400631static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000632{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000633 /* Schedule a timer to re-enable the PHY polling unit. */
Vivien Didelotfad09c72016-06-21 12:28:20 -0400634 mod_timer(&chip->ppu_timer, jiffies + msecs_to_jiffies(10));
635 mutex_unlock(&chip->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000636}
637
Vivien Didelotfad09c72016-06-21 12:28:20 -0400638static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_chip *chip)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000639{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400640 mutex_init(&chip->ppu_mutex);
641 INIT_WORK(&chip->ppu_work, mv88e6xxx_ppu_reenable_work);
642 init_timer(&chip->ppu_timer);
643 chip->ppu_timer.data = (unsigned long)chip;
644 chip->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000645}
646
Andrew Lunn930188c2016-08-22 16:01:03 +0200647static void mv88e6xxx_ppu_state_destroy(struct mv88e6xxx_chip *chip)
648{
649 del_timer_sync(&chip->ppu_timer);
650}
651
Vivien Didelote57e5e72016-08-15 17:19:00 -0400652static int mv88e6xxx_phy_ppu_read(struct mv88e6xxx_chip *chip, int addr,
653 int reg, u16 *val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000654{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400655 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000656
Vivien Didelote57e5e72016-08-15 17:19:00 -0400657 err = mv88e6xxx_ppu_access_get(chip);
658 if (!err) {
659 err = mv88e6xxx_read(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400660 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000661 }
662
Vivien Didelote57e5e72016-08-15 17:19:00 -0400663 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000664}
665
Vivien Didelote57e5e72016-08-15 17:19:00 -0400666static int mv88e6xxx_phy_ppu_write(struct mv88e6xxx_chip *chip, int addr,
667 int reg, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000668{
Vivien Didelote57e5e72016-08-15 17:19:00 -0400669 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000670
Vivien Didelote57e5e72016-08-15 17:19:00 -0400671 err = mv88e6xxx_ppu_access_get(chip);
672 if (!err) {
673 err = mv88e6xxx_write(chip, addr, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -0400674 mv88e6xxx_ppu_access_put(chip);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000675 }
676
Vivien Didelote57e5e72016-08-15 17:19:00 -0400677 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000678}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000679
Vivien Didelotfad09c72016-06-21 12:28:20 -0400680static bool mv88e6xxx_6065_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200681{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400682 return chip->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200683}
684
Vivien Didelotfad09c72016-06-21 12:28:20 -0400685static bool mv88e6xxx_6095_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200686{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400687 return chip->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200688}
689
Vivien Didelotfad09c72016-06-21 12:28:20 -0400690static bool mv88e6xxx_6097_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200691{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400692 return chip->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200693}
694
Vivien Didelotfad09c72016-06-21 12:28:20 -0400695static bool mv88e6xxx_6165_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200696{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400697 return chip->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200698}
699
Vivien Didelotfad09c72016-06-21 12:28:20 -0400700static bool mv88e6xxx_6185_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200701{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400702 return chip->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200703}
704
Vivien Didelotfad09c72016-06-21 12:28:20 -0400705static bool mv88e6xxx_6320_family(struct mv88e6xxx_chip *chip)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700706{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400707 return chip->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700708}
709
Vivien Didelotfad09c72016-06-21 12:28:20 -0400710static bool mv88e6xxx_6351_family(struct mv88e6xxx_chip *chip)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200711{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400712 return chip->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200713}
714
Vivien Didelotfad09c72016-06-21 12:28:20 -0400715static bool mv88e6xxx_6352_family(struct mv88e6xxx_chip *chip)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200716{
Vivien Didelotfad09c72016-06-21 12:28:20 -0400717 return chip->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200718}
719
Andrew Lunndea87022015-08-31 15:56:47 +0200720/* We expect the switch to perform auto negotiation if there is a real
721 * phy. However, in the case of a fixed link phy, we force the port
722 * settings from the fixed link settings.
723 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400724static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
725 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200726{
Vivien Didelot04bed142016-08-31 18:06:13 -0400727 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200728 u16 reg;
729 int err;
Andrew Lunndea87022015-08-31 15:56:47 +0200730
731 if (!phy_is_pseudo_fixed_link(phydev))
732 return;
733
Vivien Didelotfad09c72016-06-21 12:28:20 -0400734 mutex_lock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200735
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200736 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
737 if (err)
Andrew Lunndea87022015-08-31 15:56:47 +0200738 goto out;
739
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200740 reg &= ~(PORT_PCS_CTRL_LINK_UP |
741 PORT_PCS_CTRL_FORCE_LINK |
742 PORT_PCS_CTRL_DUPLEX_FULL |
743 PORT_PCS_CTRL_FORCE_DUPLEX |
744 PORT_PCS_CTRL_UNFORCED);
Andrew Lunndea87022015-08-31 15:56:47 +0200745
746 reg |= PORT_PCS_CTRL_FORCE_LINK;
747 if (phydev->link)
Vivien Didelot57d32312016-06-20 13:13:58 -0400748 reg |= PORT_PCS_CTRL_LINK_UP;
Andrew Lunndea87022015-08-31 15:56:47 +0200749
Vivien Didelotfad09c72016-06-21 12:28:20 -0400750 if (mv88e6xxx_6065_family(chip) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200751 goto out;
752
753 switch (phydev->speed) {
754 case SPEED_1000:
755 reg |= PORT_PCS_CTRL_1000;
756 break;
757 case SPEED_100:
758 reg |= PORT_PCS_CTRL_100;
759 break;
760 case SPEED_10:
761 reg |= PORT_PCS_CTRL_10;
762 break;
763 default:
764 pr_info("Unknown speed");
765 goto out;
766 }
767
768 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
769 if (phydev->duplex == DUPLEX_FULL)
770 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
771
Vivien Didelotfad09c72016-06-21 12:28:20 -0400772 if ((mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip)) &&
Vivien Didelot370b4ff2016-09-29 12:21:57 -0400773 (port >= mv88e6xxx_num_ports(chip) - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200774 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
775 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
776 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
777 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
778 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
779 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
780 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
781 }
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200782 mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200783
784out:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400785 mutex_unlock(&chip->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200786}
787
Vivien Didelotfad09c72016-06-21 12:28:20 -0400788static int _mv88e6xxx_stats_wait(struct mv88e6xxx_chip *chip)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000789{
Vivien Didelota935c052016-09-29 12:21:53 -0400790 u16 val;
791 int i, err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000792
793 for (i = 0; i < 10; i++) {
Vivien Didelota935c052016-09-29 12:21:53 -0400794 err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_OP, &val);
795 if ((val & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000796 return 0;
797 }
798
799 return -ETIMEDOUT;
800}
801
Vivien Didelotfad09c72016-06-21 12:28:20 -0400802static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_chip *chip, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000803{
Vivien Didelota935c052016-09-29 12:21:53 -0400804 int err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000805
Vivien Didelotfad09c72016-06-21 12:28:20 -0400806 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200807 port = (port + 1) << 5;
808
Barry Grussling3675c8d2013-01-08 16:05:53 +0000809 /* Snapshot the hardware statistics counters for this port. */
Vivien Didelota935c052016-09-29 12:21:53 -0400810 err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
811 GLOBAL_STATS_OP_CAPTURE_PORT |
812 GLOBAL_STATS_OP_HIST_RX_TX | port);
813 if (err)
814 return err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000815
Barry Grussling3675c8d2013-01-08 16:05:53 +0000816 /* Wait for the snapshotting to complete. */
Vivien Didelota935c052016-09-29 12:21:53 -0400817 return _mv88e6xxx_stats_wait(chip);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000818}
819
Vivien Didelotfad09c72016-06-21 12:28:20 -0400820static void _mv88e6xxx_stats_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -0400821 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000822{
Vivien Didelota935c052016-09-29 12:21:53 -0400823 u32 value;
824 u16 reg;
825 int err;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000826
827 *val = 0;
828
Vivien Didelota935c052016-09-29 12:21:53 -0400829 err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
830 GLOBAL_STATS_OP_READ_CAPTURED |
831 GLOBAL_STATS_OP_HIST_RX_TX | stat);
832 if (err)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000833 return;
834
Vivien Didelota935c052016-09-29 12:21:53 -0400835 err = _mv88e6xxx_stats_wait(chip);
836 if (err)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000837 return;
838
Vivien Didelota935c052016-09-29 12:21:53 -0400839 err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_32, &reg);
840 if (err)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000841 return;
842
Vivien Didelota935c052016-09-29 12:21:53 -0400843 value = reg << 16;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000844
Vivien Didelota935c052016-09-29 12:21:53 -0400845 err = mv88e6xxx_g1_read(chip, GLOBAL_STATS_COUNTER_01, &reg);
846 if (err)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000847 return;
848
Vivien Didelota935c052016-09-29 12:21:53 -0400849 *val = value | reg;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000850}
851
Andrew Lunne413e7e2015-04-02 04:06:38 +0200852static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100853 { "in_good_octets", 8, 0x00, BANK0, },
854 { "in_bad_octets", 4, 0x02, BANK0, },
855 { "in_unicast", 4, 0x04, BANK0, },
856 { "in_broadcasts", 4, 0x06, BANK0, },
857 { "in_multicasts", 4, 0x07, BANK0, },
858 { "in_pause", 4, 0x16, BANK0, },
859 { "in_undersize", 4, 0x18, BANK0, },
860 { "in_fragments", 4, 0x19, BANK0, },
861 { "in_oversize", 4, 0x1a, BANK0, },
862 { "in_jabber", 4, 0x1b, BANK0, },
863 { "in_rx_error", 4, 0x1c, BANK0, },
864 { "in_fcs_error", 4, 0x1d, BANK0, },
865 { "out_octets", 8, 0x0e, BANK0, },
866 { "out_unicast", 4, 0x10, BANK0, },
867 { "out_broadcasts", 4, 0x13, BANK0, },
868 { "out_multicasts", 4, 0x12, BANK0, },
869 { "out_pause", 4, 0x15, BANK0, },
870 { "excessive", 4, 0x11, BANK0, },
871 { "collisions", 4, 0x1e, BANK0, },
872 { "deferred", 4, 0x05, BANK0, },
873 { "single", 4, 0x14, BANK0, },
874 { "multiple", 4, 0x17, BANK0, },
875 { "out_fcs_error", 4, 0x03, BANK0, },
876 { "late", 4, 0x1f, BANK0, },
877 { "hist_64bytes", 4, 0x08, BANK0, },
878 { "hist_65_127bytes", 4, 0x09, BANK0, },
879 { "hist_128_255bytes", 4, 0x0a, BANK0, },
880 { "hist_256_511bytes", 4, 0x0b, BANK0, },
881 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
882 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
883 { "sw_in_discards", 4, 0x10, PORT, },
884 { "sw_in_filtered", 2, 0x12, PORT, },
885 { "sw_out_filtered", 2, 0x13, PORT, },
886 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
887 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
888 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
889 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
890 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
891 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
892 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
893 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
894 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
895 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
896 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
897 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
898 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
899 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
900 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
901 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
902 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
903 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
904 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
905 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
906 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
907 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
908 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
909 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
910 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
911 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200912};
913
Vivien Didelotfad09c72016-06-21 12:28:20 -0400914static bool mv88e6xxx_has_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100915 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200916{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100917 switch (stat->type) {
918 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200919 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100920 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400921 return mv88e6xxx_6320_family(chip);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100922 case PORT:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400923 return mv88e6xxx_6095_family(chip) ||
924 mv88e6xxx_6185_family(chip) ||
925 mv88e6xxx_6097_family(chip) ||
926 mv88e6xxx_6165_family(chip) ||
927 mv88e6xxx_6351_family(chip) ||
928 mv88e6xxx_6352_family(chip);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200929 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100930 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000931}
932
Vivien Didelotfad09c72016-06-21 12:28:20 -0400933static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_chip *chip,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100934 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200935 int port)
936{
Andrew Lunn80c46272015-06-20 18:42:30 +0200937 u32 low;
938 u32 high = 0;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200939 int err;
940 u16 reg;
Andrew Lunn80c46272015-06-20 18:42:30 +0200941 u64 value;
942
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100943 switch (s->type) {
944 case PORT:
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200945 err = mv88e6xxx_port_read(chip, port, s->reg, &reg);
946 if (err)
Andrew Lunn80c46272015-06-20 18:42:30 +0200947 return UINT64_MAX;
948
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200949 low = reg;
Andrew Lunn80c46272015-06-20 18:42:30 +0200950 if (s->sizeof_stat == 4) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200951 err = mv88e6xxx_port_read(chip, port, s->reg + 1, &reg);
952 if (err)
Andrew Lunn80c46272015-06-20 18:42:30 +0200953 return UINT64_MAX;
Andrew Lunn0e7b9922016-09-21 01:40:31 +0200954 high = reg;
Andrew Lunn80c46272015-06-20 18:42:30 +0200955 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100956 break;
957 case BANK0:
958 case BANK1:
Vivien Didelotfad09c72016-06-21 12:28:20 -0400959 _mv88e6xxx_stats_read(chip, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200960 if (s->sizeof_stat == 8)
Vivien Didelotfad09c72016-06-21 12:28:20 -0400961 _mv88e6xxx_stats_read(chip, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200962 }
963 value = (((u64)high) << 16) | low;
964 return value;
965}
966
Vivien Didelotf81ec902016-05-09 13:22:58 -0400967static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
968 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100969{
Vivien Didelot04bed142016-08-31 18:06:13 -0400970 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100971 struct mv88e6xxx_hw_stat *stat;
972 int i, j;
973
974 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
975 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400976 if (mv88e6xxx_has_stat(chip, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100977 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
978 ETH_GSTRING_LEN);
979 j++;
980 }
981 }
982}
983
Vivien Didelotf81ec902016-05-09 13:22:58 -0400984static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100985{
Vivien Didelot04bed142016-08-31 18:06:13 -0400986 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100987 struct mv88e6xxx_hw_stat *stat;
988 int i, j;
989
990 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
991 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -0400992 if (mv88e6xxx_has_stat(chip, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100993 j++;
994 }
995 return j;
996}
997
Vivien Didelotf81ec902016-05-09 13:22:58 -0400998static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
999 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001000{
Vivien Didelot04bed142016-08-31 18:06:13 -04001001 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +01001002 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001003 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +01001004 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001005
Vivien Didelotfad09c72016-06-21 12:28:20 -04001006 mutex_lock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001007
Vivien Didelotfad09c72016-06-21 12:28:20 -04001008 ret = _mv88e6xxx_stats_snapshot(chip, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001009 if (ret < 0) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001010 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001011 return;
1012 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +01001013 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
1014 stat = &mv88e6xxx_hw_stats[i];
Vivien Didelotfad09c72016-06-21 12:28:20 -04001015 if (mv88e6xxx_has_stat(chip, stat)) {
1016 data[j] = _mv88e6xxx_get_ethtool_stat(chip, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +01001017 j++;
1018 }
1019 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001020
Vivien Didelotfad09c72016-06-21 12:28:20 -04001021 mutex_unlock(&chip->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001022}
Ben Hutchings98e67302011-11-25 14:36:19 +00001023
Vivien Didelotf81ec902016-05-09 13:22:58 -04001024static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001025{
1026 return 32 * sizeof(u16);
1027}
1028
Vivien Didelotf81ec902016-05-09 13:22:58 -04001029static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
1030 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001031{
Vivien Didelot04bed142016-08-31 18:06:13 -04001032 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001033 int err;
1034 u16 reg;
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001035 u16 *p = _p;
1036 int i;
1037
1038 regs->version = 0;
1039
1040 memset(p, 0xff, 32 * sizeof(u16));
1041
Vivien Didelotfad09c72016-06-21 12:28:20 -04001042 mutex_lock(&chip->reg_lock);
Vivien Didelot23062512016-05-09 13:22:45 -04001043
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001044 for (i = 0; i < 32; i++) {
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001045
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001046 err = mv88e6xxx_port_read(chip, port, i, &reg);
1047 if (!err)
1048 p[i] = reg;
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001049 }
Vivien Didelot23062512016-05-09 13:22:45 -04001050
Vivien Didelotfad09c72016-06-21 12:28:20 -04001051 mutex_unlock(&chip->reg_lock);
Guenter Roecka1ab91f2014-10-29 10:45:05 -07001052}
1053
Vivien Didelotfad09c72016-06-21 12:28:20 -04001054static int _mv88e6xxx_atu_wait(struct mv88e6xxx_chip *chip)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001055{
Vivien Didelota935c052016-09-29 12:21:53 -04001056 return mv88e6xxx_g1_wait(chip, GLOBAL_ATU_OP, GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001057}
1058
Vivien Didelotf81ec902016-05-09 13:22:58 -04001059static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1060 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001061{
Vivien Didelot04bed142016-08-31 18:06:13 -04001062 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot9c938292016-08-15 17:19:02 -04001063 u16 reg;
1064 int err;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001065
Vivien Didelotfad09c72016-06-21 12:28:20 -04001066 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001067 return -EOPNOTSUPP;
1068
Vivien Didelotfad09c72016-06-21 12:28:20 -04001069 mutex_lock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001070
Vivien Didelot9c938292016-08-15 17:19:02 -04001071 err = mv88e6xxx_phy_read(chip, port, 16, &reg);
1072 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001073 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001074
1075 e->eee_enabled = !!(reg & 0x0200);
1076 e->tx_lpi_enabled = !!(reg & 0x0100);
1077
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001078 err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
Vivien Didelot9c938292016-08-15 17:19:02 -04001079 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001080 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001081
Andrew Lunncca8b132015-04-02 04:06:39 +02001082 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001083out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001084 mutex_unlock(&chip->reg_lock);
Vivien Didelot9c938292016-08-15 17:19:02 -04001085
1086 return err;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001087}
1088
Vivien Didelotf81ec902016-05-09 13:22:58 -04001089static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1090 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001091{
Vivien Didelot04bed142016-08-31 18:06:13 -04001092 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot9c938292016-08-15 17:19:02 -04001093 u16 reg;
1094 int err;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001095
Vivien Didelotfad09c72016-06-21 12:28:20 -04001096 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_EEE))
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001097 return -EOPNOTSUPP;
1098
Vivien Didelotfad09c72016-06-21 12:28:20 -04001099 mutex_lock(&chip->reg_lock);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001100
Vivien Didelot9c938292016-08-15 17:19:02 -04001101 err = mv88e6xxx_phy_read(chip, port, 16, &reg);
1102 if (err)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001103 goto out;
1104
Vivien Didelot9c938292016-08-15 17:19:02 -04001105 reg &= ~0x0300;
Andrew Lunn2f40c692015-04-02 04:06:37 +02001106 if (e->eee_enabled)
1107 reg |= 0x0200;
1108 if (e->tx_lpi_enabled)
1109 reg |= 0x0100;
1110
Vivien Didelot9c938292016-08-15 17:19:02 -04001111 err = mv88e6xxx_phy_write(chip, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001112out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001113 mutex_unlock(&chip->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001114
Vivien Didelot9c938292016-08-15 17:19:02 -04001115 return err;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001116}
1117
Vivien Didelotfad09c72016-06-21 12:28:20 -04001118static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_chip *chip, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001119{
Vivien Didelota935c052016-09-29 12:21:53 -04001120 u16 val;
1121 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001122
Vivien Didelot6dc10bb2016-09-29 12:21:55 -04001123 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_ATU_FID)) {
Vivien Didelota935c052016-09-29 12:21:53 -04001124 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_FID, fid);
1125 if (err)
1126 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001127 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001128 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Vivien Didelota935c052016-09-29 12:21:53 -04001129 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
1130 if (err)
1131 return err;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001132
Vivien Didelota935c052016-09-29 12:21:53 -04001133 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
1134 (val & 0xfff) | ((fid << 8) & 0xf000));
1135 if (err)
1136 return err;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001137
1138 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1139 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001140 }
1141
Vivien Didelota935c052016-09-29 12:21:53 -04001142 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_OP, cmd);
1143 if (err)
1144 return err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001145
Vivien Didelotfad09c72016-06-21 12:28:20 -04001146 return _mv88e6xxx_atu_wait(chip);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001147}
1148
Vivien Didelotfad09c72016-06-21 12:28:20 -04001149static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelot37705b72015-09-04 14:34:11 -04001150 struct mv88e6xxx_atu_entry *entry)
1151{
1152 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1153
1154 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1155 unsigned int mask, shift;
1156
1157 if (entry->trunk) {
1158 data |= GLOBAL_ATU_DATA_TRUNK;
1159 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1160 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1161 } else {
1162 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1163 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1164 }
1165
1166 data |= (entry->portv_trunkid << shift) & mask;
1167 }
1168
Vivien Didelota935c052016-09-29 12:21:53 -04001169 return mv88e6xxx_g1_write(chip, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001170}
1171
Vivien Didelotfad09c72016-06-21 12:28:20 -04001172static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_chip *chip,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001173 struct mv88e6xxx_atu_entry *entry,
1174 bool static_too)
1175{
1176 int op;
1177 int err;
1178
Vivien Didelotfad09c72016-06-21 12:28:20 -04001179 err = _mv88e6xxx_atu_wait(chip);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001180 if (err)
1181 return err;
1182
Vivien Didelotfad09c72016-06-21 12:28:20 -04001183 err = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001184 if (err)
1185 return err;
1186
1187 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001188 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1189 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1190 } else {
1191 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1192 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1193 }
1194
Vivien Didelotfad09c72016-06-21 12:28:20 -04001195 return _mv88e6xxx_atu_cmd(chip, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001196}
1197
Vivien Didelotfad09c72016-06-21 12:28:20 -04001198static int _mv88e6xxx_atu_flush(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001199 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001200{
1201 struct mv88e6xxx_atu_entry entry = {
1202 .fid = fid,
1203 .state = 0, /* EntryState bits must be 0 */
1204 };
1205
Vivien Didelotfad09c72016-06-21 12:28:20 -04001206 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001207}
1208
Vivien Didelotfad09c72016-06-21 12:28:20 -04001209static int _mv88e6xxx_atu_move(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001210 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001211{
1212 struct mv88e6xxx_atu_entry entry = {
1213 .trunk = false,
1214 .fid = fid,
1215 };
1216
1217 /* EntryState bits must be 0xF */
1218 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1219
1220 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1221 entry.portv_trunkid = (to_port & 0x0f) << 4;
1222 entry.portv_trunkid |= from_port & 0x0f;
1223
Vivien Didelotfad09c72016-06-21 12:28:20 -04001224 return _mv88e6xxx_atu_flush_move(chip, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001225}
1226
Vivien Didelotfad09c72016-06-21 12:28:20 -04001227static int _mv88e6xxx_atu_remove(struct mv88e6xxx_chip *chip, u16 fid,
Andrew Lunn158bc062016-04-28 21:24:06 -04001228 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001229{
1230 /* Destination port 0xF means remove the entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001231 return _mv88e6xxx_atu_move(chip, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001232}
1233
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001234static const char * const mv88e6xxx_port_state_names[] = {
1235 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1236 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1237 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1238 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1239};
1240
Vivien Didelotfad09c72016-06-21 12:28:20 -04001241static int _mv88e6xxx_port_state(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001242 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001243{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001244 struct dsa_switch *ds = chip->ds;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001245 u16 reg;
1246 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001247 u8 oldstate;
1248
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001249 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL, &reg);
1250 if (err)
1251 return err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001252
Andrew Lunncca8b132015-04-02 04:06:39 +02001253 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001254
Vivien Didelot749efcb2016-09-22 16:49:24 -04001255 reg &= ~PORT_CONTROL_STATE_MASK;
1256 reg |= state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001257
Vivien Didelot749efcb2016-09-22 16:49:24 -04001258 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
1259 if (err)
1260 return err;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001261
Vivien Didelot749efcb2016-09-22 16:49:24 -04001262 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
1263 mv88e6xxx_port_state_names[state],
1264 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001265
Vivien Didelot749efcb2016-09-22 16:49:24 -04001266 return 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001267}
1268
Vivien Didelotfad09c72016-06-21 12:28:20 -04001269static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001270{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001271 struct net_device *bridge = chip->ports[port].bridge_dev;
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001272 const u16 mask = (1 << mv88e6xxx_num_ports(chip)) - 1;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001273 struct dsa_switch *ds = chip->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001274 u16 output_ports = 0;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001275 u16 reg;
1276 int err;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001277 int i;
1278
1279 /* allow CPU port or DSA link(s) to send frames to every port */
1280 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1281 output_ports = mask;
1282 } else {
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001283 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001284 /* allow sending frames to every group member */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001285 if (bridge && chip->ports[i].bridge_dev == bridge)
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001286 output_ports |= BIT(i);
1287
1288 /* allow sending frames to CPU port and DSA link(s) */
1289 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1290 output_ports |= BIT(i);
1291 }
1292 }
1293
1294 /* prevent frames from going back out of the port they came in on */
1295 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001296
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001297 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
1298 if (err)
1299 return err;
Vivien Didelotede80982015-10-11 18:08:35 -04001300
1301 reg &= ~mask;
1302 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001303
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001304 return mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001305}
1306
Vivien Didelotf81ec902016-05-09 13:22:58 -04001307static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1308 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001309{
Vivien Didelot04bed142016-08-31 18:06:13 -04001310 struct mv88e6xxx_chip *chip = ds->priv;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001311 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001312 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001313
1314 switch (state) {
1315 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001316 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001317 break;
1318 case BR_STATE_BLOCKING:
1319 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001320 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001321 break;
1322 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001323 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001324 break;
1325 case BR_STATE_FORWARDING:
1326 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001327 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001328 break;
1329 }
1330
Vivien Didelotfad09c72016-06-21 12:28:20 -04001331 mutex_lock(&chip->reg_lock);
1332 err = _mv88e6xxx_port_state(chip, port, stp_state);
1333 mutex_unlock(&chip->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001334
1335 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001336 netdev_err(ds->ports[port].netdev,
1337 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001338 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001339}
1340
Vivien Didelot749efcb2016-09-22 16:49:24 -04001341static void mv88e6xxx_port_fast_age(struct dsa_switch *ds, int port)
1342{
1343 struct mv88e6xxx_chip *chip = ds->priv;
1344 int err;
1345
1346 mutex_lock(&chip->reg_lock);
1347 err = _mv88e6xxx_atu_remove(chip, 0, port, false);
1348 mutex_unlock(&chip->reg_lock);
1349
1350 if (err)
1351 netdev_err(ds->ports[port].netdev, "failed to flush ATU\n");
1352}
1353
Vivien Didelotfad09c72016-06-21 12:28:20 -04001354static int _mv88e6xxx_port_pvid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001355 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001356{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001357 struct dsa_switch *ds = chip->ds;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001358 u16 pvid, reg;
1359 int err;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001360
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001361 err = mv88e6xxx_port_read(chip, port, PORT_DEFAULT_VLAN, &reg);
1362 if (err)
1363 return err;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001364
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001365 pvid = reg & PORT_DEFAULT_VLAN_MASK;
Vivien Didelot5da96032016-03-07 18:24:39 -05001366
1367 if (new) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001368 reg &= ~PORT_DEFAULT_VLAN_MASK;
1369 reg |= *new & PORT_DEFAULT_VLAN_MASK;
Vivien Didelot5da96032016-03-07 18:24:39 -05001370
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001371 err = mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, reg);
1372 if (err)
1373 return err;
Vivien Didelot5da96032016-03-07 18:24:39 -05001374
Andrew Lunnc8b09802016-06-04 21:16:57 +02001375 netdev_dbg(ds->ports[port].netdev,
1376 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001377 }
1378
1379 if (old)
1380 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001381
1382 return 0;
1383}
1384
Vivien Didelotfad09c72016-06-21 12:28:20 -04001385static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001386 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001387{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001388 return _mv88e6xxx_port_pvid(chip, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001389}
1390
Vivien Didelotfad09c72016-06-21 12:28:20 -04001391static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001392 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001393{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001394 return _mv88e6xxx_port_pvid(chip, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001395}
1396
Vivien Didelotfad09c72016-06-21 12:28:20 -04001397static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001398{
Vivien Didelota935c052016-09-29 12:21:53 -04001399 return mv88e6xxx_g1_wait(chip, GLOBAL_VTU_OP, GLOBAL_VTU_OP_BUSY);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001400}
1401
Vivien Didelotfad09c72016-06-21 12:28:20 -04001402static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_chip *chip, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001403{
Vivien Didelota935c052016-09-29 12:21:53 -04001404 int err;
Vivien Didelot6b17e862015-08-13 12:52:18 -04001405
Vivien Didelota935c052016-09-29 12:21:53 -04001406 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_OP, op);
1407 if (err)
1408 return err;
Vivien Didelot6b17e862015-08-13 12:52:18 -04001409
Vivien Didelotfad09c72016-06-21 12:28:20 -04001410 return _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001411}
1412
Vivien Didelotfad09c72016-06-21 12:28:20 -04001413static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_chip *chip)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001414{
1415 int ret;
1416
Vivien Didelotfad09c72016-06-21 12:28:20 -04001417 ret = _mv88e6xxx_vtu_wait(chip);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001418 if (ret < 0)
1419 return ret;
1420
Vivien Didelotfad09c72016-06-21 12:28:20 -04001421 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001422}
1423
Vivien Didelotfad09c72016-06-21 12:28:20 -04001424static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001425 struct mv88e6xxx_vtu_entry *entry,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001426 unsigned int nibble_offset)
1427{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001428 u16 regs[3];
Vivien Didelota935c052016-09-29 12:21:53 -04001429 int i, err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001430
1431 for (i = 0; i < 3; ++i) {
Vivien Didelota935c052016-09-29 12:21:53 -04001432 u16 *reg = &regs[i];
Vivien Didelotb8fee952015-08-13 12:52:19 -04001433
Vivien Didelota935c052016-09-29 12:21:53 -04001434 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
1435 if (err)
1436 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001437 }
1438
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001439 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001440 unsigned int shift = (i % 4) * 4 + nibble_offset;
1441 u16 reg = regs[i / 4];
1442
1443 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1444 }
1445
1446 return 0;
1447}
1448
Vivien Didelotfad09c72016-06-21 12:28:20 -04001449static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001450 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001451{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001452 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001453}
1454
Vivien Didelotfad09c72016-06-21 12:28:20 -04001455static int mv88e6xxx_stu_data_read(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001456 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001457{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001458 return _mv88e6xxx_vtu_stu_data_read(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001459}
1460
Vivien Didelotfad09c72016-06-21 12:28:20 -04001461static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001462 struct mv88e6xxx_vtu_entry *entry,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001463 unsigned int nibble_offset)
1464{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001465 u16 regs[3] = { 0 };
Vivien Didelota935c052016-09-29 12:21:53 -04001466 int i, err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001467
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001468 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001469 unsigned int shift = (i % 4) * 4 + nibble_offset;
1470 u8 data = entry->data[i];
1471
1472 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1473 }
1474
1475 for (i = 0; i < 3; ++i) {
Vivien Didelota935c052016-09-29 12:21:53 -04001476 u16 reg = regs[i];
1477
1478 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_DATA_0_3 + i, reg);
1479 if (err)
1480 return err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001481 }
1482
1483 return 0;
1484}
1485
Vivien Didelotfad09c72016-06-21 12:28:20 -04001486static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001487 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001488{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001489 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 0);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001490}
1491
Vivien Didelotfad09c72016-06-21 12:28:20 -04001492static int mv88e6xxx_stu_data_write(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001493 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001494{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001495 return _mv88e6xxx_vtu_stu_data_write(chip, entry, 2);
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001496}
1497
Vivien Didelotfad09c72016-06-21 12:28:20 -04001498static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_chip *chip, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001499{
Vivien Didelota935c052016-09-29 12:21:53 -04001500 return mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID,
1501 vid & GLOBAL_VTU_VID_MASK);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001502}
1503
Vivien Didelotfad09c72016-06-21 12:28:20 -04001504static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001505 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelotb8fee952015-08-13 12:52:19 -04001506{
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001507 struct mv88e6xxx_vtu_entry next = { 0 };
Vivien Didelota935c052016-09-29 12:21:53 -04001508 u16 val;
1509 int err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001510
Vivien Didelota935c052016-09-29 12:21:53 -04001511 err = _mv88e6xxx_vtu_wait(chip);
1512 if (err)
1513 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001514
Vivien Didelota935c052016-09-29 12:21:53 -04001515 err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_VTU_GET_NEXT);
1516 if (err)
1517 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001518
Vivien Didelota935c052016-09-29 12:21:53 -04001519 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
1520 if (err)
1521 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001522
Vivien Didelota935c052016-09-29 12:21:53 -04001523 next.vid = val & GLOBAL_VTU_VID_MASK;
1524 next.valid = !!(val & GLOBAL_VTU_VID_VALID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001525
1526 if (next.valid) {
Vivien Didelota935c052016-09-29 12:21:53 -04001527 err = mv88e6xxx_vtu_data_read(chip, &next);
1528 if (err)
1529 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001530
Vivien Didelot6dc10bb2016-09-29 12:21:55 -04001531 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
Vivien Didelota935c052016-09-29 12:21:53 -04001532 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_FID, &val);
1533 if (err)
1534 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001535
Vivien Didelota935c052016-09-29 12:21:53 -04001536 next.fid = val & GLOBAL_VTU_FID_MASK;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001537 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001538 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1539 * VTU DBNum[3:0] are located in VTU Operation 3:0
1540 */
Vivien Didelota935c052016-09-29 12:21:53 -04001541 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_OP, &val);
1542 if (err)
1543 return err;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001544
Vivien Didelota935c052016-09-29 12:21:53 -04001545 next.fid = (val & 0xf00) >> 4;
1546 next.fid |= val & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001547 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001548
Vivien Didelotfad09c72016-06-21 12:28:20 -04001549 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
Vivien Didelota935c052016-09-29 12:21:53 -04001550 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
1551 if (err)
1552 return err;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001553
Vivien Didelota935c052016-09-29 12:21:53 -04001554 next.sid = val & GLOBAL_VTU_SID_MASK;
Vivien Didelotb8fee952015-08-13 12:52:19 -04001555 }
1556 }
1557
1558 *entry = next;
1559 return 0;
1560}
1561
Vivien Didelotf81ec902016-05-09 13:22:58 -04001562static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1563 struct switchdev_obj_port_vlan *vlan,
1564 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001565{
Vivien Didelot04bed142016-08-31 18:06:13 -04001566 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001567 struct mv88e6xxx_vtu_entry next;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001568 u16 pvid;
1569 int err;
1570
Vivien Didelotfad09c72016-06-21 12:28:20 -04001571 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04001572 return -EOPNOTSUPP;
1573
Vivien Didelotfad09c72016-06-21 12:28:20 -04001574 mutex_lock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001575
Vivien Didelotfad09c72016-06-21 12:28:20 -04001576 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001577 if (err)
1578 goto unlock;
1579
Vivien Didelotfad09c72016-06-21 12:28:20 -04001580 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001581 if (err)
1582 goto unlock;
1583
1584 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001585 err = _mv88e6xxx_vtu_getnext(chip, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001586 if (err)
1587 break;
1588
1589 if (!next.valid)
1590 break;
1591
1592 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1593 continue;
1594
1595 /* reinit and dump this VLAN obj */
Vivien Didelot57d32312016-06-20 13:13:58 -04001596 vlan->vid_begin = next.vid;
1597 vlan->vid_end = next.vid;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001598 vlan->flags = 0;
1599
1600 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1601 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1602
1603 if (next.vid == pvid)
1604 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1605
1606 err = cb(&vlan->obj);
1607 if (err)
1608 break;
1609 } while (next.vid < GLOBAL_VTU_VID_MASK);
1610
1611unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001612 mutex_unlock(&chip->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001613
1614 return err;
1615}
1616
Vivien Didelotfad09c72016-06-21 12:28:20 -04001617static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001618 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001619{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001620 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001621 u16 reg = 0;
Vivien Didelota935c052016-09-29 12:21:53 -04001622 int err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001623
Vivien Didelota935c052016-09-29 12:21:53 -04001624 err = _mv88e6xxx_vtu_wait(chip);
1625 if (err)
1626 return err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001627
1628 if (!entry->valid)
1629 goto loadpurge;
1630
1631 /* Write port member tags */
Vivien Didelota935c052016-09-29 12:21:53 -04001632 err = mv88e6xxx_vtu_data_write(chip, entry);
1633 if (err)
1634 return err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001635
Vivien Didelotfad09c72016-06-21 12:28:20 -04001636 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001637 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelota935c052016-09-29 12:21:53 -04001638 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
1639 if (err)
1640 return err;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001641 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001642
Vivien Didelot6dc10bb2016-09-29 12:21:55 -04001643 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G1_VTU_FID)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001644 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Vivien Didelota935c052016-09-29 12:21:53 -04001645 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_FID, reg);
1646 if (err)
1647 return err;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001648 } else if (mv88e6xxx_num_databases(chip) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001649 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1650 * VTU DBNum[3:0] are located in VTU Operation 3:0
1651 */
1652 op |= (entry->fid & 0xf0) << 8;
1653 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001654 }
1655
1656 reg = GLOBAL_VTU_VID_VALID;
1657loadpurge:
1658 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Vivien Didelota935c052016-09-29 12:21:53 -04001659 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
1660 if (err)
1661 return err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001662
Vivien Didelotfad09c72016-06-21 12:28:20 -04001663 return _mv88e6xxx_vtu_cmd(chip, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001664}
1665
Vivien Didelotfad09c72016-06-21 12:28:20 -04001666static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_chip *chip, u8 sid,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001667 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001668{
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001669 struct mv88e6xxx_vtu_entry next = { 0 };
Vivien Didelota935c052016-09-29 12:21:53 -04001670 u16 val;
1671 int err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001672
Vivien Didelota935c052016-09-29 12:21:53 -04001673 err = _mv88e6xxx_vtu_wait(chip);
1674 if (err)
1675 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001676
Vivien Didelota935c052016-09-29 12:21:53 -04001677 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID,
1678 sid & GLOBAL_VTU_SID_MASK);
1679 if (err)
1680 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001681
Vivien Didelota935c052016-09-29 12:21:53 -04001682 err = _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_GET_NEXT);
1683 if (err)
1684 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001685
Vivien Didelota935c052016-09-29 12:21:53 -04001686 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_SID, &val);
1687 if (err)
1688 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001689
Vivien Didelota935c052016-09-29 12:21:53 -04001690 next.sid = val & GLOBAL_VTU_SID_MASK;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001691
Vivien Didelota935c052016-09-29 12:21:53 -04001692 err = mv88e6xxx_g1_read(chip, GLOBAL_VTU_VID, &val);
1693 if (err)
1694 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001695
Vivien Didelota935c052016-09-29 12:21:53 -04001696 next.valid = !!(val & GLOBAL_VTU_VID_VALID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001697
1698 if (next.valid) {
Vivien Didelota935c052016-09-29 12:21:53 -04001699 err = mv88e6xxx_stu_data_read(chip, &next);
1700 if (err)
1701 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001702 }
1703
1704 *entry = next;
1705 return 0;
1706}
1707
Vivien Didelotfad09c72016-06-21 12:28:20 -04001708static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_chip *chip,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001709 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001710{
1711 u16 reg = 0;
Vivien Didelota935c052016-09-29 12:21:53 -04001712 int err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001713
Vivien Didelota935c052016-09-29 12:21:53 -04001714 err = _mv88e6xxx_vtu_wait(chip);
1715 if (err)
1716 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001717
1718 if (!entry->valid)
1719 goto loadpurge;
1720
1721 /* Write port states */
Vivien Didelota935c052016-09-29 12:21:53 -04001722 err = mv88e6xxx_stu_data_write(chip, entry);
1723 if (err)
1724 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001725
1726 reg = GLOBAL_VTU_VID_VALID;
1727loadpurge:
Vivien Didelota935c052016-09-29 12:21:53 -04001728 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_VID, reg);
1729 if (err)
1730 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001731
1732 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Vivien Didelota935c052016-09-29 12:21:53 -04001733 err = mv88e6xxx_g1_write(chip, GLOBAL_VTU_SID, reg);
1734 if (err)
1735 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001736
Vivien Didelotfad09c72016-06-21 12:28:20 -04001737 return _mv88e6xxx_vtu_cmd(chip, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001738}
1739
Vivien Didelotfad09c72016-06-21 12:28:20 -04001740static int _mv88e6xxx_port_fid(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04001741 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001742{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001743 struct dsa_switch *ds = chip->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001744 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001745 u16 fid;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001746 u16 reg;
1747 int err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001748
Vivien Didelotfad09c72016-06-21 12:28:20 -04001749 if (mv88e6xxx_num_databases(chip) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001750 upper_mask = 0xff;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001751 else if (mv88e6xxx_num_databases(chip) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001752 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001753 else
1754 return -EOPNOTSUPP;
1755
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001756 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001757 err = mv88e6xxx_port_read(chip, port, PORT_BASE_VLAN, &reg);
1758 if (err)
1759 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001760
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001761 fid = (reg & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001762
1763 if (new) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001764 reg &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1765 reg |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001766
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001767 err = mv88e6xxx_port_write(chip, port, PORT_BASE_VLAN, reg);
1768 if (err)
1769 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001770 }
1771
1772 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001773 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_1, &reg);
1774 if (err)
1775 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001776
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001777 fid |= (reg & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001778
1779 if (new) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001780 reg &= ~upper_mask;
1781 reg |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001782
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001783 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, reg);
1784 if (err)
1785 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001786
Andrew Lunnc8b09802016-06-04 21:16:57 +02001787 netdev_dbg(ds->ports[port].netdev,
1788 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001789 }
1790
1791 if (old)
1792 *old = fid;
1793
1794 return 0;
1795}
1796
Vivien Didelotfad09c72016-06-21 12:28:20 -04001797static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001798 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001799{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001800 return _mv88e6xxx_port_fid(chip, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001801}
1802
Vivien Didelotfad09c72016-06-21 12:28:20 -04001803static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04001804 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001805{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001806 return _mv88e6xxx_port_fid(chip, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001807}
1808
Vivien Didelotfad09c72016-06-21 12:28:20 -04001809static int _mv88e6xxx_fid_new(struct mv88e6xxx_chip *chip, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001810{
1811 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001812 struct mv88e6xxx_vtu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001813 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001814
1815 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1816
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001817 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001818 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001819 err = _mv88e6xxx_port_fid_get(chip, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001820 if (err)
1821 return err;
1822
1823 set_bit(*fid, fid_bitmap);
1824 }
1825
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001826 /* Set every FID bit used by the VLAN entries */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001827 err = _mv88e6xxx_vtu_vid_write(chip, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001828 if (err)
1829 return err;
1830
1831 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001832 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001833 if (err)
1834 return err;
1835
1836 if (!vlan.valid)
1837 break;
1838
1839 set_bit(vlan.fid, fid_bitmap);
1840 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1841
1842 /* The reset value 0x000 is used to indicate that multiple address
1843 * databases are not needed. Return the next positive available.
1844 */
1845 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotfad09c72016-06-21 12:28:20 -04001846 if (unlikely(*fid >= mv88e6xxx_num_databases(chip)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001847 return -ENOSPC;
1848
1849 /* Clear the database */
Vivien Didelotfad09c72016-06-21 12:28:20 -04001850 return _mv88e6xxx_atu_flush(chip, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001851}
1852
Vivien Didelotfad09c72016-06-21 12:28:20 -04001853static int _mv88e6xxx_vtu_new(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001854 struct mv88e6xxx_vtu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001855{
Vivien Didelotfad09c72016-06-21 12:28:20 -04001856 struct dsa_switch *ds = chip->ds;
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001857 struct mv88e6xxx_vtu_entry vlan = {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001858 .valid = true,
1859 .vid = vid,
1860 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001861 int i, err;
1862
Vivien Didelotfad09c72016-06-21 12:28:20 -04001863 err = _mv88e6xxx_fid_new(chip, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001864 if (err)
1865 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001866
Vivien Didelot3d131f02015-11-03 10:52:52 -05001867 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001868 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001869 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1870 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1871 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001872
Vivien Didelotfad09c72016-06-21 12:28:20 -04001873 if (mv88e6xxx_6097_family(chip) || mv88e6xxx_6165_family(chip) ||
1874 mv88e6xxx_6351_family(chip) || mv88e6xxx_6352_family(chip)) {
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001875 struct mv88e6xxx_vtu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001876
1877 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1878 * implemented, only one STU entry is needed to cover all VTU
1879 * entries. Thus, validate the SID 0.
1880 */
1881 vlan.sid = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04001882 err = _mv88e6xxx_stu_getnext(chip, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001883 if (err)
1884 return err;
1885
1886 if (vstp.sid != vlan.sid || !vstp.valid) {
1887 memset(&vstp, 0, sizeof(vstp));
1888 vstp.valid = true;
1889 vstp.sid = vlan.sid;
1890
Vivien Didelotfad09c72016-06-21 12:28:20 -04001891 err = _mv88e6xxx_stu_loadpurge(chip, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001892 if (err)
1893 return err;
1894 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001895 }
1896
1897 *entry = vlan;
1898 return 0;
1899}
1900
Vivien Didelotfad09c72016-06-21 12:28:20 -04001901static int _mv88e6xxx_vtu_get(struct mv88e6xxx_chip *chip, u16 vid,
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001902 struct mv88e6xxx_vtu_entry *entry, bool creat)
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001903{
1904 int err;
1905
1906 if (!vid)
1907 return -EINVAL;
1908
Vivien Didelotfad09c72016-06-21 12:28:20 -04001909 err = _mv88e6xxx_vtu_vid_write(chip, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001910 if (err)
1911 return err;
1912
Vivien Didelotfad09c72016-06-21 12:28:20 -04001913 err = _mv88e6xxx_vtu_getnext(chip, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001914 if (err)
1915 return err;
1916
1917 if (entry->vid != vid || !entry->valid) {
1918 if (!creat)
1919 return -EOPNOTSUPP;
1920 /* -ENOENT would've been more appropriate, but switchdev expects
1921 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1922 */
1923
Vivien Didelotfad09c72016-06-21 12:28:20 -04001924 err = _mv88e6xxx_vtu_new(chip, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001925 }
1926
1927 return err;
1928}
1929
Vivien Didelotda9c3592016-02-12 12:09:40 -05001930static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1931 u16 vid_begin, u16 vid_end)
1932{
Vivien Didelot04bed142016-08-31 18:06:13 -04001933 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotb4e47c02016-09-29 12:21:58 -04001934 struct mv88e6xxx_vtu_entry vlan;
Vivien Didelotda9c3592016-02-12 12:09:40 -05001935 int i, err;
1936
1937 if (!vid_begin)
1938 return -EOPNOTSUPP;
1939
Vivien Didelotfad09c72016-06-21 12:28:20 -04001940 mutex_lock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001941
Vivien Didelotfad09c72016-06-21 12:28:20 -04001942 err = _mv88e6xxx_vtu_vid_write(chip, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001943 if (err)
1944 goto unlock;
1945
1946 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04001947 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001948 if (err)
1949 goto unlock;
1950
1951 if (!vlan.valid)
1952 break;
1953
1954 if (vlan.vid > vid_end)
1955 break;
1956
Vivien Didelot370b4ff2016-09-29 12:21:57 -04001957 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001958 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1959 continue;
1960
1961 if (vlan.data[i] ==
1962 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1963 continue;
1964
Vivien Didelotfad09c72016-06-21 12:28:20 -04001965 if (chip->ports[i].bridge_dev ==
1966 chip->ports[port].bridge_dev)
Vivien Didelotda9c3592016-02-12 12:09:40 -05001967 break; /* same bridge, check next VLAN */
1968
Andrew Lunnc8b09802016-06-04 21:16:57 +02001969 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05001970 "hardware VLAN %d already used by %s\n",
1971 vlan.vid,
Vivien Didelotfad09c72016-06-21 12:28:20 -04001972 netdev_name(chip->ports[i].bridge_dev));
Vivien Didelotda9c3592016-02-12 12:09:40 -05001973 err = -EOPNOTSUPP;
1974 goto unlock;
1975 }
1976 } while (vlan.vid < vid_end);
1977
1978unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04001979 mutex_unlock(&chip->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001980
1981 return err;
1982}
1983
Vivien Didelot214cdb92016-02-26 13:16:08 -05001984static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1985 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1986 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1987 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1988 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1989};
1990
Vivien Didelotf81ec902016-05-09 13:22:58 -04001991static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1992 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05001993{
Vivien Didelot04bed142016-08-31 18:06:13 -04001994 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001995 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1996 PORT_CONTROL_2_8021Q_DISABLED;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02001997 u16 reg;
1998 int err;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001999
Vivien Didelotfad09c72016-06-21 12:28:20 -04002000 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002001 return -EOPNOTSUPP;
2002
Vivien Didelotfad09c72016-06-21 12:28:20 -04002003 mutex_lock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002004
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002005 err = mv88e6xxx_port_read(chip, port, PORT_CONTROL_2, &reg);
2006 if (err)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002007 goto unlock;
2008
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002009 old = reg & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002010
Vivien Didelot5220ef12016-03-07 18:24:52 -05002011 if (new != old) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002012 reg &= ~PORT_CONTROL_2_8021Q_MASK;
2013 reg |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002014
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002015 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
2016 if (err)
Vivien Didelot5220ef12016-03-07 18:24:52 -05002017 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002018
Andrew Lunnc8b09802016-06-04 21:16:57 +02002019 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05002020 mv88e6xxx_port_8021q_mode_names[new],
2021 mv88e6xxx_port_8021q_mode_names[old]);
2022 }
2023
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002024 err = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002025unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002026 mutex_unlock(&chip->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002027
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002028 return err;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002029}
2030
Vivien Didelot57d32312016-06-20 13:13:58 -04002031static int
2032mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2033 const struct switchdev_obj_port_vlan *vlan,
2034 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002035{
Vivien Didelot04bed142016-08-31 18:06:13 -04002036 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotda9c3592016-02-12 12:09:40 -05002037 int err;
2038
Vivien Didelotfad09c72016-06-21 12:28:20 -04002039 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002040 return -EOPNOTSUPP;
2041
Vivien Didelotda9c3592016-02-12 12:09:40 -05002042 /* If the requested port doesn't belong to the same bridge as the VLAN
2043 * members, do not support it (yet) and fallback to software VLAN.
2044 */
2045 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2046 vlan->vid_end);
2047 if (err)
2048 return err;
2049
Vivien Didelot76e398a2015-11-01 12:33:55 -05002050 /* We don't need any dynamic resource from the kernel (yet),
2051 * so skip the prepare phase.
2052 */
2053 return 0;
2054}
2055
Vivien Didelotfad09c72016-06-21 12:28:20 -04002056static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_chip *chip, int port,
Andrew Lunn158bc062016-04-28 21:24:06 -04002057 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002058{
Vivien Didelotb4e47c02016-09-29 12:21:58 -04002059 struct mv88e6xxx_vtu_entry vlan;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002060 int err;
2061
Vivien Didelotfad09c72016-06-21 12:28:20 -04002062 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002063 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002064 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002065
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002066 vlan.data[port] = untagged ?
2067 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2068 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2069
Vivien Didelotfad09c72016-06-21 12:28:20 -04002070 return _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002071}
2072
Vivien Didelotf81ec902016-05-09 13:22:58 -04002073static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2074 const struct switchdev_obj_port_vlan *vlan,
2075 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002076{
Vivien Didelot04bed142016-08-31 18:06:13 -04002077 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002078 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2079 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2080 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002081
Vivien Didelotfad09c72016-06-21 12:28:20 -04002082 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002083 return;
2084
Vivien Didelotfad09c72016-06-21 12:28:20 -04002085 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002086
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002087 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002088 if (_mv88e6xxx_port_vlan_add(chip, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002089 netdev_err(ds->ports[port].netdev,
2090 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002091 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002092
Vivien Didelotfad09c72016-06-21 12:28:20 -04002093 if (pvid && _mv88e6xxx_port_pvid_set(chip, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002094 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002095 vlan->vid_end);
2096
Vivien Didelotfad09c72016-06-21 12:28:20 -04002097 mutex_unlock(&chip->reg_lock);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002098}
2099
Vivien Didelotfad09c72016-06-21 12:28:20 -04002100static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002101 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002102{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002103 struct dsa_switch *ds = chip->ds;
Vivien Didelotb4e47c02016-09-29 12:21:58 -04002104 struct mv88e6xxx_vtu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002105 int i, err;
2106
Vivien Didelotfad09c72016-06-21 12:28:20 -04002107 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002108 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002109 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002110
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002111 /* Tell switchdev if this VLAN is handled in software */
2112 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002113 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002114
2115 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2116
2117 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002118 vlan.valid = false;
Vivien Didelot370b4ff2016-09-29 12:21:57 -04002119 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002120 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002121 continue;
2122
2123 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002124 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002125 break;
2126 }
2127 }
2128
Vivien Didelotfad09c72016-06-21 12:28:20 -04002129 err = _mv88e6xxx_vtu_loadpurge(chip, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002130 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002131 return err;
2132
Vivien Didelotfad09c72016-06-21 12:28:20 -04002133 return _mv88e6xxx_atu_remove(chip, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002134}
2135
Vivien Didelotf81ec902016-05-09 13:22:58 -04002136static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2137 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002138{
Vivien Didelot04bed142016-08-31 18:06:13 -04002139 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002140 u16 pvid, vid;
2141 int err = 0;
2142
Vivien Didelotfad09c72016-06-21 12:28:20 -04002143 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_VTU))
Vivien Didelot54d77b52016-05-09 13:22:47 -04002144 return -EOPNOTSUPP;
2145
Vivien Didelotfad09c72016-06-21 12:28:20 -04002146 mutex_lock(&chip->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002147
Vivien Didelotfad09c72016-06-21 12:28:20 -04002148 err = _mv88e6xxx_port_pvid_get(chip, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002149 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002150 goto unlock;
2151
Vivien Didelot76e398a2015-11-01 12:33:55 -05002152 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002153 err = _mv88e6xxx_port_vlan_del(chip, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002154 if (err)
2155 goto unlock;
2156
2157 if (vid == pvid) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002158 err = _mv88e6xxx_port_pvid_set(chip, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002159 if (err)
2160 goto unlock;
2161 }
2162 }
2163
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002164unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04002165 mutex_unlock(&chip->reg_lock);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002166
2167 return err;
2168}
2169
Vivien Didelotfad09c72016-06-21 12:28:20 -04002170static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_chip *chip,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002171 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002172{
Vivien Didelota935c052016-09-29 12:21:53 -04002173 int i, err;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002174
2175 for (i = 0; i < 3; i++) {
Vivien Didelota935c052016-09-29 12:21:53 -04002176 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_MAC_01 + i,
2177 (addr[i * 2] << 8) | addr[i * 2 + 1]);
2178 if (err)
2179 return err;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002180 }
2181
2182 return 0;
2183}
2184
Vivien Didelotfad09c72016-06-21 12:28:20 -04002185static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_chip *chip,
Andrew Lunn158bc062016-04-28 21:24:06 -04002186 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002187{
Vivien Didelota935c052016-09-29 12:21:53 -04002188 u16 val;
2189 int i, err;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002190
2191 for (i = 0; i < 3; i++) {
Vivien Didelota935c052016-09-29 12:21:53 -04002192 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_MAC_01 + i, &val);
2193 if (err)
2194 return err;
2195
2196 addr[i * 2] = val >> 8;
2197 addr[i * 2 + 1] = val & 0xff;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002198 }
2199
2200 return 0;
2201}
2202
Vivien Didelotfad09c72016-06-21 12:28:20 -04002203static int _mv88e6xxx_atu_load(struct mv88e6xxx_chip *chip,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002204 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002205{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002206 int ret;
2207
Vivien Didelotfad09c72016-06-21 12:28:20 -04002208 ret = _mv88e6xxx_atu_wait(chip);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002209 if (ret < 0)
2210 return ret;
2211
Vivien Didelotfad09c72016-06-21 12:28:20 -04002212 ret = _mv88e6xxx_atu_mac_write(chip, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002213 if (ret < 0)
2214 return ret;
2215
Vivien Didelotfad09c72016-06-21 12:28:20 -04002216 ret = _mv88e6xxx_atu_data_write(chip, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002217 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002218 return ret;
2219
Vivien Didelotfad09c72016-06-21 12:28:20 -04002220 return _mv88e6xxx_atu_cmd(chip, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002221}
David S. Millercdf09692015-08-11 12:00:37 -07002222
Vivien Didelot88472932016-09-19 19:56:11 -04002223static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
2224 struct mv88e6xxx_atu_entry *entry);
2225
2226static int mv88e6xxx_atu_get(struct mv88e6xxx_chip *chip, int fid,
2227 const u8 *addr, struct mv88e6xxx_atu_entry *entry)
2228{
2229 struct mv88e6xxx_atu_entry next;
2230 int err;
2231
2232 eth_broadcast_addr(next.mac);
2233
2234 err = _mv88e6xxx_atu_mac_write(chip, next.mac);
2235 if (err)
2236 return err;
2237
2238 do {
2239 err = _mv88e6xxx_atu_getnext(chip, fid, &next);
2240 if (err)
2241 return err;
2242
2243 if (next.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2244 break;
2245
2246 if (ether_addr_equal(next.mac, addr)) {
2247 *entry = next;
2248 return 0;
2249 }
2250 } while (!is_broadcast_ether_addr(next.mac));
2251
2252 memset(entry, 0, sizeof(*entry));
2253 entry->fid = fid;
2254 ether_addr_copy(entry->mac, addr);
2255
2256 return 0;
2257}
2258
Vivien Didelot83dabd12016-08-31 11:50:04 -04002259static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port,
2260 const unsigned char *addr, u16 vid,
2261 u8 state)
Vivien Didelotfd231c82015-08-10 09:09:50 -04002262{
Vivien Didelotb4e47c02016-09-29 12:21:58 -04002263 struct mv88e6xxx_vtu_entry vlan;
Vivien Didelot88472932016-09-19 19:56:11 -04002264 struct mv88e6xxx_atu_entry entry;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002265 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002266
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002267 /* Null VLAN ID corresponds to the port private database */
2268 if (vid == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002269 err = _mv88e6xxx_port_fid_get(chip, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002270 else
Vivien Didelotfad09c72016-06-21 12:28:20 -04002271 err = _mv88e6xxx_vtu_get(chip, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002272 if (err)
2273 return err;
2274
Vivien Didelot88472932016-09-19 19:56:11 -04002275 err = mv88e6xxx_atu_get(chip, vlan.fid, addr, &entry);
2276 if (err)
2277 return err;
2278
2279 /* Purge the ATU entry only if no port is using it anymore */
2280 if (state == GLOBAL_ATU_DATA_STATE_UNUSED) {
2281 entry.portv_trunkid &= ~BIT(port);
2282 if (!entry.portv_trunkid)
2283 entry.state = GLOBAL_ATU_DATA_STATE_UNUSED;
2284 } else {
2285 entry.portv_trunkid |= BIT(port);
2286 entry.state = state;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002287 }
2288
Vivien Didelotfad09c72016-06-21 12:28:20 -04002289 return _mv88e6xxx_atu_load(chip, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002290}
2291
Vivien Didelotf81ec902016-05-09 13:22:58 -04002292static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2293 const struct switchdev_obj_port_fdb *fdb,
2294 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002295{
2296 /* We don't need any dynamic resource from the kernel (yet),
2297 * so skip the prepare phase.
2298 */
2299 return 0;
2300}
2301
Vivien Didelotf81ec902016-05-09 13:22:58 -04002302static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2303 const struct switchdev_obj_port_fdb *fdb,
2304 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002305{
Vivien Didelot04bed142016-08-31 18:06:13 -04002306 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot6630e232015-08-06 01:44:07 -04002307
Vivien Didelotfad09c72016-06-21 12:28:20 -04002308 mutex_lock(&chip->reg_lock);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002309 if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
2310 GLOBAL_ATU_DATA_STATE_UC_STATIC))
2311 netdev_err(ds->ports[port].netdev, "failed to load unicast MAC address\n");
Vivien Didelotfad09c72016-06-21 12:28:20 -04002312 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002313}
2314
Vivien Didelotf81ec902016-05-09 13:22:58 -04002315static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2316 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002317{
Vivien Didelot04bed142016-08-31 18:06:13 -04002318 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot83dabd12016-08-31 11:50:04 -04002319 int err;
David S. Millercdf09692015-08-11 12:00:37 -07002320
Vivien Didelotfad09c72016-06-21 12:28:20 -04002321 mutex_lock(&chip->reg_lock);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002322 err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid,
2323 GLOBAL_ATU_DATA_STATE_UNUSED);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002324 mutex_unlock(&chip->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002325
Vivien Didelot83dabd12016-08-31 11:50:04 -04002326 return err;
David S. Millercdf09692015-08-11 12:00:37 -07002327}
2328
Vivien Didelotfad09c72016-06-21 12:28:20 -04002329static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_chip *chip, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002330 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002331{
Vivien Didelot1d194042015-08-10 09:09:51 -04002332 struct mv88e6xxx_atu_entry next = { 0 };
Vivien Didelota935c052016-09-29 12:21:53 -04002333 u16 val;
2334 int err;
Vivien Didelot1d194042015-08-10 09:09:51 -04002335
2336 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002337
Vivien Didelota935c052016-09-29 12:21:53 -04002338 err = _mv88e6xxx_atu_wait(chip);
2339 if (err)
2340 return err;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002341
Vivien Didelota935c052016-09-29 12:21:53 -04002342 err = _mv88e6xxx_atu_cmd(chip, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
2343 if (err)
2344 return err;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002345
Vivien Didelota935c052016-09-29 12:21:53 -04002346 err = _mv88e6xxx_atu_mac_read(chip, next.mac);
2347 if (err)
2348 return err;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002349
Vivien Didelota935c052016-09-29 12:21:53 -04002350 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_DATA, &val);
2351 if (err)
2352 return err;
Vivien Didelot1d194042015-08-10 09:09:51 -04002353
Vivien Didelota935c052016-09-29 12:21:53 -04002354 next.state = val & GLOBAL_ATU_DATA_STATE_MASK;
Vivien Didelot1d194042015-08-10 09:09:51 -04002355 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2356 unsigned int mask, shift;
2357
Vivien Didelota935c052016-09-29 12:21:53 -04002358 if (val & GLOBAL_ATU_DATA_TRUNK) {
Vivien Didelot1d194042015-08-10 09:09:51 -04002359 next.trunk = true;
2360 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2361 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2362 } else {
2363 next.trunk = false;
2364 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2365 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2366 }
2367
Vivien Didelota935c052016-09-29 12:21:53 -04002368 next.portv_trunkid = (val & mask) >> shift;
Vivien Didelot1d194042015-08-10 09:09:51 -04002369 }
2370
2371 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002372 return 0;
2373}
2374
Vivien Didelot83dabd12016-08-31 11:50:04 -04002375static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip,
2376 u16 fid, u16 vid, int port,
2377 struct switchdev_obj *obj,
2378 int (*cb)(struct switchdev_obj *obj))
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002379{
2380 struct mv88e6xxx_atu_entry addr = {
2381 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2382 };
2383 int err;
2384
Vivien Didelotfad09c72016-06-21 12:28:20 -04002385 err = _mv88e6xxx_atu_mac_write(chip, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002386 if (err)
2387 return err;
2388
2389 do {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002390 err = _mv88e6xxx_atu_getnext(chip, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002391 if (err)
Vivien Didelot83dabd12016-08-31 11:50:04 -04002392 return err;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002393
2394 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2395 break;
2396
Vivien Didelot83dabd12016-08-31 11:50:04 -04002397 if (addr.trunk || (addr.portv_trunkid & BIT(port)) == 0)
2398 continue;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002399
Vivien Didelot83dabd12016-08-31 11:50:04 -04002400 if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) {
2401 struct switchdev_obj_port_fdb *fdb;
2402
2403 if (!is_unicast_ether_addr(addr.mac))
2404 continue;
2405
2406 fdb = SWITCHDEV_OBJ_PORT_FDB(obj);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002407 fdb->vid = vid;
2408 ether_addr_copy(fdb->addr, addr.mac);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002409 if (addr.state == GLOBAL_ATU_DATA_STATE_UC_STATIC)
2410 fdb->ndm_state = NUD_NOARP;
2411 else
2412 fdb->ndm_state = NUD_REACHABLE;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04002413 } else if (obj->id == SWITCHDEV_OBJ_ID_PORT_MDB) {
2414 struct switchdev_obj_port_mdb *mdb;
2415
2416 if (!is_multicast_ether_addr(addr.mac))
2417 continue;
2418
2419 mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
2420 mdb->vid = vid;
2421 ether_addr_copy(mdb->addr, addr.mac);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002422 } else {
2423 return -EOPNOTSUPP;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002424 }
Vivien Didelot83dabd12016-08-31 11:50:04 -04002425
2426 err = cb(obj);
2427 if (err)
2428 return err;
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002429 } while (!is_broadcast_ether_addr(addr.mac));
2430
2431 return err;
2432}
2433
Vivien Didelot83dabd12016-08-31 11:50:04 -04002434static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port,
2435 struct switchdev_obj *obj,
2436 int (*cb)(struct switchdev_obj *obj))
2437{
Vivien Didelotb4e47c02016-09-29 12:21:58 -04002438 struct mv88e6xxx_vtu_entry vlan = {
Vivien Didelot83dabd12016-08-31 11:50:04 -04002439 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2440 };
2441 u16 fid;
2442 int err;
2443
2444 /* Dump port's default Filtering Information Database (VLAN ID 0) */
2445 err = _mv88e6xxx_port_fid_get(chip, port, &fid);
2446 if (err)
2447 return err;
2448
2449 err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb);
2450 if (err)
2451 return err;
2452
2453 /* Dump VLANs' Filtering Information Databases */
2454 err = _mv88e6xxx_vtu_vid_write(chip, vlan.vid);
2455 if (err)
2456 return err;
2457
2458 do {
2459 err = _mv88e6xxx_vtu_getnext(chip, &vlan);
2460 if (err)
2461 return err;
2462
2463 if (!vlan.valid)
2464 break;
2465
2466 err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port,
2467 obj, cb);
2468 if (err)
2469 return err;
2470 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2471
2472 return err;
2473}
2474
Vivien Didelotf81ec902016-05-09 13:22:58 -04002475static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2476 struct switchdev_obj_port_fdb *fdb,
2477 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002478{
Vivien Didelot04bed142016-08-31 18:06:13 -04002479 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002480 int err;
2481
Vivien Didelotfad09c72016-06-21 12:28:20 -04002482 mutex_lock(&chip->reg_lock);
Vivien Didelot83dabd12016-08-31 11:50:04 -04002483 err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002484 mutex_unlock(&chip->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002485
2486 return err;
2487}
2488
Vivien Didelotf81ec902016-05-09 13:22:58 -04002489static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2490 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002491{
Vivien Didelot04bed142016-08-31 18:06:13 -04002492 struct mv88e6xxx_chip *chip = ds->priv;
Colin Ian King1d9619d2016-04-25 23:11:22 +01002493 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002494
Vivien Didelotfad09c72016-06-21 12:28:20 -04002495 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002496
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002497 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002498 chip->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002499
Vivien Didelot370b4ff2016-09-29 12:21:57 -04002500 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002501 if (chip->ports[i].bridge_dev == bridge) {
2502 err = _mv88e6xxx_port_based_vlan_map(chip, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002503 if (err)
2504 break;
2505 }
2506 }
2507
Vivien Didelotfad09c72016-06-21 12:28:20 -04002508 mutex_unlock(&chip->reg_lock);
Vivien Didelota6692752016-02-12 12:09:39 -05002509
Vivien Didelot466dfa02016-02-26 13:16:05 -05002510 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002511}
2512
Vivien Didelotf81ec902016-05-09 13:22:58 -04002513static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002514{
Vivien Didelot04bed142016-08-31 18:06:13 -04002515 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002516 struct net_device *bridge = chip->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002517 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002518
Vivien Didelotfad09c72016-06-21 12:28:20 -04002519 mutex_lock(&chip->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002520
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002521 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002522 chip->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002523
Vivien Didelot370b4ff2016-09-29 12:21:57 -04002524 for (i = 0; i < mv88e6xxx_num_ports(chip); ++i)
Vivien Didelotfad09c72016-06-21 12:28:20 -04002525 if (i == port || chip->ports[i].bridge_dev == bridge)
2526 if (_mv88e6xxx_port_based_vlan_map(chip, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002527 netdev_warn(ds->ports[i].netdev,
2528 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002529
Vivien Didelotfad09c72016-06-21 12:28:20 -04002530 mutex_unlock(&chip->reg_lock);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002531}
2532
Vivien Didelotfad09c72016-06-21 12:28:20 -04002533static int mv88e6xxx_switch_reset(struct mv88e6xxx_chip *chip)
Vivien Didelot552238b2016-05-09 13:22:49 -04002534{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002535 bool ppu_active = mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE);
Vivien Didelot552238b2016-05-09 13:22:49 -04002536 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Vivien Didelotfad09c72016-06-21 12:28:20 -04002537 struct gpio_desc *gpiod = chip->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002538 unsigned long timeout;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002539 u16 reg;
Vivien Didelota935c052016-09-29 12:21:53 -04002540 int err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002541 int i;
2542
2543 /* Set all ports to the disabled state. */
Vivien Didelot370b4ff2016-09-29 12:21:57 -04002544 for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002545 err = mv88e6xxx_port_read(chip, i, PORT_CONTROL, &reg);
2546 if (err)
2547 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002548
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002549 err = mv88e6xxx_port_write(chip, i, PORT_CONTROL,
2550 reg & 0xfffc);
2551 if (err)
2552 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002553 }
2554
2555 /* Wait for transmit queues to drain. */
2556 usleep_range(2000, 4000);
2557
2558 /* If there is a gpio connected to the reset pin, toggle it */
2559 if (gpiod) {
2560 gpiod_set_value_cansleep(gpiod, 1);
2561 usleep_range(10000, 20000);
2562 gpiod_set_value_cansleep(gpiod, 0);
2563 usleep_range(10000, 20000);
2564 }
2565
2566 /* Reset the switch. Keep the PPU active if requested. The PPU
2567 * needs to be active to support indirect phy register access
2568 * through global registers 0x18 and 0x19.
2569 */
2570 if (ppu_active)
Vivien Didelota935c052016-09-29 12:21:53 -04002571 err = mv88e6xxx_g1_write(chip, 0x04, 0xc000);
Vivien Didelot552238b2016-05-09 13:22:49 -04002572 else
Vivien Didelota935c052016-09-29 12:21:53 -04002573 err = mv88e6xxx_g1_write(chip, 0x04, 0xc400);
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002574 if (err)
2575 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002576
2577 /* Wait up to one second for reset to complete. */
2578 timeout = jiffies + 1 * HZ;
2579 while (time_before(jiffies, timeout)) {
Vivien Didelota935c052016-09-29 12:21:53 -04002580 err = mv88e6xxx_g1_read(chip, 0x00, &reg);
2581 if (err)
2582 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002583
Vivien Didelota935c052016-09-29 12:21:53 -04002584 if ((reg & is_reset) == is_reset)
Vivien Didelot552238b2016-05-09 13:22:49 -04002585 break;
2586 usleep_range(1000, 2000);
2587 }
2588 if (time_after(jiffies, timeout))
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002589 err = -ETIMEDOUT;
Vivien Didelot552238b2016-05-09 13:22:49 -04002590 else
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002591 err = 0;
Vivien Didelot552238b2016-05-09 13:22:49 -04002592
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002593 return err;
Vivien Didelot552238b2016-05-09 13:22:49 -04002594}
2595
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002596static int mv88e6xxx_serdes_power_on(struct mv88e6xxx_chip *chip)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002597{
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002598 u16 val;
2599 int err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002600
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002601 /* Clear Power Down bit */
2602 err = mv88e6xxx_serdes_read(chip, MII_BMCR, &val);
2603 if (err)
2604 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002605
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002606 if (val & BMCR_PDOWN) {
2607 val &= ~BMCR_PDOWN;
2608 err = mv88e6xxx_serdes_write(chip, MII_BMCR, val);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002609 }
2610
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002611 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002612}
2613
Vivien Didelotfad09c72016-06-21 12:28:20 -04002614static int mv88e6xxx_setup_port(struct mv88e6xxx_chip *chip, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002615{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002616 struct dsa_switch *ds = chip->ds;
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002617 int err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002618 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002619
Vivien Didelotfad09c72016-06-21 12:28:20 -04002620 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2621 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2622 mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip) ||
2623 mv88e6xxx_6065_family(chip) || mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002624 /* MAC Forcing register: don't force link, speed,
2625 * duplex or flow control state to any particular
2626 * values on physical ports, but force the CPU port
2627 * and all DSA ports to their maximum bandwidth and
2628 * full duplex.
2629 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002630 err = mv88e6xxx_port_read(chip, port, PORT_PCS_CTRL, &reg);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002631 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002632 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002633 reg |= PORT_PCS_CTRL_FORCE_LINK |
2634 PORT_PCS_CTRL_LINK_UP |
2635 PORT_PCS_CTRL_DUPLEX_FULL |
2636 PORT_PCS_CTRL_FORCE_DUPLEX;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002637 if (mv88e6xxx_6065_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002638 reg |= PORT_PCS_CTRL_100;
2639 else
2640 reg |= PORT_PCS_CTRL_1000;
2641 } else {
2642 reg |= PORT_PCS_CTRL_UNFORCED;
2643 }
2644
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002645 err = mv88e6xxx_port_write(chip, port, PORT_PCS_CTRL, reg);
2646 if (err)
2647 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002648 }
2649
2650 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2651 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2652 * tunneling, determine priority by looking at 802.1p and IP
2653 * priority fields (IP prio has precedence), and set STP state
2654 * to Forwarding.
2655 *
2656 * If this is the CPU link, use DSA or EDSA tagging depending
2657 * on which tagging mode was configured.
2658 *
2659 * If this is a link to another switch, use DSA tagging mode.
2660 *
2661 * If this is the upstream port for this switch, enable
2662 * forwarding of unknown unicasts and multicasts.
2663 */
2664 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002665 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2666 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2667 mv88e6xxx_6095_family(chip) || mv88e6xxx_6065_family(chip) ||
2668 mv88e6xxx_6185_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002669 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2670 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2671 PORT_CONTROL_STATE_FORWARDING;
2672 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002673 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
Andrew Lunn5377b802016-06-04 21:17:02 +02002674 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002675 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002676 else
2677 reg |= PORT_CONTROL_DSA_TAG;
Jamie Lentinf027e0c2016-08-22 16:01:04 +02002678 reg |= PORT_CONTROL_EGRESS_ADD_TAG |
2679 PORT_CONTROL_FORWARD_UNKNOWN;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002680 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002681 if (dsa_is_dsa_port(ds, port)) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04002682 if (mv88e6xxx_6095_family(chip) ||
2683 mv88e6xxx_6185_family(chip))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002684 reg |= PORT_CONTROL_DSA_TAG;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002685 if (mv88e6xxx_6352_family(chip) ||
2686 mv88e6xxx_6351_family(chip) ||
2687 mv88e6xxx_6165_family(chip) ||
2688 mv88e6xxx_6097_family(chip) ||
2689 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002690 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002691 }
2692
Andrew Lunn54d792f2015-05-06 01:09:47 +02002693 if (port == dsa_upstream_port(ds))
2694 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2695 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2696 }
2697 if (reg) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002698 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL, reg);
2699 if (err)
2700 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002701 }
2702
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002703 /* If this port is connected to a SerDes, make sure the SerDes is not
2704 * powered down.
2705 */
Vivien Didelot09cb7df2016-08-15 17:19:01 -04002706 if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_SERDES)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002707 err = mv88e6xxx_port_read(chip, port, PORT_STATUS, &reg);
2708 if (err)
2709 return err;
2710 reg &= PORT_STATUS_CMODE_MASK;
2711 if ((reg == PORT_STATUS_CMODE_100BASE_X) ||
2712 (reg == PORT_STATUS_CMODE_1000BASE_X) ||
2713 (reg == PORT_STATUS_CMODE_SGMII)) {
2714 err = mv88e6xxx_serdes_power_on(chip);
2715 if (err < 0)
2716 return err;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002717 }
2718 }
2719
Vivien Didelot8efdda42015-08-13 12:52:23 -04002720 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002721 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002722 * untagged frames on this port, do a destination address lookup on all
2723 * received packets as usual, disable ARP mirroring and don't send a
2724 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002725 */
2726 reg = 0;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002727 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2728 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2729 mv88e6xxx_6095_family(chip) || mv88e6xxx_6320_family(chip) ||
2730 mv88e6xxx_6185_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002731 reg = PORT_CONTROL_2_MAP_DA;
2732
Vivien Didelotfad09c72016-06-21 12:28:20 -04002733 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2734 mv88e6xxx_6165_family(chip) || mv88e6xxx_6320_family(chip))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002735 reg |= PORT_CONTROL_2_JUMBO_10240;
2736
Vivien Didelotfad09c72016-06-21 12:28:20 -04002737 if (mv88e6xxx_6095_family(chip) || mv88e6xxx_6185_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002738 /* Set the upstream port this port should use */
2739 reg |= dsa_upstream_port(ds);
2740 /* enable forwarding of unknown multicast addresses to
2741 * the upstream port
2742 */
2743 if (port == dsa_upstream_port(ds))
2744 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2745 }
2746
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002747 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002748
Andrew Lunn54d792f2015-05-06 01:09:47 +02002749 if (reg) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002750 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_2, reg);
2751 if (err)
2752 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002753 }
2754
2755 /* Port Association Vector: when learning source addresses
2756 * of packets, add the address to the address database using
2757 * a port bitmap that has only the bit for this port set and
2758 * the other bits clear.
2759 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002760 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002761 /* Disable learning for CPU port */
2762 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002763 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002764
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002765 err = mv88e6xxx_port_write(chip, port, PORT_ASSOC_VECTOR, reg);
2766 if (err)
2767 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002768
2769 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002770 err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL_2, 0x0000);
2771 if (err)
2772 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002773
Vivien Didelotfad09c72016-06-21 12:28:20 -04002774 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2775 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
2776 mv88e6xxx_6320_family(chip)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002777 /* Do not limit the period of time that this port can
2778 * be paused for by the remote end or the period of
2779 * time that this port can pause the remote end.
2780 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002781 err = mv88e6xxx_port_write(chip, port, PORT_PAUSE_CTRL, 0x0000);
2782 if (err)
2783 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002784
2785 /* Port ATU control: disable limiting the number of
2786 * address database entries that this port is allowed
2787 * to use.
2788 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002789 err = mv88e6xxx_port_write(chip, port, PORT_ATU_CONTROL,
2790 0x0000);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002791 /* Priority Override: disable DA, SA and VTU priority
2792 * override.
2793 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002794 err = mv88e6xxx_port_write(chip, port, PORT_PRI_OVERRIDE,
2795 0x0000);
2796 if (err)
2797 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002798
2799 /* Port Ethertype: use the Ethertype DSA Ethertype
2800 * value.
2801 */
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002802 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002803 err = mv88e6xxx_port_write(chip, port, PORT_ETH_TYPE,
2804 ETH_P_EDSA);
2805 if (err)
2806 return err;
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02002807 }
2808
Andrew Lunn54d792f2015-05-06 01:09:47 +02002809 /* Tag Remap: use an identity 802.1p prio -> switch
2810 * prio mapping.
2811 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002812 err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_0123,
2813 0x3210);
2814 if (err)
2815 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002816
2817 /* Tag Remap 2: use an identity 802.1p prio -> switch
2818 * prio mapping.
2819 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002820 err = mv88e6xxx_port_write(chip, port, PORT_TAG_REGMAP_4567,
2821 0x7654);
2822 if (err)
2823 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002824 }
2825
Jamie Lentin1bc261f2016-08-22 22:47:08 +01002826 /* Rate Control: disable ingress rate limiting. */
Vivien Didelotfad09c72016-06-21 12:28:20 -04002827 if (mv88e6xxx_6352_family(chip) || mv88e6xxx_6351_family(chip) ||
2828 mv88e6xxx_6165_family(chip) || mv88e6xxx_6097_family(chip) ||
Vivien Didelotfad09c72016-06-21 12:28:20 -04002829 mv88e6xxx_6320_family(chip)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002830 err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
2831 0x0001);
2832 if (err)
2833 return err;
Jamie Lentin1bc261f2016-08-22 22:47:08 +01002834 } else if (mv88e6xxx_6185_family(chip) || mv88e6xxx_6095_family(chip)) {
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002835 err = mv88e6xxx_port_write(chip, port, PORT_RATE_CONTROL,
2836 0x0000);
2837 if (err)
2838 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002839 }
2840
Guenter Roeck366f0a02015-03-26 18:36:30 -07002841 /* Port Control 1: disable trunking, disable sending
2842 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002843 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002844 err = mv88e6xxx_port_write(chip, port, PORT_CONTROL_1, 0x0000);
2845 if (err)
2846 return err;
Guenter Roeckd827e882015-03-26 18:36:29 -07002847
Vivien Didelot207afda2016-04-14 14:42:09 -04002848 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002849 * database, and allow bidirectional communication between the
2850 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002851 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002852 err = _mv88e6xxx_port_fid_set(chip, port, 0);
2853 if (err)
2854 return err;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002855
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002856 err = _mv88e6xxx_port_based_vlan_map(chip, port);
2857 if (err)
2858 return err;
Guenter Roeckd827e882015-03-26 18:36:29 -07002859
2860 /* Default VLAN ID and priority: don't set a default VLAN
2861 * ID, and set the default packet priority to zero.
2862 */
Andrew Lunn0e7b9922016-09-21 01:40:31 +02002863 return mv88e6xxx_port_write(chip, port, PORT_DEFAULT_VLAN, 0x0000);
Andrew Lunndbde9e62015-05-06 01:09:48 +02002864}
2865
Vivien Didelota935c052016-09-29 12:21:53 -04002866int mv88e6xxx_g1_set_switch_mac(struct mv88e6xxx_chip *chip, u8 *addr)
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002867{
2868 int err;
2869
Vivien Didelota935c052016-09-29 12:21:53 -04002870 err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002871 if (err)
2872 return err;
2873
Vivien Didelota935c052016-09-29 12:21:53 -04002874 err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002875 if (err)
2876 return err;
2877
Vivien Didelota935c052016-09-29 12:21:53 -04002878 err = mv88e6xxx_g1_write(chip, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
2879 if (err)
2880 return err;
2881
2882 return 0;
Vivien Didelot3b4caa12016-07-18 20:45:34 -04002883}
2884
Vivien Didelotacddbd22016-07-18 20:45:39 -04002885static int mv88e6xxx_g1_set_age_time(struct mv88e6xxx_chip *chip,
2886 unsigned int msecs)
2887{
2888 const unsigned int coeff = chip->info->age_time_coeff;
2889 const unsigned int min = 0x01 * coeff;
2890 const unsigned int max = 0xff * coeff;
2891 u8 age_time;
2892 u16 val;
2893 int err;
2894
2895 if (msecs < min || msecs > max)
2896 return -ERANGE;
2897
2898 /* Round to nearest multiple of coeff */
2899 age_time = (msecs + coeff / 2) / coeff;
2900
Vivien Didelota935c052016-09-29 12:21:53 -04002901 err = mv88e6xxx_g1_read(chip, GLOBAL_ATU_CONTROL, &val);
Vivien Didelotacddbd22016-07-18 20:45:39 -04002902 if (err)
2903 return err;
2904
2905 /* AgeTime is 11:4 bits */
2906 val &= ~0xff0;
2907 val |= age_time << 4;
2908
Vivien Didelota935c052016-09-29 12:21:53 -04002909 return mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL, val);
Vivien Didelotacddbd22016-07-18 20:45:39 -04002910}
2911
Vivien Didelot2cfcd962016-07-18 20:45:40 -04002912static int mv88e6xxx_set_ageing_time(struct dsa_switch *ds,
2913 unsigned int ageing_time)
2914{
Vivien Didelot04bed142016-08-31 18:06:13 -04002915 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot2cfcd962016-07-18 20:45:40 -04002916 int err;
2917
2918 mutex_lock(&chip->reg_lock);
2919 err = mv88e6xxx_g1_set_age_time(chip, ageing_time);
2920 mutex_unlock(&chip->reg_lock);
2921
2922 return err;
2923}
2924
Vivien Didelot97299342016-07-18 20:45:30 -04002925static int mv88e6xxx_g1_setup(struct mv88e6xxx_chip *chip)
Vivien Didelot08a01262016-05-09 13:22:50 -04002926{
Vivien Didelotfad09c72016-06-21 12:28:20 -04002927 struct dsa_switch *ds = chip->ds;
Vivien Didelotb0745e872016-05-09 13:22:53 -04002928 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002929 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002930 int err;
Vivien Didelot08a01262016-05-09 13:22:50 -04002931
Vivien Didelot119477b2016-05-09 13:22:51 -04002932 /* Enable the PHY Polling Unit if present, don't discard any packets,
2933 * and mask all interrupt sources.
2934 */
Andrew Lunndc30c352016-10-16 19:56:49 +02002935 err = mv88e6xxx_g1_read(chip, GLOBAL_CONTROL, &reg);
2936 if (err < 0)
2937 return err;
2938
2939 reg &= ~GLOBAL_CONTROL_PPU_ENABLE;
Vivien Didelotfad09c72016-06-21 12:28:20 -04002940 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU) ||
2941 mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU_ACTIVE))
Vivien Didelot119477b2016-05-09 13:22:51 -04002942 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2943
Vivien Didelota935c052016-09-29 12:21:53 -04002944 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL, reg);
Vivien Didelot119477b2016-05-09 13:22:51 -04002945 if (err)
2946 return err;
2947
Vivien Didelotb0745e872016-05-09 13:22:53 -04002948 /* Configure the upstream port, and configure it as the port to which
2949 * ingress and egress and ARP monitor frames are to be sent.
2950 */
2951 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2952 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2953 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
Vivien Didelota935c052016-09-29 12:21:53 -04002954 err = mv88e6xxx_g1_write(chip, GLOBAL_MONITOR_CONTROL, reg);
Vivien Didelotb0745e872016-05-09 13:22:53 -04002955 if (err)
2956 return err;
2957
Vivien Didelot50484ff2016-05-09 13:22:54 -04002958 /* Disable remote management, and set the switch's DSA device number. */
Vivien Didelota935c052016-09-29 12:21:53 -04002959 err = mv88e6xxx_g1_write(chip, GLOBAL_CONTROL_2,
2960 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2961 (ds->index & 0x1f));
Vivien Didelot50484ff2016-05-09 13:22:54 -04002962 if (err)
2963 return err;
2964
Vivien Didelotacddbd22016-07-18 20:45:39 -04002965 /* Clear all the VTU and STU entries */
2966 err = _mv88e6xxx_vtu_stu_flush(chip);
2967 if (err < 0)
2968 return err;
2969
Vivien Didelot08a01262016-05-09 13:22:50 -04002970 /* Set the default address aging time to 5 minutes, and
2971 * enable address learn messages to be sent to all message
2972 * ports.
2973 */
Vivien Didelota935c052016-09-29 12:21:53 -04002974 err = mv88e6xxx_g1_write(chip, GLOBAL_ATU_CONTROL,
2975 GLOBAL_ATU_CONTROL_LEARN2ALL);
Vivien Didelot08a01262016-05-09 13:22:50 -04002976 if (err)
2977 return err;
2978
Vivien Didelotacddbd22016-07-18 20:45:39 -04002979 err = mv88e6xxx_g1_set_age_time(chip, 300000);
2980 if (err)
Vivien Didelot97299342016-07-18 20:45:30 -04002981 return err;
2982
2983 /* Clear all ATU entries */
2984 err = _mv88e6xxx_atu_flush(chip, 0, true);
2985 if (err)
2986 return err;
2987
Vivien Didelot08a01262016-05-09 13:22:50 -04002988 /* Configure the IP ToS mapping registers. */
Vivien Didelota935c052016-09-29 12:21:53 -04002989 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_0, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002990 if (err)
2991 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04002992 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_1, 0x0000);
Vivien Didelot08a01262016-05-09 13:22:50 -04002993 if (err)
2994 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04002995 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_2, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002996 if (err)
2997 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04002998 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_3, 0x5555);
Vivien Didelot08a01262016-05-09 13:22:50 -04002999 if (err)
3000 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04003001 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_4, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04003002 if (err)
3003 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04003004 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_5, 0xaaaa);
Vivien Didelot08a01262016-05-09 13:22:50 -04003005 if (err)
3006 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04003007 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_6, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04003008 if (err)
3009 return err;
Vivien Didelota935c052016-09-29 12:21:53 -04003010 err = mv88e6xxx_g1_write(chip, GLOBAL_IP_PRI_7, 0xffff);
Vivien Didelot08a01262016-05-09 13:22:50 -04003011 if (err)
3012 return err;
3013
3014 /* Configure the IEEE 802.1p priority mapping register. */
Vivien Didelota935c052016-09-29 12:21:53 -04003015 err = mv88e6xxx_g1_write(chip, GLOBAL_IEEE_PRI, 0xfa41);
Vivien Didelot08a01262016-05-09 13:22:50 -04003016 if (err)
3017 return err;
3018
Vivien Didelot97299342016-07-18 20:45:30 -04003019 /* Clear the statistics counters for all ports */
Vivien Didelota935c052016-09-29 12:21:53 -04003020 err = mv88e6xxx_g1_write(chip, GLOBAL_STATS_OP,
3021 GLOBAL_STATS_OP_FLUSH_ALL);
Vivien Didelot97299342016-07-18 20:45:30 -04003022 if (err)
3023 return err;
3024
3025 /* Wait for the flush to complete. */
3026 err = _mv88e6xxx_stats_wait(chip);
3027 if (err)
3028 return err;
3029
3030 return 0;
3031}
3032
Vivien Didelotf81ec902016-05-09 13:22:58 -04003033static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003034{
Vivien Didelot04bed142016-08-31 18:06:13 -04003035 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot552238b2016-05-09 13:22:49 -04003036 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003037 int i;
3038
Vivien Didelotfad09c72016-06-21 12:28:20 -04003039 chip->ds = ds;
3040 ds->slave_mii_bus = chip->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003041
Vivien Didelotfad09c72016-06-21 12:28:20 -04003042 mutex_lock(&chip->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04003043
Vivien Didelot97299342016-07-18 20:45:30 -04003044 /* Setup Switch Port Registers */
Vivien Didelot370b4ff2016-09-29 12:21:57 -04003045 for (i = 0; i < mv88e6xxx_num_ports(chip); i++) {
Vivien Didelot97299342016-07-18 20:45:30 -04003046 err = mv88e6xxx_setup_port(chip, i);
3047 if (err)
3048 goto unlock;
3049 }
3050
3051 /* Setup Switch Global 1 Registers */
3052 err = mv88e6xxx_g1_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003053 if (err)
3054 goto unlock;
3055
Vivien Didelot97299342016-07-18 20:45:30 -04003056 /* Setup Switch Global 2 Registers */
3057 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_GLOBAL2)) {
3058 err = mv88e6xxx_g2_setup(chip);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003059 if (err)
3060 goto unlock;
3061 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003062
Vivien Didelot6b17e862015-08-13 12:52:18 -04003063unlock:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003064 mutex_unlock(&chip->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02003065
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003066 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003067}
3068
Vivien Didelot3b4caa12016-07-18 20:45:34 -04003069static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
3070{
Vivien Didelot04bed142016-08-31 18:06:13 -04003071 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot3b4caa12016-07-18 20:45:34 -04003072 int err;
3073
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003074 if (!chip->info->ops->set_switch_mac)
3075 return -EOPNOTSUPP;
3076
Vivien Didelot3b4caa12016-07-18 20:45:34 -04003077 mutex_lock(&chip->reg_lock);
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003078 err = chip->info->ops->set_switch_mac(chip, addr);
Vivien Didelot3b4caa12016-07-18 20:45:34 -04003079 mutex_unlock(&chip->reg_lock);
3080
3081 return err;
3082}
3083
Vivien Didelote57e5e72016-08-15 17:19:00 -04003084static int mv88e6xxx_mdio_read(struct mii_bus *bus, int phy, int reg)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003085{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003086 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04003087 u16 val;
3088 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003089
Vivien Didelot370b4ff2016-09-29 12:21:57 -04003090 if (phy >= mv88e6xxx_num_ports(chip))
Andrew Lunn158bc062016-04-28 21:24:06 -04003091 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003092
Vivien Didelotfad09c72016-06-21 12:28:20 -04003093 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003094 err = mv88e6xxx_phy_read(chip, phy, reg, &val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003095 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003096
3097 return err ? err : val;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003098}
3099
Vivien Didelote57e5e72016-08-15 17:19:00 -04003100static int mv88e6xxx_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003101{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003102 struct mv88e6xxx_chip *chip = bus->priv;
Vivien Didelote57e5e72016-08-15 17:19:00 -04003103 int err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003104
Vivien Didelot370b4ff2016-09-29 12:21:57 -04003105 if (phy >= mv88e6xxx_num_ports(chip))
Andrew Lunn158bc062016-04-28 21:24:06 -04003106 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003107
Vivien Didelotfad09c72016-06-21 12:28:20 -04003108 mutex_lock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003109 err = mv88e6xxx_phy_write(chip, phy, reg, val);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003110 mutex_unlock(&chip->reg_lock);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003111
3112 return err;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003113}
3114
Vivien Didelotfad09c72016-06-21 12:28:20 -04003115static int mv88e6xxx_mdio_register(struct mv88e6xxx_chip *chip,
Andrew Lunnb516d452016-06-04 21:17:06 +02003116 struct device_node *np)
3117{
3118 static int index;
3119 struct mii_bus *bus;
3120 int err;
3121
Andrew Lunnb516d452016-06-04 21:17:06 +02003122 if (np)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003123 chip->mdio_np = of_get_child_by_name(np, "mdio");
Andrew Lunnb516d452016-06-04 21:17:06 +02003124
Vivien Didelotfad09c72016-06-21 12:28:20 -04003125 bus = devm_mdiobus_alloc(chip->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003126 if (!bus)
3127 return -ENOMEM;
3128
Vivien Didelotfad09c72016-06-21 12:28:20 -04003129 bus->priv = (void *)chip;
Andrew Lunnb516d452016-06-04 21:17:06 +02003130 if (np) {
3131 bus->name = np->full_name;
3132 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3133 } else {
3134 bus->name = "mv88e6xxx SMI";
3135 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3136 }
3137
3138 bus->read = mv88e6xxx_mdio_read;
3139 bus->write = mv88e6xxx_mdio_write;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003140 bus->parent = chip->dev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003141
Vivien Didelotfad09c72016-06-21 12:28:20 -04003142 if (chip->mdio_np)
3143 err = of_mdiobus_register(bus, chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003144 else
3145 err = mdiobus_register(bus);
3146 if (err) {
Vivien Didelotfad09c72016-06-21 12:28:20 -04003147 dev_err(chip->dev, "Cannot register MDIO bus (%d)\n", err);
Andrew Lunnb516d452016-06-04 21:17:06 +02003148 goto out;
3149 }
Vivien Didelotfad09c72016-06-21 12:28:20 -04003150 chip->mdio_bus = bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003151
3152 return 0;
3153
3154out:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003155 if (chip->mdio_np)
3156 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003157
3158 return err;
3159}
3160
Vivien Didelotfad09c72016-06-21 12:28:20 -04003161static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_chip *chip)
Andrew Lunnb516d452016-06-04 21:17:06 +02003162
3163{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003164 struct mii_bus *bus = chip->mdio_bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003165
3166 mdiobus_unregister(bus);
3167
Vivien Didelotfad09c72016-06-21 12:28:20 -04003168 if (chip->mdio_np)
3169 of_node_put(chip->mdio_np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003170}
3171
Guenter Roeckc22995c2015-07-25 09:42:28 -07003172#ifdef CONFIG_NET_DSA_HWMON
3173
3174static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3175{
Vivien Didelot04bed142016-08-31 18:06:13 -04003176 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot9c938292016-08-15 17:19:02 -04003177 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003178 int ret;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003179
3180 *temp = 0;
3181
Vivien Didelotfad09c72016-06-21 12:28:20 -04003182 mutex_lock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003183
Vivien Didelot9c938292016-08-15 17:19:02 -04003184 ret = mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003185 if (ret < 0)
3186 goto error;
3187
3188 /* Enable temperature sensor */
Vivien Didelot9c938292016-08-15 17:19:02 -04003189 ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003190 if (ret < 0)
3191 goto error;
3192
Vivien Didelot9c938292016-08-15 17:19:02 -04003193 ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003194 if (ret < 0)
3195 goto error;
3196
3197 /* Wait for temperature to stabilize */
3198 usleep_range(10000, 12000);
3199
Vivien Didelot9c938292016-08-15 17:19:02 -04003200 ret = mv88e6xxx_phy_read(chip, 0x0, 0x1a, &val);
3201 if (ret < 0)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003202 goto error;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003203
3204 /* Disable temperature sensor */
Vivien Didelot9c938292016-08-15 17:19:02 -04003205 ret = mv88e6xxx_phy_write(chip, 0x0, 0x1a, val & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003206 if (ret < 0)
3207 goto error;
3208
3209 *temp = ((val & 0x1f) - 5) * 5;
3210
3211error:
Vivien Didelot9c938292016-08-15 17:19:02 -04003212 mv88e6xxx_phy_write(chip, 0x0, 0x16, 0x0);
Vivien Didelotfad09c72016-06-21 12:28:20 -04003213 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003214 return ret;
3215}
3216
3217static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3218{
Vivien Didelot04bed142016-08-31 18:06:13 -04003219 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003220 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003221 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003222 int ret;
3223
3224 *temp = 0;
3225
Vivien Didelot9c938292016-08-15 17:19:02 -04003226 mutex_lock(&chip->reg_lock);
3227 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 27, &val);
3228 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003229 if (ret < 0)
3230 return ret;
3231
Vivien Didelot9c938292016-08-15 17:19:02 -04003232 *temp = (val & 0xff) - 25;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003233
3234 return 0;
3235}
3236
Vivien Didelotf81ec902016-05-09 13:22:58 -04003237static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003238{
Vivien Didelot04bed142016-08-31 18:06:13 -04003239 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn158bc062016-04-28 21:24:06 -04003240
Vivien Didelotfad09c72016-06-21 12:28:20 -04003241 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP))
Vivien Didelot6594f612016-05-09 13:22:42 -04003242 return -EOPNOTSUPP;
3243
Vivien Didelotfad09c72016-06-21 12:28:20 -04003244 if (mv88e6xxx_6320_family(chip) || mv88e6xxx_6352_family(chip))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003245 return mv88e63xx_get_temp(ds, temp);
3246
3247 return mv88e61xx_get_temp(ds, temp);
3248}
3249
Vivien Didelotf81ec902016-05-09 13:22:58 -04003250static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003251{
Vivien Didelot04bed142016-08-31 18:06:13 -04003252 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003253 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003254 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003255 int ret;
3256
Vivien Didelotfad09c72016-06-21 12:28:20 -04003257 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003258 return -EOPNOTSUPP;
3259
3260 *temp = 0;
3261
Vivien Didelot9c938292016-08-15 17:19:02 -04003262 mutex_lock(&chip->reg_lock);
3263 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3264 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003265 if (ret < 0)
3266 return ret;
3267
Vivien Didelot9c938292016-08-15 17:19:02 -04003268 *temp = (((val >> 8) & 0x1f) * 5) - 25;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003269
3270 return 0;
3271}
3272
Vivien Didelotf81ec902016-05-09 13:22:58 -04003273static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003274{
Vivien Didelot04bed142016-08-31 18:06:13 -04003275 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003276 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003277 u16 val;
3278 int err;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003279
Vivien Didelotfad09c72016-06-21 12:28:20 -04003280 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003281 return -EOPNOTSUPP;
3282
Vivien Didelot9c938292016-08-15 17:19:02 -04003283 mutex_lock(&chip->reg_lock);
3284 err = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3285 if (err)
3286 goto unlock;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003287 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Vivien Didelot9c938292016-08-15 17:19:02 -04003288 err = mv88e6xxx_phy_page_write(chip, phy, 6, 26,
3289 (val & 0xe0ff) | (temp << 8));
3290unlock:
3291 mutex_unlock(&chip->reg_lock);
3292
3293 return err;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003294}
3295
Vivien Didelotf81ec902016-05-09 13:22:58 -04003296static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003297{
Vivien Didelot04bed142016-08-31 18:06:13 -04003298 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003299 int phy = mv88e6xxx_6320_family(chip) ? 3 : 0;
Vivien Didelot9c938292016-08-15 17:19:02 -04003300 u16 val;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003301 int ret;
3302
Vivien Didelotfad09c72016-06-21 12:28:20 -04003303 if (!mv88e6xxx_has(chip, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003304 return -EOPNOTSUPP;
3305
3306 *alarm = false;
3307
Vivien Didelot9c938292016-08-15 17:19:02 -04003308 mutex_lock(&chip->reg_lock);
3309 ret = mv88e6xxx_phy_page_read(chip, phy, 6, 26, &val);
3310 mutex_unlock(&chip->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003311 if (ret < 0)
3312 return ret;
3313
Vivien Didelot9c938292016-08-15 17:19:02 -04003314 *alarm = !!(val & 0x40);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003315
3316 return 0;
3317}
3318#endif /* CONFIG_NET_DSA_HWMON */
3319
Vivien Didelot855b1932016-07-20 18:18:35 -04003320static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
3321{
Vivien Didelot04bed142016-08-31 18:06:13 -04003322 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot855b1932016-07-20 18:18:35 -04003323
3324 return chip->eeprom_len;
3325}
3326
Vivien Didelot855b1932016-07-20 18:18:35 -04003327static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
3328 struct ethtool_eeprom *eeprom, u8 *data)
3329{
Vivien Didelot04bed142016-08-31 18:06:13 -04003330 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot855b1932016-07-20 18:18:35 -04003331 int err;
3332
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003333 if (!chip->info->ops->get_eeprom)
3334 return -EOPNOTSUPP;
3335
Vivien Didelot855b1932016-07-20 18:18:35 -04003336 mutex_lock(&chip->reg_lock);
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003337 err = chip->info->ops->get_eeprom(chip, eeprom, data);
Vivien Didelot855b1932016-07-20 18:18:35 -04003338 mutex_unlock(&chip->reg_lock);
3339
3340 if (err)
3341 return err;
3342
3343 eeprom->magic = 0xc3ec4951;
3344
3345 return 0;
3346}
3347
Vivien Didelot855b1932016-07-20 18:18:35 -04003348static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
3349 struct ethtool_eeprom *eeprom, u8 *data)
3350{
Vivien Didelot04bed142016-08-31 18:06:13 -04003351 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot855b1932016-07-20 18:18:35 -04003352 int err;
3353
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003354 if (!chip->info->ops->set_eeprom)
3355 return -EOPNOTSUPP;
3356
Vivien Didelot855b1932016-07-20 18:18:35 -04003357 if (eeprom->magic != 0xc3ec4951)
3358 return -EINVAL;
3359
3360 mutex_lock(&chip->reg_lock);
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003361 err = chip->info->ops->set_eeprom(chip, eeprom, data);
Vivien Didelot855b1932016-07-20 18:18:35 -04003362 mutex_unlock(&chip->reg_lock);
3363
3364 return err;
3365}
3366
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003367static const struct mv88e6xxx_ops mv88e6085_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003368 .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003369 .phy_read = mv88e6xxx_phy_ppu_read,
3370 .phy_write = mv88e6xxx_phy_ppu_write,
3371};
3372
3373static const struct mv88e6xxx_ops mv88e6095_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003374 .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003375 .phy_read = mv88e6xxx_phy_ppu_read,
3376 .phy_write = mv88e6xxx_phy_ppu_write,
3377};
3378
3379static const struct mv88e6xxx_ops mv88e6123_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003380 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003381 .phy_read = mv88e6xxx_read,
3382 .phy_write = mv88e6xxx_write,
3383};
3384
3385static const struct mv88e6xxx_ops mv88e6131_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003386 .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003387 .phy_read = mv88e6xxx_phy_ppu_read,
3388 .phy_write = mv88e6xxx_phy_ppu_write,
3389};
3390
3391static const struct mv88e6xxx_ops mv88e6161_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003392 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003393 .phy_read = mv88e6xxx_read,
3394 .phy_write = mv88e6xxx_write,
3395};
3396
3397static const struct mv88e6xxx_ops mv88e6165_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003398 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003399 .phy_read = mv88e6xxx_read,
3400 .phy_write = mv88e6xxx_write,
3401};
3402
3403static const struct mv88e6xxx_ops mv88e6171_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003404 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003405 .phy_read = mv88e6xxx_g2_smi_phy_read,
3406 .phy_write = mv88e6xxx_g2_smi_phy_write,
3407};
3408
3409static const struct mv88e6xxx_ops mv88e6172_ops = {
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003410 .get_eeprom = mv88e6xxx_g2_get_eeprom16,
3411 .set_eeprom = mv88e6xxx_g2_set_eeprom16,
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003412 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003413 .phy_read = mv88e6xxx_g2_smi_phy_read,
3414 .phy_write = mv88e6xxx_g2_smi_phy_write,
3415};
3416
3417static const struct mv88e6xxx_ops mv88e6175_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003418 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003419 .phy_read = mv88e6xxx_g2_smi_phy_read,
3420 .phy_write = mv88e6xxx_g2_smi_phy_write,
3421};
3422
3423static const struct mv88e6xxx_ops mv88e6176_ops = {
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003424 .get_eeprom = mv88e6xxx_g2_get_eeprom16,
3425 .set_eeprom = mv88e6xxx_g2_set_eeprom16,
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003426 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003427 .phy_read = mv88e6xxx_g2_smi_phy_read,
3428 .phy_write = mv88e6xxx_g2_smi_phy_write,
3429};
3430
3431static const struct mv88e6xxx_ops mv88e6185_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003432 .set_switch_mac = mv88e6xxx_g1_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003433 .phy_read = mv88e6xxx_phy_ppu_read,
3434 .phy_write = mv88e6xxx_phy_ppu_write,
3435};
3436
3437static const struct mv88e6xxx_ops mv88e6240_ops = {
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003438 .get_eeprom = mv88e6xxx_g2_get_eeprom16,
3439 .set_eeprom = mv88e6xxx_g2_set_eeprom16,
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003440 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003441 .phy_read = mv88e6xxx_g2_smi_phy_read,
3442 .phy_write = mv88e6xxx_g2_smi_phy_write,
3443};
3444
3445static const struct mv88e6xxx_ops mv88e6320_ops = {
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003446 .get_eeprom = mv88e6xxx_g2_get_eeprom16,
3447 .set_eeprom = mv88e6xxx_g2_set_eeprom16,
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003448 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003449 .phy_read = mv88e6xxx_g2_smi_phy_read,
3450 .phy_write = mv88e6xxx_g2_smi_phy_write,
3451};
3452
3453static const struct mv88e6xxx_ops mv88e6321_ops = {
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003454 .get_eeprom = mv88e6xxx_g2_get_eeprom16,
3455 .set_eeprom = mv88e6xxx_g2_set_eeprom16,
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003456 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003457 .phy_read = mv88e6xxx_g2_smi_phy_read,
3458 .phy_write = mv88e6xxx_g2_smi_phy_write,
3459};
3460
3461static const struct mv88e6xxx_ops mv88e6350_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003462 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003463 .phy_read = mv88e6xxx_g2_smi_phy_read,
3464 .phy_write = mv88e6xxx_g2_smi_phy_write,
3465};
3466
3467static const struct mv88e6xxx_ops mv88e6351_ops = {
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003468 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003469 .phy_read = mv88e6xxx_g2_smi_phy_read,
3470 .phy_write = mv88e6xxx_g2_smi_phy_write,
3471};
3472
3473static const struct mv88e6xxx_ops mv88e6352_ops = {
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04003474 .get_eeprom = mv88e6xxx_g2_get_eeprom16,
3475 .set_eeprom = mv88e6xxx_g2_set_eeprom16,
Vivien Didelotb073d4e2016-09-29 12:22:01 -04003476 .set_switch_mac = mv88e6xxx_g2_set_switch_mac,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003477 .phy_read = mv88e6xxx_g2_smi_phy_read,
3478 .phy_write = mv88e6xxx_g2_smi_phy_write,
3479};
3480
Vivien Didelotf81ec902016-05-09 13:22:58 -04003481static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3482 [MV88E6085] = {
3483 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3484 .family = MV88E6XXX_FAMILY_6097,
3485 .name = "Marvell 88E6085",
3486 .num_databases = 4096,
3487 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003488 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003489 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003490 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003491 .g1_irqs = 8,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003492 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003493 .ops = &mv88e6085_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003494 },
3495
3496 [MV88E6095] = {
3497 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3498 .family = MV88E6XXX_FAMILY_6095,
3499 .name = "Marvell 88E6095/88E6095F",
3500 .num_databases = 256,
3501 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003502 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003503 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003504 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003505 .g1_irqs = 8,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003506 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003507 .ops = &mv88e6095_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003508 },
3509
3510 [MV88E6123] = {
3511 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3512 .family = MV88E6XXX_FAMILY_6165,
3513 .name = "Marvell 88E6123",
3514 .num_databases = 4096,
3515 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003516 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003517 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003518 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003519 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003520 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003521 .ops = &mv88e6123_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003522 },
3523
3524 [MV88E6131] = {
3525 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3526 .family = MV88E6XXX_FAMILY_6185,
3527 .name = "Marvell 88E6131",
3528 .num_databases = 256,
3529 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003530 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003531 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003532 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003533 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003534 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003535 .ops = &mv88e6131_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003536 },
3537
3538 [MV88E6161] = {
3539 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3540 .family = MV88E6XXX_FAMILY_6165,
3541 .name = "Marvell 88E6161",
3542 .num_databases = 4096,
3543 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003544 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003545 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003546 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003547 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003548 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003549 .ops = &mv88e6161_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003550 },
3551
3552 [MV88E6165] = {
3553 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3554 .family = MV88E6XXX_FAMILY_6165,
3555 .name = "Marvell 88E6165",
3556 .num_databases = 4096,
3557 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003558 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003559 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003560 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003561 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003562 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003563 .ops = &mv88e6165_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003564 },
3565
3566 [MV88E6171] = {
3567 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3568 .family = MV88E6XXX_FAMILY_6351,
3569 .name = "Marvell 88E6171",
3570 .num_databases = 4096,
3571 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003572 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003573 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003574 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003575 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003576 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003577 .ops = &mv88e6171_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003578 },
3579
3580 [MV88E6172] = {
3581 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3582 .family = MV88E6XXX_FAMILY_6352,
3583 .name = "Marvell 88E6172",
3584 .num_databases = 4096,
3585 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003586 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003587 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003588 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003589 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003590 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003591 .ops = &mv88e6172_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003592 },
3593
3594 [MV88E6175] = {
3595 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3596 .family = MV88E6XXX_FAMILY_6351,
3597 .name = "Marvell 88E6175",
3598 .num_databases = 4096,
3599 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003600 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003601 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003602 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003603 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003604 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003605 .ops = &mv88e6175_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003606 },
3607
3608 [MV88E6176] = {
3609 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3610 .family = MV88E6XXX_FAMILY_6352,
3611 .name = "Marvell 88E6176",
3612 .num_databases = 4096,
3613 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003614 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003615 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003616 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003617 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003618 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003619 .ops = &mv88e6176_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003620 },
3621
3622 [MV88E6185] = {
3623 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3624 .family = MV88E6XXX_FAMILY_6185,
3625 .name = "Marvell 88E6185",
3626 .num_databases = 256,
3627 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003628 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003629 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003630 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003631 .g1_irqs = 8,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003632 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003633 .ops = &mv88e6185_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003634 },
3635
3636 [MV88E6240] = {
3637 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3638 .family = MV88E6XXX_FAMILY_6352,
3639 .name = "Marvell 88E6240",
3640 .num_databases = 4096,
3641 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003642 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003643 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003644 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003645 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003646 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003647 .ops = &mv88e6240_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003648 },
3649
3650 [MV88E6320] = {
3651 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3652 .family = MV88E6XXX_FAMILY_6320,
3653 .name = "Marvell 88E6320",
3654 .num_databases = 4096,
3655 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003656 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003657 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003658 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003659 .g1_irqs = 8,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003660 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003661 .ops = &mv88e6320_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003662 },
3663
3664 [MV88E6321] = {
3665 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3666 .family = MV88E6XXX_FAMILY_6320,
3667 .name = "Marvell 88E6321",
3668 .num_databases = 4096,
3669 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003670 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003671 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003672 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003673 .g1_irqs = 8,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003674 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003675 .ops = &mv88e6321_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003676 },
3677
3678 [MV88E6350] = {
3679 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3680 .family = MV88E6XXX_FAMILY_6351,
3681 .name = "Marvell 88E6350",
3682 .num_databases = 4096,
3683 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003684 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003685 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003686 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003687 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003688 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003689 .ops = &mv88e6350_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003690 },
3691
3692 [MV88E6351] = {
3693 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3694 .family = MV88E6XXX_FAMILY_6351,
3695 .name = "Marvell 88E6351",
3696 .num_databases = 4096,
3697 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003698 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003699 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003700 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003701 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003702 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003703 .ops = &mv88e6351_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003704 },
3705
3706 [MV88E6352] = {
3707 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3708 .family = MV88E6XXX_FAMILY_6352,
3709 .name = "Marvell 88E6352",
3710 .num_databases = 4096,
3711 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003712 .port_base_addr = 0x10,
Vivien Didelota935c052016-09-29 12:21:53 -04003713 .global1_addr = 0x1b,
Vivien Didelotacddbd22016-07-18 20:45:39 -04003714 .age_time_coeff = 15000,
Andrew Lunndc30c352016-10-16 19:56:49 +02003715 .g1_irqs = 9,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003716 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003717 .ops = &mv88e6352_ops,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003718 },
3719};
3720
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003721static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003722{
Vivien Didelota439c062016-04-17 13:23:58 -04003723 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003724
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003725 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3726 if (mv88e6xxx_table[i].prod_num == prod_num)
3727 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003728
Vivien Didelotb9b37712015-10-30 19:39:48 -04003729 return NULL;
3730}
3731
Vivien Didelotfad09c72016-06-21 12:28:20 -04003732static int mv88e6xxx_detect(struct mv88e6xxx_chip *chip)
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003733{
3734 const struct mv88e6xxx_info *info;
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003735 unsigned int prod_num, rev;
3736 u16 id;
3737 int err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003738
Vivien Didelot8f6345b2016-07-20 18:18:36 -04003739 mutex_lock(&chip->reg_lock);
3740 err = mv88e6xxx_port_read(chip, 0, PORT_SWITCH_ID, &id);
3741 mutex_unlock(&chip->reg_lock);
3742 if (err)
3743 return err;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003744
3745 prod_num = (id & 0xfff0) >> 4;
3746 rev = id & 0x000f;
3747
3748 info = mv88e6xxx_lookup_info(prod_num);
3749 if (!info)
3750 return -ENODEV;
3751
Vivien Didelotcaac8542016-06-20 13:14:09 -04003752 /* Update the compatible info with the probed one */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003753 chip->info = info;
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003754
Vivien Didelotca070c12016-09-02 14:45:34 -04003755 err = mv88e6xxx_g2_require(chip);
3756 if (err)
3757 return err;
3758
Vivien Didelotfad09c72016-06-21 12:28:20 -04003759 dev_info(chip->dev, "switch 0x%x detected: %s, revision %u\n",
3760 chip->info->prod_num, chip->info->name, rev);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003761
3762 return 0;
3763}
3764
Vivien Didelotfad09c72016-06-21 12:28:20 -04003765static struct mv88e6xxx_chip *mv88e6xxx_alloc_chip(struct device *dev)
Vivien Didelot469d7292016-06-20 13:14:06 -04003766{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003767 struct mv88e6xxx_chip *chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003768
Vivien Didelotfad09c72016-06-21 12:28:20 -04003769 chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
3770 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003771 return NULL;
3772
Vivien Didelotfad09c72016-06-21 12:28:20 -04003773 chip->dev = dev;
Vivien Didelot469d7292016-06-20 13:14:06 -04003774
Vivien Didelotfad09c72016-06-21 12:28:20 -04003775 mutex_init(&chip->reg_lock);
Vivien Didelot469d7292016-06-20 13:14:06 -04003776
Vivien Didelotfad09c72016-06-21 12:28:20 -04003777 return chip;
Vivien Didelot469d7292016-06-20 13:14:06 -04003778}
3779
Vivien Didelote57e5e72016-08-15 17:19:00 -04003780static void mv88e6xxx_phy_init(struct mv88e6xxx_chip *chip)
3781{
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003782 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
Vivien Didelote57e5e72016-08-15 17:19:00 -04003783 mv88e6xxx_ppu_state_init(chip);
Vivien Didelote57e5e72016-08-15 17:19:00 -04003784}
3785
Andrew Lunn930188c2016-08-22 16:01:03 +02003786static void mv88e6xxx_phy_destroy(struct mv88e6xxx_chip *chip)
3787{
Vivien Didelotb3469dd2016-09-29 12:22:00 -04003788 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_PPU))
Andrew Lunn930188c2016-08-22 16:01:03 +02003789 mv88e6xxx_ppu_state_destroy(chip);
Andrew Lunn930188c2016-08-22 16:01:03 +02003790}
3791
Vivien Didelotfad09c72016-06-21 12:28:20 -04003792static int mv88e6xxx_smi_init(struct mv88e6xxx_chip *chip,
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003793 struct mii_bus *bus, int sw_addr)
3794{
3795 /* ADDR[0] pin is unavailable externally and considered zero */
3796 if (sw_addr & 0x1)
3797 return -EINVAL;
3798
Vivien Didelot914b32f2016-06-20 13:14:11 -04003799 if (sw_addr == 0)
Vivien Didelotfad09c72016-06-21 12:28:20 -04003800 chip->smi_ops = &mv88e6xxx_smi_single_chip_ops;
Vivien Didelota0ffff22016-08-15 17:18:58 -04003801 else if (mv88e6xxx_has(chip, MV88E6XXX_FLAGS_MULTI_CHIP))
Vivien Didelotfad09c72016-06-21 12:28:20 -04003802 chip->smi_ops = &mv88e6xxx_smi_multi_chip_ops;
Vivien Didelot914b32f2016-06-20 13:14:11 -04003803 else
3804 return -EINVAL;
3805
Vivien Didelotfad09c72016-06-21 12:28:20 -04003806 chip->bus = bus;
3807 chip->sw_addr = sw_addr;
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003808
3809 return 0;
3810}
3811
Andrew Lunn7b314362016-08-22 16:01:01 +02003812static enum dsa_tag_protocol mv88e6xxx_get_tag_protocol(struct dsa_switch *ds)
3813{
Vivien Didelot04bed142016-08-31 18:06:13 -04003814 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn2bbb33b2016-08-22 16:01:02 +02003815
3816 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_EDSA))
3817 return DSA_TAG_PROTO_EDSA;
3818
3819 return DSA_TAG_PROTO_DSA;
Andrew Lunn7b314362016-08-22 16:01:01 +02003820}
3821
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003822static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3823 struct device *host_dev, int sw_addr,
3824 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003825{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003826 struct mv88e6xxx_chip *chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003827 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003828 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003829
Vivien Didelota439c062016-04-17 13:23:58 -04003830 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003831 if (!bus)
3832 return NULL;
3833
Vivien Didelotfad09c72016-06-21 12:28:20 -04003834 chip = mv88e6xxx_alloc_chip(dsa_dev);
3835 if (!chip)
Vivien Didelot469d7292016-06-20 13:14:06 -04003836 return NULL;
3837
Vivien Didelotcaac8542016-06-20 13:14:09 -04003838 /* Legacy SMI probing will only support chips similar to 88E6085 */
Vivien Didelotfad09c72016-06-21 12:28:20 -04003839 chip->info = &mv88e6xxx_table[MV88E6085];
Vivien Didelotcaac8542016-06-20 13:14:09 -04003840
Vivien Didelotfad09c72016-06-21 12:28:20 -04003841 err = mv88e6xxx_smi_init(chip, bus, sw_addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003842 if (err)
3843 goto free;
3844
Vivien Didelotfad09c72016-06-21 12:28:20 -04003845 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003846 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003847 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003848
Andrew Lunndc30c352016-10-16 19:56:49 +02003849 mutex_lock(&chip->reg_lock);
3850 err = mv88e6xxx_switch_reset(chip);
3851 mutex_unlock(&chip->reg_lock);
3852 if (err)
3853 goto free;
3854
Vivien Didelote57e5e72016-08-15 17:19:00 -04003855 mv88e6xxx_phy_init(chip);
3856
Vivien Didelotfad09c72016-06-21 12:28:20 -04003857 err = mv88e6xxx_mdio_register(chip, NULL);
Andrew Lunnb516d452016-06-04 21:17:06 +02003858 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003859 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003860
Vivien Didelotfad09c72016-06-21 12:28:20 -04003861 *priv = chip;
Vivien Didelota439c062016-04-17 13:23:58 -04003862
Vivien Didelotfad09c72016-06-21 12:28:20 -04003863 return chip->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003864free:
Vivien Didelotfad09c72016-06-21 12:28:20 -04003865 devm_kfree(dsa_dev, chip);
Vivien Didelot469d7292016-06-20 13:14:06 -04003866
3867 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003868}
3869
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003870static int mv88e6xxx_port_mdb_prepare(struct dsa_switch *ds, int port,
3871 const struct switchdev_obj_port_mdb *mdb,
3872 struct switchdev_trans *trans)
3873{
3874 /* We don't need any dynamic resource from the kernel (yet),
3875 * so skip the prepare phase.
3876 */
3877
3878 return 0;
3879}
3880
3881static void mv88e6xxx_port_mdb_add(struct dsa_switch *ds, int port,
3882 const struct switchdev_obj_port_mdb *mdb,
3883 struct switchdev_trans *trans)
3884{
Vivien Didelot04bed142016-08-31 18:06:13 -04003885 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003886
3887 mutex_lock(&chip->reg_lock);
3888 if (mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
3889 GLOBAL_ATU_DATA_STATE_MC_STATIC))
3890 netdev_err(ds->ports[port].netdev, "failed to load multicast MAC address\n");
3891 mutex_unlock(&chip->reg_lock);
3892}
3893
3894static int mv88e6xxx_port_mdb_del(struct dsa_switch *ds, int port,
3895 const struct switchdev_obj_port_mdb *mdb)
3896{
Vivien Didelot04bed142016-08-31 18:06:13 -04003897 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003898 int err;
3899
3900 mutex_lock(&chip->reg_lock);
3901 err = mv88e6xxx_port_db_load_purge(chip, port, mdb->addr, mdb->vid,
3902 GLOBAL_ATU_DATA_STATE_UNUSED);
3903 mutex_unlock(&chip->reg_lock);
3904
3905 return err;
3906}
3907
3908static int mv88e6xxx_port_mdb_dump(struct dsa_switch *ds, int port,
3909 struct switchdev_obj_port_mdb *mdb,
3910 int (*cb)(struct switchdev_obj *obj))
3911{
Vivien Didelot04bed142016-08-31 18:06:13 -04003912 struct mv88e6xxx_chip *chip = ds->priv;
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003913 int err;
3914
3915 mutex_lock(&chip->reg_lock);
3916 err = mv88e6xxx_port_db_dump(chip, port, &mdb->obj, cb);
3917 mutex_unlock(&chip->reg_lock);
3918
3919 return err;
3920}
3921
Vivien Didelot9d490b42016-08-23 12:38:56 -04003922static struct dsa_switch_ops mv88e6xxx_switch_ops = {
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003923 .probe = mv88e6xxx_drv_probe,
Andrew Lunn7b314362016-08-22 16:01:01 +02003924 .get_tag_protocol = mv88e6xxx_get_tag_protocol,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003925 .setup = mv88e6xxx_setup,
3926 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003927 .adjust_link = mv88e6xxx_adjust_link,
3928 .get_strings = mv88e6xxx_get_strings,
3929 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3930 .get_sset_count = mv88e6xxx_get_sset_count,
3931 .set_eee = mv88e6xxx_set_eee,
3932 .get_eee = mv88e6xxx_get_eee,
3933#ifdef CONFIG_NET_DSA_HWMON
3934 .get_temp = mv88e6xxx_get_temp,
3935 .get_temp_limit = mv88e6xxx_get_temp_limit,
3936 .set_temp_limit = mv88e6xxx_set_temp_limit,
3937 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3938#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003939 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003940 .get_eeprom = mv88e6xxx_get_eeprom,
3941 .set_eeprom = mv88e6xxx_set_eeprom,
3942 .get_regs_len = mv88e6xxx_get_regs_len,
3943 .get_regs = mv88e6xxx_get_regs,
Vivien Didelot2cfcd962016-07-18 20:45:40 -04003944 .set_ageing_time = mv88e6xxx_set_ageing_time,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003945 .port_bridge_join = mv88e6xxx_port_bridge_join,
3946 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3947 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
Vivien Didelot749efcb2016-09-22 16:49:24 -04003948 .port_fast_age = mv88e6xxx_port_fast_age,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003949 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3950 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3951 .port_vlan_add = mv88e6xxx_port_vlan_add,
3952 .port_vlan_del = mv88e6xxx_port_vlan_del,
3953 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3954 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3955 .port_fdb_add = mv88e6xxx_port_fdb_add,
3956 .port_fdb_del = mv88e6xxx_port_fdb_del,
3957 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
Vivien Didelot7df8fbd2016-08-31 11:50:05 -04003958 .port_mdb_prepare = mv88e6xxx_port_mdb_prepare,
3959 .port_mdb_add = mv88e6xxx_port_mdb_add,
3960 .port_mdb_del = mv88e6xxx_port_mdb_del,
3961 .port_mdb_dump = mv88e6xxx_port_mdb_dump,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003962};
3963
Vivien Didelotfad09c72016-06-21 12:28:20 -04003964static int mv88e6xxx_register_switch(struct mv88e6xxx_chip *chip,
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003965 struct device_node *np)
3966{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003967 struct device *dev = chip->dev;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003968 struct dsa_switch *ds;
3969
3970 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3971 if (!ds)
3972 return -ENOMEM;
3973
3974 ds->dev = dev;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003975 ds->priv = chip;
Vivien Didelot9d490b42016-08-23 12:38:56 -04003976 ds->ops = &mv88e6xxx_switch_ops;
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003977
3978 dev_set_drvdata(dev, ds);
3979
3980 return dsa_register_switch(ds, np);
3981}
3982
Vivien Didelotfad09c72016-06-21 12:28:20 -04003983static void mv88e6xxx_unregister_switch(struct mv88e6xxx_chip *chip)
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003984{
Vivien Didelotfad09c72016-06-21 12:28:20 -04003985 dsa_unregister_switch(chip->ds);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003986}
3987
Vivien Didelot57d32312016-06-20 13:13:58 -04003988static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003989{
3990 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003991 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003992 const struct mv88e6xxx_info *compat_info;
Vivien Didelotfad09c72016-06-21 12:28:20 -04003993 struct mv88e6xxx_chip *chip;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003994 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003995 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003996
Vivien Didelotcaac8542016-06-20 13:14:09 -04003997 compat_info = of_device_get_match_data(dev);
3998 if (!compat_info)
3999 return -EINVAL;
4000
Vivien Didelotfad09c72016-06-21 12:28:20 -04004001 chip = mv88e6xxx_alloc_chip(dev);
4002 if (!chip)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004003 return -ENOMEM;
4004
Vivien Didelotfad09c72016-06-21 12:28:20 -04004005 chip->info = compat_info;
Vivien Didelotcaac8542016-06-20 13:14:09 -04004006
Vivien Didelotfad09c72016-06-21 12:28:20 -04004007 err = mv88e6xxx_smi_init(chip, mdiodev->bus, mdiodev->addr);
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04004008 if (err)
4009 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004010
Vivien Didelotfad09c72016-06-21 12:28:20 -04004011 err = mv88e6xxx_detect(chip);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04004012 if (err)
4013 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004014
Vivien Didelote57e5e72016-08-15 17:19:00 -04004015 mv88e6xxx_phy_init(chip);
4016
Vivien Didelotfad09c72016-06-21 12:28:20 -04004017 chip->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
4018 if (IS_ERR(chip->reset))
4019 return PTR_ERR(chip->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02004020
Vivien Didelotee4dc2e72016-09-29 12:22:02 -04004021 if (chip->info->ops->get_eeprom &&
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004022 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
Vivien Didelotfad09c72016-06-21 12:28:20 -04004023 chip->eeprom_len = eeprom_len;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02004024
Andrew Lunndc30c352016-10-16 19:56:49 +02004025 mutex_lock(&chip->reg_lock);
4026 err = mv88e6xxx_switch_reset(chip);
4027 mutex_unlock(&chip->reg_lock);
Andrew Lunnb516d452016-06-04 21:17:06 +02004028 if (err)
Andrew Lunndc30c352016-10-16 19:56:49 +02004029 goto out;
Andrew Lunnb516d452016-06-04 21:17:06 +02004030
Andrew Lunndc30c352016-10-16 19:56:49 +02004031 chip->irq = of_irq_get(np, 0);
4032 if (chip->irq == -EPROBE_DEFER) {
4033 err = chip->irq;
4034 goto out;
Andrew Lunn83c0afa2016-06-04 21:17:07 +02004035 }
4036
Andrew Lunndc30c352016-10-16 19:56:49 +02004037 if (chip->irq > 0) {
4038 /* Has to be performed before the MDIO bus is created,
4039 * because the PHYs will link there interrupts to these
4040 * interrupt controllers
4041 */
4042 mutex_lock(&chip->reg_lock);
4043 err = mv88e6xxx_g1_irq_setup(chip);
4044 mutex_unlock(&chip->reg_lock);
4045
4046 if (err)
4047 goto out;
4048
4049 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT)) {
4050 err = mv88e6xxx_g2_irq_setup(chip);
4051 if (err)
4052 goto out_g1_irq;
4053 }
4054 }
4055
4056 err = mv88e6xxx_mdio_register(chip, np);
4057 if (err)
4058 goto out_g2_irq;
4059
4060 err = mv88e6xxx_register_switch(chip, np);
4061 if (err)
4062 goto out_mdio;
4063
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004064 return 0;
Andrew Lunndc30c352016-10-16 19:56:49 +02004065
4066out_mdio:
4067 mv88e6xxx_mdio_unregister(chip);
4068out_g2_irq:
4069 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
4070 mv88e6xxx_g2_irq_free(chip);
4071out_g1_irq:
4072 mv88e6xxx_g1_irq_free(chip);
4073out:
4074 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004075}
4076
4077static void mv88e6xxx_remove(struct mdio_device *mdiodev)
4078{
4079 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
Vivien Didelot04bed142016-08-31 18:06:13 -04004080 struct mv88e6xxx_chip *chip = ds->priv;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004081
Andrew Lunn930188c2016-08-22 16:01:03 +02004082 mv88e6xxx_phy_destroy(chip);
Vivien Didelotfad09c72016-06-21 12:28:20 -04004083 mv88e6xxx_unregister_switch(chip);
4084 mv88e6xxx_mdio_unregister(chip);
Andrew Lunndc30c352016-10-16 19:56:49 +02004085
4086 if (mv88e6xxx_has(chip, MV88E6XXX_FLAG_G2_INT))
4087 mv88e6xxx_g2_irq_free(chip);
4088 mv88e6xxx_g1_irq_free(chip);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004089}
4090
4091static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04004092 {
4093 .compatible = "marvell,mv88e6085",
4094 .data = &mv88e6xxx_table[MV88E6085],
4095 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004096 { /* sentinel */ },
4097};
4098
4099MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
4100
4101static struct mdio_driver mv88e6xxx_driver = {
4102 .probe = mv88e6xxx_probe,
4103 .remove = mv88e6xxx_remove,
4104 .mdiodrv.driver = {
4105 .name = "mv88e6085",
4106 .of_match_table = mv88e6xxx_of_match,
4107 },
4108};
4109
Ben Hutchings98e67302011-11-25 14:36:19 +00004110static int __init mv88e6xxx_init(void)
4111{
Vivien Didelot9d490b42016-08-23 12:38:56 -04004112 register_switch_driver(&mv88e6xxx_switch_ops);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004113 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00004114}
4115module_init(mv88e6xxx_init);
4116
4117static void __exit mv88e6xxx_cleanup(void)
4118{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02004119 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelot9d490b42016-08-23 12:38:56 -04004120 unregister_switch_driver(&mv88e6xxx_switch_ops);
Ben Hutchings98e67302011-11-25 14:36:19 +00004121}
4122module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00004123
4124MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
4125MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
4126MODULE_LICENSE("GPL");