aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/mv88e6131.c
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2008-10-07 09:45:18 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-08 20:24:09 -0400
commit2e5f032095ff101274dfb03d5fd5e06d9aeb83cd (patch)
treeeeb61cf6665452288a25434c54bc8d4ff8031cef /net/dsa/mv88e6131.c
parentcf85d08fdf4548ee46657ccfb7f9949a85145db5 (diff)
dsa: add support for the Marvell 88E6131 switch chip
Add support for the Marvell 88E6131 switch chip. This chip only supports the original (ethertype-less) DSA tagging format. On the 88E6131, there is a PHY Polling Unit (PPU) which has exclusive access to each of the PHYs's MII management registers. If we want to talk to the PHYs from software, we have to disable the PPU and wait for it to complete its current transaction before we can do so, and we need to re-enable the PPU afterwards to make sure that the switch will notice changes in link state and speed on the individual ports as they occur. Since disabling the PPU is rather slow, and since MII management accesses are typically done in bursts, this patch keeps the PPU disabled for 10ms after a software access completes. This makes handling the PPU slightly more complex, but speeds up something like running ethtool on one of the switch slave interfaces from ~300ms to ~30ms on typical hardware. Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Nicolas Pitre <nico@marvell.com> Tested-by: Peter van Valderen <linux@ddcrew.com> Tested-by: Dirk Teurlings <dirk@upexia.nl> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa/mv88e6131.c')
-rw-r--r--net/dsa/mv88e6131.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/net/dsa/mv88e6131.c b/net/dsa/mv88e6131.c
new file mode 100644
index 000000000000..36e01eb863a0
--- /dev/null
+++ b/net/dsa/mv88e6131.c
@@ -0,0 +1,380 @@
1/*
2 * net/dsa/mv88e6131.c - Marvell 88e6131 switch chip support
3 * Copyright (c) 2008 Marvell Semiconductor
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11#include <linux/list.h>
12#include <linux/netdevice.h>
13#include <linux/phy.h>
14#include "dsa_priv.h"
15#include "mv88e6xxx.h"
16
17static char *mv88e6131_probe(struct mii_bus *bus, int sw_addr)
18{
19 int ret;
20
21 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
22 if (ret >= 0) {
23 ret &= 0xfff0;
24 if (ret == 0x1060)
25 return "Marvell 88E6131";
26 }
27
28 return NULL;
29}
30
31static int mv88e6131_switch_reset(struct dsa_switch *ds)
32{
33 int i;
34 int ret;
35
36 /*
37 * Set all ports to the disabled state.
38 */
39 for (i = 0; i < 8; i++) {
40 ret = REG_READ(REG_PORT(i), 0x04);
41 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
42 }
43
44 /*
45 * Wait for transmit queues to drain.
46 */
47 msleep(2);
48
49 /*
50 * Reset the switch.
51 */
52 REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
53
54 /*
55 * Wait up to one second for reset to complete.
56 */
57 for (i = 0; i < 1000; i++) {
58 ret = REG_READ(REG_GLOBAL, 0x00);
59 if ((ret & 0xc800) == 0xc800)
60 break;
61
62 msleep(1);
63 }
64 if (i == 1000)
65 return -ETIMEDOUT;
66
67 return 0;
68}
69
70static int mv88e6131_setup_global(struct dsa_switch *ds)
71{
72 int ret;
73 int i;
74
75 /*
76 * Enable the PHY polling unit, don't discard packets with
77 * excessive collisions, use a weighted fair queueing scheme
78 * to arbitrate between packet queues, set the maximum frame
79 * size to 1632, and mask all interrupt sources.
80 */
81 REG_WRITE(REG_GLOBAL, 0x04, 0x4400);
82
83 /*
84 * Set the default address aging time to 5 minutes, and
85 * enable address learn messages to be sent to all message
86 * ports.
87 */
88 REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
89
90 /*
91 * Configure the priority mapping registers.
92 */
93 ret = mv88e6xxx_config_prio(ds);
94 if (ret < 0)
95 return ret;
96
97 /*
98 * Set the VLAN ethertype to 0x8100.
99 */
100 REG_WRITE(REG_GLOBAL, 0x19, 0x8100);
101
102 /*
103 * Disable ARP mirroring, and configure the cpu port as the
104 * port to which ingress and egress monitor frames are to be
105 * sent.
106 */
107 REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1100) | 0x00f0);
108
109 /*
110 * Disable cascade port functionality, and set the switch's
111 * DSA device number to zero.
112 */
113 REG_WRITE(REG_GLOBAL, 0x1c, 0xe000);
114
115 /*
116 * Send all frames with destination addresses matching
117 * 01:80:c2:00:00:0x to the CPU port.
118 */
119 REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
120
121 /*
122 * Ignore removed tag data on doubly tagged packets, disable
123 * flow control messages, force flow control priority to the
124 * highest, and send all special multicast frames to the CPU
125 * port at the higest priority.
126 */
127 REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
128
129 /*
130 * Map all DSA device IDs to the CPU port.
131 */
132 for (i = 0; i < 32; i++)
133 REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port);
134
135 /*
136 * Clear all trunk masks.
137 */
138 for (i = 0; i < 8; i++)
139 REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
140
141 /*
142 * Clear all trunk mappings.
143 */
144 for (i = 0; i < 16; i++)
145 REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
146
147 /*
148 * Force the priority of IGMP/MLD snoop frames and ARP frames
149 * to the highest setting.
150 */
151 REG_WRITE(REG_GLOBAL2, 0x0f, 0x00ff);
152
153 return 0;
154}
155
156static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
157{
158 int addr = REG_PORT(p);
159
160 /*
161 * MAC Forcing register: don't force link, speed, duplex
162 * or flow control state to any particular values.
163 */
164 REG_WRITE(addr, 0x01, 0x0003);
165
166 /*
167 * Port Control: disable Core Tag, disable Drop-on-Lock,
168 * transmit frames unmodified, disable Header mode,
169 * enable IGMP/MLD snoop, disable DoubleTag, disable VLAN
170 * tunneling, determine priority by looking at 802.1p and
171 * IP priority fields (IP prio has precedence), and set STP
172 * state to Forwarding. Finally, if this is the CPU port,
173 * additionally enable DSA tagging and forwarding of unknown
174 * unicast addresses.
175 */
176 REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x0537 : 0x0433);
177
178 /*
179 * Port Control 1: disable trunking. Also, if this is the
180 * CPU port, enable learn messages to be sent to this port.
181 */
182 REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000);
183
184 /*
185 * Port based VLAN map: give each port its own address
186 * database, allow the CPU port to talk to each of the 'real'
187 * ports, and allow each of the 'real' ports to only talk to
188 * the CPU port.
189 */
190 REG_WRITE(addr, 0x06,
191 ((p & 0xf) << 12) |
192 ((p == ds->cpu_port) ?
193 ds->valid_port_mask :
194 (1 << ds->cpu_port)));
195
196 /*
197 * Default VLAN ID and priority: don't set a default VLAN
198 * ID, and set the default packet priority to zero.
199 */
200 REG_WRITE(addr, 0x07, 0x0000);
201
202 /*
203 * Port Control 2: don't force a good FCS, don't use
204 * VLAN-based, source address-based or destination
205 * address-based priority overrides, don't let the switch
206 * add or strip 802.1q tags, don't discard tagged or
207 * untagged frames on this port, do a destination address
208 * lookup on received packets as usual, don't send a copy
209 * of all transmitted/received frames on this port to the
210 * CPU, and configure the CPU port number. Also, if this
211 * is the CPU port, enable forwarding of unknown multicast
212 * addresses.
213 */
214 REG_WRITE(addr, 0x08,
215 ((p == ds->cpu_port) ? 0x00c0 : 0x0080) |
216 ds->cpu_port);
217
218 /*
219 * Rate Control: disable ingress rate limiting.
220 */
221 REG_WRITE(addr, 0x09, 0x0000);
222
223 /*
224 * Rate Control 2: disable egress rate limiting.
225 */
226 REG_WRITE(addr, 0x0a, 0x0000);
227
228 /*
229 * Port Association Vector: when learning source addresses
230 * of packets, add the address to the address database using
231 * a port bitmap that has only the bit for this port set and
232 * the other bits clear.
233 */
234 REG_WRITE(addr, 0x0b, 1 << p);
235
236 /*
237 * Tag Remap: use an identity 802.1p prio -> switch prio
238 * mapping.
239 */
240 REG_WRITE(addr, 0x18, 0x3210);
241
242 /*
243 * Tag Remap 2: use an identity 802.1p prio -> switch prio
244 * mapping.
245 */
246 REG_WRITE(addr, 0x19, 0x7654);
247
248 return 0;
249}
250
251static int mv88e6131_setup(struct dsa_switch *ds)
252{
253 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
254 int i;
255 int ret;
256
257 mutex_init(&ps->smi_mutex);
258 mv88e6xxx_ppu_state_init(ds);
259 mutex_init(&ps->stats_mutex);
260
261 ret = mv88e6131_switch_reset(ds);
262 if (ret < 0)
263 return ret;
264
265 /* @@@ initialise vtu and atu */
266
267 ret = mv88e6131_setup_global(ds);
268 if (ret < 0)
269 return ret;
270
271 for (i = 0; i < 6; i++) {
272 ret = mv88e6131_setup_port(ds, i);
273 if (ret < 0)
274 return ret;
275 }
276
277 return 0;
278}
279
280static int mv88e6131_port_to_phy_addr(int port)
281{
282 if (port >= 0 && port != 3 && port <= 7)
283 return port;
284 return -1;
285}
286
287static int
288mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
289{
290 int addr = mv88e6131_port_to_phy_addr(port);
291 return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
292}
293
294static int
295mv88e6131_phy_write(struct dsa_switch *ds,
296 int port, int regnum, u16 val)
297{
298 int addr = mv88e6131_port_to_phy_addr(port);
299 return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
300}
301
302static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = {
303 { "in_good_octets", 8, 0x00, },
304 { "in_bad_octets", 4, 0x02, },
305 { "in_unicast", 4, 0x04, },
306 { "in_broadcasts", 4, 0x06, },
307 { "in_multicasts", 4, 0x07, },
308 { "in_pause", 4, 0x16, },
309 { "in_undersize", 4, 0x18, },
310 { "in_fragments", 4, 0x19, },
311 { "in_oversize", 4, 0x1a, },
312 { "in_jabber", 4, 0x1b, },
313 { "in_rx_error", 4, 0x1c, },
314 { "in_fcs_error", 4, 0x1d, },
315 { "out_octets", 8, 0x0e, },
316 { "out_unicast", 4, 0x10, },
317 { "out_broadcasts", 4, 0x13, },
318 { "out_multicasts", 4, 0x12, },
319 { "out_pause", 4, 0x15, },
320 { "excessive", 4, 0x11, },
321 { "collisions", 4, 0x1e, },
322 { "deferred", 4, 0x05, },
323 { "single", 4, 0x14, },
324 { "multiple", 4, 0x17, },
325 { "out_fcs_error", 4, 0x03, },
326 { "late", 4, 0x1f, },
327 { "hist_64bytes", 4, 0x08, },
328 { "hist_65_127bytes", 4, 0x09, },
329 { "hist_128_255bytes", 4, 0x0a, },
330 { "hist_256_511bytes", 4, 0x0b, },
331 { "hist_512_1023bytes", 4, 0x0c, },
332 { "hist_1024_max_bytes", 4, 0x0d, },
333};
334
335static void
336mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
337{
338 mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats),
339 mv88e6131_hw_stats, port, data);
340}
341
342static void
343mv88e6131_get_ethtool_stats(struct dsa_switch *ds,
344 int port, uint64_t *data)
345{
346 mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats),
347 mv88e6131_hw_stats, port, data);
348}
349
350static int mv88e6131_get_sset_count(struct dsa_switch *ds)
351{
352 return ARRAY_SIZE(mv88e6131_hw_stats);
353}
354
355static struct dsa_switch_driver mv88e6131_switch_driver = {
356 .tag_protocol = __constant_htons(ETH_P_DSA),
357 .priv_size = sizeof(struct mv88e6xxx_priv_state),
358 .probe = mv88e6131_probe,
359 .setup = mv88e6131_setup,
360 .set_addr = mv88e6xxx_set_addr_direct,
361 .phy_read = mv88e6131_phy_read,
362 .phy_write = mv88e6131_phy_write,
363 .poll_link = mv88e6xxx_poll_link,
364 .get_strings = mv88e6131_get_strings,
365 .get_ethtool_stats = mv88e6131_get_ethtool_stats,
366 .get_sset_count = mv88e6131_get_sset_count,
367};
368
369int __init mv88e6131_init(void)
370{
371 register_switch_driver(&mv88e6131_switch_driver);
372 return 0;
373}
374module_init(mv88e6131_init);
375
376void __exit mv88e6131_cleanup(void)
377{
378 unregister_switch_driver(&mv88e6131_switch_driver);
379}
380module_exit(mv88e6131_cleanup);