blob: d277350069d07676a10b6b9901811f4cd0744bfd [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
Andrew Lunn158bc062016-04-28 21:24:06 -040028static void assert_smi_lock(struct mv88e6xxx_priv_state *ps)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040029{
Vivien Didelot3996a4f2015-10-30 18:56:45 -040030 if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
Andrew Lunn158bc062016-04-28 21:24:06 -040031 dev_err(ps->dev, "SMI lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040032 dump_stack();
33 }
34}
35
Barry Grussling3675c8d2013-01-08 16:05:53 +000036/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000037 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
38 * will be directly accessible on some {device address,register address}
39 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
40 * will only respond to SMI transactions to that specific address, and
41 * an indirect addressing mechanism needs to be used to access its
42 * registers.
43 */
44static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
45{
46 int ret;
47 int i;
48
49 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020050 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000051 if (ret < 0)
52 return ret;
53
Andrew Lunncca8b132015-04-02 04:06:39 +020054 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000055 return 0;
56 }
57
58 return -ETIMEDOUT;
59}
60
Vivien Didelotb9b37712015-10-30 19:39:48 -040061static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
62 int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000063{
64 int ret;
65
66 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020067 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000068
Barry Grussling3675c8d2013-01-08 16:05:53 +000069 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000070 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
71 if (ret < 0)
72 return ret;
73
Barry Grussling3675c8d2013-01-08 16:05:53 +000074 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020075 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
76 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000077 if (ret < 0)
78 return ret;
79
Barry Grussling3675c8d2013-01-08 16:05:53 +000080 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000081 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
82 if (ret < 0)
83 return ret;
84
Barry Grussling3675c8d2013-01-08 16:05:53 +000085 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020086 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000087 if (ret < 0)
88 return ret;
89
90 return ret & 0xffff;
91}
92
Andrew Lunn158bc062016-04-28 21:24:06 -040093static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
94 int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000095{
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000096 int ret;
97
Andrew Lunn158bc062016-04-28 21:24:06 -040098 assert_smi_lock(ps);
Vivien Didelot3996a4f2015-10-30 18:56:45 -040099
Andrew Lunna77d43f2016-04-13 02:40:42 +0200100 ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500101 if (ret < 0)
102 return ret;
103
Andrew Lunn158bc062016-04-28 21:24:06 -0400104 dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500105 addr, reg, ret);
106
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000107 return ret;
108}
109
Andrew Lunn158bc062016-04-28 21:24:06 -0400110int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700111{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700112 int ret;
113
114 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400115 ret = _mv88e6xxx_reg_read(ps, addr, reg);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700116 mutex_unlock(&ps->smi_mutex);
117
118 return ret;
119}
120
Vivien Didelotb9b37712015-10-30 19:39:48 -0400121static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
122 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000123{
124 int ret;
125
126 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200127 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000128
Barry Grussling3675c8d2013-01-08 16:05:53 +0000129 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000130 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
131 if (ret < 0)
132 return ret;
133
Barry Grussling3675c8d2013-01-08 16:05:53 +0000134 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200135 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000136 if (ret < 0)
137 return ret;
138
Barry Grussling3675c8d2013-01-08 16:05:53 +0000139 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200140 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
141 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000142 if (ret < 0)
143 return ret;
144
Barry Grussling3675c8d2013-01-08 16:05:53 +0000145 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000146 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
147 if (ret < 0)
148 return ret;
149
150 return 0;
151}
152
Andrew Lunn158bc062016-04-28 21:24:06 -0400153static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
154 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000155{
Andrew Lunn158bc062016-04-28 21:24:06 -0400156 assert_smi_lock(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000157
Andrew Lunn158bc062016-04-28 21:24:06 -0400158 dev_dbg(ps->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500159 addr, reg, val);
160
Andrew Lunna77d43f2016-04-13 02:40:42 +0200161 return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700162}
163
Andrew Lunn158bc062016-04-28 21:24:06 -0400164int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
165 int reg, u16 val)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700166{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700167 int ret;
168
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000169 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400170 ret = _mv88e6xxx_reg_write(ps, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000171 mutex_unlock(&ps->smi_mutex);
172
173 return ret;
174}
175
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000176int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
177{
Andrew Lunn158bc062016-04-28 21:24:06 -0400178 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200179 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000180
Andrew Lunn158bc062016-04-28 21:24:06 -0400181 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_01,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200182 (addr[0] << 8) | addr[1]);
183 if (err)
184 return err;
185
Andrew Lunn158bc062016-04-28 21:24:06 -0400186 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_23,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200187 (addr[2] << 8) | addr[3]);
188 if (err)
189 return err;
190
Andrew Lunn158bc062016-04-28 21:24:06 -0400191 return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_45,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200192 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000193}
194
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000195int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
196{
Andrew Lunn158bc062016-04-28 21:24:06 -0400197 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000198 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200199 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000200
201 for (i = 0; i < 6; i++) {
202 int j;
203
Barry Grussling3675c8d2013-01-08 16:05:53 +0000204 /* Write the MAC address byte. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400205 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200206 GLOBAL2_SWITCH_MAC_BUSY |
207 (i << 8) | addr[i]);
208 if (ret)
209 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000210
Barry Grussling3675c8d2013-01-08 16:05:53 +0000211 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000212 for (j = 0; j < 16; j++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400213 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200214 GLOBAL2_SWITCH_MAC);
215 if (ret < 0)
216 return ret;
217
Andrew Lunncca8b132015-04-02 04:06:39 +0200218 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000219 break;
220 }
221 if (j == 16)
222 return -ETIMEDOUT;
223 }
224
225 return 0;
226}
227
Andrew Lunn158bc062016-04-28 21:24:06 -0400228static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr,
229 int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000230{
231 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400232 return _mv88e6xxx_reg_read(ps, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000233 return 0xffff;
234}
235
Andrew Lunn158bc062016-04-28 21:24:06 -0400236static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr,
237 int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000238{
239 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400240 return _mv88e6xxx_reg_write(ps, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000241 return 0;
242}
243
Andrew Lunn158bc062016-04-28 21:24:06 -0400244static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000245{
246 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000247 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000248
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400249 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200250 if (ret < 0)
251 return ret;
252
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400253 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
254 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200255 if (ret)
256 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000257
Barry Grussling19b2f972013-01-08 16:05:54 +0000258 timeout = jiffies + 1 * HZ;
259 while (time_before(jiffies, timeout)) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400260 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200261 if (ret < 0)
262 return ret;
263
Barry Grussling19b2f972013-01-08 16:05:54 +0000264 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200265 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
266 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000267 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000268 }
269
270 return -ETIMEDOUT;
271}
272
Andrew Lunn158bc062016-04-28 21:24:06 -0400273static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000274{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200275 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000276 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000277
Andrew Lunn158bc062016-04-28 21:24:06 -0400278 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200279 if (ret < 0)
280 return ret;
281
Andrew Lunn158bc062016-04-28 21:24:06 -0400282 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200283 ret | GLOBAL_CONTROL_PPU_ENABLE);
284 if (err)
285 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000286
Barry Grussling19b2f972013-01-08 16:05:54 +0000287 timeout = jiffies + 1 * HZ;
288 while (time_before(jiffies, timeout)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400289 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200290 if (ret < 0)
291 return ret;
292
Barry Grussling19b2f972013-01-08 16:05:54 +0000293 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200294 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
295 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000296 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000297 }
298
299 return -ETIMEDOUT;
300}
301
302static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
303{
304 struct mv88e6xxx_priv_state *ps;
305
306 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
307 if (mutex_trylock(&ps->ppu_mutex)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400308 if (mv88e6xxx_ppu_enable(ps) == 0)
Barry Grussling85686582013-01-08 16:05:56 +0000309 ps->ppu_disabled = 0;
310 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000311 }
312}
313
314static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
315{
316 struct mv88e6xxx_priv_state *ps = (void *)_ps;
317
318 schedule_work(&ps->ppu_work);
319}
320
Andrew Lunn158bc062016-04-28 21:24:06 -0400321static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000322{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000323 int ret;
324
325 mutex_lock(&ps->ppu_mutex);
326
Barry Grussling3675c8d2013-01-08 16:05:53 +0000327 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000328 * we can access the PHY registers. If it was already
329 * disabled, cancel the timer that is going to re-enable
330 * it.
331 */
332 if (!ps->ppu_disabled) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400333 ret = mv88e6xxx_ppu_disable(ps);
Barry Grussling85686582013-01-08 16:05:56 +0000334 if (ret < 0) {
335 mutex_unlock(&ps->ppu_mutex);
336 return ret;
337 }
338 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000339 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000340 del_timer(&ps->ppu_timer);
341 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000342 }
343
344 return ret;
345}
346
Andrew Lunn158bc062016-04-28 21:24:06 -0400347static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000348{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000349 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000350 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
351 mutex_unlock(&ps->ppu_mutex);
352}
353
Andrew Lunn158bc062016-04-28 21:24:06 -0400354void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000355{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000356 mutex_init(&ps->ppu_mutex);
357 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
358 init_timer(&ps->ppu_timer);
359 ps->ppu_timer.data = (unsigned long)ps;
360 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
361}
362
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400363static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
364 int regnum)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000365{
366 int ret;
367
Andrew Lunn158bc062016-04-28 21:24:06 -0400368 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000369 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400370 ret = _mv88e6xxx_reg_read(ps, addr, regnum);
Andrew Lunn158bc062016-04-28 21:24:06 -0400371 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000372 }
373
374 return ret;
375}
376
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400377static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
378 int regnum, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000379{
380 int ret;
381
Andrew Lunn158bc062016-04-28 21:24:06 -0400382 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000383 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400384 ret = _mv88e6xxx_reg_write(ps, addr, regnum, val);
Andrew Lunn158bc062016-04-28 21:24:06 -0400385 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000386 }
387
388 return ret;
389}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000390
Andrew Lunn158bc062016-04-28 21:24:06 -0400391static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200392{
Vivien Didelot22356472016-04-17 13:24:00 -0400393 return ps->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200394}
395
Andrew Lunn158bc062016-04-28 21:24:06 -0400396static bool mv88e6xxx_6095_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200397{
Vivien Didelot22356472016-04-17 13:24:00 -0400398 return ps->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200399}
400
Andrew Lunn158bc062016-04-28 21:24:06 -0400401static bool mv88e6xxx_6097_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200402{
Vivien Didelot22356472016-04-17 13:24:00 -0400403 return ps->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200404}
405
Andrew Lunn158bc062016-04-28 21:24:06 -0400406static bool mv88e6xxx_6165_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200407{
Vivien Didelot22356472016-04-17 13:24:00 -0400408 return ps->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200409}
410
Andrew Lunn158bc062016-04-28 21:24:06 -0400411static bool mv88e6xxx_6185_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200412{
Vivien Didelot22356472016-04-17 13:24:00 -0400413 return ps->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200414}
415
Andrew Lunn158bc062016-04-28 21:24:06 -0400416static bool mv88e6xxx_6320_family(struct mv88e6xxx_priv_state *ps)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700417{
Vivien Didelot22356472016-04-17 13:24:00 -0400418 return ps->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700419}
420
Andrew Lunn158bc062016-04-28 21:24:06 -0400421static bool mv88e6xxx_6351_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200422{
Vivien Didelot22356472016-04-17 13:24:00 -0400423 return ps->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200424}
425
Andrew Lunn158bc062016-04-28 21:24:06 -0400426static bool mv88e6xxx_6352_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200427{
Vivien Didelot22356472016-04-17 13:24:00 -0400428 return ps->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200429}
430
Andrew Lunn158bc062016-04-28 21:24:06 -0400431static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_priv_state *ps)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400432{
Vivien Didelotcd5a2c82016-04-17 13:24:02 -0400433 return ps->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400434}
435
Andrew Lunn158bc062016-04-28 21:24:06 -0400436static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400437{
438 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Andrew Lunn158bc062016-04-28 21:24:06 -0400439 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
440 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400441 return true;
442
443 return false;
444}
445
Andrew Lunn158bc062016-04-28 21:24:06 -0400446static bool mv88e6xxx_has_stu(struct mv88e6xxx_priv_state *ps)
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -0400447{
448 /* Does the device have STU and dedicated SID registers for VTU ops? */
Andrew Lunn158bc062016-04-28 21:24:06 -0400449 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
450 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps))
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -0400451 return true;
452
453 return false;
454}
455
Andrew Lunndea87022015-08-31 15:56:47 +0200456/* We expect the switch to perform auto negotiation if there is a real
457 * phy. However, in the case of a fixed link phy, we force the port
458 * settings from the fixed link settings.
459 */
460void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
461 struct phy_device *phydev)
462{
463 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200464 u32 reg;
465 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200466
467 if (!phy_is_pseudo_fixed_link(phydev))
468 return;
469
470 mutex_lock(&ps->smi_mutex);
471
Andrew Lunn158bc062016-04-28 21:24:06 -0400472 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200473 if (ret < 0)
474 goto out;
475
476 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
477 PORT_PCS_CTRL_FORCE_LINK |
478 PORT_PCS_CTRL_DUPLEX_FULL |
479 PORT_PCS_CTRL_FORCE_DUPLEX |
480 PORT_PCS_CTRL_UNFORCED);
481
482 reg |= PORT_PCS_CTRL_FORCE_LINK;
483 if (phydev->link)
484 reg |= PORT_PCS_CTRL_LINK_UP;
485
Andrew Lunn158bc062016-04-28 21:24:06 -0400486 if (mv88e6xxx_6065_family(ps) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200487 goto out;
488
489 switch (phydev->speed) {
490 case SPEED_1000:
491 reg |= PORT_PCS_CTRL_1000;
492 break;
493 case SPEED_100:
494 reg |= PORT_PCS_CTRL_100;
495 break;
496 case SPEED_10:
497 reg |= PORT_PCS_CTRL_10;
498 break;
499 default:
500 pr_info("Unknown speed");
501 goto out;
502 }
503
504 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
505 if (phydev->duplex == DUPLEX_FULL)
506 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
507
Andrew Lunn158bc062016-04-28 21:24:06 -0400508 if ((mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps)) &&
Vivien Didelot009a2b92016-04-17 13:24:01 -0400509 (port >= ps->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200510 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
511 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
512 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
513 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
514 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
515 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
516 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
517 }
Andrew Lunn158bc062016-04-28 21:24:06 -0400518 _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200519
520out:
521 mutex_unlock(&ps->smi_mutex);
522}
523
Andrew Lunn158bc062016-04-28 21:24:06 -0400524static int _mv88e6xxx_stats_wait(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000525{
526 int ret;
527 int i;
528
529 for (i = 0; i < 10; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400530 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200531 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000532 return 0;
533 }
534
535 return -ETIMEDOUT;
536}
537
Andrew Lunn158bc062016-04-28 21:24:06 -0400538static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_priv_state *ps,
539 int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000540{
541 int ret;
542
Andrew Lunn158bc062016-04-28 21:24:06 -0400543 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200544 port = (port + 1) << 5;
545
Barry Grussling3675c8d2013-01-08 16:05:53 +0000546 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400547 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200548 GLOBAL_STATS_OP_CAPTURE_PORT |
549 GLOBAL_STATS_OP_HIST_RX_TX | port);
550 if (ret < 0)
551 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000552
Barry Grussling3675c8d2013-01-08 16:05:53 +0000553 /* Wait for the snapshotting to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400554 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000555 if (ret < 0)
556 return ret;
557
558 return 0;
559}
560
Andrew Lunn158bc062016-04-28 21:24:06 -0400561static void _mv88e6xxx_stats_read(struct mv88e6xxx_priv_state *ps,
562 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000563{
564 u32 _val;
565 int ret;
566
567 *val = 0;
568
Andrew Lunn158bc062016-04-28 21:24:06 -0400569 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200570 GLOBAL_STATS_OP_READ_CAPTURED |
571 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000572 if (ret < 0)
573 return;
574
Andrew Lunn158bc062016-04-28 21:24:06 -0400575 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000576 if (ret < 0)
577 return;
578
Andrew Lunn158bc062016-04-28 21:24:06 -0400579 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000580 if (ret < 0)
581 return;
582
583 _val = ret << 16;
584
Andrew Lunn158bc062016-04-28 21:24:06 -0400585 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000586 if (ret < 0)
587 return;
588
589 *val = _val | ret;
590}
591
Andrew Lunne413e7e2015-04-02 04:06:38 +0200592static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100593 { "in_good_octets", 8, 0x00, BANK0, },
594 { "in_bad_octets", 4, 0x02, BANK0, },
595 { "in_unicast", 4, 0x04, BANK0, },
596 { "in_broadcasts", 4, 0x06, BANK0, },
597 { "in_multicasts", 4, 0x07, BANK0, },
598 { "in_pause", 4, 0x16, BANK0, },
599 { "in_undersize", 4, 0x18, BANK0, },
600 { "in_fragments", 4, 0x19, BANK0, },
601 { "in_oversize", 4, 0x1a, BANK0, },
602 { "in_jabber", 4, 0x1b, BANK0, },
603 { "in_rx_error", 4, 0x1c, BANK0, },
604 { "in_fcs_error", 4, 0x1d, BANK0, },
605 { "out_octets", 8, 0x0e, BANK0, },
606 { "out_unicast", 4, 0x10, BANK0, },
607 { "out_broadcasts", 4, 0x13, BANK0, },
608 { "out_multicasts", 4, 0x12, BANK0, },
609 { "out_pause", 4, 0x15, BANK0, },
610 { "excessive", 4, 0x11, BANK0, },
611 { "collisions", 4, 0x1e, BANK0, },
612 { "deferred", 4, 0x05, BANK0, },
613 { "single", 4, 0x14, BANK0, },
614 { "multiple", 4, 0x17, BANK0, },
615 { "out_fcs_error", 4, 0x03, BANK0, },
616 { "late", 4, 0x1f, BANK0, },
617 { "hist_64bytes", 4, 0x08, BANK0, },
618 { "hist_65_127bytes", 4, 0x09, BANK0, },
619 { "hist_128_255bytes", 4, 0x0a, BANK0, },
620 { "hist_256_511bytes", 4, 0x0b, BANK0, },
621 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
622 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
623 { "sw_in_discards", 4, 0x10, PORT, },
624 { "sw_in_filtered", 2, 0x12, PORT, },
625 { "sw_out_filtered", 2, 0x13, PORT, },
626 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
627 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
628 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
629 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
630 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
631 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
632 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
633 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
634 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
635 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
636 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
637 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
638 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
639 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
640 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
641 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
642 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
643 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
644 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
645 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
646 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
647 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
648 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
649 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
650 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
651 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200652};
653
Andrew Lunn158bc062016-04-28 21:24:06 -0400654static bool mv88e6xxx_has_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100655 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200656{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100657 switch (stat->type) {
658 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200659 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100660 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400661 return mv88e6xxx_6320_family(ps);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100662 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400663 return mv88e6xxx_6095_family(ps) ||
664 mv88e6xxx_6185_family(ps) ||
665 mv88e6xxx_6097_family(ps) ||
666 mv88e6xxx_6165_family(ps) ||
667 mv88e6xxx_6351_family(ps) ||
668 mv88e6xxx_6352_family(ps);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200669 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100670 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000671}
672
Andrew Lunn158bc062016-04-28 21:24:06 -0400673static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100674 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200675 int port)
676{
Andrew Lunn80c46272015-06-20 18:42:30 +0200677 u32 low;
678 u32 high = 0;
679 int ret;
680 u64 value;
681
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100682 switch (s->type) {
683 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400684 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200685 if (ret < 0)
686 return UINT64_MAX;
687
688 low = ret;
689 if (s->sizeof_stat == 4) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400690 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100691 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200692 if (ret < 0)
693 return UINT64_MAX;
694 high = ret;
695 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100696 break;
697 case BANK0:
698 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400699 _mv88e6xxx_stats_read(ps, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200700 if (s->sizeof_stat == 8)
Andrew Lunn158bc062016-04-28 21:24:06 -0400701 _mv88e6xxx_stats_read(ps, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200702 }
703 value = (((u64)high) << 16) | low;
704 return value;
705}
706
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100707void mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
708{
Andrew Lunn158bc062016-04-28 21:24:06 -0400709 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100710 struct mv88e6xxx_hw_stat *stat;
711 int i, j;
712
713 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
714 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400715 if (mv88e6xxx_has_stat(ps, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100716 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
717 ETH_GSTRING_LEN);
718 j++;
719 }
720 }
721}
722
723int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
724{
Andrew Lunn158bc062016-04-28 21:24:06 -0400725 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100726 struct mv88e6xxx_hw_stat *stat;
727 int i, j;
728
729 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
730 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400731 if (mv88e6xxx_has_stat(ps, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100732 j++;
733 }
734 return j;
735}
736
737void
738mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
739 int port, uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000740{
Florian Fainellia22adce2014-04-28 11:14:28 -0700741 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100742 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000743 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100744 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000745
Andrew Lunn31888232015-05-06 01:09:54 +0200746 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000747
Andrew Lunn158bc062016-04-28 21:24:06 -0400748 ret = _mv88e6xxx_stats_snapshot(ps, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000749 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200750 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000751 return;
752 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100753 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
754 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400755 if (mv88e6xxx_has_stat(ps, stat)) {
756 data[j] = _mv88e6xxx_get_ethtool_stat(ps, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100757 j++;
758 }
759 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000760
Andrew Lunn31888232015-05-06 01:09:54 +0200761 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000762}
Ben Hutchings98e67302011-11-25 14:36:19 +0000763
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700764int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
765{
766 return 32 * sizeof(u16);
767}
768
769void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
770 struct ethtool_regs *regs, void *_p)
771{
Andrew Lunn158bc062016-04-28 21:24:06 -0400772 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700773 u16 *p = _p;
774 int i;
775
776 regs->version = 0;
777
778 memset(p, 0xff, 32 * sizeof(u16));
779
780 for (i = 0; i < 32; i++) {
781 int ret;
782
Andrew Lunn158bc062016-04-28 21:24:06 -0400783 ret = mv88e6xxx_reg_read(ps, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700784 if (ret >= 0)
785 p[i] = ret;
786 }
787}
788
Andrew Lunn158bc062016-04-28 21:24:06 -0400789static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset,
Andrew Lunn3898c142015-05-06 01:09:53 +0200790 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700791{
792 unsigned long timeout = jiffies + HZ / 10;
793
794 while (time_before(jiffies, timeout)) {
795 int ret;
796
Andrew Lunn158bc062016-04-28 21:24:06 -0400797 ret = _mv88e6xxx_reg_read(ps, reg, offset);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700798 if (ret < 0)
799 return ret;
800 if (!(ret & mask))
801 return 0;
802
803 usleep_range(1000, 2000);
804 }
805 return -ETIMEDOUT;
806}
807
Andrew Lunn158bc062016-04-28 21:24:06 -0400808static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg,
809 int offset, u16 mask)
Andrew Lunn3898c142015-05-06 01:09:53 +0200810{
Andrew Lunn3898c142015-05-06 01:09:53 +0200811 int ret;
812
813 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400814 ret = _mv88e6xxx_wait(ps, reg, offset, mask);
Andrew Lunn3898c142015-05-06 01:09:53 +0200815 mutex_unlock(&ps->smi_mutex);
816
817 return ret;
818}
819
Andrew Lunn158bc062016-04-28 21:24:06 -0400820static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps)
Andrew Lunn3898c142015-05-06 01:09:53 +0200821{
Andrew Lunn158bc062016-04-28 21:24:06 -0400822 return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200823 GLOBAL2_SMI_OP_BUSY);
824}
825
Vivien Didelotd24645b2016-05-09 13:22:41 -0400826static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200827{
Andrew Lunn158bc062016-04-28 21:24:06 -0400828 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
829
830 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200831 GLOBAL2_EEPROM_OP_LOAD);
832}
833
Vivien Didelotd24645b2016-05-09 13:22:41 -0400834static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200835{
Andrew Lunn158bc062016-04-28 21:24:06 -0400836 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
837
838 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200839 GLOBAL2_EEPROM_OP_BUSY);
840}
841
Vivien Didelotd24645b2016-05-09 13:22:41 -0400842static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
843{
844 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
845 int ret;
846
847 mutex_lock(&ps->eeprom_mutex);
848
849 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
850 GLOBAL2_EEPROM_OP_READ |
851 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
852 if (ret < 0)
853 goto error;
854
855 ret = mv88e6xxx_eeprom_busy_wait(ds);
856 if (ret < 0)
857 goto error;
858
859 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
860error:
861 mutex_unlock(&ps->eeprom_mutex);
862 return ret;
863}
864
865int mv88e6xxx_get_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
866 u8 *data)
867{
868 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
869 int offset;
870 int len;
871 int ret;
872
873 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
874 return -EOPNOTSUPP;
875
876 offset = eeprom->offset;
877 len = eeprom->len;
878 eeprom->len = 0;
879
880 eeprom->magic = 0xc3ec4951;
881
882 ret = mv88e6xxx_eeprom_load_wait(ds);
883 if (ret < 0)
884 return ret;
885
886 if (offset & 1) {
887 int word;
888
889 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
890 if (word < 0)
891 return word;
892
893 *data++ = (word >> 8) & 0xff;
894
895 offset++;
896 len--;
897 eeprom->len++;
898 }
899
900 while (len >= 2) {
901 int word;
902
903 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
904 if (word < 0)
905 return word;
906
907 *data++ = word & 0xff;
908 *data++ = (word >> 8) & 0xff;
909
910 offset += 2;
911 len -= 2;
912 eeprom->len += 2;
913 }
914
915 if (len) {
916 int word;
917
918 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
919 if (word < 0)
920 return word;
921
922 *data++ = word & 0xff;
923
924 offset++;
925 len--;
926 eeprom->len++;
927 }
928
929 return 0;
930}
931
932static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
933{
934 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
935 int ret;
936
937 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
938 if (ret < 0)
939 return ret;
940
941 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
942 return -EROFS;
943
944 return 0;
945}
946
947static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
948 u16 data)
949{
950 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
951 int ret;
952
953 mutex_lock(&ps->eeprom_mutex);
954
955 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
956 if (ret < 0)
957 goto error;
958
959 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
960 GLOBAL2_EEPROM_OP_WRITE |
961 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
962 if (ret < 0)
963 goto error;
964
965 ret = mv88e6xxx_eeprom_busy_wait(ds);
966error:
967 mutex_unlock(&ps->eeprom_mutex);
968 return ret;
969}
970
971int mv88e6xxx_set_eeprom(struct dsa_switch *ds, struct ethtool_eeprom *eeprom,
972 u8 *data)
973{
974 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
975 int offset;
976 int ret;
977 int len;
978
979 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
980 return -EOPNOTSUPP;
981
982 if (eeprom->magic != 0xc3ec4951)
983 return -EINVAL;
984
985 ret = mv88e6xxx_eeprom_is_readonly(ds);
986 if (ret)
987 return ret;
988
989 offset = eeprom->offset;
990 len = eeprom->len;
991 eeprom->len = 0;
992
993 ret = mv88e6xxx_eeprom_load_wait(ds);
994 if (ret < 0)
995 return ret;
996
997 if (offset & 1) {
998 int word;
999
1000 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1001 if (word < 0)
1002 return word;
1003
1004 word = (*data++ << 8) | (word & 0xff);
1005
1006 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1007 if (ret < 0)
1008 return ret;
1009
1010 offset++;
1011 len--;
1012 eeprom->len++;
1013 }
1014
1015 while (len >= 2) {
1016 int word;
1017
1018 word = *data++;
1019 word |= *data++ << 8;
1020
1021 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1022 if (ret < 0)
1023 return ret;
1024
1025 offset += 2;
1026 len -= 2;
1027 eeprom->len += 2;
1028 }
1029
1030 if (len) {
1031 int word;
1032
1033 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1034 if (word < 0)
1035 return word;
1036
1037 word = (word & 0xff00) | *data++;
1038
1039 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1040 if (ret < 0)
1041 return ret;
1042
1043 offset++;
1044 len--;
1045 eeprom->len++;
1046 }
1047
1048 return 0;
1049}
1050
Andrew Lunn158bc062016-04-28 21:24:06 -04001051static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001052{
Andrew Lunn158bc062016-04-28 21:24:06 -04001053 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001054 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001055}
1056
Andrew Lunn158bc062016-04-28 21:24:06 -04001057static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
1058 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001059{
1060 int ret;
1061
Andrew Lunn158bc062016-04-28 21:24:06 -04001062 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001063 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1064 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001065 if (ret < 0)
1066 return ret;
1067
Andrew Lunn158bc062016-04-28 21:24:06 -04001068 ret = _mv88e6xxx_phy_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +02001069 if (ret < 0)
1070 return ret;
1071
Andrew Lunn158bc062016-04-28 21:24:06 -04001072 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
1073
1074 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001075}
1076
Andrew Lunn158bc062016-04-28 21:24:06 -04001077static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
1078 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001079{
Andrew Lunn3898c142015-05-06 01:09:53 +02001080 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001081
Andrew Lunn158bc062016-04-28 21:24:06 -04001082 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001083 if (ret < 0)
1084 return ret;
1085
Andrew Lunn158bc062016-04-28 21:24:06 -04001086 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001087 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1088 regnum);
1089
Andrew Lunn158bc062016-04-28 21:24:06 -04001090 return _mv88e6xxx_phy_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +01001091}
1092
Guenter Roeck11b3b452015-03-06 22:23:51 -08001093int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
1094{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001095 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001096 int reg;
1097
Andrew Lunn3898c142015-05-06 01:09:53 +02001098 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001099
Andrew Lunn158bc062016-04-28 21:24:06 -04001100 reg = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001101 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001102 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001103
1104 e->eee_enabled = !!(reg & 0x0200);
1105 e->tx_lpi_enabled = !!(reg & 0x0100);
1106
Andrew Lunn158bc062016-04-28 21:24:06 -04001107 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001108 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001109 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001110
Andrew Lunncca8b132015-04-02 04:06:39 +02001111 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001112 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001113
Andrew Lunn2f40c692015-04-02 04:06:37 +02001114out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001115 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001116 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001117}
1118
1119int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1120 struct phy_device *phydev, struct ethtool_eee *e)
1121{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001122 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1123 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001124 int ret;
1125
Andrew Lunn3898c142015-05-06 01:09:53 +02001126 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001127
Andrew Lunn158bc062016-04-28 21:24:06 -04001128 ret = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001129 if (ret < 0)
1130 goto out;
1131
1132 reg = ret & ~0x0300;
1133 if (e->eee_enabled)
1134 reg |= 0x0200;
1135 if (e->tx_lpi_enabled)
1136 reg |= 0x0100;
1137
Andrew Lunn158bc062016-04-28 21:24:06 -04001138 ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001139out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001140 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001141
1142 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001143}
1144
Andrew Lunn158bc062016-04-28 21:24:06 -04001145static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001146{
1147 int ret;
1148
Andrew Lunn158bc062016-04-28 21:24:06 -04001149 if (mv88e6xxx_has_fid_reg(ps)) {
1150 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001151 if (ret < 0)
1152 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001153 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001154 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001155 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001156 if (ret < 0)
1157 return ret;
1158
Andrew Lunn158bc062016-04-28 21:24:06 -04001159 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001160 (ret & 0xfff) |
1161 ((fid << 8) & 0xf000));
1162 if (ret < 0)
1163 return ret;
1164
1165 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1166 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001167 }
1168
Andrew Lunn158bc062016-04-28 21:24:06 -04001169 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001170 if (ret < 0)
1171 return ret;
1172
Andrew Lunn158bc062016-04-28 21:24:06 -04001173 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001174}
1175
Andrew Lunn158bc062016-04-28 21:24:06 -04001176static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -04001177 struct mv88e6xxx_atu_entry *entry)
1178{
1179 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1180
1181 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1182 unsigned int mask, shift;
1183
1184 if (entry->trunk) {
1185 data |= GLOBAL_ATU_DATA_TRUNK;
1186 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1187 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1188 } else {
1189 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1190 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1191 }
1192
1193 data |= (entry->portv_trunkid << shift) & mask;
1194 }
1195
Andrew Lunn158bc062016-04-28 21:24:06 -04001196 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001197}
1198
Andrew Lunn158bc062016-04-28 21:24:06 -04001199static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001200 struct mv88e6xxx_atu_entry *entry,
1201 bool static_too)
1202{
1203 int op;
1204 int err;
1205
Andrew Lunn158bc062016-04-28 21:24:06 -04001206 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001207 if (err)
1208 return err;
1209
Andrew Lunn158bc062016-04-28 21:24:06 -04001210 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001211 if (err)
1212 return err;
1213
1214 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001215 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1216 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1217 } else {
1218 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1219 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1220 }
1221
Andrew Lunn158bc062016-04-28 21:24:06 -04001222 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001223}
1224
Andrew Lunn158bc062016-04-28 21:24:06 -04001225static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1226 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001227{
1228 struct mv88e6xxx_atu_entry entry = {
1229 .fid = fid,
1230 .state = 0, /* EntryState bits must be 0 */
1231 };
1232
Andrew Lunn158bc062016-04-28 21:24:06 -04001233 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001234}
1235
Andrew Lunn158bc062016-04-28 21:24:06 -04001236static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1237 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001238{
1239 struct mv88e6xxx_atu_entry entry = {
1240 .trunk = false,
1241 .fid = fid,
1242 };
1243
1244 /* EntryState bits must be 0xF */
1245 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1246
1247 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1248 entry.portv_trunkid = (to_port & 0x0f) << 4;
1249 entry.portv_trunkid |= from_port & 0x0f;
1250
Andrew Lunn158bc062016-04-28 21:24:06 -04001251 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001252}
1253
Andrew Lunn158bc062016-04-28 21:24:06 -04001254static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1255 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001256{
1257 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001258 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001259}
1260
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001261static const char * const mv88e6xxx_port_state_names[] = {
1262 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1263 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1264 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1265 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1266};
1267
Andrew Lunn158bc062016-04-28 21:24:06 -04001268static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1269 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001270{
Andrew Lunn158bc062016-04-28 21:24:06 -04001271 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001272 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001273 u8 oldstate;
1274
Andrew Lunn158bc062016-04-28 21:24:06 -04001275 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001276 if (reg < 0)
1277 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001278
Andrew Lunncca8b132015-04-02 04:06:39 +02001279 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001280
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001281 if (oldstate != state) {
1282 /* Flush forwarding database if we're moving a port
1283 * from Learning or Forwarding state to Disabled or
1284 * Blocking or Listening state.
1285 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001286 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1287 oldstate == PORT_CONTROL_STATE_FORWARDING)
1288 && (state == PORT_CONTROL_STATE_DISABLED ||
1289 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001290 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001291 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001292 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001293 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001294
Andrew Lunncca8b132015-04-02 04:06:39 +02001295 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001296 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001297 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001298 if (ret)
1299 return ret;
1300
1301 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1302 mv88e6xxx_port_state_names[state],
1303 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001304 }
1305
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001306 return ret;
1307}
1308
Andrew Lunn158bc062016-04-28 21:24:06 -04001309static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1310 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001311{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001312 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001313 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001314 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001315 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001316 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001317 int i;
1318
1319 /* allow CPU port or DSA link(s) to send frames to every port */
1320 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1321 output_ports = mask;
1322 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001323 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001324 /* allow sending frames to every group member */
1325 if (bridge && ps->ports[i].bridge_dev == bridge)
1326 output_ports |= BIT(i);
1327
1328 /* allow sending frames to CPU port and DSA link(s) */
1329 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1330 output_ports |= BIT(i);
1331 }
1332 }
1333
1334 /* prevent frames from going back out of the port they came in on */
1335 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001336
Andrew Lunn158bc062016-04-28 21:24:06 -04001337 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001338 if (reg < 0)
1339 return reg;
1340
1341 reg &= ~mask;
1342 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001343
Andrew Lunn158bc062016-04-28 21:24:06 -04001344 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001345}
1346
Vivien Didelot43c44a92016-04-06 11:55:03 -04001347void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001348{
1349 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1350 int stp_state;
1351
1352 switch (state) {
1353 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001354 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001355 break;
1356 case BR_STATE_BLOCKING:
1357 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001358 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001359 break;
1360 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001361 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001362 break;
1363 case BR_STATE_FORWARDING:
1364 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001365 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001366 break;
1367 }
1368
Vivien Didelot43c44a92016-04-06 11:55:03 -04001369 /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled,
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001370 * so we can not update the port state directly but need to schedule it.
1371 */
Vivien Didelotd715fa62016-02-12 12:09:38 -05001372 ps->ports[port].state = stp_state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001373 set_bit(port, ps->port_state_update_mask);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001374 schedule_work(&ps->bridge_work);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001375}
1376
Andrew Lunn158bc062016-04-28 21:24:06 -04001377static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1378 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001379{
Andrew Lunn158bc062016-04-28 21:24:06 -04001380 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001381 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001382 int ret;
1383
Andrew Lunn158bc062016-04-28 21:24:06 -04001384 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001385 if (ret < 0)
1386 return ret;
1387
Vivien Didelot5da96032016-03-07 18:24:39 -05001388 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1389
1390 if (new) {
1391 ret &= ~PORT_DEFAULT_VLAN_MASK;
1392 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1393
Andrew Lunn158bc062016-04-28 21:24:06 -04001394 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001395 PORT_DEFAULT_VLAN, ret);
1396 if (ret < 0)
1397 return ret;
1398
1399 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1400 pvid);
1401 }
1402
1403 if (old)
1404 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001405
1406 return 0;
1407}
1408
Andrew Lunn158bc062016-04-28 21:24:06 -04001409static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1410 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001411{
Andrew Lunn158bc062016-04-28 21:24:06 -04001412 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001413}
1414
Andrew Lunn158bc062016-04-28 21:24:06 -04001415static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1416 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001417{
Andrew Lunn158bc062016-04-28 21:24:06 -04001418 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001419}
1420
Andrew Lunn158bc062016-04-28 21:24:06 -04001421static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001422{
Andrew Lunn158bc062016-04-28 21:24:06 -04001423 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001424 GLOBAL_VTU_OP_BUSY);
1425}
1426
Andrew Lunn158bc062016-04-28 21:24:06 -04001427static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001428{
1429 int ret;
1430
Andrew Lunn158bc062016-04-28 21:24:06 -04001431 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001432 if (ret < 0)
1433 return ret;
1434
Andrew Lunn158bc062016-04-28 21:24:06 -04001435 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001436}
1437
Andrew Lunn158bc062016-04-28 21:24:06 -04001438static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001439{
1440 int ret;
1441
Andrew Lunn158bc062016-04-28 21:24:06 -04001442 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001443 if (ret < 0)
1444 return ret;
1445
Andrew Lunn158bc062016-04-28 21:24:06 -04001446 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001447}
1448
Andrew Lunn158bc062016-04-28 21:24:06 -04001449static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001450 struct mv88e6xxx_vtu_stu_entry *entry,
1451 unsigned int nibble_offset)
1452{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001453 u16 regs[3];
1454 int i;
1455 int ret;
1456
1457 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001458 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001459 GLOBAL_VTU_DATA_0_3 + i);
1460 if (ret < 0)
1461 return ret;
1462
1463 regs[i] = ret;
1464 }
1465
Vivien Didelot009a2b92016-04-17 13:24:01 -04001466 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001467 unsigned int shift = (i % 4) * 4 + nibble_offset;
1468 u16 reg = regs[i / 4];
1469
1470 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1471 }
1472
1473 return 0;
1474}
1475
Andrew Lunn158bc062016-04-28 21:24:06 -04001476static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001477 struct mv88e6xxx_vtu_stu_entry *entry,
1478 unsigned int nibble_offset)
1479{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001480 u16 regs[3] = { 0 };
1481 int i;
1482 int ret;
1483
Vivien Didelot009a2b92016-04-17 13:24:01 -04001484 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001485 unsigned int shift = (i % 4) * 4 + nibble_offset;
1486 u8 data = entry->data[i];
1487
1488 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1489 }
1490
1491 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001492 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001493 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1494 if (ret < 0)
1495 return ret;
1496 }
1497
1498 return 0;
1499}
1500
Andrew Lunn158bc062016-04-28 21:24:06 -04001501static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001502{
Andrew Lunn158bc062016-04-28 21:24:06 -04001503 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001504 vid & GLOBAL_VTU_VID_MASK);
1505}
1506
Andrew Lunn158bc062016-04-28 21:24:06 -04001507static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001508 struct mv88e6xxx_vtu_stu_entry *entry)
1509{
1510 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1511 int ret;
1512
Andrew Lunn158bc062016-04-28 21:24:06 -04001513 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001514 if (ret < 0)
1515 return ret;
1516
Andrew Lunn158bc062016-04-28 21:24:06 -04001517 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001518 if (ret < 0)
1519 return ret;
1520
Andrew Lunn158bc062016-04-28 21:24:06 -04001521 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001522 if (ret < 0)
1523 return ret;
1524
1525 next.vid = ret & GLOBAL_VTU_VID_MASK;
1526 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1527
1528 if (next.valid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001529 ret = _mv88e6xxx_vtu_stu_data_read(ps, &next, 0);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001530 if (ret < 0)
1531 return ret;
1532
Andrew Lunn158bc062016-04-28 21:24:06 -04001533 if (mv88e6xxx_has_fid_reg(ps)) {
1534 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001535 GLOBAL_VTU_FID);
1536 if (ret < 0)
1537 return ret;
1538
1539 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001540 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001541 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1542 * VTU DBNum[3:0] are located in VTU Operation 3:0
1543 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001544 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001545 GLOBAL_VTU_OP);
1546 if (ret < 0)
1547 return ret;
1548
1549 next.fid = (ret & 0xf00) >> 4;
1550 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001551 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001552
Andrew Lunn158bc062016-04-28 21:24:06 -04001553 if (mv88e6xxx_has_stu(ps)) {
1554 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001555 GLOBAL_VTU_SID);
1556 if (ret < 0)
1557 return ret;
1558
1559 next.sid = ret & GLOBAL_VTU_SID_MASK;
1560 }
1561 }
1562
1563 *entry = next;
1564 return 0;
1565}
1566
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001567int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1568 struct switchdev_obj_port_vlan *vlan,
1569 int (*cb)(struct switchdev_obj *obj))
1570{
1571 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1572 struct mv88e6xxx_vtu_stu_entry next;
1573 u16 pvid;
1574 int err;
1575
1576 mutex_lock(&ps->smi_mutex);
1577
Andrew Lunn158bc062016-04-28 21:24:06 -04001578 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001579 if (err)
1580 goto unlock;
1581
Andrew Lunn158bc062016-04-28 21:24:06 -04001582 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001583 if (err)
1584 goto unlock;
1585
1586 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001587 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001588 if (err)
1589 break;
1590
1591 if (!next.valid)
1592 break;
1593
1594 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1595 continue;
1596
1597 /* reinit and dump this VLAN obj */
1598 vlan->vid_begin = vlan->vid_end = next.vid;
1599 vlan->flags = 0;
1600
1601 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1602 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1603
1604 if (next.vid == pvid)
1605 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1606
1607 err = cb(&vlan->obj);
1608 if (err)
1609 break;
1610 } while (next.vid < GLOBAL_VTU_VID_MASK);
1611
1612unlock:
1613 mutex_unlock(&ps->smi_mutex);
1614
1615 return err;
1616}
1617
Andrew Lunn158bc062016-04-28 21:24:06 -04001618static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001619 struct mv88e6xxx_vtu_stu_entry *entry)
1620{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001621 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001622 u16 reg = 0;
1623 int ret;
1624
Andrew Lunn158bc062016-04-28 21:24:06 -04001625 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001626 if (ret < 0)
1627 return ret;
1628
1629 if (!entry->valid)
1630 goto loadpurge;
1631
1632 /* Write port member tags */
Andrew Lunn158bc062016-04-28 21:24:06 -04001633 ret = _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001634 if (ret < 0)
1635 return ret;
1636
Andrew Lunn158bc062016-04-28 21:24:06 -04001637 if (mv88e6xxx_has_stu(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001638 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001639 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001640 if (ret < 0)
1641 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001642 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001643
Andrew Lunn158bc062016-04-28 21:24:06 -04001644 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001645 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001646 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001647 if (ret < 0)
1648 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001649 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001650 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1651 * VTU DBNum[3:0] are located in VTU Operation 3:0
1652 */
1653 op |= (entry->fid & 0xf0) << 8;
1654 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001655 }
1656
1657 reg = GLOBAL_VTU_VID_VALID;
1658loadpurge:
1659 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001660 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001661 if (ret < 0)
1662 return ret;
1663
Andrew Lunn158bc062016-04-28 21:24:06 -04001664 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001665}
1666
Andrew Lunn158bc062016-04-28 21:24:06 -04001667static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001668 struct mv88e6xxx_vtu_stu_entry *entry)
1669{
1670 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1671 int ret;
1672
Andrew Lunn158bc062016-04-28 21:24:06 -04001673 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001674 if (ret < 0)
1675 return ret;
1676
Andrew Lunn158bc062016-04-28 21:24:06 -04001677 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001678 sid & GLOBAL_VTU_SID_MASK);
1679 if (ret < 0)
1680 return ret;
1681
Andrew Lunn158bc062016-04-28 21:24:06 -04001682 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001683 if (ret < 0)
1684 return ret;
1685
Andrew Lunn158bc062016-04-28 21:24:06 -04001686 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001687 if (ret < 0)
1688 return ret;
1689
1690 next.sid = ret & GLOBAL_VTU_SID_MASK;
1691
Andrew Lunn158bc062016-04-28 21:24:06 -04001692 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001693 if (ret < 0)
1694 return ret;
1695
1696 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1697
1698 if (next.valid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001699 ret = _mv88e6xxx_vtu_stu_data_read(ps, &next, 2);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001700 if (ret < 0)
1701 return ret;
1702 }
1703
1704 *entry = next;
1705 return 0;
1706}
1707
Andrew Lunn158bc062016-04-28 21:24:06 -04001708static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001709 struct mv88e6xxx_vtu_stu_entry *entry)
1710{
1711 u16 reg = 0;
1712 int ret;
1713
Andrew Lunn158bc062016-04-28 21:24:06 -04001714 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001715 if (ret < 0)
1716 return ret;
1717
1718 if (!entry->valid)
1719 goto loadpurge;
1720
1721 /* Write port states */
Andrew Lunn158bc062016-04-28 21:24:06 -04001722 ret = _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001723 if (ret < 0)
1724 return ret;
1725
1726 reg = GLOBAL_VTU_VID_VALID;
1727loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001728 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001729 if (ret < 0)
1730 return ret;
1731
1732 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001733 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001734 if (ret < 0)
1735 return ret;
1736
Andrew Lunn158bc062016-04-28 21:24:06 -04001737 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001738}
1739
Andrew Lunn158bc062016-04-28 21:24:06 -04001740static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1741 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001742{
Andrew Lunn158bc062016-04-28 21:24:06 -04001743 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001744 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001745 u16 fid;
1746 int ret;
1747
Andrew Lunn158bc062016-04-28 21:24:06 -04001748 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001749 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001750 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001751 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001752 else
1753 return -EOPNOTSUPP;
1754
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001755 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001756 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001757 if (ret < 0)
1758 return ret;
1759
1760 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1761
1762 if (new) {
1763 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1764 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1765
Andrew Lunn158bc062016-04-28 21:24:06 -04001766 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001767 ret);
1768 if (ret < 0)
1769 return ret;
1770 }
1771
1772 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001773 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001774 if (ret < 0)
1775 return ret;
1776
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001777 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001778
1779 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001780 ret &= ~upper_mask;
1781 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001782
Andrew Lunn158bc062016-04-28 21:24:06 -04001783 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001784 ret);
1785 if (ret < 0)
1786 return ret;
1787
1788 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1789 }
1790
1791 if (old)
1792 *old = fid;
1793
1794 return 0;
1795}
1796
Andrew Lunn158bc062016-04-28 21:24:06 -04001797static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1798 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001799{
Andrew Lunn158bc062016-04-28 21:24:06 -04001800 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001801}
1802
Andrew Lunn158bc062016-04-28 21:24:06 -04001803static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1804 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001805{
Andrew Lunn158bc062016-04-28 21:24:06 -04001806 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001807}
1808
Andrew Lunn158bc062016-04-28 21:24:06 -04001809static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001810{
1811 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1812 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001813 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001814
1815 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1816
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001817 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001818 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001819 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001820 if (err)
1821 return err;
1822
1823 set_bit(*fid, fid_bitmap);
1824 }
1825
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001826 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001827 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001828 if (err)
1829 return err;
1830
1831 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001832 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001833 if (err)
1834 return err;
1835
1836 if (!vlan.valid)
1837 break;
1838
1839 set_bit(vlan.fid, fid_bitmap);
1840 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1841
1842 /* The reset value 0x000 is used to indicate that multiple address
1843 * databases are not needed. Return the next positive available.
1844 */
1845 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001846 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001847 return -ENOSPC;
1848
1849 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001850 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001851}
1852
Andrew Lunn158bc062016-04-28 21:24:06 -04001853static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001854 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001855{
Andrew Lunn158bc062016-04-28 21:24:06 -04001856 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001857 struct mv88e6xxx_vtu_stu_entry vlan = {
1858 .valid = true,
1859 .vid = vid,
1860 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001861 int i, err;
1862
Andrew Lunn158bc062016-04-28 21:24:06 -04001863 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001864 if (err)
1865 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001866
Vivien Didelot3d131f02015-11-03 10:52:52 -05001867 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001868 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001869 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1870 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1871 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001872
Andrew Lunn158bc062016-04-28 21:24:06 -04001873 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1874 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001875 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001876
1877 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1878 * implemented, only one STU entry is needed to cover all VTU
1879 * entries. Thus, validate the SID 0.
1880 */
1881 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001882 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001883 if (err)
1884 return err;
1885
1886 if (vstp.sid != vlan.sid || !vstp.valid) {
1887 memset(&vstp, 0, sizeof(vstp));
1888 vstp.valid = true;
1889 vstp.sid = vlan.sid;
1890
Andrew Lunn158bc062016-04-28 21:24:06 -04001891 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001892 if (err)
1893 return err;
1894 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001895 }
1896
1897 *entry = vlan;
1898 return 0;
1899}
1900
Andrew Lunn158bc062016-04-28 21:24:06 -04001901static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001902 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1903{
1904 int err;
1905
1906 if (!vid)
1907 return -EINVAL;
1908
Andrew Lunn158bc062016-04-28 21:24:06 -04001909 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001910 if (err)
1911 return err;
1912
Andrew Lunn158bc062016-04-28 21:24:06 -04001913 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001914 if (err)
1915 return err;
1916
1917 if (entry->vid != vid || !entry->valid) {
1918 if (!creat)
1919 return -EOPNOTSUPP;
1920 /* -ENOENT would've been more appropriate, but switchdev expects
1921 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1922 */
1923
Andrew Lunn158bc062016-04-28 21:24:06 -04001924 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001925 }
1926
1927 return err;
1928}
1929
Vivien Didelotda9c3592016-02-12 12:09:40 -05001930static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1931 u16 vid_begin, u16 vid_end)
1932{
1933 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1934 struct mv88e6xxx_vtu_stu_entry vlan;
1935 int i, err;
1936
1937 if (!vid_begin)
1938 return -EOPNOTSUPP;
1939
1940 mutex_lock(&ps->smi_mutex);
1941
Andrew Lunn158bc062016-04-28 21:24:06 -04001942 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001943 if (err)
1944 goto unlock;
1945
1946 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001947 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001948 if (err)
1949 goto unlock;
1950
1951 if (!vlan.valid)
1952 break;
1953
1954 if (vlan.vid > vid_end)
1955 break;
1956
Vivien Didelot009a2b92016-04-17 13:24:01 -04001957 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001958 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1959 continue;
1960
1961 if (vlan.data[i] ==
1962 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1963 continue;
1964
1965 if (ps->ports[i].bridge_dev ==
1966 ps->ports[port].bridge_dev)
1967 break; /* same bridge, check next VLAN */
1968
1969 netdev_warn(ds->ports[port],
1970 "hardware VLAN %d already used by %s\n",
1971 vlan.vid,
1972 netdev_name(ps->ports[i].bridge_dev));
1973 err = -EOPNOTSUPP;
1974 goto unlock;
1975 }
1976 } while (vlan.vid < vid_end);
1977
1978unlock:
1979 mutex_unlock(&ps->smi_mutex);
1980
1981 return err;
1982}
1983
Vivien Didelot214cdb92016-02-26 13:16:08 -05001984static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1985 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1986 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1987 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1988 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1989};
1990
1991int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1992 bool vlan_filtering)
1993{
1994 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1995 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1996 PORT_CONTROL_2_8021Q_DISABLED;
1997 int ret;
1998
1999 mutex_lock(&ps->smi_mutex);
2000
Andrew Lunn158bc062016-04-28 21:24:06 -04002001 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002002 if (ret < 0)
2003 goto unlock;
2004
2005 old = ret & PORT_CONTROL_2_8021Q_MASK;
2006
Vivien Didelot5220ef12016-03-07 18:24:52 -05002007 if (new != old) {
2008 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2009 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002010
Andrew Lunn158bc062016-04-28 21:24:06 -04002011 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002012 ret);
2013 if (ret < 0)
2014 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002015
Vivien Didelot5220ef12016-03-07 18:24:52 -05002016 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
2017 mv88e6xxx_port_8021q_mode_names[new],
2018 mv88e6xxx_port_8021q_mode_names[old]);
2019 }
2020
2021 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002022unlock:
2023 mutex_unlock(&ps->smi_mutex);
2024
2025 return ret;
2026}
2027
Vivien Didelot76e398a2015-11-01 12:33:55 -05002028int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2029 const struct switchdev_obj_port_vlan *vlan,
2030 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002031{
Vivien Didelotda9c3592016-02-12 12:09:40 -05002032 int err;
2033
Vivien Didelotda9c3592016-02-12 12:09:40 -05002034 /* If the requested port doesn't belong to the same bridge as the VLAN
2035 * members, do not support it (yet) and fallback to software VLAN.
2036 */
2037 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2038 vlan->vid_end);
2039 if (err)
2040 return err;
2041
Vivien Didelot76e398a2015-11-01 12:33:55 -05002042 /* We don't need any dynamic resource from the kernel (yet),
2043 * so skip the prepare phase.
2044 */
2045 return 0;
2046}
2047
Andrew Lunn158bc062016-04-28 21:24:06 -04002048static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
2049 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002050{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002051 struct mv88e6xxx_vtu_stu_entry vlan;
2052 int err;
2053
Andrew Lunn158bc062016-04-28 21:24:06 -04002054 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002055 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002056 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002057
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002058 vlan.data[port] = untagged ?
2059 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2060 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2061
Andrew Lunn158bc062016-04-28 21:24:06 -04002062 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002063}
2064
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002065void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2066 const struct switchdev_obj_port_vlan *vlan,
2067 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002068{
2069 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2070 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2071 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2072 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002073
2074 mutex_lock(&ps->smi_mutex);
2075
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002076 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04002077 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002078 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
2079 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002080
Andrew Lunn158bc062016-04-28 21:24:06 -04002081 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002082 netdev_err(ds->ports[port], "failed to set PVID %d\n",
2083 vlan->vid_end);
2084
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002085 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002086}
2087
Andrew Lunn158bc062016-04-28 21:24:06 -04002088static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
2089 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002090{
Andrew Lunn158bc062016-04-28 21:24:06 -04002091 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002092 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002093 int i, err;
2094
Andrew Lunn158bc062016-04-28 21:24:06 -04002095 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002096 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002097 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002098
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002099 /* Tell switchdev if this VLAN is handled in software */
2100 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002101 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002102
2103 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2104
2105 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002106 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04002107 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002108 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002109 continue;
2110
2111 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002112 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002113 break;
2114 }
2115 }
2116
Andrew Lunn158bc062016-04-28 21:24:06 -04002117 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002118 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002119 return err;
2120
Andrew Lunn158bc062016-04-28 21:24:06 -04002121 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002122}
2123
2124int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2125 const struct switchdev_obj_port_vlan *vlan)
2126{
2127 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2128 u16 pvid, vid;
2129 int err = 0;
2130
2131 mutex_lock(&ps->smi_mutex);
2132
Andrew Lunn158bc062016-04-28 21:24:06 -04002133 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002134 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002135 goto unlock;
2136
Vivien Didelot76e398a2015-11-01 12:33:55 -05002137 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002138 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002139 if (err)
2140 goto unlock;
2141
2142 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002143 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002144 if (err)
2145 goto unlock;
2146 }
2147 }
2148
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002149unlock:
2150 mutex_unlock(&ps->smi_mutex);
2151
2152 return err;
2153}
2154
Andrew Lunn158bc062016-04-28 21:24:06 -04002155static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002156 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002157{
2158 int i, ret;
2159
2160 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002161 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002162 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002163 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002164 if (ret < 0)
2165 return ret;
2166 }
2167
2168 return 0;
2169}
2170
Andrew Lunn158bc062016-04-28 21:24:06 -04002171static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
2172 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002173{
2174 int i, ret;
2175
2176 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002177 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002178 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002179 if (ret < 0)
2180 return ret;
2181 addr[i * 2] = ret >> 8;
2182 addr[i * 2 + 1] = ret & 0xff;
2183 }
2184
2185 return 0;
2186}
2187
Andrew Lunn158bc062016-04-28 21:24:06 -04002188static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002189 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002190{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002191 int ret;
2192
Andrew Lunn158bc062016-04-28 21:24:06 -04002193 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002194 if (ret < 0)
2195 return ret;
2196
Andrew Lunn158bc062016-04-28 21:24:06 -04002197 ret = _mv88e6xxx_atu_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002198 if (ret < 0)
2199 return ret;
2200
Andrew Lunn158bc062016-04-28 21:24:06 -04002201 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002202 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002203 return ret;
2204
Andrew Lunn158bc062016-04-28 21:24:06 -04002205 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002206}
David S. Millercdf09692015-08-11 12:00:37 -07002207
Andrew Lunn158bc062016-04-28 21:24:06 -04002208static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002209 const unsigned char *addr, u16 vid,
2210 u8 state)
2211{
2212 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002213 struct mv88e6xxx_vtu_stu_entry vlan;
2214 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002215
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002216 /* Null VLAN ID corresponds to the port private database */
2217 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002218 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002219 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002220 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002221 if (err)
2222 return err;
2223
2224 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002225 entry.state = state;
2226 ether_addr_copy(entry.mac, addr);
2227 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2228 entry.trunk = false;
2229 entry.portv_trunkid = BIT(port);
2230 }
2231
Andrew Lunn158bc062016-04-28 21:24:06 -04002232 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002233}
2234
Vivien Didelot146a3202015-10-08 11:35:12 -04002235int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2236 const struct switchdev_obj_port_fdb *fdb,
2237 struct switchdev_trans *trans)
2238{
2239 /* We don't need any dynamic resource from the kernel (yet),
2240 * so skip the prepare phase.
2241 */
2242 return 0;
2243}
2244
Vivien Didelot8497aa62016-04-06 11:55:04 -04002245void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2246 const struct switchdev_obj_port_fdb *fdb,
2247 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002248{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002249 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002250 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2251 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2252 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002253
David S. Millercdf09692015-08-11 12:00:37 -07002254 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002255 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Vivien Didelot8497aa62016-04-06 11:55:04 -04002256 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002257 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002258}
2259
2260int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
Vivien Didelot8057b3e2015-10-08 11:35:14 -04002261 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002262{
2263 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2264 int ret;
2265
2266 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002267 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002268 GLOBAL_ATU_DATA_STATE_UNUSED);
2269 mutex_unlock(&ps->smi_mutex);
2270
2271 return ret;
2272}
2273
Andrew Lunn158bc062016-04-28 21:24:06 -04002274static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002275 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002276{
Vivien Didelot1d194042015-08-10 09:09:51 -04002277 struct mv88e6xxx_atu_entry next = { 0 };
2278 int ret;
2279
2280 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002281
Andrew Lunn158bc062016-04-28 21:24:06 -04002282 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002283 if (ret < 0)
2284 return ret;
2285
Andrew Lunn158bc062016-04-28 21:24:06 -04002286 ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002287 if (ret < 0)
2288 return ret;
2289
Andrew Lunn158bc062016-04-28 21:24:06 -04002290 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002291 if (ret < 0)
2292 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002293
Andrew Lunn158bc062016-04-28 21:24:06 -04002294 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002295 if (ret < 0)
2296 return ret;
2297
2298 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2299 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2300 unsigned int mask, shift;
2301
2302 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2303 next.trunk = true;
2304 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2305 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2306 } else {
2307 next.trunk = false;
2308 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2309 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2310 }
2311
2312 next.portv_trunkid = (ret & mask) >> shift;
2313 }
2314
2315 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002316 return 0;
2317}
2318
Andrew Lunn158bc062016-04-28 21:24:06 -04002319static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2320 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002321 struct switchdev_obj_port_fdb *fdb,
2322 int (*cb)(struct switchdev_obj *obj))
2323{
2324 struct mv88e6xxx_atu_entry addr = {
2325 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2326 };
2327 int err;
2328
Andrew Lunn158bc062016-04-28 21:24:06 -04002329 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002330 if (err)
2331 return err;
2332
2333 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002334 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002335 if (err)
2336 break;
2337
2338 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2339 break;
2340
2341 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2342 bool is_static = addr.state ==
2343 (is_multicast_ether_addr(addr.mac) ?
2344 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2345 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2346
2347 fdb->vid = vid;
2348 ether_addr_copy(fdb->addr, addr.mac);
2349 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2350
2351 err = cb(&fdb->obj);
2352 if (err)
2353 break;
2354 }
2355 } while (!is_broadcast_ether_addr(addr.mac));
2356
2357 return err;
2358}
2359
Vivien Didelotf33475b2015-10-22 09:34:41 -04002360int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2361 struct switchdev_obj_port_fdb *fdb,
2362 int (*cb)(struct switchdev_obj *obj))
2363{
2364 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2365 struct mv88e6xxx_vtu_stu_entry vlan = {
2366 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2367 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002368 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002369 int err;
2370
2371 mutex_lock(&ps->smi_mutex);
2372
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002373 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002374 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002375 if (err)
2376 goto unlock;
2377
Andrew Lunn158bc062016-04-28 21:24:06 -04002378 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002379 if (err)
2380 goto unlock;
2381
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002382 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002383 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002384 if (err)
2385 goto unlock;
2386
2387 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002388 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002389 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002390 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002391
2392 if (!vlan.valid)
2393 break;
2394
Andrew Lunn158bc062016-04-28 21:24:06 -04002395 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002396 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002397 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002398 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002399 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2400
2401unlock:
2402 mutex_unlock(&ps->smi_mutex);
2403
2404 return err;
2405}
2406
Vivien Didelota6692752016-02-12 12:09:39 -05002407int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2408 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002409{
Vivien Didelota6692752016-02-12 12:09:39 -05002410 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002411 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002412
2413 mutex_lock(&ps->smi_mutex);
2414
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002415 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002416 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002417
Vivien Didelot009a2b92016-04-17 13:24:01 -04002418 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002419 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002420 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002421 if (err)
2422 break;
2423 }
2424 }
2425
Vivien Didelot466dfa02016-02-26 13:16:05 -05002426 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002427
Vivien Didelot466dfa02016-02-26 13:16:05 -05002428 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002429}
2430
Vivien Didelot16bfa702016-03-13 16:21:33 -04002431void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002432{
Vivien Didelota6692752016-02-12 12:09:39 -05002433 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002434 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002435 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002436
2437 mutex_lock(&ps->smi_mutex);
2438
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002439 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002440 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002441
Vivien Didelot009a2b92016-04-17 13:24:01 -04002442 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002443 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002444 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Vivien Didelot16bfa702016-03-13 16:21:33 -04002445 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002446
Vivien Didelot466dfa02016-02-26 13:16:05 -05002447 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002448}
2449
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002450static void mv88e6xxx_bridge_work(struct work_struct *work)
2451{
2452 struct mv88e6xxx_priv_state *ps;
2453 struct dsa_switch *ds;
2454 int port;
2455
2456 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002457 ds = ps->ds;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002458
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002459 mutex_lock(&ps->smi_mutex);
2460
Vivien Didelot009a2b92016-04-17 13:24:01 -04002461 for (port = 0; port < ps->info->num_ports; ++port)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002462 if (test_and_clear_bit(port, ps->port_state_update_mask) &&
Andrew Lunn158bc062016-04-28 21:24:06 -04002463 _mv88e6xxx_port_state(ps, port, ps->ports[port].state))
2464 netdev_warn(ds->ports[port],
2465 "failed to update state to %s\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002466 mv88e6xxx_port_state_names[ps->ports[port].state]);
2467
2468 mutex_unlock(&ps->smi_mutex);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002469}
2470
Andrew Lunn158bc062016-04-28 21:24:06 -04002471static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps,
2472 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002473{
2474 int ret;
2475
Andrew Lunn158bc062016-04-28 21:24:06 -04002476 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002477 if (ret < 0)
2478 goto restore_page_0;
2479
Andrew Lunn158bc062016-04-28 21:24:06 -04002480 ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002481restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002482 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002483
2484 return ret;
2485}
2486
Andrew Lunn158bc062016-04-28 21:24:06 -04002487static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps,
2488 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002489{
2490 int ret;
2491
Andrew Lunn158bc062016-04-28 21:24:06 -04002492 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002493 if (ret < 0)
2494 goto restore_page_0;
2495
Andrew Lunn158bc062016-04-28 21:24:06 -04002496 ret = _mv88e6xxx_phy_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002497restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002498 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002499
2500 return ret;
2501}
2502
Andrew Lunn158bc062016-04-28 21:24:06 -04002503static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002504{
2505 int ret;
2506
Andrew Lunn158bc062016-04-28 21:24:06 -04002507 ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002508 MII_BMCR);
2509 if (ret < 0)
2510 return ret;
2511
2512 if (ret & BMCR_PDOWN) {
2513 ret &= ~BMCR_PDOWN;
Andrew Lunn158bc062016-04-28 21:24:06 -04002514 ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002515 PAGE_FIBER_SERDES, MII_BMCR,
2516 ret);
2517 }
2518
2519 return ret;
2520}
2521
Andrew Lunndbde9e62015-05-06 01:09:48 +02002522static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002523{
2524 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002525 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002526 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002527
2528 mutex_lock(&ps->smi_mutex);
2529
Andrew Lunn158bc062016-04-28 21:24:06 -04002530 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2531 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2532 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2533 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002534 /* MAC Forcing register: don't force link, speed,
2535 * duplex or flow control state to any particular
2536 * values on physical ports, but force the CPU port
2537 * and all DSA ports to their maximum bandwidth and
2538 * full duplex.
2539 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002540 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002541 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002542 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002543 reg |= PORT_PCS_CTRL_FORCE_LINK |
2544 PORT_PCS_CTRL_LINK_UP |
2545 PORT_PCS_CTRL_DUPLEX_FULL |
2546 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002547 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002548 reg |= PORT_PCS_CTRL_100;
2549 else
2550 reg |= PORT_PCS_CTRL_1000;
2551 } else {
2552 reg |= PORT_PCS_CTRL_UNFORCED;
2553 }
2554
Andrew Lunn158bc062016-04-28 21:24:06 -04002555 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002556 PORT_PCS_CTRL, reg);
2557 if (ret)
2558 goto abort;
2559 }
2560
2561 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2562 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2563 * tunneling, determine priority by looking at 802.1p and IP
2564 * priority fields (IP prio has precedence), and set STP state
2565 * to Forwarding.
2566 *
2567 * If this is the CPU link, use DSA or EDSA tagging depending
2568 * on which tagging mode was configured.
2569 *
2570 * If this is a link to another switch, use DSA tagging mode.
2571 *
2572 * If this is the upstream port for this switch, enable
2573 * forwarding of unknown unicasts and multicasts.
2574 */
2575 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002576 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2577 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2578 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2579 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002580 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2581 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2582 PORT_CONTROL_STATE_FORWARDING;
2583 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002584 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002585 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002586 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2587 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2588 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002589 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2590 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2591 else
2592 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002593 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2594 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002595 }
2596
Andrew Lunn158bc062016-04-28 21:24:06 -04002597 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2598 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2599 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2600 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002601 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2602 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2603 }
2604 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002605 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002606 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002607 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002608 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2609 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2610 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002611 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002612 }
2613
Andrew Lunn54d792f2015-05-06 01:09:47 +02002614 if (port == dsa_upstream_port(ds))
2615 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2616 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2617 }
2618 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002619 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002620 PORT_CONTROL, reg);
2621 if (ret)
2622 goto abort;
2623 }
2624
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002625 /* If this port is connected to a SerDes, make sure the SerDes is not
2626 * powered down.
2627 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002628 if (mv88e6xxx_6352_family(ps)) {
2629 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002630 if (ret < 0)
2631 goto abort;
2632 ret &= PORT_STATUS_CMODE_MASK;
2633 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2634 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2635 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002636 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002637 if (ret < 0)
2638 goto abort;
2639 }
2640 }
2641
Vivien Didelot8efdda42015-08-13 12:52:23 -04002642 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002643 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002644 * untagged frames on this port, do a destination address lookup on all
2645 * received packets as usual, disable ARP mirroring and don't send a
2646 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002647 */
2648 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002649 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2650 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2651 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2652 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002653 reg = PORT_CONTROL_2_MAP_DA;
2654
Andrew Lunn158bc062016-04-28 21:24:06 -04002655 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2656 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002657 reg |= PORT_CONTROL_2_JUMBO_10240;
2658
Andrew Lunn158bc062016-04-28 21:24:06 -04002659 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002660 /* Set the upstream port this port should use */
2661 reg |= dsa_upstream_port(ds);
2662 /* enable forwarding of unknown multicast addresses to
2663 * the upstream port
2664 */
2665 if (port == dsa_upstream_port(ds))
2666 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2667 }
2668
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002669 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002670
Andrew Lunn54d792f2015-05-06 01:09:47 +02002671 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002672 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002673 PORT_CONTROL_2, reg);
2674 if (ret)
2675 goto abort;
2676 }
2677
2678 /* Port Association Vector: when learning source addresses
2679 * of packets, add the address to the address database using
2680 * a port bitmap that has only the bit for this port set and
2681 * the other bits clear.
2682 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002683 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002684 /* Disable learning for CPU port */
2685 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002686 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002687
Andrew Lunn158bc062016-04-28 21:24:06 -04002688 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002689 if (ret)
2690 goto abort;
2691
2692 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002693 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002694 0x0000);
2695 if (ret)
2696 goto abort;
2697
Andrew Lunn158bc062016-04-28 21:24:06 -04002698 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2699 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2700 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002701 /* Do not limit the period of time that this port can
2702 * be paused for by the remote end or the period of
2703 * time that this port can pause the remote end.
2704 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002705 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002706 PORT_PAUSE_CTRL, 0x0000);
2707 if (ret)
2708 goto abort;
2709
2710 /* Port ATU control: disable limiting the number of
2711 * address database entries that this port is allowed
2712 * to use.
2713 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002714 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002715 PORT_ATU_CONTROL, 0x0000);
2716 /* Priority Override: disable DA, SA and VTU priority
2717 * override.
2718 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002719 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002720 PORT_PRI_OVERRIDE, 0x0000);
2721 if (ret)
2722 goto abort;
2723
2724 /* Port Ethertype: use the Ethertype DSA Ethertype
2725 * value.
2726 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002727 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002728 PORT_ETH_TYPE, ETH_P_EDSA);
2729 if (ret)
2730 goto abort;
2731 /* Tag Remap: use an identity 802.1p prio -> switch
2732 * prio mapping.
2733 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002734 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002735 PORT_TAG_REGMAP_0123, 0x3210);
2736 if (ret)
2737 goto abort;
2738
2739 /* Tag Remap 2: use an identity 802.1p prio -> switch
2740 * prio mapping.
2741 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002742 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002743 PORT_TAG_REGMAP_4567, 0x7654);
2744 if (ret)
2745 goto abort;
2746 }
2747
Andrew Lunn158bc062016-04-28 21:24:06 -04002748 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2749 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2750 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2751 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002752 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002753 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002754 PORT_RATE_CONTROL, 0x0001);
2755 if (ret)
2756 goto abort;
2757 }
2758
Guenter Roeck366f0a02015-03-26 18:36:30 -07002759 /* Port Control 1: disable trunking, disable sending
2760 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002761 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002762 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002763 if (ret)
2764 goto abort;
2765
Vivien Didelot207afda2016-04-14 14:42:09 -04002766 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002767 * database, and allow bidirectional communication between the
2768 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002769 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002770 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002771 if (ret)
2772 goto abort;
2773
Andrew Lunn158bc062016-04-28 21:24:06 -04002774 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002775 if (ret)
2776 goto abort;
2777
2778 /* Default VLAN ID and priority: don't set a default VLAN
2779 * ID, and set the default packet priority to zero.
2780 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002781 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002782 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002783abort:
2784 mutex_unlock(&ps->smi_mutex);
2785 return ret;
2786}
2787
Andrew Lunndbde9e62015-05-06 01:09:48 +02002788int mv88e6xxx_setup_ports(struct dsa_switch *ds)
2789{
2790 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2791 int ret;
2792 int i;
2793
Vivien Didelot009a2b92016-04-17 13:24:01 -04002794 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunndbde9e62015-05-06 01:09:48 +02002795 ret = mv88e6xxx_setup_port(ds, i);
2796 if (ret < 0)
2797 return ret;
2798 }
2799 return 0;
2800}
2801
Andrew Lunn158bc062016-04-28 21:24:06 -04002802int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002803{
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002804 mutex_init(&ps->smi_mutex);
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002805
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002806 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
2807
Vivien Didelotd24645b2016-05-09 13:22:41 -04002808 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
2809 mutex_init(&ps->eeprom_mutex);
2810
Vivien Didelot8c9983a2016-05-09 13:22:39 -04002811 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
2812 mv88e6xxx_ppu_state_init(ps);
2813
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002814 return 0;
2815}
2816
Andrew Lunn54d792f2015-05-06 01:09:47 +02002817int mv88e6xxx_setup_global(struct dsa_switch *ds)
2818{
2819 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002820 int err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002821 int i;
2822
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002823 mutex_lock(&ps->smi_mutex);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002824 /* Set the default address aging time to 5 minutes, and
2825 * enable address learn messages to be sent to all message
2826 * ports.
2827 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002828 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002829 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2830 if (err)
2831 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002832
2833 /* Configure the IP ToS mapping registers. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002834 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002835 if (err)
2836 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002837 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002838 if (err)
2839 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002840 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002841 if (err)
2842 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002843 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002844 if (err)
2845 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002846 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002847 if (err)
2848 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002849 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002850 if (err)
2851 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002852 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002853 if (err)
2854 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002855 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002856 if (err)
2857 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002858
2859 /* Configure the IEEE 802.1p priority mapping register. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002860 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002861 if (err)
2862 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002863
2864 /* Send all frames with destination addresses matching
2865 * 01:80:c2:00:00:0x to the CPU port.
2866 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002867 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002868 if (err)
2869 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002870
2871 /* Ignore removed tag data on doubly tagged packets, disable
2872 * flow control messages, force flow control priority to the
2873 * highest, and send all special multicast frames to the CPU
2874 * port at the highest priority.
2875 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002876 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002877 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
2878 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
2879 if (err)
2880 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002881
2882 /* Program the DSA routing table. */
2883 for (i = 0; i < 32; i++) {
2884 int nexthop = 0x1f;
2885
2886 if (ds->pd->rtable &&
2887 i != ds->index && i < ds->dst->pd->nr_chips)
2888 nexthop = ds->pd->rtable[i] & 0x1f;
2889
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002890 err = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002891 ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002892 GLOBAL2_DEVICE_MAPPING,
2893 GLOBAL2_DEVICE_MAPPING_UPDATE |
2894 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
2895 if (err)
2896 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002897 }
2898
2899 /* Clear all trunk masks. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002900 for (i = 0; i < 8; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002901 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002902 0x8000 |
2903 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
Vivien Didelot009a2b92016-04-17 13:24:01 -04002904 ((1 << ps->info->num_ports) - 1));
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002905 if (err)
2906 goto unlock;
2907 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002908
2909 /* Clear all trunk mappings. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002910 for (i = 0; i < 16; i++) {
2911 err = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002912 ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002913 GLOBAL2_TRUNK_MAPPING,
2914 GLOBAL2_TRUNK_MAPPING_UPDATE |
2915 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
2916 if (err)
2917 goto unlock;
2918 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002919
Andrew Lunn158bc062016-04-28 21:24:06 -04002920 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2921 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2922 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002923 /* Send all frames with destination addresses matching
2924 * 01:80:c2:00:00:2x to the CPU port.
2925 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002926 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002927 GLOBAL2_MGMT_EN_2X, 0xffff);
2928 if (err)
2929 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002930
2931 /* Initialise cross-chip port VLAN table to reset
2932 * defaults.
2933 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002934 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002935 GLOBAL2_PVT_ADDR, 0x9000);
2936 if (err)
2937 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002938
2939 /* Clear the priority override table. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002940 for (i = 0; i < 16; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002941 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002942 GLOBAL2_PRIO_OVERRIDE,
2943 0x8000 | (i << 8));
2944 if (err)
2945 goto unlock;
2946 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002947 }
2948
Andrew Lunn158bc062016-04-28 21:24:06 -04002949 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2950 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2951 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2952 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002953 /* Disable ingress rate limiting by resetting all
2954 * ingress rate limit registers to their initial
2955 * state.
2956 */
Vivien Didelot009a2b92016-04-17 13:24:01 -04002957 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002958 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002959 GLOBAL2_INGRESS_OP,
2960 0x9000 | (i << 8));
2961 if (err)
2962 goto unlock;
2963 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002964 }
2965
Andrew Lunndb687a52015-06-20 21:31:29 +02002966 /* Clear the statistics counters for all ports */
Andrew Lunn158bc062016-04-28 21:24:06 -04002967 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002968 GLOBAL_STATS_OP_FLUSH_ALL);
2969 if (err)
2970 goto unlock;
Andrew Lunndb687a52015-06-20 21:31:29 +02002971
2972 /* Wait for the flush to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002973 err = _mv88e6xxx_stats_wait(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002974 if (err < 0)
Vivien Didelot6b17e862015-08-13 12:52:18 -04002975 goto unlock;
2976
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002977 /* Clear all ATU entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04002978 err = _mv88e6xxx_atu_flush(ps, 0, true);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002979 if (err < 0)
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002980 goto unlock;
2981
Vivien Didelot6b17e862015-08-13 12:52:18 -04002982 /* Clear all the VTU and STU entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04002983 err = _mv88e6xxx_vtu_stu_flush(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04002984unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04002985 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02002986
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002987 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002988}
2989
Andrew Lunn158bc062016-04-28 21:24:06 -04002990int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps, bool ppu_active)
Andrew Lunn143a8302015-04-02 04:06:34 +02002991{
Andrew Lunn143a8302015-04-02 04:06:34 +02002992 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunn158bc062016-04-28 21:24:06 -04002993 struct gpio_desc *gpiod = ps->ds->pd->reset;
Andrew Lunn143a8302015-04-02 04:06:34 +02002994 unsigned long timeout;
2995 int ret;
2996 int i;
2997
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002998 mutex_lock(&ps->smi_mutex);
2999
Andrew Lunn143a8302015-04-02 04:06:34 +02003000 /* Set all ports to the disabled state. */
Vivien Didelot009a2b92016-04-17 13:24:01 -04003001 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04003002 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003003 if (ret < 0)
3004 goto unlock;
3005
Andrew Lunn158bc062016-04-28 21:24:06 -04003006 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003007 ret & 0xfffc);
3008 if (ret)
3009 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02003010 }
3011
3012 /* Wait for transmit queues to drain. */
3013 usleep_range(2000, 4000);
3014
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +01003015 /* If there is a gpio connected to the reset pin, toggle it */
3016 if (gpiod) {
3017 gpiod_set_value_cansleep(gpiod, 1);
3018 usleep_range(10000, 20000);
3019 gpiod_set_value_cansleep(gpiod, 0);
3020 usleep_range(10000, 20000);
3021 }
3022
Andrew Lunn143a8302015-04-02 04:06:34 +02003023 /* Reset the switch. Keep the PPU active if requested. The PPU
3024 * needs to be active to support indirect phy register access
3025 * through global registers 0x18 and 0x19.
3026 */
3027 if (ppu_active)
Andrew Lunn158bc062016-04-28 21:24:06 -04003028 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
Andrew Lunn143a8302015-04-02 04:06:34 +02003029 else
Andrew Lunn158bc062016-04-28 21:24:06 -04003030 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003031 if (ret)
3032 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02003033
3034 /* Wait up to one second for reset to complete. */
3035 timeout = jiffies + 1 * HZ;
3036 while (time_before(jiffies, timeout)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04003037 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003038 if (ret < 0)
3039 goto unlock;
3040
Andrew Lunn143a8302015-04-02 04:06:34 +02003041 if ((ret & is_reset) == is_reset)
3042 break;
3043 usleep_range(1000, 2000);
3044 }
3045 if (time_after(jiffies, timeout))
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003046 ret = -ETIMEDOUT;
3047 else
3048 ret = 0;
3049unlock:
3050 mutex_unlock(&ps->smi_mutex);
Andrew Lunn143a8302015-04-02 04:06:34 +02003051
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003052 return ret;
Andrew Lunn143a8302015-04-02 04:06:34 +02003053}
3054
Andrew Lunn491435852015-04-02 04:06:35 +02003055int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
3056{
3057 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3058 int ret;
3059
Andrew Lunn3898c142015-05-06 01:09:53 +02003060 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003061 ret = _mv88e6xxx_phy_page_read(ps, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02003062 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003063
Andrew Lunn491435852015-04-02 04:06:35 +02003064 return ret;
3065}
3066
3067int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
3068 int reg, int val)
3069{
3070 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3071 int ret;
3072
Andrew Lunn3898c142015-05-06 01:09:53 +02003073 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003074 ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003075 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003076
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003077 return ret;
3078}
3079
Andrew Lunn158bc062016-04-28 21:24:06 -04003080static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
3081 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003082{
Vivien Didelot009a2b92016-04-17 13:24:01 -04003083 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003084 return port;
3085 return -EINVAL;
3086}
3087
3088int
3089mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
3090{
3091 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003092 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003093 int ret;
3094
3095 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003096 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003097
Andrew Lunn3898c142015-05-06 01:09:53 +02003098 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003099
3100 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3101 ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003102 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3103 ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003104 else
3105 ret = _mv88e6xxx_phy_read(ps, addr, regnum);
3106
Andrew Lunn3898c142015-05-06 01:09:53 +02003107 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003108 return ret;
3109}
3110
3111int
3112mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
3113{
3114 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003115 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003116 int ret;
3117
3118 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003119 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003120
Andrew Lunn3898c142015-05-06 01:09:53 +02003121 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003122
3123 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3124 ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003125 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3126 ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003127 else
3128 ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
3129
Andrew Lunn3898c142015-05-06 01:09:53 +02003130 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003131 return ret;
3132}
3133
Guenter Roeckc22995c2015-07-25 09:42:28 -07003134#ifdef CONFIG_NET_DSA_HWMON
3135
3136static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3137{
3138 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3139 int ret;
3140 int val;
3141
3142 *temp = 0;
3143
3144 mutex_lock(&ps->smi_mutex);
3145
Andrew Lunn158bc062016-04-28 21:24:06 -04003146 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003147 if (ret < 0)
3148 goto error;
3149
3150 /* Enable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003151 ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003152 if (ret < 0)
3153 goto error;
3154
Andrew Lunn158bc062016-04-28 21:24:06 -04003155 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003156 if (ret < 0)
3157 goto error;
3158
3159 /* Wait for temperature to stabilize */
3160 usleep_range(10000, 12000);
3161
Andrew Lunn158bc062016-04-28 21:24:06 -04003162 val = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003163 if (val < 0) {
3164 ret = val;
3165 goto error;
3166 }
3167
3168 /* Disable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003169 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003170 if (ret < 0)
3171 goto error;
3172
3173 *temp = ((val & 0x1f) - 5) * 5;
3174
3175error:
Andrew Lunn158bc062016-04-28 21:24:06 -04003176 _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003177 mutex_unlock(&ps->smi_mutex);
3178 return ret;
3179}
3180
3181static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3182{
Andrew Lunn158bc062016-04-28 21:24:06 -04003183 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3184 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003185 int ret;
3186
3187 *temp = 0;
3188
3189 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
3190 if (ret < 0)
3191 return ret;
3192
3193 *temp = (ret & 0xff) - 25;
3194
3195 return 0;
3196}
3197
3198int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
3199{
Andrew Lunn158bc062016-04-28 21:24:06 -04003200 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3201
3202 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003203 return mv88e63xx_get_temp(ds, temp);
3204
3205 return mv88e61xx_get_temp(ds, temp);
3206}
3207
3208int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
3209{
Andrew Lunn158bc062016-04-28 21:24:06 -04003210 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3211 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003212 int ret;
3213
Andrew Lunn158bc062016-04-28 21:24:06 -04003214 if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003215 return -EOPNOTSUPP;
3216
3217 *temp = 0;
3218
3219 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3220 if (ret < 0)
3221 return ret;
3222
3223 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3224
3225 return 0;
3226}
3227
3228int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
3229{
Andrew Lunn158bc062016-04-28 21:24:06 -04003230 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3231 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003232 int ret;
3233
Andrew Lunn158bc062016-04-28 21:24:06 -04003234 if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003235 return -EOPNOTSUPP;
3236
3237 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3238 if (ret < 0)
3239 return ret;
3240 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3241 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3242 (ret & 0xe0ff) | (temp << 8));
3243}
3244
3245int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
3246{
Andrew Lunn158bc062016-04-28 21:24:06 -04003247 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3248 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003249 int ret;
3250
Andrew Lunn158bc062016-04-28 21:24:06 -04003251 if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003252 return -EOPNOTSUPP;
3253
3254 *alarm = false;
3255
3256 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3257 if (ret < 0)
3258 return ret;
3259
3260 *alarm = !!(ret & 0x40);
3261
3262 return 0;
3263}
3264#endif /* CONFIG_NET_DSA_HWMON */
3265
Vivien Didelotf6271e62016-04-17 13:23:59 -04003266static const struct mv88e6xxx_info *
3267mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003268 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003269{
Vivien Didelota439c062016-04-17 13:23:58 -04003270 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003271
Vivien Didelotb9b37712015-10-30 19:39:48 -04003272 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003273 if (table[i].prod_num == prod_num)
3274 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003275
Vivien Didelotb9b37712015-10-30 19:39:48 -04003276 return NULL;
3277}
3278
Vivien Didelot0209d142016-04-17 13:23:55 -04003279const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
3280 int sw_addr, void **priv,
Vivien Didelotf6271e62016-04-17 13:23:59 -04003281 const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003282 unsigned int num)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003283{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003284 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003285 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003286 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003287 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003288 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003289
Vivien Didelota439c062016-04-17 13:23:58 -04003290 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003291 if (!bus)
3292 return NULL;
3293
Vivien Didelota439c062016-04-17 13:23:58 -04003294 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3295 if (id < 0)
3296 return NULL;
3297
3298 prod_num = (id & 0xfff0) >> 4;
3299 rev = id & 0x000f;
3300
Vivien Didelotf6271e62016-04-17 13:23:59 -04003301 info = mv88e6xxx_lookup_info(prod_num, table, num);
3302 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003303 return NULL;
3304
Vivien Didelotf6271e62016-04-17 13:23:59 -04003305 name = info->name;
3306
Vivien Didelota439c062016-04-17 13:23:58 -04003307 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3308 if (!ps)
3309 return NULL;
3310
3311 ps->bus = bus;
3312 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003313 ps->info = info;
Vivien Didelota439c062016-04-17 13:23:58 -04003314
3315 *priv = ps;
3316
3317 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3318 prod_num, name, rev);
3319
Andrew Lunna77d43f2016-04-13 02:40:42 +02003320 return name;
3321}
3322
Ben Hutchings98e67302011-11-25 14:36:19 +00003323static int __init mv88e6xxx_init(void)
3324{
3325#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3326 register_switch_driver(&mv88e6131_switch_driver);
3327#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003328#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3329 register_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003330#endif
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07003331#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3332 register_switch_driver(&mv88e6352_switch_driver);
3333#endif
Andrew Lunn42f27252014-09-12 23:58:44 +02003334#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3335 register_switch_driver(&mv88e6171_switch_driver);
3336#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00003337 return 0;
3338}
3339module_init(mv88e6xxx_init);
3340
3341static void __exit mv88e6xxx_cleanup(void)
3342{
Andrew Lunn42f27252014-09-12 23:58:44 +02003343#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3344 unregister_switch_driver(&mv88e6171_switch_driver);
3345#endif
Vivien Didelot4212b542015-05-01 10:43:52 -04003346#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3347 unregister_switch_driver(&mv88e6352_switch_driver);
3348#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003349#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3350 unregister_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003351#endif
3352#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3353 unregister_switch_driver(&mv88e6131_switch_driver);
3354#endif
3355}
3356module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003357
3358MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3359MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3360MODULE_LICENSE("GPL");