openwrt/target/linux/generic/files/drivers/net/phy/ar8216.c
Felix Fietkau 6ce848f622 ar8216: decrease page switch wait time
Until a few years ago the page switch wait time was set to msleep(1)
what was changed to usleep_range(1000, 2000) later.

I can not imagine that a low-level operation like switching page
on register level takes so much time.
Most likely the value of 1ms was initially set to check whether
it fixes an issue and then remained w/o further checking whether
also a smaller value would be sufficient.

Now the wait time is set to 5us and I successfully tested this on
AR8327. IMHO 5us should be plenty of time for all supported chips.
However I couldn't test this due to missing hardware.

If other chips should need a longer wait time we can add the
wait time as a parameter to the ar8xxx_chip struct.

Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>

SVN-Revision: 44103
2015-01-24 19:42:01 +00:00

1996 lines
45 KiB
C

/*
* ar8216.c: AR8216 switch driver
*
* Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org>
* Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/if.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/if_ether.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
#include <linux/bitops.h>
#include <net/genetlink.h>
#include <linux/switch.h>
#include <linux/delay.h>
#include <linux/phy.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/lockdep.h>
#include <linux/ar8216_platform.h>
#include <linux/workqueue.h>
#include <linux/version.h>
#include "ar8216.h"
extern const struct ar8xxx_chip ar8327_chip;
extern const struct ar8xxx_chip ar8337_chip;
#define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */
#define MIB_DESC(_s , _o, _n) \
{ \
.size = (_s), \
.offset = (_o), \
.name = (_n), \
}
static const struct ar8xxx_mib_desc ar8216_mibs[] = {
MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"),
MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"),
MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"),
MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"),
MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"),
MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"),
MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"),
MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"),
MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"),
MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"),
MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"),
MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"),
MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"),
MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"),
MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"),
MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"),
MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"),
MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"),
MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"),
MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"),
MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"),
MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"),
MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"),
MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"),
MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"),
MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"),
MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"),
MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"),
MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"),
MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"),
MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"),
MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"),
MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"),
MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"),
MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"),
MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"),
MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"),
};
const struct ar8xxx_mib_desc ar8236_mibs[39] = {
MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"),
MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"),
MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"),
MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"),
MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"),
MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"),
MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"),
MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"),
MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"),
MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"),
MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"),
MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"),
MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"),
MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"),
MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"),
MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"),
MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"),
MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"),
MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"),
MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"),
MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"),
MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"),
MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"),
MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"),
MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"),
MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"),
MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"),
MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"),
MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"),
MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"),
MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"),
MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"),
MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"),
MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"),
MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"),
MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"),
MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"),
MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"),
MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"),
};
static DEFINE_MUTEX(ar8xxx_dev_list_lock);
static LIST_HEAD(ar8xxx_dev_list);
static inline void
split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
{
regaddr >>= 1;
*r1 = regaddr & 0x1e;
regaddr >>= 5;
*r2 = regaddr & 0x7;
regaddr >>= 3;
*page = regaddr & 0x1ff;
}
/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
static int
ar8xxx_phy_poll_reset(struct mii_bus *bus)
{
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 < AR8XXX_NUM_PHYS; i++) {
ret = mdiobus_read(bus, i, MII_BMCR);
if (ret < 0)
return ret;
if (ret & BMCR_RESET)
break;
if (i == AR8XXX_NUM_PHYS - 1) {
usleep_range(1000, 2000);
return 0;
}
}
}
return -ETIMEDOUT;
}
static int
ar8xxx_phy_check_aneg(struct phy_device *phydev)
{
int ret;
if (phydev->autoneg != AUTONEG_ENABLE)
return 0;
/*
* BMCR_ANENABLE might have been cleared
* by phy_init_hw in certain kernel versions
* therefore check for it
*/
ret = phy_read(phydev, MII_BMCR);
if (ret < 0)
return ret;
if (ret & BMCR_ANENABLE)
return 0;
dev_info(&phydev->dev, "ANEG disabled, re-enabling ...\n");
ret |= BMCR_ANENABLE | BMCR_ANRESTART;
return phy_write(phydev, MII_BMCR, ret);
}
void
ar8xxx_phy_init(struct ar8xxx_priv *priv)
{
int i;
struct mii_bus *bus;
bus = priv->mii_bus;
for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
if (priv->chip->phy_fixup)
priv->chip->phy_fixup(priv, i);
/* initialize the port itself */
mdiobus_write(bus, i, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
if (ar8xxx_has_gige(priv))
mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
}
ar8xxx_phy_poll_reset(bus);
}
static u32
mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
{
struct mii_bus *bus = priv->mii_bus;
u16 lo, hi;
lo = bus->read(bus, phy_id, regnum);
hi = bus->read(bus, phy_id, regnum + 1);
return (hi << 16) | lo;
}
static void
mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
{
struct mii_bus *bus = priv->mii_bus;
u16 lo, hi;
lo = val & 0xffff;
hi = (u16) (val >> 16);
if (priv->chip->mii_lo_first)
{
bus->write(bus, phy_id, regnum, lo);
bus->write(bus, phy_id, regnum + 1, hi);
} else {
bus->write(bus, phy_id, regnum + 1, hi);
bus->write(bus, phy_id, regnum, lo);
}
}
u32
ar8xxx_read(struct ar8xxx_priv *priv, int reg)
{
struct mii_bus *bus = priv->mii_bus;
u16 r1, r2, page;
u32 val;
split_addr((u32) reg, &r1, &r2, &page);
mutex_lock(&bus->mdio_lock);
bus->write(bus, 0x18, 0, page);
wait_for_page_switch();
val = mii_read32(priv, 0x10 | r2, r1);
mutex_unlock(&bus->mdio_lock);
return val;
}
void
ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val)
{
struct mii_bus *bus = priv->mii_bus;
u16 r1, r2, page;
split_addr((u32) reg, &r1, &r2, &page);
mutex_lock(&bus->mdio_lock);
bus->write(bus, 0x18, 0, page);
wait_for_page_switch();
mii_write32(priv, 0x10 | r2, r1, val);
mutex_unlock(&bus->mdio_lock);
}
u32
ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
{
struct mii_bus *bus = priv->mii_bus;
u16 r1, r2, page;
u32 ret;
split_addr((u32) reg, &r1, &r2, &page);
mutex_lock(&bus->mdio_lock);
bus->write(bus, 0x18, 0, page);
wait_for_page_switch();
ret = mii_read32(priv, 0x10 | r2, r1);
ret &= ~mask;
ret |= val;
mii_write32(priv, 0x10 | r2, r1, ret);
mutex_unlock(&bus->mdio_lock);
return ret;
}
void
ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
u16 dbg_addr, u16 dbg_data)
{
struct mii_bus *bus = priv->mii_bus;
mutex_lock(&bus->mdio_lock);
bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
mutex_unlock(&bus->mdio_lock);
}
void
ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data)
{
struct mii_bus *bus = priv->mii_bus;
mutex_lock(&bus->mdio_lock);
bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
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)
{
struct mii_bus *bus = priv->mii_bus;
u16 data;
mutex_lock(&bus->mdio_lock);
bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
mutex_unlock(&bus->mdio_lock);
return data;
}
static int
ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
unsigned timeout)
{
int i;
for (i = 0; i < timeout; i++) {
u32 t;
t = ar8xxx_read(priv, reg);
if ((t & mask) == val)
return 0;
usleep_range(1000, 2000);
}
return -ETIMEDOUT;
}
static int
ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
{
unsigned mib_func = priv->chip->mib_func;
int ret;
lockdep_assert_held(&priv->mib_lock);
/* Capture the hardware statistics for all ports */
ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S));
/* Wait for the capturing to complete. */
ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10);
if (ret)
goto out;
ret = 0;
out:
return ret;
}
static int
ar8xxx_mib_capture(struct ar8xxx_priv *priv)
{
return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE);
}
static int
ar8xxx_mib_flush(struct ar8xxx_priv *priv)
{
return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH);
}
static void
ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush)
{
unsigned int base;
u64 *mib_stats;
int i;
WARN_ON(port >= priv->dev.ports);
lockdep_assert_held(&priv->mib_lock);
base = priv->chip->reg_port_stats_start +
priv->chip->reg_port_stats_length * port;
mib_stats = &priv->mib_stats[port * priv->chip->num_mibs];
for (i = 0; i < priv->chip->num_mibs; i++) {
const struct ar8xxx_mib_desc *mib;
u64 t;
mib = &priv->chip->mib_decs[i];
t = ar8xxx_read(priv, base + mib->offset);
if (mib->size == 2) {
u64 hi;
hi = ar8xxx_read(priv, base + mib->offset + 4);
t |= hi << 32;
}
if (flush)
mib_stats[i] = 0;
else
mib_stats[i] += t;
}
}
static void
ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
struct switch_port_link *link)
{
u32 status;
u32 speed;
memset(link, '\0', sizeof(*link));
status = priv->chip->read_port_status(priv, port);
link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO);
if (link->aneg) {
link->link = !!(status & AR8216_PORT_STATUS_LINK_UP);
} else {
link->link = true;
if (priv->get_port_link) {
int err;
err = priv->get_port_link(port);
if (err >= 0)
link->link = !!err;
}
}
if (!link->link)
return;
link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX);
link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
if (link->aneg && link->duplex && priv->chip->read_port_eee_status)
link->eee = priv->chip->read_port_eee_status(priv, port);
speed = (status & AR8216_PORT_STATUS_SPEED) >>
AR8216_PORT_STATUS_SPEED_S;
switch (speed) {
case AR8216_PORT_SPEED_10M:
link->speed = SWITCH_PORT_SPEED_10;
break;
case AR8216_PORT_SPEED_100M:
link->speed = SWITCH_PORT_SPEED_100;
break;
case AR8216_PORT_SPEED_1000M:
link->speed = SWITCH_PORT_SPEED_1000;
break;
default:
link->speed = SWITCH_PORT_SPEED_UNKNOWN;
break;
}
}
static struct sk_buff *
ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb)
{
struct ar8xxx_priv *priv = dev->phy_ptr;
unsigned char *buf;
if (unlikely(!priv))
goto error;
if (!priv->vlan)
goto send;
if (unlikely(skb_headroom(skb) < 2)) {
if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0)
goto error;
}
buf = skb_push(skb, 2);
buf[0] = 0x10;
buf[1] = 0x80;
send:
return skb;
error:
dev_kfree_skb_any(skb);
return NULL;
}
static void
ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
{
struct ar8xxx_priv *priv;
unsigned char *buf;
int port, vlan;
priv = dev->phy_ptr;
if (!priv)
return;
/* don't strip the header if vlan mode is disabled */
if (!priv->vlan)
return;
/* strip header, get vlan id */
buf = skb->data;
skb_pull(skb, 2);
/* check for vlan header presence */
if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00))
return;
port = buf[0] & 0xf;
/* no need to fix up packets coming from a tagged source */
if (priv->vlan_tagged & (1 << port))
return;
/* lookup port vid from local table, the switch passes an invalid vlan id */
vlan = priv->vlan_id[priv->pvid[port]];
buf[14 + 2] &= 0xf0;
buf[14 + 2] |= vlan >> 8;
buf[15 + 2] = vlan & 0xff;
}
int
ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
{
int timeout = 20;
u32 t = 0;
while (1) {
t = ar8xxx_read(priv, reg);
if ((t & mask) == val)
return 0;
if (timeout-- <= 0)
break;
udelay(10);
}
pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n",
(unsigned int) reg, t, mask, val);
return -ETIMEDOUT;
}
static void
ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
{
if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0))
return;
if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) {
val &= AR8216_VTUDATA_MEMBER;
val |= AR8216_VTUDATA_VALID;
ar8xxx_write(priv, AR8216_REG_VTU_DATA, val);
}
op |= AR8216_VTU_ACTIVE;
ar8xxx_write(priv, AR8216_REG_VTU, op);
}
static void
ar8216_vtu_flush(struct ar8xxx_priv *priv)
{
ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
}
static void
ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
{
u32 op;
op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S);
ar8216_vtu_op(priv, op, port_mask);
}
static int
ar8216_atu_flush(struct ar8xxx_priv *priv)
{
int ret;
ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0);
if (!ret)
ar8xxx_write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH |
AR8216_ATU_ACTIVE);
return ret;
}
static u32
ar8216_read_port_status(struct ar8xxx_priv *priv, int port)
{
return ar8xxx_read(priv, AR8216_REG_PORT_STATUS(port));
}
static void
ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
{
u32 header;
u32 egress, ingress;
u32 pvid;
if (priv->vlan) {
pvid = priv->vlan_id[priv->pvid[port]];
if (priv->vlan_tagged & (1 << port))
egress = AR8216_OUT_ADD_VLAN;
else
egress = AR8216_OUT_STRIP_VLAN;
ingress = AR8216_IN_SECURE;
} else {
pvid = port;
egress = AR8216_OUT_KEEP;
ingress = AR8216_IN_PORT_ONLY;
}
if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU)
header = AR8216_PORT_CTRL_HEADER;
else
header = 0;
ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
AR8216_PORT_CTRL_LEARN | header |
(egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
(AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port),
AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE |
AR8216_PORT_VLAN_DEFAULT_ID,
(members << AR8216_PORT_VLAN_DEST_PORTS_S) |
(ingress << AR8216_PORT_VLAN_MODE_S) |
(pvid << AR8216_PORT_VLAN_DEFAULT_ID_S));
}
static int
ar8216_hw_init(struct ar8xxx_priv *priv)
{
if (priv->initialized)
return 0;
ar8xxx_phy_init(priv);
priv->initialized = true;
return 0;
}
static void
ar8216_init_globals(struct ar8xxx_priv *priv)
{
/* standard atheros magic */
ar8xxx_write(priv, 0x38, 0xc000050e);
ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
AR8216_GCTRL_MTU, 1518 + 8 + 2);
}
static void
ar8216_init_port(struct ar8xxx_priv *priv, int port)
{
/* Enable port learning and tx */
ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port),
AR8216_PORT_CTRL_LEARN |
(4 << AR8216_PORT_CTRL_STATE_S));
ar8xxx_write(priv, AR8216_REG_PORT_VLAN(port), 0);
if (port == AR8216_PORT_CPU) {
ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
AR8216_PORT_STATUS_LINK_UP |
(ar8xxx_has_gige(priv) ?
AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) |
AR8216_PORT_STATUS_TXMAC |
AR8216_PORT_STATUS_RXMAC |
(chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) |
(chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) |
AR8216_PORT_STATUS_DUPLEX);
} else {
ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
AR8216_PORT_STATUS_LINK_AUTO);
}
}
static void
ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
{
u32 egress, ingress;
u32 pvid;
if (priv->vlan) {
pvid = priv->vlan_id[priv->pvid[port]];
if (priv->vlan_tagged & (1 << port))
egress = AR8216_OUT_ADD_VLAN;
else
egress = AR8216_OUT_STRIP_VLAN;
ingress = AR8216_IN_SECURE;
} else {
pvid = port;
egress = AR8216_OUT_KEEP;
ingress = AR8216_IN_PORT_ONLY;
}
ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
AR8216_PORT_CTRL_LEARN |
(egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
(AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port),
AR8236_PORT_VLAN_DEFAULT_ID,
(pvid << AR8236_PORT_VLAN_DEFAULT_ID_S));
ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port),
AR8236_PORT_VLAN2_VLAN_MODE |
AR8236_PORT_VLAN2_MEMBER,
(ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) |
(members << AR8236_PORT_VLAN2_MEMBER_S));
}
static void
ar8236_init_globals(struct ar8xxx_priv *priv)
{
/* enable jumbo frames */
ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
AR8316_GCTRL_MTU, 9018 + 8 + 2);
/* enable cpu port to receive arp frames */
ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL,
AR8236_ATU_CTRL_RES);
/* enable cpu port to receive multicast and broadcast frames */
ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK,
AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN);
/* Enable MIB counters */
ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
(AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
AR8236_MIB_EN);
}
static int
ar8316_hw_init(struct ar8xxx_priv *priv)
{
u32 val, newval;
val = ar8xxx_read(priv, AR8316_REG_POSTRIP);
if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
if (priv->port4_phy) {
/* value taken from Ubiquiti RouterStation Pro */
newval = 0x81461bea;
pr_info("ar8316: Using port 4 as PHY\n");
} else {
newval = 0x01261be2;
pr_info("ar8316: Using port 4 as switch port\n");
}
} else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) {
/* value taken from AVM Fritz!Box 7390 sources */
newval = 0x010e5b71;
} else {
/* no known value for phy interface */
pr_err("ar8316: unsupported mii mode: %d.\n",
priv->phy->interface);
return -EINVAL;
}
if (val == newval)
goto out;
ar8xxx_write(priv, AR8316_REG_POSTRIP, newval);
if (priv->port4_phy &&
priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
/* work around for phy4 rgmii mode */
ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c);
/* rx delay */
ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e);
/* tx delay */
ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47);
msleep(1000);
}
ar8xxx_phy_init(priv);
out:
priv->initialized = true;
return 0;
}
static void
ar8316_init_globals(struct ar8xxx_priv *priv)
{
/* standard atheros magic */
ar8xxx_write(priv, 0x38, 0xc000050e);
/* enable cpu port to receive multicast and broadcast frames */
ar8xxx_write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f);
/* enable jumbo frames */
ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
AR8316_GCTRL_MTU, 9018 + 8 + 2);
/* Enable MIB counters */
ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
(AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
AR8236_MIB_EN);
}
int
ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
priv->vlan = !!val->value.i;
return 0;
}
int
ar8xxx_sw_get_vlan(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->vlan;
return 0;
}
int
ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
/* make sure no invalid PVIDs get set */
if (vlan >= dev->vlans)
return -EINVAL;
priv->pvid[port] = vlan;
return 0;
}
int
ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
*vlan = priv->pvid[port];
return 0;
}
static int
ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
priv->vlan_id[val->port_vlan] = val->value.i;
return 0;
}
static int
ar8xxx_sw_get_vid(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->vlan_id[val->port_vlan];
return 0;
}
int
ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
struct switch_port_link *link)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
ar8216_read_port_link(priv, port, link);
return 0;
}
static int
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];
int i;
val->len = 0;
for (i = 0; i < dev->ports; i++) {
struct switch_port *p;
if (!(ports & (1 << i)))
continue;
p = &val->value.ports[val->len++];
p->id = i;
if (priv->vlan_tagged & (1 << i))
p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
else
p->flags = 0;
}
return 0;
}
static int
ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
u8 *vt = &priv->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)) {
priv->vlan_tagged |= (1 << p->id);
} else {
priv->vlan_tagged &= ~(1 << p->id);
priv->pvid[p->id] = val->port_vlan;
/* make sure that an untagged port does not
* appear in other vlans */
for (j = 0; j < AR8X16_MAX_VLANS; j++) {
if (j == val->port_vlan)
continue;
priv->vlan_table[j] &= ~(1 << p->id);
}
}
*vt |= 1 << p->id;
}
return 0;
}
static void
ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
{
int port;
/* reset all mirror registers */
ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
(0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
for (port = 0; port < AR8216_NUM_PORTS; port++) {
ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
AR8216_PORT_CTRL_MIRROR_RX);
ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
AR8216_PORT_CTRL_MIRROR_TX);
}
/* now enable mirroring if necessary */
if (priv->source_port >= AR8216_NUM_PORTS ||
priv->monitor_port >= AR8216_NUM_PORTS ||
priv->source_port == priv->monitor_port) {
return;
}
ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
(priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
if (priv->mirror_rx)
ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
AR8216_PORT_CTRL_MIRROR_RX);
if (priv->mirror_tx)
ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
AR8216_PORT_CTRL_MIRROR_TX);
}
int
ar8xxx_sw_hw_apply(struct switch_dev *dev)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
u8 portmask[AR8X16_MAX_PORTS];
int i, j;
mutex_lock(&priv->reg_mutex);
/* flush all vlan translation unit entries */
priv->chip->vtu_flush(priv);
memset(portmask, 0, sizeof(portmask));
if (!priv->init) {
/* calculate the port destination masks and load vlans
* into the vlan translation unit */
for (j = 0; j < AR8X16_MAX_VLANS; j++) {
u8 vp = priv->vlan_table[j];
if (!vp)
continue;
for (i = 0; i < dev->ports; i++) {
u8 mask = (1 << i);
if (vp & mask)
portmask[i] |= vp & ~mask;
}
priv->chip->vtu_load_vlan(priv, priv->vlan_id[j],
priv->vlan_table[j]);
}
} else {
/* vlan disabled:
* isolate all ports, but connect them to the cpu port */
for (i = 0; i < dev->ports; i++) {
if (i == AR8216_PORT_CPU)
continue;
portmask[i] = 1 << AR8216_PORT_CPU;
portmask[AR8216_PORT_CPU] |= (1 << i);
}
}
/* update the port destination mask registers and tag settings */
for (i = 0; i < dev->ports; i++) {
priv->chip->setup_port(priv, i, portmask[i]);
}
priv->chip->set_mirror_regs(priv);
mutex_unlock(&priv->reg_mutex);
return 0;
}
int
ar8xxx_sw_reset_switch(struct switch_dev *dev)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
const struct ar8xxx_chip *chip = priv->chip;
int i;
mutex_lock(&priv->reg_mutex);
memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) -
offsetof(struct ar8xxx_priv, vlan));
for (i = 0; i < AR8X16_MAX_VLANS; i++)
priv->vlan_id[i] = i;
/* Configure all ports */
for (i = 0; i < dev->ports; i++)
chip->init_port(priv, i);
priv->mirror_rx = false;
priv->mirror_tx = false;
priv->source_port = 0;
priv->monitor_port = 0;
chip->init_globals(priv);
mutex_unlock(&priv->reg_mutex);
return chip->sw_hw_apply(dev);
}
int
ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
unsigned int len;
int ret;
if (!ar8xxx_has_mib_counters(priv))
return -EOPNOTSUPP;
mutex_lock(&priv->mib_lock);
len = priv->dev.ports * priv->chip->num_mibs *
sizeof(*priv->mib_stats);
memset(priv->mib_stats, '\0', len);
ret = ar8xxx_mib_flush(priv);
if (ret)
goto unlock;
ret = 0;
unlock:
mutex_unlock(&priv->mib_lock);
return ret;
}
int
ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
mutex_lock(&priv->reg_mutex);
priv->mirror_rx = !!val->value.i;
priv->chip->set_mirror_regs(priv);
mutex_unlock(&priv->reg_mutex);
return 0;
}
int
ar8xxx_sw_get_mirror_rx_enable(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->mirror_rx;
return 0;
}
int
ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
mutex_lock(&priv->reg_mutex);
priv->mirror_tx = !!val->value.i;
priv->chip->set_mirror_regs(priv);
mutex_unlock(&priv->reg_mutex);
return 0;
}
int
ar8xxx_sw_get_mirror_tx_enable(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->mirror_tx;
return 0;
}
int
ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
mutex_lock(&priv->reg_mutex);
priv->monitor_port = val->value.i;
priv->chip->set_mirror_regs(priv);
mutex_unlock(&priv->reg_mutex);
return 0;
}
int
ar8xxx_sw_get_mirror_monitor_port(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->monitor_port;
return 0;
}
int
ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
mutex_lock(&priv->reg_mutex);
priv->source_port = val->value.i;
priv->chip->set_mirror_regs(priv);
mutex_unlock(&priv->reg_mutex);
return 0;
}
int
ar8xxx_sw_get_mirror_source_port(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->source_port;
return 0;
}
int
ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
int port;
int ret;
if (!ar8xxx_has_mib_counters(priv))
return -EOPNOTSUPP;
port = val->port_vlan;
if (port >= dev->ports)
return -EINVAL;
mutex_lock(&priv->mib_lock);
ret = ar8xxx_mib_capture(priv);
if (ret)
goto unlock;
ar8xxx_mib_fetch_port_stat(priv, port, true);
ret = 0;
unlock:
mutex_unlock(&priv->mib_lock);
return ret;
}
int
ar8xxx_sw_get_port_mib(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val)
{
struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
const struct ar8xxx_chip *chip = priv->chip;
u64 *mib_stats;
int port;
int ret;
char *buf = priv->buf;
int i, len = 0;
if (!ar8xxx_has_mib_counters(priv))
return -EOPNOTSUPP;
port = val->port_vlan;
if (port >= dev->ports)
return -EINVAL;
mutex_lock(&priv->mib_lock);
ret = ar8xxx_mib_capture(priv);
if (ret)
goto unlock;
ar8xxx_mib_fetch_port_stat(priv, port, false);
len += snprintf(buf + len, sizeof(priv->buf) - len,
"Port %d MIB counters\n",
port);
mib_stats = &priv->mib_stats[port * chip->num_mibs];
for (i = 0; i < chip->num_mibs; i++)
len += snprintf(buf + len, sizeof(priv->buf) - len,
"%-12s: %llu\n",
chip->mib_decs[i].name,
mib_stats[i]);
val->value.s = buf;
val->len = len;
ret = 0;
unlock:
mutex_unlock(&priv->mib_lock);
return ret;
}
static const struct switch_attr ar8xxx_sw_attr_globals[] = {
{
.type = SWITCH_TYPE_INT,
.name = "enable_vlan",
.description = "Enable VLAN mode",
.set = ar8xxx_sw_set_vlan,
.get = ar8xxx_sw_get_vlan,
.max = 1
},
{
.type = SWITCH_TYPE_NOVAL,
.name = "reset_mibs",
.description = "Reset all MIB counters",
.set = ar8xxx_sw_set_reset_mibs,
},
{
.type = SWITCH_TYPE_INT,
.name = "enable_mirror_rx",
.description = "Enable mirroring of RX packets",
.set = ar8xxx_sw_set_mirror_rx_enable,
.get = ar8xxx_sw_get_mirror_rx_enable,
.max = 1
},
{
.type = SWITCH_TYPE_INT,
.name = "enable_mirror_tx",
.description = "Enable mirroring of TX packets",
.set = ar8xxx_sw_set_mirror_tx_enable,
.get = ar8xxx_sw_get_mirror_tx_enable,
.max = 1
},
{
.type = SWITCH_TYPE_INT,
.name = "mirror_monitor_port",
.description = "Mirror monitor port",
.set = ar8xxx_sw_set_mirror_monitor_port,
.get = ar8xxx_sw_get_mirror_monitor_port,
.max = AR8216_NUM_PORTS - 1
},
{
.type = SWITCH_TYPE_INT,
.name = "mirror_source_port",
.description = "Mirror source port",
.set = ar8xxx_sw_set_mirror_source_port,
.get = ar8xxx_sw_get_mirror_source_port,
.max = AR8216_NUM_PORTS - 1
},
};
const struct switch_attr ar8xxx_sw_attr_port[2] = {
{
.type = SWITCH_TYPE_NOVAL,
.name = "reset_mib",
.description = "Reset single port MIB counters",
.set = ar8xxx_sw_set_port_reset_mib,
},
{
.type = SWITCH_TYPE_STRING,
.name = "mib",
.description = "Get port's MIB counters",
.set = NULL,
.get = ar8xxx_sw_get_port_mib,
},
};
const struct switch_attr ar8xxx_sw_attr_vlan[1] = {
{
.type = SWITCH_TYPE_INT,
.name = "vid",
.description = "VLAN ID (0-4094)",
.set = ar8xxx_sw_set_vid,
.get = ar8xxx_sw_get_vid,
.max = 4094,
},
};
static const struct switch_dev_ops ar8xxx_sw_ops = {
.attr_global = {
.attr = ar8xxx_sw_attr_globals,
.n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals),
},
.attr_port = {
.attr = ar8xxx_sw_attr_port,
.n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
},
.attr_vlan = {
.attr = ar8xxx_sw_attr_vlan,
.n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
},
.get_port_pvid = ar8xxx_sw_get_pvid,
.set_port_pvid = ar8xxx_sw_set_pvid,
.get_vlan_ports = ar8xxx_sw_get_ports,
.set_vlan_ports = ar8xxx_sw_set_ports,
.apply_config = ar8xxx_sw_hw_apply,
.reset_switch = ar8xxx_sw_reset_switch,
.get_port_link = ar8xxx_sw_get_port_link,
};
static const struct ar8xxx_chip ar8216_chip = {
.caps = AR8XXX_CAP_MIB_COUNTERS,
.reg_port_stats_start = 0x19000,
.reg_port_stats_length = 0xa0,
.name = "Atheros AR8216",
.ports = AR8216_NUM_PORTS,
.vlans = AR8216_NUM_VLANS,
.swops = &ar8xxx_sw_ops,
.hw_init = ar8216_hw_init,
.init_globals = ar8216_init_globals,
.init_port = ar8216_init_port,
.setup_port = ar8216_setup_port,
.read_port_status = ar8216_read_port_status,
.atu_flush = ar8216_atu_flush,
.vtu_flush = ar8216_vtu_flush,
.vtu_load_vlan = ar8216_vtu_load_vlan,
.set_mirror_regs = ar8216_set_mirror_regs,
.sw_hw_apply = ar8xxx_sw_hw_apply,
.num_mibs = ARRAY_SIZE(ar8216_mibs),
.mib_decs = ar8216_mibs,
.mib_func = AR8216_REG_MIB_FUNC
};
static const struct ar8xxx_chip ar8236_chip = {
.caps = AR8XXX_CAP_MIB_COUNTERS,
.reg_port_stats_start = 0x20000,
.reg_port_stats_length = 0x100,
.name = "Atheros AR8236",
.ports = AR8216_NUM_PORTS,
.vlans = AR8216_NUM_VLANS,
.swops = &ar8xxx_sw_ops,
.hw_init = ar8216_hw_init,
.init_globals = ar8236_init_globals,
.init_port = ar8216_init_port,
.setup_port = ar8236_setup_port,
.read_port_status = ar8216_read_port_status,
.atu_flush = ar8216_atu_flush,
.vtu_flush = ar8216_vtu_flush,
.vtu_load_vlan = ar8216_vtu_load_vlan,
.set_mirror_regs = ar8216_set_mirror_regs,
.sw_hw_apply = ar8xxx_sw_hw_apply,
.num_mibs = ARRAY_SIZE(ar8236_mibs),
.mib_decs = ar8236_mibs,
.mib_func = AR8216_REG_MIB_FUNC
};
static const struct ar8xxx_chip ar8316_chip = {
.caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
.reg_port_stats_start = 0x20000,
.reg_port_stats_length = 0x100,
.name = "Atheros AR8316",
.ports = AR8216_NUM_PORTS,
.vlans = AR8X16_MAX_VLANS,
.swops = &ar8xxx_sw_ops,
.hw_init = ar8316_hw_init,
.init_globals = ar8316_init_globals,
.init_port = ar8216_init_port,
.setup_port = ar8216_setup_port,
.read_port_status = ar8216_read_port_status,
.atu_flush = ar8216_atu_flush,
.vtu_flush = ar8216_vtu_flush,
.vtu_load_vlan = ar8216_vtu_load_vlan,
.set_mirror_regs = ar8216_set_mirror_regs,
.sw_hw_apply = ar8xxx_sw_hw_apply,
.num_mibs = ARRAY_SIZE(ar8236_mibs),
.mib_decs = ar8236_mibs,
.mib_func = AR8216_REG_MIB_FUNC
};
static int
ar8xxx_id_chip(struct ar8xxx_priv *priv)
{
u32 val;
u16 id;
int i;
val = ar8xxx_read(priv, AR8216_REG_CTRL);
if (val == ~0)
return -ENODEV;
id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
for (i = 0; i < AR8X16_PROBE_RETRIES; i++) {
u16 t;
val = ar8xxx_read(priv, AR8216_REG_CTRL);
if (val == ~0)
return -ENODEV;
t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
if (t != id)
return -ENODEV;
}
priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S;
priv->chip_rev = (id & AR8216_CTRL_REVISION);
switch (priv->chip_ver) {
case AR8XXX_VER_AR8216:
priv->chip = &ar8216_chip;
break;
case AR8XXX_VER_AR8236:
priv->chip = &ar8236_chip;
break;
case AR8XXX_VER_AR8316:
priv->chip = &ar8316_chip;
break;
case AR8XXX_VER_AR8327:
priv->chip = &ar8327_chip;
break;
case AR8XXX_VER_AR8337:
priv->chip = &ar8337_chip;
break;
default:
pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n",
priv->chip_ver, priv->chip_rev);
return -ENODEV;
}
return 0;
}
static void
ar8xxx_mib_work_func(struct work_struct *work)
{
struct ar8xxx_priv *priv;
int err;
priv = container_of(work, struct ar8xxx_priv, mib_work.work);
mutex_lock(&priv->mib_lock);
err = ar8xxx_mib_capture(priv);
if (err)
goto next_port;
ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false);
next_port:
priv->mib_next_port++;
if (priv->mib_next_port >= priv->dev.ports)
priv->mib_next_port = 0;
mutex_unlock(&priv->mib_lock);
schedule_delayed_work(&priv->mib_work,
msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
}
static int
ar8xxx_mib_init(struct ar8xxx_priv *priv)
{
unsigned int len;
if (!ar8xxx_has_mib_counters(priv))
return 0;
BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs);
len = priv->dev.ports * priv->chip->num_mibs *
sizeof(*priv->mib_stats);
priv->mib_stats = kzalloc(len, GFP_KERNEL);
if (!priv->mib_stats)
return -ENOMEM;
return 0;
}
static void
ar8xxx_mib_start(struct ar8xxx_priv *priv)
{
if (!ar8xxx_has_mib_counters(priv))
return;
schedule_delayed_work(&priv->mib_work,
msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
}
static void
ar8xxx_mib_stop(struct ar8xxx_priv *priv)
{
if (!ar8xxx_has_mib_counters(priv))
return;
cancel_delayed_work(&priv->mib_work);
}
static struct ar8xxx_priv *
ar8xxx_create(void)
{
struct ar8xxx_priv *priv;
priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL);
if (priv == NULL)
return NULL;
mutex_init(&priv->reg_mutex);
mutex_init(&priv->mib_lock);
INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func);
return priv;
}
static void
ar8xxx_free(struct ar8xxx_priv *priv)
{
if (priv->chip && priv->chip->cleanup)
priv->chip->cleanup(priv);
kfree(priv->chip_data);
kfree(priv->mib_stats);
kfree(priv);
}
static int
ar8xxx_probe_switch(struct ar8xxx_priv *priv)
{
const struct ar8xxx_chip *chip;
struct switch_dev *swdev;
int ret;
ret = ar8xxx_id_chip(priv);
if (ret)
return ret;
chip = priv->chip;
swdev = &priv->dev;
swdev->cpu_port = AR8216_PORT_CPU;
swdev->name = chip->name;
swdev->vlans = chip->vlans;
swdev->ports = chip->ports;
swdev->ops = chip->swops;
ret = ar8xxx_mib_init(priv);
if (ret)
return ret;
return 0;
}
static int
ar8xxx_start(struct ar8xxx_priv *priv)
{
int ret;
priv->init = true;
ret = priv->chip->hw_init(priv);
if (ret)
return ret;
ret = ar8xxx_sw_reset_switch(&priv->dev);
if (ret)
return ret;
priv->init = false;
ar8xxx_mib_start(priv);
return 0;
}
static int
ar8xxx_phy_config_init(struct phy_device *phydev)
{
struct ar8xxx_priv *priv = phydev->priv;
struct net_device *dev = phydev->attached_dev;
int ret;
if (WARN_ON(!priv))
return -ENODEV;
if (priv->chip->config_at_probe)
return ar8xxx_phy_check_aneg(phydev);
priv->phy = phydev;
if (phydev->addr != 0) {
if (chip_is_ar8316(priv)) {
/* switch device has been initialized, reinit */
priv->dev.ports = (AR8216_NUM_PORTS - 1);
priv->initialized = false;
priv->port4_phy = true;
ar8316_hw_init(priv);
return 0;
}
return 0;
}
ret = ar8xxx_start(priv);
if (ret)
return ret;
/* VID fixup only needed on ar8216 */
if (chip_is_ar8216(priv)) {
dev->phy_ptr = priv;
dev->priv_flags |= IFF_NO_IP_ALIGN;
dev->eth_mangle_rx = ar8216_mangle_rx;
dev->eth_mangle_tx = ar8216_mangle_tx;
}
return 0;
}
static bool
ar8xxx_check_link_states(struct ar8xxx_priv *priv)
{
bool link_new, changed = false;
u32 status;
int i;
mutex_lock(&priv->reg_mutex);
for (i = 0; i < priv->dev.ports; i++) {
status = priv->chip->read_port_status(priv, i);
link_new = !!(status & AR8216_PORT_STATUS_LINK_UP);
if (link_new == priv->link_up[i])
continue;
priv->link_up[i] = link_new;
changed = true;
dev_info(&priv->phy->dev, "Port %d is %s\n",
i, link_new ? "up" : "down");
}
if (changed)
priv->chip->atu_flush(priv);
mutex_unlock(&priv->reg_mutex);
return changed;
}
static int
ar8xxx_phy_read_status(struct phy_device *phydev)
{
struct ar8xxx_priv *priv = phydev->priv;
struct switch_port_link link;
/* check for link changes and flush ATU
* if a change was detected
*/
if (phydev->state == PHY_CHANGELINK)
ar8xxx_check_link_states(priv);
if (phydev->addr != 0)
return genphy_read_status(phydev);
ar8216_read_port_link(priv, phydev->addr, &link);
phydev->link = !!link.link;
if (!phydev->link)
return 0;
switch (link.speed) {
case SWITCH_PORT_SPEED_10:
phydev->speed = SPEED_10;
break;
case SWITCH_PORT_SPEED_100:
phydev->speed = SPEED_100;
break;
case SWITCH_PORT_SPEED_1000:
phydev->speed = SPEED_1000;
break;
default:
phydev->speed = 0;
}
phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF;
phydev->state = PHY_RUNNING;
netif_carrier_on(phydev->attached_dev);
phydev->adjust_link(phydev->attached_dev);
return 0;
}
static int
ar8xxx_phy_config_aneg(struct phy_device *phydev)
{
if (phydev->addr == 0)
return 0;
return genphy_config_aneg(phydev);
}
static const u32 ar8xxx_phy_ids[] = {
0x004dd033,
0x004dd034, /* AR8327 */
0x004dd036, /* AR8337 */
0x004dd041,
0x004dd042,
0x004dd043, /* AR8236 */
};
static bool
ar8xxx_phy_match(u32 phy_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++)
if (phy_id == ar8xxx_phy_ids[i])
return true;
return false;
}
static bool
ar8xxx_is_possible(struct mii_bus *bus)
{
unsigned i;
for (i = 0; i < 4; 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)) {
pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n",
dev_name(&bus->dev), i, phy_id);
return false;
}
}
return true;
}
static int
ar8xxx_phy_probe(struct phy_device *phydev)
{
struct ar8xxx_priv *priv;
struct switch_dev *swdev;
int ret;
/* skip PHYs at unused adresses */
if (phydev->addr != 0 && phydev->addr != 4)
return -ENODEV;
if (!ar8xxx_is_possible(phydev->bus))
return -ENODEV;
mutex_lock(&ar8xxx_dev_list_lock);
list_for_each_entry(priv, &ar8xxx_dev_list, list)
if (priv->mii_bus == phydev->bus)
goto found;
priv = ar8xxx_create();
if (priv == NULL) {
ret = -ENOMEM;
goto unlock;
}
priv->mii_bus = phydev->bus;
ret = ar8xxx_probe_switch(priv);
if (ret)
goto free_priv;
swdev = &priv->dev;
swdev->alias = dev_name(&priv->mii_bus->dev);
ret = register_switch(swdev, NULL);
if (ret)
goto free_priv;
pr_info("%s: %s rev. %u switch registered on %s\n",
swdev->devname, swdev->name, priv->chip_rev,
dev_name(&priv->mii_bus->dev));
found:
priv->use_count++;
if (phydev->addr == 0) {
if (ar8xxx_has_gige(priv)) {
phydev->supported = SUPPORTED_1000baseT_Full;
phydev->advertising = ADVERTISED_1000baseT_Full;
} else {
phydev->supported = SUPPORTED_100baseT_Full;
phydev->advertising = ADVERTISED_100baseT_Full;
}
if (priv->chip->config_at_probe) {
priv->phy = phydev;
ret = ar8xxx_start(priv);
if (ret)
goto err_unregister_switch;
}
} else {
if (ar8xxx_has_gige(priv)) {
phydev->supported |= SUPPORTED_1000baseT_Full;
phydev->advertising |= ADVERTISED_1000baseT_Full;
}
}
phydev->priv = priv;
list_add(&priv->list, &ar8xxx_dev_list);
mutex_unlock(&ar8xxx_dev_list_lock);
return 0;
err_unregister_switch:
if (--priv->use_count)
goto unlock;
unregister_switch(&priv->dev);
free_priv:
ar8xxx_free(priv);
unlock:
mutex_unlock(&ar8xxx_dev_list_lock);
return ret;
}
static void
ar8xxx_phy_detach(struct phy_device *phydev)
{
struct net_device *dev = phydev->attached_dev;
if (!dev)
return;
dev->phy_ptr = NULL;
dev->priv_flags &= ~IFF_NO_IP_ALIGN;
dev->eth_mangle_rx = NULL;
dev->eth_mangle_tx = NULL;
}
static void
ar8xxx_phy_remove(struct phy_device *phydev)
{
struct ar8xxx_priv *priv = phydev->priv;
if (WARN_ON(!priv))
return;
phydev->priv = NULL;
if (--priv->use_count > 0)
return;
mutex_lock(&ar8xxx_dev_list_lock);
list_del(&priv->list);
mutex_unlock(&ar8xxx_dev_list_lock);
unregister_switch(&priv->dev);
ar8xxx_mib_stop(priv);
ar8xxx_free(priv);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
static int
ar8xxx_phy_soft_reset(struct phy_device *phydev)
{
/* we don't need an extra reset */
return 0;
}
#endif
static struct phy_driver ar8xxx_phy_driver = {
.phy_id = 0x004d0000,
.name = "Atheros AR8216/AR8236/AR8316",
.phy_id_mask = 0xffff0000,
.features = PHY_BASIC_FEATURES,
.probe = ar8xxx_phy_probe,
.remove = ar8xxx_phy_remove,
.detach = ar8xxx_phy_detach,
.config_init = ar8xxx_phy_config_init,
.config_aneg = ar8xxx_phy_config_aneg,
.read_status = ar8xxx_phy_read_status,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0)
.soft_reset = ar8xxx_phy_soft_reset,
#endif
.driver = { .owner = THIS_MODULE },
};
int __init
ar8xxx_init(void)
{
return phy_driver_register(&ar8xxx_phy_driver);
}
void __exit
ar8xxx_exit(void)
{
phy_driver_unregister(&ar8xxx_phy_driver);
}
module_init(ar8xxx_init);
module_exit(ar8xxx_exit);
MODULE_LICENSE("GPL");