blob: 49f085a8453d91ca8fa751b2a051b38ce960633f [file] [log] [blame]
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001/*
2 * net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support
3 * Copyright (c) 2008 Marvell Semiconductor
4 *
Vivien Didelotb8fee952015-08-13 12:52:19 -04005 * Copyright (c) 2015 CMC Electronics, Inc.
6 * Added support for VLAN Table Unit operations
7 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
Barry Grussling19b2f972013-01-08 16:05:54 +000014#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070015#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020016#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070017#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000018#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000019#include <linux/list.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000020#include <linux/module.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000021#include <linux/netdevice.h>
Andrew Lunnc8c1b392015-11-20 03:56:24 +010022#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000023#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000024#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040025#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000026#include "mv88e6xxx.h"
27
Vivien Didelot3996a4f2015-10-30 18:56:45 -040028static void assert_smi_lock(struct dsa_switch *ds)
29{
30 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
31
32 if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
33 dev_err(ds->master_dev, "SMI lock not held!\n");
34 dump_stack();
35 }
36}
37
Barry Grussling3675c8d2013-01-08 16:05:53 +000038/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000039 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
40 * will be directly accessible on some {device address,register address}
41 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
42 * will only respond to SMI transactions to that specific address, and
43 * an indirect addressing mechanism needs to be used to access its
44 * registers.
45 */
46static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
47{
48 int ret;
49 int i;
50
51 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020052 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000053 if (ret < 0)
54 return ret;
55
Andrew Lunncca8b132015-04-02 04:06:39 +020056 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000057 return 0;
58 }
59
60 return -ETIMEDOUT;
61}
62
Vivien Didelotb9b37712015-10-30 19:39:48 -040063static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
64 int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000065{
66 int ret;
67
68 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020069 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000070
Barry Grussling3675c8d2013-01-08 16:05:53 +000071 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000072 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
73 if (ret < 0)
74 return ret;
75
Barry Grussling3675c8d2013-01-08 16:05:53 +000076 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020077 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
78 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000079 if (ret < 0)
80 return ret;
81
Barry Grussling3675c8d2013-01-08 16:05:53 +000082 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000083 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
84 if (ret < 0)
85 return ret;
86
Barry Grussling3675c8d2013-01-08 16:05:53 +000087 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020088 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000089 if (ret < 0)
90 return ret;
91
92 return ret & 0xffff;
93}
94
Guenter Roeck8d6d09e2015-03-26 18:36:31 -070095static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000096{
Andrew Lunna77d43f2016-04-13 02:40:42 +020097 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000098 int ret;
99
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400100 assert_smi_lock(ds);
101
Andrew Lunna77d43f2016-04-13 02:40:42 +0200102 ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500103 if (ret < 0)
104 return ret;
105
106 dev_dbg(ds->master_dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
107 addr, reg, ret);
108
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000109 return ret;
110}
111
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700112int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
113{
114 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
115 int ret;
116
117 mutex_lock(&ps->smi_mutex);
118 ret = _mv88e6xxx_reg_read(ds, addr, reg);
119 mutex_unlock(&ps->smi_mutex);
120
121 return ret;
122}
123
Vivien Didelotb9b37712015-10-30 19:39:48 -0400124static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
125 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000126{
127 int ret;
128
129 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200130 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000131
Barry Grussling3675c8d2013-01-08 16:05:53 +0000132 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000133 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
134 if (ret < 0)
135 return ret;
136
Barry Grussling3675c8d2013-01-08 16:05:53 +0000137 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200138 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000139 if (ret < 0)
140 return ret;
141
Barry Grussling3675c8d2013-01-08 16:05:53 +0000142 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200143 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
144 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000145 if (ret < 0)
146 return ret;
147
Barry Grussling3675c8d2013-01-08 16:05:53 +0000148 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000149 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
150 if (ret < 0)
151 return ret;
152
153 return 0;
154}
155
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700156static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg,
157 u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000158{
Andrew Lunna77d43f2016-04-13 02:40:42 +0200159 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000160
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400161 assert_smi_lock(ds);
162
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500163 dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
164 addr, reg, val);
165
Andrew Lunna77d43f2016-04-13 02:40:42 +0200166 return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700167}
168
169int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
170{
171 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
172 int ret;
173
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000174 mutex_lock(&ps->smi_mutex);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700175 ret = _mv88e6xxx_reg_write(ds, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000176 mutex_unlock(&ps->smi_mutex);
177
178 return ret;
179}
180
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000181int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
182{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200183 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000184
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200185 err = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_MAC_01,
186 (addr[0] << 8) | addr[1]);
187 if (err)
188 return err;
189
190 err = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_MAC_23,
191 (addr[2] << 8) | addr[3]);
192 if (err)
193 return err;
194
195 return mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_MAC_45,
196 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000197}
198
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000199int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
200{
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000201 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200202 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000203
204 for (i = 0; i < 6; i++) {
205 int j;
206
Barry Grussling3675c8d2013-01-08 16:05:53 +0000207 /* Write the MAC address byte. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200208 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
209 GLOBAL2_SWITCH_MAC_BUSY |
210 (i << 8) | addr[i]);
211 if (ret)
212 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000213
Barry Grussling3675c8d2013-01-08 16:05:53 +0000214 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000215 for (j = 0; j < 16; j++) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200216 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL2,
217 GLOBAL2_SWITCH_MAC);
218 if (ret < 0)
219 return ret;
220
Andrew Lunncca8b132015-04-02 04:06:39 +0200221 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000222 break;
223 }
224 if (j == 16)
225 return -ETIMEDOUT;
226 }
227
228 return 0;
229}
230
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200231static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000232{
233 if (addr >= 0)
Andrew Lunn3898c142015-05-06 01:09:53 +0200234 return _mv88e6xxx_reg_read(ds, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000235 return 0xffff;
236}
237
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200238static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
239 u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000240{
241 if (addr >= 0)
Andrew Lunn3898c142015-05-06 01:09:53 +0200242 return _mv88e6xxx_reg_write(ds, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000243 return 0;
244}
245
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000246#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
247static int mv88e6xxx_ppu_disable(struct dsa_switch *ds)
248{
249 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000250 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000251
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200252 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_CONTROL);
253 if (ret < 0)
254 return ret;
255
256 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_CONTROL,
257 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
258 if (ret)
259 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000260
Barry Grussling19b2f972013-01-08 16:05:54 +0000261 timeout = jiffies + 1 * HZ;
262 while (time_before(jiffies, timeout)) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200263 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATUS);
264 if (ret < 0)
265 return ret;
266
Barry Grussling19b2f972013-01-08 16:05:54 +0000267 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200268 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
269 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000270 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000271 }
272
273 return -ETIMEDOUT;
274}
275
276static int mv88e6xxx_ppu_enable(struct dsa_switch *ds)
277{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200278 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000279 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000280
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200281 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_CONTROL);
282 if (ret < 0)
283 return ret;
284
285 err = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_CONTROL,
286 ret | GLOBAL_CONTROL_PPU_ENABLE);
287 if (err)
288 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000289
Barry Grussling19b2f972013-01-08 16:05:54 +0000290 timeout = jiffies + 1 * HZ;
291 while (time_before(jiffies, timeout)) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200292 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATUS);
293 if (ret < 0)
294 return ret;
295
Barry Grussling19b2f972013-01-08 16:05:54 +0000296 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200297 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
298 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000299 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000300 }
301
302 return -ETIMEDOUT;
303}
304
305static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
306{
307 struct mv88e6xxx_priv_state *ps;
308
309 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
310 if (mutex_trylock(&ps->ppu_mutex)) {
Andrew Lunn7543a6d2016-04-13 02:40:40 +0200311 struct dsa_switch *ds = ps->ds;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000312
Barry Grussling85686582013-01-08 16:05:56 +0000313 if (mv88e6xxx_ppu_enable(ds) == 0)
314 ps->ppu_disabled = 0;
315 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000316 }
317}
318
319static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
320{
321 struct mv88e6xxx_priv_state *ps = (void *)_ps;
322
323 schedule_work(&ps->ppu_work);
324}
325
326static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
327{
Florian Fainellia22adce2014-04-28 11:14:28 -0700328 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000329 int ret;
330
331 mutex_lock(&ps->ppu_mutex);
332
Barry Grussling3675c8d2013-01-08 16:05:53 +0000333 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000334 * we can access the PHY registers. If it was already
335 * disabled, cancel the timer that is going to re-enable
336 * it.
337 */
338 if (!ps->ppu_disabled) {
Barry Grussling85686582013-01-08 16:05:56 +0000339 ret = mv88e6xxx_ppu_disable(ds);
340 if (ret < 0) {
341 mutex_unlock(&ps->ppu_mutex);
342 return ret;
343 }
344 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000345 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000346 del_timer(&ps->ppu_timer);
347 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000348 }
349
350 return ret;
351}
352
353static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
354{
Florian Fainellia22adce2014-04-28 11:14:28 -0700355 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000356
Barry Grussling3675c8d2013-01-08 16:05:53 +0000357 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000358 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
359 mutex_unlock(&ps->ppu_mutex);
360}
361
362void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
363{
Florian Fainellia22adce2014-04-28 11:14:28 -0700364 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000365
366 mutex_init(&ps->ppu_mutex);
367 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
368 init_timer(&ps->ppu_timer);
369 ps->ppu_timer.data = (unsigned long)ps;
370 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
371}
372
373int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum)
374{
375 int ret;
376
377 ret = mv88e6xxx_ppu_access_get(ds);
378 if (ret >= 0) {
Barry Grussling85686582013-01-08 16:05:56 +0000379 ret = mv88e6xxx_reg_read(ds, addr, regnum);
380 mv88e6xxx_ppu_access_put(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000381 }
382
383 return ret;
384}
385
386int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
387 int regnum, u16 val)
388{
389 int ret;
390
391 ret = mv88e6xxx_ppu_access_get(ds);
392 if (ret >= 0) {
Barry Grussling85686582013-01-08 16:05:56 +0000393 ret = mv88e6xxx_reg_write(ds, addr, regnum, val);
394 mv88e6xxx_ppu_access_put(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000395 }
396
397 return ret;
398}
399#endif
400
Andrew Lunn54d792f2015-05-06 01:09:47 +0200401static bool mv88e6xxx_6065_family(struct dsa_switch *ds)
402{
403 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
404
405 switch (ps->id) {
406 case PORT_SWITCH_ID_6031:
407 case PORT_SWITCH_ID_6061:
408 case PORT_SWITCH_ID_6035:
409 case PORT_SWITCH_ID_6065:
410 return true;
411 }
412 return false;
413}
414
415static bool mv88e6xxx_6095_family(struct dsa_switch *ds)
416{
417 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
418
419 switch (ps->id) {
420 case PORT_SWITCH_ID_6092:
421 case PORT_SWITCH_ID_6095:
422 return true;
423 }
424 return false;
425}
426
427static bool mv88e6xxx_6097_family(struct dsa_switch *ds)
428{
429 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
430
431 switch (ps->id) {
432 case PORT_SWITCH_ID_6046:
433 case PORT_SWITCH_ID_6085:
434 case PORT_SWITCH_ID_6096:
435 case PORT_SWITCH_ID_6097:
436 return true;
437 }
438 return false;
439}
440
441static bool mv88e6xxx_6165_family(struct dsa_switch *ds)
442{
443 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
444
445 switch (ps->id) {
446 case PORT_SWITCH_ID_6123:
447 case PORT_SWITCH_ID_6161:
448 case PORT_SWITCH_ID_6165:
449 return true;
450 }
451 return false;
452}
453
454static bool mv88e6xxx_6185_family(struct dsa_switch *ds)
455{
456 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
457
458 switch (ps->id) {
459 case PORT_SWITCH_ID_6121:
460 case PORT_SWITCH_ID_6122:
461 case PORT_SWITCH_ID_6152:
462 case PORT_SWITCH_ID_6155:
463 case PORT_SWITCH_ID_6182:
464 case PORT_SWITCH_ID_6185:
465 case PORT_SWITCH_ID_6108:
466 case PORT_SWITCH_ID_6131:
467 return true;
468 }
469 return false;
470}
471
Guenter Roeckc22995c2015-07-25 09:42:28 -0700472static bool mv88e6xxx_6320_family(struct dsa_switch *ds)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700473{
474 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
475
476 switch (ps->id) {
477 case PORT_SWITCH_ID_6320:
478 case PORT_SWITCH_ID_6321:
479 return true;
480 }
481 return false;
482}
483
Andrew Lunn54d792f2015-05-06 01:09:47 +0200484static bool mv88e6xxx_6351_family(struct dsa_switch *ds)
485{
486 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
487
488 switch (ps->id) {
489 case PORT_SWITCH_ID_6171:
490 case PORT_SWITCH_ID_6175:
491 case PORT_SWITCH_ID_6350:
492 case PORT_SWITCH_ID_6351:
493 return true;
494 }
495 return false;
496}
497
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200498static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
499{
500 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
501
502 switch (ps->id) {
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200503 case PORT_SWITCH_ID_6172:
504 case PORT_SWITCH_ID_6176:
Andrew Lunn54d792f2015-05-06 01:09:47 +0200505 case PORT_SWITCH_ID_6240:
506 case PORT_SWITCH_ID_6352:
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200507 return true;
508 }
509 return false;
510}
511
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400512static unsigned int mv88e6xxx_num_databases(struct dsa_switch *ds)
513{
514 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
515
516 /* The following devices have 4-bit identifiers for 16 databases */
517 if (ps->id == PORT_SWITCH_ID_6061)
518 return 16;
519
520 /* The following devices have 6-bit identifiers for 64 databases */
521 if (ps->id == PORT_SWITCH_ID_6065)
522 return 64;
523
524 /* The following devices have 8-bit identifiers for 256 databases */
525 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
526 return 256;
527
528 /* The following devices have 12-bit identifiers for 4096 databases */
529 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
530 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds))
531 return 4096;
532
533 return 0;
534}
535
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400536static bool mv88e6xxx_has_fid_reg(struct dsa_switch *ds)
537{
538 /* Does the device have dedicated FID registers for ATU and VTU ops? */
539 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
540 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds))
541 return true;
542
543 return false;
544}
545
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -0400546static bool mv88e6xxx_has_stu(struct dsa_switch *ds)
547{
548 /* Does the device have STU and dedicated SID registers for VTU ops? */
549 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
550 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds))
551 return true;
552
553 return false;
554}
555
Andrew Lunndea87022015-08-31 15:56:47 +0200556/* We expect the switch to perform auto negotiation if there is a real
557 * phy. However, in the case of a fixed link phy, we force the port
558 * settings from the fixed link settings.
559 */
560void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
561 struct phy_device *phydev)
562{
563 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200564 u32 reg;
565 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200566
567 if (!phy_is_pseudo_fixed_link(phydev))
568 return;
569
570 mutex_lock(&ps->smi_mutex);
571
572 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
573 if (ret < 0)
574 goto out;
575
576 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
577 PORT_PCS_CTRL_FORCE_LINK |
578 PORT_PCS_CTRL_DUPLEX_FULL |
579 PORT_PCS_CTRL_FORCE_DUPLEX |
580 PORT_PCS_CTRL_UNFORCED);
581
582 reg |= PORT_PCS_CTRL_FORCE_LINK;
583 if (phydev->link)
584 reg |= PORT_PCS_CTRL_LINK_UP;
585
586 if (mv88e6xxx_6065_family(ds) && phydev->speed > SPEED_100)
587 goto out;
588
589 switch (phydev->speed) {
590 case SPEED_1000:
591 reg |= PORT_PCS_CTRL_1000;
592 break;
593 case SPEED_100:
594 reg |= PORT_PCS_CTRL_100;
595 break;
596 case SPEED_10:
597 reg |= PORT_PCS_CTRL_10;
598 break;
599 default:
600 pr_info("Unknown speed");
601 goto out;
602 }
603
604 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
605 if (phydev->duplex == DUPLEX_FULL)
606 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
607
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200608 if ((mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds)) &&
609 (port >= ps->num_ports - 2)) {
610 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
611 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
612 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
613 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
614 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
615 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
616 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
617 }
Andrew Lunndea87022015-08-31 15:56:47 +0200618 _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_PCS_CTRL, reg);
619
620out:
621 mutex_unlock(&ps->smi_mutex);
622}
623
Andrew Lunn31888232015-05-06 01:09:54 +0200624static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000625{
626 int ret;
627 int i;
628
629 for (i = 0; i < 10; i++) {
Andrew Lunn31888232015-05-06 01:09:54 +0200630 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200631 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000632 return 0;
633 }
634
635 return -ETIMEDOUT;
636}
637
Andrew Lunn31888232015-05-06 01:09:54 +0200638static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000639{
640 int ret;
641
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700642 if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200643 port = (port + 1) << 5;
644
Barry Grussling3675c8d2013-01-08 16:05:53 +0000645 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn31888232015-05-06 01:09:54 +0200646 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
647 GLOBAL_STATS_OP_CAPTURE_PORT |
648 GLOBAL_STATS_OP_HIST_RX_TX | port);
649 if (ret < 0)
650 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000651
Barry Grussling3675c8d2013-01-08 16:05:53 +0000652 /* Wait for the snapshotting to complete. */
Andrew Lunn31888232015-05-06 01:09:54 +0200653 ret = _mv88e6xxx_stats_wait(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000654 if (ret < 0)
655 return ret;
656
657 return 0;
658}
659
Andrew Lunn31888232015-05-06 01:09:54 +0200660static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000661{
662 u32 _val;
663 int ret;
664
665 *val = 0;
666
Andrew Lunn31888232015-05-06 01:09:54 +0200667 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
668 GLOBAL_STATS_OP_READ_CAPTURED |
669 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000670 if (ret < 0)
671 return;
672
Andrew Lunn31888232015-05-06 01:09:54 +0200673 ret = _mv88e6xxx_stats_wait(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000674 if (ret < 0)
675 return;
676
Andrew Lunn31888232015-05-06 01:09:54 +0200677 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000678 if (ret < 0)
679 return;
680
681 _val = ret << 16;
682
Andrew Lunn31888232015-05-06 01:09:54 +0200683 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000684 if (ret < 0)
685 return;
686
687 *val = _val | ret;
688}
689
Andrew Lunne413e7e2015-04-02 04:06:38 +0200690static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100691 { "in_good_octets", 8, 0x00, BANK0, },
692 { "in_bad_octets", 4, 0x02, BANK0, },
693 { "in_unicast", 4, 0x04, BANK0, },
694 { "in_broadcasts", 4, 0x06, BANK0, },
695 { "in_multicasts", 4, 0x07, BANK0, },
696 { "in_pause", 4, 0x16, BANK0, },
697 { "in_undersize", 4, 0x18, BANK0, },
698 { "in_fragments", 4, 0x19, BANK0, },
699 { "in_oversize", 4, 0x1a, BANK0, },
700 { "in_jabber", 4, 0x1b, BANK0, },
701 { "in_rx_error", 4, 0x1c, BANK0, },
702 { "in_fcs_error", 4, 0x1d, BANK0, },
703 { "out_octets", 8, 0x0e, BANK0, },
704 { "out_unicast", 4, 0x10, BANK0, },
705 { "out_broadcasts", 4, 0x13, BANK0, },
706 { "out_multicasts", 4, 0x12, BANK0, },
707 { "out_pause", 4, 0x15, BANK0, },
708 { "excessive", 4, 0x11, BANK0, },
709 { "collisions", 4, 0x1e, BANK0, },
710 { "deferred", 4, 0x05, BANK0, },
711 { "single", 4, 0x14, BANK0, },
712 { "multiple", 4, 0x17, BANK0, },
713 { "out_fcs_error", 4, 0x03, BANK0, },
714 { "late", 4, 0x1f, BANK0, },
715 { "hist_64bytes", 4, 0x08, BANK0, },
716 { "hist_65_127bytes", 4, 0x09, BANK0, },
717 { "hist_128_255bytes", 4, 0x0a, BANK0, },
718 { "hist_256_511bytes", 4, 0x0b, BANK0, },
719 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
720 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
721 { "sw_in_discards", 4, 0x10, PORT, },
722 { "sw_in_filtered", 2, 0x12, PORT, },
723 { "sw_out_filtered", 2, 0x13, PORT, },
724 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
725 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
726 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
727 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
728 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
729 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
730 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
731 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
732 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
733 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
734 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
735 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
736 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
737 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
738 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
739 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
740 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
741 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
742 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
743 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
744 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
745 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
746 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
747 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
748 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
749 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200750};
751
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100752static bool mv88e6xxx_has_stat(struct dsa_switch *ds,
753 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200754{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100755 switch (stat->type) {
756 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200757 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100758 case BANK1:
759 return mv88e6xxx_6320_family(ds);
760 case PORT:
761 return mv88e6xxx_6095_family(ds) ||
762 mv88e6xxx_6185_family(ds) ||
763 mv88e6xxx_6097_family(ds) ||
764 mv88e6xxx_6165_family(ds) ||
765 mv88e6xxx_6351_family(ds) ||
766 mv88e6xxx_6352_family(ds);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200767 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100768 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000769}
770
Andrew Lunn80c46272015-06-20 18:42:30 +0200771static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100772 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200773 int port)
774{
Andrew Lunn80c46272015-06-20 18:42:30 +0200775 u32 low;
776 u32 high = 0;
777 int ret;
778 u64 value;
779
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100780 switch (s->type) {
781 case PORT:
782 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200783 if (ret < 0)
784 return UINT64_MAX;
785
786 low = ret;
787 if (s->sizeof_stat == 4) {
788 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100789 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200790 if (ret < 0)
791 return UINT64_MAX;
792 high = ret;
793 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100794 break;
795 case BANK0:
796 case BANK1:
Andrew Lunn80c46272015-06-20 18:42:30 +0200797 _mv88e6xxx_stats_read(ds, s->reg, &low);
798 if (s->sizeof_stat == 8)
799 _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
800 }
801 value = (((u64)high) << 16) | low;
802 return value;
803}
804
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100805void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
806{
807 struct mv88e6xxx_hw_stat *stat;
808 int i, j;
809
810 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
811 stat = &mv88e6xxx_hw_stats[i];
812 if (mv88e6xxx_has_stat(ds, stat)) {
813 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
814 ETH_GSTRING_LEN);
815 j++;
816 }
817 }
818}
819
820int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
821{
822 struct mv88e6xxx_hw_stat *stat;
823 int i, j;
824
825 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
826 stat = &mv88e6xxx_hw_stats[i];
827 if (mv88e6xxx_has_stat(ds, stat))
828 j++;
829 }
830 return j;
831}
832
833void
834mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
835 int port, uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000836{
Florian Fainellia22adce2014-04-28 11:14:28 -0700837 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100838 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000839 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100840 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000841
Andrew Lunn31888232015-05-06 01:09:54 +0200842 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000843
Andrew Lunn31888232015-05-06 01:09:54 +0200844 ret = _mv88e6xxx_stats_snapshot(ds, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000845 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200846 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000847 return;
848 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100849 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
850 stat = &mv88e6xxx_hw_stats[i];
851 if (mv88e6xxx_has_stat(ds, stat)) {
852 data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port);
853 j++;
854 }
855 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000856
Andrew Lunn31888232015-05-06 01:09:54 +0200857 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000858}
Ben Hutchings98e67302011-11-25 14:36:19 +0000859
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700860int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
861{
862 return 32 * sizeof(u16);
863}
864
865void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
866 struct ethtool_regs *regs, void *_p)
867{
868 u16 *p = _p;
869 int i;
870
871 regs->version = 0;
872
873 memset(p, 0xff, 32 * sizeof(u16));
874
875 for (i = 0; i < 32; i++) {
876 int ret;
877
878 ret = mv88e6xxx_reg_read(ds, REG_PORT(port), i);
879 if (ret >= 0)
880 p[i] = ret;
881 }
882}
883
Andrew Lunn3898c142015-05-06 01:09:53 +0200884static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset,
885 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700886{
887 unsigned long timeout = jiffies + HZ / 10;
888
889 while (time_before(jiffies, timeout)) {
890 int ret;
891
892 ret = _mv88e6xxx_reg_read(ds, reg, offset);
893 if (ret < 0)
894 return ret;
895 if (!(ret & mask))
896 return 0;
897
898 usleep_range(1000, 2000);
899 }
900 return -ETIMEDOUT;
901}
902
Andrew Lunn3898c142015-05-06 01:09:53 +0200903static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
904{
905 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
906 int ret;
907
908 mutex_lock(&ps->smi_mutex);
909 ret = _mv88e6xxx_wait(ds, reg, offset, mask);
910 mutex_unlock(&ps->smi_mutex);
911
912 return ret;
913}
914
915static int _mv88e6xxx_phy_wait(struct dsa_switch *ds)
916{
917 return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
918 GLOBAL2_SMI_OP_BUSY);
919}
920
921int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
922{
923 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
924 GLOBAL2_EEPROM_OP_LOAD);
925}
926
927int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
928{
929 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
930 GLOBAL2_EEPROM_OP_BUSY);
931}
932
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700933static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
934{
Andrew Lunncca8b132015-04-02 04:06:39 +0200935 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
936 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700937}
938
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200939static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
940 int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +0100941{
942 int ret;
943
Andrew Lunn3898c142015-05-06 01:09:53 +0200944 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
945 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
946 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +0100947 if (ret < 0)
948 return ret;
949
Andrew Lunn3898c142015-05-06 01:09:53 +0200950 ret = _mv88e6xxx_phy_wait(ds);
951 if (ret < 0)
952 return ret;
953
954 return _mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA);
Andrew Lunnf3044682015-02-14 19:17:50 +0100955}
956
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200957static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
958 int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +0100959{
Andrew Lunn3898c142015-05-06 01:09:53 +0200960 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +0100961
Andrew Lunn3898c142015-05-06 01:09:53 +0200962 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
963 if (ret < 0)
964 return ret;
965
966 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
967 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
968 regnum);
969
970 return _mv88e6xxx_phy_wait(ds);
Andrew Lunnf3044682015-02-14 19:17:50 +0100971}
972
Guenter Roeck11b3b452015-03-06 22:23:51 -0800973int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
974{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200975 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800976 int reg;
977
Andrew Lunn3898c142015-05-06 01:09:53 +0200978 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200979
980 reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800981 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200982 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800983
984 e->eee_enabled = !!(reg & 0x0200);
985 e->tx_lpi_enabled = !!(reg & 0x0100);
986
Andrew Lunn3898c142015-05-06 01:09:53 +0200987 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800988 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200989 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800990
Andrew Lunncca8b132015-04-02 04:06:39 +0200991 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200992 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800993
Andrew Lunn2f40c692015-04-02 04:06:37 +0200994out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200995 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200996 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800997}
998
999int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1000 struct phy_device *phydev, struct ethtool_eee *e)
1001{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001002 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1003 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001004 int ret;
1005
Andrew Lunn3898c142015-05-06 01:09:53 +02001006 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001007
Andrew Lunn2f40c692015-04-02 04:06:37 +02001008 ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
1009 if (ret < 0)
1010 goto out;
1011
1012 reg = ret & ~0x0300;
1013 if (e->eee_enabled)
1014 reg |= 0x0200;
1015 if (e->tx_lpi_enabled)
1016 reg |= 0x0100;
1017
1018 ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
1019out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001020 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001021
1022 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001023}
1024
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001025static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001026{
1027 int ret;
1028
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001029 if (mv88e6xxx_has_fid_reg(ds)) {
1030 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid);
1031 if (ret < 0)
1032 return ret;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001033 } else if (mv88e6xxx_num_databases(ds) == 256) {
1034 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
1035 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL);
1036 if (ret < 0)
1037 return ret;
1038
1039 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL,
1040 (ret & 0xfff) |
1041 ((fid << 8) & 0xf000));
1042 if (ret < 0)
1043 return ret;
1044
1045 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1046 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001047 }
1048
Andrew Lunncca8b132015-04-02 04:06:39 +02001049 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001050 if (ret < 0)
1051 return ret;
1052
1053 return _mv88e6xxx_atu_wait(ds);
1054}
1055
Vivien Didelot37705b72015-09-04 14:34:11 -04001056static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds,
1057 struct mv88e6xxx_atu_entry *entry)
1058{
1059 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1060
1061 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1062 unsigned int mask, shift;
1063
1064 if (entry->trunk) {
1065 data |= GLOBAL_ATU_DATA_TRUNK;
1066 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1067 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1068 } else {
1069 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1070 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1071 }
1072
1073 data |= (entry->portv_trunkid << shift) & mask;
1074 }
1075
1076 return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, data);
1077}
1078
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001079static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
1080 struct mv88e6xxx_atu_entry *entry,
1081 bool static_too)
1082{
1083 int op;
1084 int err;
1085
1086 err = _mv88e6xxx_atu_wait(ds);
1087 if (err)
1088 return err;
1089
1090 err = _mv88e6xxx_atu_data_write(ds, entry);
1091 if (err)
1092 return err;
1093
1094 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001095 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1096 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1097 } else {
1098 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1099 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1100 }
1101
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001102 return _mv88e6xxx_atu_cmd(ds, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001103}
1104
1105static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too)
1106{
1107 struct mv88e6xxx_atu_entry entry = {
1108 .fid = fid,
1109 .state = 0, /* EntryState bits must be 0 */
1110 };
1111
1112 return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
1113}
1114
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001115static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port,
1116 int to_port, bool static_too)
1117{
1118 struct mv88e6xxx_atu_entry entry = {
1119 .trunk = false,
1120 .fid = fid,
1121 };
1122
1123 /* EntryState bits must be 0xF */
1124 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1125
1126 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1127 entry.portv_trunkid = (to_port & 0x0f) << 4;
1128 entry.portv_trunkid |= from_port & 0x0f;
1129
1130 return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
1131}
1132
1133static int _mv88e6xxx_atu_remove(struct dsa_switch *ds, u16 fid, int port,
1134 bool static_too)
1135{
1136 /* Destination port 0xF means remove the entries */
1137 return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too);
1138}
1139
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001140static const char * const mv88e6xxx_port_state_names[] = {
1141 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1142 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1143 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1144 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1145};
1146
1147static int _mv88e6xxx_port_state(struct dsa_switch *ds, int port, u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001148{
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001149 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001150 u8 oldstate;
1151
Andrew Lunncca8b132015-04-02 04:06:39 +02001152 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001153 if (reg < 0)
1154 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001155
Andrew Lunncca8b132015-04-02 04:06:39 +02001156 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001157
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001158 if (oldstate != state) {
1159 /* Flush forwarding database if we're moving a port
1160 * from Learning or Forwarding state to Disabled or
1161 * Blocking or Listening state.
1162 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001163 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1164 oldstate == PORT_CONTROL_STATE_FORWARDING)
1165 && (state == PORT_CONTROL_STATE_DISABLED ||
1166 state == PORT_CONTROL_STATE_BLOCKING)) {
Vivien Didelot2b8157b2015-09-04 14:34:16 -04001167 ret = _mv88e6xxx_atu_remove(ds, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001168 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001169 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001170 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001171
Andrew Lunncca8b132015-04-02 04:06:39 +02001172 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
1173 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
1174 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001175 if (ret)
1176 return ret;
1177
1178 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1179 mv88e6xxx_port_state_names[state],
1180 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001181 }
1182
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001183 return ret;
1184}
1185
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001186static int _mv88e6xxx_port_based_vlan_map(struct dsa_switch *ds, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001187{
1188 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001189 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelotede80982015-10-11 18:08:35 -04001190 const u16 mask = (1 << ps->num_ports) - 1;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001191 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001192 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001193 int i;
1194
1195 /* allow CPU port or DSA link(s) to send frames to every port */
1196 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1197 output_ports = mask;
1198 } else {
1199 for (i = 0; i < ps->num_ports; ++i) {
1200 /* allow sending frames to every group member */
1201 if (bridge && ps->ports[i].bridge_dev == bridge)
1202 output_ports |= BIT(i);
1203
1204 /* allow sending frames to CPU port and DSA link(s) */
1205 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1206 output_ports |= BIT(i);
1207 }
1208 }
1209
1210 /* prevent frames from going back out of the port they came in on */
1211 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001212
Vivien Didelotede80982015-10-11 18:08:35 -04001213 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
1214 if (reg < 0)
1215 return reg;
1216
1217 reg &= ~mask;
1218 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001219
Andrew Lunncca8b132015-04-02 04:06:39 +02001220 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001221}
1222
Vivien Didelot43c44a92016-04-06 11:55:03 -04001223void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001224{
1225 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1226 int stp_state;
1227
1228 switch (state) {
1229 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001230 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001231 break;
1232 case BR_STATE_BLOCKING:
1233 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001234 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001235 break;
1236 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001237 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001238 break;
1239 case BR_STATE_FORWARDING:
1240 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001241 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001242 break;
1243 }
1244
Vivien Didelot43c44a92016-04-06 11:55:03 -04001245 /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled,
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001246 * so we can not update the port state directly but need to schedule it.
1247 */
Vivien Didelotd715fa62016-02-12 12:09:38 -05001248 ps->ports[port].state = stp_state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001249 set_bit(port, ps->port_state_update_mask);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001250 schedule_work(&ps->bridge_work);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001251}
1252
Vivien Didelot5da96032016-03-07 18:24:39 -05001253static int _mv88e6xxx_port_pvid(struct dsa_switch *ds, int port, u16 *new,
1254 u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001255{
Vivien Didelot5da96032016-03-07 18:24:39 -05001256 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001257 int ret;
1258
1259 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
1260 if (ret < 0)
1261 return ret;
1262
Vivien Didelot5da96032016-03-07 18:24:39 -05001263 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1264
1265 if (new) {
1266 ret &= ~PORT_DEFAULT_VLAN_MASK;
1267 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1268
1269 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
1270 PORT_DEFAULT_VLAN, ret);
1271 if (ret < 0)
1272 return ret;
1273
1274 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1275 pvid);
1276 }
1277
1278 if (old)
1279 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001280
1281 return 0;
1282}
1283
Vivien Didelot5da96032016-03-07 18:24:39 -05001284static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
1285{
1286 return _mv88e6xxx_port_pvid(ds, port, NULL, pvid);
1287}
1288
Vivien Didelot76e398a2015-11-01 12:33:55 -05001289static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001290{
Vivien Didelot5da96032016-03-07 18:24:39 -05001291 return _mv88e6xxx_port_pvid(ds, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001292}
1293
Vivien Didelot6b17e862015-08-13 12:52:18 -04001294static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
1295{
1296 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
1297 GLOBAL_VTU_OP_BUSY);
1298}
1299
1300static int _mv88e6xxx_vtu_cmd(struct dsa_switch *ds, u16 op)
1301{
1302 int ret;
1303
1304 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_OP, op);
1305 if (ret < 0)
1306 return ret;
1307
1308 return _mv88e6xxx_vtu_wait(ds);
1309}
1310
1311static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds)
1312{
1313 int ret;
1314
1315 ret = _mv88e6xxx_vtu_wait(ds);
1316 if (ret < 0)
1317 return ret;
1318
1319 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL);
1320}
1321
Vivien Didelotb8fee952015-08-13 12:52:19 -04001322static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds,
1323 struct mv88e6xxx_vtu_stu_entry *entry,
1324 unsigned int nibble_offset)
1325{
1326 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1327 u16 regs[3];
1328 int i;
1329 int ret;
1330
1331 for (i = 0; i < 3; ++i) {
1332 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1333 GLOBAL_VTU_DATA_0_3 + i);
1334 if (ret < 0)
1335 return ret;
1336
1337 regs[i] = ret;
1338 }
1339
1340 for (i = 0; i < ps->num_ports; ++i) {
1341 unsigned int shift = (i % 4) * 4 + nibble_offset;
1342 u16 reg = regs[i / 4];
1343
1344 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1345 }
1346
1347 return 0;
1348}
1349
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001350static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds,
1351 struct mv88e6xxx_vtu_stu_entry *entry,
1352 unsigned int nibble_offset)
1353{
1354 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1355 u16 regs[3] = { 0 };
1356 int i;
1357 int ret;
1358
1359 for (i = 0; i < ps->num_ports; ++i) {
1360 unsigned int shift = (i % 4) * 4 + nibble_offset;
1361 u8 data = entry->data[i];
1362
1363 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1364 }
1365
1366 for (i = 0; i < 3; ++i) {
1367 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL,
1368 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1369 if (ret < 0)
1370 return ret;
1371 }
1372
1373 return 0;
1374}
1375
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001376static int _mv88e6xxx_vtu_vid_write(struct dsa_switch *ds, u16 vid)
1377{
1378 return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
1379 vid & GLOBAL_VTU_VID_MASK);
1380}
1381
1382static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001383 struct mv88e6xxx_vtu_stu_entry *entry)
1384{
1385 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1386 int ret;
1387
1388 ret = _mv88e6xxx_vtu_wait(ds);
1389 if (ret < 0)
1390 return ret;
1391
Vivien Didelotb8fee952015-08-13 12:52:19 -04001392 ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT);
1393 if (ret < 0)
1394 return ret;
1395
1396 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1397 if (ret < 0)
1398 return ret;
1399
1400 next.vid = ret & GLOBAL_VTU_VID_MASK;
1401 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1402
1403 if (next.valid) {
1404 ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 0);
1405 if (ret < 0)
1406 return ret;
1407
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001408 if (mv88e6xxx_has_fid_reg(ds)) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001409 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1410 GLOBAL_VTU_FID);
1411 if (ret < 0)
1412 return ret;
1413
1414 next.fid = ret & GLOBAL_VTU_FID_MASK;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001415 } else if (mv88e6xxx_num_databases(ds) == 256) {
1416 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1417 * VTU DBNum[3:0] are located in VTU Operation 3:0
1418 */
1419 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1420 GLOBAL_VTU_OP);
1421 if (ret < 0)
1422 return ret;
1423
1424 next.fid = (ret & 0xf00) >> 4;
1425 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001426 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001427
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001428 if (mv88e6xxx_has_stu(ds)) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001429 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1430 GLOBAL_VTU_SID);
1431 if (ret < 0)
1432 return ret;
1433
1434 next.sid = ret & GLOBAL_VTU_SID_MASK;
1435 }
1436 }
1437
1438 *entry = next;
1439 return 0;
1440}
1441
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001442int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1443 struct switchdev_obj_port_vlan *vlan,
1444 int (*cb)(struct switchdev_obj *obj))
1445{
1446 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1447 struct mv88e6xxx_vtu_stu_entry next;
1448 u16 pvid;
1449 int err;
1450
1451 mutex_lock(&ps->smi_mutex);
1452
1453 err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
1454 if (err)
1455 goto unlock;
1456
1457 err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK);
1458 if (err)
1459 goto unlock;
1460
1461 do {
1462 err = _mv88e6xxx_vtu_getnext(ds, &next);
1463 if (err)
1464 break;
1465
1466 if (!next.valid)
1467 break;
1468
1469 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1470 continue;
1471
1472 /* reinit and dump this VLAN obj */
1473 vlan->vid_begin = vlan->vid_end = next.vid;
1474 vlan->flags = 0;
1475
1476 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1477 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1478
1479 if (next.vid == pvid)
1480 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1481
1482 err = cb(&vlan->obj);
1483 if (err)
1484 break;
1485 } while (next.vid < GLOBAL_VTU_VID_MASK);
1486
1487unlock:
1488 mutex_unlock(&ps->smi_mutex);
1489
1490 return err;
1491}
1492
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001493static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
1494 struct mv88e6xxx_vtu_stu_entry *entry)
1495{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001496 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001497 u16 reg = 0;
1498 int ret;
1499
1500 ret = _mv88e6xxx_vtu_wait(ds);
1501 if (ret < 0)
1502 return ret;
1503
1504 if (!entry->valid)
1505 goto loadpurge;
1506
1507 /* Write port member tags */
1508 ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 0);
1509 if (ret < 0)
1510 return ret;
1511
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001512 if (mv88e6xxx_has_stu(ds)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001513 reg = entry->sid & GLOBAL_VTU_SID_MASK;
1514 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1515 if (ret < 0)
1516 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001517 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001518
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001519 if (mv88e6xxx_has_fid_reg(ds)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001520 reg = entry->fid & GLOBAL_VTU_FID_MASK;
1521 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg);
1522 if (ret < 0)
1523 return ret;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001524 } else if (mv88e6xxx_num_databases(ds) == 256) {
1525 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1526 * VTU DBNum[3:0] are located in VTU Operation 3:0
1527 */
1528 op |= (entry->fid & 0xf0) << 8;
1529 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001530 }
1531
1532 reg = GLOBAL_VTU_VID_VALID;
1533loadpurge:
1534 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
1535 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1536 if (ret < 0)
1537 return ret;
1538
Vivien Didelot11ea8092016-03-31 16:53:44 -04001539 return _mv88e6xxx_vtu_cmd(ds, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001540}
1541
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001542static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
1543 struct mv88e6xxx_vtu_stu_entry *entry)
1544{
1545 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1546 int ret;
1547
1548 ret = _mv88e6xxx_vtu_wait(ds);
1549 if (ret < 0)
1550 return ret;
1551
1552 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID,
1553 sid & GLOBAL_VTU_SID_MASK);
1554 if (ret < 0)
1555 return ret;
1556
1557 ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT);
1558 if (ret < 0)
1559 return ret;
1560
1561 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID);
1562 if (ret < 0)
1563 return ret;
1564
1565 next.sid = ret & GLOBAL_VTU_SID_MASK;
1566
1567 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1568 if (ret < 0)
1569 return ret;
1570
1571 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1572
1573 if (next.valid) {
1574 ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2);
1575 if (ret < 0)
1576 return ret;
1577 }
1578
1579 *entry = next;
1580 return 0;
1581}
1582
1583static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
1584 struct mv88e6xxx_vtu_stu_entry *entry)
1585{
1586 u16 reg = 0;
1587 int ret;
1588
1589 ret = _mv88e6xxx_vtu_wait(ds);
1590 if (ret < 0)
1591 return ret;
1592
1593 if (!entry->valid)
1594 goto loadpurge;
1595
1596 /* Write port states */
1597 ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2);
1598 if (ret < 0)
1599 return ret;
1600
1601 reg = GLOBAL_VTU_VID_VALID;
1602loadpurge:
1603 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1604 if (ret < 0)
1605 return ret;
1606
1607 reg = entry->sid & GLOBAL_VTU_SID_MASK;
1608 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1609 if (ret < 0)
1610 return ret;
1611
1612 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE);
1613}
1614
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001615static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new,
1616 u16 *old)
1617{
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001618 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001619 u16 fid;
1620 int ret;
1621
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001622 if (mv88e6xxx_num_databases(ds) == 4096)
1623 upper_mask = 0xff;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001624 else if (mv88e6xxx_num_databases(ds) == 256)
1625 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001626 else
1627 return -EOPNOTSUPP;
1628
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001629 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
1630 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
1631 if (ret < 0)
1632 return ret;
1633
1634 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1635
1636 if (new) {
1637 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1638 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1639
1640 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN,
1641 ret);
1642 if (ret < 0)
1643 return ret;
1644 }
1645
1646 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
1647 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_1);
1648 if (ret < 0)
1649 return ret;
1650
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001651 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001652
1653 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001654 ret &= ~upper_mask;
1655 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001656
1657 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1,
1658 ret);
1659 if (ret < 0)
1660 return ret;
1661
1662 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1663 }
1664
1665 if (old)
1666 *old = fid;
1667
1668 return 0;
1669}
1670
1671static int _mv88e6xxx_port_fid_get(struct dsa_switch *ds, int port, u16 *fid)
1672{
1673 return _mv88e6xxx_port_fid(ds, port, NULL, fid);
1674}
1675
1676static int _mv88e6xxx_port_fid_set(struct dsa_switch *ds, int port, u16 fid)
1677{
1678 return _mv88e6xxx_port_fid(ds, port, &fid, NULL);
1679}
1680
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001681static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid)
1682{
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001683 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001684 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1685 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001686 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001687
1688 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1689
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001690 /* Set every FID bit used by the (un)bridged ports */
1691 for (i = 0; i < ps->num_ports; ++i) {
1692 err = _mv88e6xxx_port_fid_get(ds, i, fid);
1693 if (err)
1694 return err;
1695
1696 set_bit(*fid, fid_bitmap);
1697 }
1698
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001699 /* Set every FID bit used by the VLAN entries */
1700 err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK);
1701 if (err)
1702 return err;
1703
1704 do {
1705 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
1706 if (err)
1707 return err;
1708
1709 if (!vlan.valid)
1710 break;
1711
1712 set_bit(vlan.fid, fid_bitmap);
1713 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1714
1715 /* The reset value 0x000 is used to indicate that multiple address
1716 * databases are not needed. Return the next positive available.
1717 */
1718 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001719 if (unlikely(*fid >= mv88e6xxx_num_databases(ds)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001720 return -ENOSPC;
1721
1722 /* Clear the database */
1723 return _mv88e6xxx_atu_flush(ds, *fid, true);
1724}
1725
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001726static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid,
1727 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001728{
1729 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1730 struct mv88e6xxx_vtu_stu_entry vlan = {
1731 .valid = true,
1732 .vid = vid,
1733 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001734 int i, err;
1735
1736 err = _mv88e6xxx_fid_new(ds, &vlan.fid);
1737 if (err)
1738 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001739
Vivien Didelot3d131f02015-11-03 10:52:52 -05001740 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001741 for (i = 0; i < ps->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001742 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1743 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1744 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001745
1746 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
1747 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
1748 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001749
1750 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1751 * implemented, only one STU entry is needed to cover all VTU
1752 * entries. Thus, validate the SID 0.
1753 */
1754 vlan.sid = 0;
1755 err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp);
1756 if (err)
1757 return err;
1758
1759 if (vstp.sid != vlan.sid || !vstp.valid) {
1760 memset(&vstp, 0, sizeof(vstp));
1761 vstp.valid = true;
1762 vstp.sid = vlan.sid;
1763
1764 err = _mv88e6xxx_stu_loadpurge(ds, &vstp);
1765 if (err)
1766 return err;
1767 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001768 }
1769
1770 *entry = vlan;
1771 return 0;
1772}
1773
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001774static int _mv88e6xxx_vtu_get(struct dsa_switch *ds, u16 vid,
1775 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1776{
1777 int err;
1778
1779 if (!vid)
1780 return -EINVAL;
1781
1782 err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
1783 if (err)
1784 return err;
1785
1786 err = _mv88e6xxx_vtu_getnext(ds, entry);
1787 if (err)
1788 return err;
1789
1790 if (entry->vid != vid || !entry->valid) {
1791 if (!creat)
1792 return -EOPNOTSUPP;
1793 /* -ENOENT would've been more appropriate, but switchdev expects
1794 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1795 */
1796
1797 err = _mv88e6xxx_vtu_new(ds, vid, entry);
1798 }
1799
1800 return err;
1801}
1802
Vivien Didelotda9c3592016-02-12 12:09:40 -05001803static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1804 u16 vid_begin, u16 vid_end)
1805{
1806 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1807 struct mv88e6xxx_vtu_stu_entry vlan;
1808 int i, err;
1809
1810 if (!vid_begin)
1811 return -EOPNOTSUPP;
1812
1813 mutex_lock(&ps->smi_mutex);
1814
1815 err = _mv88e6xxx_vtu_vid_write(ds, vid_begin - 1);
1816 if (err)
1817 goto unlock;
1818
1819 do {
1820 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
1821 if (err)
1822 goto unlock;
1823
1824 if (!vlan.valid)
1825 break;
1826
1827 if (vlan.vid > vid_end)
1828 break;
1829
1830 for (i = 0; i < ps->num_ports; ++i) {
1831 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1832 continue;
1833
1834 if (vlan.data[i] ==
1835 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1836 continue;
1837
1838 if (ps->ports[i].bridge_dev ==
1839 ps->ports[port].bridge_dev)
1840 break; /* same bridge, check next VLAN */
1841
1842 netdev_warn(ds->ports[port],
1843 "hardware VLAN %d already used by %s\n",
1844 vlan.vid,
1845 netdev_name(ps->ports[i].bridge_dev));
1846 err = -EOPNOTSUPP;
1847 goto unlock;
1848 }
1849 } while (vlan.vid < vid_end);
1850
1851unlock:
1852 mutex_unlock(&ps->smi_mutex);
1853
1854 return err;
1855}
1856
Vivien Didelot214cdb92016-02-26 13:16:08 -05001857static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1858 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1859 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1860 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1861 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1862};
1863
1864int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1865 bool vlan_filtering)
1866{
1867 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1868 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1869 PORT_CONTROL_2_8021Q_DISABLED;
1870 int ret;
1871
1872 mutex_lock(&ps->smi_mutex);
1873
1874 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_2);
1875 if (ret < 0)
1876 goto unlock;
1877
1878 old = ret & PORT_CONTROL_2_8021Q_MASK;
1879
Vivien Didelot5220ef12016-03-07 18:24:52 -05001880 if (new != old) {
1881 ret &= ~PORT_CONTROL_2_8021Q_MASK;
1882 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001883
Vivien Didelot5220ef12016-03-07 18:24:52 -05001884 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_2,
1885 ret);
1886 if (ret < 0)
1887 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001888
Vivien Didelot5220ef12016-03-07 18:24:52 -05001889 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
1890 mv88e6xxx_port_8021q_mode_names[new],
1891 mv88e6xxx_port_8021q_mode_names[old]);
1892 }
1893
1894 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001895unlock:
1896 mutex_unlock(&ps->smi_mutex);
1897
1898 return ret;
1899}
1900
Vivien Didelot76e398a2015-11-01 12:33:55 -05001901int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1902 const struct switchdev_obj_port_vlan *vlan,
1903 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001904{
Vivien Didelotda9c3592016-02-12 12:09:40 -05001905 int err;
1906
Vivien Didelotda9c3592016-02-12 12:09:40 -05001907 /* If the requested port doesn't belong to the same bridge as the VLAN
1908 * members, do not support it (yet) and fallback to software VLAN.
1909 */
1910 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1911 vlan->vid_end);
1912 if (err)
1913 return err;
1914
Vivien Didelot76e398a2015-11-01 12:33:55 -05001915 /* We don't need any dynamic resource from the kernel (yet),
1916 * so skip the prepare phase.
1917 */
1918 return 0;
1919}
1920
1921static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
1922 bool untagged)
1923{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001924 struct mv88e6xxx_vtu_stu_entry vlan;
1925 int err;
1926
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001927 err = _mv88e6xxx_vtu_get(ds, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001928 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001929 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001930
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001931 vlan.data[port] = untagged ?
1932 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1933 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1934
Vivien Didelot76e398a2015-11-01 12:33:55 -05001935 return _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1936}
1937
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001938void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1939 const struct switchdev_obj_port_vlan *vlan,
1940 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001941{
1942 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1943 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1944 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1945 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001946
1947 mutex_lock(&ps->smi_mutex);
1948
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001949 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
1950 if (_mv88e6xxx_port_vlan_add(ds, port, vid, untagged))
1951 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
1952 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05001953
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001954 if (pvid && _mv88e6xxx_port_pvid_set(ds, port, vlan->vid_end))
1955 netdev_err(ds->ports[port], "failed to set PVID %d\n",
1956 vlan->vid_end);
1957
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001958 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001959}
1960
Vivien Didelot76e398a2015-11-01 12:33:55 -05001961static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001962{
1963 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1964 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001965 int i, err;
1966
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001967 err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001968 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001969 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001970
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001971 /* Tell switchdev if this VLAN is handled in software */
1972 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05001973 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001974
1975 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1976
1977 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001978 vlan.valid = false;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001979 for (i = 0; i < ps->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05001980 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001981 continue;
1982
1983 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001984 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001985 break;
1986 }
1987 }
1988
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001989 err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1990 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001991 return err;
1992
1993 return _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
1994}
1995
1996int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1997 const struct switchdev_obj_port_vlan *vlan)
1998{
1999 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2000 u16 pvid, vid;
2001 int err = 0;
2002
2003 mutex_lock(&ps->smi_mutex);
2004
2005 err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
2006 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002007 goto unlock;
2008
Vivien Didelot76e398a2015-11-01 12:33:55 -05002009 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
2010 err = _mv88e6xxx_port_vlan_del(ds, port, vid);
2011 if (err)
2012 goto unlock;
2013
2014 if (vid == pvid) {
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002015 err = _mv88e6xxx_port_pvid_set(ds, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002016 if (err)
2017 goto unlock;
2018 }
2019 }
2020
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002021unlock:
2022 mutex_unlock(&ps->smi_mutex);
2023
2024 return err;
2025}
2026
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002027static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
2028 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002029{
2030 int i, ret;
2031
2032 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002033 ret = _mv88e6xxx_reg_write(
2034 ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
2035 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002036 if (ret < 0)
2037 return ret;
2038 }
2039
2040 return 0;
2041}
2042
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002043static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002044{
2045 int i, ret;
2046
2047 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002048 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
2049 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002050 if (ret < 0)
2051 return ret;
2052 addr[i * 2] = ret >> 8;
2053 addr[i * 2 + 1] = ret & 0xff;
2054 }
2055
2056 return 0;
2057}
2058
Vivien Didelotfd231c82015-08-10 09:09:50 -04002059static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
2060 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002061{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002062 int ret;
2063
2064 ret = _mv88e6xxx_atu_wait(ds);
2065 if (ret < 0)
2066 return ret;
2067
Vivien Didelotfd231c82015-08-10 09:09:50 -04002068 ret = _mv88e6xxx_atu_mac_write(ds, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002069 if (ret < 0)
2070 return ret;
2071
Vivien Didelot37705b72015-09-04 14:34:11 -04002072 ret = _mv88e6xxx_atu_data_write(ds, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002073 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002074 return ret;
2075
Vivien Didelotb426e5f2016-03-31 16:53:42 -04002076 return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002077}
David S. Millercdf09692015-08-11 12:00:37 -07002078
Vivien Didelotfd231c82015-08-10 09:09:50 -04002079static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
2080 const unsigned char *addr, u16 vid,
2081 u8 state)
2082{
2083 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002084 struct mv88e6xxx_vtu_stu_entry vlan;
2085 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002086
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002087 /* Null VLAN ID corresponds to the port private database */
2088 if (vid == 0)
2089 err = _mv88e6xxx_port_fid_get(ds, port, &vlan.fid);
2090 else
2091 err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002092 if (err)
2093 return err;
2094
2095 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002096 entry.state = state;
2097 ether_addr_copy(entry.mac, addr);
2098 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2099 entry.trunk = false;
2100 entry.portv_trunkid = BIT(port);
2101 }
2102
2103 return _mv88e6xxx_atu_load(ds, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002104}
2105
Vivien Didelot146a3202015-10-08 11:35:12 -04002106int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2107 const struct switchdev_obj_port_fdb *fdb,
2108 struct switchdev_trans *trans)
2109{
2110 /* We don't need any dynamic resource from the kernel (yet),
2111 * so skip the prepare phase.
2112 */
2113 return 0;
2114}
2115
Vivien Didelot8497aa62016-04-06 11:55:04 -04002116void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2117 const struct switchdev_obj_port_fdb *fdb,
2118 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002119{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002120 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002121 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2122 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2123 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002124
David S. Millercdf09692015-08-11 12:00:37 -07002125 mutex_lock(&ps->smi_mutex);
Vivien Didelot8497aa62016-04-06 11:55:04 -04002126 if (_mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state))
2127 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002128 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002129}
2130
2131int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
Vivien Didelot8057b3e2015-10-08 11:35:14 -04002132 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002133{
2134 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2135 int ret;
2136
2137 mutex_lock(&ps->smi_mutex);
Vivien Didelot8057b3e2015-10-08 11:35:14 -04002138 ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002139 GLOBAL_ATU_DATA_STATE_UNUSED);
2140 mutex_unlock(&ps->smi_mutex);
2141
2142 return ret;
2143}
2144
Vivien Didelot1d194042015-08-10 09:09:51 -04002145static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002146 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002147{
Vivien Didelot1d194042015-08-10 09:09:51 -04002148 struct mv88e6xxx_atu_entry next = { 0 };
2149 int ret;
2150
2151 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002152
2153 ret = _mv88e6xxx_atu_wait(ds);
2154 if (ret < 0)
2155 return ret;
2156
Vivien Didelotb426e5f2016-03-31 16:53:42 -04002157 ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002158 if (ret < 0)
2159 return ret;
2160
Vivien Didelot1d194042015-08-10 09:09:51 -04002161 ret = _mv88e6xxx_atu_mac_read(ds, next.mac);
2162 if (ret < 0)
2163 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002164
Vivien Didelot1d194042015-08-10 09:09:51 -04002165 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
2166 if (ret < 0)
2167 return ret;
2168
2169 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2170 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2171 unsigned int mask, shift;
2172
2173 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2174 next.trunk = true;
2175 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2176 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2177 } else {
2178 next.trunk = false;
2179 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2180 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2181 }
2182
2183 next.portv_trunkid = (ret & mask) >> shift;
2184 }
2185
2186 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002187 return 0;
2188}
2189
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002190static int _mv88e6xxx_port_fdb_dump_one(struct dsa_switch *ds, u16 fid, u16 vid,
2191 int port,
2192 struct switchdev_obj_port_fdb *fdb,
2193 int (*cb)(struct switchdev_obj *obj))
2194{
2195 struct mv88e6xxx_atu_entry addr = {
2196 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2197 };
2198 int err;
2199
2200 err = _mv88e6xxx_atu_mac_write(ds, addr.mac);
2201 if (err)
2202 return err;
2203
2204 do {
2205 err = _mv88e6xxx_atu_getnext(ds, fid, &addr);
2206 if (err)
2207 break;
2208
2209 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2210 break;
2211
2212 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2213 bool is_static = addr.state ==
2214 (is_multicast_ether_addr(addr.mac) ?
2215 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2216 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2217
2218 fdb->vid = vid;
2219 ether_addr_copy(fdb->addr, addr.mac);
2220 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2221
2222 err = cb(&fdb->obj);
2223 if (err)
2224 break;
2225 }
2226 } while (!is_broadcast_ether_addr(addr.mac));
2227
2228 return err;
2229}
2230
Vivien Didelotf33475b2015-10-22 09:34:41 -04002231int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2232 struct switchdev_obj_port_fdb *fdb,
2233 int (*cb)(struct switchdev_obj *obj))
2234{
2235 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2236 struct mv88e6xxx_vtu_stu_entry vlan = {
2237 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2238 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002239 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002240 int err;
2241
2242 mutex_lock(&ps->smi_mutex);
2243
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002244 /* Dump port's default Filtering Information Database (VLAN ID 0) */
2245 err = _mv88e6xxx_port_fid_get(ds, port, &fid);
2246 if (err)
2247 goto unlock;
2248
2249 err = _mv88e6xxx_port_fdb_dump_one(ds, fid, 0, port, fdb, cb);
2250 if (err)
2251 goto unlock;
2252
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002253 /* Dump VLANs' Filtering Information Databases */
Vivien Didelotf33475b2015-10-22 09:34:41 -04002254 err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid);
2255 if (err)
2256 goto unlock;
2257
2258 do {
Vivien Didelotf33475b2015-10-22 09:34:41 -04002259 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
2260 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002261 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002262
2263 if (!vlan.valid)
2264 break;
2265
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002266 err = _mv88e6xxx_port_fdb_dump_one(ds, vlan.fid, vlan.vid, port,
2267 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002268 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002269 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002270 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2271
2272unlock:
2273 mutex_unlock(&ps->smi_mutex);
2274
2275 return err;
2276}
2277
Vivien Didelota6692752016-02-12 12:09:39 -05002278int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2279 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002280{
Vivien Didelota6692752016-02-12 12:09:39 -05002281 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002282 u16 fid;
2283 int i, err;
2284
2285 mutex_lock(&ps->smi_mutex);
2286
2287 /* Get or create the bridge FID and assign it to the port */
2288 for (i = 0; i < ps->num_ports; ++i)
2289 if (ps->ports[i].bridge_dev == bridge)
2290 break;
2291
2292 if (i < ps->num_ports)
2293 err = _mv88e6xxx_port_fid_get(ds, i, &fid);
2294 else
2295 err = _mv88e6xxx_fid_new(ds, &fid);
2296 if (err)
2297 goto unlock;
2298
2299 err = _mv88e6xxx_port_fid_set(ds, port, fid);
2300 if (err)
2301 goto unlock;
Vivien Didelota6692752016-02-12 12:09:39 -05002302
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002303 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002304 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002305
2306 for (i = 0; i < ps->num_ports; ++i) {
2307 if (ps->ports[i].bridge_dev == bridge) {
2308 err = _mv88e6xxx_port_based_vlan_map(ds, i);
2309 if (err)
2310 break;
2311 }
2312 }
2313
Vivien Didelot466dfa02016-02-26 13:16:05 -05002314unlock:
2315 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002316
Vivien Didelot466dfa02016-02-26 13:16:05 -05002317 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002318}
2319
Vivien Didelot16bfa702016-03-13 16:21:33 -04002320void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002321{
Vivien Didelota6692752016-02-12 12:09:39 -05002322 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002323 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002324 u16 fid;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002325 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002326
2327 mutex_lock(&ps->smi_mutex);
2328
2329 /* Give the port a fresh Filtering Information Database */
Vivien Didelot16bfa702016-03-13 16:21:33 -04002330 if (_mv88e6xxx_fid_new(ds, &fid) ||
2331 _mv88e6xxx_port_fid_set(ds, port, fid))
2332 netdev_warn(ds->ports[port], "failed to assign a new FID\n");
Vivien Didelota6692752016-02-12 12:09:39 -05002333
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002334 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002335 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002336
Vivien Didelot16bfa702016-03-13 16:21:33 -04002337 for (i = 0; i < ps->num_ports; ++i)
2338 if (i == port || ps->ports[i].bridge_dev == bridge)
2339 if (_mv88e6xxx_port_based_vlan_map(ds, i))
2340 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002341
Vivien Didelot466dfa02016-02-26 13:16:05 -05002342 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002343}
2344
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002345static void mv88e6xxx_bridge_work(struct work_struct *work)
2346{
2347 struct mv88e6xxx_priv_state *ps;
2348 struct dsa_switch *ds;
2349 int port;
2350
2351 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002352 ds = ps->ds;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002353
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002354 mutex_lock(&ps->smi_mutex);
2355
2356 for (port = 0; port < ps->num_ports; ++port)
2357 if (test_and_clear_bit(port, ps->port_state_update_mask) &&
2358 _mv88e6xxx_port_state(ds, port, ps->ports[port].state))
2359 netdev_warn(ds->ports[port], "failed to update state to %s\n",
2360 mv88e6xxx_port_state_names[ps->ports[port].state]);
2361
2362 mutex_unlock(&ps->smi_mutex);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002363}
2364
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002365static int _mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
2366 int reg, int val)
2367{
2368 int ret;
2369
2370 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
2371 if (ret < 0)
2372 goto restore_page_0;
2373
2374 ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
2375restore_page_0:
2376 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
2377
2378 return ret;
2379}
2380
2381static int _mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page,
2382 int reg)
2383{
2384 int ret;
2385
2386 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
2387 if (ret < 0)
2388 goto restore_page_0;
2389
2390 ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
2391restore_page_0:
2392 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
2393
2394 return ret;
2395}
2396
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002397static int mv88e6xxx_power_on_serdes(struct dsa_switch *ds)
2398{
2399 int ret;
2400
2401 ret = _mv88e6xxx_phy_page_read(ds, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
2402 MII_BMCR);
2403 if (ret < 0)
2404 return ret;
2405
2406 if (ret & BMCR_PDOWN) {
2407 ret &= ~BMCR_PDOWN;
2408 ret = _mv88e6xxx_phy_page_write(ds, REG_FIBER_SERDES,
2409 PAGE_FIBER_SERDES, MII_BMCR,
2410 ret);
2411 }
2412
2413 return ret;
2414}
2415
Andrew Lunndbde9e62015-05-06 01:09:48 +02002416static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002417{
2418 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002419 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002420 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002421
2422 mutex_lock(&ps->smi_mutex);
2423
Andrew Lunn54d792f2015-05-06 01:09:47 +02002424 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2425 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2426 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002427 mv88e6xxx_6065_family(ds) || mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002428 /* MAC Forcing register: don't force link, speed,
2429 * duplex or flow control state to any particular
2430 * values on physical ports, but force the CPU port
2431 * and all DSA ports to their maximum bandwidth and
2432 * full duplex.
2433 */
2434 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002435 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002436 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002437 reg |= PORT_PCS_CTRL_FORCE_LINK |
2438 PORT_PCS_CTRL_LINK_UP |
2439 PORT_PCS_CTRL_DUPLEX_FULL |
2440 PORT_PCS_CTRL_FORCE_DUPLEX;
2441 if (mv88e6xxx_6065_family(ds))
2442 reg |= PORT_PCS_CTRL_100;
2443 else
2444 reg |= PORT_PCS_CTRL_1000;
2445 } else {
2446 reg |= PORT_PCS_CTRL_UNFORCED;
2447 }
2448
2449 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2450 PORT_PCS_CTRL, reg);
2451 if (ret)
2452 goto abort;
2453 }
2454
2455 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2456 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2457 * tunneling, determine priority by looking at 802.1p and IP
2458 * priority fields (IP prio has precedence), and set STP state
2459 * to Forwarding.
2460 *
2461 * If this is the CPU link, use DSA or EDSA tagging depending
2462 * on which tagging mode was configured.
2463 *
2464 * If this is a link to another switch, use DSA tagging mode.
2465 *
2466 * If this is the upstream port for this switch, enable
2467 * forwarding of unknown unicasts and multicasts.
2468 */
2469 reg = 0;
2470 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2471 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2472 mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002473 mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002474 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2475 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2476 PORT_CONTROL_STATE_FORWARDING;
2477 if (dsa_is_cpu_port(ds, port)) {
2478 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
2479 reg |= PORT_CONTROL_DSA_TAG;
2480 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002481 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2482 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002483 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2484 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2485 else
2486 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002487 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2488 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002489 }
2490
2491 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2492 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2493 mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002494 mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002495 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2496 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2497 }
2498 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002499 if (dsa_is_dsa_port(ds, port)) {
2500 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
2501 reg |= PORT_CONTROL_DSA_TAG;
2502 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2503 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2504 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002505 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002506 }
2507
Andrew Lunn54d792f2015-05-06 01:09:47 +02002508 if (port == dsa_upstream_port(ds))
2509 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2510 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2511 }
2512 if (reg) {
2513 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2514 PORT_CONTROL, reg);
2515 if (ret)
2516 goto abort;
2517 }
2518
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002519 /* If this port is connected to a SerDes, make sure the SerDes is not
2520 * powered down.
2521 */
2522 if (mv88e6xxx_6352_family(ds)) {
2523 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
2524 if (ret < 0)
2525 goto abort;
2526 ret &= PORT_STATUS_CMODE_MASK;
2527 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2528 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2529 (ret == PORT_STATUS_CMODE_SGMII)) {
2530 ret = mv88e6xxx_power_on_serdes(ds);
2531 if (ret < 0)
2532 goto abort;
2533 }
2534 }
2535
Vivien Didelot8efdda42015-08-13 12:52:23 -04002536 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002537 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002538 * untagged frames on this port, do a destination address lookup on all
2539 * received packets as usual, disable ARP mirroring and don't send a
2540 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002541 */
2542 reg = 0;
2543 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2544 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Vivien Didelotf93dd042016-03-31 16:53:45 -04002545 mv88e6xxx_6095_family(ds) || mv88e6xxx_6320_family(ds) ||
2546 mv88e6xxx_6185_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002547 reg = PORT_CONTROL_2_MAP_DA;
2548
2549 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002550 mv88e6xxx_6165_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002551 reg |= PORT_CONTROL_2_JUMBO_10240;
2552
2553 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) {
2554 /* Set the upstream port this port should use */
2555 reg |= dsa_upstream_port(ds);
2556 /* enable forwarding of unknown multicast addresses to
2557 * the upstream port
2558 */
2559 if (port == dsa_upstream_port(ds))
2560 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2561 }
2562
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002563 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002564
Andrew Lunn54d792f2015-05-06 01:09:47 +02002565 if (reg) {
2566 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2567 PORT_CONTROL_2, reg);
2568 if (ret)
2569 goto abort;
2570 }
2571
2572 /* Port Association Vector: when learning source addresses
2573 * of packets, add the address to the address database using
2574 * a port bitmap that has only the bit for this port set and
2575 * the other bits clear.
2576 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002577 reg = 1 << port;
2578 /* Disable learning for DSA and CPU ports */
2579 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
2580 reg = PORT_ASSOC_VECTOR_LOCKED_PORT;
2581
2582 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002583 if (ret)
2584 goto abort;
2585
2586 /* Egress rate control 2: disable egress rate control. */
2587 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_RATE_CONTROL_2,
2588 0x0000);
2589 if (ret)
2590 goto abort;
2591
2592 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002593 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2594 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002595 /* Do not limit the period of time that this port can
2596 * be paused for by the remote end or the period of
2597 * time that this port can pause the remote end.
2598 */
2599 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2600 PORT_PAUSE_CTRL, 0x0000);
2601 if (ret)
2602 goto abort;
2603
2604 /* Port ATU control: disable limiting the number of
2605 * address database entries that this port is allowed
2606 * to use.
2607 */
2608 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2609 PORT_ATU_CONTROL, 0x0000);
2610 /* Priority Override: disable DA, SA and VTU priority
2611 * override.
2612 */
2613 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2614 PORT_PRI_OVERRIDE, 0x0000);
2615 if (ret)
2616 goto abort;
2617
2618 /* Port Ethertype: use the Ethertype DSA Ethertype
2619 * value.
2620 */
2621 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2622 PORT_ETH_TYPE, ETH_P_EDSA);
2623 if (ret)
2624 goto abort;
2625 /* Tag Remap: use an identity 802.1p prio -> switch
2626 * prio mapping.
2627 */
2628 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2629 PORT_TAG_REGMAP_0123, 0x3210);
2630 if (ret)
2631 goto abort;
2632
2633 /* Tag Remap 2: use an identity 802.1p prio -> switch
2634 * prio mapping.
2635 */
2636 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2637 PORT_TAG_REGMAP_4567, 0x7654);
2638 if (ret)
2639 goto abort;
2640 }
2641
2642 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2643 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002644 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
2645 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002646 /* Rate Control: disable ingress rate limiting. */
2647 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2648 PORT_RATE_CONTROL, 0x0001);
2649 if (ret)
2650 goto abort;
2651 }
2652
Guenter Roeck366f0a02015-03-26 18:36:30 -07002653 /* Port Control 1: disable trunking, disable sending
2654 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002655 */
Vivien Didelot614f03f2015-04-20 17:19:23 -04002656 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002657 if (ret)
2658 goto abort;
2659
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002660 /* Port based VLAN map: give each port its own address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002661 * database, and allow bidirectional communication between the
2662 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002663 */
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002664 ret = _mv88e6xxx_port_fid_set(ds, port, port + 1);
2665 if (ret)
2666 goto abort;
2667
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002668 ret = _mv88e6xxx_port_based_vlan_map(ds, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002669 if (ret)
2670 goto abort;
2671
2672 /* Default VLAN ID and priority: don't set a default VLAN
2673 * ID, and set the default packet priority to zero.
2674 */
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002675 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
2676 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002677abort:
2678 mutex_unlock(&ps->smi_mutex);
2679 return ret;
2680}
2681
Andrew Lunndbde9e62015-05-06 01:09:48 +02002682int mv88e6xxx_setup_ports(struct dsa_switch *ds)
2683{
2684 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2685 int ret;
2686 int i;
2687
2688 for (i = 0; i < ps->num_ports; i++) {
2689 ret = mv88e6xxx_setup_port(ds, i);
2690 if (ret < 0)
2691 return ret;
2692 }
2693 return 0;
2694}
2695
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002696int mv88e6xxx_setup_common(struct dsa_switch *ds)
2697{
2698 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2699
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002700 ps->ds = ds;
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002701 mutex_init(&ps->smi_mutex);
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002702
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002703 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
2704
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002705 return 0;
2706}
2707
Andrew Lunn54d792f2015-05-06 01:09:47 +02002708int mv88e6xxx_setup_global(struct dsa_switch *ds)
2709{
2710 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002711 int err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002712 int i;
2713
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002714 mutex_lock(&ps->smi_mutex);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002715 /* Set the default address aging time to 5 minutes, and
2716 * enable address learn messages to be sent to all message
2717 * ports.
2718 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002719 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2720 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2721 if (err)
2722 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002723
2724 /* Configure the IP ToS mapping registers. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002725 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2726 if (err)
2727 goto unlock;
2728 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2729 if (err)
2730 goto unlock;
2731 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2732 if (err)
2733 goto unlock;
2734 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2735 if (err)
2736 goto unlock;
2737 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2738 if (err)
2739 goto unlock;
2740 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2741 if (err)
2742 goto unlock;
2743 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2744 if (err)
2745 goto unlock;
2746 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2747 if (err)
2748 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002749
2750 /* Configure the IEEE 802.1p priority mapping register. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002751 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
2752 if (err)
2753 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002754
2755 /* Send all frames with destination addresses matching
2756 * 01:80:c2:00:00:0x to the CPU port.
2757 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002758 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
2759 if (err)
2760 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002761
2762 /* Ignore removed tag data on doubly tagged packets, disable
2763 * flow control messages, force flow control priority to the
2764 * highest, and send all special multicast frames to the CPU
2765 * port at the highest priority.
2766 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002767 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
2768 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
2769 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
2770 if (err)
2771 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002772
2773 /* Program the DSA routing table. */
2774 for (i = 0; i < 32; i++) {
2775 int nexthop = 0x1f;
2776
2777 if (ds->pd->rtable &&
2778 i != ds->index && i < ds->dst->pd->nr_chips)
2779 nexthop = ds->pd->rtable[i] & 0x1f;
2780
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002781 err = _mv88e6xxx_reg_write(
2782 ds, REG_GLOBAL2,
2783 GLOBAL2_DEVICE_MAPPING,
2784 GLOBAL2_DEVICE_MAPPING_UPDATE |
2785 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
2786 if (err)
2787 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002788 }
2789
2790 /* Clear all trunk masks. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002791 for (i = 0; i < 8; i++) {
2792 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
2793 0x8000 |
2794 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
2795 ((1 << ps->num_ports) - 1));
2796 if (err)
2797 goto unlock;
2798 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002799
2800 /* Clear all trunk mappings. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002801 for (i = 0; i < 16; i++) {
2802 err = _mv88e6xxx_reg_write(
2803 ds, REG_GLOBAL2,
2804 GLOBAL2_TRUNK_MAPPING,
2805 GLOBAL2_TRUNK_MAPPING_UPDATE |
2806 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
2807 if (err)
2808 goto unlock;
2809 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002810
2811 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002812 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2813 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002814 /* Send all frames with destination addresses matching
2815 * 01:80:c2:00:00:2x to the CPU port.
2816 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002817 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2818 GLOBAL2_MGMT_EN_2X, 0xffff);
2819 if (err)
2820 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002821
2822 /* Initialise cross-chip port VLAN table to reset
2823 * defaults.
2824 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002825 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2826 GLOBAL2_PVT_ADDR, 0x9000);
2827 if (err)
2828 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002829
2830 /* Clear the priority override table. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002831 for (i = 0; i < 16; i++) {
2832 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2833 GLOBAL2_PRIO_OVERRIDE,
2834 0x8000 | (i << 8));
2835 if (err)
2836 goto unlock;
2837 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002838 }
2839
2840 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2841 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002842 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
2843 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002844 /* Disable ingress rate limiting by resetting all
2845 * ingress rate limit registers to their initial
2846 * state.
2847 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002848 for (i = 0; i < ps->num_ports; i++) {
2849 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2850 GLOBAL2_INGRESS_OP,
2851 0x9000 | (i << 8));
2852 if (err)
2853 goto unlock;
2854 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002855 }
2856
Andrew Lunndb687a52015-06-20 21:31:29 +02002857 /* Clear the statistics counters for all ports */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002858 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
2859 GLOBAL_STATS_OP_FLUSH_ALL);
2860 if (err)
2861 goto unlock;
Andrew Lunndb687a52015-06-20 21:31:29 +02002862
2863 /* Wait for the flush to complete. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002864 err = _mv88e6xxx_stats_wait(ds);
2865 if (err < 0)
Vivien Didelot6b17e862015-08-13 12:52:18 -04002866 goto unlock;
2867
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002868 /* Clear all ATU entries */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002869 err = _mv88e6xxx_atu_flush(ds, 0, true);
2870 if (err < 0)
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002871 goto unlock;
2872
Vivien Didelot6b17e862015-08-13 12:52:18 -04002873 /* Clear all the VTU and STU entries */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002874 err = _mv88e6xxx_vtu_stu_flush(ds);
Vivien Didelot6b17e862015-08-13 12:52:18 -04002875unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04002876 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02002877
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002878 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002879}
2880
Andrew Lunn143a8302015-04-02 04:06:34 +02002881int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
2882{
2883 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2884 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunnc8c1b392015-11-20 03:56:24 +01002885 struct gpio_desc *gpiod = ds->pd->reset;
Andrew Lunn143a8302015-04-02 04:06:34 +02002886 unsigned long timeout;
2887 int ret;
2888 int i;
2889
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002890 mutex_lock(&ps->smi_mutex);
2891
Andrew Lunn143a8302015-04-02 04:06:34 +02002892 /* Set all ports to the disabled state. */
2893 for (i = 0; i < ps->num_ports; i++) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002894 ret = _mv88e6xxx_reg_read(ds, REG_PORT(i), PORT_CONTROL);
2895 if (ret < 0)
2896 goto unlock;
2897
2898 ret = _mv88e6xxx_reg_write(ds, REG_PORT(i), PORT_CONTROL,
2899 ret & 0xfffc);
2900 if (ret)
2901 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02002902 }
2903
2904 /* Wait for transmit queues to drain. */
2905 usleep_range(2000, 4000);
2906
Andrew Lunnc8c1b392015-11-20 03:56:24 +01002907 /* If there is a gpio connected to the reset pin, toggle it */
2908 if (gpiod) {
2909 gpiod_set_value_cansleep(gpiod, 1);
2910 usleep_range(10000, 20000);
2911 gpiod_set_value_cansleep(gpiod, 0);
2912 usleep_range(10000, 20000);
2913 }
2914
Andrew Lunn143a8302015-04-02 04:06:34 +02002915 /* Reset the switch. Keep the PPU active if requested. The PPU
2916 * needs to be active to support indirect phy register access
2917 * through global registers 0x18 and 0x19.
2918 */
2919 if (ppu_active)
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002920 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x04, 0xc000);
Andrew Lunn143a8302015-04-02 04:06:34 +02002921 else
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002922 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x04, 0xc400);
2923 if (ret)
2924 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02002925
2926 /* Wait up to one second for reset to complete. */
2927 timeout = jiffies + 1 * HZ;
2928 while (time_before(jiffies, timeout)) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002929 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x00);
2930 if (ret < 0)
2931 goto unlock;
2932
Andrew Lunn143a8302015-04-02 04:06:34 +02002933 if ((ret & is_reset) == is_reset)
2934 break;
2935 usleep_range(1000, 2000);
2936 }
2937 if (time_after(jiffies, timeout))
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002938 ret = -ETIMEDOUT;
2939 else
2940 ret = 0;
2941unlock:
2942 mutex_unlock(&ps->smi_mutex);
Andrew Lunn143a8302015-04-02 04:06:34 +02002943
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002944 return ret;
Andrew Lunn143a8302015-04-02 04:06:34 +02002945}
2946
Andrew Lunn491435852015-04-02 04:06:35 +02002947int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
2948{
2949 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2950 int ret;
2951
Andrew Lunn3898c142015-05-06 01:09:53 +02002952 mutex_lock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002953 ret = _mv88e6xxx_phy_page_read(ds, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02002954 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002955
Andrew Lunn491435852015-04-02 04:06:35 +02002956 return ret;
2957}
2958
2959int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
2960 int reg, int val)
2961{
2962 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2963 int ret;
2964
Andrew Lunn3898c142015-05-06 01:09:53 +02002965 mutex_lock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002966 ret = _mv88e6xxx_phy_page_write(ds, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002967 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002968
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002969 return ret;
2970}
2971
2972static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port)
2973{
2974 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2975
2976 if (port >= 0 && port < ps->num_ports)
2977 return port;
2978 return -EINVAL;
2979}
2980
2981int
2982mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
2983{
2984 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2985 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2986 int ret;
2987
2988 if (addr < 0)
2989 return addr;
2990
Andrew Lunn3898c142015-05-06 01:09:53 +02002991 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002992 ret = _mv88e6xxx_phy_read(ds, addr, regnum);
Andrew Lunn3898c142015-05-06 01:09:53 +02002993 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002994 return ret;
2995}
2996
2997int
2998mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
2999{
3000 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3001 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
3002 int ret;
3003
3004 if (addr < 0)
3005 return addr;
3006
Andrew Lunn3898c142015-05-06 01:09:53 +02003007 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003008 ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003009 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003010 return ret;
3011}
3012
3013int
3014mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
3015{
3016 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3017 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
3018 int ret;
3019
3020 if (addr < 0)
3021 return addr;
3022
Andrew Lunn3898c142015-05-06 01:09:53 +02003023 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003024 ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
Andrew Lunn3898c142015-05-06 01:09:53 +02003025 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003026 return ret;
3027}
3028
3029int
3030mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
3031 u16 val)
3032{
3033 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3034 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
3035 int ret;
3036
3037 if (addr < 0)
3038 return addr;
3039
Andrew Lunn3898c142015-05-06 01:09:53 +02003040 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003041 ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003042 mutex_unlock(&ps->smi_mutex);
Andrew Lunn491435852015-04-02 04:06:35 +02003043 return ret;
3044}
3045
Guenter Roeckc22995c2015-07-25 09:42:28 -07003046#ifdef CONFIG_NET_DSA_HWMON
3047
3048static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3049{
3050 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3051 int ret;
3052 int val;
3053
3054 *temp = 0;
3055
3056 mutex_lock(&ps->smi_mutex);
3057
3058 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
3059 if (ret < 0)
3060 goto error;
3061
3062 /* Enable temperature sensor */
3063 ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
3064 if (ret < 0)
3065 goto error;
3066
3067 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
3068 if (ret < 0)
3069 goto error;
3070
3071 /* Wait for temperature to stabilize */
3072 usleep_range(10000, 12000);
3073
3074 val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
3075 if (val < 0) {
3076 ret = val;
3077 goto error;
3078 }
3079
3080 /* Disable temperature sensor */
3081 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
3082 if (ret < 0)
3083 goto error;
3084
3085 *temp = ((val & 0x1f) - 5) * 5;
3086
3087error:
3088 _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
3089 mutex_unlock(&ps->smi_mutex);
3090 return ret;
3091}
3092
3093static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3094{
3095 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3096 int ret;
3097
3098 *temp = 0;
3099
3100 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
3101 if (ret < 0)
3102 return ret;
3103
3104 *temp = (ret & 0xff) - 25;
3105
3106 return 0;
3107}
3108
3109int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
3110{
3111 if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds))
3112 return mv88e63xx_get_temp(ds, temp);
3113
3114 return mv88e61xx_get_temp(ds, temp);
3115}
3116
3117int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
3118{
3119 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3120 int ret;
3121
3122 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
3123 return -EOPNOTSUPP;
3124
3125 *temp = 0;
3126
3127 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3128 if (ret < 0)
3129 return ret;
3130
3131 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3132
3133 return 0;
3134}
3135
3136int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
3137{
3138 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3139 int ret;
3140
3141 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
3142 return -EOPNOTSUPP;
3143
3144 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3145 if (ret < 0)
3146 return ret;
3147 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3148 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3149 (ret & 0xe0ff) | (temp << 8));
3150}
3151
3152int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
3153{
3154 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3155 int ret;
3156
3157 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
3158 return -EOPNOTSUPP;
3159
3160 *alarm = false;
3161
3162 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3163 if (ret < 0)
3164 return ret;
3165
3166 *alarm = !!(ret & 0x40);
3167
3168 return 0;
3169}
3170#endif /* CONFIG_NET_DSA_HWMON */
3171
Vivien Didelot0209d142016-04-17 13:23:55 -04003172static const char *
Vivien Didelota439c062016-04-17 13:23:58 -04003173mv88e6xxx_lookup_name(unsigned int id, const struct mv88e6xxx_switch_id *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003174 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003175{
Vivien Didelota439c062016-04-17 13:23:58 -04003176 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003177
Vivien Didelotb9b37712015-10-30 19:39:48 -04003178 for (i = 0; i < num; ++i)
Vivien Didelota439c062016-04-17 13:23:58 -04003179 if (table[i].id == (id & 0xfff0))
Vivien Didelotb9b37712015-10-30 19:39:48 -04003180 return table[i].name;
3181
Vivien Didelotb9b37712015-10-30 19:39:48 -04003182 return NULL;
3183}
3184
Vivien Didelot0209d142016-04-17 13:23:55 -04003185const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
3186 int sw_addr, void **priv,
3187 const struct mv88e6xxx_switch_id *table,
3188 unsigned int num)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003189{
3190 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003191 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003192 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003193 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003194
Vivien Didelota439c062016-04-17 13:23:58 -04003195 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003196 if (!bus)
3197 return NULL;
3198
Vivien Didelota439c062016-04-17 13:23:58 -04003199 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3200 if (id < 0)
3201 return NULL;
3202
3203 prod_num = (id & 0xfff0) >> 4;
3204 rev = id & 0x000f;
3205
3206 name = mv88e6xxx_lookup_name(id, table, num);
3207 if (!name)
3208 return NULL;
3209
3210 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3211 if (!ps)
3212 return NULL;
3213
3214 ps->bus = bus;
3215 ps->sw_addr = sw_addr;
3216 ps->id = id & 0xfff0;
3217
3218 *priv = ps;
3219
3220 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3221 prod_num, name, rev);
3222
Andrew Lunna77d43f2016-04-13 02:40:42 +02003223 return name;
3224}
3225
Ben Hutchings98e67302011-11-25 14:36:19 +00003226static int __init mv88e6xxx_init(void)
3227{
3228#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3229 register_switch_driver(&mv88e6131_switch_driver);
3230#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003231#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3232 register_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003233#endif
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07003234#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3235 register_switch_driver(&mv88e6352_switch_driver);
3236#endif
Andrew Lunn42f27252014-09-12 23:58:44 +02003237#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3238 register_switch_driver(&mv88e6171_switch_driver);
3239#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00003240 return 0;
3241}
3242module_init(mv88e6xxx_init);
3243
3244static void __exit mv88e6xxx_cleanup(void)
3245{
Andrew Lunn42f27252014-09-12 23:58:44 +02003246#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3247 unregister_switch_driver(&mv88e6171_switch_driver);
3248#endif
Vivien Didelot4212b542015-05-01 10:43:52 -04003249#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3250 unregister_switch_driver(&mv88e6352_switch_driver);
3251#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003252#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3253 unregister_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003254#endif
3255#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3256 unregister_switch_driver(&mv88e6131_switch_driver);
3257#endif
3258}
3259module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003260
3261MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3262MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3263MODULE_LICENSE("GPL");