blob: ae1cb191a0e0260de9e549fef915303ade62d941 [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
Vivien Didelotf81ec902016-05-09 13:22:58 -0400872static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
873 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400874{
875 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
876 int offset;
877 int len;
878 int ret;
879
880 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
881 return -EOPNOTSUPP;
882
883 offset = eeprom->offset;
884 len = eeprom->len;
885 eeprom->len = 0;
886
887 eeprom->magic = 0xc3ec4951;
888
889 ret = mv88e6xxx_eeprom_load_wait(ds);
890 if (ret < 0)
891 return ret;
892
893 if (offset & 1) {
894 int word;
895
896 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
897 if (word < 0)
898 return word;
899
900 *data++ = (word >> 8) & 0xff;
901
902 offset++;
903 len--;
904 eeprom->len++;
905 }
906
907 while (len >= 2) {
908 int word;
909
910 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
911 if (word < 0)
912 return word;
913
914 *data++ = word & 0xff;
915 *data++ = (word >> 8) & 0xff;
916
917 offset += 2;
918 len -= 2;
919 eeprom->len += 2;
920 }
921
922 if (len) {
923 int word;
924
925 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
926 if (word < 0)
927 return word;
928
929 *data++ = word & 0xff;
930
931 offset++;
932 len--;
933 eeprom->len++;
934 }
935
936 return 0;
937}
938
939static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
940{
941 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
942 int ret;
943
944 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
945 if (ret < 0)
946 return ret;
947
948 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
949 return -EROFS;
950
951 return 0;
952}
953
954static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
955 u16 data)
956{
957 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
958 int ret;
959
960 mutex_lock(&ps->eeprom_mutex);
961
962 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
963 if (ret < 0)
964 goto error;
965
966 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
967 GLOBAL2_EEPROM_OP_WRITE |
968 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
969 if (ret < 0)
970 goto error;
971
972 ret = mv88e6xxx_eeprom_busy_wait(ds);
973error:
974 mutex_unlock(&ps->eeprom_mutex);
975 return ret;
976}
977
Vivien Didelotf81ec902016-05-09 13:22:58 -0400978static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
979 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400980{
981 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
982 int offset;
983 int ret;
984 int len;
985
986 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
987 return -EOPNOTSUPP;
988
989 if (eeprom->magic != 0xc3ec4951)
990 return -EINVAL;
991
992 ret = mv88e6xxx_eeprom_is_readonly(ds);
993 if (ret)
994 return ret;
995
996 offset = eeprom->offset;
997 len = eeprom->len;
998 eeprom->len = 0;
999
1000 ret = mv88e6xxx_eeprom_load_wait(ds);
1001 if (ret < 0)
1002 return ret;
1003
1004 if (offset & 1) {
1005 int word;
1006
1007 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1008 if (word < 0)
1009 return word;
1010
1011 word = (*data++ << 8) | (word & 0xff);
1012
1013 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1014 if (ret < 0)
1015 return ret;
1016
1017 offset++;
1018 len--;
1019 eeprom->len++;
1020 }
1021
1022 while (len >= 2) {
1023 int word;
1024
1025 word = *data++;
1026 word |= *data++ << 8;
1027
1028 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1029 if (ret < 0)
1030 return ret;
1031
1032 offset += 2;
1033 len -= 2;
1034 eeprom->len += 2;
1035 }
1036
1037 if (len) {
1038 int word;
1039
1040 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1041 if (word < 0)
1042 return word;
1043
1044 word = (word & 0xff00) | *data++;
1045
1046 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1047 if (ret < 0)
1048 return ret;
1049
1050 offset++;
1051 len--;
1052 eeprom->len++;
1053 }
1054
1055 return 0;
1056}
1057
Andrew Lunn158bc062016-04-28 21:24:06 -04001058static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001059{
Andrew Lunn158bc062016-04-28 21:24:06 -04001060 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001061 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001062}
1063
Andrew Lunn158bc062016-04-28 21:24:06 -04001064static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
1065 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001066{
1067 int ret;
1068
Andrew Lunn158bc062016-04-28 21:24:06 -04001069 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001070 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1071 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001072 if (ret < 0)
1073 return ret;
1074
Andrew Lunn158bc062016-04-28 21:24:06 -04001075 ret = _mv88e6xxx_phy_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +02001076 if (ret < 0)
1077 return ret;
1078
Andrew Lunn158bc062016-04-28 21:24:06 -04001079 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
1080
1081 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001082}
1083
Andrew Lunn158bc062016-04-28 21:24:06 -04001084static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
1085 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001086{
Andrew Lunn3898c142015-05-06 01:09:53 +02001087 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001088
Andrew Lunn158bc062016-04-28 21:24:06 -04001089 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001090 if (ret < 0)
1091 return ret;
1092
Andrew Lunn158bc062016-04-28 21:24:06 -04001093 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001094 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1095 regnum);
1096
Andrew Lunn158bc062016-04-28 21:24:06 -04001097 return _mv88e6xxx_phy_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +01001098}
1099
Vivien Didelotf81ec902016-05-09 13:22:58 -04001100static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1101 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001102{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001103 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001104 int reg;
1105
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001106 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1107 return -EOPNOTSUPP;
1108
Andrew Lunn3898c142015-05-06 01:09:53 +02001109 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001110
Andrew Lunn158bc062016-04-28 21:24:06 -04001111 reg = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001112 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001113 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001114
1115 e->eee_enabled = !!(reg & 0x0200);
1116 e->tx_lpi_enabled = !!(reg & 0x0100);
1117
Andrew Lunn158bc062016-04-28 21:24:06 -04001118 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001119 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001120 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001121
Andrew Lunncca8b132015-04-02 04:06:39 +02001122 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001123 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001124
Andrew Lunn2f40c692015-04-02 04:06:37 +02001125out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001126 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001127 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001128}
1129
Vivien Didelotf81ec902016-05-09 13:22:58 -04001130static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1131 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001132{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001133 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1134 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001135 int ret;
1136
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001137 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1138 return -EOPNOTSUPP;
1139
Andrew Lunn3898c142015-05-06 01:09:53 +02001140 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001141
Andrew Lunn158bc062016-04-28 21:24:06 -04001142 ret = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001143 if (ret < 0)
1144 goto out;
1145
1146 reg = ret & ~0x0300;
1147 if (e->eee_enabled)
1148 reg |= 0x0200;
1149 if (e->tx_lpi_enabled)
1150 reg |= 0x0100;
1151
Andrew Lunn158bc062016-04-28 21:24:06 -04001152 ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001153out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001154 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001155
1156 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001157}
1158
Andrew Lunn158bc062016-04-28 21:24:06 -04001159static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001160{
1161 int ret;
1162
Andrew Lunn158bc062016-04-28 21:24:06 -04001163 if (mv88e6xxx_has_fid_reg(ps)) {
1164 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001165 if (ret < 0)
1166 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001167 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001168 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001169 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001170 if (ret < 0)
1171 return ret;
1172
Andrew Lunn158bc062016-04-28 21:24:06 -04001173 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001174 (ret & 0xfff) |
1175 ((fid << 8) & 0xf000));
1176 if (ret < 0)
1177 return ret;
1178
1179 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1180 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001181 }
1182
Andrew Lunn158bc062016-04-28 21:24:06 -04001183 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001184 if (ret < 0)
1185 return ret;
1186
Andrew Lunn158bc062016-04-28 21:24:06 -04001187 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001188}
1189
Andrew Lunn158bc062016-04-28 21:24:06 -04001190static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -04001191 struct mv88e6xxx_atu_entry *entry)
1192{
1193 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1194
1195 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1196 unsigned int mask, shift;
1197
1198 if (entry->trunk) {
1199 data |= GLOBAL_ATU_DATA_TRUNK;
1200 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1201 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1202 } else {
1203 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1204 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1205 }
1206
1207 data |= (entry->portv_trunkid << shift) & mask;
1208 }
1209
Andrew Lunn158bc062016-04-28 21:24:06 -04001210 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001211}
1212
Andrew Lunn158bc062016-04-28 21:24:06 -04001213static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001214 struct mv88e6xxx_atu_entry *entry,
1215 bool static_too)
1216{
1217 int op;
1218 int err;
1219
Andrew Lunn158bc062016-04-28 21:24:06 -04001220 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001221 if (err)
1222 return err;
1223
Andrew Lunn158bc062016-04-28 21:24:06 -04001224 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001225 if (err)
1226 return err;
1227
1228 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001229 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1230 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1231 } else {
1232 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1233 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1234 }
1235
Andrew Lunn158bc062016-04-28 21:24:06 -04001236 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001237}
1238
Andrew Lunn158bc062016-04-28 21:24:06 -04001239static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1240 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001241{
1242 struct mv88e6xxx_atu_entry entry = {
1243 .fid = fid,
1244 .state = 0, /* EntryState bits must be 0 */
1245 };
1246
Andrew Lunn158bc062016-04-28 21:24:06 -04001247 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001248}
1249
Andrew Lunn158bc062016-04-28 21:24:06 -04001250static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1251 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001252{
1253 struct mv88e6xxx_atu_entry entry = {
1254 .trunk = false,
1255 .fid = fid,
1256 };
1257
1258 /* EntryState bits must be 0xF */
1259 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1260
1261 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1262 entry.portv_trunkid = (to_port & 0x0f) << 4;
1263 entry.portv_trunkid |= from_port & 0x0f;
1264
Andrew Lunn158bc062016-04-28 21:24:06 -04001265 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001266}
1267
Andrew Lunn158bc062016-04-28 21:24:06 -04001268static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1269 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001270{
1271 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001272 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001273}
1274
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001275static const char * const mv88e6xxx_port_state_names[] = {
1276 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1277 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1278 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1279 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1280};
1281
Andrew Lunn158bc062016-04-28 21:24:06 -04001282static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1283 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001284{
Andrew Lunn158bc062016-04-28 21:24:06 -04001285 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001286 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001287 u8 oldstate;
1288
Andrew Lunn158bc062016-04-28 21:24:06 -04001289 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001290 if (reg < 0)
1291 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001292
Andrew Lunncca8b132015-04-02 04:06:39 +02001293 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001294
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001295 if (oldstate != state) {
1296 /* Flush forwarding database if we're moving a port
1297 * from Learning or Forwarding state to Disabled or
1298 * Blocking or Listening state.
1299 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001300 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1301 oldstate == PORT_CONTROL_STATE_FORWARDING)
1302 && (state == PORT_CONTROL_STATE_DISABLED ||
1303 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001304 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001305 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001306 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001307 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001308
Andrew Lunncca8b132015-04-02 04:06:39 +02001309 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001310 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001311 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001312 if (ret)
1313 return ret;
1314
1315 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1316 mv88e6xxx_port_state_names[state],
1317 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001318 }
1319
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001320 return ret;
1321}
1322
Andrew Lunn158bc062016-04-28 21:24:06 -04001323static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1324 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001325{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001326 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001327 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001328 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001329 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001330 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001331 int i;
1332
1333 /* allow CPU port or DSA link(s) to send frames to every port */
1334 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1335 output_ports = mask;
1336 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001337 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001338 /* allow sending frames to every group member */
1339 if (bridge && ps->ports[i].bridge_dev == bridge)
1340 output_ports |= BIT(i);
1341
1342 /* allow sending frames to CPU port and DSA link(s) */
1343 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1344 output_ports |= BIT(i);
1345 }
1346 }
1347
1348 /* prevent frames from going back out of the port they came in on */
1349 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001350
Andrew Lunn158bc062016-04-28 21:24:06 -04001351 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001352 if (reg < 0)
1353 return reg;
1354
1355 reg &= ~mask;
1356 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001357
Andrew Lunn158bc062016-04-28 21:24:06 -04001358 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001359}
1360
Vivien Didelotf81ec902016-05-09 13:22:58 -04001361static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1362 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001363{
1364 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1365 int stp_state;
1366
Vivien Didelot936f2342016-05-09 13:22:46 -04001367 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE))
1368 return;
1369
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001370 switch (state) {
1371 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001372 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001373 break;
1374 case BR_STATE_BLOCKING:
1375 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001376 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001377 break;
1378 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001379 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001380 break;
1381 case BR_STATE_FORWARDING:
1382 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001383 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001384 break;
1385 }
1386
Vivien Didelot43c44a92016-04-06 11:55:03 -04001387 /* mv88e6xxx_port_stp_state_set may be called with softirqs disabled,
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001388 * so we can not update the port state directly but need to schedule it.
1389 */
Vivien Didelotd715fa62016-02-12 12:09:38 -05001390 ps->ports[port].state = stp_state;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001391 set_bit(port, ps->port_state_update_mask);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001392 schedule_work(&ps->bridge_work);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001393}
1394
Andrew Lunn158bc062016-04-28 21:24:06 -04001395static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1396 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001397{
Andrew Lunn158bc062016-04-28 21:24:06 -04001398 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001399 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001400 int ret;
1401
Andrew Lunn158bc062016-04-28 21:24:06 -04001402 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001403 if (ret < 0)
1404 return ret;
1405
Vivien Didelot5da96032016-03-07 18:24:39 -05001406 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1407
1408 if (new) {
1409 ret &= ~PORT_DEFAULT_VLAN_MASK;
1410 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1411
Andrew Lunn158bc062016-04-28 21:24:06 -04001412 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001413 PORT_DEFAULT_VLAN, ret);
1414 if (ret < 0)
1415 return ret;
1416
1417 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1418 pvid);
1419 }
1420
1421 if (old)
1422 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001423
1424 return 0;
1425}
1426
Andrew Lunn158bc062016-04-28 21:24:06 -04001427static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1428 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001429{
Andrew Lunn158bc062016-04-28 21:24:06 -04001430 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001431}
1432
Andrew Lunn158bc062016-04-28 21:24:06 -04001433static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1434 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001435{
Andrew Lunn158bc062016-04-28 21:24:06 -04001436 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001437}
1438
Andrew Lunn158bc062016-04-28 21:24:06 -04001439static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001440{
Andrew Lunn158bc062016-04-28 21:24:06 -04001441 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001442 GLOBAL_VTU_OP_BUSY);
1443}
1444
Andrew Lunn158bc062016-04-28 21:24:06 -04001445static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001446{
1447 int ret;
1448
Andrew Lunn158bc062016-04-28 21:24:06 -04001449 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001450 if (ret < 0)
1451 return ret;
1452
Andrew Lunn158bc062016-04-28 21:24:06 -04001453 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001454}
1455
Andrew Lunn158bc062016-04-28 21:24:06 -04001456static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001457{
1458 int ret;
1459
Andrew Lunn158bc062016-04-28 21:24:06 -04001460 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001461 if (ret < 0)
1462 return ret;
1463
Andrew Lunn158bc062016-04-28 21:24:06 -04001464 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001465}
1466
Andrew Lunn158bc062016-04-28 21:24:06 -04001467static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001468 struct mv88e6xxx_vtu_stu_entry *entry,
1469 unsigned int nibble_offset)
1470{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001471 u16 regs[3];
1472 int i;
1473 int ret;
1474
1475 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001476 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001477 GLOBAL_VTU_DATA_0_3 + i);
1478 if (ret < 0)
1479 return ret;
1480
1481 regs[i] = ret;
1482 }
1483
Vivien Didelot009a2b92016-04-17 13:24:01 -04001484 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001485 unsigned int shift = (i % 4) * 4 + nibble_offset;
1486 u16 reg = regs[i / 4];
1487
1488 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1489 }
1490
1491 return 0;
1492}
1493
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001494static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps,
1495 struct mv88e6xxx_vtu_stu_entry *entry)
1496{
1497 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0);
1498}
1499
1500static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps,
1501 struct mv88e6xxx_vtu_stu_entry *entry)
1502{
1503 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2);
1504}
1505
Andrew Lunn158bc062016-04-28 21:24:06 -04001506static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001507 struct mv88e6xxx_vtu_stu_entry *entry,
1508 unsigned int nibble_offset)
1509{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001510 u16 regs[3] = { 0 };
1511 int i;
1512 int ret;
1513
Vivien Didelot009a2b92016-04-17 13:24:01 -04001514 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001515 unsigned int shift = (i % 4) * 4 + nibble_offset;
1516 u8 data = entry->data[i];
1517
1518 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1519 }
1520
1521 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001522 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001523 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1524 if (ret < 0)
1525 return ret;
1526 }
1527
1528 return 0;
1529}
1530
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001531static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps,
1532 struct mv88e6xxx_vtu_stu_entry *entry)
1533{
1534 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
1535}
1536
1537static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps,
1538 struct mv88e6xxx_vtu_stu_entry *entry)
1539{
1540 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
1541}
1542
Andrew Lunn158bc062016-04-28 21:24:06 -04001543static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001544{
Andrew Lunn158bc062016-04-28 21:24:06 -04001545 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001546 vid & GLOBAL_VTU_VID_MASK);
1547}
1548
Andrew Lunn158bc062016-04-28 21:24:06 -04001549static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001550 struct mv88e6xxx_vtu_stu_entry *entry)
1551{
1552 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1553 int ret;
1554
Andrew Lunn158bc062016-04-28 21:24:06 -04001555 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001556 if (ret < 0)
1557 return ret;
1558
Andrew Lunn158bc062016-04-28 21:24:06 -04001559 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001560 if (ret < 0)
1561 return ret;
1562
Andrew Lunn158bc062016-04-28 21:24:06 -04001563 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001564 if (ret < 0)
1565 return ret;
1566
1567 next.vid = ret & GLOBAL_VTU_VID_MASK;
1568 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1569
1570 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001571 ret = mv88e6xxx_vtu_data_read(ps, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001572 if (ret < 0)
1573 return ret;
1574
Andrew Lunn158bc062016-04-28 21:24:06 -04001575 if (mv88e6xxx_has_fid_reg(ps)) {
1576 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001577 GLOBAL_VTU_FID);
1578 if (ret < 0)
1579 return ret;
1580
1581 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001582 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001583 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1584 * VTU DBNum[3:0] are located in VTU Operation 3:0
1585 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001586 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001587 GLOBAL_VTU_OP);
1588 if (ret < 0)
1589 return ret;
1590
1591 next.fid = (ret & 0xf00) >> 4;
1592 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001593 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001594
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001595 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001596 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001597 GLOBAL_VTU_SID);
1598 if (ret < 0)
1599 return ret;
1600
1601 next.sid = ret & GLOBAL_VTU_SID_MASK;
1602 }
1603 }
1604
1605 *entry = next;
1606 return 0;
1607}
1608
Vivien Didelotf81ec902016-05-09 13:22:58 -04001609static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1610 struct switchdev_obj_port_vlan *vlan,
1611 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001612{
1613 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1614 struct mv88e6xxx_vtu_stu_entry next;
1615 u16 pvid;
1616 int err;
1617
Vivien Didelot54d77b52016-05-09 13:22:47 -04001618 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
1619 return -EOPNOTSUPP;
1620
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001621 mutex_lock(&ps->smi_mutex);
1622
Andrew Lunn158bc062016-04-28 21:24:06 -04001623 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001624 if (err)
1625 goto unlock;
1626
Andrew Lunn158bc062016-04-28 21:24:06 -04001627 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001628 if (err)
1629 goto unlock;
1630
1631 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001632 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001633 if (err)
1634 break;
1635
1636 if (!next.valid)
1637 break;
1638
1639 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1640 continue;
1641
1642 /* reinit and dump this VLAN obj */
1643 vlan->vid_begin = vlan->vid_end = next.vid;
1644 vlan->flags = 0;
1645
1646 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1647 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1648
1649 if (next.vid == pvid)
1650 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1651
1652 err = cb(&vlan->obj);
1653 if (err)
1654 break;
1655 } while (next.vid < GLOBAL_VTU_VID_MASK);
1656
1657unlock:
1658 mutex_unlock(&ps->smi_mutex);
1659
1660 return err;
1661}
1662
Andrew Lunn158bc062016-04-28 21:24:06 -04001663static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001664 struct mv88e6xxx_vtu_stu_entry *entry)
1665{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001666 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001667 u16 reg = 0;
1668 int ret;
1669
Andrew Lunn158bc062016-04-28 21:24:06 -04001670 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001671 if (ret < 0)
1672 return ret;
1673
1674 if (!entry->valid)
1675 goto loadpurge;
1676
1677 /* Write port member tags */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001678 ret = mv88e6xxx_vtu_data_write(ps, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001679 if (ret < 0)
1680 return ret;
1681
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001682 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001683 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001684 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001685 if (ret < 0)
1686 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001687 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001688
Andrew Lunn158bc062016-04-28 21:24:06 -04001689 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001690 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001691 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001692 if (ret < 0)
1693 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001694 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001695 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1696 * VTU DBNum[3:0] are located in VTU Operation 3:0
1697 */
1698 op |= (entry->fid & 0xf0) << 8;
1699 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001700 }
1701
1702 reg = GLOBAL_VTU_VID_VALID;
1703loadpurge:
1704 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001705 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001706 if (ret < 0)
1707 return ret;
1708
Andrew Lunn158bc062016-04-28 21:24:06 -04001709 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001710}
1711
Andrew Lunn158bc062016-04-28 21:24:06 -04001712static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001713 struct mv88e6xxx_vtu_stu_entry *entry)
1714{
1715 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1716 int ret;
1717
Andrew Lunn158bc062016-04-28 21:24:06 -04001718 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001719 if (ret < 0)
1720 return ret;
1721
Andrew Lunn158bc062016-04-28 21:24:06 -04001722 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001723 sid & GLOBAL_VTU_SID_MASK);
1724 if (ret < 0)
1725 return ret;
1726
Andrew Lunn158bc062016-04-28 21:24:06 -04001727 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001728 if (ret < 0)
1729 return ret;
1730
Andrew Lunn158bc062016-04-28 21:24:06 -04001731 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001732 if (ret < 0)
1733 return ret;
1734
1735 next.sid = ret & GLOBAL_VTU_SID_MASK;
1736
Andrew Lunn158bc062016-04-28 21:24:06 -04001737 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001738 if (ret < 0)
1739 return ret;
1740
1741 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1742
1743 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001744 ret = mv88e6xxx_stu_data_read(ps, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001745 if (ret < 0)
1746 return ret;
1747 }
1748
1749 *entry = next;
1750 return 0;
1751}
1752
Andrew Lunn158bc062016-04-28 21:24:06 -04001753static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001754 struct mv88e6xxx_vtu_stu_entry *entry)
1755{
1756 u16 reg = 0;
1757 int ret;
1758
Andrew Lunn158bc062016-04-28 21:24:06 -04001759 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001760 if (ret < 0)
1761 return ret;
1762
1763 if (!entry->valid)
1764 goto loadpurge;
1765
1766 /* Write port states */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001767 ret = mv88e6xxx_stu_data_write(ps, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001768 if (ret < 0)
1769 return ret;
1770
1771 reg = GLOBAL_VTU_VID_VALID;
1772loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001773 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001774 if (ret < 0)
1775 return ret;
1776
1777 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001778 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001779 if (ret < 0)
1780 return ret;
1781
Andrew Lunn158bc062016-04-28 21:24:06 -04001782 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001783}
1784
Andrew Lunn158bc062016-04-28 21:24:06 -04001785static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1786 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001787{
Andrew Lunn158bc062016-04-28 21:24:06 -04001788 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001789 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001790 u16 fid;
1791 int ret;
1792
Andrew Lunn158bc062016-04-28 21:24:06 -04001793 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001794 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001795 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001796 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001797 else
1798 return -EOPNOTSUPP;
1799
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001800 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001801 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001802 if (ret < 0)
1803 return ret;
1804
1805 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1806
1807 if (new) {
1808 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1809 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1810
Andrew Lunn158bc062016-04-28 21:24:06 -04001811 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001812 ret);
1813 if (ret < 0)
1814 return ret;
1815 }
1816
1817 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001818 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001819 if (ret < 0)
1820 return ret;
1821
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001822 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001823
1824 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001825 ret &= ~upper_mask;
1826 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001827
Andrew Lunn158bc062016-04-28 21:24:06 -04001828 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001829 ret);
1830 if (ret < 0)
1831 return ret;
1832
1833 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1834 }
1835
1836 if (old)
1837 *old = fid;
1838
1839 return 0;
1840}
1841
Andrew Lunn158bc062016-04-28 21:24:06 -04001842static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1843 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001844{
Andrew Lunn158bc062016-04-28 21:24:06 -04001845 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001846}
1847
Andrew Lunn158bc062016-04-28 21:24:06 -04001848static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1849 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001850{
Andrew Lunn158bc062016-04-28 21:24:06 -04001851 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001852}
1853
Andrew Lunn158bc062016-04-28 21:24:06 -04001854static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001855{
1856 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1857 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001858 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001859
1860 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1861
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001862 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001863 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001864 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001865 if (err)
1866 return err;
1867
1868 set_bit(*fid, fid_bitmap);
1869 }
1870
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001871 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001872 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001873 if (err)
1874 return err;
1875
1876 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001877 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001878 if (err)
1879 return err;
1880
1881 if (!vlan.valid)
1882 break;
1883
1884 set_bit(vlan.fid, fid_bitmap);
1885 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1886
1887 /* The reset value 0x000 is used to indicate that multiple address
1888 * databases are not needed. Return the next positive available.
1889 */
1890 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001891 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001892 return -ENOSPC;
1893
1894 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001895 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001896}
1897
Andrew Lunn158bc062016-04-28 21:24:06 -04001898static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001899 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001900{
Andrew Lunn158bc062016-04-28 21:24:06 -04001901 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001902 struct mv88e6xxx_vtu_stu_entry vlan = {
1903 .valid = true,
1904 .vid = vid,
1905 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001906 int i, err;
1907
Andrew Lunn158bc062016-04-28 21:24:06 -04001908 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001909 if (err)
1910 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001911
Vivien Didelot3d131f02015-11-03 10:52:52 -05001912 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001913 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001914 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1915 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1916 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001917
Andrew Lunn158bc062016-04-28 21:24:06 -04001918 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1919 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001920 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001921
1922 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1923 * implemented, only one STU entry is needed to cover all VTU
1924 * entries. Thus, validate the SID 0.
1925 */
1926 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001927 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001928 if (err)
1929 return err;
1930
1931 if (vstp.sid != vlan.sid || !vstp.valid) {
1932 memset(&vstp, 0, sizeof(vstp));
1933 vstp.valid = true;
1934 vstp.sid = vlan.sid;
1935
Andrew Lunn158bc062016-04-28 21:24:06 -04001936 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001937 if (err)
1938 return err;
1939 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001940 }
1941
1942 *entry = vlan;
1943 return 0;
1944}
1945
Andrew Lunn158bc062016-04-28 21:24:06 -04001946static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001947 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1948{
1949 int err;
1950
1951 if (!vid)
1952 return -EINVAL;
1953
Andrew Lunn158bc062016-04-28 21:24:06 -04001954 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001955 if (err)
1956 return err;
1957
Andrew Lunn158bc062016-04-28 21:24:06 -04001958 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001959 if (err)
1960 return err;
1961
1962 if (entry->vid != vid || !entry->valid) {
1963 if (!creat)
1964 return -EOPNOTSUPP;
1965 /* -ENOENT would've been more appropriate, but switchdev expects
1966 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1967 */
1968
Andrew Lunn158bc062016-04-28 21:24:06 -04001969 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001970 }
1971
1972 return err;
1973}
1974
Vivien Didelotda9c3592016-02-12 12:09:40 -05001975static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1976 u16 vid_begin, u16 vid_end)
1977{
1978 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1979 struct mv88e6xxx_vtu_stu_entry vlan;
1980 int i, err;
1981
1982 if (!vid_begin)
1983 return -EOPNOTSUPP;
1984
1985 mutex_lock(&ps->smi_mutex);
1986
Andrew Lunn158bc062016-04-28 21:24:06 -04001987 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001988 if (err)
1989 goto unlock;
1990
1991 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001992 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05001993 if (err)
1994 goto unlock;
1995
1996 if (!vlan.valid)
1997 break;
1998
1999 if (vlan.vid > vid_end)
2000 break;
2001
Vivien Didelot009a2b92016-04-17 13:24:01 -04002002 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05002003 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
2004 continue;
2005
2006 if (vlan.data[i] ==
2007 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2008 continue;
2009
2010 if (ps->ports[i].bridge_dev ==
2011 ps->ports[port].bridge_dev)
2012 break; /* same bridge, check next VLAN */
2013
2014 netdev_warn(ds->ports[port],
2015 "hardware VLAN %d already used by %s\n",
2016 vlan.vid,
2017 netdev_name(ps->ports[i].bridge_dev));
2018 err = -EOPNOTSUPP;
2019 goto unlock;
2020 }
2021 } while (vlan.vid < vid_end);
2022
2023unlock:
2024 mutex_unlock(&ps->smi_mutex);
2025
2026 return err;
2027}
2028
Vivien Didelot214cdb92016-02-26 13:16:08 -05002029static const char * const mv88e6xxx_port_8021q_mode_names[] = {
2030 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
2031 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
2032 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
2033 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
2034};
2035
Vivien Didelotf81ec902016-05-09 13:22:58 -04002036static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
2037 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002038{
2039 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2040 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
2041 PORT_CONTROL_2_8021Q_DISABLED;
2042 int ret;
2043
Vivien Didelot54d77b52016-05-09 13:22:47 -04002044 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2045 return -EOPNOTSUPP;
2046
Vivien Didelot214cdb92016-02-26 13:16:08 -05002047 mutex_lock(&ps->smi_mutex);
2048
Andrew Lunn158bc062016-04-28 21:24:06 -04002049 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002050 if (ret < 0)
2051 goto unlock;
2052
2053 old = ret & PORT_CONTROL_2_8021Q_MASK;
2054
Vivien Didelot5220ef12016-03-07 18:24:52 -05002055 if (new != old) {
2056 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2057 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002058
Andrew Lunn158bc062016-04-28 21:24:06 -04002059 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002060 ret);
2061 if (ret < 0)
2062 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002063
Vivien Didelot5220ef12016-03-07 18:24:52 -05002064 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
2065 mv88e6xxx_port_8021q_mode_names[new],
2066 mv88e6xxx_port_8021q_mode_names[old]);
2067 }
2068
2069 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002070unlock:
2071 mutex_unlock(&ps->smi_mutex);
2072
2073 return ret;
2074}
2075
Vivien Didelotf81ec902016-05-09 13:22:58 -04002076static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2077 const struct switchdev_obj_port_vlan *vlan,
2078 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002079{
Vivien Didelot54d77b52016-05-09 13:22:47 -04002080 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002081 int err;
2082
Vivien Didelot54d77b52016-05-09 13:22:47 -04002083 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2084 return -EOPNOTSUPP;
2085
Vivien Didelotda9c3592016-02-12 12:09:40 -05002086 /* If the requested port doesn't belong to the same bridge as the VLAN
2087 * members, do not support it (yet) and fallback to software VLAN.
2088 */
2089 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2090 vlan->vid_end);
2091 if (err)
2092 return err;
2093
Vivien Didelot76e398a2015-11-01 12:33:55 -05002094 /* We don't need any dynamic resource from the kernel (yet),
2095 * so skip the prepare phase.
2096 */
2097 return 0;
2098}
2099
Andrew Lunn158bc062016-04-28 21:24:06 -04002100static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
2101 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002102{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002103 struct mv88e6xxx_vtu_stu_entry vlan;
2104 int err;
2105
Andrew Lunn158bc062016-04-28 21:24:06 -04002106 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002107 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002108 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002109
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002110 vlan.data[port] = untagged ?
2111 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2112 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2113
Andrew Lunn158bc062016-04-28 21:24:06 -04002114 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002115}
2116
Vivien Didelotf81ec902016-05-09 13:22:58 -04002117static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2118 const struct switchdev_obj_port_vlan *vlan,
2119 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002120{
2121 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2122 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2123 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2124 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002125
Vivien Didelot54d77b52016-05-09 13:22:47 -04002126 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2127 return;
2128
Vivien Didelot76e398a2015-11-01 12:33:55 -05002129 mutex_lock(&ps->smi_mutex);
2130
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002131 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04002132 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002133 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
2134 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002135
Andrew Lunn158bc062016-04-28 21:24:06 -04002136 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002137 netdev_err(ds->ports[port], "failed to set PVID %d\n",
2138 vlan->vid_end);
2139
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002140 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002141}
2142
Andrew Lunn158bc062016-04-28 21:24:06 -04002143static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
2144 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002145{
Andrew Lunn158bc062016-04-28 21:24:06 -04002146 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002147 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002148 int i, err;
2149
Andrew Lunn158bc062016-04-28 21:24:06 -04002150 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002151 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002152 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002153
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002154 /* Tell switchdev if this VLAN is handled in software */
2155 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002156 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002157
2158 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2159
2160 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002161 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04002162 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002163 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002164 continue;
2165
2166 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002167 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002168 break;
2169 }
2170 }
2171
Andrew Lunn158bc062016-04-28 21:24:06 -04002172 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002173 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002174 return err;
2175
Andrew Lunn158bc062016-04-28 21:24:06 -04002176 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002177}
2178
Vivien Didelotf81ec902016-05-09 13:22:58 -04002179static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2180 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002181{
2182 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2183 u16 pvid, vid;
2184 int err = 0;
2185
Vivien Didelot54d77b52016-05-09 13:22:47 -04002186 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2187 return -EOPNOTSUPP;
2188
Vivien Didelot76e398a2015-11-01 12:33:55 -05002189 mutex_lock(&ps->smi_mutex);
2190
Andrew Lunn158bc062016-04-28 21:24:06 -04002191 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002192 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002193 goto unlock;
2194
Vivien Didelot76e398a2015-11-01 12:33:55 -05002195 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002196 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002197 if (err)
2198 goto unlock;
2199
2200 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002201 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002202 if (err)
2203 goto unlock;
2204 }
2205 }
2206
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002207unlock:
2208 mutex_unlock(&ps->smi_mutex);
2209
2210 return err;
2211}
2212
Andrew Lunn158bc062016-04-28 21:24:06 -04002213static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002214 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002215{
2216 int i, ret;
2217
2218 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002219 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002220 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002221 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002222 if (ret < 0)
2223 return ret;
2224 }
2225
2226 return 0;
2227}
2228
Andrew Lunn158bc062016-04-28 21:24:06 -04002229static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
2230 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002231{
2232 int i, ret;
2233
2234 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002235 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002236 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002237 if (ret < 0)
2238 return ret;
2239 addr[i * 2] = ret >> 8;
2240 addr[i * 2 + 1] = ret & 0xff;
2241 }
2242
2243 return 0;
2244}
2245
Andrew Lunn158bc062016-04-28 21:24:06 -04002246static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002247 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002248{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002249 int ret;
2250
Andrew Lunn158bc062016-04-28 21:24:06 -04002251 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002252 if (ret < 0)
2253 return ret;
2254
Andrew Lunn158bc062016-04-28 21:24:06 -04002255 ret = _mv88e6xxx_atu_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002256 if (ret < 0)
2257 return ret;
2258
Andrew Lunn158bc062016-04-28 21:24:06 -04002259 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002260 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002261 return ret;
2262
Andrew Lunn158bc062016-04-28 21:24:06 -04002263 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002264}
David S. Millercdf09692015-08-11 12:00:37 -07002265
Andrew Lunn158bc062016-04-28 21:24:06 -04002266static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002267 const unsigned char *addr, u16 vid,
2268 u8 state)
2269{
2270 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002271 struct mv88e6xxx_vtu_stu_entry vlan;
2272 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002273
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002274 /* Null VLAN ID corresponds to the port private database */
2275 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002276 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002277 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002278 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002279 if (err)
2280 return err;
2281
2282 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002283 entry.state = state;
2284 ether_addr_copy(entry.mac, addr);
2285 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2286 entry.trunk = false;
2287 entry.portv_trunkid = BIT(port);
2288 }
2289
Andrew Lunn158bc062016-04-28 21:24:06 -04002290 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002291}
2292
Vivien Didelotf81ec902016-05-09 13:22:58 -04002293static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2294 const struct switchdev_obj_port_fdb *fdb,
2295 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002296{
Vivien Didelot2672f822016-05-09 13:22:48 -04002297 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2298
2299 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2300 return -EOPNOTSUPP;
2301
Vivien Didelot146a3202015-10-08 11:35:12 -04002302 /* We don't need any dynamic resource from the kernel (yet),
2303 * so skip the prepare phase.
2304 */
2305 return 0;
2306}
2307
Vivien Didelotf81ec902016-05-09 13:22:58 -04002308static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2309 const struct switchdev_obj_port_fdb *fdb,
2310 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002311{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002312 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002313 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2314 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2315 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002316
Vivien Didelot2672f822016-05-09 13:22:48 -04002317 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2318 return;
2319
David S. Millercdf09692015-08-11 12:00:37 -07002320 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002321 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Vivien Didelot8497aa62016-04-06 11:55:04 -04002322 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002323 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002324}
2325
Vivien Didelotf81ec902016-05-09 13:22:58 -04002326static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2327 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002328{
2329 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2330 int ret;
2331
Vivien Didelot2672f822016-05-09 13:22:48 -04002332 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2333 return -EOPNOTSUPP;
2334
David S. Millercdf09692015-08-11 12:00:37 -07002335 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002336 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002337 GLOBAL_ATU_DATA_STATE_UNUSED);
2338 mutex_unlock(&ps->smi_mutex);
2339
2340 return ret;
2341}
2342
Andrew Lunn158bc062016-04-28 21:24:06 -04002343static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002344 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002345{
Vivien Didelot1d194042015-08-10 09:09:51 -04002346 struct mv88e6xxx_atu_entry next = { 0 };
2347 int ret;
2348
2349 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002350
Andrew Lunn158bc062016-04-28 21:24:06 -04002351 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002352 if (ret < 0)
2353 return ret;
2354
Andrew Lunn158bc062016-04-28 21:24:06 -04002355 ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002356 if (ret < 0)
2357 return ret;
2358
Andrew Lunn158bc062016-04-28 21:24:06 -04002359 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002360 if (ret < 0)
2361 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002362
Andrew Lunn158bc062016-04-28 21:24:06 -04002363 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002364 if (ret < 0)
2365 return ret;
2366
2367 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2368 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2369 unsigned int mask, shift;
2370
2371 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2372 next.trunk = true;
2373 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2374 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2375 } else {
2376 next.trunk = false;
2377 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2378 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2379 }
2380
2381 next.portv_trunkid = (ret & mask) >> shift;
2382 }
2383
2384 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002385 return 0;
2386}
2387
Andrew Lunn158bc062016-04-28 21:24:06 -04002388static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2389 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002390 struct switchdev_obj_port_fdb *fdb,
2391 int (*cb)(struct switchdev_obj *obj))
2392{
2393 struct mv88e6xxx_atu_entry addr = {
2394 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2395 };
2396 int err;
2397
Andrew Lunn158bc062016-04-28 21:24:06 -04002398 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002399 if (err)
2400 return err;
2401
2402 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002403 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002404 if (err)
2405 break;
2406
2407 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2408 break;
2409
2410 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2411 bool is_static = addr.state ==
2412 (is_multicast_ether_addr(addr.mac) ?
2413 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2414 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2415
2416 fdb->vid = vid;
2417 ether_addr_copy(fdb->addr, addr.mac);
2418 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2419
2420 err = cb(&fdb->obj);
2421 if (err)
2422 break;
2423 }
2424 } while (!is_broadcast_ether_addr(addr.mac));
2425
2426 return err;
2427}
2428
Vivien Didelotf81ec902016-05-09 13:22:58 -04002429static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2430 struct switchdev_obj_port_fdb *fdb,
2431 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002432{
2433 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2434 struct mv88e6xxx_vtu_stu_entry vlan = {
2435 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2436 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002437 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002438 int err;
2439
Vivien Didelot2672f822016-05-09 13:22:48 -04002440 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2441 return -EOPNOTSUPP;
2442
Vivien Didelotf33475b2015-10-22 09:34:41 -04002443 mutex_lock(&ps->smi_mutex);
2444
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002445 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002446 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002447 if (err)
2448 goto unlock;
2449
Andrew Lunn158bc062016-04-28 21:24:06 -04002450 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002451 if (err)
2452 goto unlock;
2453
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002454 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002455 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002456 if (err)
2457 goto unlock;
2458
2459 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002460 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002461 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002462 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002463
2464 if (!vlan.valid)
2465 break;
2466
Andrew Lunn158bc062016-04-28 21:24:06 -04002467 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002468 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002469 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002470 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002471 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2472
2473unlock:
2474 mutex_unlock(&ps->smi_mutex);
2475
2476 return err;
2477}
2478
Vivien Didelotf81ec902016-05-09 13:22:58 -04002479static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2480 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002481{
Vivien Didelota6692752016-02-12 12:09:39 -05002482 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002483 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002484
Vivien Didelot936f2342016-05-09 13:22:46 -04002485 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2486 return -EOPNOTSUPP;
2487
Vivien Didelot466dfa02016-02-26 13:16:05 -05002488 mutex_lock(&ps->smi_mutex);
2489
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002490 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002491 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002492
Vivien Didelot009a2b92016-04-17 13:24:01 -04002493 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002494 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002495 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002496 if (err)
2497 break;
2498 }
2499 }
2500
Vivien Didelot466dfa02016-02-26 13:16:05 -05002501 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002502
Vivien Didelot466dfa02016-02-26 13:16:05 -05002503 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002504}
2505
Vivien Didelotf81ec902016-05-09 13:22:58 -04002506static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002507{
Vivien Didelota6692752016-02-12 12:09:39 -05002508 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002509 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002510 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002511
Vivien Didelot936f2342016-05-09 13:22:46 -04002512 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2513 return;
2514
Vivien Didelot466dfa02016-02-26 13:16:05 -05002515 mutex_lock(&ps->smi_mutex);
2516
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002517 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002518 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002519
Vivien Didelot009a2b92016-04-17 13:24:01 -04002520 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002521 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002522 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Vivien Didelot16bfa702016-03-13 16:21:33 -04002523 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002524
Vivien Didelot466dfa02016-02-26 13:16:05 -05002525 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002526}
2527
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002528static void mv88e6xxx_bridge_work(struct work_struct *work)
2529{
2530 struct mv88e6xxx_priv_state *ps;
2531 struct dsa_switch *ds;
2532 int port;
2533
2534 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
Andrew Lunn7543a6d2016-04-13 02:40:40 +02002535 ds = ps->ds;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002536
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002537 mutex_lock(&ps->smi_mutex);
2538
Vivien Didelot009a2b92016-04-17 13:24:01 -04002539 for (port = 0; port < ps->info->num_ports; ++port)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002540 if (test_and_clear_bit(port, ps->port_state_update_mask) &&
Andrew Lunn158bc062016-04-28 21:24:06 -04002541 _mv88e6xxx_port_state(ps, port, ps->ports[port].state))
2542 netdev_warn(ds->ports[port],
2543 "failed to update state to %s\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05002544 mv88e6xxx_port_state_names[ps->ports[port].state]);
2545
2546 mutex_unlock(&ps->smi_mutex);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002547}
2548
Andrew Lunn158bc062016-04-28 21:24:06 -04002549static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps,
2550 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002551{
2552 int ret;
2553
Andrew Lunn158bc062016-04-28 21:24:06 -04002554 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002555 if (ret < 0)
2556 goto restore_page_0;
2557
Andrew Lunn158bc062016-04-28 21:24:06 -04002558 ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002559restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002560 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002561
2562 return ret;
2563}
2564
Andrew Lunn158bc062016-04-28 21:24:06 -04002565static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps,
2566 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002567{
2568 int ret;
2569
Andrew Lunn158bc062016-04-28 21:24:06 -04002570 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002571 if (ret < 0)
2572 goto restore_page_0;
2573
Andrew Lunn158bc062016-04-28 21:24:06 -04002574 ret = _mv88e6xxx_phy_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002575restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002576 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002577
2578 return ret;
2579}
2580
Vivien Didelot552238b2016-05-09 13:22:49 -04002581static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps)
2582{
2583 bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE);
2584 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
2585 struct gpio_desc *gpiod = ps->ds->pd->reset;
2586 unsigned long timeout;
2587 int ret;
2588 int i;
2589
2590 /* Set all ports to the disabled state. */
2591 for (i = 0; i < ps->info->num_ports; i++) {
2592 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
2593 if (ret < 0)
2594 return ret;
2595
2596 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
2597 ret & 0xfffc);
2598 if (ret)
2599 return ret;
2600 }
2601
2602 /* Wait for transmit queues to drain. */
2603 usleep_range(2000, 4000);
2604
2605 /* If there is a gpio connected to the reset pin, toggle it */
2606 if (gpiod) {
2607 gpiod_set_value_cansleep(gpiod, 1);
2608 usleep_range(10000, 20000);
2609 gpiod_set_value_cansleep(gpiod, 0);
2610 usleep_range(10000, 20000);
2611 }
2612
2613 /* Reset the switch. Keep the PPU active if requested. The PPU
2614 * needs to be active to support indirect phy register access
2615 * through global registers 0x18 and 0x19.
2616 */
2617 if (ppu_active)
2618 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
2619 else
2620 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
2621 if (ret)
2622 return ret;
2623
2624 /* Wait up to one second for reset to complete. */
2625 timeout = jiffies + 1 * HZ;
2626 while (time_before(jiffies, timeout)) {
2627 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
2628 if (ret < 0)
2629 return ret;
2630
2631 if ((ret & is_reset) == is_reset)
2632 break;
2633 usleep_range(1000, 2000);
2634 }
2635 if (time_after(jiffies, timeout))
2636 ret = -ETIMEDOUT;
2637 else
2638 ret = 0;
2639
2640 return ret;
2641}
2642
Andrew Lunn158bc062016-04-28 21:24:06 -04002643static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002644{
2645 int ret;
2646
Andrew Lunn158bc062016-04-28 21:24:06 -04002647 ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002648 MII_BMCR);
2649 if (ret < 0)
2650 return ret;
2651
2652 if (ret & BMCR_PDOWN) {
2653 ret &= ~BMCR_PDOWN;
Andrew Lunn158bc062016-04-28 21:24:06 -04002654 ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002655 PAGE_FIBER_SERDES, MII_BMCR,
2656 ret);
2657 }
2658
2659 return ret;
2660}
2661
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002662static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002663{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002664 struct dsa_switch *ds = ps->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002665 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002666 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002667
Andrew Lunn158bc062016-04-28 21:24:06 -04002668 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2669 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2670 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2671 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002672 /* MAC Forcing register: don't force link, speed,
2673 * duplex or flow control state to any particular
2674 * values on physical ports, but force the CPU port
2675 * and all DSA ports to their maximum bandwidth and
2676 * full duplex.
2677 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002678 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002679 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002680 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002681 reg |= PORT_PCS_CTRL_FORCE_LINK |
2682 PORT_PCS_CTRL_LINK_UP |
2683 PORT_PCS_CTRL_DUPLEX_FULL |
2684 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002685 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002686 reg |= PORT_PCS_CTRL_100;
2687 else
2688 reg |= PORT_PCS_CTRL_1000;
2689 } else {
2690 reg |= PORT_PCS_CTRL_UNFORCED;
2691 }
2692
Andrew Lunn158bc062016-04-28 21:24:06 -04002693 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002694 PORT_PCS_CTRL, reg);
2695 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002696 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002697 }
2698
2699 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2700 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2701 * tunneling, determine priority by looking at 802.1p and IP
2702 * priority fields (IP prio has precedence), and set STP state
2703 * to Forwarding.
2704 *
2705 * If this is the CPU link, use DSA or EDSA tagging depending
2706 * on which tagging mode was configured.
2707 *
2708 * If this is a link to another switch, use DSA tagging mode.
2709 *
2710 * If this is the upstream port for this switch, enable
2711 * forwarding of unknown unicasts and multicasts.
2712 */
2713 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002714 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2715 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2716 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2717 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002718 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2719 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2720 PORT_CONTROL_STATE_FORWARDING;
2721 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002722 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002723 reg |= PORT_CONTROL_DSA_TAG;
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_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002727 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2728 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2729 else
2730 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002731 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2732 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002733 }
2734
Andrew Lunn158bc062016-04-28 21:24:06 -04002735 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2736 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2737 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2738 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002739 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2740 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2741 }
2742 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002743 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002744 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002745 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002746 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2747 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2748 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002749 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002750 }
2751
Andrew Lunn54d792f2015-05-06 01:09:47 +02002752 if (port == dsa_upstream_port(ds))
2753 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2754 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2755 }
2756 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002757 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002758 PORT_CONTROL, reg);
2759 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002760 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002761 }
2762
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002763 /* If this port is connected to a SerDes, make sure the SerDes is not
2764 * powered down.
2765 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002766 if (mv88e6xxx_6352_family(ps)) {
2767 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002768 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002769 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002770 ret &= PORT_STATUS_CMODE_MASK;
2771 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2772 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2773 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002774 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002775 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002776 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002777 }
2778 }
2779
Vivien Didelot8efdda42015-08-13 12:52:23 -04002780 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002781 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002782 * untagged frames on this port, do a destination address lookup on all
2783 * received packets as usual, disable ARP mirroring and don't send a
2784 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002785 */
2786 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002787 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2788 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2789 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2790 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002791 reg = PORT_CONTROL_2_MAP_DA;
2792
Andrew Lunn158bc062016-04-28 21:24:06 -04002793 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2794 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002795 reg |= PORT_CONTROL_2_JUMBO_10240;
2796
Andrew Lunn158bc062016-04-28 21:24:06 -04002797 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002798 /* Set the upstream port this port should use */
2799 reg |= dsa_upstream_port(ds);
2800 /* enable forwarding of unknown multicast addresses to
2801 * the upstream port
2802 */
2803 if (port == dsa_upstream_port(ds))
2804 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2805 }
2806
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002807 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002808
Andrew Lunn54d792f2015-05-06 01:09:47 +02002809 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002810 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002811 PORT_CONTROL_2, reg);
2812 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002813 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002814 }
2815
2816 /* Port Association Vector: when learning source addresses
2817 * of packets, add the address to the address database using
2818 * a port bitmap that has only the bit for this port set and
2819 * the other bits clear.
2820 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002821 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002822 /* Disable learning for CPU port */
2823 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002824 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002825
Andrew Lunn158bc062016-04-28 21:24:06 -04002826 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002827 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002828 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002829
2830 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002831 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002832 0x0000);
2833 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002834 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002835
Andrew Lunn158bc062016-04-28 21:24:06 -04002836 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2837 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2838 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002839 /* Do not limit the period of time that this port can
2840 * be paused for by the remote end or the period of
2841 * time that this port can pause the remote end.
2842 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002843 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002844 PORT_PAUSE_CTRL, 0x0000);
2845 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002846 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002847
2848 /* Port ATU control: disable limiting the number of
2849 * address database entries that this port is allowed
2850 * to use.
2851 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002852 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002853 PORT_ATU_CONTROL, 0x0000);
2854 /* Priority Override: disable DA, SA and VTU priority
2855 * override.
2856 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002857 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002858 PORT_PRI_OVERRIDE, 0x0000);
2859 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002860 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002861
2862 /* Port Ethertype: use the Ethertype DSA Ethertype
2863 * value.
2864 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002865 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002866 PORT_ETH_TYPE, ETH_P_EDSA);
2867 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002868 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002869 /* Tag Remap: use an identity 802.1p prio -> switch
2870 * prio mapping.
2871 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002872 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002873 PORT_TAG_REGMAP_0123, 0x3210);
2874 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002875 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002876
2877 /* Tag Remap 2: use an identity 802.1p prio -> switch
2878 * prio mapping.
2879 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002880 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002881 PORT_TAG_REGMAP_4567, 0x7654);
2882 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002883 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002884 }
2885
Andrew Lunn158bc062016-04-28 21:24:06 -04002886 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2887 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2888 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2889 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002890 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002891 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002892 PORT_RATE_CONTROL, 0x0001);
2893 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002894 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002895 }
2896
Guenter Roeck366f0a02015-03-26 18:36:30 -07002897 /* Port Control 1: disable trunking, disable sending
2898 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002899 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002900 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002901 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002902 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002903
Vivien Didelot207afda2016-04-14 14:42:09 -04002904 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002905 * database, and allow bidirectional communication between the
2906 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002907 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002908 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002909 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002910 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002911
Andrew Lunn158bc062016-04-28 21:24:06 -04002912 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002913 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002914 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002915
2916 /* Default VLAN ID and priority: don't set a default VLAN
2917 * ID, and set the default packet priority to zero.
2918 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002919 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002920 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002921 if (ret)
2922 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002923
Andrew Lunndbde9e62015-05-06 01:09:48 +02002924 return 0;
2925}
2926
Vivien Didelot08a01262016-05-09 13:22:50 -04002927static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
2928{
Vivien Didelotb0745e872016-05-09 13:22:53 -04002929 struct dsa_switch *ds = ps->ds;
2930 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002931 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002932 int err;
2933 int i;
2934
Vivien Didelot119477b2016-05-09 13:22:51 -04002935 /* Enable the PHY Polling Unit if present, don't discard any packets,
2936 * and mask all interrupt sources.
2937 */
2938 reg = 0;
2939 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) ||
2940 mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE))
2941 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2942
2943 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg);
2944 if (err)
2945 return err;
2946
Vivien Didelotb0745e872016-05-09 13:22:53 -04002947 /* Configure the upstream port, and configure it as the port to which
2948 * ingress and egress and ARP monitor frames are to be sent.
2949 */
2950 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2951 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2952 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2953 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
2954 if (err)
2955 return err;
2956
Vivien Didelot50484ff2016-05-09 13:22:54 -04002957 /* Disable remote management, and set the switch's DSA device number. */
2958 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
2959 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2960 (ds->index & 0x1f));
2961 if (err)
2962 return err;
2963
Vivien Didelot08a01262016-05-09 13:22:50 -04002964 /* Set the default address aging time to 5 minutes, and
2965 * enable address learn messages to be sent to all message
2966 * ports.
2967 */
2968 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2969 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2970 if (err)
2971 return err;
2972
2973 /* Configure the IP ToS mapping registers. */
2974 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2975 if (err)
2976 return err;
2977 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2978 if (err)
2979 return err;
2980 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2981 if (err)
2982 return err;
2983 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2984 if (err)
2985 return err;
2986 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2987 if (err)
2988 return err;
2989 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2990 if (err)
2991 return err;
2992 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2993 if (err)
2994 return err;
2995 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2996 if (err)
2997 return err;
2998
2999 /* Configure the IEEE 802.1p priority mapping register. */
3000 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
3001 if (err)
3002 return err;
3003
3004 /* Send all frames with destination addresses matching
3005 * 01:80:c2:00:00:0x to the CPU port.
3006 */
3007 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
3008 if (err)
3009 return err;
3010
3011 /* Ignore removed tag data on doubly tagged packets, disable
3012 * flow control messages, force flow control priority to the
3013 * highest, and send all special multicast frames to the CPU
3014 * port at the highest priority.
3015 */
3016 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
3017 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3018 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3019 if (err)
3020 return err;
3021
3022 /* Program the DSA routing table. */
3023 for (i = 0; i < 32; i++) {
3024 int nexthop = 0x1f;
3025
3026 if (ps->ds->pd->rtable &&
3027 i != ps->ds->index && i < ps->ds->dst->pd->nr_chips)
3028 nexthop = ps->ds->pd->rtable[i] & 0x1f;
3029
3030 err = _mv88e6xxx_reg_write(
3031 ps, REG_GLOBAL2,
3032 GLOBAL2_DEVICE_MAPPING,
3033 GLOBAL2_DEVICE_MAPPING_UPDATE |
3034 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
3035 if (err)
3036 return err;
3037 }
3038
3039 /* Clear all trunk masks. */
3040 for (i = 0; i < 8; i++) {
3041 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
3042 0x8000 |
3043 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
3044 ((1 << ps->info->num_ports) - 1));
3045 if (err)
3046 return err;
3047 }
3048
3049 /* Clear all trunk mappings. */
3050 for (i = 0; i < 16; i++) {
3051 err = _mv88e6xxx_reg_write(
3052 ps, REG_GLOBAL2,
3053 GLOBAL2_TRUNK_MAPPING,
3054 GLOBAL2_TRUNK_MAPPING_UPDATE |
3055 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
3056 if (err)
3057 return err;
3058 }
3059
3060 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3061 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3062 mv88e6xxx_6320_family(ps)) {
3063 /* Send all frames with destination addresses matching
3064 * 01:80:c2:00:00:2x to the CPU port.
3065 */
3066 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3067 GLOBAL2_MGMT_EN_2X, 0xffff);
3068 if (err)
3069 return err;
3070
3071 /* Initialise cross-chip port VLAN table to reset
3072 * defaults.
3073 */
3074 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3075 GLOBAL2_PVT_ADDR, 0x9000);
3076 if (err)
3077 return err;
3078
3079 /* Clear the priority override table. */
3080 for (i = 0; i < 16; i++) {
3081 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3082 GLOBAL2_PRIO_OVERRIDE,
3083 0x8000 | (i << 8));
3084 if (err)
3085 return err;
3086 }
3087 }
3088
3089 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3090 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3091 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
3092 mv88e6xxx_6320_family(ps)) {
3093 /* Disable ingress rate limiting by resetting all
3094 * ingress rate limit registers to their initial
3095 * state.
3096 */
3097 for (i = 0; i < ps->info->num_ports; i++) {
3098 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3099 GLOBAL2_INGRESS_OP,
3100 0x9000 | (i << 8));
3101 if (err)
3102 return err;
3103 }
3104 }
3105
3106 /* Clear the statistics counters for all ports */
3107 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
3108 GLOBAL_STATS_OP_FLUSH_ALL);
3109 if (err)
3110 return err;
3111
3112 /* Wait for the flush to complete. */
3113 err = _mv88e6xxx_stats_wait(ps);
3114 if (err)
3115 return err;
3116
3117 /* Clear all ATU entries */
3118 err = _mv88e6xxx_atu_flush(ps, 0, true);
3119 if (err)
3120 return err;
3121
3122 /* Clear all the VTU and STU entries */
3123 err = _mv88e6xxx_vtu_stu_flush(ps);
3124 if (err < 0)
3125 return err;
3126
3127 return err;
3128}
3129
Vivien Didelotf81ec902016-05-09 13:22:58 -04003130static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003131{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003132 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003133 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003134 int i;
3135
3136 ps->ds = ds;
Vivien Didelot552238b2016-05-09 13:22:49 -04003137
Guenter Roeckfacd95b2015-03-26 18:36:35 -07003138 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
3139
Vivien Didelotd24645b2016-05-09 13:22:41 -04003140 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
3141 mutex_init(&ps->eeprom_mutex);
3142
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003143 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3144 mv88e6xxx_ppu_state_init(ps);
3145
Vivien Didelot552238b2016-05-09 13:22:49 -04003146 mutex_lock(&ps->smi_mutex);
3147
3148 err = mv88e6xxx_switch_reset(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003149 if (err)
3150 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003151
Vivien Didelot08a01262016-05-09 13:22:50 -04003152 err = mv88e6xxx_setup_global(ps);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003153 if (err)
3154 goto unlock;
3155
3156 for (i = 0; i < ps->info->num_ports; i++) {
3157 err = mv88e6xxx_setup_port(ps, i);
3158 if (err)
3159 goto unlock;
3160 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003161
Vivien Didelot6b17e862015-08-13 12:52:18 -04003162unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04003163 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02003164
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003165 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003166}
3167
Andrew Lunn491435852015-04-02 04:06:35 +02003168int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
3169{
3170 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3171 int ret;
3172
Andrew Lunn3898c142015-05-06 01:09:53 +02003173 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003174 ret = _mv88e6xxx_phy_page_read(ps, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02003175 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003176
Andrew Lunn491435852015-04-02 04:06:35 +02003177 return ret;
3178}
3179
3180int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
3181 int reg, int val)
3182{
3183 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3184 int ret;
3185
Andrew Lunn3898c142015-05-06 01:09:53 +02003186 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003187 ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003188 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003189
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003190 return ret;
3191}
3192
Andrew Lunn158bc062016-04-28 21:24:06 -04003193static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
3194 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003195{
Vivien Didelot009a2b92016-04-17 13:24:01 -04003196 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003197 return port;
3198 return -EINVAL;
3199}
3200
Vivien Didelotf81ec902016-05-09 13:22:58 -04003201static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003202{
3203 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003204 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003205 int ret;
3206
3207 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003208 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003209
Andrew Lunn3898c142015-05-06 01:09:53 +02003210 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003211
3212 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3213 ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003214 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3215 ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003216 else
3217 ret = _mv88e6xxx_phy_read(ps, addr, regnum);
3218
Andrew Lunn3898c142015-05-06 01:09:53 +02003219 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003220 return ret;
3221}
3222
Vivien Didelotf81ec902016-05-09 13:22:58 -04003223static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum,
3224 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003225{
3226 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003227 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003228 int ret;
3229
3230 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003231 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003232
Andrew Lunn3898c142015-05-06 01:09:53 +02003233 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003234
3235 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3236 ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003237 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3238 ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003239 else
3240 ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
3241
Andrew Lunn3898c142015-05-06 01:09:53 +02003242 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003243 return ret;
3244}
3245
Guenter Roeckc22995c2015-07-25 09:42:28 -07003246#ifdef CONFIG_NET_DSA_HWMON
3247
3248static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3249{
3250 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3251 int ret;
3252 int val;
3253
3254 *temp = 0;
3255
3256 mutex_lock(&ps->smi_mutex);
3257
Andrew Lunn158bc062016-04-28 21:24:06 -04003258 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003259 if (ret < 0)
3260 goto error;
3261
3262 /* Enable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003263 ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003264 if (ret < 0)
3265 goto error;
3266
Andrew Lunn158bc062016-04-28 21:24:06 -04003267 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003268 if (ret < 0)
3269 goto error;
3270
3271 /* Wait for temperature to stabilize */
3272 usleep_range(10000, 12000);
3273
Andrew Lunn158bc062016-04-28 21:24:06 -04003274 val = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003275 if (val < 0) {
3276 ret = val;
3277 goto error;
3278 }
3279
3280 /* Disable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003281 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003282 if (ret < 0)
3283 goto error;
3284
3285 *temp = ((val & 0x1f) - 5) * 5;
3286
3287error:
Andrew Lunn158bc062016-04-28 21:24:06 -04003288 _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003289 mutex_unlock(&ps->smi_mutex);
3290 return ret;
3291}
3292
3293static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3294{
Andrew Lunn158bc062016-04-28 21:24:06 -04003295 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3296 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003297 int ret;
3298
3299 *temp = 0;
3300
3301 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
3302 if (ret < 0)
3303 return ret;
3304
3305 *temp = (ret & 0xff) - 25;
3306
3307 return 0;
3308}
3309
Vivien Didelotf81ec902016-05-09 13:22:58 -04003310static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003311{
Andrew Lunn158bc062016-04-28 21:24:06 -04003312 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3313
Vivien Didelot6594f612016-05-09 13:22:42 -04003314 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
3315 return -EOPNOTSUPP;
3316
Andrew Lunn158bc062016-04-28 21:24:06 -04003317 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003318 return mv88e63xx_get_temp(ds, temp);
3319
3320 return mv88e61xx_get_temp(ds, temp);
3321}
3322
Vivien Didelotf81ec902016-05-09 13:22:58 -04003323static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003324{
Andrew Lunn158bc062016-04-28 21:24:06 -04003325 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3326 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003327 int ret;
3328
Vivien Didelot6594f612016-05-09 13:22:42 -04003329 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003330 return -EOPNOTSUPP;
3331
3332 *temp = 0;
3333
3334 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3335 if (ret < 0)
3336 return ret;
3337
3338 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3339
3340 return 0;
3341}
3342
Vivien Didelotf81ec902016-05-09 13:22:58 -04003343static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003344{
Andrew Lunn158bc062016-04-28 21:24:06 -04003345 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3346 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003347 int ret;
3348
Vivien Didelot6594f612016-05-09 13:22:42 -04003349 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003350 return -EOPNOTSUPP;
3351
3352 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3353 if (ret < 0)
3354 return ret;
3355 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3356 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3357 (ret & 0xe0ff) | (temp << 8));
3358}
3359
Vivien Didelotf81ec902016-05-09 13:22:58 -04003360static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003361{
Andrew Lunn158bc062016-04-28 21:24:06 -04003362 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3363 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003364 int ret;
3365
Vivien Didelot6594f612016-05-09 13:22:42 -04003366 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003367 return -EOPNOTSUPP;
3368
3369 *alarm = false;
3370
3371 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3372 if (ret < 0)
3373 return ret;
3374
3375 *alarm = !!(ret & 0x40);
3376
3377 return 0;
3378}
3379#endif /* CONFIG_NET_DSA_HWMON */
3380
Vivien Didelotf81ec902016-05-09 13:22:58 -04003381static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3382 [MV88E6085] = {
3383 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3384 .family = MV88E6XXX_FAMILY_6097,
3385 .name = "Marvell 88E6085",
3386 .num_databases = 4096,
3387 .num_ports = 10,
3388 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3389 },
3390
3391 [MV88E6095] = {
3392 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3393 .family = MV88E6XXX_FAMILY_6095,
3394 .name = "Marvell 88E6095/88E6095F",
3395 .num_databases = 256,
3396 .num_ports = 11,
3397 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3398 },
3399
3400 [MV88E6123] = {
3401 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3402 .family = MV88E6XXX_FAMILY_6165,
3403 .name = "Marvell 88E6123",
3404 .num_databases = 4096,
3405 .num_ports = 3,
3406 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3407 },
3408
3409 [MV88E6131] = {
3410 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3411 .family = MV88E6XXX_FAMILY_6185,
3412 .name = "Marvell 88E6131",
3413 .num_databases = 256,
3414 .num_ports = 8,
3415 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3416 },
3417
3418 [MV88E6161] = {
3419 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3420 .family = MV88E6XXX_FAMILY_6165,
3421 .name = "Marvell 88E6161",
3422 .num_databases = 4096,
3423 .num_ports = 6,
3424 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3425 },
3426
3427 [MV88E6165] = {
3428 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3429 .family = MV88E6XXX_FAMILY_6165,
3430 .name = "Marvell 88E6165",
3431 .num_databases = 4096,
3432 .num_ports = 6,
3433 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3434 },
3435
3436 [MV88E6171] = {
3437 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3438 .family = MV88E6XXX_FAMILY_6351,
3439 .name = "Marvell 88E6171",
3440 .num_databases = 4096,
3441 .num_ports = 7,
3442 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3443 },
3444
3445 [MV88E6172] = {
3446 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3447 .family = MV88E6XXX_FAMILY_6352,
3448 .name = "Marvell 88E6172",
3449 .num_databases = 4096,
3450 .num_ports = 7,
3451 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3452 },
3453
3454 [MV88E6175] = {
3455 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3456 .family = MV88E6XXX_FAMILY_6351,
3457 .name = "Marvell 88E6175",
3458 .num_databases = 4096,
3459 .num_ports = 7,
3460 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3461 },
3462
3463 [MV88E6176] = {
3464 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3465 .family = MV88E6XXX_FAMILY_6352,
3466 .name = "Marvell 88E6176",
3467 .num_databases = 4096,
3468 .num_ports = 7,
3469 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3470 },
3471
3472 [MV88E6185] = {
3473 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3474 .family = MV88E6XXX_FAMILY_6185,
3475 .name = "Marvell 88E6185",
3476 .num_databases = 256,
3477 .num_ports = 10,
3478 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3479 },
3480
3481 [MV88E6240] = {
3482 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3483 .family = MV88E6XXX_FAMILY_6352,
3484 .name = "Marvell 88E6240",
3485 .num_databases = 4096,
3486 .num_ports = 7,
3487 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3488 },
3489
3490 [MV88E6320] = {
3491 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3492 .family = MV88E6XXX_FAMILY_6320,
3493 .name = "Marvell 88E6320",
3494 .num_databases = 4096,
3495 .num_ports = 7,
3496 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3497 },
3498
3499 [MV88E6321] = {
3500 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3501 .family = MV88E6XXX_FAMILY_6320,
3502 .name = "Marvell 88E6321",
3503 .num_databases = 4096,
3504 .num_ports = 7,
3505 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3506 },
3507
3508 [MV88E6350] = {
3509 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3510 .family = MV88E6XXX_FAMILY_6351,
3511 .name = "Marvell 88E6350",
3512 .num_databases = 4096,
3513 .num_ports = 7,
3514 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3515 },
3516
3517 [MV88E6351] = {
3518 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3519 .family = MV88E6XXX_FAMILY_6351,
3520 .name = "Marvell 88E6351",
3521 .num_databases = 4096,
3522 .num_ports = 7,
3523 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3524 },
3525
3526 [MV88E6352] = {
3527 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3528 .family = MV88E6XXX_FAMILY_6352,
3529 .name = "Marvell 88E6352",
3530 .num_databases = 4096,
3531 .num_ports = 7,
3532 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3533 },
3534};
3535
Vivien Didelotf6271e62016-04-17 13:23:59 -04003536static const struct mv88e6xxx_info *
3537mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003538 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003539{
Vivien Didelota439c062016-04-17 13:23:58 -04003540 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003541
Vivien Didelotb9b37712015-10-30 19:39:48 -04003542 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003543 if (table[i].prod_num == prod_num)
3544 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003545
Vivien Didelotb9b37712015-10-30 19:39:48 -04003546 return NULL;
3547}
3548
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003549static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3550 struct device *host_dev, int sw_addr,
3551 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003552{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003553 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003554 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003555 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003556 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003557 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003558
Vivien Didelota439c062016-04-17 13:23:58 -04003559 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003560 if (!bus)
3561 return NULL;
3562
Vivien Didelota439c062016-04-17 13:23:58 -04003563 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3564 if (id < 0)
3565 return NULL;
3566
3567 prod_num = (id & 0xfff0) >> 4;
3568 rev = id & 0x000f;
3569
Vivien Didelotf81ec902016-05-09 13:22:58 -04003570 info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3571 ARRAY_SIZE(mv88e6xxx_table));
Vivien Didelotf6271e62016-04-17 13:23:59 -04003572 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003573 return NULL;
3574
Vivien Didelotf6271e62016-04-17 13:23:59 -04003575 name = info->name;
3576
Vivien Didelota439c062016-04-17 13:23:58 -04003577 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3578 if (!ps)
3579 return NULL;
3580
3581 ps->bus = bus;
3582 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003583 ps->info = info;
Andrew Lunnb6819572016-05-10 23:27:19 +02003584 mutex_init(&ps->smi_mutex);
Vivien Didelota439c062016-04-17 13:23:58 -04003585
3586 *priv = ps;
3587
3588 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3589 prod_num, name, rev);
3590
Andrew Lunna77d43f2016-04-13 02:40:42 +02003591 return name;
3592}
3593
Vivien Didelotf81ec902016-05-09 13:22:58 -04003594struct dsa_switch_driver mv88e6xxx_switch_driver = {
3595 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003596 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003597 .setup = mv88e6xxx_setup,
3598 .set_addr = mv88e6xxx_set_addr,
3599 .phy_read = mv88e6xxx_phy_read,
3600 .phy_write = mv88e6xxx_phy_write,
3601 .adjust_link = mv88e6xxx_adjust_link,
3602 .get_strings = mv88e6xxx_get_strings,
3603 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3604 .get_sset_count = mv88e6xxx_get_sset_count,
3605 .set_eee = mv88e6xxx_set_eee,
3606 .get_eee = mv88e6xxx_get_eee,
3607#ifdef CONFIG_NET_DSA_HWMON
3608 .get_temp = mv88e6xxx_get_temp,
3609 .get_temp_limit = mv88e6xxx_get_temp_limit,
3610 .set_temp_limit = mv88e6xxx_set_temp_limit,
3611 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3612#endif
3613 .get_eeprom = mv88e6xxx_get_eeprom,
3614 .set_eeprom = mv88e6xxx_set_eeprom,
3615 .get_regs_len = mv88e6xxx_get_regs_len,
3616 .get_regs = mv88e6xxx_get_regs,
3617 .port_bridge_join = mv88e6xxx_port_bridge_join,
3618 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3619 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3620 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3621 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3622 .port_vlan_add = mv88e6xxx_port_vlan_add,
3623 .port_vlan_del = mv88e6xxx_port_vlan_del,
3624 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3625 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3626 .port_fdb_add = mv88e6xxx_port_fdb_add,
3627 .port_fdb_del = mv88e6xxx_port_fdb_del,
3628 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3629};
3630
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003631int mv88e6xxx_probe(struct mdio_device *mdiodev)
3632{
3633 struct device *dev = &mdiodev->dev;
3634 struct mv88e6xxx_priv_state *ps;
3635 int id, prod_num, rev;
3636 struct dsa_switch *ds;
3637
3638 ds = devm_kzalloc(dev, sizeof(*ds) + sizeof(*ps), GFP_KERNEL);
3639 if (!ds)
3640 return -ENOMEM;
3641
3642 ps = (struct mv88e6xxx_priv_state *)(ds + 1);
3643 ds->priv = ps;
3644 ps->dev = dev;
3645 ps->ds = ds;
3646 ps->bus = mdiodev->bus;
3647 ps->sw_addr = mdiodev->addr;
3648 mutex_init(&ps->smi_mutex);
3649
3650 get_device(&ps->bus->dev);
3651
3652 ds->drv = &mv88e6xxx_switch_driver;
3653
3654 id = mv88e6xxx_reg_read(ps, REG_PORT(0), PORT_SWITCH_ID);
3655 if (id < 0)
3656 return id;
3657
3658 prod_num = (id & 0xfff0) >> 4;
3659 rev = id & 0x000f;
3660
3661 ps->info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3662 ARRAY_SIZE(mv88e6xxx_table));
3663 if (!ps->info)
3664 return -ENODEV;
3665
3666 dev_set_drvdata(dev, ds);
3667
3668 dev_info(dev, "switch 0x%x probed: %s, revision %u\n",
3669 prod_num, ps->info->name, rev);
3670
3671 return 0;
3672}
3673
3674static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3675{
3676 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
3677 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3678
3679 put_device(&ps->bus->dev);
3680}
3681
3682static const struct of_device_id mv88e6xxx_of_match[] = {
3683 { .compatible = "marvell,mv88e6085" },
3684 { /* sentinel */ },
3685};
3686
3687MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3688
3689static struct mdio_driver mv88e6xxx_driver = {
3690 .probe = mv88e6xxx_probe,
3691 .remove = mv88e6xxx_remove,
3692 .mdiodrv.driver = {
3693 .name = "mv88e6085",
3694 .of_match_table = mv88e6xxx_of_match,
3695 },
3696};
3697
Ben Hutchings98e67302011-11-25 14:36:19 +00003698static int __init mv88e6xxx_init(void)
3699{
Vivien Didelotf81ec902016-05-09 13:22:58 -04003700 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003701 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003702}
3703module_init(mv88e6xxx_init);
3704
3705static void __exit mv88e6xxx_cleanup(void)
3706{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003707 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04003708 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003709}
3710module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003711
3712MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3713MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3714MODULE_LICENSE("GPL");