aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic-2.6/files-2.6.25/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic-2.6/files-2.6.25/drivers')
-rw-r--r--target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.c83
-rw-r--r--target/linux/generic-2.6/files-2.6.25/drivers/net/phy/mvswitch.h16
2 files changed, 70 insertions, 29 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