From: Jo-Philipp Wich Date: Mon, 5 Apr 2010 23:03:16 +0000 (+0000) Subject: Add support for the ar8316 switch. This patch enhances the ar8216 driver with ar8316... X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=23bebe33a7b79bd6e639386f6c5e30595cb0fc33;p=openwrt%2Fstaging%2Faparcar.git Add support for the ar8316 switch. This patch enhances the ar8216 driver with ar8316 support and fixes some minor issues with the ar8216 driver itself. It should not break anything, but isn't tested on ar8216 devices. [PATCH 2/2] ar71xx: Add the ar8316 driver to rs pro/rb-450g. Add the ar8216 driver to the ar71xx target, and add network configurations for the RouterStation Pro and the RouterBoard RB-450G. SVN-Revision: 20722 --- diff --git a/target/linux/ar71xx/base-files/etc/defconfig/rb-450g/network b/target/linux/ar71xx/base-files/etc/defconfig/rb-450g/network new file mode 100644 index 0000000000..f678030ce2 --- /dev/null +++ b/target/linux/ar71xx/base-files/etc/defconfig/rb-450g/network @@ -0,0 +1,26 @@ +config interface loopback + option ifname lo + option proto static + option ipaddr 127.0.0.1 + option netmask 255.0.0.0 + +config interface lan + option ifname eth1 + option type bridge + option proto static + option ipaddr 192.168.1.1 + option netmask 255.255.255.0 + +config interface wan + option ifname eth0 + option proto dhcp + +config switch + option name eth1 + option reset 1 + option enable_vlan 1 + +config switch_vlan + option device eth1 + option vlan 1 + option ports "0 1 2 3 4" diff --git a/target/linux/ar71xx/base-files/etc/defconfig/routerstation-pro/network b/target/linux/ar71xx/base-files/etc/defconfig/routerstation-pro/network index ff7b42d875..f678030ce2 100644 --- a/target/linux/ar71xx/base-files/etc/defconfig/routerstation-pro/network +++ b/target/linux/ar71xx/base-files/etc/defconfig/routerstation-pro/network @@ -14,3 +14,13 @@ config interface lan config interface wan option ifname eth0 option proto dhcp + +config switch + option name eth1 + option reset 1 + option enable_vlan 1 + +config switch_vlan + option device eth1 + option vlan 1 + option ports "0 1 2 3 4" diff --git a/target/linux/ar71xx/config-2.6.32 b/target/linux/ar71xx/config-2.6.32 index 7770266724..417d718223 100644 --- a/target/linux/ar71xx/config-2.6.32 +++ b/target/linux/ar71xx/config-2.6.32 @@ -44,6 +44,7 @@ CONFIG_AR71XX_MACH_WRT400N=y CONFIG_AR71XX_MACH_WZR_HP_G300NH=y CONFIG_AR71XX_NVRAM=y CONFIG_AR71XX_WDT=y +CONFIG_AR8216_PHY=y # CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U64 is not set CONFIG_ARCH_HIBERNATION_POSSIBLE=y diff --git a/target/linux/ar71xx/config-2.6.33 b/target/linux/ar71xx/config-2.6.33 index 1c29cc9f54..e147b01495 100644 --- a/target/linux/ar71xx/config-2.6.33 +++ b/target/linux/ar71xx/config-2.6.33 @@ -44,6 +44,7 @@ CONFIG_AR71XX_MACH_WRT400N=y CONFIG_AR71XX_MACH_WZR_HP_G300NH=y CONFIG_AR71XX_NVRAM=y CONFIG_AR71XX_WDT=y +CONFIG_AR8216_PHY=y # CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U64 is not set CONFIG_ARCH_HIBERNATION_POSSIBLE=y diff --git a/target/linux/ar71xx/config-2.6.34 b/target/linux/ar71xx/config-2.6.34 index 888a0e4d70..549b7a8c7a 100644 --- a/target/linux/ar71xx/config-2.6.34 +++ b/target/linux/ar71xx/config-2.6.34 @@ -43,6 +43,7 @@ CONFIG_AR71XX_MACH_WRT400N=y CONFIG_AR71XX_MACH_WZR_HP_G300NH=y CONFIG_AR71XX_NVRAM=y CONFIG_AR71XX_WDT=y +CONFIG_AR8216_PHY=y # CONFIG_ARCH_HAS_ILOG2_U32 is not set # CONFIG_ARCH_HAS_ILOG2_U64 is not set CONFIG_ARCH_HIBERNATION_POSSIBLE=y diff --git a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb4xx.c b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb4xx.c index f3fa4cf30e..57c06d8c0c 100644 --- a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb4xx.c +++ b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-rb4xx.c @@ -239,6 +239,7 @@ static void __init rb450_generic_setup(int gige) ar71xx_add_device_mdio(0xffffffe0); ar71xx_eth0_data.phy_if_mode = (gige) ? PHY_INTERFACE_MODE_RGMII : PHY_INTERFACE_MODE_MII; + ar71xx_eth0_data.phy_mask = (gige) ? (1 << 0) : 0; ar71xx_eth0_data.speed = (gige) ? SPEED_1000 : SPEED_100; ar71xx_eth0_data.duplex = DUPLEX_FULL; diff --git a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-ubnt.c b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-ubnt.c index 3cd5a63ef5..04a08dcf3e 100644 --- a/target/linux/ar71xx/files/arch/mips/ar71xx/mach-ubnt.c +++ b/target/linux/ar71xx/files/arch/mips/ar71xx/mach-ubnt.c @@ -177,6 +177,7 @@ static void __init ubnt_rspro_setup(void) ar71xx_eth0_data.phy_mask = UBNT_RSPRO_WAN_PHYMASK; ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII; + ar71xx_eth1_data.phy_mask = UBNT_RSPRO_LAN_PHYMASK; ar71xx_eth1_data.speed = SPEED_1000; ar71xx_eth1_data.duplex = DUPLEX_FULL; diff --git a/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c b/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c index 0a6eed9577..394e50d479 100644 --- a/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic-2.6/files/drivers/net/phy/ar8216.c @@ -31,6 +31,8 @@ #include #include "ar8216.h" +/* size of the vlan table */ +#define AR8X16_MAX_VLANS 128 struct ar8216_priv { struct switch_dev dev; @@ -39,11 +41,13 @@ struct ar8216_priv { void (*write)(struct ar8216_priv *priv, int reg, u32 val); const struct net_device_ops *ndo_old; struct net_device_ops ndo; + struct mutex reg_mutex; + int chip; /* all fields below are cleared on reset */ bool vlan; - u16 vlan_id[AR8216_NUM_VLANS]; - u8 vlan_table[AR8216_NUM_VLANS]; + u16 vlan_id[AR8X16_MAX_VLANS]; + u8 vlan_table[AR8X16_MAX_VLANS]; u8 vlan_tagged; u16 pvid[AR8216_NUM_PORTS]; }; @@ -110,6 +114,30 @@ ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) return v; } +static inline int +ar8216_id_chip(struct ar8216_priv *priv) +{ + u32 val; + u16 id; + val = ar8216_mii_read(priv, AR8216_REG_CTRL); + id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION); + switch (id) { + case 0x0101: + return AR8216; + case 0x1001: + return AR8316; + default: + printk(KERN_ERR + "ar8216: Unknown Atheros device [ver=%d, rev=%d, phy_id=%04x%04x]\n", + (int)(val >> AR8216_CTRL_VERSION_S), + (int)(val & AR8216_CTRL_REVISION), + priv->phy->bus->read(priv->phy->bus, priv->phy->addr, 2), + priv->phy->bus->read(priv->phy->bus, priv->phy->addr, 3)); + + return UNKNOWN; + } +} + static int ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -136,7 +164,7 @@ ar8216_set_pvid(struct switch_dev *dev, int port, int vlan) /* make sure no invalid PVIDs get set */ - if (vlan >= AR8216_NUM_VLANS) + if (vlan >= dev->vlans) return -EINVAL; priv->pvid[port] = vlan; @@ -336,7 +364,7 @@ ar8216_set_ports(struct switch_dev *dev, struct switch_val *val) /* make sure that an untagged port does not * appear in other vlans */ - for (j = 0; j < AR8216_NUM_VLANS; j++) { + for (j = 0; j < AR8X16_MAX_VLANS; j++) { if (j == val->port_vlan) continue; priv->vlan_table[j] &= ~(1 << p->id); @@ -383,6 +411,7 @@ ar8216_hw_apply(struct switch_dev *dev) u8 portmask[AR8216_NUM_PORTS]; int i, j; + mutex_lock(&priv->reg_mutex); /* flush all vlan translation unit entries */ ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); @@ -390,7 +419,7 @@ ar8216_hw_apply(struct switch_dev *dev) if (priv->vlan) { /* calculate the port destination masks and load vlans * into the vlan translation unit */ - for (j = 0; j < AR8216_NUM_VLANS; j++) { + for (j = 0; j < AR8X16_MAX_VLANS; j++) { u8 vp = priv->vlan_table[j]; if (!vp) @@ -446,7 +475,7 @@ ar8216_hw_apply(struct switch_dev *dev) AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE | AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK, AR8216_PORT_CTRL_LEARN | - (priv->vlan && i == AR8216_PORT_CPU ? + (priv->vlan && i == AR8216_PORT_CPU && (priv->chip == AR8216) ? AR8216_PORT_CTRL_HEADER : 0) | (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); @@ -458,7 +487,73 @@ ar8216_hw_apply(struct switch_dev *dev) (ingress << AR8216_PORT_VLAN_MODE_S) | (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); } + mutex_unlock(&priv->reg_mutex); + return 0; +} + +static int +ar8316_hw_init(struct ar8216_priv *priv) { + static int initialized; + int i; + u32 val; + struct mii_bus *bus; + + if (initialized) + return 0; + + val = priv->read(priv, 0x8); + + if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* value taken from Ubiquiti RouterStation Pro */ + if (val == 0x81461bea) { + /* switch already intialized by bootloader */ + initialized = true; + return 0; + } + priv->write(priv, 0x8, 0x81461bea); + } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) { + /* value taken from AVM Fritz!Box 7390 sources */ + if (val == 0x010e5b71) { + /* switch already initialized by bootloader */ + initialized = true; + return 0; + } + priv->write(priv, 0x8, 0x010e5b71); + } else { + /* no known value for phy interface */ + printk(KERN_ERR "ar8316: unsupported mii mode: %d.\n", + priv->phy->interface); + return -EINVAL; + } + + /* standard atheros magic */ + priv->write(priv, 0x38, 0xc000050e); + /* Initialize the ports */ + bus = priv->phy->bus; + for (i = 0; i < 5; i++) { + if ((i == 4) && + priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { + /* work around for phy4 rgmii mode */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x12); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x480c); + /* rx delay */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x0); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x824e); + /* tx delay */ + bus->write(bus, i, MII_ATH_DBG_ADDR, 0x5); + bus->write(bus, i, MII_ATH_DBG_DATA, 0x3d47); + msleep(1000); + } + + /* initialize the port itself */ + bus->write(bus, i, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); + bus->write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); + bus->write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + msleep(1000); + } + initialized = true; return 0; } @@ -468,9 +563,10 @@ ar8216_reset_switch(struct switch_dev *dev) struct ar8216_priv *priv = to_ar8216(dev); int i; + mutex_lock(&priv->reg_mutex); memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - offsetof(struct ar8216_priv, vlan)); - for (i = 0; i < AR8216_NUM_VLANS; i++) { + for (i = 0; i < AR8X16_MAX_VLANS; i++) { priv->vlan_id[i] = i; } for (i = 0; i < AR8216_NUM_PORTS; i++) { @@ -485,9 +581,12 @@ ar8216_reset_switch(struct switch_dev *dev) if (i == AR8216_PORT_CPU) { priv->write(priv, AR8216_REG_PORT_STATUS(i), AR8216_PORT_STATUS_LINK_UP | - AR8216_PORT_SPEED_100M | + ((priv->chip == AR8316) ? + AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC | + ((priv->chip == AR8316) ? AR8216_PORT_STATUS_RXFLOW : 0) | + ((priv->chip == AR8316) ? AR8216_PORT_STATUS_TXFLOW : 0) | AR8216_PORT_STATUS_DUPLEX); } else { priv->write(priv, AR8216_REG_PORT_STATUS(i), @@ -497,9 +596,20 @@ ar8216_reset_switch(struct switch_dev *dev) /* XXX: undocumented magic from atheros, required! */ priv->write(priv, 0x38, 0xc000050e); - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, - AR8216_GCTRL_MTU, 1518 + 8 + 2); + if (priv->chip == AR8216) { + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8216_GCTRL_MTU, 1518 + 8 + 2); + } else if (priv->chip == AR8316) { + /* enable jumbo frames */ + ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8316_GCTRL_MTU, 9018 + 8 + 2); + } + if (priv->chip == AR8316) { + /* enable cpu port to receive multicast and broadcast frames */ + priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); + } + mutex_unlock(&priv->reg_mutex); return ar8216_hw_apply(dev); } @@ -510,37 +620,74 @@ ar8216_config_init(struct phy_device *pdev) struct net_device *dev = pdev->attached_dev; int ret; - printk("%s: AR8216 PHY driver attached.\n", pdev->attached_dev->name); - pdev->supported = ADVERTISED_100baseT_Full; - pdev->advertising = ADVERTISED_100baseT_Full; - priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); if (priv == NULL) return -ENOMEM; priv->phy = pdev; + + priv->chip = ar8216_id_chip(priv); + + printk(KERN_INFO "%s: AR%d PHY driver attached.\n", + pdev->attached_dev->name, priv->chip); + + if (pdev->addr != 0) { + if (priv->chip == AR8316) { + pdev->supported |= SUPPORTED_1000baseT_Full; + pdev->advertising |= ADVERTISED_1000baseT_Full; + } + kfree(priv); + return 0; + } + + pdev->supported = priv->chip == AR8316 ? + SUPPORTED_1000baseT_Full : SUPPORTED_100baseT_Full; + pdev->advertising = pdev->supported; + + mutex_init(&priv->reg_mutex); priv->read = ar8216_mii_read; priv->write = ar8216_mii_write; memcpy(&priv->dev, &athdev, sizeof(struct switch_dev)); pdev->priv = priv; + + if (priv->chip == AR8316) { + priv->dev.name = "Atheros AR8316"; + priv->dev.vlans = AR8X16_MAX_VLANS; + /* port 5 connected to the other mac, therefore unusable */ + priv->dev.ports = (AR8216_NUM_PORTS - 1); + } + if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) { kfree(priv); goto done; } + if (priv->chip == AR8316) { + ret = ar8316_hw_init(priv); + if (ret) { + kfree(priv); + goto done; + } + } + ret = ar8216_reset_switch(&priv->dev); - if (ret) + if (ret) { + kfree(priv); goto done; + } dev->phy_ptr = priv; - pdev->pkt_align = 2; - pdev->netif_receive_skb = ar8216_netif_receive_skb; - pdev->netif_rx = ar8216_netif_rx; - priv->ndo_old = dev->netdev_ops; - memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops)); - priv->ndo.ndo_start_xmit = ar8216_mangle_tx; - dev->netdev_ops = &priv->ndo; + /* VID fixup only needed on ar8216 */ + if (pdev->addr == 0 && priv->chip == AR8216) { + pdev->pkt_align = 2; + pdev->netif_receive_skb = ar8216_netif_receive_skb; + pdev->netif_rx = ar8216_netif_rx; + priv->ndo_old = dev->netdev_ops; + memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops)); + priv->ndo.ndo_start_xmit = ar8216_mangle_tx; + dev->netdev_ops = &priv->ndo; + } done: return ret; @@ -550,28 +697,39 @@ static int ar8216_read_status(struct phy_device *phydev) { struct ar8216_priv *priv = phydev->priv; + int ret; + if (phydev->addr != 0) { + return genphy_read_status(phydev); + } - phydev->speed = SPEED_100; + phydev->speed = priv->chip == AR8316 ? SPEED_1000 : SPEED_100; phydev->duplex = DUPLEX_FULL; phydev->link = 1; /* flush the address translation unit */ - if (ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0)) - return -ETIMEDOUT; + mutex_lock(&priv->reg_mutex); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); - priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + if (!ret) + priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + else + ret = -ETIMEDOUT; + mutex_unlock(&priv->reg_mutex); phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); phydev->adjust_link(phydev->attached_dev); - return 0; + return ret; } static int ar8216_config_aneg(struct phy_device *phydev) { - return 0; + if (phydev->addr == 0) + return 0; + + return genphy_config_aneg(phydev); } static int @@ -579,16 +737,10 @@ ar8216_probe(struct phy_device *pdev) { struct ar8216_priv priv; - u8 id, rev; - u32 val; - priv.phy = pdev; - val = ar8216_mii_read(&priv, AR8216_REG_CTRL); - rev = val & AR8216_CTRL_REVISION; - id = (val & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S; - if ((id != 1) || (rev != 1)) + if (ar8216_id_chip(&priv) == UNKNOWN) { return -ENODEV; - + } return 0; } @@ -603,7 +755,8 @@ ar8216_remove(struct phy_device *pdev) if (priv->ndo_old && dev) dev->netdev_ops = priv->ndo_old; - unregister_switch(&priv->dev); + if (pdev->addr == 0) + unregister_switch(&priv->dev); kfree(priv); } @@ -634,7 +787,9 @@ static struct switch_dev athdev = { }; static struct phy_driver ar8216_driver = { - .name = "Atheros AR8216", + .phy_id = 0x004d0000, + .name = "Atheros AR8216/AR8316", + .phy_id_mask = 0xffff0000, .features = PHY_BASIC_FEATURES, .probe = ar8216_probe, .remove = ar8216_remove, diff --git a/target/linux/generic-2.6/files/drivers/net/phy/ar8216.h b/target/linux/generic-2.6/files/drivers/net/phy/ar8216.h index 0ba79fb3cd..5a8fa3c003 100644 --- a/target/linux/generic-2.6/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic-2.6/files/drivers/net/phy/ar8216.h @@ -22,6 +22,11 @@ #define AR8216_PORT_CPU 0 #define AR8216_NUM_PORTS 6 #define AR8216_NUM_VLANS 16 +#define AR8316_NUM_VLANS 4096 + +/* Atheros specific MII registers */ +#define MII_ATH_DBG_ADDR 0x1d +#define MII_ATH_DBG_DATA 0x1e #define AR8216_REG_CTRL 0x0000 #define AR8216_CTRL_REVISION BITS(0, 8) @@ -30,8 +35,13 @@ #define AR8216_CTRL_VERSION_S 8 #define AR8216_CTRL_RESET BIT(31) +#define AR8216_REG_FLOOD_MASK 0x002C +#define AR8216_FM_UNI_DEST_PORTS BITS(0, 6) +#define AR8216_FM_MULTI_DEST_PORTS BITS(16, 6) + #define AR8216_REG_GLOBAL_CTRL 0x0030 #define AR8216_GCTRL_MTU BITS(0, 11) +#define AR8316_GCTRL_MTU BITS(0, 14) #define AR8216_REG_VTU 0x0040 #define AR8216_VTU_OP BITS(0, 3) @@ -75,6 +85,11 @@ #define AR8216_ATU_ADDR1 BITS(16, 8) #define AR8216_ATU_ADDR0 BITS(24, 8) +#define AR8216_REG_ATU_CTRL 0x005C +#define AR8216_ATU_CTRL_AGE_EN BIT(17) +#define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16) +#define AR8216_ATU_CTRL_AGE_TIME_S 0 + #define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1)) #define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000) #define AR8216_PORT_STATUS_SPEED BITS(0,2) @@ -162,4 +177,11 @@ enum { AR8216_PORT_STATE_FORWARD = 4 }; +/* device */ +enum { + UNKNOWN = 0, + AR8216 = 8216, + AR8316 = 8316 +}; + #endif