2 * Marvell 88E6060 switch driver
3 * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
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
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>
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 #include <linux/version.h>
31 #include <asm/uaccess.h>
34 /* Undefine this to use trailer mode instead.
35 * I don't know if header mode works with all chips */
38 MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
39 MODULE_AUTHOR("Felix Fietkau");
40 MODULE_LICENSE("GPL");
42 #define MVSWITCH_MAGIC 0x88E6060
44 struct mvswitch_priv
{
45 netdev_features_t orig_features
;
49 #define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
52 r16(struct phy_device
*phydev
, int addr
, int reg
)
54 struct mii_bus
*bus
= phydev
->mdio
.bus
;
56 return bus
->read(bus
, addr
, reg
);
60 w16(struct phy_device
*phydev
, int addr
, int reg
, u16 val
)
62 struct mii_bus
*bus
= phydev
->mdio
.bus
;
64 bus
->write(bus
, addr
, reg
, val
);
68 static struct sk_buff
*
69 mvswitch_mangle_tx(struct net_device
*dev
, struct sk_buff
*skb
)
71 struct mvswitch_priv
*priv
;
79 if (unlikely(skb
->len
< 16))
83 if (__vlan_hwaccel_get_tag(skb
, &vid
))
86 if (skb_cloned(skb
) || (skb
->len
<= 62) || (skb_headroom(skb
) < MV_HEADER_SIZE
)) {
87 if (pskb_expand_head(skb
, MV_HEADER_SIZE
, (skb
->len
< 62 ? 62 - skb
->len
: 0), GFP_ATOMIC
))
92 buf
= skb_push(skb
, MV_HEADER_SIZE
);
94 if (__vlan_get_tag(skb
, &vid
))
97 if (unlikely((vid
> 15 || !priv
->vlans
[vid
])))
100 if (skb
->len
<= 64) {
101 if (pskb_expand_head(skb
, 0, 64 + MV_TRAILER_SIZE
- skb
->len
, GFP_ATOMIC
))
104 buf
= skb
->data
+ 64;
105 skb
->len
= 64 + MV_TRAILER_SIZE
;
107 if (skb_cloned(skb
) || unlikely(skb_tailroom(skb
) < 4)) {
108 if (pskb_expand_head(skb
, 0, 4, GFP_ATOMIC
))
111 buf
= skb_put(skb
, 4);
114 /* move the ethernet header 4 bytes forward, overwriting the vlan tag */
115 memmove(skb
->data
+ 4, skb
->data
, 12);
118 skb
->mac_header
+= 4;
126 /* prepend the tag */
127 *((__be16
*) buf
) = cpu_to_be16(
128 ((vid
<< MV_HEADER_VLAN_S
) & MV_HEADER_VLAN_M
) |
129 ((priv
->vlans
[vid
] << MV_HEADER_PORTS_S
) & MV_HEADER_PORTS_M
)
133 *((__be32
*) buf
) = cpu_to_be32((
134 (MV_TRAILER_OVERRIDE
<< MV_TRAILER_FLAGS_S
) |
135 ((priv
->vlans
[vid
] & MV_TRAILER_PORTS_M
) << MV_TRAILER_PORTS_S
)
143 printk("%s: failed to expand/update skb for the switch\n", dev
->name
);
146 /* any errors? drop the packet! */
147 dev_kfree_skb_any(skb
);
152 mvswitch_mangle_rx(struct net_device
*dev
, struct sk_buff
*skb
)
154 struct mvswitch_priv
*priv
;
160 if (WARN_ON_ONCE(!priv
))
165 skb_pull(skb
, MV_HEADER_SIZE
);
167 buf
= skb
->data
+ skb
->len
- MV_TRAILER_SIZE
;
172 /* look for the vlan matching the incoming port */
173 for (i
= 0; i
< ARRAY_SIZE(priv
->vlans
); i
++) {
174 if ((1 << buf
[1]) & priv
->vlans
[i
])
181 __vlan_hwaccel_put_tag(skb
, htons(ETH_P_8021Q
), vlan
);
186 mvswitch_wait_mask(struct phy_device
*pdev
, int addr
, int reg
, u16 mask
, u16 val
)
192 r
= r16(pdev
, addr
, reg
) & mask
;
200 mvswitch_config_init(struct phy_device
*pdev
)
202 struct mvswitch_priv
*priv
= to_mvsw(pdev
);
203 struct net_device
*dev
= pdev
->attached_dev
;
210 printk("%s: Marvell 88E6060 PHY driver attached.\n", dev
->name
);
211 linkmode_zero(pdev
->supported
);
212 linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT
, pdev
->supported
);
213 linkmode_copy(pdev
->advertising
, pdev
->supported
);
215 pdev
->irq
= PHY_POLL
;
217 dev
->flags
|= IFF_PROMISC
;
220 /* initialize default vlans */
221 for (i
= 0; i
< MV_PORTS
; i
++)
222 priv
->vlans
[(i
== MV_WANPORT
? 2 : 1)] |= (1 << i
);
224 /* before entering reset, disable all ports */
225 for (i
= 0; i
< MV_PORTS
; i
++)
226 w16(pdev
, MV_PORTREG(CONTROL
, i
), 0x00);
228 msleep(2); /* wait for the status change to settle in */
230 /* put the ATU in reset */
231 w16(pdev
, MV_SWITCHREG(ATU_CTRL
), MV_ATUCTL_RESET
);
233 i
= mvswitch_wait_mask(pdev
, MV_SWITCHREG(ATU_CTRL
), MV_ATUCTL_RESET
, 0);
235 printk("%s: Timeout waiting for the switch to reset.\n", dev
->name
);
239 /* set the ATU flags */
240 w16(pdev
, MV_SWITCHREG(ATU_CTRL
),
243 MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN
) /* minimum without disabling ageing */
246 /* initialize the cpu port */
247 w16(pdev
, MV_PORTREG(CONTROL
, MV_CPUPORT
),
256 /* wait for the phy change to settle in */
258 for (i
= 0; i
< MV_PORTS
; i
++) {
264 /* look for the matching vlan */
265 for (j
= 0; j
< ARRAY_SIZE(priv
->vlans
); j
++) {
266 if (priv
->vlans
[j
] & (1 << i
)) {
267 vlmap
= priv
->vlans
[j
];
271 /* leave port unconfigured if it's not part of a vlan */
275 /* add the cpu port to the allowed destinations list */
276 vlmap
|= (1 << MV_CPUPORT
);
278 /* take port out of its own vlan destination map */
281 /* apply vlan settings */
282 w16(pdev
, MV_PORTREG(VLANMAP
, i
),
283 MV_PORTVLAN_PORTS(vlmap
) |
288 w16(pdev
, MV_PORTREG(CONTROL
, i
),
293 w16(pdev
, MV_PORTREG(VLANMAP
, MV_CPUPORT
),
294 MV_PORTVLAN_ID(MV_CPUPORT
)
297 /* set the port association vector */
298 for (i
= 0; i
<= MV_PORTS
; i
++) {
299 w16(pdev
, MV_PORTREG(ASSOC
, i
),
300 MV_PORTASSOC_PORTS(1 << i
)
304 /* init switch control */
305 w16(pdev
, MV_SWITCHREG(CTRL
),
310 dev
->eth_mangle_rx
= mvswitch_mangle_rx
;
311 dev
->eth_mangle_tx
= mvswitch_mangle_tx
;
312 priv
->orig_features
= dev
->features
;
315 dev
->priv_flags
|= IFF_NO_IP_ALIGN
;
316 dev
->features
|= NETIF_F_HW_VLAN_CTAG_RX
| NETIF_F_HW_VLAN_CTAG_TX
;
318 dev
->features
|= NETIF_F_HW_VLAN_CTAG_RX
;
325 mvswitch_read_status(struct phy_device
*pdev
)
327 pdev
->speed
= SPEED_100
;
328 pdev
->duplex
= DUPLEX_FULL
;
331 /* XXX ugly workaround: we can't force the switch
332 * to gracefully handle hosts moving from one port to another,
333 * so we have to regularly clear the ATU database */
335 /* wait for the ATU to become available */
336 mvswitch_wait_mask(pdev
, MV_SWITCHREG(ATU_OP
), MV_ATUOP_INPROGRESS
, 0);
339 w16(pdev
, MV_SWITCHREG(ATU_OP
),
340 MV_ATUOP_INPROGRESS
|
344 /* wait for operation to complete */
345 mvswitch_wait_mask(pdev
, MV_SWITCHREG(ATU_OP
), MV_ATUOP_INPROGRESS
, 0);
351 mvswitch_aneg_done(struct phy_device
*phydev
)
353 return 1; /* Return any positive value */
357 mvswitch_config_aneg(struct phy_device
*phydev
)
363 mvswitch_detach(struct phy_device
*pdev
)
365 struct mvswitch_priv
*priv
= to_mvsw(pdev
);
366 struct net_device
*dev
= pdev
->attached_dev
;
372 dev
->eth_mangle_rx
= NULL
;
373 dev
->eth_mangle_tx
= NULL
;
374 dev
->features
= priv
->orig_features
;
375 dev
->priv_flags
&= ~IFF_NO_IP_ALIGN
;
379 mvswitch_remove(struct phy_device
*pdev
)
381 struct mvswitch_priv
*priv
= to_mvsw(pdev
);
387 mvswitch_probe(struct phy_device
*pdev
)
389 struct mvswitch_priv
*priv
;
391 priv
= kzalloc(sizeof(struct mvswitch_priv
), GFP_KERNEL
);
401 mvswitch_fixup(struct phy_device
*dev
)
403 struct mii_bus
*bus
= dev
->mdio
.bus
;
406 if (dev
->mdio
.addr
!= 0x10)
409 reg
= bus
->read(bus
, MV_PORTREG(IDENT
, 0)) & MV_IDENT_MASK
;
410 if (reg
!= MV_IDENT_VALUE
)
413 dev
->phy_id
= MVSWITCH_MAGIC
;
418 static struct phy_driver mvswitch_driver
= {
419 .name
= "Marvell 88E6060",
420 .phy_id
= MVSWITCH_MAGIC
,
421 .phy_id_mask
= 0xffffffff,
422 .features
= PHY_BASIC_FEATURES
,
423 .probe
= &mvswitch_probe
,
424 .remove
= &mvswitch_remove
,
425 .detach
= &mvswitch_detach
,
426 .config_init
= &mvswitch_config_init
,
427 .config_aneg
= &mvswitch_config_aneg
,
428 .aneg_done
= &mvswitch_aneg_done
,
429 .read_status
= &mvswitch_read_status
,
435 phy_register_fixup_for_id(PHY_ANY_ID
, mvswitch_fixup
);
436 return phy_driver_register(&mvswitch_driver
, THIS_MODULE
);
442 phy_driver_unregister(&mvswitch_driver
);
445 module_init(mvswitch_init
);
446 module_exit(mvswitch_exit
);