aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa
diff options
context:
space:
mode:
Diffstat (limited to 'net/dsa')
-rw-r--r--net/dsa/Kconfig60
-rw-r--r--net/dsa/Makefile13
-rw-r--r--net/dsa/dsa.c392
-rw-r--r--net/dsa/dsa_priv.h116
-rw-r--r--net/dsa/mv88e6060.c287
-rw-r--r--net/dsa/mv88e6123_61_65.c421
-rw-r--r--net/dsa/mv88e6131.c380
-rw-r--r--net/dsa/mv88e6xxx.c522
-rw-r--r--net/dsa/mv88e6xxx.h93
-rw-r--r--net/dsa/slave.c298
-rw-r--r--net/dsa/tag_dsa.c194
-rw-r--r--net/dsa/tag_edsa.c213
-rw-r--r--net/dsa/tag_trailer.c130
13 files changed, 3119 insertions, 0 deletions
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
new file mode 100644
index 000000000000..49211b35725b
--- /dev/null
+++ b/net/dsa/Kconfig
@@ -0,0 +1,60 @@
1menuconfig NET_DSA
2 bool "Distributed Switch Architecture support"
3 default n
4 depends on EXPERIMENTAL && !S390
5 select PHYLIB
6 ---help---
7 This allows you to use hardware switch chips that use
8 the Distributed Switch Architecture.
9
10
11if NET_DSA
12
13# tagging formats
14config NET_DSA_TAG_DSA
15 bool
16 default n
17
18config NET_DSA_TAG_EDSA
19 bool
20 default n
21
22config NET_DSA_TAG_TRAILER
23 bool
24 default n
25
26
27# switch drivers
28config NET_DSA_MV88E6XXX
29 bool
30 default n
31
32config NET_DSA_MV88E6060
33 bool "Marvell 88E6060 ethernet switch chip support"
34 select NET_DSA_TAG_TRAILER
35 ---help---
36 This enables support for the Marvell 88E6060 ethernet switch
37 chip.
38
39config NET_DSA_MV88E6XXX_NEED_PPU
40 bool
41 default n
42
43config NET_DSA_MV88E6131
44 bool "Marvell 88E6131 ethernet switch chip support"
45 select NET_DSA_MV88E6XXX
46 select NET_DSA_MV88E6XXX_NEED_PPU
47 select NET_DSA_TAG_DSA
48 ---help---
49 This enables support for the Marvell 88E6131 ethernet switch
50 chip.
51
52config NET_DSA_MV88E6123_61_65
53 bool "Marvell 88E6123/6161/6165 ethernet switch chip support"
54 select NET_DSA_MV88E6XXX
55 select NET_DSA_TAG_EDSA
56 ---help---
57 This enables support for the Marvell 88E6123/6161/6165
58 ethernet switch chips.
59
60endif
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
new file mode 100644
index 000000000000..2374faff4dea
--- /dev/null
+++ b/net/dsa/Makefile
@@ -0,0 +1,13 @@
1# tagging formats
2obj-$(CONFIG_NET_DSA_TAG_DSA) += tag_dsa.o
3obj-$(CONFIG_NET_DSA_TAG_EDSA) += tag_edsa.o
4obj-$(CONFIG_NET_DSA_TAG_TRAILER) += tag_trailer.o
5
6# switch drivers
7obj-$(CONFIG_NET_DSA_MV88E6XXX) += mv88e6xxx.o
8obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
9obj-$(CONFIG_NET_DSA_MV88E6123_61_65) += mv88e6123_61_65.o
10obj-$(CONFIG_NET_DSA_MV88E6131) += mv88e6131.o
11
12# the core
13obj-$(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..33e99462023a
--- /dev/null
+++ b/net/dsa/dsa.c
@@ -0,0 +1,392 @@
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/* hooks for ethertype-less tagging formats *********************************/
206/*
207 * The original DSA tag format and some other tag formats have no
208 * ethertype, which means that we need to add a little hack to the
209 * networking receive path to make sure that received frames get
210 * the right ->protocol assigned to them when one of those tag
211 * formats is in use.
212 */
213bool dsa_uses_dsa_tags(void *dsa_ptr)
214{
215 struct dsa_switch *ds = dsa_ptr;
216
217 return !!(ds->tag_protocol == htons(ETH_P_DSA));
218}
219
220bool dsa_uses_trailer_tags(void *dsa_ptr)
221{
222 struct dsa_switch *ds = dsa_ptr;
223
224 return !!(ds->tag_protocol == htons(ETH_P_TRAILER));
225}
226
227
228/* link polling *************************************************************/
229static void dsa_link_poll_work(struct work_struct *ugly)
230{
231 struct dsa_switch *ds;
232
233 ds = container_of(ugly, struct dsa_switch, link_poll_work);
234
235 ds->drv->poll_link(ds);
236 mod_timer(&ds->link_poll_timer, round_jiffies(jiffies + HZ));
237}
238
239static void dsa_link_poll_timer(unsigned long _ds)
240{
241 struct dsa_switch *ds = (void *)_ds;
242
243 schedule_work(&ds->link_poll_work);
244}
245
246
247/* platform driver init and cleanup *****************************************/
248static int dev_is_class(struct device *dev, void *class)
249{
250 if (dev->class != NULL && !strcmp(dev->class->name, class))
251 return 1;
252
253 return 0;
254}
255
256static struct device *dev_find_class(struct device *parent, char *class)
257{
258 if (dev_is_class(parent, class)) {
259 get_device(parent);
260 return parent;
261 }
262
263 return device_find_child(parent, class, dev_is_class);
264}
265
266static struct mii_bus *dev_to_mii_bus(struct device *dev)
267{
268 struct device *d;
269
270 d = dev_find_class(dev, "mdio_bus");
271 if (d != NULL) {
272 struct mii_bus *bus;
273
274 bus = to_mii_bus(d);
275 put_device(d);
276
277 return bus;
278 }
279
280 return NULL;
281}
282
283static struct net_device *dev_to_net_device(struct device *dev)
284{
285 struct device *d;
286
287 d = dev_find_class(dev, "net");
288 if (d != NULL) {
289 struct net_device *nd;
290
291 nd = to_net_dev(d);
292 dev_hold(nd);
293 put_device(d);
294
295 return nd;
296 }
297
298 return NULL;
299}
300
301static int dsa_probe(struct platform_device *pdev)
302{
303 static int dsa_version_printed;
304 struct dsa_platform_data *pd = pdev->dev.platform_data;
305 struct net_device *dev;
306 struct mii_bus *bus;
307 struct dsa_switch *ds;
308
309 if (!dsa_version_printed++)
310 printk(KERN_NOTICE "Distributed Switch Architecture "
311 "driver version %s\n", dsa_driver_version);
312
313 if (pd == NULL || pd->mii_bus == NULL || pd->netdev == NULL)
314 return -EINVAL;
315
316 bus = dev_to_mii_bus(pd->mii_bus);
317 if (bus == NULL)
318 return -EINVAL;
319
320 dev = dev_to_net_device(pd->netdev);
321 if (dev == NULL)
322 return -EINVAL;
323
324 if (dev->dsa_ptr != NULL) {
325 dev_put(dev);
326 return -EEXIST;
327 }
328
329 ds = dsa_switch_setup(&pdev->dev, pd, bus, dev);
330 if (IS_ERR(ds)) {
331 dev_put(dev);
332 return PTR_ERR(ds);
333 }
334
335 if (ds->drv->poll_link != NULL) {
336 INIT_WORK(&ds->link_poll_work, dsa_link_poll_work);
337 init_timer(&ds->link_poll_timer);
338 ds->link_poll_timer.data = (unsigned long)ds;
339 ds->link_poll_timer.function = dsa_link_poll_timer;
340 ds->link_poll_timer.expires = round_jiffies(jiffies + HZ);
341 add_timer(&ds->link_poll_timer);
342 }
343
344 platform_set_drvdata(pdev, ds);
345
346 return 0;
347}
348
349static int dsa_remove(struct platform_device *pdev)
350{
351 struct dsa_switch *ds = platform_get_drvdata(pdev);
352
353 if (ds->drv->poll_link != NULL)
354 del_timer_sync(&ds->link_poll_timer);
355
356 flush_scheduled_work();
357
358 dsa_switch_destroy(ds);
359
360 return 0;
361}
362
363static void dsa_shutdown(struct platform_device *pdev)
364{
365}
366
367static struct platform_driver dsa_driver = {
368 .probe = dsa_probe,
369 .remove = dsa_remove,
370 .shutdown = dsa_shutdown,
371 .driver = {
372 .name = "dsa",
373 .owner = THIS_MODULE,
374 },
375};
376
377static int __init dsa_init_module(void)
378{
379 return platform_driver_register(&dsa_driver);
380}
381module_init(dsa_init_module);
382
383static void __exit dsa_cleanup_module(void)
384{
385 platform_driver_unregister(&dsa_driver);
386}
387module_exit(dsa_cleanup_module);
388
389MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>")
390MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips");
391MODULE_LICENSE("GPL");
392MODULE_ALIAS("platform:dsa");
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
new file mode 100644
index 000000000000..7063378a1ebf
--- /dev/null
+++ b/net/dsa/dsa_priv.h
@@ -0,0 +1,116 @@
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_dsa.c */
107int dsa_xmit(struct sk_buff *skb, struct net_device *dev);
108
109/* tag_edsa.c */
110int edsa_xmit(struct sk_buff *skb, struct net_device *dev);
111
112/* tag_trailer.c */
113int trailer_xmit(struct sk_buff *skb, struct net_device *dev);
114
115
116#endif
diff --git a/net/dsa/mv88e6060.c b/net/dsa/mv88e6060.c
new file mode 100644
index 000000000000..54068ef251e8
--- /dev/null
+++ b/net/dsa/mv88e6060.c
@@ -0,0 +1,287 @@
1/*
2 * net/dsa/mv88e6060.c - Driver for Marvell 88e6060 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#include <linux/list.h>
12#include <linux/netdevice.h>
13#include <linux/phy.h>
14#include "dsa_priv.h"
15
16#define REG_PORT(p) (8 + (p))
17#define REG_GLOBAL 0x0f
18
19static int reg_read(struct dsa_switch *ds, int addr, int reg)
20{
21 return mdiobus_read(ds->master_mii_bus, addr, reg);
22}
23
24#define REG_READ(addr, reg) \
25 ({ \
26 int __ret; \
27 \
28 __ret = reg_read(ds, addr, reg); \
29 if (__ret < 0) \
30 return __ret; \
31 __ret; \
32 })
33
34
35static int reg_write(struct dsa_switch *ds, int addr, int reg, u16 val)
36{
37 return mdiobus_write(ds->master_mii_bus, addr, reg, val);
38}
39
40#define REG_WRITE(addr, reg, val) \
41 ({ \
42 int __ret; \
43 \
44 __ret = reg_write(ds, addr, reg, val); \
45 if (__ret < 0) \
46 return __ret; \
47 })
48
49static char *mv88e6060_probe(struct mii_bus *bus, int sw_addr)
50{
51 int ret;
52
53 ret = mdiobus_read(bus, REG_PORT(0), 0x03);
54 if (ret >= 0) {
55 ret &= 0xfff0;
56 if (ret == 0x0600)
57 return "Marvell 88E6060";
58 }
59
60 return NULL;
61}
62
63static int mv88e6060_switch_reset(struct dsa_switch *ds)
64{
65 int i;
66 int ret;
67
68 /*
69 * Set all ports to the disabled state.
70 */
71 for (i = 0; i < 6; i++) {
72 ret = REG_READ(REG_PORT(i), 0x04);
73 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
74 }
75
76 /*
77 * Wait for transmit queues to drain.
78 */
79 msleep(2);
80
81 /*
82 * Reset the switch.
83 */
84 REG_WRITE(REG_GLOBAL, 0x0A, 0xa130);
85
86 /*
87 * Wait up to one second for reset to complete.
88 */
89 for (i = 0; i < 1000; i++) {
90 ret = REG_READ(REG_GLOBAL, 0x00);
91 if ((ret & 0x8000) == 0x0000)
92 break;
93
94 msleep(1);
95 }
96 if (i == 1000)
97 return -ETIMEDOUT;
98
99 return 0;
100}
101
102static int mv88e6060_setup_global(struct dsa_switch *ds)
103{
104 /*
105 * Disable discarding of frames with excessive collisions,
106 * set the maximum frame size to 1536 bytes, and mask all
107 * interrupt sources.
108 */
109 REG_WRITE(REG_GLOBAL, 0x04, 0x0800);
110
111 /*
112 * Enable automatic address learning, set the address
113 * database size to 1024 entries, and set the default aging
114 * time to 5 minutes.
115 */
116 REG_WRITE(REG_GLOBAL, 0x0a, 0x2130);
117
118 return 0;
119}
120
121static int mv88e6060_setup_port(struct dsa_switch *ds, int p)
122{
123 int addr = REG_PORT(p);
124
125 /*
126 * Do not force flow control, disable Ingress and Egress
127 * Header tagging, disable VLAN tunneling, and set the port
128 * state to Forwarding. Additionally, if this is the CPU
129 * port, enable Ingress and Egress Trailer tagging mode.
130 */
131 REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x4103 : 0x0003);
132
133 /*
134 * Port based VLAN map: give each port its own address
135 * database, allow the CPU port to talk to each of the 'real'
136 * ports, and allow each of the 'real' ports to only talk to
137 * the CPU port.
138 */
139 REG_WRITE(addr, 0x06,
140 ((p & 0xf) << 12) |
141 ((p == ds->cpu_port) ?
142 ds->valid_port_mask :
143 (1 << ds->cpu_port)));
144
145 /*
146 * Port Association Vector: when learning source addresses
147 * of packets, add the address to the address database using
148 * a port bitmap that has only the bit for this port set and
149 * the other bits clear.
150 */
151 REG_WRITE(addr, 0x0b, 1 << p);
152
153 return 0;
154}
155
156static int mv88e6060_setup(struct dsa_switch *ds)
157{
158 int i;
159 int ret;
160
161 ret = mv88e6060_switch_reset(ds);
162 if (ret < 0)
163 return ret;
164
165 /* @@@ initialise atu */
166
167 ret = mv88e6060_setup_global(ds);
168 if (ret < 0)
169 return ret;
170
171 for (i = 0; i < 6; i++) {
172 ret = mv88e6060_setup_port(ds, i);
173 if (ret < 0)
174 return ret;
175 }
176
177 return 0;
178}
179
180static int mv88e6060_set_addr(struct dsa_switch *ds, u8 *addr)
181{
182 REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]);
183 REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]);
184 REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]);
185
186 return 0;
187}
188
189static int mv88e6060_port_to_phy_addr(int port)
190{
191 if (port >= 0 && port <= 5)
192 return port;
193 return -1;
194}
195
196static int mv88e6060_phy_read(struct dsa_switch *ds, int port, int regnum)
197{
198 int addr;
199
200 addr = mv88e6060_port_to_phy_addr(port);
201 if (addr == -1)
202 return 0xffff;
203
204 return reg_read(ds, addr, regnum);
205}
206
207static int
208mv88e6060_phy_write(struct dsa_switch *ds, int port, int regnum, u16 val)
209{
210 int addr;
211
212 addr = mv88e6060_port_to_phy_addr(port);
213 if (addr == -1)
214 return 0xffff;
215
216 return reg_write(ds, addr, regnum, val);
217}
218
219static void mv88e6060_poll_link(struct dsa_switch *ds)
220{
221 int i;
222
223 for (i = 0; i < DSA_MAX_PORTS; i++) {
224 struct net_device *dev;
225 int port_status;
226 int link;
227 int speed;
228 int duplex;
229 int fc;
230
231 dev = ds->ports[i];
232 if (dev == NULL)
233 continue;
234
235 link = 0;
236 if (dev->flags & IFF_UP) {
237 port_status = reg_read(ds, REG_PORT(i), 0x00);
238 if (port_status < 0)
239 continue;
240
241 link = !!(port_status & 0x1000);
242 }
243
244 if (!link) {
245 if (netif_carrier_ok(dev)) {
246 printk(KERN_INFO "%s: link down\n", dev->name);
247 netif_carrier_off(dev);
248 }
249 continue;
250 }
251
252 speed = (port_status & 0x0100) ? 100 : 10;
253 duplex = (port_status & 0x0200) ? 1 : 0;
254 fc = ((port_status & 0xc000) == 0xc000) ? 1 : 0;
255
256 if (!netif_carrier_ok(dev)) {
257 printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
258 "flow control %sabled\n", dev->name,
259 speed, duplex ? "full" : "half",
260 fc ? "en" : "dis");
261 netif_carrier_on(dev);
262 }
263 }
264}
265
266static struct dsa_switch_driver mv88e6060_switch_driver = {
267 .tag_protocol = htons(ETH_P_TRAILER),
268 .probe = mv88e6060_probe,
269 .setup = mv88e6060_setup,
270 .set_addr = mv88e6060_set_addr,
271 .phy_read = mv88e6060_phy_read,
272 .phy_write = mv88e6060_phy_write,
273 .poll_link = mv88e6060_poll_link,
274};
275
276int __init mv88e6060_init(void)
277{
278 register_switch_driver(&mv88e6060_switch_driver);
279 return 0;
280}
281module_init(mv88e6060_init);
282
283void __exit mv88e6060_cleanup(void)
284{
285 unregister_switch_driver(&mv88e6060_switch_driver);
286}
287module_exit(mv88e6060_cleanup);
diff --git a/net/dsa/mv88e6123_61_65.c b/net/dsa/mv88e6123_61_65.c
new file mode 100644
index 000000000000..555b164082fc
--- /dev/null
+++ b/net/dsa/mv88e6123_61_65.c
@@ -0,0 +1,421 @@
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 requested (DSA/EDSA) tagging mode if this is
196 * the CPU port, disable Header mode, enable IGMP/MLD snooping,
197 * disable VLAN tunneling, determine priority by looking at
198 * 802.1p and IP priority fields (IP prio has precedence), and
199 * set STP state to Forwarding. Finally, if this is the CPU
200 * port, additionally enable forwarding of unknown unicast and
201 * multicast addresses.
202 */
203 REG_WRITE(addr, 0x04,
204 (p == ds->cpu_port) ?
205 (ds->tag_protocol == htons(ETH_P_DSA)) ?
206 0x053f : 0x373f :
207 0x0433);
208
209 /*
210 * Port Control 1: disable trunking. Also, if this is the
211 * CPU port, enable learn messages to be sent to this port.
212 */
213 REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000);
214
215 /*
216 * Port based VLAN map: give each port its own address
217 * database, allow the CPU port to talk to each of the 'real'
218 * ports, and allow each of the 'real' ports to only talk to
219 * the CPU port.
220 */
221 REG_WRITE(addr, 0x06,
222 ((p & 0xf) << 12) |
223 ((p == ds->cpu_port) ?
224 ds->valid_port_mask :
225 (1 << ds->cpu_port)));
226
227 /*
228 * Default VLAN ID and priority: don't set a default VLAN
229 * ID, and set the default packet priority to zero.
230 */
231 REG_WRITE(addr, 0x07, 0x0000);
232
233 /*
234 * Port Control 2: don't force a good FCS, set the maximum
235 * frame size to 10240 bytes, don't let the switch add or
236 * strip 802.1q tags, don't discard tagged or untagged frames
237 * on this port, do a destination address lookup on all
238 * received packets as usual, disable ARP mirroring and don't
239 * send a copy of all transmitted/received frames on this port
240 * to the CPU.
241 */
242 REG_WRITE(addr, 0x08, 0x2080);
243
244 /*
245 * Egress rate control: disable egress rate control.
246 */
247 REG_WRITE(addr, 0x09, 0x0001);
248
249 /*
250 * Egress rate control 2: disable egress rate control.
251 */
252 REG_WRITE(addr, 0x0a, 0x0000);
253
254 /*
255 * Port Association Vector: when learning source addresses
256 * of packets, add the address to the address database using
257 * a port bitmap that has only the bit for this port set and
258 * the other bits clear.
259 */
260 REG_WRITE(addr, 0x0b, 1 << p);
261
262 /*
263 * Port ATU control: disable limiting the number of address
264 * database entries that this port is allowed to use.
265 */
266 REG_WRITE(addr, 0x0c, 0x0000);
267
268 /*
269 * Priorit Override: disable DA, SA and VTU priority override.
270 */
271 REG_WRITE(addr, 0x0d, 0x0000);
272
273 /*
274 * Port Ethertype: use the Ethertype DSA Ethertype value.
275 */
276 REG_WRITE(addr, 0x0f, ETH_P_EDSA);
277
278 /*
279 * Tag Remap: use an identity 802.1p prio -> switch prio
280 * mapping.
281 */
282 REG_WRITE(addr, 0x18, 0x3210);
283
284 /*
285 * Tag Remap 2: use an identity 802.1p prio -> switch prio
286 * mapping.
287 */
288 REG_WRITE(addr, 0x19, 0x7654);
289
290 return 0;
291}
292
293static int mv88e6123_61_65_setup(struct dsa_switch *ds)
294{
295 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
296 int i;
297 int ret;
298
299 mutex_init(&ps->smi_mutex);
300 mutex_init(&ps->stats_mutex);
301
302 ret = mv88e6123_61_65_switch_reset(ds);
303 if (ret < 0)
304 return ret;
305
306 /* @@@ initialise vtu and atu */
307
308 ret = mv88e6123_61_65_setup_global(ds);
309 if (ret < 0)
310 return ret;
311
312 for (i = 0; i < 6; i++) {
313 ret = mv88e6123_61_65_setup_port(ds, i);
314 if (ret < 0)
315 return ret;
316 }
317
318 return 0;
319}
320
321static int mv88e6123_61_65_port_to_phy_addr(int port)
322{
323 if (port >= 0 && port <= 4)
324 return port;
325 return -1;
326}
327
328static int
329mv88e6123_61_65_phy_read(struct dsa_switch *ds, int port, int regnum)
330{
331 int addr = mv88e6123_61_65_port_to_phy_addr(port);
332 return mv88e6xxx_phy_read(ds, addr, regnum);
333}
334
335static int
336mv88e6123_61_65_phy_write(struct dsa_switch *ds,
337 int port, int regnum, u16 val)
338{
339 int addr = mv88e6123_61_65_port_to_phy_addr(port);
340 return mv88e6xxx_phy_write(ds, addr, regnum, val);
341}
342
343static struct mv88e6xxx_hw_stat mv88e6123_61_65_hw_stats[] = {
344 { "in_good_octets", 8, 0x00, },
345 { "in_bad_octets", 4, 0x02, },
346 { "in_unicast", 4, 0x04, },
347 { "in_broadcasts", 4, 0x06, },
348 { "in_multicasts", 4, 0x07, },
349 { "in_pause", 4, 0x16, },
350 { "in_undersize", 4, 0x18, },
351 { "in_fragments", 4, 0x19, },
352 { "in_oversize", 4, 0x1a, },
353 { "in_jabber", 4, 0x1b, },
354 { "in_rx_error", 4, 0x1c, },
355 { "in_fcs_error", 4, 0x1d, },
356 { "out_octets", 8, 0x0e, },
357 { "out_unicast", 4, 0x10, },
358 { "out_broadcasts", 4, 0x13, },
359 { "out_multicasts", 4, 0x12, },
360 { "out_pause", 4, 0x15, },
361 { "excessive", 4, 0x11, },
362 { "collisions", 4, 0x1e, },
363 { "deferred", 4, 0x05, },
364 { "single", 4, 0x14, },
365 { "multiple", 4, 0x17, },
366 { "out_fcs_error", 4, 0x03, },
367 { "late", 4, 0x1f, },
368 { "hist_64bytes", 4, 0x08, },
369 { "hist_65_127bytes", 4, 0x09, },
370 { "hist_128_255bytes", 4, 0x0a, },
371 { "hist_256_511bytes", 4, 0x0b, },
372 { "hist_512_1023bytes", 4, 0x0c, },
373 { "hist_1024_max_bytes", 4, 0x0d, },
374};
375
376static void
377mv88e6123_61_65_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
378{
379 mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
380 mv88e6123_61_65_hw_stats, port, data);
381}
382
383static void
384mv88e6123_61_65_get_ethtool_stats(struct dsa_switch *ds,
385 int port, uint64_t *data)
386{
387 mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6123_61_65_hw_stats),
388 mv88e6123_61_65_hw_stats, port, data);
389}
390
391static int mv88e6123_61_65_get_sset_count(struct dsa_switch *ds)
392{
393 return ARRAY_SIZE(mv88e6123_61_65_hw_stats);
394}
395
396static struct dsa_switch_driver mv88e6123_61_65_switch_driver = {
397 .tag_protocol = __constant_htons(ETH_P_EDSA),
398 .priv_size = sizeof(struct mv88e6xxx_priv_state),
399 .probe = mv88e6123_61_65_probe,
400 .setup = mv88e6123_61_65_setup,
401 .set_addr = mv88e6xxx_set_addr_indirect,
402 .phy_read = mv88e6123_61_65_phy_read,
403 .phy_write = mv88e6123_61_65_phy_write,
404 .poll_link = mv88e6xxx_poll_link,
405 .get_strings = mv88e6123_61_65_get_strings,
406 .get_ethtool_stats = mv88e6123_61_65_get_ethtool_stats,
407 .get_sset_count = mv88e6123_61_65_get_sset_count,
408};
409
410int __init mv88e6123_61_65_init(void)
411{
412 register_switch_driver(&mv88e6123_61_65_switch_driver);
413 return 0;
414}
415module_init(mv88e6123_61_65_init);
416
417void __exit mv88e6123_61_65_cleanup(void)
418{
419 unregister_switch_driver(&mv88e6123_61_65_switch_driver);
420}
421module_exit(mv88e6123_61_65_cleanup);
diff --git a/net/dsa/mv88e6131.c b/net/dsa/mv88e6131.c
new file mode 100644
index 000000000000..36e01eb863a0
--- /dev/null
+++ b/net/dsa/mv88e6131.c
@@ -0,0 +1,380 @@
1/*
2 * net/dsa/mv88e6131.c - Marvell 88e6131 switch chip support
3 * Copyright (c) 2008 Marvell Semiconductor
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 */
10
11#include <linux/list.h>
12#include <linux/netdevice.h>
13#include <linux/phy.h>
14#include "dsa_priv.h"
15#include "mv88e6xxx.h"
16
17static char *mv88e6131_probe(struct mii_bus *bus, int sw_addr)
18{
19 int ret;
20
21 ret = __mv88e6xxx_reg_read(bus, sw_addr, REG_PORT(0), 0x03);
22 if (ret >= 0) {
23 ret &= 0xfff0;
24 if (ret == 0x1060)
25 return "Marvell 88E6131";
26 }
27
28 return NULL;
29}
30
31static int mv88e6131_switch_reset(struct dsa_switch *ds)
32{
33 int i;
34 int ret;
35
36 /*
37 * Set all ports to the disabled state.
38 */
39 for (i = 0; i < 8; i++) {
40 ret = REG_READ(REG_PORT(i), 0x04);
41 REG_WRITE(REG_PORT(i), 0x04, ret & 0xfffc);
42 }
43
44 /*
45 * Wait for transmit queues to drain.
46 */
47 msleep(2);
48
49 /*
50 * Reset the switch.
51 */
52 REG_WRITE(REG_GLOBAL, 0x04, 0xc400);
53
54 /*
55 * Wait up to one second for reset to complete.
56 */
57 for (i = 0; i < 1000; i++) {
58 ret = REG_READ(REG_GLOBAL, 0x00);
59 if ((ret & 0xc800) == 0xc800)
60 break;
61
62 msleep(1);
63 }
64 if (i == 1000)
65 return -ETIMEDOUT;
66
67 return 0;
68}
69
70static int mv88e6131_setup_global(struct dsa_switch *ds)
71{
72 int ret;
73 int i;
74
75 /*
76 * Enable the PHY polling unit, don't discard packets with
77 * excessive collisions, use a weighted fair queueing scheme
78 * to arbitrate between packet queues, set the maximum frame
79 * size to 1632, and mask all interrupt sources.
80 */
81 REG_WRITE(REG_GLOBAL, 0x04, 0x4400);
82
83 /*
84 * Set the default address aging time to 5 minutes, and
85 * enable address learn messages to be sent to all message
86 * ports.
87 */
88 REG_WRITE(REG_GLOBAL, 0x0a, 0x0148);
89
90 /*
91 * Configure the priority mapping registers.
92 */
93 ret = mv88e6xxx_config_prio(ds);
94 if (ret < 0)
95 return ret;
96
97 /*
98 * Set the VLAN ethertype to 0x8100.
99 */
100 REG_WRITE(REG_GLOBAL, 0x19, 0x8100);
101
102 /*
103 * Disable ARP mirroring, and configure the cpu port as the
104 * port to which ingress and egress monitor frames are to be
105 * sent.
106 */
107 REG_WRITE(REG_GLOBAL, 0x1a, (ds->cpu_port * 0x1100) | 0x00f0);
108
109 /*
110 * Disable cascade port functionality, and set the switch's
111 * DSA device number to zero.
112 */
113 REG_WRITE(REG_GLOBAL, 0x1c, 0xe000);
114
115 /*
116 * Send all frames with destination addresses matching
117 * 01:80:c2:00:00:0x to the CPU port.
118 */
119 REG_WRITE(REG_GLOBAL2, 0x03, 0xffff);
120
121 /*
122 * Ignore removed tag data on doubly tagged packets, disable
123 * flow control messages, force flow control priority to the
124 * highest, and send all special multicast frames to the CPU
125 * port at the higest priority.
126 */
127 REG_WRITE(REG_GLOBAL2, 0x05, 0x00ff);
128
129 /*
130 * Map all DSA device IDs to the CPU port.
131 */
132 for (i = 0; i < 32; i++)
133 REG_WRITE(REG_GLOBAL2, 0x06, 0x8000 | (i << 8) | ds->cpu_port);
134
135 /*
136 * Clear all trunk masks.
137 */
138 for (i = 0; i < 8; i++)
139 REG_WRITE(REG_GLOBAL2, 0x07, 0x8000 | (i << 12) | 0xff);
140
141 /*
142 * Clear all trunk mappings.
143 */
144 for (i = 0; i < 16; i++)
145 REG_WRITE(REG_GLOBAL2, 0x08, 0x8000 | (i << 11));
146
147 /*
148 * Force the priority of IGMP/MLD snoop frames and ARP frames
149 * to the highest setting.
150 */
151 REG_WRITE(REG_GLOBAL2, 0x0f, 0x00ff);
152
153 return 0;
154}
155
156static int mv88e6131_setup_port(struct dsa_switch *ds, int p)
157{
158 int addr = REG_PORT(p);
159
160 /*
161 * MAC Forcing register: don't force link, speed, duplex
162 * or flow control state to any particular values.
163 */
164 REG_WRITE(addr, 0x01, 0x0003);
165
166 /*
167 * Port Control: disable Core Tag, disable Drop-on-Lock,
168 * transmit frames unmodified, disable Header mode,
169 * enable IGMP/MLD snoop, disable DoubleTag, disable VLAN
170 * tunneling, determine priority by looking at 802.1p and
171 * IP priority fields (IP prio has precedence), and set STP
172 * state to Forwarding. Finally, if this is the CPU port,
173 * additionally enable DSA tagging and forwarding of unknown
174 * unicast addresses.
175 */
176 REG_WRITE(addr, 0x04, (p == ds->cpu_port) ? 0x0537 : 0x0433);
177
178 /*
179 * Port Control 1: disable trunking. Also, if this is the
180 * CPU port, enable learn messages to be sent to this port.
181 */
182 REG_WRITE(addr, 0x05, (p == ds->cpu_port) ? 0x8000 : 0x0000);
183
184 /*
185 * Port based VLAN map: give each port its own address
186 * database, allow the CPU port to talk to each of the 'real'
187 * ports, and allow each of the 'real' ports to only talk to
188 * the CPU port.
189 */
190 REG_WRITE(addr, 0x06,
191 ((p & 0xf) << 12) |
192 ((p == ds->cpu_port) ?
193 ds->valid_port_mask :
194 (1 << ds->cpu_port)));
195
196 /*
197 * Default VLAN ID and priority: don't set a default VLAN
198 * ID, and set the default packet priority to zero.
199 */
200 REG_WRITE(addr, 0x07, 0x0000);
201
202 /*
203 * Port Control 2: don't force a good FCS, don't use
204 * VLAN-based, source address-based or destination
205 * address-based priority overrides, don't let the switch
206 * add or strip 802.1q tags, don't discard tagged or
207 * untagged frames on this port, do a destination address
208 * lookup on received packets as usual, don't send a copy
209 * of all transmitted/received frames on this port to the
210 * CPU, and configure the CPU port number. Also, if this
211 * is the CPU port, enable forwarding of unknown multicast
212 * addresses.
213 */
214 REG_WRITE(addr, 0x08,
215 ((p == ds->cpu_port) ? 0x00c0 : 0x0080) |
216 ds->cpu_port);
217
218 /*
219 * Rate Control: disable ingress rate limiting.
220 */
221 REG_WRITE(addr, 0x09, 0x0000);
222
223 /*
224 * Rate Control 2: disable egress rate limiting.
225 */
226 REG_WRITE(addr, 0x0a, 0x0000);
227
228 /*
229 * Port Association Vector: when learning source addresses
230 * of packets, add the address to the address database using
231 * a port bitmap that has only the bit for this port set and
232 * the other bits clear.
233 */
234 REG_WRITE(addr, 0x0b, 1 << p);
235
236 /*
237 * Tag Remap: use an identity 802.1p prio -> switch prio
238 * mapping.
239 */
240 REG_WRITE(addr, 0x18, 0x3210);
241
242 /*
243 * Tag Remap 2: use an identity 802.1p prio -> switch prio
244 * mapping.
245 */
246 REG_WRITE(addr, 0x19, 0x7654);
247
248 return 0;
249}
250
251static int mv88e6131_setup(struct dsa_switch *ds)
252{
253 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
254 int i;
255 int ret;
256
257 mutex_init(&ps->smi_mutex);
258 mv88e6xxx_ppu_state_init(ds);
259 mutex_init(&ps->stats_mutex);
260
261 ret = mv88e6131_switch_reset(ds);
262 if (ret < 0)
263 return ret;
264
265 /* @@@ initialise vtu and atu */
266
267 ret = mv88e6131_setup_global(ds);
268 if (ret < 0)
269 return ret;
270
271 for (i = 0; i < 6; i++) {
272 ret = mv88e6131_setup_port(ds, i);
273 if (ret < 0)
274 return ret;
275 }
276
277 return 0;
278}
279
280static int mv88e6131_port_to_phy_addr(int port)
281{
282 if (port >= 0 && port != 3 && port <= 7)
283 return port;
284 return -1;
285}
286
287static int
288mv88e6131_phy_read(struct dsa_switch *ds, int port, int regnum)
289{
290 int addr = mv88e6131_port_to_phy_addr(port);
291 return mv88e6xxx_phy_read_ppu(ds, addr, regnum);
292}
293
294static int
295mv88e6131_phy_write(struct dsa_switch *ds,
296 int port, int regnum, u16 val)
297{
298 int addr = mv88e6131_port_to_phy_addr(port);
299 return mv88e6xxx_phy_write_ppu(ds, addr, regnum, val);
300}
301
302static struct mv88e6xxx_hw_stat mv88e6131_hw_stats[] = {
303 { "in_good_octets", 8, 0x00, },
304 { "in_bad_octets", 4, 0x02, },
305 { "in_unicast", 4, 0x04, },
306 { "in_broadcasts", 4, 0x06, },
307 { "in_multicasts", 4, 0x07, },
308 { "in_pause", 4, 0x16, },
309 { "in_undersize", 4, 0x18, },
310 { "in_fragments", 4, 0x19, },
311 { "in_oversize", 4, 0x1a, },
312 { "in_jabber", 4, 0x1b, },
313 { "in_rx_error", 4, 0x1c, },
314 { "in_fcs_error", 4, 0x1d, },
315 { "out_octets", 8, 0x0e, },
316 { "out_unicast", 4, 0x10, },
317 { "out_broadcasts", 4, 0x13, },
318 { "out_multicasts", 4, 0x12, },
319 { "out_pause", 4, 0x15, },
320 { "excessive", 4, 0x11, },
321 { "collisions", 4, 0x1e, },
322 { "deferred", 4, 0x05, },
323 { "single", 4, 0x14, },
324 { "multiple", 4, 0x17, },
325 { "out_fcs_error", 4, 0x03, },
326 { "late", 4, 0x1f, },
327 { "hist_64bytes", 4, 0x08, },
328 { "hist_65_127bytes", 4, 0x09, },
329 { "hist_128_255bytes", 4, 0x0a, },
330 { "hist_256_511bytes", 4, 0x0b, },
331 { "hist_512_1023bytes", 4, 0x0c, },
332 { "hist_1024_max_bytes", 4, 0x0d, },
333};
334
335static void
336mv88e6131_get_strings(struct dsa_switch *ds, int port, uint8_t *data)
337{
338 mv88e6xxx_get_strings(ds, ARRAY_SIZE(mv88e6131_hw_stats),
339 mv88e6131_hw_stats, port, data);
340}
341
342static void
343mv88e6131_get_ethtool_stats(struct dsa_switch *ds,
344 int port, uint64_t *data)
345{
346 mv88e6xxx_get_ethtool_stats(ds, ARRAY_SIZE(mv88e6131_hw_stats),
347 mv88e6131_hw_stats, port, data);
348}
349
350static int mv88e6131_get_sset_count(struct dsa_switch *ds)
351{
352 return ARRAY_SIZE(mv88e6131_hw_stats);
353}
354
355static struct dsa_switch_driver mv88e6131_switch_driver = {
356 .tag_protocol = __constant_htons(ETH_P_DSA),
357 .priv_size = sizeof(struct mv88e6xxx_priv_state),
358 .probe = mv88e6131_probe,
359 .setup = mv88e6131_setup,
360 .set_addr = mv88e6xxx_set_addr_direct,
361 .phy_read = mv88e6131_phy_read,
362 .phy_write = mv88e6131_phy_write,
363 .poll_link = mv88e6xxx_poll_link,
364 .get_strings = mv88e6131_get_strings,
365 .get_ethtool_stats = mv88e6131_get_ethtool_stats,
366 .get_sset_count = mv88e6131_get_sset_count,
367};
368
369int __init mv88e6131_init(void)
370{
371 register_switch_driver(&mv88e6131_switch_driver);
372 return 0;
373}
374module_init(mv88e6131_init);
375
376void __exit mv88e6131_cleanup(void)
377{
378 unregister_switch_driver(&mv88e6131_switch_driver);
379}
380module_exit(mv88e6131_cleanup);
diff --git a/net/dsa/mv88e6xxx.c b/net/dsa/mv88e6xxx.c
new file mode 100644
index 000000000000..aa6c609c59f2
--- /dev/null
+++ b/net/dsa/mv88e6xxx.c
@@ -0,0 +1,522 @@
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_direct(struct dsa_switch *ds, u8 *addr)
169{
170 REG_WRITE(REG_GLOBAL, 0x01, (addr[0] << 8) | addr[1]);
171 REG_WRITE(REG_GLOBAL, 0x02, (addr[2] << 8) | addr[3]);
172 REG_WRITE(REG_GLOBAL, 0x03, (addr[4] << 8) | addr[5]);
173
174 return 0;
175}
176
177int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr)
178{
179 int i;
180 int ret;
181
182 for (i = 0; i < 6; i++) {
183 int j;
184
185 /*
186 * Write the MAC address byte.
187 */
188 REG_WRITE(REG_GLOBAL2, 0x0d, 0x8000 | (i << 8) | addr[i]);
189
190 /*
191 * Wait for the write to complete.
192 */
193 for (j = 0; j < 16; j++) {
194 ret = REG_READ(REG_GLOBAL2, 0x0d);
195 if ((ret & 0x8000) == 0)
196 break;
197 }
198 if (j == 16)
199 return -ETIMEDOUT;
200 }
201
202 return 0;
203}
204
205int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum)
206{
207 if (addr >= 0)
208 return mv88e6xxx_reg_read(ds, addr, regnum);
209 return 0xffff;
210}
211
212int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val)
213{
214 if (addr >= 0)
215 return mv88e6xxx_reg_write(ds, addr, regnum, val);
216 return 0;
217}
218
219#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
220static int mv88e6xxx_ppu_disable(struct dsa_switch *ds)
221{
222 int ret;
223 int i;
224
225 ret = REG_READ(REG_GLOBAL, 0x04);
226 REG_WRITE(REG_GLOBAL, 0x04, ret & ~0x4000);
227
228 for (i = 0; i < 1000; i++) {
229 ret = REG_READ(REG_GLOBAL, 0x00);
230 msleep(1);
231 if ((ret & 0xc000) != 0xc000)
232 return 0;
233 }
234
235 return -ETIMEDOUT;
236}
237
238static int mv88e6xxx_ppu_enable(struct dsa_switch *ds)
239{
240 int ret;
241 int i;
242
243 ret = REG_READ(REG_GLOBAL, 0x04);
244 REG_WRITE(REG_GLOBAL, 0x04, ret | 0x4000);
245
246 for (i = 0; i < 1000; i++) {
247 ret = REG_READ(REG_GLOBAL, 0x00);
248 msleep(1);
249 if ((ret & 0xc000) == 0xc000)
250 return 0;
251 }
252
253 return -ETIMEDOUT;
254}
255
256static void mv88e6xxx_ppu_reenable_work(struct work_struct *ugly)
257{
258 struct mv88e6xxx_priv_state *ps;
259
260 ps = container_of(ugly, struct mv88e6xxx_priv_state, ppu_work);
261 if (mutex_trylock(&ps->ppu_mutex)) {
262 struct dsa_switch *ds = ((struct dsa_switch *)ps) - 1;
263
264 if (mv88e6xxx_ppu_enable(ds) == 0)
265 ps->ppu_disabled = 0;
266 mutex_unlock(&ps->ppu_mutex);
267 }
268}
269
270static void mv88e6xxx_ppu_reenable_timer(unsigned long _ps)
271{
272 struct mv88e6xxx_priv_state *ps = (void *)_ps;
273
274 schedule_work(&ps->ppu_work);
275}
276
277static int mv88e6xxx_ppu_access_get(struct dsa_switch *ds)
278{
279 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
280 int ret;
281
282 mutex_lock(&ps->ppu_mutex);
283
284 /*
285 * If the PHY polling unit is enabled, disable it so that
286 * we can access the PHY registers. If it was already
287 * disabled, cancel the timer that is going to re-enable
288 * it.
289 */
290 if (!ps->ppu_disabled) {
291 ret = mv88e6xxx_ppu_disable(ds);
292 if (ret < 0) {
293 mutex_unlock(&ps->ppu_mutex);
294 return ret;
295 }
296 ps->ppu_disabled = 1;
297 } else {
298 del_timer(&ps->ppu_timer);
299 ret = 0;
300 }
301
302 return ret;
303}
304
305static void mv88e6xxx_ppu_access_put(struct dsa_switch *ds)
306{
307 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
308
309 /*
310 * Schedule a timer to re-enable the PHY polling unit.
311 */
312 mod_timer(&ps->ppu_timer, jiffies + msecs_to_jiffies(10));
313 mutex_unlock(&ps->ppu_mutex);
314}
315
316void mv88e6xxx_ppu_state_init(struct dsa_switch *ds)
317{
318 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
319
320 mutex_init(&ps->ppu_mutex);
321 INIT_WORK(&ps->ppu_work, mv88e6xxx_ppu_reenable_work);
322 init_timer(&ps->ppu_timer);
323 ps->ppu_timer.data = (unsigned long)ps;
324 ps->ppu_timer.function = mv88e6xxx_ppu_reenable_timer;
325}
326
327int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum)
328{
329 int ret;
330
331 ret = mv88e6xxx_ppu_access_get(ds);
332 if (ret >= 0) {
333 ret = mv88e6xxx_reg_read(ds, addr, regnum);
334 mv88e6xxx_ppu_access_put(ds);
335 }
336
337 return ret;
338}
339
340int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
341 int regnum, u16 val)
342{
343 int ret;
344
345 ret = mv88e6xxx_ppu_access_get(ds);
346 if (ret >= 0) {
347 ret = mv88e6xxx_reg_write(ds, addr, regnum, val);
348 mv88e6xxx_ppu_access_put(ds);
349 }
350
351 return ret;
352}
353#endif
354
355void mv88e6xxx_poll_link(struct dsa_switch *ds)
356{
357 int i;
358
359 for (i = 0; i < DSA_MAX_PORTS; i++) {
360 struct net_device *dev;
361 int port_status;
362 int link;
363 int speed;
364 int duplex;
365 int fc;
366
367 dev = ds->ports[i];
368 if (dev == NULL)
369 continue;
370
371 link = 0;
372 if (dev->flags & IFF_UP) {
373 port_status = mv88e6xxx_reg_read(ds, REG_PORT(i), 0x00);
374 if (port_status < 0)
375 continue;
376
377 link = !!(port_status & 0x0800);
378 }
379
380 if (!link) {
381 if (netif_carrier_ok(dev)) {
382 printk(KERN_INFO "%s: link down\n", dev->name);
383 netif_carrier_off(dev);
384 }
385 continue;
386 }
387
388 switch (port_status & 0x0300) {
389 case 0x0000:
390 speed = 10;
391 break;
392 case 0x0100:
393 speed = 100;
394 break;
395 case 0x0200:
396 speed = 1000;
397 break;
398 default:
399 speed = -1;
400 break;
401 }
402 duplex = (port_status & 0x0400) ? 1 : 0;
403 fc = (port_status & 0x8000) ? 1 : 0;
404
405 if (!netif_carrier_ok(dev)) {
406 printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, "
407 "flow control %sabled\n", dev->name,
408 speed, duplex ? "full" : "half",
409 fc ? "en" : "dis");
410 netif_carrier_on(dev);
411 }
412 }
413}
414
415static int mv88e6xxx_stats_wait(struct dsa_switch *ds)
416{
417 int ret;
418 int i;
419
420 for (i = 0; i < 10; i++) {
421 ret = REG_READ(REG_GLOBAL2, 0x1d);
422 if ((ret & 0x8000) == 0)
423 return 0;
424 }
425
426 return -ETIMEDOUT;
427}
428
429static int mv88e6xxx_stats_snapshot(struct dsa_switch *ds, int port)
430{
431 int ret;
432
433 /*
434 * Snapshot the hardware statistics counters for this port.
435 */
436 REG_WRITE(REG_GLOBAL, 0x1d, 0xdc00 | port);
437
438 /*
439 * Wait for the snapshotting to complete.
440 */
441 ret = mv88e6xxx_stats_wait(ds);
442 if (ret < 0)
443 return ret;
444
445 return 0;
446}
447
448static void mv88e6xxx_stats_read(struct dsa_switch *ds, int stat, u32 *val)
449{
450 u32 _val;
451 int ret;
452
453 *val = 0;
454
455 ret = mv88e6xxx_reg_write(ds, REG_GLOBAL, 0x1d, 0xcc00 | stat);
456 if (ret < 0)
457 return;
458
459 ret = mv88e6xxx_stats_wait(ds);
460 if (ret < 0)
461 return;
462
463 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1e);
464 if (ret < 0)
465 return;
466
467 _val = ret << 16;
468
469 ret = mv88e6xxx_reg_read(ds, REG_GLOBAL, 0x1f);
470 if (ret < 0)
471 return;
472
473 *val = _val | ret;
474}
475
476void mv88e6xxx_get_strings(struct dsa_switch *ds,
477 int nr_stats, struct mv88e6xxx_hw_stat *stats,
478 int port, uint8_t *data)
479{
480 int i;
481
482 for (i = 0; i < nr_stats; i++) {
483 memcpy(data + i * ETH_GSTRING_LEN,
484 stats[i].string, ETH_GSTRING_LEN);
485 }
486}
487
488void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
489 int nr_stats, struct mv88e6xxx_hw_stat *stats,
490 int port, uint64_t *data)
491{
492 struct mv88e6xxx_priv_state *ps = (void *)(ds + 1);
493 int ret;
494 int i;
495
496 mutex_lock(&ps->stats_mutex);
497
498 ret = mv88e6xxx_stats_snapshot(ds, port);
499 if (ret < 0) {
500 mutex_unlock(&ps->stats_mutex);
501 return;
502 }
503
504 /*
505 * Read each of the counters.
506 */
507 for (i = 0; i < nr_stats; i++) {
508 struct mv88e6xxx_hw_stat *s = stats + i;
509 u32 low;
510 u32 high;
511
512 mv88e6xxx_stats_read(ds, s->reg, &low);
513 if (s->sizeof_stat == 8)
514 mv88e6xxx_stats_read(ds, s->reg + 1, &high);
515 else
516 high = 0;
517
518 data[i] = (((u64)high) << 32) | low;
519 }
520
521 mutex_unlock(&ps->stats_mutex);
522}
diff --git a/net/dsa/mv88e6xxx.h b/net/dsa/mv88e6xxx.h
new file mode 100644
index 000000000000..eb0e0aaa9f1b
--- /dev/null
+++ b/net/dsa/mv88e6xxx.h
@@ -0,0 +1,93 @@
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#ifdef CONFIG_NET_DSA_MV88E6XXX_NEED_PPU
27 /*
28 * Handles automatic disabling and re-enabling of the PHY
29 * polling unit.
30 */
31 struct mutex ppu_mutex;
32 int ppu_disabled;
33 struct work_struct ppu_work;
34 struct timer_list ppu_timer;
35#endif
36
37 /*
38 * This mutex serialises access to the statistics unit.
39 * Hold this mutex over snapshot + dump sequences.
40 */
41 struct mutex stats_mutex;
42};
43
44struct mv88e6xxx_hw_stat {
45 char string[ETH_GSTRING_LEN];
46 int sizeof_stat;
47 int reg;
48};
49
50int __mv88e6xxx_reg_read(struct mii_bus *bus, int sw_addr, int addr, int reg);
51int mv88e6xxx_reg_read(struct dsa_switch *ds, int addr, int reg);
52int __mv88e6xxx_reg_write(struct mii_bus *bus, int sw_addr, int addr,
53 int reg, u16 val);
54int mv88e6xxx_reg_write(struct dsa_switch *ds, int addr, int reg, u16 val);
55int mv88e6xxx_config_prio(struct dsa_switch *ds);
56int mv88e6xxx_set_addr_direct(struct dsa_switch *ds, u8 *addr);
57int mv88e6xxx_set_addr_indirect(struct dsa_switch *ds, u8 *addr);
58int mv88e6xxx_phy_read(struct dsa_switch *ds, int addr, int regnum);
59int mv88e6xxx_phy_write(struct dsa_switch *ds, int addr, int regnum, u16 val);
60void mv88e6xxx_ppu_state_init(struct dsa_switch *ds);
61int mv88e6xxx_phy_read_ppu(struct dsa_switch *ds, int addr, int regnum);
62int mv88e6xxx_phy_write_ppu(struct dsa_switch *ds, int addr,
63 int regnum, u16 val);
64void mv88e6xxx_poll_link(struct dsa_switch *ds);
65void mv88e6xxx_get_strings(struct dsa_switch *ds,
66 int nr_stats, struct mv88e6xxx_hw_stat *stats,
67 int port, uint8_t *data);
68void mv88e6xxx_get_ethtool_stats(struct dsa_switch *ds,
69 int nr_stats, struct mv88e6xxx_hw_stat *stats,
70 int port, uint64_t *data);
71
72#define REG_READ(addr, reg) \
73 ({ \
74 int __ret; \
75 \
76 __ret = mv88e6xxx_reg_read(ds, addr, reg); \
77 if (__ret < 0) \
78 return __ret; \
79 __ret; \
80 })
81
82#define REG_WRITE(addr, reg, val) \
83 ({ \
84 int __ret; \
85 \
86 __ret = mv88e6xxx_reg_write(ds, addr, reg, val); \
87 if (__ret < 0) \
88 return __ret; \
89 })
90
91
92
93#endif
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
new file mode 100644
index 000000000000..37616884b8a9
--- /dev/null
+++ b/net/dsa/slave.c
@@ -0,0 +1,298 @@
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_DSA
243 case htons(ETH_P_DSA):
244 slave_dev->hard_start_xmit = dsa_xmit;
245 break;
246#endif
247#ifdef CONFIG_NET_DSA_TAG_EDSA
248 case htons(ETH_P_EDSA):
249 slave_dev->hard_start_xmit = edsa_xmit;
250 break;
251#endif
252#ifdef CONFIG_NET_DSA_TAG_TRAILER
253 case htons(ETH_P_TRAILER):
254 slave_dev->hard_start_xmit = trailer_xmit;
255 break;
256#endif
257 default:
258 BUG();
259 }
260 slave_dev->open = dsa_slave_open;
261 slave_dev->stop = dsa_slave_close;
262 slave_dev->change_rx_flags = dsa_slave_change_rx_flags;
263 slave_dev->set_rx_mode = dsa_slave_set_rx_mode;
264 slave_dev->set_multicast_list = dsa_slave_set_rx_mode;
265 slave_dev->set_mac_address = dsa_slave_set_mac_address;
266 slave_dev->do_ioctl = dsa_slave_ioctl;
267 SET_NETDEV_DEV(slave_dev, parent);
268 slave_dev->vlan_features = master->vlan_features;
269
270 p = netdev_priv(slave_dev);
271 p->dev = slave_dev;
272 p->parent = ds;
273 p->port = port;
274 p->phy = ds->slave_mii_bus->phy_map[port];
275
276 ret = register_netdev(slave_dev);
277 if (ret) {
278 printk(KERN_ERR "%s: error %d registering interface %s\n",
279 master->name, ret, slave_dev->name);
280 free_netdev(slave_dev);
281 return NULL;
282 }
283
284 netif_carrier_off(slave_dev);
285
286 if (p->phy != NULL) {
287 phy_attach(slave_dev, p->phy->dev.bus_id,
288 0, PHY_INTERFACE_MODE_GMII);
289
290 p->phy->autoneg = AUTONEG_ENABLE;
291 p->phy->speed = 0;
292 p->phy->duplex = 0;
293 p->phy->advertising = p->phy->supported | ADVERTISED_Autoneg;
294 phy_start_aneg(p->phy);
295 }
296
297 return slave_dev;
298}
diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c
new file mode 100644
index 000000000000..bdc0510b53b7
--- /dev/null
+++ b/net/dsa/tag_dsa.c
@@ -0,0 +1,194 @@
1/*
2 * net/dsa/tag_dsa.c - (Non-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
18int dsa_xmit(struct sk_buff *skb, struct net_device *dev)
19{
20 struct dsa_slave_priv *p = netdev_priv(dev);
21 u8 *dsa_header;
22
23 dev->stats.tx_packets++;
24 dev->stats.tx_bytes += skb->len;
25
26 /*
27 * Convert the outermost 802.1q tag to a DSA tag for tagged
28 * packets, or insert a DSA tag between the addresses and
29 * the ethertype field for untagged packets.
30 */
31 if (skb->protocol == htons(ETH_P_8021Q)) {
32 if (skb_cow_head(skb, 0) < 0)
33 goto out_free;
34
35 /*
36 * Construct tagged FROM_CPU DSA tag from 802.1q tag.
37 */
38 dsa_header = skb->data + 2 * ETH_ALEN;
39 dsa_header[0] = 0x60;
40 dsa_header[1] = p->port << 3;
41
42 /*
43 * Move CFI field from byte 2 to byte 1.
44 */
45 if (dsa_header[2] & 0x10) {
46 dsa_header[1] |= 0x01;
47 dsa_header[2] &= ~0x10;
48 }
49 } else {
50 if (skb_cow_head(skb, DSA_HLEN) < 0)
51 goto out_free;
52 skb_push(skb, DSA_HLEN);
53
54 memmove(skb->data, skb->data + DSA_HLEN, 2 * ETH_ALEN);
55
56 /*
57 * Construct untagged FROM_CPU DSA tag.
58 */
59 dsa_header = skb->data + 2 * ETH_ALEN;
60 dsa_header[0] = 0x40;
61 dsa_header[1] = p->port << 3;
62 dsa_header[2] = 0x00;
63 dsa_header[3] = 0x00;
64 }
65
66 skb->protocol = htons(ETH_P_DSA);
67
68 skb->dev = p->parent->master_netdev;
69 dev_queue_xmit(skb);
70
71 return NETDEV_TX_OK;
72
73out_free:
74 kfree_skb(skb);
75 return NETDEV_TX_OK;
76}
77
78static int dsa_rcv(struct sk_buff *skb, struct net_device *dev,
79 struct packet_type *pt, struct net_device *orig_dev)
80{
81 struct dsa_switch *ds = dev->dsa_ptr;
82 u8 *dsa_header;
83 int source_port;
84
85 if (unlikely(ds == NULL))
86 goto out_drop;
87
88 skb = skb_unshare(skb, GFP_ATOMIC);
89 if (skb == NULL)
90 goto out;
91
92 if (unlikely(!pskb_may_pull(skb, DSA_HLEN)))
93 goto out_drop;
94
95 /*
96 * The ethertype field is part of the DSA header.
97 */
98 dsa_header = skb->data - 2;
99
100 /*
101 * Check that frame type is either TO_CPU or FORWARD, and
102 * that the source device is zero.
103 */
104 if ((dsa_header[0] & 0xdf) != 0x00 && (dsa_header[0] & 0xdf) != 0xc0)
105 goto out_drop;
106
107 /*
108 * Check that the source port is a registered DSA port.
109 */
110 source_port = (dsa_header[1] >> 3) & 0x1f;
111 if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
112 goto out_drop;
113
114 /*
115 * Convert the DSA header to an 802.1q header if the 'tagged'
116 * bit in the DSA header is set. If the 'tagged' bit is clear,
117 * delete the DSA header entirely.
118 */
119 if (dsa_header[0] & 0x20) {
120 u8 new_header[4];
121
122 /*
123 * Insert 802.1q ethertype and copy the VLAN-related
124 * fields, but clear the bit that will hold CFI (since
125 * DSA uses that bit location for another purpose).
126 */
127 new_header[0] = (ETH_P_8021Q >> 8) & 0xff;
128 new_header[1] = ETH_P_8021Q & 0xff;
129 new_header[2] = dsa_header[2] & ~0x10;
130 new_header[3] = dsa_header[3];
131
132 /*
133 * Move CFI bit from its place in the DSA header to
134 * its 802.1q-designated place.
135 */
136 if (dsa_header[1] & 0x01)
137 new_header[2] |= 0x10;
138
139 /*
140 * Update packet checksum if skb is CHECKSUM_COMPLETE.
141 */
142 if (skb->ip_summed == CHECKSUM_COMPLETE) {
143 __wsum c = skb->csum;
144 c = csum_add(c, csum_partial(new_header + 2, 2, 0));
145 c = csum_sub(c, csum_partial(dsa_header + 2, 2, 0));
146 skb->csum = c;
147 }
148
149 memcpy(dsa_header, new_header, DSA_HLEN);
150 } else {
151 /*
152 * Remove DSA tag and update checksum.
153 */
154 skb_pull_rcsum(skb, DSA_HLEN);
155 memmove(skb->data - ETH_HLEN,
156 skb->data - ETH_HLEN - DSA_HLEN,
157 2 * ETH_ALEN);
158 }
159
160 skb->dev = ds->ports[source_port];
161 skb_push(skb, ETH_HLEN);
162 skb->protocol = eth_type_trans(skb, skb->dev);
163
164 skb->dev->last_rx = jiffies;
165 skb->dev->stats.rx_packets++;
166 skb->dev->stats.rx_bytes += skb->len;
167
168 netif_receive_skb(skb);
169
170 return 0;
171
172out_drop:
173 kfree_skb(skb);
174out:
175 return 0;
176}
177
178static struct packet_type dsa_packet_type = {
179 .type = __constant_htons(ETH_P_DSA),
180 .func = dsa_rcv,
181};
182
183static int __init dsa_init_module(void)
184{
185 dev_add_pack(&dsa_packet_type);
186 return 0;
187}
188module_init(dsa_init_module);
189
190static void __exit dsa_cleanup_module(void)
191{
192 dev_remove_pack(&dsa_packet_type);
193}
194module_exit(dsa_cleanup_module);
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);
diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c
new file mode 100644
index 000000000000..d3117764b2c2
--- /dev/null
+++ b/net/dsa/tag_trailer.c
@@ -0,0 +1,130 @@
1/*
2 * net/dsa/tag_trailer.c - Trailer tag format 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/etherdevice.h>
12#include <linux/list.h>
13#include <linux/netdevice.h>
14#include "dsa_priv.h"
15
16int trailer_xmit(struct sk_buff *skb, struct net_device *dev)
17{
18 struct dsa_slave_priv *p = netdev_priv(dev);
19 struct sk_buff *nskb;
20 int padlen;
21 u8 *trailer;
22
23 dev->stats.tx_packets++;
24 dev->stats.tx_bytes += skb->len;
25
26 /*
27 * We have to make sure that the trailer ends up as the very
28 * last 4 bytes of the packet. This means that we have to pad
29 * the packet to the minimum ethernet frame size, if necessary,
30 * before adding the trailer.
31 */
32 padlen = 0;
33 if (skb->len < 60)
34 padlen = 60 - skb->len;
35
36 nskb = alloc_skb(NET_IP_ALIGN + skb->len + padlen + 4, GFP_ATOMIC);
37 if (nskb == NULL) {
38 kfree_skb(skb);
39 return NETDEV_TX_OK;
40 }
41 skb_reserve(nskb, NET_IP_ALIGN);
42
43 skb_reset_mac_header(nskb);
44 skb_set_network_header(nskb, skb_network_header(skb) - skb->head);
45 skb_set_transport_header(nskb, skb_transport_header(skb) - skb->head);
46 skb_copy_and_csum_dev(skb, skb_put(nskb, skb->len));
47 kfree_skb(skb);
48
49 if (padlen) {
50 u8 *pad = skb_put(nskb, padlen);
51 memset(pad, 0, padlen);
52 }
53
54 trailer = skb_put(nskb, 4);
55 trailer[0] = 0x80;
56 trailer[1] = 1 << p->port;
57 trailer[2] = 0x10;
58 trailer[3] = 0x00;
59
60 nskb->protocol = htons(ETH_P_TRAILER);
61
62 nskb->dev = p->parent->master_netdev;
63 dev_queue_xmit(nskb);
64
65 return NETDEV_TX_OK;
66}
67
68static int trailer_rcv(struct sk_buff *skb, struct net_device *dev,
69 struct packet_type *pt, struct net_device *orig_dev)
70{
71 struct dsa_switch *ds = dev->dsa_ptr;
72 u8 *trailer;
73 int source_port;
74
75 if (unlikely(ds == NULL))
76 goto out_drop;
77
78 skb = skb_unshare(skb, GFP_ATOMIC);
79 if (skb == NULL)
80 goto out;
81
82 if (skb_linearize(skb))
83 goto out_drop;
84
85 trailer = skb_tail_pointer(skb) - 4;
86 if (trailer[0] != 0x80 || (trailer[1] & 0xf8) != 0x00 ||
87 (trailer[3] & 0xef) != 0x00 || trailer[3] != 0x00)
88 goto out_drop;
89
90 source_port = trailer[1] & 7;
91 if (source_port >= DSA_MAX_PORTS || ds->ports[source_port] == NULL)
92 goto out_drop;
93
94 pskb_trim_rcsum(skb, skb->len - 4);
95
96 skb->dev = ds->ports[source_port];
97 skb_push(skb, ETH_HLEN);
98 skb->protocol = eth_type_trans(skb, skb->dev);
99
100 skb->dev->last_rx = jiffies;
101 skb->dev->stats.rx_packets++;
102 skb->dev->stats.rx_bytes += skb->len;
103
104 netif_receive_skb(skb);
105
106 return 0;
107
108out_drop:
109 kfree_skb(skb);
110out:
111 return 0;
112}
113
114static struct packet_type trailer_packet_type = {
115 .type = __constant_htons(ETH_P_TRAILER),
116 .func = trailer_rcv,
117};
118
119static int __init trailer_init_module(void)
120{
121 dev_add_pack(&trailer_packet_type);
122 return 0;
123}
124module_init(trailer_init_module);
125
126static void __exit trailer_cleanup_module(void)
127{
128 dev_remove_pack(&trailer_packet_type);
129}
130module_exit(trailer_cleanup_module);