/* * Driver for the built-in ethernet switch of the Atheros AR7240 SoC * 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 #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_REG_GLOBAL_CTRL 0x30 #define AR7240_GLOBAL_CTRL_MTU_M BITM(11) #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_MIRROR_PORT_M BITM(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_NUM_PHYS 5 #define AR7240_PHY_ID1 0x004d #define AR7240_PHY_ID2 0xd041 #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_FLOOD_MASK_MC_DP(_p) BIT(16 + (_p)) #define AR934X_FLOOD_MASK_BC_DP(_p) BIT(25 + (_p)) #define AR934X_REG_QM_CTRL 0x3c #define AR934X_QM_CTRL_ARP_EN BIT(15) #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_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 mii_bus *switch_mii_bus; struct device_node *of_node; struct device_node *mdio_node; 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]; 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); mutex_lock(&mii->mdio_lock); mii->write(mii, 0x1f, 0x10, mk_high_addr(reg)); lo = (u32) mii->read(mii, phy_addr, phy_reg); hi = (u32) mii->read(mii, phy_addr, phy_reg + 1); mutex_unlock(&mii->mdio_lock); 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); mutex_lock(&mii->mdio_lock); mii->write(mii, 0x1f, 0x10, mk_high_addr(reg)); mii->write(mii, phy_addr, phy_reg + 1, (val >> 16)); mii->write(mii, phy_addr, phy_reg, (val & 0xffff)); mutex_unlock(&mii->mdio_lock); 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; } int ar7240sw_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) { u32 t, val = 0xffff; int err; struct ar7240sw *as = bus->priv; struct mii_bus *mii = as->mii_bus; 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 *bus, int phy_addr, int reg_addr, u16 reg_val) { u32 t; int ret; struct ar7240sw *as = bus->priv; struct mii_bus *mii = as->mii_bus; 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/Multicast frames transmitted to the CPU */ ar7240sw_reg_set(mii, AR934X_REG_FLOOD_MASK, AR934X_FLOOD_MASK_BC_DP(0) | AR934X_FLOOD_MASK_MC_DP(0)); /* setup MTU */ ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL, AR9340_GLOBAL_CTRL_MTU_M, AR9340_GLOBAL_CTRL_MTU_M); /* 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); /* setup MTU */ ar7240sw_reg_rmw(mii, AR7240_REG_GLOBAL_CTRL, AR7240_GLOBAL_CTRL_MTU_M, AR7240_GLOBAL_CTRL_MTU_M); } /* 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; struct mii_bus *swmii = as->switch_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. */ 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++) { ar7240sw_phy_write(swmii, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); ar7240sw_phy_write(swmii, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); } ret = ar7240sw_phy_poll_reset(swmii); if (ret) return ret; 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_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 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 int ar7240_set_mirror_monitor_port(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; int port = val->value.i; if (port > 15) return -EINVAL; ar7240sw_reg_rmw(mii, AR7240_REG_CPU_PORT, AR7240_MIRROR_PORT_M << AR7240_MIRROR_PORT_S, port << AR7240_MIRROR_PORT_S); return 0; } static int ar7240_get_mirror_monitor_port(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 ret; ret = ar7240sw_reg_read(mii, AR7240_REG_CPU_PORT); val->value.i = (ret >> AR7240_MIRROR_PORT_S) & AR7240_MIRROR_PORT_M; return 0; } static int ar7240_set_mirror_rx(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; int port = val->port_vlan; if (port >= dev->ports) return -EINVAL; if (val && val->value.i == 1) ar7240sw_reg_set(mii, AR7240_REG_PORT_CTRL(port), AR7240_PORT_CTRL_MIRROR_RX); else ar7240sw_reg_rmw(mii, AR7240_REG_PORT_CTRL(port), AR7240_PORT_CTRL_MIRROR_RX, 0); return 0; } static int ar7240_get_mirror_rx(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 ctrl; int port = val->port_vlan; if (port >= dev->ports) return -EINVAL; ctrl = ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port)); if ((ctrl & AR7240_PORT_CTRL_MIRROR_RX) == AR7240_PORT_CTRL_MIRROR_RX) val->value.i = 1; else val->value.i = 0; return 0; } static int ar7240_set_mirror_tx(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; int port = val->port_vlan; if (port >= dev->ports) return -EINVAL; if (val && val->value.i == 1) ar7240sw_reg_set(mii, AR7240_REG_PORT_CTRL(port), AR7240_PORT_CTRL_MIRROR_TX); else ar7240sw_reg_rmw(mii, AR7240_REG_PORT_CTRL(port), AR7240_PORT_CTRL_MIRROR_TX, 0); return 0; } static int ar7240_get_mirror_tx(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 ctrl; int port = val->port_vlan; if (port >= dev->ports) return -EINVAL; ctrl = ar7240sw_reg_read(mii, AR7240_REG_PORT_CTRL(port)); if ((ctrl & AR7240_PORT_CTRL_MIRROR_TX) == AR7240_PORT_CTRL_MIRROR_TX) val->value.i = 1; else val->value.i = 0; 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 = "mirror_monitor_port", .description = "Mirror monitor port", .set = ar7240_set_mirror_monitor_port, .get = ar7240_get_mirror_monitor_port, .max = 15 }, }; static struct switch_attr ar7240_port[] = { { .type = SWITCH_TYPE_INT, .name = "enable_mirror_rx", .description = "Enable mirroring of RX packets", .set = ar7240_set_mirror_rx, .get = ar7240_get_mirror_rx, .max = 1 }, { .type = SWITCH_TYPE_INT, .name = "enable_mirror_tx", .description = "Enable mirroring of TX packets", .set = ar7240_set_mirror_tx, .get = ar7240_get_mirror_tx, .max = 1 }, }; 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 int ag71xx_ar7240_probe(struct mdio_device *mdiodev) { struct mii_bus *mii = mdiodev->bus; struct ar7240sw *as; struct switch_dev *swdev; struct reset_control *switch_reset; u32 ctrl; int phy_if_mode, err, i; as = devm_kzalloc(&mdiodev->dev, sizeof(*as), GFP_KERNEL); if (!as) return -ENOMEM; as->mii_bus = mii; as->of_node = mdiodev->dev.of_node; as->mdio_node = of_get_child_by_name(as->of_node, "mdio-bus"); swdev = &as->swdev; switch_reset = devm_reset_control_get_optional(&mdiodev->dev, "switch"); if (switch_reset) { reset_control_assert(switch_reset); msleep(50); reset_control_deassert(switch_reset); msleep(200); } 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"; phy_if_mode = of_get_phy_mode(as->of_node); if (phy_if_mode == PHY_INTERFACE_MODE_GMII) { ar7240sw_reg_set(mii, AR934X_REG_OPER_MODE0, AR934X_OPER_MODE0_MAC_GMII_EN); } else if (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(&mdiodev->dev)); return -EINVAL; } if (of_property_read_bool(as->of_node, "phy4-mii-enable")) { 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(&mdiodev->dev), ctrl); return -EINVAL; } swdev->cpu_port = AR7240_PORT_CPU; swdev->vlans = AR7240_MAX_VLANS; swdev->ops = &ar7240_ops; swdev->alias = dev_name(&mdiodev->dev); if ((err = register_switch(&as->swdev, NULL)) < 0) return err; pr_info("%s: Found an %s\n", dev_name(&mdiodev->dev), swdev->name); as->switch_mii_bus = devm_mdiobus_alloc(&mdiodev->dev); as->switch_mii_bus->name = "ar7240sw_mdio"; as->switch_mii_bus->read = ar7240sw_phy_read; as->switch_mii_bus->write = ar7240sw_phy_write; as->switch_mii_bus->priv = as; as->switch_mii_bus->parent = &mdiodev->dev; snprintf(as->switch_mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(&mdiodev->dev)); if(as->mdio_node) { err = of_mdiobus_register(as->switch_mii_bus, as->mdio_node); if (err) return err; } /* initialize defaults */ for (i = 0; i < AR7240_MAX_VLANS; i++) as->vlan_id[i] = i; as->vlan_table[0] = ar7240sw_port_mask_all(as); ar7240sw_reset(as); ar7240_hw_apply(&as->swdev); rwlock_init(&as->stats_lock); dev_set_drvdata(&mdiodev->dev, as); return 0; } static void ag71xx_ar7240_remove(struct mdio_device *mdiodev) { struct ar7240sw *as = dev_get_drvdata(&mdiodev->dev); if(as->mdio_node) mdiobus_unregister(as->switch_mii_bus); unregister_switch(&as->swdev); } static const struct of_device_id ag71xx_sw_of_match[] = { { .compatible = "qca,ar8216-builtin" }, { .compatible = "qca,ar8229-builtin" }, { /* sentinel */ }, }; static struct mdio_driver ag71xx_sw_driver = { .probe = ag71xx_ar7240_probe, .remove = ag71xx_ar7240_remove, .mdiodrv.driver = { .name = "ag71xx-switch", .of_match_table = ag71xx_sw_of_match, }, }; mdio_module_driver(ag71xx_sw_driver); MODULE_LICENSE("GPL");