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=c256d0584ff4da61299d8950c24d8fceb2d6a66e;hb=72a1e3aa51b6486cd914ff52dc57e7986b440f24;hpb=a769ca08b56ac7a9a066240a78c1e5ac8df48966 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 c256d0584f..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,23 +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 mutex reg_mutex; + 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 { @@ -210,117 +249,137 @@ struct ar7240sw_hw_stat { int reg; }; +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; - mutex_init(&as->reg_mutex); + 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) { - return (0x17 & ((reg >> 4) | 0x10)); + return 0x17 & ((reg >> 4) | 0x10); } static inline u16 mk_phy_reg(u32 reg) { - return ((reg << 1) & 0x1e); + return (reg << 1) & 0x1e; } static inline u16 mk_high_addr(u32 reg) { - return ((reg >> 7) & 0x1ff); + return (reg >> 7) & 0x1ff; } -static u32 __ar7240sw_reg_read(struct ar7240sw *as, u32 reg) +static u32 __ar7240sw_reg_read(struct mii_bus *mii, u32 reg) { - struct mii_bus *mii = as->mii_bus; + unsigned long flags; u16 phy_addr; u16 phy_reg; u32 hi, lo; reg = (reg & 0xfffffffc) >> 2; - - mdiobus_write(mii, 0x1f, 0x10, mk_high_addr(reg)); - phy_addr = mk_phy_addr(reg); phy_reg = mk_phy_reg(reg); - lo = (u32) mdiobus_read(mii, phy_addr, phy_reg); - hi = (u32) mdiobus_read(mii, phy_addr, phy_reg + 1); + 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); + return (hi << 16) | lo; } -static void __ar7240sw_reg_write(struct ar7240sw *as, u32 reg, u32 val) +static void __ar7240sw_reg_write(struct mii_bus *mii, u32 reg, u32 val) { - struct mii_bus *mii = as->mii_bus; + unsigned long flags; u16 phy_addr; u16 phy_reg; reg = (reg & 0xfffffffc) >> 2; - - mdiobus_write(mii, 0x1f, 0x10, mk_high_addr(reg)); - phy_addr = mk_phy_addr(reg); phy_reg = mk_phy_reg(reg); - mdiobus_write(mii, phy_addr, phy_reg + 1, (val >> 16)); - mdiobus_write(mii, phy_addr, phy_reg, (val & 0xffff)); + 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 ar7240sw *as, u32 reg_addr) +static u32 ar7240sw_reg_read(struct mii_bus *mii, u32 reg_addr) { u32 ret; - mutex_lock(&as->reg_mutex); - ret = __ar7240sw_reg_read(as, reg_addr); - mutex_unlock(&as->reg_mutex); + mutex_lock(®_mutex); + ret = __ar7240sw_reg_read(mii, reg_addr); + mutex_unlock(®_mutex); return ret; } -static void ar7240sw_reg_write(struct ar7240sw *as, u32 reg_addr, u32 reg_val) +static void ar7240sw_reg_write(struct mii_bus *mii, u32 reg_addr, u32 reg_val) { - mutex_lock(&as->reg_mutex); - __ar7240sw_reg_write(as, reg_addr, reg_val); - mutex_unlock(&as->reg_mutex); + mutex_lock(®_mutex); + __ar7240sw_reg_write(mii, reg_addr, reg_val); + mutex_unlock(®_mutex); } -static u32 ar7240sw_reg_rmw(struct ar7240sw *as, u32 reg, u32 mask, u32 val) +static u32 ar7240sw_reg_rmw(struct mii_bus *mii, u32 reg, u32 mask, u32 val) { u32 t; - mutex_lock(&as->reg_mutex); - t = __ar7240sw_reg_read(as, reg); + mutex_lock(®_mutex); + t = __ar7240sw_reg_read(mii, reg); t &= ~mask; t |= val; - __ar7240sw_reg_write(as, reg, t); - mutex_unlock(&as->reg_mutex); + __ar7240sw_reg_write(mii, reg, t); + mutex_unlock(®_mutex); return t; } -static void ar7240sw_reg_set(struct ar7240sw *as, u32 reg, u32 val) +static void ar7240sw_reg_set(struct mii_bus *mii, u32 reg, u32 val) { u32 t; - mutex_lock(&as->reg_mutex); - t = __ar7240sw_reg_read(as, reg); + mutex_lock(®_mutex); + t = __ar7240sw_reg_read(mii, reg); t |= val; - __ar7240sw_reg_write(as, reg, t); - mutex_unlock(&as->reg_mutex); + __ar7240sw_reg_write(mii, reg, t); + mutex_unlock(®_mutex); } -static int ar7240sw_reg_wait(struct ar7240sw *as, u32 reg, u32 mask, u32 val, - unsigned timeout) +static int __ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val, + unsigned timeout) { int i; for (i = 0; i < timeout; i++) { u32 t; - t = ar7240sw_reg_read(as, reg); + t = __ar7240sw_reg_read(mii, reg); if ((t & mask) == val) return 0; @@ -330,33 +389,45 @@ static int ar7240sw_reg_wait(struct ar7240sw *as, u32 reg, u32 mask, u32 val, return -ETIMEDOUT; } -static u16 ar7240sw_phy_read(struct ar7240sw *as, unsigned phy_addr, - unsigned reg_addr) +static int ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val, + unsigned timeout) { - u32 t; + int ret; + + mutex_lock(®_mutex); + ret = __ar7240sw_reg_wait(mii, reg, mask, val, timeout); + mutex_unlock(®_mutex); + return ret; +} + +u16 ar7240sw_phy_read(struct mii_bus *mii, unsigned phy_addr, + unsigned reg_addr) +{ + u32 t, val = 0xffff; int err; if (phy_addr >= AR7240_NUM_PHYS) return 0xffff; + mutex_lock(®_mutex); t = (reg_addr << AR7240_MDIO_CTRL_REG_ADDR_S) | (phy_addr << AR7240_MDIO_CTRL_PHY_ADDR_S) | AR7240_MDIO_CTRL_MASTER_EN | AR7240_MDIO_CTRL_BUSY | AR7240_MDIO_CTRL_CMD_READ; - ar7240sw_reg_write(as, AR7240_REG_MDIO_CTRL, t); - err = ar7240sw_reg_wait(as, AR7240_REG_MDIO_CTRL, - AR7240_MDIO_CTRL_BUSY, 0, 5); - if (err) - return 0xffff; + __ar7240sw_reg_write(mii, AR7240_REG_MDIO_CTRL, t); + err = __ar7240sw_reg_wait(mii, AR7240_REG_MDIO_CTRL, + AR7240_MDIO_CTRL_BUSY, 0, 5); + if (!err) + val = __ar7240sw_reg_read(mii, AR7240_REG_MDIO_CTRL); + mutex_unlock(®_mutex); - t = ar7240sw_reg_read(as, AR7240_REG_MDIO_CTRL); - return (t & AR7240_MDIO_CTRL_DATA_M); + return val & AR7240_MDIO_CTRL_DATA_M; } -static int ar7240sw_phy_write(struct ar7240sw *as, unsigned phy_addr, - unsigned reg_addr, u16 reg_val) +int ar7240sw_phy_write(struct mii_bus *mii, unsigned phy_addr, + unsigned reg_addr, u16 reg_val) { u32 t; int ret; @@ -364,6 +435,7 @@ static int ar7240sw_phy_write(struct ar7240sw *as, unsigned phy_addr, if (phy_addr >= AR7240_NUM_PHYS) return -EINVAL; + mutex_lock(®_mutex); t = (phy_addr << AR7240_MDIO_CTRL_PHY_ADDR_S) | (reg_addr << AR7240_MDIO_CTRL_REG_ADDR_S) | AR7240_MDIO_CTRL_MASTER_EN | @@ -371,34 +443,55 @@ static int ar7240sw_phy_write(struct ar7240sw *as, unsigned phy_addr, AR7240_MDIO_CTRL_CMD_WRITE | reg_val; - ar7240sw_reg_write(as, AR7240_REG_MDIO_CTRL, t); - ret = ar7240sw_reg_wait(as, AR7240_REG_MDIO_CTRL, - AR7240_MDIO_CTRL_BUSY, 0, 5); - return ret; -} - -static int ar7240sw_capture_stats(struct ar7240sw *as) -{ - int ret; - - /* Capture the hardware statistics for all ports */ - ar7240sw_reg_write(as, AR7240_REG_MIB_FUNCTION0, - (AR7240_MIB_FUNC_CAPTURE << AR7240_MIB_FUNC_S)); + __ar7240sw_reg_write(mii, AR7240_REG_MDIO_CTRL, t); + ret = __ar7240sw_reg_wait(mii, AR7240_REG_MDIO_CTRL, + AR7240_MDIO_CTRL_BUSY, 0, 5); + mutex_unlock(®_mutex); - /* Wait for the capturing to complete. */ - ret = ar7240sw_reg_wait(as, 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, AR7240_REG_PORT_CTRL(port), + ar7240sw_reg_write(as->mii_bus, AR7240_REG_PORT_CTRL(port), AR7240_PORT_CTRL_STATE_DISABLED); } +static void ar7240sw_setup(struct ar7240sw *as) +{ + struct mii_bus *mii = as->mii_bus; + + /* Enable CPU port, and disable mirror port */ + ar7240sw_reg_write(mii, AR7240_REG_CPU_PORT, + AR7240_CPU_PORT_EN | + (15 << AR7240_MIRROR_PORT_S)); + + /* Setup TAG priority mapping */ + ar7240sw_reg_write(mii, AR7240_REG_TAG_PRIORITY, 0xfa50); + + /* 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, + AR7240_FLOOD_MASK_BROAD_TO_CPU); + + /* setup MTU */ + ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL, AR7240_GLOBAL_CTRL_MTU_M, + 1536); + + /* setup Service TAG */ + 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; @@ -410,50 +503,27 @@ static int ar7240sw_reset(struct ar7240sw *as) msleep(2); /* Reset the switch. */ - ar7240sw_reg_write(as, AR7240_REG_MASK_CTRL, + ar7240sw_reg_write(mii, AR7240_REG_MASK_CTRL, AR7240_MASK_CTRL_SOFT_RESET); - ret = ar7240sw_reg_wait(as, AR7240_REG_MASK_CTRL, + 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) -{ - /* Enable CPU port, and disable mirror port */ - ar7240sw_reg_write(as, AR7240_REG_CPU_PORT, - AR7240_CPU_PORT_EN | - (15 << AR7240_MIRROR_PORT_S)); - - /* Setup TAG priority mapping */ - ar7240sw_reg_write(as, AR7240_REG_TAG_PRIORITY, 0xfa50); - - /* Enable ARP frame acknowledge */ - ar7240sw_reg_set(as, AR7240_REG_AT_CTRL, AR7240_AT_CTRL_ARP_EN); - /* Enable Broadcast frames transmitted to the CPU */ - ar7240sw_reg_set(as, AR7240_REG_FLOOD_MASK, - AR7240_FLOOD_MASK_BROAD_TO_CPU); - - /* setup MTU */ - ar7240sw_reg_rmw(as, AR7240_REG_GLOBAL_CTRL, AR7240_GLOBAL_CTRL_MTU_M, - 1536); - - /* setup Service TAG */ - ar7240sw_reg_rmw(as, AR7240_REG_SERVICE_TAG, AR7240_SERVICE_TAG_M, 0); + 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; if (port == AR7240_PORT_CPU) { - ar7240sw_reg_write(as, AR7240_REG_PORT_STATUS(port), + ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port), AR7240_PORT_STATUS_SPEED_1000 | AR7240_PORT_STATUS_TXFLOW | AR7240_PORT_STATUS_RXFLOW | @@ -461,19 +531,17 @@ static void ar7240sw_setup_port(struct ar7240sw *as, unsigned port, u8 portmask) AR7240_PORT_STATUS_RXMAC | AR7240_PORT_STATUS_DUPLEX); } else { - ar7240sw_reg_write(as, AR7240_REG_PORT_STATUS(port), + ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port), AR7240_PORT_STATUS_LINK_AUTO); } /* 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))) { @@ -486,32 +554,45 @@ 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); + portmask &= ar7240sw_port_mask_but(as, port); + + ar7240sw_reg_write(mii, AR7240_REG_PORT_CTRL(port), ctrl); + if (sw_is_ar934x(as)) { + u32 vlan1, vlan2; - /* set default VID and and destination ports for this VLAN */ - vlan |= (portmask << AR7240_PORT_VLAN_DEST_PORTS_S); + 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; - ar7240sw_reg_write(as, AR7240_REG_PORT_CTRL(port), ctrl); - ar7240sw_reg_write(as, AR7240_REG_PORT_VLAN(port), 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) { + struct mii_bus *mii = as->mii_bus; u32 t; t = (addr[4] << 8) | addr[5]; - ar7240sw_reg_write(as, AR7240_REG_MAC_ADDR0, t); + ar7240sw_reg_write(mii, AR7240_REG_MAC_ADDR0, t); t = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; - ar7240sw_reg_write(as, AR7240_REG_MAC_ADDR1, t); + ar7240sw_reg_write(mii, AR7240_REG_MAC_ADDR1, t); return 0; } @@ -564,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))) @@ -629,20 +710,79 @@ 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) { - if (ar7240sw_reg_wait(as, AR7240_REG_VTU, AR7240_VTU_ACTIVE, 0, 5)) + struct mii_bus *mii = as->mii_bus; + + if (ar7240sw_reg_wait(mii, AR7240_REG_VTU, AR7240_VTU_ACTIVE, 0, 5)) return; if ((op & AR7240_VTU_OP) == AR7240_VTU_OP_LOAD) { val &= AR7240_VTUDATA_MEMBER; val |= AR7240_VTUDATA_VALID; - ar7240sw_reg_write(as, AR7240_REG_VTU_DATA, val); + ar7240sw_reg_write(mii, AR7240_REG_VTU_DATA, val); } op |= AR7240_VTU_ACTIVE; - ar7240sw_reg_write(as, AR7240_REG_VTU, op); + ar7240sw_reg_write(mii, AR7240_REG_VTU, op); } static int @@ -665,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; @@ -679,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; @@ -689,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; @@ -715,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[] = { @@ -751,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; - ctrl = ar7240sw_reg_read(as, AR7240_REG_MASK_CTRL); + swdev = &as->swdev; - ver = (ctrl >> AR7240_MASK_CTRL_VERSION_S) & AR7240_MASK_CTRL_VERSION_M; - if (ver != 1) { + 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; + } + + 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(as, 0, MII_PHYSID1); - phy_id2 = ar7240sw_phy_read(as, 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; - printk("%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) @@ -811,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) @@ -836,10 +1034,12 @@ 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; } -void __devexit ag71xx_ar7240_cleanup(struct ag71xx *ag) +void ag71xx_ar7240_cleanup(struct ag71xx *ag) { struct ar7240sw *as = ag->phy_priv;