some more mvswitch fixes: - initialize the vlan destination map properly - workaround for moving node bug: clear the ATU database on every PHY poll

SVN-Revision: 11881
This commit is contained in:
Felix Fietkau 2008-07-19 23:09:56 +00:00
parent 87e673487a
commit 3ad51a1c33

View file

@ -79,7 +79,7 @@ mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev)
goto error; goto error;
if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) { if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
if (pskb_expand_head(skb, MV_HEADER_SIZE, 0, GFP_ATOMIC)) if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
goto error_expand; goto error_expand;
if (skb->len < 62) if (skb->len < 62)
skb->len = 62; skb->len = 62;
@ -216,6 +216,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 static int
mvswitch_config_init(struct phy_device *pdev) mvswitch_config_init(struct phy_device *pdev)
{ {
@ -231,6 +245,7 @@ mvswitch_config_init(struct phy_device *pdev)
pdev->supported = ADVERTISED_100baseT_Full; pdev->supported = ADVERTISED_100baseT_Full;
pdev->advertising = ADVERTISED_100baseT_Full; pdev->advertising = ADVERTISED_100baseT_Full;
dev->phy_ptr = priv; dev->phy_ptr = priv;
dev->irq = PHY_POLL;
/* initialize default vlans */ /* initialize default vlans */
for (i = 0; i < MV_PORTS; i++) 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 */ 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), w16(pdev, MV_SWITCHREG(ATU_CTRL),
MV_ATUCTL_RESET | MV_ATUCTL_NO_LEARN |
MV_ATUCTL_ATU_1K | MV_ATUCTL_ATU_1K |
MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */ 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 */ /* initialize the cpu port */
w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT), w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
#ifdef HEADER_MODE #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 */ /* leave port unconfigured if it's not part of a vlan */
if (!vlmap) if (!vlmap)
break; continue;
/* add the cpu port to the allowed destinations list */ /* add the cpu port to the allowed destinations list */
vlmap |= (1 << MV_CPUPORT); vlmap |= (1 << MV_CPUPORT);
@ -299,19 +311,17 @@ mvswitch_config_init(struct phy_device *pdev)
/* apply vlan settings */ /* apply vlan settings */
w16(pdev, MV_PORTREG(VLANMAP, i), w16(pdev, MV_PORTREG(VLANMAP, i),
MV_PORTVLAN_PORTS(vlmap) | MV_PORTVLAN_PORTS(vlmap) |
MV_PORTVLAN_ID(pvid) MV_PORTVLAN_ID(i)
); );
/* re-enable port */ /* 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), w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
MV_PORTVLAN_PORTS(vlmap) MV_PORTVLAN_ID(MV_CPUPORT)
); );
/* set the port association vector */ /* set the port association vector */
@ -343,11 +353,28 @@ mvswitch_config_init(struct phy_device *pdev)
} }
static int static int
mvswitch_read_status(struct phy_device *phydev) mvswitch_read_status(struct phy_device *pdev)
{ {
phydev->speed = SPEED_100; pdev->speed = SPEED_100;
phydev->duplex = DUPLEX_FULL; pdev->duplex = DUPLEX_FULL;
phydev->state = PHY_UP; 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; return 0;
} }