diff options
Diffstat (limited to 'target/linux')
4 files changed, 140 insertions, 58 deletions
diff --git a/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.c b/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.c index be9e6afd2d..78e5afe9bd 100644 --- a/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.c +++ b/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.c @@ -78,8 +78,8 @@ mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev) if (__vlan_hwaccel_get_tag(skb, &vid)) goto error; - if ((skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) { - if (pskb_expand_head(skb, MV_HEADER_SIZE, 0, GFP_ATOMIC)) + if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) { + if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC)) goto error_expand; if (skb->len < 62) skb->len = 62; @@ -217,6 +217,20 @@ mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) static int +mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val) +{ + int i = 100; + u16 r; + + do { + r = r16(pdev, addr, reg) & mask; + if (r == val) + return 0; + } while(--i > 0); + return -ETIMEDOUT; +} + +static int mvswitch_config_init(struct phy_device *pdev) { struct mvswitch_priv *priv = to_mvsw(pdev); @@ -231,6 +245,7 @@ mvswitch_config_init(struct phy_device *pdev) pdev->supported = ADVERTISED_100baseT_Full; pdev->advertising = ADVERTISED_100baseT_Full; dev->phy_ptr = priv; + dev->irq = PHY_POLL; /* initialize default vlans */ for (i = 0; i < MV_PORTS; i++) @@ -242,25 +257,22 @@ mvswitch_config_init(struct phy_device *pdev) msleep(2); /* wait for the status change to settle in */ - /* put the device in reset and set ATU flags */ + /* put the ATU in reset */ + w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET); + + i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0); + if (i < 0) { + printk("%s: Timeout waiting for the switch to reset.\n", dev->name); + return i; + } + + /* set the ATU flags */ w16(pdev, MV_SWITCHREG(ATU_CTRL), - MV_ATUCTL_RESET | + MV_ATUCTL_NO_LEARN | MV_ATUCTL_ATU_1K | MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */ ); - i = 100; /* timeout */ - do { - if (!(r16(pdev, MV_SWITCHREG(ATU_CTRL)) & MV_ATUCTL_RESET)) - break; - msleep(1); - } while (--i > 0); - - if (!i) { - printk("%s: Timeout waiting for the switch to reset.\n", dev->name); - return -ETIMEDOUT; - } - /* initialize the cpu port */ w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT), #ifdef HEADER_MODE @@ -288,7 +300,7 @@ mvswitch_config_init(struct phy_device *pdev) } /* leave port unconfigured if it's not part of a vlan */ if (!vlmap) - break; + continue; /* add the cpu port to the allowed destinations list */ vlmap |= (1 << MV_CPUPORT); @@ -299,19 +311,17 @@ mvswitch_config_init(struct phy_device *pdev) /* apply vlan settings */ w16(pdev, MV_PORTREG(VLANMAP, i), MV_PORTVLAN_PORTS(vlmap) | - MV_PORTVLAN_ID(pvid) + MV_PORTVLAN_ID(i) ); /* re-enable port */ - w16(pdev, MV_PORTREG(CONTROL, i), MV_PORTCTRL_ENABLED); + w16(pdev, MV_PORTREG(CONTROL, i), + MV_PORTCTRL_ENABLED + ); } - /* build the target list for the cpu port */ - for (i = 0; i < MV_PORTS; i++) - vlmap |= (1 << i); - w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT), - MV_PORTVLAN_PORTS(vlmap) + MV_PORTVLAN_ID(MV_CPUPORT) ); /* set the port association vector */ @@ -343,11 +353,28 @@ mvswitch_config_init(struct phy_device *pdev) } static int -mvswitch_read_status(struct phy_device *phydev) +mvswitch_read_status(struct phy_device *pdev) { - phydev->speed = SPEED_100; - phydev->duplex = DUPLEX_FULL; - phydev->state = PHY_UP; + pdev->speed = SPEED_100; + pdev->duplex = DUPLEX_FULL; + pdev->state = PHY_UP; + + /* XXX ugly workaround: we can't force the switch + * to gracefully handle hosts moving from one port to another, + * so we have to regularly clear the ATU database */ + + /* wait for the ATU to become available */ + mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0); + + /* flush the ATU */ + w16(pdev, MV_SWITCHREG(ATU_OP), + MV_ATUOP_INPROGRESS | + MV_ATUOP_FLUSH_ALL + ); + + /* wait for operation to complete */ + mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0); + return 0; } diff --git a/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.h b/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.h index a172e37265..1563eec4d5 100644 --- a/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.h +++ b/target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.h @@ -123,7 +123,21 @@ enum { MV_ATUCTL_ATUMASK = (3 << 12), MV_ATUCTL_NO_LEARN = (1 << 14), MV_ATUCTL_RESET = (1 << 15), -} +}; + +enum { +#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f) + + MV_ATUOP_NOOP = (0 << 12), + MV_ATUOP_FLUSH_ALL = (1 << 12), + MV_ATUOP_FLUSH_U = (2 << 12), + MV_ATUOP_LOAD_DB = (3 << 12), + MV_ATUOP_GET_NEXT = (4 << 12), + MV_ATUOP_FLUSH_DB = (5 << 12), + MV_ATUOP_FLUSH_DB_UU= (6 << 12), + + MV_ATUOP_INPROGRESS = (1 << 15), +}; #define MV_IDENT_MASK 0xfff0 #define MV_IDENT_VALUE 0x0600 diff --git a/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.c b/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.c index be9e6afd2d..78e5afe9bd 100644 --- a/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.c +++ b/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.c @@ -78,8 +78,8 @@ mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev) if (__vlan_hwaccel_get_tag(skb, &vid)) goto error; - if ((skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) { - if (pskb_expand_head(skb, MV_HEADER_SIZE, 0, GFP_ATOMIC)) + if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) { + if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC)) goto error_expand; if (skb->len < 62) skb->len = 62; @@ -217,6 +217,20 @@ mvswitch_vlan_rx_register(struct net_device *dev, struct vlan_group *grp) static int +mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val) +{ + int i = 100; + u16 r; + + do { + r = r16(pdev, addr, reg) & mask; + if (r == val) + return 0; + } while(--i > 0); + return -ETIMEDOUT; +} + +static int mvswitch_config_init(struct phy_device *pdev) { struct mvswitch_priv *priv = to_mvsw(pdev); @@ -231,6 +245,7 @@ mvswitch_config_init(struct phy_device *pdev) pdev->supported = ADVERTISED_100baseT_Full; pdev->advertising = ADVERTISED_100baseT_Full; dev->phy_ptr = priv; + dev->irq = PHY_POLL; /* initialize default vlans */ for (i = 0; i < MV_PORTS; i++) @@ -242,25 +257,22 @@ mvswitch_config_init(struct phy_device *pdev) msleep(2); /* wait for the status change to settle in */ - /* put the device in reset and set ATU flags */ + /* put the ATU in reset */ + w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET); + + i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0); + if (i < 0) { + printk("%s: Timeout waiting for the switch to reset.\n", dev->name); + return i; + } + + /* set the ATU flags */ w16(pdev, MV_SWITCHREG(ATU_CTRL), - MV_ATUCTL_RESET | + MV_ATUCTL_NO_LEARN | MV_ATUCTL_ATU_1K | MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */ ); - i = 100; /* timeout */ - do { - if (!(r16(pdev, MV_SWITCHREG(ATU_CTRL)) & MV_ATUCTL_RESET)) - break; - msleep(1); - } while (--i > 0); - - if (!i) { - printk("%s: Timeout waiting for the switch to reset.\n", dev->name); - return -ETIMEDOUT; - } - /* initialize the cpu port */ w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT), #ifdef HEADER_MODE @@ -288,7 +300,7 @@ mvswitch_config_init(struct phy_device *pdev) } /* leave port unconfigured if it's not part of a vlan */ if (!vlmap) - break; + continue; /* add the cpu port to the allowed destinations list */ vlmap |= (1 << MV_CPUPORT); @@ -299,19 +311,17 @@ mvswitch_config_init(struct phy_device *pdev) /* apply vlan settings */ w16(pdev, MV_PORTREG(VLANMAP, i), MV_PORTVLAN_PORTS(vlmap) | - MV_PORTVLAN_ID(pvid) + MV_PORTVLAN_ID(i) ); /* re-enable port */ - w16(pdev, MV_PORTREG(CONTROL, i), MV_PORTCTRL_ENABLED); + w16(pdev, MV_PORTREG(CONTROL, i), + MV_PORTCTRL_ENABLED + ); } - /* build the target list for the cpu port */ - for (i = 0; i < MV_PORTS; i++) - vlmap |= (1 << i); - w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT), - MV_PORTVLAN_PORTS(vlmap) + MV_PORTVLAN_ID(MV_CPUPORT) ); /* set the port association vector */ @@ -343,11 +353,28 @@ mvswitch_config_init(struct phy_device *pdev) } static int -mvswitch_read_status(struct phy_device *phydev) +mvswitch_read_status(struct phy_device *pdev) { - phydev->speed = SPEED_100; - phydev->duplex = DUPLEX_FULL; - phydev->state = PHY_UP; + pdev->speed = SPEED_100; + pdev->duplex = DUPLEX_FULL; + pdev->state = PHY_UP; + + /* XXX ugly workaround: we can't force the switch + * to gracefully handle hosts moving from one port to another, + * so we have to regularly clear the ATU database */ + + /* wait for the ATU to become available */ + mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0); + + /* flush the ATU */ + w16(pdev, MV_SWITCHREG(ATU_OP), + MV_ATUOP_INPROGRESS | + MV_ATUOP_FLUSH_ALL + ); + + /* wait for operation to complete */ + mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0); + return 0; } diff --git a/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.h b/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.h index a172e37265..1563eec4d5 100644 --- a/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.h +++ b/target/linux/generic-2.6/files-2.6.26/drivers/net/phy/mvswitch.h @@ -123,7 +123,21 @@ enum { MV_ATUCTL_ATUMASK = (3 << 12), MV_ATUCTL_NO_LEARN = (1 << 14), MV_ATUCTL_RESET = (1 << 15), -} +}; + +enum { +#define MV_ATUOP_DBNUM(_n) ((_n) & 0x0f) + + MV_ATUOP_NOOP = (0 << 12), + MV_ATUOP_FLUSH_ALL = (1 << 12), + MV_ATUOP_FLUSH_U = (2 << 12), + MV_ATUOP_LOAD_DB = (3 << 12), + MV_ATUOP_GET_NEXT = (4 << 12), + MV_ATUOP_FLUSH_DB = (5 << 12), + MV_ATUOP_FLUSH_DB_UU= (6 << 12), + + MV_ATUOP_INPROGRESS = (1 << 15), +}; #define MV_IDENT_MASK 0xfff0 #define MV_IDENT_VALUE 0x0600 |