aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);