root/src/linux/ar531x/linux-2.6.23/drivers/net/phy/mvswitch.c

Revision 12398, 11.2 kB (checked in by BrainSlayer, 5 months ago)

ar8216 switch support

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