blob: 2c8c5e1d16bce59029bad334ab004115f953e125 [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
826int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
827{
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
834int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
835{
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
Andrew Lunn158bc062016-04-28 21:24:06 -0400842static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700843{
Andrew Lunn158bc062016-04-28 21:24:06 -0400844 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +0200845 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700846}
847
Andrew Lunn158bc062016-04-28 21:24:06 -0400848static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
849 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +0100850{
851 int ret;
852
Andrew Lunn158bc062016-04-28 21:24:06 -0400853 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200854 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
855 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +0100856 if (ret < 0)
857 return ret;
858
Andrew Lunn158bc062016-04-28 21:24:06 -0400859 ret = _mv88e6xxx_phy_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +0200860 if (ret < 0)
861 return ret;
862
Andrew Lunn158bc062016-04-28 21:24:06 -0400863 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
864
865 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +0100866}
867
Andrew Lunn158bc062016-04-28 21:24:06 -0400868static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
869 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +0100870{
Andrew Lunn3898c142015-05-06 01:09:53 +0200871 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +0100872
Andrew Lunn158bc062016-04-28 21:24:06 -0400873 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +0200874 if (ret < 0)
875 return ret;
876
Andrew Lunn158bc062016-04-28 21:24:06 -0400877 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200878 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
879 regnum);
880
Andrew Lunn158bc062016-04-28 21:24:06 -0400881 return _mv88e6xxx_phy_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +0100882}
883
Guenter Roeck11b3b452015-03-06 22:23:51 -0800884int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
885{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200886 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800887 int reg;
888
Andrew Lunn3898c142015-05-06 01:09:53 +0200889 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200890
Andrew Lunn158bc062016-04-28 21:24:06 -0400891 reg = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800892 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200893 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800894
895 e->eee_enabled = !!(reg & 0x0200);
896 e->tx_lpi_enabled = !!(reg & 0x0100);
897
Andrew Lunn158bc062016-04-28 21:24:06 -0400898 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800899 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200900 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800901
Andrew Lunncca8b132015-04-02 04:06:39 +0200902 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200903 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800904
Andrew Lunn2f40c692015-04-02 04:06:37 +0200905out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200906 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200907 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800908}
909
910int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
911 struct phy_device *phydev, struct ethtool_eee *e)
912{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200913 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
914 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800915 int ret;
916
Andrew Lunn3898c142015-05-06 01:09:53 +0200917 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800918
Andrew Lunn158bc062016-04-28 21:24:06 -0400919 ret = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200920 if (ret < 0)
921 goto out;
922
923 reg = ret & ~0x0300;
924 if (e->eee_enabled)
925 reg |= 0x0200;
926 if (e->tx_lpi_enabled)
927 reg |= 0x0100;
928
Andrew Lunn158bc062016-04-28 21:24:06 -0400929 ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200930out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200931 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200932
933 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800934}
935
Andrew Lunn158bc062016-04-28 21:24:06 -0400936static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700937{
938 int ret;
939
Andrew Lunn158bc062016-04-28 21:24:06 -0400940 if (mv88e6xxx_has_fid_reg(ps)) {
941 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400942 if (ret < 0)
943 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -0400944 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -0400945 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -0400946 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -0400947 if (ret < 0)
948 return ret;
949
Andrew Lunn158bc062016-04-28 21:24:06 -0400950 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -0400951 (ret & 0xfff) |
952 ((fid << 8) & 0xf000));
953 if (ret < 0)
954 return ret;
955
956 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
957 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400958 }
959
Andrew Lunn158bc062016-04-28 21:24:06 -0400960 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700961 if (ret < 0)
962 return ret;
963
Andrew Lunn158bc062016-04-28 21:24:06 -0400964 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700965}
966
Andrew Lunn158bc062016-04-28 21:24:06 -0400967static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -0400968 struct mv88e6xxx_atu_entry *entry)
969{
970 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
971
972 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
973 unsigned int mask, shift;
974
975 if (entry->trunk) {
976 data |= GLOBAL_ATU_DATA_TRUNK;
977 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
978 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
979 } else {
980 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
981 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
982 }
983
984 data |= (entry->portv_trunkid << shift) & mask;
985 }
986
Andrew Lunn158bc062016-04-28 21:24:06 -0400987 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -0400988}
989
Andrew Lunn158bc062016-04-28 21:24:06 -0400990static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -0400991 struct mv88e6xxx_atu_entry *entry,
992 bool static_too)
993{
994 int op;
995 int err;
996
Andrew Lunn158bc062016-04-28 21:24:06 -0400997 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -0400998 if (err)
999 return err;
1000
Andrew Lunn158bc062016-04-28 21:24:06 -04001001 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001002 if (err)
1003 return err;
1004
1005 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001006 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1007 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1008 } else {
1009 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1010 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1011 }
1012
Andrew Lunn158bc062016-04-28 21:24:06 -04001013 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001014}
1015
Andrew Lunn158bc062016-04-28 21:24:06 -04001016static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1017 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001018{
1019 struct mv88e6xxx_atu_entry entry = {
1020 .fid = fid,
1021 .state = 0, /* EntryState bits must be 0 */
1022 };
1023
Andrew Lunn158bc062016-04-28 21:24:06 -04001024 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001025}
1026
Andrew Lunn158bc062016-04-28 21:24:06 -04001027static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1028 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001029{
1030 struct mv88e6xxx_atu_entry entry = {
1031 .trunk = false,
1032 .fid = fid,
1033 };
1034
1035 /* EntryState bits must be 0xF */
1036 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1037
1038 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1039 entry.portv_trunkid = (to_port & 0x0f) << 4;
1040 entry.portv_trunkid |= from_port & 0x0f;
1041
Andrew Lunn158bc062016-04-28 21:24:06 -04001042 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001043}
1044
Andrew Lunn158bc062016-04-28 21:24:06 -04001045static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1046 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001047{
1048 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001049 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001050}
1051
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001052static const char * const mv88e6xxx_port_state_names[] = {
1053 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1054 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1055 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1056 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1057};
1058
Andrew Lunn158bc062016-04-28 21:24:06 -04001059static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1060 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001061{
Andrew Lunn158bc062016-04-28 21:24:06 -04001062 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001063 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001064 u8 oldstate;
1065
Andrew Lunn158bc062016-04-28 21:24:06 -04001066 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001067 if (reg < 0)
1068 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001069
Andrew Lunncca8b132015-04-02 04:06:39 +02001070 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001071
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001072 if (oldstate != state) {
1073 /* Flush forwarding database if we're moving a port
1074 * from Learning or Forwarding state to Disabled or
1075 * Blocking or Listening state.
1076 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001077 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1078 oldstate == PORT_CONTROL_STATE_FORWARDING)
1079 && (state == PORT_CONTROL_STATE_DISABLED ||
1080 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001081 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001082 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001083 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001084 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001085
Andrew Lunncca8b132015-04-02 04:06:39 +02001086 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001087 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001088 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001089 if (ret)
1090 return ret;
1091
1092 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1093 mv88e6xxx_port_state_names[state],
1094 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001095 }
1096
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001097 return ret;
1098}
1099
Andrew Lunn158bc062016-04-28 21:24:06 -04001100static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1101 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001102{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001103 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001104 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001105 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001106 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001107 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001108 int i;
1109
1110 /* allow CPU port or DSA link(s) to send frames to every port */
1111 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1112 output_ports = mask;
1113 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001114 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001115 /* allow sending frames to every group member */
1116 if (bridge && ps->ports[i].bridge_dev == bridge)
1117 output_ports |= BIT(i);
1118
1119 /* allow sending frames to CPU port and DSA link(s) */
1120 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1121 output_ports |= BIT(i);
1122 }
1123 }
1124
1125 /* prevent frames from going back out of the port they came in on */
1126 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001127
Andrew Lunn158bc062016-04-28 21:24:06 -04001128 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001129 if (reg < 0)
1130 return reg;
1131
1132 reg &= ~mask;
1133 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001134
Andrew Lunn158bc062016-04-28 21:24:06 -04001135 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001136}
1137
Vivien Didelot43c44a92016-04-06 11:55:03 -04001138void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port, u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001139{
1140 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1141 int stp_state;
1142
1143 switch (state) {
1144 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001145 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001146 break;
1147 case BR_STATE_BLOCKING:
1148 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001149 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001150 break;
1151 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001152 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001153 break;
1154 case BR_STATE_FORWARDING:
1155 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001156 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001157 break;
1158 }
1159
Vivien Didelot43c44a92016-04-06 11:55:03 -04001160 /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled,
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001161 * so we can not update the port state directly but need to schedule it.
1162 */
Vivien Didelotd715fa62016-02-12 12:09:38 -05001163 ps->ports[port].state = stp_state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001164 set_bit(port, ps->port_state_update_mask);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001165 schedule_work(&ps->bridge_work);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001166}
1167
Andrew Lunn158bc062016-04-28 21:24:06 -04001168static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1169 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001170{
Andrew Lunn158bc062016-04-28 21:24:06 -04001171 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001172 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001173 int ret;
1174
Andrew Lunn158bc062016-04-28 21:24:06 -04001175 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001176 if (ret < 0)
1177 return ret;
1178
Vivien Didelot5da96032016-03-07 18:24:39 -05001179 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1180
1181 if (new) {
1182 ret &= ~PORT_DEFAULT_VLAN_MASK;
1183 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1184
Andrew Lunn158bc062016-04-28 21:24:06 -04001185 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001186 PORT_DEFAULT_VLAN, ret);
1187 if (ret < 0)
1188 return ret;
1189
1190 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1191 pvid);
1192 }
1193
1194 if (old)
1195 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001196
1197 return 0;
1198}
1199
Andrew Lunn158bc062016-04-28 21:24:06 -04001200static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1201 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001202{
Andrew Lunn158bc062016-04-28 21:24:06 -04001203 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001204}
1205
Andrew Lunn158bc062016-04-28 21:24:06 -04001206static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1207 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001208{
Andrew Lunn158bc062016-04-28 21:24:06 -04001209 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001210}
1211
Andrew Lunn158bc062016-04-28 21:24:06 -04001212static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001213{
Andrew Lunn158bc062016-04-28 21:24:06 -04001214 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001215 GLOBAL_VTU_OP_BUSY);
1216}
1217
Andrew Lunn158bc062016-04-28 21:24:06 -04001218static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001219{
1220 int ret;
1221
Andrew Lunn158bc062016-04-28 21:24:06 -04001222 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001223 if (ret < 0)
1224 return ret;
1225
Andrew Lunn158bc062016-04-28 21:24:06 -04001226 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001227}
1228
Andrew Lunn158bc062016-04-28 21:24:06 -04001229static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001230{
1231 int ret;
1232
Andrew Lunn158bc062016-04-28 21:24:06 -04001233 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001234 if (ret < 0)
1235 return ret;
1236
Andrew Lunn158bc062016-04-28 21:24:06 -04001237 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001238}
1239
Andrew Lunn158bc062016-04-28 21:24:06 -04001240static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001241 struct mv88e6xxx_vtu_stu_entry *entry,
1242 unsigned int nibble_offset)
1243{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001244 u16 regs[3];
1245 int i;
1246 int ret;
1247
1248 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001249 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001250 GLOBAL_VTU_DATA_0_3 + i);
1251 if (ret < 0)
1252 return ret;
1253
1254 regs[i] = ret;
1255 }
1256
Vivien Didelot009a2b92016-04-17 13:24:01 -04001257 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001258 unsigned int shift = (i % 4) * 4 + nibble_offset;
1259 u16 reg = regs[i / 4];
1260
1261 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1262 }
1263
1264 return 0;
1265}
1266
Andrew Lunn158bc062016-04-28 21:24:06 -04001267static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001268 struct mv88e6xxx_vtu_stu_entry *entry,
1269 unsigned int nibble_offset)
1270{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001271 u16 regs[3] = { 0 };
1272 int i;
1273 int ret;
1274
Vivien Didelot009a2b92016-04-17 13:24:01 -04001275 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001276 unsigned int shift = (i % 4) * 4 + nibble_offset;
1277 u8 data = entry->data[i];
1278
1279 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1280 }
1281
1282 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001283 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001284 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1285 if (ret < 0)
1286 return ret;
1287 }
1288
1289 return 0;
1290}
1291
Andrew Lunn158bc062016-04-28 21:24:06 -04001292static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001293{
Andrew Lunn158bc062016-04-28 21:24:06 -04001294 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001295 vid & GLOBAL_VTU_VID_MASK);
1296}
1297
Andrew Lunn158bc062016-04-28 21:24:06 -04001298static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001299 struct mv88e6xxx_vtu_stu_entry *entry)
1300{
1301 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1302 int ret;
1303
Andrew Lunn158bc062016-04-28 21:24:06 -04001304 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001305 if (ret < 0)
1306 return ret;
1307
Andrew Lunn158bc062016-04-28 21:24:06 -04001308 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001309 if (ret < 0)
1310 return ret;
1311
Andrew Lunn158bc062016-04-28 21:24:06 -04001312 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001313 if (ret < 0)
1314 return ret;
1315
1316 next.vid = ret & GLOBAL_VTU_VID_MASK;
1317 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1318
1319 if (next.valid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001320 ret = _mv88e6xxx_vtu_stu_data_read(ps, &next, 0);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001321 if (ret < 0)
1322 return ret;
1323
Andrew Lunn158bc062016-04-28 21:24:06 -04001324 if (mv88e6xxx_has_fid_reg(ps)) {
1325 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001326 GLOBAL_VTU_FID);
1327 if (ret < 0)
1328 return ret;
1329
1330 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001331 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001332 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1333 * VTU DBNum[3:0] are located in VTU Operation 3:0
1334 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001335 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001336 GLOBAL_VTU_OP);
1337 if (ret < 0)
1338 return ret;
1339
1340 next.fid = (ret & 0xf00) >> 4;
1341 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001342 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001343
Andrew Lunn158bc062016-04-28 21:24:06 -04001344 if (mv88e6xxx_has_stu(ps)) {
1345 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001346 GLOBAL_VTU_SID);
1347 if (ret < 0)
1348 return ret;
1349
1350 next.sid = ret & GLOBAL_VTU_SID_MASK;
1351 }
1352 }
1353
1354 *entry = next;
1355 return 0;
1356}
1357
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001358int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1359 struct switchdev_obj_port_vlan *vlan,
1360 int (*cb)(struct switchdev_obj *obj))
1361{
1362 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1363 struct mv88e6xxx_vtu_stu_entry next;
1364 u16 pvid;
1365 int err;
1366
1367 mutex_lock(&ps->smi_mutex);
1368
Andrew Lunn158bc062016-04-28 21:24:06 -04001369 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001370 if (err)
1371 goto unlock;
1372
Andrew Lunn158bc062016-04-28 21:24:06 -04001373 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001374 if (err)
1375 goto unlock;
1376
1377 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001378 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001379 if (err)
1380 break;
1381
1382 if (!next.valid)
1383 break;
1384
1385 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1386 continue;
1387
1388 /* reinit and dump this VLAN obj */
1389 vlan->vid_begin = vlan->vid_end = next.vid;
1390 vlan->flags = 0;
1391
1392 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1393 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1394
1395 if (next.vid == pvid)
1396 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1397
1398 err = cb(&vlan->obj);
1399 if (err)
1400 break;
1401 } while (next.vid < GLOBAL_VTU_VID_MASK);
1402
1403unlock:
1404 mutex_unlock(&ps->smi_mutex);
1405
1406 return err;
1407}
1408
Andrew Lunn158bc062016-04-28 21:24:06 -04001409static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001410 struct mv88e6xxx_vtu_stu_entry *entry)
1411{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001412 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001413 u16 reg = 0;
1414 int ret;
1415
Andrew Lunn158bc062016-04-28 21:24:06 -04001416 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001417 if (ret < 0)
1418 return ret;
1419
1420 if (!entry->valid)
1421 goto loadpurge;
1422
1423 /* Write port member tags */
Andrew Lunn158bc062016-04-28 21:24:06 -04001424 ret = _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001425 if (ret < 0)
1426 return ret;
1427
Andrew Lunn158bc062016-04-28 21:24:06 -04001428 if (mv88e6xxx_has_stu(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001429 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001430 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001431 if (ret < 0)
1432 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001433 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001434
Andrew Lunn158bc062016-04-28 21:24:06 -04001435 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001436 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001437 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001438 if (ret < 0)
1439 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001440 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001441 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1442 * VTU DBNum[3:0] are located in VTU Operation 3:0
1443 */
1444 op |= (entry->fid & 0xf0) << 8;
1445 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001446 }
1447
1448 reg = GLOBAL_VTU_VID_VALID;
1449loadpurge:
1450 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001451 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001452 if (ret < 0)
1453 return ret;
1454
Andrew Lunn158bc062016-04-28 21:24:06 -04001455 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001456}
1457
Andrew Lunn158bc062016-04-28 21:24:06 -04001458static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001459 struct mv88e6xxx_vtu_stu_entry *entry)
1460{
1461 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1462 int ret;
1463
Andrew Lunn158bc062016-04-28 21:24:06 -04001464 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001465 if (ret < 0)
1466 return ret;
1467
Andrew Lunn158bc062016-04-28 21:24:06 -04001468 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001469 sid & GLOBAL_VTU_SID_MASK);
1470 if (ret < 0)
1471 return ret;
1472
Andrew Lunn158bc062016-04-28 21:24:06 -04001473 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001474 if (ret < 0)
1475 return ret;
1476
Andrew Lunn158bc062016-04-28 21:24:06 -04001477 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001478 if (ret < 0)
1479 return ret;
1480
1481 next.sid = ret & GLOBAL_VTU_SID_MASK;
1482
Andrew Lunn158bc062016-04-28 21:24:06 -04001483 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001484 if (ret < 0)
1485 return ret;
1486
1487 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1488
1489 if (next.valid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001490 ret = _mv88e6xxx_vtu_stu_data_read(ps, &next, 2);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001491 if (ret < 0)
1492 return ret;
1493 }
1494
1495 *entry = next;
1496 return 0;
1497}
1498
Andrew Lunn158bc062016-04-28 21:24:06 -04001499static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001500 struct mv88e6xxx_vtu_stu_entry *entry)
1501{
1502 u16 reg = 0;
1503 int ret;
1504
Andrew Lunn158bc062016-04-28 21:24:06 -04001505 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001506 if (ret < 0)
1507 return ret;
1508
1509 if (!entry->valid)
1510 goto loadpurge;
1511
1512 /* Write port states */
Andrew Lunn158bc062016-04-28 21:24:06 -04001513 ret = _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001514 if (ret < 0)
1515 return ret;
1516
1517 reg = GLOBAL_VTU_VID_VALID;
1518loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001519 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001520 if (ret < 0)
1521 return ret;
1522
1523 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001524 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001525 if (ret < 0)
1526 return ret;
1527
Andrew Lunn158bc062016-04-28 21:24:06 -04001528 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001529}
1530
Andrew Lunn158bc062016-04-28 21:24:06 -04001531static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1532 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001533{
Andrew Lunn158bc062016-04-28 21:24:06 -04001534 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001535 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001536 u16 fid;
1537 int ret;
1538
Andrew Lunn158bc062016-04-28 21:24:06 -04001539 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001540 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001541 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001542 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001543 else
1544 return -EOPNOTSUPP;
1545
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001546 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001547 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001548 if (ret < 0)
1549 return ret;
1550
1551 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1552
1553 if (new) {
1554 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1555 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1556
Andrew Lunn158bc062016-04-28 21:24:06 -04001557 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001558 ret);
1559 if (ret < 0)
1560 return ret;
1561 }
1562
1563 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001564 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001565 if (ret < 0)
1566 return ret;
1567
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001568 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001569
1570 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001571 ret &= ~upper_mask;
1572 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001573
Andrew Lunn158bc062016-04-28 21:24:06 -04001574 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001575 ret);
1576 if (ret < 0)
1577 return ret;
1578
1579 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1580 }
1581
1582 if (old)
1583 *old = fid;
1584
1585 return 0;
1586}
1587
Andrew Lunn158bc062016-04-28 21:24:06 -04001588static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1589 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001590{
Andrew Lunn158bc062016-04-28 21:24:06 -04001591 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001592}
1593
Andrew Lunn158bc062016-04-28 21:24:06 -04001594static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1595 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001596{
Andrew Lunn158bc062016-04-28 21:24:06 -04001597 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001598}
1599
Andrew Lunn158bc062016-04-28 21:24:06 -04001600static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001601{
1602 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1603 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001604 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001605
1606 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1607
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001608 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001609 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001610 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001611 if (err)
1612 return err;
1613
1614 set_bit(*fid, fid_bitmap);
1615 }
1616
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001617 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001618 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001619 if (err)
1620 return err;
1621
1622 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001623 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001624 if (err)
1625 return err;
1626
1627 if (!vlan.valid)
1628 break;
1629
1630 set_bit(vlan.fid, fid_bitmap);
1631 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1632
1633 /* The reset value 0x000 is used to indicate that multiple address
1634 * databases are not needed. Return the next positive available.
1635 */
1636 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001637 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001638 return -ENOSPC;
1639
1640 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001641 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001642}
1643
Andrew Lunn158bc062016-04-28 21:24:06 -04001644static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001645 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001646{
Andrew Lunn158bc062016-04-28 21:24:06 -04001647 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001648 struct mv88e6xxx_vtu_stu_entry vlan = {
1649 .valid = true,
1650 .vid = vid,
1651 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001652 int i, err;
1653
Andrew Lunn158bc062016-04-28 21:24:06 -04001654 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001655 if (err)
1656 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001657
Vivien Didelot3d131f02015-11-03 10:52:52 -05001658 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001659 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001660 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1661 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1662 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001663
Andrew Lunn158bc062016-04-28 21:24:06 -04001664 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1665 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001666 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001667
1668 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1669 * implemented, only one STU entry is needed to cover all VTU
1670 * entries. Thus, validate the SID 0.
1671 */
1672 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001673 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001674 if (err)
1675 return err;
1676
1677 if (vstp.sid != vlan.sid || !vstp.valid) {
1678 memset(&vstp, 0, sizeof(vstp));
1679 vstp.valid = true;
1680 vstp.sid = vlan.sid;
1681
Andrew Lunn158bc062016-04-28 21:24:06 -04001682 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001683 if (err)
1684 return err;
1685 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001686 }
1687
1688 *entry = vlan;
1689 return 0;
1690}
1691
Andrew Lunn158bc062016-04-28 21:24:06 -04001692static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001693 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1694{
1695 int err;
1696
1697 if (!vid)
1698 return -EINVAL;
1699
Andrew Lunn158bc062016-04-28 21:24:06 -04001700 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001701 if (err)
1702 return err;
1703
Andrew Lunn158bc062016-04-28 21:24:06 -04001704 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001705 if (err)
1706 return err;
1707
1708 if (entry->vid != vid || !entry->valid) {
1709 if (!creat)
1710 return -EOPNOTSUPP;
1711 /* -ENOENT would've been more appropriate, but switchdev expects
1712 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1713 */
1714
Andrew Lunn158bc062016-04-28 21:24:06 -04001715 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001716 }
1717
1718 return err;
1719}
1720
Vivien Didelotda9c3592016-02-12 12:09:40 -05001721static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1722 u16 vid_begin, u16 vid_end)
1723{
1724 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1725 struct mv88e6xxx_vtu_stu_entry vlan;
1726 int i, err;
1727
1728 if (!vid_begin)
1729 return -EOPNOTSUPP;
1730
1731 mutex_lock(&ps->smi_mutex);
1732
Andrew Lunn158bc062016-04-28 21:24:06 -04001733 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001734 if (err)
1735 goto unlock;
1736
1737 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001738 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001739 if (err)
1740 goto unlock;
1741
1742 if (!vlan.valid)
1743 break;
1744
1745 if (vlan.vid > vid_end)
1746 break;
1747
Vivien Didelot009a2b92016-04-17 13:24:01 -04001748 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05001749 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
1750 continue;
1751
1752 if (vlan.data[i] ==
1753 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1754 continue;
1755
1756 if (ps->ports[i].bridge_dev ==
1757 ps->ports[port].bridge_dev)
1758 break; /* same bridge, check next VLAN */
1759
1760 netdev_warn(ds->ports[port],
1761 "hardware VLAN %d already used by %s\n",
1762 vlan.vid,
1763 netdev_name(ps->ports[i].bridge_dev));
1764 err = -EOPNOTSUPP;
1765 goto unlock;
1766 }
1767 } while (vlan.vid < vid_end);
1768
1769unlock:
1770 mutex_unlock(&ps->smi_mutex);
1771
1772 return err;
1773}
1774
Vivien Didelot214cdb92016-02-26 13:16:08 -05001775static const char * const mv88e6xxx_port_8021q_mode_names[] = {
1776 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
1777 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
1778 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
1779 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
1780};
1781
1782int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
1783 bool vlan_filtering)
1784{
1785 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1786 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
1787 PORT_CONTROL_2_8021Q_DISABLED;
1788 int ret;
1789
1790 mutex_lock(&ps->smi_mutex);
1791
Andrew Lunn158bc062016-04-28 21:24:06 -04001792 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05001793 if (ret < 0)
1794 goto unlock;
1795
1796 old = ret & PORT_CONTROL_2_8021Q_MASK;
1797
Vivien Didelot5220ef12016-03-07 18:24:52 -05001798 if (new != old) {
1799 ret &= ~PORT_CONTROL_2_8021Q_MASK;
1800 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001801
Andrew Lunn158bc062016-04-28 21:24:06 -04001802 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05001803 ret);
1804 if (ret < 0)
1805 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001806
Vivien Didelot5220ef12016-03-07 18:24:52 -05001807 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
1808 mv88e6xxx_port_8021q_mode_names[new],
1809 mv88e6xxx_port_8021q_mode_names[old]);
1810 }
1811
1812 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05001813unlock:
1814 mutex_unlock(&ps->smi_mutex);
1815
1816 return ret;
1817}
1818
Vivien Didelot76e398a2015-11-01 12:33:55 -05001819int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1820 const struct switchdev_obj_port_vlan *vlan,
1821 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001822{
Vivien Didelotda9c3592016-02-12 12:09:40 -05001823 int err;
1824
Vivien Didelotda9c3592016-02-12 12:09:40 -05001825 /* If the requested port doesn't belong to the same bridge as the VLAN
1826 * members, do not support it (yet) and fallback to software VLAN.
1827 */
1828 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
1829 vlan->vid_end);
1830 if (err)
1831 return err;
1832
Vivien Didelot76e398a2015-11-01 12:33:55 -05001833 /* We don't need any dynamic resource from the kernel (yet),
1834 * so skip the prepare phase.
1835 */
1836 return 0;
1837}
1838
Andrew Lunn158bc062016-04-28 21:24:06 -04001839static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
1840 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001841{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001842 struct mv88e6xxx_vtu_stu_entry vlan;
1843 int err;
1844
Andrew Lunn158bc062016-04-28 21:24:06 -04001845 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001846 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001847 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001848
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001849 vlan.data[port] = untagged ?
1850 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1851 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1852
Andrew Lunn158bc062016-04-28 21:24:06 -04001853 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001854}
1855
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001856void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1857 const struct switchdev_obj_port_vlan *vlan,
1858 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001859{
1860 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1861 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1862 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1863 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001864
1865 mutex_lock(&ps->smi_mutex);
1866
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001867 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04001868 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001869 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
1870 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05001871
Andrew Lunn158bc062016-04-28 21:24:06 -04001872 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04001873 netdev_err(ds->ports[port], "failed to set PVID %d\n",
1874 vlan->vid_end);
1875
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001876 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001877}
1878
Andrew Lunn158bc062016-04-28 21:24:06 -04001879static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
1880 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001881{
Andrew Lunn158bc062016-04-28 21:24:06 -04001882 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001883 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001884 int i, err;
1885
Andrew Lunn158bc062016-04-28 21:24:06 -04001886 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001887 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001888 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001889
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001890 /* Tell switchdev if this VLAN is handled in software */
1891 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05001892 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001893
1894 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1895
1896 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001897 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001898 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05001899 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001900 continue;
1901
1902 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001903 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001904 break;
1905 }
1906 }
1907
Andrew Lunn158bc062016-04-28 21:24:06 -04001908 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001909 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001910 return err;
1911
Andrew Lunn158bc062016-04-28 21:24:06 -04001912 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001913}
1914
1915int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1916 const struct switchdev_obj_port_vlan *vlan)
1917{
1918 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1919 u16 pvid, vid;
1920 int err = 0;
1921
1922 mutex_lock(&ps->smi_mutex);
1923
Andrew Lunn158bc062016-04-28 21:24:06 -04001924 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001925 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001926 goto unlock;
1927
Vivien Didelot76e398a2015-11-01 12:33:55 -05001928 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001929 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001930 if (err)
1931 goto unlock;
1932
1933 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001934 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001935 if (err)
1936 goto unlock;
1937 }
1938 }
1939
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001940unlock:
1941 mutex_unlock(&ps->smi_mutex);
1942
1943 return err;
1944}
1945
Andrew Lunn158bc062016-04-28 21:24:06 -04001946static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04001947 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001948{
1949 int i, ret;
1950
1951 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001952 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04001953 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02001954 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001955 if (ret < 0)
1956 return ret;
1957 }
1958
1959 return 0;
1960}
1961
Andrew Lunn158bc062016-04-28 21:24:06 -04001962static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
1963 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001964{
1965 int i, ret;
1966
1967 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001968 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001969 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001970 if (ret < 0)
1971 return ret;
1972 addr[i * 2] = ret >> 8;
1973 addr[i * 2 + 1] = ret & 0xff;
1974 }
1975
1976 return 0;
1977}
1978
Andrew Lunn158bc062016-04-28 21:24:06 -04001979static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04001980 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001981{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001982 int ret;
1983
Andrew Lunn158bc062016-04-28 21:24:06 -04001984 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001985 if (ret < 0)
1986 return ret;
1987
Andrew Lunn158bc062016-04-28 21:24:06 -04001988 ret = _mv88e6xxx_atu_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001989 if (ret < 0)
1990 return ret;
1991
Andrew Lunn158bc062016-04-28 21:24:06 -04001992 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04001993 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001994 return ret;
1995
Andrew Lunn158bc062016-04-28 21:24:06 -04001996 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04001997}
David S. Millercdf09692015-08-11 12:00:37 -07001998
Andrew Lunn158bc062016-04-28 21:24:06 -04001999static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002000 const unsigned char *addr, u16 vid,
2001 u8 state)
2002{
2003 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002004 struct mv88e6xxx_vtu_stu_entry vlan;
2005 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002006
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002007 /* Null VLAN ID corresponds to the port private database */
2008 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002009 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002010 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002011 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002012 if (err)
2013 return err;
2014
2015 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002016 entry.state = state;
2017 ether_addr_copy(entry.mac, addr);
2018 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2019 entry.trunk = false;
2020 entry.portv_trunkid = BIT(port);
2021 }
2022
Andrew Lunn158bc062016-04-28 21:24:06 -04002023 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002024}
2025
Vivien Didelot146a3202015-10-08 11:35:12 -04002026int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2027 const struct switchdev_obj_port_fdb *fdb,
2028 struct switchdev_trans *trans)
2029{
2030 /* We don't need any dynamic resource from the kernel (yet),
2031 * so skip the prepare phase.
2032 */
2033 return 0;
2034}
2035
Vivien Didelot8497aa62016-04-06 11:55:04 -04002036void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2037 const struct switchdev_obj_port_fdb *fdb,
2038 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002039{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002040 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002041 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2042 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2043 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002044
David S. Millercdf09692015-08-11 12:00:37 -07002045 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002046 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Vivien Didelot8497aa62016-04-06 11:55:04 -04002047 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002048 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002049}
2050
2051int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
Vivien Didelot8057b3e2015-10-08 11:35:14 -04002052 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002053{
2054 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2055 int ret;
2056
2057 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002058 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002059 GLOBAL_ATU_DATA_STATE_UNUSED);
2060 mutex_unlock(&ps->smi_mutex);
2061
2062 return ret;
2063}
2064
Andrew Lunn158bc062016-04-28 21:24:06 -04002065static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002066 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002067{
Vivien Didelot1d194042015-08-10 09:09:51 -04002068 struct mv88e6xxx_atu_entry next = { 0 };
2069 int ret;
2070
2071 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002072
Andrew Lunn158bc062016-04-28 21:24:06 -04002073 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002074 if (ret < 0)
2075 return ret;
2076
Andrew Lunn158bc062016-04-28 21:24:06 -04002077 ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002078 if (ret < 0)
2079 return ret;
2080
Andrew Lunn158bc062016-04-28 21:24:06 -04002081 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002082 if (ret < 0)
2083 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002084
Andrew Lunn158bc062016-04-28 21:24:06 -04002085 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002086 if (ret < 0)
2087 return ret;
2088
2089 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2090 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2091 unsigned int mask, shift;
2092
2093 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2094 next.trunk = true;
2095 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2096 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2097 } else {
2098 next.trunk = false;
2099 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2100 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2101 }
2102
2103 next.portv_trunkid = (ret & mask) >> shift;
2104 }
2105
2106 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002107 return 0;
2108}
2109
Andrew Lunn158bc062016-04-28 21:24:06 -04002110static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2111 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002112 struct switchdev_obj_port_fdb *fdb,
2113 int (*cb)(struct switchdev_obj *obj))
2114{
2115 struct mv88e6xxx_atu_entry addr = {
2116 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2117 };
2118 int err;
2119
Andrew Lunn158bc062016-04-28 21:24:06 -04002120 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002121 if (err)
2122 return err;
2123
2124 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002125 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002126 if (err)
2127 break;
2128
2129 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2130 break;
2131
2132 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2133 bool is_static = addr.state ==
2134 (is_multicast_ether_addr(addr.mac) ?
2135 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2136 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2137
2138 fdb->vid = vid;
2139 ether_addr_copy(fdb->addr, addr.mac);
2140 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2141
2142 err = cb(&fdb->obj);
2143 if (err)
2144 break;
2145 }
2146 } while (!is_broadcast_ether_addr(addr.mac));
2147
2148 return err;
2149}
2150
Vivien Didelotf33475b2015-10-22 09:34:41 -04002151int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2152 struct switchdev_obj_port_fdb *fdb,
2153 int (*cb)(struct switchdev_obj *obj))
2154{
2155 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2156 struct mv88e6xxx_vtu_stu_entry vlan = {
2157 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2158 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002159 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002160 int err;
2161
2162 mutex_lock(&ps->smi_mutex);
2163
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002164 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002165 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002166 if (err)
2167 goto unlock;
2168
Andrew Lunn158bc062016-04-28 21:24:06 -04002169 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002170 if (err)
2171 goto unlock;
2172
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002173 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002174 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002175 if (err)
2176 goto unlock;
2177
2178 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002179 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002180 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002181 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002182
2183 if (!vlan.valid)
2184 break;
2185
Andrew Lunn158bc062016-04-28 21:24:06 -04002186 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002187 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002188 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002189 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002190 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2191
2192unlock:
2193 mutex_unlock(&ps->smi_mutex);
2194
2195 return err;
2196}
2197
Vivien Didelota6692752016-02-12 12:09:39 -05002198int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2199 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002200{
Vivien Didelota6692752016-02-12 12:09:39 -05002201 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002202 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002203
2204 mutex_lock(&ps->smi_mutex);
2205
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002206 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002207 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002208
Vivien Didelot009a2b92016-04-17 13:24:01 -04002209 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002210 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002211 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002212 if (err)
2213 break;
2214 }
2215 }
2216
Vivien Didelot466dfa02016-02-26 13:16:05 -05002217 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002218
Vivien Didelot466dfa02016-02-26 13:16:05 -05002219 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002220}
2221
Vivien Didelot16bfa702016-03-13 16:21:33 -04002222void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002223{
Vivien Didelota6692752016-02-12 12:09:39 -05002224 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002225 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002226 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002227
2228 mutex_lock(&ps->smi_mutex);
2229
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002230 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002231 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002232
Vivien Didelot009a2b92016-04-17 13:24:01 -04002233 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002234 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002235 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Vivien Didelot16bfa702016-03-13 16:21:33 -04002236 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002237
Vivien Didelot466dfa02016-02-26 13:16:05 -05002238 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002239}
2240
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002241static void mv88e6xxx_bridge_work(struct work_struct *work)
2242{
2243 struct mv88e6xxx_priv_state *ps;
2244 struct dsa_switch *ds;
2245 int port;
2246
2247 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002248 ds = ps->ds;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002249
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002250 mutex_lock(&ps->smi_mutex);
2251
Vivien Didelot009a2b92016-04-17 13:24:01 -04002252 for (port = 0; port < ps->info->num_ports; ++port)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002253 if (test_and_clear_bit(port, ps->port_state_update_mask) &&
Andrew Lunn158bc062016-04-28 21:24:06 -04002254 _mv88e6xxx_port_state(ps, port, ps->ports[port].state))
2255 netdev_warn(ds->ports[port],
2256 "failed to update state to %s\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002257 mv88e6xxx_port_state_names[ps->ports[port].state]);
2258
2259 mutex_unlock(&ps->smi_mutex);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002260}
2261
Andrew Lunn158bc062016-04-28 21:24:06 -04002262static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps,
2263 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002264{
2265 int ret;
2266
Andrew Lunn158bc062016-04-28 21:24:06 -04002267 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002268 if (ret < 0)
2269 goto restore_page_0;
2270
Andrew Lunn158bc062016-04-28 21:24:06 -04002271 ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002272restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002273 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002274
2275 return ret;
2276}
2277
Andrew Lunn158bc062016-04-28 21:24:06 -04002278static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps,
2279 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002280{
2281 int ret;
2282
Andrew Lunn158bc062016-04-28 21:24:06 -04002283 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002284 if (ret < 0)
2285 goto restore_page_0;
2286
Andrew Lunn158bc062016-04-28 21:24:06 -04002287 ret = _mv88e6xxx_phy_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002288restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002289 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002290
2291 return ret;
2292}
2293
Andrew Lunn158bc062016-04-28 21:24:06 -04002294static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002295{
2296 int ret;
2297
Andrew Lunn158bc062016-04-28 21:24:06 -04002298 ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002299 MII_BMCR);
2300 if (ret < 0)
2301 return ret;
2302
2303 if (ret & BMCR_PDOWN) {
2304 ret &= ~BMCR_PDOWN;
Andrew Lunn158bc062016-04-28 21:24:06 -04002305 ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002306 PAGE_FIBER_SERDES, MII_BMCR,
2307 ret);
2308 }
2309
2310 return ret;
2311}
2312
Andrew Lunndbde9e62015-05-06 01:09:48 +02002313static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002314{
2315 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002316 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002317 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002318
2319 mutex_lock(&ps->smi_mutex);
2320
Andrew Lunn158bc062016-04-28 21:24:06 -04002321 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2322 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2323 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2324 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002325 /* MAC Forcing register: don't force link, speed,
2326 * duplex or flow control state to any particular
2327 * values on physical ports, but force the CPU port
2328 * and all DSA ports to their maximum bandwidth and
2329 * full duplex.
2330 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002331 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002332 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002333 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002334 reg |= PORT_PCS_CTRL_FORCE_LINK |
2335 PORT_PCS_CTRL_LINK_UP |
2336 PORT_PCS_CTRL_DUPLEX_FULL |
2337 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002338 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002339 reg |= PORT_PCS_CTRL_100;
2340 else
2341 reg |= PORT_PCS_CTRL_1000;
2342 } else {
2343 reg |= PORT_PCS_CTRL_UNFORCED;
2344 }
2345
Andrew Lunn158bc062016-04-28 21:24:06 -04002346 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002347 PORT_PCS_CTRL, reg);
2348 if (ret)
2349 goto abort;
2350 }
2351
2352 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2353 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2354 * tunneling, determine priority by looking at 802.1p and IP
2355 * priority fields (IP prio has precedence), and set STP state
2356 * to Forwarding.
2357 *
2358 * If this is the CPU link, use DSA or EDSA tagging depending
2359 * on which tagging mode was configured.
2360 *
2361 * If this is a link to another switch, use DSA tagging mode.
2362 *
2363 * If this is the upstream port for this switch, enable
2364 * forwarding of unknown unicasts and multicasts.
2365 */
2366 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002367 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2368 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2369 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2370 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002371 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2372 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2373 PORT_CONTROL_STATE_FORWARDING;
2374 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002375 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002376 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002377 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2378 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2379 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002380 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2381 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2382 else
2383 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002384 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2385 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002386 }
2387
Andrew Lunn158bc062016-04-28 21:24:06 -04002388 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2389 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2390 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2391 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002392 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2393 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2394 }
2395 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002396 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002397 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002398 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002399 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2400 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2401 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002402 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002403 }
2404
Andrew Lunn54d792f2015-05-06 01:09:47 +02002405 if (port == dsa_upstream_port(ds))
2406 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2407 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2408 }
2409 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002410 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002411 PORT_CONTROL, reg);
2412 if (ret)
2413 goto abort;
2414 }
2415
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002416 /* If this port is connected to a SerDes, make sure the SerDes is not
2417 * powered down.
2418 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002419 if (mv88e6xxx_6352_family(ps)) {
2420 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002421 if (ret < 0)
2422 goto abort;
2423 ret &= PORT_STATUS_CMODE_MASK;
2424 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2425 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2426 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002427 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002428 if (ret < 0)
2429 goto abort;
2430 }
2431 }
2432
Vivien Didelot8efdda42015-08-13 12:52:23 -04002433 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002434 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002435 * untagged frames on this port, do a destination address lookup on all
2436 * received packets as usual, disable ARP mirroring and don't send a
2437 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002438 */
2439 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002440 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2441 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2442 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2443 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002444 reg = PORT_CONTROL_2_MAP_DA;
2445
Andrew Lunn158bc062016-04-28 21:24:06 -04002446 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2447 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002448 reg |= PORT_CONTROL_2_JUMBO_10240;
2449
Andrew Lunn158bc062016-04-28 21:24:06 -04002450 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002451 /* Set the upstream port this port should use */
2452 reg |= dsa_upstream_port(ds);
2453 /* enable forwarding of unknown multicast addresses to
2454 * the upstream port
2455 */
2456 if (port == dsa_upstream_port(ds))
2457 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2458 }
2459
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002460 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002461
Andrew Lunn54d792f2015-05-06 01:09:47 +02002462 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002463 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002464 PORT_CONTROL_2, reg);
2465 if (ret)
2466 goto abort;
2467 }
2468
2469 /* Port Association Vector: when learning source addresses
2470 * of packets, add the address to the address database using
2471 * a port bitmap that has only the bit for this port set and
2472 * the other bits clear.
2473 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002474 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002475 /* Disable learning for CPU port */
2476 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002477 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002478
Andrew Lunn158bc062016-04-28 21:24:06 -04002479 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002480 if (ret)
2481 goto abort;
2482
2483 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002484 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002485 0x0000);
2486 if (ret)
2487 goto abort;
2488
Andrew Lunn158bc062016-04-28 21:24:06 -04002489 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2490 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2491 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002492 /* Do not limit the period of time that this port can
2493 * be paused for by the remote end or the period of
2494 * time that this port can pause the remote end.
2495 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002496 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002497 PORT_PAUSE_CTRL, 0x0000);
2498 if (ret)
2499 goto abort;
2500
2501 /* Port ATU control: disable limiting the number of
2502 * address database entries that this port is allowed
2503 * to use.
2504 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002505 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002506 PORT_ATU_CONTROL, 0x0000);
2507 /* Priority Override: disable DA, SA and VTU priority
2508 * override.
2509 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002510 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002511 PORT_PRI_OVERRIDE, 0x0000);
2512 if (ret)
2513 goto abort;
2514
2515 /* Port Ethertype: use the Ethertype DSA Ethertype
2516 * value.
2517 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002518 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002519 PORT_ETH_TYPE, ETH_P_EDSA);
2520 if (ret)
2521 goto abort;
2522 /* Tag Remap: use an identity 802.1p prio -> switch
2523 * prio mapping.
2524 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002525 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002526 PORT_TAG_REGMAP_0123, 0x3210);
2527 if (ret)
2528 goto abort;
2529
2530 /* Tag Remap 2: use an identity 802.1p prio -> switch
2531 * prio mapping.
2532 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002533 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002534 PORT_TAG_REGMAP_4567, 0x7654);
2535 if (ret)
2536 goto abort;
2537 }
2538
Andrew Lunn158bc062016-04-28 21:24:06 -04002539 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2540 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2541 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2542 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002543 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002544 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002545 PORT_RATE_CONTROL, 0x0001);
2546 if (ret)
2547 goto abort;
2548 }
2549
Guenter Roeck366f0a02015-03-26 18:36:30 -07002550 /* Port Control 1: disable trunking, disable sending
2551 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002552 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002553 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002554 if (ret)
2555 goto abort;
2556
Vivien Didelot207afda2016-04-14 14:42:09 -04002557 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002558 * database, and allow bidirectional communication between the
2559 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002560 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002561 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002562 if (ret)
2563 goto abort;
2564
Andrew Lunn158bc062016-04-28 21:24:06 -04002565 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002566 if (ret)
2567 goto abort;
2568
2569 /* Default VLAN ID and priority: don't set a default VLAN
2570 * ID, and set the default packet priority to zero.
2571 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002572 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002573 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002574abort:
2575 mutex_unlock(&ps->smi_mutex);
2576 return ret;
2577}
2578
Andrew Lunndbde9e62015-05-06 01:09:48 +02002579int mv88e6xxx_setup_ports(struct dsa_switch *ds)
2580{
2581 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2582 int ret;
2583 int i;
2584
Vivien Didelot009a2b92016-04-17 13:24:01 -04002585 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunndbde9e62015-05-06 01:09:48 +02002586 ret = mv88e6xxx_setup_port(ds, i);
2587 if (ret < 0)
2588 return ret;
2589 }
2590 return 0;
2591}
2592
Andrew Lunn158bc062016-04-28 21:24:06 -04002593int mv88e6xxx_setup_common(struct mv88e6xxx_priv_state *ps)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002594{
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002595 mutex_init(&ps->smi_mutex);
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002596
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002597 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
2598
Vivien Didelot8c9983a2016-05-09 13:22:39 -04002599 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
2600 mv88e6xxx_ppu_state_init(ps);
2601
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002602 return 0;
2603}
2604
Andrew Lunn54d792f2015-05-06 01:09:47 +02002605int mv88e6xxx_setup_global(struct dsa_switch *ds)
2606{
2607 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002608 int err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002609 int i;
2610
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002611 mutex_lock(&ps->smi_mutex);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002612 /* Set the default address aging time to 5 minutes, and
2613 * enable address learn messages to be sent to all message
2614 * ports.
2615 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002616 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002617 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2618 if (err)
2619 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002620
2621 /* Configure the IP ToS mapping registers. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002622 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002623 if (err)
2624 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002625 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002626 if (err)
2627 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002628 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002629 if (err)
2630 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002631 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002632 if (err)
2633 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002634 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002635 if (err)
2636 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002637 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002638 if (err)
2639 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002640 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002641 if (err)
2642 goto unlock;
Andrew Lunn158bc062016-04-28 21:24:06 -04002643 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002644 if (err)
2645 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002646
2647 /* Configure the IEEE 802.1p priority mapping register. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002648 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002649 if (err)
2650 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002651
2652 /* Send all frames with destination addresses matching
2653 * 01:80:c2:00:00:0x to the CPU port.
2654 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002655 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002656 if (err)
2657 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002658
2659 /* Ignore removed tag data on doubly tagged packets, disable
2660 * flow control messages, force flow control priority to the
2661 * highest, and send all special multicast frames to the CPU
2662 * port at the highest priority.
2663 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002664 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002665 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
2666 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
2667 if (err)
2668 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002669
2670 /* Program the DSA routing table. */
2671 for (i = 0; i < 32; i++) {
2672 int nexthop = 0x1f;
2673
2674 if (ds->pd->rtable &&
2675 i != ds->index && i < ds->dst->pd->nr_chips)
2676 nexthop = ds->pd->rtable[i] & 0x1f;
2677
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002678 err = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002679 ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002680 GLOBAL2_DEVICE_MAPPING,
2681 GLOBAL2_DEVICE_MAPPING_UPDATE |
2682 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
2683 if (err)
2684 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002685 }
2686
2687 /* Clear all trunk masks. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002688 for (i = 0; i < 8; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002689 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002690 0x8000 |
2691 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
Vivien Didelot009a2b92016-04-17 13:24:01 -04002692 ((1 << ps->info->num_ports) - 1));
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002693 if (err)
2694 goto unlock;
2695 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002696
2697 /* Clear all trunk mappings. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002698 for (i = 0; i < 16; i++) {
2699 err = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002700 ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002701 GLOBAL2_TRUNK_MAPPING,
2702 GLOBAL2_TRUNK_MAPPING_UPDATE |
2703 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
2704 if (err)
2705 goto unlock;
2706 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002707
Andrew Lunn158bc062016-04-28 21:24:06 -04002708 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2709 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2710 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002711 /* Send all frames with destination addresses matching
2712 * 01:80:c2:00:00:2x to the CPU port.
2713 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002714 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002715 GLOBAL2_MGMT_EN_2X, 0xffff);
2716 if (err)
2717 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002718
2719 /* Initialise cross-chip port VLAN table to reset
2720 * defaults.
2721 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002722 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002723 GLOBAL2_PVT_ADDR, 0x9000);
2724 if (err)
2725 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002726
2727 /* Clear the priority override table. */
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002728 for (i = 0; i < 16; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002729 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002730 GLOBAL2_PRIO_OVERRIDE,
2731 0x8000 | (i << 8));
2732 if (err)
2733 goto unlock;
2734 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002735 }
2736
Andrew Lunn158bc062016-04-28 21:24:06 -04002737 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2738 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2739 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2740 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002741 /* Disable ingress rate limiting by resetting all
2742 * ingress rate limit registers to their initial
2743 * state.
2744 */
Vivien Didelot009a2b92016-04-17 13:24:01 -04002745 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002746 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002747 GLOBAL2_INGRESS_OP,
2748 0x9000 | (i << 8));
2749 if (err)
2750 goto unlock;
2751 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02002752 }
2753
Andrew Lunndb687a52015-06-20 21:31:29 +02002754 /* Clear the statistics counters for all ports */
Andrew Lunn158bc062016-04-28 21:24:06 -04002755 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002756 GLOBAL_STATS_OP_FLUSH_ALL);
2757 if (err)
2758 goto unlock;
Andrew Lunndb687a52015-06-20 21:31:29 +02002759
2760 /* Wait for the flush to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002761 err = _mv88e6xxx_stats_wait(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002762 if (err < 0)
Vivien Didelot6b17e862015-08-13 12:52:18 -04002763 goto unlock;
2764
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002765 /* Clear all ATU entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04002766 err = _mv88e6xxx_atu_flush(ps, 0, true);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002767 if (err < 0)
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002768 goto unlock;
2769
Vivien Didelot6b17e862015-08-13 12:52:18 -04002770 /* Clear all the VTU and STU entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04002771 err = _mv88e6xxx_vtu_stu_flush(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04002772unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04002773 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02002774
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002775 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002776}
2777
Andrew Lunn158bc062016-04-28 21:24:06 -04002778int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps, bool ppu_active)
Andrew Lunn143a8302015-04-02 04:06:34 +02002779{
Andrew Lunn143a8302015-04-02 04:06:34 +02002780 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunn158bc062016-04-28 21:24:06 -04002781 struct gpio_desc *gpiod = ps->ds->pd->reset;
Andrew Lunn143a8302015-04-02 04:06:34 +02002782 unsigned long timeout;
2783 int ret;
2784 int i;
2785
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002786 mutex_lock(&ps->smi_mutex);
2787
Andrew Lunn143a8302015-04-02 04:06:34 +02002788 /* Set all ports to the disabled state. */
Vivien Didelot009a2b92016-04-17 13:24:01 -04002789 for (i = 0; i < ps->info->num_ports; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002790 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002791 if (ret < 0)
2792 goto unlock;
2793
Andrew Lunn158bc062016-04-28 21:24:06 -04002794 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002795 ret & 0xfffc);
2796 if (ret)
2797 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02002798 }
2799
2800 /* Wait for transmit queues to drain. */
2801 usleep_range(2000, 4000);
2802
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +01002803 /* If there is a gpio connected to the reset pin, toggle it */
2804 if (gpiod) {
2805 gpiod_set_value_cansleep(gpiod, 1);
2806 usleep_range(10000, 20000);
2807 gpiod_set_value_cansleep(gpiod, 0);
2808 usleep_range(10000, 20000);
2809 }
2810
Andrew Lunn143a8302015-04-02 04:06:34 +02002811 /* Reset the switch. Keep the PPU active if requested. The PPU
2812 * needs to be active to support indirect phy register access
2813 * through global registers 0x18 and 0x19.
2814 */
2815 if (ppu_active)
Andrew Lunn158bc062016-04-28 21:24:06 -04002816 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
Andrew Lunn143a8302015-04-02 04:06:34 +02002817 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002818 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002819 if (ret)
2820 goto unlock;
Andrew Lunn143a8302015-04-02 04:06:34 +02002821
2822 /* Wait up to one second for reset to complete. */
2823 timeout = jiffies + 1 * HZ;
2824 while (time_before(jiffies, timeout)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002825 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002826 if (ret < 0)
2827 goto unlock;
2828
Andrew Lunn143a8302015-04-02 04:06:34 +02002829 if ((ret & is_reset) == is_reset)
2830 break;
2831 usleep_range(1000, 2000);
2832 }
2833 if (time_after(jiffies, timeout))
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002834 ret = -ETIMEDOUT;
2835 else
2836 ret = 0;
2837unlock:
2838 mutex_unlock(&ps->smi_mutex);
Andrew Lunn143a8302015-04-02 04:06:34 +02002839
Andrew Lunn48ace4e2016-04-14 23:47:12 +02002840 return ret;
Andrew Lunn143a8302015-04-02 04:06:34 +02002841}
2842
Andrew Lunn491435852015-04-02 04:06:35 +02002843int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
2844{
2845 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2846 int ret;
2847
Andrew Lunn3898c142015-05-06 01:09:53 +02002848 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002849 ret = _mv88e6xxx_phy_page_read(ps, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02002850 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002851
Andrew Lunn491435852015-04-02 04:06:35 +02002852 return ret;
2853}
2854
2855int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
2856 int reg, int val)
2857{
2858 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2859 int ret;
2860
Andrew Lunn3898c142015-05-06 01:09:53 +02002861 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002862 ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002863 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002864
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002865 return ret;
2866}
2867
Andrew Lunn158bc062016-04-28 21:24:06 -04002868static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
2869 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002870{
Vivien Didelot009a2b92016-04-17 13:24:01 -04002871 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002872 return port;
2873 return -EINVAL;
2874}
2875
2876int
2877mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
2878{
2879 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04002880 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002881 int ret;
2882
2883 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002884 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002885
Andrew Lunn3898c142015-05-06 01:09:53 +02002886 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04002887
2888 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
2889 ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04002890 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
2891 ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04002892 else
2893 ret = _mv88e6xxx_phy_read(ps, addr, regnum);
2894
Andrew Lunn3898c142015-05-06 01:09:53 +02002895 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002896 return ret;
2897}
2898
2899int
2900mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
2901{
2902 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04002903 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002904 int ret;
2905
2906 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002907 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002908
Andrew Lunn3898c142015-05-06 01:09:53 +02002909 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04002910
2911 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
2912 ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04002913 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
2914 ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04002915 else
2916 ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
2917
Andrew Lunn3898c142015-05-06 01:09:53 +02002918 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002919 return ret;
2920}
2921
Guenter Roeckc22995c2015-07-25 09:42:28 -07002922#ifdef CONFIG_NET_DSA_HWMON
2923
2924static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
2925{
2926 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2927 int ret;
2928 int val;
2929
2930 *temp = 0;
2931
2932 mutex_lock(&ps->smi_mutex);
2933
Andrew Lunn158bc062016-04-28 21:24:06 -04002934 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07002935 if (ret < 0)
2936 goto error;
2937
2938 /* Enable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04002939 ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07002940 if (ret < 0)
2941 goto error;
2942
Andrew Lunn158bc062016-04-28 21:24:06 -04002943 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07002944 if (ret < 0)
2945 goto error;
2946
2947 /* Wait for temperature to stabilize */
2948 usleep_range(10000, 12000);
2949
Andrew Lunn158bc062016-04-28 21:24:06 -04002950 val = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07002951 if (val < 0) {
2952 ret = val;
2953 goto error;
2954 }
2955
2956 /* Disable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04002957 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07002958 if (ret < 0)
2959 goto error;
2960
2961 *temp = ((val & 0x1f) - 5) * 5;
2962
2963error:
Andrew Lunn158bc062016-04-28 21:24:06 -04002964 _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0);
Guenter Roeckc22995c2015-07-25 09:42:28 -07002965 mutex_unlock(&ps->smi_mutex);
2966 return ret;
2967}
2968
2969static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
2970{
Andrew Lunn158bc062016-04-28 21:24:06 -04002971 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2972 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07002973 int ret;
2974
2975 *temp = 0;
2976
2977 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
2978 if (ret < 0)
2979 return ret;
2980
2981 *temp = (ret & 0xff) - 25;
2982
2983 return 0;
2984}
2985
2986int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
2987{
Andrew Lunn158bc062016-04-28 21:24:06 -04002988 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2989
2990 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07002991 return mv88e63xx_get_temp(ds, temp);
2992
2993 return mv88e61xx_get_temp(ds, temp);
2994}
2995
2996int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
2997{
Andrew Lunn158bc062016-04-28 21:24:06 -04002998 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2999 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003000 int ret;
3001
Andrew Lunn158bc062016-04-28 21:24:06 -04003002 if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003003 return -EOPNOTSUPP;
3004
3005 *temp = 0;
3006
3007 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3008 if (ret < 0)
3009 return ret;
3010
3011 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3012
3013 return 0;
3014}
3015
3016int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
3017{
Andrew Lunn158bc062016-04-28 21:24:06 -04003018 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3019 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003020 int ret;
3021
Andrew Lunn158bc062016-04-28 21:24:06 -04003022 if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003023 return -EOPNOTSUPP;
3024
3025 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3026 if (ret < 0)
3027 return ret;
3028 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3029 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3030 (ret & 0xe0ff) | (temp << 8));
3031}
3032
3033int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
3034{
Andrew Lunn158bc062016-04-28 21:24:06 -04003035 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3036 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003037 int ret;
3038
Andrew Lunn158bc062016-04-28 21:24:06 -04003039 if (!mv88e6xxx_6320_family(ps) && !mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003040 return -EOPNOTSUPP;
3041
3042 *alarm = false;
3043
3044 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3045 if (ret < 0)
3046 return ret;
3047
3048 *alarm = !!(ret & 0x40);
3049
3050 return 0;
3051}
3052#endif /* CONFIG_NET_DSA_HWMON */
3053
Vivien Didelotf6271e62016-04-17 13:23:59 -04003054static const struct mv88e6xxx_info *
3055mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003056 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003057{
Vivien Didelota439c062016-04-17 13:23:58 -04003058 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003059
Vivien Didelotb9b37712015-10-30 19:39:48 -04003060 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003061 if (table[i].prod_num == prod_num)
3062 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003063
Vivien Didelotb9b37712015-10-30 19:39:48 -04003064 return NULL;
3065}
3066
Vivien Didelot0209d142016-04-17 13:23:55 -04003067const char *mv88e6xxx_drv_probe(struct device *dsa_dev, struct device *host_dev,
3068 int sw_addr, void **priv,
Vivien Didelotf6271e62016-04-17 13:23:59 -04003069 const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003070 unsigned int num)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003071{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003072 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003073 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003074 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003075 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003076 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003077
Vivien Didelota439c062016-04-17 13:23:58 -04003078 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003079 if (!bus)
3080 return NULL;
3081
Vivien Didelota439c062016-04-17 13:23:58 -04003082 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3083 if (id < 0)
3084 return NULL;
3085
3086 prod_num = (id & 0xfff0) >> 4;
3087 rev = id & 0x000f;
3088
Vivien Didelotf6271e62016-04-17 13:23:59 -04003089 info = mv88e6xxx_lookup_info(prod_num, table, num);
3090 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003091 return NULL;
3092
Vivien Didelotf6271e62016-04-17 13:23:59 -04003093 name = info->name;
3094
Vivien Didelota439c062016-04-17 13:23:58 -04003095 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3096 if (!ps)
3097 return NULL;
3098
3099 ps->bus = bus;
3100 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003101 ps->info = info;
Vivien Didelota439c062016-04-17 13:23:58 -04003102
3103 *priv = ps;
3104
3105 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3106 prod_num, name, rev);
3107
Andrew Lunna77d43f2016-04-13 02:40:42 +02003108 return name;
3109}
3110
Ben Hutchings98e67302011-11-25 14:36:19 +00003111static int __init mv88e6xxx_init(void)
3112{
3113#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3114 register_switch_driver(&mv88e6131_switch_driver);
3115#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003116#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3117 register_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003118#endif
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07003119#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3120 register_switch_driver(&mv88e6352_switch_driver);
3121#endif
Andrew Lunn42f27252014-09-12 23:58:44 +02003122#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3123 register_switch_driver(&mv88e6171_switch_driver);
3124#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00003125 return 0;
3126}
3127module_init(mv88e6xxx_init);
3128
3129static void __exit mv88e6xxx_cleanup(void)
3130{
Andrew Lunn42f27252014-09-12 23:58:44 +02003131#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
3132 unregister_switch_driver(&mv88e6171_switch_driver);
3133#endif
Vivien Didelot4212b542015-05-01 10:43:52 -04003134#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
3135 unregister_switch_driver(&mv88e6352_switch_driver);
3136#endif
Andrew Lunnca3dfa52016-03-12 00:01:36 +01003137#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123)
3138 unregister_switch_driver(&mv88e6123_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003139#endif
3140#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
3141 unregister_switch_driver(&mv88e6131_switch_driver);
3142#endif
3143}
3144module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003145
3146MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3147MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3148MODULE_LICENSE("GPL");