source: src/linux/ar531x/linux-2.6.23/drivers/net/phy/mvswitch.c @ 10868

Last change on this file since 10868 was 10868, checked in by BrainSlayer, 5 years ago

fix marvell switch performance problem (affects fonera 2200 and dir 400) thx nbd

File size: 10.1 KB
Line 
1/*
2 * Marvell 88E6060 switch driver
3 * Copyright (c) 2008 Felix Fietkau <nbd@openwrt.org>
4 *
5 * This program is free software; you can redistribute  it and/or modify it
6 * under  the terms of the GNU General Public License v2 as published by the
7 * Free Software Foundation
8 */
9#include <linux/kernel.h>
10#include <linux/string.h>
11#include <linux/errno.h>
12#include <linux/unistd.h>
13#include <linux/slab.h>
14#include <linux/interrupt.h>
15#include <linux/init.h>
16#include <linux/delay.h>
17#include <linux/netdevice.h>
18#include <linux/etherdevice.h>
19#include <linux/skbuff.h>
20#include <linux/spinlock.h>
21#include <linux/mm.h>
22#include <linux/module.h>
23#include <linux/mii.h>
24#include <linux/ethtool.h>
25#include <linux/phy.h>
26#include <linux/if_vlan.h>
27
28#include <asm/io.h>
29#include <asm/irq.h>
30#include <asm/uaccess.h>
31#include "mvswitch.h"
32
33/* Undefine this to use trailer mode instead.
34 * I don't know if header mode works with all chips */
35#define HEADER_MODE     1
36
37MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
38MODULE_AUTHOR("Felix Fietkau");
39MODULE_LICENSE("GPL");
40
41struct mvswitch_priv {
42        /* the driver's tx function */
43        int (*hardstart)(struct sk_buff *skb, struct net_device *dev);
44        struct vlan_group *grp;
45        u8 vlans[16];
46};
47
48#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
49
50static inline u16
51r16(struct phy_device *phydev, int addr, int reg)
52{
53        return phydev->bus->read(phydev->bus, addr, reg);
54}
55
56static inline void
57w16(struct phy_device *phydev, int addr, int reg, u16 val)
58{
59        phydev->bus->write(phydev->bus, addr, reg, val);
60}
61
62
63static int
64mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev)
65{
66        struct mvswitch_priv *priv;
67        char *buf = NULL;
68        u16 vid;
69
70        priv = dev->phy_ptr;
71        if (unlikely(!priv))
72                goto error;
73
74        if (unlikely(skb->len < 16))
75                goto error;
76
77#ifdef HEADER_MODE
78        if (__vlan_hwaccel_get_tag(skb, &vid))
79                goto error;
80
81        if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
82                if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
83                        goto error_expand;
84                if (skb->len < 62)
85                        skb->len = 62;
86        }
87        buf = skb_push(skb, MV_HEADER_SIZE);
88#else
89        if (__vlan_get_tag(skb, &vid))
90                goto error;
91
92        if (unlikely((vid > 15 || !priv->vlans[vid])))
93                goto error;
94
95        if (skb->len <= 64) {
96                if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
97                        goto error_expand;
98
99                buf = skb->data + 64;
100                skb->len = 64 + MV_TRAILER_SIZE;
101        } else {
102                if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
103                        if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
104                                goto error_expand;
105                }
106                buf = skb_put(skb, 4);
107        }
108
109        /* move the ethernet header 4 bytes forward, overwriting the vlan tag */
110        memmove(skb->data + 4, skb->data, 12);
111        skb->data += 4;
112        skb->len -= 4;
113        skb->mac_header += 4;
114#endif
115
116        if (!buf)
117                goto error;
118
119
120#ifdef HEADER_MODE
121        /* prepend the tag */
122        *((__be16 *) buf) = cpu_to_be16(
123                ((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
124                ((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
125        );
126#else
127        /* append the tag */
128        *((__be32 *) buf) = cpu_to_be32((
129                (MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
130                ((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
131        ));
132#endif
133
134        return priv->hardstart(skb, dev);
135
136error_expand:
137        if (net_ratelimit())
138                printk("%s: failed to expand/update skb for the switch\n", dev->name);
139
140error:
141        /* any errors? drop the packet! */
142        dev_kfree_skb_any(skb);
143        return 0;
144}
145
146static int
147mvswitch_mangle_rx(struct sk_buff *skb, int napi)
148{
149        struct mvswitch_priv *priv;
150        struct net_device *dev;
151        int vlan = -1;
152        unsigned char *buf;
153        int i;
154
155        dev = skb->dev;
156        if (!dev)
157                goto error;
158
159        priv = dev->phy_ptr;
160        if (!priv)
161                goto error;
162
163        if (!priv->grp)
164                goto error;
165
166#ifdef HEADER_MODE
167        buf = skb->data;
168        skb_pull(skb, MV_HEADER_SIZE);
169#else
170        buf = skb->data + skb->len - MV_TRAILER_SIZE;
171        if (buf[0] != 0x80)
172                goto error;
173#endif
174
175        /* look for the vlan matching the incoming port */
176        for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
177                if ((1 << buf[1]) & priv->vlans[i])
178                        vlan = i;
179        }
180
181        if (vlan == -1)
182                goto error;
183
184        skb->protocol = eth_type_trans(skb, skb->dev);
185
186        if (napi)
187                return vlan_hwaccel_receive_skb(skb, priv->grp, vlan);
188        else
189                return vlan_hwaccel_rx(skb, priv->grp, vlan);
190
191error:
192        /* no vlan? eat the packet! */
193        dev_kfree_skb_any(skb);
194        return 0;
195}
196
197
198static int
199mvswitch_netif_rx(struct sk_buff *skb)
200{
201        return mvswitch_mangle_rx(skb, 0);
202}
203
204static int
205mvswitch_netif_receive_skb(struct sk_buff *skb)
206{
207        return mvswitch_mangle_rx(skb, 1);
208}
209
210
211static void
212mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
213{
214        struct mvswitch_priv *priv = dev->phy_ptr;
215        priv->grp = grp;
216}
217
218
219static int
220mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
221{
222        int i = 100;
223        u16 r;
224
225        do {
226                r = r16(pdev, addr, reg) & mask;
227                if (r == val)
228                        return 0;
229        } while(--i > 0);
230        return -ETIMEDOUT;
231}
232
233static int
234mvswitch_config_init(struct phy_device *pdev)
235{
236        struct mvswitch_priv *priv = to_mvsw(pdev);
237        struct net_device *dev = pdev->attached_dev;
238        u8 vlmap = 0;
239        int i;
240
241        if (!dev)
242                return -EINVAL;
243
244        printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
245        pdev->supported = ADVERTISED_100baseT_Full;
246        pdev->advertising = ADVERTISED_100baseT_Full;
247        dev->phy_ptr = priv;
248        dev->irq = PHY_POLL;
249
250        /* initialize default vlans */
251        for (i = 0; i < MV_PORTS; i++)
252                priv->vlans[(i == MV_WANPORT ? 1 : 0)] |= (1 << i);
253
254        /* before entering reset, disable all ports */
255        for (i = 0; i < MV_PORTS; i++)
256                w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
257
258        msleep(2); /* wait for the status change to settle in */
259
260        /* put the ATU in reset */
261        w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
262
263        i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
264        if (i < 0) {
265                printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
266                return i;
267        }
268
269        /* set the ATU flags */
270        w16(pdev, MV_SWITCHREG(ATU_CTRL),
271                MV_ATUCTL_NO_LEARN |
272                MV_ATUCTL_ATU_1K |
273                MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
274        );
275
276        /* initialize the cpu port */
277        w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
278#ifdef HEADER_MODE
279                MV_PORTCTRL_HEADER |
280#else
281                MV_PORTCTRL_RXTR |
282                MV_PORTCTRL_TXTR |
283#endif
284                MV_PORTCTRL_ENABLED
285        );
286        /* wait for the phy change to settle in */
287        msleep(2);
288        for (i = 0; i < MV_PORTS; i++) {
289                u8 pvid = 0;
290                int j;
291
292                vlmap = 0;
293
294                /* look for the matching vlan */
295                for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
296                        if (priv->vlans[j] & (1 << i)) {
297                                vlmap = priv->vlans[j];
298                                pvid = j;
299                        }
300                }
301                /* leave port unconfigured if it's not part of a vlan */
302                if (!vlmap)
303                        continue;
304
305                /* add the cpu port to the allowed destinations list */
306                vlmap |= (1 << MV_CPUPORT);
307
308                /* take port out of its own vlan destination map */
309                vlmap &= ~(1 << i);
310
311                /* apply vlan settings */
312                w16(pdev, MV_PORTREG(VLANMAP, i),
313                        MV_PORTVLAN_PORTS(vlmap) |
314                        MV_PORTVLAN_ID(i)
315                );
316
317                /* re-enable port */
318                w16(pdev, MV_PORTREG(CONTROL, i),
319                        MV_PORTCTRL_ENABLED
320                );
321        }
322
323        w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
324                MV_PORTVLAN_ID(MV_CPUPORT)
325        );
326
327        /* set the port association vector */
328        for (i = 0; i <= MV_PORTS; i++) {
329                w16(pdev, MV_PORTREG(ASSOC, i),
330                        MV_PORTASSOC_PORTS(1 << i)
331                );
332        }
333
334        /* init switch control */
335        w16(pdev, MV_SWITCHREG(CTRL),
336                MV_SWITCHCTL_MSIZE |
337                MV_SWITCHCTL_DROP
338        );
339
340        /* hook into the tx function */
341        pdev->pkt_align = 2;
342        priv->hardstart = dev->hard_start_xmit;
343        pdev->netif_receive_skb = mvswitch_netif_receive_skb;
344        pdev->netif_rx = mvswitch_netif_rx;
345        dev->hard_start_xmit = mvswitch_mangle_tx;
346        dev->vlan_rx_register = mvswitch_vlan_rx_register;
347#ifdef HEADER_MODE
348        dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_TX;
349#else
350        dev->features |= NETIF_F_HW_VLAN_RX;
351#endif
352
353        return 0;
354}
355
356static int
357mvswitch_read_status(struct phy_device *pdev)
358{
359        pdev->speed = SPEED_100;
360        pdev->duplex = DUPLEX_FULL;
361        pdev->state = PHY_UP;
362
363        /* XXX ugly workaround: we can't force the switch
364         * to gracefully handle hosts moving from one port to another,
365         * so we have to regularly clear the ATU database */
366
367        /* wait for the ATU to become available */
368        mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
369
370        /* flush the ATU */
371        w16(pdev, MV_SWITCHREG(ATU_OP),
372                MV_ATUOP_INPROGRESS |
373                MV_ATUOP_FLUSH_ALL
374        );
375
376        /* wait for operation to complete */
377        mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
378
379        return 0;
380}
381
382static int
383mvswitch_config_aneg(struct phy_device *phydev)
384{
385        return 0;
386}
387
388static void
389mvswitch_remove(struct phy_device *pdev)
390{
391        struct mvswitch_priv *priv = to_mvsw(pdev);
392        struct net_device *dev = pdev->attached_dev;
393
394        /* restore old xmit handler */
395        if (priv->hardstart && dev)
396                dev->hard_start_xmit = priv->hardstart;
397        dev->vlan_rx_register = NULL;
398        dev->vlan_rx_kill_vid = NULL;
399        dev->phy_ptr = NULL;
400        dev->features &= ~NETIF_F_HW_VLAN_RX;
401        kfree(priv);
402}
403
404static bool
405mvswitch_detect(struct mii_bus *bus, int addr)
406{
407        u16 reg;
408        int i;
409
410        /* we attach to phy id 31 to make sure that the late probe works */
411        if (addr != 31)
412                return false;
413
414        /* look for the switch on the bus */
415        reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
416        if (reg != MV_IDENT_VALUE)
417                return false;
418
419        /*
420         * Now that we've established that the switch actually exists, let's
421         * get rid of the competition :)
422         */
423        for (i = 0; i < 31; i++) {
424                if (!bus->phy_map[i])
425                        continue;
426
427                device_unregister(&bus->phy_map[i]->dev);
428                kfree(bus->phy_map[i]);
429                bus->phy_map[i] = NULL;
430        }
431
432        return true;
433}
434
435static int
436mvswitch_probe(struct phy_device *pdev)
437{
438        struct mvswitch_priv *priv;
439
440        priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
441        if (priv == NULL)
442                return -ENOMEM;
443
444        pdev->priv = priv;
445
446        return 0;
447}
448
449
450static struct phy_driver mvswitch_driver = {
451        .name           = "Marvell 88E6060",
452        .features       = PHY_BASIC_FEATURES,
453        .detect         = &mvswitch_detect,
454        .probe          = &mvswitch_probe,
455        .remove         = &mvswitch_remove,
456        .config_init    = &mvswitch_config_init,
457        .config_aneg    = &mvswitch_config_aneg,
458        .read_status    = &mvswitch_read_status,
459        .driver         = { .owner = THIS_MODULE,},
460};
461
462static int __init
463mvswitch_init(void)
464{
465        return phy_driver_register(&mvswitch_driver);
466}
467
468static void __exit
469mvswitch_exit(void)
470{
471        phy_driver_unregister(&mvswitch_driver);
472}
473
474module_init(mvswitch_init);
475module_exit(mvswitch_exit);
Note: See TracBrowser for help on using the repository browser.