blob: ba9dfc9421ef806581f8df8a0afc418c857e3201 [file] [log] [blame]
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00001/*
2 * net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support
3 * Copyright (c) 2008 Marvell Semiconductor
4 *
Vivien Didelotb8fee952015-08-13 12:52:19 -04005 * Copyright (c) 2015 CMC Electronics, Inc.
6 * Added support for VLAN Table Unit operations
7 *
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02008 * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch>
9 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000010 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
15
Barry Grussling19b2f972013-01-08 16:05:54 +000016#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070017#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020018#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070019#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000020#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000021#include <linux/list.h>
Andrew Lunn14c7b3c2016-05-10 23:27:21 +020022#include <linux/mdio.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000023#include <linux/module.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000024#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010025#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000026#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000027#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040028#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000029#include "mv88e6xxx.h"
30
Andrew Lunn158bc062016-04-28 21:24:06 -040031static void assert_smi_lock(struct mv88e6xxx_priv_state *ps)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040032{
Vivien Didelot3996a4f2015-10-30 18:56:45 -040033 if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
Andrew Lunn158bc062016-04-28 21:24:06 -040034 dev_err(ps->dev, "SMI lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040035 dump_stack();
36 }
37}
38
Barry Grussling3675c8d2013-01-08 16:05:53 +000039/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000040 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
41 * will be directly accessible on some {device address,register address}
42 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
43 * will only respond to SMI transactions to that specific address, and
44 * an indirect addressing mechanism needs to be used to access its
45 * registers.
46 */
47static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
48{
49 int ret;
50 int i;
51
52 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020053 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000054 if (ret < 0)
55 return ret;
56
Andrew Lunncca8b132015-04-02 04:06:39 +020057 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000058 return 0;
59 }
60
61 return -ETIMEDOUT;
62}
63
Vivien Didelotb9b37712015-10-30 19:39:48 -040064static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
65 int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000066{
67 int ret;
68
69 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020070 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000071
Barry Grussling3675c8d2013-01-08 16:05:53 +000072 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000073 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
74 if (ret < 0)
75 return ret;
76
Barry Grussling3675c8d2013-01-08 16:05:53 +000077 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020078 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
79 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000080 if (ret < 0)
81 return ret;
82
Barry Grussling3675c8d2013-01-08 16:05:53 +000083 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000084 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
85 if (ret < 0)
86 return ret;
87
Barry Grussling3675c8d2013-01-08 16:05:53 +000088 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020089 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000090 if (ret < 0)
91 return ret;
92
93 return ret & 0xffff;
94}
95
Andrew Lunn158bc062016-04-28 21:24:06 -040096static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
97 int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000098{
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000099 int ret;
100
Andrew Lunn158bc062016-04-28 21:24:06 -0400101 assert_smi_lock(ps);
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400102
Andrew Lunna77d43f2016-04-13 02:40:42 +0200103 ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500104 if (ret < 0)
105 return ret;
106
Andrew Lunn158bc062016-04-28 21:24:06 -0400107 dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500108 addr, reg, ret);
109
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000110 return ret;
111}
112
Andrew Lunn158bc062016-04-28 21:24:06 -0400113int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700114{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700115 int ret;
116
117 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400118 ret = _mv88e6xxx_reg_read(ps, addr, reg);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700119 mutex_unlock(&ps->smi_mutex);
120
121 return ret;
122}
123
Vivien Didelotb9b37712015-10-30 19:39:48 -0400124static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
125 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000126{
127 int ret;
128
129 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200130 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000131
Barry Grussling3675c8d2013-01-08 16:05:53 +0000132 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000133 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
134 if (ret < 0)
135 return ret;
136
Barry Grussling3675c8d2013-01-08 16:05:53 +0000137 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200138 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000139 if (ret < 0)
140 return ret;
141
Barry Grussling3675c8d2013-01-08 16:05:53 +0000142 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200143 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
144 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000145 if (ret < 0)
146 return ret;
147
Barry Grussling3675c8d2013-01-08 16:05:53 +0000148 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000149 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
150 if (ret < 0)
151 return ret;
152
153 return 0;
154}
155
Andrew Lunn158bc062016-04-28 21:24:06 -0400156static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
157 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000158{
Andrew Lunn158bc062016-04-28 21:24:06 -0400159 assert_smi_lock(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000160
Andrew Lunn158bc062016-04-28 21:24:06 -0400161 dev_dbg(ps->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500162 addr, reg, val);
163
Andrew Lunna77d43f2016-04-13 02:40:42 +0200164 return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700165}
166
Andrew Lunn158bc062016-04-28 21:24:06 -0400167int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
168 int reg, u16 val)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700169{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700170 int ret;
171
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000172 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400173 ret = _mv88e6xxx_reg_write(ps, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000174 mutex_unlock(&ps->smi_mutex);
175
176 return ret;
177}
178
Vivien Didelot1d13a062016-05-09 13:22:43 -0400179static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000180{
Andrew Lunn158bc062016-04-28 21:24:06 -0400181 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200182 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000183
Andrew Lunn158bc062016-04-28 21:24:06 -0400184 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_01,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200185 (addr[0] << 8) | addr[1]);
186 if (err)
187 return err;
188
Andrew Lunn158bc062016-04-28 21:24:06 -0400189 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_23,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200190 (addr[2] << 8) | addr[3]);
191 if (err)
192 return err;
193
Andrew Lunn158bc062016-04-28 21:24:06 -0400194 return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_45,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200195 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000196}
197
Vivien Didelot1d13a062016-05-09 13:22:43 -0400198static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000199{
Andrew Lunn158bc062016-04-28 21:24:06 -0400200 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000201 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200202 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000203
204 for (i = 0; i < 6; i++) {
205 int j;
206
Barry Grussling3675c8d2013-01-08 16:05:53 +0000207 /* Write the MAC address byte. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400208 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200209 GLOBAL2_SWITCH_MAC_BUSY |
210 (i << 8) | addr[i]);
211 if (ret)
212 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000213
Barry Grussling3675c8d2013-01-08 16:05:53 +0000214 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000215 for (j = 0; j < 16; j++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400216 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200217 GLOBAL2_SWITCH_MAC);
218 if (ret < 0)
219 return ret;
220
Andrew Lunncca8b132015-04-02 04:06:39 +0200221 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000222 break;
223 }
224 if (j == 16)
225 return -ETIMEDOUT;
226 }
227
228 return 0;
229}
230
Vivien Didelot1d13a062016-05-09 13:22:43 -0400231int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
232{
233 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
234
235 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC))
236 return mv88e6xxx_set_addr_indirect(ds, addr);
237 else
238 return mv88e6xxx_set_addr_direct(ds, addr);
239}
240
Andrew Lunn158bc062016-04-28 21:24:06 -0400241static int _mv88e6xxx_phy_read(struct mv88e6xxx_priv_state *ps, int addr,
242 int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000243{
244 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400245 return _mv88e6xxx_reg_read(ps, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000246 return 0xffff;
247}
248
Andrew Lunn158bc062016-04-28 21:24:06 -0400249static int _mv88e6xxx_phy_write(struct mv88e6xxx_priv_state *ps, int addr,
250 int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000251{
252 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400253 return _mv88e6xxx_reg_write(ps, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000254 return 0;
255}
256
Andrew Lunn158bc062016-04-28 21:24:06 -0400257static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000258{
259 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000260 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000261
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400262 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200263 if (ret < 0)
264 return ret;
265
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400266 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
267 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200268 if (ret)
269 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000270
Barry Grussling19b2f972013-01-08 16:05:54 +0000271 timeout = jiffies + 1 * HZ;
272 while (time_before(jiffies, timeout)) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400273 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200274 if (ret < 0)
275 return ret;
276
Barry Grussling19b2f972013-01-08 16:05:54 +0000277 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200278 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
279 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000280 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000281 }
282
283 return -ETIMEDOUT;
284}
285
Andrew Lunn158bc062016-04-28 21:24:06 -0400286static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000287{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200288 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000289 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000290
Andrew Lunn158bc062016-04-28 21:24:06 -0400291 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200292 if (ret < 0)
293 return ret;
294
Andrew Lunn158bc062016-04-28 21:24:06 -0400295 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200296 ret | GLOBAL_CONTROL_PPU_ENABLE);
297 if (err)
298 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000299
Barry Grussling19b2f972013-01-08 16:05:54 +0000300 timeout = jiffies + 1 * HZ;
301 while (time_before(jiffies, timeout)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400302 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200303 if (ret < 0)
304 return ret;
305
Barry Grussling19b2f972013-01-08 16:05:54 +0000306 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200307 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
308 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000309 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000310 }
311
312 return -ETIMEDOUT;
313}
314
315static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
316{
317 struct mv88e6xxx_priv_state *ps;
318
319 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
320 if (mutex_trylock(&ps->ppu_mutex)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400321 if (mv88e6xxx_ppu_enable(ps) == 0)
Barry Grussling85686582013-01-08 16:05:56 +0000322 ps->ppu_disabled = 0;
323 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000324 }
325}
326
327static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
328{
329 struct mv88e6xxx_priv_state *ps = (void *)_ps;
330
331 schedule_work(&ps->ppu_work);
332}
333
Andrew Lunn158bc062016-04-28 21:24:06 -0400334static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000335{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000336 int ret;
337
338 mutex_lock(&ps->ppu_mutex);
339
Barry Grussling3675c8d2013-01-08 16:05:53 +0000340 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000341 * we can access the PHY registers. If it was already
342 * disabled, cancel the timer that is going to re-enable
343 * it.
344 */
345 if (!ps->ppu_disabled) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400346 ret = mv88e6xxx_ppu_disable(ps);
Barry Grussling85686582013-01-08 16:05:56 +0000347 if (ret < 0) {
348 mutex_unlock(&ps->ppu_mutex);
349 return ret;
350 }
351 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000352 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000353 del_timer(&ps->ppu_timer);
354 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000355 }
356
357 return ret;
358}
359
Andrew Lunn158bc062016-04-28 21:24:06 -0400360static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000361{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000362 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000363 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
364 mutex_unlock(&ps->ppu_mutex);
365}
366
Andrew Lunn158bc062016-04-28 21:24:06 -0400367void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000368{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000369 mutex_init(&ps->ppu_mutex);
370 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
371 init_timer(&ps->ppu_timer);
372 ps->ppu_timer.data = (unsigned long)ps;
373 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
374}
375
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400376static int mv88e6xxx_phy_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
377 int regnum)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000378{
379 int ret;
380
Andrew Lunn158bc062016-04-28 21:24:06 -0400381 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000382 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400383 ret = _mv88e6xxx_reg_read(ps, addr, regnum);
Andrew Lunn158bc062016-04-28 21:24:06 -0400384 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000385 }
386
387 return ret;
388}
389
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400390static int mv88e6xxx_phy_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
391 int regnum, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000392{
393 int ret;
394
Andrew Lunn158bc062016-04-28 21:24:06 -0400395 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000396 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400397 ret = _mv88e6xxx_reg_write(ps, addr, regnum, val);
Andrew Lunn158bc062016-04-28 21:24:06 -0400398 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000399 }
400
401 return ret;
402}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000403
Andrew Lunn158bc062016-04-28 21:24:06 -0400404static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200405{
Vivien Didelot22356472016-04-17 13:24:00 -0400406 return ps->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200407}
408
Andrew Lunn158bc062016-04-28 21:24:06 -0400409static bool mv88e6xxx_6095_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200410{
Vivien Didelot22356472016-04-17 13:24:00 -0400411 return ps->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200412}
413
Andrew Lunn158bc062016-04-28 21:24:06 -0400414static bool mv88e6xxx_6097_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200415{
Vivien Didelot22356472016-04-17 13:24:00 -0400416 return ps->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200417}
418
Andrew Lunn158bc062016-04-28 21:24:06 -0400419static bool mv88e6xxx_6165_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200420{
Vivien Didelot22356472016-04-17 13:24:00 -0400421 return ps->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200422}
423
Andrew Lunn158bc062016-04-28 21:24:06 -0400424static bool mv88e6xxx_6185_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200425{
Vivien Didelot22356472016-04-17 13:24:00 -0400426 return ps->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200427}
428
Andrew Lunn158bc062016-04-28 21:24:06 -0400429static bool mv88e6xxx_6320_family(struct mv88e6xxx_priv_state *ps)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700430{
Vivien Didelot22356472016-04-17 13:24:00 -0400431 return ps->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700432}
433
Andrew Lunn158bc062016-04-28 21:24:06 -0400434static bool mv88e6xxx_6351_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200435{
Vivien Didelot22356472016-04-17 13:24:00 -0400436 return ps->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200437}
438
Andrew Lunn158bc062016-04-28 21:24:06 -0400439static bool mv88e6xxx_6352_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200440{
Vivien Didelot22356472016-04-17 13:24:00 -0400441 return ps->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200442}
443
Andrew Lunn158bc062016-04-28 21:24:06 -0400444static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_priv_state *ps)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400445{
Vivien Didelotcd5a2c82016-04-17 13:24:02 -0400446 return ps->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400447}
448
Andrew Lunn158bc062016-04-28 21:24:06 -0400449static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400450{
451 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Andrew Lunn158bc062016-04-28 21:24:06 -0400452 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
453 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400454 return true;
455
456 return false;
457}
458
Andrew Lunndea87022015-08-31 15:56:47 +0200459/* We expect the switch to perform auto negotiation if there is a real
460 * phy. However, in the case of a fixed link phy, we force the port
461 * settings from the fixed link settings.
462 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400463static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
464 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200465{
466 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200467 u32 reg;
468 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200469
470 if (!phy_is_pseudo_fixed_link(phydev))
471 return;
472
473 mutex_lock(&ps->smi_mutex);
474
Andrew Lunn158bc062016-04-28 21:24:06 -0400475 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200476 if (ret < 0)
477 goto out;
478
479 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
480 PORT_PCS_CTRL_FORCE_LINK |
481 PORT_PCS_CTRL_DUPLEX_FULL |
482 PORT_PCS_CTRL_FORCE_DUPLEX |
483 PORT_PCS_CTRL_UNFORCED);
484
485 reg |= PORT_PCS_CTRL_FORCE_LINK;
486 if (phydev->link)
487 reg |= PORT_PCS_CTRL_LINK_UP;
488
Andrew Lunn158bc062016-04-28 21:24:06 -0400489 if (mv88e6xxx_6065_family(ps) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200490 goto out;
491
492 switch (phydev->speed) {
493 case SPEED_1000:
494 reg |= PORT_PCS_CTRL_1000;
495 break;
496 case SPEED_100:
497 reg |= PORT_PCS_CTRL_100;
498 break;
499 case SPEED_10:
500 reg |= PORT_PCS_CTRL_10;
501 break;
502 default:
503 pr_info("Unknown speed");
504 goto out;
505 }
506
507 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
508 if (phydev->duplex == DUPLEX_FULL)
509 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
510
Andrew Lunn158bc062016-04-28 21:24:06 -0400511 if ((mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps)) &&
Vivien Didelot009a2b92016-04-17 13:24:01 -0400512 (port >= ps->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200513 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
514 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
515 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
516 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
517 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
518 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
519 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
520 }
Andrew Lunn158bc062016-04-28 21:24:06 -0400521 _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200522
523out:
524 mutex_unlock(&ps->smi_mutex);
525}
526
Andrew Lunn158bc062016-04-28 21:24:06 -0400527static int _mv88e6xxx_stats_wait(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000528{
529 int ret;
530 int i;
531
532 for (i = 0; i < 10; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400533 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200534 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000535 return 0;
536 }
537
538 return -ETIMEDOUT;
539}
540
Andrew Lunn158bc062016-04-28 21:24:06 -0400541static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_priv_state *ps,
542 int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000543{
544 int ret;
545
Andrew Lunn158bc062016-04-28 21:24:06 -0400546 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200547 port = (port + 1) << 5;
548
Barry Grussling3675c8d2013-01-08 16:05:53 +0000549 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400550 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200551 GLOBAL_STATS_OP_CAPTURE_PORT |
552 GLOBAL_STATS_OP_HIST_RX_TX | port);
553 if (ret < 0)
554 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000555
Barry Grussling3675c8d2013-01-08 16:05:53 +0000556 /* Wait for the snapshotting to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400557 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000558 if (ret < 0)
559 return ret;
560
561 return 0;
562}
563
Andrew Lunn158bc062016-04-28 21:24:06 -0400564static void _mv88e6xxx_stats_read(struct mv88e6xxx_priv_state *ps,
565 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000566{
567 u32 _val;
568 int ret;
569
570 *val = 0;
571
Andrew Lunn158bc062016-04-28 21:24:06 -0400572 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200573 GLOBAL_STATS_OP_READ_CAPTURED |
574 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000575 if (ret < 0)
576 return;
577
Andrew Lunn158bc062016-04-28 21:24:06 -0400578 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000579 if (ret < 0)
580 return;
581
Andrew Lunn158bc062016-04-28 21:24:06 -0400582 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000583 if (ret < 0)
584 return;
585
586 _val = ret << 16;
587
Andrew Lunn158bc062016-04-28 21:24:06 -0400588 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000589 if (ret < 0)
590 return;
591
592 *val = _val | ret;
593}
594
Andrew Lunne413e7e2015-04-02 04:06:38 +0200595static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100596 { "in_good_octets", 8, 0x00, BANK0, },
597 { "in_bad_octets", 4, 0x02, BANK0, },
598 { "in_unicast", 4, 0x04, BANK0, },
599 { "in_broadcasts", 4, 0x06, BANK0, },
600 { "in_multicasts", 4, 0x07, BANK0, },
601 { "in_pause", 4, 0x16, BANK0, },
602 { "in_undersize", 4, 0x18, BANK0, },
603 { "in_fragments", 4, 0x19, BANK0, },
604 { "in_oversize", 4, 0x1a, BANK0, },
605 { "in_jabber", 4, 0x1b, BANK0, },
606 { "in_rx_error", 4, 0x1c, BANK0, },
607 { "in_fcs_error", 4, 0x1d, BANK0, },
608 { "out_octets", 8, 0x0e, BANK0, },
609 { "out_unicast", 4, 0x10, BANK0, },
610 { "out_broadcasts", 4, 0x13, BANK0, },
611 { "out_multicasts", 4, 0x12, BANK0, },
612 { "out_pause", 4, 0x15, BANK0, },
613 { "excessive", 4, 0x11, BANK0, },
614 { "collisions", 4, 0x1e, BANK0, },
615 { "deferred", 4, 0x05, BANK0, },
616 { "single", 4, 0x14, BANK0, },
617 { "multiple", 4, 0x17, BANK0, },
618 { "out_fcs_error", 4, 0x03, BANK0, },
619 { "late", 4, 0x1f, BANK0, },
620 { "hist_64bytes", 4, 0x08, BANK0, },
621 { "hist_65_127bytes", 4, 0x09, BANK0, },
622 { "hist_128_255bytes", 4, 0x0a, BANK0, },
623 { "hist_256_511bytes", 4, 0x0b, BANK0, },
624 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
625 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
626 { "sw_in_discards", 4, 0x10, PORT, },
627 { "sw_in_filtered", 2, 0x12, PORT, },
628 { "sw_out_filtered", 2, 0x13, PORT, },
629 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
630 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
631 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
632 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
633 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
634 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
635 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
636 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
637 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
638 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
639 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
640 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
641 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
642 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
643 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
644 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
645 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
646 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
647 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
648 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
649 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
650 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
651 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
652 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
653 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
654 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200655};
656
Andrew Lunn158bc062016-04-28 21:24:06 -0400657static bool mv88e6xxx_has_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100658 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200659{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100660 switch (stat->type) {
661 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200662 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100663 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400664 return mv88e6xxx_6320_family(ps);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100665 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400666 return mv88e6xxx_6095_family(ps) ||
667 mv88e6xxx_6185_family(ps) ||
668 mv88e6xxx_6097_family(ps) ||
669 mv88e6xxx_6165_family(ps) ||
670 mv88e6xxx_6351_family(ps) ||
671 mv88e6xxx_6352_family(ps);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200672 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100673 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000674}
675
Andrew Lunn158bc062016-04-28 21:24:06 -0400676static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100677 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200678 int port)
679{
Andrew Lunn80c46272015-06-20 18:42:30 +0200680 u32 low;
681 u32 high = 0;
682 int ret;
683 u64 value;
684
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100685 switch (s->type) {
686 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400687 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200688 if (ret < 0)
689 return UINT64_MAX;
690
691 low = ret;
692 if (s->sizeof_stat == 4) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400693 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100694 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200695 if (ret < 0)
696 return UINT64_MAX;
697 high = ret;
698 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100699 break;
700 case BANK0:
701 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400702 _mv88e6xxx_stats_read(ps, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200703 if (s->sizeof_stat == 8)
Andrew Lunn158bc062016-04-28 21:24:06 -0400704 _mv88e6xxx_stats_read(ps, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200705 }
706 value = (((u64)high) << 16) | low;
707 return value;
708}
709
Vivien Didelotf81ec902016-05-09 13:22:58 -0400710static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
711 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100712{
Andrew Lunn158bc062016-04-28 21:24:06 -0400713 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100714 struct mv88e6xxx_hw_stat *stat;
715 int i, j;
716
717 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
718 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400719 if (mv88e6xxx_has_stat(ps, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100720 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
721 ETH_GSTRING_LEN);
722 j++;
723 }
724 }
725}
726
Vivien Didelotf81ec902016-05-09 13:22:58 -0400727static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100728{
Andrew Lunn158bc062016-04-28 21:24:06 -0400729 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100730 struct mv88e6xxx_hw_stat *stat;
731 int i, j;
732
733 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
734 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400735 if (mv88e6xxx_has_stat(ps, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100736 j++;
737 }
738 return j;
739}
740
Vivien Didelotf81ec902016-05-09 13:22:58 -0400741static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
742 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000743{
Florian Fainellia22adce2014-04-28 11:14:28 -0700744 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100745 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000746 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100747 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000748
Andrew Lunn31888232015-05-06 01:09:54 +0200749 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000750
Andrew Lunn158bc062016-04-28 21:24:06 -0400751 ret = _mv88e6xxx_stats_snapshot(ps, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000752 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200753 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000754 return;
755 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100756 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
757 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400758 if (mv88e6xxx_has_stat(ps, stat)) {
759 data[j] = _mv88e6xxx_get_ethtool_stat(ps, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100760 j++;
761 }
762 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000763
Andrew Lunn31888232015-05-06 01:09:54 +0200764 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000765}
Ben Hutchings98e67302011-11-25 14:36:19 +0000766
Vivien Didelotf81ec902016-05-09 13:22:58 -0400767static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700768{
769 return 32 * sizeof(u16);
770}
771
Vivien Didelotf81ec902016-05-09 13:22:58 -0400772static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
773 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700774{
Andrew Lunn158bc062016-04-28 21:24:06 -0400775 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700776 u16 *p = _p;
777 int i;
778
779 regs->version = 0;
780
781 memset(p, 0xff, 32 * sizeof(u16));
782
Vivien Didelot23062512016-05-09 13:22:45 -0400783 mutex_lock(&ps->smi_mutex);
784
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700785 for (i = 0; i < 32; i++) {
786 int ret;
787
Vivien Didelot23062512016-05-09 13:22:45 -0400788 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700789 if (ret >= 0)
790 p[i] = ret;
791 }
Vivien Didelot23062512016-05-09 13:22:45 -0400792
793 mutex_unlock(&ps->smi_mutex);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700794}
795
Andrew Lunn158bc062016-04-28 21:24:06 -0400796static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset,
Andrew Lunn3898c142015-05-06 01:09:53 +0200797 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700798{
799 unsigned long timeout = jiffies + HZ / 10;
800
801 while (time_before(jiffies, timeout)) {
802 int ret;
803
Andrew Lunn158bc062016-04-28 21:24:06 -0400804 ret = _mv88e6xxx_reg_read(ps, reg, offset);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700805 if (ret < 0)
806 return ret;
807 if (!(ret & mask))
808 return 0;
809
810 usleep_range(1000, 2000);
811 }
812 return -ETIMEDOUT;
813}
814
Andrew Lunn158bc062016-04-28 21:24:06 -0400815static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg,
816 int offset, u16 mask)
Andrew Lunn3898c142015-05-06 01:09:53 +0200817{
Andrew Lunn3898c142015-05-06 01:09:53 +0200818 int ret;
819
820 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400821 ret = _mv88e6xxx_wait(ps, reg, offset, mask);
Andrew Lunn3898c142015-05-06 01:09:53 +0200822 mutex_unlock(&ps->smi_mutex);
823
824 return ret;
825}
826
Andrew Lunn158bc062016-04-28 21:24:06 -0400827static int _mv88e6xxx_phy_wait(struct mv88e6xxx_priv_state *ps)
Andrew Lunn3898c142015-05-06 01:09:53 +0200828{
Andrew Lunn158bc062016-04-28 21:24:06 -0400829 return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200830 GLOBAL2_SMI_OP_BUSY);
831}
832
Vivien Didelotd24645b2016-05-09 13:22:41 -0400833static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200834{
Andrew Lunn158bc062016-04-28 21:24:06 -0400835 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
836
837 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200838 GLOBAL2_EEPROM_OP_LOAD);
839}
840
Vivien Didelotd24645b2016-05-09 13:22:41 -0400841static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200842{
Andrew Lunn158bc062016-04-28 21:24:06 -0400843 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
844
845 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200846 GLOBAL2_EEPROM_OP_BUSY);
847}
848
Vivien Didelotd24645b2016-05-09 13:22:41 -0400849static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
850{
851 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
852 int ret;
853
854 mutex_lock(&ps->eeprom_mutex);
855
856 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
857 GLOBAL2_EEPROM_OP_READ |
858 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
859 if (ret < 0)
860 goto error;
861
862 ret = mv88e6xxx_eeprom_busy_wait(ds);
863 if (ret < 0)
864 goto error;
865
866 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
867error:
868 mutex_unlock(&ps->eeprom_mutex);
869 return ret;
870}
871
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200872static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
873{
874 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
875
876 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
877 return ps->eeprom_len;
878
879 return 0;
880}
881
Vivien Didelotf81ec902016-05-09 13:22:58 -0400882static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
883 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400884{
885 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
886 int offset;
887 int len;
888 int ret;
889
890 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
891 return -EOPNOTSUPP;
892
893 offset = eeprom->offset;
894 len = eeprom->len;
895 eeprom->len = 0;
896
897 eeprom->magic = 0xc3ec4951;
898
899 ret = mv88e6xxx_eeprom_load_wait(ds);
900 if (ret < 0)
901 return ret;
902
903 if (offset & 1) {
904 int word;
905
906 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
907 if (word < 0)
908 return word;
909
910 *data++ = (word >> 8) & 0xff;
911
912 offset++;
913 len--;
914 eeprom->len++;
915 }
916
917 while (len >= 2) {
918 int word;
919
920 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
921 if (word < 0)
922 return word;
923
924 *data++ = word & 0xff;
925 *data++ = (word >> 8) & 0xff;
926
927 offset += 2;
928 len -= 2;
929 eeprom->len += 2;
930 }
931
932 if (len) {
933 int word;
934
935 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
936 if (word < 0)
937 return word;
938
939 *data++ = word & 0xff;
940
941 offset++;
942 len--;
943 eeprom->len++;
944 }
945
946 return 0;
947}
948
949static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
950{
951 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
952 int ret;
953
954 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
955 if (ret < 0)
956 return ret;
957
958 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
959 return -EROFS;
960
961 return 0;
962}
963
964static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
965 u16 data)
966{
967 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
968 int ret;
969
970 mutex_lock(&ps->eeprom_mutex);
971
972 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
973 if (ret < 0)
974 goto error;
975
976 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
977 GLOBAL2_EEPROM_OP_WRITE |
978 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
979 if (ret < 0)
980 goto error;
981
982 ret = mv88e6xxx_eeprom_busy_wait(ds);
983error:
984 mutex_unlock(&ps->eeprom_mutex);
985 return ret;
986}
987
Vivien Didelotf81ec902016-05-09 13:22:58 -0400988static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
989 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400990{
991 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
992 int offset;
993 int ret;
994 int len;
995
996 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
997 return -EOPNOTSUPP;
998
999 if (eeprom->magic != 0xc3ec4951)
1000 return -EINVAL;
1001
1002 ret = mv88e6xxx_eeprom_is_readonly(ds);
1003 if (ret)
1004 return ret;
1005
1006 offset = eeprom->offset;
1007 len = eeprom->len;
1008 eeprom->len = 0;
1009
1010 ret = mv88e6xxx_eeprom_load_wait(ds);
1011 if (ret < 0)
1012 return ret;
1013
1014 if (offset & 1) {
1015 int word;
1016
1017 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1018 if (word < 0)
1019 return word;
1020
1021 word = (*data++ << 8) | (word & 0xff);
1022
1023 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1024 if (ret < 0)
1025 return ret;
1026
1027 offset++;
1028 len--;
1029 eeprom->len++;
1030 }
1031
1032 while (len >= 2) {
1033 int word;
1034
1035 word = *data++;
1036 word |= *data++ << 8;
1037
1038 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1039 if (ret < 0)
1040 return ret;
1041
1042 offset += 2;
1043 len -= 2;
1044 eeprom->len += 2;
1045 }
1046
1047 if (len) {
1048 int word;
1049
1050 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1051 if (word < 0)
1052 return word;
1053
1054 word = (word & 0xff00) | *data++;
1055
1056 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1057 if (ret < 0)
1058 return ret;
1059
1060 offset++;
1061 len--;
1062 eeprom->len++;
1063 }
1064
1065 return 0;
1066}
1067
Andrew Lunn158bc062016-04-28 21:24:06 -04001068static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001069{
Andrew Lunn158bc062016-04-28 21:24:06 -04001070 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001071 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001072}
1073
Andrew Lunn158bc062016-04-28 21:24:06 -04001074static int _mv88e6xxx_phy_read_indirect(struct mv88e6xxx_priv_state *ps,
1075 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001076{
1077 int ret;
1078
Andrew Lunn158bc062016-04-28 21:24:06 -04001079 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001080 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1081 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001082 if (ret < 0)
1083 return ret;
1084
Andrew Lunn158bc062016-04-28 21:24:06 -04001085 ret = _mv88e6xxx_phy_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +02001086 if (ret < 0)
1087 return ret;
1088
Andrew Lunn158bc062016-04-28 21:24:06 -04001089 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
1090
1091 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001092}
1093
Andrew Lunn158bc062016-04-28 21:24:06 -04001094static int _mv88e6xxx_phy_write_indirect(struct mv88e6xxx_priv_state *ps,
1095 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001096{
Andrew Lunn3898c142015-05-06 01:09:53 +02001097 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001098
Andrew Lunn158bc062016-04-28 21:24:06 -04001099 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001100 if (ret < 0)
1101 return ret;
1102
Andrew Lunn158bc062016-04-28 21:24:06 -04001103 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001104 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1105 regnum);
1106
Andrew Lunn158bc062016-04-28 21:24:06 -04001107 return _mv88e6xxx_phy_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +01001108}
1109
Vivien Didelotf81ec902016-05-09 13:22:58 -04001110static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1111 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001112{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001113 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001114 int reg;
1115
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001116 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1117 return -EOPNOTSUPP;
1118
Andrew Lunn3898c142015-05-06 01:09:53 +02001119 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001120
Andrew Lunn158bc062016-04-28 21:24:06 -04001121 reg = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001122 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001123 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001124
1125 e->eee_enabled = !!(reg & 0x0200);
1126 e->tx_lpi_enabled = !!(reg & 0x0100);
1127
Andrew Lunn158bc062016-04-28 21:24:06 -04001128 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001129 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001130 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001131
Andrew Lunncca8b132015-04-02 04:06:39 +02001132 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001133 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001134
Andrew Lunn2f40c692015-04-02 04:06:37 +02001135out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001136 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001137 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001138}
1139
Vivien Didelotf81ec902016-05-09 13:22:58 -04001140static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1141 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001142{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001143 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1144 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001145 int ret;
1146
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001147 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1148 return -EOPNOTSUPP;
1149
Andrew Lunn3898c142015-05-06 01:09:53 +02001150 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001151
Andrew Lunn158bc062016-04-28 21:24:06 -04001152 ret = _mv88e6xxx_phy_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001153 if (ret < 0)
1154 goto out;
1155
1156 reg = ret & ~0x0300;
1157 if (e->eee_enabled)
1158 reg |= 0x0200;
1159 if (e->tx_lpi_enabled)
1160 reg |= 0x0100;
1161
Andrew Lunn158bc062016-04-28 21:24:06 -04001162 ret = _mv88e6xxx_phy_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001163out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001164 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001165
1166 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001167}
1168
Andrew Lunn158bc062016-04-28 21:24:06 -04001169static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001170{
1171 int ret;
1172
Andrew Lunn158bc062016-04-28 21:24:06 -04001173 if (mv88e6xxx_has_fid_reg(ps)) {
1174 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001175 if (ret < 0)
1176 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001177 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001178 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001179 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001180 if (ret < 0)
1181 return ret;
1182
Andrew Lunn158bc062016-04-28 21:24:06 -04001183 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001184 (ret & 0xfff) |
1185 ((fid << 8) & 0xf000));
1186 if (ret < 0)
1187 return ret;
1188
1189 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1190 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001191 }
1192
Andrew Lunn158bc062016-04-28 21:24:06 -04001193 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001194 if (ret < 0)
1195 return ret;
1196
Andrew Lunn158bc062016-04-28 21:24:06 -04001197 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001198}
1199
Andrew Lunn158bc062016-04-28 21:24:06 -04001200static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -04001201 struct mv88e6xxx_atu_entry *entry)
1202{
1203 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1204
1205 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1206 unsigned int mask, shift;
1207
1208 if (entry->trunk) {
1209 data |= GLOBAL_ATU_DATA_TRUNK;
1210 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1211 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1212 } else {
1213 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1214 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1215 }
1216
1217 data |= (entry->portv_trunkid << shift) & mask;
1218 }
1219
Andrew Lunn158bc062016-04-28 21:24:06 -04001220 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001221}
1222
Andrew Lunn158bc062016-04-28 21:24:06 -04001223static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001224 struct mv88e6xxx_atu_entry *entry,
1225 bool static_too)
1226{
1227 int op;
1228 int err;
1229
Andrew Lunn158bc062016-04-28 21:24:06 -04001230 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001231 if (err)
1232 return err;
1233
Andrew Lunn158bc062016-04-28 21:24:06 -04001234 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001235 if (err)
1236 return err;
1237
1238 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001239 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1240 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1241 } else {
1242 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1243 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1244 }
1245
Andrew Lunn158bc062016-04-28 21:24:06 -04001246 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001247}
1248
Andrew Lunn158bc062016-04-28 21:24:06 -04001249static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1250 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001251{
1252 struct mv88e6xxx_atu_entry entry = {
1253 .fid = fid,
1254 .state = 0, /* EntryState bits must be 0 */
1255 };
1256
Andrew Lunn158bc062016-04-28 21:24:06 -04001257 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001258}
1259
Andrew Lunn158bc062016-04-28 21:24:06 -04001260static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1261 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001262{
1263 struct mv88e6xxx_atu_entry entry = {
1264 .trunk = false,
1265 .fid = fid,
1266 };
1267
1268 /* EntryState bits must be 0xF */
1269 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1270
1271 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1272 entry.portv_trunkid = (to_port & 0x0f) << 4;
1273 entry.portv_trunkid |= from_port & 0x0f;
1274
Andrew Lunn158bc062016-04-28 21:24:06 -04001275 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001276}
1277
Andrew Lunn158bc062016-04-28 21:24:06 -04001278static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1279 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001280{
1281 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001282 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001283}
1284
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001285static const char * const mv88e6xxx_port_state_names[] = {
1286 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1287 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1288 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1289 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1290};
1291
Andrew Lunn158bc062016-04-28 21:24:06 -04001292static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1293 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001294{
Andrew Lunn158bc062016-04-28 21:24:06 -04001295 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001296 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001297 u8 oldstate;
1298
Andrew Lunn158bc062016-04-28 21:24:06 -04001299 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001300 if (reg < 0)
1301 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001302
Andrew Lunncca8b132015-04-02 04:06:39 +02001303 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001304
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001305 if (oldstate != state) {
1306 /* Flush forwarding database if we're moving a port
1307 * from Learning or Forwarding state to Disabled or
1308 * Blocking or Listening state.
1309 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001310 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1311 oldstate == PORT_CONTROL_STATE_FORWARDING)
1312 && (state == PORT_CONTROL_STATE_DISABLED ||
1313 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001314 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001315 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001316 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001317 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001318
Andrew Lunncca8b132015-04-02 04:06:39 +02001319 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001320 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001321 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001322 if (ret)
1323 return ret;
1324
1325 netdev_dbg(ds->ports[port], "PortState %s (was %s)\n",
1326 mv88e6xxx_port_state_names[state],
1327 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001328 }
1329
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001330 return ret;
1331}
1332
Andrew Lunn158bc062016-04-28 21:24:06 -04001333static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1334 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001335{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001336 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001337 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001338 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001339 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001340 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001341 int i;
1342
1343 /* allow CPU port or DSA link(s) to send frames to every port */
1344 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1345 output_ports = mask;
1346 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001347 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001348 /* allow sending frames to every group member */
1349 if (bridge && ps->ports[i].bridge_dev == bridge)
1350 output_ports |= BIT(i);
1351
1352 /* allow sending frames to CPU port and DSA link(s) */
1353 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1354 output_ports |= BIT(i);
1355 }
1356 }
1357
1358 /* prevent frames from going back out of the port they came in on */
1359 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001360
Andrew Lunn158bc062016-04-28 21:24:06 -04001361 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001362 if (reg < 0)
1363 return reg;
1364
1365 reg &= ~mask;
1366 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001367
Andrew Lunn158bc062016-04-28 21:24:06 -04001368 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001369}
1370
Vivien Didelotf81ec902016-05-09 13:22:58 -04001371static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1372 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001373{
1374 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1375 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001376 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001377
Vivien Didelot936f2342016-05-09 13:22:46 -04001378 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE))
1379 return;
1380
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001381 switch (state) {
1382 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001383 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001384 break;
1385 case BR_STATE_BLOCKING:
1386 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001387 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001388 break;
1389 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001390 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001391 break;
1392 case BR_STATE_FORWARDING:
1393 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001394 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001395 break;
1396 }
1397
Vivien Didelot553eb542016-05-13 20:38:23 -04001398 mutex_lock(&ps->smi_mutex);
1399 err = _mv88e6xxx_port_state(ps, port, stp_state);
1400 mutex_unlock(&ps->smi_mutex);
1401
1402 if (err)
1403 netdev_err(ds->ports[port], "failed to update state to %s\n",
1404 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001405}
1406
Andrew Lunn158bc062016-04-28 21:24:06 -04001407static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1408 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001409{
Andrew Lunn158bc062016-04-28 21:24:06 -04001410 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001411 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001412 int ret;
1413
Andrew Lunn158bc062016-04-28 21:24:06 -04001414 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001415 if (ret < 0)
1416 return ret;
1417
Vivien Didelot5da96032016-03-07 18:24:39 -05001418 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1419
1420 if (new) {
1421 ret &= ~PORT_DEFAULT_VLAN_MASK;
1422 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1423
Andrew Lunn158bc062016-04-28 21:24:06 -04001424 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001425 PORT_DEFAULT_VLAN, ret);
1426 if (ret < 0)
1427 return ret;
1428
1429 netdev_dbg(ds->ports[port], "DefaultVID %d (was %d)\n", *new,
1430 pvid);
1431 }
1432
1433 if (old)
1434 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001435
1436 return 0;
1437}
1438
Andrew Lunn158bc062016-04-28 21:24:06 -04001439static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1440 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001441{
Andrew Lunn158bc062016-04-28 21:24:06 -04001442 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001443}
1444
Andrew Lunn158bc062016-04-28 21:24:06 -04001445static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1446 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001447{
Andrew Lunn158bc062016-04-28 21:24:06 -04001448 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001449}
1450
Andrew Lunn158bc062016-04-28 21:24:06 -04001451static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001452{
Andrew Lunn158bc062016-04-28 21:24:06 -04001453 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001454 GLOBAL_VTU_OP_BUSY);
1455}
1456
Andrew Lunn158bc062016-04-28 21:24:06 -04001457static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001458{
1459 int ret;
1460
Andrew Lunn158bc062016-04-28 21:24:06 -04001461 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001462 if (ret < 0)
1463 return ret;
1464
Andrew Lunn158bc062016-04-28 21:24:06 -04001465 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001466}
1467
Andrew Lunn158bc062016-04-28 21:24:06 -04001468static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001469{
1470 int ret;
1471
Andrew Lunn158bc062016-04-28 21:24:06 -04001472 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001473 if (ret < 0)
1474 return ret;
1475
Andrew Lunn158bc062016-04-28 21:24:06 -04001476 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001477}
1478
Andrew Lunn158bc062016-04-28 21:24:06 -04001479static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001480 struct mv88e6xxx_vtu_stu_entry *entry,
1481 unsigned int nibble_offset)
1482{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001483 u16 regs[3];
1484 int i;
1485 int ret;
1486
1487 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001488 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001489 GLOBAL_VTU_DATA_0_3 + i);
1490 if (ret < 0)
1491 return ret;
1492
1493 regs[i] = ret;
1494 }
1495
Vivien Didelot009a2b92016-04-17 13:24:01 -04001496 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001497 unsigned int shift = (i % 4) * 4 + nibble_offset;
1498 u16 reg = regs[i / 4];
1499
1500 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1501 }
1502
1503 return 0;
1504}
1505
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001506static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps,
1507 struct mv88e6xxx_vtu_stu_entry *entry)
1508{
1509 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0);
1510}
1511
1512static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps,
1513 struct mv88e6xxx_vtu_stu_entry *entry)
1514{
1515 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2);
1516}
1517
Andrew Lunn158bc062016-04-28 21:24:06 -04001518static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001519 struct mv88e6xxx_vtu_stu_entry *entry,
1520 unsigned int nibble_offset)
1521{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001522 u16 regs[3] = { 0 };
1523 int i;
1524 int ret;
1525
Vivien Didelot009a2b92016-04-17 13:24:01 -04001526 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001527 unsigned int shift = (i % 4) * 4 + nibble_offset;
1528 u8 data = entry->data[i];
1529
1530 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1531 }
1532
1533 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001534 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001535 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1536 if (ret < 0)
1537 return ret;
1538 }
1539
1540 return 0;
1541}
1542
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001543static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps,
1544 struct mv88e6xxx_vtu_stu_entry *entry)
1545{
1546 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
1547}
1548
1549static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps,
1550 struct mv88e6xxx_vtu_stu_entry *entry)
1551{
1552 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
1553}
1554
Andrew Lunn158bc062016-04-28 21:24:06 -04001555static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001556{
Andrew Lunn158bc062016-04-28 21:24:06 -04001557 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001558 vid & GLOBAL_VTU_VID_MASK);
1559}
1560
Andrew Lunn158bc062016-04-28 21:24:06 -04001561static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001562 struct mv88e6xxx_vtu_stu_entry *entry)
1563{
1564 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1565 int ret;
1566
Andrew Lunn158bc062016-04-28 21:24:06 -04001567 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001568 if (ret < 0)
1569 return ret;
1570
Andrew Lunn158bc062016-04-28 21:24:06 -04001571 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001572 if (ret < 0)
1573 return ret;
1574
Andrew Lunn158bc062016-04-28 21:24:06 -04001575 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001576 if (ret < 0)
1577 return ret;
1578
1579 next.vid = ret & GLOBAL_VTU_VID_MASK;
1580 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1581
1582 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001583 ret = mv88e6xxx_vtu_data_read(ps, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001584 if (ret < 0)
1585 return ret;
1586
Andrew Lunn158bc062016-04-28 21:24:06 -04001587 if (mv88e6xxx_has_fid_reg(ps)) {
1588 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001589 GLOBAL_VTU_FID);
1590 if (ret < 0)
1591 return ret;
1592
1593 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001594 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001595 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1596 * VTU DBNum[3:0] are located in VTU Operation 3:0
1597 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001598 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001599 GLOBAL_VTU_OP);
1600 if (ret < 0)
1601 return ret;
1602
1603 next.fid = (ret & 0xf00) >> 4;
1604 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001605 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001606
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001607 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001608 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001609 GLOBAL_VTU_SID);
1610 if (ret < 0)
1611 return ret;
1612
1613 next.sid = ret & GLOBAL_VTU_SID_MASK;
1614 }
1615 }
1616
1617 *entry = next;
1618 return 0;
1619}
1620
Vivien Didelotf81ec902016-05-09 13:22:58 -04001621static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1622 struct switchdev_obj_port_vlan *vlan,
1623 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001624{
1625 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1626 struct mv88e6xxx_vtu_stu_entry next;
1627 u16 pvid;
1628 int err;
1629
Vivien Didelot54d77b52016-05-09 13:22:47 -04001630 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
1631 return -EOPNOTSUPP;
1632
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001633 mutex_lock(&ps->smi_mutex);
1634
Andrew Lunn158bc062016-04-28 21:24:06 -04001635 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001636 if (err)
1637 goto unlock;
1638
Andrew Lunn158bc062016-04-28 21:24:06 -04001639 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001640 if (err)
1641 goto unlock;
1642
1643 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001644 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001645 if (err)
1646 break;
1647
1648 if (!next.valid)
1649 break;
1650
1651 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1652 continue;
1653
1654 /* reinit and dump this VLAN obj */
1655 vlan->vid_begin = vlan->vid_end = next.vid;
1656 vlan->flags = 0;
1657
1658 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1659 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1660
1661 if (next.vid == pvid)
1662 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1663
1664 err = cb(&vlan->obj);
1665 if (err)
1666 break;
1667 } while (next.vid < GLOBAL_VTU_VID_MASK);
1668
1669unlock:
1670 mutex_unlock(&ps->smi_mutex);
1671
1672 return err;
1673}
1674
Andrew Lunn158bc062016-04-28 21:24:06 -04001675static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001676 struct mv88e6xxx_vtu_stu_entry *entry)
1677{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001678 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001679 u16 reg = 0;
1680 int ret;
1681
Andrew Lunn158bc062016-04-28 21:24:06 -04001682 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001683 if (ret < 0)
1684 return ret;
1685
1686 if (!entry->valid)
1687 goto loadpurge;
1688
1689 /* Write port member tags */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001690 ret = mv88e6xxx_vtu_data_write(ps, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001691 if (ret < 0)
1692 return ret;
1693
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001694 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001695 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001696 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001697 if (ret < 0)
1698 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001699 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001700
Andrew Lunn158bc062016-04-28 21:24:06 -04001701 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001702 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001703 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001704 if (ret < 0)
1705 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001706 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001707 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1708 * VTU DBNum[3:0] are located in VTU Operation 3:0
1709 */
1710 op |= (entry->fid & 0xf0) << 8;
1711 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001712 }
1713
1714 reg = GLOBAL_VTU_VID_VALID;
1715loadpurge:
1716 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001717 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001718 if (ret < 0)
1719 return ret;
1720
Andrew Lunn158bc062016-04-28 21:24:06 -04001721 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001722}
1723
Andrew Lunn158bc062016-04-28 21:24:06 -04001724static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001725 struct mv88e6xxx_vtu_stu_entry *entry)
1726{
1727 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1728 int ret;
1729
Andrew Lunn158bc062016-04-28 21:24:06 -04001730 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001731 if (ret < 0)
1732 return ret;
1733
Andrew Lunn158bc062016-04-28 21:24:06 -04001734 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001735 sid & GLOBAL_VTU_SID_MASK);
1736 if (ret < 0)
1737 return ret;
1738
Andrew Lunn158bc062016-04-28 21:24:06 -04001739 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001740 if (ret < 0)
1741 return ret;
1742
Andrew Lunn158bc062016-04-28 21:24:06 -04001743 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001744 if (ret < 0)
1745 return ret;
1746
1747 next.sid = ret & GLOBAL_VTU_SID_MASK;
1748
Andrew Lunn158bc062016-04-28 21:24:06 -04001749 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001750 if (ret < 0)
1751 return ret;
1752
1753 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1754
1755 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001756 ret = mv88e6xxx_stu_data_read(ps, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001757 if (ret < 0)
1758 return ret;
1759 }
1760
1761 *entry = next;
1762 return 0;
1763}
1764
Andrew Lunn158bc062016-04-28 21:24:06 -04001765static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001766 struct mv88e6xxx_vtu_stu_entry *entry)
1767{
1768 u16 reg = 0;
1769 int ret;
1770
Andrew Lunn158bc062016-04-28 21:24:06 -04001771 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001772 if (ret < 0)
1773 return ret;
1774
1775 if (!entry->valid)
1776 goto loadpurge;
1777
1778 /* Write port states */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001779 ret = mv88e6xxx_stu_data_write(ps, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001780 if (ret < 0)
1781 return ret;
1782
1783 reg = GLOBAL_VTU_VID_VALID;
1784loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001785 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001786 if (ret < 0)
1787 return ret;
1788
1789 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001790 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001791 if (ret < 0)
1792 return ret;
1793
Andrew Lunn158bc062016-04-28 21:24:06 -04001794 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001795}
1796
Andrew Lunn158bc062016-04-28 21:24:06 -04001797static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1798 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001799{
Andrew Lunn158bc062016-04-28 21:24:06 -04001800 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001801 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001802 u16 fid;
1803 int ret;
1804
Andrew Lunn158bc062016-04-28 21:24:06 -04001805 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001806 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001807 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001808 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001809 else
1810 return -EOPNOTSUPP;
1811
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001812 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001813 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001814 if (ret < 0)
1815 return ret;
1816
1817 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1818
1819 if (new) {
1820 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1821 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1822
Andrew Lunn158bc062016-04-28 21:24:06 -04001823 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001824 ret);
1825 if (ret < 0)
1826 return ret;
1827 }
1828
1829 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001830 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001831 if (ret < 0)
1832 return ret;
1833
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001834 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001835
1836 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001837 ret &= ~upper_mask;
1838 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001839
Andrew Lunn158bc062016-04-28 21:24:06 -04001840 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001841 ret);
1842 if (ret < 0)
1843 return ret;
1844
1845 netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid);
1846 }
1847
1848 if (old)
1849 *old = fid;
1850
1851 return 0;
1852}
1853
Andrew Lunn158bc062016-04-28 21:24:06 -04001854static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1855 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001856{
Andrew Lunn158bc062016-04-28 21:24:06 -04001857 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001858}
1859
Andrew Lunn158bc062016-04-28 21:24:06 -04001860static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1861 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001862{
Andrew Lunn158bc062016-04-28 21:24:06 -04001863 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001864}
1865
Andrew Lunn158bc062016-04-28 21:24:06 -04001866static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001867{
1868 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1869 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001870 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001871
1872 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1873
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001874 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001875 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001876 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001877 if (err)
1878 return err;
1879
1880 set_bit(*fid, fid_bitmap);
1881 }
1882
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001883 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001884 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001885 if (err)
1886 return err;
1887
1888 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001889 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001890 if (err)
1891 return err;
1892
1893 if (!vlan.valid)
1894 break;
1895
1896 set_bit(vlan.fid, fid_bitmap);
1897 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1898
1899 /* The reset value 0x000 is used to indicate that multiple address
1900 * databases are not needed. Return the next positive available.
1901 */
1902 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001903 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001904 return -ENOSPC;
1905
1906 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001907 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001908}
1909
Andrew Lunn158bc062016-04-28 21:24:06 -04001910static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001911 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001912{
Andrew Lunn158bc062016-04-28 21:24:06 -04001913 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001914 struct mv88e6xxx_vtu_stu_entry vlan = {
1915 .valid = true,
1916 .vid = vid,
1917 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001918 int i, err;
1919
Andrew Lunn158bc062016-04-28 21:24:06 -04001920 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001921 if (err)
1922 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001923
Vivien Didelot3d131f02015-11-03 10:52:52 -05001924 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001925 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001926 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1927 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1928 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001929
Andrew Lunn158bc062016-04-28 21:24:06 -04001930 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1931 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001932 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001933
1934 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1935 * implemented, only one STU entry is needed to cover all VTU
1936 * entries. Thus, validate the SID 0.
1937 */
1938 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001939 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001940 if (err)
1941 return err;
1942
1943 if (vstp.sid != vlan.sid || !vstp.valid) {
1944 memset(&vstp, 0, sizeof(vstp));
1945 vstp.valid = true;
1946 vstp.sid = vlan.sid;
1947
Andrew Lunn158bc062016-04-28 21:24:06 -04001948 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001949 if (err)
1950 return err;
1951 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001952 }
1953
1954 *entry = vlan;
1955 return 0;
1956}
1957
Andrew Lunn158bc062016-04-28 21:24:06 -04001958static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001959 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1960{
1961 int err;
1962
1963 if (!vid)
1964 return -EINVAL;
1965
Andrew Lunn158bc062016-04-28 21:24:06 -04001966 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001967 if (err)
1968 return err;
1969
Andrew Lunn158bc062016-04-28 21:24:06 -04001970 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001971 if (err)
1972 return err;
1973
1974 if (entry->vid != vid || !entry->valid) {
1975 if (!creat)
1976 return -EOPNOTSUPP;
1977 /* -ENOENT would've been more appropriate, but switchdev expects
1978 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1979 */
1980
Andrew Lunn158bc062016-04-28 21:24:06 -04001981 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001982 }
1983
1984 return err;
1985}
1986
Vivien Didelotda9c3592016-02-12 12:09:40 -05001987static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1988 u16 vid_begin, u16 vid_end)
1989{
1990 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1991 struct mv88e6xxx_vtu_stu_entry vlan;
1992 int i, err;
1993
1994 if (!vid_begin)
1995 return -EOPNOTSUPP;
1996
1997 mutex_lock(&ps->smi_mutex);
1998
Andrew Lunn158bc062016-04-28 21:24:06 -04001999 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002000 if (err)
2001 goto unlock;
2002
2003 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002004 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002005 if (err)
2006 goto unlock;
2007
2008 if (!vlan.valid)
2009 break;
2010
2011 if (vlan.vid > vid_end)
2012 break;
2013
Vivien Didelot009a2b92016-04-17 13:24:01 -04002014 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05002015 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
2016 continue;
2017
2018 if (vlan.data[i] ==
2019 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2020 continue;
2021
2022 if (ps->ports[i].bridge_dev ==
2023 ps->ports[port].bridge_dev)
2024 break; /* same bridge, check next VLAN */
2025
2026 netdev_warn(ds->ports[port],
2027 "hardware VLAN %d already used by %s\n",
2028 vlan.vid,
2029 netdev_name(ps->ports[i].bridge_dev));
2030 err = -EOPNOTSUPP;
2031 goto unlock;
2032 }
2033 } while (vlan.vid < vid_end);
2034
2035unlock:
2036 mutex_unlock(&ps->smi_mutex);
2037
2038 return err;
2039}
2040
Vivien Didelot214cdb92016-02-26 13:16:08 -05002041static const char * const mv88e6xxx_port_8021q_mode_names[] = {
2042 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
2043 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
2044 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
2045 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
2046};
2047
Vivien Didelotf81ec902016-05-09 13:22:58 -04002048static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
2049 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002050{
2051 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2052 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
2053 PORT_CONTROL_2_8021Q_DISABLED;
2054 int ret;
2055
Vivien Didelot54d77b52016-05-09 13:22:47 -04002056 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2057 return -EOPNOTSUPP;
2058
Vivien Didelot214cdb92016-02-26 13:16:08 -05002059 mutex_lock(&ps->smi_mutex);
2060
Andrew Lunn158bc062016-04-28 21:24:06 -04002061 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002062 if (ret < 0)
2063 goto unlock;
2064
2065 old = ret & PORT_CONTROL_2_8021Q_MASK;
2066
Vivien Didelot5220ef12016-03-07 18:24:52 -05002067 if (new != old) {
2068 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2069 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002070
Andrew Lunn158bc062016-04-28 21:24:06 -04002071 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002072 ret);
2073 if (ret < 0)
2074 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002075
Vivien Didelot5220ef12016-03-07 18:24:52 -05002076 netdev_dbg(ds->ports[port], "802.1Q Mode %s (was %s)\n",
2077 mv88e6xxx_port_8021q_mode_names[new],
2078 mv88e6xxx_port_8021q_mode_names[old]);
2079 }
2080
2081 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002082unlock:
2083 mutex_unlock(&ps->smi_mutex);
2084
2085 return ret;
2086}
2087
Vivien Didelotf81ec902016-05-09 13:22:58 -04002088static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2089 const struct switchdev_obj_port_vlan *vlan,
2090 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002091{
Vivien Didelot54d77b52016-05-09 13:22:47 -04002092 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002093 int err;
2094
Vivien Didelot54d77b52016-05-09 13:22:47 -04002095 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2096 return -EOPNOTSUPP;
2097
Vivien Didelotda9c3592016-02-12 12:09:40 -05002098 /* If the requested port doesn't belong to the same bridge as the VLAN
2099 * members, do not support it (yet) and fallback to software VLAN.
2100 */
2101 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2102 vlan->vid_end);
2103 if (err)
2104 return err;
2105
Vivien Didelot76e398a2015-11-01 12:33:55 -05002106 /* We don't need any dynamic resource from the kernel (yet),
2107 * so skip the prepare phase.
2108 */
2109 return 0;
2110}
2111
Andrew Lunn158bc062016-04-28 21:24:06 -04002112static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
2113 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002114{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002115 struct mv88e6xxx_vtu_stu_entry vlan;
2116 int err;
2117
Andrew Lunn158bc062016-04-28 21:24:06 -04002118 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002119 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002120 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002121
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002122 vlan.data[port] = untagged ?
2123 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2124 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2125
Andrew Lunn158bc062016-04-28 21:24:06 -04002126 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002127}
2128
Vivien Didelotf81ec902016-05-09 13:22:58 -04002129static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2130 const struct switchdev_obj_port_vlan *vlan,
2131 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002132{
2133 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2134 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2135 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2136 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002137
Vivien Didelot54d77b52016-05-09 13:22:47 -04002138 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2139 return;
2140
Vivien Didelot76e398a2015-11-01 12:33:55 -05002141 mutex_lock(&ps->smi_mutex);
2142
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002143 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04002144 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002145 netdev_err(ds->ports[port], "failed to add VLAN %d%c\n",
2146 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002147
Andrew Lunn158bc062016-04-28 21:24:06 -04002148 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002149 netdev_err(ds->ports[port], "failed to set PVID %d\n",
2150 vlan->vid_end);
2151
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002152 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002153}
2154
Andrew Lunn158bc062016-04-28 21:24:06 -04002155static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
2156 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002157{
Andrew Lunn158bc062016-04-28 21:24:06 -04002158 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002159 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002160 int i, err;
2161
Andrew Lunn158bc062016-04-28 21:24:06 -04002162 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002163 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002164 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002165
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002166 /* Tell switchdev if this VLAN is handled in software */
2167 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002168 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002169
2170 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2171
2172 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002173 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04002174 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002175 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002176 continue;
2177
2178 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002179 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002180 break;
2181 }
2182 }
2183
Andrew Lunn158bc062016-04-28 21:24:06 -04002184 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002185 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002186 return err;
2187
Andrew Lunn158bc062016-04-28 21:24:06 -04002188 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002189}
2190
Vivien Didelotf81ec902016-05-09 13:22:58 -04002191static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2192 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002193{
2194 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2195 u16 pvid, vid;
2196 int err = 0;
2197
Vivien Didelot54d77b52016-05-09 13:22:47 -04002198 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2199 return -EOPNOTSUPP;
2200
Vivien Didelot76e398a2015-11-01 12:33:55 -05002201 mutex_lock(&ps->smi_mutex);
2202
Andrew Lunn158bc062016-04-28 21:24:06 -04002203 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002204 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002205 goto unlock;
2206
Vivien Didelot76e398a2015-11-01 12:33:55 -05002207 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002208 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002209 if (err)
2210 goto unlock;
2211
2212 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002213 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002214 if (err)
2215 goto unlock;
2216 }
2217 }
2218
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002219unlock:
2220 mutex_unlock(&ps->smi_mutex);
2221
2222 return err;
2223}
2224
Andrew Lunn158bc062016-04-28 21:24:06 -04002225static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002226 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002227{
2228 int i, ret;
2229
2230 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002231 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002232 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002233 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002234 if (ret < 0)
2235 return ret;
2236 }
2237
2238 return 0;
2239}
2240
Andrew Lunn158bc062016-04-28 21:24:06 -04002241static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
2242 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002243{
2244 int i, ret;
2245
2246 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002247 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002248 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002249 if (ret < 0)
2250 return ret;
2251 addr[i * 2] = ret >> 8;
2252 addr[i * 2 + 1] = ret & 0xff;
2253 }
2254
2255 return 0;
2256}
2257
Andrew Lunn158bc062016-04-28 21:24:06 -04002258static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002259 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002260{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002261 int ret;
2262
Andrew Lunn158bc062016-04-28 21:24:06 -04002263 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002264 if (ret < 0)
2265 return ret;
2266
Andrew Lunn158bc062016-04-28 21:24:06 -04002267 ret = _mv88e6xxx_atu_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002268 if (ret < 0)
2269 return ret;
2270
Andrew Lunn158bc062016-04-28 21:24:06 -04002271 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002272 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002273 return ret;
2274
Andrew Lunn158bc062016-04-28 21:24:06 -04002275 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002276}
David S. Millercdf09692015-08-11 12:00:37 -07002277
Andrew Lunn158bc062016-04-28 21:24:06 -04002278static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002279 const unsigned char *addr, u16 vid,
2280 u8 state)
2281{
2282 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002283 struct mv88e6xxx_vtu_stu_entry vlan;
2284 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002285
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002286 /* Null VLAN ID corresponds to the port private database */
2287 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002288 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002289 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002290 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002291 if (err)
2292 return err;
2293
2294 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002295 entry.state = state;
2296 ether_addr_copy(entry.mac, addr);
2297 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2298 entry.trunk = false;
2299 entry.portv_trunkid = BIT(port);
2300 }
2301
Andrew Lunn158bc062016-04-28 21:24:06 -04002302 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002303}
2304
Vivien Didelotf81ec902016-05-09 13:22:58 -04002305static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2306 const struct switchdev_obj_port_fdb *fdb,
2307 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002308{
Vivien Didelot2672f822016-05-09 13:22:48 -04002309 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2310
2311 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2312 return -EOPNOTSUPP;
2313
Vivien Didelot146a3202015-10-08 11:35:12 -04002314 /* We don't need any dynamic resource from the kernel (yet),
2315 * so skip the prepare phase.
2316 */
2317 return 0;
2318}
2319
Vivien Didelotf81ec902016-05-09 13:22:58 -04002320static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2321 const struct switchdev_obj_port_fdb *fdb,
2322 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002323{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002324 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002325 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2326 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2327 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002328
Vivien Didelot2672f822016-05-09 13:22:48 -04002329 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2330 return;
2331
David S. Millercdf09692015-08-11 12:00:37 -07002332 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002333 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Vivien Didelot8497aa62016-04-06 11:55:04 -04002334 netdev_err(ds->ports[port], "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002335 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002336}
2337
Vivien Didelotf81ec902016-05-09 13:22:58 -04002338static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2339 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002340{
2341 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2342 int ret;
2343
Vivien Didelot2672f822016-05-09 13:22:48 -04002344 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2345 return -EOPNOTSUPP;
2346
David S. Millercdf09692015-08-11 12:00:37 -07002347 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002348 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002349 GLOBAL_ATU_DATA_STATE_UNUSED);
2350 mutex_unlock(&ps->smi_mutex);
2351
2352 return ret;
2353}
2354
Andrew Lunn158bc062016-04-28 21:24:06 -04002355static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002356 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002357{
Vivien Didelot1d194042015-08-10 09:09:51 -04002358 struct mv88e6xxx_atu_entry next = { 0 };
2359 int ret;
2360
2361 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002362
Andrew Lunn158bc062016-04-28 21:24:06 -04002363 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002364 if (ret < 0)
2365 return ret;
2366
Andrew Lunn158bc062016-04-28 21:24:06 -04002367 ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002368 if (ret < 0)
2369 return ret;
2370
Andrew Lunn158bc062016-04-28 21:24:06 -04002371 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002372 if (ret < 0)
2373 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002374
Andrew Lunn158bc062016-04-28 21:24:06 -04002375 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002376 if (ret < 0)
2377 return ret;
2378
2379 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2380 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2381 unsigned int mask, shift;
2382
2383 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2384 next.trunk = true;
2385 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2386 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2387 } else {
2388 next.trunk = false;
2389 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2390 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2391 }
2392
2393 next.portv_trunkid = (ret & mask) >> shift;
2394 }
2395
2396 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002397 return 0;
2398}
2399
Andrew Lunn158bc062016-04-28 21:24:06 -04002400static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2401 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002402 struct switchdev_obj_port_fdb *fdb,
2403 int (*cb)(struct switchdev_obj *obj))
2404{
2405 struct mv88e6xxx_atu_entry addr = {
2406 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2407 };
2408 int err;
2409
Andrew Lunn158bc062016-04-28 21:24:06 -04002410 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002411 if (err)
2412 return err;
2413
2414 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002415 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002416 if (err)
2417 break;
2418
2419 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2420 break;
2421
2422 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2423 bool is_static = addr.state ==
2424 (is_multicast_ether_addr(addr.mac) ?
2425 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2426 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2427
2428 fdb->vid = vid;
2429 ether_addr_copy(fdb->addr, addr.mac);
2430 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2431
2432 err = cb(&fdb->obj);
2433 if (err)
2434 break;
2435 }
2436 } while (!is_broadcast_ether_addr(addr.mac));
2437
2438 return err;
2439}
2440
Vivien Didelotf81ec902016-05-09 13:22:58 -04002441static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2442 struct switchdev_obj_port_fdb *fdb,
2443 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002444{
2445 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2446 struct mv88e6xxx_vtu_stu_entry vlan = {
2447 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2448 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002449 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002450 int err;
2451
Vivien Didelot2672f822016-05-09 13:22:48 -04002452 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2453 return -EOPNOTSUPP;
2454
Vivien Didelotf33475b2015-10-22 09:34:41 -04002455 mutex_lock(&ps->smi_mutex);
2456
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002457 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002458 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002459 if (err)
2460 goto unlock;
2461
Andrew Lunn158bc062016-04-28 21:24:06 -04002462 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002463 if (err)
2464 goto unlock;
2465
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002466 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002467 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002468 if (err)
2469 goto unlock;
2470
2471 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002472 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002473 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002474 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002475
2476 if (!vlan.valid)
2477 break;
2478
Andrew Lunn158bc062016-04-28 21:24:06 -04002479 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002480 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002481 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002482 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002483 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2484
2485unlock:
2486 mutex_unlock(&ps->smi_mutex);
2487
2488 return err;
2489}
2490
Vivien Didelotf81ec902016-05-09 13:22:58 -04002491static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2492 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002493{
Vivien Didelota6692752016-02-12 12:09:39 -05002494 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002495 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002496
Vivien Didelot936f2342016-05-09 13:22:46 -04002497 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2498 return -EOPNOTSUPP;
2499
Vivien Didelot466dfa02016-02-26 13:16:05 -05002500 mutex_lock(&ps->smi_mutex);
2501
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002502 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002503 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002504
Vivien Didelot009a2b92016-04-17 13:24:01 -04002505 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002506 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002507 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002508 if (err)
2509 break;
2510 }
2511 }
2512
Vivien Didelot466dfa02016-02-26 13:16:05 -05002513 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002514
Vivien Didelot466dfa02016-02-26 13:16:05 -05002515 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002516}
2517
Vivien Didelotf81ec902016-05-09 13:22:58 -04002518static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002519{
Vivien Didelota6692752016-02-12 12:09:39 -05002520 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002521 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002522 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002523
Vivien Didelot936f2342016-05-09 13:22:46 -04002524 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2525 return;
2526
Vivien Didelot466dfa02016-02-26 13:16:05 -05002527 mutex_lock(&ps->smi_mutex);
2528
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002529 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002530 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002531
Vivien Didelot009a2b92016-04-17 13:24:01 -04002532 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002533 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002534 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Vivien Didelot16bfa702016-03-13 16:21:33 -04002535 netdev_warn(ds->ports[i], "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002536
Vivien Didelot466dfa02016-02-26 13:16:05 -05002537 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002538}
2539
Andrew Lunn158bc062016-04-28 21:24:06 -04002540static int _mv88e6xxx_phy_page_write(struct mv88e6xxx_priv_state *ps,
2541 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002542{
2543 int ret;
2544
Andrew Lunn158bc062016-04-28 21:24:06 -04002545 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002546 if (ret < 0)
2547 goto restore_page_0;
2548
Andrew Lunn158bc062016-04-28 21:24:06 -04002549 ret = _mv88e6xxx_phy_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002550restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002551 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002552
2553 return ret;
2554}
2555
Andrew Lunn158bc062016-04-28 21:24:06 -04002556static int _mv88e6xxx_phy_page_read(struct mv88e6xxx_priv_state *ps,
2557 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002558{
2559 int ret;
2560
Andrew Lunn158bc062016-04-28 21:24:06 -04002561 ret = _mv88e6xxx_phy_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002562 if (ret < 0)
2563 goto restore_page_0;
2564
Andrew Lunn158bc062016-04-28 21:24:06 -04002565 ret = _mv88e6xxx_phy_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002566restore_page_0:
Andrew Lunn158bc062016-04-28 21:24:06 -04002567 _mv88e6xxx_phy_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002568
2569 return ret;
2570}
2571
Vivien Didelot552238b2016-05-09 13:22:49 -04002572static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps)
2573{
2574 bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE);
2575 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunn52638f72016-05-10 23:27:22 +02002576 struct gpio_desc *gpiod = ps->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002577 unsigned long timeout;
2578 int ret;
2579 int i;
2580
2581 /* Set all ports to the disabled state. */
2582 for (i = 0; i < ps->info->num_ports; i++) {
2583 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
2584 if (ret < 0)
2585 return ret;
2586
2587 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
2588 ret & 0xfffc);
2589 if (ret)
2590 return ret;
2591 }
2592
2593 /* Wait for transmit queues to drain. */
2594 usleep_range(2000, 4000);
2595
2596 /* If there is a gpio connected to the reset pin, toggle it */
2597 if (gpiod) {
2598 gpiod_set_value_cansleep(gpiod, 1);
2599 usleep_range(10000, 20000);
2600 gpiod_set_value_cansleep(gpiod, 0);
2601 usleep_range(10000, 20000);
2602 }
2603
2604 /* Reset the switch. Keep the PPU active if requested. The PPU
2605 * needs to be active to support indirect phy register access
2606 * through global registers 0x18 and 0x19.
2607 */
2608 if (ppu_active)
2609 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
2610 else
2611 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
2612 if (ret)
2613 return ret;
2614
2615 /* Wait up to one second for reset to complete. */
2616 timeout = jiffies + 1 * HZ;
2617 while (time_before(jiffies, timeout)) {
2618 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
2619 if (ret < 0)
2620 return ret;
2621
2622 if ((ret & is_reset) == is_reset)
2623 break;
2624 usleep_range(1000, 2000);
2625 }
2626 if (time_after(jiffies, timeout))
2627 ret = -ETIMEDOUT;
2628 else
2629 ret = 0;
2630
2631 return ret;
2632}
2633
Andrew Lunn158bc062016-04-28 21:24:06 -04002634static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002635{
2636 int ret;
2637
Andrew Lunn158bc062016-04-28 21:24:06 -04002638 ret = _mv88e6xxx_phy_page_read(ps, REG_FIBER_SERDES, PAGE_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002639 MII_BMCR);
2640 if (ret < 0)
2641 return ret;
2642
2643 if (ret & BMCR_PDOWN) {
2644 ret &= ~BMCR_PDOWN;
Andrew Lunn158bc062016-04-28 21:24:06 -04002645 ret = _mv88e6xxx_phy_page_write(ps, REG_FIBER_SERDES,
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002646 PAGE_FIBER_SERDES, MII_BMCR,
2647 ret);
2648 }
2649
2650 return ret;
2651}
2652
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002653static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002654{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002655 struct dsa_switch *ds = ps->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002656 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002657 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002658
Andrew Lunn158bc062016-04-28 21:24:06 -04002659 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2660 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2661 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2662 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002663 /* MAC Forcing register: don't force link, speed,
2664 * duplex or flow control state to any particular
2665 * values on physical ports, but force the CPU port
2666 * and all DSA ports to their maximum bandwidth and
2667 * full duplex.
2668 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002669 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002670 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002671 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002672 reg |= PORT_PCS_CTRL_FORCE_LINK |
2673 PORT_PCS_CTRL_LINK_UP |
2674 PORT_PCS_CTRL_DUPLEX_FULL |
2675 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002676 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002677 reg |= PORT_PCS_CTRL_100;
2678 else
2679 reg |= PORT_PCS_CTRL_1000;
2680 } else {
2681 reg |= PORT_PCS_CTRL_UNFORCED;
2682 }
2683
Andrew Lunn158bc062016-04-28 21:24:06 -04002684 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002685 PORT_PCS_CTRL, reg);
2686 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002687 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002688 }
2689
2690 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2691 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2692 * tunneling, determine priority by looking at 802.1p and IP
2693 * priority fields (IP prio has precedence), and set STP state
2694 * to Forwarding.
2695 *
2696 * If this is the CPU link, use DSA or EDSA tagging depending
2697 * on which tagging mode was configured.
2698 *
2699 * If this is a link to another switch, use DSA tagging mode.
2700 *
2701 * If this is the upstream port for this switch, enable
2702 * forwarding of unknown unicasts and multicasts.
2703 */
2704 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002705 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2706 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2707 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2708 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002709 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2710 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2711 PORT_CONTROL_STATE_FORWARDING;
2712 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002713 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002714 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002715 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2716 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2717 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002718 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2719 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
2720 else
2721 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002722 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2723 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002724 }
2725
Andrew Lunn158bc062016-04-28 21:24:06 -04002726 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2727 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2728 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2729 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002730 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
2731 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2732 }
2733 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002734 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002735 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002736 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002737 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2738 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2739 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002740 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002741 }
2742
Andrew Lunn54d792f2015-05-06 01:09:47 +02002743 if (port == dsa_upstream_port(ds))
2744 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2745 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2746 }
2747 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002748 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002749 PORT_CONTROL, reg);
2750 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002751 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002752 }
2753
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002754 /* If this port is connected to a SerDes, make sure the SerDes is not
2755 * powered down.
2756 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002757 if (mv88e6xxx_6352_family(ps)) {
2758 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002759 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002760 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002761 ret &= PORT_STATUS_CMODE_MASK;
2762 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2763 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2764 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002765 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002766 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002767 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002768 }
2769 }
2770
Vivien Didelot8efdda42015-08-13 12:52:23 -04002771 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002772 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002773 * untagged frames on this port, do a destination address lookup on all
2774 * received packets as usual, disable ARP mirroring and don't send a
2775 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002776 */
2777 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002778 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2779 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2780 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2781 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002782 reg = PORT_CONTROL_2_MAP_DA;
2783
Andrew Lunn158bc062016-04-28 21:24:06 -04002784 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2785 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002786 reg |= PORT_CONTROL_2_JUMBO_10240;
2787
Andrew Lunn158bc062016-04-28 21:24:06 -04002788 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002789 /* Set the upstream port this port should use */
2790 reg |= dsa_upstream_port(ds);
2791 /* enable forwarding of unknown multicast addresses to
2792 * the upstream port
2793 */
2794 if (port == dsa_upstream_port(ds))
2795 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2796 }
2797
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002798 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002799
Andrew Lunn54d792f2015-05-06 01:09:47 +02002800 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002801 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002802 PORT_CONTROL_2, reg);
2803 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002804 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002805 }
2806
2807 /* Port Association Vector: when learning source addresses
2808 * of packets, add the address to the address database using
2809 * a port bitmap that has only the bit for this port set and
2810 * the other bits clear.
2811 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002812 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002813 /* Disable learning for CPU port */
2814 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002815 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002816
Andrew Lunn158bc062016-04-28 21:24:06 -04002817 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002818 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002819 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002820
2821 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002822 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002823 0x0000);
2824 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002825 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002826
Andrew Lunn158bc062016-04-28 21:24:06 -04002827 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2828 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2829 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002830 /* Do not limit the period of time that this port can
2831 * be paused for by the remote end or the period of
2832 * time that this port can pause the remote end.
2833 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002834 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002835 PORT_PAUSE_CTRL, 0x0000);
2836 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002837 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002838
2839 /* Port ATU control: disable limiting the number of
2840 * address database entries that this port is allowed
2841 * to use.
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_ATU_CONTROL, 0x0000);
2845 /* Priority Override: disable DA, SA and VTU priority
2846 * override.
2847 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002848 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002849 PORT_PRI_OVERRIDE, 0x0000);
2850 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002851 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002852
2853 /* Port Ethertype: use the Ethertype DSA Ethertype
2854 * value.
2855 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002856 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002857 PORT_ETH_TYPE, ETH_P_EDSA);
2858 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002859 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002860 /* Tag Remap: use an identity 802.1p prio -> switch
2861 * prio mapping.
2862 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002863 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002864 PORT_TAG_REGMAP_0123, 0x3210);
2865 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002866 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002867
2868 /* Tag Remap 2: use an identity 802.1p prio -> switch
2869 * prio mapping.
2870 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002871 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002872 PORT_TAG_REGMAP_4567, 0x7654);
2873 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002874 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002875 }
2876
Andrew Lunn158bc062016-04-28 21:24:06 -04002877 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2878 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2879 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2880 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002881 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002882 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002883 PORT_RATE_CONTROL, 0x0001);
2884 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002885 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002886 }
2887
Guenter Roeck366f0a02015-03-26 18:36:30 -07002888 /* Port Control 1: disable trunking, disable sending
2889 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002890 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002891 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002892 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002893 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002894
Vivien Didelot207afda2016-04-14 14:42:09 -04002895 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002896 * database, and allow bidirectional communication between the
2897 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002898 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002899 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002900 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002901 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002902
Andrew Lunn158bc062016-04-28 21:24:06 -04002903 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002904 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002905 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002906
2907 /* Default VLAN ID and priority: don't set a default VLAN
2908 * ID, and set the default packet priority to zero.
2909 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002910 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002911 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002912 if (ret)
2913 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002914
Andrew Lunndbde9e62015-05-06 01:09:48 +02002915 return 0;
2916}
2917
Vivien Didelot08a01262016-05-09 13:22:50 -04002918static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
2919{
Vivien Didelotb0745e872016-05-09 13:22:53 -04002920 struct dsa_switch *ds = ps->ds;
2921 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002922 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002923 int err;
2924 int i;
2925
Vivien Didelot119477b2016-05-09 13:22:51 -04002926 /* Enable the PHY Polling Unit if present, don't discard any packets,
2927 * and mask all interrupt sources.
2928 */
2929 reg = 0;
2930 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) ||
2931 mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE))
2932 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2933
2934 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg);
2935 if (err)
2936 return err;
2937
Vivien Didelotb0745e872016-05-09 13:22:53 -04002938 /* Configure the upstream port, and configure it as the port to which
2939 * ingress and egress and ARP monitor frames are to be sent.
2940 */
2941 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2942 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2943 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2944 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
2945 if (err)
2946 return err;
2947
Vivien Didelot50484ff2016-05-09 13:22:54 -04002948 /* Disable remote management, and set the switch's DSA device number. */
2949 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
2950 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2951 (ds->index & 0x1f));
2952 if (err)
2953 return err;
2954
Vivien Didelot08a01262016-05-09 13:22:50 -04002955 /* Set the default address aging time to 5 minutes, and
2956 * enable address learn messages to be sent to all message
2957 * ports.
2958 */
2959 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2960 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2961 if (err)
2962 return err;
2963
2964 /* Configure the IP ToS mapping registers. */
2965 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2966 if (err)
2967 return err;
2968 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2969 if (err)
2970 return err;
2971 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2972 if (err)
2973 return err;
2974 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2975 if (err)
2976 return err;
2977 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2978 if (err)
2979 return err;
2980 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2981 if (err)
2982 return err;
2983 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2984 if (err)
2985 return err;
2986 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2987 if (err)
2988 return err;
2989
2990 /* Configure the IEEE 802.1p priority mapping register. */
2991 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
2992 if (err)
2993 return err;
2994
2995 /* Send all frames with destination addresses matching
2996 * 01:80:c2:00:00:0x to the CPU port.
2997 */
2998 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
2999 if (err)
3000 return err;
3001
3002 /* Ignore removed tag data on doubly tagged packets, disable
3003 * flow control messages, force flow control priority to the
3004 * highest, and send all special multicast frames to the CPU
3005 * port at the highest priority.
3006 */
3007 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
3008 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3009 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3010 if (err)
3011 return err;
3012
3013 /* Program the DSA routing table. */
3014 for (i = 0; i < 32; i++) {
3015 int nexthop = 0x1f;
3016
Andrew Lunnff049552016-05-10 23:27:24 +02003017 if (ps->ds->cd->rtable &&
Vivien Didelot08a01262016-05-09 13:22:50 -04003018 i != ps->ds->index && i < ps->ds->dst->pd->nr_chips)
Andrew Lunnff049552016-05-10 23:27:24 +02003019 nexthop = ps->ds->cd->rtable[i] & 0x1f;
Vivien Didelot08a01262016-05-09 13:22:50 -04003020
3021 err = _mv88e6xxx_reg_write(
3022 ps, REG_GLOBAL2,
3023 GLOBAL2_DEVICE_MAPPING,
3024 GLOBAL2_DEVICE_MAPPING_UPDATE |
3025 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
3026 if (err)
3027 return err;
3028 }
3029
3030 /* Clear all trunk masks. */
3031 for (i = 0; i < 8; i++) {
3032 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
3033 0x8000 |
3034 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
3035 ((1 << ps->info->num_ports) - 1));
3036 if (err)
3037 return err;
3038 }
3039
3040 /* Clear all trunk mappings. */
3041 for (i = 0; i < 16; i++) {
3042 err = _mv88e6xxx_reg_write(
3043 ps, REG_GLOBAL2,
3044 GLOBAL2_TRUNK_MAPPING,
3045 GLOBAL2_TRUNK_MAPPING_UPDATE |
3046 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
3047 if (err)
3048 return err;
3049 }
3050
3051 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3052 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3053 mv88e6xxx_6320_family(ps)) {
3054 /* Send all frames with destination addresses matching
3055 * 01:80:c2:00:00:2x to the CPU port.
3056 */
3057 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3058 GLOBAL2_MGMT_EN_2X, 0xffff);
3059 if (err)
3060 return err;
3061
3062 /* Initialise cross-chip port VLAN table to reset
3063 * defaults.
3064 */
3065 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3066 GLOBAL2_PVT_ADDR, 0x9000);
3067 if (err)
3068 return err;
3069
3070 /* Clear the priority override table. */
3071 for (i = 0; i < 16; i++) {
3072 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3073 GLOBAL2_PRIO_OVERRIDE,
3074 0x8000 | (i << 8));
3075 if (err)
3076 return err;
3077 }
3078 }
3079
3080 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3081 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3082 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
3083 mv88e6xxx_6320_family(ps)) {
3084 /* Disable ingress rate limiting by resetting all
3085 * ingress rate limit registers to their initial
3086 * state.
3087 */
3088 for (i = 0; i < ps->info->num_ports; i++) {
3089 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3090 GLOBAL2_INGRESS_OP,
3091 0x9000 | (i << 8));
3092 if (err)
3093 return err;
3094 }
3095 }
3096
3097 /* Clear the statistics counters for all ports */
3098 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
3099 GLOBAL_STATS_OP_FLUSH_ALL);
3100 if (err)
3101 return err;
3102
3103 /* Wait for the flush to complete. */
3104 err = _mv88e6xxx_stats_wait(ps);
3105 if (err)
3106 return err;
3107
3108 /* Clear all ATU entries */
3109 err = _mv88e6xxx_atu_flush(ps, 0, true);
3110 if (err)
3111 return err;
3112
3113 /* Clear all the VTU and STU entries */
3114 err = _mv88e6xxx_vtu_stu_flush(ps);
3115 if (err < 0)
3116 return err;
3117
3118 return err;
3119}
3120
Vivien Didelotf81ec902016-05-09 13:22:58 -04003121static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003122{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003123 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003124 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003125 int i;
3126
3127 ps->ds = ds;
Vivien Didelot552238b2016-05-09 13:22:49 -04003128
Vivien Didelotd24645b2016-05-09 13:22:41 -04003129 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
3130 mutex_init(&ps->eeprom_mutex);
3131
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003132 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3133 mv88e6xxx_ppu_state_init(ps);
3134
Vivien Didelot552238b2016-05-09 13:22:49 -04003135 mutex_lock(&ps->smi_mutex);
3136
3137 err = mv88e6xxx_switch_reset(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003138 if (err)
3139 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003140
Vivien Didelot08a01262016-05-09 13:22:50 -04003141 err = mv88e6xxx_setup_global(ps);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003142 if (err)
3143 goto unlock;
3144
3145 for (i = 0; i < ps->info->num_ports; i++) {
3146 err = mv88e6xxx_setup_port(ps, i);
3147 if (err)
3148 goto unlock;
3149 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003150
Vivien Didelot6b17e862015-08-13 12:52:18 -04003151unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04003152 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02003153
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003154 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003155}
3156
Andrew Lunn491435852015-04-02 04:06:35 +02003157int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
3158{
3159 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3160 int ret;
3161
Andrew Lunn3898c142015-05-06 01:09:53 +02003162 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003163 ret = _mv88e6xxx_phy_page_read(ps, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02003164 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003165
Andrew Lunn491435852015-04-02 04:06:35 +02003166 return ret;
3167}
3168
3169int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
3170 int reg, int val)
3171{
3172 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3173 int ret;
3174
Andrew Lunn3898c142015-05-06 01:09:53 +02003175 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04003176 ret = _mv88e6xxx_phy_page_write(ps, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003177 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003178
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003179 return ret;
3180}
3181
Andrew Lunn158bc062016-04-28 21:24:06 -04003182static int mv88e6xxx_port_to_phy_addr(struct mv88e6xxx_priv_state *ps,
3183 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003184{
Vivien Didelot009a2b92016-04-17 13:24:01 -04003185 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003186 return port;
3187 return -EINVAL;
3188}
3189
Vivien Didelotf81ec902016-05-09 13:22:58 -04003190static int mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003191{
3192 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003193 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003194 int ret;
3195
3196 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003197 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003198
Andrew Lunn3898c142015-05-06 01:09:53 +02003199 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003200
3201 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3202 ret = mv88e6xxx_phy_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003203 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3204 ret = _mv88e6xxx_phy_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003205 else
3206 ret = _mv88e6xxx_phy_read(ps, addr, regnum);
3207
Andrew Lunn3898c142015-05-06 01:09:53 +02003208 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003209 return ret;
3210}
3211
Vivien Didelotf81ec902016-05-09 13:22:58 -04003212static int mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum,
3213 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003214{
3215 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn158bc062016-04-28 21:24:06 -04003216 int addr = mv88e6xxx_port_to_phy_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003217 int ret;
3218
3219 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003220 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003221
Andrew Lunn3898c142015-05-06 01:09:53 +02003222 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003223
3224 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3225 ret = mv88e6xxx_phy_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003226 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
3227 ret = _mv88e6xxx_phy_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003228 else
3229 ret = _mv88e6xxx_phy_write(ps, addr, regnum, val);
3230
Andrew Lunn3898c142015-05-06 01:09:53 +02003231 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003232 return ret;
3233}
3234
Guenter Roeckc22995c2015-07-25 09:42:28 -07003235#ifdef CONFIG_NET_DSA_HWMON
3236
3237static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3238{
3239 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3240 int ret;
3241 int val;
3242
3243 *temp = 0;
3244
3245 mutex_lock(&ps->smi_mutex);
3246
Andrew Lunn158bc062016-04-28 21:24:06 -04003247 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003248 if (ret < 0)
3249 goto error;
3250
3251 /* Enable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003252 ret = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003253 if (ret < 0)
3254 goto error;
3255
Andrew Lunn158bc062016-04-28 21:24:06 -04003256 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003257 if (ret < 0)
3258 goto error;
3259
3260 /* Wait for temperature to stabilize */
3261 usleep_range(10000, 12000);
3262
Andrew Lunn158bc062016-04-28 21:24:06 -04003263 val = _mv88e6xxx_phy_read(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003264 if (val < 0) {
3265 ret = val;
3266 goto error;
3267 }
3268
3269 /* Disable temperature sensor */
Andrew Lunn158bc062016-04-28 21:24:06 -04003270 ret = _mv88e6xxx_phy_write(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003271 if (ret < 0)
3272 goto error;
3273
3274 *temp = ((val & 0x1f) - 5) * 5;
3275
3276error:
Andrew Lunn158bc062016-04-28 21:24:06 -04003277 _mv88e6xxx_phy_write(ps, 0x0, 0x16, 0x0);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003278 mutex_unlock(&ps->smi_mutex);
3279 return ret;
3280}
3281
3282static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3283{
Andrew Lunn158bc062016-04-28 21:24:06 -04003284 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3285 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003286 int ret;
3287
3288 *temp = 0;
3289
3290 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
3291 if (ret < 0)
3292 return ret;
3293
3294 *temp = (ret & 0xff) - 25;
3295
3296 return 0;
3297}
3298
Vivien Didelotf81ec902016-05-09 13:22:58 -04003299static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003300{
Andrew Lunn158bc062016-04-28 21:24:06 -04003301 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3302
Vivien Didelot6594f612016-05-09 13:22:42 -04003303 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
3304 return -EOPNOTSUPP;
3305
Andrew Lunn158bc062016-04-28 21:24:06 -04003306 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003307 return mv88e63xx_get_temp(ds, temp);
3308
3309 return mv88e61xx_get_temp(ds, temp);
3310}
3311
Vivien Didelotf81ec902016-05-09 13:22:58 -04003312static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003313{
Andrew Lunn158bc062016-04-28 21:24:06 -04003314 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3315 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003316 int ret;
3317
Vivien Didelot6594f612016-05-09 13:22:42 -04003318 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003319 return -EOPNOTSUPP;
3320
3321 *temp = 0;
3322
3323 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3324 if (ret < 0)
3325 return ret;
3326
3327 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3328
3329 return 0;
3330}
3331
Vivien Didelotf81ec902016-05-09 13:22:58 -04003332static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003333{
Andrew Lunn158bc062016-04-28 21:24:06 -04003334 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3335 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003336 int ret;
3337
Vivien Didelot6594f612016-05-09 13:22:42 -04003338 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003339 return -EOPNOTSUPP;
3340
3341 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3342 if (ret < 0)
3343 return ret;
3344 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
3345 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
3346 (ret & 0xe0ff) | (temp << 8));
3347}
3348
Vivien Didelotf81ec902016-05-09 13:22:58 -04003349static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003350{
Andrew Lunn158bc062016-04-28 21:24:06 -04003351 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3352 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003353 int ret;
3354
Vivien Didelot6594f612016-05-09 13:22:42 -04003355 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003356 return -EOPNOTSUPP;
3357
3358 *alarm = false;
3359
3360 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
3361 if (ret < 0)
3362 return ret;
3363
3364 *alarm = !!(ret & 0x40);
3365
3366 return 0;
3367}
3368#endif /* CONFIG_NET_DSA_HWMON */
3369
Vivien Didelotf81ec902016-05-09 13:22:58 -04003370static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3371 [MV88E6085] = {
3372 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3373 .family = MV88E6XXX_FAMILY_6097,
3374 .name = "Marvell 88E6085",
3375 .num_databases = 4096,
3376 .num_ports = 10,
3377 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3378 },
3379
3380 [MV88E6095] = {
3381 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3382 .family = MV88E6XXX_FAMILY_6095,
3383 .name = "Marvell 88E6095/88E6095F",
3384 .num_databases = 256,
3385 .num_ports = 11,
3386 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3387 },
3388
3389 [MV88E6123] = {
3390 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3391 .family = MV88E6XXX_FAMILY_6165,
3392 .name = "Marvell 88E6123",
3393 .num_databases = 4096,
3394 .num_ports = 3,
3395 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3396 },
3397
3398 [MV88E6131] = {
3399 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3400 .family = MV88E6XXX_FAMILY_6185,
3401 .name = "Marvell 88E6131",
3402 .num_databases = 256,
3403 .num_ports = 8,
3404 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3405 },
3406
3407 [MV88E6161] = {
3408 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3409 .family = MV88E6XXX_FAMILY_6165,
3410 .name = "Marvell 88E6161",
3411 .num_databases = 4096,
3412 .num_ports = 6,
3413 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3414 },
3415
3416 [MV88E6165] = {
3417 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3418 .family = MV88E6XXX_FAMILY_6165,
3419 .name = "Marvell 88E6165",
3420 .num_databases = 4096,
3421 .num_ports = 6,
3422 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3423 },
3424
3425 [MV88E6171] = {
3426 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3427 .family = MV88E6XXX_FAMILY_6351,
3428 .name = "Marvell 88E6171",
3429 .num_databases = 4096,
3430 .num_ports = 7,
3431 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3432 },
3433
3434 [MV88E6172] = {
3435 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3436 .family = MV88E6XXX_FAMILY_6352,
3437 .name = "Marvell 88E6172",
3438 .num_databases = 4096,
3439 .num_ports = 7,
3440 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3441 },
3442
3443 [MV88E6175] = {
3444 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3445 .family = MV88E6XXX_FAMILY_6351,
3446 .name = "Marvell 88E6175",
3447 .num_databases = 4096,
3448 .num_ports = 7,
3449 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3450 },
3451
3452 [MV88E6176] = {
3453 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3454 .family = MV88E6XXX_FAMILY_6352,
3455 .name = "Marvell 88E6176",
3456 .num_databases = 4096,
3457 .num_ports = 7,
3458 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3459 },
3460
3461 [MV88E6185] = {
3462 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3463 .family = MV88E6XXX_FAMILY_6185,
3464 .name = "Marvell 88E6185",
3465 .num_databases = 256,
3466 .num_ports = 10,
3467 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3468 },
3469
3470 [MV88E6240] = {
3471 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3472 .family = MV88E6XXX_FAMILY_6352,
3473 .name = "Marvell 88E6240",
3474 .num_databases = 4096,
3475 .num_ports = 7,
3476 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3477 },
3478
3479 [MV88E6320] = {
3480 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3481 .family = MV88E6XXX_FAMILY_6320,
3482 .name = "Marvell 88E6320",
3483 .num_databases = 4096,
3484 .num_ports = 7,
3485 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3486 },
3487
3488 [MV88E6321] = {
3489 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3490 .family = MV88E6XXX_FAMILY_6320,
3491 .name = "Marvell 88E6321",
3492 .num_databases = 4096,
3493 .num_ports = 7,
3494 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3495 },
3496
3497 [MV88E6350] = {
3498 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3499 .family = MV88E6XXX_FAMILY_6351,
3500 .name = "Marvell 88E6350",
3501 .num_databases = 4096,
3502 .num_ports = 7,
3503 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3504 },
3505
3506 [MV88E6351] = {
3507 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3508 .family = MV88E6XXX_FAMILY_6351,
3509 .name = "Marvell 88E6351",
3510 .num_databases = 4096,
3511 .num_ports = 7,
3512 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3513 },
3514
3515 [MV88E6352] = {
3516 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3517 .family = MV88E6XXX_FAMILY_6352,
3518 .name = "Marvell 88E6352",
3519 .num_databases = 4096,
3520 .num_ports = 7,
3521 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3522 },
3523};
3524
Vivien Didelotf6271e62016-04-17 13:23:59 -04003525static const struct mv88e6xxx_info *
3526mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003527 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003528{
Vivien Didelota439c062016-04-17 13:23:58 -04003529 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003530
Vivien Didelotb9b37712015-10-30 19:39:48 -04003531 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003532 if (table[i].prod_num == prod_num)
3533 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003534
Vivien Didelotb9b37712015-10-30 19:39:48 -04003535 return NULL;
3536}
3537
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003538static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3539 struct device *host_dev, int sw_addr,
3540 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003541{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003542 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003543 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003544 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003545 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003546 int id, prod_num, rev;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003547
Vivien Didelota439c062016-04-17 13:23:58 -04003548 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003549 if (!bus)
3550 return NULL;
3551
Vivien Didelota439c062016-04-17 13:23:58 -04003552 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3553 if (id < 0)
3554 return NULL;
3555
3556 prod_num = (id & 0xfff0) >> 4;
3557 rev = id & 0x000f;
3558
Vivien Didelotf81ec902016-05-09 13:22:58 -04003559 info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3560 ARRAY_SIZE(mv88e6xxx_table));
Vivien Didelotf6271e62016-04-17 13:23:59 -04003561 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003562 return NULL;
3563
Vivien Didelotf6271e62016-04-17 13:23:59 -04003564 name = info->name;
3565
Vivien Didelota439c062016-04-17 13:23:58 -04003566 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3567 if (!ps)
3568 return NULL;
3569
3570 ps->bus = bus;
3571 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003572 ps->info = info;
Andrew Lunnb6819572016-05-10 23:27:19 +02003573 mutex_init(&ps->smi_mutex);
Vivien Didelota439c062016-04-17 13:23:58 -04003574
3575 *priv = ps;
3576
3577 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3578 prod_num, name, rev);
3579
Andrew Lunna77d43f2016-04-13 02:40:42 +02003580 return name;
3581}
3582
Vivien Didelotf81ec902016-05-09 13:22:58 -04003583struct dsa_switch_driver mv88e6xxx_switch_driver = {
3584 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003585 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003586 .setup = mv88e6xxx_setup,
3587 .set_addr = mv88e6xxx_set_addr,
3588 .phy_read = mv88e6xxx_phy_read,
3589 .phy_write = mv88e6xxx_phy_write,
3590 .adjust_link = mv88e6xxx_adjust_link,
3591 .get_strings = mv88e6xxx_get_strings,
3592 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3593 .get_sset_count = mv88e6xxx_get_sset_count,
3594 .set_eee = mv88e6xxx_set_eee,
3595 .get_eee = mv88e6xxx_get_eee,
3596#ifdef CONFIG_NET_DSA_HWMON
3597 .get_temp = mv88e6xxx_get_temp,
3598 .get_temp_limit = mv88e6xxx_get_temp_limit,
3599 .set_temp_limit = mv88e6xxx_set_temp_limit,
3600 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3601#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003602 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003603 .get_eeprom = mv88e6xxx_get_eeprom,
3604 .set_eeprom = mv88e6xxx_set_eeprom,
3605 .get_regs_len = mv88e6xxx_get_regs_len,
3606 .get_regs = mv88e6xxx_get_regs,
3607 .port_bridge_join = mv88e6xxx_port_bridge_join,
3608 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3609 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3610 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3611 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3612 .port_vlan_add = mv88e6xxx_port_vlan_add,
3613 .port_vlan_del = mv88e6xxx_port_vlan_del,
3614 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3615 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3616 .port_fdb_add = mv88e6xxx_port_fdb_add,
3617 .port_fdb_del = mv88e6xxx_port_fdb_del,
3618 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3619};
3620
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003621int mv88e6xxx_probe(struct mdio_device *mdiodev)
3622{
3623 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003624 struct device_node *np = dev->of_node;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003625 struct mv88e6xxx_priv_state *ps;
3626 int id, prod_num, rev;
3627 struct dsa_switch *ds;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003628 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003629 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003630
3631 ds = devm_kzalloc(dev, sizeof(*ds) + sizeof(*ps), GFP_KERNEL);
3632 if (!ds)
3633 return -ENOMEM;
3634
3635 ps = (struct mv88e6xxx_priv_state *)(ds + 1);
3636 ds->priv = ps;
Andrew Lunnc33063d2016-05-10 23:27:23 +02003637 ds->dev = dev;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003638 ps->dev = dev;
3639 ps->ds = ds;
3640 ps->bus = mdiodev->bus;
3641 ps->sw_addr = mdiodev->addr;
3642 mutex_init(&ps->smi_mutex);
3643
3644 get_device(&ps->bus->dev);
3645
3646 ds->drv = &mv88e6xxx_switch_driver;
3647
3648 id = mv88e6xxx_reg_read(ps, REG_PORT(0), PORT_SWITCH_ID);
3649 if (id < 0)
3650 return id;
3651
3652 prod_num = (id & 0xfff0) >> 4;
3653 rev = id & 0x000f;
3654
3655 ps->info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3656 ARRAY_SIZE(mv88e6xxx_table));
3657 if (!ps->info)
3658 return -ENODEV;
3659
Andrew Lunn52638f72016-05-10 23:27:22 +02003660 ps->reset = devm_gpiod_get(&mdiodev->dev, "reset", GPIOD_ASIS);
3661 if (IS_ERR(ps->reset)) {
3662 err = PTR_ERR(ps->reset);
3663 if (err == -ENOENT) {
3664 /* Optional, so not an error */
3665 ps->reset = NULL;
3666 } else {
3667 return err;
3668 }
3669 }
3670
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003671 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM) &&
3672 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3673 ps->eeprom_len = eeprom_len;
3674
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003675 dev_set_drvdata(dev, ds);
3676
3677 dev_info(dev, "switch 0x%x probed: %s, revision %u\n",
3678 prod_num, ps->info->name, rev);
3679
3680 return 0;
3681}
3682
3683static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3684{
3685 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
3686 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3687
3688 put_device(&ps->bus->dev);
3689}
3690
3691static const struct of_device_id mv88e6xxx_of_match[] = {
3692 { .compatible = "marvell,mv88e6085" },
3693 { /* sentinel */ },
3694};
3695
3696MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3697
3698static struct mdio_driver mv88e6xxx_driver = {
3699 .probe = mv88e6xxx_probe,
3700 .remove = mv88e6xxx_remove,
3701 .mdiodrv.driver = {
3702 .name = "mv88e6085",
3703 .of_match_table = mv88e6xxx_of_match,
3704 },
3705};
3706
Ben Hutchings98e67302011-11-25 14:36:19 +00003707static int __init mv88e6xxx_init(void)
3708{
Vivien Didelotf81ec902016-05-09 13:22:58 -04003709 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003710 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003711}
3712module_init(mv88e6xxx_init);
3713
3714static void __exit mv88e6xxx_cleanup(void)
3715{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003716 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04003717 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003718}
3719module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003720
3721MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3722MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3723MODULE_LICENSE("GPL");