blob: fc8d3b6ffe8e0b35e64ed2aaa78b342a1b86f66a [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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
Barry Grussling19b2f972013-01-08 16:05:54 +000011#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070012#include <linux/etherdevice.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070013#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000014#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000015#include <linux/list.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000016#include <linux/module.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000017#include <linux/netdevice.h>
18#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000019#include <net/dsa.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000020#include "mv88e6xxx.h"
21
Barry Grussling3675c8d2013-01-08 16:05:53 +000022/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000023 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
24 * will be directly accessible on some {device address,register address}
25 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
26 * will only respond to SMI transactions to that specific address, and
27 * an indirect addressing mechanism needs to be used to access its
28 * registers.
29 */
30static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
31{
32 int ret;
33 int i;
34
35 for (i = 0; i < 16; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +020036 ret = mdiobus_read(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000037 if (ret < 0)
38 return ret;
39
Andrew Lunncca8b132015-04-02 04:06:39 +020040 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000041 return 0;
42 }
43
44 return -ETIMEDOUT;
45}
46
47int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
48{
49 int ret;
50
51 if (sw_addr == 0)
52 return mdiobus_read(bus, addr, reg);
53
Barry Grussling3675c8d2013-01-08 16:05:53 +000054 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000055 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
56 if (ret < 0)
57 return ret;
58
Barry Grussling3675c8d2013-01-08 16:05:53 +000059 /* Transmit the read command. */
Andrew Lunncca8b132015-04-02 04:06:39 +020060 ret = mdiobus_write(bus, sw_addr, SMI_CMD,
61 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000062 if (ret < 0)
63 return ret;
64
Barry Grussling3675c8d2013-01-08 16:05:53 +000065 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000066 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
67 if (ret < 0)
68 return ret;
69
Barry Grussling3675c8d2013-01-08 16:05:53 +000070 /* Read the data. */
Andrew Lunncca8b132015-04-02 04:06:39 +020071 ret = mdiobus_read(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000072 if (ret < 0)
73 return ret;
74
75 return ret & 0xffff;
76}
77
Guenter Roeck8d6d09e2015-03-26 18:36:31 -070078/* Must be called with SMI mutex held */
79static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000080{
Guenter Roeckb184e492014-10-17 12:30:58 -070081 struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000082 int ret;
83
Guenter Roeckb184e492014-10-17 12:30:58 -070084 if (bus == NULL)
85 return -EINVAL;
86
Guenter Roeckb184e492014-10-17 12:30:58 -070087 ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -050088 if (ret < 0)
89 return ret;
90
91 dev_dbg(ds->master_dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
92 addr, reg, ret);
93
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000094 return ret;
95}
96
Guenter Roeck8d6d09e2015-03-26 18:36:31 -070097int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
98{
99 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
100 int ret;
101
102 mutex_lock(&ps->smi_mutex);
103 ret = _mv88e6xxx_reg_read(ds, addr, reg);
104 mutex_unlock(&ps->smi_mutex);
105
106 return ret;
107}
108
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000109int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
110 int reg, u16 val)
111{
112 int ret;
113
114 if (sw_addr == 0)
115 return mdiobus_write(bus, addr, reg, val);
116
Barry Grussling3675c8d2013-01-08 16:05:53 +0000117 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000118 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
119 if (ret < 0)
120 return ret;
121
Barry Grussling3675c8d2013-01-08 16:05:53 +0000122 /* Transmit the data to write. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200123 ret = mdiobus_write(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000124 if (ret < 0)
125 return ret;
126
Barry Grussling3675c8d2013-01-08 16:05:53 +0000127 /* Transmit the write command. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200128 ret = mdiobus_write(bus, sw_addr, SMI_CMD,
129 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000130 if (ret < 0)
131 return ret;
132
Barry Grussling3675c8d2013-01-08 16:05:53 +0000133 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000134 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
135 if (ret < 0)
136 return ret;
137
138 return 0;
139}
140
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700141/* Must be called with SMI mutex held */
142static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg,
143 u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000144{
Guenter Roeckb184e492014-10-17 12:30:58 -0700145 struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000146
Guenter Roeckb184e492014-10-17 12:30:58 -0700147 if (bus == NULL)
148 return -EINVAL;
149
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500150 dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
151 addr, reg, val);
152
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700153 return __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val);
154}
155
156int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
157{
158 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
159 int ret;
160
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000161 mutex_lock(&ps->smi_mutex);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700162 ret = _mv88e6xxx_reg_write(ds, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000163 mutex_unlock(&ps->smi_mutex);
164
165 return ret;
166}
167
168int mv88e6xxx_config_prio(struct dsa_switch *ds)
169{
Barry Grussling3675c8d2013-01-08 16:05:53 +0000170 /* Configure the IP ToS mapping registers. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200171 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
172 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
173 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
174 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
175 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
176 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
177 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
178 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000179
Barry Grussling3675c8d2013-01-08 16:05:53 +0000180 /* Configure the IEEE 802.1p priority mapping register. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200181 REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000182
183 return 0;
184}
185
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000186int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
187{
Andrew Lunncca8b132015-04-02 04:06:39 +0200188 REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
189 REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
190 REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000191
192 return 0;
193}
194
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000195int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
196{
197 int i;
198 int ret;
199
200 for (i = 0; i < 6; i++) {
201 int j;
202
Barry Grussling3675c8d2013-01-08 16:05:53 +0000203 /* Write the MAC address byte. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200204 REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
205 GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000206
Barry Grussling3675c8d2013-01-08 16:05:53 +0000207 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000208 for (j = 0; j < 16; j++) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200209 ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC);
210 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000211 break;
212 }
213 if (j == 16)
214 return -ETIMEDOUT;
215 }
216
217 return 0;
218}
219
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200220/* Must be called with phy mutex held */
221static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000222{
223 if (addr >= 0)
224 return mv88e6xxx_reg_read(ds, addr, regnum);
225 return 0xffff;
226}
227
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200228/* Must be called with phy mutex held */
229static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
230 u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000231{
232 if (addr >= 0)
233 return mv88e6xxx_reg_write(ds, addr, regnum, val);
234 return 0;
235}
236
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000237#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
238static int mv88e6xxx_ppu_disable(struct dsa_switch *ds)
239{
240 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000241 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000242
Andrew Lunncca8b132015-04-02 04:06:39 +0200243 ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
244 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
245 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000246
Barry Grussling19b2f972013-01-08 16:05:54 +0000247 timeout = jiffies + 1 * HZ;
248 while (time_before(jiffies, timeout)) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200249 ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
Barry Grussling19b2f972013-01-08 16:05:54 +0000250 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200251 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
252 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000253 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000254 }
255
256 return -ETIMEDOUT;
257}
258
259static int mv88e6xxx_ppu_enable(struct dsa_switch *ds)
260{
261 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000262 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000263
Andrew Lunncca8b132015-04-02 04:06:39 +0200264 ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
265 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000266
Barry Grussling19b2f972013-01-08 16:05:54 +0000267 timeout = jiffies + 1 * HZ;
268 while (time_before(jiffies, timeout)) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200269 ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
Barry Grussling19b2f972013-01-08 16:05:54 +0000270 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200271 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
272 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000273 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000274 }
275
276 return -ETIMEDOUT;
277}
278
279static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
280{
281 struct mv88e6xxx_priv_state *ps;
282
283 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
284 if (mutex_trylock(&ps->ppu_mutex)) {
Barry Grussling85686582013-01-08 16:05:56 +0000285 struct dsa_switch *ds = ((struct dsa_switch *)ps) - 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000286
Barry Grussling85686582013-01-08 16:05:56 +0000287 if (mv88e6xxx_ppu_enable(ds) == 0)
288 ps->ppu_disabled = 0;
289 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000290 }
291}
292
293static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
294{
295 struct mv88e6xxx_priv_state *ps = (void *)_ps;
296
297 schedule_work(&ps->ppu_work);
298}
299
300static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
301{
Florian Fainellia22adce2014-04-28 11:14:28 -0700302 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000303 int ret;
304
305 mutex_lock(&ps->ppu_mutex);
306
Barry Grussling3675c8d2013-01-08 16:05:53 +0000307 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000308 * we can access the PHY registers. If it was already
309 * disabled, cancel the timer that is going to re-enable
310 * it.
311 */
312 if (!ps->ppu_disabled) {
Barry Grussling85686582013-01-08 16:05:56 +0000313 ret = mv88e6xxx_ppu_disable(ds);
314 if (ret < 0) {
315 mutex_unlock(&ps->ppu_mutex);
316 return ret;
317 }
318 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000319 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000320 del_timer(&ps->ppu_timer);
321 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000322 }
323
324 return ret;
325}
326
327static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
328{
Florian Fainellia22adce2014-04-28 11:14:28 -0700329 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000330
Barry Grussling3675c8d2013-01-08 16:05:53 +0000331 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000332 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
333 mutex_unlock(&ps->ppu_mutex);
334}
335
336void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
337{
Florian Fainellia22adce2014-04-28 11:14:28 -0700338 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000339
340 mutex_init(&ps->ppu_mutex);
341 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
342 init_timer(&ps->ppu_timer);
343 ps->ppu_timer.data = (unsigned long)ps;
344 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
345}
346
347int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum)
348{
349 int ret;
350
351 ret = mv88e6xxx_ppu_access_get(ds);
352 if (ret >= 0) {
Barry Grussling85686582013-01-08 16:05:56 +0000353 ret = mv88e6xxx_reg_read(ds, addr, regnum);
354 mv88e6xxx_ppu_access_put(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000355 }
356
357 return ret;
358}
359
360int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
361 int regnum, u16 val)
362{
363 int ret;
364
365 ret = mv88e6xxx_ppu_access_get(ds);
366 if (ret >= 0) {
Barry Grussling85686582013-01-08 16:05:56 +0000367 ret = mv88e6xxx_reg_write(ds, addr, regnum, val);
368 mv88e6xxx_ppu_access_put(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000369 }
370
371 return ret;
372}
373#endif
374
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000375void mv88e6xxx_poll_link(struct dsa_switch *ds)
376{
377 int i;
378
379 for (i = 0; i < DSA_MAX_PORTS; i++) {
380 struct net_device *dev;
Ingo Molnar2a9e7972008-11-25 16:50:49 -0800381 int uninitialized_var(port_status);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000382 int link;
383 int speed;
384 int duplex;
385 int fc;
386
387 dev = ds->ports[i];
388 if (dev == NULL)
389 continue;
390
391 link = 0;
392 if (dev->flags & IFF_UP) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200393 port_status = mv88e6xxx_reg_read(ds, REG_PORT(i),
394 PORT_STATUS);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000395 if (port_status < 0)
396 continue;
397
Andrew Lunncca8b132015-04-02 04:06:39 +0200398 link = !!(port_status & PORT_STATUS_LINK);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000399 }
400
401 if (!link) {
402 if (netif_carrier_ok(dev)) {
Barry Grusslingab381a92013-01-08 16:05:55 +0000403 netdev_info(dev, "link down\n");
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000404 netif_carrier_off(dev);
405 }
406 continue;
407 }
408
Andrew Lunncca8b132015-04-02 04:06:39 +0200409 switch (port_status & PORT_STATUS_SPEED_MASK) {
410 case PORT_STATUS_SPEED_10:
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000411 speed = 10;
412 break;
Andrew Lunncca8b132015-04-02 04:06:39 +0200413 case PORT_STATUS_SPEED_100:
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000414 speed = 100;
415 break;
Andrew Lunncca8b132015-04-02 04:06:39 +0200416 case PORT_STATUS_SPEED_1000:
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000417 speed = 1000;
418 break;
419 default:
420 speed = -1;
421 break;
422 }
Andrew Lunncca8b132015-04-02 04:06:39 +0200423 duplex = (port_status & PORT_STATUS_DUPLEX) ? 1 : 0;
424 fc = (port_status & PORT_STATUS_PAUSE_EN) ? 1 : 0;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000425
426 if (!netif_carrier_ok(dev)) {
Barry Grusslingab381a92013-01-08 16:05:55 +0000427 netdev_info(dev,
428 "link up, %d Mb/s, %s duplex, flow control %sabled\n",
429 speed,
430 duplex ? "full" : "half",
431 fc ? "en" : "dis");
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000432 netif_carrier_on(dev);
433 }
434 }
435}
436
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200437static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
438{
439 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
440
441 switch (ps->id) {
442 case PORT_SWITCH_ID_6352:
443 case PORT_SWITCH_ID_6172:
444 case PORT_SWITCH_ID_6176:
445 return true;
446 }
447 return false;
448}
449
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000450static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
451{
452 int ret;
453 int i;
454
455 for (i = 0; i < 10; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200456 ret = REG_READ(REG_GLOBAL, GLOBAL_STATS_OP);
457 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000458 return 0;
459 }
460
461 return -ETIMEDOUT;
462}
463
464static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
465{
466 int ret;
467
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200468 if (mv88e6xxx_6352_family(ds))
469 port = (port + 1) << 5;
470
Barry Grussling3675c8d2013-01-08 16:05:53 +0000471 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200472 REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP,
473 GLOBAL_STATS_OP_CAPTURE_PORT |
474 GLOBAL_STATS_OP_HIST_RX_TX | port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000475
Barry Grussling3675c8d2013-01-08 16:05:53 +0000476 /* Wait for the snapshotting to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000477 ret = mv88e6xxx_stats_wait(ds);
478 if (ret < 0)
479 return ret;
480
481 return 0;
482}
483
484static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
485{
486 u32 _val;
487 int ret;
488
489 *val = 0;
490
Andrew Lunncca8b132015-04-02 04:06:39 +0200491 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
492 GLOBAL_STATS_OP_READ_CAPTURED |
493 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000494 if (ret < 0)
495 return;
496
497 ret = mv88e6xxx_stats_wait(ds);
498 if (ret < 0)
499 return;
500
Andrew Lunncca8b132015-04-02 04:06:39 +0200501 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000502 if (ret < 0)
503 return;
504
505 _val = ret << 16;
506
Andrew Lunncca8b132015-04-02 04:06:39 +0200507 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000508 if (ret < 0)
509 return;
510
511 *val = _val | ret;
512}
513
Andrew Lunne413e7e2015-04-02 04:06:38 +0200514static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
515 { "in_good_octets", 8, 0x00, },
516 { "in_bad_octets", 4, 0x02, },
517 { "in_unicast", 4, 0x04, },
518 { "in_broadcasts", 4, 0x06, },
519 { "in_multicasts", 4, 0x07, },
520 { "in_pause", 4, 0x16, },
521 { "in_undersize", 4, 0x18, },
522 { "in_fragments", 4, 0x19, },
523 { "in_oversize", 4, 0x1a, },
524 { "in_jabber", 4, 0x1b, },
525 { "in_rx_error", 4, 0x1c, },
526 { "in_fcs_error", 4, 0x1d, },
527 { "out_octets", 8, 0x0e, },
528 { "out_unicast", 4, 0x10, },
529 { "out_broadcasts", 4, 0x13, },
530 { "out_multicasts", 4, 0x12, },
531 { "out_pause", 4, 0x15, },
532 { "excessive", 4, 0x11, },
533 { "collisions", 4, 0x1e, },
534 { "deferred", 4, 0x05, },
535 { "single", 4, 0x14, },
536 { "multiple", 4, 0x17, },
537 { "out_fcs_error", 4, 0x03, },
538 { "late", 4, 0x1f, },
539 { "hist_64bytes", 4, 0x08, },
540 { "hist_65_127bytes", 4, 0x09, },
541 { "hist_128_255bytes", 4, 0x0a, },
542 { "hist_256_511bytes", 4, 0x0b, },
543 { "hist_512_1023bytes", 4, 0x0c, },
544 { "hist_1024_max_bytes", 4, 0x0d, },
545 /* Not all devices have the following counters */
546 { "sw_in_discards", 4, 0x110, },
547 { "sw_in_filtered", 2, 0x112, },
548 { "sw_out_filtered", 2, 0x113, },
549
550};
551
552static bool have_sw_in_discards(struct dsa_switch *ds)
553{
554 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
555
556 switch (ps->id) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200557 case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
558 case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
559 case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
560 case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
561 case PORT_SWITCH_ID_6352:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200562 return true;
563 default:
564 return false;
565 }
566}
567
568static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
569 int nr_stats,
570 struct mv88e6xxx_hw_stat *stats,
571 int port, uint8_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000572{
573 int i;
574
575 for (i = 0; i < nr_stats; i++) {
576 memcpy(data + i * ETH_GSTRING_LEN,
577 stats[i].string, ETH_GSTRING_LEN);
578 }
579}
580
Andrew Lunne413e7e2015-04-02 04:06:38 +0200581static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
582 int nr_stats,
583 struct mv88e6xxx_hw_stat *stats,
584 int port, uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000585{
Florian Fainellia22adce2014-04-28 11:14:28 -0700586 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000587 int ret;
588 int i;
589
590 mutex_lock(&ps->stats_mutex);
591
592 ret = mv88e6xxx_stats_snapshot(ds, port);
593 if (ret < 0) {
594 mutex_unlock(&ps->stats_mutex);
595 return;
596 }
597
Barry Grussling3675c8d2013-01-08 16:05:53 +0000598 /* Read each of the counters. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000599 for (i = 0; i < nr_stats; i++) {
600 struct mv88e6xxx_hw_stat *s = stats + i;
601 u32 low;
Guenter Roeck17ee3e02014-10-29 10:45:07 -0700602 u32 high = 0;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000603
Guenter Roeck17ee3e02014-10-29 10:45:07 -0700604 if (s->reg >= 0x100) {
605 int ret;
606
607 ret = mv88e6xxx_reg_read(ds, REG_PORT(port),
608 s->reg - 0x100);
609 if (ret < 0)
610 goto error;
611 low = ret;
612 if (s->sizeof_stat == 4) {
613 ret = mv88e6xxx_reg_read(ds, REG_PORT(port),
614 s->reg - 0x100 + 1);
615 if (ret < 0)
616 goto error;
617 high = ret;
618 }
619 data[i] = (((u64)high) << 16) | low;
620 continue;
621 }
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000622 mv88e6xxx_stats_read(ds, s->reg, &low);
623 if (s->sizeof_stat == 8)
624 mv88e6xxx_stats_read(ds, s->reg + 1, &high);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000625
626 data[i] = (((u64)high) << 32) | low;
627 }
Guenter Roeck17ee3e02014-10-29 10:45:07 -0700628error:
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000629 mutex_unlock(&ps->stats_mutex);
630}
Ben Hutchings98e67302011-11-25 14:36:19 +0000631
Andrew Lunne413e7e2015-04-02 04:06:38 +0200632/* All the statistics in the table */
633void
634mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
635{
636 if (have_sw_in_discards(ds))
637 _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
638 mv88e6xxx_hw_stats, port, data);
639 else
640 _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
641 mv88e6xxx_hw_stats, port, data);
642}
643
644int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
645{
646 if (have_sw_in_discards(ds))
647 return ARRAY_SIZE(mv88e6xxx_hw_stats);
648 return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
649}
650
651void
652mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
653 int port, uint64_t *data)
654{
655 if (have_sw_in_discards(ds))
656 _mv88e6xxx_get_ethtool_stats(
657 ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
658 mv88e6xxx_hw_stats, port, data);
659 else
660 _mv88e6xxx_get_ethtool_stats(
661 ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
662 mv88e6xxx_hw_stats, port, data);
663}
664
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700665int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
666{
667 return 32 * sizeof(u16);
668}
669
670void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
671 struct ethtool_regs *regs, void *_p)
672{
673 u16 *p = _p;
674 int i;
675
676 regs->version = 0;
677
678 memset(p, 0xff, 32 * sizeof(u16));
679
680 for (i = 0; i < 32; i++) {
681 int ret;
682
683 ret = mv88e6xxx_reg_read(ds, REG_PORT(port), i);
684 if (ret >= 0)
685 p[i] = ret;
686 }
687}
688
Andrew Lunneaa23762014-11-15 22:24:51 +0100689#ifdef CONFIG_NET_DSA_HWMON
690
691int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
692{
693 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
694 int ret;
695 int val;
696
697 *temp = 0;
698
699 mutex_lock(&ps->phy_mutex);
700
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200701 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
Andrew Lunneaa23762014-11-15 22:24:51 +0100702 if (ret < 0)
703 goto error;
704
705 /* Enable temperature sensor */
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200706 ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
Andrew Lunneaa23762014-11-15 22:24:51 +0100707 if (ret < 0)
708 goto error;
709
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200710 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
Andrew Lunneaa23762014-11-15 22:24:51 +0100711 if (ret < 0)
712 goto error;
713
714 /* Wait for temperature to stabilize */
715 usleep_range(10000, 12000);
716
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200717 val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
Andrew Lunneaa23762014-11-15 22:24:51 +0100718 if (val < 0) {
719 ret = val;
720 goto error;
721 }
722
723 /* Disable temperature sensor */
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200724 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
Andrew Lunneaa23762014-11-15 22:24:51 +0100725 if (ret < 0)
726 goto error;
727
728 *temp = ((val & 0x1f) - 5) * 5;
729
730error:
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200731 _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
Andrew Lunneaa23762014-11-15 22:24:51 +0100732 mutex_unlock(&ps->phy_mutex);
733 return ret;
734}
735#endif /* CONFIG_NET_DSA_HWMON */
736
Andrew Lunnf3044682015-02-14 19:17:50 +0100737static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
738{
739 unsigned long timeout = jiffies + HZ / 10;
740
741 while (time_before(jiffies, timeout)) {
742 int ret;
743
744 ret = REG_READ(reg, offset);
745 if (!(ret & mask))
746 return 0;
747
748 usleep_range(1000, 2000);
749 }
750 return -ETIMEDOUT;
751}
752
753int mv88e6xxx_phy_wait(struct dsa_switch *ds)
754{
Andrew Lunncca8b132015-04-02 04:06:39 +0200755 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
756 GLOBAL2_SMI_OP_BUSY);
Andrew Lunnf3044682015-02-14 19:17:50 +0100757}
758
759int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
760{
Andrew Lunncca8b132015-04-02 04:06:39 +0200761 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
762 GLOBAL2_EEPROM_OP_LOAD);
Andrew Lunnf3044682015-02-14 19:17:50 +0100763}
764
765int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
766{
Andrew Lunncca8b132015-04-02 04:06:39 +0200767 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
768 GLOBAL2_EEPROM_OP_BUSY);
Andrew Lunnf3044682015-02-14 19:17:50 +0100769}
770
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700771/* Must be called with SMI lock held */
772static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
773{
774 unsigned long timeout = jiffies + HZ / 10;
775
776 while (time_before(jiffies, timeout)) {
777 int ret;
778
779 ret = _mv88e6xxx_reg_read(ds, reg, offset);
780 if (ret < 0)
781 return ret;
782 if (!(ret & mask))
783 return 0;
784
785 usleep_range(1000, 2000);
786 }
787 return -ETIMEDOUT;
788}
789
790/* Must be called with SMI lock held */
791static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
792{
Andrew Lunncca8b132015-04-02 04:06:39 +0200793 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
794 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700795}
796
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200797/* Must be called with phy mutex held */
798static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
799 int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +0100800{
801 int ret;
802
Andrew Lunncca8b132015-04-02 04:06:39 +0200803 REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
804 GLOBAL2_SMI_OP_22_READ | (addr << 5) | regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +0100805
806 ret = mv88e6xxx_phy_wait(ds);
807 if (ret < 0)
808 return ret;
809
Andrew Lunncca8b132015-04-02 04:06:39 +0200810 return REG_READ(REG_GLOBAL2, GLOBAL2_SMI_DATA);
Andrew Lunnf3044682015-02-14 19:17:50 +0100811}
812
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200813/* Must be called with phy mutex held */
814static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
815 int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +0100816{
Andrew Lunncca8b132015-04-02 04:06:39 +0200817 REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
818 REG_WRITE(REG_GLOBAL2, GLOBAL2_SMI_OP,
819 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) | regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +0100820
821 return mv88e6xxx_phy_wait(ds);
822}
823
Guenter Roeck11b3b452015-03-06 22:23:51 -0800824int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
825{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200826 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800827 int reg;
828
Andrew Lunn2f40c692015-04-02 04:06:37 +0200829 mutex_lock(&ps->phy_mutex);
830
831 reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800832 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200833 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800834
835 e->eee_enabled = !!(reg & 0x0200);
836 e->tx_lpi_enabled = !!(reg & 0x0100);
837
Andrew Lunncca8b132015-04-02 04:06:39 +0200838 reg = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800839 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200840 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800841
Andrew Lunncca8b132015-04-02 04:06:39 +0200842 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200843 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800844
Andrew Lunn2f40c692015-04-02 04:06:37 +0200845out:
846 mutex_unlock(&ps->phy_mutex);
847 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800848}
849
850int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
851 struct phy_device *phydev, struct ethtool_eee *e)
852{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200853 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
854 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800855 int ret;
856
Andrew Lunn2f40c692015-04-02 04:06:37 +0200857 mutex_lock(&ps->phy_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800858
Andrew Lunn2f40c692015-04-02 04:06:37 +0200859 ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
860 if (ret < 0)
861 goto out;
862
863 reg = ret & ~0x0300;
864 if (e->eee_enabled)
865 reg |= 0x0200;
866 if (e->tx_lpi_enabled)
867 reg |= 0x0100;
868
869 ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
870out:
871 mutex_unlock(&ps->phy_mutex);
872
873 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800874}
875
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700876static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, int fid, u16 cmd)
877{
878 int ret;
879
880 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x01, fid);
881 if (ret < 0)
882 return ret;
883
Andrew Lunncca8b132015-04-02 04:06:39 +0200884 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700885 if (ret < 0)
886 return ret;
887
888 return _mv88e6xxx_atu_wait(ds);
889}
890
891static int _mv88e6xxx_flush_fid(struct dsa_switch *ds, int fid)
892{
893 int ret;
894
895 ret = _mv88e6xxx_atu_wait(ds);
896 if (ret < 0)
897 return ret;
898
Andrew Lunncca8b132015-04-02 04:06:39 +0200899 return _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_FLUSH_NON_STATIC_DB);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700900}
901
902static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
903{
904 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
905 int reg, ret;
906 u8 oldstate;
907
908 mutex_lock(&ps->smi_mutex);
909
Andrew Lunncca8b132015-04-02 04:06:39 +0200910 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700911 if (reg < 0)
912 goto abort;
913
Andrew Lunncca8b132015-04-02 04:06:39 +0200914 oldstate = reg & PORT_CONTROL_STATE_MASK;
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700915 if (oldstate != state) {
916 /* Flush forwarding database if we're moving a port
917 * from Learning or Forwarding state to Disabled or
918 * Blocking or Listening state.
919 */
Andrew Lunncca8b132015-04-02 04:06:39 +0200920 if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
921 state <= PORT_CONTROL_STATE_BLOCKING) {
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700922 ret = _mv88e6xxx_flush_fid(ds, ps->fid[port]);
923 if (ret)
924 goto abort;
925 }
Andrew Lunncca8b132015-04-02 04:06:39 +0200926 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
927 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
928 reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700929 }
930
931abort:
932 mutex_unlock(&ps->smi_mutex);
933 return ret;
934}
935
936/* Must be called with smi lock held */
937static int _mv88e6xxx_update_port_config(struct dsa_switch *ds, int port)
938{
939 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
940 u8 fid = ps->fid[port];
941 u16 reg = fid << 12;
942
943 if (dsa_is_cpu_port(ds, port))
944 reg |= ds->phys_port_mask;
945 else
946 reg |= (ps->bridge_mask[fid] |
947 (1 << dsa_upstream_port(ds))) & ~(1 << port);
948
Andrew Lunncca8b132015-04-02 04:06:39 +0200949 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700950}
951
952/* Must be called with smi lock held */
953static int _mv88e6xxx_update_bridge_config(struct dsa_switch *ds, int fid)
954{
955 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
956 int port;
957 u32 mask;
958 int ret;
959
960 mask = ds->phys_port_mask;
961 while (mask) {
962 port = __ffs(mask);
963 mask &= ~(1 << port);
964 if (ps->fid[port] != fid)
965 continue;
966
967 ret = _mv88e6xxx_update_port_config(ds, port);
968 if (ret)
969 return ret;
970 }
971
972 return _mv88e6xxx_flush_fid(ds, fid);
973}
974
975/* Bridge handling functions */
976
977int mv88e6xxx_join_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
978{
979 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
980 int ret = 0;
981 u32 nmask;
982 int fid;
983
984 /* If the bridge group is not empty, join that group.
985 * Otherwise create a new group.
986 */
987 fid = ps->fid[port];
988 nmask = br_port_mask & ~(1 << port);
989 if (nmask)
990 fid = ps->fid[__ffs(nmask)];
991
992 nmask = ps->bridge_mask[fid] | (1 << port);
993 if (nmask != br_port_mask) {
994 netdev_err(ds->ports[port],
995 "join: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
996 fid, br_port_mask, nmask);
997 return -EINVAL;
998 }
999
1000 mutex_lock(&ps->smi_mutex);
1001
1002 ps->bridge_mask[fid] = br_port_mask;
1003
1004 if (fid != ps->fid[port]) {
1005 ps->fid_mask |= 1 << ps->fid[port];
1006 ps->fid[port] = fid;
1007 ret = _mv88e6xxx_update_bridge_config(ds, fid);
1008 }
1009
1010 mutex_unlock(&ps->smi_mutex);
1011
1012 return ret;
1013}
1014
1015int mv88e6xxx_leave_bridge(struct dsa_switch *ds, int port, u32 br_port_mask)
1016{
1017 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1018 u8 fid, newfid;
1019 int ret;
1020
1021 fid = ps->fid[port];
1022
1023 if (ps->bridge_mask[fid] != br_port_mask) {
1024 netdev_err(ds->ports[port],
1025 "leave: Bridge port mask mismatch fid=%d mask=0x%x expected 0x%x\n",
1026 fid, br_port_mask, ps->bridge_mask[fid]);
1027 return -EINVAL;
1028 }
1029
1030 /* If the port was the last port of a bridge, we are done.
1031 * Otherwise assign a new fid to the port, and fix up
1032 * the bridge configuration.
1033 */
1034 if (br_port_mask == (1 << port))
1035 return 0;
1036
1037 mutex_lock(&ps->smi_mutex);
1038
1039 newfid = __ffs(ps->fid_mask);
1040 ps->fid[port] = newfid;
1041 ps->fid_mask &= (1 << newfid);
1042 ps->bridge_mask[fid] &= ~(1 << port);
1043 ps->bridge_mask[newfid] = 1 << port;
1044
1045 ret = _mv88e6xxx_update_bridge_config(ds, fid);
1046 if (!ret)
1047 ret = _mv88e6xxx_update_bridge_config(ds, newfid);
1048
1049 mutex_unlock(&ps->smi_mutex);
1050
1051 return ret;
1052}
1053
1054int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
1055{
1056 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1057 int stp_state;
1058
1059 switch (state) {
1060 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001061 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001062 break;
1063 case BR_STATE_BLOCKING:
1064 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001065 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001066 break;
1067 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001068 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001069 break;
1070 case BR_STATE_FORWARDING:
1071 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001072 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001073 break;
1074 }
1075
1076 netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state);
1077
1078 /* mv88e6xxx_port_stp_update may be called with softirqs disabled,
1079 * so we can not update the port state directly but need to schedule it.
1080 */
1081 ps->port_state[port] = stp_state;
1082 set_bit(port, &ps->port_state_update_mask);
1083 schedule_work(&ps->bridge_work);
1084
1085 return 0;
1086}
1087
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001088static int __mv88e6xxx_write_addr(struct dsa_switch *ds,
1089 const unsigned char *addr)
1090{
1091 int i, ret;
1092
1093 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001094 ret = _mv88e6xxx_reg_write(
1095 ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
1096 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001097 if (ret < 0)
1098 return ret;
1099 }
1100
1101 return 0;
1102}
1103
1104static int __mv88e6xxx_read_addr(struct dsa_switch *ds, unsigned char *addr)
1105{
1106 int i, ret;
1107
1108 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001109 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1110 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001111 if (ret < 0)
1112 return ret;
1113 addr[i * 2] = ret >> 8;
1114 addr[i * 2 + 1] = ret & 0xff;
1115 }
1116
1117 return 0;
1118}
1119
1120static int __mv88e6xxx_port_fdb_cmd(struct dsa_switch *ds, int port,
1121 const unsigned char *addr, int state)
1122{
1123 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1124 u8 fid = ps->fid[port];
1125 int ret;
1126
1127 ret = _mv88e6xxx_atu_wait(ds);
1128 if (ret < 0)
1129 return ret;
1130
1131 ret = __mv88e6xxx_write_addr(ds, addr);
1132 if (ret < 0)
1133 return ret;
1134
Andrew Lunncca8b132015-04-02 04:06:39 +02001135 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA,
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001136 (0x10 << port) | state);
1137 if (ret)
1138 return ret;
1139
Andrew Lunncca8b132015-04-02 04:06:39 +02001140 ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_LOAD_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001141
1142 return ret;
1143}
1144
1145int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
1146 const unsigned char *addr, u16 vid)
1147{
1148 int state = is_multicast_ether_addr(addr) ?
Andrew Lunncca8b132015-04-02 04:06:39 +02001149 GLOBAL_ATU_DATA_STATE_MC_STATIC :
1150 GLOBAL_ATU_DATA_STATE_UC_STATIC;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001151 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1152 int ret;
1153
1154 mutex_lock(&ps->smi_mutex);
1155 ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr, state);
1156 mutex_unlock(&ps->smi_mutex);
1157
1158 return ret;
1159}
1160
1161int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
1162 const unsigned char *addr, u16 vid)
1163{
1164 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1165 int ret;
1166
1167 mutex_lock(&ps->smi_mutex);
Andrew Lunncca8b132015-04-02 04:06:39 +02001168 ret = __mv88e6xxx_port_fdb_cmd(ds, port, addr,
1169 GLOBAL_ATU_DATA_STATE_UNUSED);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001170 mutex_unlock(&ps->smi_mutex);
1171
1172 return ret;
1173}
1174
1175static int __mv88e6xxx_port_getnext(struct dsa_switch *ds, int port,
1176 unsigned char *addr, bool *is_static)
1177{
1178 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1179 u8 fid = ps->fid[port];
1180 int ret, state;
1181
1182 ret = _mv88e6xxx_atu_wait(ds);
1183 if (ret < 0)
1184 return ret;
1185
1186 ret = __mv88e6xxx_write_addr(ds, addr);
1187 if (ret < 0)
1188 return ret;
1189
1190 do {
Andrew Lunncca8b132015-04-02 04:06:39 +02001191 ret = _mv88e6xxx_atu_cmd(ds, fid, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001192 if (ret < 0)
1193 return ret;
1194
Andrew Lunncca8b132015-04-02 04:06:39 +02001195 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001196 if (ret < 0)
1197 return ret;
Andrew Lunncca8b132015-04-02 04:06:39 +02001198 state = ret & GLOBAL_ATU_DATA_STATE_MASK;
1199 if (state == GLOBAL_ATU_DATA_STATE_UNUSED)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001200 return -ENOENT;
1201 } while (!(((ret >> 4) & 0xff) & (1 << port)));
1202
1203 ret = __mv88e6xxx_read_addr(ds, addr);
1204 if (ret < 0)
1205 return ret;
1206
1207 *is_static = state == (is_multicast_ether_addr(addr) ?
Andrew Lunncca8b132015-04-02 04:06:39 +02001208 GLOBAL_ATU_DATA_STATE_MC_STATIC :
1209 GLOBAL_ATU_DATA_STATE_UC_STATIC);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001210
1211 return 0;
1212}
1213
1214/* get next entry for port */
1215int mv88e6xxx_port_fdb_getnext(struct dsa_switch *ds, int port,
1216 unsigned char *addr, bool *is_static)
1217{
1218 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1219 int ret;
1220
1221 mutex_lock(&ps->smi_mutex);
1222 ret = __mv88e6xxx_port_getnext(ds, port, addr, is_static);
1223 mutex_unlock(&ps->smi_mutex);
1224
1225 return ret;
1226}
1227
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001228static void mv88e6xxx_bridge_work(struct work_struct *work)
1229{
1230 struct mv88e6xxx_priv_state *ps;
1231 struct dsa_switch *ds;
1232 int port;
1233
1234 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
1235 ds = ((struct dsa_switch *)ps) - 1;
1236
1237 while (ps->port_state_update_mask) {
1238 port = __ffs(ps->port_state_update_mask);
1239 clear_bit(port, &ps->port_state_update_mask);
1240 mv88e6xxx_set_port_state(ds, port, ps->port_state[port]);
1241 }
1242}
1243
Guenter Roeckd827e882015-03-26 18:36:29 -07001244int mv88e6xxx_setup_port_common(struct dsa_switch *ds, int port)
1245{
1246 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001247 int ret, fid;
Guenter Roeckd827e882015-03-26 18:36:29 -07001248
1249 mutex_lock(&ps->smi_mutex);
1250
Guenter Roeck366f0a02015-03-26 18:36:30 -07001251 /* Port Control 1: disable trunking, disable sending
1252 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07001253 */
Andrew Lunncca8b132015-04-02 04:06:39 +02001254 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
1255 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07001256 if (ret)
1257 goto abort;
1258
1259 /* Port based VLAN map: give each port its own address
1260 * database, allow the CPU port to talk to each of the 'real'
1261 * ports, and allow each of the 'real' ports to only talk to
1262 * the upstream port.
1263 */
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001264 fid = __ffs(ps->fid_mask);
1265 ps->fid[port] = fid;
1266 ps->fid_mask &= ~(1 << fid);
Guenter Roeckd827e882015-03-26 18:36:29 -07001267
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001268 if (!dsa_is_cpu_port(ds, port))
1269 ps->bridge_mask[fid] = 1 << port;
1270
1271 ret = _mv88e6xxx_update_port_config(ds, port);
Guenter Roeckd827e882015-03-26 18:36:29 -07001272 if (ret)
1273 goto abort;
1274
1275 /* Default VLAN ID and priority: don't set a default VLAN
1276 * ID, and set the default packet priority to zero.
1277 */
1278 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), 0x07, 0x0000);
1279abort:
1280 mutex_unlock(&ps->smi_mutex);
1281 return ret;
1282}
1283
Guenter Roeckacdaffc2015-03-26 18:36:28 -07001284int mv88e6xxx_setup_common(struct dsa_switch *ds)
1285{
1286 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1287
1288 mutex_init(&ps->smi_mutex);
1289 mutex_init(&ps->stats_mutex);
1290 mutex_init(&ps->phy_mutex);
1291
Andrew Lunncca8b132015-04-02 04:06:39 +02001292 ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;
Andrew Lunna8f064c2015-03-26 18:36:40 -07001293
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001294 ps->fid_mask = (1 << DSA_MAX_PORTS) - 1;
1295
1296 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
1297
Guenter Roeckacdaffc2015-03-26 18:36:28 -07001298 return 0;
1299}
1300
Andrew Lunn143a8302015-04-02 04:06:34 +02001301int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
1302{
1303 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1304 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
1305 unsigned long timeout;
1306 int ret;
1307 int i;
1308
1309 /* Set all ports to the disabled state. */
1310 for (i = 0; i < ps->num_ports; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001311 ret = REG_READ(REG_PORT(i), PORT_CONTROL);
1312 REG_WRITE(REG_PORT(i), PORT_CONTROL, ret & 0xfffc);
Andrew Lunn143a8302015-04-02 04:06:34 +02001313 }
1314
1315 /* Wait for transmit queues to drain. */
1316 usleep_range(2000, 4000);
1317
1318 /* Reset the switch. Keep the PPU active if requested. The PPU
1319 * needs to be active to support indirect phy register access
1320 * through global registers 0x18 and 0x19.
1321 */
1322 if (ppu_active)
1323 REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
1324 else
1325 REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
1326
1327 /* Wait up to one second for reset to complete. */
1328 timeout = jiffies + 1 * HZ;
1329 while (time_before(jiffies, timeout)) {
1330 ret = REG_READ(REG_GLOBAL, 0x00);
1331 if ((ret & is_reset) == is_reset)
1332 break;
1333 usleep_range(1000, 2000);
1334 }
1335 if (time_after(jiffies, timeout))
1336 return -ETIMEDOUT;
1337
1338 return 0;
1339}
1340
Andrew Lunn491435852015-04-02 04:06:35 +02001341int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
1342{
1343 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1344 int ret;
1345
1346 mutex_lock(&ps->phy_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02001347 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
Andrew Lunn491435852015-04-02 04:06:35 +02001348 if (ret < 0)
1349 goto error;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02001350 ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
Andrew Lunn491435852015-04-02 04:06:35 +02001351error:
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02001352 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
Andrew Lunn491435852015-04-02 04:06:35 +02001353 mutex_unlock(&ps->phy_mutex);
1354 return ret;
1355}
1356
1357int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
1358 int reg, int val)
1359{
1360 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1361 int ret;
1362
1363 mutex_lock(&ps->phy_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02001364 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
Andrew Lunn491435852015-04-02 04:06:35 +02001365 if (ret < 0)
1366 goto error;
1367
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02001368 ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
Andrew Lunn491435852015-04-02 04:06:35 +02001369error:
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02001370 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
1371 mutex_unlock(&ps->phy_mutex);
1372 return ret;
1373}
1374
1375static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port)
1376{
1377 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1378
1379 if (port >= 0 && port < ps->num_ports)
1380 return port;
1381 return -EINVAL;
1382}
1383
1384int
1385mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
1386{
1387 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1388 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
1389 int ret;
1390
1391 if (addr < 0)
1392 return addr;
1393
1394 mutex_lock(&ps->phy_mutex);
1395 ret = _mv88e6xxx_phy_read(ds, addr, regnum);
1396 mutex_unlock(&ps->phy_mutex);
1397 return ret;
1398}
1399
1400int
1401mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
1402{
1403 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1404 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
1405 int ret;
1406
1407 if (addr < 0)
1408 return addr;
1409
1410 mutex_lock(&ps->phy_mutex);
1411 ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
1412 mutex_unlock(&ps->phy_mutex);
1413 return ret;
1414}
1415
1416int
1417mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
1418{
1419 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1420 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
1421 int ret;
1422
1423 if (addr < 0)
1424 return addr;
1425
1426 mutex_lock(&ps->phy_mutex);
1427 ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
1428 mutex_unlock(&ps->phy_mutex);
1429 return ret;
1430}
1431
1432int
1433mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
1434 u16 val)
1435{
1436 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1437 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
1438 int ret;
1439
1440 if (addr < 0)
1441 return addr;
1442
1443 mutex_lock(&ps->phy_mutex);
1444 ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
Andrew Lunn491435852015-04-02 04:06:35 +02001445 mutex_unlock(&ps->phy_mutex);
1446 return ret;
1447}
1448
Ben Hutchings98e67302011-11-25 14:36:19 +00001449static int __init mv88e6xxx_init(void)
1450{
1451#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
1452 register_switch_driver(&mv88e6131_switch_driver);
1453#endif
1454#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
1455 register_switch_driver(&mv88e6123_61_65_switch_driver);
1456#endif
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07001457#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
1458 register_switch_driver(&mv88e6352_switch_driver);
1459#endif
Andrew Lunn42f27252014-09-12 23:58:44 +02001460#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
1461 register_switch_driver(&mv88e6171_switch_driver);
1462#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00001463 return 0;
1464}
1465module_init(mv88e6xxx_init);
1466
1467static void __exit mv88e6xxx_cleanup(void)
1468{
Andrew Lunn42f27252014-09-12 23:58:44 +02001469#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
1470 unregister_switch_driver(&mv88e6171_switch_driver);
1471#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00001472#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
1473 unregister_switch_driver(&mv88e6123_61_65_switch_driver);
1474#endif
1475#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
1476 unregister_switch_driver(&mv88e6131_switch_driver);
1477#endif
1478}
1479module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00001480
1481MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
1482MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
1483MODULE_LICENSE("GPL");