X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=blobdiff_plain;f=target%2Flinux%2Far71xx%2Ffiles%2Fdrivers%2Fnet%2Fag71xx%2Fag71xx_ar7240.c;h=491127d0503b8863617a98f9f92694ecbc01b340;hp=0270e60511577a8ee589ac5f540299fc56c3552b;hb=72a1e3aa51b6486cd914ff52dc57e7986b440f24;hpb=3ec16dd1d272669a4fc42119e688e18ed58f1e97 diff --git a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c index 0270e60511..491127d050 100644 --- a/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c +++ b/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_ar7240.c @@ -25,6 +25,8 @@ #define AR7240_MASK_CTRL_REVISION_M BITM(8) #define AR7240_MASK_CTRL_VERSION_M BITM(8) #define AR7240_MASK_CTRL_VERSION_S 8 +#define AR7240_MASK_CTRL_VERSION_AR7240 0x01 +#define AR7240_MASK_CTRL_VERSION_AR934X 0x02 #define AR7240_MASK_CTRL_SOFT_RESET BIT(31) #define AR7240_REG_MAC_ADDR0 0x20 @@ -57,7 +59,14 @@ #define AR7240_VTUDATA_MEMBER BITS(0, 10) #define AR7240_VTUDATA_VALID BIT(11) +#define AR7240_REG_ATU 0x50 +#define AR7240_ATU_FLUSH_ALL 0x1 + #define AR7240_REG_AT_CTRL 0x5c +#define AR7240_AT_CTRL_AGE_TIME BITS(0, 15) +#define AR7240_AT_CTRL_AGE_EN BIT(17) +#define AR7240_AT_CTRL_LEARN_CHANGE BIT(18) +#define AR7240_AT_CTRL_RESERVED BIT(19) #define AR7240_AT_CTRL_ARP_EN BIT(20) #define AR7240_REG_TAG_PRIORITY 0x70 @@ -90,6 +99,7 @@ #define AR7240_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100) #define AR7240_REG_PORT_STATUS(_port) (AR7240_REG_PORT_BASE((_port)) + 0x00) +#define AR7240_PORT_STATUS_SPEED_S 0 #define AR7240_PORT_STATUS_SPEED_M BITM(2) #define AR7240_PORT_STATUS_SPEED_10 0 #define AR7240_PORT_STATUS_SPEED_100 1 @@ -185,22 +195,52 @@ #define AR7240_PHY_ID1 0x004d #define AR7240_PHY_ID2 0xd041 -#define AR7240_PORT_MASK(_port) BIT((_port)) -#define AR7240_PORT_MASK_ALL BITM(AR7240_NUM_PORTS) -#define AR7240_PORT_MASK_BUT(_port) (AR7240_PORT_MASK_ALL & ~BIT((_port))) +#define AR934X_PHY_ID1 0x004d +#define AR934X_PHY_ID2 0xd042 #define AR7240_MAX_VLANS 16 +#define AR934X_REG_OPER_MODE0 0x04 +#define AR934X_OPER_MODE0_MAC_GMII_EN BIT(6) +#define AR934X_OPER_MODE0_PHY_MII_EN BIT(10) + +#define AR934X_REG_OPER_MODE1 0x08 +#define AR934X_REG_OPER_MODE1_PHY4_MII_EN BIT(28) + +#define AR934X_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100) + +#define AR934X_REG_PORT_VLAN1(_port) (AR934X_REG_PORT_BASE((_port)) + 0x08) +#define AR934X_PORT_VLAN1_DEFAULT_SVID_S 0 +#define AR934X_PORT_VLAN1_FORCE_DEFAULT_VID_EN BIT(12) +#define AR934X_PORT_VLAN1_PORT_TLS_MODE BIT(13) +#define AR934X_PORT_VLAN1_PORT_VLAN_PROP_EN BIT(14) +#define AR934X_PORT_VLAN1_PORT_CLONE_EN BIT(15) +#define AR934X_PORT_VLAN1_DEFAULT_CVID_S 16 +#define AR934X_PORT_VLAN1_FORCE_PORT_VLAN_EN BIT(28) +#define AR934X_PORT_VLAN1_ING_PORT_PRI_S 29 + +#define AR934X_REG_PORT_VLAN2(_port) (AR934X_REG_PORT_BASE((_port)) + 0x0c) +#define AR934X_PORT_VLAN2_PORT_VID_MEM_S 16 +#define AR934X_PORT_VLAN2_8021Q_MODE_S 30 +#define AR934X_PORT_VLAN2_8021Q_MODE_PORT_ONLY 0 +#define AR934X_PORT_VLAN2_8021Q_MODE_PORT_FALLBACK 1 +#define AR934X_PORT_VLAN2_8021Q_MODE_VLAN_ONLY 2 +#define AR934X_PORT_VLAN2_8021Q_MODE_SECURE 3 + #define sw_to_ar7240(_dev) container_of(_dev, struct ar7240sw, swdev) struct ar7240sw { struct mii_bus *mii_bus; + struct ag71xx_switch_platform_data *swdata; struct switch_dev swdev; + int num_ports; + u8 ver; bool vlan; u16 vlan_id[AR7240_MAX_VLANS]; u8 vlan_table[AR7240_MAX_VLANS]; u8 vlan_tagged; u16 pvid[AR7240_NUM_PORTS]; + char buf[80]; }; struct ar7240sw_hw_stat { @@ -211,9 +251,29 @@ struct ar7240sw_hw_stat { static DEFINE_MUTEX(reg_mutex); -static inline void ar7240sw_init(struct ar7240sw *as, struct mii_bus *mii) +static inline int sw_is_ar7240(struct ar7240sw *as) { - as->mii_bus = mii; + return as->ver == AR7240_MASK_CTRL_VERSION_AR7240; +} + +static inline int sw_is_ar934x(struct ar7240sw *as) +{ + return as->ver == AR7240_MASK_CTRL_VERSION_AR934X; +} + +static inline u32 ar7240sw_port_mask(struct ar7240sw *as, int port) +{ + return BIT(port); +} + +static inline u32 ar7240sw_port_mask_all(struct ar7240sw *as) +{ + return BIT(as->swdev.ports) - 1; +} + +static inline u32 ar7240sw_port_mask_but(struct ar7240sw *as, int port) +{ + return ar7240sw_port_mask_all(as) & ~BIT(port); } static inline u16 mk_phy_addr(u32 reg) @@ -233,37 +293,39 @@ static inline u16 mk_high_addr(u32 reg) static u32 __ar7240sw_reg_read(struct mii_bus *mii, u32 reg) { + unsigned long flags; u16 phy_addr; u16 phy_reg; u32 hi, lo; reg = (reg & 0xfffffffc) >> 2; - - ag71xx_mdio_mii_write(mii->priv, 0x1f, 0x10, mk_high_addr(reg)); - phy_addr = mk_phy_addr(reg); phy_reg = mk_phy_reg(reg); + local_irq_save(flags); + ag71xx_mdio_mii_write(mii->priv, 0x1f, 0x10, mk_high_addr(reg)); lo = (u32) ag71xx_mdio_mii_read(mii->priv, phy_addr, phy_reg); hi = (u32) ag71xx_mdio_mii_read(mii->priv, phy_addr, phy_reg + 1); + local_irq_restore(flags); return (hi << 16) | lo; } static void __ar7240sw_reg_write(struct mii_bus *mii, u32 reg, u32 val) { + unsigned long flags; u16 phy_addr; u16 phy_reg; reg = (reg & 0xfffffffc) >> 2; - - ag71xx_mdio_mii_write(mii->priv, 0x1f, 0x10, mk_high_addr(reg)); - phy_addr = mk_phy_addr(reg); phy_reg = mk_phy_reg(reg); + local_irq_save(flags); + ag71xx_mdio_mii_write(mii->priv, 0x1f, 0x10, mk_high_addr(reg)); ag71xx_mdio_mii_write(mii->priv, phy_addr, phy_reg + 1, (val >> 16)); ag71xx_mdio_mii_write(mii->priv, phy_addr, phy_reg, (val & 0xffff)); + local_irq_restore(flags); } static u32 ar7240sw_reg_read(struct mii_bus *mii, u32 reg_addr) @@ -389,49 +451,12 @@ int ar7240sw_phy_write(struct mii_bus *mii, unsigned phy_addr, return ret; } -static int ar7240sw_capture_stats(struct ar7240sw *as) -{ - struct mii_bus *mii = as->mii_bus; - int ret; - - /* Capture the hardware statistics for all ports */ - ar7240sw_reg_write(mii, AR7240_REG_MIB_FUNCTION0, - (AR7240_MIB_FUNC_CAPTURE << AR7240_MIB_FUNC_S)); - - /* Wait for the capturing to complete. */ - ret = ar7240sw_reg_wait(mii, AR7240_REG_MIB_FUNCTION0, - AR7240_MIB_BUSY, 0, 10); - return ret; -} - static void ar7240sw_disable_port(struct ar7240sw *as, unsigned port) { ar7240sw_reg_write(as->mii_bus, AR7240_REG_PORT_CTRL(port), AR7240_PORT_CTRL_STATE_DISABLED); } -static int ar7240sw_reset(struct ar7240sw *as) -{ - struct mii_bus *mii = as->mii_bus; - int ret; - int i; - - /* Set all ports to disabled state. */ - for (i = 0; i < AR7240_NUM_PORTS; i++) - ar7240sw_disable_port(as, i); - - /* Wait for transmit queues to drain. */ - msleep(2); - - /* Reset the switch. */ - ar7240sw_reg_write(mii, AR7240_REG_MASK_CTRL, - AR7240_MASK_CTRL_SOFT_RESET); - - ret = ar7240sw_reg_wait(mii, AR7240_REG_MASK_CTRL, - AR7240_MASK_CTRL_SOFT_RESET, 0, 1000); - return ret; -} - static void ar7240sw_setup(struct ar7240sw *as) { struct mii_bus *mii = as->mii_bus; @@ -444,8 +469,13 @@ static void ar7240sw_setup(struct ar7240sw *as) /* Setup TAG priority mapping */ ar7240sw_reg_write(mii, AR7240_REG_TAG_PRIORITY, 0xfa50); - /* Enable ARP frame acknowledge */ - ar7240sw_reg_set(mii, AR7240_REG_AT_CTRL, AR7240_AT_CTRL_ARP_EN); + /* Enable ARP frame acknowledge, aging, MAC replacing */ + ar7240sw_reg_write(mii, AR7240_REG_AT_CTRL, + AR7240_AT_CTRL_RESERVED | + 0x2b /* 5 min age time */ | + AR7240_AT_CTRL_AGE_EN | + AR7240_AT_CTRL_ARP_EN | + AR7240_AT_CTRL_LEARN_CHANGE); /* Enable Broadcast frames transmitted to the CPU */ ar7240sw_reg_set(mii, AR7240_REG_FLOOD_MASK, @@ -459,12 +489,35 @@ static void ar7240sw_setup(struct ar7240sw *as) ar7240sw_reg_rmw(mii, AR7240_REG_SERVICE_TAG, AR7240_SERVICE_TAG_M, 0); } +static int ar7240sw_reset(struct ar7240sw *as) +{ + struct mii_bus *mii = as->mii_bus; + int ret; + int i; + + /* Set all ports to disabled state. */ + for (i = 0; i < AR7240_NUM_PORTS; i++) + ar7240sw_disable_port(as, i); + + /* Wait for transmit queues to drain. */ + msleep(2); + + /* Reset the switch. */ + ar7240sw_reg_write(mii, AR7240_REG_MASK_CTRL, + AR7240_MASK_CTRL_SOFT_RESET); + + ret = ar7240sw_reg_wait(mii, AR7240_REG_MASK_CTRL, + AR7240_MASK_CTRL_SOFT_RESET, 0, 1000); + + ar7240sw_setup(as); + return ret; +} + static void ar7240sw_setup_port(struct ar7240sw *as, unsigned port, u8 portmask) { struct mii_bus *mii = as->mii_bus; u32 ctrl; - u32 dest_ports; - u32 vlan; + u32 vid, mode; ctrl = AR7240_PORT_CTRL_STATE_FORWARD | AR7240_PORT_CTRL_LEARN | AR7240_PORT_CTRL_SINGLE_VLAN; @@ -484,13 +537,11 @@ static void ar7240sw_setup_port(struct ar7240sw *as, unsigned port, u8 portmask) /* Set the default VID for this port */ if (as->vlan) { - vlan = as->vlan_id[as->pvid[port]]; - vlan |= AR7240_PORT_VLAN_MODE_SECURE << - AR7240_PORT_VLAN_MODE_S; + vid = as->vlan_id[as->pvid[port]]; + mode = AR7240_PORT_VLAN_MODE_SECURE; } else { - vlan = port; - vlan |= AR7240_PORT_VLAN_MODE_PORT_ONLY << - AR7240_PORT_VLAN_MODE_S; + vid = port; + mode = AR7240_PORT_VLAN_MODE_PORT_ONLY; } if (as->vlan && (as->vlan_tagged & BIT(port))) { @@ -503,21 +554,33 @@ static void ar7240sw_setup_port(struct ar7240sw *as, unsigned port, u8 portmask) if (!portmask) { if (port == AR7240_PORT_CPU) - portmask = AR7240_PORT_MASK_BUT(AR7240_PORT_CPU); + portmask = ar7240sw_port_mask_but(as, AR7240_PORT_CPU); else - portmask = AR7240_PORT_MASK(AR7240_PORT_CPU); + portmask = ar7240sw_port_mask(as, AR7240_PORT_CPU); } /* allow the port to talk to all other ports, but exclude its * own ID to prevent frames from being reflected back to the * port that they came from */ - dest_ports = AR7240_PORT_MASK_BUT(port); - - /* set default VID and and destination ports for this VLAN */ - vlan |= (portmask << AR7240_PORT_VLAN_DEST_PORTS_S); + portmask &= ar7240sw_port_mask_but(as, port); ar7240sw_reg_write(mii, AR7240_REG_PORT_CTRL(port), ctrl); - ar7240sw_reg_write(mii, AR7240_REG_PORT_VLAN(port), vlan); + if (sw_is_ar934x(as)) { + u32 vlan1, vlan2; + + vlan1 = (vid << AR934X_PORT_VLAN1_DEFAULT_CVID_S); + vlan2 = (portmask << AR934X_PORT_VLAN2_PORT_VID_MEM_S) | + (mode << AR934X_PORT_VLAN2_8021Q_MODE_S); + ar7240sw_reg_write(mii, AR934X_REG_PORT_VLAN1(port), vlan1); + ar7240sw_reg_write(mii, AR934X_REG_PORT_VLAN2(port), vlan2); + } else { + u32 vlan; + + vlan = vid | (mode << AR7240_PORT_VLAN_MODE_S) | + (portmask << AR7240_PORT_VLAN_DEST_PORTS_S); + + ar7240sw_reg_write(mii, AR7240_REG_PORT_VLAN(port), vlan); + } } static int ar7240_set_addr(struct ar7240sw *as, u8 *addr) @@ -582,7 +645,7 @@ ar7240_get_ports(struct switch_dev *dev, struct switch_val *val) int i; val->len = 0; - for (i = 0; i < AR7240_NUM_PORTS; i++) { + for (i = 0; i < as->swdev.ports; i++) { struct switch_port *p; if (!(ports & (1 << i))) @@ -647,6 +710,63 @@ ar7240_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, return 0; } +static const char * +ar7240_speed_str(u32 status) +{ + u32 speed; + + speed = (status >> AR7240_PORT_STATUS_SPEED_S) & + AR7240_PORT_STATUS_SPEED_M; + switch (speed) { + case AR7240_PORT_STATUS_SPEED_10: + return "10baseT"; + case AR7240_PORT_STATUS_SPEED_100: + return "100baseT"; + case AR7240_PORT_STATUS_SPEED_1000: + return "1000baseT"; + } + + return "unknown"; +} + +static int +ar7240_port_get_link(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar7240sw *as = sw_to_ar7240(dev); + struct mii_bus *mii = as->mii_bus; + u32 len; + u32 status; + int port; + + port = val->port_vlan; + + memset(as->buf, '\0', sizeof(as->buf)); + status = ar7240sw_reg_read(mii, AR7240_REG_PORT_STATUS(port)); + + if (status & AR7240_PORT_STATUS_LINK_UP) { + len = snprintf(as->buf, sizeof(as->buf), + "port:%d link:up speed:%s %s-duplex %s%s%s", + port, + ar7240_speed_str(status), + (status & AR7240_PORT_STATUS_DUPLEX) ? + "full" : "half", + (status & AR7240_PORT_STATUS_TXFLOW) ? + "txflow ": "", + (status & AR7240_PORT_STATUS_RXFLOW) ? + "rxflow " : "", + (status & AR7240_PORT_STATUS_LINK_AUTO) ? + "auto ": ""); + } else { + len = snprintf(as->buf, sizeof(as->buf), + "port:%d link:down", port); + } + + val->value.s = as->buf; + val->len = len; + + return 0; +} static void ar7240_vtu_op(struct ar7240sw *as, u32 op, u32 val) @@ -685,7 +805,7 @@ ar7240_hw_apply(struct switch_dev *dev) if (!vp) continue; - for (i = 0; i < AR7240_NUM_PORTS; i++) { + for (i = 0; i < as->swdev.ports; i++) { u8 mask = (1 << i); if (vp & mask) portmask[i] |= vp & ~mask; @@ -699,7 +819,7 @@ ar7240_hw_apply(struct switch_dev *dev) } else { /* vlan disabled: * isolate all ports, but connect them to the cpu port */ - for (i = 0; i < AR7240_NUM_PORTS; i++) { + for (i = 0; i < as->swdev.ports; i++) { if (i == AR7240_PORT_CPU) continue; @@ -709,7 +829,7 @@ ar7240_hw_apply(struct switch_dev *dev) } /* update the port destination mask registers and tag settings */ - for (i = 0; i < AR7240_NUM_PORTS; i++) + for (i = 0; i < as->swdev.ports; i++) ar7240sw_setup_port(as, i, portmask[i]); return 0; @@ -735,6 +855,14 @@ static struct switch_attr ar7240_globals[] = { }; static struct switch_attr ar7240_port[] = { + { + .type = SWITCH_TYPE_STRING, + .name = "link", + .description = "Get port link information", + .max = 1, + .set = NULL, + .get = ar7240_port_get_link, + }, }; static struct switch_attr ar7240_vlan[] = { @@ -771,59 +899,108 @@ static const struct switch_dev_ops ar7240_ops = { static struct ar7240sw *ar7240_probe(struct ag71xx *ag) { + struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); struct mii_bus *mii = ag->mii_bus; struct ar7240sw *as; struct switch_dev *swdev; u32 ctrl; u16 phy_id1; u16 phy_id2; - u8 ver; int i; + phy_id1 = ar7240sw_phy_read(mii, 0, MII_PHYSID1); + phy_id2 = ar7240sw_phy_read(mii, 0, MII_PHYSID2); + if ((phy_id1 != AR7240_PHY_ID1 || phy_id2 != AR7240_PHY_ID2) && + (phy_id1 != AR934X_PHY_ID1 || phy_id2 != AR934X_PHY_ID2)) { + pr_err("%s: unknown phy id '%04x:%04x'\n", + ag->dev->name, phy_id1, phy_id2); + return NULL; + } + as = kzalloc(sizeof(*as), GFP_KERNEL); if (!as) return NULL; - ar7240sw_init(as, mii); + as->mii_bus = mii; + as->swdata = pdata->switch_data; + + swdev = &as->swdev; ctrl = ar7240sw_reg_read(mii, AR7240_REG_MASK_CTRL); + as->ver = (ctrl >> AR7240_MASK_CTRL_VERSION_S) & + AR7240_MASK_CTRL_VERSION_M; + + if (sw_is_ar7240(as)) { + swdev->name = "AR7240/AR9330 built-in switch"; + } else if (sw_is_ar934x(as)) { + swdev->name = "AR934X built-in switch"; + + if (pdata->phy_if_mode == PHY_INTERFACE_MODE_GMII) { + ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0, + AR934X_OPER_MODE0_MAC_GMII_EN); + } else if (pdata->phy_if_mode == PHY_INTERFACE_MODE_MII) { + ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0, + AR934X_OPER_MODE0_PHY_MII_EN); + } else { + pr_err("%s: invalid PHY interface mode\n", + ag->dev->name); + goto err_free; + } - ver = (ctrl >> AR7240_MASK_CTRL_VERSION_S) & AR7240_MASK_CTRL_VERSION_M; - if (ver != 1) { + if (as->swdata->phy4_mii_en) + ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1, + AR934X_REG_OPER_MODE1_PHY4_MII_EN); + } else { pr_err("%s: unsupported chip, ctrl=%08x\n", ag->dev->name, ctrl); - return NULL; - } - - phy_id1 = ar7240sw_phy_read(mii, 0, MII_PHYSID1); - phy_id2 = ar7240sw_phy_read(mii, 0, MII_PHYSID2); - if (phy_id1 != AR7240_PHY_ID1 || phy_id2 != AR7240_PHY_ID2) { - pr_err("%s: unknown phy id '%04x:%04x'\n", - ag->dev->name, phy_id1, phy_id2); - return NULL; + goto err_free; } - swdev = &as->swdev; - swdev->name = "AR7240 built-in switch"; - swdev->ports = AR7240_NUM_PORTS; + swdev->ports = AR7240_NUM_PORTS - 1; swdev->cpu_port = AR7240_PORT_CPU; swdev->vlans = AR7240_MAX_VLANS; swdev->ops = &ar7240_ops; - if (register_switch(&as->swdev, ag->dev) < 0) { - kfree(as); - return NULL; - } + if (register_switch(&as->swdev, ag->dev) < 0) + goto err_free; - pr_info("%s: Found an AR7240 built-in switch\n", ag->dev->name); + pr_info("%s: Found an %s\n", ag->dev->name, swdev->name); /* initialize defaults */ for (i = 0; i < AR7240_MAX_VLANS; i++) as->vlan_id[i] = i; - as->vlan_table[0] = AR7240_PORT_MASK_ALL; + as->vlan_table[0] = ar7240sw_port_mask_all(as); return as; + +err_free: + kfree(as); + return NULL; +} + +static void link_function(struct work_struct *work) { + struct ag71xx *ag = container_of(work, struct ag71xx, link_work.work); + unsigned long flags; + int i; + int status = 0; + + for (i = 0; i < 4; i++) { + int link = ar7240sw_phy_read(ag->mii_bus, i, MII_BMSR); + if(link & BMSR_LSTATUS) { + status = 1; + break; + } + } + + spin_lock_irqsave(&ag->lock, flags); + if(status != ag->link) { + ag->link = status; + ag71xx_link_adjust(ag); + } + spin_unlock_irqrestore(&ag->lock, flags); + + schedule_delayed_work(&ag->link_work, HZ / 2); } void ag71xx_ar7240_start(struct ag71xx *ag) @@ -831,18 +1008,19 @@ void ag71xx_ar7240_start(struct ag71xx *ag) struct ar7240sw *as = ag->phy_priv; ar7240sw_reset(as); - ar7240sw_setup(as); ag->speed = SPEED_1000; - ag->link = 1; ag->duplex = 1; ar7240_set_addr(as, ag->dev->dev_addr); ar7240_hw_apply(&as->swdev); + + schedule_delayed_work(&ag->link_work, HZ / 10); } void ag71xx_ar7240_stop(struct ag71xx *ag) { + cancel_delayed_work_sync(&ag->link_work); } int __devinit ag71xx_ar7240_init(struct ag71xx *ag) @@ -856,6 +1034,8 @@ int __devinit ag71xx_ar7240_init(struct ag71xx *ag) ag->phy_priv = as; ar7240sw_reset(as); + INIT_DELAYED_WORK(&ag->link_work, link_function); + return 0; }