/*
* ar8216.c: AR8216 switch driver
*
- * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
*
* This program is free software; you can redistribute it and/or
mutex_unlock(&bus->mdio_lock);
}
+static inline void
+ar8xxx_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg)
+{
+ bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+ bus->write(bus, phy_addr, MII_ATH_MMD_DATA, reg);
+ bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
+}
+
void
-ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data)
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data)
{
struct mii_bus *bus = priv->mii_bus;
mutex_lock(&bus->mdio_lock);
- bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+ ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data);
mutex_unlock(&bus->mdio_lock);
}
u16
-ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr)
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg)
{
struct mii_bus *bus = priv->mii_bus;
u16 data;
mutex_lock(&bus->mdio_lock);
- bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+ ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
mutex_unlock(&bus->mdio_lock);
if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00))
return;
- port = buf[0] & 0xf;
+ port = buf[0] & 0x7;
/* no need to fix up packets coming from a tagged source */
if (priv->vlan_tagged & (1 << port))
/* make sure no invalid PVIDs get set */
- if (vlan >= dev->vlans)
+ if (vlan < 0 || vlan >= dev->vlans ||
+ port < 0 || port >= AR8X16_MAX_PORTS)
return -EINVAL;
priv->pvid[port] = vlan;
ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+ if (port < 0 || port >= AR8X16_MAX_PORTS)
+ return -EINVAL;
+
*vlan = priv->pvid[port];
return 0;
}
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+ if (val->port_vlan >= AR8X16_MAX_PORTS)
+ return -EINVAL;
+
priv->vlan_id[val->port_vlan] = val->value.i;
return 0;
}
ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
- u8 ports = priv->vlan_table[val->port_vlan];
+ u8 ports;
int i;
+ if (val->port_vlan >= AR8X16_MAX_VLANS)
+ return -EINVAL;
+
+ ports = priv->vlan_table[val->port_vlan];
val->len = 0;
for (i = 0; i < dev->ports; i++) {
struct switch_port *p;
AR8216_PORT_CTRL_MIRROR_TX);
}
+static inline u32
+ar8xxx_age_time_val(int age_time)
+{
+ return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) /
+ AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS;
+}
+
+static inline void
+ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg)
+{
+ u32 age_time = ar8xxx_age_time_val(priv->arl_age_time);
+ ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S);
+}
+
int
ar8xxx_sw_hw_apply(struct switch_dev *dev)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ const struct ar8xxx_chip *chip = priv->chip;
u8 portmask[AR8X16_MAX_PORTS];
int i, j;
portmask[i] |= vp & ~mask;
}
- priv->chip->vtu_load_vlan(priv, priv->vlan_id[j],
- priv->vlan_table[j]);
+ chip->vtu_load_vlan(priv, priv->vlan_id[j],
+ priv->vlan_table[j]);
}
} else {
/* vlan disabled:
/* update the port destination mask registers and tag settings */
for (i = 0; i < dev->ports; i++) {
- priv->chip->setup_port(priv, i, portmask[i]);
+ chip->setup_port(priv, i, portmask[i]);
}
- priv->chip->set_mirror_regs(priv);
+ chip->set_mirror_regs(priv);
+
+ /* set age time */
+ if (chip->reg_arl_ctrl)
+ ar8xxx_set_age_time(priv, chip->reg_arl_ctrl);
mutex_unlock(&priv->reg_mutex);
return 0;
priv->mirror_tx = false;
priv->source_port = 0;
priv->monitor_port = 0;
+ priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME;
chip->init_globals(priv);
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
const struct ar8xxx_chip *chip = priv->chip;
u64 *mib_stats, mib_data;
- int port;
+ unsigned int port;
int ret;
char *buf = priv->buf;
char buf1[64];
ar8xxx_mib_fetch_port_stat(priv, port, false);
len += snprintf(buf + len, sizeof(priv->buf) - len,
- "Port %d MIB counters\n",
- port);
+ "MIB counters\n");
mib_stats = &priv->mib_stats[port * chip->num_mibs];
for (i = 0; i < chip->num_mibs; i++) {
return ret;
}
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ int age_time = val->value.i;
+ u32 age_time_val;
+
+ if (age_time < 0)
+ return -EINVAL;
+
+ age_time_val = ar8xxx_age_time_val(age_time);
+ if (age_time_val == 0 || age_time_val > 0xffff)
+ return -EINVAL;
+
+ priv->arl_age_time = age_time;
+ return 0;
+}
+
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+ val->value.i = priv->arl_age_time;
+ return 0;
+}
+
int
ar8xxx_sw_get_arl_table(struct switch_dev *dev,
const struct switch_attr *attr,
.reg_port_stats_start = 0x19000,
.reg_port_stats_length = 0xa0,
+ .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
.name = "Atheros AR8216",
.ports = AR8216_NUM_PORTS,
.reg_port_stats_start = 0x20000,
.reg_port_stats_length = 0x100,
+ .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
.name = "Atheros AR8236",
.ports = AR8216_NUM_PORTS,
.reg_port_stats_start = 0x20000,
.reg_port_stats_length = 0x100,
+ .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
.name = "Atheros AR8316",
.ports = AR8216_NUM_PORTS,
if (!ar8xxx_has_mib_counters(priv))
return;
- cancel_delayed_work(&priv->mib_work);
+ cancel_delayed_work_sync(&priv->mib_work);
}
static struct ar8xxx_priv *
static bool
ar8xxx_is_possible(struct mii_bus *bus)
{
- unsigned i;
+ unsigned int i, found_phys = 0;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < 5; i++) {
u32 phy_id;
phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16;
phy_id |= mdiobus_read(bus, i, MII_PHYSID2);
- if (!ar8xxx_phy_match(phy_id)) {
+ if (ar8xxx_phy_match(phy_id)) {
+ found_phys++;
+ } else if (phy_id) {
pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n",
dev_name(&bus->dev), i, phy_id);
- return false;
}
}
-
- return true;
+ return !!found_phys;
}
static int
swdev->devname, swdev->name, priv->chip_rev,
dev_name(&priv->mii_bus->dev));
+ list_add(&priv->list, &ar8xxx_dev_list);
+
found:
priv->use_count++;
phydev->priv = priv;
- list_add(&priv->list, &ar8xxx_dev_list);
-
mutex_unlock(&ar8xxx_dev_list_lock);
return 0;
return;
phydev->priv = NULL;
- if (--priv->use_count > 0)
- return;
mutex_lock(&ar8xxx_dev_list_lock);
+
+ if (--priv->use_count > 0) {
+ mutex_unlock(&ar8xxx_dev_list_lock);
+ return;
+ }
+
list_del(&priv->list);
mutex_unlock(&ar8xxx_dev_list_lock);
module_init(ar8xxx_init);
module_exit(ar8xxx_exit);
MODULE_LICENSE("GPL");
-