[PATCH 1/2] 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. git-svn-id: svn://svn.openwrt.org/openwrt/trunk@20722 3c298f89-4303-0410-b956-a3cf2f4a3e73master
parent
9e277d3878
commit
fd27f8a1d9
|
@ -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"
|
|
@ -14,3 +14,13 @@ config interface lan
|
||||||
config interface wan
|
config interface wan
|
||||||
option ifname eth0
|
option ifname eth0
|
||||||
option proto dhcp
|
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"
|
||||||
|
|
|
@ -44,6 +44,7 @@ CONFIG_AR71XX_MACH_WRT400N=y
|
||||||
CONFIG_AR71XX_MACH_WZR_HP_G300NH=y
|
CONFIG_AR71XX_MACH_WZR_HP_G300NH=y
|
||||||
CONFIG_AR71XX_NVRAM=y
|
CONFIG_AR71XX_NVRAM=y
|
||||||
CONFIG_AR71XX_WDT=y
|
CONFIG_AR71XX_WDT=y
|
||||||
|
CONFIG_AR8216_PHY=y
|
||||||
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
|
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
|
||||||
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
|
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
|
||||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||||
|
|
|
@ -44,6 +44,7 @@ CONFIG_AR71XX_MACH_WRT400N=y
|
||||||
CONFIG_AR71XX_MACH_WZR_HP_G300NH=y
|
CONFIG_AR71XX_MACH_WZR_HP_G300NH=y
|
||||||
CONFIG_AR71XX_NVRAM=y
|
CONFIG_AR71XX_NVRAM=y
|
||||||
CONFIG_AR71XX_WDT=y
|
CONFIG_AR71XX_WDT=y
|
||||||
|
CONFIG_AR8216_PHY=y
|
||||||
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
|
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
|
||||||
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
|
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
|
||||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||||
|
|
|
@ -43,6 +43,7 @@ CONFIG_AR71XX_MACH_WRT400N=y
|
||||||
CONFIG_AR71XX_MACH_WZR_HP_G300NH=y
|
CONFIG_AR71XX_MACH_WZR_HP_G300NH=y
|
||||||
CONFIG_AR71XX_NVRAM=y
|
CONFIG_AR71XX_NVRAM=y
|
||||||
CONFIG_AR71XX_WDT=y
|
CONFIG_AR71XX_WDT=y
|
||||||
|
CONFIG_AR8216_PHY=y
|
||||||
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
|
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
|
||||||
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
|
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
|
||||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||||
|
|
|
@ -239,6 +239,7 @@ static void __init rb450_generic_setup(int gige)
|
||||||
ar71xx_add_device_mdio(0xffffffe0);
|
ar71xx_add_device_mdio(0xffffffe0);
|
||||||
|
|
||||||
ar71xx_eth0_data.phy_if_mode = (gige) ? PHY_INTERFACE_MODE_RGMII : PHY_INTERFACE_MODE_MII;
|
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.speed = (gige) ? SPEED_1000 : SPEED_100;
|
||||||
ar71xx_eth0_data.duplex = DUPLEX_FULL;
|
ar71xx_eth0_data.duplex = DUPLEX_FULL;
|
||||||
|
|
||||||
|
|
|
@ -177,6 +177,7 @@ static void __init ubnt_rspro_setup(void)
|
||||||
ar71xx_eth0_data.phy_mask = UBNT_RSPRO_WAN_PHYMASK;
|
ar71xx_eth0_data.phy_mask = UBNT_RSPRO_WAN_PHYMASK;
|
||||||
|
|
||||||
ar71xx_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_RGMII;
|
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.speed = SPEED_1000;
|
||||||
ar71xx_eth1_data.duplex = DUPLEX_FULL;
|
ar71xx_eth1_data.duplex = DUPLEX_FULL;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
#include <linux/etherdevice.h>
|
#include <linux/etherdevice.h>
|
||||||
#include "ar8216.h"
|
#include "ar8216.h"
|
||||||
|
|
||||||
|
/* size of the vlan table */
|
||||||
|
#define AR8X16_MAX_VLANS 128
|
||||||
|
|
||||||
struct ar8216_priv {
|
struct ar8216_priv {
|
||||||
struct switch_dev dev;
|
struct switch_dev dev;
|
||||||
|
@ -39,11 +41,13 @@ struct ar8216_priv {
|
||||||
void (*write)(struct ar8216_priv *priv, int reg, u32 val);
|
void (*write)(struct ar8216_priv *priv, int reg, u32 val);
|
||||||
const struct net_device_ops *ndo_old;
|
const struct net_device_ops *ndo_old;
|
||||||
struct net_device_ops ndo;
|
struct net_device_ops ndo;
|
||||||
|
struct mutex reg_mutex;
|
||||||
|
int chip;
|
||||||
|
|
||||||
/* all fields below are cleared on reset */
|
/* all fields below are cleared on reset */
|
||||||
bool vlan;
|
bool vlan;
|
||||||
u16 vlan_id[AR8216_NUM_VLANS];
|
u16 vlan_id[AR8X16_MAX_VLANS];
|
||||||
u8 vlan_table[AR8216_NUM_VLANS];
|
u8 vlan_table[AR8X16_MAX_VLANS];
|
||||||
u8 vlan_tagged;
|
u8 vlan_tagged;
|
||||||
u16 pvid[AR8216_NUM_PORTS];
|
u16 pvid[AR8216_NUM_PORTS];
|
||||||
};
|
};
|
||||||
|
@ -110,6 +114,30 @@ ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val)
|
||||||
return v;
|
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
|
static int
|
||||||
ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
|
ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
|
||||||
struct switch_val *val)
|
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 */
|
/* make sure no invalid PVIDs get set */
|
||||||
|
|
||||||
if (vlan >= AR8216_NUM_VLANS)
|
if (vlan >= dev->vlans)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
priv->pvid[port] = vlan;
|
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
|
/* make sure that an untagged port does not
|
||||||
* appear in other vlans */
|
* 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)
|
if (j == val->port_vlan)
|
||||||
continue;
|
continue;
|
||||||
priv->vlan_table[j] &= ~(1 << p->id);
|
priv->vlan_table[j] &= ~(1 << p->id);
|
||||||
|
@ -383,6 +411,7 @@ ar8216_hw_apply(struct switch_dev *dev)
|
||||||
u8 portmask[AR8216_NUM_PORTS];
|
u8 portmask[AR8216_NUM_PORTS];
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
mutex_lock(&priv->reg_mutex);
|
||||||
/* flush all vlan translation unit entries */
|
/* flush all vlan translation unit entries */
|
||||||
ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
|
ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
|
||||||
|
|
||||||
|
@ -390,7 +419,7 @@ ar8216_hw_apply(struct switch_dev *dev)
|
||||||
if (priv->vlan) {
|
if (priv->vlan) {
|
||||||
/* calculate the port destination masks and load vlans
|
/* calculate the port destination masks and load vlans
|
||||||
* into the vlan translation unit */
|
* 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];
|
u8 vp = priv->vlan_table[j];
|
||||||
|
|
||||||
if (!vp)
|
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_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
|
||||||
AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
|
AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
|
||||||
AR8216_PORT_CTRL_LEARN |
|
AR8216_PORT_CTRL_LEARN |
|
||||||
(priv->vlan && i == AR8216_PORT_CPU ?
|
(priv->vlan && i == AR8216_PORT_CPU && (priv->chip == AR8216) ?
|
||||||
AR8216_PORT_CTRL_HEADER : 0) |
|
AR8216_PORT_CTRL_HEADER : 0) |
|
||||||
(egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
|
(egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
|
||||||
(AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_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) |
|
(ingress << AR8216_PORT_VLAN_MODE_S) |
|
||||||
(pvid << AR8216_PORT_VLAN_DEFAULT_ID_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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,9 +563,10 @@ ar8216_reset_switch(struct switch_dev *dev)
|
||||||
struct ar8216_priv *priv = to_ar8216(dev);
|
struct ar8216_priv *priv = to_ar8216(dev);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
mutex_lock(&priv->reg_mutex);
|
||||||
memset(&priv->vlan, 0, sizeof(struct ar8216_priv) -
|
memset(&priv->vlan, 0, sizeof(struct ar8216_priv) -
|
||||||
offsetof(struct ar8216_priv, vlan));
|
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;
|
priv->vlan_id[i] = i;
|
||||||
}
|
}
|
||||||
for (i = 0; i < AR8216_NUM_PORTS; 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) {
|
if (i == AR8216_PORT_CPU) {
|
||||||
priv->write(priv, AR8216_REG_PORT_STATUS(i),
|
priv->write(priv, AR8216_REG_PORT_STATUS(i),
|
||||||
AR8216_PORT_STATUS_LINK_UP |
|
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_TXMAC |
|
||||||
AR8216_PORT_STATUS_RXMAC |
|
AR8216_PORT_STATUS_RXMAC |
|
||||||
|
((priv->chip == AR8316) ? AR8216_PORT_STATUS_RXFLOW : 0) |
|
||||||
|
((priv->chip == AR8316) ? AR8216_PORT_STATUS_TXFLOW : 0) |
|
||||||
AR8216_PORT_STATUS_DUPLEX);
|
AR8216_PORT_STATUS_DUPLEX);
|
||||||
} else {
|
} else {
|
||||||
priv->write(priv, AR8216_REG_PORT_STATUS(i),
|
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! */
|
/* XXX: undocumented magic from atheros, required! */
|
||||||
priv->write(priv, 0x38, 0xc000050e);
|
priv->write(priv, 0x38, 0xc000050e);
|
||||||
|
|
||||||
ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL,
|
if (priv->chip == AR8216) {
|
||||||
AR8216_GCTRL_MTU, 1518 + 8 + 2);
|
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);
|
return ar8216_hw_apply(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,37 +620,74 @@ ar8216_config_init(struct phy_device *pdev)
|
||||||
struct net_device *dev = pdev->attached_dev;
|
struct net_device *dev = pdev->attached_dev;
|
||||||
int ret;
|
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);
|
priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL);
|
||||||
if (priv == NULL)
|
if (priv == NULL)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
priv->phy = pdev;
|
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->read = ar8216_mii_read;
|
||||||
priv->write = ar8216_mii_write;
|
priv->write = ar8216_mii_write;
|
||||||
memcpy(&priv->dev, &athdev, sizeof(struct switch_dev));
|
memcpy(&priv->dev, &athdev, sizeof(struct switch_dev));
|
||||||
pdev->priv = priv;
|
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) {
|
if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->chip == AR8316) {
|
||||||
|
ret = ar8316_hw_init(priv);
|
||||||
|
if (ret) {
|
||||||
|
kfree(priv);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = ar8216_reset_switch(&priv->dev);
|
ret = ar8216_reset_switch(&priv->dev);
|
||||||
if (ret)
|
if (ret) {
|
||||||
|
kfree(priv);
|
||||||
goto done;
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
dev->phy_ptr = priv;
|
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;
|
/* VID fixup only needed on ar8216 */
|
||||||
memcpy(&priv->ndo, priv->ndo_old, sizeof(struct net_device_ops));
|
if (pdev->addr == 0 && priv->chip == AR8216) {
|
||||||
priv->ndo.ndo_start_xmit = ar8216_mangle_tx;
|
pdev->pkt_align = 2;
|
||||||
dev->netdev_ops = &priv->ndo;
|
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:
|
done:
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -550,28 +697,39 @@ static int
|
||||||
ar8216_read_status(struct phy_device *phydev)
|
ar8216_read_status(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
struct ar8216_priv *priv = phydev->priv;
|
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->duplex = DUPLEX_FULL;
|
||||||
phydev->link = 1;
|
phydev->link = 1;
|
||||||
|
|
||||||
/* flush the address translation unit */
|
/* flush the address translation unit */
|
||||||
if (ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0))
|
mutex_lock(&priv->reg_mutex);
|
||||||
return -ETIMEDOUT;
|
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;
|
phydev->state = PHY_RUNNING;
|
||||||
netif_carrier_on(phydev->attached_dev);
|
netif_carrier_on(phydev->attached_dev);
|
||||||
phydev->adjust_link(phydev->attached_dev);
|
phydev->adjust_link(phydev->attached_dev);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ar8216_config_aneg(struct phy_device *phydev)
|
ar8216_config_aneg(struct phy_device *phydev)
|
||||||
{
|
{
|
||||||
return 0;
|
if (phydev->addr == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return genphy_config_aneg(phydev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -579,16 +737,10 @@ ar8216_probe(struct phy_device *pdev)
|
||||||
{
|
{
|
||||||
struct ar8216_priv priv;
|
struct ar8216_priv priv;
|
||||||
|
|
||||||
u8 id, rev;
|
|
||||||
u32 val;
|
|
||||||
|
|
||||||
priv.phy = pdev;
|
priv.phy = pdev;
|
||||||
val = ar8216_mii_read(&priv, AR8216_REG_CTRL);
|
if (ar8216_id_chip(&priv) == UNKNOWN) {
|
||||||
rev = val & AR8216_CTRL_REVISION;
|
|
||||||
id = (val & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S;
|
|
||||||
if ((id != 1) || (rev != 1))
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,7 +755,8 @@ ar8216_remove(struct phy_device *pdev)
|
||||||
|
|
||||||
if (priv->ndo_old && dev)
|
if (priv->ndo_old && dev)
|
||||||
dev->netdev_ops = priv->ndo_old;
|
dev->netdev_ops = priv->ndo_old;
|
||||||
unregister_switch(&priv->dev);
|
if (pdev->addr == 0)
|
||||||
|
unregister_switch(&priv->dev);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,7 +787,9 @@ static struct switch_dev athdev = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct phy_driver ar8216_driver = {
|
static struct phy_driver ar8216_driver = {
|
||||||
.name = "Atheros AR8216",
|
.phy_id = 0x004d0000,
|
||||||
|
.name = "Atheros AR8216/AR8316",
|
||||||
|
.phy_id_mask = 0xffff0000,
|
||||||
.features = PHY_BASIC_FEATURES,
|
.features = PHY_BASIC_FEATURES,
|
||||||
.probe = ar8216_probe,
|
.probe = ar8216_probe,
|
||||||
.remove = ar8216_remove,
|
.remove = ar8216_remove,
|
||||||
|
|
|
@ -22,6 +22,11 @@
|
||||||
#define AR8216_PORT_CPU 0
|
#define AR8216_PORT_CPU 0
|
||||||
#define AR8216_NUM_PORTS 6
|
#define AR8216_NUM_PORTS 6
|
||||||
#define AR8216_NUM_VLANS 16
|
#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_REG_CTRL 0x0000
|
||||||
#define AR8216_CTRL_REVISION BITS(0, 8)
|
#define AR8216_CTRL_REVISION BITS(0, 8)
|
||||||
|
@ -30,8 +35,13 @@
|
||||||
#define AR8216_CTRL_VERSION_S 8
|
#define AR8216_CTRL_VERSION_S 8
|
||||||
#define AR8216_CTRL_RESET BIT(31)
|
#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_REG_GLOBAL_CTRL 0x0030
|
||||||
#define AR8216_GCTRL_MTU BITS(0, 11)
|
#define AR8216_GCTRL_MTU BITS(0, 11)
|
||||||
|
#define AR8316_GCTRL_MTU BITS(0, 14)
|
||||||
|
|
||||||
#define AR8216_REG_VTU 0x0040
|
#define AR8216_REG_VTU 0x0040
|
||||||
#define AR8216_VTU_OP BITS(0, 3)
|
#define AR8216_VTU_OP BITS(0, 3)
|
||||||
|
@ -75,6 +85,11 @@
|
||||||
#define AR8216_ATU_ADDR1 BITS(16, 8)
|
#define AR8216_ATU_ADDR1 BITS(16, 8)
|
||||||
#define AR8216_ATU_ADDR0 BITS(24, 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_PORT_OFFSET(_i) (0x0100 * (_i + 1))
|
||||||
#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000)
|
#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000)
|
||||||
#define AR8216_PORT_STATUS_SPEED BITS(0,2)
|
#define AR8216_PORT_STATUS_SPEED BITS(0,2)
|
||||||
|
@ -162,4 +177,11 @@ enum {
|
||||||
AR8216_PORT_STATE_FORWARD = 4
|
AR8216_PORT_STATE_FORWARD = 4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* device */
|
||||||
|
enum {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
AR8216 = 8216,
|
||||||
|
AR8316 = 8316
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue