blob: d8866988a88a09ac348d504db153353ab4403155 [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 *
Lennert Buytenhek91da11f2008-10-07 13:44:02 +00008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
Barry Grussling19b2f972013-01-08 16:05:54 +000014#include <linux/delay.h>
Guenter Roeckdefb05b2015-03-26 18:36:38 -070015#include <linux/etherdevice.h>
Andrew Lunndea87022015-08-31 15:56:47 +020016#include <linux/ethtool.h>
Guenter Roeckfacd95b2015-03-26 18:36:35 -070017#include <linux/if_bridge.h>
Barry Grussling19b2f972013-01-08 16:05:54 +000018#include <linux/jiffies.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000019#include <linux/list.h>
Paul Gortmaker2bbba272012-01-24 10:41:40 +000020#include <linux/module.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000021#include <linux/netdevice.h>
22#include <linux/phy.h>
Ben Hutchingsc8f0b862011-11-27 17:06:08 +000023#include <net/dsa.h>
Vivien Didelot1f36faf2015-10-08 11:35:13 -040024#include <net/switchdev.h>
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000025#include "mv88e6xxx.h"
26
Vivien Didelot3996a4f2015-10-30 18:56:45 -040027static void assert_smi_lock(struct dsa_switch *ds)
28{
29 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
30
31 if (unlikely(!mutex_is_locked(&ps->smi_mutex))) {
32 dev_err(ds->master_dev, "SMI lock not held!\n");
33 dump_stack();
34 }
35}
36
Barry Grussling3675c8d2013-01-08 16:05:53 +000037/* If the switch's ADDR[4:0] strap pins are strapped to zero, it will
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000038 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
39 * will be directly accessible on some {device address,register address}
40 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
41 * will only respond to SMI transactions to that specific address, and
42 * an indirect addressing mechanism needs to be used to access its
43 * registers.
44 */
45static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
46{
47 int ret;
48 int i;
49
50 for (i = 0; i < 16; i++) {
Neil Armstrong6e899e62015-10-22 10:37:53 +020051 ret = mdiobus_read_nested(bus, sw_addr, SMI_CMD);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000052 if (ret < 0)
53 return ret;
54
Andrew Lunncca8b132015-04-02 04:06:39 +020055 if ((ret & SMI_CMD_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000056 return 0;
57 }
58
59 return -ETIMEDOUT;
60}
61
62int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
63{
64 int ret;
65
66 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +020067 return mdiobus_read_nested(bus, addr, reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000068
Barry Grussling3675c8d2013-01-08 16:05:53 +000069 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000070 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
71 if (ret < 0)
72 return ret;
73
Barry Grussling3675c8d2013-01-08 16:05:53 +000074 /* Transmit the read command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020075 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
76 SMI_CMD_OP_22_READ | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000077 if (ret < 0)
78 return ret;
79
Barry Grussling3675c8d2013-01-08 16:05:53 +000080 /* Wait for the read command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000081 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
82 if (ret < 0)
83 return ret;
84
Barry Grussling3675c8d2013-01-08 16:05:53 +000085 /* Read the data. */
Neil Armstrong6e899e62015-10-22 10:37:53 +020086 ret = mdiobus_read_nested(bus, sw_addr, SMI_DATA);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000087 if (ret < 0)
88 return ret;
89
90 return ret & 0xffff;
91}
92
Guenter Roeck8d6d09e2015-03-26 18:36:31 -070093static int _mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000094{
Guenter Roeckb184e492014-10-17 12:30:58 -070095 struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +000096 int ret;
97
Vivien Didelot3996a4f2015-10-30 18:56:45 -040098 assert_smi_lock(ds);
99
Guenter Roeckb184e492014-10-17 12:30:58 -0700100 if (bus == NULL)
101 return -EINVAL;
102
Guenter Roeckb184e492014-10-17 12:30:58 -0700103 ret = __mv88e6xxx_reg_read(bus, ds->pd->sw_addr, addr, reg);
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500104 if (ret < 0)
105 return ret;
106
107 dev_dbg(ds->master_dev, "<- addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
108 addr, reg, ret);
109
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000110 return ret;
111}
112
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700113int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
114{
115 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
116 int ret;
117
118 mutex_lock(&ps->smi_mutex);
119 ret = _mv88e6xxx_reg_read(ds, addr, reg);
120 mutex_unlock(&ps->smi_mutex);
121
122 return ret;
123}
124
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000125int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
126 int reg, u16 val)
127{
128 int ret;
129
130 if (sw_addr == 0)
Neil Armstrong6e899e62015-10-22 10:37:53 +0200131 return mdiobus_write_nested(bus, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000132
Barry Grussling3675c8d2013-01-08 16:05:53 +0000133 /* Wait for the bus to become free. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000134 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
135 if (ret < 0)
136 return ret;
137
Barry Grussling3675c8d2013-01-08 16:05:53 +0000138 /* Transmit the data to write. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200139 ret = mdiobus_write_nested(bus, sw_addr, SMI_DATA, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000140 if (ret < 0)
141 return ret;
142
Barry Grussling3675c8d2013-01-08 16:05:53 +0000143 /* Transmit the write command. */
Neil Armstrong6e899e62015-10-22 10:37:53 +0200144 ret = mdiobus_write_nested(bus, sw_addr, SMI_CMD,
145 SMI_CMD_OP_22_WRITE | (addr << 5) | reg);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000146 if (ret < 0)
147 return ret;
148
Barry Grussling3675c8d2013-01-08 16:05:53 +0000149 /* Wait for the write command to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000150 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
151 if (ret < 0)
152 return ret;
153
154 return 0;
155}
156
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700157static int _mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg,
158 u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000159{
Guenter Roeckb184e492014-10-17 12:30:58 -0700160 struct mii_bus *bus = dsa_host_dev_to_mii_bus(ds->master_dev);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000161
Vivien Didelot3996a4f2015-10-30 18:56:45 -0400162 assert_smi_lock(ds);
163
Guenter Roeckb184e492014-10-17 12:30:58 -0700164 if (bus == NULL)
165 return -EINVAL;
166
Vivien Didelotbb92ea52015-01-23 16:10:36 -0500167 dev_dbg(ds->master_dev, "-> addr: 0x%.2x reg: 0x%.2x val: 0x%.4x\n",
168 addr, reg, val);
169
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700170 return __mv88e6xxx_reg_write(bus, ds->pd->sw_addr, addr, reg, val);
171}
172
173int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
174{
175 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
176 int ret;
177
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000178 mutex_lock(&ps->smi_mutex);
Guenter Roeck8d6d09e2015-03-26 18:36:31 -0700179 ret = _mv88e6xxx_reg_write(ds, addr, reg, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000180 mutex_unlock(&ps->smi_mutex);
181
182 return ret;
183}
184
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000185int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr)
186{
Andrew Lunncca8b132015-04-02 04:06:39 +0200187 REG_WRITE(REG_GLOBAL, GLOBAL_MAC_01, (addr[0] << 8) | addr[1]);
188 REG_WRITE(REG_GLOBAL, GLOBAL_MAC_23, (addr[2] << 8) | addr[3]);
189 REG_WRITE(REG_GLOBAL, GLOBAL_MAC_45, (addr[4] << 8) | addr[5]);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000190
191 return 0;
192}
193
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000194int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
195{
196 int i;
197 int ret;
198
199 for (i = 0; i < 6; i++) {
200 int j;
201
Barry Grussling3675c8d2013-01-08 16:05:53 +0000202 /* Write the MAC address byte. */
Andrew Lunncca8b132015-04-02 04:06:39 +0200203 REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MAC,
204 GLOBAL2_SWITCH_MAC_BUSY | (i << 8) | addr[i]);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000205
Barry Grussling3675c8d2013-01-08 16:05:53 +0000206 /* Wait for the write to complete. */
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000207 for (j = 0; j < 16; j++) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200208 ret = REG_READ(REG_GLOBAL2, GLOBAL2_SWITCH_MAC);
209 if ((ret & GLOBAL2_SWITCH_MAC_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000210 break;
211 }
212 if (j == 16)
213 return -ETIMEDOUT;
214 }
215
216 return 0;
217}
218
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200219static int _mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000220{
221 if (addr >= 0)
Andrew Lunn3898c142015-05-06 01:09:53 +0200222 return _mv88e6xxx_reg_read(ds, addr, regnum);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000223 return 0xffff;
224}
225
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200226static int _mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum,
227 u16 val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000228{
229 if (addr >= 0)
Andrew Lunn3898c142015-05-06 01:09:53 +0200230 return _mv88e6xxx_reg_write(ds, addr, regnum, val);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000231 return 0;
232}
233
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000234#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
235static int mv88e6xxx_ppu_disable(struct dsa_switch *ds)
236{
237 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000238 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000239
Andrew Lunncca8b132015-04-02 04:06:39 +0200240 ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
241 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL,
242 ret & ~GLOBAL_CONTROL_PPU_ENABLE);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000243
Barry Grussling19b2f972013-01-08 16:05:54 +0000244 timeout = jiffies + 1 * HZ;
245 while (time_before(jiffies, timeout)) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200246 ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
Barry Grussling19b2f972013-01-08 16:05:54 +0000247 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200248 if ((ret & GLOBAL_STATUS_PPU_MASK) !=
249 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000250 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000251 }
252
253 return -ETIMEDOUT;
254}
255
256static int mv88e6xxx_ppu_enable(struct dsa_switch *ds)
257{
258 int ret;
Barry Grussling19b2f972013-01-08 16:05:54 +0000259 unsigned long timeout;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000260
Andrew Lunncca8b132015-04-02 04:06:39 +0200261 ret = REG_READ(REG_GLOBAL, GLOBAL_CONTROL);
262 REG_WRITE(REG_GLOBAL, GLOBAL_CONTROL, ret | GLOBAL_CONTROL_PPU_ENABLE);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000263
Barry Grussling19b2f972013-01-08 16:05:54 +0000264 timeout = jiffies + 1 * HZ;
265 while (time_before(jiffies, timeout)) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200266 ret = REG_READ(REG_GLOBAL, GLOBAL_STATUS);
Barry Grussling19b2f972013-01-08 16:05:54 +0000267 usleep_range(1000, 2000);
Andrew Lunncca8b132015-04-02 04:06:39 +0200268 if ((ret & GLOBAL_STATUS_PPU_MASK) ==
269 GLOBAL_STATUS_PPU_POLLING)
Barry Grussling85686582013-01-08 16:05:56 +0000270 return 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000271 }
272
273 return -ETIMEDOUT;
274}
275
276static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
277{
278 struct mv88e6xxx_priv_state *ps;
279
280 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
281 if (mutex_trylock(&ps->ppu_mutex)) {
Barry Grussling85686582013-01-08 16:05:56 +0000282 struct dsa_switch *ds = ((struct dsa_switch *)ps) - 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000283
Barry Grussling85686582013-01-08 16:05:56 +0000284 if (mv88e6xxx_ppu_enable(ds) == 0)
285 ps->ppu_disabled = 0;
286 mutex_unlock(&ps->ppu_mutex);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000287 }
288}
289
290static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
291{
292 struct mv88e6xxx_priv_state *ps = (void *)_ps;
293
294 schedule_work(&ps->ppu_work);
295}
296
297static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
298{
Florian Fainellia22adce2014-04-28 11:14:28 -0700299 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000300 int ret;
301
302 mutex_lock(&ps->ppu_mutex);
303
Barry Grussling3675c8d2013-01-08 16:05:53 +0000304 /* If the PHY polling unit is enabled, disable it so that
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000305 * we can access the PHY registers. If it was already
306 * disabled, cancel the timer that is going to re-enable
307 * it.
308 */
309 if (!ps->ppu_disabled) {
Barry Grussling85686582013-01-08 16:05:56 +0000310 ret = mv88e6xxx_ppu_disable(ds);
311 if (ret < 0) {
312 mutex_unlock(&ps->ppu_mutex);
313 return ret;
314 }
315 ps->ppu_disabled = 1;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000316 } else {
Barry Grussling85686582013-01-08 16:05:56 +0000317 del_timer(&ps->ppu_timer);
318 ret = 0;
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000319 }
320
321 return ret;
322}
323
324static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
325{
Florian Fainellia22adce2014-04-28 11:14:28 -0700326 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000327
Barry Grussling3675c8d2013-01-08 16:05:53 +0000328 /* Schedule a timer to re-enable the PHY polling unit. */
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000329 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
330 mutex_unlock(&ps->ppu_mutex);
331}
332
333void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
334{
Florian Fainellia22adce2014-04-28 11:14:28 -0700335 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000336
337 mutex_init(&ps->ppu_mutex);
338 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
339 init_timer(&ps->ppu_timer);
340 ps->ppu_timer.data = (unsigned long)ps;
341 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
342}
343
344int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum)
345{
346 int ret;
347
348 ret = mv88e6xxx_ppu_access_get(ds);
349 if (ret >= 0) {
Barry Grussling85686582013-01-08 16:05:56 +0000350 ret = mv88e6xxx_reg_read(ds, addr, regnum);
351 mv88e6xxx_ppu_access_put(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000352 }
353
354 return ret;
355}
356
357int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
358 int regnum, u16 val)
359{
360 int ret;
361
362 ret = mv88e6xxx_ppu_access_get(ds);
363 if (ret >= 0) {
Barry Grussling85686582013-01-08 16:05:56 +0000364 ret = mv88e6xxx_reg_write(ds, addr, regnum, val);
365 mv88e6xxx_ppu_access_put(ds);
Lennert Buytenhek2e5f0322008-10-07 13:45:18 +0000366 }
367
368 return ret;
369}
370#endif
371
Andrew Lunn54d792f2015-05-06 01:09:47 +0200372static bool mv88e6xxx_6065_family(struct dsa_switch *ds)
373{
374 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
375
376 switch (ps->id) {
377 case PORT_SWITCH_ID_6031:
378 case PORT_SWITCH_ID_6061:
379 case PORT_SWITCH_ID_6035:
380 case PORT_SWITCH_ID_6065:
381 return true;
382 }
383 return false;
384}
385
386static bool mv88e6xxx_6095_family(struct dsa_switch *ds)
387{
388 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
389
390 switch (ps->id) {
391 case PORT_SWITCH_ID_6092:
392 case PORT_SWITCH_ID_6095:
393 return true;
394 }
395 return false;
396}
397
398static bool mv88e6xxx_6097_family(struct dsa_switch *ds)
399{
400 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
401
402 switch (ps->id) {
403 case PORT_SWITCH_ID_6046:
404 case PORT_SWITCH_ID_6085:
405 case PORT_SWITCH_ID_6096:
406 case PORT_SWITCH_ID_6097:
407 return true;
408 }
409 return false;
410}
411
412static bool mv88e6xxx_6165_family(struct dsa_switch *ds)
413{
414 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
415
416 switch (ps->id) {
417 case PORT_SWITCH_ID_6123:
418 case PORT_SWITCH_ID_6161:
419 case PORT_SWITCH_ID_6165:
420 return true;
421 }
422 return false;
423}
424
425static bool mv88e6xxx_6185_family(struct dsa_switch *ds)
426{
427 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
428
429 switch (ps->id) {
430 case PORT_SWITCH_ID_6121:
431 case PORT_SWITCH_ID_6122:
432 case PORT_SWITCH_ID_6152:
433 case PORT_SWITCH_ID_6155:
434 case PORT_SWITCH_ID_6182:
435 case PORT_SWITCH_ID_6185:
436 case PORT_SWITCH_ID_6108:
437 case PORT_SWITCH_ID_6131:
438 return true;
439 }
440 return false;
441}
442
Guenter Roeckc22995c2015-07-25 09:42:28 -0700443static bool mv88e6xxx_6320_family(struct dsa_switch *ds)
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700444{
445 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
446
447 switch (ps->id) {
448 case PORT_SWITCH_ID_6320:
449 case PORT_SWITCH_ID_6321:
450 return true;
451 }
452 return false;
453}
454
Andrew Lunn54d792f2015-05-06 01:09:47 +0200455static bool mv88e6xxx_6351_family(struct dsa_switch *ds)
456{
457 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
458
459 switch (ps->id) {
460 case PORT_SWITCH_ID_6171:
461 case PORT_SWITCH_ID_6175:
462 case PORT_SWITCH_ID_6350:
463 case PORT_SWITCH_ID_6351:
464 return true;
465 }
466 return false;
467}
468
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200469static bool mv88e6xxx_6352_family(struct dsa_switch *ds)
470{
471 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
472
473 switch (ps->id) {
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200474 case PORT_SWITCH_ID_6172:
475 case PORT_SWITCH_ID_6176:
Andrew Lunn54d792f2015-05-06 01:09:47 +0200476 case PORT_SWITCH_ID_6240:
477 case PORT_SWITCH_ID_6352:
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200478 return true;
479 }
480 return false;
481}
482
Andrew Lunndea87022015-08-31 15:56:47 +0200483/* We expect the switch to perform auto negotiation if there is a real
484 * phy. However, in the case of a fixed link phy, we force the port
485 * settings from the fixed link settings.
486 */
487void mv88e6xxx_adjust_link(struct dsa_switch *ds, int port,
488 struct phy_device *phydev)
489{
490 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Andrew Lunn49052872015-09-29 01:53:48 +0200491 u32 reg;
492 int ret;
Andrew Lunndea87022015-08-31 15:56:47 +0200493
494 if (!phy_is_pseudo_fixed_link(phydev))
495 return;
496
497 mutex_lock(&ps->smi_mutex);
498
499 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
500 if (ret < 0)
501 goto out;
502
503 reg = ret & ~(PORT_PCS_CTRL_LINK_UP |
504 PORT_PCS_CTRL_FORCE_LINK |
505 PORT_PCS_CTRL_DUPLEX_FULL |
506 PORT_PCS_CTRL_FORCE_DUPLEX |
507 PORT_PCS_CTRL_UNFORCED);
508
509 reg |= PORT_PCS_CTRL_FORCE_LINK;
510 if (phydev->link)
511 reg |= PORT_PCS_CTRL_LINK_UP;
512
513 if (mv88e6xxx_6065_family(ds) && phydev->speed > SPEED_100)
514 goto out;
515
516 switch (phydev->speed) {
517 case SPEED_1000:
518 reg |= PORT_PCS_CTRL_1000;
519 break;
520 case SPEED_100:
521 reg |= PORT_PCS_CTRL_100;
522 break;
523 case SPEED_10:
524 reg |= PORT_PCS_CTRL_10;
525 break;
526 default:
527 pr_info("Unknown speed");
528 goto out;
529 }
530
531 reg |= PORT_PCS_CTRL_FORCE_DUPLEX;
532 if (phydev->duplex == DUPLEX_FULL)
533 reg |= PORT_PCS_CTRL_DUPLEX_FULL;
534
Andrew Lunne7e72ac2015-08-31 15:56:51 +0200535 if ((mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds)) &&
536 (port >= ps->num_ports - 2)) {
537 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
538 reg |= PORT_PCS_CTRL_RGMII_DELAY_RXCLK;
539 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
540 reg |= PORT_PCS_CTRL_RGMII_DELAY_TXCLK;
541 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
542 reg |= (PORT_PCS_CTRL_RGMII_DELAY_RXCLK |
543 PORT_PCS_CTRL_RGMII_DELAY_TXCLK);
544 }
Andrew Lunndea87022015-08-31 15:56:47 +0200545 _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_PCS_CTRL, reg);
546
547out:
548 mutex_unlock(&ps->smi_mutex);
549}
550
Andrew Lunn31888232015-05-06 01:09:54 +0200551static int _mv88e6xxx_stats_wait(struct dsa_switch *ds)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000552{
553 int ret;
554 int i;
555
556 for (i = 0; i < 10; i++) {
Andrew Lunn31888232015-05-06 01:09:54 +0200557 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_OP);
Andrew Lunncca8b132015-04-02 04:06:39 +0200558 if ((ret & GLOBAL_STATS_OP_BUSY) == 0)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000559 return 0;
560 }
561
562 return -ETIMEDOUT;
563}
564
Andrew Lunn31888232015-05-06 01:09:54 +0200565static int _mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000566{
567 int ret;
568
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -0700569 if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds))
Andrew Lunnf3a8b6b2015-04-02 04:06:40 +0200570 port = (port + 1) << 5;
571
Barry Grussling3675c8d2013-01-08 16:05:53 +0000572 /* Snapshot the hardware statistics counters for this port. */
Andrew Lunn31888232015-05-06 01:09:54 +0200573 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
574 GLOBAL_STATS_OP_CAPTURE_PORT |
575 GLOBAL_STATS_OP_HIST_RX_TX | port);
576 if (ret < 0)
577 return ret;
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000578
Barry Grussling3675c8d2013-01-08 16:05:53 +0000579 /* Wait for the snapshotting to complete. */
Andrew Lunn31888232015-05-06 01:09:54 +0200580 ret = _mv88e6xxx_stats_wait(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000581 if (ret < 0)
582 return ret;
583
584 return 0;
585}
586
Andrew Lunn31888232015-05-06 01:09:54 +0200587static void _mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000588{
589 u32 _val;
590 int ret;
591
592 *val = 0;
593
Andrew Lunn31888232015-05-06 01:09:54 +0200594 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_STATS_OP,
595 GLOBAL_STATS_OP_READ_CAPTURED |
596 GLOBAL_STATS_OP_HIST_RX_TX | stat);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000597 if (ret < 0)
598 return;
599
Andrew Lunn31888232015-05-06 01:09:54 +0200600 ret = _mv88e6xxx_stats_wait(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000601 if (ret < 0)
602 return;
603
Andrew Lunn31888232015-05-06 01:09:54 +0200604 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_32);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000605 if (ret < 0)
606 return;
607
608 _val = ret << 16;
609
Andrew Lunn31888232015-05-06 01:09:54 +0200610 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_STATS_COUNTER_01);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000611 if (ret < 0)
612 return;
613
614 *val = _val | ret;
615}
616
Andrew Lunne413e7e2015-04-02 04:06:38 +0200617static struct mv88e6xxx_hw_stat mv88e6xxx_hw_stats[] = {
618 { "in_good_octets", 8, 0x00, },
619 { "in_bad_octets", 4, 0x02, },
620 { "in_unicast", 4, 0x04, },
621 { "in_broadcasts", 4, 0x06, },
622 { "in_multicasts", 4, 0x07, },
623 { "in_pause", 4, 0x16, },
624 { "in_undersize", 4, 0x18, },
625 { "in_fragments", 4, 0x19, },
626 { "in_oversize", 4, 0x1a, },
627 { "in_jabber", 4, 0x1b, },
628 { "in_rx_error", 4, 0x1c, },
629 { "in_fcs_error", 4, 0x1d, },
630 { "out_octets", 8, 0x0e, },
631 { "out_unicast", 4, 0x10, },
632 { "out_broadcasts", 4, 0x13, },
633 { "out_multicasts", 4, 0x12, },
634 { "out_pause", 4, 0x15, },
635 { "excessive", 4, 0x11, },
636 { "collisions", 4, 0x1e, },
637 { "deferred", 4, 0x05, },
638 { "single", 4, 0x14, },
639 { "multiple", 4, 0x17, },
640 { "out_fcs_error", 4, 0x03, },
641 { "late", 4, 0x1f, },
642 { "hist_64bytes", 4, 0x08, },
643 { "hist_65_127bytes", 4, 0x09, },
644 { "hist_128_255bytes", 4, 0x0a, },
645 { "hist_256_511bytes", 4, 0x0b, },
646 { "hist_512_1023bytes", 4, 0x0c, },
647 { "hist_1024_max_bytes", 4, 0x0d, },
648 /* Not all devices have the following counters */
649 { "sw_in_discards", 4, 0x110, },
650 { "sw_in_filtered", 2, 0x112, },
651 { "sw_out_filtered", 2, 0x113, },
652
653};
654
655static bool have_sw_in_discards(struct dsa_switch *ds)
656{
657 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
658
659 switch (ps->id) {
Andrew Lunncca8b132015-04-02 04:06:39 +0200660 case PORT_SWITCH_ID_6095: case PORT_SWITCH_ID_6161:
661 case PORT_SWITCH_ID_6165: case PORT_SWITCH_ID_6171:
662 case PORT_SWITCH_ID_6172: case PORT_SWITCH_ID_6176:
663 case PORT_SWITCH_ID_6182: case PORT_SWITCH_ID_6185:
664 case PORT_SWITCH_ID_6352:
Andrew Lunne413e7e2015-04-02 04:06:38 +0200665 return true;
666 default:
667 return false;
668 }
669}
670
671static void _mv88e6xxx_get_strings(struct dsa_switch *ds,
672 int nr_stats,
673 struct mv88e6xxx_hw_stat *stats,
674 int port, uint8_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000675{
676 int i;
677
678 for (i = 0; i < nr_stats; i++) {
679 memcpy(data + i * ETH_GSTRING_LEN,
680 stats[i].string, ETH_GSTRING_LEN);
681 }
682}
683
Andrew Lunn80c46272015-06-20 18:42:30 +0200684static uint64_t _mv88e6xxx_get_ethtool_stat(struct dsa_switch *ds,
685 int stat,
686 struct mv88e6xxx_hw_stat *stats,
687 int port)
688{
689 struct mv88e6xxx_hw_stat *s = stats + stat;
690 u32 low;
691 u32 high = 0;
692 int ret;
693 u64 value;
694
695 if (s->reg >= 0x100) {
696 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
697 s->reg - 0x100);
698 if (ret < 0)
699 return UINT64_MAX;
700
701 low = ret;
702 if (s->sizeof_stat == 4) {
703 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port),
704 s->reg - 0x100 + 1);
705 if (ret < 0)
706 return UINT64_MAX;
707 high = ret;
708 }
709 } else {
710 _mv88e6xxx_stats_read(ds, s->reg, &low);
711 if (s->sizeof_stat == 8)
712 _mv88e6xxx_stats_read(ds, s->reg + 1, &high);
713 }
714 value = (((u64)high) << 16) | low;
715 return value;
716}
717
Andrew Lunne413e7e2015-04-02 04:06:38 +0200718static void _mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
719 int nr_stats,
720 struct mv88e6xxx_hw_stat *stats,
721 int port, uint64_t *data)
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000722{
Florian Fainellia22adce2014-04-28 11:14:28 -0700723 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000724 int ret;
725 int i;
726
Andrew Lunn31888232015-05-06 01:09:54 +0200727 mutex_lock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000728
Andrew Lunn31888232015-05-06 01:09:54 +0200729 ret = _mv88e6xxx_stats_snapshot(ds, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000730 if (ret < 0) {
Andrew Lunn31888232015-05-06 01:09:54 +0200731 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000732 return;
733 }
734
Barry Grussling3675c8d2013-01-08 16:05:53 +0000735 /* Read each of the counters. */
Andrew Lunn80c46272015-06-20 18:42:30 +0200736 for (i = 0; i < nr_stats; i++)
737 data[i] = _mv88e6xxx_get_ethtool_stat(ds, i, stats, port);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000738
Andrew Lunn31888232015-05-06 01:09:54 +0200739 mutex_unlock(&ps->smi_mutex);
Lennert Buytenhek91da11f2008-10-07 13:44:02 +0000740}
Ben Hutchings98e67302011-11-25 14:36:19 +0000741
Andrew Lunne413e7e2015-04-02 04:06:38 +0200742/* All the statistics in the table */
743void
744mv88e6xxx_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
745{
746 if (have_sw_in_discards(ds))
747 _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
748 mv88e6xxx_hw_stats, port, data);
749 else
750 _mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
751 mv88e6xxx_hw_stats, port, data);
752}
753
754int mv88e6xxx_get_sset_count(struct dsa_switch *ds)
755{
756 if (have_sw_in_discards(ds))
757 return ARRAY_SIZE(mv88e6xxx_hw_stats);
758 return ARRAY_SIZE(mv88e6xxx_hw_stats) - 3;
759}
760
761void
762mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
763 int port, uint64_t *data)
764{
765 if (have_sw_in_discards(ds))
766 _mv88e6xxx_get_ethtool_stats(
767 ds, ARRAY_SIZE(mv88e6xxx_hw_stats),
768 mv88e6xxx_hw_stats, port, data);
769 else
770 _mv88e6xxx_get_ethtool_stats(
771 ds, ARRAY_SIZE(mv88e6xxx_hw_stats) - 3,
772 mv88e6xxx_hw_stats, port, data);
773}
774
Guenter Roecka1ab91f2014-10-29 10:45:05 -0700775int mv88e6xxx_get_regs_len(struct dsa_switch *ds, int port)
776{
777 return 32 * sizeof(u16);
778}
779
780void mv88e6xxx_get_regs(struct dsa_switch *ds, int port,
781 struct ethtool_regs *regs, void *_p)
782{
783 u16 *p = _p;
784 int i;
785
786 regs->version = 0;
787
788 memset(p, 0xff, 32 * sizeof(u16));
789
790 for (i = 0; i < 32; i++) {
791 int ret;
792
793 ret = mv88e6xxx_reg_read(ds, REG_PORT(port), i);
794 if (ret >= 0)
795 p[i] = ret;
796 }
797}
798
Andrew Lunn3898c142015-05-06 01:09:53 +0200799static int _mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset,
800 u16 mask)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700801{
802 unsigned long timeout = jiffies + HZ / 10;
803
804 while (time_before(jiffies, timeout)) {
805 int ret;
806
807 ret = _mv88e6xxx_reg_read(ds, reg, offset);
808 if (ret < 0)
809 return ret;
810 if (!(ret & mask))
811 return 0;
812
813 usleep_range(1000, 2000);
814 }
815 return -ETIMEDOUT;
816}
817
Andrew Lunn3898c142015-05-06 01:09:53 +0200818static int mv88e6xxx_wait(struct dsa_switch *ds, int reg, int offset, u16 mask)
819{
820 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
821 int ret;
822
823 mutex_lock(&ps->smi_mutex);
824 ret = _mv88e6xxx_wait(ds, reg, offset, mask);
825 mutex_unlock(&ps->smi_mutex);
826
827 return ret;
828}
829
830static int _mv88e6xxx_phy_wait(struct dsa_switch *ds)
831{
832 return _mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
833 GLOBAL2_SMI_OP_BUSY);
834}
835
836int mv88e6xxx_eeprom_load_wait(struct dsa_switch *ds)
837{
838 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
839 GLOBAL2_EEPROM_OP_LOAD);
840}
841
842int mv88e6xxx_eeprom_busy_wait(struct dsa_switch *ds)
843{
844 return mv88e6xxx_wait(ds, REG_GLOBAL2, GLOBAL2_EEPROM_OP,
845 GLOBAL2_EEPROM_OP_BUSY);
846}
847
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700848static int _mv88e6xxx_atu_wait(struct dsa_switch *ds)
849{
Andrew Lunncca8b132015-04-02 04:06:39 +0200850 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_ATU_OP,
851 GLOBAL_ATU_OP_BUSY);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700852}
853
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200854static int _mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int addr,
855 int regnum)
Andrew Lunnf3044682015-02-14 19:17:50 +0100856{
857 int ret;
858
Andrew Lunn3898c142015-05-06 01:09:53 +0200859 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
860 GLOBAL2_SMI_OP_22_READ | (addr << 5) |
861 regnum);
Andrew Lunnf3044682015-02-14 19:17:50 +0100862 if (ret < 0)
863 return ret;
864
Andrew Lunn3898c142015-05-06 01:09:53 +0200865 ret = _mv88e6xxx_phy_wait(ds);
866 if (ret < 0)
867 return ret;
868
869 return _mv88e6xxx_reg_read(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA);
Andrew Lunnf3044682015-02-14 19:17:50 +0100870}
871
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +0200872static int _mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int addr,
873 int regnum, u16 val)
Andrew Lunnf3044682015-02-14 19:17:50 +0100874{
Andrew Lunn3898c142015-05-06 01:09:53 +0200875 int ret;
Andrew Lunnf3044682015-02-14 19:17:50 +0100876
Andrew Lunn3898c142015-05-06 01:09:53 +0200877 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_DATA, val);
878 if (ret < 0)
879 return ret;
880
881 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL2, GLOBAL2_SMI_OP,
882 GLOBAL2_SMI_OP_22_WRITE | (addr << 5) |
883 regnum);
884
885 return _mv88e6xxx_phy_wait(ds);
Andrew Lunnf3044682015-02-14 19:17:50 +0100886}
887
Guenter Roeck11b3b452015-03-06 22:23:51 -0800888int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e)
889{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200890 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800891 int reg;
892
Andrew Lunn3898c142015-05-06 01:09:53 +0200893 mutex_lock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200894
895 reg = _mv88e6xxx_phy_read_indirect(ds, port, 16);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800896 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200897 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800898
899 e->eee_enabled = !!(reg & 0x0200);
900 e->tx_lpi_enabled = !!(reg & 0x0100);
901
Andrew Lunn3898c142015-05-06 01:09:53 +0200902 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_STATUS);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800903 if (reg < 0)
Andrew Lunn2f40c692015-04-02 04:06:37 +0200904 goto out;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800905
Andrew Lunncca8b132015-04-02 04:06:39 +0200906 e->eee_active = !!(reg & PORT_STATUS_EEE);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200907 reg = 0;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800908
Andrew Lunn2f40c692015-04-02 04:06:37 +0200909out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200910 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200911 return reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800912}
913
914int mv88e6xxx_set_eee(struct dsa_switch *ds, int port,
915 struct phy_device *phydev, struct ethtool_eee *e)
916{
Andrew Lunn2f40c692015-04-02 04:06:37 +0200917 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
918 int reg;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800919 int ret;
920
Andrew Lunn3898c142015-05-06 01:09:53 +0200921 mutex_lock(&ps->smi_mutex);
Guenter Roeck11b3b452015-03-06 22:23:51 -0800922
Andrew Lunn2f40c692015-04-02 04:06:37 +0200923 ret = _mv88e6xxx_phy_read_indirect(ds, port, 16);
924 if (ret < 0)
925 goto out;
926
927 reg = ret & ~0x0300;
928 if (e->eee_enabled)
929 reg |= 0x0200;
930 if (e->tx_lpi_enabled)
931 reg |= 0x0100;
932
933 ret = _mv88e6xxx_phy_write_indirect(ds, port, 16, reg);
934out:
Andrew Lunn3898c142015-05-06 01:09:53 +0200935 mutex_unlock(&ps->smi_mutex);
Andrew Lunn2f40c692015-04-02 04:06:37 +0200936
937 return ret;
Guenter Roeck11b3b452015-03-06 22:23:51 -0800938}
939
Vivien Didelot70cc99d2015-09-04 14:34:10 -0400940static int _mv88e6xxx_atu_cmd(struct dsa_switch *ds, u16 cmd)
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700941{
942 int ret;
943
Andrew Lunncca8b132015-04-02 04:06:39 +0200944 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_OP, cmd);
Guenter Roeckfacd95b2015-03-26 18:36:35 -0700945 if (ret < 0)
946 return ret;
947
948 return _mv88e6xxx_atu_wait(ds);
949}
950
Vivien Didelot37705b72015-09-04 14:34:11 -0400951static int _mv88e6xxx_atu_data_write(struct dsa_switch *ds,
952 struct mv88e6xxx_atu_entry *entry)
953{
954 u16 data = entry->state & GLOBAL_ATU_DATA_STATE_MASK;
955
956 if (entry->state != GLOBAL_ATU_DATA_STATE_UNUSED) {
957 unsigned int mask, shift;
958
959 if (entry->trunk) {
960 data |= GLOBAL_ATU_DATA_TRUNK;
961 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
962 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
963 } else {
964 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
965 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
966 }
967
968 data |= (entry->portv_trunkid << shift) & mask;
969 }
970
971 return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_DATA, data);
972}
973
Vivien Didelot7fb5e752015-09-04 14:34:12 -0400974static int _mv88e6xxx_atu_flush_move(struct dsa_switch *ds,
975 struct mv88e6xxx_atu_entry *entry,
976 bool static_too)
977{
978 int op;
979 int err;
980
981 err = _mv88e6xxx_atu_wait(ds);
982 if (err)
983 return err;
984
985 err = _mv88e6xxx_atu_data_write(ds, entry);
986 if (err)
987 return err;
988
989 if (entry->fid) {
990 err = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID,
991 entry->fid);
992 if (err)
993 return err;
994
995 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL_DB :
996 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC_DB;
997 } else {
998 op = static_too ? GLOBAL_ATU_OP_FLUSH_MOVE_ALL :
999 GLOBAL_ATU_OP_FLUSH_MOVE_NON_STATIC;
1000 }
1001
1002 return _mv88e6xxx_atu_cmd(ds, op);
1003}
1004
1005static int _mv88e6xxx_atu_flush(struct dsa_switch *ds, u16 fid, bool static_too)
1006{
1007 struct mv88e6xxx_atu_entry entry = {
1008 .fid = fid,
1009 .state = 0, /* EntryState bits must be 0 */
1010 };
1011
1012 return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
1013}
1014
Vivien Didelot9f4d55d2015-09-04 14:34:15 -04001015static int _mv88e6xxx_atu_move(struct dsa_switch *ds, u16 fid, int from_port,
1016 int to_port, bool static_too)
1017{
1018 struct mv88e6xxx_atu_entry entry = {
1019 .trunk = false,
1020 .fid = fid,
1021 };
1022
1023 /* EntryState bits must be 0xF */
1024 entry.state = GLOBAL_ATU_DATA_STATE_MASK;
1025
1026 /* ToPort and FromPort are respectively in PortVec bits 7:4 and 3:0 */
1027 entry.portv_trunkid = (to_port & 0x0f) << 4;
1028 entry.portv_trunkid |= from_port & 0x0f;
1029
1030 return _mv88e6xxx_atu_flush_move(ds, &entry, static_too);
1031}
1032
1033static int _mv88e6xxx_atu_remove(struct dsa_switch *ds, u16 fid, int port,
1034 bool static_too)
1035{
1036 /* Destination port 0xF means remove the entries */
1037 return _mv88e6xxx_atu_move(ds, fid, port, 0x0f, static_too);
1038}
1039
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001040static int mv88e6xxx_set_port_state(struct dsa_switch *ds, int port, u8 state)
1041{
1042 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Geert Uytterhoevenc3ffe6d2015-04-16 20:49:14 +02001043 int reg, ret = 0;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001044 u8 oldstate;
1045
1046 mutex_lock(&ps->smi_mutex);
1047
Andrew Lunncca8b132015-04-02 04:06:39 +02001048 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL);
Guenter Roeck538cc282015-04-15 22:12:42 -07001049 if (reg < 0) {
1050 ret = reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001051 goto abort;
Guenter Roeck538cc282015-04-15 22:12:42 -07001052 }
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001053
Andrew Lunncca8b132015-04-02 04:06:39 +02001054 oldstate = reg & PORT_CONTROL_STATE_MASK;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001055 if (oldstate != state) {
1056 /* Flush forwarding database if we're moving a port
1057 * from Learning or Forwarding state to Disabled or
1058 * Blocking or Listening state.
1059 */
Andrew Lunncca8b132015-04-02 04:06:39 +02001060 if (oldstate >= PORT_CONTROL_STATE_LEARNING &&
1061 state <= PORT_CONTROL_STATE_BLOCKING) {
Vivien Didelot2b8157b2015-09-04 14:34:16 -04001062 ret = _mv88e6xxx_atu_remove(ds, 0, port, false);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001063 if (ret)
1064 goto abort;
1065 }
Andrew Lunncca8b132015-04-02 04:06:39 +02001066 reg = (reg & ~PORT_CONTROL_STATE_MASK) | state;
1067 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL,
1068 reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001069 }
1070
1071abort:
1072 mutex_unlock(&ps->smi_mutex);
1073 return ret;
1074}
1075
Vivien Didelotede80982015-10-11 18:08:35 -04001076static int _mv88e6xxx_port_vlan_map_set(struct dsa_switch *ds, int port,
1077 u16 output_ports)
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001078{
1079 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotede80982015-10-11 18:08:35 -04001080 const u16 mask = (1 << ps->num_ports) - 1;
1081 int reg;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001082
Vivien Didelotede80982015-10-11 18:08:35 -04001083 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN);
1084 if (reg < 0)
1085 return reg;
1086
1087 reg &= ~mask;
1088 reg |= output_ports & mask;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001089
Andrew Lunncca8b132015-04-02 04:06:39 +02001090 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, reg);
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001091}
1092
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001093int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state)
1094{
1095 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1096 int stp_state;
1097
1098 switch (state) {
1099 case BR_STATE_DISABLED:
Andrew Lunncca8b132015-04-02 04:06:39 +02001100 stp_state = PORT_CONTROL_STATE_DISABLED;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001101 break;
1102 case BR_STATE_BLOCKING:
1103 case BR_STATE_LISTENING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001104 stp_state = PORT_CONTROL_STATE_BLOCKING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001105 break;
1106 case BR_STATE_LEARNING:
Andrew Lunncca8b132015-04-02 04:06:39 +02001107 stp_state = PORT_CONTROL_STATE_LEARNING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001108 break;
1109 case BR_STATE_FORWARDING:
1110 default:
Andrew Lunncca8b132015-04-02 04:06:39 +02001111 stp_state = PORT_CONTROL_STATE_FORWARDING;
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001112 break;
1113 }
1114
1115 netdev_dbg(ds->ports[port], "port state %d [%d]\n", state, stp_state);
1116
1117 /* mv88e6xxx_port_stp_update may be called with softirqs disabled,
1118 * so we can not update the port state directly but need to schedule it.
1119 */
1120 ps->port_state[port] = stp_state;
1121 set_bit(port, &ps->port_state_update_mask);
1122 schedule_work(&ps->bridge_work);
1123
1124 return 0;
1125}
1126
Vivien Didelot76e398a2015-11-01 12:33:55 -05001127static int _mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
1128{
1129 int ret;
1130
1131 ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
1132 if (ret < 0)
1133 return ret;
1134
1135 *pvid = ret & PORT_DEFAULT_VLAN_MASK;
1136
1137 return 0;
1138}
1139
Vivien Didelotb8fee952015-08-13 12:52:19 -04001140int mv88e6xxx_port_pvid_get(struct dsa_switch *ds, int port, u16 *pvid)
1141{
1142 int ret;
1143
1144 ret = mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_DEFAULT_VLAN);
1145 if (ret < 0)
1146 return ret;
1147
1148 *pvid = ret & PORT_DEFAULT_VLAN_MASK;
1149
1150 return 0;
1151}
1152
Vivien Didelot76e398a2015-11-01 12:33:55 -05001153static int _mv88e6xxx_port_pvid_set(struct dsa_switch *ds, int port, u16 pvid)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001154{
Vivien Didelot76e398a2015-11-01 12:33:55 -05001155 return _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001156 pvid & PORT_DEFAULT_VLAN_MASK);
1157}
1158
Vivien Didelot6b17e862015-08-13 12:52:18 -04001159static int _mv88e6xxx_vtu_wait(struct dsa_switch *ds)
1160{
1161 return _mv88e6xxx_wait(ds, REG_GLOBAL, GLOBAL_VTU_OP,
1162 GLOBAL_VTU_OP_BUSY);
1163}
1164
1165static int _mv88e6xxx_vtu_cmd(struct dsa_switch *ds, u16 op)
1166{
1167 int ret;
1168
1169 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_OP, op);
1170 if (ret < 0)
1171 return ret;
1172
1173 return _mv88e6xxx_vtu_wait(ds);
1174}
1175
1176static int _mv88e6xxx_vtu_stu_flush(struct dsa_switch *ds)
1177{
1178 int ret;
1179
1180 ret = _mv88e6xxx_vtu_wait(ds);
1181 if (ret < 0)
1182 return ret;
1183
1184 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_FLUSH_ALL);
1185}
1186
Vivien Didelotb8fee952015-08-13 12:52:19 -04001187static int _mv88e6xxx_vtu_stu_data_read(struct dsa_switch *ds,
1188 struct mv88e6xxx_vtu_stu_entry *entry,
1189 unsigned int nibble_offset)
1190{
1191 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1192 u16 regs[3];
1193 int i;
1194 int ret;
1195
1196 for (i = 0; i < 3; ++i) {
1197 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1198 GLOBAL_VTU_DATA_0_3 + i);
1199 if (ret < 0)
1200 return ret;
1201
1202 regs[i] = ret;
1203 }
1204
1205 for (i = 0; i < ps->num_ports; ++i) {
1206 unsigned int shift = (i % 4) * 4 + nibble_offset;
1207 u16 reg = regs[i / 4];
1208
1209 entry->data[i] = (reg >> shift) & GLOBAL_VTU_STU_DATA_MASK;
1210 }
1211
1212 return 0;
1213}
1214
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001215static int _mv88e6xxx_vtu_stu_data_write(struct dsa_switch *ds,
1216 struct mv88e6xxx_vtu_stu_entry *entry,
1217 unsigned int nibble_offset)
1218{
1219 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1220 u16 regs[3] = { 0 };
1221 int i;
1222 int ret;
1223
1224 for (i = 0; i < ps->num_ports; ++i) {
1225 unsigned int shift = (i % 4) * 4 + nibble_offset;
1226 u8 data = entry->data[i];
1227
1228 regs[i / 4] |= (data & GLOBAL_VTU_STU_DATA_MASK) << shift;
1229 }
1230
1231 for (i = 0; i < 3; ++i) {
1232 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL,
1233 GLOBAL_VTU_DATA_0_3 + i, regs[i]);
1234 if (ret < 0)
1235 return ret;
1236 }
1237
1238 return 0;
1239}
1240
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001241static int _mv88e6xxx_vtu_vid_write(struct dsa_switch *ds, u16 vid)
1242{
1243 return _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID,
1244 vid & GLOBAL_VTU_VID_MASK);
1245}
1246
1247static int _mv88e6xxx_vtu_getnext(struct dsa_switch *ds,
Vivien Didelotb8fee952015-08-13 12:52:19 -04001248 struct mv88e6xxx_vtu_stu_entry *entry)
1249{
1250 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1251 int ret;
1252
1253 ret = _mv88e6xxx_vtu_wait(ds);
1254 if (ret < 0)
1255 return ret;
1256
Vivien Didelotb8fee952015-08-13 12:52:19 -04001257 ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_GET_NEXT);
1258 if (ret < 0)
1259 return ret;
1260
1261 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1262 if (ret < 0)
1263 return ret;
1264
1265 next.vid = ret & GLOBAL_VTU_VID_MASK;
1266 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1267
1268 if (next.valid) {
1269 ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 0);
1270 if (ret < 0)
1271 return ret;
1272
1273 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
1274 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
1275 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1276 GLOBAL_VTU_FID);
1277 if (ret < 0)
1278 return ret;
1279
1280 next.fid = ret & GLOBAL_VTU_FID_MASK;
1281
1282 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1283 GLOBAL_VTU_SID);
1284 if (ret < 0)
1285 return ret;
1286
1287 next.sid = ret & GLOBAL_VTU_SID_MASK;
1288 }
1289 }
1290
1291 *entry = next;
1292 return 0;
1293}
1294
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001295static int _mv88e6xxx_vtu_loadpurge(struct dsa_switch *ds,
1296 struct mv88e6xxx_vtu_stu_entry *entry)
1297{
1298 u16 reg = 0;
1299 int ret;
1300
1301 ret = _mv88e6xxx_vtu_wait(ds);
1302 if (ret < 0)
1303 return ret;
1304
1305 if (!entry->valid)
1306 goto loadpurge;
1307
1308 /* Write port member tags */
1309 ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 0);
1310 if (ret < 0)
1311 return ret;
1312
1313 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
1314 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
1315 reg = entry->sid & GLOBAL_VTU_SID_MASK;
1316 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1317 if (ret < 0)
1318 return ret;
1319
1320 reg = entry->fid & GLOBAL_VTU_FID_MASK;
1321 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_FID, reg);
1322 if (ret < 0)
1323 return ret;
1324 }
1325
1326 reg = GLOBAL_VTU_VID_VALID;
1327loadpurge:
1328 reg |= entry->vid & GLOBAL_VTU_VID_MASK;
1329 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1330 if (ret < 0)
1331 return ret;
1332
1333 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_VTU_LOAD_PURGE);
1334}
1335
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001336static int _mv88e6xxx_stu_getnext(struct dsa_switch *ds, u8 sid,
1337 struct mv88e6xxx_vtu_stu_entry *entry)
1338{
1339 struct mv88e6xxx_vtu_stu_entry next = { 0 };
1340 int ret;
1341
1342 ret = _mv88e6xxx_vtu_wait(ds);
1343 if (ret < 0)
1344 return ret;
1345
1346 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID,
1347 sid & GLOBAL_VTU_SID_MASK);
1348 if (ret < 0)
1349 return ret;
1350
1351 ret = _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_GET_NEXT);
1352 if (ret < 0)
1353 return ret;
1354
1355 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_SID);
1356 if (ret < 0)
1357 return ret;
1358
1359 next.sid = ret & GLOBAL_VTU_SID_MASK;
1360
1361 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_VTU_VID);
1362 if (ret < 0)
1363 return ret;
1364
1365 next.valid = !!(ret & GLOBAL_VTU_VID_VALID);
1366
1367 if (next.valid) {
1368 ret = _mv88e6xxx_vtu_stu_data_read(ds, &next, 2);
1369 if (ret < 0)
1370 return ret;
1371 }
1372
1373 *entry = next;
1374 return 0;
1375}
1376
1377static int _mv88e6xxx_stu_loadpurge(struct dsa_switch *ds,
1378 struct mv88e6xxx_vtu_stu_entry *entry)
1379{
1380 u16 reg = 0;
1381 int ret;
1382
1383 ret = _mv88e6xxx_vtu_wait(ds);
1384 if (ret < 0)
1385 return ret;
1386
1387 if (!entry->valid)
1388 goto loadpurge;
1389
1390 /* Write port states */
1391 ret = _mv88e6xxx_vtu_stu_data_write(ds, entry, 2);
1392 if (ret < 0)
1393 return ret;
1394
1395 reg = GLOBAL_VTU_VID_VALID;
1396loadpurge:
1397 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_VID, reg);
1398 if (ret < 0)
1399 return ret;
1400
1401 reg = entry->sid & GLOBAL_VTU_SID_MASK;
1402 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_VTU_SID, reg);
1403 if (ret < 0)
1404 return ret;
1405
1406 return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE);
1407}
1408
1409static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid,
1410 struct mv88e6xxx_vtu_stu_entry *entry)
1411{
1412 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1413 struct mv88e6xxx_vtu_stu_entry vlan = {
1414 .valid = true,
1415 .vid = vid,
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001416 .fid = vid, /* We use one FID per VLAN */
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001417 };
1418 int i;
1419
1420 /* exclude all ports except the CPU */
1421 for (i = 0; i < ps->num_ports; ++i)
1422 vlan.data[i] = dsa_is_cpu_port(ds, i) ?
1423 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED :
1424 GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1425
1426 if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) ||
1427 mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) {
1428 struct mv88e6xxx_vtu_stu_entry vstp;
1429 int err;
1430
1431 /* Adding a VTU entry requires a valid STU entry. As VSTP is not
1432 * implemented, only one STU entry is needed to cover all VTU
1433 * entries. Thus, validate the SID 0.
1434 */
1435 vlan.sid = 0;
1436 err = _mv88e6xxx_stu_getnext(ds, GLOBAL_VTU_SID_MASK, &vstp);
1437 if (err)
1438 return err;
1439
1440 if (vstp.sid != vlan.sid || !vstp.valid) {
1441 memset(&vstp, 0, sizeof(vstp));
1442 vstp.valid = true;
1443 vstp.sid = vlan.sid;
1444
1445 err = _mv88e6xxx_stu_loadpurge(ds, &vstp);
1446 if (err)
1447 return err;
1448 }
1449
Vivien Didelot7c400012015-09-04 14:34:14 -04001450 /* Clear all MAC addresses from the new database */
1451 err = _mv88e6xxx_atu_flush(ds, vlan.fid, true);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001452 if (err)
1453 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001454 }
1455
1456 *entry = vlan;
1457 return 0;
1458}
1459
Vivien Didelot76e398a2015-11-01 12:33:55 -05001460int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
1461 const struct switchdev_obj_port_vlan *vlan,
1462 struct switchdev_trans *trans)
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001463{
Vivien Didelot76e398a2015-11-01 12:33:55 -05001464 /* We don't need any dynamic resource from the kernel (yet),
1465 * so skip the prepare phase.
1466 */
1467 return 0;
1468}
1469
1470static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid,
1471 bool untagged)
1472{
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001473 struct mv88e6xxx_vtu_stu_entry vlan;
1474 int err;
1475
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001476 err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
1477 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001478 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001479
1480 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001481 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001482 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001483
1484 if (vlan.vid != vid || !vlan.valid) {
1485 err = _mv88e6xxx_vlan_init(ds, vid, &vlan);
1486 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001487 return err;
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001488 }
1489
1490 vlan.data[port] = untagged ?
1491 GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED :
1492 GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED;
1493
Vivien Didelot76e398a2015-11-01 12:33:55 -05001494 return _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1495}
1496
1497int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
1498 const struct switchdev_obj_port_vlan *vlan,
1499 struct switchdev_trans *trans)
1500{
1501 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1502 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
1503 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
1504 u16 vid;
1505 int err = 0;
1506
1507 mutex_lock(&ps->smi_mutex);
1508
1509 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
1510 err = _mv88e6xxx_port_vlan_add(ds, port, vid, untagged);
1511 if (err)
1512 goto unlock;
1513 }
1514
1515 /* no PVID with ranges, otherwise it's a bug */
1516 if (pvid)
1517 err = _mv88e6xxx_port_pvid_set(ds, port, vid);
Vivien Didelot0d3b33e2015-08-13 12:52:22 -04001518unlock:
1519 mutex_unlock(&ps->smi_mutex);
1520
1521 return err;
1522}
1523
Vivien Didelot76e398a2015-11-01 12:33:55 -05001524static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001525{
1526 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1527 struct mv88e6xxx_vtu_stu_entry vlan;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001528 int i, err;
1529
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001530 err = _mv88e6xxx_vtu_vid_write(ds, vid - 1);
1531 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001532 return err;
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001533
1534 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001535 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001536 return err;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001537
1538 if (vlan.vid != vid || !vlan.valid ||
Vivien Didelot76e398a2015-11-01 12:33:55 -05001539 vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER)
1540 return -ENOENT;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001541
1542 vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER;
1543
1544 /* keep the VLAN unless all ports are excluded */
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001545 vlan.valid = false;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001546 for (i = 0; i < ps->num_ports; ++i) {
1547 if (dsa_is_cpu_port(ds, i))
1548 continue;
1549
1550 if (vlan.data[i] != GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) {
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001551 vlan.valid = true;
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001552 break;
1553 }
1554 }
1555
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001556 err = _mv88e6xxx_vtu_loadpurge(ds, &vlan);
1557 if (err)
Vivien Didelot76e398a2015-11-01 12:33:55 -05001558 return err;
1559
1560 return _mv88e6xxx_atu_remove(ds, vlan.fid, port, false);
1561}
1562
1563int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
1564 const struct switchdev_obj_port_vlan *vlan)
1565{
1566 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1567 u16 pvid, vid;
1568 int err = 0;
1569
1570 mutex_lock(&ps->smi_mutex);
1571
1572 err = _mv88e6xxx_port_pvid_get(ds, port, &pvid);
1573 if (err)
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001574 goto unlock;
1575
Vivien Didelot76e398a2015-11-01 12:33:55 -05001576 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
1577 err = _mv88e6xxx_port_vlan_del(ds, port, vid);
1578 if (err)
1579 goto unlock;
1580
1581 if (vid == pvid) {
1582 err = _mv88e6xxx_port_pvid_set(ds, port, 0);
1583 if (err)
1584 goto unlock;
1585 }
1586 }
1587
Vivien Didelot7dad08d2015-08-13 12:52:21 -04001588unlock:
1589 mutex_unlock(&ps->smi_mutex);
1590
1591 return err;
1592}
1593
Vivien Didelotb8fee952015-08-13 12:52:19 -04001594int mv88e6xxx_vlan_getnext(struct dsa_switch *ds, u16 *vid,
1595 unsigned long *ports, unsigned long *untagged)
1596{
1597 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1598 struct mv88e6xxx_vtu_stu_entry next;
1599 int port;
1600 int err;
1601
1602 if (*vid == 4095)
1603 return -ENOENT;
1604
1605 mutex_lock(&ps->smi_mutex);
Vivien Didelot36d04ba2015-10-22 09:34:39 -04001606 err = _mv88e6xxx_vtu_vid_write(ds, *vid);
1607 if (err)
1608 goto unlock;
1609
1610 err = _mv88e6xxx_vtu_getnext(ds, &next);
1611unlock:
Vivien Didelotb8fee952015-08-13 12:52:19 -04001612 mutex_unlock(&ps->smi_mutex);
1613
1614 if (err)
1615 return err;
1616
1617 if (!next.valid)
1618 return -ENOENT;
1619
1620 *vid = next.vid;
1621
1622 for (port = 0; port < ps->num_ports; ++port) {
1623 clear_bit(port, ports);
1624 clear_bit(port, untagged);
1625
1626 if (dsa_is_cpu_port(ds, port))
1627 continue;
1628
1629 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED ||
1630 next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1631 set_bit(port, ports);
1632
1633 if (next.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED)
1634 set_bit(port, untagged);
1635 }
1636
1637 return 0;
1638}
1639
Vivien Didelotc5723ac2015-08-10 09:09:48 -04001640static int _mv88e6xxx_atu_mac_write(struct dsa_switch *ds,
1641 const unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001642{
1643 int i, ret;
1644
1645 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001646 ret = _mv88e6xxx_reg_write(
1647 ds, REG_GLOBAL, GLOBAL_ATU_MAC_01 + i,
1648 (addr[i * 2] << 8) | addr[i * 2 + 1]);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001649 if (ret < 0)
1650 return ret;
1651 }
1652
1653 return 0;
1654}
1655
Vivien Didelotc5723ac2015-08-10 09:09:48 -04001656static int _mv88e6xxx_atu_mac_read(struct dsa_switch *ds, unsigned char *addr)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001657{
1658 int i, ret;
1659
1660 for (i = 0; i < 3; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02001661 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL,
1662 GLOBAL_ATU_MAC_01 + i);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001663 if (ret < 0)
1664 return ret;
1665 addr[i * 2] = ret >> 8;
1666 addr[i * 2 + 1] = ret & 0xff;
1667 }
1668
1669 return 0;
1670}
1671
Vivien Didelotfd231c82015-08-10 09:09:50 -04001672static int _mv88e6xxx_atu_load(struct dsa_switch *ds,
1673 struct mv88e6xxx_atu_entry *entry)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001674{
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001675 int ret;
1676
1677 ret = _mv88e6xxx_atu_wait(ds);
1678 if (ret < 0)
1679 return ret;
1680
Vivien Didelotfd231c82015-08-10 09:09:50 -04001681 ret = _mv88e6xxx_atu_mac_write(ds, entry->mac);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001682 if (ret < 0)
1683 return ret;
1684
Vivien Didelot37705b72015-09-04 14:34:11 -04001685 ret = _mv88e6xxx_atu_data_write(ds, entry);
Vivien Didelotfd231c82015-08-10 09:09:50 -04001686 if (ret < 0)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001687 return ret;
1688
Vivien Didelot70cc99d2015-09-04 14:34:10 -04001689 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, entry->fid);
1690 if (ret < 0)
1691 return ret;
1692
1693 return _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_LOAD_DB);
Vivien Didelotfd231c82015-08-10 09:09:50 -04001694}
David S. Millercdf09692015-08-11 12:00:37 -07001695
Vivien Didelotfd231c82015-08-10 09:09:50 -04001696static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port,
1697 const unsigned char *addr, u16 vid,
1698 u8 state)
1699{
1700 struct mv88e6xxx_atu_entry entry = { 0 };
Vivien Didelotfd231c82015-08-10 09:09:50 -04001701
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001702 entry.fid = vid; /* We use one FID per VLAN */
Vivien Didelotfd231c82015-08-10 09:09:50 -04001703 entry.state = state;
1704 ether_addr_copy(entry.mac, addr);
1705 if (state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1706 entry.trunk = false;
1707 entry.portv_trunkid = BIT(port);
1708 }
1709
1710 return _mv88e6xxx_atu_load(ds, &entry);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001711}
1712
Vivien Didelot146a3202015-10-08 11:35:12 -04001713int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port,
1714 const struct switchdev_obj_port_fdb *fdb,
1715 struct switchdev_trans *trans)
1716{
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001717 /* We don't use per-port FDB */
1718 if (fdb->vid == 0)
1719 return -EOPNOTSUPP;
1720
Vivien Didelot146a3202015-10-08 11:35:12 -04001721 /* We don't need any dynamic resource from the kernel (yet),
1722 * so skip the prepare phase.
1723 */
1724 return 0;
1725}
1726
David S. Millercdf09692015-08-11 12:00:37 -07001727int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port,
Vivien Didelot1f36faf2015-10-08 11:35:13 -04001728 const struct switchdev_obj_port_fdb *fdb,
1729 struct switchdev_trans *trans)
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001730{
Vivien Didelot1f36faf2015-10-08 11:35:13 -04001731 int state = is_multicast_ether_addr(fdb->addr) ?
David S. Millercdf09692015-08-11 12:00:37 -07001732 GLOBAL_ATU_DATA_STATE_MC_STATIC :
1733 GLOBAL_ATU_DATA_STATE_UC_STATIC;
1734 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot6630e232015-08-06 01:44:07 -04001735 int ret;
1736
David S. Millercdf09692015-08-11 12:00:37 -07001737 mutex_lock(&ps->smi_mutex);
Vivien Didelot1f36faf2015-10-08 11:35:13 -04001738 ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid, state);
David S. Millercdf09692015-08-11 12:00:37 -07001739 mutex_unlock(&ps->smi_mutex);
1740
1741 return ret;
1742}
1743
1744int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port,
Vivien Didelot8057b3e2015-10-08 11:35:14 -04001745 const struct switchdev_obj_port_fdb *fdb)
David S. Millercdf09692015-08-11 12:00:37 -07001746{
1747 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1748 int ret;
1749
1750 mutex_lock(&ps->smi_mutex);
Vivien Didelot8057b3e2015-10-08 11:35:14 -04001751 ret = _mv88e6xxx_port_fdb_load(ds, port, fdb->addr, fdb->vid,
David S. Millercdf09692015-08-11 12:00:37 -07001752 GLOBAL_ATU_DATA_STATE_UNUSED);
1753 mutex_unlock(&ps->smi_mutex);
1754
1755 return ret;
1756}
1757
Vivien Didelot1d194042015-08-10 09:09:51 -04001758static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid,
Vivien Didelot1d194042015-08-10 09:09:51 -04001759 struct mv88e6xxx_atu_entry *entry)
David S. Millercdf09692015-08-11 12:00:37 -07001760{
Vivien Didelot1d194042015-08-10 09:09:51 -04001761 struct mv88e6xxx_atu_entry next = { 0 };
1762 int ret;
1763
1764 next.fid = fid;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001765
1766 ret = _mv88e6xxx_atu_wait(ds);
1767 if (ret < 0)
1768 return ret;
1769
Vivien Didelot70cc99d2015-09-04 14:34:10 -04001770 ret = _mv88e6xxx_reg_write(ds, REG_GLOBAL, GLOBAL_ATU_FID, fid);
1771 if (ret < 0)
1772 return ret;
1773
1774 ret = _mv88e6xxx_atu_cmd(ds, GLOBAL_ATU_OP_GET_NEXT_DB);
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001775 if (ret < 0)
1776 return ret;
1777
Vivien Didelot1d194042015-08-10 09:09:51 -04001778 ret = _mv88e6xxx_atu_mac_read(ds, next.mac);
1779 if (ret < 0)
1780 return ret;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001781
Vivien Didelot1d194042015-08-10 09:09:51 -04001782 ret = _mv88e6xxx_reg_read(ds, REG_GLOBAL, GLOBAL_ATU_DATA);
1783 if (ret < 0)
1784 return ret;
1785
1786 next.state = ret & GLOBAL_ATU_DATA_STATE_MASK;
1787 if (next.state != GLOBAL_ATU_DATA_STATE_UNUSED) {
1788 unsigned int mask, shift;
1789
1790 if (ret & GLOBAL_ATU_DATA_TRUNK) {
1791 next.trunk = true;
1792 mask = GLOBAL_ATU_DATA_TRUNK_ID_MASK;
1793 shift = GLOBAL_ATU_DATA_TRUNK_ID_SHIFT;
1794 } else {
1795 next.trunk = false;
1796 mask = GLOBAL_ATU_DATA_PORT_VECTOR_MASK;
1797 shift = GLOBAL_ATU_DATA_PORT_VECTOR_SHIFT;
1798 }
1799
1800 next.portv_trunkid = (ret & mask) >> shift;
1801 }
1802
1803 *entry = next;
Guenter Roeckdefb05b2015-03-26 18:36:38 -07001804 return 0;
1805}
1806
Vivien Didelotf33475b2015-10-22 09:34:41 -04001807int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port,
1808 struct switchdev_obj_port_fdb *fdb,
1809 int (*cb)(struct switchdev_obj *obj))
1810{
1811 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
1812 struct mv88e6xxx_vtu_stu_entry vlan = {
1813 .vid = GLOBAL_VTU_VID_MASK, /* all ones */
1814 };
1815 int err;
1816
1817 mutex_lock(&ps->smi_mutex);
1818
1819 err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid);
1820 if (err)
1821 goto unlock;
1822
1823 do {
1824 struct mv88e6xxx_atu_entry addr = {
1825 .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff },
1826 };
1827
1828 err = _mv88e6xxx_vtu_getnext(ds, &vlan);
1829 if (err)
1830 goto unlock;
1831
1832 if (!vlan.valid)
1833 break;
1834
1835 err = _mv88e6xxx_atu_mac_write(ds, addr.mac);
1836 if (err)
1837 goto unlock;
1838
1839 do {
1840 err = _mv88e6xxx_atu_getnext(ds, vlan.fid, &addr);
1841 if (err)
1842 goto unlock;
1843
1844 if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED)
1845 break;
1846
1847 if (!addr.trunk && addr.portv_trunkid & BIT(port)) {
1848 bool is_static = addr.state ==
1849 (is_multicast_ether_addr(addr.mac) ?
1850 GLOBAL_ATU_DATA_STATE_MC_STATIC :
1851 GLOBAL_ATU_DATA_STATE_UC_STATIC);
1852
1853 fdb->vid = vlan.vid;
1854 ether_addr_copy(fdb->addr, addr.mac);
1855 fdb->ndm_state = is_static ? NUD_NOARP :
1856 NUD_REACHABLE;
1857
1858 err = cb(&fdb->obj);
1859 if (err)
1860 goto unlock;
1861 }
1862 } while (!is_broadcast_ether_addr(addr.mac));
1863
1864 } while (vlan.vid < GLOBAL_VTU_VID_MASK);
1865
1866unlock:
1867 mutex_unlock(&ps->smi_mutex);
1868
1869 return err;
1870}
1871
Guenter Roeckfacd95b2015-03-26 18:36:35 -07001872static void mv88e6xxx_bridge_work(struct work_struct *work)
1873{
1874 struct mv88e6xxx_priv_state *ps;
1875 struct dsa_switch *ds;
1876 int port;
1877
1878 ps = container_of(work, struct mv88e6xxx_priv_state, bridge_work);
1879 ds = ((struct dsa_switch *)ps) - 1;
1880
1881 while (ps->port_state_update_mask) {
1882 port = __ffs(ps->port_state_update_mask);
1883 clear_bit(port, &ps->port_state_update_mask);
1884 mv88e6xxx_set_port_state(ds, port, ps->port_state[port]);
1885 }
1886}
1887
Andrew Lunndbde9e62015-05-06 01:09:48 +02001888static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port)
Guenter Roeckd827e882015-03-26 18:36:29 -07001889{
1890 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelotf02bdff2015-10-11 18:08:36 -04001891 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02001892 u16 reg;
Guenter Roeckd827e882015-03-26 18:36:29 -07001893
1894 mutex_lock(&ps->smi_mutex);
1895
Andrew Lunn54d792f2015-05-06 01:09:47 +02001896 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
1897 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
1898 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07001899 mv88e6xxx_6065_family(ds) || mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02001900 /* MAC Forcing register: don't force link, speed,
1901 * duplex or flow control state to any particular
1902 * values on physical ports, but force the CPU port
1903 * and all DSA ports to their maximum bandwidth and
1904 * full duplex.
1905 */
1906 reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_PCS_CTRL);
Andrew Lunn60045cb2015-08-17 23:52:51 +02001907 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) {
Russell King53adc9e2015-09-21 21:42:59 +01001908 reg &= ~PORT_PCS_CTRL_UNFORCED;
Andrew Lunn54d792f2015-05-06 01:09:47 +02001909 reg |= PORT_PCS_CTRL_FORCE_LINK |
1910 PORT_PCS_CTRL_LINK_UP |
1911 PORT_PCS_CTRL_DUPLEX_FULL |
1912 PORT_PCS_CTRL_FORCE_DUPLEX;
1913 if (mv88e6xxx_6065_family(ds))
1914 reg |= PORT_PCS_CTRL_100;
1915 else
1916 reg |= PORT_PCS_CTRL_1000;
1917 } else {
1918 reg |= PORT_PCS_CTRL_UNFORCED;
1919 }
1920
1921 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
1922 PORT_PCS_CTRL, reg);
1923 if (ret)
1924 goto abort;
1925 }
1926
1927 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
1928 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
1929 * tunneling, determine priority by looking at 802.1p and IP
1930 * priority fields (IP prio has precedence), and set STP state
1931 * to Forwarding.
1932 *
1933 * If this is the CPU link, use DSA or EDSA tagging depending
1934 * on which tagging mode was configured.
1935 *
1936 * If this is a link to another switch, use DSA tagging mode.
1937 *
1938 * If this is the upstream port for this switch, enable
1939 * forwarding of unknown unicasts and multicasts.
1940 */
1941 reg = 0;
1942 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
1943 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
1944 mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07001945 mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02001946 reg = PORT_CONTROL_IGMP_MLD_SNOOP |
1947 PORT_CONTROL_USE_TAG | PORT_CONTROL_USE_IP |
1948 PORT_CONTROL_STATE_FORWARDING;
1949 if (dsa_is_cpu_port(ds, port)) {
1950 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
1951 reg |= PORT_CONTROL_DSA_TAG;
1952 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07001953 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
1954 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02001955 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
1956 reg |= PORT_CONTROL_FRAME_ETHER_TYPE_DSA;
1957 else
1958 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunnc047a1f2015-09-29 01:50:56 +02001959 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
1960 PORT_CONTROL_FORWARD_UNKNOWN_MC;
Andrew Lunn54d792f2015-05-06 01:09:47 +02001961 }
1962
1963 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
1964 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
1965 mv88e6xxx_6095_family(ds) || mv88e6xxx_6065_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07001966 mv88e6xxx_6185_family(ds) || mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02001967 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
1968 reg |= PORT_CONTROL_EGRESS_ADD_TAG;
1969 }
1970 }
Andrew Lunn6083ce72015-08-17 23:52:52 +02001971 if (dsa_is_dsa_port(ds, port)) {
1972 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds))
1973 reg |= PORT_CONTROL_DSA_TAG;
1974 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
1975 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
1976 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02001977 reg |= PORT_CONTROL_FRAME_MODE_DSA;
Andrew Lunn6083ce72015-08-17 23:52:52 +02001978 }
1979
Andrew Lunn54d792f2015-05-06 01:09:47 +02001980 if (port == dsa_upstream_port(ds))
1981 reg |= PORT_CONTROL_FORWARD_UNKNOWN |
1982 PORT_CONTROL_FORWARD_UNKNOWN_MC;
1983 }
1984 if (reg) {
1985 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
1986 PORT_CONTROL, reg);
1987 if (ret)
1988 goto abort;
1989 }
1990
Vivien Didelot8efdda42015-08-13 12:52:23 -04001991 /* Port Control 2: don't force a good FCS, set the maximum frame size to
1992 * 10240 bytes, enable secure 802.1q tags, don't discard tagged or
1993 * untagged frames on this port, do a destination address lookup on all
1994 * received packets as usual, disable ARP mirroring and don't send a
1995 * copy of all transmitted/received frames on this port to the CPU.
Andrew Lunn54d792f2015-05-06 01:09:47 +02001996 */
1997 reg = 0;
1998 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
1999 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002000 mv88e6xxx_6095_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002001 reg = PORT_CONTROL_2_MAP_DA;
2002
2003 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002004 mv88e6xxx_6165_family(ds) || mv88e6xxx_6320_family(ds))
Andrew Lunn54d792f2015-05-06 01:09:47 +02002005 reg |= PORT_CONTROL_2_JUMBO_10240;
2006
2007 if (mv88e6xxx_6095_family(ds) || mv88e6xxx_6185_family(ds)) {
2008 /* Set the upstream port this port should use */
2009 reg |= dsa_upstream_port(ds);
2010 /* enable forwarding of unknown multicast addresses to
2011 * the upstream port
2012 */
2013 if (port == dsa_upstream_port(ds))
2014 reg |= PORT_CONTROL_2_FORWARD_UNKNOWN;
2015 }
2016
Vivien Didelot5fe7f682015-10-11 18:08:38 -04002017 reg |= PORT_CONTROL_2_8021Q_SECURE;
Vivien Didelot8efdda42015-08-13 12:52:23 -04002018
Andrew Lunn54d792f2015-05-06 01:09:47 +02002019 if (reg) {
2020 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2021 PORT_CONTROL_2, reg);
2022 if (ret)
2023 goto abort;
2024 }
2025
2026 /* Port Association Vector: when learning source addresses
2027 * of packets, add the address to the address database using
2028 * a port bitmap that has only the bit for this port set and
2029 * the other bits clear.
2030 */
2031 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_ASSOC_VECTOR,
2032 1 << port);
2033 if (ret)
2034 goto abort;
2035
2036 /* Egress rate control 2: disable egress rate control. */
2037 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_RATE_CONTROL_2,
2038 0x0000);
2039 if (ret)
2040 goto abort;
2041
2042 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002043 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2044 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002045 /* Do not limit the period of time that this port can
2046 * be paused for by the remote end or the period of
2047 * time that this port can pause the remote end.
2048 */
2049 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2050 PORT_PAUSE_CTRL, 0x0000);
2051 if (ret)
2052 goto abort;
2053
2054 /* Port ATU control: disable limiting the number of
2055 * address database entries that this port is allowed
2056 * to use.
2057 */
2058 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2059 PORT_ATU_CONTROL, 0x0000);
2060 /* Priority Override: disable DA, SA and VTU priority
2061 * override.
2062 */
2063 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2064 PORT_PRI_OVERRIDE, 0x0000);
2065 if (ret)
2066 goto abort;
2067
2068 /* Port Ethertype: use the Ethertype DSA Ethertype
2069 * value.
2070 */
2071 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2072 PORT_ETH_TYPE, ETH_P_EDSA);
2073 if (ret)
2074 goto abort;
2075 /* Tag Remap: use an identity 802.1p prio -> switch
2076 * prio mapping.
2077 */
2078 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2079 PORT_TAG_REGMAP_0123, 0x3210);
2080 if (ret)
2081 goto abort;
2082
2083 /* Tag Remap 2: use an identity 802.1p prio -> switch
2084 * prio mapping.
2085 */
2086 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2087 PORT_TAG_REGMAP_4567, 0x7654);
2088 if (ret)
2089 goto abort;
2090 }
2091
2092 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2093 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002094 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
2095 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002096 /* Rate Control: disable ingress rate limiting. */
2097 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port),
2098 PORT_RATE_CONTROL, 0x0001);
2099 if (ret)
2100 goto abort;
2101 }
2102
Guenter Roeck366f0a02015-03-26 18:36:30 -07002103 /* Port Control 1: disable trunking, disable sending
2104 * learning messages to this port.
Guenter Roeckd827e882015-03-26 18:36:29 -07002105 */
Vivien Didelot614f03f2015-04-20 17:19:23 -04002106 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002107 if (ret)
2108 goto abort;
2109
Vivien Didelotf02bdff2015-10-11 18:08:36 -04002110 /* Port based VLAN map: do not give each port its own address
Vivien Didelot5fe7f682015-10-11 18:08:38 -04002111 * database, and allow every port to egress frames on all other ports.
Guenter Roeckd827e882015-03-26 18:36:29 -07002112 */
Vivien Didelot5fe7f682015-10-11 18:08:38 -04002113 reg = BIT(ps->num_ports) - 1; /* all ports */
Vivien Didelotede80982015-10-11 18:08:35 -04002114 ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg & ~port);
Guenter Roeckd827e882015-03-26 18:36:29 -07002115 if (ret)
2116 goto abort;
2117
2118 /* Default VLAN ID and priority: don't set a default VLAN
2119 * ID, and set the default packet priority to zero.
2120 */
Vivien Didelot47cf1e652015-04-20 17:43:26 -04002121 ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_DEFAULT_VLAN,
2122 0x0000);
Guenter Roeckd827e882015-03-26 18:36:29 -07002123abort:
2124 mutex_unlock(&ps->smi_mutex);
2125 return ret;
2126}
2127
Andrew Lunndbde9e62015-05-06 01:09:48 +02002128int mv88e6xxx_setup_ports(struct dsa_switch *ds)
2129{
2130 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2131 int ret;
2132 int i;
2133
2134 for (i = 0; i < ps->num_ports; i++) {
2135 ret = mv88e6xxx_setup_port(ds, i);
2136 if (ret < 0)
2137 return ret;
2138 }
2139 return 0;
2140}
2141
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002142int mv88e6xxx_setup_common(struct dsa_switch *ds)
2143{
2144 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2145
2146 mutex_init(&ps->smi_mutex);
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002147
Andrew Lunncca8b132015-04-02 04:06:39 +02002148 ps->id = REG_READ(REG_PORT(0), PORT_SWITCH_ID) & 0xfff0;
Andrew Lunna8f064c2015-03-26 18:36:40 -07002149
Guenter Roeckfacd95b2015-03-26 18:36:35 -07002150 INIT_WORK(&ps->bridge_work, mv88e6xxx_bridge_work);
2151
Guenter Roeckacdaffc2015-03-26 18:36:28 -07002152 return 0;
2153}
2154
Andrew Lunn54d792f2015-05-06 01:09:47 +02002155int mv88e6xxx_setup_global(struct dsa_switch *ds)
2156{
2157 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
Vivien Didelot24751e22015-08-03 09:17:44 -04002158 int ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002159 int i;
2160
2161 /* Set the default address aging time to 5 minutes, and
2162 * enable address learn messages to be sent to all message
2163 * ports.
2164 */
2165 REG_WRITE(REG_GLOBAL, GLOBAL_ATU_CONTROL,
2166 0x0140 | GLOBAL_ATU_CONTROL_LEARN2ALL);
2167
2168 /* Configure the IP ToS mapping registers. */
2169 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_0, 0x0000);
2170 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_1, 0x0000);
2171 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_2, 0x5555);
2172 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_3, 0x5555);
2173 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_4, 0xaaaa);
2174 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_5, 0xaaaa);
2175 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_6, 0xffff);
2176 REG_WRITE(REG_GLOBAL, GLOBAL_IP_PRI_7, 0xffff);
2177
2178 /* Configure the IEEE 802.1p priority mapping register. */
2179 REG_WRITE(REG_GLOBAL, GLOBAL_IEEE_PRI, 0xfa41);
2180
2181 /* Send all frames with destination addresses matching
2182 * 01:80:c2:00:00:0x to the CPU port.
2183 */
2184 REG_WRITE(REG_GLOBAL2, GLOBAL2_MGMT_EN_0X, 0xffff);
2185
2186 /* Ignore removed tag data on doubly tagged packets, disable
2187 * flow control messages, force flow control priority to the
2188 * highest, and send all special multicast frames to the CPU
2189 * port at the highest priority.
2190 */
2191 REG_WRITE(REG_GLOBAL2, GLOBAL2_SWITCH_MGMT,
2192 0x7 | GLOBAL2_SWITCH_MGMT_RSVD2CPU | 0x70 |
2193 GLOBAL2_SWITCH_MGMT_FORCE_FLOW_CTRL_PRI);
2194
2195 /* Program the DSA routing table. */
2196 for (i = 0; i < 32; i++) {
2197 int nexthop = 0x1f;
2198
2199 if (ds->pd->rtable &&
2200 i != ds->index && i < ds->dst->pd->nr_chips)
2201 nexthop = ds->pd->rtable[i] & 0x1f;
2202
2203 REG_WRITE(REG_GLOBAL2, GLOBAL2_DEVICE_MAPPING,
2204 GLOBAL2_DEVICE_MAPPING_UPDATE |
2205 (i << GLOBAL2_DEVICE_MAPPING_TARGET_SHIFT) |
2206 nexthop);
2207 }
2208
2209 /* Clear all trunk masks. */
2210 for (i = 0; i < 8; i++)
2211 REG_WRITE(REG_GLOBAL2, GLOBAL2_TRUNK_MASK,
2212 0x8000 | (i << GLOBAL2_TRUNK_MASK_NUM_SHIFT) |
2213 ((1 << ps->num_ports) - 1));
2214
2215 /* Clear all trunk mappings. */
2216 for (i = 0; i < 16; i++)
2217 REG_WRITE(REG_GLOBAL2, GLOBAL2_TRUNK_MAPPING,
2218 GLOBAL2_TRUNK_MAPPING_UPDATE |
2219 (i << GLOBAL2_TRUNK_MAPPING_ID_SHIFT));
2220
2221 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002222 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
2223 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002224 /* Send all frames with destination addresses matching
2225 * 01:80:c2:00:00:2x to the CPU port.
2226 */
2227 REG_WRITE(REG_GLOBAL2, GLOBAL2_MGMT_EN_2X, 0xffff);
2228
2229 /* Initialise cross-chip port VLAN table to reset
2230 * defaults.
2231 */
2232 REG_WRITE(REG_GLOBAL2, GLOBAL2_PVT_ADDR, 0x9000);
2233
2234 /* Clear the priority override table. */
2235 for (i = 0; i < 16; i++)
2236 REG_WRITE(REG_GLOBAL2, GLOBAL2_PRIO_OVERRIDE,
2237 0x8000 | (i << 8));
2238 }
2239
2240 if (mv88e6xxx_6352_family(ds) || mv88e6xxx_6351_family(ds) ||
2241 mv88e6xxx_6165_family(ds) || mv88e6xxx_6097_family(ds) ||
Aleksey S. Kazantsev7c3d0d62015-07-07 20:38:15 -07002242 mv88e6xxx_6185_family(ds) || mv88e6xxx_6095_family(ds) ||
2243 mv88e6xxx_6320_family(ds)) {
Andrew Lunn54d792f2015-05-06 01:09:47 +02002244 /* Disable ingress rate limiting by resetting all
2245 * ingress rate limit registers to their initial
2246 * state.
2247 */
2248 for (i = 0; i < ps->num_ports; i++)
2249 REG_WRITE(REG_GLOBAL2, GLOBAL2_INGRESS_OP,
2250 0x9000 | (i << 8));
2251 }
2252
Andrew Lunndb687a52015-06-20 21:31:29 +02002253 /* Clear the statistics counters for all ports */
2254 REG_WRITE(REG_GLOBAL, GLOBAL_STATS_OP, GLOBAL_STATS_OP_FLUSH_ALL);
2255
2256 /* Wait for the flush to complete. */
Vivien Didelot24751e22015-08-03 09:17:44 -04002257 mutex_lock(&ps->smi_mutex);
2258 ret = _mv88e6xxx_stats_wait(ds);
Vivien Didelot6b17e862015-08-13 12:52:18 -04002259 if (ret < 0)
2260 goto unlock;
2261
Vivien Didelotc161d0a2015-09-04 14:34:13 -04002262 /* Clear all ATU entries */
2263 ret = _mv88e6xxx_atu_flush(ds, 0, true);
2264 if (ret < 0)
2265 goto unlock;
2266
Vivien Didelot6b17e862015-08-13 12:52:18 -04002267 /* Clear all the VTU and STU entries */
2268 ret = _mv88e6xxx_vtu_stu_flush(ds);
2269unlock:
Vivien Didelot24751e22015-08-03 09:17:44 -04002270 mutex_unlock(&ps->smi_mutex);
Andrew Lunndb687a52015-06-20 21:31:29 +02002271
Vivien Didelot24751e22015-08-03 09:17:44 -04002272 return ret;
Andrew Lunn54d792f2015-05-06 01:09:47 +02002273}
2274
Andrew Lunn143a8302015-04-02 04:06:34 +02002275int mv88e6xxx_switch_reset(struct dsa_switch *ds, bool ppu_active)
2276{
2277 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2278 u16 is_reset = (ppu_active ? 0x8800 : 0xc800);
2279 unsigned long timeout;
2280 int ret;
2281 int i;
2282
2283 /* Set all ports to the disabled state. */
2284 for (i = 0; i < ps->num_ports; i++) {
Andrew Lunncca8b132015-04-02 04:06:39 +02002285 ret = REG_READ(REG_PORT(i), PORT_CONTROL);
2286 REG_WRITE(REG_PORT(i), PORT_CONTROL, ret & 0xfffc);
Andrew Lunn143a8302015-04-02 04:06:34 +02002287 }
2288
2289 /* Wait for transmit queues to drain. */
2290 usleep_range(2000, 4000);
2291
2292 /* Reset the switch. Keep the PPU active if requested. The PPU
2293 * needs to be active to support indirect phy register access
2294 * through global registers 0x18 and 0x19.
2295 */
2296 if (ppu_active)
2297 REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
2298 else
2299 REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
2300
2301 /* Wait up to one second for reset to complete. */
2302 timeout = jiffies + 1 * HZ;
2303 while (time_before(jiffies, timeout)) {
2304 ret = REG_READ(REG_GLOBAL, 0x00);
2305 if ((ret & is_reset) == is_reset)
2306 break;
2307 usleep_range(1000, 2000);
2308 }
2309 if (time_after(jiffies, timeout))
2310 return -ETIMEDOUT;
2311
2312 return 0;
2313}
2314
Andrew Lunn491435852015-04-02 04:06:35 +02002315int mv88e6xxx_phy_page_read(struct dsa_switch *ds, int port, int page, int reg)
2316{
2317 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2318 int ret;
2319
Andrew Lunn3898c142015-05-06 01:09:53 +02002320 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002321 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
Andrew Lunn491435852015-04-02 04:06:35 +02002322 if (ret < 0)
2323 goto error;
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002324 ret = _mv88e6xxx_phy_read_indirect(ds, port, reg);
Andrew Lunn491435852015-04-02 04:06:35 +02002325error:
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002326 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
Andrew Lunn3898c142015-05-06 01:09:53 +02002327 mutex_unlock(&ps->smi_mutex);
Andrew Lunn491435852015-04-02 04:06:35 +02002328 return ret;
2329}
2330
2331int mv88e6xxx_phy_page_write(struct dsa_switch *ds, int port, int page,
2332 int reg, int val)
2333{
2334 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2335 int ret;
2336
Andrew Lunn3898c142015-05-06 01:09:53 +02002337 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002338 ret = _mv88e6xxx_phy_write_indirect(ds, port, 0x16, page);
Andrew Lunn491435852015-04-02 04:06:35 +02002339 if (ret < 0)
2340 goto error;
2341
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002342 ret = _mv88e6xxx_phy_write_indirect(ds, port, reg, val);
Andrew Lunn491435852015-04-02 04:06:35 +02002343error:
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002344 _mv88e6xxx_phy_write_indirect(ds, port, 0x16, 0x0);
Andrew Lunn3898c142015-05-06 01:09:53 +02002345 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002346 return ret;
2347}
2348
2349static int mv88e6xxx_port_to_phy_addr(struct dsa_switch *ds, int port)
2350{
2351 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2352
2353 if (port >= 0 && port < ps->num_ports)
2354 return port;
2355 return -EINVAL;
2356}
2357
2358int
2359mv88e6xxx_phy_read(struct dsa_switch *ds, int port, int regnum)
2360{
2361 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2362 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2363 int ret;
2364
2365 if (addr < 0)
2366 return addr;
2367
Andrew Lunn3898c142015-05-06 01:09:53 +02002368 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002369 ret = _mv88e6xxx_phy_read(ds, addr, regnum);
Andrew Lunn3898c142015-05-06 01:09:53 +02002370 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002371 return ret;
2372}
2373
2374int
2375mv88e6xxx_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
2376{
2377 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2378 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2379 int ret;
2380
2381 if (addr < 0)
2382 return addr;
2383
Andrew Lunn3898c142015-05-06 01:09:53 +02002384 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002385 ret = _mv88e6xxx_phy_write(ds, addr, regnum, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002386 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002387 return ret;
2388}
2389
2390int
2391mv88e6xxx_phy_read_indirect(struct dsa_switch *ds, int port, int regnum)
2392{
2393 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2394 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2395 int ret;
2396
2397 if (addr < 0)
2398 return addr;
2399
Andrew Lunn3898c142015-05-06 01:09:53 +02002400 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002401 ret = _mv88e6xxx_phy_read_indirect(ds, addr, regnum);
Andrew Lunn3898c142015-05-06 01:09:53 +02002402 mutex_unlock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002403 return ret;
2404}
2405
2406int
2407mv88e6xxx_phy_write_indirect(struct dsa_switch *ds, int port, int regnum,
2408 u16 val)
2409{
2410 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2411 int addr = mv88e6xxx_port_to_phy_addr(ds, port);
2412 int ret;
2413
2414 if (addr < 0)
2415 return addr;
2416
Andrew Lunn3898c142015-05-06 01:09:53 +02002417 mutex_lock(&ps->smi_mutex);
Andrew Lunnfd3a0ee2015-04-02 04:06:36 +02002418 ret = _mv88e6xxx_phy_write_indirect(ds, addr, regnum, val);
Andrew Lunn3898c142015-05-06 01:09:53 +02002419 mutex_unlock(&ps->smi_mutex);
Andrew Lunn491435852015-04-02 04:06:35 +02002420 return ret;
2421}
2422
Guenter Roeckc22995c2015-07-25 09:42:28 -07002423#ifdef CONFIG_NET_DSA_HWMON
2424
2425static int mv88e61xx_get_temp(struct dsa_switch *ds, int *temp)
2426{
2427 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
2428 int ret;
2429 int val;
2430
2431 *temp = 0;
2432
2433 mutex_lock(&ps->smi_mutex);
2434
2435 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x6);
2436 if (ret < 0)
2437 goto error;
2438
2439 /* Enable temperature sensor */
2440 ret = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
2441 if (ret < 0)
2442 goto error;
2443
2444 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret | (1 << 5));
2445 if (ret < 0)
2446 goto error;
2447
2448 /* Wait for temperature to stabilize */
2449 usleep_range(10000, 12000);
2450
2451 val = _mv88e6xxx_phy_read(ds, 0x0, 0x1a);
2452 if (val < 0) {
2453 ret = val;
2454 goto error;
2455 }
2456
2457 /* Disable temperature sensor */
2458 ret = _mv88e6xxx_phy_write(ds, 0x0, 0x1a, ret & ~(1 << 5));
2459 if (ret < 0)
2460 goto error;
2461
2462 *temp = ((val & 0x1f) - 5) * 5;
2463
2464error:
2465 _mv88e6xxx_phy_write(ds, 0x0, 0x16, 0x0);
2466 mutex_unlock(&ps->smi_mutex);
2467 return ret;
2468}
2469
2470static int mv88e63xx_get_temp(struct dsa_switch *ds, int *temp)
2471{
2472 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
2473 int ret;
2474
2475 *temp = 0;
2476
2477 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 27);
2478 if (ret < 0)
2479 return ret;
2480
2481 *temp = (ret & 0xff) - 25;
2482
2483 return 0;
2484}
2485
2486int mv88e6xxx_get_temp(struct dsa_switch *ds, int *temp)
2487{
2488 if (mv88e6xxx_6320_family(ds) || mv88e6xxx_6352_family(ds))
2489 return mv88e63xx_get_temp(ds, temp);
2490
2491 return mv88e61xx_get_temp(ds, temp);
2492}
2493
2494int mv88e6xxx_get_temp_limit(struct dsa_switch *ds, int *temp)
2495{
2496 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
2497 int ret;
2498
2499 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
2500 return -EOPNOTSUPP;
2501
2502 *temp = 0;
2503
2504 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
2505 if (ret < 0)
2506 return ret;
2507
2508 *temp = (((ret >> 8) & 0x1f) * 5) - 25;
2509
2510 return 0;
2511}
2512
2513int mv88e6xxx_set_temp_limit(struct dsa_switch *ds, int temp)
2514{
2515 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
2516 int ret;
2517
2518 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
2519 return -EOPNOTSUPP;
2520
2521 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
2522 if (ret < 0)
2523 return ret;
2524 temp = clamp_val(DIV_ROUND_CLOSEST(temp, 5) + 5, 0, 0x1f);
2525 return mv88e6xxx_phy_page_write(ds, phy, 6, 26,
2526 (ret & 0xe0ff) | (temp << 8));
2527}
2528
2529int mv88e6xxx_get_temp_alarm(struct dsa_switch *ds, bool *alarm)
2530{
2531 int phy = mv88e6xxx_6320_family(ds) ? 3 : 0;
2532 int ret;
2533
2534 if (!mv88e6xxx_6320_family(ds) && !mv88e6xxx_6352_family(ds))
2535 return -EOPNOTSUPP;
2536
2537 *alarm = false;
2538
2539 ret = mv88e6xxx_phy_page_read(ds, phy, 6, 26);
2540 if (ret < 0)
2541 return ret;
2542
2543 *alarm = !!(ret & 0x40);
2544
2545 return 0;
2546}
2547#endif /* CONFIG_NET_DSA_HWMON */
2548
Ben Hutchings98e67302011-11-25 14:36:19 +00002549static int __init mv88e6xxx_init(void)
2550{
2551#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
2552 register_switch_driver(&mv88e6131_switch_driver);
2553#endif
2554#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
2555 register_switch_driver(&mv88e6123_61_65_switch_driver);
2556#endif
Guenter Roeck3ad50cc2014-10-29 10:44:56 -07002557#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
2558 register_switch_driver(&mv88e6352_switch_driver);
2559#endif
Andrew Lunn42f27252014-09-12 23:58:44 +02002560#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
2561 register_switch_driver(&mv88e6171_switch_driver);
2562#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00002563 return 0;
2564}
2565module_init(mv88e6xxx_init);
2566
2567static void __exit mv88e6xxx_cleanup(void)
2568{
Andrew Lunn42f27252014-09-12 23:58:44 +02002569#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
2570 unregister_switch_driver(&mv88e6171_switch_driver);
2571#endif
Vivien Didelot4212b542015-05-01 10:43:52 -04002572#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
2573 unregister_switch_driver(&mv88e6352_switch_driver);
2574#endif
Ben Hutchings98e67302011-11-25 14:36:19 +00002575#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
2576 unregister_switch_driver(&mv88e6123_61_65_switch_driver);
2577#endif
2578#if IS_ENABLED(CONFIG_NET_DSA_MV88E6131)
2579 unregister_switch_driver(&mv88e6131_switch_driver);
2580#endif
2581}
2582module_exit(mv88e6xxx_cleanup);
Ben Hutchings3d825ed2011-11-25 14:37:16 +00002583
2584MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>");
2585MODULE_DESCRIPTION("Driver for Marvell 88E6XXX ethernet switch chips");
2586MODULE_LICENSE("GPL");