aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa/mv88e6xxx.c
diff options
context:
space:
mode:
authorLennert Buytenhek <buytenh@wantstofly.org>2008-10-07 09:44:02 -0400
committerDavid S. Miller <davem@davemloft.net>2008-10-08 20:15:19 -0400
commit91da11f870f00a3322b81c73042291d7f0be5a17 (patch)
tree670fedb54ee3c8fa403e9095f6d7e95ee560f346 /net/dsa/mv88e6xxx.c
parent176eaa589b3d242f25f24e472883fcce5f196777 (diff)
net: Distributed Switch Architecture protocol support
Distributed Switch Architecture is a protocol for managing hardware switch chips. It consists of a set of MII management registers and commands to configure the switch, and an ethernet header format to signal which of the ports of the switch a packet was received from or is intended to be sent to. The switches that this driver supports are typically embedded in access points and routers, and a typical setup with a DSA switch looks something like this: +-----------+ +-----------+ | | RGMII | | | +-------+ +------ 1000baseT MDI ("WAN") | | | 6-port +------ 1000baseT MDI ("LAN1") | CPU | | ethernet +------ 1000baseT MDI ("LAN2") | |MIImgmt| switch +------ 1000baseT MDI ("LAN3") | +-------+ w/5 PHYs +------ 1000baseT MDI ("LAN4") | | | | +-----------+ +-----------+ The switch driver presents each port on the switch as a separate network interface to Linux, polls the switch to maintain software link state of those ports, forwards MII management interface accesses to those network interfaces (e.g. as done by ethtool) to the switch, and exposes the switch's hardware statistics counters via the appropriate Linux kernel interfaces. This initial patch supports the MII management interface register layout of the Marvell 88E6123, 88E6161 and 88E6165 switch chips, and supports the "Ethertype DSA" packet tagging format. (There is no officially registered ethertype for the Ethertype DSA packet format, so we just grab a random one. The ethertype to use is programmed into the switch, and the switch driver uses the value of ETH_P_EDSA for this, so this define can be changed at any time in the future if the one we chose is allocated to another protocol or if Ethertype DSA gets its own officially registered ethertype, and everything will continue to work.) Signed-off-by: Lennert Buytenhek <buytenh@marvell.com> Tested-by: Nicolas Pitre <nico@marvell.com> Tested-by: Byron Bradley <byron.bbradley@gmail.com> Tested-by: Tim Ellis <tim.ellis@mac.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/mv88e6xxx.c')
-rw-r--r--net/dsa/mv88e6xxx.c377
1 files changed, 377 insertions, 0 deletions
diff --git a/net/dsa/mv88e6xxx.c b/net/dsa/mv88e6xxx.c
new file mode 100644
index 000000000000..13d2328a2406
--- /dev/null
+++ b/net/dsa/mv88e6xxx.c
@@ -0,0 +1,377 @@
1/*
2 * net/dsa/mv88e6xxx.c - Marvell 88e6xxx switch chip support
3 * Copyright (c) 2008 Marvell Semiconductor
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
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
17/*
18 * If the switch's ADDR[4:0] strap pins are strapped to zero, it will
19 * use all 32 SMI bus addresses on its SMI bus, and all switch registers
20 * will be directly accessible on some {device address,register address}
21 * pair. If the ADDR[4:0] pins are not strapped to zero, the switch
22 * will only respond to SMI transactions to that specific address, and
23 * an indirect addressing mechanism needs to be used to access its
24 * registers.
25 */
26static int mv88e6xxx_reg_wait_ready(struct mii_bus *bus, int sw_addr)
27{
28 int ret;
29 int i;
30
31 for (i = 0; i < 16; i++) {
32 ret = mdiobus_read(bus, sw_addr, 0);
33 if (ret < 0)
34 return ret;
35
36 if ((ret & 0x8000) == 0)
37 return 0;
38 }
39
40 return -ETIMEDOUT;
41}
42
43int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg)
44{
45 int ret;
46
47 if (sw_addr == 0)
48 return mdiobus_read(bus, addr, reg);
49
50 /*
51 * Wait for the bus to become free.
52 */
53 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
54 if (ret < 0)
55 return ret;
56
57 /*
58 * Transmit the read command.
59 */
60 ret = mdiobus_write(bus, sw_addr, 0, 0x9800 | (addr << 5) | reg);
61 if (ret < 0)
62 return ret;
63
64 /*
65 * Wait for the read command to complete.
66 */
67 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
68 if (ret < 0)
69 return ret;
70
71 /*
72 * Read the data.
73 */
74 ret = mdiobus_read(bus, sw_addr, 1);
75 if (ret < 0)
76 return ret;
77
78 return ret & 0xffff;
79}
80
81int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg)
82{
83 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
84 int ret;
85
86 mutex_lock(&ps->smi_mutex);
87 ret = __mv88e6xxx_reg_read(ds->master_mii_bus,
88 ds->pd->sw_addr, addr, reg);
89 mutex_unlock(&ps->smi_mutex);
90
91 return ret;
92}
93
94int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
95 int reg, u16 val)
96{
97 int ret;
98
99 if (sw_addr == 0)
100 return mdiobus_write(bus, addr, reg, val);
101
102 /*
103 * Wait for the bus to become free.
104 */
105 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
106 if (ret < 0)
107 return ret;
108
109 /*
110 * Transmit the data to write.
111 */
112 ret = mdiobus_write(bus, sw_addr, 1, val);
113 if (ret < 0)
114 return ret;
115
116 /*
117 * Transmit the write command.
118 */
119 ret = mdiobus_write(bus, sw_addr, 0, 0x9400 | (addr << 5) | reg);
120 if (ret < 0)
121 return ret;
122
123 /*
124 * Wait for the write command to complete.
125 */
126 ret = mv88e6xxx_reg_wait_ready(bus, sw_addr);
127 if (ret < 0)
128 return ret;
129
130 return 0;
131}
132
133int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
134{
135 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
136 int ret;
137
138 mutex_lock(&ps->smi_mutex);
139 ret = __mv88e6xxx_reg_write(ds->master_mii_bus,
140 ds->pd->sw_addr, addr, reg, val);
141 mutex_unlock(&ps->smi_mutex);
142
143 return ret;
144}
145
146int mv88e6xxx_config_prio(struct dsa_switch *ds)
147{
148 /*
149 * Configure the IP ToS mapping registers.
150 */
151 REG_WRITE(REG_GLOBAL, 0x10, 0x0000);
152 REG_WRITE(REG_GLOBAL, 0x11, 0x0000);
153 REG_WRITE(REG_GLOBAL, 0x12, 0x5555);
154 REG_WRITE(REG_GLOBAL, 0x13, 0x5555);
155 REG_WRITE(REG_GLOBAL, 0x14, 0xaaaa);
156 REG_WRITE(REG_GLOBAL, 0x15, 0xaaaa);
157 REG_WRITE(REG_GLOBAL, 0x16, 0xffff);
158 REG_WRITE(REG_GLOBAL, 0x17, 0xffff);
159
160 /*
161 * Configure the IEEE 802.1p priority mapping register.
162 */
163 REG_WRITE(REG_GLOBAL, 0x18, 0xfa41);
164
165 return 0;
166}
167
168int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
169{
170 int i;
171 int ret;
172
173 for (i = 0; i < 6; i++) {
174 int j;
175
176 /*
177 * Write the MAC address byte.
178 */
179 REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]);
180
181 /*
182 * Wait for the write to complete.
183 */
184 for (j = 0; j < 16; j++) {
185 ret = REG_READ(REG_GLOBAL2, 0x0d);
186 if ((ret & 0x8000) == 0)
187 break;
188 }
189 if (j == 16)
190 return -ETIMEDOUT;
191 }
192
193 return 0;
194}
195
196int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
197{
198 if (addr >= 0)
199 return mv88e6xxx_reg_read(ds, addr, regnum);
200 return 0xffff;
201}
202
203int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val)
204{
205 if (addr >= 0)
206 return mv88e6xxx_reg_write(ds, addr, regnum, val);
207 return 0;
208}
209
210void mv88e6xxx_poll_link(struct dsa_switch *ds)
211{
212 int i;
213
214 for (i = 0; i < DSA_MAX_PORTS; i++) {
215 struct net_device *dev;
216 int port_status;
217 int link;
218 int speed;
219 int duplex;
220 int fc;
221
222 dev = ds->ports[i];
223 if (dev == NULL)
224 continue;
225
226 link = 0;
227 if (dev->flags & IFF_UP) {
228 port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00);
229 if (port_status < 0)
230 continue;
231
232 link = !!(port_status & 0x0800);
233 }
234
235 if (!link) {
236 if (netif_carrier_ok(dev)) {
237 printk(KERN_INFO "%s: link down\n", dev->name);
238 netif_carrier_off(dev);
239 }
240 continue;
241 }
242
243 switch (port_status & 0x0300) {
244 case 0x0000:
245 speed = 10;
246 break;
247 case 0x0100:
248 speed = 100;
249 break;
250 case 0x0200:
251 speed = 1000;
252 break;
253 default:
254 speed = -1;
255 break;
256 }
257 duplex = (port_status & 0x0400) ? 1 : 0;
258 fc = (port_status & 0x8000) ? 1 : 0;
259
260 if (!netif_carrier_ok(dev)) {
261 printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
262 "flow control %sabled\n", dev->name,
263 speed, duplex ? "full" : "half",
264 fc ? "en" : "dis");
265 netif_carrier_on(dev);
266 }
267 }
268}
269
270static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
271{
272 int ret;
273 int i;
274
275 for (i = 0; i < 10; i++) {
276 ret = REG_READ(REG_GLOBAL2, 0x1d);
277 if ((ret & 0x8000) == 0)
278 return 0;
279 }
280
281 return -ETIMEDOUT;
282}
283
284static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
285{
286 int ret;
287
288 /*
289 * Snapshot the hardware statistics counters for this port.
290 */
291 REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port);
292
293 /*
294 * Wait for the snapshotting to complete.
295 */
296 ret = mv88e6xxx_stats_wait(ds);
297 if (ret < 0)
298 return ret;
299
300 return 0;
301}
302
303static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
304{
305 u32 _val;
306 int ret;
307
308 *val = 0;
309
310 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat);
311 if (ret < 0)
312 return;
313
314 ret = mv88e6xxx_stats_wait(ds);
315 if (ret < 0)
316 return;
317
318 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e);
319 if (ret < 0)
320 return;
321
322 _val = ret << 16;
323
324 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f);
325 if (ret < 0)
326 return;
327
328 *val = _val | ret;
329}
330
331void mv88e6xxx_get_strings(struct dsa_switch *ds,
332 int nr_stats, struct mv88e6xxx_hw_stat *stats,
333 int port, uint8_t *data)
334{
335 int i;
336
337 for (i = 0; i < nr_stats; i++) {
338 memcpy(data + i * ETH_GSTRING_LEN,
339 stats[i].string, ETH_GSTRING_LEN);
340 }
341}
342
343void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
344 int nr_stats, struct mv88e6xxx_hw_stat *stats,
345 int port, uint64_t *data)
346{
347 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
348 int ret;
349 int i;
350
351 mutex_lock(&ps->stats_mutex);
352
353 ret = mv88e6xxx_stats_snapshot(ds, port);
354 if (ret < 0) {
355 mutex_unlock(&ps->stats_mutex);
356 return;
357 }
358
359 /*
360 * Read each of the counters.
361 */
362 for (i = 0; i < nr_stats; i++) {
363 struct mv88e6xxx_hw_stat *s = stats + i;
364 u32 low;
365 u32 high;
366
367 mv88e6xxx_stats_read(ds, s->reg, &low);
368 if (s->sizeof_stat == 8)
369 mv88e6xxx_stats_read(ds, s->reg + 1, &high);
370 else
371 high = 0;
372
373 data[i] = (((u64)high) << 32) | low;
374 }
375
376 mutex_unlock(&ps->stats_mutex);
377}