blob: a3f0e7ec406766733df313f0bc1f7c73d24461e3 [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 *
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02008 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
9 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15
Barry Grussling19b2f972013-01-08 16:05:54 +000016#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070017#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020018#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070019#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000020#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000021#include <linux/list.h>
Andrew Lunn14c7b3c2016-05-10 23:27:21 +020022#include <linux/mdio.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000023#include <linux/module.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000024#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010025#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000026#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000027#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040028#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000029#include "mv88e6xxx.h"
30
Andrew Lunn158bc062016-04-28 21:24:06 -040031static void assert_smi_lock(struct mv88e6xxx_priv_state *ps)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040032{
Vivien Didelot3996a4f2015-10-30 18:56:45 -040033 if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
Andrew Lunn158bc062016-04-28 21:24:06 -040034 dev_err(ps->dev, "SMI lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040035 dump_stack();
36 }
37}
38
Barry Grussling3675c8d2013-01-08 16:05:53 +000039/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000040 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
41 * will be directly accessible on some {device address,register address}
42 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
43 * will only respond to SMI transactions to that specific address, and
44 * an indirect addressing mechanism needs to be used to access its
45 * registers.
46 */
47static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
48{
49 int ret;
50 int i;
51
52 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020053 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000054 if (ret < 0)
55 return ret;
56
Andrew Lunncca8b132015-04-02 04:06:39 +020057 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000058 return 0;
59 }
60
61 return -ETIMEDOUT;
62}
63
Vivien Didelotb9b37712015-10-30 19:39:48 -040064static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
65 int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000066{
67 int ret;
68
69 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020070 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000071
Barry Grussling3675c8d2013-01-08 16:05:53 +000072 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000073 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
74 if (ret < 0)
75 return ret;
76
Barry Grussling3675c8d2013-01-08 16:05:53 +000077 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020078 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
79 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000080 if (ret < 0)
81 return ret;
82
Barry Grussling3675c8d2013-01-08 16:05:53 +000083 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000084 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
85 if (ret < 0)
86 return ret;
87
Barry Grussling3675c8d2013-01-08 16:05:53 +000088 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020089 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000090 if (ret < 0)
91 return ret;
92
93 return ret & 0xffff;
94}
95
Andrew Lunn158bc062016-04-28 21:24:06 -040096static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
97 int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000098{
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000099 int ret;
100
Andrew Lunn158bc062016-04-28 21:24:06 -0400101 assert_smi_lock(ps);
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400102
Andrew Lunna77d43f2016-04-13 02:40:42 +0200103 ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500104 if (ret < 0)
105 return ret;
106
Andrew Lunn158bc062016-04-28 21:24:06 -0400107 dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500108 addr, reg, ret);
109
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000110 return ret;
111}
112
Andrew Lunn158bc062016-04-28 21:24:06 -0400113int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700114{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700115 int ret;
116
117 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400118 ret = _mv88e6xxx_reg_read(ps, addr, reg);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700119 mutex_unlock(&ps->smi_mutex);
120
121 return ret;
122}
123
Vivien Didelotb9b37712015-10-30 19:39:48 -0400124static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
125 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000126{
127 int ret;
128
129 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200130 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000131
Barry Grussling3675c8d2013-01-08 16:05:53 +0000132 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000133 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
134 if (ret < 0)
135 return ret;
136
Barry Grussling3675c8d2013-01-08 16:05:53 +0000137 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200138 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000139 if (ret < 0)
140 return ret;
141
Barry Grussling3675c8d2013-01-08 16:05:53 +0000142 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200143 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
144 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000145 if (ret < 0)
146 return ret;
147
Barry Grussling3675c8d2013-01-08 16:05:53 +0000148 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000149 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
150 if (ret < 0)
151 return ret;
152
153 return 0;
154}
155
Andrew Lunn158bc062016-04-28 21:24:06 -0400156static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
157 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000158{
Andrew Lunn158bc062016-04-28 21:24:06 -0400159 assert_smi_lock(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000160
Andrew Lunn158bc062016-04-28 21:24:06 -0400161 dev_dbg(ps->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500162 addr, reg, val);
163
Andrew Lunna77d43f2016-04-13 02:40:42 +0200164 return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700165}
166
Andrew Lunn158bc062016-04-28 21:24:06 -0400167int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
168 int reg, u16 val)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700169{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700170 int ret;
171
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000172 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400173 ret = _mv88e6xxx_reg_write(ps, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000174 mutex_unlock(&ps->smi_mutex);
175
176 return ret;
177}
178
Vivien Didelot1d13a062016-05-09 13:22:43 -0400179static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000180{
Andrew Lunn158bc062016-04-28 21:24:06 -0400181 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200182 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000183
Andrew Lunn158bc062016-04-28 21:24:06 -0400184 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_01,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200185 (addr[0] << 8) | addr[1]);
186 if (err)
187 return err;
188
Andrew Lunn158bc062016-04-28 21:24:06 -0400189 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_23,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200190 (addr[2] << 8) | addr[3]);
191 if (err)
192 return err;
193
Andrew Lunn158bc062016-04-28 21:24:06 -0400194 return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_45,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200195 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000196}
197
Vivien Didelot1d13a062016-05-09 13:22:43 -0400198static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000199{
Andrew Lunn158bc062016-04-28 21:24:06 -0400200 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000201 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200202 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000203
204 for (i = 0; i < 6; i++) {
205 int j;
206
Barry Grussling3675c8d2013-01-08 16:05:53 +0000207 /* Write the MAC address byte. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400208 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200209 GLOBAL2_SWITCH_MAC_BUSY |
210 (i << 8) | addr[i]);
211 if (ret)
212 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000213
Barry Grussling3675c8d2013-01-08 16:05:53 +0000214 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000215 for (j = 0; j < 16; j++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400216 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200217 GLOBAL2_SWITCH_MAC);
218 if (ret < 0)
219 return ret;
220
Andrew Lunncca8b132015-04-02 04:06:39 +0200221 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000222 break;
223 }
224 if (j == 16)
225 return -ETIMEDOUT;
226 }
227
228 return 0;
229}
230
Vivien Didelot1d13a062016-05-09 13:22:43 -0400231int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
232{
233 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
234
235 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC))
236 return mv88e6xxx_set_addr_indirect(ds, addr);
237 else
238 return mv88e6xxx_set_addr_direct(ds, addr);
239}
240
Andrew Lunn158bc062016-04-28 21:24:06 -0400241static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr,
242 int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000243{
244 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400245 return _mv88e6xxx_reg_read(ps, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000246 return 0xffff;
247}
248
Andrew Lunn158bc062016-04-28 21:24:06 -0400249static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr,
250 int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000251{
252 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400253 return _mv88e6xxx_reg_write(ps, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000254 return 0;
255}
256
Andrew Lunn158bc062016-04-28 21:24:06 -0400257static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000258{
259 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000260 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000261
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400262 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200263 if (ret < 0)
264 return ret;
265
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400266 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
267 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200268 if (ret)
269 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000270
Barry Grussling19b2f972013-01-08 16:05:54 +0000271 timeout = jiffies + 1 * HZ;
272 while (time_before(jiffies, timeout)) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400273 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200274 if (ret < 0)
275 return ret;
276
Barry Grussling19b2f972013-01-08 16:05:54 +0000277 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200278 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
279 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000280 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000281 }
282
283 return -ETIMEDOUT;
284}
285
Andrew Lunn158bc062016-04-28 21:24:06 -0400286static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000287{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200288 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000289 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000290
Andrew Lunn158bc062016-04-28 21:24:06 -0400291 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200292 if (ret < 0)
293 return ret;
294
Andrew Lunn158bc062016-04-28 21:24:06 -0400295 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200296 ret | GLOBAL_CONTROL_PPU_ENABLE);
297 if (err)
298 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000299
Barry Grussling19b2f972013-01-08 16:05:54 +0000300 timeout = jiffies + 1 * HZ;
301 while (time_before(jiffies, timeout)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400302 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200303 if (ret < 0)
304 return ret;
305
Barry Grussling19b2f972013-01-08 16:05:54 +0000306 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200307 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
308 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000309 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000310 }
311
312 return -ETIMEDOUT;
313}
314
315static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
316{
317 struct mv88e6xxx_priv_state *ps;
318
319 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
320 if (mutex_trylock(&ps->ppu_mutex)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400321 if (mv88e6xxx_ppu_enable(ps) == 0)
Barry Grussling85686582013-01-08 16:05:56 +0000322 ps->ppu_disabled = 0;
323 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000324 }
325}
326
327static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
328{
329 struct mv88e6xxx_priv_state *ps = (void *)_ps;
330
331 schedule_work(&ps->ppu_work);
332}
333
Andrew Lunn158bc062016-04-28 21:24:06 -0400334static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000335{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000336 int ret;
337
338 mutex_lock(&ps->ppu_mutex);
339
Barry Grussling3675c8d2013-01-08 16:05:53 +0000340 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000341 * we can access the PHY registers. If it was already
342 * disabled, cancel the timer that is going to re-enable
343 * it.
344 */
345 if (!ps->ppu_disabled) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400346 ret = mv88e6xxx_ppu_disable(ps);
Barry Grussling85686582013-01-08 16:05:56 +0000347 if (ret < 0) {
348 mutex_unlock(&ps->ppu_mutex);
349 return ret;
350 }
351 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000352 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000353 del_timer(&ps->ppu_timer);
354 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000355 }
356
357 return ret;
358}
359
Andrew Lunn158bc062016-04-28 21:24:06 -0400360static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000361{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000362 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000363 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
364 mutex_unlock(&ps->ppu_mutex);
365}
366
Andrew Lunn158bc062016-04-28 21:24:06 -0400367void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000368{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000369 mutex_init(&ps->ppu_mutex);
370 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
371 init_timer(&ps->ppu_timer);
372 ps->ppu_timer.data = (unsigned long)ps;
373 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
374}
375
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400376static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
377 int regnum)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000378{
379 int ret;
380
Andrew Lunn158bc062016-04-28 21:24:06 -0400381 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000382 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400383 ret = _mv88e6xxx_reg_read(ps, addr, regnum);
Andrew Lunn158bc062016-04-28 21:24:06 -0400384 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000385 }
386
387 return ret;
388}
389
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400390static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
391 int regnum, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000392{
393 int ret;
394
Andrew Lunn158bc062016-04-28 21:24:06 -0400395 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000396 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400397 ret = _mv88e6xxx_reg_write(ps, addr, regnum, val);
Andrew Lunn158bc062016-04-28 21:24:06 -0400398 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000399 }
400
401 return ret;
402}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000403
Andrew Lunn158bc062016-04-28 21:24:06 -0400404static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200405{
Vivien Didelot22356472016-04-17 13:24:00 -0400406 return ps->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200407}
408
Andrew Lunn158bc062016-04-28 21:24:06 -0400409static bool mv88e6xxx_6095_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200410{
Vivien Didelot22356472016-04-17 13:24:00 -0400411 return ps->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200412}
413
Andrew Lunn158bc062016-04-28 21:24:06 -0400414static bool mv88e6xxx_6097_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200415{
Vivien Didelot22356472016-04-17 13:24:00 -0400416 return ps->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200417}
418
Andrew Lunn158bc062016-04-28 21:24:06 -0400419static bool mv88e6xxx_6165_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200420{
Vivien Didelot22356472016-04-17 13:24:00 -0400421 return ps->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200422}
423
Andrew Lunn158bc062016-04-28 21:24:06 -0400424static bool mv88e6xxx_6185_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200425{
Vivien Didelot22356472016-04-17 13:24:00 -0400426 return ps->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200427}
428
Andrew Lunn158bc062016-04-28 21:24:06 -0400429static bool mv88e6xxx_6320_family(struct mv88e6xxx_priv_state *ps)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700430{
Vivien Didelot22356472016-04-17 13:24:00 -0400431 return ps->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700432}
433
Andrew Lunn158bc062016-04-28 21:24:06 -0400434static bool mv88e6xxx_6351_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200435{
Vivien Didelot22356472016-04-17 13:24:00 -0400436 return ps->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200437}
438
Andrew Lunn158bc062016-04-28 21:24:06 -0400439static bool mv88e6xxx_6352_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200440{
Vivien Didelot22356472016-04-17 13:24:00 -0400441 return ps->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200442}
443
Andrew Lunn158bc062016-04-28 21:24:06 -0400444static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_priv_state *ps)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400445{
Vivien Didelotcd5a2c82016-04-17 13:24:02 -0400446 return ps->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400447}
448
Andrew Lunn158bc062016-04-28 21:24:06 -0400449static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400450{
451 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Andrew Lunn158bc062016-04-28 21:24:06 -0400452 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
453 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400454 return true;
455
456 return false;
457}
458
Andrew Lunndea87022015-08-31 15:56:47 +0200459/* We expect the switch to perform auto negotiation if there is a real
460 * phy. However, in the case of a fixed link phy, we force the port
461 * settings from the fixed link settings.
462 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400463static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
464 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200465{
466 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200467 u32 reg;
468 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200469
470 if (!phy_is_pseudo_fixed_link(phydev))
471 return;
472
473 mutex_lock(&ps->smi_mutex);
474
Andrew Lunn158bc062016-04-28 21:24:06 -0400475 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200476 if (ret < 0)
477 goto out;
478
479 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
480 PORT_PCS_CTRL_FORCE_LINK |
481 PORT_PCS_CTRL_DUPLEX_FULL |
482 PORT_PCS_CTRL_FORCE_DUPLEX |
483 PORT_PCS_CTRL_UNFORCED);
484
485 reg |= PORT_PCS_CTRL_FORCE_LINK;
486 if (phydev->link)
487 reg |= PORT_PCS_CTRL_LINK_UP;
488
Andrew Lunn158bc062016-04-28 21:24:06 -0400489 if (mv88e6xxx_6065_family(ps) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200490 goto out;
491
492 switch (phydev->speed) {
493 case SPEED_1000:
494 reg |= PORT_PCS_CTRL_1000;
495 break;
496 case SPEED_100:
497 reg |= PORT_PCS_CTRL_100;
498 break;
499 case SPEED_10:
500 reg |= PORT_PCS_CTRL_10;
501 break;
502 default:
503 pr_info("Unknown speed");
504 goto out;
505 }
506
507 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
508 if (phydev->duplex == DUPLEX_FULL)
509 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
510
Andrew Lunn158bc062016-04-28 21:24:06 -0400511 if ((mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps)) &&
Vivien Didelot009a2b92016-04-17 13:24:01 -0400512 (port >= ps->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200513 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
514 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
515 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
516 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
517 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
518 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
519 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
520 }
Andrew Lunn158bc062016-04-28 21:24:06 -0400521 _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200522
523out:
524 mutex_unlock(&ps->smi_mutex);
525}
526
Andrew Lunn158bc062016-04-28 21:24:06 -0400527static int _mv88e6xxx_stats_wait(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000528{
529 int ret;
530 int i;
531
532 for (i = 0; i < 10; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400533 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200534 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000535 return 0;
536 }
537
538 return -ETIMEDOUT;
539}
540
Andrew Lunn158bc062016-04-28 21:24:06 -0400541static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_priv_state *ps,
542 int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000543{
544 int ret;
545
Andrew Lunn158bc062016-04-28 21:24:06 -0400546 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200547 port = (port + 1) << 5;
548
Barry Grussling3675c8d2013-01-08 16:05:53 +0000549 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400550 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200551 GLOBAL_STATS_OP_CAPTURE_PORT |
552 GLOBAL_STATS_OP_HIST_RX_TX | port);
553 if (ret < 0)
554 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000555
Barry Grussling3675c8d2013-01-08 16:05:53 +0000556 /* Wait for the snapshotting to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400557 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000558 if (ret < 0)
559 return ret;
560
561 return 0;
562}
563
Andrew Lunn158bc062016-04-28 21:24:06 -0400564static void _mv88e6xxx_stats_read(struct mv88e6xxx_priv_state *ps,
565 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000566{
567 u32 _val;
568 int ret;
569
570 *val = 0;
571
Andrew Lunn158bc062016-04-28 21:24:06 -0400572 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200573 GLOBAL_STATS_OP_READ_CAPTURED |
574 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000575 if (ret < 0)
576 return;
577
Andrew Lunn158bc062016-04-28 21:24:06 -0400578 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000579 if (ret < 0)
580 return;
581
Andrew Lunn158bc062016-04-28 21:24:06 -0400582 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000583 if (ret < 0)
584 return;
585
586 _val = ret << 16;
587
Andrew Lunn158bc062016-04-28 21:24:06 -0400588 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000589 if (ret < 0)
590 return;
591
592 *val = _val | ret;
593}
594
Andrew Lunne413e7e2015-04-02 04:06:38 +0200595static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100596 { "in_good_octets", 8, 0x00, BANK0, },
597 { "in_bad_octets", 4, 0x02, BANK0, },
598 { "in_unicast", 4, 0x04, BANK0, },
599 { "in_broadcasts", 4, 0x06, BANK0, },
600 { "in_multicasts", 4, 0x07, BANK0, },
601 { "in_pause", 4, 0x16, BANK0, },
602 { "in_undersize", 4, 0x18, BANK0, },
603 { "in_fragments", 4, 0x19, BANK0, },
604 { "in_oversize", 4, 0x1a, BANK0, },
605 { "in_jabber", 4, 0x1b, BANK0, },
606 { "in_rx_error", 4, 0x1c, BANK0, },
607 { "in_fcs_error", 4, 0x1d, BANK0, },
608 { "out_octets", 8, 0x0e, BANK0, },
609 { "out_unicast", 4, 0x10, BANK0, },
610 { "out_broadcasts", 4, 0x13, BANK0, },
611 { "out_multicasts", 4, 0x12, BANK0, },
612 { "out_pause", 4, 0x15, BANK0, },
613 { "excessive", 4, 0x11, BANK0, },
614 { "collisions", 4, 0x1e, BANK0, },
615 { "deferred", 4, 0x05, BANK0, },
616 { "single", 4, 0x14, BANK0, },
617 { "multiple", 4, 0x17, BANK0, },
618 { "out_fcs_error", 4, 0x03, BANK0, },
619 { "late", 4, 0x1f, BANK0, },
620 { "hist_64bytes", 4, 0x08, BANK0, },
621 { "hist_65_127bytes", 4, 0x09, BANK0, },
622 { "hist_128_255bytes", 4, 0x0a, BANK0, },
623 { "hist_256_511bytes", 4, 0x0b, BANK0, },
624 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
625 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
626 { "sw_in_discards", 4, 0x10, PORT, },
627 { "sw_in_filtered", 2, 0x12, PORT, },
628 { "sw_out_filtered", 2, 0x13, PORT, },
629 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
630 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
631 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
632 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
633 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
634 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
635 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
636 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
637 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
638 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
639 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
640 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
641 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
642 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
643 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
644 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
645 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
646 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
647 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
648 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
649 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
650 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
651 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
652 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
653 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
654 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200655};
656
Andrew Lunn158bc062016-04-28 21:24:06 -0400657static bool mv88e6xxx_has_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100658 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200659{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100660 switch (stat->type) {
661 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200662 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100663 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400664 return mv88e6xxx_6320_family(ps);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100665 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400666 return mv88e6xxx_6095_family(ps) ||
667 mv88e6xxx_6185_family(ps) ||
668 mv88e6xxx_6097_family(ps) ||
669 mv88e6xxx_6165_family(ps) ||
670 mv88e6xxx_6351_family(ps) ||
671 mv88e6xxx_6352_family(ps);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200672 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100673 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000674}
675
Andrew Lunn158bc062016-04-28 21:24:06 -0400676static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100677 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200678 int port)
679{
Andrew Lunn80c46272015-06-20 18:42:30 +0200680 u32 low;
681 u32 high = 0;
682 int ret;
683 u64 value;
684
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100685 switch (s->type) {
686 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400687 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200688 if (ret < 0)
689 return UINT64_MAX;
690
691 low = ret;
692 if (s->sizeof_stat == 4) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400693 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100694 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200695 if (ret < 0)
696 return UINT64_MAX;
697 high = ret;
698 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100699 break;
700 case BANK0:
701 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400702 _mv88e6xxx_stats_read(ps, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200703 if (s->sizeof_stat == 8)
Andrew Lunn158bc062016-04-28 21:24:06 -0400704 _mv88e6xxx_stats_read(ps, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200705 }
706 value = (((u64)high) << 16) | low;
707 return value;
708}
709
Vivien Didelotf81ec902016-05-09 13:22:58 -0400710static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
711 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100712{
Andrew Lunn158bc062016-04-28 21:24:06 -0400713 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100714 struct mv88e6xxx_hw_stat *stat;
715 int i, j;
716
717 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
718 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400719 if (mv88e6xxx_has_stat(ps, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100720 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
721 ETH_GSTRING_LEN);
722 j++;
723 }
724 }
725}
726
Vivien Didelotf81ec902016-05-09 13:22:58 -0400727static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100728{
Andrew Lunn158bc062016-04-28 21:24:06 -0400729 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100730 struct mv88e6xxx_hw_stat *stat;
731 int i, j;
732
733 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
734 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400735 if (mv88e6xxx_has_stat(ps, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100736 j++;
737 }
738 return j;
739}
740
Vivien Didelotf81ec902016-05-09 13:22:58 -0400741static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
742 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000743{
Florian Fainellia22adce2014-04-28 11:14:28 -0700744 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100745 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000746 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100747 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000748
Andrew Lunn31888232015-05-06 01:09:54 +0200749 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000750
Andrew Lunn158bc062016-04-28 21:24:06 -0400751 ret = _mv88e6xxx_stats_snapshot(ps, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000752 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200753 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000754 return;
755 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100756 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
757 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400758 if (mv88e6xxx_has_stat(ps, stat)) {
759 data[j] = _mv88e6xxx_get_ethtool_stat(ps, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100760 j++;
761 }
762 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000763
Andrew Lunn31888232015-05-06 01:09:54 +0200764 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000765}
Ben Hutchings98e67302011-11-25 14:36:19 +0000766
Vivien Didelotf81ec902016-05-09 13:22:58 -0400767static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700768{
769 return 32 * sizeof(u16);
770}
771
Vivien Didelotf81ec902016-05-09 13:22:58 -0400772static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
773 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700774{
Andrew Lunn158bc062016-04-28 21:24:06 -0400775 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700776 u16 *p = _p;
777 int i;
778
779 regs->version = 0;
780
781 memset(p, 0xff, 32 * sizeof(u16));
782
Vivien Didelot23062512016-05-09 13:22:45 -0400783 mutex_lock(&ps->smi_mutex);
784
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700785 for (i = 0; i < 32; i++) {
786 int ret;
787
Vivien Didelot23062512016-05-09 13:22:45 -0400788 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700789 if (ret >= 0)
790 p[i] = ret;
791 }
Vivien Didelot23062512016-05-09 13:22:45 -0400792
793 mutex_unlock(&ps->smi_mutex);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700794}
795
Andrew Lunn158bc062016-04-28 21:24:06 -0400796static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset,
Andrew Lunn3898c142015-05-06 01:09:53 +0200797 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700798{
799 unsigned long timeout = jiffies + HZ / 10;
800
801 while (time_before(jiffies, timeout)) {
802 int ret;
803
Andrew Lunn158bc062016-04-28 21:24:06 -0400804 ret = _mv88e6xxx_reg_read(ps, reg, offset);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700805 if (ret < 0)
806 return ret;
807 if (!(ret & mask))
808 return 0;
809
810 usleep_range(1000, 2000);
811 }
812 return -ETIMEDOUT;
813}
814
Andrew Lunn158bc062016-04-28 21:24:06 -0400815static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg,
816 int offset, u16 mask)
Andrew Lunn3898c142015-05-06 01:09:53 +0200817{
Andrew Lunn3898c142015-05-06 01:09:53 +0200818 int ret;
819
820 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400821 ret = _mv88e6xxx_wait(ps, reg, offset, mask);
Andrew Lunn3898c142015-05-06 01:09:53 +0200822 mutex_unlock(&ps->smi_mutex);
823
824 return ret;
825}
826
Andrew Lunn158bc062016-04-28 21:24:06 -0400827static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps)
Andrew Lunn3898c142015-05-06 01:09:53 +0200828{
Andrew Lunn158bc062016-04-28 21:24:06 -0400829 return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200830 GLOBAL2_SMI_OP_BUSY);
831}
832
Vivien Didelotd24645b2016-05-09 13:22:41 -0400833static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200834{
Andrew Lunn158bc062016-04-28 21:24:06 -0400835 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
836
837 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200838 GLOBAL2_EEPROM_OP_LOAD);
839}
840
Vivien Didelotd24645b2016-05-09 13:22:41 -0400841static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200842{
Andrew Lunn158bc062016-04-28 21:24:06 -0400843 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
844
845 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200846 GLOBAL2_EEPROM_OP_BUSY);
847}
848
Vivien Didelotd24645b2016-05-09 13:22:41 -0400849static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
850{
851 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
852 int ret;
853
854 mutex_lock(&ps->eeprom_mutex);
855
856 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
857 GLOBAL2_EEPROM_OP_READ |
858 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
859 if (ret < 0)
860 goto error;
861
862 ret = mv88e6xxx_eeprom_busy_wait(ds);
863 if (ret < 0)
864 goto error;
865
866 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
867error:
868 mutex_unlock(&ps->eeprom_mutex);
869 return ret;
870}
871
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200872static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
873{
874 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
875
876 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
877 return ps->eeprom_len;
878
879 return 0;
880}
881
Vivien Didelotf81ec902016-05-09 13:22:58 -0400882static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
883 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400884{
885 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
886 int offset;
887 int len;
888 int ret;
889
890 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
891 return -EOPNOTSUPP;
892
893 offset = eeprom->offset;
894 len = eeprom->len;
895 eeprom->len = 0;
896
897 eeprom->magic = 0xc3ec4951;
898
899 ret = mv88e6xxx_eeprom_load_wait(ds);
900 if (ret < 0)
901 return ret;
902
903 if (offset & 1) {
904 int word;
905
906 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
907 if (word < 0)
908 return word;
909
910 *data++ = (word >> 8) & 0xff;
911
912 offset++;
913 len--;
914 eeprom->len++;
915 }
916
917 while (len >= 2) {
918 int word;
919
920 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
921 if (word < 0)
922 return word;
923
924 *data++ = word & 0xff;
925 *data++ = (word >> 8) & 0xff;
926
927 offset += 2;
928 len -= 2;
929 eeprom->len += 2;
930 }
931
932 if (len) {
933 int word;
934
935 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
936 if (word < 0)
937 return word;
938
939 *data++ = word & 0xff;
940
941 offset++;
942 len--;
943 eeprom->len++;
944 }
945
946 return 0;
947}
948
949static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
950{
951 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
952 int ret;
953
954 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
955 if (ret < 0)
956 return ret;
957
958 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
959 return -EROFS;
960
961 return 0;
962}
963
964static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
965 u16 data)
966{
967 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
968 int ret;
969
970 mutex_lock(&ps->eeprom_mutex);
971
972 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
973 if (ret < 0)
974 goto error;
975
976 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
977 GLOBAL2_EEPROM_OP_WRITE |
978 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
979 if (ret < 0)
980 goto error;
981
982 ret = mv88e6xxx_eeprom_busy_wait(ds);
983error:
984 mutex_unlock(&ps->eeprom_mutex);
985 return ret;
986}
987
Vivien Didelotf81ec902016-05-09 13:22:58 -0400988static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
989 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400990{
991 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
992 int offset;
993 int ret;
994 int len;
995
996 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
997 return -EOPNOTSUPP;
998
999 if (eeprom->magic != 0xc3ec4951)
1000 return -EINVAL;
1001
1002 ret = mv88e6xxx_eeprom_is_readonly(ds);
1003 if (ret)
1004 return ret;
1005
1006 offset = eeprom->offset;
1007 len = eeprom->len;
1008 eeprom->len = 0;
1009
1010 ret = mv88e6xxx_eeprom_load_wait(ds);
1011 if (ret < 0)
1012 return ret;
1013
1014 if (offset & 1) {
1015 int word;
1016
1017 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1018 if (word < 0)
1019 return word;
1020
1021 word = (*data++ << 8) | (word & 0xff);
1022
1023 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1024 if (ret < 0)
1025 return ret;
1026
1027 offset++;
1028 len--;
1029 eeprom->len++;
1030 }
1031
1032 while (len >= 2) {
1033 int word;
1034
1035 word = *data++;
1036 word |= *data++ << 8;
1037
1038 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1039 if (ret < 0)
1040 return ret;
1041
1042 offset += 2;
1043 len -= 2;
1044 eeprom->len += 2;
1045 }
1046
1047 if (len) {
1048 int word;
1049
1050 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1051 if (word < 0)
1052 return word;
1053
1054 word = (word & 0xff00) | *data++;
1055
1056 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1057 if (ret < 0)
1058 return ret;
1059
1060 offset++;
1061 len--;
1062 eeprom->len++;
1063 }
1064
1065 return 0;
1066}
1067
Andrew Lunn158bc062016-04-28 21:24:06 -04001068static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001069{
Andrew Lunn158bc062016-04-28 21:24:06 -04001070 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001071 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001072}
1073
Andrew Lunn158bc062016-04-28 21:24:06 -04001074static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
1075 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001076{
1077 int ret;
1078
Andrew Lunn158bc062016-04-28 21:24:06 -04001079 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001080 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1081 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001082 if (ret < 0)
1083 return ret;
1084
Andrew Lunn158bc062016-04-28 21:24:06 -04001085 ret = _mv88e6xxx_phy_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +02001086 if (ret < 0)
1087 return ret;
1088
Andrew Lunn158bc062016-04-28 21:24:06 -04001089 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
1090
1091 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001092}
1093
Andrew Lunn158bc062016-04-28 21:24:06 -04001094static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
1095 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001096{
Andrew Lunn3898c142015-05-06 01:09:53 +02001097 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001098
Andrew Lunn158bc062016-04-28 21:24:06 -04001099 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001100 if (ret < 0)
1101 return ret;
1102
Andrew Lunn158bc062016-04-28 21:24:06 -04001103 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001104 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1105 regnum);
1106
Andrew Lunn158bc062016-04-28 21:24:06 -04001107 return _mv88e6xxx_phy_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +01001108}
1109
Vivien Didelotf81ec902016-05-09 13:22:58 -04001110static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1111 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001112{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001113 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001114 int reg;
1115
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001116 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1117 return -EOPNOTSUPP;
1118
Andrew Lunn3898c142015-05-06 01:09:53 +02001119 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001120
Andrew Lunn158bc062016-04-28 21:24:06 -04001121 reg = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001122 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001123 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001124
1125 e->eee_enabled = !!(reg & 0x0200);
1126 e->tx_lpi_enabled = !!(reg & 0x0100);
1127
Andrew Lunn158bc062016-04-28 21:24:06 -04001128 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001129 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001130 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001131
Andrew Lunncca8b132015-04-02 04:06:39 +02001132 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001133 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001134
Andrew Lunn2f40c692015-04-02 04:06:37 +02001135out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001136 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001137 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001138}
1139
Vivien Didelotf81ec902016-05-09 13:22:58 -04001140static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1141 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001142{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001143 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1144 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001145 int ret;
1146
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001147 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1148 return -EOPNOTSUPP;
1149
Andrew Lunn3898c142015-05-06 01:09:53 +02001150 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001151
Andrew Lunn158bc062016-04-28 21:24:06 -04001152 ret = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001153 if (ret < 0)
1154 goto out;
1155
1156 reg = ret & ~0x0300;
1157 if (e->eee_enabled)
1158 reg |= 0x0200;
1159 if (e->tx_lpi_enabled)
1160 reg |= 0x0100;
1161
Andrew Lunn158bc062016-04-28 21:24:06 -04001162 ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001163out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001164 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001165
1166 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001167}
1168
Andrew Lunn158bc062016-04-28 21:24:06 -04001169static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001170{
1171 int ret;
1172
Andrew Lunn158bc062016-04-28 21:24:06 -04001173 if (mv88e6xxx_has_fid_reg(ps)) {
1174 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001175 if (ret < 0)
1176 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001177 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001178 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001179 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001180 if (ret < 0)
1181 return ret;
1182
Andrew Lunn158bc062016-04-28 21:24:06 -04001183 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001184 (ret & 0xfff) |
1185 ((fid << 8) & 0xf000));
1186 if (ret < 0)
1187 return ret;
1188
1189 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1190 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001191 }
1192
Andrew Lunn158bc062016-04-28 21:24:06 -04001193 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001194 if (ret < 0)
1195 return ret;
1196
Andrew Lunn158bc062016-04-28 21:24:06 -04001197 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001198}
1199
Andrew Lunn158bc062016-04-28 21:24:06 -04001200static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -04001201 struct mv88e6xxx_atu_entry *entry)
1202{
1203 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1204
1205 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1206 unsigned int mask, shift;
1207
1208 if (entry->trunk) {
1209 data |= GLOBAL_ATU_DATA_TRUNK;
1210 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1211 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1212 } else {
1213 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1214 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1215 }
1216
1217 data |= (entry->portv_trunkid << shift) & mask;
1218 }
1219
Andrew Lunn158bc062016-04-28 21:24:06 -04001220 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001221}
1222
Andrew Lunn158bc062016-04-28 21:24:06 -04001223static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001224 struct mv88e6xxx_atu_entry *entry,
1225 bool static_too)
1226{
1227 int op;
1228 int err;
1229
Andrew Lunn158bc062016-04-28 21:24:06 -04001230 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001231 if (err)
1232 return err;
1233
Andrew Lunn158bc062016-04-28 21:24:06 -04001234 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001235 if (err)
1236 return err;
1237
1238 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001239 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1240 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1241 } else {
1242 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1243 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1244 }
1245
Andrew Lunn158bc062016-04-28 21:24:06 -04001246 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001247}
1248
Andrew Lunn158bc062016-04-28 21:24:06 -04001249static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1250 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001251{
1252 struct mv88e6xxx_atu_entry entry = {
1253 .fid = fid,
1254 .state = 0, /* EntryState bits must be 0 */
1255 };
1256
Andrew Lunn158bc062016-04-28 21:24:06 -04001257 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001258}
1259
Andrew Lunn158bc062016-04-28 21:24:06 -04001260static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1261 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001262{
1263 struct mv88e6xxx_atu_entry entry = {
1264 .trunk = false,
1265 .fid = fid,
1266 };
1267
1268 /* EntryState bits must be 0xF */
1269 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1270
1271 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1272 entry.portv_trunkid = (to_port & 0x0f) << 4;
1273 entry.portv_trunkid |= from_port & 0x0f;
1274
Andrew Lunn158bc062016-04-28 21:24:06 -04001275 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001276}
1277
Andrew Lunn158bc062016-04-28 21:24:06 -04001278static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1279 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001280{
1281 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001282 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001283}
1284
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001285static const char * const mv88e6xxx_port_state_names[] = {
1286 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1287 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1288 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1289 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1290};
1291
Andrew Lunn158bc062016-04-28 21:24:06 -04001292static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1293 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001294{
Andrew Lunn158bc062016-04-28 21:24:06 -04001295 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001296 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001297 u8 oldstate;
1298
Andrew Lunn158bc062016-04-28 21:24:06 -04001299 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001300 if (reg < 0)
1301 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001302
Andrew Lunncca8b132015-04-02 04:06:39 +02001303 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001304
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001305 if (oldstate != state) {
1306 /* Flush forwarding database if we're moving a port
1307 * from Learning or Forwarding state to Disabled or
1308 * Blocking or Listening state.
1309 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001310 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1311 oldstate == PORT_CONTROL_STATE_FORWARDING)
1312 && (state == PORT_CONTROL_STATE_DISABLED ||
1313 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001314 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001315 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001316 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001317 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001318
Andrew Lunncca8b132015-04-02 04:06:39 +02001319 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001320 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001321 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001322 if (ret)
1323 return ret;
1324
1325 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1326 mv88e6xxx_port_state_names[state],
1327 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001328 }
1329
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001330 return ret;
1331}
1332
Andrew Lunn158bc062016-04-28 21:24:06 -04001333static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1334 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001335{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001336 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001337 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001338 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001339 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001340 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001341 int i;
1342
1343 /* allow CPU port or DSA link(s) to send frames to every port */
1344 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1345 output_ports = mask;
1346 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001347 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001348 /* allow sending frames to every group member */
1349 if (bridge && ps->ports[i].bridge_dev == bridge)
1350 output_ports |= BIT(i);
1351
1352 /* allow sending frames to CPU port and DSA link(s) */
1353 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1354 output_ports |= BIT(i);
1355 }
1356 }
1357
1358 /* prevent frames from going back out of the port they came in on */
1359 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001360
Andrew Lunn158bc062016-04-28 21:24:06 -04001361 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001362 if (reg < 0)
1363 return reg;
1364
1365 reg &= ~mask;
1366 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001367
Andrew Lunn158bc062016-04-28 21:24:06 -04001368 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001369}
1370
Vivien Didelotf81ec902016-05-09 13:22:58 -04001371static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1372 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001373{
1374 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1375 int stp_state;
1376
Vivien Didelot936f2342016-05-09 13:22:46 -04001377 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE))
1378 return;
1379
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001380 switch (state) {
1381 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001382 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001383 break;
1384 case BR_STATE_BLOCKING:
1385 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001386 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001387 break;
1388 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001389 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001390 break;
1391 case BR_STATE_FORWARDING:
1392 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001393 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001394 break;
1395 }
1396
Vivien Didelot43c44a92016-04-06 11:55:03 -04001397 /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled,
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001398 * so we can not update the port state directly but need to schedule it.
1399 */
Vivien Didelotd715fa62016-02-12 12:09:38 -05001400 ps->ports[port].state = stp_state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001401 set_bit(port, ps->port_state_update_mask);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001402 schedule_work(&ps->bridge_work);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001403}
1404
Andrew Lunn158bc062016-04-28 21:24:06 -04001405static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1406 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001407{
Andrew Lunn158bc062016-04-28 21:24:06 -04001408 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001409 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001410 int ret;
1411
Andrew Lunn158bc062016-04-28 21:24:06 -04001412 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001413 if (ret < 0)
1414 return ret;
1415
Vivien Didelot5da96032016-03-07 18:24:39 -05001416 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1417
1418 if (new) {
1419 ret &= ~PORT_DEFAULT_VLAN_MASK;
1420 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1421
Andrew Lunn158bc062016-04-28 21:24:06 -04001422 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001423 PORT_DEFAULT_VLAN, ret);
1424 if (ret < 0)
1425 return ret;
1426
1427 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1428 pvid);
1429 }
1430
1431 if (old)
1432 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001433
1434 return 0;
1435}
1436
Andrew Lunn158bc062016-04-28 21:24:06 -04001437static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1438 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001439{
Andrew Lunn158bc062016-04-28 21:24:06 -04001440 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001441}
1442
Andrew Lunn158bc062016-04-28 21:24:06 -04001443static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1444 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001445{
Andrew Lunn158bc062016-04-28 21:24:06 -04001446 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001447}
1448
Andrew Lunn158bc062016-04-28 21:24:06 -04001449static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001450{
Andrew Lunn158bc062016-04-28 21:24:06 -04001451 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001452 GLOBAL_VTU_OP_BUSY);
1453}
1454
Andrew Lunn158bc062016-04-28 21:24:06 -04001455static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001456{
1457 int ret;
1458
Andrew Lunn158bc062016-04-28 21:24:06 -04001459 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001460 if (ret < 0)
1461 return ret;
1462
Andrew Lunn158bc062016-04-28 21:24:06 -04001463 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001464}
1465
Andrew Lunn158bc062016-04-28 21:24:06 -04001466static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001467{
1468 int ret;
1469
Andrew Lunn158bc062016-04-28 21:24:06 -04001470 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001471 if (ret < 0)
1472 return ret;
1473
Andrew Lunn158bc062016-04-28 21:24:06 -04001474 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001475}
1476
Andrew Lunn158bc062016-04-28 21:24:06 -04001477static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001478 struct mv88e6xxx_vtu_stu_entry *entry,
1479 unsigned int nibble_offset)
1480{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001481 u16 regs[3];
1482 int i;
1483 int ret;
1484
1485 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001486 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001487 GLOBAL_VTU_DATA_0_3 + i);
1488 if (ret < 0)
1489 return ret;
1490
1491 regs[i] = ret;
1492 }
1493
Vivien Didelot009a2b92016-04-17 13:24:01 -04001494 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001495 unsigned int shift = (i % 4) * 4 + nibble_offset;
1496 u16 reg = regs[i / 4];
1497
1498 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1499 }
1500
1501 return 0;
1502}
1503
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001504static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps,
1505 struct mv88e6xxx_vtu_stu_entry *entry)
1506{
1507 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0);
1508}
1509
1510static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps,
1511 struct mv88e6xxx_vtu_stu_entry *entry)
1512{
1513 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2);
1514}
1515
Andrew Lunn158bc062016-04-28 21:24:06 -04001516static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001517 struct mv88e6xxx_vtu_stu_entry *entry,
1518 unsigned int nibble_offset)
1519{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001520 u16 regs[3] = { 0 };
1521 int i;
1522 int ret;
1523
Vivien Didelot009a2b92016-04-17 13:24:01 -04001524 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001525 unsigned int shift = (i % 4) * 4 + nibble_offset;
1526 u8 data = entry->data[i];
1527
1528 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1529 }
1530
1531 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001532 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001533 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1534 if (ret < 0)
1535 return ret;
1536 }
1537
1538 return 0;
1539}
1540
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001541static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps,
1542 struct mv88e6xxx_vtu_stu_entry *entry)
1543{
1544 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
1545}
1546
1547static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps,
1548 struct mv88e6xxx_vtu_stu_entry *entry)
1549{
1550 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
1551}
1552
Andrew Lunn158bc062016-04-28 21:24:06 -04001553static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001554{
Andrew Lunn158bc062016-04-28 21:24:06 -04001555 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001556 vid & GLOBAL_VTU_VID_MASK);
1557}
1558
Andrew Lunn158bc062016-04-28 21:24:06 -04001559static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001560 struct mv88e6xxx_vtu_stu_entry *entry)
1561{
1562 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1563 int ret;
1564
Andrew Lunn158bc062016-04-28 21:24:06 -04001565 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001566 if (ret < 0)
1567 return ret;
1568
Andrew Lunn158bc062016-04-28 21:24:06 -04001569 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001570 if (ret < 0)
1571 return ret;
1572
Andrew Lunn158bc062016-04-28 21:24:06 -04001573 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001574 if (ret < 0)
1575 return ret;
1576
1577 next.vid = ret & GLOBAL_VTU_VID_MASK;
1578 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1579
1580 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001581 ret = mv88e6xxx_vtu_data_read(ps, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001582 if (ret < 0)
1583 return ret;
1584
Andrew Lunn158bc062016-04-28 21:24:06 -04001585 if (mv88e6xxx_has_fid_reg(ps)) {
1586 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001587 GLOBAL_VTU_FID);
1588 if (ret < 0)
1589 return ret;
1590
1591 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001592 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001593 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1594 * VTU DBNum[3:0] are located in VTU Operation 3:0
1595 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001596 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001597 GLOBAL_VTU_OP);
1598 if (ret < 0)
1599 return ret;
1600
1601 next.fid = (ret & 0xf00) >> 4;
1602 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001603 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001604
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001605 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001606 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001607 GLOBAL_VTU_SID);
1608 if (ret < 0)
1609 return ret;
1610
1611 next.sid = ret & GLOBAL_VTU_SID_MASK;
1612 }
1613 }
1614
1615 *entry = next;
1616 return 0;
1617}
1618
Vivien Didelotf81ec902016-05-09 13:22:58 -04001619static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1620 struct switchdev_obj_port_vlan *vlan,
1621 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001622{
1623 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1624 struct mv88e6xxx_vtu_stu_entry next;
1625 u16 pvid;
1626 int err;
1627
Vivien Didelot54d77b52016-05-09 13:22:47 -04001628 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
1629 return -EOPNOTSUPP;
1630
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001631 mutex_lock(&ps->smi_mutex);
1632
Andrew Lunn158bc062016-04-28 21:24:06 -04001633 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001634 if (err)
1635 goto unlock;
1636
Andrew Lunn158bc062016-04-28 21:24:06 -04001637 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001638 if (err)
1639 goto unlock;
1640
1641 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001642 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001643 if (err)
1644 break;
1645
1646 if (!next.valid)
1647 break;
1648
1649 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1650 continue;
1651
1652 /* reinit and dump this VLAN obj */
1653 vlan->vid_begin = vlan->vid_end = next.vid;
1654 vlan->flags = 0;
1655
1656 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1657 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1658
1659 if (next.vid == pvid)
1660 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1661
1662 err = cb(&vlan->obj);
1663 if (err)
1664 break;
1665 } while (next.vid < GLOBAL_VTU_VID_MASK);
1666
1667unlock:
1668 mutex_unlock(&ps->smi_mutex);
1669
1670 return err;
1671}
1672
Andrew Lunn158bc062016-04-28 21:24:06 -04001673static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001674 struct mv88e6xxx_vtu_stu_entry *entry)
1675{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001676 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001677 u16 reg = 0;
1678 int ret;
1679
Andrew Lunn158bc062016-04-28 21:24:06 -04001680 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001681 if (ret < 0)
1682 return ret;
1683
1684 if (!entry->valid)
1685 goto loadpurge;
1686
1687 /* Write port member tags */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001688 ret = mv88e6xxx_vtu_data_write(ps, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001689 if (ret < 0)
1690 return ret;
1691
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001692 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001693 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001694 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001695 if (ret < 0)
1696 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001697 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001698
Andrew Lunn158bc062016-04-28 21:24:06 -04001699 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001700 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001701 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001702 if (ret < 0)
1703 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001704 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001705 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1706 * VTU DBNum[3:0] are located in VTU Operation 3:0
1707 */
1708 op |= (entry->fid & 0xf0) << 8;
1709 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001710 }
1711
1712 reg = GLOBAL_VTU_VID_VALID;
1713loadpurge:
1714 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001715 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001716 if (ret < 0)
1717 return ret;
1718
Andrew Lunn158bc062016-04-28 21:24:06 -04001719 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001720}
1721
Andrew Lunn158bc062016-04-28 21:24:06 -04001722static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001723 struct mv88e6xxx_vtu_stu_entry *entry)
1724{
1725 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1726 int ret;
1727
Andrew Lunn158bc062016-04-28 21:24:06 -04001728 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001729 if (ret < 0)
1730 return ret;
1731
Andrew Lunn158bc062016-04-28 21:24:06 -04001732 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001733 sid & GLOBAL_VTU_SID_MASK);
1734 if (ret < 0)
1735 return ret;
1736
Andrew Lunn158bc062016-04-28 21:24:06 -04001737 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001738 if (ret < 0)
1739 return ret;
1740
Andrew Lunn158bc062016-04-28 21:24:06 -04001741 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001742 if (ret < 0)
1743 return ret;
1744
1745 next.sid = ret & GLOBAL_VTU_SID_MASK;
1746
Andrew Lunn158bc062016-04-28 21:24:06 -04001747 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001748 if (ret < 0)
1749 return ret;
1750
1751 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1752
1753 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001754 ret = mv88e6xxx_stu_data_read(ps, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001755 if (ret < 0)
1756 return ret;
1757 }
1758
1759 *entry = next;
1760 return 0;
1761}
1762
Andrew Lunn158bc062016-04-28 21:24:06 -04001763static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001764 struct mv88e6xxx_vtu_stu_entry *entry)
1765{
1766 u16 reg = 0;
1767 int ret;
1768
Andrew Lunn158bc062016-04-28 21:24:06 -04001769 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001770 if (ret < 0)
1771 return ret;
1772
1773 if (!entry->valid)
1774 goto loadpurge;
1775
1776 /* Write port states */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001777 ret = mv88e6xxx_stu_data_write(ps, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001778 if (ret < 0)
1779 return ret;
1780
1781 reg = GLOBAL_VTU_VID_VALID;
1782loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001783 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001784 if (ret < 0)
1785 return ret;
1786
1787 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001788 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001789 if (ret < 0)
1790 return ret;
1791
Andrew Lunn158bc062016-04-28 21:24:06 -04001792 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001793}
1794
Andrew Lunn158bc062016-04-28 21:24:06 -04001795static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1796 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001797{
Andrew Lunn158bc062016-04-28 21:24:06 -04001798 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001799 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001800 u16 fid;
1801 int ret;
1802
Andrew Lunn158bc062016-04-28 21:24:06 -04001803 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001804 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001805 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001806 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001807 else
1808 return -EOPNOTSUPP;
1809
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001810 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001811 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001812 if (ret < 0)
1813 return ret;
1814
1815 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1816
1817 if (new) {
1818 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1819 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1820
Andrew Lunn158bc062016-04-28 21:24:06 -04001821 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001822 ret);
1823 if (ret < 0)
1824 return ret;
1825 }
1826
1827 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001828 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001829 if (ret < 0)
1830 return ret;
1831
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001832 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001833
1834 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001835 ret &= ~upper_mask;
1836 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001837
Andrew Lunn158bc062016-04-28 21:24:06 -04001838 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001839 ret);
1840 if (ret < 0)
1841 return ret;
1842
1843 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1844 }
1845
1846 if (old)
1847 *old = fid;
1848
1849 return 0;
1850}
1851
Andrew Lunn158bc062016-04-28 21:24:06 -04001852static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1853 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001854{
Andrew Lunn158bc062016-04-28 21:24:06 -04001855 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001856}
1857
Andrew Lunn158bc062016-04-28 21:24:06 -04001858static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1859 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001860{
Andrew Lunn158bc062016-04-28 21:24:06 -04001861 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001862}
1863
Andrew Lunn158bc062016-04-28 21:24:06 -04001864static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001865{
1866 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1867 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001868 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001869
1870 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1871
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001872 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001873 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001874 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001875 if (err)
1876 return err;
1877
1878 set_bit(*fid, fid_bitmap);
1879 }
1880
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001881 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001882 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001883 if (err)
1884 return err;
1885
1886 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001887 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001888 if (err)
1889 return err;
1890
1891 if (!vlan.valid)
1892 break;
1893
1894 set_bit(vlan.fid, fid_bitmap);
1895 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1896
1897 /* The reset value 0x000 is used to indicate that multiple address
1898 * databases are not needed. Return the next positive available.
1899 */
1900 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001901 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001902 return -ENOSPC;
1903
1904 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001905 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001906}
1907
Andrew Lunn158bc062016-04-28 21:24:06 -04001908static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001909 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001910{
Andrew Lunn158bc062016-04-28 21:24:06 -04001911 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001912 struct mv88e6xxx_vtu_stu_entry vlan = {
1913 .valid = true,
1914 .vid = vid,
1915 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001916 int i, err;
1917
Andrew Lunn158bc062016-04-28 21:24:06 -04001918 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001919 if (err)
1920 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001921
Vivien Didelot3d131f02015-11-03 10:52:52 -05001922 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001923 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001924 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1925 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1926 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001927
Andrew Lunn158bc062016-04-28 21:24:06 -04001928 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1929 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001930 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001931
1932 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1933 * implemented, only one STU entry is needed to cover all VTU
1934 * entries. Thus, validate the SID 0.
1935 */
1936 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001937 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001938 if (err)
1939 return err;
1940
1941 if (vstp.sid != vlan.sid || !vstp.valid) {
1942 memset(&vstp, 0, sizeof(vstp));
1943 vstp.valid = true;
1944 vstp.sid = vlan.sid;
1945
Andrew Lunn158bc062016-04-28 21:24:06 -04001946 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001947 if (err)
1948 return err;
1949 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001950 }
1951
1952 *entry = vlan;
1953 return 0;
1954}
1955
Andrew Lunn158bc062016-04-28 21:24:06 -04001956static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001957 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1958{
1959 int err;
1960
1961 if (!vid)
1962 return -EINVAL;
1963
Andrew Lunn158bc062016-04-28 21:24:06 -04001964 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001965 if (err)
1966 return err;
1967
Andrew Lunn158bc062016-04-28 21:24:06 -04001968 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001969 if (err)
1970 return err;
1971
1972 if (entry->vid != vid || !entry->valid) {
1973 if (!creat)
1974 return -EOPNOTSUPP;
1975 /* -ENOENT would've been more appropriate, but switchdev expects
1976 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1977 */
1978
Andrew Lunn158bc062016-04-28 21:24:06 -04001979 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001980 }
1981
1982 return err;
1983}
1984
Vivien Didelotda9c3592016-02-12 12:09:40 -05001985static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1986 u16 vid_begin, u16 vid_end)
1987{
1988 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1989 struct mv88e6xxx_vtu_stu_entry vlan;
1990 int i, err;
1991
1992 if (!vid_begin)
1993 return -EOPNOTSUPP;
1994
1995 mutex_lock(&ps->smi_mutex);
1996
Andrew Lunn158bc062016-04-28 21:24:06 -04001997 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001998 if (err)
1999 goto unlock;
2000
2001 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002002 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002003 if (err)
2004 goto unlock;
2005
2006 if (!vlan.valid)
2007 break;
2008
2009 if (vlan.vid > vid_end)
2010 break;
2011
Vivien Didelot009a2b92016-04-17 13:24:01 -04002012 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05002013 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
2014 continue;
2015
2016 if (vlan.data[i] ==
2017 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2018 continue;
2019
2020 if (ps->ports[i].bridge_dev ==
2021 ps->ports[port].bridge_dev)
2022 break; /* same bridge, check next VLAN */
2023
2024 netdev_warn(ds->ports[port],
2025 "hardware VLAN %d already used by %s\n",
2026 vlan.vid,
2027 netdev_name(ps->ports[i].bridge_dev));
2028 err = -EOPNOTSUPP;
2029 goto unlock;
2030 }
2031 } while (vlan.vid < vid_end);
2032
2033unlock:
2034 mutex_unlock(&ps->smi_mutex);
2035
2036 return err;
2037}
2038
Vivien Didelot214cdb92016-02-26 13:16:08 -05002039static const char * const mv88e6xxx_port_8021q_mode_names[] = {
2040 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
2041 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
2042 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
2043 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
2044};
2045
Vivien Didelotf81ec902016-05-09 13:22:58 -04002046static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
2047 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002048{
2049 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2050 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
2051 PORT_CONTROL_2_8021Q_DISABLED;
2052 int ret;
2053
Vivien Didelot54d77b52016-05-09 13:22:47 -04002054 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2055 return -EOPNOTSUPP;
2056
Vivien Didelot214cdb92016-02-26 13:16:08 -05002057 mutex_lock(&ps->smi_mutex);
2058
Andrew Lunn158bc062016-04-28 21:24:06 -04002059 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002060 if (ret < 0)
2061 goto unlock;
2062
2063 old = ret & PORT_CONTROL_2_8021Q_MASK;
2064
Vivien Didelot5220ef12016-03-07 18:24:52 -05002065 if (new != old) {
2066 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2067 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002068
Andrew Lunn158bc062016-04-28 21:24:06 -04002069 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002070 ret);
2071 if (ret < 0)
2072 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002073
Vivien Didelot5220ef12016-03-07 18:24:52 -05002074 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
2075 mv88e6xxx_port_8021q_mode_names[new],
2076 mv88e6xxx_port_8021q_mode_names[old]);
2077 }
2078
2079 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002080unlock:
2081 mutex_unlock(&ps->smi_mutex);
2082
2083 return ret;
2084}
2085
Vivien Didelotf81ec902016-05-09 13:22:58 -04002086static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2087 const struct switchdev_obj_port_vlan *vlan,
2088 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002089{
Vivien Didelot54d77b52016-05-09 13:22:47 -04002090 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002091 int err;
2092
Vivien Didelot54d77b52016-05-09 13:22:47 -04002093 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2094 return -EOPNOTSUPP;
2095
Vivien Didelotda9c3592016-02-12 12:09:40 -05002096 /* If the requested port doesn't belong to the same bridge as the VLAN
2097 * members, do not support it (yet) and fallback to software VLAN.
2098 */
2099 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2100 vlan->vid_end);
2101 if (err)
2102 return err;
2103
Vivien Didelot76e398a2015-11-01 12:33:55 -05002104 /* We don't need any dynamic resource from the kernel (yet),
2105 * so skip the prepare phase.
2106 */
2107 return 0;
2108}
2109
Andrew Lunn158bc062016-04-28 21:24:06 -04002110static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
2111 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002112{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002113 struct mv88e6xxx_vtu_stu_entry vlan;
2114 int err;
2115
Andrew Lunn158bc062016-04-28 21:24:06 -04002116 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002117 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002118 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002119
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002120 vlan.data[port] = untagged ?
2121 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2122 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2123
Andrew Lunn158bc062016-04-28 21:24:06 -04002124 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002125}
2126
Vivien Didelotf81ec902016-05-09 13:22:58 -04002127static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2128 const struct switchdev_obj_port_vlan *vlan,
2129 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002130{
2131 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2132 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2133 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2134 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002135
Vivien Didelot54d77b52016-05-09 13:22:47 -04002136 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2137 return;
2138
Vivien Didelot76e398a2015-11-01 12:33:55 -05002139 mutex_lock(&ps->smi_mutex);
2140
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002141 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04002142 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002143 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
2144 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002145
Andrew Lunn158bc062016-04-28 21:24:06 -04002146 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002147 netdev_err(ds->ports[port], "failed to set PVID %d\n",
2148 vlan->vid_end);
2149
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002150 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002151}
2152
Andrew Lunn158bc062016-04-28 21:24:06 -04002153static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
2154 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002155{
Andrew Lunn158bc062016-04-28 21:24:06 -04002156 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002157 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002158 int i, err;
2159
Andrew Lunn158bc062016-04-28 21:24:06 -04002160 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002161 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002162 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002163
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002164 /* Tell switchdev if this VLAN is handled in software */
2165 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002166 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002167
2168 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2169
2170 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002171 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04002172 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002173 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002174 continue;
2175
2176 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002177 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002178 break;
2179 }
2180 }
2181
Andrew Lunn158bc062016-04-28 21:24:06 -04002182 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002183 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002184 return err;
2185
Andrew Lunn158bc062016-04-28 21:24:06 -04002186 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002187}
2188
Vivien Didelotf81ec902016-05-09 13:22:58 -04002189static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2190 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002191{
2192 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2193 u16 pvid, vid;
2194 int err = 0;
2195
Vivien Didelot54d77b52016-05-09 13:22:47 -04002196 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2197 return -EOPNOTSUPP;
2198
Vivien Didelot76e398a2015-11-01 12:33:55 -05002199 mutex_lock(&ps->smi_mutex);
2200
Andrew Lunn158bc062016-04-28 21:24:06 -04002201 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002202 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002203 goto unlock;
2204
Vivien Didelot76e398a2015-11-01 12:33:55 -05002205 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002206 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002207 if (err)
2208 goto unlock;
2209
2210 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002211 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002212 if (err)
2213 goto unlock;
2214 }
2215 }
2216
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002217unlock:
2218 mutex_unlock(&ps->smi_mutex);
2219
2220 return err;
2221}
2222
Andrew Lunn158bc062016-04-28 21:24:06 -04002223static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002224 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002225{
2226 int i, ret;
2227
2228 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002229 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002230 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002231 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002232 if (ret < 0)
2233 return ret;
2234 }
2235
2236 return 0;
2237}
2238
Andrew Lunn158bc062016-04-28 21:24:06 -04002239static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
2240 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002241{
2242 int i, ret;
2243
2244 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002245 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002246 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002247 if (ret < 0)
2248 return ret;
2249 addr[i * 2] = ret >> 8;
2250 addr[i * 2 + 1] = ret & 0xff;
2251 }
2252
2253 return 0;
2254}
2255
Andrew Lunn158bc062016-04-28 21:24:06 -04002256static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002257 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002258{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002259 int ret;
2260
Andrew Lunn158bc062016-04-28 21:24:06 -04002261 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002262 if (ret < 0)
2263 return ret;
2264
Andrew Lunn158bc062016-04-28 21:24:06 -04002265 ret = _mv88e6xxx_atu_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002266 if (ret < 0)
2267 return ret;
2268
Andrew Lunn158bc062016-04-28 21:24:06 -04002269 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002270 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002271 return ret;
2272
Andrew Lunn158bc062016-04-28 21:24:06 -04002273 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002274}
David S. Millercdf09692015-08-11 12:00:37 -07002275
Andrew Lunn158bc062016-04-28 21:24:06 -04002276static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002277 const unsigned char *addr, u16 vid,
2278 u8 state)
2279{
2280 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002281 struct mv88e6xxx_vtu_stu_entry vlan;
2282 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002283
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002284 /* Null VLAN ID corresponds to the port private database */
2285 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002286 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002287 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002288 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002289 if (err)
2290 return err;
2291
2292 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002293 entry.state = state;
2294 ether_addr_copy(entry.mac, addr);
2295 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2296 entry.trunk = false;
2297 entry.portv_trunkid = BIT(port);
2298 }
2299
Andrew Lunn158bc062016-04-28 21:24:06 -04002300 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002301}
2302
Vivien Didelotf81ec902016-05-09 13:22:58 -04002303static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2304 const struct switchdev_obj_port_fdb *fdb,
2305 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002306{
Vivien Didelot2672f822016-05-09 13:22:48 -04002307 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2308
2309 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2310 return -EOPNOTSUPP;
2311
Vivien Didelot146a3202015-10-08 11:35:12 -04002312 /* We don't need any dynamic resource from the kernel (yet),
2313 * so skip the prepare phase.
2314 */
2315 return 0;
2316}
2317
Vivien Didelotf81ec902016-05-09 13:22:58 -04002318static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2319 const struct switchdev_obj_port_fdb *fdb,
2320 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002321{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002322 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002323 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2324 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2325 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002326
Vivien Didelot2672f822016-05-09 13:22:48 -04002327 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2328 return;
2329
David S. Millercdf09692015-08-11 12:00:37 -07002330 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002331 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Vivien Didelot8497aa62016-04-06 11:55:04 -04002332 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002333 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002334}
2335
Vivien Didelotf81ec902016-05-09 13:22:58 -04002336static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2337 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002338{
2339 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2340 int ret;
2341
Vivien Didelot2672f822016-05-09 13:22:48 -04002342 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2343 return -EOPNOTSUPP;
2344
David S. Millercdf09692015-08-11 12:00:37 -07002345 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002346 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002347 GLOBAL_ATU_DATA_STATE_UNUSED);
2348 mutex_unlock(&ps->smi_mutex);
2349
2350 return ret;
2351}
2352
Andrew Lunn158bc062016-04-28 21:24:06 -04002353static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002354 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002355{
Vivien Didelot1d194042015-08-10 09:09:51 -04002356 struct mv88e6xxx_atu_entry next = { 0 };
2357 int ret;
2358
2359 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002360
Andrew Lunn158bc062016-04-28 21:24:06 -04002361 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002362 if (ret < 0)
2363 return ret;
2364
Andrew Lunn158bc062016-04-28 21:24:06 -04002365 ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002366 if (ret < 0)
2367 return ret;
2368
Andrew Lunn158bc062016-04-28 21:24:06 -04002369 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002370 if (ret < 0)
2371 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002372
Andrew Lunn158bc062016-04-28 21:24:06 -04002373 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002374 if (ret < 0)
2375 return ret;
2376
2377 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2378 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2379 unsigned int mask, shift;
2380
2381 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2382 next.trunk = true;
2383 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2384 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2385 } else {
2386 next.trunk = false;
2387 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2388 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2389 }
2390
2391 next.portv_trunkid = (ret & mask) >> shift;
2392 }
2393
2394 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002395 return 0;
2396}
2397
Andrew Lunn158bc062016-04-28 21:24:06 -04002398static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2399 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002400 struct switchdev_obj_port_fdb *fdb,
2401 int (*cb)(struct switchdev_obj *obj))
2402{
2403 struct mv88e6xxx_atu_entry addr = {
2404 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2405 };
2406 int err;
2407
Andrew Lunn158bc062016-04-28 21:24:06 -04002408 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002409 if (err)
2410 return err;
2411
2412 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002413 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002414 if (err)
2415 break;
2416
2417 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2418 break;
2419
2420 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2421 bool is_static = addr.state ==
2422 (is_multicast_ether_addr(addr.mac) ?
2423 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2424 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2425
2426 fdb->vid = vid;
2427 ether_addr_copy(fdb->addr, addr.mac);
2428 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2429
2430 err = cb(&fdb->obj);
2431 if (err)
2432 break;
2433 }
2434 } while (!is_broadcast_ether_addr(addr.mac));
2435
2436 return err;
2437}
2438
Vivien Didelotf81ec902016-05-09 13:22:58 -04002439static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2440 struct switchdev_obj_port_fdb *fdb,
2441 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002442{
2443 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2444 struct mv88e6xxx_vtu_stu_entry vlan = {
2445 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2446 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002447 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002448 int err;
2449
Vivien Didelot2672f822016-05-09 13:22:48 -04002450 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2451 return -EOPNOTSUPP;
2452
Vivien Didelotf33475b2015-10-22 09:34:41 -04002453 mutex_lock(&ps->smi_mutex);
2454
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002455 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002456 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002457 if (err)
2458 goto unlock;
2459
Andrew Lunn158bc062016-04-28 21:24:06 -04002460 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002461 if (err)
2462 goto unlock;
2463
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002464 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002465 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002466 if (err)
2467 goto unlock;
2468
2469 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002470 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002471 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002472 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002473
2474 if (!vlan.valid)
2475 break;
2476
Andrew Lunn158bc062016-04-28 21:24:06 -04002477 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002478 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002479 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002480 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002481 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2482
2483unlock:
2484 mutex_unlock(&ps->smi_mutex);
2485
2486 return err;
2487}
2488
Vivien Didelotf81ec902016-05-09 13:22:58 -04002489static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2490 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002491{
Vivien Didelota6692752016-02-12 12:09:39 -05002492 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002493 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002494
Vivien Didelot936f2342016-05-09 13:22:46 -04002495 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2496 return -EOPNOTSUPP;
2497
Vivien Didelot466dfa02016-02-26 13:16:05 -05002498 mutex_lock(&ps->smi_mutex);
2499
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002500 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002501 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002502
Vivien Didelot009a2b92016-04-17 13:24:01 -04002503 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002504 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002505 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002506 if (err)
2507 break;
2508 }
2509 }
2510
Vivien Didelot466dfa02016-02-26 13:16:05 -05002511 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002512
Vivien Didelot466dfa02016-02-26 13:16:05 -05002513 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002514}
2515
Vivien Didelotf81ec902016-05-09 13:22:58 -04002516static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002517{
Vivien Didelota6692752016-02-12 12:09:39 -05002518 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002519 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002520 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002521
Vivien Didelot936f2342016-05-09 13:22:46 -04002522 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2523 return;
2524
Vivien Didelot466dfa02016-02-26 13:16:05 -05002525 mutex_lock(&ps->smi_mutex);
2526
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002527 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002528 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002529
Vivien Didelot009a2b92016-04-17 13:24:01 -04002530 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002531 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002532 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Vivien Didelot16bfa702016-03-13 16:21:33 -04002533 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002534
Vivien Didelot466dfa02016-02-26 13:16:05 -05002535 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002536}
2537
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002538static void mv88e6xxx_bridge_work(struct work_struct *work)
2539{
2540 struct mv88e6xxx_priv_state *ps;
2541 struct dsa_switch *ds;
2542 int port;
2543
2544 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002545 ds = ps->ds;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002546
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002547 mutex_lock(&ps->smi_mutex);
2548
Vivien Didelot009a2b92016-04-17 13:24:01 -04002549 for (port = 0; port < ps->info->num_ports; ++port)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002550 if (test_and_clear_bit(port, ps->port_state_update_mask) &&
Andrew Lunn158bc062016-04-28 21:24:06 -04002551 _mv88e6xxx_port_state(ps, port, ps->ports[port].state))
2552 netdev_warn(ds->ports[port],
2553 "failed to update state to %s\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002554 mv88e6xxx_port_state_names[ps->ports[port].state]);
2555
2556 mutex_unlock(&ps->smi_mutex);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002557}
2558
Andrew Lunn158bc062016-04-28 21:24:06 -04002559static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps,
2560 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002561{
2562 int ret;
2563
Andrew Lunn158bc062016-04-28 21:24:06 -04002564 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002565 if (ret < 0)
2566 goto restore_page_0;
2567
Andrew Lunn158bc062016-04-28 21:24:06 -04002568 ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002569restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002570 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002571
2572 return ret;
2573}
2574
Andrew Lunn158bc062016-04-28 21:24:06 -04002575static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps,
2576 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002577{
2578 int ret;
2579
Andrew Lunn158bc062016-04-28 21:24:06 -04002580 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002581 if (ret < 0)
2582 goto restore_page_0;
2583
Andrew Lunn158bc062016-04-28 21:24:06 -04002584 ret = _mv88e6xxx_phy_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002585restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002586 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002587
2588 return ret;
2589}
2590
Vivien Didelot552238b2016-05-09 13:22:49 -04002591static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps)
2592{
2593 bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE);
2594 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunn52638f72016-05-10 23:27:22 +02002595 struct gpio_desc *gpiod = ps->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002596 unsigned long timeout;
2597 int ret;
2598 int i;
2599
2600 /* Set all ports to the disabled state. */
2601 for (i = 0; i < ps->info->num_ports; i++) {
2602 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
2603 if (ret < 0)
2604 return ret;
2605
2606 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
2607 ret & 0xfffc);
2608 if (ret)
2609 return ret;
2610 }
2611
2612 /* Wait for transmit queues to drain. */
2613 usleep_range(2000, 4000);
2614
2615 /* If there is a gpio connected to the reset pin, toggle it */
2616 if (gpiod) {
2617 gpiod_set_value_cansleep(gpiod, 1);
2618 usleep_range(10000, 20000);
2619 gpiod_set_value_cansleep(gpiod, 0);
2620 usleep_range(10000, 20000);
2621 }
2622
2623 /* Reset the switch. Keep the PPU active if requested. The PPU
2624 * needs to be active to support indirect phy register access
2625 * through global registers 0x18 and 0x19.
2626 */
2627 if (ppu_active)
2628 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
2629 else
2630 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
2631 if (ret)
2632 return ret;
2633
2634 /* Wait up to one second for reset to complete. */
2635 timeout = jiffies + 1 * HZ;
2636 while (time_before(jiffies, timeout)) {
2637 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
2638 if (ret < 0)
2639 return ret;
2640
2641 if ((ret & is_reset) == is_reset)
2642 break;
2643 usleep_range(1000, 2000);
2644 }
2645 if (time_after(jiffies, timeout))
2646 ret = -ETIMEDOUT;
2647 else
2648 ret = 0;
2649
2650 return ret;
2651}
2652
Andrew Lunn158bc062016-04-28 21:24:06 -04002653static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002654{
2655 int ret;
2656
Andrew Lunn158bc062016-04-28 21:24:06 -04002657 ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002658 MII_BMCR);
2659 if (ret < 0)
2660 return ret;
2661
2662 if (ret & BMCR_PDOWN) {
2663 ret &= ~BMCR_PDOWN;
Andrew Lunn158bc062016-04-28 21:24:06 -04002664 ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002665 PAGE_FIBER_SERDES, MII_BMCR,
2666 ret);
2667 }
2668
2669 return ret;
2670}
2671
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002672static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002673{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002674 struct dsa_switch *ds = ps->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002675 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002676 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002677
Andrew Lunn158bc062016-04-28 21:24:06 -04002678 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2679 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2680 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2681 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002682 /* MAC Forcing register: don't force link, speed,
2683 * duplex or flow control state to any particular
2684 * values on physical ports, but force the CPU port
2685 * and all DSA ports to their maximum bandwidth and
2686 * full duplex.
2687 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002688 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002689 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002690 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002691 reg |= PORT_PCS_CTRL_FORCE_LINK |
2692 PORT_PCS_CTRL_LINK_UP |
2693 PORT_PCS_CTRL_DUPLEX_FULL |
2694 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002695 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002696 reg |= PORT_PCS_CTRL_100;
2697 else
2698 reg |= PORT_PCS_CTRL_1000;
2699 } else {
2700 reg |= PORT_PCS_CTRL_UNFORCED;
2701 }
2702
Andrew Lunn158bc062016-04-28 21:24:06 -04002703 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002704 PORT_PCS_CTRL, reg);
2705 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002706 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002707 }
2708
2709 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2710 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2711 * tunneling, determine priority by looking at 802.1p and IP
2712 * priority fields (IP prio has precedence), and set STP state
2713 * to Forwarding.
2714 *
2715 * If this is the CPU link, use DSA or EDSA tagging depending
2716 * on which tagging mode was configured.
2717 *
2718 * If this is a link to another switch, use DSA tagging mode.
2719 *
2720 * If this is the upstream port for this switch, enable
2721 * forwarding of unknown unicasts and multicasts.
2722 */
2723 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002724 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2725 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2726 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2727 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002728 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2729 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2730 PORT_CONTROL_STATE_FORWARDING;
2731 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002732 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002733 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002734 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2735 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2736 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002737 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2738 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2739 else
2740 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002741 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2742 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002743 }
2744
Andrew Lunn158bc062016-04-28 21:24:06 -04002745 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2746 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2747 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2748 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002749 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2750 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2751 }
2752 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002753 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002754 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002755 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002756 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2757 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2758 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002759 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002760 }
2761
Andrew Lunn54d792f2015-05-06 01:09:47 +02002762 if (port == dsa_upstream_port(ds))
2763 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2764 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2765 }
2766 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002767 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002768 PORT_CONTROL, reg);
2769 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002770 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002771 }
2772
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002773 /* If this port is connected to a SerDes, make sure the SerDes is not
2774 * powered down.
2775 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002776 if (mv88e6xxx_6352_family(ps)) {
2777 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002778 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002779 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002780 ret &= PORT_STATUS_CMODE_MASK;
2781 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2782 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2783 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002784 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002785 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002786 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002787 }
2788 }
2789
Vivien Didelot8efdda42015-08-13 12:52:23 -04002790 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002791 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002792 * untagged frames on this port, do a destination address lookup on all
2793 * received packets as usual, disable ARP mirroring and don't send a
2794 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002795 */
2796 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002797 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2798 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2799 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2800 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002801 reg = PORT_CONTROL_2_MAP_DA;
2802
Andrew Lunn158bc062016-04-28 21:24:06 -04002803 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2804 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002805 reg |= PORT_CONTROL_2_JUMBO_10240;
2806
Andrew Lunn158bc062016-04-28 21:24:06 -04002807 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002808 /* Set the upstream port this port should use */
2809 reg |= dsa_upstream_port(ds);
2810 /* enable forwarding of unknown multicast addresses to
2811 * the upstream port
2812 */
2813 if (port == dsa_upstream_port(ds))
2814 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2815 }
2816
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002817 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002818
Andrew Lunn54d792f2015-05-06 01:09:47 +02002819 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002820 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002821 PORT_CONTROL_2, reg);
2822 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002823 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002824 }
2825
2826 /* Port Association Vector: when learning source addresses
2827 * of packets, add the address to the address database using
2828 * a port bitmap that has only the bit for this port set and
2829 * the other bits clear.
2830 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002831 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002832 /* Disable learning for CPU port */
2833 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002834 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002835
Andrew Lunn158bc062016-04-28 21:24:06 -04002836 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002837 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002838 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002839
2840 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002841 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002842 0x0000);
2843 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002844 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002845
Andrew Lunn158bc062016-04-28 21:24:06 -04002846 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2847 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2848 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002849 /* Do not limit the period of time that this port can
2850 * be paused for by the remote end or the period of
2851 * time that this port can pause the remote end.
2852 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002853 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002854 PORT_PAUSE_CTRL, 0x0000);
2855 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002856 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002857
2858 /* Port ATU control: disable limiting the number of
2859 * address database entries that this port is allowed
2860 * to use.
2861 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002862 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002863 PORT_ATU_CONTROL, 0x0000);
2864 /* Priority Override: disable DA, SA and VTU priority
2865 * override.
2866 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002867 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002868 PORT_PRI_OVERRIDE, 0x0000);
2869 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002870 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002871
2872 /* Port Ethertype: use the Ethertype DSA Ethertype
2873 * value.
2874 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002875 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002876 PORT_ETH_TYPE, ETH_P_EDSA);
2877 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002878 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002879 /* Tag Remap: use an identity 802.1p prio -> switch
2880 * prio mapping.
2881 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002882 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002883 PORT_TAG_REGMAP_0123, 0x3210);
2884 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002885 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002886
2887 /* Tag Remap 2: use an identity 802.1p prio -> switch
2888 * prio mapping.
2889 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002890 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002891 PORT_TAG_REGMAP_4567, 0x7654);
2892 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002893 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002894 }
2895
Andrew Lunn158bc062016-04-28 21:24:06 -04002896 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2897 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2898 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2899 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002900 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002901 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002902 PORT_RATE_CONTROL, 0x0001);
2903 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002904 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002905 }
2906
Guenter Roeck366f0a02015-03-26 18:36:30 -07002907 /* Port Control 1: disable trunking, disable sending
2908 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002909 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002910 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002911 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002912 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002913
Vivien Didelot207afda2016-04-14 14:42:09 -04002914 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002915 * database, and allow bidirectional communication between the
2916 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002917 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002918 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002919 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002920 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002921
Andrew Lunn158bc062016-04-28 21:24:06 -04002922 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002923 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002924 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002925
2926 /* Default VLAN ID and priority: don't set a default VLAN
2927 * ID, and set the default packet priority to zero.
2928 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002929 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002930 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002931 if (ret)
2932 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002933
Andrew Lunndbde9e62015-05-06 01:09:48 +02002934 return 0;
2935}
2936
Vivien Didelot08a01262016-05-09 13:22:50 -04002937static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
2938{
Vivien Didelotb0745e872016-05-09 13:22:53 -04002939 struct dsa_switch *ds = ps->ds;
2940 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002941 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002942 int err;
2943 int i;
2944
Vivien Didelot119477b2016-05-09 13:22:51 -04002945 /* Enable the PHY Polling Unit if present, don't discard any packets,
2946 * and mask all interrupt sources.
2947 */
2948 reg = 0;
2949 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) ||
2950 mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE))
2951 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2952
2953 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg);
2954 if (err)
2955 return err;
2956
Vivien Didelotb0745e872016-05-09 13:22:53 -04002957 /* Configure the upstream port, and configure it as the port to which
2958 * ingress and egress and ARP monitor frames are to be sent.
2959 */
2960 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2961 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2962 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2963 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
2964 if (err)
2965 return err;
2966
Vivien Didelot50484ff2016-05-09 13:22:54 -04002967 /* Disable remote management, and set the switch's DSA device number. */
2968 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
2969 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2970 (ds->index & 0x1f));
2971 if (err)
2972 return err;
2973
Vivien Didelot08a01262016-05-09 13:22:50 -04002974 /* Set the default address aging time to 5 minutes, and
2975 * enable address learn messages to be sent to all message
2976 * ports.
2977 */
2978 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2979 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2980 if (err)
2981 return err;
2982
2983 /* Configure the IP ToS mapping registers. */
2984 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2985 if (err)
2986 return err;
2987 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2988 if (err)
2989 return err;
2990 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2991 if (err)
2992 return err;
2993 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2994 if (err)
2995 return err;
2996 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2997 if (err)
2998 return err;
2999 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
3000 if (err)
3001 return err;
3002 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
3003 if (err)
3004 return err;
3005 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
3006 if (err)
3007 return err;
3008
3009 /* Configure the IEEE 802.1p priority mapping register. */
3010 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
3011 if (err)
3012 return err;
3013
3014 /* Send all frames with destination addresses matching
3015 * 01:80:c2:00:00:0x to the CPU port.
3016 */
3017 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
3018 if (err)
3019 return err;
3020
3021 /* Ignore removed tag data on doubly tagged packets, disable
3022 * flow control messages, force flow control priority to the
3023 * highest, and send all special multicast frames to the CPU
3024 * port at the highest priority.
3025 */
3026 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
3027 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3028 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3029 if (err)
3030 return err;
3031
3032 /* Program the DSA routing table. */
3033 for (i = 0; i < 32; i++) {
3034 int nexthop = 0x1f;
3035
Andrew Lunnff049552016-05-10 23:27:24 +02003036 if (ps->ds->cd->rtable &&
Vivien Didelot08a01262016-05-09 13:22:50 -04003037 i != ps->ds->index && i < ps->ds->dst->pd->nr_chips)
Andrew Lunnff049552016-05-10 23:27:24 +02003038 nexthop = ps->ds->cd->rtable[i] & 0x1f;
Vivien Didelot08a01262016-05-09 13:22:50 -04003039
3040 err = _mv88e6xxx_reg_write(
3041 ps, REG_GLOBAL2,
3042 GLOBAL2_DEVICE_MAPPING,
3043 GLOBAL2_DEVICE_MAPPING_UPDATE |
3044 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
3045 if (err)
3046 return err;
3047 }
3048
3049 /* Clear all trunk masks. */
3050 for (i = 0; i < 8; i++) {
3051 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
3052 0x8000 |
3053 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
3054 ((1 << ps->info->num_ports) - 1));
3055 if (err)
3056 return err;
3057 }
3058
3059 /* Clear all trunk mappings. */
3060 for (i = 0; i < 16; i++) {
3061 err = _mv88e6xxx_reg_write(
3062 ps, REG_GLOBAL2,
3063 GLOBAL2_TRUNK_MAPPING,
3064 GLOBAL2_TRUNK_MAPPING_UPDATE |
3065 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
3066 if (err)
3067 return err;
3068 }
3069
3070 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3071 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3072 mv88e6xxx_6320_family(ps)) {
3073 /* Send all frames with destination addresses matching
3074 * 01:80:c2:00:00:2x to the CPU port.
3075 */
3076 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3077 GLOBAL2_MGMT_EN_2X, 0xffff);
3078 if (err)
3079 return err;
3080
3081 /* Initialise cross-chip port VLAN table to reset
3082 * defaults.
3083 */
3084 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3085 GLOBAL2_PVT_ADDR, 0x9000);
3086 if (err)
3087 return err;
3088
3089 /* Clear the priority override table. */
3090 for (i = 0; i < 16; i++) {
3091 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3092 GLOBAL2_PRIO_OVERRIDE,
3093 0x8000 | (i << 8));
3094 if (err)
3095 return err;
3096 }
3097 }
3098
3099 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3100 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3101 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
3102 mv88e6xxx_6320_family(ps)) {
3103 /* Disable ingress rate limiting by resetting all
3104 * ingress rate limit registers to their initial
3105 * state.
3106 */
3107 for (i = 0; i < ps->info->num_ports; i++) {
3108 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3109 GLOBAL2_INGRESS_OP,
3110 0x9000 | (i << 8));
3111 if (err)
3112 return err;
3113 }
3114 }
3115
3116 /* Clear the statistics counters for all ports */
3117 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
3118 GLOBAL_STATS_OP_FLUSH_ALL);
3119 if (err)
3120 return err;
3121
3122 /* Wait for the flush to complete. */
3123 err = _mv88e6xxx_stats_wait(ps);
3124 if (err)
3125 return err;
3126
3127 /* Clear all ATU entries */
3128 err = _mv88e6xxx_atu_flush(ps, 0, true);
3129 if (err)
3130 return err;
3131
3132 /* Clear all the VTU and STU entries */
3133 err = _mv88e6xxx_vtu_stu_flush(ps);
3134 if (err < 0)
3135 return err;
3136
3137 return err;
3138}
3139
Vivien Didelotf81ec902016-05-09 13:22:58 -04003140static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003141{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003142 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003143 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003144 int i;
3145
3146 ps->ds = ds;
Vivien Didelot552238b2016-05-09 13:22:49 -04003147
Guenter Roeckfacd95b2015-03-26 18:36:35 -07003148 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
3149
Vivien Didelotd24645b2016-05-09 13:22:41 -04003150 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
3151 mutex_init(&ps->eeprom_mutex);
3152
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003153 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3154 mv88e6xxx_ppu_state_init(ps);
3155
Vivien Didelot552238b2016-05-09 13:22:49 -04003156 mutex_lock(&ps->smi_mutex);
3157
3158 err = mv88e6xxx_switch_reset(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003159 if (err)
3160 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003161
Vivien Didelot08a01262016-05-09 13:22:50 -04003162 err = mv88e6xxx_setup_global(ps);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003163 if (err)
3164 goto unlock;
3165
3166 for (i = 0; i < ps->info->num_ports; i++) {
3167 err = mv88e6xxx_setup_port(ps, i);
3168 if (err)
3169 goto unlock;
3170 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003171
Vivien Didelot6b17e862015-08-13 12:52:18 -04003172unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04003173 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02003174
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003175 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003176}
3177
Andrew Lunn491435852015-04-02 04:06:35 +02003178int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
3179{
3180 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3181 int ret;
3182
Andrew Lunn3898c142015-05-06 01:09:53 +02003183 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003184 ret = _mv88e6xxx_phy_page_read(ps, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02003185 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003186
Andrew Lunn491435852015-04-02 04:06:35 +02003187 return ret;
3188}
3189
3190int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
3191 int reg, int val)
3192{
3193 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3194 int ret;
3195
Andrew Lunn3898c142015-05-06 01:09:53 +02003196 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003197 ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003198 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003199
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003200 return ret;
3201}
3202
Andrew Lunn158bc062016-04-28 21:24:06 -04003203static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
3204 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003205{
Vivien Didelot009a2b92016-04-17 13:24:01 -04003206 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003207 return port;
3208 return -EINVAL;
3209}
3210
Vivien Didelotf81ec902016-05-09 13:22:58 -04003211static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003212{
3213 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003214 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003215 int ret;
3216
3217 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003218 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003219
Andrew Lunn3898c142015-05-06 01:09:53 +02003220 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003221
3222 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3223 ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003224 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3225 ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003226 else
3227 ret = _mv88e6xxx_phy_read(ps, addr, regnum);
3228
Andrew Lunn3898c142015-05-06 01:09:53 +02003229 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003230 return ret;
3231}
3232
Vivien Didelotf81ec902016-05-09 13:22:58 -04003233static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum,
3234 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003235{
3236 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003237 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003238 int ret;
3239
3240 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003241 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003242
Andrew Lunn3898c142015-05-06 01:09:53 +02003243 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003244
3245 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3246 ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003247 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3248 ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003249 else
3250 ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
3251
Andrew Lunn3898c142015-05-06 01:09:53 +02003252 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003253 return ret;
3254}
3255
Guenter Roeckc22995c2015-07-25 09:42:28 -07003256#ifdef CONFIG_NET_DSA_HWMON
3257
3258static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3259{
3260 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3261 int ret;
3262 int val;
3263
3264 *temp = 0;
3265
3266 mutex_lock(&ps->smi_mutex);
3267
Andrew Lunn158bc062016-04-28 21:24:06 -04003268 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003269 if (ret < 0)
3270 goto error;
3271
3272 /* Enable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003273 ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003274 if (ret < 0)
3275 goto error;
3276
Andrew Lunn158bc062016-04-28 21:24:06 -04003277 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003278 if (ret < 0)
3279 goto error;
3280
3281 /* Wait for temperature to stabilize */
3282 usleep_range(10000, 12000);
3283
Andrew Lunn158bc062016-04-28 21:24:06 -04003284 val = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003285 if (val < 0) {
3286 ret = val;
3287 goto error;
3288 }
3289
3290 /* Disable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003291 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003292 if (ret < 0)
3293 goto error;
3294
3295 *temp = ((val & 0x1f) - 5) * 5;
3296
3297error:
Andrew Lunn158bc062016-04-28 21:24:06 -04003298 _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003299 mutex_unlock(&ps->smi_mutex);
3300 return ret;
3301}
3302
3303static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3304{
Andrew Lunn158bc062016-04-28 21:24:06 -04003305 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3306 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003307 int ret;
3308
3309 *temp = 0;
3310
3311 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
3312 if (ret < 0)
3313 return ret;
3314
3315 *temp = (ret & 0xff) - 25;
3316
3317 return 0;
3318}
3319
Vivien Didelotf81ec902016-05-09 13:22:58 -04003320static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003321{
Andrew Lunn158bc062016-04-28 21:24:06 -04003322 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3323
Vivien Didelot6594f612016-05-09 13:22:42 -04003324 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
3325 return -EOPNOTSUPP;
3326
Andrew Lunn158bc062016-04-28 21:24:06 -04003327 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003328 return mv88e63xx_get_temp(ds, temp);
3329
3330 return mv88e61xx_get_temp(ds, temp);
3331}
3332
Vivien Didelotf81ec902016-05-09 13:22:58 -04003333static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003334{
Andrew Lunn158bc062016-04-28 21:24:06 -04003335 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3336 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003337 int ret;
3338
Vivien Didelot6594f612016-05-09 13:22:42 -04003339 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003340 return -EOPNOTSUPP;
3341
3342 *temp = 0;
3343
3344 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3345 if (ret < 0)
3346 return ret;
3347
3348 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3349
3350 return 0;
3351}
3352
Vivien Didelotf81ec902016-05-09 13:22:58 -04003353static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003354{
Andrew Lunn158bc062016-04-28 21:24:06 -04003355 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3356 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003357 int ret;
3358
Vivien Didelot6594f612016-05-09 13:22:42 -04003359 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003360 return -EOPNOTSUPP;
3361
3362 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3363 if (ret < 0)
3364 return ret;
3365 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3366 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3367 (ret & 0xe0ff) | (temp << 8));
3368}
3369
Vivien Didelotf81ec902016-05-09 13:22:58 -04003370static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003371{
Andrew Lunn158bc062016-04-28 21:24:06 -04003372 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3373 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003374 int ret;
3375
Vivien Didelot6594f612016-05-09 13:22:42 -04003376 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003377 return -EOPNOTSUPP;
3378
3379 *alarm = false;
3380
3381 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3382 if (ret < 0)
3383 return ret;
3384
3385 *alarm = !!(ret & 0x40);
3386
3387 return 0;
3388}
3389#endif /* CONFIG_NET_DSA_HWMON */
3390
Vivien Didelotf81ec902016-05-09 13:22:58 -04003391static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3392 [MV88E6085] = {
3393 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3394 .family = MV88E6XXX_FAMILY_6097,
3395 .name = "Marvell 88E6085",
3396 .num_databases = 4096,
3397 .num_ports = 10,
3398 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3399 },
3400
3401 [MV88E6095] = {
3402 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3403 .family = MV88E6XXX_FAMILY_6095,
3404 .name = "Marvell 88E6095/88E6095F",
3405 .num_databases = 256,
3406 .num_ports = 11,
3407 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3408 },
3409
3410 [MV88E6123] = {
3411 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3412 .family = MV88E6XXX_FAMILY_6165,
3413 .name = "Marvell 88E6123",
3414 .num_databases = 4096,
3415 .num_ports = 3,
3416 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3417 },
3418
3419 [MV88E6131] = {
3420 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3421 .family = MV88E6XXX_FAMILY_6185,
3422 .name = "Marvell 88E6131",
3423 .num_databases = 256,
3424 .num_ports = 8,
3425 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3426 },
3427
3428 [MV88E6161] = {
3429 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3430 .family = MV88E6XXX_FAMILY_6165,
3431 .name = "Marvell 88E6161",
3432 .num_databases = 4096,
3433 .num_ports = 6,
3434 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3435 },
3436
3437 [MV88E6165] = {
3438 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3439 .family = MV88E6XXX_FAMILY_6165,
3440 .name = "Marvell 88E6165",
3441 .num_databases = 4096,
3442 .num_ports = 6,
3443 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3444 },
3445
3446 [MV88E6171] = {
3447 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3448 .family = MV88E6XXX_FAMILY_6351,
3449 .name = "Marvell 88E6171",
3450 .num_databases = 4096,
3451 .num_ports = 7,
3452 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3453 },
3454
3455 [MV88E6172] = {
3456 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3457 .family = MV88E6XXX_FAMILY_6352,
3458 .name = "Marvell 88E6172",
3459 .num_databases = 4096,
3460 .num_ports = 7,
3461 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3462 },
3463
3464 [MV88E6175] = {
3465 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3466 .family = MV88E6XXX_FAMILY_6351,
3467 .name = "Marvell 88E6175",
3468 .num_databases = 4096,
3469 .num_ports = 7,
3470 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3471 },
3472
3473 [MV88E6176] = {
3474 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3475 .family = MV88E6XXX_FAMILY_6352,
3476 .name = "Marvell 88E6176",
3477 .num_databases = 4096,
3478 .num_ports = 7,
3479 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3480 },
3481
3482 [MV88E6185] = {
3483 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3484 .family = MV88E6XXX_FAMILY_6185,
3485 .name = "Marvell 88E6185",
3486 .num_databases = 256,
3487 .num_ports = 10,
3488 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3489 },
3490
3491 [MV88E6240] = {
3492 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3493 .family = MV88E6XXX_FAMILY_6352,
3494 .name = "Marvell 88E6240",
3495 .num_databases = 4096,
3496 .num_ports = 7,
3497 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3498 },
3499
3500 [MV88E6320] = {
3501 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3502 .family = MV88E6XXX_FAMILY_6320,
3503 .name = "Marvell 88E6320",
3504 .num_databases = 4096,
3505 .num_ports = 7,
3506 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3507 },
3508
3509 [MV88E6321] = {
3510 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3511 .family = MV88E6XXX_FAMILY_6320,
3512 .name = "Marvell 88E6321",
3513 .num_databases = 4096,
3514 .num_ports = 7,
3515 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3516 },
3517
3518 [MV88E6350] = {
3519 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3520 .family = MV88E6XXX_FAMILY_6351,
3521 .name = "Marvell 88E6350",
3522 .num_databases = 4096,
3523 .num_ports = 7,
3524 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3525 },
3526
3527 [MV88E6351] = {
3528 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3529 .family = MV88E6XXX_FAMILY_6351,
3530 .name = "Marvell 88E6351",
3531 .num_databases = 4096,
3532 .num_ports = 7,
3533 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3534 },
3535
3536 [MV88E6352] = {
3537 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3538 .family = MV88E6XXX_FAMILY_6352,
3539 .name = "Marvell 88E6352",
3540 .num_databases = 4096,
3541 .num_ports = 7,
3542 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3543 },
3544};
3545
Vivien Didelotf6271e62016-04-17 13:23:59 -04003546static const struct mv88e6xxx_info *
3547mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003548 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003549{
Vivien Didelota439c062016-04-17 13:23:58 -04003550 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003551
Vivien Didelotb9b37712015-10-30 19:39:48 -04003552 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003553 if (table[i].prod_num == prod_num)
3554 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003555
Vivien Didelotb9b37712015-10-30 19:39:48 -04003556 return NULL;
3557}
3558
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003559static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3560 struct device *host_dev, int sw_addr,
3561 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003562{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003563 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003564 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003565 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003566 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003567 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003568
Vivien Didelota439c062016-04-17 13:23:58 -04003569 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003570 if (!bus)
3571 return NULL;
3572
Vivien Didelota439c062016-04-17 13:23:58 -04003573 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3574 if (id < 0)
3575 return NULL;
3576
3577 prod_num = (id & 0xfff0) >> 4;
3578 rev = id & 0x000f;
3579
Vivien Didelotf81ec902016-05-09 13:22:58 -04003580 info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3581 ARRAY_SIZE(mv88e6xxx_table));
Vivien Didelotf6271e62016-04-17 13:23:59 -04003582 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003583 return NULL;
3584
Vivien Didelotf6271e62016-04-17 13:23:59 -04003585 name = info->name;
3586
Vivien Didelota439c062016-04-17 13:23:58 -04003587 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3588 if (!ps)
3589 return NULL;
3590
3591 ps->bus = bus;
3592 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003593 ps->info = info;
Andrew Lunnb6819572016-05-10 23:27:19 +02003594 mutex_init(&ps->smi_mutex);
Vivien Didelota439c062016-04-17 13:23:58 -04003595
3596 *priv = ps;
3597
3598 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3599 prod_num, name, rev);
3600
Andrew Lunna77d43f2016-04-13 02:40:42 +02003601 return name;
3602}
3603
Vivien Didelotf81ec902016-05-09 13:22:58 -04003604struct dsa_switch_driver mv88e6xxx_switch_driver = {
3605 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003606 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003607 .setup = mv88e6xxx_setup,
3608 .set_addr = mv88e6xxx_set_addr,
3609 .phy_read = mv88e6xxx_phy_read,
3610 .phy_write = mv88e6xxx_phy_write,
3611 .adjust_link = mv88e6xxx_adjust_link,
3612 .get_strings = mv88e6xxx_get_strings,
3613 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3614 .get_sset_count = mv88e6xxx_get_sset_count,
3615 .set_eee = mv88e6xxx_set_eee,
3616 .get_eee = mv88e6xxx_get_eee,
3617#ifdef CONFIG_NET_DSA_HWMON
3618 .get_temp = mv88e6xxx_get_temp,
3619 .get_temp_limit = mv88e6xxx_get_temp_limit,
3620 .set_temp_limit = mv88e6xxx_set_temp_limit,
3621 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3622#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003623 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003624 .get_eeprom = mv88e6xxx_get_eeprom,
3625 .set_eeprom = mv88e6xxx_set_eeprom,
3626 .get_regs_len = mv88e6xxx_get_regs_len,
3627 .get_regs = mv88e6xxx_get_regs,
3628 .port_bridge_join = mv88e6xxx_port_bridge_join,
3629 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3630 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3631 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3632 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3633 .port_vlan_add = mv88e6xxx_port_vlan_add,
3634 .port_vlan_del = mv88e6xxx_port_vlan_del,
3635 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3636 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3637 .port_fdb_add = mv88e6xxx_port_fdb_add,
3638 .port_fdb_del = mv88e6xxx_port_fdb_del,
3639 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3640};
3641
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003642int mv88e6xxx_probe(struct mdio_device *mdiodev)
3643{
3644 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003645 struct device_node *np = dev->of_node;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003646 struct mv88e6xxx_priv_state *ps;
3647 int id, prod_num, rev;
3648 struct dsa_switch *ds;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003649 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003650 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003651
3652 ds = devm_kzalloc(dev, sizeof(*ds) + sizeof(*ps), GFP_KERNEL);
3653 if (!ds)
3654 return -ENOMEM;
3655
3656 ps = (struct mv88e6xxx_priv_state *)(ds + 1);
3657 ds->priv = ps;
Andrew Lunnc33063d2016-05-10 23:27:23 +02003658 ds->dev = dev;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003659 ps->dev = dev;
3660 ps->ds = ds;
3661 ps->bus = mdiodev->bus;
3662 ps->sw_addr = mdiodev->addr;
3663 mutex_init(&ps->smi_mutex);
3664
3665 get_device(&ps->bus->dev);
3666
3667 ds->drv = &mv88e6xxx_switch_driver;
3668
3669 id = mv88e6xxx_reg_read(ps, REG_PORT(0), PORT_SWITCH_ID);
3670 if (id < 0)
3671 return id;
3672
3673 prod_num = (id & 0xfff0) >> 4;
3674 rev = id & 0x000f;
3675
3676 ps->info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3677 ARRAY_SIZE(mv88e6xxx_table));
3678 if (!ps->info)
3679 return -ENODEV;
3680
Andrew Lunn52638f72016-05-10 23:27:22 +02003681 ps->reset = devm_gpiod_get(&mdiodev->dev, "reset", GPIOD_ASIS);
3682 if (IS_ERR(ps->reset)) {
3683 err = PTR_ERR(ps->reset);
3684 if (err == -ENOENT) {
3685 /* Optional, so not an error */
3686 ps->reset = NULL;
3687 } else {
3688 return err;
3689 }
3690 }
3691
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003692 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM) &&
3693 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3694 ps->eeprom_len = eeprom_len;
3695
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003696 dev_set_drvdata(dev, ds);
3697
3698 dev_info(dev, "switch 0x%x probed: %s, revision %u\n",
3699 prod_num, ps->info->name, rev);
3700
3701 return 0;
3702}
3703
3704static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3705{
3706 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
3707 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3708
3709 put_device(&ps->bus->dev);
3710}
3711
3712static const struct of_device_id mv88e6xxx_of_match[] = {
3713 { .compatible = "marvell,mv88e6085" },
3714 { /* sentinel */ },
3715};
3716
3717MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3718
3719static struct mdio_driver mv88e6xxx_driver = {
3720 .probe = mv88e6xxx_probe,
3721 .remove = mv88e6xxx_remove,
3722 .mdiodrv.driver = {
3723 .name = "mv88e6085",
3724 .of_match_table = mv88e6xxx_of_match,
3725 },
3726};
3727
Ben Hutchings98e67302011-11-25 14:36:19 +00003728static int __init mv88e6xxx_init(void)
3729{
Vivien Didelotf81ec902016-05-09 13:22:58 -04003730 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003731 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003732}
3733module_init(mv88e6xxx_init);
3734
3735static void __exit mv88e6xxx_cleanup(void)
3736{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003737 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04003738 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003739}
3740module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003741
3742MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3743MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3744MODULE_LICENSE("GPL");