blob: 789f938a7f3699bf724ff9345a74d946ed9cca7c [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>
Vivien Didelotcaac8542016-06-20 13:14:09 -040024#include <linux/of_device.h>
Andrew Lunnb516d452016-06-04 21:17:06 +020025#include <linux/of_mdio.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000026#include <linux/netdevice.h>
Andrew Lunnc8c1b39a2015-11-20 03:56:24 +010027#include <linux/gpio/consumer.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000028#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000029#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040030#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000031#include "mv88e6xxx.h"
32
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -040033static void assert_reg_lock(struct mv88e6xxx_priv_state *ps)
Vivien Didelot3996a4f2015-10-30 18:56:45 -040034{
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -040035 if (unlikely(!mutex_is_locked(&ps->reg_lock))) {
36 dev_err(ps->dev, "Switch registers lock not held!\n");
Vivien Didelot3996a4f2015-10-30 18:56:45 -040037 dump_stack();
38 }
39}
40
Barry Grussling3675c8d2013-01-08 16:05:53 +000041/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000042 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
43 * will be directly accessible on some {device address,register address}
44 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
45 * will only respond to SMI transactions to that specific address, and
46 * an indirect addressing mechanism needs to be used to access its
47 * registers.
48 */
49static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
50{
51 int ret;
52 int i;
53
54 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020055 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000056 if (ret < 0)
57 return ret;
58
Andrew Lunncca8b132015-04-02 04:06:39 +020059 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000060 return 0;
61 }
62
63 return -ETIMEDOUT;
64}
65
Vivien Didelotb9b37712015-10-30 19:39:48 -040066static int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr,
67 int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000068{
69 int ret;
70
71 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020072 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000073
Barry Grussling3675c8d2013-01-08 16:05:53 +000074 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000075 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
76 if (ret < 0)
77 return ret;
78
Barry Grussling3675c8d2013-01-08 16:05:53 +000079 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020080 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
81 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000082 if (ret < 0)
83 return ret;
84
Barry Grussling3675c8d2013-01-08 16:05:53 +000085 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000086 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
87 if (ret < 0)
88 return ret;
89
Barry Grussling3675c8d2013-01-08 16:05:53 +000090 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020091 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000092 if (ret < 0)
93 return ret;
94
95 return ret & 0xffff;
96}
97
Andrew Lunn158bc062016-04-28 21:24:06 -040098static int _mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps,
99 int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000100{
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000101 int ret;
102
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400103 assert_reg_lock(ps);
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400104
Andrew Lunna77d43f2016-04-13 02:40:42 +0200105 ret = __mv88e6xxx_reg_read(ps->bus, ps->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500106 if (ret < 0)
107 return ret;
108
Andrew Lunn158bc062016-04-28 21:24:06 -0400109 dev_dbg(ps->dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500110 addr, reg, ret);
111
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000112 return ret;
113}
114
Vivien Didelot57d32312016-06-20 13:13:58 -0400115static int mv88e6xxx_reg_read(struct mv88e6xxx_priv_state *ps, int addr,
116 int reg)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700117{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700118 int ret;
119
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400120 mutex_lock(&ps->reg_lock);
Andrew Lunn158bc062016-04-28 21:24:06 -0400121 ret = _mv88e6xxx_reg_read(ps, addr, reg);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400122 mutex_unlock(&ps->reg_lock);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700123
124 return ret;
125}
126
Vivien Didelotb9b37712015-10-30 19:39:48 -0400127static int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
128 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000129{
130 int ret;
131
132 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200133 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000134
Barry Grussling3675c8d2013-01-08 16:05:53 +0000135 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000136 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
137 if (ret < 0)
138 return ret;
139
Barry Grussling3675c8d2013-01-08 16:05:53 +0000140 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200141 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000142 if (ret < 0)
143 return ret;
144
Barry Grussling3675c8d2013-01-08 16:05:53 +0000145 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200146 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
147 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000148 if (ret < 0)
149 return ret;
150
Barry Grussling3675c8d2013-01-08 16:05:53 +0000151 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000152 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
153 if (ret < 0)
154 return ret;
155
156 return 0;
157}
158
Andrew Lunn158bc062016-04-28 21:24:06 -0400159static int _mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
160 int reg, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000161{
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400162 assert_reg_lock(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000163
Andrew Lunn158bc062016-04-28 21:24:06 -0400164 dev_dbg(ps->dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500165 addr, reg, val);
166
Andrew Lunna77d43f2016-04-13 02:40:42 +0200167 return __mv88e6xxx_reg_write(ps->bus, ps->sw_addr, addr, reg, val);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700168}
169
Vivien Didelot57d32312016-06-20 13:13:58 -0400170static int mv88e6xxx_reg_write(struct mv88e6xxx_priv_state *ps, int addr,
171 int reg, u16 val)
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700172{
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700173 int ret;
174
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400175 mutex_lock(&ps->reg_lock);
Andrew Lunn158bc062016-04-28 21:24:06 -0400176 ret = _mv88e6xxx_reg_write(ps, addr, reg, val);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400177 mutex_unlock(&ps->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000178
179 return ret;
180}
181
Vivien Didelot1d13a062016-05-09 13:22:43 -0400182static int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000183{
Andrew Lunn158bc062016-04-28 21:24:06 -0400184 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200185 int err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000186
Andrew Lunn158bc062016-04-28 21:24:06 -0400187 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_01,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200188 (addr[0] << 8) | addr[1]);
189 if (err)
190 return err;
191
Andrew Lunn158bc062016-04-28 21:24:06 -0400192 err = mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_23,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200193 (addr[2] << 8) | addr[3]);
194 if (err)
195 return err;
196
Andrew Lunn158bc062016-04-28 21:24:06 -0400197 return mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MAC_45,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200198 (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000199}
200
Vivien Didelot1d13a062016-05-09 13:22:43 -0400201static int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000202{
Andrew Lunn158bc062016-04-28 21:24:06 -0400203 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000204 int ret;
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200205 int i;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000206
207 for (i = 0; i < 6; i++) {
208 int j;
209
Barry Grussling3675c8d2013-01-08 16:05:53 +0000210 /* Write the MAC address byte. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400211 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200212 GLOBAL2_SWITCH_MAC_BUSY |
213 (i << 8) | addr[i]);
214 if (ret)
215 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000216
Barry Grussling3675c8d2013-01-08 16:05:53 +0000217 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000218 for (j = 0; j < 16; j++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400219 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2,
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200220 GLOBAL2_SWITCH_MAC);
221 if (ret < 0)
222 return ret;
223
Andrew Lunncca8b132015-04-02 04:06:39 +0200224 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000225 break;
226 }
227 if (j == 16)
228 return -ETIMEDOUT;
229 }
230
231 return 0;
232}
233
Vivien Didelot57d32312016-06-20 13:13:58 -0400234static int mv88e6xxx_set_addr(struct dsa_switch *ds, u8 *addr)
Vivien Didelot1d13a062016-05-09 13:22:43 -0400235{
236 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
237
238 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SWITCH_MAC))
239 return mv88e6xxx_set_addr_indirect(ds, addr);
240 else
241 return mv88e6xxx_set_addr_direct(ds, addr);
242}
243
Andrew Lunn03a4a542016-06-04 21:17:05 +0200244static int mv88e6xxx_mdio_read_direct(struct mv88e6xxx_priv_state *ps,
245 int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000246{
247 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400248 return _mv88e6xxx_reg_read(ps, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000249 return 0xffff;
250}
251
Andrew Lunn03a4a542016-06-04 21:17:05 +0200252static int mv88e6xxx_mdio_write_direct(struct mv88e6xxx_priv_state *ps,
253 int addr, int regnum, u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000254{
255 if (addr >= 0)
Andrew Lunn158bc062016-04-28 21:24:06 -0400256 return _mv88e6xxx_reg_write(ps, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000257 return 0;
258}
259
Andrew Lunn158bc062016-04-28 21:24:06 -0400260static int mv88e6xxx_ppu_disable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000261{
262 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000263 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000264
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400265 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200266 if (ret < 0)
267 return ret;
268
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400269 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
270 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200271 if (ret)
272 return ret;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000273
Barry Grussling19b2f972013-01-08 16:05:54 +0000274 timeout = jiffies + 1 * HZ;
275 while (time_before(jiffies, timeout)) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400276 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200277 if (ret < 0)
278 return ret;
279
Barry Grussling19b2f972013-01-08 16:05:54 +0000280 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200281 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
282 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000283 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000284 }
285
286 return -ETIMEDOUT;
287}
288
Andrew Lunn158bc062016-04-28 21:24:06 -0400289static int mv88e6xxx_ppu_enable(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000290{
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200291 int ret, err;
Barry Grussling19b2f972013-01-08 16:05:54 +0000292 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000293
Vivien Didelot762eb672016-06-04 21:16:54 +0200294 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_CONTROL);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200295 if (ret < 0)
296 return ret;
297
Vivien Didelot762eb672016-06-04 21:16:54 +0200298 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL,
299 ret | GLOBAL_CONTROL_PPU_ENABLE);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200300 if (err)
301 return err;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000302
Barry Grussling19b2f972013-01-08 16:05:54 +0000303 timeout = jiffies + 1 * HZ;
304 while (time_before(jiffies, timeout)) {
Vivien Didelot762eb672016-06-04 21:16:54 +0200305 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATUS);
Andrew Lunn48ace4e2016-04-14 23:47:12 +0200306 if (ret < 0)
307 return ret;
308
Barry Grussling19b2f972013-01-08 16:05:54 +0000309 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200310 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
311 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000312 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000313 }
314
315 return -ETIMEDOUT;
316}
317
318static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
319{
320 struct mv88e6xxx_priv_state *ps;
321
322 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
Vivien Didelot762eb672016-06-04 21:16:54 +0200323
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400324 mutex_lock(&ps->reg_lock);
Vivien Didelot762eb672016-06-04 21:16:54 +0200325
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000326 if (mutex_trylock(&ps->ppu_mutex)) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400327 if (mv88e6xxx_ppu_enable(ps) == 0)
Barry Grussling85686582013-01-08 16:05:56 +0000328 ps->ppu_disabled = 0;
329 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000330 }
Vivien Didelot762eb672016-06-04 21:16:54 +0200331
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400332 mutex_unlock(&ps->reg_lock);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000333}
334
335static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
336{
337 struct mv88e6xxx_priv_state *ps = (void *)_ps;
338
339 schedule_work(&ps->ppu_work);
340}
341
Andrew Lunn158bc062016-04-28 21:24:06 -0400342static int mv88e6xxx_ppu_access_get(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000343{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000344 int ret;
345
346 mutex_lock(&ps->ppu_mutex);
347
Barry Grussling3675c8d2013-01-08 16:05:53 +0000348 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000349 * we can access the PHY registers. If it was already
350 * disabled, cancel the timer that is going to re-enable
351 * it.
352 */
353 if (!ps->ppu_disabled) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400354 ret = mv88e6xxx_ppu_disable(ps);
Barry Grussling85686582013-01-08 16:05:56 +0000355 if (ret < 0) {
356 mutex_unlock(&ps->ppu_mutex);
357 return ret;
358 }
359 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000360 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000361 del_timer(&ps->ppu_timer);
362 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000363 }
364
365 return ret;
366}
367
Andrew Lunn158bc062016-04-28 21:24:06 -0400368static void mv88e6xxx_ppu_access_put(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000369{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000370 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000371 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
372 mutex_unlock(&ps->ppu_mutex);
373}
374
Vivien Didelot57d32312016-06-20 13:13:58 -0400375static void mv88e6xxx_ppu_state_init(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000376{
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000377 mutex_init(&ps->ppu_mutex);
378 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
379 init_timer(&ps->ppu_timer);
380 ps->ppu_timer.data = (unsigned long)ps;
381 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
382}
383
Andrew Lunn03a4a542016-06-04 21:17:05 +0200384static int mv88e6xxx_mdio_read_ppu(struct mv88e6xxx_priv_state *ps, int addr,
385 int regnum)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000386{
387 int ret;
388
Andrew Lunn158bc062016-04-28 21:24:06 -0400389 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000390 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400391 ret = _mv88e6xxx_reg_read(ps, addr, regnum);
Andrew Lunn158bc062016-04-28 21:24:06 -0400392 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000393 }
394
395 return ret;
396}
397
Andrew Lunn03a4a542016-06-04 21:17:05 +0200398static int mv88e6xxx_mdio_write_ppu(struct mv88e6xxx_priv_state *ps, int addr,
399 int regnum, u16 val)
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000400{
401 int ret;
402
Andrew Lunn158bc062016-04-28 21:24:06 -0400403 ret = mv88e6xxx_ppu_access_get(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000404 if (ret >= 0) {
Vivien Didelot8c9983a2016-05-09 13:22:39 -0400405 ret = _mv88e6xxx_reg_write(ps, addr, regnum, val);
Andrew Lunn158bc062016-04-28 21:24:06 -0400406 mv88e6xxx_ppu_access_put(ps);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000407 }
408
409 return ret;
410}
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000411
Andrew Lunn158bc062016-04-28 21:24:06 -0400412static bool mv88e6xxx_6065_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200413{
Vivien Didelot22356472016-04-17 13:24:00 -0400414 return ps->info->family == MV88E6XXX_FAMILY_6065;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200415}
416
Andrew Lunn158bc062016-04-28 21:24:06 -0400417static bool mv88e6xxx_6095_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200418{
Vivien Didelot22356472016-04-17 13:24:00 -0400419 return ps->info->family == MV88E6XXX_FAMILY_6095;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200420}
421
Andrew Lunn158bc062016-04-28 21:24:06 -0400422static bool mv88e6xxx_6097_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200423{
Vivien Didelot22356472016-04-17 13:24:00 -0400424 return ps->info->family == MV88E6XXX_FAMILY_6097;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200425}
426
Andrew Lunn158bc062016-04-28 21:24:06 -0400427static bool mv88e6xxx_6165_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200428{
Vivien Didelot22356472016-04-17 13:24:00 -0400429 return ps->info->family == MV88E6XXX_FAMILY_6165;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200430}
431
Andrew Lunn158bc062016-04-28 21:24:06 -0400432static bool mv88e6xxx_6185_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200433{
Vivien Didelot22356472016-04-17 13:24:00 -0400434 return ps->info->family == MV88E6XXX_FAMILY_6185;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200435}
436
Andrew Lunn158bc062016-04-28 21:24:06 -0400437static bool mv88e6xxx_6320_family(struct mv88e6xxx_priv_state *ps)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700438{
Vivien Didelot22356472016-04-17 13:24:00 -0400439 return ps->info->family == MV88E6XXX_FAMILY_6320;
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700440}
441
Andrew Lunn158bc062016-04-28 21:24:06 -0400442static bool mv88e6xxx_6351_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunn54d792f2015-05-06 01:09:47 +0200443{
Vivien Didelot22356472016-04-17 13:24:00 -0400444 return ps->info->family == MV88E6XXX_FAMILY_6351;
Andrew Lunn54d792f2015-05-06 01:09:47 +0200445}
446
Andrew Lunn158bc062016-04-28 21:24:06 -0400447static bool mv88e6xxx_6352_family(struct mv88e6xxx_priv_state *ps)
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200448{
Vivien Didelot22356472016-04-17 13:24:00 -0400449 return ps->info->family == MV88E6XXX_FAMILY_6352;
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200450}
451
Andrew Lunn158bc062016-04-28 21:24:06 -0400452static unsigned int mv88e6xxx_num_databases(struct mv88e6xxx_priv_state *ps)
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400453{
Vivien Didelotcd5a2c82016-04-17 13:24:02 -0400454 return ps->info->num_databases;
Vivien Didelotf74df0b2016-03-31 16:53:43 -0400455}
456
Andrew Lunn158bc062016-04-28 21:24:06 -0400457static bool mv88e6xxx_has_fid_reg(struct mv88e6xxx_priv_state *ps)
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400458{
459 /* Does the device have dedicated FID registers for ATU and VTU ops? */
Andrew Lunn158bc062016-04-28 21:24:06 -0400460 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
461 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps))
Vivien Didelotb426e5f2016-03-31 16:53:42 -0400462 return true;
463
464 return false;
465}
466
Andrew Lunndea87022015-08-31 15:56:47 +0200467/* We expect the switch to perform auto negotiation if there is a real
468 * phy. However, in the case of a fixed link phy, we force the port
469 * settings from the fixed link settings.
470 */
Vivien Didelotf81ec902016-05-09 13:22:58 -0400471static void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
472 struct phy_device *phydev)
Andrew Lunndea87022015-08-31 15:56:47 +0200473{
474 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200475 u32 reg;
476 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200477
478 if (!phy_is_pseudo_fixed_link(phydev))
479 return;
480
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400481 mutex_lock(&ps->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200482
Andrew Lunn158bc062016-04-28 21:24:06 -0400483 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunndea87022015-08-31 15:56:47 +0200484 if (ret < 0)
485 goto out;
486
487 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
488 PORT_PCS_CTRL_FORCE_LINK |
489 PORT_PCS_CTRL_DUPLEX_FULL |
490 PORT_PCS_CTRL_FORCE_DUPLEX |
491 PORT_PCS_CTRL_UNFORCED);
492
493 reg |= PORT_PCS_CTRL_FORCE_LINK;
494 if (phydev->link)
Vivien Didelot57d32312016-06-20 13:13:58 -0400495 reg |= PORT_PCS_CTRL_LINK_UP;
Andrew Lunndea87022015-08-31 15:56:47 +0200496
Andrew Lunn158bc062016-04-28 21:24:06 -0400497 if (mv88e6xxx_6065_family(ps) && phydev->speed > SPEED_100)
Andrew Lunndea87022015-08-31 15:56:47 +0200498 goto out;
499
500 switch (phydev->speed) {
501 case SPEED_1000:
502 reg |= PORT_PCS_CTRL_1000;
503 break;
504 case SPEED_100:
505 reg |= PORT_PCS_CTRL_100;
506 break;
507 case SPEED_10:
508 reg |= PORT_PCS_CTRL_10;
509 break;
510 default:
511 pr_info("Unknown speed");
512 goto out;
513 }
514
515 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
516 if (phydev->duplex == DUPLEX_FULL)
517 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
518
Andrew Lunn158bc062016-04-28 21:24:06 -0400519 if ((mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps)) &&
Vivien Didelot009a2b92016-04-17 13:24:01 -0400520 (port >= ps->info->num_ports - 2)) {
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200521 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
522 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
523 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
524 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
525 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
526 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
527 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
528 }
Andrew Lunn158bc062016-04-28 21:24:06 -0400529 _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_PCS_CTRL, reg);
Andrew Lunndea87022015-08-31 15:56:47 +0200530
531out:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400532 mutex_unlock(&ps->reg_lock);
Andrew Lunndea87022015-08-31 15:56:47 +0200533}
534
Andrew Lunn158bc062016-04-28 21:24:06 -0400535static int _mv88e6xxx_stats_wait(struct mv88e6xxx_priv_state *ps)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000536{
537 int ret;
538 int i;
539
540 for (i = 0; i < 10; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400541 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200542 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000543 return 0;
544 }
545
546 return -ETIMEDOUT;
547}
548
Andrew Lunn158bc062016-04-28 21:24:06 -0400549static int _mv88e6xxx_stats_snapshot(struct mv88e6xxx_priv_state *ps,
550 int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000551{
552 int ret;
553
Andrew Lunn158bc062016-04-28 21:24:06 -0400554 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200555 port = (port + 1) << 5;
556
Barry Grussling3675c8d2013-01-08 16:05:53 +0000557 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400558 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200559 GLOBAL_STATS_OP_CAPTURE_PORT |
560 GLOBAL_STATS_OP_HIST_RX_TX | port);
561 if (ret < 0)
562 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000563
Barry Grussling3675c8d2013-01-08 16:05:53 +0000564 /* Wait for the snapshotting to complete. */
Andrew Lunn158bc062016-04-28 21:24:06 -0400565 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000566 if (ret < 0)
567 return ret;
568
569 return 0;
570}
571
Andrew Lunn158bc062016-04-28 21:24:06 -0400572static void _mv88e6xxx_stats_read(struct mv88e6xxx_priv_state *ps,
573 int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000574{
575 u32 _val;
576 int ret;
577
578 *val = 0;
579
Andrew Lunn158bc062016-04-28 21:24:06 -0400580 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
Andrew Lunn31888232015-05-06 01:09:54 +0200581 GLOBAL_STATS_OP_READ_CAPTURED |
582 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000583 if (ret < 0)
584 return;
585
Andrew Lunn158bc062016-04-28 21:24:06 -0400586 ret = _mv88e6xxx_stats_wait(ps);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000587 if (ret < 0)
588 return;
589
Andrew Lunn158bc062016-04-28 21:24:06 -0400590 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000591 if (ret < 0)
592 return;
593
594 _val = ret << 16;
595
Andrew Lunn158bc062016-04-28 21:24:06 -0400596 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000597 if (ret < 0)
598 return;
599
600 *val = _val | ret;
601}
602
Andrew Lunne413e7e2015-04-02 04:06:38 +0200603static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100604 { "in_good_octets", 8, 0x00, BANK0, },
605 { "in_bad_octets", 4, 0x02, BANK0, },
606 { "in_unicast", 4, 0x04, BANK0, },
607 { "in_broadcasts", 4, 0x06, BANK0, },
608 { "in_multicasts", 4, 0x07, BANK0, },
609 { "in_pause", 4, 0x16, BANK0, },
610 { "in_undersize", 4, 0x18, BANK0, },
611 { "in_fragments", 4, 0x19, BANK0, },
612 { "in_oversize", 4, 0x1a, BANK0, },
613 { "in_jabber", 4, 0x1b, BANK0, },
614 { "in_rx_error", 4, 0x1c, BANK0, },
615 { "in_fcs_error", 4, 0x1d, BANK0, },
616 { "out_octets", 8, 0x0e, BANK0, },
617 { "out_unicast", 4, 0x10, BANK0, },
618 { "out_broadcasts", 4, 0x13, BANK0, },
619 { "out_multicasts", 4, 0x12, BANK0, },
620 { "out_pause", 4, 0x15, BANK0, },
621 { "excessive", 4, 0x11, BANK0, },
622 { "collisions", 4, 0x1e, BANK0, },
623 { "deferred", 4, 0x05, BANK0, },
624 { "single", 4, 0x14, BANK0, },
625 { "multiple", 4, 0x17, BANK0, },
626 { "out_fcs_error", 4, 0x03, BANK0, },
627 { "late", 4, 0x1f, BANK0, },
628 { "hist_64bytes", 4, 0x08, BANK0, },
629 { "hist_65_127bytes", 4, 0x09, BANK0, },
630 { "hist_128_255bytes", 4, 0x0a, BANK0, },
631 { "hist_256_511bytes", 4, 0x0b, BANK0, },
632 { "hist_512_1023bytes", 4, 0x0c, BANK0, },
633 { "hist_1024_max_bytes", 4, 0x0d, BANK0, },
634 { "sw_in_discards", 4, 0x10, PORT, },
635 { "sw_in_filtered", 2, 0x12, PORT, },
636 { "sw_out_filtered", 2, 0x13, PORT, },
637 { "in_discards", 4, 0x00 | GLOBAL_STATS_OP_BANK_1, BANK1, },
638 { "in_filtered", 4, 0x01 | GLOBAL_STATS_OP_BANK_1, BANK1, },
639 { "in_accepted", 4, 0x02 | GLOBAL_STATS_OP_BANK_1, BANK1, },
640 { "in_bad_accepted", 4, 0x03 | GLOBAL_STATS_OP_BANK_1, BANK1, },
641 { "in_good_avb_class_a", 4, 0x04 | GLOBAL_STATS_OP_BANK_1, BANK1, },
642 { "in_good_avb_class_b", 4, 0x05 | GLOBAL_STATS_OP_BANK_1, BANK1, },
643 { "in_bad_avb_class_a", 4, 0x06 | GLOBAL_STATS_OP_BANK_1, BANK1, },
644 { "in_bad_avb_class_b", 4, 0x07 | GLOBAL_STATS_OP_BANK_1, BANK1, },
645 { "tcam_counter_0", 4, 0x08 | GLOBAL_STATS_OP_BANK_1, BANK1, },
646 { "tcam_counter_1", 4, 0x09 | GLOBAL_STATS_OP_BANK_1, BANK1, },
647 { "tcam_counter_2", 4, 0x0a | GLOBAL_STATS_OP_BANK_1, BANK1, },
648 { "tcam_counter_3", 4, 0x0b | GLOBAL_STATS_OP_BANK_1, BANK1, },
649 { "in_da_unknown", 4, 0x0e | GLOBAL_STATS_OP_BANK_1, BANK1, },
650 { "in_management", 4, 0x0f | GLOBAL_STATS_OP_BANK_1, BANK1, },
651 { "out_queue_0", 4, 0x10 | GLOBAL_STATS_OP_BANK_1, BANK1, },
652 { "out_queue_1", 4, 0x11 | GLOBAL_STATS_OP_BANK_1, BANK1, },
653 { "out_queue_2", 4, 0x12 | GLOBAL_STATS_OP_BANK_1, BANK1, },
654 { "out_queue_3", 4, 0x13 | GLOBAL_STATS_OP_BANK_1, BANK1, },
655 { "out_queue_4", 4, 0x14 | GLOBAL_STATS_OP_BANK_1, BANK1, },
656 { "out_queue_5", 4, 0x15 | GLOBAL_STATS_OP_BANK_1, BANK1, },
657 { "out_queue_6", 4, 0x16 | GLOBAL_STATS_OP_BANK_1, BANK1, },
658 { "out_queue_7", 4, 0x17 | GLOBAL_STATS_OP_BANK_1, BANK1, },
659 { "out_cut_through", 4, 0x18 | GLOBAL_STATS_OP_BANK_1, BANK1, },
660 { "out_octets_a", 4, 0x1a | GLOBAL_STATS_OP_BANK_1, BANK1, },
661 { "out_octets_b", 4, 0x1b | GLOBAL_STATS_OP_BANK_1, BANK1, },
662 { "out_management", 4, 0x1f | GLOBAL_STATS_OP_BANK_1, BANK1, },
Andrew Lunne413e7e2015-04-02 04:06:38 +0200663};
664
Andrew Lunn158bc062016-04-28 21:24:06 -0400665static bool mv88e6xxx_has_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100666 struct mv88e6xxx_hw_stat *stat)
Andrew Lunne413e7e2015-04-02 04:06:38 +0200667{
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100668 switch (stat->type) {
669 case BANK0:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200670 return true;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100671 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400672 return mv88e6xxx_6320_family(ps);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100673 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400674 return mv88e6xxx_6095_family(ps) ||
675 mv88e6xxx_6185_family(ps) ||
676 mv88e6xxx_6097_family(ps) ||
677 mv88e6xxx_6165_family(ps) ||
678 mv88e6xxx_6351_family(ps) ||
679 mv88e6xxx_6352_family(ps);
Andrew Lunne413e7e2015-04-02 04:06:38 +0200680 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100681 return false;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000682}
683
Andrew Lunn158bc062016-04-28 21:24:06 -0400684static uint64_t _mv88e6xxx_get_ethtool_stat(struct mv88e6xxx_priv_state *ps,
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100685 struct mv88e6xxx_hw_stat *s,
Andrew Lunn80c46272015-06-20 18:42:30 +0200686 int port)
687{
Andrew Lunn80c46272015-06-20 18:42:30 +0200688 u32 low;
689 u32 high = 0;
690 int ret;
691 u64 value;
692
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100693 switch (s->type) {
694 case PORT:
Andrew Lunn158bc062016-04-28 21:24:06 -0400695 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), s->reg);
Andrew Lunn80c46272015-06-20 18:42:30 +0200696 if (ret < 0)
697 return UINT64_MAX;
698
699 low = ret;
700 if (s->sizeof_stat == 4) {
Andrew Lunn158bc062016-04-28 21:24:06 -0400701 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port),
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100702 s->reg + 1);
Andrew Lunn80c46272015-06-20 18:42:30 +0200703 if (ret < 0)
704 return UINT64_MAX;
705 high = ret;
706 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100707 break;
708 case BANK0:
709 case BANK1:
Andrew Lunn158bc062016-04-28 21:24:06 -0400710 _mv88e6xxx_stats_read(ps, s->reg, &low);
Andrew Lunn80c46272015-06-20 18:42:30 +0200711 if (s->sizeof_stat == 8)
Andrew Lunn158bc062016-04-28 21:24:06 -0400712 _mv88e6xxx_stats_read(ps, s->reg + 1, &high);
Andrew Lunn80c46272015-06-20 18:42:30 +0200713 }
714 value = (((u64)high) << 16) | low;
715 return value;
716}
717
Vivien Didelotf81ec902016-05-09 13:22:58 -0400718static void mv88e6xxx_get_strings(struct dsa_switch *ds, int port,
719 uint8_t *data)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100720{
Andrew Lunn158bc062016-04-28 21:24:06 -0400721 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100722 struct mv88e6xxx_hw_stat *stat;
723 int i, j;
724
725 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
726 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400727 if (mv88e6xxx_has_stat(ps, stat)) {
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100728 memcpy(data + j * ETH_GSTRING_LEN, stat->string,
729 ETH_GSTRING_LEN);
730 j++;
731 }
732 }
733}
734
Vivien Didelotf81ec902016-05-09 13:22:58 -0400735static int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100736{
Andrew Lunn158bc062016-04-28 21:24:06 -0400737 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100738 struct mv88e6xxx_hw_stat *stat;
739 int i, j;
740
741 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
742 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400743 if (mv88e6xxx_has_stat(ps, stat))
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100744 j++;
745 }
746 return j;
747}
748
Vivien Didelotf81ec902016-05-09 13:22:58 -0400749static void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds, int port,
750 uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000751{
Florian Fainellia22adce2014-04-28 11:14:28 -0700752 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100753 struct mv88e6xxx_hw_stat *stat;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000754 int ret;
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100755 int i, j;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000756
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400757 mutex_lock(&ps->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000758
Andrew Lunn158bc062016-04-28 21:24:06 -0400759 ret = _mv88e6xxx_stats_snapshot(ps, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000760 if (ret < 0) {
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400761 mutex_unlock(&ps->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000762 return;
763 }
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100764 for (i = 0, j = 0; i < ARRAY_SIZE(mv88e6xxx_hw_stats); i++) {
765 stat = &mv88e6xxx_hw_stats[i];
Andrew Lunn158bc062016-04-28 21:24:06 -0400766 if (mv88e6xxx_has_stat(ps, stat)) {
767 data[j] = _mv88e6xxx_get_ethtool_stat(ps, stat, port);
Andrew Lunnf5e2ed02015-12-23 13:23:17 +0100768 j++;
769 }
770 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000771
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400772 mutex_unlock(&ps->reg_lock);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000773}
Ben Hutchings98e67302011-11-25 14:36:19 +0000774
Vivien Didelotf81ec902016-05-09 13:22:58 -0400775static int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700776{
777 return 32 * sizeof(u16);
778}
779
Vivien Didelotf81ec902016-05-09 13:22:58 -0400780static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
781 struct ethtool_regs *regs, void *_p)
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700782{
Andrew Lunn158bc062016-04-28 21:24:06 -0400783 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700784 u16 *p = _p;
785 int i;
786
787 regs->version = 0;
788
789 memset(p, 0xff, 32 * sizeof(u16));
790
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400791 mutex_lock(&ps->reg_lock);
Vivien Didelot23062512016-05-09 13:22:45 -0400792
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700793 for (i = 0; i < 32; i++) {
794 int ret;
795
Vivien Didelot23062512016-05-09 13:22:45 -0400796 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), i);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700797 if (ret >= 0)
798 p[i] = ret;
799 }
Vivien Didelot23062512016-05-09 13:22:45 -0400800
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400801 mutex_unlock(&ps->reg_lock);
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700802}
803
Andrew Lunn158bc062016-04-28 21:24:06 -0400804static int _mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg, int offset,
Andrew Lunn3898c142015-05-06 01:09:53 +0200805 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700806{
807 unsigned long timeout = jiffies + HZ / 10;
808
809 while (time_before(jiffies, timeout)) {
810 int ret;
811
Andrew Lunn158bc062016-04-28 21:24:06 -0400812 ret = _mv88e6xxx_reg_read(ps, reg, offset);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700813 if (ret < 0)
814 return ret;
815 if (!(ret & mask))
816 return 0;
817
818 usleep_range(1000, 2000);
819 }
820 return -ETIMEDOUT;
821}
822
Andrew Lunn158bc062016-04-28 21:24:06 -0400823static int mv88e6xxx_wait(struct mv88e6xxx_priv_state *ps, int reg,
824 int offset, u16 mask)
Andrew Lunn3898c142015-05-06 01:09:53 +0200825{
Andrew Lunn3898c142015-05-06 01:09:53 +0200826 int ret;
827
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400828 mutex_lock(&ps->reg_lock);
Andrew Lunn158bc062016-04-28 21:24:06 -0400829 ret = _mv88e6xxx_wait(ps, reg, offset, mask);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -0400830 mutex_unlock(&ps->reg_lock);
Andrew Lunn3898c142015-05-06 01:09:53 +0200831
832 return ret;
833}
834
Andrew Lunn03a4a542016-06-04 21:17:05 +0200835static int mv88e6xxx_mdio_wait(struct mv88e6xxx_priv_state *ps)
Andrew Lunn3898c142015-05-06 01:09:53 +0200836{
Andrew Lunn158bc062016-04-28 21:24:06 -0400837 return _mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200838 GLOBAL2_SMI_OP_BUSY);
839}
840
Vivien Didelotd24645b2016-05-09 13:22:41 -0400841static int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200842{
Andrew Lunn158bc062016-04-28 21:24:06 -0400843 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
844
845 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200846 GLOBAL2_EEPROM_OP_LOAD);
847}
848
Vivien Didelotd24645b2016-05-09 13:22:41 -0400849static int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
Andrew Lunn3898c142015-05-06 01:09:53 +0200850{
Andrew Lunn158bc062016-04-28 21:24:06 -0400851 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
852
853 return mv88e6xxx_wait(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +0200854 GLOBAL2_EEPROM_OP_BUSY);
855}
856
Vivien Didelotd24645b2016-05-09 13:22:41 -0400857static int mv88e6xxx_read_eeprom_word(struct dsa_switch *ds, int addr)
858{
859 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
860 int ret;
861
862 mutex_lock(&ps->eeprom_mutex);
863
864 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
865 GLOBAL2_EEPROM_OP_READ |
866 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
867 if (ret < 0)
868 goto error;
869
870 ret = mv88e6xxx_eeprom_busy_wait(ds);
871 if (ret < 0)
872 goto error;
873
874 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA);
875error:
876 mutex_unlock(&ps->eeprom_mutex);
877 return ret;
878}
879
Andrew Lunnf8cd8752016-05-10 23:27:25 +0200880static int mv88e6xxx_get_eeprom_len(struct dsa_switch *ds)
881{
882 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
883
884 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
885 return ps->eeprom_len;
886
887 return 0;
888}
889
Vivien Didelotf81ec902016-05-09 13:22:58 -0400890static int mv88e6xxx_get_eeprom(struct dsa_switch *ds,
891 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400892{
893 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
894 int offset;
895 int len;
896 int ret;
897
898 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
899 return -EOPNOTSUPP;
900
901 offset = eeprom->offset;
902 len = eeprom->len;
903 eeprom->len = 0;
904
905 eeprom->magic = 0xc3ec4951;
906
907 ret = mv88e6xxx_eeprom_load_wait(ds);
908 if (ret < 0)
909 return ret;
910
911 if (offset & 1) {
912 int word;
913
914 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
915 if (word < 0)
916 return word;
917
918 *data++ = (word >> 8) & 0xff;
919
920 offset++;
921 len--;
922 eeprom->len++;
923 }
924
925 while (len >= 2) {
926 int word;
927
928 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
929 if (word < 0)
930 return word;
931
932 *data++ = word & 0xff;
933 *data++ = (word >> 8) & 0xff;
934
935 offset += 2;
936 len -= 2;
937 eeprom->len += 2;
938 }
939
940 if (len) {
941 int word;
942
943 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
944 if (word < 0)
945 return word;
946
947 *data++ = word & 0xff;
948
949 offset++;
950 len--;
951 eeprom->len++;
952 }
953
954 return 0;
955}
956
957static int mv88e6xxx_eeprom_is_readonly(struct dsa_switch *ds)
958{
959 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
960 int ret;
961
962 ret = mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP);
963 if (ret < 0)
964 return ret;
965
966 if (!(ret & GLOBAL2_EEPROM_OP_WRITE_EN))
967 return -EROFS;
968
969 return 0;
970}
971
972static int mv88e6xxx_write_eeprom_word(struct dsa_switch *ds, int addr,
973 u16 data)
974{
975 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
976 int ret;
977
978 mutex_lock(&ps->eeprom_mutex);
979
980 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_DATA, data);
981 if (ret < 0)
982 goto error;
983
984 ret = mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
985 GLOBAL2_EEPROM_OP_WRITE |
986 (addr & GLOBAL2_EEPROM_OP_ADDR_MASK));
987 if (ret < 0)
988 goto error;
989
990 ret = mv88e6xxx_eeprom_busy_wait(ds);
991error:
992 mutex_unlock(&ps->eeprom_mutex);
993 return ret;
994}
995
Vivien Didelotf81ec902016-05-09 13:22:58 -0400996static int mv88e6xxx_set_eeprom(struct dsa_switch *ds,
997 struct ethtool_eeprom *eeprom, u8 *data)
Vivien Didelotd24645b2016-05-09 13:22:41 -0400998{
999 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1000 int offset;
1001 int ret;
1002 int len;
1003
1004 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
1005 return -EOPNOTSUPP;
1006
1007 if (eeprom->magic != 0xc3ec4951)
1008 return -EINVAL;
1009
1010 ret = mv88e6xxx_eeprom_is_readonly(ds);
1011 if (ret)
1012 return ret;
1013
1014 offset = eeprom->offset;
1015 len = eeprom->len;
1016 eeprom->len = 0;
1017
1018 ret = mv88e6xxx_eeprom_load_wait(ds);
1019 if (ret < 0)
1020 return ret;
1021
1022 if (offset & 1) {
1023 int word;
1024
1025 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1026 if (word < 0)
1027 return word;
1028
1029 word = (*data++ << 8) | (word & 0xff);
1030
1031 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1032 if (ret < 0)
1033 return ret;
1034
1035 offset++;
1036 len--;
1037 eeprom->len++;
1038 }
1039
1040 while (len >= 2) {
1041 int word;
1042
1043 word = *data++;
1044 word |= *data++ << 8;
1045
1046 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1047 if (ret < 0)
1048 return ret;
1049
1050 offset += 2;
1051 len -= 2;
1052 eeprom->len += 2;
1053 }
1054
1055 if (len) {
1056 int word;
1057
1058 word = mv88e6xxx_read_eeprom_word(ds, offset >> 1);
1059 if (word < 0)
1060 return word;
1061
1062 word = (word & 0xff00) | *data++;
1063
1064 ret = mv88e6xxx_write_eeprom_word(ds, offset >> 1, word);
1065 if (ret < 0)
1066 return ret;
1067
1068 offset++;
1069 len--;
1070 eeprom->len++;
1071 }
1072
1073 return 0;
1074}
1075
Andrew Lunn158bc062016-04-28 21:24:06 -04001076static int _mv88e6xxx_atu_wait(struct mv88e6xxx_priv_state *ps)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001077{
Andrew Lunn158bc062016-04-28 21:24:06 -04001078 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_ATU_OP,
Andrew Lunncca8b132015-04-02 04:06:39 +02001079 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001080}
1081
Andrew Lunn03a4a542016-06-04 21:17:05 +02001082static int mv88e6xxx_mdio_read_indirect(struct mv88e6xxx_priv_state *ps,
Andrew Lunn158bc062016-04-28 21:24:06 -04001083 int addr, int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +01001084{
1085 int ret;
1086
Andrew Lunn158bc062016-04-28 21:24:06 -04001087 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001088 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
1089 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +01001090 if (ret < 0)
1091 return ret;
1092
Andrew Lunn03a4a542016-06-04 21:17:05 +02001093 ret = mv88e6xxx_mdio_wait(ps);
Andrew Lunn3898c142015-05-06 01:09:53 +02001094 if (ret < 0)
1095 return ret;
1096
Andrew Lunn158bc062016-04-28 21:24:06 -04001097 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA);
1098
1099 return ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001100}
1101
Andrew Lunn03a4a542016-06-04 21:17:05 +02001102static int mv88e6xxx_mdio_write_indirect(struct mv88e6xxx_priv_state *ps,
Andrew Lunn158bc062016-04-28 21:24:06 -04001103 int addr, int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +01001104{
Andrew Lunn3898c142015-05-06 01:09:53 +02001105 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +01001106
Andrew Lunn158bc062016-04-28 21:24:06 -04001107 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02001108 if (ret < 0)
1109 return ret;
1110
Andrew Lunn158bc062016-04-28 21:24:06 -04001111 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SMI_OP,
Andrew Lunn3898c142015-05-06 01:09:53 +02001112 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
1113 regnum);
1114
Andrew Lunn03a4a542016-06-04 21:17:05 +02001115 return mv88e6xxx_mdio_wait(ps);
Andrew Lunnf3044682015-02-14 19:17:50 +01001116}
1117
Vivien Didelotf81ec902016-05-09 13:22:58 -04001118static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port,
1119 struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001120{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001121 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001122 int reg;
1123
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001124 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1125 return -EOPNOTSUPP;
1126
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001127 mutex_lock(&ps->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001128
Andrew Lunn03a4a542016-06-04 21:17:05 +02001129 reg = mv88e6xxx_mdio_read_indirect(ps, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001130 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001131 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001132
1133 e->eee_enabled = !!(reg & 0x0200);
1134 e->tx_lpi_enabled = !!(reg & 0x0100);
1135
Andrew Lunn158bc062016-04-28 21:24:06 -04001136 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001137 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +02001138 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001139
Andrew Lunncca8b132015-04-02 04:06:39 +02001140 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001141 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001142
Andrew Lunn2f40c692015-04-02 04:06:37 +02001143out:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001144 mutex_unlock(&ps->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001145 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001146}
1147
Vivien Didelotf81ec902016-05-09 13:22:58 -04001148static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
1149 struct phy_device *phydev, struct ethtool_eee *e)
Guenter Roeck11b3b452015-03-06 22:23:51 -08001150{
Andrew Lunn2f40c692015-04-02 04:06:37 +02001151 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1152 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001153 int ret;
1154
Vivien Didelotaadbdb82016-05-09 13:22:44 -04001155 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEE))
1156 return -EOPNOTSUPP;
1157
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001158 mutex_lock(&ps->reg_lock);
Guenter Roeck11b3b452015-03-06 22:23:51 -08001159
Andrew Lunn03a4a542016-06-04 21:17:05 +02001160 ret = mv88e6xxx_mdio_read_indirect(ps, port, 16);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001161 if (ret < 0)
1162 goto out;
1163
1164 reg = ret & ~0x0300;
1165 if (e->eee_enabled)
1166 reg |= 0x0200;
1167 if (e->tx_lpi_enabled)
1168 reg |= 0x0100;
1169
Andrew Lunn03a4a542016-06-04 21:17:05 +02001170 ret = mv88e6xxx_mdio_write_indirect(ps, port, 16, reg);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001171out:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001172 mutex_unlock(&ps->reg_lock);
Andrew Lunn2f40c692015-04-02 04:06:37 +02001173
1174 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -08001175}
1176
Andrew Lunn158bc062016-04-28 21:24:06 -04001177static int _mv88e6xxx_atu_cmd(struct mv88e6xxx_priv_state *ps, u16 fid, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001178{
1179 int ret;
1180
Andrew Lunn158bc062016-04-28 21:24:06 -04001181 if (mv88e6xxx_has_fid_reg(ps)) {
1182 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_FID, fid);
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001183 if (ret < 0)
1184 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001185 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001186 /* ATU DBNum[7:4] are located in ATU Control 15:12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001187 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL);
Vivien Didelot11ea8092016-03-31 16:53:44 -04001188 if (ret < 0)
1189 return ret;
1190
Andrew Lunn158bc062016-04-28 21:24:06 -04001191 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001192 (ret & 0xfff) |
1193 ((fid << 8) & 0xf000));
1194 if (ret < 0)
1195 return ret;
1196
1197 /* ATU DBNum[3:0] are located in ATU Operation 3:0 */
1198 cmd |= fid & 0xf;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001199 }
1200
Andrew Lunn158bc062016-04-28 21:24:06 -04001201 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001202 if (ret < 0)
1203 return ret;
1204
Andrew Lunn158bc062016-04-28 21:24:06 -04001205 return _mv88e6xxx_atu_wait(ps);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001206}
1207
Andrew Lunn158bc062016-04-28 21:24:06 -04001208static int _mv88e6xxx_atu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot37705b72015-09-04 14:34:11 -04001209 struct mv88e6xxx_atu_entry *entry)
1210{
1211 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
1212
1213 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1214 unsigned int mask, shift;
1215
1216 if (entry->trunk) {
1217 data |= GLOBAL_ATU_DATA_TRUNK;
1218 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1219 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1220 } else {
1221 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1222 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1223 }
1224
1225 data |= (entry->portv_trunkid << shift) & mask;
1226 }
1227
Andrew Lunn158bc062016-04-28 21:24:06 -04001228 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_DATA, data);
Vivien Didelot37705b72015-09-04 14:34:11 -04001229}
1230
Andrew Lunn158bc062016-04-28 21:24:06 -04001231static int _mv88e6xxx_atu_flush_move(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001232 struct mv88e6xxx_atu_entry *entry,
1233 bool static_too)
1234{
1235 int op;
1236 int err;
1237
Andrew Lunn158bc062016-04-28 21:24:06 -04001238 err = _mv88e6xxx_atu_wait(ps);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001239 if (err)
1240 return err;
1241
Andrew Lunn158bc062016-04-28 21:24:06 -04001242 err = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001243 if (err)
1244 return err;
1245
1246 if (entry->fid) {
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001247 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
1248 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
1249 } else {
1250 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
1251 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1252 }
1253
Andrew Lunn158bc062016-04-28 21:24:06 -04001254 return _mv88e6xxx_atu_cmd(ps, entry->fid, op);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001255}
1256
Andrew Lunn158bc062016-04-28 21:24:06 -04001257static int _mv88e6xxx_atu_flush(struct mv88e6xxx_priv_state *ps,
1258 u16 fid, bool static_too)
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001259{
1260 struct mv88e6xxx_atu_entry entry = {
1261 .fid = fid,
1262 .state = 0, /* EntryState bits must be 0 */
1263 };
1264
Andrew Lunn158bc062016-04-28 21:24:06 -04001265 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot7fb5e752015-09-04 14:34:12 -04001266}
1267
Andrew Lunn158bc062016-04-28 21:24:06 -04001268static int _mv88e6xxx_atu_move(struct mv88e6xxx_priv_state *ps, u16 fid,
1269 int from_port, int to_port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001270{
1271 struct mv88e6xxx_atu_entry entry = {
1272 .trunk = false,
1273 .fid = fid,
1274 };
1275
1276 /* EntryState bits must be 0xF */
1277 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1278
1279 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1280 entry.portv_trunkid = (to_port & 0x0f) << 4;
1281 entry.portv_trunkid |= from_port & 0x0f;
1282
Andrew Lunn158bc062016-04-28 21:24:06 -04001283 return _mv88e6xxx_atu_flush_move(ps, &entry, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001284}
1285
Andrew Lunn158bc062016-04-28 21:24:06 -04001286static int _mv88e6xxx_atu_remove(struct mv88e6xxx_priv_state *ps, u16 fid,
1287 int port, bool static_too)
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001288{
1289 /* Destination port 0xF means remove the entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001290 return _mv88e6xxx_atu_move(ps, fid, port, 0x0f, static_too);
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001291}
1292
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001293static const char * const mv88e6xxx_port_state_names[] = {
1294 [PORT_CONTROL_STATE_DISABLED] = "Disabled",
1295 [PORT_CONTROL_STATE_BLOCKING] = "Blocking/Listening",
1296 [PORT_CONTROL_STATE_LEARNING] = "Learning",
1297 [PORT_CONTROL_STATE_FORWARDING] = "Forwarding",
1298};
1299
Andrew Lunn158bc062016-04-28 21:24:06 -04001300static int _mv88e6xxx_port_state(struct mv88e6xxx_priv_state *ps, int port,
1301 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001302{
Andrew Lunn158bc062016-04-28 21:24:06 -04001303 struct dsa_switch *ds = ps->ds;
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001304 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001305 u8 oldstate;
1306
Andrew Lunn158bc062016-04-28 21:24:06 -04001307 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001308 if (reg < 0)
1309 return reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001310
Andrew Lunncca8b132015-04-02 04:06:39 +02001311 oldstate = reg & PORT_CONTROL_STATE_MASK;
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001312
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001313 if (oldstate != state) {
1314 /* Flush forwarding database if we're moving a port
1315 * from Learning or Forwarding state to Disabled or
1316 * Blocking or Listening state.
1317 */
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001318 if ((oldstate == PORT_CONTROL_STATE_LEARNING ||
Vivien Didelot57d32312016-06-20 13:13:58 -04001319 oldstate == PORT_CONTROL_STATE_FORWARDING) &&
1320 (state == PORT_CONTROL_STATE_DISABLED ||
1321 state == PORT_CONTROL_STATE_BLOCKING)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001322 ret = _mv88e6xxx_atu_remove(ps, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001323 if (ret)
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001324 return ret;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001325 }
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001326
Andrew Lunncca8b132015-04-02 04:06:39 +02001327 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
Andrew Lunn158bc062016-04-28 21:24:06 -04001328 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL,
Andrew Lunncca8b132015-04-02 04:06:39 +02001329 reg);
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001330 if (ret)
1331 return ret;
1332
Andrew Lunnc8b09802016-06-04 21:16:57 +02001333 netdev_dbg(ds->ports[port].netdev, "PortState %s (was %s)\n",
Vivien Didelot2d9deae2016-03-07 18:24:17 -05001334 mv88e6xxx_port_state_names[state],
1335 mv88e6xxx_port_state_names[oldstate]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001336 }
1337
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001338 return ret;
1339}
1340
Andrew Lunn158bc062016-04-28 21:24:06 -04001341static int _mv88e6xxx_port_based_vlan_map(struct mv88e6xxx_priv_state *ps,
1342 int port)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001343{
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001344 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot009a2b92016-04-17 13:24:01 -04001345 const u16 mask = (1 << ps->info->num_ports) - 1;
Andrew Lunn158bc062016-04-28 21:24:06 -04001346 struct dsa_switch *ds = ps->ds;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001347 u16 output_ports = 0;
Vivien Didelotede80982015-10-11 18:08:35 -04001348 int reg;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001349 int i;
1350
1351 /* allow CPU port or DSA link(s) to send frames to every port */
1352 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
1353 output_ports = mask;
1354 } else {
Vivien Didelot009a2b92016-04-17 13:24:01 -04001355 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05001356 /* allow sending frames to every group member */
1357 if (bridge && ps->ports[i].bridge_dev == bridge)
1358 output_ports |= BIT(i);
1359
1360 /* allow sending frames to CPU port and DSA link(s) */
1361 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
1362 output_ports |= BIT(i);
1363 }
1364 }
1365
1366 /* prevent frames from going back out of the port they came in on */
1367 output_ports &= ~BIT(port);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001368
Andrew Lunn158bc062016-04-28 21:24:06 -04001369 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelotede80982015-10-11 18:08:35 -04001370 if (reg < 0)
1371 return reg;
1372
1373 reg &= ~mask;
1374 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001375
Andrew Lunn158bc062016-04-28 21:24:06 -04001376 return _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001377}
1378
Vivien Didelotf81ec902016-05-09 13:22:58 -04001379static void mv88e6xxx_port_stp_state_set(struct dsa_switch *ds, int port,
1380 u8 state)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001381{
1382 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1383 int stp_state;
Vivien Didelot553eb542016-05-13 20:38:23 -04001384 int err;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001385
Vivien Didelot936f2342016-05-09 13:22:46 -04001386 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_PORTSTATE))
1387 return;
1388
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001389 switch (state) {
1390 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001391 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001392 break;
1393 case BR_STATE_BLOCKING:
1394 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001395 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001396 break;
1397 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001398 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001399 break;
1400 case BR_STATE_FORWARDING:
1401 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001402 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001403 break;
1404 }
1405
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001406 mutex_lock(&ps->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001407 err = _mv88e6xxx_port_state(ps, port, stp_state);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001408 mutex_unlock(&ps->reg_lock);
Vivien Didelot553eb542016-05-13 20:38:23 -04001409
1410 if (err)
Andrew Lunnc8b09802016-06-04 21:16:57 +02001411 netdev_err(ds->ports[port].netdev,
1412 "failed to update state to %s\n",
Vivien Didelot553eb542016-05-13 20:38:23 -04001413 mv88e6xxx_port_state_names[stp_state]);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001414}
1415
Andrew Lunn158bc062016-04-28 21:24:06 -04001416static int _mv88e6xxx_port_pvid(struct mv88e6xxx_priv_state *ps, int port,
1417 u16 *new, u16 *old)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001418{
Andrew Lunn158bc062016-04-28 21:24:06 -04001419 struct dsa_switch *ds = ps->ds;
Vivien Didelot5da96032016-03-07 18:24:39 -05001420 u16 pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001421 int ret;
1422
Andrew Lunn158bc062016-04-28 21:24:06 -04001423 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_DEFAULT_VLAN);
Vivien Didelot76e398a2015-11-01 12:33:55 -05001424 if (ret < 0)
1425 return ret;
1426
Vivien Didelot5da96032016-03-07 18:24:39 -05001427 pvid = ret & PORT_DEFAULT_VLAN_MASK;
1428
1429 if (new) {
1430 ret &= ~PORT_DEFAULT_VLAN_MASK;
1431 ret |= *new & PORT_DEFAULT_VLAN_MASK;
1432
Andrew Lunn158bc062016-04-28 21:24:06 -04001433 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Vivien Didelot5da96032016-03-07 18:24:39 -05001434 PORT_DEFAULT_VLAN, ret);
1435 if (ret < 0)
1436 return ret;
1437
Andrew Lunnc8b09802016-06-04 21:16:57 +02001438 netdev_dbg(ds->ports[port].netdev,
1439 "DefaultVID %d (was %d)\n", *new, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001440 }
1441
1442 if (old)
1443 *old = pvid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05001444
1445 return 0;
1446}
1447
Andrew Lunn158bc062016-04-28 21:24:06 -04001448static int _mv88e6xxx_port_pvid_get(struct mv88e6xxx_priv_state *ps,
1449 int port, u16 *pvid)
Vivien Didelot5da96032016-03-07 18:24:39 -05001450{
Andrew Lunn158bc062016-04-28 21:24:06 -04001451 return _mv88e6xxx_port_pvid(ps, port, NULL, pvid);
Vivien Didelot5da96032016-03-07 18:24:39 -05001452}
1453
Andrew Lunn158bc062016-04-28 21:24:06 -04001454static int _mv88e6xxx_port_pvid_set(struct mv88e6xxx_priv_state *ps,
1455 int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001456{
Andrew Lunn158bc062016-04-28 21:24:06 -04001457 return _mv88e6xxx_port_pvid(ps, port, &pvid, NULL);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001458}
1459
Andrew Lunn158bc062016-04-28 21:24:06 -04001460static int _mv88e6xxx_vtu_wait(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001461{
Andrew Lunn158bc062016-04-28 21:24:06 -04001462 return _mv88e6xxx_wait(ps, REG_GLOBAL, GLOBAL_VTU_OP,
Vivien Didelot6b17e862015-08-13 12:52:18 -04001463 GLOBAL_VTU_OP_BUSY);
1464}
1465
Andrew Lunn158bc062016-04-28 21:24:06 -04001466static int _mv88e6xxx_vtu_cmd(struct mv88e6xxx_priv_state *ps, u16 op)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001467{
1468 int ret;
1469
Andrew Lunn158bc062016-04-28 21:24:06 -04001470 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_OP, op);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001471 if (ret < 0)
1472 return ret;
1473
Andrew Lunn158bc062016-04-28 21:24:06 -04001474 return _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001475}
1476
Andrew Lunn158bc062016-04-28 21:24:06 -04001477static int _mv88e6xxx_vtu_stu_flush(struct mv88e6xxx_priv_state *ps)
Vivien Didelot6b17e862015-08-13 12:52:18 -04001478{
1479 int ret;
1480
Andrew Lunn158bc062016-04-28 21:24:06 -04001481 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001482 if (ret < 0)
1483 return ret;
1484
Andrew Lunn158bc062016-04-28 21:24:06 -04001485 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_FLUSH_ALL);
Vivien Didelot6b17e862015-08-13 12:52:18 -04001486}
1487
Andrew Lunn158bc062016-04-28 21:24:06 -04001488static int _mv88e6xxx_vtu_stu_data_read(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001489 struct mv88e6xxx_vtu_stu_entry *entry,
1490 unsigned int nibble_offset)
1491{
Vivien Didelotb8fee952015-08-13 12:52:19 -04001492 u16 regs[3];
1493 int i;
1494 int ret;
1495
1496 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001497 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001498 GLOBAL_VTU_DATA_0_3 + i);
1499 if (ret < 0)
1500 return ret;
1501
1502 regs[i] = ret;
1503 }
1504
Vivien Didelot009a2b92016-04-17 13:24:01 -04001505 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb8fee952015-08-13 12:52:19 -04001506 unsigned int shift = (i % 4) * 4 + nibble_offset;
1507 u16 reg = regs[i / 4];
1508
1509 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1510 }
1511
1512 return 0;
1513}
1514
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001515static int mv88e6xxx_vtu_data_read(struct mv88e6xxx_priv_state *ps,
1516 struct mv88e6xxx_vtu_stu_entry *entry)
1517{
1518 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 0);
1519}
1520
1521static int mv88e6xxx_stu_data_read(struct mv88e6xxx_priv_state *ps,
1522 struct mv88e6xxx_vtu_stu_entry *entry)
1523{
1524 return _mv88e6xxx_vtu_stu_data_read(ps, entry, 2);
1525}
1526
Andrew Lunn158bc062016-04-28 21:24:06 -04001527static int _mv88e6xxx_vtu_stu_data_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001528 struct mv88e6xxx_vtu_stu_entry *entry,
1529 unsigned int nibble_offset)
1530{
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001531 u16 regs[3] = { 0 };
1532 int i;
1533 int ret;
1534
Vivien Didelot009a2b92016-04-17 13:24:01 -04001535 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001536 unsigned int shift = (i % 4) * 4 + nibble_offset;
1537 u8 data = entry->data[i];
1538
1539 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1540 }
1541
1542 for (i = 0; i < 3; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001543 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001544 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1545 if (ret < 0)
1546 return ret;
1547 }
1548
1549 return 0;
1550}
1551
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001552static int mv88e6xxx_vtu_data_write(struct mv88e6xxx_priv_state *ps,
1553 struct mv88e6xxx_vtu_stu_entry *entry)
1554{
1555 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 0);
1556}
1557
1558static int mv88e6xxx_stu_data_write(struct mv88e6xxx_priv_state *ps,
1559 struct mv88e6xxx_vtu_stu_entry *entry)
1560{
1561 return _mv88e6xxx_vtu_stu_data_write(ps, entry, 2);
1562}
1563
Andrew Lunn158bc062016-04-28 21:24:06 -04001564static int _mv88e6xxx_vtu_vid_write(struct mv88e6xxx_priv_state *ps, u16 vid)
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001565{
Andrew Lunn158bc062016-04-28 21:24:06 -04001566 return _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID,
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001567 vid & GLOBAL_VTU_VID_MASK);
1568}
1569
Andrew Lunn158bc062016-04-28 21:24:06 -04001570static int _mv88e6xxx_vtu_getnext(struct mv88e6xxx_priv_state *ps,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001571 struct mv88e6xxx_vtu_stu_entry *entry)
1572{
1573 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1574 int ret;
1575
Andrew Lunn158bc062016-04-28 21:24:06 -04001576 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001577 if (ret < 0)
1578 return ret;
1579
Andrew Lunn158bc062016-04-28 21:24:06 -04001580 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_VTU_GET_NEXT);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001581 if (ret < 0)
1582 return ret;
1583
Andrew Lunn158bc062016-04-28 21:24:06 -04001584 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001585 if (ret < 0)
1586 return ret;
1587
1588 next.vid = ret & GLOBAL_VTU_VID_MASK;
1589 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1590
1591 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001592 ret = mv88e6xxx_vtu_data_read(ps, &next);
Vivien Didelotb8fee952015-08-13 12:52:19 -04001593 if (ret < 0)
1594 return ret;
1595
Andrew Lunn158bc062016-04-28 21:24:06 -04001596 if (mv88e6xxx_has_fid_reg(ps)) {
1597 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001598 GLOBAL_VTU_FID);
1599 if (ret < 0)
1600 return ret;
1601
1602 next.fid = ret & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001603 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001604 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1605 * VTU DBNum[3:0] are located in VTU Operation 3:0
1606 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001607 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelot11ea8092016-03-31 16:53:44 -04001608 GLOBAL_VTU_OP);
1609 if (ret < 0)
1610 return ret;
1611
1612 next.fid = (ret & 0xf00) >> 4;
1613 next.fid |= ret & 0xf;
Vivien Didelot2e7bd5e2016-03-31 16:53:41 -04001614 }
Vivien Didelotb8fee952015-08-13 12:52:19 -04001615
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001616 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001617 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001618 GLOBAL_VTU_SID);
1619 if (ret < 0)
1620 return ret;
1621
1622 next.sid = ret & GLOBAL_VTU_SID_MASK;
1623 }
1624 }
1625
1626 *entry = next;
1627 return 0;
1628}
1629
Vivien Didelotf81ec902016-05-09 13:22:58 -04001630static int mv88e6xxx_port_vlan_dump(struct dsa_switch *ds, int port,
1631 struct switchdev_obj_port_vlan *vlan,
1632 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001633{
1634 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1635 struct mv88e6xxx_vtu_stu_entry next;
1636 u16 pvid;
1637 int err;
1638
Vivien Didelot54d77b52016-05-09 13:22:47 -04001639 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
1640 return -EOPNOTSUPP;
1641
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001642 mutex_lock(&ps->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001643
Andrew Lunn158bc062016-04-28 21:24:06 -04001644 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001645 if (err)
1646 goto unlock;
1647
Andrew Lunn158bc062016-04-28 21:24:06 -04001648 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001649 if (err)
1650 goto unlock;
1651
1652 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001653 err = _mv88e6xxx_vtu_getnext(ps, &next);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001654 if (err)
1655 break;
1656
1657 if (!next.valid)
1658 break;
1659
1660 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1661 continue;
1662
1663 /* reinit and dump this VLAN obj */
Vivien Didelot57d32312016-06-20 13:13:58 -04001664 vlan->vid_begin = next.vid;
1665 vlan->vid_end = next.vid;
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001666 vlan->flags = 0;
1667
1668 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1669 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
1670
1671 if (next.vid == pvid)
1672 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
1673
1674 err = cb(&vlan->obj);
1675 if (err)
1676 break;
1677 } while (next.vid < GLOBAL_VTU_VID_MASK);
1678
1679unlock:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04001680 mutex_unlock(&ps->reg_lock);
Vivien Didelotceff5ef2016-02-23 12:13:55 -05001681
1682 return err;
1683}
1684
Andrew Lunn158bc062016-04-28 21:24:06 -04001685static int _mv88e6xxx_vtu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001686 struct mv88e6xxx_vtu_stu_entry *entry)
1687{
Vivien Didelot11ea8092016-03-31 16:53:44 -04001688 u16 op = GLOBAL_VTU_OP_VTU_LOAD_PURGE;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001689 u16 reg = 0;
1690 int ret;
1691
Andrew Lunn158bc062016-04-28 21:24:06 -04001692 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001693 if (ret < 0)
1694 return ret;
1695
1696 if (!entry->valid)
1697 goto loadpurge;
1698
1699 /* Write port member tags */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001700 ret = mv88e6xxx_vtu_data_write(ps, entry);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001701 if (ret < 0)
1702 return ret;
1703
Vivien Didelotcb9b9022016-05-10 15:44:29 -04001704 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_STU)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001705 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001706 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001707 if (ret < 0)
1708 return ret;
Vivien Didelotb426e5f2016-03-31 16:53:42 -04001709 }
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001710
Andrew Lunn158bc062016-04-28 21:24:06 -04001711 if (mv88e6xxx_has_fid_reg(ps)) {
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001712 reg = entry->fid & GLOBAL_VTU_FID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001713 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_FID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001714 if (ret < 0)
1715 return ret;
Andrew Lunn158bc062016-04-28 21:24:06 -04001716 } else if (mv88e6xxx_num_databases(ps) == 256) {
Vivien Didelot11ea8092016-03-31 16:53:44 -04001717 /* VTU DBNum[7:4] are located in VTU Operation 11:8, and
1718 * VTU DBNum[3:0] are located in VTU Operation 3:0
1719 */
1720 op |= (entry->fid & 0xf0) << 8;
1721 op |= entry->fid & 0xf;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001722 }
1723
1724 reg = GLOBAL_VTU_VID_VALID;
1725loadpurge:
1726 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001727 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001728 if (ret < 0)
1729 return ret;
1730
Andrew Lunn158bc062016-04-28 21:24:06 -04001731 return _mv88e6xxx_vtu_cmd(ps, op);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001732}
1733
Andrew Lunn158bc062016-04-28 21:24:06 -04001734static int _mv88e6xxx_stu_getnext(struct mv88e6xxx_priv_state *ps, u8 sid,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001735 struct mv88e6xxx_vtu_stu_entry *entry)
1736{
1737 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1738 int ret;
1739
Andrew Lunn158bc062016-04-28 21:24:06 -04001740 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001741 if (ret < 0)
1742 return ret;
1743
Andrew Lunn158bc062016-04-28 21:24:06 -04001744 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001745 sid & GLOBAL_VTU_SID_MASK);
1746 if (ret < 0)
1747 return ret;
1748
Andrew Lunn158bc062016-04-28 21:24:06 -04001749 ret = _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_GET_NEXT);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001750 if (ret < 0)
1751 return ret;
1752
Andrew Lunn158bc062016-04-28 21:24:06 -04001753 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_SID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001754 if (ret < 0)
1755 return ret;
1756
1757 next.sid = ret & GLOBAL_VTU_SID_MASK;
1758
Andrew Lunn158bc062016-04-28 21:24:06 -04001759 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_VTU_VID);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001760 if (ret < 0)
1761 return ret;
1762
1763 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1764
1765 if (next.valid) {
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001766 ret = mv88e6xxx_stu_data_read(ps, &next);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001767 if (ret < 0)
1768 return ret;
1769 }
1770
1771 *entry = next;
1772 return 0;
1773}
1774
Andrew Lunn158bc062016-04-28 21:24:06 -04001775static int _mv88e6xxx_stu_loadpurge(struct mv88e6xxx_priv_state *ps,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001776 struct mv88e6xxx_vtu_stu_entry *entry)
1777{
1778 u16 reg = 0;
1779 int ret;
1780
Andrew Lunn158bc062016-04-28 21:24:06 -04001781 ret = _mv88e6xxx_vtu_wait(ps);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001782 if (ret < 0)
1783 return ret;
1784
1785 if (!entry->valid)
1786 goto loadpurge;
1787
1788 /* Write port states */
Vivien Didelot15d7d7d2016-05-10 15:44:28 -04001789 ret = mv88e6xxx_stu_data_write(ps, entry);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001790 if (ret < 0)
1791 return ret;
1792
1793 reg = GLOBAL_VTU_VID_VALID;
1794loadpurge:
Andrew Lunn158bc062016-04-28 21:24:06 -04001795 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_VID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001796 if (ret < 0)
1797 return ret;
1798
1799 reg = entry->sid & GLOBAL_VTU_SID_MASK;
Andrew Lunn158bc062016-04-28 21:24:06 -04001800 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_VTU_SID, reg);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001801 if (ret < 0)
1802 return ret;
1803
Andrew Lunn158bc062016-04-28 21:24:06 -04001804 return _mv88e6xxx_vtu_cmd(ps, GLOBAL_VTU_OP_STU_LOAD_PURGE);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001805}
1806
Andrew Lunn158bc062016-04-28 21:24:06 -04001807static int _mv88e6xxx_port_fid(struct mv88e6xxx_priv_state *ps, int port,
1808 u16 *new, u16 *old)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001809{
Andrew Lunn158bc062016-04-28 21:24:06 -04001810 struct dsa_switch *ds = ps->ds;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001811 u16 upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001812 u16 fid;
1813 int ret;
1814
Andrew Lunn158bc062016-04-28 21:24:06 -04001815 if (mv88e6xxx_num_databases(ps) == 4096)
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001816 upper_mask = 0xff;
Andrew Lunn158bc062016-04-28 21:24:06 -04001817 else if (mv88e6xxx_num_databases(ps) == 256)
Vivien Didelot11ea8092016-03-31 16:53:44 -04001818 upper_mask = 0xf;
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001819 else
1820 return -EOPNOTSUPP;
1821
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001822 /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001823 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_BASE_VLAN);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001824 if (ret < 0)
1825 return ret;
1826
1827 fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12;
1828
1829 if (new) {
1830 ret &= ~PORT_BASE_VLAN_FID_3_0_MASK;
1831 ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK;
1832
Andrew Lunn158bc062016-04-28 21:24:06 -04001833 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_BASE_VLAN,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001834 ret);
1835 if (ret < 0)
1836 return ret;
1837 }
1838
1839 /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */
Andrew Lunn158bc062016-04-28 21:24:06 -04001840 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_1);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001841 if (ret < 0)
1842 return ret;
1843
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001844 fid |= (ret & upper_mask) << 4;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001845
1846 if (new) {
Vivien Didelotf74df0b2016-03-31 16:53:43 -04001847 ret &= ~upper_mask;
1848 ret |= (*new >> 4) & upper_mask;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001849
Andrew Lunn158bc062016-04-28 21:24:06 -04001850 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1,
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001851 ret);
1852 if (ret < 0)
1853 return ret;
1854
Andrew Lunnc8b09802016-06-04 21:16:57 +02001855 netdev_dbg(ds->ports[port].netdev,
1856 "FID %d (was %d)\n", *new, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001857 }
1858
1859 if (old)
1860 *old = fid;
1861
1862 return 0;
1863}
1864
Andrew Lunn158bc062016-04-28 21:24:06 -04001865static int _mv88e6xxx_port_fid_get(struct mv88e6xxx_priv_state *ps,
1866 int port, u16 *fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001867{
Andrew Lunn158bc062016-04-28 21:24:06 -04001868 return _mv88e6xxx_port_fid(ps, port, NULL, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001869}
1870
Andrew Lunn158bc062016-04-28 21:24:06 -04001871static int _mv88e6xxx_port_fid_set(struct mv88e6xxx_priv_state *ps,
1872 int port, u16 fid)
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001873{
Andrew Lunn158bc062016-04-28 21:24:06 -04001874 return _mv88e6xxx_port_fid(ps, port, &fid, NULL);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001875}
1876
Andrew Lunn158bc062016-04-28 21:24:06 -04001877static int _mv88e6xxx_fid_new(struct mv88e6xxx_priv_state *ps, u16 *fid)
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001878{
1879 DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID);
1880 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001881 int i, err;
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001882
1883 bitmap_zero(fid_bitmap, MV88E6XXX_N_FID);
1884
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001885 /* Set every FID bit used by the (un)bridged ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001886 for (i = 0; i < ps->info->num_ports; ++i) {
Andrew Lunn158bc062016-04-28 21:24:06 -04001887 err = _mv88e6xxx_port_fid_get(ps, i, fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05001888 if (err)
1889 return err;
1890
1891 set_bit(*fid, fid_bitmap);
1892 }
1893
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001894 /* Set every FID bit used by the VLAN entries */
Andrew Lunn158bc062016-04-28 21:24:06 -04001895 err = _mv88e6xxx_vtu_vid_write(ps, GLOBAL_VTU_VID_MASK);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001896 if (err)
1897 return err;
1898
1899 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04001900 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001901 if (err)
1902 return err;
1903
1904 if (!vlan.valid)
1905 break;
1906
1907 set_bit(vlan.fid, fid_bitmap);
1908 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1909
1910 /* The reset value 0x000 is used to indicate that multiple address
1911 * databases are not needed. Return the next positive available.
1912 */
1913 *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1);
Andrew Lunn158bc062016-04-28 21:24:06 -04001914 if (unlikely(*fid >= mv88e6xxx_num_databases(ps)))
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001915 return -ENOSPC;
1916
1917 /* Clear the database */
Andrew Lunn158bc062016-04-28 21:24:06 -04001918 return _mv88e6xxx_atu_flush(ps, *fid, true);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001919}
1920
Andrew Lunn158bc062016-04-28 21:24:06 -04001921static int _mv88e6xxx_vtu_new(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001922 struct mv88e6xxx_vtu_stu_entry *entry)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001923{
Andrew Lunn158bc062016-04-28 21:24:06 -04001924 struct dsa_switch *ds = ps->ds;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001925 struct mv88e6xxx_vtu_stu_entry vlan = {
1926 .valid = true,
1927 .vid = vid,
1928 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001929 int i, err;
1930
Andrew Lunn158bc062016-04-28 21:24:06 -04001931 err = _mv88e6xxx_fid_new(ps, &vlan.fid);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05001932 if (err)
1933 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001934
Vivien Didelot3d131f02015-11-03 10:52:52 -05001935 /* exclude all ports except the CPU and DSA ports */
Vivien Didelot009a2b92016-04-17 13:24:01 -04001936 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot3d131f02015-11-03 10:52:52 -05001937 vlan.data[i] = dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)
1938 ? GLOBAL_VTU_DATA_MEMBER_TAG_UNMODIFIED
1939 : GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001940
Andrew Lunn158bc062016-04-28 21:24:06 -04001941 if (mv88e6xxx_6097_family(ps) || mv88e6xxx_6165_family(ps) ||
1942 mv88e6xxx_6351_family(ps) || mv88e6xxx_6352_family(ps)) {
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001943 struct mv88e6xxx_vtu_stu_entry vstp;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001944
1945 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1946 * implemented, only one STU entry is needed to cover all VTU
1947 * entries. Thus, validate the SID 0.
1948 */
1949 vlan.sid = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04001950 err = _mv88e6xxx_stu_getnext(ps, GLOBAL_VTU_SID_MASK, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001951 if (err)
1952 return err;
1953
1954 if (vstp.sid != vlan.sid || !vstp.valid) {
1955 memset(&vstp, 0, sizeof(vstp));
1956 vstp.valid = true;
1957 vstp.sid = vlan.sid;
1958
Andrew Lunn158bc062016-04-28 21:24:06 -04001959 err = _mv88e6xxx_stu_loadpurge(ps, &vstp);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001960 if (err)
1961 return err;
1962 }
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001963 }
1964
1965 *entry = vlan;
1966 return 0;
1967}
1968
Andrew Lunn158bc062016-04-28 21:24:06 -04001969static int _mv88e6xxx_vtu_get(struct mv88e6xxx_priv_state *ps, u16 vid,
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001970 struct mv88e6xxx_vtu_stu_entry *entry, bool creat)
1971{
1972 int err;
1973
1974 if (!vid)
1975 return -EINVAL;
1976
Andrew Lunn158bc062016-04-28 21:24:06 -04001977 err = _mv88e6xxx_vtu_vid_write(ps, vid - 1);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001978 if (err)
1979 return err;
1980
Andrew Lunn158bc062016-04-28 21:24:06 -04001981 err = _mv88e6xxx_vtu_getnext(ps, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001982 if (err)
1983 return err;
1984
1985 if (entry->vid != vid || !entry->valid) {
1986 if (!creat)
1987 return -EOPNOTSUPP;
1988 /* -ENOENT would've been more appropriate, but switchdev expects
1989 * -EOPNOTSUPP to inform bridge about an eventual software VLAN.
1990 */
1991
Andrew Lunn158bc062016-04-28 21:24:06 -04001992 err = _mv88e6xxx_vtu_new(ps, vid, entry);
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05001993 }
1994
1995 return err;
1996}
1997
Vivien Didelotda9c3592016-02-12 12:09:40 -05001998static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port,
1999 u16 vid_begin, u16 vid_end)
2000{
2001 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2002 struct mv88e6xxx_vtu_stu_entry vlan;
2003 int i, err;
2004
2005 if (!vid_begin)
2006 return -EOPNOTSUPP;
2007
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002008 mutex_lock(&ps->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002009
Andrew Lunn158bc062016-04-28 21:24:06 -04002010 err = _mv88e6xxx_vtu_vid_write(ps, vid_begin - 1);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002011 if (err)
2012 goto unlock;
2013
2014 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002015 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002016 if (err)
2017 goto unlock;
2018
2019 if (!vlan.valid)
2020 break;
2021
2022 if (vlan.vid > vid_end)
2023 break;
2024
Vivien Didelot009a2b92016-04-17 13:24:01 -04002025 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotda9c3592016-02-12 12:09:40 -05002026 if (dsa_is_dsa_port(ds, i) || dsa_is_cpu_port(ds, i))
2027 continue;
2028
2029 if (vlan.data[i] ==
2030 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
2031 continue;
2032
2033 if (ps->ports[i].bridge_dev ==
2034 ps->ports[port].bridge_dev)
2035 break; /* same bridge, check next VLAN */
2036
Andrew Lunnc8b09802016-06-04 21:16:57 +02002037 netdev_warn(ds->ports[port].netdev,
Vivien Didelotda9c3592016-02-12 12:09:40 -05002038 "hardware VLAN %d already used by %s\n",
2039 vlan.vid,
2040 netdev_name(ps->ports[i].bridge_dev));
2041 err = -EOPNOTSUPP;
2042 goto unlock;
2043 }
2044 } while (vlan.vid < vid_end);
2045
2046unlock:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002047 mutex_unlock(&ps->reg_lock);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002048
2049 return err;
2050}
2051
Vivien Didelot214cdb92016-02-26 13:16:08 -05002052static const char * const mv88e6xxx_port_8021q_mode_names[] = {
2053 [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled",
2054 [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback",
2055 [PORT_CONTROL_2_8021Q_CHECK] = "Check",
2056 [PORT_CONTROL_2_8021Q_SECURE] = "Secure",
2057};
2058
Vivien Didelotf81ec902016-05-09 13:22:58 -04002059static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
2060 bool vlan_filtering)
Vivien Didelot214cdb92016-02-26 13:16:08 -05002061{
2062 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2063 u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE :
2064 PORT_CONTROL_2_8021Q_DISABLED;
2065 int ret;
2066
Vivien Didelot54d77b52016-05-09 13:22:47 -04002067 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2068 return -EOPNOTSUPP;
2069
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002070 mutex_lock(&ps->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002071
Andrew Lunn158bc062016-04-28 21:24:06 -04002072 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_CONTROL_2);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002073 if (ret < 0)
2074 goto unlock;
2075
2076 old = ret & PORT_CONTROL_2_8021Q_MASK;
2077
Vivien Didelot5220ef12016-03-07 18:24:52 -05002078 if (new != old) {
2079 ret &= ~PORT_CONTROL_2_8021Q_MASK;
2080 ret |= new & PORT_CONTROL_2_8021Q_MASK;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002081
Andrew Lunn158bc062016-04-28 21:24:06 -04002082 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_2,
Vivien Didelot5220ef12016-03-07 18:24:52 -05002083 ret);
2084 if (ret < 0)
2085 goto unlock;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002086
Andrew Lunnc8b09802016-06-04 21:16:57 +02002087 netdev_dbg(ds->ports[port].netdev, "802.1Q Mode %s (was %s)\n",
Vivien Didelot5220ef12016-03-07 18:24:52 -05002088 mv88e6xxx_port_8021q_mode_names[new],
2089 mv88e6xxx_port_8021q_mode_names[old]);
2090 }
2091
2092 ret = 0;
Vivien Didelot214cdb92016-02-26 13:16:08 -05002093unlock:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002094 mutex_unlock(&ps->reg_lock);
Vivien Didelot214cdb92016-02-26 13:16:08 -05002095
2096 return ret;
2097}
2098
Vivien Didelot57d32312016-06-20 13:13:58 -04002099static int
2100mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
2101 const struct switchdev_obj_port_vlan *vlan,
2102 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002103{
Vivien Didelot54d77b52016-05-09 13:22:47 -04002104 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotda9c3592016-02-12 12:09:40 -05002105 int err;
2106
Vivien Didelot54d77b52016-05-09 13:22:47 -04002107 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2108 return -EOPNOTSUPP;
2109
Vivien Didelotda9c3592016-02-12 12:09:40 -05002110 /* If the requested port doesn't belong to the same bridge as the VLAN
2111 * members, do not support it (yet) and fallback to software VLAN.
2112 */
2113 err = mv88e6xxx_port_check_hw_vlan(ds, port, vlan->vid_begin,
2114 vlan->vid_end);
2115 if (err)
2116 return err;
2117
Vivien Didelot76e398a2015-11-01 12:33:55 -05002118 /* We don't need any dynamic resource from the kernel (yet),
2119 * so skip the prepare phase.
2120 */
2121 return 0;
2122}
2123
Andrew Lunn158bc062016-04-28 21:24:06 -04002124static int _mv88e6xxx_port_vlan_add(struct mv88e6xxx_priv_state *ps, int port,
2125 u16 vid, bool untagged)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002126{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002127 struct mv88e6xxx_vtu_stu_entry vlan;
2128 int err;
2129
Andrew Lunn158bc062016-04-28 21:24:06 -04002130 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, true);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002131 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002132 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002133
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002134 vlan.data[port] = untagged ?
2135 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
2136 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
2137
Andrew Lunn158bc062016-04-28 21:24:06 -04002138 return _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002139}
2140
Vivien Didelotf81ec902016-05-09 13:22:58 -04002141static void mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
2142 const struct switchdev_obj_port_vlan *vlan,
2143 struct switchdev_trans *trans)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002144{
2145 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2146 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
2147 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
2148 u16 vid;
Vivien Didelot76e398a2015-11-01 12:33:55 -05002149
Vivien Didelot54d77b52016-05-09 13:22:47 -04002150 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2151 return;
2152
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002153 mutex_lock(&ps->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002154
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002155 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid)
Andrew Lunn158bc062016-04-28 21:24:06 -04002156 if (_mv88e6xxx_port_vlan_add(ps, port, vid, untagged))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002157 netdev_err(ds->ports[port].netdev,
2158 "failed to add VLAN %d%c\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002159 vid, untagged ? 'u' : 't');
Vivien Didelot76e398a2015-11-01 12:33:55 -05002160
Andrew Lunn158bc062016-04-28 21:24:06 -04002161 if (pvid && _mv88e6xxx_port_pvid_set(ps, port, vlan->vid_end))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002162 netdev_err(ds->ports[port].netdev, "failed to set PVID %d\n",
Vivien Didelot4d5770b2016-04-06 11:55:05 -04002163 vlan->vid_end);
2164
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002165 mutex_unlock(&ps->reg_lock);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04002166}
2167
Andrew Lunn158bc062016-04-28 21:24:06 -04002168static int _mv88e6xxx_port_vlan_del(struct mv88e6xxx_priv_state *ps,
2169 int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002170{
Andrew Lunn158bc062016-04-28 21:24:06 -04002171 struct dsa_switch *ds = ps->ds;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002172 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002173 int i, err;
2174
Andrew Lunn158bc062016-04-28 21:24:06 -04002175 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002176 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002177 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04002178
Vivien Didelot2fb5ef02016-02-26 13:16:01 -05002179 /* Tell switchdev if this VLAN is handled in software */
2180 if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
Vivien Didelot3c06f082016-02-05 14:04:39 -05002181 return -EOPNOTSUPP;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002182
2183 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
2184
2185 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002186 vlan.valid = false;
Vivien Didelot009a2b92016-04-17 13:24:01 -04002187 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelot3d131f02015-11-03 10:52:52 -05002188 if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i))
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002189 continue;
2190
2191 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002192 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002193 break;
2194 }
2195 }
2196
Andrew Lunn158bc062016-04-28 21:24:06 -04002197 err = _mv88e6xxx_vtu_loadpurge(ps, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002198 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002199 return err;
2200
Andrew Lunn158bc062016-04-28 21:24:06 -04002201 return _mv88e6xxx_atu_remove(ps, vlan.fid, port, false);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002202}
2203
Vivien Didelotf81ec902016-05-09 13:22:58 -04002204static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
2205 const struct switchdev_obj_port_vlan *vlan)
Vivien Didelot76e398a2015-11-01 12:33:55 -05002206{
2207 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2208 u16 pvid, vid;
2209 int err = 0;
2210
Vivien Didelot54d77b52016-05-09 13:22:47 -04002211 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VTU))
2212 return -EOPNOTSUPP;
2213
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002214 mutex_lock(&ps->reg_lock);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002215
Andrew Lunn158bc062016-04-28 21:24:06 -04002216 err = _mv88e6xxx_port_pvid_get(ps, port, &pvid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002217 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002218 goto unlock;
2219
Vivien Didelot76e398a2015-11-01 12:33:55 -05002220 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002221 err = _mv88e6xxx_port_vlan_del(ps, port, vid);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002222 if (err)
2223 goto unlock;
2224
2225 if (vid == pvid) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002226 err = _mv88e6xxx_port_pvid_set(ps, port, 0);
Vivien Didelot76e398a2015-11-01 12:33:55 -05002227 if (err)
2228 goto unlock;
2229 }
2230 }
2231
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002232unlock:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002233 mutex_unlock(&ps->reg_lock);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04002234
2235 return err;
2236}
2237
Andrew Lunn158bc062016-04-28 21:24:06 -04002238static int _mv88e6xxx_atu_mac_write(struct mv88e6xxx_priv_state *ps,
Vivien Didelotc5723ac2015-08-10 09:09:48 -04002239 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002240{
2241 int i, ret;
2242
2243 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002244 ret = _mv88e6xxx_reg_write(
Andrew Lunn158bc062016-04-28 21:24:06 -04002245 ps, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
Andrew Lunncca8b132015-04-02 04:06:39 +02002246 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002247 if (ret < 0)
2248 return ret;
2249 }
2250
2251 return 0;
2252}
2253
Andrew Lunn158bc062016-04-28 21:24:06 -04002254static int _mv88e6xxx_atu_mac_read(struct mv88e6xxx_priv_state *ps,
2255 unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002256{
2257 int i, ret;
2258
2259 for (i = 0; i < 3; i++) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002260 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL,
Andrew Lunncca8b132015-04-02 04:06:39 +02002261 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002262 if (ret < 0)
2263 return ret;
2264 addr[i * 2] = ret >> 8;
2265 addr[i * 2 + 1] = ret & 0xff;
2266 }
2267
2268 return 0;
2269}
2270
Andrew Lunn158bc062016-04-28 21:24:06 -04002271static int _mv88e6xxx_atu_load(struct mv88e6xxx_priv_state *ps,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002272 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002273{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002274 int ret;
2275
Andrew Lunn158bc062016-04-28 21:24:06 -04002276 ret = _mv88e6xxx_atu_wait(ps);
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_mac_write(ps, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002281 if (ret < 0)
2282 return ret;
2283
Andrew Lunn158bc062016-04-28 21:24:06 -04002284 ret = _mv88e6xxx_atu_data_write(ps, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002285 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002286 return ret;
2287
Andrew Lunn158bc062016-04-28 21:24:06 -04002288 return _mv88e6xxx_atu_cmd(ps, entry->fid, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04002289}
David S. Millercdf09692015-08-11 12:00:37 -07002290
Andrew Lunn158bc062016-04-28 21:24:06 -04002291static int _mv88e6xxx_port_fdb_load(struct mv88e6xxx_priv_state *ps, int port,
Vivien Didelotfd231c82015-08-10 09:09:50 -04002292 const unsigned char *addr, u16 vid,
2293 u8 state)
2294{
2295 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002296 struct mv88e6xxx_vtu_stu_entry vlan;
2297 int err;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002298
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002299 /* Null VLAN ID corresponds to the port private database */
2300 if (vid == 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04002301 err = _mv88e6xxx_port_fid_get(ps, port, &vlan.fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002302 else
Andrew Lunn158bc062016-04-28 21:24:06 -04002303 err = _mv88e6xxx_vtu_get(ps, vid, &vlan, false);
Vivien Didelot3285f9e2016-02-26 13:16:03 -05002304 if (err)
2305 return err;
2306
2307 entry.fid = vlan.fid;
Vivien Didelotfd231c82015-08-10 09:09:50 -04002308 entry.state = state;
2309 ether_addr_copy(entry.mac, addr);
2310 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2311 entry.trunk = false;
2312 entry.portv_trunkid = BIT(port);
2313 }
2314
Andrew Lunn158bc062016-04-28 21:24:06 -04002315 return _mv88e6xxx_atu_load(ps, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002316}
2317
Vivien Didelotf81ec902016-05-09 13:22:58 -04002318static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
2319 const struct switchdev_obj_port_fdb *fdb,
2320 struct switchdev_trans *trans)
Vivien Didelot146a3202015-10-08 11:35:12 -04002321{
Vivien Didelot2672f822016-05-09 13:22:48 -04002322 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2323
2324 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2325 return -EOPNOTSUPP;
2326
Vivien Didelot146a3202015-10-08 11:35:12 -04002327 /* We don't need any dynamic resource from the kernel (yet),
2328 * so skip the prepare phase.
2329 */
2330 return 0;
2331}
2332
Vivien Didelotf81ec902016-05-09 13:22:58 -04002333static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
2334 const struct switchdev_obj_port_fdb *fdb,
2335 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002336{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04002337 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07002338 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2339 GLOBAL_ATU_DATA_STATE_UC_STATIC;
2340 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04002341
Vivien Didelot2672f822016-05-09 13:22:48 -04002342 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2343 return;
2344
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002345 mutex_lock(&ps->reg_lock);
Andrew Lunn158bc062016-04-28 21:24:06 -04002346 if (_mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid, state))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002347 netdev_err(ds->ports[port].netdev,
2348 "failed to load MAC address\n");
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002349 mutex_unlock(&ps->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002350}
2351
Vivien Didelotf81ec902016-05-09 13:22:58 -04002352static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
2353 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07002354{
2355 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2356 int ret;
2357
Vivien Didelot2672f822016-05-09 13:22:48 -04002358 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2359 return -EOPNOTSUPP;
2360
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002361 mutex_lock(&ps->reg_lock);
Andrew Lunn158bc062016-04-28 21:24:06 -04002362 ret = _mv88e6xxx_port_fdb_load(ps, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07002363 GLOBAL_ATU_DATA_STATE_UNUSED);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002364 mutex_unlock(&ps->reg_lock);
David S. Millercdf09692015-08-11 12:00:37 -07002365
2366 return ret;
2367}
2368
Andrew Lunn158bc062016-04-28 21:24:06 -04002369static int _mv88e6xxx_atu_getnext(struct mv88e6xxx_priv_state *ps, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04002370 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07002371{
Vivien Didelot1d194042015-08-10 09:09:51 -04002372 struct mv88e6xxx_atu_entry next = { 0 };
2373 int ret;
2374
2375 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002376
Andrew Lunn158bc062016-04-28 21:24:06 -04002377 ret = _mv88e6xxx_atu_wait(ps);
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_cmd(ps, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002382 if (ret < 0)
2383 return ret;
2384
Andrew Lunn158bc062016-04-28 21:24:06 -04002385 ret = _mv88e6xxx_atu_mac_read(ps, next.mac);
Vivien Didelot1d194042015-08-10 09:09:51 -04002386 if (ret < 0)
2387 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002388
Andrew Lunn158bc062016-04-28 21:24:06 -04002389 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, GLOBAL_ATU_DATA);
Vivien Didelot1d194042015-08-10 09:09:51 -04002390 if (ret < 0)
2391 return ret;
2392
2393 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
2394 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
2395 unsigned int mask, shift;
2396
2397 if (ret & GLOBAL_ATU_DATA_TRUNK) {
2398 next.trunk = true;
2399 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
2400 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
2401 } else {
2402 next.trunk = false;
2403 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
2404 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
2405 }
2406
2407 next.portv_trunkid = (ret & mask) >> shift;
2408 }
2409
2410 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07002411 return 0;
2412}
2413
Andrew Lunn158bc062016-04-28 21:24:06 -04002414static int _mv88e6xxx_port_fdb_dump_one(struct mv88e6xxx_priv_state *ps,
2415 u16 fid, u16 vid, int port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002416 struct switchdev_obj_port_fdb *fdb,
2417 int (*cb)(struct switchdev_obj *obj))
2418{
2419 struct mv88e6xxx_atu_entry addr = {
2420 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
2421 };
2422 int err;
2423
Andrew Lunn158bc062016-04-28 21:24:06 -04002424 err = _mv88e6xxx_atu_mac_write(ps, addr.mac);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002425 if (err)
2426 return err;
2427
2428 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002429 err = _mv88e6xxx_atu_getnext(ps, fid, &addr);
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002430 if (err)
2431 break;
2432
2433 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
2434 break;
2435
2436 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
2437 bool is_static = addr.state ==
2438 (is_multicast_ether_addr(addr.mac) ?
2439 GLOBAL_ATU_DATA_STATE_MC_STATIC :
2440 GLOBAL_ATU_DATA_STATE_UC_STATIC);
2441
2442 fdb->vid = vid;
2443 ether_addr_copy(fdb->addr, addr.mac);
2444 fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
2445
2446 err = cb(&fdb->obj);
2447 if (err)
2448 break;
2449 }
2450 } while (!is_broadcast_ether_addr(addr.mac));
2451
2452 return err;
2453}
2454
Vivien Didelotf81ec902016-05-09 13:22:58 -04002455static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
2456 struct switchdev_obj_port_fdb *fdb,
2457 int (*cb)(struct switchdev_obj *obj))
Vivien Didelotf33475b2015-10-22 09:34:41 -04002458{
2459 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2460 struct mv88e6xxx_vtu_stu_entry vlan = {
2461 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
2462 };
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002463 u16 fid;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002464 int err;
2465
Vivien Didelot2672f822016-05-09 13:22:48 -04002466 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_ATU))
2467 return -EOPNOTSUPP;
2468
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002469 mutex_lock(&ps->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002470
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002471 /* Dump port's default Filtering Information Database (VLAN ID 0) */
Andrew Lunn158bc062016-04-28 21:24:06 -04002472 err = _mv88e6xxx_port_fid_get(ps, port, &fid);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002473 if (err)
2474 goto unlock;
2475
Andrew Lunn158bc062016-04-28 21:24:06 -04002476 err = _mv88e6xxx_port_fdb_dump_one(ps, fid, 0, port, fdb, cb);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002477 if (err)
2478 goto unlock;
2479
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002480 /* Dump VLANs' Filtering Information Databases */
Andrew Lunn158bc062016-04-28 21:24:06 -04002481 err = _mv88e6xxx_vtu_vid_write(ps, vlan.vid);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002482 if (err)
2483 goto unlock;
2484
2485 do {
Andrew Lunn158bc062016-04-28 21:24:06 -04002486 err = _mv88e6xxx_vtu_getnext(ps, &vlan);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002487 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002488 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002489
2490 if (!vlan.valid)
2491 break;
2492
Andrew Lunn158bc062016-04-28 21:24:06 -04002493 err = _mv88e6xxx_port_fdb_dump_one(ps, vlan.fid, vlan.vid, port,
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002494 fdb, cb);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002495 if (err)
Vivien Didelot74b6ba02016-02-26 13:16:02 -05002496 break;
Vivien Didelotf33475b2015-10-22 09:34:41 -04002497 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
2498
2499unlock:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002500 mutex_unlock(&ps->reg_lock);
Vivien Didelotf33475b2015-10-22 09:34:41 -04002501
2502 return err;
2503}
2504
Vivien Didelotf81ec902016-05-09 13:22:58 -04002505static int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port,
2506 struct net_device *bridge)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002507{
Vivien Didelota6692752016-02-12 12:09:39 -05002508 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Colin Ian King1d9619d2016-04-25 23:11:22 +01002509 int i, err = 0;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002510
Vivien Didelot936f2342016-05-09 13:22:46 -04002511 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2512 return -EOPNOTSUPP;
2513
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002514 mutex_lock(&ps->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002515
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002516 /* Assign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002517 ps->ports[port].bridge_dev = bridge;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002518
Vivien Didelot009a2b92016-04-17 13:24:01 -04002519 for (i = 0; i < ps->info->num_ports; ++i) {
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002520 if (ps->ports[i].bridge_dev == bridge) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002521 err = _mv88e6xxx_port_based_vlan_map(ps, i);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002522 if (err)
2523 break;
2524 }
2525 }
2526
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002527 mutex_unlock(&ps->reg_lock);
Vivien Didelota6692752016-02-12 12:09:39 -05002528
Vivien Didelot466dfa02016-02-26 13:16:05 -05002529 return err;
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002530}
2531
Vivien Didelotf81ec902016-05-09 13:22:58 -04002532static void mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port)
Vivien Didelote79a8bc2015-11-04 17:23:40 -05002533{
Vivien Didelota6692752016-02-12 12:09:39 -05002534 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002535 struct net_device *bridge = ps->ports[port].bridge_dev;
Vivien Didelot16bfa702016-03-13 16:21:33 -04002536 int i;
Vivien Didelot466dfa02016-02-26 13:16:05 -05002537
Vivien Didelot936f2342016-05-09 13:22:46 -04002538 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_VLANTABLE))
2539 return;
2540
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002541 mutex_lock(&ps->reg_lock);
Vivien Didelot466dfa02016-02-26 13:16:05 -05002542
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002543 /* Unassign the bridge and remap each port's VLANTable */
Vivien Didelota6692752016-02-12 12:09:39 -05002544 ps->ports[port].bridge_dev = NULL;
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002545
Vivien Didelot009a2b92016-04-17 13:24:01 -04002546 for (i = 0; i < ps->info->num_ports; ++i)
Vivien Didelot16bfa702016-03-13 16:21:33 -04002547 if (i == port || ps->ports[i].bridge_dev == bridge)
Andrew Lunn158bc062016-04-28 21:24:06 -04002548 if (_mv88e6xxx_port_based_vlan_map(ps, i))
Andrew Lunnc8b09802016-06-04 21:16:57 +02002549 netdev_warn(ds->ports[i].netdev,
2550 "failed to remap\n");
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002551
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04002552 mutex_unlock(&ps->reg_lock);
Vivien Didelot66d9cd02016-02-05 14:07:14 -05002553}
2554
Andrew Lunn03a4a542016-06-04 21:17:05 +02002555static int _mv88e6xxx_mdio_page_write(struct mv88e6xxx_priv_state *ps,
2556 int port, int page, int reg, int val)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002557{
2558 int ret;
2559
Andrew Lunn03a4a542016-06-04 21:17:05 +02002560 ret = mv88e6xxx_mdio_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002561 if (ret < 0)
2562 goto restore_page_0;
2563
Andrew Lunn03a4a542016-06-04 21:17:05 +02002564 ret = mv88e6xxx_mdio_write_indirect(ps, port, reg, val);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002565restore_page_0:
Andrew Lunn03a4a542016-06-04 21:17:05 +02002566 mv88e6xxx_mdio_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002567
2568 return ret;
2569}
2570
Andrew Lunn03a4a542016-06-04 21:17:05 +02002571static int _mv88e6xxx_mdio_page_read(struct mv88e6xxx_priv_state *ps,
2572 int port, int page, int reg)
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002573{
2574 int ret;
2575
Andrew Lunn03a4a542016-06-04 21:17:05 +02002576 ret = mv88e6xxx_mdio_write_indirect(ps, port, 0x16, page);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002577 if (ret < 0)
2578 goto restore_page_0;
2579
Andrew Lunn03a4a542016-06-04 21:17:05 +02002580 ret = mv88e6xxx_mdio_read_indirect(ps, port, reg);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002581restore_page_0:
Andrew Lunn03a4a542016-06-04 21:17:05 +02002582 mv88e6xxx_mdio_write_indirect(ps, port, 0x16, 0x0);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00002583
2584 return ret;
2585}
2586
Vivien Didelot552238b2016-05-09 13:22:49 -04002587static int mv88e6xxx_switch_reset(struct mv88e6xxx_priv_state *ps)
2588{
2589 bool ppu_active = mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE);
2590 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
Andrew Lunn52638f72016-05-10 23:27:22 +02002591 struct gpio_desc *gpiod = ps->reset;
Vivien Didelot552238b2016-05-09 13:22:49 -04002592 unsigned long timeout;
2593 int ret;
2594 int i;
2595
2596 /* Set all ports to the disabled state. */
2597 for (i = 0; i < ps->info->num_ports; i++) {
2598 ret = _mv88e6xxx_reg_read(ps, REG_PORT(i), PORT_CONTROL);
2599 if (ret < 0)
2600 return ret;
2601
2602 ret = _mv88e6xxx_reg_write(ps, REG_PORT(i), PORT_CONTROL,
2603 ret & 0xfffc);
2604 if (ret)
2605 return ret;
2606 }
2607
2608 /* Wait for transmit queues to drain. */
2609 usleep_range(2000, 4000);
2610
2611 /* If there is a gpio connected to the reset pin, toggle it */
2612 if (gpiod) {
2613 gpiod_set_value_cansleep(gpiod, 1);
2614 usleep_range(10000, 20000);
2615 gpiod_set_value_cansleep(gpiod, 0);
2616 usleep_range(10000, 20000);
2617 }
2618
2619 /* Reset the switch. Keep the PPU active if requested. The PPU
2620 * needs to be active to support indirect phy register access
2621 * through global registers 0x18 and 0x19.
2622 */
2623 if (ppu_active)
2624 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc000);
2625 else
2626 ret = _mv88e6xxx_reg_write(ps, REG_GLOBAL, 0x04, 0xc400);
2627 if (ret)
2628 return ret;
2629
2630 /* Wait up to one second for reset to complete. */
2631 timeout = jiffies + 1 * HZ;
2632 while (time_before(jiffies, timeout)) {
2633 ret = _mv88e6xxx_reg_read(ps, REG_GLOBAL, 0x00);
2634 if (ret < 0)
2635 return ret;
2636
2637 if ((ret & is_reset) == is_reset)
2638 break;
2639 usleep_range(1000, 2000);
2640 }
2641 if (time_after(jiffies, timeout))
2642 ret = -ETIMEDOUT;
2643 else
2644 ret = 0;
2645
2646 return ret;
2647}
2648
Andrew Lunn158bc062016-04-28 21:24:06 -04002649static int mv88e6xxx_power_on_serdes(struct mv88e6xxx_priv_state *ps)
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002650{
2651 int ret;
2652
Andrew Lunn03a4a542016-06-04 21:17:05 +02002653 ret = _mv88e6xxx_mdio_page_read(ps, REG_FIBER_SERDES,
2654 PAGE_FIBER_SERDES, MII_BMCR);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002655 if (ret < 0)
2656 return ret;
2657
2658 if (ret & BMCR_PDOWN) {
2659 ret &= ~BMCR_PDOWN;
Andrew Lunn03a4a542016-06-04 21:17:05 +02002660 ret = _mv88e6xxx_mdio_page_write(ps, REG_FIBER_SERDES,
2661 PAGE_FIBER_SERDES, MII_BMCR,
2662 ret);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002663 }
2664
2665 return ret;
2666}
2667
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002668static int mv88e6xxx_setup_port(struct mv88e6xxx_priv_state *ps, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07002669{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002670 struct dsa_switch *ds = ps->ds;
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002671 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002672 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07002673
Andrew Lunn158bc062016-04-28 21:24:06 -04002674 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2675 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2676 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2677 mv88e6xxx_6065_family(ps) || mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002678 /* MAC Forcing register: don't force link, speed,
2679 * duplex or flow control state to any particular
2680 * values on physical ports, but force the CPU port
2681 * and all DSA ports to their maximum bandwidth and
2682 * full duplex.
2683 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002684 reg = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02002685 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01002686 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002687 reg |= PORT_PCS_CTRL_FORCE_LINK |
2688 PORT_PCS_CTRL_LINK_UP |
2689 PORT_PCS_CTRL_DUPLEX_FULL |
2690 PORT_PCS_CTRL_FORCE_DUPLEX;
Andrew Lunn158bc062016-04-28 21:24:06 -04002691 if (mv88e6xxx_6065_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002692 reg |= PORT_PCS_CTRL_100;
2693 else
2694 reg |= PORT_PCS_CTRL_1000;
2695 } else {
2696 reg |= PORT_PCS_CTRL_UNFORCED;
2697 }
2698
Andrew Lunn158bc062016-04-28 21:24:06 -04002699 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002700 PORT_PCS_CTRL, reg);
2701 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002702 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002703 }
2704
2705 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
2706 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
2707 * tunneling, determine priority by looking at 802.1p and IP
2708 * priority fields (IP prio has precedence), and set STP state
2709 * to Forwarding.
2710 *
2711 * If this is the CPU link, use DSA or EDSA tagging depending
2712 * on which tagging mode was configured.
2713 *
2714 * If this is a link to another switch, use DSA tagging mode.
2715 *
2716 * If this is the upstream port for this switch, enable
2717 * forwarding of unknown unicasts and multicasts.
2718 */
2719 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002720 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2721 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2722 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2723 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002724 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
2725 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
2726 PORT_CONTROL_STATE_FORWARDING;
2727 if (dsa_is_cpu_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002728 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002729 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002730 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2731 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2732 mv88e6xxx_6320_family(ps)) {
Andrew Lunn5377b802016-06-04 21:17:02 +02002733 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA |
2734 PORT_CONTROL_FORWARD_UNKNOWN |
Andrew Lunnc047a1f2015-09-29 01:50:56 +02002735 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002736 }
2737
Andrew Lunn158bc062016-04-28 21:24:06 -04002738 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2739 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2740 mv88e6xxx_6095_family(ps) || mv88e6xxx_6065_family(ps) ||
2741 mv88e6xxx_6185_family(ps) || mv88e6xxx_6320_family(ps)) {
Vivien Didelot57d32312016-06-20 13:13:58 -04002742 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002743 }
2744 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02002745 if (dsa_is_dsa_port(ds, port)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002746 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps))
Andrew Lunn6083ce72015-08-17 23:52:52 +02002747 reg |= PORT_CONTROL_DSA_TAG;
Andrew Lunn158bc062016-04-28 21:24:06 -04002748 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2749 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2750 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002751 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02002752 }
2753
Andrew Lunn54d792f2015-05-06 01:09:47 +02002754 if (port == dsa_upstream_port(ds))
2755 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
2756 PORT_CONTROL_FORWARD_UNKNOWN_MC;
2757 }
2758 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002759 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002760 PORT_CONTROL, reg);
2761 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002762 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002763 }
2764
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002765 /* If this port is connected to a SerDes, make sure the SerDes is not
2766 * powered down.
2767 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002768 if (mv88e6xxx_6352_family(ps)) {
2769 ret = _mv88e6xxx_reg_read(ps, REG_PORT(port), PORT_STATUS);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002770 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002771 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002772 ret &= PORT_STATUS_CMODE_MASK;
2773 if ((ret == PORT_STATUS_CMODE_100BASE_X) ||
2774 (ret == PORT_STATUS_CMODE_1000BASE_X) ||
2775 (ret == PORT_STATUS_CMODE_SGMII)) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002776 ret = mv88e6xxx_power_on_serdes(ps);
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002777 if (ret < 0)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002778 return ret;
Patrick Uiterwijk13a7ebb2016-03-30 01:39:41 +00002779 }
2780 }
2781
Vivien Didelot8efdda42015-08-13 12:52:23 -04002782 /* Port Control 2: don't force a good FCS, set the maximum frame size to
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002783 * 10240 bytes, disable 802.1q tags checking, don't discard tagged or
Vivien Didelot8efdda42015-08-13 12:52:23 -04002784 * untagged frames on this port, do a destination address lookup on all
2785 * received packets as usual, disable ARP mirroring and don't send a
2786 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02002787 */
2788 reg = 0;
Andrew Lunn158bc062016-04-28 21:24:06 -04002789 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2790 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2791 mv88e6xxx_6095_family(ps) || mv88e6xxx_6320_family(ps) ||
2792 mv88e6xxx_6185_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002793 reg = PORT_CONTROL_2_MAP_DA;
2794
Andrew Lunn158bc062016-04-28 21:24:06 -04002795 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2796 mv88e6xxx_6165_family(ps) || mv88e6xxx_6320_family(ps))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002797 reg |= PORT_CONTROL_2_JUMBO_10240;
2798
Andrew Lunn158bc062016-04-28 21:24:06 -04002799 if (mv88e6xxx_6095_family(ps) || mv88e6xxx_6185_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002800 /* Set the upstream port this port should use */
2801 reg |= dsa_upstream_port(ds);
2802 /* enable forwarding of unknown multicast addresses to
2803 * the upstream port
2804 */
2805 if (port == dsa_upstream_port(ds))
2806 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2807 }
2808
Vivien Didelot46fbe5e2016-02-26 13:16:07 -05002809 reg |= PORT_CONTROL_2_8021Q_DISABLED;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002810
Andrew Lunn54d792f2015-05-06 01:09:47 +02002811 if (reg) {
Andrew Lunn158bc062016-04-28 21:24:06 -04002812 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002813 PORT_CONTROL_2, reg);
2814 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002815 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002816 }
2817
2818 /* Port Association Vector: when learning source addresses
2819 * of packets, add the address to the address database using
2820 * a port bitmap that has only the bit for this port set and
2821 * the other bits clear.
2822 */
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002823 reg = 1 << port;
Vivien Didelot996ecb82016-04-14 14:42:08 -04002824 /* Disable learning for CPU port */
2825 if (dsa_is_cpu_port(ds, port))
Vivien Didelot65fa4022016-04-14 14:42:07 -04002826 reg = 0;
Andrew Lunn4c7ea3c2015-11-03 10:52:36 -05002827
Andrew Lunn158bc062016-04-28 21:24:06 -04002828 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_ASSOC_VECTOR, reg);
Andrew Lunn54d792f2015-05-06 01:09:47 +02002829 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002830 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002831
2832 /* Egress rate control 2: disable egress rate control. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002833 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_RATE_CONTROL_2,
Andrew Lunn54d792f2015-05-06 01:09:47 +02002834 0x0000);
2835 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002836 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002837
Andrew Lunn158bc062016-04-28 21:24:06 -04002838 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2839 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2840 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002841 /* Do not limit the period of time that this port can
2842 * be paused for by the remote end or the period of
2843 * time that this port can pause the remote end.
2844 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002845 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002846 PORT_PAUSE_CTRL, 0x0000);
2847 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002848 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002849
2850 /* Port ATU control: disable limiting the number of
2851 * address database entries that this port is allowed
2852 * to use.
2853 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002854 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002855 PORT_ATU_CONTROL, 0x0000);
2856 /* Priority Override: disable DA, SA and VTU priority
2857 * override.
2858 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002859 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002860 PORT_PRI_OVERRIDE, 0x0000);
2861 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002862 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002863
2864 /* Port Ethertype: use the Ethertype DSA Ethertype
2865 * value.
2866 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002867 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002868 PORT_ETH_TYPE, ETH_P_EDSA);
2869 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002870 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002871 /* Tag Remap: use an identity 802.1p prio -> switch
2872 * prio mapping.
2873 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002874 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002875 PORT_TAG_REGMAP_0123, 0x3210);
2876 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002877 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002878
2879 /* Tag Remap 2: use an identity 802.1p prio -> switch
2880 * prio mapping.
2881 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002882 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002883 PORT_TAG_REGMAP_4567, 0x7654);
2884 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002885 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002886 }
2887
Andrew Lunn158bc062016-04-28 21:24:06 -04002888 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
2889 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
2890 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
2891 mv88e6xxx_6320_family(ps)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002892 /* Rate Control: disable ingress rate limiting. */
Andrew Lunn158bc062016-04-28 21:24:06 -04002893 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port),
Andrew Lunn54d792f2015-05-06 01:09:47 +02002894 PORT_RATE_CONTROL, 0x0001);
2895 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002896 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002897 }
2898
Guenter Roeck366f0a02015-03-26 18:36:30 -07002899 /* Port Control 1: disable trunking, disable sending
2900 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002901 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002902 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002903 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002904 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002905
Vivien Didelot207afda2016-04-14 14:42:09 -04002906 /* Port based VLAN map: give each port the same default address
Vivien Didelotb7666ef2016-02-26 13:16:06 -05002907 * database, and allow bidirectional communication between the
2908 * CPU and DSA port(s), and the other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002909 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002910 ret = _mv88e6xxx_port_fid_set(ps, port, 0);
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002911 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002912 return ret;
Vivien Didelot2db9ce12016-02-26 13:16:04 -05002913
Andrew Lunn158bc062016-04-28 21:24:06 -04002914 ret = _mv88e6xxx_port_based_vlan_map(ps, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002915 if (ret)
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002916 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002917
2918 /* Default VLAN ID and priority: don't set a default VLAN
2919 * ID, and set the default packet priority to zero.
2920 */
Andrew Lunn158bc062016-04-28 21:24:06 -04002921 ret = _mv88e6xxx_reg_write(ps, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002922 0x0000);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04002923 if (ret)
2924 return ret;
Guenter Roeckd827e882015-03-26 18:36:29 -07002925
Andrew Lunndbde9e62015-05-06 01:09:48 +02002926 return 0;
2927}
2928
Vivien Didelot08a01262016-05-09 13:22:50 -04002929static int mv88e6xxx_setup_global(struct mv88e6xxx_priv_state *ps)
2930{
Vivien Didelotb0745e872016-05-09 13:22:53 -04002931 struct dsa_switch *ds = ps->ds;
2932 u32 upstream_port = dsa_upstream_port(ds);
Vivien Didelot119477b2016-05-09 13:22:51 -04002933 u16 reg;
Vivien Didelot08a01262016-05-09 13:22:50 -04002934 int err;
2935 int i;
2936
Vivien Didelot119477b2016-05-09 13:22:51 -04002937 /* Enable the PHY Polling Unit if present, don't discard any packets,
2938 * and mask all interrupt sources.
2939 */
2940 reg = 0;
2941 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU) ||
2942 mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU_ACTIVE))
2943 reg |= GLOBAL_CONTROL_PPU_ENABLE;
2944
2945 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL, reg);
2946 if (err)
2947 return err;
2948
Vivien Didelotb0745e872016-05-09 13:22:53 -04002949 /* Configure the upstream port, and configure it as the port to which
2950 * ingress and egress and ARP monitor frames are to be sent.
2951 */
2952 reg = upstream_port << GLOBAL_MONITOR_CONTROL_INGRESS_SHIFT |
2953 upstream_port << GLOBAL_MONITOR_CONTROL_EGRESS_SHIFT |
2954 upstream_port << GLOBAL_MONITOR_CONTROL_ARP_SHIFT;
2955 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_MONITOR_CONTROL, reg);
2956 if (err)
2957 return err;
2958
Vivien Didelot50484ff2016-05-09 13:22:54 -04002959 /* Disable remote management, and set the switch's DSA device number. */
2960 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_CONTROL_2,
2961 GLOBAL_CONTROL_2_MULTIPLE_CASCADE |
2962 (ds->index & 0x1f));
2963 if (err)
2964 return err;
2965
Vivien Didelot08a01262016-05-09 13:22:50 -04002966 /* Set the default address aging time to 5 minutes, and
2967 * enable address learn messages to be sent to all message
2968 * ports.
2969 */
2970 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_ATU_CONTROL,
2971 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2972 if (err)
2973 return err;
2974
2975 /* Configure the IP ToS mapping registers. */
2976 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2977 if (err)
2978 return err;
2979 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2980 if (err)
2981 return err;
2982 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2983 if (err)
2984 return err;
2985 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2986 if (err)
2987 return err;
2988 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2989 if (err)
2990 return err;
2991 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2992 if (err)
2993 return err;
2994 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2995 if (err)
2996 return err;
2997 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2998 if (err)
2999 return err;
3000
3001 /* Configure the IEEE 802.1p priority mapping register. */
3002 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
3003 if (err)
3004 return err;
3005
3006 /* Send all frames with destination addresses matching
3007 * 01:80:c2:00:00:0x to the CPU port.
3008 */
3009 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
3010 if (err)
3011 return err;
3012
3013 /* Ignore removed tag data on doubly tagged packets, disable
3014 * flow control messages, force flow control priority to the
3015 * highest, and send all special multicast frames to the CPU
3016 * port at the highest priority.
3017 */
3018 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
3019 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
3020 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
3021 if (err)
3022 return err;
3023
3024 /* Program the DSA routing table. */
3025 for (i = 0; i < 32; i++) {
3026 int nexthop = 0x1f;
3027
Andrew Lunn66472fc2016-06-04 21:17:00 +02003028 if (i != ds->index && i < DSA_MAX_SWITCHES)
3029 nexthop = ds->rtable[i] & 0x1f;
Vivien Didelot08a01262016-05-09 13:22:50 -04003030
3031 err = _mv88e6xxx_reg_write(
3032 ps, REG_GLOBAL2,
3033 GLOBAL2_DEVICE_MAPPING,
3034 GLOBAL2_DEVICE_MAPPING_UPDATE |
3035 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) | nexthop);
3036 if (err)
3037 return err;
3038 }
3039
3040 /* Clear all trunk masks. */
3041 for (i = 0; i < 8; i++) {
3042 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
3043 0x8000 |
3044 (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
3045 ((1 << ps->info->num_ports) - 1));
3046 if (err)
3047 return err;
3048 }
3049
3050 /* Clear all trunk mappings. */
3051 for (i = 0; i < 16; i++) {
3052 err = _mv88e6xxx_reg_write(
3053 ps, REG_GLOBAL2,
3054 GLOBAL2_TRUNK_MAPPING,
3055 GLOBAL2_TRUNK_MAPPING_UPDATE |
3056 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
3057 if (err)
3058 return err;
3059 }
3060
3061 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3062 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3063 mv88e6xxx_6320_family(ps)) {
3064 /* Send all frames with destination addresses matching
3065 * 01:80:c2:00:00:2x to the CPU port.
3066 */
3067 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3068 GLOBAL2_MGMT_EN_2X, 0xffff);
3069 if (err)
3070 return err;
3071
3072 /* Initialise cross-chip port VLAN table to reset
3073 * defaults.
3074 */
3075 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3076 GLOBAL2_PVT_ADDR, 0x9000);
3077 if (err)
3078 return err;
3079
3080 /* Clear the priority override table. */
3081 for (i = 0; i < 16; i++) {
3082 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3083 GLOBAL2_PRIO_OVERRIDE,
3084 0x8000 | (i << 8));
3085 if (err)
3086 return err;
3087 }
3088 }
3089
3090 if (mv88e6xxx_6352_family(ps) || mv88e6xxx_6351_family(ps) ||
3091 mv88e6xxx_6165_family(ps) || mv88e6xxx_6097_family(ps) ||
3092 mv88e6xxx_6185_family(ps) || mv88e6xxx_6095_family(ps) ||
3093 mv88e6xxx_6320_family(ps)) {
3094 /* Disable ingress rate limiting by resetting all
3095 * ingress rate limit registers to their initial
3096 * state.
3097 */
3098 for (i = 0; i < ps->info->num_ports; i++) {
3099 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL2,
3100 GLOBAL2_INGRESS_OP,
3101 0x9000 | (i << 8));
3102 if (err)
3103 return err;
3104 }
3105 }
3106
3107 /* Clear the statistics counters for all ports */
3108 err = _mv88e6xxx_reg_write(ps, REG_GLOBAL, GLOBAL_STATS_OP,
3109 GLOBAL_STATS_OP_FLUSH_ALL);
3110 if (err)
3111 return err;
3112
3113 /* Wait for the flush to complete. */
3114 err = _mv88e6xxx_stats_wait(ps);
3115 if (err)
3116 return err;
3117
3118 /* Clear all ATU entries */
3119 err = _mv88e6xxx_atu_flush(ps, 0, true);
3120 if (err)
3121 return err;
3122
3123 /* Clear all the VTU and STU entries */
3124 err = _mv88e6xxx_vtu_stu_flush(ps);
3125 if (err < 0)
3126 return err;
3127
3128 return err;
3129}
3130
Vivien Didelotf81ec902016-05-09 13:22:58 -04003131static int mv88e6xxx_setup(struct dsa_switch *ds)
Guenter Roeckacdaffc2015-03-26 18:36:28 -07003132{
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003133 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot552238b2016-05-09 13:22:49 -04003134 int err;
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003135 int i;
3136
3137 ps->ds = ds;
Andrew Lunnb516d452016-06-04 21:17:06 +02003138 ds->slave_mii_bus = ps->mdio_bus;
Vivien Didelot552238b2016-05-09 13:22:49 -04003139
Vivien Didelotd24645b2016-05-09 13:22:41 -04003140 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM))
3141 mutex_init(&ps->eeprom_mutex);
3142
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003143 mutex_lock(&ps->reg_lock);
Vivien Didelot552238b2016-05-09 13:22:49 -04003144
3145 err = mv88e6xxx_switch_reset(ps);
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003146 if (err)
3147 goto unlock;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003148
Vivien Didelot08a01262016-05-09 13:22:50 -04003149 err = mv88e6xxx_setup_global(ps);
Vivien Didelota1a6a4d2016-05-09 13:22:56 -04003150 if (err)
3151 goto unlock;
3152
3153 for (i = 0; i < ps->info->num_ports; i++) {
3154 err = mv88e6xxx_setup_port(ps, i);
3155 if (err)
3156 goto unlock;
3157 }
Andrew Lunn54d792f2015-05-06 01:09:47 +02003158
Vivien Didelot6b17e862015-08-13 12:52:18 -04003159unlock:
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003160 mutex_unlock(&ps->reg_lock);
Andrew Lunndb687a52015-06-20 21:31:29 +02003161
Andrew Lunn48ace4e2016-04-14 23:47:12 +02003162 return err;
Andrew Lunn54d792f2015-05-06 01:09:47 +02003163}
3164
Vivien Didelot57d32312016-06-20 13:13:58 -04003165static int mv88e6xxx_mdio_page_read(struct dsa_switch *ds, int port, int page,
3166 int reg)
Andrew Lunn491435852015-04-02 04:06:35 +02003167{
3168 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3169 int ret;
3170
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003171 mutex_lock(&ps->reg_lock);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003172 ret = _mv88e6xxx_mdio_page_read(ps, port, page, reg);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003173 mutex_unlock(&ps->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003174
Andrew Lunn491435852015-04-02 04:06:35 +02003175 return ret;
3176}
3177
Vivien Didelot57d32312016-06-20 13:13:58 -04003178static int mv88e6xxx_mdio_page_write(struct dsa_switch *ds, int port, int page,
3179 int reg, int val)
Andrew Lunn491435852015-04-02 04:06:35 +02003180{
3181 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3182 int ret;
3183
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003184 mutex_lock(&ps->reg_lock);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003185 ret = _mv88e6xxx_mdio_page_write(ps, port, page, reg, val);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003186 mutex_unlock(&ps->reg_lock);
Patrick Uiterwijk75baacf2016-03-30 01:39:40 +00003187
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003188 return ret;
3189}
3190
Andrew Lunn03a4a542016-06-04 21:17:05 +02003191static int mv88e6xxx_port_to_mdio_addr(struct mv88e6xxx_priv_state *ps,
3192 int port)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003193{
Vivien Didelot009a2b92016-04-17 13:24:01 -04003194 if (port >= 0 && port < ps->info->num_ports)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003195 return port;
3196 return -EINVAL;
3197}
3198
Andrew Lunnb516d452016-06-04 21:17:06 +02003199static int mv88e6xxx_mdio_read(struct mii_bus *bus, int port, int regnum)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003200{
Andrew Lunnb516d452016-06-04 21:17:06 +02003201 struct mv88e6xxx_priv_state *ps = bus->priv;
Andrew Lunn03a4a542016-06-04 21:17:05 +02003202 int addr = mv88e6xxx_port_to_mdio_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003203 int ret;
3204
3205 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003206 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003207
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003208 mutex_lock(&ps->reg_lock);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003209
3210 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003211 ret = mv88e6xxx_mdio_read_ppu(ps, addr, regnum);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003212 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003213 ret = mv88e6xxx_mdio_read_indirect(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003214 else
Andrew Lunn03a4a542016-06-04 21:17:05 +02003215 ret = mv88e6xxx_mdio_read_direct(ps, addr, regnum);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003216
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003217 mutex_unlock(&ps->reg_lock);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003218 return ret;
3219}
3220
Andrew Lunnb516d452016-06-04 21:17:06 +02003221static int mv88e6xxx_mdio_write(struct mii_bus *bus, int port, int regnum,
Andrew Lunn03a4a542016-06-04 21:17:05 +02003222 u16 val)
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003223{
Andrew Lunnb516d452016-06-04 21:17:06 +02003224 struct mv88e6xxx_priv_state *ps = bus->priv;
Andrew Lunn03a4a542016-06-04 21:17:05 +02003225 int addr = mv88e6xxx_port_to_mdio_addr(ps, port);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003226 int ret;
3227
3228 if (addr < 0)
Andrew Lunn158bc062016-04-28 21:24:06 -04003229 return 0xffff;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003230
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003231 mutex_lock(&ps->reg_lock);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003232
3233 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003234 ret = mv88e6xxx_mdio_write_ppu(ps, addr, regnum, val);
Vivien Didelot6d5834a2016-05-09 13:22:40 -04003235 else if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_SMI_PHY))
Andrew Lunn03a4a542016-06-04 21:17:05 +02003236 ret = mv88e6xxx_mdio_write_indirect(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003237 else
Andrew Lunn03a4a542016-06-04 21:17:05 +02003238 ret = mv88e6xxx_mdio_write_direct(ps, addr, regnum, val);
Vivien Didelot8c9983a2016-05-09 13:22:39 -04003239
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003240 mutex_unlock(&ps->reg_lock);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02003241 return ret;
3242}
3243
Andrew Lunnb516d452016-06-04 21:17:06 +02003244static int mv88e6xxx_mdio_register(struct mv88e6xxx_priv_state *ps,
3245 struct device_node *np)
3246{
3247 static int index;
3248 struct mii_bus *bus;
3249 int err;
3250
3251 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_PPU))
3252 mv88e6xxx_ppu_state_init(ps);
3253
3254 if (np)
3255 ps->mdio_np = of_get_child_by_name(np, "mdio");
3256
3257 bus = devm_mdiobus_alloc(ps->dev);
3258 if (!bus)
3259 return -ENOMEM;
3260
3261 bus->priv = (void *)ps;
3262 if (np) {
3263 bus->name = np->full_name;
3264 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", np->full_name);
3265 } else {
3266 bus->name = "mv88e6xxx SMI";
3267 snprintf(bus->id, MII_BUS_ID_SIZE, "mv88e6xxx-%d", index++);
3268 }
3269
3270 bus->read = mv88e6xxx_mdio_read;
3271 bus->write = mv88e6xxx_mdio_write;
3272 bus->parent = ps->dev;
3273
3274 if (ps->mdio_np)
3275 err = of_mdiobus_register(bus, ps->mdio_np);
3276 else
3277 err = mdiobus_register(bus);
3278 if (err) {
3279 dev_err(ps->dev, "Cannot register MDIO bus (%d)\n", err);
3280 goto out;
3281 }
3282 ps->mdio_bus = bus;
3283
3284 return 0;
3285
3286out:
3287 if (ps->mdio_np)
3288 of_node_put(ps->mdio_np);
3289
3290 return err;
3291}
3292
3293static void mv88e6xxx_mdio_unregister(struct mv88e6xxx_priv_state *ps)
3294
3295{
3296 struct mii_bus *bus = ps->mdio_bus;
3297
3298 mdiobus_unregister(bus);
3299
3300 if (ps->mdio_np)
3301 of_node_put(ps->mdio_np);
3302}
3303
Guenter Roeckc22995c2015-07-25 09:42:28 -07003304#ifdef CONFIG_NET_DSA_HWMON
3305
3306static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
3307{
3308 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3309 int ret;
3310 int val;
3311
3312 *temp = 0;
3313
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003314 mutex_lock(&ps->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003315
Andrew Lunn03a4a542016-06-04 21:17:05 +02003316 ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x16, 0x6);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003317 if (ret < 0)
3318 goto error;
3319
3320 /* Enable temperature sensor */
Andrew Lunn03a4a542016-06-04 21:17:05 +02003321 ret = mv88e6xxx_mdio_read_direct(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003322 if (ret < 0)
3323 goto error;
3324
Andrew Lunn03a4a542016-06-04 21:17:05 +02003325 ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x1a, ret | (1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003326 if (ret < 0)
3327 goto error;
3328
3329 /* Wait for temperature to stabilize */
3330 usleep_range(10000, 12000);
3331
Andrew Lunn03a4a542016-06-04 21:17:05 +02003332 val = mv88e6xxx_mdio_read_direct(ps, 0x0, 0x1a);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003333 if (val < 0) {
3334 ret = val;
3335 goto error;
3336 }
3337
3338 /* Disable temperature sensor */
Andrew Lunn03a4a542016-06-04 21:17:05 +02003339 ret = mv88e6xxx_mdio_write_direct(ps, 0x0, 0x1a, ret & ~(1 << 5));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003340 if (ret < 0)
3341 goto error;
3342
3343 *temp = ((val & 0x1f) - 5) * 5;
3344
3345error:
Andrew Lunn03a4a542016-06-04 21:17:05 +02003346 mv88e6xxx_mdio_write_direct(ps, 0x0, 0x16, 0x0);
Vivien Didelot9f8b3ee2016-06-20 13:14:05 -04003347 mutex_unlock(&ps->reg_lock);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003348 return ret;
3349}
3350
3351static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
3352{
Andrew Lunn158bc062016-04-28 21:24:06 -04003353 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3354 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003355 int ret;
3356
3357 *temp = 0;
3358
Andrew Lunn03a4a542016-06-04 21:17:05 +02003359 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 27);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003360 if (ret < 0)
3361 return ret;
3362
3363 *temp = (ret & 0xff) - 25;
3364
3365 return 0;
3366}
3367
Vivien Didelotf81ec902016-05-09 13:22:58 -04003368static int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003369{
Andrew Lunn158bc062016-04-28 21:24:06 -04003370 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3371
Vivien Didelot6594f612016-05-09 13:22:42 -04003372 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP))
3373 return -EOPNOTSUPP;
3374
Andrew Lunn158bc062016-04-28 21:24:06 -04003375 if (mv88e6xxx_6320_family(ps) || mv88e6xxx_6352_family(ps))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003376 return mv88e63xx_get_temp(ds, temp);
3377
3378 return mv88e61xx_get_temp(ds, temp);
3379}
3380
Vivien Didelotf81ec902016-05-09 13:22:58 -04003381static int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003382{
Andrew Lunn158bc062016-04-28 21:24:06 -04003383 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3384 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003385 int ret;
3386
Vivien Didelot6594f612016-05-09 13:22:42 -04003387 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003388 return -EOPNOTSUPP;
3389
3390 *temp = 0;
3391
Andrew Lunn03a4a542016-06-04 21:17:05 +02003392 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003393 if (ret < 0)
3394 return ret;
3395
3396 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
3397
3398 return 0;
3399}
3400
Vivien Didelotf81ec902016-05-09 13:22:58 -04003401static int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003402{
Andrew Lunn158bc062016-04-28 21:24:06 -04003403 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3404 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003405 int ret;
3406
Vivien Didelot6594f612016-05-09 13:22:42 -04003407 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003408 return -EOPNOTSUPP;
3409
Andrew Lunn03a4a542016-06-04 21:17:05 +02003410 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003411 if (ret < 0)
3412 return ret;
3413 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
Andrew Lunn03a4a542016-06-04 21:17:05 +02003414 return mv88e6xxx_mdio_page_write(ds, phy, 6, 26,
3415 (ret & 0xe0ff) | (temp << 8));
Guenter Roeckc22995c2015-07-25 09:42:28 -07003416}
3417
Vivien Didelotf81ec902016-05-09 13:22:58 -04003418static int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
Guenter Roeckc22995c2015-07-25 09:42:28 -07003419{
Andrew Lunn158bc062016-04-28 21:24:06 -04003420 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3421 int phy = mv88e6xxx_6320_family(ps) ? 3 : 0;
Guenter Roeckc22995c2015-07-25 09:42:28 -07003422 int ret;
3423
Vivien Didelot6594f612016-05-09 13:22:42 -04003424 if (!mv88e6xxx_has(ps, MV88E6XXX_FLAG_TEMP_LIMIT))
Guenter Roeckc22995c2015-07-25 09:42:28 -07003425 return -EOPNOTSUPP;
3426
3427 *alarm = false;
3428
Andrew Lunn03a4a542016-06-04 21:17:05 +02003429 ret = mv88e6xxx_mdio_page_read(ds, phy, 6, 26);
Guenter Roeckc22995c2015-07-25 09:42:28 -07003430 if (ret < 0)
3431 return ret;
3432
3433 *alarm = !!(ret & 0x40);
3434
3435 return 0;
3436}
3437#endif /* CONFIG_NET_DSA_HWMON */
3438
Vivien Didelotf81ec902016-05-09 13:22:58 -04003439static const struct mv88e6xxx_info mv88e6xxx_table[] = {
3440 [MV88E6085] = {
3441 .prod_num = PORT_SWITCH_ID_PROD_NUM_6085,
3442 .family = MV88E6XXX_FAMILY_6097,
3443 .name = "Marvell 88E6085",
3444 .num_databases = 4096,
3445 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003446 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003447 .flags = MV88E6XXX_FLAGS_FAMILY_6097,
3448 },
3449
3450 [MV88E6095] = {
3451 .prod_num = PORT_SWITCH_ID_PROD_NUM_6095,
3452 .family = MV88E6XXX_FAMILY_6095,
3453 .name = "Marvell 88E6095/88E6095F",
3454 .num_databases = 256,
3455 .num_ports = 11,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003456 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003457 .flags = MV88E6XXX_FLAGS_FAMILY_6095,
3458 },
3459
3460 [MV88E6123] = {
3461 .prod_num = PORT_SWITCH_ID_PROD_NUM_6123,
3462 .family = MV88E6XXX_FAMILY_6165,
3463 .name = "Marvell 88E6123",
3464 .num_databases = 4096,
3465 .num_ports = 3,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003466 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003467 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3468 },
3469
3470 [MV88E6131] = {
3471 .prod_num = PORT_SWITCH_ID_PROD_NUM_6131,
3472 .family = MV88E6XXX_FAMILY_6185,
3473 .name = "Marvell 88E6131",
3474 .num_databases = 256,
3475 .num_ports = 8,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003476 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003477 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3478 },
3479
3480 [MV88E6161] = {
3481 .prod_num = PORT_SWITCH_ID_PROD_NUM_6161,
3482 .family = MV88E6XXX_FAMILY_6165,
3483 .name = "Marvell 88E6161",
3484 .num_databases = 4096,
3485 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003486 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003487 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3488 },
3489
3490 [MV88E6165] = {
3491 .prod_num = PORT_SWITCH_ID_PROD_NUM_6165,
3492 .family = MV88E6XXX_FAMILY_6165,
3493 .name = "Marvell 88E6165",
3494 .num_databases = 4096,
3495 .num_ports = 6,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003496 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003497 .flags = MV88E6XXX_FLAGS_FAMILY_6165,
3498 },
3499
3500 [MV88E6171] = {
3501 .prod_num = PORT_SWITCH_ID_PROD_NUM_6171,
3502 .family = MV88E6XXX_FAMILY_6351,
3503 .name = "Marvell 88E6171",
3504 .num_databases = 4096,
3505 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003506 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003507 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3508 },
3509
3510 [MV88E6172] = {
3511 .prod_num = PORT_SWITCH_ID_PROD_NUM_6172,
3512 .family = MV88E6XXX_FAMILY_6352,
3513 .name = "Marvell 88E6172",
3514 .num_databases = 4096,
3515 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003516 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003517 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3518 },
3519
3520 [MV88E6175] = {
3521 .prod_num = PORT_SWITCH_ID_PROD_NUM_6175,
3522 .family = MV88E6XXX_FAMILY_6351,
3523 .name = "Marvell 88E6175",
3524 .num_databases = 4096,
3525 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003526 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003527 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3528 },
3529
3530 [MV88E6176] = {
3531 .prod_num = PORT_SWITCH_ID_PROD_NUM_6176,
3532 .family = MV88E6XXX_FAMILY_6352,
3533 .name = "Marvell 88E6176",
3534 .num_databases = 4096,
3535 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003536 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003537 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3538 },
3539
3540 [MV88E6185] = {
3541 .prod_num = PORT_SWITCH_ID_PROD_NUM_6185,
3542 .family = MV88E6XXX_FAMILY_6185,
3543 .name = "Marvell 88E6185",
3544 .num_databases = 256,
3545 .num_ports = 10,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003546 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003547 .flags = MV88E6XXX_FLAGS_FAMILY_6185,
3548 },
3549
3550 [MV88E6240] = {
3551 .prod_num = PORT_SWITCH_ID_PROD_NUM_6240,
3552 .family = MV88E6XXX_FAMILY_6352,
3553 .name = "Marvell 88E6240",
3554 .num_databases = 4096,
3555 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003556 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003557 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3558 },
3559
3560 [MV88E6320] = {
3561 .prod_num = PORT_SWITCH_ID_PROD_NUM_6320,
3562 .family = MV88E6XXX_FAMILY_6320,
3563 .name = "Marvell 88E6320",
3564 .num_databases = 4096,
3565 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003566 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003567 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3568 },
3569
3570 [MV88E6321] = {
3571 .prod_num = PORT_SWITCH_ID_PROD_NUM_6321,
3572 .family = MV88E6XXX_FAMILY_6320,
3573 .name = "Marvell 88E6321",
3574 .num_databases = 4096,
3575 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003576 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003577 .flags = MV88E6XXX_FLAGS_FAMILY_6320,
3578 },
3579
3580 [MV88E6350] = {
3581 .prod_num = PORT_SWITCH_ID_PROD_NUM_6350,
3582 .family = MV88E6XXX_FAMILY_6351,
3583 .name = "Marvell 88E6350",
3584 .num_databases = 4096,
3585 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003586 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003587 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3588 },
3589
3590 [MV88E6351] = {
3591 .prod_num = PORT_SWITCH_ID_PROD_NUM_6351,
3592 .family = MV88E6XXX_FAMILY_6351,
3593 .name = "Marvell 88E6351",
3594 .num_databases = 4096,
3595 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003596 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003597 .flags = MV88E6XXX_FLAGS_FAMILY_6351,
3598 },
3599
3600 [MV88E6352] = {
3601 .prod_num = PORT_SWITCH_ID_PROD_NUM_6352,
3602 .family = MV88E6XXX_FAMILY_6352,
3603 .name = "Marvell 88E6352",
3604 .num_databases = 4096,
3605 .num_ports = 7,
Vivien Didelot9dddd472016-06-20 13:14:10 -04003606 .port_base_addr = 0x10,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003607 .flags = MV88E6XXX_FLAGS_FAMILY_6352,
3608 },
3609};
3610
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003611static const struct mv88e6xxx_info *mv88e6xxx_lookup_info(unsigned int prod_num)
Vivien Didelotb9b37712015-10-30 19:39:48 -04003612{
Vivien Didelota439c062016-04-17 13:23:58 -04003613 int i;
Vivien Didelotb9b37712015-10-30 19:39:48 -04003614
Vivien Didelot5f7c0362016-06-20 13:14:04 -04003615 for (i = 0; i < ARRAY_SIZE(mv88e6xxx_table); ++i)
3616 if (mv88e6xxx_table[i].prod_num == prod_num)
3617 return &mv88e6xxx_table[i];
Vivien Didelotb9b37712015-10-30 19:39:48 -04003618
Vivien Didelotb9b37712015-10-30 19:39:48 -04003619 return NULL;
3620}
3621
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003622static int mv88e6xxx_detect(struct mv88e6xxx_priv_state *ps)
3623{
3624 const struct mv88e6xxx_info *info;
3625 int id, prod_num, rev;
3626
Vivien Didelot9dddd472016-06-20 13:14:10 -04003627 id = mv88e6xxx_reg_read(ps, ps->info->port_base_addr, PORT_SWITCH_ID);
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003628 if (id < 0)
3629 return id;
3630
3631 prod_num = (id & 0xfff0) >> 4;
3632 rev = id & 0x000f;
3633
3634 info = mv88e6xxx_lookup_info(prod_num);
3635 if (!info)
3636 return -ENODEV;
3637
Vivien Didelotcaac8542016-06-20 13:14:09 -04003638 /* Update the compatible info with the probed one */
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003639 ps->info = info;
3640
3641 dev_info(ps->dev, "switch 0x%x detected: %s, revision %u\n",
3642 ps->info->prod_num, ps->info->name, rev);
3643
3644 return 0;
3645}
3646
Vivien Didelot469d7292016-06-20 13:14:06 -04003647static struct mv88e6xxx_priv_state *mv88e6xxx_alloc_chip(struct device *dev)
3648{
3649 struct mv88e6xxx_priv_state *ps;
3650
3651 ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL);
3652 if (!ps)
3653 return NULL;
3654
3655 ps->dev = dev;
3656
3657 mutex_init(&ps->reg_lock);
3658
3659 return ps;
3660}
3661
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003662static int mv88e6xxx_smi_init(struct mv88e6xxx_priv_state *ps,
3663 struct mii_bus *bus, int sw_addr)
3664{
3665 /* ADDR[0] pin is unavailable externally and considered zero */
3666 if (sw_addr & 0x1)
3667 return -EINVAL;
3668
3669 ps->bus = bus;
3670 ps->sw_addr = sw_addr;
3671
3672 return 0;
3673}
3674
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003675static const char *mv88e6xxx_drv_probe(struct device *dsa_dev,
3676 struct device *host_dev, int sw_addr,
3677 void **priv)
Andrew Lunna77d43f2016-04-13 02:40:42 +02003678{
3679 struct mv88e6xxx_priv_state *ps;
Vivien Didelota439c062016-04-17 13:23:58 -04003680 struct mii_bus *bus;
Andrew Lunnb516d452016-06-04 21:17:06 +02003681 int err;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003682
Vivien Didelota439c062016-04-17 13:23:58 -04003683 bus = dsa_host_dev_to_mii_bus(host_dev);
Andrew Lunnc1569132016-04-13 02:40:45 +02003684 if (!bus)
3685 return NULL;
3686
Vivien Didelot469d7292016-06-20 13:14:06 -04003687 ps = mv88e6xxx_alloc_chip(dsa_dev);
3688 if (!ps)
3689 return NULL;
3690
Vivien Didelotcaac8542016-06-20 13:14:09 -04003691 /* Legacy SMI probing will only support chips similar to 88E6085 */
3692 ps->info = &mv88e6xxx_table[MV88E6085];
3693
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003694 err = mv88e6xxx_smi_init(ps, bus, sw_addr);
3695 if (err)
3696 goto free;
3697
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003698 err = mv88e6xxx_detect(ps);
3699 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003700 goto free;
Vivien Didelota439c062016-04-17 13:23:58 -04003701
Andrew Lunnb516d452016-06-04 21:17:06 +02003702 err = mv88e6xxx_mdio_register(ps, NULL);
3703 if (err)
Vivien Didelot469d7292016-06-20 13:14:06 -04003704 goto free;
Andrew Lunnb516d452016-06-04 21:17:06 +02003705
Vivien Didelota439c062016-04-17 13:23:58 -04003706 *priv = ps;
3707
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003708 return ps->info->name;
Vivien Didelot469d7292016-06-20 13:14:06 -04003709free:
3710 devm_kfree(dsa_dev, ps);
3711
3712 return NULL;
Andrew Lunna77d43f2016-04-13 02:40:42 +02003713}
3714
Vivien Didelot57d32312016-06-20 13:13:58 -04003715static struct dsa_switch_driver mv88e6xxx_switch_driver = {
Vivien Didelotf81ec902016-05-09 13:22:58 -04003716 .tag_protocol = DSA_TAG_PROTO_EDSA,
Andrew Lunnfcdce7d2016-05-10 23:27:20 +02003717 .probe = mv88e6xxx_drv_probe,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003718 .setup = mv88e6xxx_setup,
3719 .set_addr = mv88e6xxx_set_addr,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003720 .adjust_link = mv88e6xxx_adjust_link,
3721 .get_strings = mv88e6xxx_get_strings,
3722 .get_ethtool_stats = mv88e6xxx_get_ethtool_stats,
3723 .get_sset_count = mv88e6xxx_get_sset_count,
3724 .set_eee = mv88e6xxx_set_eee,
3725 .get_eee = mv88e6xxx_get_eee,
3726#ifdef CONFIG_NET_DSA_HWMON
3727 .get_temp = mv88e6xxx_get_temp,
3728 .get_temp_limit = mv88e6xxx_get_temp_limit,
3729 .set_temp_limit = mv88e6xxx_set_temp_limit,
3730 .get_temp_alarm = mv88e6xxx_get_temp_alarm,
3731#endif
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003732 .get_eeprom_len = mv88e6xxx_get_eeprom_len,
Vivien Didelotf81ec902016-05-09 13:22:58 -04003733 .get_eeprom = mv88e6xxx_get_eeprom,
3734 .set_eeprom = mv88e6xxx_set_eeprom,
3735 .get_regs_len = mv88e6xxx_get_regs_len,
3736 .get_regs = mv88e6xxx_get_regs,
3737 .port_bridge_join = mv88e6xxx_port_bridge_join,
3738 .port_bridge_leave = mv88e6xxx_port_bridge_leave,
3739 .port_stp_state_set = mv88e6xxx_port_stp_state_set,
3740 .port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
3741 .port_vlan_prepare = mv88e6xxx_port_vlan_prepare,
3742 .port_vlan_add = mv88e6xxx_port_vlan_add,
3743 .port_vlan_del = mv88e6xxx_port_vlan_del,
3744 .port_vlan_dump = mv88e6xxx_port_vlan_dump,
3745 .port_fdb_prepare = mv88e6xxx_port_fdb_prepare,
3746 .port_fdb_add = mv88e6xxx_port_fdb_add,
3747 .port_fdb_del = mv88e6xxx_port_fdb_del,
3748 .port_fdb_dump = mv88e6xxx_port_fdb_dump,
3749};
3750
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003751static int mv88e6xxx_register_switch(struct mv88e6xxx_priv_state *ps,
3752 struct device_node *np)
3753{
3754 struct device *dev = ps->dev;
3755 struct dsa_switch *ds;
3756
3757 ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
3758 if (!ds)
3759 return -ENOMEM;
3760
3761 ds->dev = dev;
3762 ds->priv = ps;
3763 ds->drv = &mv88e6xxx_switch_driver;
3764
3765 dev_set_drvdata(dev, ds);
3766
3767 return dsa_register_switch(ds, np);
3768}
3769
3770static void mv88e6xxx_unregister_switch(struct mv88e6xxx_priv_state *ps)
3771{
3772 dsa_unregister_switch(ps->ds);
3773}
3774
Vivien Didelot57d32312016-06-20 13:13:58 -04003775static int mv88e6xxx_probe(struct mdio_device *mdiodev)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003776{
3777 struct device *dev = &mdiodev->dev;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003778 struct device_node *np = dev->of_node;
Vivien Didelotcaac8542016-06-20 13:14:09 -04003779 const struct mv88e6xxx_info *compat_info;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003780 struct mv88e6xxx_priv_state *ps;
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003781 u32 eeprom_len;
Andrew Lunn52638f72016-05-10 23:27:22 +02003782 int err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003783
Vivien Didelotcaac8542016-06-20 13:14:09 -04003784 compat_info = of_device_get_match_data(dev);
3785 if (!compat_info)
3786 return -EINVAL;
3787
Vivien Didelot469d7292016-06-20 13:14:06 -04003788 ps = mv88e6xxx_alloc_chip(dev);
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003789 if (!ps)
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003790 return -ENOMEM;
3791
Vivien Didelotcaac8542016-06-20 13:14:09 -04003792 ps->info = compat_info;
3793
Vivien Didelot4a70c4a2016-06-20 13:14:07 -04003794 err = mv88e6xxx_smi_init(ps, mdiodev->bus, mdiodev->addr);
3795 if (err)
3796 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003797
Vivien Didelotbc46a3d2016-06-20 13:14:08 -04003798 err = mv88e6xxx_detect(ps);
3799 if (err)
3800 return err;
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003801
Vivien Didelotc6d19ab2016-06-20 13:14:03 -04003802 ps->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
3803 if (IS_ERR(ps->reset))
3804 return PTR_ERR(ps->reset);
Andrew Lunn52638f72016-05-10 23:27:22 +02003805
Andrew Lunnf8cd8752016-05-10 23:27:25 +02003806 if (mv88e6xxx_has(ps, MV88E6XXX_FLAG_EEPROM) &&
3807 !of_property_read_u32(np, "eeprom-length", &eeprom_len))
3808 ps->eeprom_len = eeprom_len;
3809
Vivien Didelotaa8ac392016-06-20 13:14:00 -04003810 err = mv88e6xxx_mdio_register(ps, np);
Andrew Lunnb516d452016-06-04 21:17:06 +02003811 if (err)
3812 return err;
3813
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003814 err = mv88e6xxx_register_switch(ps, np);
Andrew Lunn83c0afa2016-06-04 21:17:07 +02003815 if (err) {
3816 mv88e6xxx_mdio_unregister(ps);
3817 return err;
3818 }
3819
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003820 return 0;
3821}
3822
3823static void mv88e6xxx_remove(struct mdio_device *mdiodev)
3824{
3825 struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
3826 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
3827
Vivien Didelotb7e66a52016-06-20 13:14:02 -04003828 mv88e6xxx_unregister_switch(ps);
Andrew Lunnb516d452016-06-04 21:17:06 +02003829 mv88e6xxx_mdio_unregister(ps);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003830}
3831
3832static const struct of_device_id mv88e6xxx_of_match[] = {
Vivien Didelotcaac8542016-06-20 13:14:09 -04003833 {
3834 .compatible = "marvell,mv88e6085",
3835 .data = &mv88e6xxx_table[MV88E6085],
3836 },
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003837 { /* sentinel */ },
3838};
3839
3840MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match);
3841
3842static struct mdio_driver mv88e6xxx_driver = {
3843 .probe = mv88e6xxx_probe,
3844 .remove = mv88e6xxx_remove,
3845 .mdiodrv.driver = {
3846 .name = "mv88e6085",
3847 .of_match_table = mv88e6xxx_of_match,
3848 },
3849};
3850
Ben Hutchings98e67302011-11-25 14:36:19 +00003851static int __init mv88e6xxx_init(void)
3852{
Vivien Didelotf81ec902016-05-09 13:22:58 -04003853 register_switch_driver(&mv88e6xxx_switch_driver);
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003854 return mdio_driver_register(&mv88e6xxx_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003855}
3856module_init(mv88e6xxx_init);
3857
3858static void __exit mv88e6xxx_cleanup(void)
3859{
Andrew Lunn14c7b3c2016-05-10 23:27:21 +02003860 mdio_driver_unregister(&mv88e6xxx_driver);
Vivien Didelotf81ec902016-05-09 13:22:58 -04003861 unregister_switch_driver(&mv88e6xxx_switch_driver);
Ben Hutchings98e67302011-11-25 14:36:19 +00003862}
3863module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00003864
3865MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
3866MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
3867MODULE_LICENSE("GPL");