X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/15333144ce2b95acca047cea3ba53a5ddab1d5a0..08b66183fa7aa740e87a509ea5030fb5557e7eee:/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c diff --git a/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c b/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c index 8eae439f7..b98699e17 100644 --- a/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c +++ b/target/linux/generic-2.6/files/drivers/net/phy/mvswitch.c @@ -38,6 +38,8 @@ MODULE_DESCRIPTION("Marvell 88E6060 Switch driver"); MODULE_AUTHOR("Felix Fietkau"); MODULE_LICENSE("GPL"); +#define MVSWITCH_MAGIC 0x88E6060 + struct mvswitch_priv { /* the driver's tx function */ int (*hardstart)(struct sk_buff *skb, struct net_device *dev); @@ -79,7 +81,7 @@ mvswitch_mangle_tx(struct sk_buff *skb, struct net_device *dev) goto error; 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; if (skb->len < 62) skb->len = 62; @@ -216,6 +218,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) { @@ -231,10 +247,11 @@ 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++) - priv->vlans[(i == MV_WANPORT ? 1 : 0)] |= (1 << i); + priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i); /* before entering reset, disable all ports */ for (i = 0; i < MV_PORTS; i++) @@ -242,25 +259,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 +302,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 +313,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 */ @@ -328,6 +340,7 @@ mvswitch_config_init(struct phy_device *pdev) ); /* hook into the tx function */ + pdev->pkt_align = 2; priv->hardstart = dev->hard_start_xmit; pdev->netif_receive_skb = mvswitch_netif_receive_skb; pdev->netif_rx = mvswitch_netif_rx; @@ -343,11 +356,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; } @@ -373,37 +403,6 @@ mvswitch_remove(struct phy_device *pdev) kfree(priv); } -static bool -mvswitch_detect(struct mii_bus *bus, int addr) -{ - u16 reg; - int i; - - /* we attach to phy id 31 to make sure that the late probe works */ - if (addr != 31) - return false; - - /* look for the switch on the bus */ - reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; - if (reg != MV_IDENT_VALUE) - return false; - - /* - * Now that we've established that the switch actually exists, let's - * get rid of the competition :) - */ - for (i = 0; i < 31; i++) { - if (!bus->phy_map[i]) - continue; - - device_unregister(&bus->phy_map[i]->dev); - kfree(bus->phy_map[i]); - bus->phy_map[i] = NULL; - } - - return true; -} - static int mvswitch_probe(struct phy_device *pdev) { @@ -418,11 +417,28 @@ mvswitch_probe(struct phy_device *pdev) return 0; } +static int +mvswitch_fixup(struct phy_device *dev) +{ + u16 reg; + + if (dev->addr != 0x10) + return 0; + + reg = dev->bus->read(dev->bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK; + if (reg != MV_IDENT_VALUE) + return 0; + + dev->phy_id = MVSWITCH_MAGIC; + return 0; +} + static struct phy_driver mvswitch_driver = { .name = "Marvell 88E6060", + .phy_id = MVSWITCH_MAGIC, + .phy_id_mask = 0xffffffff, .features = PHY_BASIC_FEATURES, - .detect = &mvswitch_detect, .probe = &mvswitch_probe, .remove = &mvswitch_remove, .config_init = &mvswitch_config_init, @@ -434,6 +450,7 @@ static struct phy_driver mvswitch_driver = { static int __init mvswitch_init(void) { + phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup); return phy_driver_register(&mvswitch_driver); }