blob: 192b39cbdbcd799621e89330923da05af54d982d [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>
Andrew Lunnb516d452016-06-04 21:17:06 +020024#include <linux/of_mdio.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000025#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010026#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000027#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000028#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040029#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000030#include "mv88e6xxx.h"
31
Andrew Lunn158bc062016-04-28 21:24:06 -040032static void assert_smi_lock(struct mv88e6xxx_priv_state *ps)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040033{
Vivien Didelot3996a4f2015-10-30 18:56:45 -040034 if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
Andrew Lunn158bc062016-04-28 21:24:06 -040035 dev_err(ps->dev, "SMI lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040036 dump_stack();
37 }
38}
39
Barry Grussling3675c8d2013-01-08 16:05:53 +000040/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000041 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
42 * will be directly accessible on some {device address,register address}
43 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
44 * will only respond to SMI transactions to that specific address, and
45 * an indirect addressing mechanism needs to be used to access its
46 * registers.
47 */
48static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
49{
50 int ret;
51 int i;
52
53 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020054 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000055 if (ret < 0)
56 return ret;
57
Andrew Lunncca8b132015-04-02 04:06:39 +020058 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000059 return 0;
60 }
61
62 return -ETIMEDOUT;
63}
64
Vivien Didelotb9b37712015-10-30 19:39:48 -040065static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
66 int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000067{
68 int ret;
69
70 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020071 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000072
Barry Grussling3675c8d2013-01-08 16:05:53 +000073 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000074 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
75 if (ret < 0)
76 return ret;
77
Barry Grussling3675c8d2013-01-08 16:05:53 +000078 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020079 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
80 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000081 if (ret < 0)
82 return ret;
83
Barry Grussling3675c8d2013-01-08 16:05:53 +000084 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000085 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
86 if (ret < 0)
87 return ret;
88
Barry Grussling3675c8d2013-01-08 16:05:53 +000089 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020090 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000091 if (ret < 0)
92 return ret;
93
94 return ret & 0xffff;
95}
96
Andrew Lunn158bc062016-04-28 21:24:06 -040097static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
98 int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000099{
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000100 int ret;
101
Andrew Lunn158bc062016-04-28 21:24:06 -0400102 assert_smi_lock(ps);
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400103
Andrew Lunna77d43f2016-04-13 02:40:42 +0200104 ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500105 if (ret < 0)
106 return ret;
107
Andrew Lunn158bc062016-04-28 21:24:06 -0400108 dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500109 addr, reg, ret);
110
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000111 return ret;
112}
113
Andrew Lunn158bc062016-04-28 21:24:06 -0400114int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr, int reg)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700115{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700116 int ret;
117
118 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400119 ret = _mv88e6xxx_reg_read(ps, addr, reg);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700120 mutex_unlock(&ps->smi_mutex);
121
122 return ret;
123}
124
Vivien Didelotb9b37712015-10-30 19:39:48 -0400125static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
126 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000127{
128 int ret;
129
130 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200131 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000132
Barry Grussling3675c8d2013-01-08 16:05:53 +0000133 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000134 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
135 if (ret < 0)
136 return ret;
137
Barry Grussling3675c8d2013-01-08 16:05:53 +0000138 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200139 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000140 if (ret < 0)
141 return ret;
142
Barry Grussling3675c8d2013-01-08 16:05:53 +0000143 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200144 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
145 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000146 if (ret < 0)
147 return ret;
148
Barry Grussling3675c8d2013-01-08 16:05:53 +0000149 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000150 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
151 if (ret < 0)
152 return ret;
153
154 return 0;
155}
156
Andrew Lunn158bc062016-04-28 21:24:06 -0400157static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
158 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000159{
Andrew Lunn158bc062016-04-28 21:24:06 -0400160 assert_smi_lock(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000161
Andrew Lunn158bc062016-04-28 21:24:06 -0400162 dev_dbg(ps->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500163 addr, reg, val);
164
Andrew Lunna77d43f2016-04-13 02:40:42 +0200165 return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700166}
167
Andrew Lunn158bc062016-04-28 21:24:06 -0400168int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
169 int reg, u16 val)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700170{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700171 int ret;
172
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000173 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400174 ret = _mv88e6xxx_reg_write(ps, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000175 mutex_unlock(&ps->smi_mutex);
176
177 return ret;
178}
179
Vivien Didelot1d13a062016-05-09 13:22:43 -0400180static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000181{
Andrew Lunn158bc062016-04-28 21:24:06 -0400182 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200183 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000184
Andrew Lunn158bc062016-04-28 21:24:06 -0400185 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_01,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200186 (addr[0] << 8) | addr[1]);
187 if (err)
188 return err;
189
Andrew Lunn158bc062016-04-28 21:24:06 -0400190 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_23,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200191 (addr[2] << 8) | addr[3]);
192 if (err)
193 return err;
194
Andrew Lunn158bc062016-04-28 21:24:06 -0400195 return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_45,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200196 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000197}
198
Vivien Didelot1d13a062016-05-09 13:22:43 -0400199static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000200{
Andrew Lunn158bc062016-04-28 21:24:06 -0400201 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000202 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200203 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000204
205 for (i = 0; i < 6; i++) {
206 int j;
207
Barry Grussling3675c8d2013-01-08 16:05:53 +0000208 /* Write the MAC address byte. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400209 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200210 GLOBAL2_SWITCH_MAC_BUSY |
211 (i << 8) | addr[i]);
212 if (ret)
213 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000214
Barry Grussling3675c8d2013-01-08 16:05:53 +0000215 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000216 for (j = 0; j < 16; j++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400217 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200218 GLOBAL2_SWITCH_MAC);
219 if (ret < 0)
220 return ret;
221
Andrew Lunncca8b132015-04-02 04:06:39 +0200222 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000223 break;
224 }
225 if (j == 16)
226 return -ETIMEDOUT;
227 }
228
229 return 0;
230}
231
Vivien Didelot1d13a062016-05-09 13:22:43 -0400232int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
233{
234 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
235
236 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC))
237 return mv88e6xxx_set_addr_indirect(ds, addr);
238 else
239 return mv88e6xxx_set_addr_direct(ds, addr);
240}
241
Andrew Lunn03a4a542016-06-04 21:17:05 +0200242static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_priv_state *ps,
243 int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000244{
245 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400246 return _mv88e6xxx_reg_read(ps, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000247 return 0xffff;
248}
249
Andrew Lunn03a4a542016-06-04 21:17:05 +0200250static int mv88e6xxx_mdio_write_direct(struct mv88e6xxx_priv_state *ps,
251 int addr, int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000252{
253 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400254 return _mv88e6xxx_reg_write(ps, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000255 return 0;
256}
257
Andrew Lunn158bc062016-04-28 21:24:06 -0400258static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000259{
260 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000261 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000262
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400263 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200264 if (ret < 0)
265 return ret;
266
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400267 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
268 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200269 if (ret)
270 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000271
Barry Grussling19b2f972013-01-08 16:05:54 +0000272 timeout = jiffies + 1 * HZ;
273 while (time_before(jiffies, timeout)) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400274 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200275 if (ret < 0)
276 return ret;
277
Barry Grussling19b2f972013-01-08 16:05:54 +0000278 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200279 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
280 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000281 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000282 }
283
284 return -ETIMEDOUT;
285}
286
Andrew Lunn158bc062016-04-28 21:24:06 -0400287static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000288{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200289 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000290 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000291
Vivien Didelot762eb672016-06-04 21:16:54 +0200292 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200293 if (ret < 0)
294 return ret;
295
Vivien Didelot762eb672016-06-04 21:16:54 +0200296 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
297 ret | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200298 if (err)
299 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000300
Barry Grussling19b2f972013-01-08 16:05:54 +0000301 timeout = jiffies + 1 * HZ;
302 while (time_before(jiffies, timeout)) {
Vivien Didelot762eb672016-06-04 21:16:54 +0200303 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200304 if (ret < 0)
305 return ret;
306
Barry Grussling19b2f972013-01-08 16:05:54 +0000307 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200308 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
309 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000310 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000311 }
312
313 return -ETIMEDOUT;
314}
315
316static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
317{
318 struct mv88e6xxx_priv_state *ps;
319
320 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200321
322 mutex_lock(&ps->smi_mutex);
323
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000324 if (mutex_trylock(&ps->ppu_mutex)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400325 if (mv88e6xxx_ppu_enable(ps) == 0)
Barry Grussling85686582013-01-08 16:05:56 +0000326 ps->ppu_disabled = 0;
327 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000328 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200329
330 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000331}
332
333static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
334{
335 struct mv88e6xxx_priv_state *ps = (void *)_ps;
336
337 schedule_work(&ps->ppu_work);
338}
339
Andrew Lunn158bc062016-04-28 21:24:06 -0400340static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000341{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000342 int ret;
343
344 mutex_lock(&ps->ppu_mutex);
345
Barry Grussling3675c8d2013-01-08 16:05:53 +0000346 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000347 * we can access the PHY registers. If it was already
348 * disabled, cancel the timer that is going to re-enable
349 * it.
350 */
351 if (!ps->ppu_disabled) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400352 ret = mv88e6xxx_ppu_disable(ps);
Barry Grussling85686582013-01-08 16:05:56 +0000353 if (ret < 0) {
354 mutex_unlock(&ps->ppu_mutex);
355 return ret;
356 }
357 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000358 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000359 del_timer(&ps->ppu_timer);
360 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000361 }
362
363 return ret;
364}
365
Andrew Lunn158bc062016-04-28 21:24:06 -0400366static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000367{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000368 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000369 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
370 mutex_unlock(&ps->ppu_mutex);
371}
372
Andrew Lunn158bc062016-04-28 21:24:06 -0400373void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000374{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000375 mutex_init(&ps->ppu_mutex);
376 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
377 init_timer(&ps->ppu_timer);
378 ps->ppu_timer.data = (unsigned long)ps;
379 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
380}
381
Andrew Lunn03a4a542016-06-04 21:17:05 +0200382static int mv88e6xxx_mdio_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
383 int regnum)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000384{
385 int ret;
386
Andrew Lunn158bc062016-04-28 21:24:06 -0400387 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000388 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400389 ret = _mv88e6xxx_reg_read(ps, addr, regnum);
Andrew Lunn158bc062016-04-28 21:24:06 -0400390 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000391 }
392
393 return ret;
394}
395
Andrew Lunn03a4a542016-06-04 21:17:05 +0200396static int mv88e6xxx_mdio_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
397 int regnum, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000398{
399 int ret;
400
Andrew Lunn158bc062016-04-28 21:24:06 -0400401 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000402 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400403 ret = _mv88e6xxx_reg_write(ps, addr, regnum, val);
Andrew Lunn158bc062016-04-28 21:24:06 -0400404 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000405 }
406
407 return ret;
408}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000409
Andrew Lunn158bc062016-04-28 21:24:06 -0400410static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200411{
Vivien Didelot22356472016-04-17 13:24:00 -0400412 return ps->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200413}
414
Andrew Lunn158bc062016-04-28 21:24:06 -0400415static bool mv88e6xxx_6095_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200416{
Vivien Didelot22356472016-04-17 13:24:00 -0400417 return ps->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200418}
419
Andrew Lunn158bc062016-04-28 21:24:06 -0400420static bool mv88e6xxx_6097_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200421{
Vivien Didelot22356472016-04-17 13:24:00 -0400422 return ps->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200423}
424
Andrew Lunn158bc062016-04-28 21:24:06 -0400425static bool mv88e6xxx_6165_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200426{
Vivien Didelot22356472016-04-17 13:24:00 -0400427 return ps->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200428}
429
Andrew Lunn158bc062016-04-28 21:24:06 -0400430static bool mv88e6xxx_6185_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200431{
Vivien Didelot22356472016-04-17 13:24:00 -0400432 return ps->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200433}
434
Andrew Lunn158bc062016-04-28 21:24:06 -0400435static bool mv88e6xxx_6320_family(struct mv88e6xxx_priv_state *ps)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700436{
Vivien Didelot22356472016-04-17 13:24:00 -0400437 return ps->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700438}
439
Andrew Lunn158bc062016-04-28 21:24:06 -0400440static bool mv88e6xxx_6351_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200441{
Vivien Didelot22356472016-04-17 13:24:00 -0400442 return ps->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200443}
444
Andrew Lunn158bc062016-04-28 21:24:06 -0400445static bool mv88e6xxx_6352_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200446{
Vivien Didelot22356472016-04-17 13:24:00 -0400447 return ps->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200448}
449
Andrew Lunn158bc062016-04-28 21:24:06 -0400450static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_priv_state *ps)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400451{
Vivien Didelotcd5a2c82016-04-17 13:24:02 -0400452 return ps->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400453}
454
Andrew Lunn158bc062016-04-28 21:24:06 -0400455static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400456{
457 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Andrew Lunn158bc062016-04-28 21:24:06 -0400458 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
459 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400460 return true;
461
462 return false;
463}
464
Andrew Lunndea87022015-08-31 15:56:47 +0200465/* We expect the switch to perform auto negotiation if there is a real
466 * phy. However, in the case of a fixed link phy, we force the port
467 * settings from the fixed link settings.
468 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400469static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
470 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200471{
472 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200473 u32 reg;
474 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200475
476 if (!phy_is_pseudo_fixed_link(phydev))
477 return;
478
479 mutex_lock(&ps->smi_mutex);
480
Andrew Lunn158bc062016-04-28 21:24:06 -0400481 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200482 if (ret < 0)
483 goto out;
484
485 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
486 PORT_PCS_CTRL_FORCE_LINK |
487 PORT_PCS_CTRL_DUPLEX_FULL |
488 PORT_PCS_CTRL_FORCE_DUPLEX |
489 PORT_PCS_CTRL_UNFORCED);
490
491 reg |= PORT_PCS_CTRL_FORCE_LINK;
492 if (phydev->link)
493 reg |= PORT_PCS_CTRL_LINK_UP;
494
Andrew Lunn158bc062016-04-28 21:24:06 -0400495 if (mv88e6xxx_6065_family(ps) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200496 goto out;
497
498 switch (phydev->speed) {
499 case SPEED_1000:
500 reg |= PORT_PCS_CTRL_1000;
501 break;
502 case SPEED_100:
503 reg |= PORT_PCS_CTRL_100;
504 break;
505 case SPEED_10:
506 reg |= PORT_PCS_CTRL_10;
507 break;
508 default:
509 pr_info("Unknown speed");
510 goto out;
511 }
512
513 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
514 if (phydev->duplex == DUPLEX_FULL)
515 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
516
Andrew Lunn158bc062016-04-28 21:24:06 -0400517 if ((mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps)) &&
Vivien Didelot009a2b92016-04-17 13:24:01 -0400518 (port >= ps->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200519 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
520 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
521 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
522 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
523 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
524 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
525 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
526 }
Andrew Lunn158bc062016-04-28 21:24:06 -0400527 _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200528
529out:
530 mutex_unlock(&ps->smi_mutex);
531}
532
Andrew Lunn158bc062016-04-28 21:24:06 -0400533static int _mv88e6xxx_stats_wait(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000534{
535 int ret;
536 int i;
537
538 for (i = 0; i < 10; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400539 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200540 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000541 return 0;
542 }
543
544 return -ETIMEDOUT;
545}
546
Andrew Lunn158bc062016-04-28 21:24:06 -0400547static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_priv_state *ps,
548 int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000549{
550 int ret;
551
Andrew Lunn158bc062016-04-28 21:24:06 -0400552 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200553 port = (port + 1) << 5;
554
Barry Grussling3675c8d2013-01-08 16:05:53 +0000555 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400556 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200557 GLOBAL_STATS_OP_CAPTURE_PORT |
558 GLOBAL_STATS_OP_HIST_RX_TX | port);
559 if (ret < 0)
560 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000561
Barry Grussling3675c8d2013-01-08 16:05:53 +0000562 /* Wait for the snapshotting to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400563 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000564 if (ret < 0)
565 return ret;
566
567 return 0;
568}
569
Andrew Lunn158bc062016-04-28 21:24:06 -0400570static void _mv88e6xxx_stats_read(struct mv88e6xxx_priv_state *ps,
571 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000572{
573 u32 _val;
574 int ret;
575
576 *val = 0;
577
Andrew Lunn158bc062016-04-28 21:24:06 -0400578 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200579 GLOBAL_STATS_OP_READ_CAPTURED |
580 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000581 if (ret < 0)
582 return;
583
Andrew Lunn158bc062016-04-28 21:24:06 -0400584 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000585 if (ret < 0)
586 return;
587
Andrew Lunn158bc062016-04-28 21:24:06 -0400588 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000589 if (ret < 0)
590 return;
591
592 _val = ret << 16;
593
Andrew Lunn158bc062016-04-28 21:24:06 -0400594 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000595 if (ret < 0)
596 return;
597
598 *val = _val | ret;
599}
600
Andrew Lunne413e7e2015-04-02 04:06:38 +0200601static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100602 { "in_good_octets", 8, 0x00, BANK0, },
603 { "in_bad_octets", 4, 0x02, BANK0, },
604 { "in_unicast", 4, 0x04, BANK0, },
605 { "in_broadcasts", 4, 0x06, BANK0, },
606 { "in_multicasts", 4, 0x07, BANK0, },
607 { "in_pause", 4, 0x16, BANK0, },
608 { "in_undersize", 4, 0x18, BANK0, },
609 { "in_fragments", 4, 0x19, BANK0, },
610 { "in_oversize", 4, 0x1a, BANK0, },
611 { "in_jabber", 4, 0x1b, BANK0, },
612 { "in_rx_error", 4, 0x1c, BANK0, },
613 { "in_fcs_error", 4, 0x1d, BANK0, },
614 { "out_octets", 8, 0x0e, BANK0, },
615 { "out_unicast", 4, 0x10, BANK0, },
616 { "out_broadcasts", 4, 0x13, BANK0, },
617 { "out_multicasts", 4, 0x12, BANK0, },
618 { "out_pause", 4, 0x15, BANK0, },
619 { "excessive", 4, 0x11, BANK0, },
620 { "collisions", 4, 0x1e, BANK0, },
621 { "deferred", 4, 0x05, BANK0, },
622 { "single", 4, 0x14, BANK0, },
623 { "multiple", 4, 0x17, BANK0, },
624 { "out_fcs_error", 4, 0x03, BANK0, },
625 { "late", 4, 0x1f, BANK0, },
626 { "hist_64bytes", 4, 0x08, BANK0, },
627 { "hist_65_127bytes", 4, 0x09, BANK0, },
628 { "hist_128_255bytes", 4, 0x0a, BANK0, },
629 { "hist_256_511bytes", 4, 0x0b, BANK0, },
630 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
631 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
632 { "sw_in_discards", 4, 0x10, PORT, },
633 { "sw_in_filtered", 2, 0x12, PORT, },
634 { "sw_out_filtered", 2, 0x13, PORT, },
635 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
636 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
637 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
638 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
639 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
640 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
641 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
642 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
643 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
644 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
645 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
646 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
647 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
648 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
649 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
650 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
651 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
652 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
653 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
654 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
655 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
656 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
657 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
658 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
659 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
660 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200661};
662
Andrew Lunn158bc062016-04-28 21:24:06 -0400663static bool mv88e6xxx_has_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100664 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200665{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100666 switch (stat->type) {
667 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200668 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100669 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400670 return mv88e6xxx_6320_family(ps);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100671 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400672 return mv88e6xxx_6095_family(ps) ||
673 mv88e6xxx_6185_family(ps) ||
674 mv88e6xxx_6097_family(ps) ||
675 mv88e6xxx_6165_family(ps) ||
676 mv88e6xxx_6351_family(ps) ||
677 mv88e6xxx_6352_family(ps);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200678 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100679 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000680}
681
Andrew Lunn158bc062016-04-28 21:24:06 -0400682static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100683 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200684 int port)
685{
Andrew Lunn80c46272015-06-20 18:42:30 +0200686 u32 low;
687 u32 high = 0;
688 int ret;
689 u64 value;
690
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100691 switch (s->type) {
692 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400693 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200694 if (ret < 0)
695 return UINT64_MAX;
696
697 low = ret;
698 if (s->sizeof_stat == 4) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400699 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100700 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200701 if (ret < 0)
702 return UINT64_MAX;
703 high = ret;
704 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100705 break;
706 case BANK0:
707 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400708 _mv88e6xxx_stats_read(ps, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200709 if (s->sizeof_stat == 8)
Andrew Lunn158bc062016-04-28 21:24:06 -0400710 _mv88e6xxx_stats_read(ps, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200711 }
712 value = (((u64)high) << 16) | low;
713 return value;
714}
715
Vivien Didelotf81ec902016-05-09 13:22:58 -0400716static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
717 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100718{
Andrew Lunn158bc062016-04-28 21:24:06 -0400719 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100720 struct mv88e6xxx_hw_stat *stat;
721 int i, j;
722
723 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
724 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400725 if (mv88e6xxx_has_stat(ps, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100726 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
727 ETH_GSTRING_LEN);
728 j++;
729 }
730 }
731}
732
Vivien Didelotf81ec902016-05-09 13:22:58 -0400733static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100734{
Andrew Lunn158bc062016-04-28 21:24:06 -0400735 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100736 struct mv88e6xxx_hw_stat *stat;
737 int i, j;
738
739 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
740 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400741 if (mv88e6xxx_has_stat(ps, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100742 j++;
743 }
744 return j;
745}
746
Vivien Didelotf81ec902016-05-09 13:22:58 -0400747static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
748 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000749{
Florian Fainellia22adce2014-04-28 11:14:28 -0700750 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100751 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000752 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100753 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000754
Andrew Lunn31888232015-05-06 01:09:54 +0200755 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000756
Andrew Lunn158bc062016-04-28 21:24:06 -0400757 ret = _mv88e6xxx_stats_snapshot(ps, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000758 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200759 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000760 return;
761 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100762 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
763 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400764 if (mv88e6xxx_has_stat(ps, stat)) {
765 data[j] = _mv88e6xxx_get_ethtool_stat(ps, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100766 j++;
767 }
768 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000769
Andrew Lunn31888232015-05-06 01:09:54 +0200770 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000771}
Ben Hutchings98e67302011-11-25 14:36:19 +0000772
Vivien Didelotf81ec902016-05-09 13:22:58 -0400773static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700774{
775 return 32 * sizeof(u16);
776}
777
Vivien Didelotf81ec902016-05-09 13:22:58 -0400778static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
779 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700780{
Andrew Lunn158bc062016-04-28 21:24:06 -0400781 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700782 u16 *p = _p;
783 int i;
784
785 regs->version = 0;
786
787 memset(p, 0xff, 32 * sizeof(u16));
788
Vivien Didelot23062512016-05-09 13:22:45 -0400789 mutex_lock(&ps->smi_mutex);
790
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700791 for (i = 0; i < 32; i++) {
792 int ret;
793
Vivien Didelot23062512016-05-09 13:22:45 -0400794 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700795 if (ret >= 0)
796 p[i] = ret;
797 }
Vivien Didelot23062512016-05-09 13:22:45 -0400798
799 mutex_unlock(&ps->smi_mutex);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700800}
801
Andrew Lunn158bc062016-04-28 21:24:06 -0400802static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset,
Andrew Lunn3898c142015-05-06 01:09:53 +0200803 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700804{
805 unsigned long timeout = jiffies + HZ / 10;
806
807 while (time_before(jiffies, timeout)) {
808 int ret;
809
Andrew Lunn158bc062016-04-28 21:24:06 -0400810 ret = _mv88e6xxx_reg_read(ps, reg, offset);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700811 if (ret < 0)
812 return ret;
813 if (!(ret & mask))
814 return 0;
815
816 usleep_range(1000, 2000);
817 }
818 return -ETIMEDOUT;
819}
820
Andrew Lunn158bc062016-04-28 21:24:06 -0400821static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg,
822 int offset, u16 mask)
Andrew Lunn3898c142015-05-06 01:09:53 +0200823{
Andrew Lunn3898c142015-05-06 01:09:53 +0200824 int ret;
825
826 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -0400827 ret = _mv88e6xxx_wait(ps, reg, offset, mask);
Andrew Lunn3898c142015-05-06 01:09:53 +0200828 mutex_unlock(&ps->smi_mutex);
829
830 return ret;
831}
832
Andrew Lunn03a4a542016-06-04 21:17:05 +0200833static int mv88e6xxx_mdio_wait(struct mv88e6xxx_priv_state *ps)
Andrew Lunn3898c142015-05-06 01:09:53 +0200834{
Andrew Lunn158bc062016-04-28 21:24:06 -0400835 return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200836 GLOBAL2_SMI_OP_BUSY);
837}
838
Vivien Didelotd24645b2016-05-09 13:22:41 -0400839static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200840{
Andrew Lunn158bc062016-04-28 21:24:06 -0400841 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
842
843 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200844 GLOBAL2_EEPROM_OP_LOAD);
845}
846
Vivien Didelotd24645b2016-05-09 13:22:41 -0400847static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200848{
Andrew Lunn158bc062016-04-28 21:24:06 -0400849 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
850
851 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200852 GLOBAL2_EEPROM_OP_BUSY);
853}
854
Vivien Didelotd24645b2016-05-09 13:22:41 -0400855static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
856{
857 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
858 int ret;
859
860 mutex_lock(&ps->eeprom_mutex);
861
862 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
863 GLOBAL2_EEPROM_OP_READ |
864 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
865 if (ret < 0)
866 goto error;
867
868 ret = mv88e6xxx_eeprom_busy_wait(ds);
869 if (ret < 0)
870 goto error;
871
872 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
873error:
874 mutex_unlock(&ps->eeprom_mutex);
875 return ret;
876}
877
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200878static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
879{
880 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
881
882 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
883 return ps->eeprom_len;
884
885 return 0;
886}
887
Vivien Didelotf81ec902016-05-09 13:22:58 -0400888static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
889 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400890{
891 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
892 int offset;
893 int len;
894 int ret;
895
896 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
897 return -EOPNOTSUPP;
898
899 offset = eeprom->offset;
900 len = eeprom->len;
901 eeprom->len = 0;
902
903 eeprom->magic = 0xc3ec4951;
904
905 ret = mv88e6xxx_eeprom_load_wait(ds);
906 if (ret < 0)
907 return ret;
908
909 if (offset & 1) {
910 int word;
911
912 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
913 if (word < 0)
914 return word;
915
916 *data++ = (word >> 8) & 0xff;
917
918 offset++;
919 len--;
920 eeprom->len++;
921 }
922
923 while (len >= 2) {
924 int word;
925
926 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
927 if (word < 0)
928 return word;
929
930 *data++ = word & 0xff;
931 *data++ = (word >> 8) & 0xff;
932
933 offset += 2;
934 len -= 2;
935 eeprom->len += 2;
936 }
937
938 if (len) {
939 int word;
940
941 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
942 if (word < 0)
943 return word;
944
945 *data++ = word & 0xff;
946
947 offset++;
948 len--;
949 eeprom->len++;
950 }
951
952 return 0;
953}
954
955static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
956{
957 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
958 int ret;
959
960 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
961 if (ret < 0)
962 return ret;
963
964 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
965 return -EROFS;
966
967 return 0;
968}
969
970static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
971 u16 data)
972{
973 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
974 int ret;
975
976 mutex_lock(&ps->eeprom_mutex);
977
978 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
979 if (ret < 0)
980 goto error;
981
982 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
983 GLOBAL2_EEPROM_OP_WRITE |
984 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
985 if (ret < 0)
986 goto error;
987
988 ret = mv88e6xxx_eeprom_busy_wait(ds);
989error:
990 mutex_unlock(&ps->eeprom_mutex);
991 return ret;
992}
993
Vivien Didelotf81ec902016-05-09 13:22:58 -0400994static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
995 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400996{
997 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
998 int offset;
999 int ret;
1000 int len;
1001
1002 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
1003 return -EOPNOTSUPP;
1004
1005 if (eeprom->magic != 0xc3ec4951)
1006 return -EINVAL;
1007
1008 ret = mv88e6xxx_eeprom_is_readonly(ds);
1009 if (ret)
1010 return ret;
1011
1012 offset = eeprom->offset;
1013 len = eeprom->len;
1014 eeprom->len = 0;
1015
1016 ret = mv88e6xxx_eeprom_load_wait(ds);
1017 if (ret < 0)
1018 return ret;
1019
1020 if (offset & 1) {
1021 int word;
1022
1023 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1024 if (word < 0)
1025 return word;
1026
1027 word = (*data++ << 8) | (word & 0xff);
1028
1029 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1030 if (ret < 0)
1031 return ret;
1032
1033 offset++;
1034 len--;
1035 eeprom->len++;
1036 }
1037
1038 while (len >= 2) {
1039 int word;
1040
1041 word = *data++;
1042 word |= *data++ << 8;
1043
1044 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1045 if (ret < 0)
1046 return ret;
1047
1048 offset += 2;
1049 len -= 2;
1050 eeprom->len += 2;
1051 }
1052
1053 if (len) {
1054 int word;
1055
1056 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1057 if (word < 0)
1058 return word;
1059
1060 word = (word & 0xff00) | *data++;
1061
1062 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1063 if (ret < 0)
1064 return ret;
1065
1066 offset++;
1067 len--;
1068 eeprom->len++;
1069 }
1070
1071 return 0;
1072}
1073
Andrew Lunn158bc062016-04-28 21:24:06 -04001074static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001075{
Andrew Lunn158bc062016-04-28 21:24:06 -04001076 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001077 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001078}
1079
Andrew Lunn03a4a542016-06-04 21:17:05 +02001080static int mv88e6xxx_mdio_read_indirect(struct mv88e6xxx_priv_state *ps,
Andrew Lunn158bc062016-04-28 21:24:06 -04001081 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001082{
1083 int ret;
1084
Andrew Lunn158bc062016-04-28 21:24:06 -04001085 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001086 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1087 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001088 if (ret < 0)
1089 return ret;
1090
Andrew Lunn03a4a542016-06-04 21:17:05 +02001091 ret = mv88e6xxx_mdio_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +02001092 if (ret < 0)
1093 return ret;
1094
Andrew Lunn158bc062016-04-28 21:24:06 -04001095 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
1096
1097 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001098}
1099
Andrew Lunn03a4a542016-06-04 21:17:05 +02001100static int mv88e6xxx_mdio_write_indirect(struct mv88e6xxx_priv_state *ps,
Andrew Lunn158bc062016-04-28 21:24:06 -04001101 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001102{
Andrew Lunn3898c142015-05-06 01:09:53 +02001103 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001104
Andrew Lunn158bc062016-04-28 21:24:06 -04001105 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001106 if (ret < 0)
1107 return ret;
1108
Andrew Lunn158bc062016-04-28 21:24:06 -04001109 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001110 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1111 regnum);
1112
Andrew Lunn03a4a542016-06-04 21:17:05 +02001113 return mv88e6xxx_mdio_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +01001114}
1115
Vivien Didelotf81ec902016-05-09 13:22:58 -04001116static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1117 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001118{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001119 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001120 int reg;
1121
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001122 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1123 return -EOPNOTSUPP;
1124
Andrew Lunn3898c142015-05-06 01:09:53 +02001125 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001126
Andrew Lunn03a4a542016-06-04 21:17:05 +02001127 reg = mv88e6xxx_mdio_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001128 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001129 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001130
1131 e->eee_enabled = !!(reg & 0x0200);
1132 e->tx_lpi_enabled = !!(reg & 0x0100);
1133
Andrew Lunn158bc062016-04-28 21:24:06 -04001134 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001135 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001136 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001137
Andrew Lunncca8b132015-04-02 04:06:39 +02001138 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001139 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001140
Andrew Lunn2f40c692015-04-02 04:06:37 +02001141out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001142 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001143 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001144}
1145
Vivien Didelotf81ec902016-05-09 13:22:58 -04001146static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1147 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001148{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001149 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1150 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001151 int ret;
1152
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001153 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1154 return -EOPNOTSUPP;
1155
Andrew Lunn3898c142015-05-06 01:09:53 +02001156 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001157
Andrew Lunn03a4a542016-06-04 21:17:05 +02001158 ret = mv88e6xxx_mdio_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001159 if (ret < 0)
1160 goto out;
1161
1162 reg = ret & ~0x0300;
1163 if (e->eee_enabled)
1164 reg |= 0x0200;
1165 if (e->tx_lpi_enabled)
1166 reg |= 0x0100;
1167
Andrew Lunn03a4a542016-06-04 21:17:05 +02001168 ret = mv88e6xxx_mdio_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001169out:
Andrew Lunn3898c142015-05-06 01:09:53 +02001170 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001171
1172 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001173}
1174
Andrew Lunn158bc062016-04-28 21:24:06 -04001175static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001176{
1177 int ret;
1178
Andrew Lunn158bc062016-04-28 21:24:06 -04001179 if (mv88e6xxx_has_fid_reg(ps)) {
1180 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001181 if (ret < 0)
1182 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001183 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001184 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001185 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001186 if (ret < 0)
1187 return ret;
1188
Andrew Lunn158bc062016-04-28 21:24:06 -04001189 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001190 (ret & 0xfff) |
1191 ((fid << 8) & 0xf000));
1192 if (ret < 0)
1193 return ret;
1194
1195 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1196 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001197 }
1198
Andrew Lunn158bc062016-04-28 21:24:06 -04001199 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001200 if (ret < 0)
1201 return ret;
1202
Andrew Lunn158bc062016-04-28 21:24:06 -04001203 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001204}
1205
Andrew Lunn158bc062016-04-28 21:24:06 -04001206static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -04001207 struct mv88e6xxx_atu_entry *entry)
1208{
1209 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1210
1211 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1212 unsigned int mask, shift;
1213
1214 if (entry->trunk) {
1215 data |= GLOBAL_ATU_DATA_TRUNK;
1216 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1217 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1218 } else {
1219 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1220 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1221 }
1222
1223 data |= (entry->portv_trunkid << shift) & mask;
1224 }
1225
Andrew Lunn158bc062016-04-28 21:24:06 -04001226 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001227}
1228
Andrew Lunn158bc062016-04-28 21:24:06 -04001229static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001230 struct mv88e6xxx_atu_entry *entry,
1231 bool static_too)
1232{
1233 int op;
1234 int err;
1235
Andrew Lunn158bc062016-04-28 21:24:06 -04001236 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001237 if (err)
1238 return err;
1239
Andrew Lunn158bc062016-04-28 21:24:06 -04001240 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001241 if (err)
1242 return err;
1243
1244 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001245 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1246 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1247 } else {
1248 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1249 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1250 }
1251
Andrew Lunn158bc062016-04-28 21:24:06 -04001252 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001253}
1254
Andrew Lunn158bc062016-04-28 21:24:06 -04001255static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1256 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001257{
1258 struct mv88e6xxx_atu_entry entry = {
1259 .fid = fid,
1260 .state = 0, /* EntryState bits must be 0 */
1261 };
1262
Andrew Lunn158bc062016-04-28 21:24:06 -04001263 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001264}
1265
Andrew Lunn158bc062016-04-28 21:24:06 -04001266static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1267 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001268{
1269 struct mv88e6xxx_atu_entry entry = {
1270 .trunk = false,
1271 .fid = fid,
1272 };
1273
1274 /* EntryState bits must be 0xF */
1275 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1276
1277 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1278 entry.portv_trunkid = (to_port & 0x0f) << 4;
1279 entry.portv_trunkid |= from_port & 0x0f;
1280
Andrew Lunn158bc062016-04-28 21:24:06 -04001281 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001282}
1283
Andrew Lunn158bc062016-04-28 21:24:06 -04001284static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1285 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001286{
1287 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001288 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001289}
1290
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001291static const char * const mv88e6xxx_port_state_names[] = {
1292 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1293 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1294 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1295 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1296};
1297
Andrew Lunn158bc062016-04-28 21:24:06 -04001298static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1299 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001300{
Andrew Lunn158bc062016-04-28 21:24:06 -04001301 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001302 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001303 u8 oldstate;
1304
Andrew Lunn158bc062016-04-28 21:24:06 -04001305 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001306 if (reg < 0)
1307 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001308
Andrew Lunncca8b132015-04-02 04:06:39 +02001309 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001310
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001311 if (oldstate != state) {
1312 /* Flush forwarding database if we're moving a port
1313 * from Learning or Forwarding state to Disabled or
1314 * Blocking or Listening state.
1315 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001316 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
1317 oldstate == PORT_CONTROL_STATE_FORWARDING)
1318 && (state == PORT_CONTROL_STATE_DISABLED ||
1319 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001320 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001321 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001322 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001323 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001324
Andrew Lunncca8b132015-04-02 04:06:39 +02001325 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001326 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001327 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001328 if (ret)
1329 return ret;
1330
Andrew Lunnc8b09802016-06-04 21:16:57 +02001331 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001332 mv88e6xxx_port_state_names[state],
1333 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001334 }
1335
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001336 return ret;
1337}
1338
Andrew Lunn158bc062016-04-28 21:24:06 -04001339static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1340 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001341{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001342 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001343 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001344 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001345 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001346 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001347 int i;
1348
1349 /* allow CPU port or DSA link(s) to send frames to every port */
1350 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1351 output_ports = mask;
1352 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001353 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001354 /* allow sending frames to every group member */
1355 if (bridge && ps->ports[i].bridge_dev == bridge)
1356 output_ports |= BIT(i);
1357
1358 /* allow sending frames to CPU port and DSA link(s) */
1359 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1360 output_ports |= BIT(i);
1361 }
1362 }
1363
1364 /* prevent frames from going back out of the port they came in on */
1365 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001366
Andrew Lunn158bc062016-04-28 21:24:06 -04001367 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001368 if (reg < 0)
1369 return reg;
1370
1371 reg &= ~mask;
1372 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001373
Andrew Lunn158bc062016-04-28 21:24:06 -04001374 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001375}
1376
Vivien Didelotf81ec902016-05-09 13:22:58 -04001377static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1378 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001379{
1380 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1381 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001382 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001383
Vivien Didelot936f2342016-05-09 13:22:46 -04001384 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE))
1385 return;
1386
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001387 switch (state) {
1388 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001389 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001390 break;
1391 case BR_STATE_BLOCKING:
1392 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001393 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001394 break;
1395 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001396 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001397 break;
1398 case BR_STATE_FORWARDING:
1399 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001400 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001401 break;
1402 }
1403
Vivien Didelot553eb542016-05-13 20:38:23 -04001404 mutex_lock(&ps->smi_mutex);
1405 err = _mv88e6xxx_port_state(ps, port, stp_state);
1406 mutex_unlock(&ps->smi_mutex);
1407
1408 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001409 netdev_err(ds->ports[port].netdev,
1410 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001411 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001412}
1413
Andrew Lunn158bc062016-04-28 21:24:06 -04001414static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1415 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001416{
Andrew Lunn158bc062016-04-28 21:24:06 -04001417 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001418 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001419 int ret;
1420
Andrew Lunn158bc062016-04-28 21:24:06 -04001421 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001422 if (ret < 0)
1423 return ret;
1424
Vivien Didelot5da96032016-03-07 18:24:39 -05001425 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1426
1427 if (new) {
1428 ret &= ~PORT_DEFAULT_VLAN_MASK;
1429 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1430
Andrew Lunn158bc062016-04-28 21:24:06 -04001431 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001432 PORT_DEFAULT_VLAN, ret);
1433 if (ret < 0)
1434 return ret;
1435
Andrew Lunnc8b09802016-06-04 21:16:57 +02001436 netdev_dbg(ds->ports[port].netdev,
1437 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001438 }
1439
1440 if (old)
1441 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001442
1443 return 0;
1444}
1445
Andrew Lunn158bc062016-04-28 21:24:06 -04001446static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1447 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001448{
Andrew Lunn158bc062016-04-28 21:24:06 -04001449 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001450}
1451
Andrew Lunn158bc062016-04-28 21:24:06 -04001452static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1453 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001454{
Andrew Lunn158bc062016-04-28 21:24:06 -04001455 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001456}
1457
Andrew Lunn158bc062016-04-28 21:24:06 -04001458static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001459{
Andrew Lunn158bc062016-04-28 21:24:06 -04001460 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001461 GLOBAL_VTU_OP_BUSY);
1462}
1463
Andrew Lunn158bc062016-04-28 21:24:06 -04001464static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001465{
1466 int ret;
1467
Andrew Lunn158bc062016-04-28 21:24:06 -04001468 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001469 if (ret < 0)
1470 return ret;
1471
Andrew Lunn158bc062016-04-28 21:24:06 -04001472 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001473}
1474
Andrew Lunn158bc062016-04-28 21:24:06 -04001475static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001476{
1477 int ret;
1478
Andrew Lunn158bc062016-04-28 21:24:06 -04001479 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001480 if (ret < 0)
1481 return ret;
1482
Andrew Lunn158bc062016-04-28 21:24:06 -04001483 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001484}
1485
Andrew Lunn158bc062016-04-28 21:24:06 -04001486static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001487 struct mv88e6xxx_vtu_stu_entry *entry,
1488 unsigned int nibble_offset)
1489{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001490 u16 regs[3];
1491 int i;
1492 int ret;
1493
1494 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001495 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001496 GLOBAL_VTU_DATA_0_3 + i);
1497 if (ret < 0)
1498 return ret;
1499
1500 regs[i] = ret;
1501 }
1502
Vivien Didelot009a2b92016-04-17 13:24:01 -04001503 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001504 unsigned int shift = (i % 4) * 4 + nibble_offset;
1505 u16 reg = regs[i / 4];
1506
1507 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1508 }
1509
1510 return 0;
1511}
1512
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001513static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps,
1514 struct mv88e6xxx_vtu_stu_entry *entry)
1515{
1516 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0);
1517}
1518
1519static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps,
1520 struct mv88e6xxx_vtu_stu_entry *entry)
1521{
1522 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2);
1523}
1524
Andrew Lunn158bc062016-04-28 21:24:06 -04001525static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001526 struct mv88e6xxx_vtu_stu_entry *entry,
1527 unsigned int nibble_offset)
1528{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001529 u16 regs[3] = { 0 };
1530 int i;
1531 int ret;
1532
Vivien Didelot009a2b92016-04-17 13:24:01 -04001533 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001534 unsigned int shift = (i % 4) * 4 + nibble_offset;
1535 u8 data = entry->data[i];
1536
1537 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1538 }
1539
1540 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001541 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001542 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1543 if (ret < 0)
1544 return ret;
1545 }
1546
1547 return 0;
1548}
1549
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001550static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps,
1551 struct mv88e6xxx_vtu_stu_entry *entry)
1552{
1553 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
1554}
1555
1556static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps,
1557 struct mv88e6xxx_vtu_stu_entry *entry)
1558{
1559 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
1560}
1561
Andrew Lunn158bc062016-04-28 21:24:06 -04001562static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001563{
Andrew Lunn158bc062016-04-28 21:24:06 -04001564 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001565 vid & GLOBAL_VTU_VID_MASK);
1566}
1567
Andrew Lunn158bc062016-04-28 21:24:06 -04001568static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001569 struct mv88e6xxx_vtu_stu_entry *entry)
1570{
1571 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1572 int ret;
1573
Andrew Lunn158bc062016-04-28 21:24:06 -04001574 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001575 if (ret < 0)
1576 return ret;
1577
Andrew Lunn158bc062016-04-28 21:24:06 -04001578 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001579 if (ret < 0)
1580 return ret;
1581
Andrew Lunn158bc062016-04-28 21:24:06 -04001582 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001583 if (ret < 0)
1584 return ret;
1585
1586 next.vid = ret & GLOBAL_VTU_VID_MASK;
1587 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1588
1589 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001590 ret = mv88e6xxx_vtu_data_read(ps, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001591 if (ret < 0)
1592 return ret;
1593
Andrew Lunn158bc062016-04-28 21:24:06 -04001594 if (mv88e6xxx_has_fid_reg(ps)) {
1595 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001596 GLOBAL_VTU_FID);
1597 if (ret < 0)
1598 return ret;
1599
1600 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001601 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001602 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1603 * VTU DBNum[3:0] are located in VTU Operation 3:0
1604 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001605 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001606 GLOBAL_VTU_OP);
1607 if (ret < 0)
1608 return ret;
1609
1610 next.fid = (ret & 0xf00) >> 4;
1611 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001612 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001613
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001614 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001615 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001616 GLOBAL_VTU_SID);
1617 if (ret < 0)
1618 return ret;
1619
1620 next.sid = ret & GLOBAL_VTU_SID_MASK;
1621 }
1622 }
1623
1624 *entry = next;
1625 return 0;
1626}
1627
Vivien Didelotf81ec902016-05-09 13:22:58 -04001628static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1629 struct switchdev_obj_port_vlan *vlan,
1630 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001631{
1632 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1633 struct mv88e6xxx_vtu_stu_entry next;
1634 u16 pvid;
1635 int err;
1636
Vivien Didelot54d77b52016-05-09 13:22:47 -04001637 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
1638 return -EOPNOTSUPP;
1639
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001640 mutex_lock(&ps->smi_mutex);
1641
Andrew Lunn158bc062016-04-28 21:24:06 -04001642 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001643 if (err)
1644 goto unlock;
1645
Andrew Lunn158bc062016-04-28 21:24:06 -04001646 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001647 if (err)
1648 goto unlock;
1649
1650 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001651 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001652 if (err)
1653 break;
1654
1655 if (!next.valid)
1656 break;
1657
1658 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1659 continue;
1660
1661 /* reinit and dump this VLAN obj */
1662 vlan->vid_begin = vlan->vid_end = next.vid;
1663 vlan->flags = 0;
1664
1665 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1666 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1667
1668 if (next.vid == pvid)
1669 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1670
1671 err = cb(&vlan->obj);
1672 if (err)
1673 break;
1674 } while (next.vid < GLOBAL_VTU_VID_MASK);
1675
1676unlock:
1677 mutex_unlock(&ps->smi_mutex);
1678
1679 return err;
1680}
1681
Andrew Lunn158bc062016-04-28 21:24:06 -04001682static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001683 struct mv88e6xxx_vtu_stu_entry *entry)
1684{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001685 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001686 u16 reg = 0;
1687 int ret;
1688
Andrew Lunn158bc062016-04-28 21:24:06 -04001689 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001690 if (ret < 0)
1691 return ret;
1692
1693 if (!entry->valid)
1694 goto loadpurge;
1695
1696 /* Write port member tags */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001697 ret = mv88e6xxx_vtu_data_write(ps, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001698 if (ret < 0)
1699 return ret;
1700
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001701 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001702 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001703 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001704 if (ret < 0)
1705 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001706 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001707
Andrew Lunn158bc062016-04-28 21:24:06 -04001708 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001709 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001710 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001711 if (ret < 0)
1712 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001713 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001714 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1715 * VTU DBNum[3:0] are located in VTU Operation 3:0
1716 */
1717 op |= (entry->fid & 0xf0) << 8;
1718 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001719 }
1720
1721 reg = GLOBAL_VTU_VID_VALID;
1722loadpurge:
1723 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001724 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001725 if (ret < 0)
1726 return ret;
1727
Andrew Lunn158bc062016-04-28 21:24:06 -04001728 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001729}
1730
Andrew Lunn158bc062016-04-28 21:24:06 -04001731static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001732 struct mv88e6xxx_vtu_stu_entry *entry)
1733{
1734 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1735 int ret;
1736
Andrew Lunn158bc062016-04-28 21:24:06 -04001737 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001738 if (ret < 0)
1739 return ret;
1740
Andrew Lunn158bc062016-04-28 21:24:06 -04001741 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001742 sid & GLOBAL_VTU_SID_MASK);
1743 if (ret < 0)
1744 return ret;
1745
Andrew Lunn158bc062016-04-28 21:24:06 -04001746 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001747 if (ret < 0)
1748 return ret;
1749
Andrew Lunn158bc062016-04-28 21:24:06 -04001750 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001751 if (ret < 0)
1752 return ret;
1753
1754 next.sid = ret & GLOBAL_VTU_SID_MASK;
1755
Andrew Lunn158bc062016-04-28 21:24:06 -04001756 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001757 if (ret < 0)
1758 return ret;
1759
1760 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1761
1762 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001763 ret = mv88e6xxx_stu_data_read(ps, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001764 if (ret < 0)
1765 return ret;
1766 }
1767
1768 *entry = next;
1769 return 0;
1770}
1771
Andrew Lunn158bc062016-04-28 21:24:06 -04001772static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001773 struct mv88e6xxx_vtu_stu_entry *entry)
1774{
1775 u16 reg = 0;
1776 int ret;
1777
Andrew Lunn158bc062016-04-28 21:24:06 -04001778 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001779 if (ret < 0)
1780 return ret;
1781
1782 if (!entry->valid)
1783 goto loadpurge;
1784
1785 /* Write port states */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001786 ret = mv88e6xxx_stu_data_write(ps, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001787 if (ret < 0)
1788 return ret;
1789
1790 reg = GLOBAL_VTU_VID_VALID;
1791loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001792 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001793 if (ret < 0)
1794 return ret;
1795
1796 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001797 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001798 if (ret < 0)
1799 return ret;
1800
Andrew Lunn158bc062016-04-28 21:24:06 -04001801 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001802}
1803
Andrew Lunn158bc062016-04-28 21:24:06 -04001804static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1805 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001806{
Andrew Lunn158bc062016-04-28 21:24:06 -04001807 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001808 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001809 u16 fid;
1810 int ret;
1811
Andrew Lunn158bc062016-04-28 21:24:06 -04001812 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001813 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001814 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001815 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001816 else
1817 return -EOPNOTSUPP;
1818
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001819 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001820 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001821 if (ret < 0)
1822 return ret;
1823
1824 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1825
1826 if (new) {
1827 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1828 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1829
Andrew Lunn158bc062016-04-28 21:24:06 -04001830 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001831 ret);
1832 if (ret < 0)
1833 return ret;
1834 }
1835
1836 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001837 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001838 if (ret < 0)
1839 return ret;
1840
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001841 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001842
1843 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001844 ret &= ~upper_mask;
1845 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001846
Andrew Lunn158bc062016-04-28 21:24:06 -04001847 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001848 ret);
1849 if (ret < 0)
1850 return ret;
1851
Andrew Lunnc8b09802016-06-04 21:16:57 +02001852 netdev_dbg(ds->ports[port].netdev,
1853 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001854 }
1855
1856 if (old)
1857 *old = fid;
1858
1859 return 0;
1860}
1861
Andrew Lunn158bc062016-04-28 21:24:06 -04001862static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1863 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001864{
Andrew Lunn158bc062016-04-28 21:24:06 -04001865 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001866}
1867
Andrew Lunn158bc062016-04-28 21:24:06 -04001868static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1869 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001870{
Andrew Lunn158bc062016-04-28 21:24:06 -04001871 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001872}
1873
Andrew Lunn158bc062016-04-28 21:24:06 -04001874static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001875{
1876 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1877 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001878 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001879
1880 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1881
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001882 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001883 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001884 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001885 if (err)
1886 return err;
1887
1888 set_bit(*fid, fid_bitmap);
1889 }
1890
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001891 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001892 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001893 if (err)
1894 return err;
1895
1896 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001897 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001898 if (err)
1899 return err;
1900
1901 if (!vlan.valid)
1902 break;
1903
1904 set_bit(vlan.fid, fid_bitmap);
1905 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1906
1907 /* The reset value 0x000 is used to indicate that multiple address
1908 * databases are not needed. Return the next positive available.
1909 */
1910 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001911 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001912 return -ENOSPC;
1913
1914 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001915 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001916}
1917
Andrew Lunn158bc062016-04-28 21:24:06 -04001918static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001919 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001920{
Andrew Lunn158bc062016-04-28 21:24:06 -04001921 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001922 struct mv88e6xxx_vtu_stu_entry vlan = {
1923 .valid = true,
1924 .vid = vid,
1925 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001926 int i, err;
1927
Andrew Lunn158bc062016-04-28 21:24:06 -04001928 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001929 if (err)
1930 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001931
Vivien Didelot3d131f02015-11-03 10:52:52 -05001932 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001933 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001934 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1935 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1936 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001937
Andrew Lunn158bc062016-04-28 21:24:06 -04001938 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1939 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001940 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001941
1942 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1943 * implemented, only one STU entry is needed to cover all VTU
1944 * entries. Thus, validate the SID 0.
1945 */
1946 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001947 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001948 if (err)
1949 return err;
1950
1951 if (vstp.sid != vlan.sid || !vstp.valid) {
1952 memset(&vstp, 0, sizeof(vstp));
1953 vstp.valid = true;
1954 vstp.sid = vlan.sid;
1955
Andrew Lunn158bc062016-04-28 21:24:06 -04001956 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001957 if (err)
1958 return err;
1959 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001960 }
1961
1962 *entry = vlan;
1963 return 0;
1964}
1965
Andrew Lunn158bc062016-04-28 21:24:06 -04001966static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001967 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1968{
1969 int err;
1970
1971 if (!vid)
1972 return -EINVAL;
1973
Andrew Lunn158bc062016-04-28 21:24:06 -04001974 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001975 if (err)
1976 return err;
1977
Andrew Lunn158bc062016-04-28 21:24:06 -04001978 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001979 if (err)
1980 return err;
1981
1982 if (entry->vid != vid || !entry->valid) {
1983 if (!creat)
1984 return -EOPNOTSUPP;
1985 /* -ENOENT would've been more appropriate, but switchdev expects
1986 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1987 */
1988
Andrew Lunn158bc062016-04-28 21:24:06 -04001989 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001990 }
1991
1992 return err;
1993}
1994
Vivien Didelotda9c3592016-02-12 12:09:40 -05001995static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1996 u16 vid_begin, u16 vid_end)
1997{
1998 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1999 struct mv88e6xxx_vtu_stu_entry vlan;
2000 int i, err;
2001
2002 if (!vid_begin)
2003 return -EOPNOTSUPP;
2004
2005 mutex_lock(&ps->smi_mutex);
2006
Andrew Lunn158bc062016-04-28 21:24:06 -04002007 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002008 if (err)
2009 goto unlock;
2010
2011 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002012 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002013 if (err)
2014 goto unlock;
2015
2016 if (!vlan.valid)
2017 break;
2018
2019 if (vlan.vid > vid_end)
2020 break;
2021
Vivien Didelot009a2b92016-04-17 13:24:01 -04002022 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05002023 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
2024 continue;
2025
2026 if (vlan.data[i] ==
2027 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2028 continue;
2029
2030 if (ps->ports[i].bridge_dev ==
2031 ps->ports[port].bridge_dev)
2032 break; /* same bridge, check next VLAN */
2033
Andrew Lunnc8b09802016-06-04 21:16:57 +02002034 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05002035 "hardware VLAN %d already used by %s\n",
2036 vlan.vid,
2037 netdev_name(ps->ports[i].bridge_dev));
2038 err = -EOPNOTSUPP;
2039 goto unlock;
2040 }
2041 } while (vlan.vid < vid_end);
2042
2043unlock:
2044 mutex_unlock(&ps->smi_mutex);
2045
2046 return err;
2047}
2048
Vivien Didelot214cdb92016-02-26 13:16:08 -05002049static const char * const mv88e6xxx_port_8021q_mode_names[] = {
2050 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
2051 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
2052 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
2053 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
2054};
2055
Vivien Didelotf81ec902016-05-09 13:22:58 -04002056static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
2057 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002058{
2059 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2060 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
2061 PORT_CONTROL_2_8021Q_DISABLED;
2062 int ret;
2063
Vivien Didelot54d77b52016-05-09 13:22:47 -04002064 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2065 return -EOPNOTSUPP;
2066
Vivien Didelot214cdb92016-02-26 13:16:08 -05002067 mutex_lock(&ps->smi_mutex);
2068
Andrew Lunn158bc062016-04-28 21:24:06 -04002069 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002070 if (ret < 0)
2071 goto unlock;
2072
2073 old = ret & PORT_CONTROL_2_8021Q_MASK;
2074
Vivien Didelot5220ef12016-03-07 18:24:52 -05002075 if (new != old) {
2076 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2077 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002078
Andrew Lunn158bc062016-04-28 21:24:06 -04002079 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002080 ret);
2081 if (ret < 0)
2082 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002083
Andrew Lunnc8b09802016-06-04 21:16:57 +02002084 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05002085 mv88e6xxx_port_8021q_mode_names[new],
2086 mv88e6xxx_port_8021q_mode_names[old]);
2087 }
2088
2089 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002090unlock:
2091 mutex_unlock(&ps->smi_mutex);
2092
2093 return ret;
2094}
2095
Vivien Didelotf81ec902016-05-09 13:22:58 -04002096static int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2097 const struct switchdev_obj_port_vlan *vlan,
2098 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002099{
Vivien Didelot54d77b52016-05-09 13:22:47 -04002100 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002101 int err;
2102
Vivien Didelot54d77b52016-05-09 13:22:47 -04002103 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2104 return -EOPNOTSUPP;
2105
Vivien Didelotda9c3592016-02-12 12:09:40 -05002106 /* If the requested port doesn't belong to the same bridge as the VLAN
2107 * members, do not support it (yet) and fallback to software VLAN.
2108 */
2109 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2110 vlan->vid_end);
2111 if (err)
2112 return err;
2113
Vivien Didelot76e398a2015-11-01 12:33:55 -05002114 /* We don't need any dynamic resource from the kernel (yet),
2115 * so skip the prepare phase.
2116 */
2117 return 0;
2118}
2119
Andrew Lunn158bc062016-04-28 21:24:06 -04002120static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
2121 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002122{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002123 struct mv88e6xxx_vtu_stu_entry vlan;
2124 int err;
2125
Andrew Lunn158bc062016-04-28 21:24:06 -04002126 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002127 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002128 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002129
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002130 vlan.data[port] = untagged ?
2131 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2132 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2133
Andrew Lunn158bc062016-04-28 21:24:06 -04002134 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002135}
2136
Vivien Didelotf81ec902016-05-09 13:22:58 -04002137static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2138 const struct switchdev_obj_port_vlan *vlan,
2139 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002140{
2141 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2142 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2143 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2144 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002145
Vivien Didelot54d77b52016-05-09 13:22:47 -04002146 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2147 return;
2148
Vivien Didelot76e398a2015-11-01 12:33:55 -05002149 mutex_lock(&ps->smi_mutex);
2150
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002151 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04002152 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002153 netdev_err(ds->ports[port].netdev,
2154 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002155 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002156
Andrew Lunn158bc062016-04-28 21:24:06 -04002157 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002158 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002159 vlan->vid_end);
2160
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002161 mutex_unlock(&ps->smi_mutex);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002162}
2163
Andrew Lunn158bc062016-04-28 21:24:06 -04002164static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
2165 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002166{
Andrew Lunn158bc062016-04-28 21:24:06 -04002167 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002168 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002169 int i, err;
2170
Andrew Lunn158bc062016-04-28 21:24:06 -04002171 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002172 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002173 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002174
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002175 /* Tell switchdev if this VLAN is handled in software */
2176 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002177 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002178
2179 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2180
2181 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002182 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04002183 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002184 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002185 continue;
2186
2187 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002188 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002189 break;
2190 }
2191 }
2192
Andrew Lunn158bc062016-04-28 21:24:06 -04002193 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002194 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002195 return err;
2196
Andrew Lunn158bc062016-04-28 21:24:06 -04002197 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002198}
2199
Vivien Didelotf81ec902016-05-09 13:22:58 -04002200static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2201 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002202{
2203 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2204 u16 pvid, vid;
2205 int err = 0;
2206
Vivien Didelot54d77b52016-05-09 13:22:47 -04002207 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2208 return -EOPNOTSUPP;
2209
Vivien Didelot76e398a2015-11-01 12:33:55 -05002210 mutex_lock(&ps->smi_mutex);
2211
Andrew Lunn158bc062016-04-28 21:24:06 -04002212 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002213 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002214 goto unlock;
2215
Vivien Didelot76e398a2015-11-01 12:33:55 -05002216 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002217 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002218 if (err)
2219 goto unlock;
2220
2221 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002222 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002223 if (err)
2224 goto unlock;
2225 }
2226 }
2227
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002228unlock:
2229 mutex_unlock(&ps->smi_mutex);
2230
2231 return err;
2232}
2233
Andrew Lunn158bc062016-04-28 21:24:06 -04002234static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002235 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002236{
2237 int i, ret;
2238
2239 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002240 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002241 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002242 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002243 if (ret < 0)
2244 return ret;
2245 }
2246
2247 return 0;
2248}
2249
Andrew Lunn158bc062016-04-28 21:24:06 -04002250static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
2251 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002252{
2253 int i, ret;
2254
2255 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002256 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002257 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002258 if (ret < 0)
2259 return ret;
2260 addr[i * 2] = ret >> 8;
2261 addr[i * 2 + 1] = ret & 0xff;
2262 }
2263
2264 return 0;
2265}
2266
Andrew Lunn158bc062016-04-28 21:24:06 -04002267static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002268 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002269{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002270 int ret;
2271
Andrew Lunn158bc062016-04-28 21:24:06 -04002272 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002273 if (ret < 0)
2274 return ret;
2275
Andrew Lunn158bc062016-04-28 21:24:06 -04002276 ret = _mv88e6xxx_atu_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002277 if (ret < 0)
2278 return ret;
2279
Andrew Lunn158bc062016-04-28 21:24:06 -04002280 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002281 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002282 return ret;
2283
Andrew Lunn158bc062016-04-28 21:24:06 -04002284 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002285}
David S. Millercdf09692015-08-11 12:00:37 -07002286
Andrew Lunn158bc062016-04-28 21:24:06 -04002287static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002288 const unsigned char *addr, u16 vid,
2289 u8 state)
2290{
2291 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002292 struct mv88e6xxx_vtu_stu_entry vlan;
2293 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002294
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002295 /* Null VLAN ID corresponds to the port private database */
2296 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002297 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002298 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002299 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002300 if (err)
2301 return err;
2302
2303 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002304 entry.state = state;
2305 ether_addr_copy(entry.mac, addr);
2306 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2307 entry.trunk = false;
2308 entry.portv_trunkid = BIT(port);
2309 }
2310
Andrew Lunn158bc062016-04-28 21:24:06 -04002311 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002312}
2313
Vivien Didelotf81ec902016-05-09 13:22:58 -04002314static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2315 const struct switchdev_obj_port_fdb *fdb,
2316 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002317{
Vivien Didelot2672f822016-05-09 13:22:48 -04002318 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2319
2320 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2321 return -EOPNOTSUPP;
2322
Vivien Didelot146a3202015-10-08 11:35:12 -04002323 /* We don't need any dynamic resource from the kernel (yet),
2324 * so skip the prepare phase.
2325 */
2326 return 0;
2327}
2328
Vivien Didelotf81ec902016-05-09 13:22:58 -04002329static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2330 const struct switchdev_obj_port_fdb *fdb,
2331 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002332{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002333 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002334 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2335 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2336 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002337
Vivien Didelot2672f822016-05-09 13:22:48 -04002338 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2339 return;
2340
David S. Millercdf09692015-08-11 12:00:37 -07002341 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002342 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002343 netdev_err(ds->ports[port].netdev,
2344 "failed to load MAC address\n");
David S. Millercdf09692015-08-11 12:00:37 -07002345 mutex_unlock(&ps->smi_mutex);
David S. Millercdf09692015-08-11 12:00:37 -07002346}
2347
Vivien Didelotf81ec902016-05-09 13:22:58 -04002348static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2349 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002350{
2351 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2352 int ret;
2353
Vivien Didelot2672f822016-05-09 13:22:48 -04002354 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2355 return -EOPNOTSUPP;
2356
David S. Millercdf09692015-08-11 12:00:37 -07002357 mutex_lock(&ps->smi_mutex);
Andrew Lunn158bc062016-04-28 21:24:06 -04002358 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002359 GLOBAL_ATU_DATA_STATE_UNUSED);
2360 mutex_unlock(&ps->smi_mutex);
2361
2362 return ret;
2363}
2364
Andrew Lunn158bc062016-04-28 21:24:06 -04002365static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002366 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002367{
Vivien Didelot1d194042015-08-10 09:09:51 -04002368 struct mv88e6xxx_atu_entry next = { 0 };
2369 int ret;
2370
2371 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002372
Andrew Lunn158bc062016-04-28 21:24:06 -04002373 ret = _mv88e6xxx_atu_wait(ps);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002374 if (ret < 0)
2375 return ret;
2376
Andrew Lunn158bc062016-04-28 21:24:06 -04002377 ret = _mv88e6xxx_atu_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002378 if (ret < 0)
2379 return ret;
2380
Andrew Lunn158bc062016-04-28 21:24:06 -04002381 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002382 if (ret < 0)
2383 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002384
Andrew Lunn158bc062016-04-28 21:24:06 -04002385 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002386 if (ret < 0)
2387 return ret;
2388
2389 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2390 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2391 unsigned int mask, shift;
2392
2393 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2394 next.trunk = true;
2395 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2396 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2397 } else {
2398 next.trunk = false;
2399 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2400 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2401 }
2402
2403 next.portv_trunkid = (ret & mask) >> shift;
2404 }
2405
2406 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002407 return 0;
2408}
2409
Andrew Lunn158bc062016-04-28 21:24:06 -04002410static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2411 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002412 struct switchdev_obj_port_fdb *fdb,
2413 int (*cb)(struct switchdev_obj *obj))
2414{
2415 struct mv88e6xxx_atu_entry addr = {
2416 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2417 };
2418 int err;
2419
Andrew Lunn158bc062016-04-28 21:24:06 -04002420 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002421 if (err)
2422 return err;
2423
2424 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002425 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002426 if (err)
2427 break;
2428
2429 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2430 break;
2431
2432 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2433 bool is_static = addr.state ==
2434 (is_multicast_ether_addr(addr.mac) ?
2435 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2436 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2437
2438 fdb->vid = vid;
2439 ether_addr_copy(fdb->addr, addr.mac);
2440 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2441
2442 err = cb(&fdb->obj);
2443 if (err)
2444 break;
2445 }
2446 } while (!is_broadcast_ether_addr(addr.mac));
2447
2448 return err;
2449}
2450
Vivien Didelotf81ec902016-05-09 13:22:58 -04002451static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2452 struct switchdev_obj_port_fdb *fdb,
2453 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002454{
2455 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2456 struct mv88e6xxx_vtu_stu_entry vlan = {
2457 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2458 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002459 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002460 int err;
2461
Vivien Didelot2672f822016-05-09 13:22:48 -04002462 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2463 return -EOPNOTSUPP;
2464
Vivien Didelotf33475b2015-10-22 09:34:41 -04002465 mutex_lock(&ps->smi_mutex);
2466
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002467 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002468 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002469 if (err)
2470 goto unlock;
2471
Andrew Lunn158bc062016-04-28 21:24:06 -04002472 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002473 if (err)
2474 goto unlock;
2475
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002476 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002477 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002478 if (err)
2479 goto unlock;
2480
2481 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002482 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002483 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002484 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002485
2486 if (!vlan.valid)
2487 break;
2488
Andrew Lunn158bc062016-04-28 21:24:06 -04002489 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002490 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002491 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002492 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002493 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2494
2495unlock:
2496 mutex_unlock(&ps->smi_mutex);
2497
2498 return err;
2499}
2500
Vivien Didelotf81ec902016-05-09 13:22:58 -04002501static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2502 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002503{
Vivien Didelota6692752016-02-12 12:09:39 -05002504 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002505 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002506
Vivien Didelot936f2342016-05-09 13:22:46 -04002507 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2508 return -EOPNOTSUPP;
2509
Vivien Didelot466dfa02016-02-26 13:16:05 -05002510 mutex_lock(&ps->smi_mutex);
2511
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002512 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002513 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002514
Vivien Didelot009a2b92016-04-17 13:24:01 -04002515 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002516 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002517 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002518 if (err)
2519 break;
2520 }
2521 }
2522
Vivien Didelot466dfa02016-02-26 13:16:05 -05002523 mutex_unlock(&ps->smi_mutex);
Vivien Didelota6692752016-02-12 12:09:39 -05002524
Vivien Didelot466dfa02016-02-26 13:16:05 -05002525 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002526}
2527
Vivien Didelotf81ec902016-05-09 13:22:58 -04002528static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002529{
Vivien Didelota6692752016-02-12 12:09:39 -05002530 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002531 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002532 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002533
Vivien Didelot936f2342016-05-09 13:22:46 -04002534 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2535 return;
2536
Vivien Didelot466dfa02016-02-26 13:16:05 -05002537 mutex_lock(&ps->smi_mutex);
2538
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002539 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002540 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002541
Vivien Didelot009a2b92016-04-17 13:24:01 -04002542 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002543 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002544 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002545 netdev_warn(ds->ports[i].netdev,
2546 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002547
Vivien Didelot466dfa02016-02-26 13:16:05 -05002548 mutex_unlock(&ps->smi_mutex);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002549}
2550
Andrew Lunn03a4a542016-06-04 21:17:05 +02002551static int _mv88e6xxx_mdio_page_write(struct mv88e6xxx_priv_state *ps,
2552 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002553{
2554 int ret;
2555
Andrew Lunn03a4a542016-06-04 21:17:05 +02002556 ret = mv88e6xxx_mdio_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002557 if (ret < 0)
2558 goto restore_page_0;
2559
Andrew Lunn03a4a542016-06-04 21:17:05 +02002560 ret = mv88e6xxx_mdio_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002561restore_page_0:
Andrew Lunn03a4a542016-06-04 21:17:05 +02002562 mv88e6xxx_mdio_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002563
2564 return ret;
2565}
2566
Andrew Lunn03a4a542016-06-04 21:17:05 +02002567static int _mv88e6xxx_mdio_page_read(struct mv88e6xxx_priv_state *ps,
2568 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002569{
2570 int ret;
2571
Andrew Lunn03a4a542016-06-04 21:17:05 +02002572 ret = mv88e6xxx_mdio_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002573 if (ret < 0)
2574 goto restore_page_0;
2575
Andrew Lunn03a4a542016-06-04 21:17:05 +02002576 ret = mv88e6xxx_mdio_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002577restore_page_0:
Andrew Lunn03a4a542016-06-04 21:17:05 +02002578 mv88e6xxx_mdio_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002579
2580 return ret;
2581}
2582
Vivien Didelot552238b2016-05-09 13:22:49 -04002583static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps)
2584{
2585 bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE);
2586 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunn52638f72016-05-10 23:27:22 +02002587 struct gpio_desc *gpiod = ps->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002588 unsigned long timeout;
2589 int ret;
2590 int i;
2591
2592 /* Set all ports to the disabled state. */
2593 for (i = 0; i < ps->info->num_ports; i++) {
2594 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
2595 if (ret < 0)
2596 return ret;
2597
2598 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
2599 ret & 0xfffc);
2600 if (ret)
2601 return ret;
2602 }
2603
2604 /* Wait for transmit queues to drain. */
2605 usleep_range(2000, 4000);
2606
2607 /* If there is a gpio connected to the reset pin, toggle it */
2608 if (gpiod) {
2609 gpiod_set_value_cansleep(gpiod, 1);
2610 usleep_range(10000, 20000);
2611 gpiod_set_value_cansleep(gpiod, 0);
2612 usleep_range(10000, 20000);
2613 }
2614
2615 /* Reset the switch. Keep the PPU active if requested. The PPU
2616 * needs to be active to support indirect phy register access
2617 * through global registers 0x18 and 0x19.
2618 */
2619 if (ppu_active)
2620 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
2621 else
2622 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
2623 if (ret)
2624 return ret;
2625
2626 /* Wait up to one second for reset to complete. */
2627 timeout = jiffies + 1 * HZ;
2628 while (time_before(jiffies, timeout)) {
2629 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
2630 if (ret < 0)
2631 return ret;
2632
2633 if ((ret & is_reset) == is_reset)
2634 break;
2635 usleep_range(1000, 2000);
2636 }
2637 if (time_after(jiffies, timeout))
2638 ret = -ETIMEDOUT;
2639 else
2640 ret = 0;
2641
2642 return ret;
2643}
2644
Andrew Lunn158bc062016-04-28 21:24:06 -04002645static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002646{
2647 int ret;
2648
Andrew Lunn03a4a542016-06-04 21:17:05 +02002649 ret = _mv88e6xxx_mdio_page_read(ps, REG_FIBER_SERDES,
2650 PAGE_FIBER_SERDES, MII_BMCR);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002651 if (ret < 0)
2652 return ret;
2653
2654 if (ret & BMCR_PDOWN) {
2655 ret &= ~BMCR_PDOWN;
Andrew Lunn03a4a542016-06-04 21:17:05 +02002656 ret = _mv88e6xxx_mdio_page_write(ps, REG_FIBER_SERDES,
2657 PAGE_FIBER_SERDES, MII_BMCR,
2658 ret);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002659 }
2660
2661 return ret;
2662}
2663
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002664static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002665{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002666 struct dsa_switch *ds = ps->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002667 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002668 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002669
Andrew Lunn158bc062016-04-28 21:24:06 -04002670 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2671 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2672 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2673 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002674 /* MAC Forcing register: don't force link, speed,
2675 * duplex or flow control state to any particular
2676 * values on physical ports, but force the CPU port
2677 * and all DSA ports to their maximum bandwidth and
2678 * full duplex.
2679 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002680 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002681 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002682 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002683 reg |= PORT_PCS_CTRL_FORCE_LINK |
2684 PORT_PCS_CTRL_LINK_UP |
2685 PORT_PCS_CTRL_DUPLEX_FULL |
2686 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002687 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002688 reg |= PORT_PCS_CTRL_100;
2689 else
2690 reg |= PORT_PCS_CTRL_1000;
2691 } else {
2692 reg |= PORT_PCS_CTRL_UNFORCED;
2693 }
2694
Andrew Lunn158bc062016-04-28 21:24:06 -04002695 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002696 PORT_PCS_CTRL, reg);
2697 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002698 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002699 }
2700
2701 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2702 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2703 * tunneling, determine priority by looking at 802.1p and IP
2704 * priority fields (IP prio has precedence), and set STP state
2705 * to Forwarding.
2706 *
2707 * If this is the CPU link, use DSA or EDSA tagging depending
2708 * on which tagging mode was configured.
2709 *
2710 * If this is a link to another switch, use DSA tagging mode.
2711 *
2712 * If this is the upstream port for this switch, enable
2713 * forwarding of unknown unicasts and multicasts.
2714 */
2715 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002716 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2717 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2718 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2719 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002720 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2721 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2722 PORT_CONTROL_STATE_FORWARDING;
2723 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002724 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002725 reg |= PORT_CONTROL_DSA_TAG;
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_6320_family(ps)) {
Andrew Lunn5377b802016-06-04 21:17:02 +02002729 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2730 PORT_CONTROL_FORWARD_UNKNOWN |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002731 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002732 }
2733
Andrew Lunn158bc062016-04-28 21:24:06 -04002734 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2735 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2736 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2737 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002738 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
2739 }
2740 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002741 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002742 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002743 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002744 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2745 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2746 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002747 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002748 }
2749
Andrew Lunn54d792f2015-05-06 01:09:47 +02002750 if (port == dsa_upstream_port(ds))
2751 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2752 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2753 }
2754 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002755 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002756 PORT_CONTROL, reg);
2757 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002758 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002759 }
2760
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002761 /* If this port is connected to a SerDes, make sure the SerDes is not
2762 * powered down.
2763 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002764 if (mv88e6xxx_6352_family(ps)) {
2765 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
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 ret &= PORT_STATUS_CMODE_MASK;
2769 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2770 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2771 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002772 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002773 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002774 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002775 }
2776 }
2777
Vivien Didelot8efdda42015-08-13 12:52:23 -04002778 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002779 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002780 * untagged frames on this port, do a destination address lookup on all
2781 * received packets as usual, disable ARP mirroring and don't send a
2782 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002783 */
2784 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002785 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2786 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2787 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2788 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002789 reg = PORT_CONTROL_2_MAP_DA;
2790
Andrew Lunn158bc062016-04-28 21:24:06 -04002791 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2792 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002793 reg |= PORT_CONTROL_2_JUMBO_10240;
2794
Andrew Lunn158bc062016-04-28 21:24:06 -04002795 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002796 /* Set the upstream port this port should use */
2797 reg |= dsa_upstream_port(ds);
2798 /* enable forwarding of unknown multicast addresses to
2799 * the upstream port
2800 */
2801 if (port == dsa_upstream_port(ds))
2802 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2803 }
2804
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002805 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002806
Andrew Lunn54d792f2015-05-06 01:09:47 +02002807 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002808 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002809 PORT_CONTROL_2, reg);
2810 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002811 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002812 }
2813
2814 /* Port Association Vector: when learning source addresses
2815 * of packets, add the address to the address database using
2816 * a port bitmap that has only the bit for this port set and
2817 * the other bits clear.
2818 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002819 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002820 /* Disable learning for CPU port */
2821 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002822 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002823
Andrew Lunn158bc062016-04-28 21:24:06 -04002824 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002825 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002826 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002827
2828 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002829 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002830 0x0000);
2831 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002832 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002833
Andrew Lunn158bc062016-04-28 21:24:06 -04002834 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2835 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2836 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002837 /* Do not limit the period of time that this port can
2838 * be paused for by the remote end or the period of
2839 * time that this port can pause the remote end.
2840 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002841 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002842 PORT_PAUSE_CTRL, 0x0000);
2843 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002844 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002845
2846 /* Port ATU control: disable limiting the number of
2847 * address database entries that this port is allowed
2848 * to use.
2849 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002850 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002851 PORT_ATU_CONTROL, 0x0000);
2852 /* Priority Override: disable DA, SA and VTU priority
2853 * override.
2854 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002855 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002856 PORT_PRI_OVERRIDE, 0x0000);
2857 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002858 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002859
2860 /* Port Ethertype: use the Ethertype DSA Ethertype
2861 * value.
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_ETH_TYPE, ETH_P_EDSA);
2865 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002866 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002867 /* Tag Remap: use an identity 802.1p prio -> switch
2868 * prio mapping.
2869 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002870 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002871 PORT_TAG_REGMAP_0123, 0x3210);
2872 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002873 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002874
2875 /* Tag Remap 2: use an identity 802.1p prio -> switch
2876 * prio mapping.
2877 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002878 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002879 PORT_TAG_REGMAP_4567, 0x7654);
2880 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002881 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002882 }
2883
Andrew Lunn158bc062016-04-28 21:24:06 -04002884 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2885 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2886 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2887 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002888 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002889 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002890 PORT_RATE_CONTROL, 0x0001);
2891 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002892 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002893 }
2894
Guenter Roeck366f0a02015-03-26 18:36:30 -07002895 /* Port Control 1: disable trunking, disable sending
2896 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002897 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002898 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002899 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002900 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002901
Vivien Didelot207afda2016-04-14 14:42:09 -04002902 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002903 * database, and allow bidirectional communication between the
2904 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002905 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002906 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002907 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002908 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002909
Andrew Lunn158bc062016-04-28 21:24:06 -04002910 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002911 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002912 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002913
2914 /* Default VLAN ID and priority: don't set a default VLAN
2915 * ID, and set the default packet priority to zero.
2916 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002917 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002918 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002919 if (ret)
2920 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002921
Andrew Lunndbde9e62015-05-06 01:09:48 +02002922 return 0;
2923}
2924
Vivien Didelot08a01262016-05-09 13:22:50 -04002925static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
2926{
Vivien Didelotb0745e872016-05-09 13:22:53 -04002927 struct dsa_switch *ds = ps->ds;
2928 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002929 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002930 int err;
2931 int i;
2932
Vivien Didelot119477b2016-05-09 13:22:51 -04002933 /* Enable the PHY Polling Unit if present, don't discard any packets,
2934 * and mask all interrupt sources.
2935 */
2936 reg = 0;
2937 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) ||
2938 mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE))
2939 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2940
2941 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg);
2942 if (err)
2943 return err;
2944
Vivien Didelotb0745e872016-05-09 13:22:53 -04002945 /* Configure the upstream port, and configure it as the port to which
2946 * ingress and egress and ARP monitor frames are to be sent.
2947 */
2948 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2949 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2950 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2951 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
2952 if (err)
2953 return err;
2954
Vivien Didelot50484ff2016-05-09 13:22:54 -04002955 /* Disable remote management, and set the switch's DSA device number. */
2956 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
2957 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2958 (ds->index & 0x1f));
2959 if (err)
2960 return err;
2961
Vivien Didelot08a01262016-05-09 13:22:50 -04002962 /* Set the default address aging time to 5 minutes, and
2963 * enable address learn messages to be sent to all message
2964 * ports.
2965 */
2966 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2967 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2968 if (err)
2969 return err;
2970
2971 /* Configure the IP ToS mapping registers. */
2972 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2973 if (err)
2974 return err;
2975 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2976 if (err)
2977 return err;
2978 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2979 if (err)
2980 return err;
2981 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2982 if (err)
2983 return err;
2984 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2985 if (err)
2986 return err;
2987 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2988 if (err)
2989 return err;
2990 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2991 if (err)
2992 return err;
2993 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2994 if (err)
2995 return err;
2996
2997 /* Configure the IEEE 802.1p priority mapping register. */
2998 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
2999 if (err)
3000 return err;
3001
3002 /* Send all frames with destination addresses matching
3003 * 01:80:c2:00:00:0x to the CPU port.
3004 */
3005 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
3006 if (err)
3007 return err;
3008
3009 /* Ignore removed tag data on doubly tagged packets, disable
3010 * flow control messages, force flow control priority to the
3011 * highest, and send all special multicast frames to the CPU
3012 * port at the highest priority.
3013 */
3014 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
3015 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3016 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3017 if (err)
3018 return err;
3019
3020 /* Program the DSA routing table. */
3021 for (i = 0; i < 32; i++) {
3022 int nexthop = 0x1f;
3023
Andrew Lunn66472fc2016-06-04 21:17:00 +02003024 if (i != ds->index && i < DSA_MAX_SWITCHES)
3025 nexthop = ds->rtable[i] & 0x1f;
Vivien Didelot08a01262016-05-09 13:22:50 -04003026
3027 err = _mv88e6xxx_reg_write(
3028 ps, REG_GLOBAL2,
3029 GLOBAL2_DEVICE_MAPPING,
3030 GLOBAL2_DEVICE_MAPPING_UPDATE |
3031 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
3032 if (err)
3033 return err;
3034 }
3035
3036 /* Clear all trunk masks. */
3037 for (i = 0; i < 8; i++) {
3038 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
3039 0x8000 |
3040 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
3041 ((1 << ps->info->num_ports) - 1));
3042 if (err)
3043 return err;
3044 }
3045
3046 /* Clear all trunk mappings. */
3047 for (i = 0; i < 16; i++) {
3048 err = _mv88e6xxx_reg_write(
3049 ps, REG_GLOBAL2,
3050 GLOBAL2_TRUNK_MAPPING,
3051 GLOBAL2_TRUNK_MAPPING_UPDATE |
3052 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
3053 if (err)
3054 return err;
3055 }
3056
3057 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3058 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3059 mv88e6xxx_6320_family(ps)) {
3060 /* Send all frames with destination addresses matching
3061 * 01:80:c2:00:00:2x to the CPU port.
3062 */
3063 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3064 GLOBAL2_MGMT_EN_2X, 0xffff);
3065 if (err)
3066 return err;
3067
3068 /* Initialise cross-chip port VLAN table to reset
3069 * defaults.
3070 */
3071 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3072 GLOBAL2_PVT_ADDR, 0x9000);
3073 if (err)
3074 return err;
3075
3076 /* Clear the priority override table. */
3077 for (i = 0; i < 16; i++) {
3078 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3079 GLOBAL2_PRIO_OVERRIDE,
3080 0x8000 | (i << 8));
3081 if (err)
3082 return err;
3083 }
3084 }
3085
3086 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3087 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3088 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
3089 mv88e6xxx_6320_family(ps)) {
3090 /* Disable ingress rate limiting by resetting all
3091 * ingress rate limit registers to their initial
3092 * state.
3093 */
3094 for (i = 0; i < ps->info->num_ports; i++) {
3095 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3096 GLOBAL2_INGRESS_OP,
3097 0x9000 | (i << 8));
3098 if (err)
3099 return err;
3100 }
3101 }
3102
3103 /* Clear the statistics counters for all ports */
3104 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
3105 GLOBAL_STATS_OP_FLUSH_ALL);
3106 if (err)
3107 return err;
3108
3109 /* Wait for the flush to complete. */
3110 err = _mv88e6xxx_stats_wait(ps);
3111 if (err)
3112 return err;
3113
3114 /* Clear all ATU entries */
3115 err = _mv88e6xxx_atu_flush(ps, 0, true);
3116 if (err)
3117 return err;
3118
3119 /* Clear all the VTU and STU entries */
3120 err = _mv88e6xxx_vtu_stu_flush(ps);
3121 if (err < 0)
3122 return err;
3123
3124 return err;
3125}
3126
Vivien Didelotf81ec902016-05-09 13:22:58 -04003127static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003128{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003129 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003130 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003131 int i;
3132
3133 ps->ds = ds;
Andrew Lunnb516d452016-06-04 21:17:06 +02003134 ds->slave_mii_bus = ps->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003135
Vivien Didelotd24645b2016-05-09 13:22:41 -04003136 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
3137 mutex_init(&ps->eeprom_mutex);
3138
Vivien Didelot552238b2016-05-09 13:22:49 -04003139 mutex_lock(&ps->smi_mutex);
3140
3141 err = mv88e6xxx_switch_reset(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003142 if (err)
3143 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003144
Vivien Didelot08a01262016-05-09 13:22:50 -04003145 err = mv88e6xxx_setup_global(ps);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003146 if (err)
3147 goto unlock;
3148
3149 for (i = 0; i < ps->info->num_ports; i++) {
3150 err = mv88e6xxx_setup_port(ps, i);
3151 if (err)
3152 goto unlock;
3153 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003154
Vivien Didelot6b17e862015-08-13 12:52:18 -04003155unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04003156 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02003157
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003158 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003159}
3160
Andrew Lunn03a4a542016-06-04 21:17:05 +02003161int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page, int reg)
Andrew Lunn491435852015-04-02 04:06:35 +02003162{
3163 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3164 int ret;
3165
Andrew Lunn3898c142015-05-06 01:09:53 +02003166 mutex_lock(&ps->smi_mutex);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003167 ret = _mv88e6xxx_mdio_page_read(ps, port, page, reg);
Andrew Lunn3898c142015-05-06 01:09:53 +02003168 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003169
Andrew Lunn491435852015-04-02 04:06:35 +02003170 return ret;
3171}
3172
Andrew Lunn03a4a542016-06-04 21:17:05 +02003173int mv88e6xxx_mdio_page_write(struct dsa_switch *ds, int port, int page,
3174 int reg, int val)
Andrew Lunn491435852015-04-02 04:06:35 +02003175{
3176 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3177 int ret;
3178
Andrew Lunn3898c142015-05-06 01:09:53 +02003179 mutex_lock(&ps->smi_mutex);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003180 ret = _mv88e6xxx_mdio_page_write(ps, port, page, reg, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02003181 mutex_unlock(&ps->smi_mutex);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003182
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003183 return ret;
3184}
3185
Andrew Lunn03a4a542016-06-04 21:17:05 +02003186static int mv88e6xxx_port_to_mdio_addr(struct mv88e6xxx_priv_state *ps,
3187 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003188{
Vivien Didelot009a2b92016-04-17 13:24:01 -04003189 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003190 return port;
3191 return -EINVAL;
3192}
3193
Andrew Lunnb516d452016-06-04 21:17:06 +02003194static int mv88e6xxx_mdio_read(struct mii_bus *bus, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003195{
Andrew Lunnb516d452016-06-04 21:17:06 +02003196 struct mv88e6xxx_priv_state *ps = bus->priv;
Andrew Lunn03a4a542016-06-04 21:17:05 +02003197 int addr = mv88e6xxx_port_to_mdio_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003198 int ret;
3199
3200 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003201 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003202
Andrew Lunn3898c142015-05-06 01:09:53 +02003203 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003204
3205 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003206 ret = mv88e6xxx_mdio_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003207 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003208 ret = mv88e6xxx_mdio_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003209 else
Andrew Lunn03a4a542016-06-04 21:17:05 +02003210 ret = mv88e6xxx_mdio_read_direct(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003211
Andrew Lunn3898c142015-05-06 01:09:53 +02003212 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003213 return ret;
3214}
3215
Andrew Lunnb516d452016-06-04 21:17:06 +02003216static int mv88e6xxx_mdio_write(struct mii_bus *bus, int port, int regnum,
Andrew Lunn03a4a542016-06-04 21:17:05 +02003217 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003218{
Andrew Lunnb516d452016-06-04 21:17:06 +02003219 struct mv88e6xxx_priv_state *ps = bus->priv;
Andrew Lunn03a4a542016-06-04 21:17:05 +02003220 int addr = mv88e6xxx_port_to_mdio_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003221 int ret;
3222
3223 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003224 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003225
Andrew Lunn3898c142015-05-06 01:09:53 +02003226 mutex_lock(&ps->smi_mutex);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003227
3228 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003229 ret = mv88e6xxx_mdio_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003230 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003231 ret = mv88e6xxx_mdio_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003232 else
Andrew Lunn03a4a542016-06-04 21:17:05 +02003233 ret = mv88e6xxx_mdio_write_direct(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003234
Andrew Lunn3898c142015-05-06 01:09:53 +02003235 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003236 return ret;
3237}
3238
Andrew Lunnb516d452016-06-04 21:17:06 +02003239static int mv88e6xxx_mdio_register(struct mv88e6xxx_priv_state *ps,
3240 struct device_node *np)
3241{
3242 static int index;
3243 struct mii_bus *bus;
3244 int err;
3245
3246 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3247 mv88e6xxx_ppu_state_init(ps);
3248
3249 if (np)
3250 ps->mdio_np = of_get_child_by_name(np, "mdio");
3251
3252 bus = devm_mdiobus_alloc(ps->dev);
3253 if (!bus)
3254 return -ENOMEM;
3255
3256 bus->priv = (void *)ps;
3257 if (np) {
3258 bus->name = np->full_name;
3259 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3260 } else {
3261 bus->name = "mv88e6xxx SMI";
3262 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3263 }
3264
3265 bus->read = mv88e6xxx_mdio_read;
3266 bus->write = mv88e6xxx_mdio_write;
3267 bus->parent = ps->dev;
3268
3269 if (ps->mdio_np)
3270 err = of_mdiobus_register(bus, ps->mdio_np);
3271 else
3272 err = mdiobus_register(bus);
3273 if (err) {
3274 dev_err(ps->dev, "Cannot register MDIO bus (%d)\n", err);
3275 goto out;
3276 }
3277 ps->mdio_bus = bus;
3278
3279 return 0;
3280
3281out:
3282 if (ps->mdio_np)
3283 of_node_put(ps->mdio_np);
3284
3285 return err;
3286}
3287
3288static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_priv_state *ps)
3289
3290{
3291 struct mii_bus *bus = ps->mdio_bus;
3292
3293 mdiobus_unregister(bus);
3294
3295 if (ps->mdio_np)
3296 of_node_put(ps->mdio_np);
3297}
3298
Guenter Roeckc22995c2015-07-25 09:42:28 -07003299#ifdef CONFIG_NET_DSA_HWMON
3300
3301static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3302{
3303 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3304 int ret;
3305 int val;
3306
3307 *temp = 0;
3308
3309 mutex_lock(&ps->smi_mutex);
3310
Andrew Lunn03a4a542016-06-04 21:17:05 +02003311 ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003312 if (ret < 0)
3313 goto error;
3314
3315 /* Enable temperature sensor */
Andrew Lunn03a4a542016-06-04 21:17:05 +02003316 ret = mv88e6xxx_mdio_read_direct(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003317 if (ret < 0)
3318 goto error;
3319
Andrew Lunn03a4a542016-06-04 21:17:05 +02003320 ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003321 if (ret < 0)
3322 goto error;
3323
3324 /* Wait for temperature to stabilize */
3325 usleep_range(10000, 12000);
3326
Andrew Lunn03a4a542016-06-04 21:17:05 +02003327 val = mv88e6xxx_mdio_read_direct(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003328 if (val < 0) {
3329 ret = val;
3330 goto error;
3331 }
3332
3333 /* Disable temperature sensor */
Andrew Lunn03a4a542016-06-04 21:17:05 +02003334 ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003335 if (ret < 0)
3336 goto error;
3337
3338 *temp = ((val & 0x1f) - 5) * 5;
3339
3340error:
Andrew Lunn03a4a542016-06-04 21:17:05 +02003341 mv88e6xxx_mdio_write_direct(ps, 0x0, 0x16, 0x0);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003342 mutex_unlock(&ps->smi_mutex);
3343 return ret;
3344}
3345
3346static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3347{
Andrew Lunn158bc062016-04-28 21:24:06 -04003348 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3349 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003350 int ret;
3351
3352 *temp = 0;
3353
Andrew Lunn03a4a542016-06-04 21:17:05 +02003354 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 27);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003355 if (ret < 0)
3356 return ret;
3357
3358 *temp = (ret & 0xff) - 25;
3359
3360 return 0;
3361}
3362
Vivien Didelotf81ec902016-05-09 13:22:58 -04003363static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003364{
Andrew Lunn158bc062016-04-28 21:24:06 -04003365 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3366
Vivien Didelot6594f612016-05-09 13:22:42 -04003367 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
3368 return -EOPNOTSUPP;
3369
Andrew Lunn158bc062016-04-28 21:24:06 -04003370 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003371 return mv88e63xx_get_temp(ds, temp);
3372
3373 return mv88e61xx_get_temp(ds, temp);
3374}
3375
Vivien Didelotf81ec902016-05-09 13:22:58 -04003376static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003377{
Andrew Lunn158bc062016-04-28 21:24:06 -04003378 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3379 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003380 int ret;
3381
Vivien Didelot6594f612016-05-09 13:22:42 -04003382 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003383 return -EOPNOTSUPP;
3384
3385 *temp = 0;
3386
Andrew Lunn03a4a542016-06-04 21:17:05 +02003387 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003388 if (ret < 0)
3389 return ret;
3390
3391 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3392
3393 return 0;
3394}
3395
Vivien Didelotf81ec902016-05-09 13:22:58 -04003396static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003397{
Andrew Lunn158bc062016-04-28 21:24:06 -04003398 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3399 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003400 int ret;
3401
Vivien Didelot6594f612016-05-09 13:22:42 -04003402 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003403 return -EOPNOTSUPP;
3404
Andrew Lunn03a4a542016-06-04 21:17:05 +02003405 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003406 if (ret < 0)
3407 return ret;
3408 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003409 return mv88e6xxx_mdio_page_write(ds, phy, 6, 26,
3410 (ret & 0xe0ff) | (temp << 8));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003411}
3412
Vivien Didelotf81ec902016-05-09 13:22:58 -04003413static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003414{
Andrew Lunn158bc062016-04-28 21:24:06 -04003415 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3416 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003417 int ret;
3418
Vivien Didelot6594f612016-05-09 13:22:42 -04003419 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003420 return -EOPNOTSUPP;
3421
3422 *alarm = false;
3423
Andrew Lunn03a4a542016-06-04 21:17:05 +02003424 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003425 if (ret < 0)
3426 return ret;
3427
3428 *alarm = !!(ret & 0x40);
3429
3430 return 0;
3431}
3432#endif /* CONFIG_NET_DSA_HWMON */
3433
Vivien Didelotf81ec902016-05-09 13:22:58 -04003434static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3435 [MV88E6085] = {
3436 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3437 .family = MV88E6XXX_FAMILY_6097,
3438 .name = "Marvell 88E6085",
3439 .num_databases = 4096,
3440 .num_ports = 10,
3441 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3442 },
3443
3444 [MV88E6095] = {
3445 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3446 .family = MV88E6XXX_FAMILY_6095,
3447 .name = "Marvell 88E6095/88E6095F",
3448 .num_databases = 256,
3449 .num_ports = 11,
3450 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3451 },
3452
3453 [MV88E6123] = {
3454 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3455 .family = MV88E6XXX_FAMILY_6165,
3456 .name = "Marvell 88E6123",
3457 .num_databases = 4096,
3458 .num_ports = 3,
3459 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3460 },
3461
3462 [MV88E6131] = {
3463 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3464 .family = MV88E6XXX_FAMILY_6185,
3465 .name = "Marvell 88E6131",
3466 .num_databases = 256,
3467 .num_ports = 8,
3468 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3469 },
3470
3471 [MV88E6161] = {
3472 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3473 .family = MV88E6XXX_FAMILY_6165,
3474 .name = "Marvell 88E6161",
3475 .num_databases = 4096,
3476 .num_ports = 6,
3477 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3478 },
3479
3480 [MV88E6165] = {
3481 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3482 .family = MV88E6XXX_FAMILY_6165,
3483 .name = "Marvell 88E6165",
3484 .num_databases = 4096,
3485 .num_ports = 6,
3486 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3487 },
3488
3489 [MV88E6171] = {
3490 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3491 .family = MV88E6XXX_FAMILY_6351,
3492 .name = "Marvell 88E6171",
3493 .num_databases = 4096,
3494 .num_ports = 7,
3495 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3496 },
3497
3498 [MV88E6172] = {
3499 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3500 .family = MV88E6XXX_FAMILY_6352,
3501 .name = "Marvell 88E6172",
3502 .num_databases = 4096,
3503 .num_ports = 7,
3504 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3505 },
3506
3507 [MV88E6175] = {
3508 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3509 .family = MV88E6XXX_FAMILY_6351,
3510 .name = "Marvell 88E6175",
3511 .num_databases = 4096,
3512 .num_ports = 7,
3513 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3514 },
3515
3516 [MV88E6176] = {
3517 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3518 .family = MV88E6XXX_FAMILY_6352,
3519 .name = "Marvell 88E6176",
3520 .num_databases = 4096,
3521 .num_ports = 7,
3522 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3523 },
3524
3525 [MV88E6185] = {
3526 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3527 .family = MV88E6XXX_FAMILY_6185,
3528 .name = "Marvell 88E6185",
3529 .num_databases = 256,
3530 .num_ports = 10,
3531 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3532 },
3533
3534 [MV88E6240] = {
3535 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3536 .family = MV88E6XXX_FAMILY_6352,
3537 .name = "Marvell 88E6240",
3538 .num_databases = 4096,
3539 .num_ports = 7,
3540 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3541 },
3542
3543 [MV88E6320] = {
3544 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3545 .family = MV88E6XXX_FAMILY_6320,
3546 .name = "Marvell 88E6320",
3547 .num_databases = 4096,
3548 .num_ports = 7,
3549 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3550 },
3551
3552 [MV88E6321] = {
3553 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3554 .family = MV88E6XXX_FAMILY_6320,
3555 .name = "Marvell 88E6321",
3556 .num_databases = 4096,
3557 .num_ports = 7,
3558 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3559 },
3560
3561 [MV88E6350] = {
3562 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3563 .family = MV88E6XXX_FAMILY_6351,
3564 .name = "Marvell 88E6350",
3565 .num_databases = 4096,
3566 .num_ports = 7,
3567 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3568 },
3569
3570 [MV88E6351] = {
3571 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3572 .family = MV88E6XXX_FAMILY_6351,
3573 .name = "Marvell 88E6351",
3574 .num_databases = 4096,
3575 .num_ports = 7,
3576 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3577 },
3578
3579 [MV88E6352] = {
3580 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3581 .family = MV88E6XXX_FAMILY_6352,
3582 .name = "Marvell 88E6352",
3583 .num_databases = 4096,
3584 .num_ports = 7,
3585 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3586 },
3587};
3588
Vivien Didelotf6271e62016-04-17 13:23:59 -04003589static const struct mv88e6xxx_info *
3590mv88e6xxx_lookup_info(unsigned int prod_num, const struct mv88e6xxx_info *table,
Vivien Didelot0209d142016-04-17 13:23:55 -04003591 unsigned int num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003592{
Vivien Didelota439c062016-04-17 13:23:58 -04003593 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003594
Vivien Didelotb9b37712015-10-30 19:39:48 -04003595 for (i = 0; i < num; ++i)
Vivien Didelotf6271e62016-04-17 13:23:59 -04003596 if (table[i].prod_num == prod_num)
3597 return &table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003598
Vivien Didelotb9b37712015-10-30 19:39:48 -04003599 return NULL;
3600}
3601
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003602static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3603 struct device *host_dev, int sw_addr,
3604 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003605{
Vivien Didelotf6271e62016-04-17 13:23:59 -04003606 const struct mv88e6xxx_info *info;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003607 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003608 struct mii_bus *bus;
Vivien Didelot0209d142016-04-17 13:23:55 -04003609 const char *name;
Vivien Didelota439c062016-04-17 13:23:58 -04003610 int id, prod_num, rev;
Andrew Lunnb516d452016-06-04 21:17:06 +02003611 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003612
Vivien Didelota439c062016-04-17 13:23:58 -04003613 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003614 if (!bus)
3615 return NULL;
3616
Vivien Didelota439c062016-04-17 13:23:58 -04003617 id = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), PORT_SWITCH_ID);
3618 if (id < 0)
3619 return NULL;
3620
3621 prod_num = (id & 0xfff0) >> 4;
3622 rev = id & 0x000f;
3623
Vivien Didelotf81ec902016-05-09 13:22:58 -04003624 info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3625 ARRAY_SIZE(mv88e6xxx_table));
Vivien Didelotf6271e62016-04-17 13:23:59 -04003626 if (!info)
Vivien Didelota439c062016-04-17 13:23:58 -04003627 return NULL;
3628
Vivien Didelotf6271e62016-04-17 13:23:59 -04003629 name = info->name;
3630
Vivien Didelota439c062016-04-17 13:23:58 -04003631 ps = devm_kzalloc(dsa_dev, sizeof(*ps), GFP_KERNEL);
3632 if (!ps)
3633 return NULL;
3634
3635 ps->bus = bus;
3636 ps->sw_addr = sw_addr;
Vivien Didelotf6271e62016-04-17 13:23:59 -04003637 ps->info = info;
Andrew Lunnb516d452016-06-04 21:17:06 +02003638 ps->dev = dsa_dev;
Andrew Lunnb6819572016-05-10 23:27:19 +02003639 mutex_init(&ps->smi_mutex);
Vivien Didelota439c062016-04-17 13:23:58 -04003640
Andrew Lunnb516d452016-06-04 21:17:06 +02003641 err = mv88e6xxx_mdio_register(ps, NULL);
3642 if (err)
3643 return NULL;
3644
Vivien Didelota439c062016-04-17 13:23:58 -04003645 *priv = ps;
3646
3647 dev_info(&ps->bus->dev, "switch 0x%x probed: %s, revision %u\n",
3648 prod_num, name, rev);
3649
Andrew Lunna77d43f2016-04-13 02:40:42 +02003650 return name;
3651}
3652
Vivien Didelotf81ec902016-05-09 13:22:58 -04003653struct dsa_switch_driver mv88e6xxx_switch_driver = {
3654 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003655 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003656 .setup = mv88e6xxx_setup,
3657 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003658 .adjust_link = mv88e6xxx_adjust_link,
3659 .get_strings = mv88e6xxx_get_strings,
3660 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3661 .get_sset_count = mv88e6xxx_get_sset_count,
3662 .set_eee = mv88e6xxx_set_eee,
3663 .get_eee = mv88e6xxx_get_eee,
3664#ifdef CONFIG_NET_DSA_HWMON
3665 .get_temp = mv88e6xxx_get_temp,
3666 .get_temp_limit = mv88e6xxx_get_temp_limit,
3667 .set_temp_limit = mv88e6xxx_set_temp_limit,
3668 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3669#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003670 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003671 .get_eeprom = mv88e6xxx_get_eeprom,
3672 .set_eeprom = mv88e6xxx_set_eeprom,
3673 .get_regs_len = mv88e6xxx_get_regs_len,
3674 .get_regs = mv88e6xxx_get_regs,
3675 .port_bridge_join = mv88e6xxx_port_bridge_join,
3676 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3677 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3678 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3679 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3680 .port_vlan_add = mv88e6xxx_port_vlan_add,
3681 .port_vlan_del = mv88e6xxx_port_vlan_del,
3682 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3683 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3684 .port_fdb_add = mv88e6xxx_port_fdb_add,
3685 .port_fdb_del = mv88e6xxx_port_fdb_del,
3686 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3687};
3688
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003689int mv88e6xxx_probe(struct mdio_device *mdiodev)
3690{
3691 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003692 struct device_node *np = dev->of_node;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003693 struct mv88e6xxx_priv_state *ps;
3694 int id, prod_num, rev;
3695 struct dsa_switch *ds;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003696 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003697 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003698
3699 ds = devm_kzalloc(dev, sizeof(*ds) + sizeof(*ps), GFP_KERNEL);
3700 if (!ds)
3701 return -ENOMEM;
3702
3703 ps = (struct mv88e6xxx_priv_state *)(ds + 1);
3704 ds->priv = ps;
Andrew Lunnc33063d2016-05-10 23:27:23 +02003705 ds->dev = dev;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003706 ps->dev = dev;
3707 ps->ds = ds;
3708 ps->bus = mdiodev->bus;
3709 ps->sw_addr = mdiodev->addr;
3710 mutex_init(&ps->smi_mutex);
3711
3712 get_device(&ps->bus->dev);
3713
3714 ds->drv = &mv88e6xxx_switch_driver;
3715
3716 id = mv88e6xxx_reg_read(ps, REG_PORT(0), PORT_SWITCH_ID);
3717 if (id < 0)
3718 return id;
3719
3720 prod_num = (id & 0xfff0) >> 4;
3721 rev = id & 0x000f;
3722
3723 ps->info = mv88e6xxx_lookup_info(prod_num, mv88e6xxx_table,
3724 ARRAY_SIZE(mv88e6xxx_table));
3725 if (!ps->info)
3726 return -ENODEV;
3727
Andrew Lunn52638f72016-05-10 23:27:22 +02003728 ps->reset = devm_gpiod_get(&mdiodev->dev, "reset", GPIOD_ASIS);
3729 if (IS_ERR(ps->reset)) {
3730 err = PTR_ERR(ps->reset);
3731 if (err == -ENOENT) {
3732 /* Optional, so not an error */
3733 ps->reset = NULL;
3734 } else {
3735 return err;
3736 }
3737 }
3738
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003739 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM) &&
3740 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3741 ps->eeprom_len = eeprom_len;
3742
Andrew Lunnb516d452016-06-04 21:17:06 +02003743 err = mv88e6xxx_mdio_register(ps, mdiodev->dev.of_node);
3744 if (err)
3745 return err;
3746
3747 ds->slave_mii_bus = ps->mdio_bus;
3748
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003749 dev_set_drvdata(dev, ds);
3750
3751 dev_info(dev, "switch 0x%x probed: %s, revision %u\n",
3752 prod_num, ps->info->name, rev);
3753
3754 return 0;
3755}
3756
3757static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3758{
3759 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
3760 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3761
3762 put_device(&ps->bus->dev);
Andrew Lunnb516d452016-06-04 21:17:06 +02003763
3764 mv88e6xxx_mdio_unregister(ps);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003765}
3766
3767static const struct of_device_id mv88e6xxx_of_match[] = {
3768 { .compatible = "marvell,mv88e6085" },
3769 { /* sentinel */ },
3770};
3771
3772MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3773
3774static struct mdio_driver mv88e6xxx_driver = {
3775 .probe = mv88e6xxx_probe,
3776 .remove = mv88e6xxx_remove,
3777 .mdiodrv.driver = {
3778 .name = "mv88e6085",
3779 .of_match_table = mv88e6xxx_of_match,
3780 },
3781};
3782
Ben Hutchings98e67302011-11-25 14:36:19 +00003783static int __init mv88e6xxx_init(void)
3784{
Vivien Didelotf81ec902016-05-09 13:22:58 -04003785 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003786 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003787}
3788module_init(mv88e6xxx_init);
3789
3790static void __exit mv88e6xxx_cleanup(void)
3791{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003792 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04003793 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003794}
3795module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003796
3797MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3798MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3799MODULE_LICENSE("GPL");