aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--include/linux/if_ether.h1
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--include/net/dsa.h34
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/dsa/Kconfig31
-rw-r--r--net/dsa/Makefile9
-rw-r--r--net/dsa/dsa.c369
-rw-r--r--net/dsa/dsa_priv.h110
-rw-r--r--net/dsa/mv88e6123_61_65.c417
-rw-r--r--net/dsa/mv88e6xxx.c377
-rw-r--r--net/dsa/mv88e6xxx.h77
-rw-r--r--net/dsa/slave.c288
-rw-r--r--net/dsa/tag_edsa.c213
14 files changed, 1931 insertions, 0 deletions
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index 723a1c5fbc6c..2140aacb6338 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -77,6 +77,7 @@
77#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ 77#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
78#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ 78#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
79#define ETH_P_TIPC 0x88CA /* TIPC */ 79#define ETH_P_TIPC 0x88CA /* TIPC */
80#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
80 81
81/* 82/*
82 * Non DIX types. Won't clash for 1500 types. 83 * Non DIX types. Won't clash for 1500 types.
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9cfd20be8b7f..794eeb4b3462 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -607,6 +607,9 @@ struct net_device
607 607
608 /* Protocol specific pointers */ 608 /* Protocol specific pointers */
609 609
610#ifdef CONFIG_NET_DSA
611 void *dsa_ptr; /* dsa specific data */
612#endif
610 void *atalk_ptr; /* AppleTalk link */ 613 void *atalk_ptr; /* AppleTalk link */
611 void *ip_ptr; /* IPv4 specific data */ 614 void *ip_ptr; /* IPv4 specific data */
612 void *dn_ptr; /* DECnet specific data */ 615 void *dn_ptr; /* DECnet specific data */
diff --git a/include/net/dsa.h b/include/net/dsa.h
new file mode 100644
index 000000000000..dc4784f54520
--- /dev/null
+++ b/include/net/dsa.h
@@ -0,0 +1,34 @@
1/*
2 * include/net/dsa.h - Driver for Distributed Switch Architecture switch chips
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#ifndef __LINUX_NET_DSA_H
12#define __LINUX_NET_DSA_H
13
14#define DSA_MAX_PORTS 12
15
16struct dsa_platform_data {
17 /*
18 * Reference to a Linux network interface that connects
19 * to the switch chip.
20 */
21 struct device *netdev;
22
23 /*
24 * How to access the switch configuration registers, and
25 * the names of the switch ports (use "cpu" to designate
26 * the switch port that the cpu is connected to).
27 */
28 struct device *mii_bus;
29 int sw_addr;
30 char *port_names[DSA_MAX_PORTS];
31};
32
33
34#endif
diff --git a/net/Kconfig b/net/Kconfig
index 9103a16a77be..d789d79551ae 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -180,6 +180,7 @@ source "net/tipc/Kconfig"
180source "net/atm/Kconfig" 180source "net/atm/Kconfig"
181source "net/802/Kconfig" 181source "net/802/Kconfig"
182source "net/bridge/Kconfig" 182source "net/bridge/Kconfig"
183source "net/dsa/Kconfig"
183source "net/8021q/Kconfig" 184source "net/8021q/Kconfig"
184source "net/decnet/Kconfig" 185source "net/decnet/Kconfig"
185source "net/llc/Kconfig" 186source "net/llc/Kconfig"
diff --git a/net/Makefile b/net/Makefile
index acaf819f24aa..27d1f10dc0e0 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_PACKET) += packet/
26obj-$(CONFIG_NET_KEY) += key/ 26obj-$(CONFIG_NET_KEY) += key/
27obj-$(CONFIG_NET_SCHED) += sched/ 27obj-$(CONFIG_NET_SCHED) += sched/
28obj-$(CONFIG_BRIDGE) += bridge/ 28obj-$(CONFIG_BRIDGE) += bridge/
29obj-$(CONFIG_NET_DSA) += dsa/
29obj-$(CONFIG_IPX) += ipx/ 30obj-$(CONFIG_IPX) += ipx/
30obj-$(CONFIG_ATALK) += appletalk/ 31obj-$(CONFIG_ATALK) += appletalk/
31obj-$(CONFIG_WAN_ROUTER) += wanrouter/ 32obj-$(CONFIG_WAN_ROUTER) += wanrouter/
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
new file mode 100644
index 000000000000..7cf55e5eb39f
--- /dev/null
+++ b/net/dsa/Kconfig
@@ -0,0 +1,31 @@
1menuconfig NET_DSA
2 bool "Distributed Switch Architecture support"
3 default n
4 depends on EXPERIMENTAL
5 ---help---
6 This allows you to use hardware switch chips that use
7 the Distributed Switch Architecture.
8
9
10if NET_DSA
11
12# tagging formats
13config NET_DSA_TAG_EDSA
14 bool
15 default n
16
17
18# switch drivers
19config NET_DSA_MV88E6XXX
20 bool
21 default n
22
23config NET_DSA_MV88E6123_61_65
24 bool "Marvell 88E6123/6161/6165 ethernet switch chip support"
25 select NET_DSA_MV88E6XXX
26 select NET_DSA_TAG_EDSA
27 ---help---
28 This enables support for the Marvell 88E6123/6161/6165
29 ethernet switch chips.
30
31endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
new file mode 100644
index 000000000000..b59a6f6bcf56
--- /dev/null
+++ b/net/dsa/Makefile
@@ -0,0 +1,9 @@
1# tagging formats
2obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
3
4# switch drivers
5obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
6obj-$(CONFIG_NET_DSA_MV88E6123_61_65) += mv88e6123_61_65.o
7
8# the core
9obj-$(CONFIG_NET_DSA) += dsa.o slave.o
diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
new file mode 100644
index 000000000000..6cc5be2ec7f1
--- /dev/null
+++ b/net/dsa/dsa.c
@@ -0,0 +1,369 @@
1/*
2 * net/dsa/dsa.c - Hardware switch handling
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/platform_device.h>
14#include <net/dsa.h>
15#include "dsa_priv.h"
16
17char dsa_driver_version[] = "0.1";
18
19
20/* switch driver registration ***********************************************/
21static DEFINE_MUTEX(dsa_switch_drivers_mutex);
22static LIST_HEAD(dsa_switch_drivers);
23
24void register_switch_driver(struct dsa_switch_driver *drv)
25{
26 mutex_lock(&dsa_switch_drivers_mutex);
27 list_add_tail(&drv->list, &dsa_switch_drivers);
28 mutex_unlock(&dsa_switch_drivers_mutex);
29}
30
31void unregister_switch_driver(struct dsa_switch_driver *drv)
32{
33 mutex_lock(&dsa_switch_drivers_mutex);
34 list_del_init(&drv->list);
35 mutex_unlock(&dsa_switch_drivers_mutex);
36}
37
38static struct dsa_switch_driver *
39dsa_switch_probe(struct mii_bus *bus, int sw_addr, char **_name)
40{
41 struct dsa_switch_driver *ret;
42 struct list_head *list;
43 char *name;
44
45 ret = NULL;
46 name = NULL;
47
48 mutex_lock(&dsa_switch_drivers_mutex);
49 list_for_each(list, &dsa_switch_drivers) {
50 struct dsa_switch_driver *drv;
51
52 drv = list_entry(list, struct dsa_switch_driver, list);
53
54 name = drv->probe(bus, sw_addr);
55 if (name != NULL) {
56 ret = drv;
57 break;
58 }
59 }
60 mutex_unlock(&dsa_switch_drivers_mutex);
61
62 *_name = name;
63
64 return ret;
65}
66
67
68/* basic switch operations **************************************************/
69static struct dsa_switch *
70dsa_switch_setup(struct device *parent, struct dsa_platform_data *pd,
71 struct mii_bus *bus, struct net_device *dev)
72{
73 struct dsa_switch *ds;
74 int ret;
75 struct dsa_switch_driver *drv;
76 char *name;
77 int i;
78
79 /*
80 * Probe for switch model.
81 */
82 drv = dsa_switch_probe(bus, pd->sw_addr, &name);
83 if (drv == NULL) {
84 printk(KERN_ERR "%s: could not detect attached switch\n",
85 dev->name);
86 return ERR_PTR(-EINVAL);
87 }
88 printk(KERN_INFO "%s: detected a %s switch\n", dev->name, name);
89
90
91 /*
92 * Allocate and initialise switch state.
93 */
94 ds = kzalloc(sizeof(*ds) + drv->priv_size, GFP_KERNEL);
95 if (ds == NULL)
96 return ERR_PTR(-ENOMEM);
97
98 ds->pd = pd;
99 ds->master_netdev = dev;
100 ds->master_mii_bus = bus;
101
102 ds->drv = drv;
103 ds->tag_protocol = drv->tag_protocol;
104
105
106 /*
107 * Validate supplied switch configuration.
108 */
109 ds->cpu_port = -1;
110 for (i = 0; i < DSA_MAX_PORTS; i++) {
111 char *name;
112
113 name = pd->port_names[i];
114 if (name == NULL)
115 continue;
116
117 if (!strcmp(name, "cpu")) {
118 if (ds->cpu_port != -1) {
119 printk(KERN_ERR "multiple cpu ports?!\n");
120 ret = -EINVAL;
121 goto out;
122 }
123 ds->cpu_port = i;
124 } else {
125 ds->valid_port_mask |= 1 << i;
126 }
127 }
128
129 if (ds->cpu_port == -1) {
130 printk(KERN_ERR "no cpu port?!\n");
131 ret = -EINVAL;
132 goto out;
133 }
134
135
136 /*
137 * If we use a tagging format that doesn't have an ethertype
138 * field, make sure that all packets from this point on get
139 * sent to the tag format's receive function. (Which will
140 * discard received packets until we set ds->ports[] below.)
141 */
142 wmb();
143 dev->dsa_ptr = (void *)ds;
144
145
146 /*
147 * Do basic register setup.
148 */
149 ret = drv->setup(ds);
150 if (ret < 0)
151 goto out;
152
153 ret = drv->set_addr(ds, dev->dev_addr);
154 if (ret < 0)
155 goto out;
156
157 ds->slave_mii_bus = mdiobus_alloc();
158 if (ds->slave_mii_bus == NULL) {
159 ret = -ENOMEM;
160 goto out;
161 }
162 dsa_slave_mii_bus_init(ds);
163
164 ret = mdiobus_register(ds->slave_mii_bus);
165 if (ret < 0)
166 goto out_free;
167
168
169 /*
170 * Create network devices for physical switch ports.
171 */
172 wmb();
173 for (i = 0; i < DSA_MAX_PORTS; i++) {
174 struct net_device *slave_dev;
175
176 if (!(ds->valid_port_mask & (1 << i)))
177 continue;
178
179 slave_dev = dsa_slave_create(ds, parent, i, pd->port_names[i]);
180 if (slave_dev == NULL) {
181 printk(KERN_ERR "%s: can't create dsa slave "
182 "device for port %d(%s)\n",
183 dev->name, i, pd->port_names[i]);
184 continue;
185 }
186
187 ds->ports[i] = slave_dev;
188 }
189
190 return ds;
191
192out_free:
193 mdiobus_free(ds->slave_mii_bus);
194out:
195 dev->dsa_ptr = NULL;
196 kfree(ds);
197 return ERR_PTR(ret);
198}
199
200static void dsa_switch_destroy(struct dsa_switch *ds)
201{
202}
203
204
205/* link polling *************************************************************/
206static void dsa_link_poll_work(struct work_struct *ugly)
207{
208 struct dsa_switch *ds;
209
210 ds = container_of(ugly, struct dsa_switch, link_poll_work);
211
212 ds->drv->poll_link(ds);
213 mod_timer(&ds->link_poll_timer, round_jiffies(jiffies + HZ));
214}
215
216static void dsa_link_poll_timer(unsigned long _ds)
217{
218 struct dsa_switch *ds = (void *)_ds;
219
220 schedule_work(&ds->link_poll_work);
221}
222
223
224/* platform driver init and cleanup *****************************************/
225static int dev_is_class(struct device *dev, void *class)
226{
227 if (dev->class != NULL && !strcmp(dev->class->name, class))
228 return 1;
229
230 return 0;
231}
232
233static struct device *dev_find_class(struct device *parent, char *class)
234{
235 if (dev_is_class(parent, class)) {
236 get_device(parent);
237 return parent;
238 }
239
240 return device_find_child(parent, class, dev_is_class);
241}
242
243static struct mii_bus *dev_to_mii_bus(struct device *dev)
244{
245 struct device *d;
246
247 d = dev_find_class(dev, "mdio_bus");
248 if (d != NULL) {
249 struct mii_bus *bus;
250
251 bus = to_mii_bus(d);
252 put_device(d);
253
254 return bus;
255 }
256
257 return NULL;
258}
259
260static struct net_device *dev_to_net_device(struct device *dev)
261{
262 struct device *d;
263
264 d = dev_find_class(dev, "net");
265 if (d != NULL) {
266 struct net_device *nd;
267
268 nd = to_net_dev(d);
269 dev_hold(nd);
270 put_device(d);
271
272 return nd;
273 }
274
275 return NULL;
276}
277
278static int dsa_probe(struct platform_device *pdev)
279{
280 static int dsa_version_printed;
281 struct dsa_platform_data *pd = pdev->dev.platform_data;
282 struct net_device *dev;
283 struct mii_bus *bus;
284 struct dsa_switch *ds;
285
286 if (!dsa_version_printed++)
287 printk(KERN_NOTICE "Distributed Switch Architecture "
288 "driver version %s\n", dsa_driver_version);
289
290 if (pd == NULL || pd->mii_bus == NULL || pd->netdev == NULL)
291 return -EINVAL;
292
293 bus = dev_to_mii_bus(pd->mii_bus);
294 if (bus == NULL)
295 return -EINVAL;
296
297 dev = dev_to_net_device(pd->netdev);
298 if (dev == NULL)
299 return -EINVAL;
300
301 if (dev->dsa_ptr != NULL) {
302 dev_put(dev);
303 return -EEXIST;
304 }
305
306 ds = dsa_switch_setup(&pdev->dev, pd, bus, dev);
307 if (IS_ERR(ds)) {
308 dev_put(dev);
309 return PTR_ERR(ds);
310 }
311
312 if (ds->drv->poll_link != NULL) {
313 INIT_WORK(&ds->link_poll_work, dsa_link_poll_work);
314 init_timer(&ds->link_poll_timer);
315 ds->link_poll_timer.data = (unsigned long)ds;
316 ds->link_poll_timer.function = dsa_link_poll_timer;
317 ds->link_poll_timer.expires = round_jiffies(jiffies + HZ);
318 add_timer(&ds->link_poll_timer);
319 }
320
321 platform_set_drvdata(pdev, ds);
322
323 return 0;
324}
325
326static int dsa_remove(struct platform_device *pdev)
327{
328 struct dsa_switch *ds = platform_get_drvdata(pdev);
329
330 if (ds->drv->poll_link != NULL)
331 del_timer_sync(&ds->link_poll_timer);
332
333 flush_scheduled_work();
334
335 dsa_switch_destroy(ds);
336
337 return 0;
338}
339
340static void dsa_shutdown(struct platform_device *pdev)
341{
342}
343
344static struct platform_driver dsa_driver = {
345 .probe = dsa_probe,
346 .remove = dsa_remove,
347 .shutdown = dsa_shutdown,
348 .driver = {
349 .name = "dsa",
350 .owner = THIS_MODULE,
351 },
352};
353
354static int __init dsa_init_module(void)
355{
356 return platform_driver_register(&dsa_driver);
357}
358module_init(dsa_init_module);
359
360static void __exit dsa_cleanup_module(void)
361{
362 platform_driver_unregister(&dsa_driver);
363}
364module_exit(dsa_cleanup_module);
365
366MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>")
367MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
368MODULE_LICENSE("GPL");
369MODULE_ALIAS("platform:dsa");
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
new file mode 100644
index 000000000000..21ee9052079a
--- /dev/null
+++ b/net/dsa/dsa_priv.h
@@ -0,0 +1,110 @@
1/*
2 * net/dsa/dsa_priv.h - Hardware switch handling
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#ifndef __DSA_PRIV_H
12#define __DSA_PRIV_H
13
14#include <linux/list.h>
15#include <linux/phy.h>
16#include <linux/timer.h>
17#include <linux/workqueue.h>
18#include <net/dsa.h>
19
20struct dsa_switch {
21 /*
22 * Configuration data for the platform device that owns
23 * this dsa switch instance.
24 */
25 struct dsa_platform_data *pd;
26
27 /*
28 * References to network device and mii bus to use.
29 */
30 struct net_device *master_netdev;
31 struct mii_bus *master_mii_bus;
32
33 /*
34 * The used switch driver and frame tagging type.
35 */
36 struct dsa_switch_driver *drv;
37 __be16 tag_protocol;
38
39 /*
40 * Slave mii_bus and devices for the individual ports.
41 */
42 int cpu_port;
43 u32 valid_port_mask;
44 struct mii_bus *slave_mii_bus;
45 struct net_device *ports[DSA_MAX_PORTS];
46
47 /*
48 * Link state polling.
49 */
50 struct work_struct link_poll_work;
51 struct timer_list link_poll_timer;
52};
53
54struct dsa_slave_priv {
55 struct net_device *dev;
56 struct dsa_switch *parent;
57 int port;
58 struct phy_device *phy;
59};
60
61struct dsa_switch_driver {
62 struct list_head list;
63
64 __be16 tag_protocol;
65 int priv_size;
66
67 /*
68 * Probing and setup.
69 */
70 char *(*probe)(struct mii_bus *bus, int sw_addr);
71 int (*setup)(struct dsa_switch *ds);
72 int (*set_addr)(struct dsa_switch *ds, u8 *addr);
73
74 /*
75 * Access to the switch's PHY registers.
76 */
77 int (*phy_read)(struct dsa_switch *ds, int port, int regnum);
78 int (*phy_write)(struct dsa_switch *ds, int port,
79 int regnum, u16 val);
80
81 /*
82 * Link state polling and IRQ handling.
83 */
84 void (*poll_link)(struct dsa_switch *ds);
85
86 /*
87 * ethtool hardware statistics.
88 */
89 void (*get_strings)(struct dsa_switch *ds, int port, uint8_t *data);
90 void (*get_ethtool_stats)(struct dsa_switch *ds,
91 int port, uint64_t *data);
92 int (*get_sset_count)(struct dsa_switch *ds);
93};
94
95/* dsa.c */
96extern char dsa_driver_version[];
97void register_switch_driver(struct dsa_switch_driver *type);
98void unregister_switch_driver(struct dsa_switch_driver *type);
99
100/* slave.c */
101void dsa_slave_mii_bus_init(struct dsa_switch *ds);
102struct net_device *dsa_slave_create(struct dsa_switch *ds,
103 struct device *parent,
104 int port, char *name);
105
106/* tag_edsa.c */
107int edsa_xmit(struct sk_buff *skb, struct net_device *dev);
108
109
110#endif
diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c
new file mode 100644
index 000000000000..147818cc706e
--- /dev/null
+++ b/net/dsa/mv88e6123_61_65.c
@@ -0,0 +1,417 @@
1/*
2 * net/dsa/mv88e6123_61_65.c - Marvell 88e6123/6161/6165 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 *mv88e6123_61_65_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 == 0x1210)
25 return "Marvell 88E6123";
26 if (ret == 0x1610)
27 return "Marvell 88E6161";
28 if (ret == 0x1650)
29 return "Marvell 88E6165";
30 }
31
32 return NULL;
33}
34
35static int mv88e6123_61_65_switch_reset(struct dsa_switch *ds)
36{
37 int i;
38 int ret;
39
40 /*
41 * Set all ports to the disabled state.
42 */
43 for (i = 0; i < 8; i++) {
44 ret = REG_READ(REG_PORT(i), 0x04);
45 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
46 }
47
48 /*
49 * Wait for transmit queues to drain.
50 */
51 msleep(2);
52
53 /*
54 * Reset the switch.
55 */
56 REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
57
58 /*
59 * Wait up to one second for reset to complete.
60 */
61 for (i = 0; i < 1000; i++) {
62 ret = REG_READ(REG_GLOBAL, 0x00);
63 if ((ret & 0xc800) == 0xc800)
64 break;
65
66 msleep(1);
67 }
68 if (i == 1000)
69 return -ETIMEDOUT;
70
71 return 0;
72}
73
74static int mv88e6123_61_65_setup_global(struct dsa_switch *ds)
75{
76 int ret;
77 int i;
78
79 /*
80 * Disable the PHY polling unit (since there won't be any
81 * external PHYs to poll), don't discard packets with
82 * excessive collisions, and mask all interrupt sources.
83 */
84 REG_WRITE(REG_GLOBAL, 0x04, 0x0000);
85
86 /*
87 * Set the default address aging time to 5 minutes, and
88 * enable address learn messages to be sent to all message
89 * ports.
90 */
91 REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
92
93 /*
94 * Configure the priority mapping registers.
95 */
96 ret = mv88e6xxx_config_prio(ds);
97 if (ret < 0)
98 return ret;
99
100 /*
101 * Configure the cpu port, and configure the cpu port as the
102 * port to which ingress and egress monitor frames are to be
103 * sent.
104 */
105 REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1110));
106
107 /*
108 * Disable remote management for now, and set the switch's
109 * DSA device number to zero.
110 */
111 REG_WRITE(REG_GLOBAL, 0x1c, 0x0000);
112
113 /*
114 * Send all frames with destination addresses matching
115 * 01:80:c2:00:00:2x to the CPU port.
116 */
117 REG_WRITE(REG_GLOBAL2, 0x02, 0xffff);
118
119 /*
120 * Send all frames with destination addresses matching
121 * 01:80:c2:00:00:0x to the CPU port.
122 */
123 REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
124
125 /*
126 * Disable the loopback filter, disable flow control
127 * messages, disable flood broadcast override, disable
128 * removing of provider tags, disable ATU age violation
129 * interrupts, disable tag flow control, force flow
130 * control priority to the highest, and send all special
131 * multicast frames to the CPU at the highest priority.
132 */
133 REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
134
135 /*
136 * Map all DSA device IDs to the CPU port.
137 */
138 for (i = 0; i < 32; i++)
139 REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port);
140
141 /*
142 * Clear all trunk masks.
143 */
144 for (i = 0; i < 8; i++)
145 REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
146
147 /*
148 * Clear all trunk mappings.
149 */
150 for (i = 0; i < 16; i++)
151 REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
152
153 /*
154 * Disable ingress rate limiting by resetting all ingress
155 * rate limit registers to their initial state.
156 */
157 for (i = 0; i < 6; i++)
158 REG_WRITE(REG_GLOBAL2, 0x09, 0x9000 | (i << 8));
159
160 /*
161 * Initialise cross-chip port VLAN table to reset defaults.
162 */
163 REG_WRITE(REG_GLOBAL2, 0x0b, 0x9000);
164
165 /*
166 * Clear the priority override table.
167 */
168 for (i = 0; i < 16; i++)
169 REG_WRITE(REG_GLOBAL2, 0x0f, 0x8000 | (i << 8));
170
171 /* @@@ initialise AVB (22/23) watchdog (27) sdet (29) registers */
172
173 return 0;
174}
175
176static int mv88e6123_61_65_setup_port(struct dsa_switch *ds, int p)
177{
178 int addr = REG_PORT(p);
179
180 /*
181 * MAC Forcing register: don't force link, speed, duplex
182 * or flow control state to any particular values.
183 */
184 REG_WRITE(addr, 0x01, 0x0003);
185
186 /*
187 * Do not limit the period of time that this port can be
188 * paused for by the remote end or the period of time that
189 * this port can pause the remote end.
190 */
191 REG_WRITE(addr, 0x02, 0x0000);
192
193 /*
194 * Port Control: disable Drop-on-Unlock, disable Drop-on-Lock,
195 * configure the EDSA tagging mode if this is the CPU port,
196 * disable Header mode, enable IGMP/MLD snooping, disable VLAN
197 * tunneling, determine priority by looking at 802.1p and IP
198 * priority fields (IP prio has precedence), and set STP state
199 * to Forwarding. Finally, if this is the CPU port, additionally
200 * enable forwarding of unknown unicast and multicast addresses.
201 */
202 REG_WRITE(addr, 0x04,
203 (p == ds->cpu_port) ? 0x373f : 0x0433);
204
205 /*
206 * Port Control 1: disable trunking. Also, if this is the
207 * CPU port, enable learn messages to be sent to this port.
208 */
209 REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000);
210
211 /*
212 * Port based VLAN map: give each port its own address
213 * database, allow the CPU port to talk to each of the 'real'
214 * ports, and allow each of the 'real' ports to only talk to
215 * the CPU port.
216 */
217 REG_WRITE(addr, 0x06,
218 ((p & 0xf) << 12) |
219 ((p == ds->cpu_port) ?
220 ds->valid_port_mask :
221 (1 << ds->cpu_port)));
222
223 /*
224 * Default VLAN ID and priority: don't set a default VLAN
225 * ID, and set the default packet priority to zero.
226 */
227 REG_WRITE(addr, 0x07, 0x0000);
228
229 /*
230 * Port Control 2: don't force a good FCS, set the maximum
231 * frame size to 10240 bytes, don't let the switch add or
232 * strip 802.1q tags, don't discard tagged or untagged frames
233 * on this port, do a destination address lookup on all
234 * received packets as usual, disable ARP mirroring and don't
235 * send a copy of all transmitted/received frames on this port
236 * to the CPU.
237 */
238 REG_WRITE(addr, 0x08, 0x2080);
239
240 /*
241 * Egress rate control: disable egress rate control.
242 */
243 REG_WRITE(addr, 0x09, 0x0001);
244
245 /*
246 * Egress rate control 2: disable egress rate control.
247 */
248 REG_WRITE(addr, 0x0a, 0x0000);
249
250 /*
251 * Port Association Vector: when learning source addresses
252 * of packets, add the address to the address database using
253 * a port bitmap that has only the bit for this port set and
254 * the other bits clear.
255 */
256 REG_WRITE(addr, 0x0b, 1 << p);
257
258 /*
259 * Port ATU control: disable limiting the number of address
260 * database entries that this port is allowed to use.
261 */
262 REG_WRITE(addr, 0x0c, 0x0000);
263
264 /*
265 * Priorit Override: disable DA, SA and VTU priority override.
266 */
267 REG_WRITE(addr, 0x0d, 0x0000);
268
269 /*
270 * Port Ethertype: use the Ethertype DSA Ethertype value.
271 */
272 REG_WRITE(addr, 0x0f, ETH_P_EDSA);
273
274 /*
275 * Tag Remap: use an identity 802.1p prio -> switch prio
276 * mapping.
277 */
278 REG_WRITE(addr, 0x18, 0x3210);
279
280 /*
281 * Tag Remap 2: use an identity 802.1p prio -> switch prio
282 * mapping.
283 */
284 REG_WRITE(addr, 0x19, 0x7654);
285
286 return 0;
287}
288
289static int mv88e6123_61_65_setup(struct dsa_switch *ds)
290{
291 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
292 int i;
293 int ret;
294
295 mutex_init(&ps->smi_mutex);
296 mutex_init(&ps->stats_mutex);
297
298 ret = mv88e6123_61_65_switch_reset(ds);
299 if (ret < 0)
300 return ret;
301
302 /* @@@ initialise vtu and atu */
303
304 ret = mv88e6123_61_65_setup_global(ds);
305 if (ret < 0)
306 return ret;
307
308 for (i = 0; i < 6; i++) {
309 ret = mv88e6123_61_65_setup_port(ds, i);
310 if (ret < 0)
311 return ret;
312 }
313
314 return 0;
315}
316
317static int mv88e6123_61_65_port_to_phy_addr(int port)
318{
319 if (port >= 0 && port <= 4)
320 return port;
321 return -1;
322}
323
324static int
325mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum)
326{
327 int addr = mv88e6123_61_65_port_to_phy_addr(port);
328 return mv88e6xxx_phy_read(ds, addr, regnum);
329}
330
331static int
332mv88e6123_61_65_phy_write(struct dsa_switch *ds,
333 int port, int regnum, u16 val)
334{
335 int addr = mv88e6123_61_65_port_to_phy_addr(port);
336 return mv88e6xxx_phy_write(ds, addr, regnum, val);
337}
338
339static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = {
340 { "in_good_octets", 8, 0x00, },
341 { "in_bad_octets", 4, 0x02, },
342 { "in_unicast", 4, 0x04, },
343 { "in_broadcasts", 4, 0x06, },
344 { "in_multicasts", 4, 0x07, },
345 { "in_pause", 4, 0x16, },
346 { "in_undersize", 4, 0x18, },
347 { "in_fragments", 4, 0x19, },
348 { "in_oversize", 4, 0x1a, },
349 { "in_jabber", 4, 0x1b, },
350 { "in_rx_error", 4, 0x1c, },
351 { "in_fcs_error", 4, 0x1d, },
352 { "out_octets", 8, 0x0e, },
353 { "out_unicast", 4, 0x10, },
354 { "out_broadcasts", 4, 0x13, },
355 { "out_multicasts", 4, 0x12, },
356 { "out_pause", 4, 0x15, },
357 { "excessive", 4, 0x11, },
358 { "collisions", 4, 0x1e, },
359 { "deferred", 4, 0x05, },
360 { "single", 4, 0x14, },
361 { "multiple", 4, 0x17, },
362 { "out_fcs_error", 4, 0x03, },
363 { "late", 4, 0x1f, },
364 { "hist_64bytes", 4, 0x08, },
365 { "hist_65_127bytes", 4, 0x09, },
366 { "hist_128_255bytes", 4, 0x0a, },
367 { "hist_256_511bytes", 4, 0x0b, },
368 { "hist_512_1023bytes", 4, 0x0c, },
369 { "hist_1024_max_bytes", 4, 0x0d, },
370};
371
372static void
373mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
374{
375 mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
376 mv88e6123_61_65_hw_stats, port, data);
377}
378
379static void
380mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds,
381 int port, uint64_t *data)
382{
383 mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
384 mv88e6123_61_65_hw_stats, port, data);
385}
386
387static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds)
388{
389 return ARRAY_SIZE(mv88e6123_61_65_hw_stats);
390}
391
392static struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
393 .tag_protocol = __constant_htons(ETH_P_EDSA),
394 .priv_size = sizeof(struct mv88e6xxx_priv_state),
395 .probe = mv88e6123_61_65_probe,
396 .setup = mv88e6123_61_65_setup,
397 .set_addr = mv88e6xxx_set_addr_indirect,
398 .phy_read = mv88e6123_61_65_phy_read,
399 .phy_write = mv88e6123_61_65_phy_write,
400 .poll_link = mv88e6xxx_poll_link,
401 .get_strings = mv88e6123_61_65_get_strings,
402 .get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats,
403 .get_sset_count = mv88e6123_61_65_get_sset_count,
404};
405
406int __init mv88e6123_61_65_init(void)
407{
408 register_switch_driver(&mv88e6123_61_65_switch_driver);
409 return 0;
410}
411module_init(mv88e6123_61_65_init);
412
413void __exit mv88e6123_61_65_cleanup(void)
414{
415 unregister_switch_driver(&mv88e6123_61_65_switch_driver);
416}
417module_exit(mv88e6123_61_65_cleanup);
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}
diff --git a/net/dsa/mv88e6xxx.h b/net/dsa/mv88e6xxx.h
new file mode 100644
index 000000000000..a004d4d02081
--- /dev/null
+++ b/net/dsa/mv88e6xxx.h
@@ -0,0 +1,77 @@
1/*
2 * net/dsa/mv88e6xxx.h - 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#ifndef __MV88E6XXX_H
12#define __MV88E6XXX_H
13
14#define REG_PORT(p) (0x10 + (p))
15#define REG_GLOBAL 0x1b
16#define REG_GLOBAL2 0x1c
17
18struct mv88e6xxx_priv_state {
19 /*
20 * When using multi-chip addressing, this mutex protects
21 * access to the indirect access registers. (In single-chip
22 * mode, this mutex is effectively useless.)
23 */
24 struct mutex smi_mutex;
25
26 /*
27 * This mutex serialises access to the statistics unit.
28 * Hold this mutex over snapshot + dump sequences.
29 */
30 struct mutex stats_mutex;
31};
32
33struct mv88e6xxx_hw_stat {
34 char string[ETH_GSTRING_LEN];
35 int sizeof_stat;
36 int reg;
37};
38
39int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
40int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg);
41int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
42 int reg, u16 val);
43int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val);
44int mv88e6xxx_config_prio(struct dsa_switch *ds);
45int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr);
46int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum);
47int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val);
48void mv88e6xxx_poll_link(struct dsa_switch *ds);
49void mv88e6xxx_get_strings(struct dsa_switch *ds,
50 int nr_stats, struct mv88e6xxx_hw_stat *stats,
51 int port, uint8_t *data);
52void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
53 int nr_stats, struct mv88e6xxx_hw_stat *stats,
54 int port, uint64_t *data);
55
56#define REG_READ(addr, reg) \
57 ({ \
58 int __ret; \
59 \
60 __ret = mv88e6xxx_reg_read(ds, addr, reg); \
61 if (__ret < 0) \
62 return __ret; \
63 __ret; \
64 })
65
66#define REG_WRITE(addr, reg, val) \
67 ({ \
68 int __ret; \
69 \
70 __ret = mv88e6xxx_reg_write(ds, addr, reg, val); \
71 if (__ret < 0) \
72 return __ret; \
73 })
74
75
76
77#endif
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
new file mode 100644
index 000000000000..3cb331e98b89
--- /dev/null
+++ b/net/dsa/slave.c
@@ -0,0 +1,288 @@
1/*
2 * net/dsa/slave.c - Slave device handling
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
16/* slave mii_bus handling ***************************************************/
17static int dsa_slave_phy_read(struct mii_bus *bus, int addr, int reg)
18{
19 struct dsa_switch *ds = bus->priv;
20
21 if (ds->valid_port_mask & (1 << addr))
22 return ds->drv->phy_read(ds, addr, reg);
23
24 return 0xffff;
25}
26
27static int dsa_slave_phy_write(struct mii_bus *bus, int addr, int reg, u16 val)
28{
29 struct dsa_switch *ds = bus->priv;
30
31 if (ds->valid_port_mask & (1 << addr))
32 return ds->drv->phy_write(ds, addr, reg, val);
33
34 return 0;
35}
36
37void dsa_slave_mii_bus_init(struct dsa_switch *ds)
38{
39 ds->slave_mii_bus->priv = (void *)ds;
40 ds->slave_mii_bus->name = "dsa slave smi";
41 ds->slave_mii_bus->read = dsa_slave_phy_read;
42 ds->slave_mii_bus->write = dsa_slave_phy_write;
43 snprintf(ds->slave_mii_bus->id, MII_BUS_ID_SIZE, "%s:%.2x",
44 ds->master_mii_bus->id, ds->pd->sw_addr);
45 ds->slave_mii_bus->parent = &(ds->master_mii_bus->dev);
46}
47
48
49/* slave device handling ****************************************************/
50static int dsa_slave_open(struct net_device *dev)
51{
52 return 0;
53}
54
55static int dsa_slave_close(struct net_device *dev)
56{
57 return 0;
58}
59
60static void dsa_slave_change_rx_flags(struct net_device *dev, int change)
61{
62 struct dsa_slave_priv *p = netdev_priv(dev);
63 struct net_device *master = p->parent->master_netdev;
64
65 if (change & IFF_ALLMULTI)
66 dev_set_allmulti(master, dev->flags & IFF_ALLMULTI ? 1 : -1);
67 if (change & IFF_PROMISC)
68 dev_set_promiscuity(master, dev->flags & IFF_PROMISC ? 1 : -1);
69}
70
71static void dsa_slave_set_rx_mode(struct net_device *dev)
72{
73 struct dsa_slave_priv *p = netdev_priv(dev);
74 struct net_device *master = p->parent->master_netdev;
75
76 dev_mc_sync(master, dev);
77 dev_unicast_sync(master, dev);
78}
79
80static int dsa_slave_set_mac_address(struct net_device *dev, void *addr)
81{
82 memcpy(dev->dev_addr, addr + 2, 6);
83
84 return 0;
85}
86
87static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
88{
89 struct dsa_slave_priv *p = netdev_priv(dev);
90 struct mii_ioctl_data *mii_data = if_mii(ifr);
91
92 if (p->phy != NULL)
93 return phy_mii_ioctl(p->phy, mii_data, cmd);
94
95 return -EOPNOTSUPP;
96}
97
98
99/* ethtool operations *******************************************************/
100static int
101dsa_slave_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
102{
103 struct dsa_slave_priv *p = netdev_priv(dev);
104 int err;
105
106 err = -EOPNOTSUPP;
107 if (p->phy != NULL) {
108 err = phy_read_status(p->phy);
109 if (err == 0)
110 err = phy_ethtool_gset(p->phy, cmd);
111 }
112
113 return err;
114}
115
116static int
117dsa_slave_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
118{
119 struct dsa_slave_priv *p = netdev_priv(dev);
120
121 if (p->phy != NULL)
122 return phy_ethtool_sset(p->phy, cmd);
123
124 return -EOPNOTSUPP;
125}
126
127static void dsa_slave_get_drvinfo(struct net_device *dev,
128 struct ethtool_drvinfo *drvinfo)
129{
130 strncpy(drvinfo->driver, "dsa", 32);
131 strncpy(drvinfo->version, dsa_driver_version, 32);
132 strncpy(drvinfo->fw_version, "N/A", 32);
133 strncpy(drvinfo->bus_info, "platform", 32);
134}
135
136static int dsa_slave_nway_reset(struct net_device *dev)
137{
138 struct dsa_slave_priv *p = netdev_priv(dev);
139
140 if (p->phy != NULL)
141 return genphy_restart_aneg(p->phy);
142
143 return -EOPNOTSUPP;
144}
145
146static u32 dsa_slave_get_link(struct net_device *dev)
147{
148 struct dsa_slave_priv *p = netdev_priv(dev);
149
150 if (p->phy != NULL) {
151 genphy_update_link(p->phy);
152 return p->phy->link;
153 }
154
155 return -EOPNOTSUPP;
156}
157
158static void dsa_slave_get_strings(struct net_device *dev,
159 uint32_t stringset, uint8_t *data)
160{
161 struct dsa_slave_priv *p = netdev_priv(dev);
162 struct dsa_switch *ds = p->parent;
163
164 if (stringset == ETH_SS_STATS) {
165 int len = ETH_GSTRING_LEN;
166
167 strncpy(data, "tx_packets", len);
168 strncpy(data + len, "tx_bytes", len);
169 strncpy(data + 2 * len, "rx_packets", len);
170 strncpy(data + 3 * len, "rx_bytes", len);
171 if (ds->drv->get_strings != NULL)
172 ds->drv->get_strings(ds, p->port, data + 4 * len);
173 }
174}
175
176static void dsa_slave_get_ethtool_stats(struct net_device *dev,
177 struct ethtool_stats *stats,
178 uint64_t *data)
179{
180 struct dsa_slave_priv *p = netdev_priv(dev);
181 struct dsa_switch *ds = p->parent;
182
183 data[0] = p->dev->stats.tx_packets;
184 data[1] = p->dev->stats.tx_bytes;
185 data[2] = p->dev->stats.rx_packets;
186 data[3] = p->dev->stats.rx_bytes;
187 if (ds->drv->get_ethtool_stats != NULL)
188 ds->drv->get_ethtool_stats(ds, p->port, data + 4);
189}
190
191static int dsa_slave_get_sset_count(struct net_device *dev, int sset)
192{
193 struct dsa_slave_priv *p = netdev_priv(dev);
194 struct dsa_switch *ds = p->parent;
195
196 if (sset == ETH_SS_STATS) {
197 int count;
198
199 count = 4;
200 if (ds->drv->get_sset_count != NULL)
201 count += ds->drv->get_sset_count(ds);
202
203 return count;
204 }
205
206 return -EOPNOTSUPP;
207}
208
209static const struct ethtool_ops dsa_slave_ethtool_ops = {
210 .get_settings = dsa_slave_get_settings,
211 .set_settings = dsa_slave_set_settings,
212 .get_drvinfo = dsa_slave_get_drvinfo,
213 .nway_reset = dsa_slave_nway_reset,
214 .get_link = dsa_slave_get_link,
215 .set_sg = ethtool_op_set_sg,
216 .get_strings = dsa_slave_get_strings,
217 .get_ethtool_stats = dsa_slave_get_ethtool_stats,
218 .get_sset_count = dsa_slave_get_sset_count,
219};
220
221
222/* slave device setup *******************************************************/
223struct net_device *
224dsa_slave_create(struct dsa_switch *ds, struct device *parent,
225 int port, char *name)
226{
227 struct net_device *master = ds->master_netdev;
228 struct net_device *slave_dev;
229 struct dsa_slave_priv *p;
230 int ret;
231
232 slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv),
233 name, ether_setup);
234 if (slave_dev == NULL)
235 return slave_dev;
236
237 slave_dev->features = master->vlan_features;
238 SET_ETHTOOL_OPS(slave_dev, &dsa_slave_ethtool_ops);
239 memcpy(slave_dev->dev_addr, master->dev_addr, ETH_ALEN);
240 slave_dev->tx_queue_len = 0;
241 switch (ds->tag_protocol) {
242#ifdef CONFIG_NET_DSA_TAG_EDSA
243 case htons(ETH_P_EDSA):
244 slave_dev->hard_start_xmit = edsa_xmit;
245 break;
246#endif
247 default:
248 BUG();
249 }
250 slave_dev->open = dsa_slave_open;
251 slave_dev->stop = dsa_slave_close;
252 slave_dev->change_rx_flags = dsa_slave_change_rx_flags;
253 slave_dev->set_rx_mode = dsa_slave_set_rx_mode;
254 slave_dev->set_multicast_list = dsa_slave_set_rx_mode;
255 slave_dev->set_mac_address = dsa_slave_set_mac_address;
256 slave_dev->do_ioctl = dsa_slave_ioctl;
257 SET_NETDEV_DEV(slave_dev, parent);
258 slave_dev->vlan_features = master->vlan_features;
259
260 p = netdev_priv(slave_dev);
261 p->dev = slave_dev;
262 p->parent = ds;
263 p->port = port;
264 p->phy = ds->slave_mii_bus->phy_map[port];
265
266 ret = register_netdev(slave_dev);
267 if (ret) {
268 printk(KERN_ERR "%s: error %d registering interface %s\n",
269 master->name, ret, slave_dev->name);
270 free_netdev(slave_dev);
271 return NULL;
272 }
273
274 netif_carrier_off(slave_dev);
275
276 if (p->phy != NULL) {
277 phy_attach(slave_dev, p->phy->dev.bus_id,
278 0, PHY_INTERFACE_MODE_GMII);
279
280 p->phy->autoneg = AUTONEG_ENABLE;
281 p->phy->speed = 0;
282 p->phy->duplex = 0;
283 p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
284 phy_start_aneg(p->phy);
285 }
286
287 return slave_dev;
288}
diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c
new file mode 100644
index 000000000000..f985ea993843
--- /dev/null
+++ b/net/dsa/tag_edsa.c
@@ -0,0 +1,213 @@
1/*
2 * net/dsa/tag_edsa.c - Ethertype DSA tagging
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/etherdevice.h>
12#include <linux/list.h>
13#include <linux/netdevice.h>
14#include "dsa_priv.h"
15
16#define DSA_HLEN 4
17#define EDSA_HLEN 8
18
19int edsa_xmit(struct sk_buff *skb, struct net_device *dev)
20{
21 struct dsa_slave_priv *p = netdev_priv(dev);
22 u8 *edsa_header;
23
24 dev->stats.tx_packets++;
25 dev->stats.tx_bytes += skb->len;
26
27 /*
28 * Convert the outermost 802.1q tag to a DSA tag and prepend
29 * a DSA ethertype field is the packet is tagged, or insert
30 * a DSA ethertype plus DSA tag between the addresses and the
31 * current ethertype field if the packet is untagged.
32 */
33 if (skb->protocol == htons(ETH_P_8021Q)) {
34 if (skb_cow_head(skb, DSA_HLEN) < 0)
35 goto out_free;
36 skb_push(skb, DSA_HLEN);
37
38 memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN);
39
40 /*
41 * Construct tagged FROM_CPU DSA tag from 802.1q tag.
42 */
43 edsa_header = skb->data + 2 * ETH_ALEN;
44 edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
45 edsa_header[1] = ETH_P_EDSA & 0xff;
46 edsa_header[2] = 0x00;
47 edsa_header[3] = 0x00;
48 edsa_header[4] = 0x60;
49 edsa_header[5] = p->port << 3;
50
51 /*
52 * Move CFI field from byte 6 to byte 5.
53 */
54 if (edsa_header[6] & 0x10) {
55 edsa_header[5] |= 0x01;
56 edsa_header[6] &= ~0x10;
57 }
58 } else {
59 if (skb_cow_head(skb, EDSA_HLEN) < 0)
60 goto out_free;
61 skb_push(skb, EDSA_HLEN);
62
63 memmove(skb->data, skb->data + EDSA_HLEN, 2 * ETH_ALEN);
64
65 /*
66 * Construct untagged FROM_CPU DSA tag.
67 */
68 edsa_header = skb->data + 2 * ETH_ALEN;
69 edsa_header[0] = (ETH_P_EDSA >> 8) & 0xff;
70 edsa_header[1] = ETH_P_EDSA & 0xff;
71 edsa_header[2] = 0x00;
72 edsa_header[3] = 0x00;
73 edsa_header[4] = 0x40;
74 edsa_header[5] = p->port << 3;
75 edsa_header[6] = 0x00;
76 edsa_header[7] = 0x00;
77 }
78
79 skb->protocol = htons(ETH_P_EDSA);
80
81 skb->dev = p->parent->master_netdev;
82 dev_queue_xmit(skb);
83
84 return NETDEV_TX_OK;
85
86out_free:
87 kfree_skb(skb);
88 return NETDEV_TX_OK;
89}
90
91static int edsa_rcv(struct sk_buff *skb, struct net_device *dev,
92 struct packet_type *pt, struct net_device *orig_dev)
93{
94 struct dsa_switch *ds = dev->dsa_ptr;
95 u8 *edsa_header;
96 int source_port;
97
98 if (unlikely(ds == NULL))
99 goto out_drop;
100
101 skb = skb_unshare(skb, GFP_ATOMIC);
102 if (skb == NULL)
103 goto out;
104
105 if (unlikely(!pskb_may_pull(skb, EDSA_HLEN)))
106 goto out_drop;
107
108 /*
109 * Skip the two null bytes after the ethertype.
110 */
111 edsa_header = skb->data + 2;
112
113 /*
114 * Check that frame type is either TO_CPU or FORWARD, and
115 * that the source device is zero.
116 */
117 if ((edsa_header[0] & 0xdf) != 0x00 && (edsa_header[0] & 0xdf) != 0xc0)
118 goto out_drop;
119
120 /*
121 * Check that the source port is a registered DSA port.
122 */
123 source_port = (edsa_header[1] >> 3) & 0x1f;
124 if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
125 goto out_drop;
126
127 /*
128 * If the 'tagged' bit is set, convert the DSA tag to a 802.1q
129 * tag and delete the ethertype part. If the 'tagged' bit is
130 * clear, delete the ethertype and the DSA tag parts.
131 */
132 if (edsa_header[0] & 0x20) {
133 u8 new_header[4];
134
135 /*
136 * Insert 802.1q ethertype and copy the VLAN-related
137 * fields, but clear the bit that will hold CFI (since
138 * DSA uses that bit location for another purpose).
139 */
140 new_header[0] = (ETH_P_8021Q >> 8) & 0xff;
141 new_header[1] = ETH_P_8021Q & 0xff;
142 new_header[2] = edsa_header[2] & ~0x10;
143 new_header[3] = edsa_header[3];
144
145 /*
146 * Move CFI bit from its place in the DSA header to
147 * its 802.1q-designated place.
148 */
149 if (edsa_header[1] & 0x01)
150 new_header[2] |= 0x10;
151
152 skb_pull_rcsum(skb, DSA_HLEN);
153
154 /*
155 * Update packet checksum if skb is CHECKSUM_COMPLETE.
156 */
157 if (skb->ip_summed == CHECKSUM_COMPLETE) {
158 __wsum c = skb->csum;
159 c = csum_add(c, csum_partial(new_header + 2, 2, 0));
160 c = csum_sub(c, csum_partial(edsa_header + 2, 2, 0));
161 skb->csum = c;
162 }
163
164 memcpy(edsa_header, new_header, DSA_HLEN);
165
166 memmove(skb->data - ETH_HLEN,
167 skb->data - ETH_HLEN - DSA_HLEN,
168 2 * ETH_ALEN);
169 } else {
170 /*
171 * Remove DSA tag and update checksum.
172 */
173 skb_pull_rcsum(skb, EDSA_HLEN);
174 memmove(skb->data - ETH_HLEN,
175 skb->data - ETH_HLEN - EDSA_HLEN,
176 2 * ETH_ALEN);
177 }
178
179 skb->dev = ds->ports[source_port];
180 skb_push(skb, ETH_HLEN);
181 skb->protocol = eth_type_trans(skb, skb->dev);
182
183 skb->dev->last_rx = jiffies;
184 skb->dev->stats.rx_packets++;
185 skb->dev->stats.rx_bytes += skb->len;
186
187 netif_receive_skb(skb);
188
189 return 0;
190
191out_drop:
192 kfree_skb(skb);
193out:
194 return 0;
195}
196
197static struct packet_type edsa_packet_type = {
198 .type = __constant_htons(ETH_P_EDSA),
199 .func = edsa_rcv,
200};
201
202static int __init edsa_init_module(void)
203{
204 dev_add_pack(&edsa_packet_type);
205 return 0;
206}
207module_init(edsa_init_module);
208
209static void __exit edsa_cleanup_module(void)
210{
211 dev_remove_pack(&edsa_packet_type);
212}
213module_exit(edsa_cleanup_module);