/* * Driver for the built-in ethernet switch of the Atheros AR7240 SoC * Copyright (c) 2016-2017 The Linux Foundation. All rights reserved. * Copyright (c) 2010 Gabor Juhos * Copyright (c) 2010 Felix Fietkau * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * */ #include #include #include #include #include #include #include #include "ag71xx.h" #define BITM(_count) (BIT(_count) - 1) #define BITS(_shift, _count) (BITM(_count) << _shift) #define AR7240_REG_MASK_CTRL 0x00 #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 #define AR7240_REG_MAC_ADDR1 0x24 #define AR7240_REG_FLOOD_MASK 0x2c #define AR7240_FLOOD_MASK_BROAD_TO_CPU BIT(26) #define AR7240_FLOOD_MASK_UNICAST_TO_CPU BIT(0) #define AR7240_REG_GLOBAL_CTRL 0x30 #define AR7240_GLOBAL_CTRL_MTU_M BITM(14) #define AR9340_GLOBAL_CTRL_MTU_M BITM(14) #define AR7240_REG_VTU 0x0040 #define AR7240_VTU_OP BITM(3) #define AR7240_VTU_OP_NOOP 0x0 #define AR7240_VTU_OP_FLUSH 0x1 #define AR7240_VTU_OP_LOAD 0x2 #define AR7240_VTU_OP_PURGE 0x3 #define AR7240_VTU_OP_REMOVE_PORT 0x4 #define AR7240_VTU_ACTIVE BIT(3) #define AR7240_VTU_FULL BIT(4) #define AR7240_VTU_PORT BITS(8, 4) #define AR7240_VTU_PORT_S 8 #define AR7240_VTU_VID BITS(16, 12) #define AR7240_VTU_VID_S 16 #define AR7240_VTU_PRIO BITS(28, 3) #define AR7240_VTU_PRIO_S 28 #define AR7240_VTU_PRIO_EN BIT(31) #define AR7240_REG_VTU_DATA 0x0044 #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 #define AR7240_REG_SERVICE_TAG 0x74 #define AR7240_SERVICE_TAG_M BITM(16) #define AR7240_REG_CPU_PORT 0x78 #define AR7240_MIRROR_PORT_S 4 #define AR7240_CPU_PORT_EN BIT(8) #define AR7240_REG_MIB_FUNCTION0 0x80 #define AR7240_MIB_TIMER_M BITM(16) #define AR7240_MIB_AT_HALF_EN BIT(16) #define AR7240_MIB_BUSY BIT(17) #define AR7240_MIB_FUNC_S 24 #define AR7240_MIB_FUNC_M BITM(3) #define AR7240_MIB_FUNC_NO_OP 0x0 #define AR7240_MIB_FUNC_FLUSH 0x1 #define AR7240_MIB_FUNC_CAPTURE 0x3 #define AR7240_REG_MDIO_CTRL 0x98 #define AR7240_MDIO_CTRL_DATA_M BITM(16) #define AR7240_MDIO_CTRL_REG_ADDR_S 16 #define AR7240_MDIO_CTRL_PHY_ADDR_S 21 #define AR7240_MDIO_CTRL_CMD_WRITE 0 #define AR7240_MDIO_CTRL_CMD_READ BIT(27) #define AR7240_MDIO_CTRL_MASTER_EN BIT(30) #define AR7240_MDIO_CTRL_BUSY BIT(31) #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 #define AR7240_PORT_STATUS_SPEED_1000 2 #define AR7240_PORT_STATUS_TXMAC BIT(2) #define AR7240_PORT_STATUS_RXMAC BIT(3) #define AR7240_PORT_STATUS_TXFLOW BIT(4) #define AR7240_PORT_STATUS_RXFLOW BIT(5) #define AR7240_PORT_STATUS_DUPLEX BIT(6) #define AR7240_PORT_STATUS_LINK_UP BIT(8) #define AR7240_PORT_STATUS_LINK_AUTO BIT(9) #define AR7240_PORT_STATUS_LINK_PAUSE BIT(10) #define AR7240_REG_PORT_CTRL(_port) (AR7240_REG_PORT_BASE((_port)) + 0x04) #define AR7240_PORT_CTRL_STATE_M BITM(3) #define AR7240_PORT_CTRL_STATE_DISABLED 0 #define AR7240_PORT_CTRL_STATE_BLOCK 1 #define AR7240_PORT_CTRL_STATE_LISTEN 2 #define AR7240_PORT_CTRL_STATE_LEARN 3 #define AR7240_PORT_CTRL_STATE_FORWARD 4 #define AR7240_PORT_CTRL_LEARN_LOCK BIT(7) #define AR7240_PORT_CTRL_VLAN_MODE_S 8 #define AR7240_PORT_CTRL_VLAN_MODE_KEEP 0 #define AR7240_PORT_CTRL_VLAN_MODE_STRIP 1 #define AR7240_PORT_CTRL_VLAN_MODE_ADD 2 #define AR7240_PORT_CTRL_VLAN_MODE_DOUBLE_TAG 3 #define AR7240_PORT_CTRL_IGMP_SNOOP BIT(10) #define AR7240_PORT_CTRL_HEADER BIT(11) #define AR7240_PORT_CTRL_MAC_LOOP BIT(12) #define AR7240_PORT_CTRL_SINGLE_VLAN BIT(13) #define AR7240_PORT_CTRL_LEARN BIT(14) #define AR7240_PORT_CTRL_DOUBLE_TAG BIT(15) #define AR7240_PORT_CTRL_MIRROR_TX BIT(16) #define AR7240_PORT_CTRL_MIRROR_RX BIT(17) #define AR7240_REG_PORT_VLAN(_port) (AR7240_REG_PORT_BASE((_port)) + 0x08) #define AR7240_PORT_VLAN_DEFAULT_ID_S 0 #define AR7240_PORT_VLAN_DEST_PORTS_S 16 #define AR7240_PORT_VLAN_MODE_S 30 #define AR7240_PORT_VLAN_MODE_PORT_ONLY 0 #define AR7240_PORT_VLAN_MODE_PORT_FALLBACK 1 #define AR7240_PORT_VLAN_MODE_VLAN_ONLY 2 #define AR7240_PORT_VLAN_MODE_SECURE 3 #define AR7240_REG_STATS_BASE(_port) (0x20000 + (_port) * 0x100) #define AR7240_STATS_RXBROAD 0x00 #define AR7240_STATS_RXPAUSE 0x04 #define AR7240_STATS_RXMULTI 0x08 #define AR7240_STATS_RXFCSERR 0x0c #define AR7240_STATS_RXALIGNERR 0x10 #define AR7240_STATS_RXRUNT 0x14 #define AR7240_STATS_RXFRAGMENT 0x18 #define AR7240_STATS_RX64BYTE 0x1c #define AR7240_STATS_RX128BYTE 0x20 #define AR7240_STATS_RX256BYTE 0x24 #define AR7240_STATS_RX512BYTE 0x28 #define AR7240_STATS_RX1024BYTE 0x2c #define AR7240_STATS_RX1518BYTE 0x30 #define AR7240_STATS_RXMAXBYTE 0x34 #define AR7240_STATS_RXTOOLONG 0x38 #define AR7240_STATS_RXGOODBYTE 0x3c #define AR7240_STATS_RXBADBYTE 0x44 #define AR7240_STATS_RXOVERFLOW 0x4c #define AR7240_STATS_FILTERED 0x50 #define AR7240_STATS_TXBROAD 0x54 #define AR7240_STATS_TXPAUSE 0x58 #define AR7240_STATS_TXMULTI 0x5c #define AR7240_STATS_TXUNDERRUN 0x60 #define AR7240_STATS_TX64BYTE 0x64 #define AR7240_STATS_TX128BYTE 0x68 #define AR7240_STATS_TX256BYTE 0x6c #define AR7240_STATS_TX512BYTE 0x70 #define AR7240_STATS_TX1024BYTE 0x74 #define AR7240_STATS_TX1518BYTE 0x78 #define AR7240_STATS_TXMAXBYTE 0x7c #define AR7240_STATS_TXOVERSIZE 0x80 #define AR7240_STATS_TXBYTE 0x84 #define AR7240_STATS_TXCOLLISION 0x8c #define AR7240_STATS_TXABORTCOL 0x90 #define AR7240_STATS_TXMULTICOL 0x94 #define AR7240_STATS_TXSINGLECOL 0x98 #define AR7240_STATS_TXEXCDEFER 0x9c #define AR7240_STATS_TXDEFER 0xa0 #define AR7240_STATS_TXLATECOL 0xa4 #define AR7240_PORT_CPU 0 #define AR7240_NUM_PORTS 6 #define AR7240_PHY_ID1 0x004d #define AR7240_PHY_ID2 0xd041 #define AR7240_PHY_SYS_CTRL_MODE 0x5 #define AR7240_PHY_CLASS_MODE BIT(1) #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_FLOOD_MASK 0x2c #define AR934X_IGMP_JOIN_LEAVE_DP BIT(8) #define AR934X_FLOOD_MASK_MC_DP(_p) BIT(16 + (_p)) #define AR934X_FLOOD_MASK_MC BITS(16, 7) #define AR934X_FLOOD_MASK_BC_DP(_p) BIT(25 + (_p)) #define AR934X_FLOOD_MASK_UC_DP(_p) BIT(0 + (_p)) #define AR934X_REG_QM_CTRL 0x3c #define AR934X_IGMP_COPY_EN BIT(11) #define AR934X_QM_CTRL_ARP_EN BIT(15) #define AR934X_IGMP_V3_EN BIT(19) #define AR934X_IGMP_JOIN_NEW_EN BIT(22) #define AR934X_REG_AT_CTRL 0x5c #define AR934X_AT_CTRL_AGE_TIME BITS(0, 15) #define AR934X_AT_CTRL_AGE_EN BIT(17) #define AR934X_AT_CTRL_LEARN_CHANGE BIT(18) #define AR934X_MIB_ENABLE BIT(30) #define AR934X_REG_PORT_BASE(_port) (0x100 + (_port) * 0x100) #define AR934X_REG_PORT_CONTROL(_port) (AR934X_REG_PORT_BASE((_port)) + 0x04) #define AR934X_PORT_CONTROL_IGMP_MLD_EN BIT(10) #define AR934X_PORT_CONTROL_IGMP_JOIN_EN BIT(20) #define AR934X_PORT_CONTROL_IGMP_LEAVE_EN BIT(21) #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_port_stat { unsigned long rx_broadcast; unsigned long rx_pause; unsigned long rx_multicast; unsigned long rx_fcs_error; unsigned long rx_align_error; unsigned long rx_runt; unsigned long rx_fragments; unsigned long rx_64byte; unsigned long rx_128byte; unsigned long rx_256byte; unsigned long rx_512byte; unsigned long rx_1024byte; unsigned long rx_1518byte; unsigned long rx_maxbyte; unsigned long rx_toolong; unsigned long rx_good_byte; unsigned long rx_bad_byte; unsigned long rx_overflow; unsigned long filtered; unsigned long tx_broadcast; unsigned long tx_pause; unsigned long tx_multicast; unsigned long tx_underrun; unsigned long tx_64byte; unsigned long tx_128byte; unsigned long tx_256byte; unsigned long tx_512byte; unsigned long tx_1024byte; unsigned long tx_1518byte; unsigned long tx_maxbyte; unsigned long tx_oversize; unsigned long tx_byte; unsigned long tx_collision; unsigned long tx_abortcol; unsigned long tx_multicol; unsigned long tx_singlecol; unsigned long tx_excdefer; unsigned long tx_defer; unsigned long tx_xlatecol; }; struct ar7240sw { struct mii_bus *mii_bus; struct ag71xx_switch_platform_data *swdata; struct switch_dev swdev; int num_ports; int num_ports_linkup; 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]; rwlock_t stats_lock; struct ar7240sw_port_stat port_stats[AR7240_NUM_PORTS]; }; struct ar7240sw_hw_stat { char string[ETH_GSTRING_LEN]; int sizeof_stat; int reg; }; static DEFINE_MUTEX(reg_mutex); static inline int sw_is_ar7240(struct ar7240sw *as) { 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); } static inline u16 mk_phy_reg(u32 reg) { return (reg << 1) & 0x1e; } static inline u16 mk_high_addr(u32 reg) { return (reg >> 7) & 0x1ff; } 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; 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; 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) { u32 ret; mutex_lock(®_mutex); ret = __ar7240sw_reg_read(mii, reg_addr); mutex_unlock(®_mutex); return ret; } static void ar7240sw_reg_write(struct mii_bus *mii, u32 reg_addr, u32 reg_val) { mutex_lock(®_mutex); __ar7240sw_reg_write(mii, reg_addr, reg_val); mutex_unlock(®_mutex); } static u32 ar7240sw_reg_rmw(struct mii_bus *mii, u32 reg, u32 mask, u32 val) { u32 t; mutex_lock(®_mutex); t = __ar7240sw_reg_read(mii, reg); t &= ~mask; t |= val; __ar7240sw_reg_write(mii, reg, t); mutex_unlock(®_mutex); return t; } static void ar7240sw_reg_set(struct mii_bus *mii, u32 reg, u32 val) { u32 t; mutex_lock(®_mutex); t = __ar7240sw_reg_read(mii, reg); t |= val; __ar7240sw_reg_write(mii, reg, t); mutex_unlock(®_mutex); } 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(mii, reg); if ((t & mask) == val) return 0; usleep_range(1000, 2000); } return -ETIMEDOUT; } static int ar7240sw_reg_wait(struct mii_bus *mii, u32 reg, u32 mask, u32 val, unsigned timeout) { 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(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); return val & AR7240_MDIO_CTRL_DATA_M; } int ar7240sw_phy_write(struct mii_bus *mii, unsigned phy_addr, unsigned reg_addr, u16 reg_val) { u32 t; int ret; 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 | AR7240_MDIO_CTRL_BUSY | AR7240_MDIO_CTRL_CMD_WRITE | reg_val; __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); return ret; } static int ar7240sw_capture_stats(struct ar7240sw *as) { struct mii_bus *mii = as->mii_bus; int port; int ret; write_lock(&as->stats_lock); /* Capture the hardware statistics for all ports */ ar7240sw_reg_rmw(mii, AR7240_REG_MIB_FUNCTION0, (AR7240_MIB_FUNC_M << AR7240_MIB_FUNC_S), (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); if (ret) goto unlock; for (port = 0; port < AR7240_NUM_PORTS; port++) { unsigned int base; struct ar7240sw_port_stat *stats; base = AR7240_REG_STATS_BASE(port); stats = &as->port_stats[port]; #define READ_STAT(_r) ar7240sw_reg_read(mii, base + AR7240_STATS_ ## _r) stats->rx_good_byte += READ_STAT(RXGOODBYTE); stats->tx_byte += READ_STAT(TXBYTE); #undef READ_STAT } ret = 0; unlock: write_unlock(&as->stats_lock); 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 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); if (sw_is_ar934x(as)) { /* Enable aging, MAC replacing */ ar7240sw_reg_write(mii, AR934X_REG_AT_CTRL, 0x2b /* 5 min age time */ | AR934X_AT_CTRL_AGE_EN | AR934X_AT_CTRL_LEARN_CHANGE); /* Enable ARP frame acknowledge */ ar7240sw_reg_set(mii, AR934X_REG_QM_CTRL, AR934X_QM_CTRL_ARP_EN); /* Enable Broadcast frames transmitted to the CPU */ ar7240sw_reg_set(mii, AR934X_REG_FLOOD_MASK, AR934X_FLOOD_MASK_BC_DP(0)); /* Enable Multicast frames transmitted to the CPU */ ar7240sw_reg_set(mii, AR934X_REG_FLOOD_MASK, AR934X_FLOOD_MASK_MC_DP(0)); /* Enable Unicast frames transmitted to the CPU */ ar7240sw_reg_set(mii, AR934X_REG_FLOOD_MASK, AR934X_FLOOD_MASK_UC_DP(0)); /* Enable MIB counters */ ar7240sw_reg_set(mii, AR7240_REG_MIB_FUNCTION0, AR934X_MIB_ENABLE); } else { /* 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); /* Enable Unicast frames transmitted to the CPU */ ar7240sw_reg_set(mii, AR7240_REG_FLOOD_MASK, AR7240_FLOOD_MASK_UNICAST_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); } /* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */ static int ar7240sw_phy_poll_reset(struct mii_bus *bus) { const unsigned int sleep_msecs = 20; int ret, elapsed, i; for (elapsed = sleep_msecs; elapsed <= 600; elapsed += sleep_msecs) { msleep(sleep_msecs); for (i = 0; i < AR7240_NUM_PHYS; i++) { ret = ar7240sw_phy_read(bus, i, MII_BMCR); if (ret < 0) return ret; if (ret & BMCR_RESET) break; if (i == AR7240_NUM_PHYS - 1) { usleep_range(1000, 2000); return 0; } } } return -ETIMEDOUT; } static int ar7240sw_reset(struct ar7240sw *as) { struct mii_bus *mii = as->mii_bus; int ret; int i; u8 mask; mask = ~as->swdata->phy_poll_mask; /* 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. */ usleep_range(2000, 3000); /* 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); /* setup PHYs */ for (i = 0; i < AR7240_NUM_PHYS; i++) { if (!(mask & BIT(i))) continue; ar7240sw_phy_write(mii, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); ar7240sw_phy_write(mii, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); } ret = ar7240sw_phy_poll_reset(mii); if (ret) return ret; /* set up class mode */ if (as->swdata->phy_classab_en) { u16 val; for (i = 0; i < AR7240_NUM_PHYS; i++) { ar7240sw_phy_write(mii, i, 0x1d, AR7240_PHY_SYS_CTRL_MODE); val = ar7240sw_phy_read(mii, i, 0x1e); val &= ~(AR7240_PHY_CLASS_MODE); ar7240sw_phy_write(mii, i, 0x1e, val); } } 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 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(mii, AR7240_REG_PORT_STATUS(port), AR7240_PORT_STATUS_SPEED_1000 | AR7240_PORT_STATUS_TXFLOW | AR7240_PORT_STATUS_RXFLOW | AR7240_PORT_STATUS_TXMAC | AR7240_PORT_STATUS_RXMAC | AR7240_PORT_STATUS_DUPLEX); } else { ar7240sw_reg_write(mii, AR7240_REG_PORT_STATUS(port), AR7240_PORT_STATUS_LINK_AUTO); } /* Set the default VID for this port */ if (as->vlan) { vid = as->vlan_id[as->pvid[port]]; mode = AR7240_PORT_VLAN_MODE_SECURE; } else { vid = port; mode = AR7240_PORT_VLAN_MODE_PORT_ONLY; } if (as->vlan) { if (as->vlan_tagged & BIT(port)) ctrl |= AR7240_PORT_CTRL_VLAN_MODE_ADD << AR7240_PORT_CTRL_VLAN_MODE_S; else ctrl |= AR7240_PORT_CTRL_VLAN_MODE_STRIP << AR7240_PORT_CTRL_VLAN_MODE_S; } else { ctrl |= AR7240_PORT_CTRL_VLAN_MODE_KEEP << AR7240_PORT_CTRL_VLAN_MODE_S; } if (!portmask) { if (port == AR7240_PORT_CPU) portmask = ar7240sw_port_mask_but(as, AR7240_PORT_CPU); else 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 */ 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; 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) { struct mii_bus *mii = as->mii_bus; u32 t; t = (addr[4] << 8) | addr[5]; ar7240sw_reg_write(mii, AR7240_REG_MAC_ADDR0, t); t = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; ar7240sw_reg_write(mii, AR7240_REG_MAC_ADDR1, t); return 0; } static int ar7240_set_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ar7240sw *as = sw_to_ar7240(dev); as->vlan_id[val->port_vlan] = val->value.i; return 0; } static int ar7240_get_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ar7240sw *as = sw_to_ar7240(dev); val->value.i = as->vlan_id[val->port_vlan]; return 0; } static int ar7240_set_pvid(struct switch_dev *dev, int port, int vlan) { struct ar7240sw *as = sw_to_ar7240(dev); /* make sure no invalid PVIDs get set */ if (vlan >= dev->vlans) return -EINVAL; as->pvid[port] = vlan; return 0; } static int ar7240_get_pvid(struct switch_dev *dev, int port, int *vlan) { struct ar7240sw *as = sw_to_ar7240(dev); *vlan = as->pvid[port]; return 0; } static int ar7240_get_ports(struct switch_dev *dev, struct switch_val *val) { struct ar7240sw *as = sw_to_ar7240(dev); u8 ports = as->vlan_table[val->port_vlan]; int i; val->len = 0; for (i = 0; i < as->swdev.ports; i++) { struct switch_port *p; if (!(ports & (1 << i))) continue; p = &val->value.ports[val->len++]; p->id = i; if (as->vlan_tagged & (1 << i)) p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); else p->flags = 0; } return 0; } static int ar7240_set_ports(struct switch_dev *dev, struct switch_val *val) { struct ar7240sw *as = sw_to_ar7240(dev); u8 *vt = &as->vlan_table[val->port_vlan]; int i, j; *vt = 0; for (i = 0; i < val->len; i++) { struct switch_port *p = &val->value.ports[i]; if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) as->vlan_tagged |= (1 << p->id); else { as->vlan_tagged &= ~(1 << p->id); as->pvid[p->id] = val->port_vlan; /* make sure that an untagged port does not * appear in other vlans */ for (j = 0; j < AR7240_MAX_VLANS; j++) { if (j == val->port_vlan) continue; as->vlan_table[j] &= ~(1 << p->id); } } *vt |= 1 << p->id; } return 0; } static int ar7240_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ar7240sw *as = sw_to_ar7240(dev); as->vlan = !!val->value.i; return 0; } static int ar7240_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ar7240sw *as = sw_to_ar7240(dev); val->value.i = as->vlan; return 0; } static int ar7240_set_max_frame_size(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; ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL, AR7240_GLOBAL_CTRL_MTU_M, val->value.i + 8 + 2); return 0; } static int ar7240_get_max_frame_size(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { u32 v = 0; struct ar7240sw *as = sw_to_ar7240(dev); struct mii_bus *mii = as->mii_bus; v = ar7240sw_reg_read(mii, AR7240_REG_GLOBAL_CTRL); v &= AR7240_GLOBAL_CTRL_MTU_M; val->value.i = v; return 0; } static int ar7240_igmp_snooping(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { u32 v = 0, i; struct ar7240sw *as = sw_to_ar7240(dev); struct mii_bus *mii = as->mii_bus; if (!sw_is_ar934x(as)) return 0; if (val->value.i) { printk(KERN_INFO "ar934x build in switch(s27): Enable igmp snooping function.\n"); for (i = 0; i < as->swdev.ports; i++) { v = ar7240sw_reg_read(mii, AR934X_REG_PORT_CONTROL(i)); v |= AR934X_PORT_CONTROL_IGMP_MLD_EN | AR934X_PORT_CONTROL_IGMP_JOIN_EN | AR934X_PORT_CONTROL_IGMP_LEAVE_EN; ar7240sw_reg_write(mii, AR934X_REG_PORT_CONTROL(i), v); } v = ar7240sw_reg_read(mii, AR934X_REG_QM_CTRL); v |= AR934X_IGMP_COPY_EN | AR934X_IGMP_V3_EN | AR934X_IGMP_JOIN_NEW_EN; ar7240sw_reg_write(mii, AR934X_REG_QM_CTRL, v); v = ar7240sw_reg_read(mii, AR934X_REG_FLOOD_MASK); v |= AR934X_IGMP_JOIN_LEAVE_DP; v &= ~(AR934X_FLOOD_MASK_MC); ar7240sw_reg_write(mii, AR934X_REG_FLOOD_MASK, v); } else { printk(KERN_INFO "ar934x build in switch(s27): Disable igmp snooping.\n"); for (i = 0; i < as->swdev.ports; i++) { v = ar7240sw_reg_read(mii, AR934X_REG_PORT_CONTROL(i)); v &= ~(AR934X_PORT_CONTROL_IGMP_MLD_EN | AR934X_PORT_CONTROL_IGMP_JOIN_EN | AR934X_PORT_CONTROL_IGMP_LEAVE_EN); ar7240sw_reg_write(mii, AR934X_REG_PORT_CONTROL(i), v); } v = ar7240sw_reg_read(mii, AR934X_REG_QM_CTRL); v &= ~(AR934X_IGMP_COPY_EN | AR934X_IGMP_V3_EN | AR934X_IGMP_JOIN_NEW_EN); ar7240sw_reg_write(mii, AR934X_REG_QM_CTRL, v); v = ar7240sw_reg_read(mii, AR934X_REG_FLOOD_MASK); v &= ~(AR934X_IGMP_JOIN_LEAVE_DP); v |= AR934X_FLOOD_MASK_MC; ar7240sw_reg_write(mii, AR934X_REG_FLOOD_MASK, v); } return 0; } static void ar7240_vtu_op(struct ar7240sw *as, u32 op, u32 val) { 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(mii, AR7240_REG_VTU_DATA, val); } op |= AR7240_VTU_ACTIVE; ar7240sw_reg_write(mii, AR7240_REG_VTU, op); } static int ar7240_hw_apply(struct switch_dev *dev) { struct ar7240sw *as = sw_to_ar7240(dev); u8 portmask[AR7240_NUM_PORTS]; int i, j; /* flush all vlan translation unit entries */ ar7240_vtu_op(as, AR7240_VTU_OP_FLUSH, 0); memset(portmask, 0, sizeof(portmask)); if (as->vlan) { /* calculate the port destination masks and load vlans * into the vlan translation unit */ for (j = 0; j < AR7240_MAX_VLANS; j++) { u8 vp = as->vlan_table[j]; if (!vp) continue; for (i = 0; i < as->swdev.ports; i++) { u8 mask = (1 << i); if (vp & mask) portmask[i] |= vp & ~mask; } ar7240_vtu_op(as, AR7240_VTU_OP_LOAD | (as->vlan_id[j] << AR7240_VTU_VID_S), as->vlan_table[j]); } } else { /* vlan disabled: * isolate all ports, but connect them to the cpu port */ for (i = 0; i < as->swdev.ports; i++) { if (i == AR7240_PORT_CPU) continue; portmask[i] = 1 << AR7240_PORT_CPU; portmask[AR7240_PORT_CPU] |= (1 << i); } } /* update the port destination mask registers and tag settings */ for (i = 0; i < as->swdev.ports; i++) ar7240sw_setup_port(as, i, portmask[i]); return 0; } static int ar7240_reset_switch(struct switch_dev *dev) { struct ar7240sw *as = sw_to_ar7240(dev); ar7240sw_reset(as); return 0; } static int ar7240_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link) { struct ar7240sw *as = sw_to_ar7240(dev); struct mii_bus *mii = as->mii_bus; u32 status; if (port > AR7240_NUM_PORTS) return -EINVAL; status = ar7240sw_reg_read(mii, AR7240_REG_PORT_STATUS(port)); link->aneg = !!(status & AR7240_PORT_STATUS_LINK_AUTO); if (link->aneg) { link->link = !!(status & AR7240_PORT_STATUS_LINK_UP); if (!link->link) return 0; } else { link->link = true; } link->duplex = !!(status & AR7240_PORT_STATUS_DUPLEX); link->tx_flow = !!(status & AR7240_PORT_STATUS_TXFLOW); link->rx_flow = !!(status & AR7240_PORT_STATUS_RXFLOW); switch (status & AR7240_PORT_STATUS_SPEED_M) { case AR7240_PORT_STATUS_SPEED_10: link->speed = SWITCH_PORT_SPEED_10; break; case AR7240_PORT_STATUS_SPEED_100: link->speed = SWITCH_PORT_SPEED_100; break; case AR7240_PORT_STATUS_SPEED_1000: link->speed = SWITCH_PORT_SPEED_1000; break; } return 0; } static int ar7240_get_port_stats(struct switch_dev *dev, int port, struct switch_port_stats *stats) { struct ar7240sw *as = sw_to_ar7240(dev); if (port > AR7240_NUM_PORTS) return -EINVAL; ar7240sw_capture_stats(as); read_lock(&as->stats_lock); stats->rx_bytes = as->port_stats[port].rx_good_byte; stats->tx_bytes = as->port_stats[port].tx_byte; read_unlock(&as->stats_lock); return 0; } static struct switch_attr ar7240_globals[] = { { .type = SWITCH_TYPE_INT, .name = "enable_vlan", .description = "Enable VLAN mode", .set = ar7240_set_vlan, .get = ar7240_get_vlan, .max = 1 }, { .type = SWITCH_TYPE_INT, .name = "max_frame_size", .description = "Max frame size can be rx and tx by mac", .set = ar7240_set_max_frame_size, .get = ar7240_get_max_frame_size, .max = 9018 }, { .type = SWITCH_TYPE_INT, .name = "igmp_snooping", .description = "Enable/Disable igmp snooping func on switch", .set = ar7240_igmp_snooping }, }; static struct switch_attr ar7240_port[] = { }; static struct switch_attr ar7240_vlan[] = { { .type = SWITCH_TYPE_INT, .name = "vid", .description = "VLAN ID", .set = ar7240_set_vid, .get = ar7240_get_vid, .max = 4094, }, }; static const struct switch_dev_ops ar7240_ops = { .attr_global = { .attr = ar7240_globals, .n_attr = ARRAY_SIZE(ar7240_globals), }, .attr_port = { .attr = ar7240_port, .n_attr = ARRAY_SIZE(ar7240_port), }, .attr_vlan = { .attr = ar7240_vlan, .n_attr = ARRAY_SIZE(ar7240_vlan), }, .get_port_pvid = ar7240_get_pvid, .set_port_pvid = ar7240_set_pvid, .get_vlan_ports = ar7240_get_ports, .set_vlan_ports = ar7240_set_ports, .apply_config = ar7240_hw_apply, .reset_switch = ar7240_reset_switch, .get_port_link = ar7240_get_port_link, .get_port_stats = ar7240_get_port_stats, }; 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; 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", dev_name(&mii->dev), phy_id1, phy_id2); return NULL; } as = kzalloc(sizeof(*as), GFP_KERNEL); if (!as) return NULL; 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"; swdev->ports = AR7240_NUM_PORTS - 1; } 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", dev_name(&mii->dev)); goto err_free; } if (as->swdata->phy4_mii_en) { ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE1, AR934X_REG_OPER_MODE1_PHY4_MII_EN); swdev->ports = AR7240_NUM_PORTS - 1; } else { swdev->ports = AR7240_NUM_PORTS; } } else { pr_err("%s: unsupported chip, ctrl=%08x\n", dev_name(&mii->dev), ctrl); goto err_free; } swdev->cpu_port = AR7240_PORT_CPU; swdev->vlans = AR7240_MAX_VLANS; swdev->ops = &ar7240_ops; if (register_switch(&as->swdev, ag->dev) < 0) goto err_free; pr_info("%s: Found an %s\n", dev_name(&mii->dev), swdev->name); /* initialize defaults */ for (i = 0; i < AR7240_MAX_VLANS; i++) as->vlan_id[i] = i; as->vlan_table[0] = ar7240sw_port_mask_all(as); return as; err_free: kfree(as); return NULL; } static u16 ar7240_phy_link[AR7240_NUM_PHYS] = {0}; static u16 ar7240_phy_speed[AR7240_NUM_PHYS] = {0}; static u16 ar7240_phy_duplex[AR7240_NUM_PHYS] = {0}; static port_link_notify_func ar7240_port_link_callback; void ar7240_port_link_notify_register(port_link_notify_func func) { u32 phy_id; ar7240_port_link_callback = func; for (phy_id = 0; phy_id < AR7240_NUM_PHYS; phy_id++) { if (ar7240_phy_link[phy_id]) (*func)(phy_id + 1, ar7240_phy_link[phy_id], ar7240_phy_speed[phy_id], ar7240_phy_duplex[phy_id]); } } EXPORT_SYMBOL(ar7240_port_link_notify_register); static void link_function(struct work_struct *work) { struct ag71xx *ag = container_of(work, struct ag71xx, link_work.work); struct ar7240sw *as = ag->phy_priv; unsigned long flags; u8 mask; int i; int status = 0, link_up_count = 0; mask = ~as->swdata->phy_poll_mask; for (i = 0; i < AR7240_NUM_PHYS; i++) { int link; u16 phy_status, speed, duplex; if (!(mask & BIT(i))) continue; phy_status = ar7240sw_phy_read(ag->mii_bus, i, MII_PHY_SPEC_STATUS); link = (phy_status & MII_PHY_LINK_STATUS) ? 1 : 0; if (link) { status = 1; link_up_count++; } if (link != ar7240_phy_link[i]) { speed = (phy_status >> 14) & 0x3; duplex = (phy_status >> 13) & 0x1; ar7240_phy_link[i] = link; ar7240_phy_speed[i] = speed; ar7240_phy_duplex[i] = duplex; if (ar7240_port_link_callback) (*ar7240_port_link_callback)(i + 1, link, speed, duplex); } } if (link_up_count != 0 && as->num_ports_linkup != link_up_count) { /* Set flowcontrol threshold based on linkup count */ if (link_up_count == 1) ar7240sw_reg_write(as->mii_bus, 0x34, 0x16602090); else if (link_up_count == 2) ar7240sw_reg_write(as->mii_bus, 0x34, 0x90bcc0ff); else if (link_up_count > 2) ar7240sw_reg_write(as->mii_bus, 0x34, 0xb0c0c0ff); as->num_ports_linkup = link_up_count; } 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) { struct ar7240sw *as = ag->phy_priv; ar7240sw_reset(as); ag->speed = SPEED_1000; 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 ag71xx_ar7240_init(struct ag71xx *ag) { struct ar7240sw *as; as = ar7240_probe(ag); if (!as) return -ENODEV; ag->phy_priv = as; ar7240sw_reset(as); rwlock_init(&as->stats_lock); INIT_DELAYED_WORK(&ag->link_work, link_function); return 0; } void ag71xx_ar7240_cleanup(struct ag71xx *ag) { struct ar7240sw *as = ag->phy_priv; if (!as) return; unregister_switch(&as->swdev); kfree(as); ag->phy_priv = NULL; }