summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuenter Roeck <linux@roeck-us.net>2014-10-29 13:44:56 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-30 14:54:10 -0400
commit3ad50cca3919c08472232a633806f591216732b9 (patch)
tree6ac0cc458846c5103af232bda1c25ab6ac4177a4
parenta93e464a45827cbbbfc89183cf4a769b18f31535 (diff)
net: dsa: Add support for Marvell 88E6352
Marvell 88E6352 is mostly compatible to MV88E6123/61/65, but requires indirect phy access. Also, its configuration registers are a bit different. Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/net/dsa/Kconfig8
-rw-r--r--drivers/net/dsa/Makefile3
-rw-r--r--drivers/net/dsa/mv88e6352.c464
-rw-r--r--drivers/net/dsa/mv88e6xxx.c3
-rw-r--r--drivers/net/dsa/mv88e6xxx.h7
6 files changed, 490 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 43898b1a8a2d..e4082ec8daeb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5848,6 +5848,11 @@ M: Russell King <rmk+kernel@arm.linux.org.uk>
5848S: Maintained 5848S: Maintained
5849F: drivers/gpu/drm/armada/ 5849F: drivers/gpu/drm/armada/
5850 5850
5851MARVELL 88E6352 DSA support
5852M: Guenter Roeck <linux@roeck-us.net>
5853S: Maintained
5854F: drivers/net/dsa/mv88e6352.c
5855
5851MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2) 5856MARVELL GIGABIT ETHERNET DRIVERS (skge/sky2)
5852M: Mirko Lindner <mlindner@marvell.com> 5857M: Mirko Lindner <mlindner@marvell.com>
5853M: Stephen Hemminger <stephen@networkplumber.org> 5858M: Stephen Hemminger <stephen@networkplumber.org>
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 9234d808cbb3..0987c336e517 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -45,6 +45,14 @@ config NET_DSA_MV88E6171
45 This enables support for the Marvell 88E6171 ethernet switch 45 This enables support for the Marvell 88E6171 ethernet switch
46 chip. 46 chip.
47 47
48config NET_DSA_MV88E6352
49 tristate "Marvell 88E6352 ethernet switch chip support"
50 select NET_DSA
51 select NET_DSA_MV88E6XXX
52 select NET_DSA_TAG_EDSA
53 ---help---
54 This enables support for the Marvell 88E6352 ethernet switch chip.
55
48config NET_DSA_BCM_SF2 56config NET_DSA_BCM_SF2
49 tristate "Broadcom Starfighter 2 Ethernet switch support" 57 tristate "Broadcom Starfighter 2 Ethernet switch support"
50 depends on HAS_IOMEM 58 depends on HAS_IOMEM
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index 23a90de9830e..e2d51c4b9382 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -7,6 +7,9 @@ endif
7ifdef CONFIG_NET_DSA_MV88E6131 7ifdef CONFIG_NET_DSA_MV88E6131
8mv88e6xxx_drv-y += mv88e6131.o 8mv88e6xxx_drv-y += mv88e6131.o
9endif 9endif
10ifdef CONFIG_NET_DSA_MV88E6352
11mv88e6xxx_drv-y += mv88e6352.o
12endif
10ifdef CONFIG_NET_DSA_MV88E6171 13ifdef CONFIG_NET_DSA_MV88E6171
11mv88e6xxx_drv-y += mv88e6171.o 14mv88e6xxx_drv-y += mv88e6171.o
12endif 15endif
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c
new file mode 100644
index 000000000000..43a58262a03d
--- /dev/null
+++ b/drivers/net/dsa/mv88e6352.c
@@ -0,0 +1,464 @@
1/*
2 * net/dsa/mv88e6352.c - Marvell 88e6352 switch chip support
3 *
4 * Copyright (c) 2014 Guenter Roeck
5 *
6 * Derived from mv88e6123_61_65.c
7 * Copyright (c) 2008-2009 Marvell Semiconductor
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 */
14
15#include <linux/delay.h>
16#include <linux/jiffies.h>
17#include <linux/list.h>
18#include <linux/module.h>
19#include <linux/netdevice.h>
20#include <linux/platform_device.h>
21#include <linux/phy.h>
22#include <net/dsa.h>
23#include "mv88e6xxx.h"
24
25static int mv88e6352_phy_wait(struct dsa_switch *ds)
26{
27 unsigned long timeout = jiffies + HZ / 10;
28
29 while (time_before(jiffies, timeout)) {
30 int ret;
31
32 ret = REG_READ(REG_GLOBAL2, 0x18);
33 if (ret < 0)
34 return ret;
35
36 if (!(ret & 0x8000))
37 return 0;
38
39 usleep_range(1000, 2000);
40 }
41 return -ETIMEDOUT;
42}
43
44static int __mv88e6352_phy_read(struct dsa_switch *ds, int addr, int regnum)
45{
46 int ret;
47
48 REG_WRITE(REG_GLOBAL2, 0x18, 0x9800 | (addr << 5) | regnum);
49
50 ret = mv88e6352_phy_wait(ds);
51 if (ret < 0)
52 return ret;
53
54 return REG_READ(REG_GLOBAL2, 0x19);
55}
56
57static int __mv88e6352_phy_write(struct dsa_switch *ds, int addr, int regnum,
58 u16 val)
59{
60 REG_WRITE(REG_GLOBAL2, 0x19, val);
61 REG_WRITE(REG_GLOBAL2, 0x18, 0x9400 | (addr << 5) | regnum);
62
63 return mv88e6352_phy_wait(ds);
64}
65
66static char *mv88e6352_probe(struct device *host_dev, int sw_addr)
67{
68 struct mii_bus *bus = dsa_host_dev_to_mii_bus(host_dev);
69 int ret;
70
71 if (bus == NULL)
72 return NULL;
73
74 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
75 if (ret >= 0) {
76 if (ret == 0x3521)
77 return "Marvell 88E6352 (A0)";
78 if (ret == 0x3522)
79 return "Marvell 88E6352 (A1)";
80 if ((ret & 0xfff0) == 0x3520)
81 return "Marvell 88E6352";
82 }
83
84 return NULL;
85}
86
87static int mv88e6352_switch_reset(struct dsa_switch *ds)
88{
89 unsigned long timeout;
90 int ret;
91 int i;
92
93 /* Set all ports to the disabled state. */
94 for (i = 0; i < 7; i++) {
95 ret = REG_READ(REG_PORT(i), 0x04);
96 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
97 }
98
99 /* Wait for transmit queues to drain. */
100 usleep_range(2000, 4000);
101
102 /* Reset the switch. Keep PPU active (bit 14, undocumented).
103 * The PPU needs to be active to support indirect phy register
104 * accesses through global registers 0x18 and 0x19.
105 */
106 REG_WRITE(REG_GLOBAL, 0x04, 0xc000);
107
108 /* Wait up to one second for reset to complete. */
109 timeout = jiffies + 1 * HZ;
110 while (time_before(jiffies, timeout)) {
111 ret = REG_READ(REG_GLOBAL, 0x00);
112 if ((ret & 0x8800) == 0x8800)
113 break;
114 usleep_range(1000, 2000);
115 }
116 if (time_after(jiffies, timeout))
117 return -ETIMEDOUT;
118
119 return 0;
120}
121
122static int mv88e6352_setup_global(struct dsa_switch *ds)
123{
124 int ret;
125 int i;
126
127 /* Discard packets with excessive collisions,
128 * mask all interrupt sources, enable PPU (bit 14, undocumented).
129 */
130 REG_WRITE(REG_GLOBAL, 0x04, 0x6000);
131
132 /* Set the default address aging time to 5 minutes, and
133 * enable address learn messages to be sent to all message
134 * ports.
135 */
136 REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
137
138 /* Configure the priority mapping registers. */
139 ret = mv88e6xxx_config_prio(ds);
140 if (ret < 0)
141 return ret;
142
143 /* Configure the upstream port, and configure the upstream
144 * port as the port to which ingress and egress monitor frames
145 * are to be sent.
146 */
147 REG_WRITE(REG_GLOBAL, 0x1a, (dsa_upstream_port(ds) * 0x1110));
148
149 /* Disable remote management for now, and set the switch's
150 * DSA device number.
151 */
152 REG_WRITE(REG_GLOBAL, 0x1c, ds->index & 0x1f);
153
154 /* Send all frames with destination addresses matching
155 * 01:80:c2:00:00:2x to the CPU port.
156 */
157 REG_WRITE(REG_GLOBAL2, 0x02, 0xffff);
158
159 /* Send all frames with destination addresses matching
160 * 01:80:c2:00:00:0x to the CPU port.
161 */
162 REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
163
164 /* Disable the loopback filter, disable flow control
165 * messages, disable flood broadcast override, disable
166 * removing of provider tags, disable ATU age violation
167 * interrupts, disable tag flow control, force flow
168 * control priority to the highest, and send all special
169 * multicast frames to the CPU at the highest priority.
170 */
171 REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
172
173 /* Program the DSA routing table. */
174 for (i = 0; i < 32; i++) {
175 int nexthop = 0x1f;
176
177 if (i != ds->index && i < ds->dst->pd->nr_chips)
178 nexthop = ds->pd->rtable[i] & 0x1f;
179
180 REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | nexthop);
181 }
182
183 /* Clear all trunk masks. */
184 for (i = 0; i < 8; i++)
185 REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0x7f);
186
187 /* Clear all trunk mappings. */
188 for (i = 0; i < 16; i++)
189 REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
190
191 /* Disable ingress rate limiting by resetting all ingress
192 * rate limit registers to their initial state.
193 */
194 for (i = 0; i < 7; i++)
195 REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8));
196
197 /* Initialise cross-chip port VLAN table to reset defaults. */
198 REG_WRITE(REG_GLOBAL2, 0x0b, 0x9000);
199
200 /* Clear the priority override table. */
201 for (i = 0; i < 16; i++)
202 REG_WRITE(REG_GLOBAL2, 0x0f, 0x8000 | (i << 8));
203
204 /* @@@ initialise AVB (22/23) watchdog (27) sdet (29) registers */
205
206 return 0;
207}
208
209static int mv88e6352_setup_port(struct dsa_switch *ds, int p)
210{
211 int addr = REG_PORT(p);
212 u16 val;
213
214 /* MAC Forcing register: don't force link, speed, duplex
215 * or flow control state to any particular values on physical
216 * ports, but force the CPU port and all DSA ports to 1000 Mb/s
217 * full duplex.
218 */
219 if (dsa_is_cpu_port(ds, p) || ds->dsa_port_mask & (1 << p))
220 REG_WRITE(addr, 0x01, 0x003e);
221 else
222 REG_WRITE(addr, 0x01, 0x0003);
223
224 /* Do not limit the period of time that this port can be
225 * paused for by the remote end or the period of time that
226 * this port can pause the remote end.
227 */
228 REG_WRITE(addr, 0x02, 0x0000);
229
230 /* Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
231 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
232 * tunneling, determine priority by looking at 802.1p and IP
233 * priority fields (IP prio has precedence), and set STP state
234 * to Forwarding.
235 *
236 * If this is the CPU link, use DSA or EDSA tagging depending
237 * on which tagging mode was configured.
238 *
239 * If this is a link to another switch, use DSA tagging mode.
240 *
241 * If this is the upstream port for this switch, enable
242 * forwarding of unknown unicasts and multicasts.
243 */
244 val = 0x0433;
245 if (dsa_is_cpu_port(ds, p)) {
246 if (ds->dst->tag_protocol == DSA_TAG_PROTO_EDSA)
247 val |= 0x3300;
248 else
249 val |= 0x0100;
250 }
251 if (ds->dsa_port_mask & (1 << p))
252 val |= 0x0100;
253 if (p == dsa_upstream_port(ds))
254 val |= 0x000c;
255 REG_WRITE(addr, 0x04, val);
256
257 /* Port Control 1: disable trunking. Also, if this is the
258 * CPU port, enable learn messages to be sent to this port.
259 */
260 REG_WRITE(addr, 0x05, dsa_is_cpu_port(ds, p) ? 0x8000 : 0x0000);
261
262 /* Port based VLAN map: give each port its own address
263 * database, allow the CPU port to talk to each of the 'real'
264 * ports, and allow each of the 'real' ports to only talk to
265 * the upstream port.
266 */
267 val = (p & 0xf) << 12;
268 if (dsa_is_cpu_port(ds, p))
269 val |= ds->phys_port_mask;
270 else
271 val |= 1 << dsa_upstream_port(ds);
272 REG_WRITE(addr, 0x06, val);
273
274 /* Default VLAN ID and priority: don't set a default VLAN
275 * ID, and set the default packet priority to zero.
276 */
277 REG_WRITE(addr, 0x07, 0x0000);
278
279 /* Port Control 2: don't force a good FCS, set the maximum
280 * frame size to 10240 bytes, don't let the switch add or
281 * strip 802.1q tags, don't discard tagged or untagged frames
282 * on this port, do a destination address lookup on all
283 * received packets as usual, disable ARP mirroring and don't
284 * send a copy of all transmitted/received frames on this port
285 * to the CPU.
286 */
287 REG_WRITE(addr, 0x08, 0x2080);
288
289 /* Egress rate control: disable egress rate control. */
290 REG_WRITE(addr, 0x09, 0x0001);
291
292 /* Egress rate control 2: disable egress rate control. */
293 REG_WRITE(addr, 0x0a, 0x0000);
294
295 /* Port Association Vector: when learning source addresses
296 * of packets, add the address to the address database using
297 * a port bitmap that has only the bit for this port set and
298 * the other bits clear.
299 */
300 REG_WRITE(addr, 0x0b, 1 << p);
301
302 /* Port ATU control: disable limiting the number of address
303 * database entries that this port is allowed to use.
304 */
305 REG_WRITE(addr, 0x0c, 0x0000);
306
307 /* Priority Override: disable DA, SA and VTU priority override. */
308 REG_WRITE(addr, 0x0d, 0x0000);
309
310 /* Port Ethertype: use the Ethertype DSA Ethertype value. */
311 REG_WRITE(addr, 0x0f, ETH_P_EDSA);
312
313 /* Tag Remap: use an identity 802.1p prio -> switch prio
314 * mapping.
315 */
316 REG_WRITE(addr, 0x18, 0x3210);
317
318 /* Tag Remap 2: use an identity 802.1p prio -> switch prio
319 * mapping.
320 */
321 REG_WRITE(addr, 0x19, 0x7654);
322
323 return 0;
324}
325
326static int mv88e6352_setup(struct dsa_switch *ds)
327{
328 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
329 int ret;
330 int i;
331
332 mutex_init(&ps->smi_mutex);
333 mutex_init(&ps->stats_mutex);
334 mutex_init(&ps->phy_mutex);
335
336 ps->id = REG_READ(REG_PORT(0), 0x03) & 0xfff0;
337
338 ret = mv88e6352_switch_reset(ds);
339 if (ret < 0)
340 return ret;
341
342 /* @@@ initialise vtu and atu */
343
344 ret = mv88e6352_setup_global(ds);
345 if (ret < 0)
346 return ret;
347
348 for (i = 0; i < 7; i++) {
349 ret = mv88e6352_setup_port(ds, i);
350 if (ret < 0)
351 return ret;
352 }
353
354 return 0;
355}
356
357static int mv88e6352_port_to_phy_addr(int port)
358{
359 if (port >= 0 && port <= 4)
360 return port;
361 return -EINVAL;
362}
363
364static int
365mv88e6352_phy_read(struct dsa_switch *ds, int port, int regnum)
366{
367 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
368 int addr = mv88e6352_port_to_phy_addr(port);
369 int ret;
370
371 if (addr < 0)
372 return addr;
373
374 mutex_lock(&ps->phy_mutex);
375 ret = __mv88e6352_phy_read(ds, addr, regnum);
376 mutex_unlock(&ps->phy_mutex);
377
378 return ret;
379}
380
381static int
382mv88e6352_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
383{
384 struct mv88e6xxx_priv_state *ps = ds_to_priv(ds);
385 int addr = mv88e6352_port_to_phy_addr(port);
386 int ret;
387
388 if (addr < 0)
389 return addr;
390
391 mutex_lock(&ps->phy_mutex);
392 ret = __mv88e6352_phy_write(ds, addr, regnum, val);
393 mutex_unlock(&ps->phy_mutex);
394
395 return ret;
396}
397
398static struct mv88e6xxx_hw_stat mv88e6352_hw_stats[] = {
399 { "in_good_octets", 8, 0x00, },
400 { "in_bad_octets", 4, 0x02, },
401 { "in_unicast", 4, 0x04, },
402 { "in_broadcasts", 4, 0x06, },
403 { "in_multicasts", 4, 0x07, },
404 { "in_pause", 4, 0x16, },
405 { "in_undersize", 4, 0x18, },
406 { "in_fragments", 4, 0x19, },
407 { "in_oversize", 4, 0x1a, },
408 { "in_jabber", 4, 0x1b, },
409 { "in_rx_error", 4, 0x1c, },
410 { "in_fcs_error", 4, 0x1d, },
411 { "out_octets", 8, 0x0e, },
412 { "out_unicast", 4, 0x10, },
413 { "out_broadcasts", 4, 0x13, },
414 { "out_multicasts", 4, 0x12, },
415 { "out_pause", 4, 0x15, },
416 { "excessive", 4, 0x11, },
417 { "collisions", 4, 0x1e, },
418 { "deferred", 4, 0x05, },
419 { "single", 4, 0x14, },
420 { "multiple", 4, 0x17, },
421 { "out_fcs_error", 4, 0x03, },
422 { "late", 4, 0x1f, },
423 { "hist_64bytes", 4, 0x08, },
424 { "hist_65_127bytes", 4, 0x09, },
425 { "hist_128_255bytes", 4, 0x0a, },
426 { "hist_256_511bytes", 4, 0x0b, },
427 { "hist_512_1023bytes", 4, 0x0c, },
428 { "hist_1024_max_bytes", 4, 0x0d, },
429};
430
431static void
432mv88e6352_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
433{
434 mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6352_hw_stats),
435 mv88e6352_hw_stats, port, data);
436}
437
438static void
439mv88e6352_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data)
440{
441 mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6352_hw_stats),
442 mv88e6352_hw_stats, port, data);
443}
444
445static int mv88e6352_get_sset_count(struct dsa_switch *ds)
446{
447 return ARRAY_SIZE(mv88e6352_hw_stats);
448}
449
450struct dsa_switch_driver mv88e6352_switch_driver = {
451 .tag_protocol = DSA_TAG_PROTO_EDSA,
452 .priv_size = sizeof(struct mv88e6xxx_priv_state),
453 .probe = mv88e6352_probe,
454 .setup = mv88e6352_setup,
455 .set_addr = mv88e6xxx_set_addr_indirect,
456 .phy_read = mv88e6352_phy_read,
457 .phy_write = mv88e6352_phy_write,
458 .poll_link = mv88e6xxx_poll_link,
459 .get_strings = mv88e6352_get_strings,
460 .get_ethtool_stats = mv88e6352_get_ethtool_stats,
461 .get_sset_count = mv88e6352_get_sset_count,
462};
463
464MODULE_ALIAS("platform:mv88e6352");
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c
index a6c90cf5634d..8e1090ba3df3 100644
--- a/drivers/net/dsa/mv88e6xxx.c
+++ b/drivers/net/dsa/mv88e6xxx.c
@@ -507,6 +507,9 @@ static int __init mv88e6xxx_init(void)
507#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65) 507#if IS_ENABLED(CONFIG_NET_DSA_MV88E6123_61_65)
508 register_switch_driver(&mv88e6123_61_65_switch_driver); 508 register_switch_driver(&mv88e6123_61_65_switch_driver);
509#endif 509#endif
510#if IS_ENABLED(CONFIG_NET_DSA_MV88E6352)
511 register_switch_driver(&mv88e6352_switch_driver);
512#endif
510#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171) 513#if IS_ENABLED(CONFIG_NET_DSA_MV88E6171)
511 register_switch_driver(&mv88e6171_switch_driver); 514 register_switch_driver(&mv88e6171_switch_driver);
512#endif 515#endif
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h
index 5e5145ad9525..c0ce133c756b 100644
--- a/drivers/net/dsa/mv88e6xxx.h
+++ b/drivers/net/dsa/mv88e6xxx.h
@@ -37,6 +37,12 @@ struct mv88e6xxx_priv_state {
37 */ 37 */
38 struct mutex stats_mutex; 38 struct mutex stats_mutex;
39 39
40 /* This mutex serializes phy access for chips with
41 * indirect phy addressing. It is unused for chips
42 * with direct phy access.
43 */
44 struct mutex phy_mutex;
45
40 int id; /* switch product id */ 46 int id; /* switch product id */
41}; 47};
42 48
@@ -70,6 +76,7 @@ void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
70 76
71extern struct dsa_switch_driver mv88e6131_switch_driver; 77extern struct dsa_switch_driver mv88e6131_switch_driver;
72extern struct dsa_switch_driver mv88e6123_61_65_switch_driver; 78extern struct dsa_switch_driver mv88e6123_61_65_switch_driver;
79extern struct dsa_switch_driver mv88e6352_switch_driver;
73extern struct dsa_switch_driver mv88e6171_switch_driver; 80extern struct dsa_switch_driver mv88e6171_switch_driver;
74 81
75#define REG_READ(addr, reg) \ 82#define REG_READ(addr, reg) \