blob: 67b1dd1c22f7d89615ac4bee26ca5536d8f9619b [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 Lunnc8c1b39a2015-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
Vivien Didelot22356472016-04-17 13:24:00 -0400405 return ps->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200406}
407
408static bool mv88e6xxx_6095_family(struct dsa_switch *ds)
409{
410 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
411
Vivien Didelot22356472016-04-17 13:24:00 -0400412 return ps->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200413}
414
415static bool mv88e6xxx_6097_family(struct dsa_switch *ds)
416{
417 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
418
Vivien Didelot22356472016-04-17 13:24:00 -0400419 return ps->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200420}
421
422static bool mv88e6xxx_6165_family(struct dsa_switch *ds)
423{
424 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
425
Vivien Didelot22356472016-04-17 13:24:00 -0400426 return ps->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200427}
428
429static bool mv88e6xxx_6185_family(struct dsa_switch *ds)
430{
431 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
432
Vivien Didelot22356472016-04-17 13:24:00 -0400433 return ps->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200434}
435
Guenter Roeckc22995c2015-07-25 09:42:28 -0700436static bool mv88e6xxx_6320_family(struct dsa_switch *ds)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700437{
438 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
439
Vivien Didelot22356472016-04-17 13:24:00 -0400440 return ps->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700441}
442
Andrew Lunn54d792f2015-05-06 01:09:47 +0200443static bool mv88e6xxx_6351_family(struct dsa_switch *ds)
444{
445 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
446
Vivien Didelot22356472016-04-17 13:24:00 -0400447 return ps->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200448}
449
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200450static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
451{
452 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
453
Vivien Didelot22356472016-04-17 13:24:00 -0400454 return ps->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200455}
456
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400457static unsigned int mv88e6xxx_num_databases(struct dsa_switch *ds)
458{
459 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
460
Vivien Didelotcd5a2c82016-04-17 13:24:02 -0400461 return ps->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400462}
463
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400464static bool mv88e6xxx_has_fid_reg(struct dsa_switch *ds)
465{
466 /* Does the device have dedicated FID registers for ATU and VTU ops? */
467 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
468 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds))
469 return true;
470
471 return false;
472}
473
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -0400474static bool mv88e6xxx_has_stu(struct dsa_switch *ds)
475{
476 /* Does the device have STU and dedicated SID registers for VTU ops? */
477 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
478 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds))
479 return true;
480
481 return false;
482}
483
Andrew Lunndea87022015-08-31 15:56:47 +0200484/* We expect the switch to perform auto negotiation if there is a real
485 * phy. However, in the case of a fixed link phy, we force the port
486 * settings from the fixed link settings.
487 */
488void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
489 struct phy_device *phydev)
490{
491 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200492 u32 reg;
493 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200494
495 if (!phy_is_pseudo_fixed_link(phydev))
496 return;
497
498 mutex_lock(&ps->smi_mutex);
499
500 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
501 if (ret < 0)
502 goto out;
503
504 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
505 PORT_PCS_CTRL_FORCE_LINK |
506 PORT_PCS_CTRL_DUPLEX_FULL |
507 PORT_PCS_CTRL_FORCE_DUPLEX |
508 PORT_PCS_CTRL_UNFORCED);
509
510 reg |= PORT_PCS_CTRL_FORCE_LINK;
511 if (phydev->link)
512 reg |= PORT_PCS_CTRL_LINK_UP;
513
514 if (mv88e6xxx_6065_family(ds) && phydev->speed > SPEED_100)
515 goto out;
516
517 switch (phydev->speed) {
518 case SPEED_1000:
519 reg |= PORT_PCS_CTRL_1000;
520 break;
521 case SPEED_100:
522 reg |= PORT_PCS_CTRL_100;
523 break;
524 case SPEED_10:
525 reg |= PORT_PCS_CTRL_10;
526 break;
527 default:
528 pr_info("Unknown speed");
529 goto out;
530 }
531
532 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
533 if (phydev->duplex == DUPLEX_FULL)
534 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
535
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200536 if ((mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds)) &&
Vivien Didelot009a2b92016-04-17 13:24:01 -0400537 (port >= ps->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200538 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
539 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
540 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
541 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
542 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
543 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
544 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
545 }
Andrew Lunndea87022015-08-31 15:56:47 +0200546 _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_PCS_CTRL, reg);
547
548out:
549 mutex_unlock(&ps->smi_mutex);
550}
551
Andrew Lunn31888232015-05-06 01:09:54 +0200552static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000553{
554 int ret;
555 int i;
556
557 for (i = 0; i < 10; i++) {
Andrew Lunn31888232015-05-06 01:09:54 +0200558 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200559 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000560 return 0;
561 }
562
563 return -ETIMEDOUT;
564}
565
Andrew Lunn31888232015-05-06 01:09:54 +0200566static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000567{
568 int ret;
569
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700570 if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200571 port = (port + 1) << 5;
572
Barry Grussling3675c8d2013-01-08 16:05:53 +0000573 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn31888232015-05-06 01:09:54 +0200574 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
575 GLOBAL_STATS_OP_CAPTURE_PORT |
576 GLOBAL_STATS_OP_HIST_RX_TX | port);
577 if (ret < 0)
578 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000579
Barry Grussling3675c8d2013-01-08 16:05:53 +0000580 /* Wait for the snapshotting to complete. */
Andrew Lunn31888232015-05-06 01:09:54 +0200581 ret = _mv88e6xxx_stats_wait(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000582 if (ret < 0)
583 return ret;
584
585 return 0;
586}
587
Andrew Lunn31888232015-05-06 01:09:54 +0200588static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000589{
590 u32 _val;
591 int ret;
592
593 *val = 0;
594
Andrew Lunn31888232015-05-06 01:09:54 +0200595 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
596 GLOBAL_STATS_OP_READ_CAPTURED |
597 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000598 if (ret < 0)
599 return;
600
Andrew Lunn31888232015-05-06 01:09:54 +0200601 ret = _mv88e6xxx_stats_wait(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000602 if (ret < 0)
603 return;
604
Andrew Lunn31888232015-05-06 01:09:54 +0200605 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000606 if (ret < 0)
607 return;
608
609 _val = ret << 16;
610
Andrew Lunn31888232015-05-06 01:09:54 +0200611 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000612 if (ret < 0)
613 return;
614
615 *val = _val | ret;
616}
617
Andrew Lunne413e7e2015-04-02 04:06:38 +0200618static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100619 { "in_good_octets", 8, 0x00, BANK0, },
620 { "in_bad_octets", 4, 0x02, BANK0, },
621 { "in_unicast", 4, 0x04, BANK0, },
622 { "in_broadcasts", 4, 0x06, BANK0, },
623 { "in_multicasts", 4, 0x07, BANK0, },
624 { "in_pause", 4, 0x16, BANK0, },
625 { "in_undersize", 4, 0x18, BANK0, },
626 { "in_fragments", 4, 0x19, BANK0, },
627 { "in_oversize", 4, 0x1a, BANK0, },
628 { "in_jabber", 4, 0x1b, BANK0, },
629 { "in_rx_error", 4, 0x1c, BANK0, },
630 { "in_fcs_error", 4, 0x1d, BANK0, },
631 { "out_octets", 8, 0x0e, BANK0, },
632 { "out_unicast", 4, 0x10, BANK0, },
633 { "out_broadcasts", 4, 0x13, BANK0, },
634 { "out_multicasts", 4, 0x12, BANK0, },
635 { "out_pause", 4, 0x15, BANK0, },
636 { "excessive", 4, 0x11, BANK0, },
637 { "collisions", 4, 0x1e, BANK0, },
638 { "deferred", 4, 0x05, BANK0, },
639 { "single", 4, 0x14, BANK0, },
640 { "multiple", 4, 0x17, BANK0, },
641 { "out_fcs_error", 4, 0x03, BANK0, },
642 { "late", 4, 0x1f, BANK0, },
643 { "hist_64bytes", 4, 0x08, BANK0, },
644 { "hist_65_127bytes", 4, 0x09, BANK0, },
645 { "hist_128_255bytes", 4, 0x0a, BANK0, },
646 { "hist_256_511bytes", 4, 0x0b, BANK0, },
647 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
648 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
649 { "sw_in_discards", 4, 0x10, PORT, },
650 { "sw_in_filtered", 2, 0x12, PORT, },
651 { "sw_out_filtered", 2, 0x13, PORT, },
652 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
653 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
654 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
655 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
656 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
657 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
658 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
659 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
660 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
661 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
662 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
663 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
664 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
665 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
666 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
667 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
668 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
669 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
670 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
671 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
672 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
673 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
674 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
675 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
676 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
677 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200678};
679
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100680static bool mv88e6xxx_has_stat(struct dsa_switch *ds,
681 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200682{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100683 switch (stat->type) {
684 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200685 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100686 case BANK1:
687 return mv88e6xxx_6320_family(ds);
688 case PORT:
689 return mv88e6xxx_6095_family(ds) ||
690 mv88e6xxx_6185_family(ds) ||
691 mv88e6xxx_6097_family(ds) ||
692 mv88e6xxx_6165_family(ds) ||
693 mv88e6xxx_6351_family(ds) ||
694 mv88e6xxx_6352_family(ds);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200695 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100696 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000697}
698
Andrew Lunn80c46272015-06-20 18:42:30 +0200699static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100700 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200701 int port)
702{
Andrew Lunn80c46272015-06-20 18:42:30 +0200703 u32 low;
704 u32 high = 0;
705 int ret;
706 u64 value;
707
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100708 switch (s->type) {
709 case PORT:
710 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200711 if (ret < 0)
712 return UINT64_MAX;
713
714 low = ret;
715 if (s->sizeof_stat == 4) {
716 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100717 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200718 if (ret < 0)
719 return UINT64_MAX;
720 high = ret;
721 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100722 break;
723 case BANK0:
724 case BANK1:
Andrew Lunn80c46272015-06-20 18:42:30 +0200725 _mv88e6xxx_stats_read(ds, s->reg, &low);
726 if (s->sizeof_stat == 8)
727 _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
728 }
729 value = (((u64)high) << 16) | low;
730 return value;
731}
732
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100733void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
734{
735 struct mv88e6xxx_hw_stat *stat;
736 int i, j;
737
738 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
739 stat = &mv88e6xxx_hw_stats[i];
740 if (mv88e6xxx_has_stat(ds, stat)) {
741 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
742 ETH_GSTRING_LEN);
743 j++;
744 }
745 }
746}
747
748int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
749{
750 struct mv88e6xxx_hw_stat *stat;
751 int i, j;
752
753 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
754 stat = &mv88e6xxx_hw_stats[i];
755 if (mv88e6xxx_has_stat(ds, stat))
756 j++;
757 }
758 return j;
759}
760
761void
762mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
763 int port, uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000764{
Florian Fainellia22adce2014-04-28 11:14:28 -0700765 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100766 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000767 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100768 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000769
Andrew Lunn31888232015-05-06 01:09:54 +0200770 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000771
Andrew Lunn31888232015-05-06 01:09:54 +0200772 ret = _mv88e6xxx_stats_snapshot(ds, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000773 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200774 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000775 return;
776 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100777 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
778 stat = &mv88e6xxx_hw_stats[i];
779 if (mv88e6xxx_has_stat(ds, stat)) {
780 data[j] = _mv88e6xxx_get_ethtool_stat(ds, stat, port);
781 j++;
782 }
783 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000784
Andrew Lunn31888232015-05-06 01:09:54 +0200785 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000786}
Ben Hutchings98e67302011-11-25 14:36:19 +0000787
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700788int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
789{
790 return 32 * sizeof(u16);
791}
792
793void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
794 struct ethtool_regs *regs, void *_p)
795{
796 u16 *p = _p;
797 int i;
798
799 regs->version = 0;
800
801 memset(p, 0xff, 32 * sizeof(u16));
802
803 for (i = 0; i < 32; i++) {
804 int ret;
805
806 ret = mv88e6xxx_reg_read(ds, REG_PORT(port), i);
807 if (ret >= 0)
808 p[i] = ret;
809 }
810}
811
Andrew Lunn3898c142015-05-06 01:09:53 +0200812static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset,
813 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700814{
815 unsigned long timeout = jiffies + HZ / 10;
816
817 while (time_before(jiffies, timeout)) {
818 int ret;
819
820 ret = _mv88e6xxx_reg_read(ds, reg, offset);
821 if (ret < 0)
822 return ret;
823 if (!(ret & mask))
824 return 0;
825
826 usleep_range(1000, 2000);
827 }
828 return -ETIMEDOUT;
829}
830
Andrew Lunn3898c142015-05-06 01:09:53 +0200831static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
832{
833 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
834 int ret;
835
836 mutex_lock(&ps->smi_mutex);
837 ret = _mv88e6xxx_wait(ds, reg, offset, mask);
838 mutex_unlock(&ps->smi_mutex);
839
840 return ret;
841}
842
843static int _mv88e6xxx_phy_wait(struct dsa_switch *ds)
844{
845 return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
846 GLOBAL2_SMI_OP_BUSY);
847}
848
849int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
850{
851 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
852 GLOBAL2_EEPROM_OP_LOAD);
853}
854
855int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
856{
857 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
858 GLOBAL2_EEPROM_OP_BUSY);
859}
860
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700861static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
862{
Andrew Lunncca8b132015-04-02 04:06:39 +0200863 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
864 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700865}
866
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200867static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
868 int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +0100869{
870 int ret;
871
Andrew Lunn3898c142015-05-06 01:09:53 +0200872 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
873 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
874 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +0100875 if (ret < 0)
876 return ret;
877
Andrew Lunn3898c142015-05-06 01:09:53 +0200878 ret = _mv88e6xxx_phy_wait(ds);
879 if (ret < 0)
880 return ret;
881
882 return _mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA);
Andrew Lunnf3044682015-02-14 19:17:50 +0100883}
884
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200885static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
886 int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +0100887{
Andrew Lunn3898c142015-05-06 01:09:53 +0200888 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +0100889
Andrew Lunn3898c142015-05-06 01:09:53 +0200890 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
891 if (ret < 0)
892 return ret;
893
894 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
895 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
896 regnum);
897
898 return _mv88e6xxx_phy_wait(ds);
Andrew Lunnf3044682015-02-14 19:17:50 +0100899}
900
Guenter Roeck11b3b452015-03-06 22:23:51 -0800901int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
902{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200903 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800904 int reg;
905
Andrew Lunn3898c142015-05-06 01:09:53 +0200906 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200907
908 reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800909 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200910 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800911
912 e->eee_enabled = !!(reg & 0x0200);
913 e->tx_lpi_enabled = !!(reg & 0x0100);
914
Andrew Lunn3898c142015-05-06 01:09:53 +0200915 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800916 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200917 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800918
Andrew Lunncca8b132015-04-02 04:06:39 +0200919 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200920 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800921
Andrew Lunn2f40c692015-04-02 04:06:37 +0200922out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200923 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200924 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800925}
926
927int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
928 struct phy_device *phydev, struct ethtool_eee *e)
929{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200930 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
931 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800932 int ret;
933
Andrew Lunn3898c142015-05-06 01:09:53 +0200934 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800935
Andrew Lunn2f40c692015-04-02 04:06:37 +0200936 ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
937 if (ret < 0)
938 goto out;
939
940 reg = ret & ~0x0300;
941 if (e->eee_enabled)
942 reg |= 0x0200;
943 if (e->tx_lpi_enabled)
944 reg |= 0x0100;
945
946 ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
947out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200948 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200949
950 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800951}
952
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400953static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700954{
955 int ret;
956
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400957 if (mv88e6xxx_has_fid_reg(ds)) {
958 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid);
959 if (ret < 0)
960 return ret;
Vivien Didelot11ea8092016-03-31 16:53:44 -0400961 } else if (mv88e6xxx_num_databases(ds) == 256) {
962 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
963 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL);
964 if (ret < 0)
965 return ret;
966
967 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL,
968 (ret & 0xfff) |
969 ((fid << 8) & 0xf000));
970 if (ret < 0)
971 return ret;
972
973 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
974 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400975 }
976
Andrew Lunncca8b132015-04-02 04:06:39 +0200977 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700978 if (ret < 0)
979 return ret;
980
981 return _mv88e6xxx_atu_wait(ds);
982}
983
Vivien Didelot37705b72015-09-04 14:34:11 -0400984static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds,
985 struct mv88e6xxx_atu_entry *entry)
986{
987 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
988
989 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
990 unsigned int mask, shift;
991
992 if (entry->trunk) {
993 data |= GLOBAL_ATU_DATA_TRUNK;
994 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
995 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
996 } else {
997 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
998 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
999 }
1000
1001 data |= (entry->portv_trunkid << shift) & mask;
1002 }
1003
1004 return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, data);
1005}
1006
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001007static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
1008 struct mv88e6xxx_atu_entry *entry,
1009 bool static_too)
1010{
1011 int op;
1012 int err;
1013
1014 err = _mv88e6xxx_atu_wait(ds);
1015 if (err)
1016 return err;
1017
1018 err = _mv88e6xxx_atu_data_write(ds, entry);
1019 if (err)
1020 return err;
1021
1022 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001023 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1024 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1025 } else {
1026 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1027 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1028 }
1029
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001030 return _mv88e6xxx_atu_cmd(ds, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001031}
1032
1033static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too)
1034{
1035 struct mv88e6xxx_atu_entry entry = {
1036 .fid = fid,
1037 .state = 0, /* EntryState bits must be 0 */
1038 };
1039
1040 return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
1041}
1042
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001043static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port,
1044 int to_port, bool static_too)
1045{
1046 struct mv88e6xxx_atu_entry entry = {
1047 .trunk = false,
1048 .fid = fid,
1049 };
1050
1051 /* EntryState bits must be 0xF */
1052 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1053
1054 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1055 entry.portv_trunkid = (to_port & 0x0f) << 4;
1056 entry.portv_trunkid |= from_port & 0x0f;
1057
1058 return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
1059}
1060
1061static int _mv88e6xxx_atu_remove(struct dsa_switch *ds, u16 fid, int port,
1062 bool static_too)
1063{
1064 /* Destination port 0xF means remove the entries */
1065 return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too);
1066}
1067
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001068static const char * const mv88e6xxx_port_state_names[] = {
1069 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1070 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1071 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1072 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1073};
1074
1075static int _mv88e6xxx_port_state(struct dsa_switch *ds, int port, u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001076{
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001077 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001078 u8 oldstate;
1079
Andrew Lunncca8b132015-04-02 04:06:39 +02001080 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001081 if (reg < 0)
1082 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001083
Andrew Lunncca8b132015-04-02 04:06:39 +02001084 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001085
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001086 if (oldstate != state) {
1087 /* Flush forwarding database if we're moving a port
1088 * from Learning or Forwarding state to Disabled or
1089 * Blocking or Listening state.
1090 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001091 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1092 oldstate == PORT_CONTROL_STATE_FORWARDING)
1093 && (state == PORT_CONTROL_STATE_DISABLED ||
1094 state == PORT_CONTROL_STATE_BLOCKING)) {
Vivien Didelot2b8157b2015-09-04 14:34:16 -04001095 ret = _mv88e6xxx_atu_remove(ds, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001096 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001097 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001098 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001099
Andrew Lunncca8b132015-04-02 04:06:39 +02001100 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
1101 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
1102 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001103 if (ret)
1104 return ret;
1105
1106 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1107 mv88e6xxx_port_state_names[state],
1108 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001109 }
1110
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001111 return ret;
1112}
1113
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001114static int _mv88e6xxx_port_based_vlan_map(struct dsa_switch *ds, int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001115{
1116 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001117 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001118 const u16 mask = (1 << ps->info->num_ports) - 1;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001119 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001120 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001121 int i;
1122
1123 /* allow CPU port or DSA link(s) to send frames to every port */
1124 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1125 output_ports = mask;
1126 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001127 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001128 /* allow sending frames to every group member */
1129 if (bridge && ps->ports[i].bridge_dev == bridge)
1130 output_ports |= BIT(i);
1131
1132 /* allow sending frames to CPU port and DSA link(s) */
1133 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1134 output_ports |= BIT(i);
1135 }
1136 }
1137
1138 /* prevent frames from going back out of the port they came in on */
1139 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001140
Vivien Didelotede80982015-10-11 18:08:35 -04001141 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
1142 if (reg < 0)
1143 return reg;
1144
1145 reg &= ~mask;
1146 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001147
Andrew Lunncca8b132015-04-02 04:06:39 +02001148 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001149}
1150
Vivien Didelot43c44a92016-04-06 11:55:03 -04001151void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001152{
1153 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1154 int stp_state;
1155
1156 switch (state) {
1157 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001158 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001159 break;
1160 case BR_STATE_BLOCKING:
1161 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001162 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001163 break;
1164 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001165 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001166 break;
1167 case BR_STATE_FORWARDING:
1168 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001169 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001170 break;
1171 }
1172
Vivien Didelot43c44a92016-04-06 11:55:03 -04001173 /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled,
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001174 * so we can not update the port state directly but need to schedule it.
1175 */
Vivien Didelotd715fa62016-02-12 12:09:38 -05001176 ps->ports[port].state = stp_state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001177 set_bit(port, ps->port_state_update_mask);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001178 schedule_work(&ps->bridge_work);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001179}
1180
Vivien Didelot5da96032016-03-07 18:24:39 -05001181static int _mv88e6xxx_port_pvid(struct dsa_switch *ds, int port, u16 *new,
1182 u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001183{
Vivien Didelot5da96032016-03-07 18:24:39 -05001184 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001185 int ret;
1186
1187 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
1188 if (ret < 0)
1189 return ret;
1190
Vivien Didelot5da96032016-03-07 18:24:39 -05001191 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1192
1193 if (new) {
1194 ret &= ~PORT_DEFAULT_VLAN_MASK;
1195 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1196
1197 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
1198 PORT_DEFAULT_VLAN, ret);
1199 if (ret < 0)
1200 return ret;
1201
1202 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1203 pvid);
1204 }
1205
1206 if (old)
1207 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001208
1209 return 0;
1210}
1211
Vivien Didelot5da96032016-03-07 18:24:39 -05001212static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
1213{
1214 return _mv88e6xxx_port_pvid(ds, port, NULL, pvid);
1215}
1216
Vivien Didelot76e398a2015-11-01 12:33:55 -05001217static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001218{
Vivien Didelot5da96032016-03-07 18:24:39 -05001219 return _mv88e6xxx_port_pvid(ds, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001220}
1221
Vivien Didelot6b17e862015-08-13 12:52:18 -04001222static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
1223{
1224 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
1225 GLOBAL_VTU_OP_BUSY);
1226}
1227
1228static int _mv88e6xxx_vtu_cmd(struct dsa_switch *ds, u16 op)
1229{
1230 int ret;
1231
1232 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_OP, op);
1233 if (ret < 0)
1234 return ret;
1235
1236 return _mv88e6xxx_vtu_wait(ds);
1237}
1238
1239static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds)
1240{
1241 int ret;
1242
1243 ret = _mv88e6xxx_vtu_wait(ds);
1244 if (ret < 0)
1245 return ret;
1246
1247 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL);
1248}
1249
Vivien Didelotb8fee952015-08-13 12:52:19 -04001250static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds,
1251 struct mv88e6xxx_vtu_stu_entry *entry,
1252 unsigned int nibble_offset)
1253{
1254 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1255 u16 regs[3];
1256 int i;
1257 int ret;
1258
1259 for (i = 0; i < 3; ++i) {
1260 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1261 GLOBAL_VTU_DATA_0_3 + i);
1262 if (ret < 0)
1263 return ret;
1264
1265 regs[i] = ret;
1266 }
1267
Vivien Didelot009a2b92016-04-17 13:24:01 -04001268 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001269 unsigned int shift = (i % 4) * 4 + nibble_offset;
1270 u16 reg = regs[i / 4];
1271
1272 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1273 }
1274
1275 return 0;
1276}
1277
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001278static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds,
1279 struct mv88e6xxx_vtu_stu_entry *entry,
1280 unsigned int nibble_offset)
1281{
1282 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1283 u16 regs[3] = { 0 };
1284 int i;
1285 int ret;
1286
Vivien Didelot009a2b92016-04-17 13:24:01 -04001287 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001288 unsigned int shift = (i % 4) * 4 + nibble_offset;
1289 u8 data = entry->data[i];
1290
1291 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1292 }
1293
1294 for (i = 0; i < 3; ++i) {
1295 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL,
1296 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1297 if (ret < 0)
1298 return ret;
1299 }
1300
1301 return 0;
1302}
1303
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001304static int _mv88e6xxx_vtu_vid_write(struct dsa_switch *ds, u16 vid)
1305{
1306 return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
1307 vid & GLOBAL_VTU_VID_MASK);
1308}
1309
1310static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001311 struct mv88e6xxx_vtu_stu_entry *entry)
1312{
1313 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1314 int ret;
1315
1316 ret = _mv88e6xxx_vtu_wait(ds);
1317 if (ret < 0)
1318 return ret;
1319
Vivien Didelotb8fee952015-08-13 12:52:19 -04001320 ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT);
1321 if (ret < 0)
1322 return ret;
1323
1324 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1325 if (ret < 0)
1326 return ret;
1327
1328 next.vid = ret & GLOBAL_VTU_VID_MASK;
1329 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1330
1331 if (next.valid) {
1332 ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 0);
1333 if (ret < 0)
1334 return ret;
1335
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001336 if (mv88e6xxx_has_fid_reg(ds)) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001337 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1338 GLOBAL_VTU_FID);
1339 if (ret < 0)
1340 return ret;
1341
1342 next.fid = ret & GLOBAL_VTU_FID_MASK;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001343 } else if (mv88e6xxx_num_databases(ds) == 256) {
1344 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1345 * VTU DBNum[3:0] are located in VTU Operation 3:0
1346 */
1347 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1348 GLOBAL_VTU_OP);
1349 if (ret < 0)
1350 return ret;
1351
1352 next.fid = (ret & 0xf00) >> 4;
1353 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001354 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001355
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001356 if (mv88e6xxx_has_stu(ds)) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001357 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1358 GLOBAL_VTU_SID);
1359 if (ret < 0)
1360 return ret;
1361
1362 next.sid = ret & GLOBAL_VTU_SID_MASK;
1363 }
1364 }
1365
1366 *entry = next;
1367 return 0;
1368}
1369
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001370int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1371 struct switchdev_obj_port_vlan *vlan,
1372 int (*cb)(struct switchdev_obj *obj))
1373{
1374 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1375 struct mv88e6xxx_vtu_stu_entry next;
1376 u16 pvid;
1377 int err;
1378
1379 mutex_lock(&ps->smi_mutex);
1380
1381 err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
1382 if (err)
1383 goto unlock;
1384
1385 err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK);
1386 if (err)
1387 goto unlock;
1388
1389 do {
1390 err = _mv88e6xxx_vtu_getnext(ds, &next);
1391 if (err)
1392 break;
1393
1394 if (!next.valid)
1395 break;
1396
1397 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1398 continue;
1399
1400 /* reinit and dump this VLAN obj */
1401 vlan->vid_begin = vlan->vid_end = next.vid;
1402 vlan->flags = 0;
1403
1404 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1405 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1406
1407 if (next.vid == pvid)
1408 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1409
1410 err = cb(&vlan->obj);
1411 if (err)
1412 break;
1413 } while (next.vid < GLOBAL_VTU_VID_MASK);
1414
1415unlock:
1416 mutex_unlock(&ps->smi_mutex);
1417
1418 return err;
1419}
1420
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001421static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
1422 struct mv88e6xxx_vtu_stu_entry *entry)
1423{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001424 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001425 u16 reg = 0;
1426 int ret;
1427
1428 ret = _mv88e6xxx_vtu_wait(ds);
1429 if (ret < 0)
1430 return ret;
1431
1432 if (!entry->valid)
1433 goto loadpurge;
1434
1435 /* Write port member tags */
1436 ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 0);
1437 if (ret < 0)
1438 return ret;
1439
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001440 if (mv88e6xxx_has_stu(ds)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001441 reg = entry->sid & GLOBAL_VTU_SID_MASK;
1442 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1443 if (ret < 0)
1444 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001445 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001446
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001447 if (mv88e6xxx_has_fid_reg(ds)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001448 reg = entry->fid & GLOBAL_VTU_FID_MASK;
1449 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg);
1450 if (ret < 0)
1451 return ret;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001452 } else if (mv88e6xxx_num_databases(ds) == 256) {
1453 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1454 * VTU DBNum[3:0] are located in VTU Operation 3:0
1455 */
1456 op |= (entry->fid & 0xf0) << 8;
1457 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001458 }
1459
1460 reg = GLOBAL_VTU_VID_VALID;
1461loadpurge:
1462 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
1463 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1464 if (ret < 0)
1465 return ret;
1466
Vivien Didelot11ea8092016-03-31 16:53:44 -04001467 return _mv88e6xxx_vtu_cmd(ds, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001468}
1469
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001470static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
1471 struct mv88e6xxx_vtu_stu_entry *entry)
1472{
1473 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1474 int ret;
1475
1476 ret = _mv88e6xxx_vtu_wait(ds);
1477 if (ret < 0)
1478 return ret;
1479
1480 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID,
1481 sid & GLOBAL_VTU_SID_MASK);
1482 if (ret < 0)
1483 return ret;
1484
1485 ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT);
1486 if (ret < 0)
1487 return ret;
1488
1489 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID);
1490 if (ret < 0)
1491 return ret;
1492
1493 next.sid = ret & GLOBAL_VTU_SID_MASK;
1494
1495 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1496 if (ret < 0)
1497 return ret;
1498
1499 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1500
1501 if (next.valid) {
1502 ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2);
1503 if (ret < 0)
1504 return ret;
1505 }
1506
1507 *entry = next;
1508 return 0;
1509}
1510
1511static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
1512 struct mv88e6xxx_vtu_stu_entry *entry)
1513{
1514 u16 reg = 0;
1515 int ret;
1516
1517 ret = _mv88e6xxx_vtu_wait(ds);
1518 if (ret < 0)
1519 return ret;
1520
1521 if (!entry->valid)
1522 goto loadpurge;
1523
1524 /* Write port states */
1525 ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2);
1526 if (ret < 0)
1527 return ret;
1528
1529 reg = GLOBAL_VTU_VID_VALID;
1530loadpurge:
1531 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1532 if (ret < 0)
1533 return ret;
1534
1535 reg = entry->sid & GLOBAL_VTU_SID_MASK;
1536 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1537 if (ret < 0)
1538 return ret;
1539
1540 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE);
1541}
1542
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001543static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new,
1544 u16 *old)
1545{
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001546 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001547 u16 fid;
1548 int ret;
1549
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001550 if (mv88e6xxx_num_databases(ds) == 4096)
1551 upper_mask = 0xff;
Vivien Didelot11ea8092016-03-31 16:53:44 -04001552 else if (mv88e6xxx_num_databases(ds) == 256)
1553 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001554 else
1555 return -EOPNOTSUPP;
1556
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001557 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
1558 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
1559 if (ret < 0)
1560 return ret;
1561
1562 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1563
1564 if (new) {
1565 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1566 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1567
1568 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN,
1569 ret);
1570 if (ret < 0)
1571 return ret;
1572 }
1573
1574 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
1575 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_1);
1576 if (ret < 0)
1577 return ret;
1578
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001579 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001580
1581 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001582 ret &= ~upper_mask;
1583 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001584
1585 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1,
1586 ret);
1587 if (ret < 0)
1588 return ret;
1589
1590 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1591 }
1592
1593 if (old)
1594 *old = fid;
1595
1596 return 0;
1597}
1598
1599static int _mv88e6xxx_port_fid_get(struct dsa_switch *ds, int port, u16 *fid)
1600{
1601 return _mv88e6xxx_port_fid(ds, port, NULL, fid);
1602}
1603
1604static int _mv88e6xxx_port_fid_set(struct dsa_switch *ds, int port, u16 fid)
1605{
1606 return _mv88e6xxx_port_fid(ds, port, &fid, NULL);
1607}
1608
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001609static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid)
1610{
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001611 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001612 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1613 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001614 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001615
1616 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1617
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001618 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001619 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001620 err = _mv88e6xxx_port_fid_get(ds, i, fid);
1621 if (err)
1622 return err;
1623
1624 set_bit(*fid, fid_bitmap);
1625 }
1626
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001627 /* Set every FID bit used by the VLAN entries */
1628 err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK);
1629 if (err)
1630 return err;
1631
1632 do {
1633 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
1634 if (err)
1635 return err;
1636
1637 if (!vlan.valid)
1638 break;
1639
1640 set_bit(vlan.fid, fid_bitmap);
1641 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1642
1643 /* The reset value 0x000 is used to indicate that multiple address
1644 * databases are not needed. Return the next positive available.
1645 */
1646 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001647 if (unlikely(*fid >= mv88e6xxx_num_databases(ds)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001648 return -ENOSPC;
1649
1650 /* Clear the database */
1651 return _mv88e6xxx_atu_flush(ds, *fid, true);
1652}
1653
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001654static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid,
1655 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001656{
1657 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1658 struct mv88e6xxx_vtu_stu_entry vlan = {
1659 .valid = true,
1660 .vid = vid,
1661 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001662 int i, err;
1663
1664 err = _mv88e6xxx_fid_new(ds, &vlan.fid);
1665 if (err)
1666 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001667
Vivien Didelot3d131f02015-11-03 10:52:52 -05001668 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001669 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001670 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1671 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1672 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001673
1674 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
1675 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
1676 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001677
1678 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1679 * implemented, only one STU entry is needed to cover all VTU
1680 * entries. Thus, validate the SID 0.
1681 */
1682 vlan.sid = 0;
1683 err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp);
1684 if (err)
1685 return err;
1686
1687 if (vstp.sid != vlan.sid || !vstp.valid) {
1688 memset(&vstp, 0, sizeof(vstp));
1689 vstp.valid = true;
1690 vstp.sid = vlan.sid;
1691
1692 err = _mv88e6xxx_stu_loadpurge(ds, &vstp);
1693 if (err)
1694 return err;
1695 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001696 }
1697
1698 *entry = vlan;
1699 return 0;
1700}
1701
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001702static int _mv88e6xxx_vtu_get(struct dsa_switch *ds, u16 vid,
1703 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1704{
1705 int err;
1706
1707 if (!vid)
1708 return -EINVAL;
1709
1710 err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
1711 if (err)
1712 return err;
1713
1714 err = _mv88e6xxx_vtu_getnext(ds, entry);
1715 if (err)
1716 return err;
1717
1718 if (entry->vid != vid || !entry->valid) {
1719 if (!creat)
1720 return -EOPNOTSUPP;
1721 /* -ENOENT would've been more appropriate, but switchdev expects
1722 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1723 */
1724
1725 err = _mv88e6xxx_vtu_new(ds, vid, entry);
1726 }
1727
1728 return err;
1729}
1730
Vivien Didelotda9c3592016-02-12 12:09:40 -05001731static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1732 u16 vid_begin, u16 vid_end)
1733{
1734 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1735 struct mv88e6xxx_vtu_stu_entry vlan;
1736 int i, err;
1737
1738 if (!vid_begin)
1739 return -EOPNOTSUPP;
1740
1741 mutex_lock(&ps->smi_mutex);
1742
1743 err = _mv88e6xxx_vtu_vid_write(ds, vid_begin - 1);
1744 if (err)
1745 goto unlock;
1746
1747 do {
1748 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
1749 if (err)
1750 goto unlock;
1751
1752 if (!vlan.valid)
1753 break;
1754
1755 if (vlan.vid > vid_end)
1756 break;
1757
Vivien Didelot009a2b92016-04-17 13:24:01 -04001758 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001759 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1760 continue;
1761
1762 if (vlan.data[i] ==
1763 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1764 continue;
1765
1766 if (ps->ports[i].bridge_dev ==
1767 ps->ports[port].bridge_dev)
1768 break; /* same bridge, check next VLAN */
1769
1770 netdev_warn(ds->ports[port],
1771 "hardware VLAN %d already used by %s\n",
1772 vlan.vid,
1773 netdev_name(ps->ports[i].bridge_dev));
1774 err = -EOPNOTSUPP;
1775 goto unlock;
1776 }
1777 } while (vlan.vid < vid_end);
1778
1779unlock:
1780 mutex_unlock(&ps->smi_mutex);
1781
1782 return err;
1783}
1784
Vivien Didelot214cdb92016-02-26 13:16:08 -05001785static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1786 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1787 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1788 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1789 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1790};
1791
1792int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1793 bool vlan_filtering)
1794{
1795 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1796 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1797 PORT_CONTROL_2_8021Q_DISABLED;
1798 int ret;
1799
1800 mutex_lock(&ps->smi_mutex);
1801
1802 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_2);
1803 if (ret < 0)
1804 goto unlock;
1805
1806 old = ret & PORT_CONTROL_2_8021Q_MASK;
1807
Vivien Didelot5220ef12016-03-07 18:24:52 -05001808 if (new != old) {
1809 ret &= ~PORT_CONTROL_2_8021Q_MASK;
1810 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001811
Vivien Didelot5220ef12016-03-07 18:24:52 -05001812 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_2,
1813 ret);
1814 if (ret < 0)
1815 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001816
Vivien Didelot5220ef12016-03-07 18:24:52 -05001817 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
1818 mv88e6xxx_port_8021q_mode_names[new],
1819 mv88e6xxx_port_8021q_mode_names[old]);
1820 }
1821
1822 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001823unlock:
1824 mutex_unlock(&ps->smi_mutex);
1825
1826 return ret;
1827}
1828
Vivien Didelot76e398a2015-11-01 12:33:55 -05001829int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1830 const struct switchdev_obj_port_vlan *vlan,
1831 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001832{
Vivien Didelotda9c3592016-02-12 12:09:40 -05001833 int err;
1834
Vivien Didelotda9c3592016-02-12 12:09:40 -05001835 /* If the requested port doesn't belong to the same bridge as the VLAN
1836 * members, do not support it (yet) and fallback to software VLAN.
1837 */
1838 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1839 vlan->vid_end);
1840 if (err)
1841 return err;
1842
Vivien Didelot76e398a2015-11-01 12:33:55 -05001843 /* We don't need any dynamic resource from the kernel (yet),
1844 * so skip the prepare phase.
1845 */
1846 return 0;
1847}
1848
1849static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
1850 bool untagged)
1851{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001852 struct mv88e6xxx_vtu_stu_entry vlan;
1853 int err;
1854
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001855 err = _mv88e6xxx_vtu_get(ds, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001856 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001857 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001858
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001859 vlan.data[port] = untagged ?
1860 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1861 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1862
Vivien Didelot76e398a2015-11-01 12:33:55 -05001863 return _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1864}
1865
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001866void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1867 const struct switchdev_obj_port_vlan *vlan,
1868 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001869{
1870 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1871 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1872 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1873 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001874
1875 mutex_lock(&ps->smi_mutex);
1876
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001877 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
1878 if (_mv88e6xxx_port_vlan_add(ds, port, vid, untagged))
1879 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
1880 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05001881
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001882 if (pvid && _mv88e6xxx_port_pvid_set(ds, port, vlan->vid_end))
1883 netdev_err(ds->ports[port], "failed to set PVID %d\n",
1884 vlan->vid_end);
1885
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001886 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001887}
1888
Vivien Didelot76e398a2015-11-01 12:33:55 -05001889static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001890{
1891 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1892 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001893 int i, err;
1894
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001895 err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001896 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001897 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001898
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001899 /* Tell switchdev if this VLAN is handled in software */
1900 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05001901 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001902
1903 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1904
1905 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001906 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001907 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05001908 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001909 continue;
1910
1911 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001912 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001913 break;
1914 }
1915 }
1916
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001917 err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1918 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001919 return err;
1920
1921 return _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
1922}
1923
1924int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1925 const struct switchdev_obj_port_vlan *vlan)
1926{
1927 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1928 u16 pvid, vid;
1929 int err = 0;
1930
1931 mutex_lock(&ps->smi_mutex);
1932
1933 err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
1934 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001935 goto unlock;
1936
Vivien Didelot76e398a2015-11-01 12:33:55 -05001937 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
1938 err = _mv88e6xxx_port_vlan_del(ds, port, vid);
1939 if (err)
1940 goto unlock;
1941
1942 if (vid == pvid) {
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05001943 err = _mv88e6xxx_port_pvid_set(ds, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001944 if (err)
1945 goto unlock;
1946 }
1947 }
1948
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001949unlock:
1950 mutex_unlock(&ps->smi_mutex);
1951
1952 return err;
1953}
1954
Vivien Didelotc5723ac2015-08-10 09:09:48 -04001955static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
1956 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001957{
1958 int i, ret;
1959
1960 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001961 ret = _mv88e6xxx_reg_write(
1962 ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
1963 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001964 if (ret < 0)
1965 return ret;
1966 }
1967
1968 return 0;
1969}
1970
Vivien Didelotc5723ac2015-08-10 09:09:48 -04001971static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001972{
1973 int i, ret;
1974
1975 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001976 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1977 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001978 if (ret < 0)
1979 return ret;
1980 addr[i * 2] = ret >> 8;
1981 addr[i * 2 + 1] = ret & 0xff;
1982 }
1983
1984 return 0;
1985}
1986
Vivien Didelotfd231c82015-08-10 09:09:50 -04001987static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
1988 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001989{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001990 int ret;
1991
1992 ret = _mv88e6xxx_atu_wait(ds);
1993 if (ret < 0)
1994 return ret;
1995
Vivien Didelotfd231c82015-08-10 09:09:50 -04001996 ret = _mv88e6xxx_atu_mac_write(ds, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001997 if (ret < 0)
1998 return ret;
1999
Vivien Didelot37705b72015-09-04 14:34:11 -04002000 ret = _mv88e6xxx_atu_data_write(ds, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002001 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002002 return ret;
2003
Vivien Didelotb426e5f2016-03-31 16:53:42 -04002004 return _mv88e6xxx_atu_cmd(ds, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002005}
David S. Millercdf09692015-08-11 12:00:37 -07002006
Vivien Didelotfd231c82015-08-10 09:09:50 -04002007static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
2008 const unsigned char *addr, u16 vid,
2009 u8 state)
2010{
2011 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002012 struct mv88e6xxx_vtu_stu_entry vlan;
2013 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002014
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002015 /* Null VLAN ID corresponds to the port private database */
2016 if (vid == 0)
2017 err = _mv88e6xxx_port_fid_get(ds, port, &vlan.fid);
2018 else
2019 err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002020 if (err)
2021 return err;
2022
2023 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002024 entry.state = state;
2025 ether_addr_copy(entry.mac, addr);
2026 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2027 entry.trunk = false;
2028 entry.portv_trunkid = BIT(port);
2029 }
2030
2031 return _mv88e6xxx_atu_load(ds, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002032}
2033
Vivien Didelot146a3202015-10-08 11:35:12 -04002034int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2035 const struct switchdev_obj_port_fdb *fdb,
2036 struct switchdev_trans *trans)
2037{
2038 /* We don't need any dynamic resource from the kernel (yet),
2039 * so skip the prepare phase.
2040 */
2041 return 0;
2042}
2043
Vivien Didelot8497aa62016-04-06 11:55:04 -04002044void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2045 const struct switchdev_obj_port_fdb *fdb,
2046 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002047{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002048 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002049 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2050 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2051 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002052
David S. Millercdf09692015-08-11 12:00:37 -07002053 mutex_lock(&ps->smi_mutex);
Vivien Didelot8497aa62016-04-06 11:55:04 -04002054 if (_mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state))
2055 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002056 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002057}
2058
2059int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
Vivien Didelot8057b3e2015-10-08 11:35:14 -04002060 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002061{
2062 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2063 int ret;
2064
2065 mutex_lock(&ps->smi_mutex);
Vivien Didelot8057b3e2015-10-08 11:35:14 -04002066 ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002067 GLOBAL_ATU_DATA_STATE_UNUSED);
2068 mutex_unlock(&ps->smi_mutex);
2069
2070 return ret;
2071}
2072
Vivien Didelot1d194042015-08-10 09:09:51 -04002073static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002074 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002075{
Vivien Didelot1d194042015-08-10 09:09:51 -04002076 struct mv88e6xxx_atu_entry next = { 0 };
2077 int ret;
2078
2079 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002080
2081 ret = _mv88e6xxx_atu_wait(ds);
2082 if (ret < 0)
2083 return ret;
2084
Vivien Didelotb426e5f2016-03-31 16:53:42 -04002085 ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002086 if (ret < 0)
2087 return ret;
2088
Vivien Didelot1d194042015-08-10 09:09:51 -04002089 ret = _mv88e6xxx_atu_mac_read(ds, next.mac);
2090 if (ret < 0)
2091 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002092
Vivien Didelot1d194042015-08-10 09:09:51 -04002093 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
2094 if (ret < 0)
2095 return ret;
2096
2097 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2098 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2099 unsigned int mask, shift;
2100
2101 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2102 next.trunk = true;
2103 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2104 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2105 } else {
2106 next.trunk = false;
2107 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2108 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2109 }
2110
2111 next.portv_trunkid = (ret & mask) >> shift;
2112 }
2113
2114 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002115 return 0;
2116}
2117
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002118static int _mv88e6xxx_port_fdb_dump_one(struct dsa_switch *ds, u16 fid, u16 vid,
2119 int port,
2120 struct switchdev_obj_port_fdb *fdb,
2121 int (*cb)(struct switchdev_obj *obj))
2122{
2123 struct mv88e6xxx_atu_entry addr = {
2124 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2125 };
2126 int err;
2127
2128 err = _mv88e6xxx_atu_mac_write(ds, addr.mac);
2129 if (err)
2130 return err;
2131
2132 do {
2133 err = _mv88e6xxx_atu_getnext(ds, fid, &addr);
2134 if (err)
2135 break;
2136
2137 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2138 break;
2139
2140 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2141 bool is_static = addr.state ==
2142 (is_multicast_ether_addr(addr.mac) ?
2143 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2144 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2145
2146 fdb->vid = vid;
2147 ether_addr_copy(fdb->addr, addr.mac);
2148 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2149
2150 err = cb(&fdb->obj);
2151 if (err)
2152 break;
2153 }
2154 } while (!is_broadcast_ether_addr(addr.mac));
2155
2156 return err;
2157}
2158
Vivien Didelotf33475b2015-10-22 09:34:41 -04002159int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2160 struct switchdev_obj_port_fdb *fdb,
2161 int (*cb)(struct switchdev_obj *obj))
2162{
2163 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2164 struct mv88e6xxx_vtu_stu_entry vlan = {
2165 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2166 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002167 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002168 int err;
2169
2170 mutex_lock(&ps->smi_mutex);
2171
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002172 /* Dump port's default Filtering Information Database (VLAN ID 0) */
2173 err = _mv88e6xxx_port_fid_get(ds, port, &fid);
2174 if (err)
2175 goto unlock;
2176
2177 err = _mv88e6xxx_port_fdb_dump_one(ds, fid, 0, port, fdb, cb);
2178 if (err)
2179 goto unlock;
2180
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002181 /* Dump VLANs' Filtering Information Databases */
Vivien Didelotf33475b2015-10-22 09:34:41 -04002182 err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid);
2183 if (err)
2184 goto unlock;
2185
2186 do {
Vivien Didelotf33475b2015-10-22 09:34:41 -04002187 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
2188 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002189 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002190
2191 if (!vlan.valid)
2192 break;
2193
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002194 err = _mv88e6xxx_port_fdb_dump_one(ds, vlan.fid, vlan.vid, port,
2195 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002196 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002197 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002198 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2199
2200unlock:
2201 mutex_unlock(&ps->smi_mutex);
2202
2203 return err;
2204}
2205
Vivien Didelota6692752016-02-12 12:09:39 -05002206int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2207 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002208{
Vivien Didelota6692752016-02-12 12:09:39 -05002209 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002210 u16 fid;
2211 int i, err;
2212
2213 mutex_lock(&ps->smi_mutex);
2214
2215 /* Get or create the bridge FID and assign it to the port */
Vivien Didelot009a2b92016-04-17 13:24:01 -04002216 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot466dfa02016-02-26 13:16:05 -05002217 if (ps->ports[i].bridge_dev == bridge)
2218 break;
2219
Vivien Didelot009a2b92016-04-17 13:24:01 -04002220 if (i < ps->info->num_ports)
Vivien Didelot466dfa02016-02-26 13:16:05 -05002221 err = _mv88e6xxx_port_fid_get(ds, i, &fid);
2222 else
2223 err = _mv88e6xxx_fid_new(ds, &fid);
2224 if (err)
2225 goto unlock;
2226
2227 err = _mv88e6xxx_port_fid_set(ds, port, fid);
2228 if (err)
2229 goto unlock;
Vivien Didelota6692752016-02-12 12:09:39 -05002230
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002231 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002232 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002233
Vivien Didelot009a2b92016-04-17 13:24:01 -04002234 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002235 if (ps->ports[i].bridge_dev == bridge) {
2236 err = _mv88e6xxx_port_based_vlan_map(ds, i);
2237 if (err)
2238 break;
2239 }
2240 }
2241
Vivien Didelot466dfa02016-02-26 13:16:05 -05002242unlock:
2243 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002244
Vivien Didelot466dfa02016-02-26 13:16:05 -05002245 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002246}
2247
Vivien Didelot16bfa702016-03-13 16:21:33 -04002248void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002249{
Vivien Didelota6692752016-02-12 12:09:39 -05002250 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002251 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002252 u16 fid;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002253 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002254
2255 mutex_lock(&ps->smi_mutex);
2256
2257 /* Give the port a fresh Filtering Information Database */
Vivien Didelot16bfa702016-03-13 16:21:33 -04002258 if (_mv88e6xxx_fid_new(ds, &fid) ||
2259 _mv88e6xxx_port_fid_set(ds, port, fid))
2260 netdev_warn(ds->ports[port], "failed to assign a new FID\n");
Vivien Didelota6692752016-02-12 12:09:39 -05002261
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002262 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002263 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002264
Vivien Didelot009a2b92016-04-17 13:24:01 -04002265 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002266 if (i == port || ps->ports[i].bridge_dev == bridge)
2267 if (_mv88e6xxx_port_based_vlan_map(ds, i))
2268 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002269
Vivien Didelot466dfa02016-02-26 13:16:05 -05002270 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002271}
2272
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002273static void mv88e6xxx_bridge_work(struct work_struct *work)
2274{
2275 struct mv88e6xxx_priv_state *ps;
2276 struct dsa_switch *ds;
2277 int port;
2278
2279 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002280 ds = ps->ds;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002281
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002282 mutex_lock(&ps->smi_mutex);
2283
Vivien Didelot009a2b92016-04-17 13:24:01 -04002284 for (port = 0; port < ps->info->num_ports; ++port)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002285 if (test_and_clear_bit(port, ps->port_state_update_mask) &&
2286 _mv88e6xxx_port_state(ds, port, ps->ports[port].state))
2287 netdev_warn(ds->ports[port], "failed to update state to %s\n",
2288 mv88e6xxx_port_state_names[ps->ports[port].state]);
2289
2290 mutex_unlock(&ps->smi_mutex);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002291}
2292
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002293static int _mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
2294 int reg, int val)
2295{
2296 int ret;
2297
2298 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
2299 if (ret < 0)
2300 goto restore_page_0;
2301
2302 ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
2303restore_page_0:
2304 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
2305
2306 return ret;
2307}
2308
2309static int _mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page,
2310 int reg)
2311{
2312 int ret;
2313
2314 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
2315 if (ret < 0)
2316 goto restore_page_0;
2317
2318 ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
2319restore_page_0:
2320 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
2321
2322 return ret;
2323}
2324
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002325static int mv88e6xxx_power_on_serdes(struct dsa_switch *ds)
2326{
2327 int ret;
2328
2329 ret = _mv88e6xxx_phy_page_read(ds, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
2330 MII_BMCR);
2331 if (ret < 0)
2332 return ret;
2333
2334 if (ret & BMCR_PDOWN) {
2335 ret &= ~BMCR_PDOWN;
2336 ret = _mv88e6xxx_phy_page_write(ds, REG_FIBER_SERDES,
2337 PAGE_FIBER_SERDES, MII_BMCR,
2338 ret);
2339 }
2340
2341 return ret;
2342}
2343
Andrew Lunndbde9e62015-05-06 01:09:48 +02002344static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002345{
2346 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002347 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002348 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002349
2350 mutex_lock(&ps->smi_mutex);
2351
Andrew Lunn54d792f2015-05-06 01:09:47 +02002352 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2353 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2354 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002355 mv88e6xxx_6065_family(ds) || mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002356 /* MAC Forcing register: don't force link, speed,
2357 * duplex or flow control state to any particular
2358 * values on physical ports, but force the CPU port
2359 * and all DSA ports to their maximum bandwidth and
2360 * full duplex.
2361 */
2362 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002363 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002364 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002365 reg |= PORT_PCS_CTRL_FORCE_LINK |
2366 PORT_PCS_CTRL_LINK_UP |
2367 PORT_PCS_CTRL_DUPLEX_FULL |
2368 PORT_PCS_CTRL_FORCE_DUPLEX;
2369 if (mv88e6xxx_6065_family(ds))
2370 reg |= PORT_PCS_CTRL_100;
2371 else
2372 reg |= PORT_PCS_CTRL_1000;
2373 } else {
2374 reg |= PORT_PCS_CTRL_UNFORCED;
2375 }
2376
2377 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2378 PORT_PCS_CTRL, reg);
2379 if (ret)
2380 goto abort;
2381 }
2382
2383 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2384 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2385 * tunneling, determine priority by looking at 802.1p and IP
2386 * priority fields (IP prio has precedence), and set STP state
2387 * to Forwarding.
2388 *
2389 * If this is the CPU link, use DSA or EDSA tagging depending
2390 * on which tagging mode was configured.
2391 *
2392 * If this is a link to another switch, use DSA tagging mode.
2393 *
2394 * If this is the upstream port for this switch, enable
2395 * forwarding of unknown unicasts and multicasts.
2396 */
2397 reg = 0;
2398 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2399 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2400 mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002401 mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002402 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2403 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2404 PORT_CONTROL_STATE_FORWARDING;
2405 if (dsa_is_cpu_port(ds, port)) {
2406 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
2407 reg |= PORT_CONTROL_DSA_TAG;
2408 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002409 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2410 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002411 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2412 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2413 else
2414 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002415 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2416 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002417 }
2418
2419 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2420 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2421 mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002422 mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002423 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2424 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2425 }
2426 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002427 if (dsa_is_dsa_port(ds, port)) {
2428 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
2429 reg |= PORT_CONTROL_DSA_TAG;
2430 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2431 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2432 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002433 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002434 }
2435
Andrew Lunn54d792f2015-05-06 01:09:47 +02002436 if (port == dsa_upstream_port(ds))
2437 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2438 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2439 }
2440 if (reg) {
2441 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2442 PORT_CONTROL, reg);
2443 if (ret)
2444 goto abort;
2445 }
2446
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002447 /* If this port is connected to a SerDes, make sure the SerDes is not
2448 * powered down.
2449 */
2450 if (mv88e6xxx_6352_family(ds)) {
2451 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
2452 if (ret < 0)
2453 goto abort;
2454 ret &= PORT_STATUS_CMODE_MASK;
2455 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2456 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2457 (ret == PORT_STATUS_CMODE_SGMII)) {
2458 ret = mv88e6xxx_power_on_serdes(ds);
2459 if (ret < 0)
2460 goto abort;
2461 }
2462 }
2463
Vivien Didelot8efdda42015-08-13 12:52:23 -04002464 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002465 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002466 * untagged frames on this port, do a destination address lookup on all
2467 * received packets as usual, disable ARP mirroring and don't send a
2468 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002469 */
2470 reg = 0;
2471 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2472 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Vivien Didelotf93dd042016-03-31 16:53:45 -04002473 mv88e6xxx_6095_family(ds) || mv88e6xxx_6320_family(ds) ||
2474 mv88e6xxx_6185_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002475 reg = PORT_CONTROL_2_MAP_DA;
2476
2477 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002478 mv88e6xxx_6165_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002479 reg |= PORT_CONTROL_2_JUMBO_10240;
2480
2481 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) {
2482 /* Set the upstream port this port should use */
2483 reg |= dsa_upstream_port(ds);
2484 /* enable forwarding of unknown multicast addresses to
2485 * the upstream port
2486 */
2487 if (port == dsa_upstream_port(ds))
2488 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2489 }
2490
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002491 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002492
Andrew Lunn54d792f2015-05-06 01:09:47 +02002493 if (reg) {
2494 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2495 PORT_CONTROL_2, reg);
2496 if (ret)
2497 goto abort;
2498 }
2499
2500 /* Port Association Vector: when learning source addresses
2501 * of packets, add the address to the address database using
2502 * a port bitmap that has only the bit for this port set and
2503 * the other bits clear.
2504 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002505 reg = 1 << port;
2506 /* Disable learning for DSA and CPU ports */
2507 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
2508 reg = PORT_ASSOC_VECTOR_LOCKED_PORT;
2509
2510 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002511 if (ret)
2512 goto abort;
2513
2514 /* Egress rate control 2: disable egress rate control. */
2515 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_RATE_CONTROL_2,
2516 0x0000);
2517 if (ret)
2518 goto abort;
2519
2520 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002521 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2522 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002523 /* Do not limit the period of time that this port can
2524 * be paused for by the remote end or the period of
2525 * time that this port can pause the remote end.
2526 */
2527 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2528 PORT_PAUSE_CTRL, 0x0000);
2529 if (ret)
2530 goto abort;
2531
2532 /* Port ATU control: disable limiting the number of
2533 * address database entries that this port is allowed
2534 * to use.
2535 */
2536 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2537 PORT_ATU_CONTROL, 0x0000);
2538 /* Priority Override: disable DA, SA and VTU priority
2539 * override.
2540 */
2541 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2542 PORT_PRI_OVERRIDE, 0x0000);
2543 if (ret)
2544 goto abort;
2545
2546 /* Port Ethertype: use the Ethertype DSA Ethertype
2547 * value.
2548 */
2549 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2550 PORT_ETH_TYPE, ETH_P_EDSA);
2551 if (ret)
2552 goto abort;
2553 /* Tag Remap: use an identity 802.1p prio -> switch
2554 * prio mapping.
2555 */
2556 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2557 PORT_TAG_REGMAP_0123, 0x3210);
2558 if (ret)
2559 goto abort;
2560
2561 /* Tag Remap 2: use an identity 802.1p prio -> switch
2562 * prio mapping.
2563 */
2564 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2565 PORT_TAG_REGMAP_4567, 0x7654);
2566 if (ret)
2567 goto abort;
2568 }
2569
2570 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2571 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002572 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
2573 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002574 /* Rate Control: disable ingress rate limiting. */
2575 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2576 PORT_RATE_CONTROL, 0x0001);
2577 if (ret)
2578 goto abort;
2579 }
2580
Guenter Roeck366f0a02015-03-26 18:36:30 -07002581 /* Port Control 1: disable trunking, disable sending
2582 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002583 */
Vivien Didelot614f03f2015-04-20 17:19:23 -04002584 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002585 if (ret)
2586 goto abort;
2587
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002588 /* Port based VLAN map: give each port its own address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002589 * database, and allow bidirectional communication between the
2590 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002591 */
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002592 ret = _mv88e6xxx_port_fid_set(ds, port, port + 1);
2593 if (ret)
2594 goto abort;
2595
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002596 ret = _mv88e6xxx_port_based_vlan_map(ds, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002597 if (ret)
2598 goto abort;
2599
2600 /* Default VLAN ID and priority: don't set a default VLAN
2601 * ID, and set the default packet priority to zero.
2602 */
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002603 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
2604 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002605abort:
2606 mutex_unlock(&ps->smi_mutex);
2607 return ret;
2608}
2609
Andrew Lunndbde9e62015-05-06 01:09:48 +02002610int mv88e6xxx_setup_ports(struct dsa_switch *ds)
2611{
2612 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2613 int ret;
2614 int i;
2615
Vivien Didelot009a2b92016-04-17 13:24:01 -04002616 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunndbde9e62015-05-06 01:09:48 +02002617 ret = mv88e6xxx_setup_port(ds, i);
2618 if (ret < 0)
2619 return ret;
2620 }
2621 return 0;
2622}
2623
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002624int mv88e6xxx_setup_common(struct dsa_switch *ds)
2625{
2626 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2627
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002628 ps->ds = ds;
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002629 mutex_init(&ps->smi_mutex);
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002630
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002631 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
2632
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002633 return 0;
2634}
2635
Andrew Lunn54d792f2015-05-06 01:09:47 +02002636int mv88e6xxx_setup_global(struct dsa_switch *ds)
2637{
2638 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002639 int err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002640 int i;
2641
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002642 mutex_lock(&ps->smi_mutex);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002643 /* Set the default address aging time to 5 minutes, and
2644 * enable address learn messages to be sent to all message
2645 * ports.
2646 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002647 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2648 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2649 if (err)
2650 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002651
2652 /* Configure the IP ToS mapping registers. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002653 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2654 if (err)
2655 goto unlock;
2656 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2657 if (err)
2658 goto unlock;
2659 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2660 if (err)
2661 goto unlock;
2662 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2663 if (err)
2664 goto unlock;
2665 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2666 if (err)
2667 goto unlock;
2668 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2669 if (err)
2670 goto unlock;
2671 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2672 if (err)
2673 goto unlock;
2674 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2675 if (err)
2676 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002677
2678 /* Configure the IEEE 802.1p priority mapping register. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002679 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
2680 if (err)
2681 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002682
2683 /* Send all frames with destination addresses matching
2684 * 01:80:c2:00:00:0x to the CPU port.
2685 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002686 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
2687 if (err)
2688 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002689
2690 /* Ignore removed tag data on doubly tagged packets, disable
2691 * flow control messages, force flow control priority to the
2692 * highest, and send all special multicast frames to the CPU
2693 * port at the highest priority.
2694 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002695 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
2696 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
2697 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
2698 if (err)
2699 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002700
2701 /* Program the DSA routing table. */
2702 for (i = 0; i < 32; i++) {
2703 int nexthop = 0x1f;
2704
2705 if (ds->pd->rtable &&
2706 i != ds->index && i < ds->dst->pd->nr_chips)
2707 nexthop = ds->pd->rtable[i] & 0x1f;
2708
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002709 err = _mv88e6xxx_reg_write(
2710 ds, REG_GLOBAL2,
2711 GLOBAL2_DEVICE_MAPPING,
2712 GLOBAL2_DEVICE_MAPPING_UPDATE |
2713 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
2714 if (err)
2715 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002716 }
2717
2718 /* Clear all trunk masks. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002719 for (i = 0; i < 8; i++) {
2720 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
2721 0x8000 |
2722 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
Vivien Didelot009a2b92016-04-17 13:24:01 -04002723 ((1 << ps->info->num_ports) - 1));
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002724 if (err)
2725 goto unlock;
2726 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002727
2728 /* Clear all trunk mappings. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002729 for (i = 0; i < 16; i++) {
2730 err = _mv88e6xxx_reg_write(
2731 ds, REG_GLOBAL2,
2732 GLOBAL2_TRUNK_MAPPING,
2733 GLOBAL2_TRUNK_MAPPING_UPDATE |
2734 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
2735 if (err)
2736 goto unlock;
2737 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002738
2739 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002740 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2741 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002742 /* Send all frames with destination addresses matching
2743 * 01:80:c2:00:00:2x to the CPU port.
2744 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002745 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2746 GLOBAL2_MGMT_EN_2X, 0xffff);
2747 if (err)
2748 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002749
2750 /* Initialise cross-chip port VLAN table to reset
2751 * defaults.
2752 */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002753 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2754 GLOBAL2_PVT_ADDR, 0x9000);
2755 if (err)
2756 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002757
2758 /* Clear the priority override table. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002759 for (i = 0; i < 16; i++) {
2760 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2761 GLOBAL2_PRIO_OVERRIDE,
2762 0x8000 | (i << 8));
2763 if (err)
2764 goto unlock;
2765 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002766 }
2767
2768 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2769 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002770 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
2771 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002772 /* Disable ingress rate limiting by resetting all
2773 * ingress rate limit registers to their initial
2774 * state.
2775 */
Vivien Didelot009a2b92016-04-17 13:24:01 -04002776 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002777 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL2,
2778 GLOBAL2_INGRESS_OP,
2779 0x9000 | (i << 8));
2780 if (err)
2781 goto unlock;
2782 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002783 }
2784
Andrew Lunndb687a52015-06-20 21:31:29 +02002785 /* Clear the statistics counters for all ports */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002786 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
2787 GLOBAL_STATS_OP_FLUSH_ALL);
2788 if (err)
2789 goto unlock;
Andrew Lunndb687a52015-06-20 21:31:29 +02002790
2791 /* Wait for the flush to complete. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002792 err = _mv88e6xxx_stats_wait(ds);
2793 if (err < 0)
Vivien Didelot6b17e862015-08-13 12:52:18 -04002794 goto unlock;
2795
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002796 /* Clear all ATU entries */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002797 err = _mv88e6xxx_atu_flush(ds, 0, true);
2798 if (err < 0)
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002799 goto unlock;
2800
Vivien Didelot6b17e862015-08-13 12:52:18 -04002801 /* Clear all the VTU and STU entries */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002802 err = _mv88e6xxx_vtu_stu_flush(ds);
Vivien Didelot6b17e862015-08-13 12:52:18 -04002803unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04002804 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02002805
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002806 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002807}
2808
Andrew Lunn143a8302015-04-02 04:06:34 +02002809int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
2810{
2811 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2812 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +01002813 struct gpio_desc *gpiod = ds->pd->reset;
Andrew Lunn143a8302015-04-02 04:06:34 +02002814 unsigned long timeout;
2815 int ret;
2816 int i;
2817
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002818 mutex_lock(&ps->smi_mutex);
2819
Andrew Lunn143a8302015-04-02 04:06:34 +02002820 /* Set all ports to the disabled state. */
Vivien Didelot009a2b92016-04-17 13:24:01 -04002821 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002822 ret = _mv88e6xxx_reg_read(ds, REG_PORT(i), PORT_CONTROL);
2823 if (ret < 0)
2824 goto unlock;
2825
2826 ret = _mv88e6xxx_reg_write(ds, REG_PORT(i), PORT_CONTROL,
2827 ret & 0xfffc);
2828 if (ret)
2829 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02002830 }
2831
2832 /* Wait for transmit queues to drain. */
2833 usleep_range(2000, 4000);
2834
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +01002835 /* If there is a gpio connected to the reset pin, toggle it */
2836 if (gpiod) {
2837 gpiod_set_value_cansleep(gpiod, 1);
2838 usleep_range(10000, 20000);
2839 gpiod_set_value_cansleep(gpiod, 0);
2840 usleep_range(10000, 20000);
2841 }
2842
Andrew Lunn143a8302015-04-02 04:06:34 +02002843 /* Reset the switch. Keep the PPU active if requested. The PPU
2844 * needs to be active to support indirect phy register access
2845 * through global registers 0x18 and 0x19.
2846 */
2847 if (ppu_active)
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002848 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x04, 0xc000);
Andrew Lunn143a8302015-04-02 04:06:34 +02002849 else
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002850 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x04, 0xc400);
2851 if (ret)
2852 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02002853
2854 /* Wait up to one second for reset to complete. */
2855 timeout = jiffies + 1 * HZ;
2856 while (time_before(jiffies, timeout)) {
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002857 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x00);
2858 if (ret < 0)
2859 goto unlock;
2860
Andrew Lunn143a8302015-04-02 04:06:34 +02002861 if ((ret & is_reset) == is_reset)
2862 break;
2863 usleep_range(1000, 2000);
2864 }
2865 if (time_after(jiffies, timeout))
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002866 ret = -ETIMEDOUT;
2867 else
2868 ret = 0;
2869unlock:
2870 mutex_unlock(&ps->smi_mutex);
Andrew Lunn143a8302015-04-02 04:06:34 +02002871
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002872 return ret;
Andrew Lunn143a8302015-04-02 04:06:34 +02002873}
2874
Andrew Lunn491435852015-04-02 04:06:35 +02002875int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
2876{
2877 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2878 int ret;
2879
Andrew Lunn3898c142015-05-06 01:09:53 +02002880 mutex_lock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002881 ret = _mv88e6xxx_phy_page_read(ds, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02002882 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002883
Andrew Lunn491435852015-04-02 04:06:35 +02002884 return ret;
2885}
2886
2887int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
2888 int reg, int val)
2889{
2890 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2891 int ret;
2892
Andrew Lunn3898c142015-05-06 01:09:53 +02002893 mutex_lock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002894 ret = _mv88e6xxx_phy_page_write(ds, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002895 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002896
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002897 return ret;
2898}
2899
2900static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port)
2901{
2902 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2903
Vivien Didelot009a2b92016-04-17 13:24:01 -04002904 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002905 return port;
2906 return -EINVAL;
2907}
2908
2909int
2910mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
2911{
2912 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2913 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2914 int ret;
2915
2916 if (addr < 0)
2917 return addr;
2918
Andrew Lunn3898c142015-05-06 01:09:53 +02002919 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002920 ret = _mv88e6xxx_phy_read(ds, addr, regnum);
Andrew Lunn3898c142015-05-06 01:09:53 +02002921 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002922 return ret;
2923}
2924
2925int
2926mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
2927{
2928 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2929 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2930 int ret;
2931
2932 if (addr < 0)
2933 return addr;
2934
Andrew Lunn3898c142015-05-06 01:09:53 +02002935 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002936 ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002937 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002938 return ret;
2939}
2940
2941int
2942mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
2943{
2944 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2945 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2946 int ret;
2947
2948 if (addr < 0)
2949 return addr;
2950
Andrew Lunn3898c142015-05-06 01:09:53 +02002951 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002952 ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
Andrew Lunn3898c142015-05-06 01:09:53 +02002953 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002954 return ret;
2955}
2956
2957int
2958mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
2959 u16 val)
2960{
2961 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2962 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2963 int ret;
2964
2965 if (addr < 0)
2966 return addr;
2967
Andrew Lunn3898c142015-05-06 01:09:53 +02002968 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002969 ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002970 mutex_unlock(&ps->smi_mutex);
Andrew Lunn491435852015-04-02 04:06:35 +02002971 return ret;
2972}
2973
Guenter Roeckc22995c2015-07-25 09:42:28 -07002974#ifdef CONFIG_NET_DSA_HWMON
2975
2976static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
2977{
2978 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2979 int ret;
2980 int val;
2981
2982 *temp = 0;
2983
2984 mutex_lock(&ps->smi_mutex);
2985
2986 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
2987 if (ret < 0)
2988 goto error;
2989
2990 /* Enable temperature sensor */
2991 ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
2992 if (ret < 0)
2993 goto error;
2994
2995 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
2996 if (ret < 0)
2997 goto error;
2998
2999 /* Wait for temperature to stabilize */
3000 usleep_range(10000, 12000);
3001
3002 val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
3003 if (val < 0) {
3004 ret = val;
3005 goto error;
3006 }
3007
3008 /* Disable temperature sensor */
3009 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
3010 if (ret < 0)
3011 goto error;
3012
3013 *temp = ((val & 0x1f) - 5) * 5;
3014
3015error:
3016 _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
3017 mutex_unlock(&ps->smi_mutex);
3018 return ret;
3019}
3020
3021static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3022{
3023 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3024 int ret;
3025
3026 *temp = 0;
3027
3028 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
3029 if (ret < 0)
3030 return ret;
3031
3032 *temp = (ret & 0xff) - 25;
3033
3034 return 0;
3035}
3036
3037int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
3038{
3039 if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds))
3040 return mv88e63xx_get_temp(ds, temp);
3041
3042 return mv88e61xx_get_temp(ds, temp);
3043}
3044
3045int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
3046{
3047 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3048 int ret;
3049
3050 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
3051 return -EOPNOTSUPP;
3052
3053 *temp = 0;
3054
3055 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3056 if (ret < 0)
3057 return ret;
3058
3059 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3060
3061 return 0;
3062}
3063
3064int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
3065{
3066 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3067 int ret;
3068
3069 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
3070 return -EOPNOTSUPP;
3071
3072 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3073 if (ret < 0)
3074 return ret;
3075 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3076 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3077 (ret & 0xe0ff) | (temp << 8));
3078}
3079
3080int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
3081{
3082 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
3083 int ret;
3084
3085 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
3086 return -EOPNOTSUPP;
3087
3088 *alarm = false;
3089
3090 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3091 if (ret < 0)
3092 return ret;
3093
3094 *alarm = !!(ret & 0x40);
3095
3096 return 0;
3097}
3098#endif /* CONFIG_NET_DSA_HWMON */
3099
Vivien Didelotf6271e62016-04-17 13:23:59 -04003100static const struct mv88e6xxx_info *
3101mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003102 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003103{
Vivien Didelota439c062016-04-17 13:23:58 -04003104 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003105
Vivien Didelotb9b37712015-10-30 19:39:48 -04003106 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003107 if (table[i].prod_num == prod_num)
3108 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003109
Vivien Didelotb9b37712015-10-30 19:39:48 -04003110 return NULL;
3111}
3112
Vivien Didelot0209d142016-04-17 13:23:55 -04003113const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
3114 int sw_addr, void **priv,
Vivien Didelotf6271e62016-04-17 13:23:59 -04003115 const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003116 unsigned int num)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003117{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003118 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003119 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003120 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003121 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003122 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003123
Vivien Didelota439c062016-04-17 13:23:58 -04003124 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003125 if (!bus)
3126 return NULL;
3127
Vivien Didelota439c062016-04-17 13:23:58 -04003128 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3129 if (id < 0)
3130 return NULL;
3131
3132 prod_num = (id & 0xfff0) >> 4;
3133 rev = id & 0x000f;
3134
Vivien Didelotf6271e62016-04-17 13:23:59 -04003135 info = mv88e6xxx_lookup_info(prod_num, table, num);
3136 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003137 return NULL;
3138
Vivien Didelotf6271e62016-04-17 13:23:59 -04003139 name = info->name;
3140
Vivien Didelota439c062016-04-17 13:23:58 -04003141 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3142 if (!ps)
3143 return NULL;
3144
3145 ps->bus = bus;
3146 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003147 ps->info = info;
Vivien Didelota439c062016-04-17 13:23:58 -04003148 ps->id = id & 0xfff0;
3149
3150 *priv = ps;
3151
3152 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3153 prod_num, name, rev);
3154
Andrew Lunna77d43f2016-04-13 02:40:42 +02003155 return name;
3156}
3157
Ben Hutchings98e67302011-11-25 14:36:19 +00003158static int __init mv88e6xxx_init(void)
3159{
3160#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3161 register_switch_driver(&mv88e6131_switch_driver);
3162#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003163#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3164 register_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003165#endif
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07003166#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3167 register_switch_driver(&mv88e6352_switch_driver);
3168#endif
Andrew Lunn42f27252014-09-12 23:58:44 +02003169#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3170 register_switch_driver(&mv88e6171_switch_driver);
3171#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00003172 return 0;
3173}
3174module_init(mv88e6xxx_init);
3175
3176static void __exit mv88e6xxx_cleanup(void)
3177{
Andrew Lunn42f27252014-09-12 23:58:44 +02003178#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3179 unregister_switch_driver(&mv88e6171_switch_driver);
3180#endif
Vivien Didelot4212b542015-05-01 10:43:52 -04003181#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3182 unregister_switch_driver(&mv88e6352_switch_driver);
3183#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003184#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3185 unregister_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003186#endif
3187#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3188 unregister_switch_driver(&mv88e6131_switch_driver);
3189#endif
3190}
3191module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003192
3193MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3194MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3195MODULE_LICENSE("GPL");