X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fsvn-archive%2Farchive.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Far8216.c;h=a07dd784881657841d3b3868c2500326e504297b;hp=497bf1c0fb5a2fdd63a1b2fcd87f22b3afd1c715;hb=934b2e6623df98a2040a95d041c2f98dbd63694e;hpb=0e0a8e4b04a2321e2cabcbb3c560ed210e638255 diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 497bf1c0fb..a07dd78488 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -2,6 +2,7 @@ * ar8216.c: AR8216 switch driver * * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2011-2012 Gabor Juhos * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,61 +31,225 @@ #include #include #include +#include +#include +#include + #include "ar8216.h" /* size of the vlan table */ #define AR8X16_MAX_VLANS 128 #define AR8X16_PROBE_RETRIES 10 +#define AR8X16_MAX_PORTS 8 + +#define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */ + +struct ar8xxx_priv; + +#define AR8XXX_CAP_GIGE BIT(0) +#define AR8XXX_CAP_MIB_COUNTERS BIT(1) -struct ar8216_priv; +enum { + AR8XXX_VER_AR8216 = 0x01, + AR8XXX_VER_AR8236 = 0x03, + AR8XXX_VER_AR8316 = 0x10, + AR8XXX_VER_AR8327 = 0x12, +}; -#define AR8XXX_CAP_GIGE BIT(0) +struct ar8xxx_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; +}; struct ar8xxx_chip { unsigned long caps; - int (*hw_init)(struct ar8216_priv *priv); - void (*init_globals)(struct ar8216_priv *priv); - void (*init_port)(struct ar8216_priv *priv, int port); - void (*setup_port)(struct ar8216_priv *priv, int port, u32 egress, + int (*hw_init)(struct ar8xxx_priv *priv); + void (*init_globals)(struct ar8xxx_priv *priv); + void (*init_port)(struct ar8xxx_priv *priv, int port); + void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid); - u32 (*read_port_status)(struct ar8216_priv *priv, int port); - int (*atu_flush)(struct ar8216_priv *priv); - void (*vtu_flush)(struct ar8216_priv *priv); - void (*vtu_load_vlan)(struct ar8216_priv *priv, u32 vid, u32 port_mask); + u32 (*read_port_status)(struct ar8xxx_priv *priv, int port); + int (*atu_flush)(struct ar8xxx_priv *priv); + void (*vtu_flush)(struct ar8xxx_priv *priv); + void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask); + + const struct ar8xxx_mib_desc *mib_decs; + unsigned num_mibs; +}; + +struct ar8327_data { + u32 port0_status; + u32 port6_status; }; -struct ar8216_priv { +struct ar8xxx_priv { struct switch_dev dev; + struct mii_bus *mii_bus; struct phy_device *phy; - u32 (*read)(struct ar8216_priv *priv, int reg); - void (*write)(struct ar8216_priv *priv, int reg, u32 val); + u32 (*read)(struct ar8xxx_priv *priv, int reg); + void (*write)(struct ar8xxx_priv *priv, int reg, u32 val); const struct net_device_ops *ndo_old; struct net_device_ops ndo; struct mutex reg_mutex; - int chip_type; + u8 chip_ver; + u8 chip_rev; const struct ar8xxx_chip *chip; + union { + struct ar8327_data ar8327; + } chip_data; bool initialized; bool port4_phy; - char buf[80]; + char buf[2048]; bool init; + bool mii_lo_first; + + struct mutex mib_lock; + struct delayed_work mib_work; + int mib_next_port; + u64 *mib_stats; + + struct list_head list; + unsigned int use_count; /* all fields below are cleared on reset */ bool vlan; u16 vlan_id[AR8X16_MAX_VLANS]; u8 vlan_table[AR8X16_MAX_VLANS]; u8 vlan_tagged; - u16 pvid[AR8216_NUM_PORTS]; + u16 pvid[AR8X16_MAX_PORTS]; +}; + +#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"), +}; + +static const struct ar8xxx_mib_desc ar8236_mibs[] = { + 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"), }; -#define to_ar8216(_dev) container_of(_dev, struct ar8216_priv, dev) +static DEFINE_MUTEX(ar8xxx_dev_list_lock); +static LIST_HEAD(ar8xxx_dev_list); + +static inline struct ar8xxx_priv * +swdev_to_ar8xxx(struct switch_dev *swdev) +{ + return container_of(swdev, struct ar8xxx_priv, dev); +} -static inline bool ar8xxx_has_gige(struct ar8216_priv *priv) +static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv) { return priv->chip->caps & AR8XXX_CAP_GIGE; } +static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv) +{ + return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS; +} + +static inline bool chip_is_ar8216(struct ar8xxx_priv *priv) +{ + return priv->chip_ver == AR8XXX_VER_AR8216; +} + +static inline bool chip_is_ar8236(struct ar8xxx_priv *priv) +{ + return priv->chip_ver == AR8XXX_VER_AR8236; +} + +static inline bool chip_is_ar8316(struct ar8xxx_priv *priv) +{ + return priv->chip_ver == AR8XXX_VER_AR8316; +} + +static inline bool chip_is_ar8327(struct ar8xxx_priv *priv) +{ + return priv->chip_ver == AR8XXX_VER_AR8327; +} + static inline void split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) { @@ -99,10 +264,9 @@ split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) } static u32 -ar8216_mii_read(struct ar8216_priv *priv, int reg) +ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg) { - struct phy_device *phy = priv->phy; - struct mii_bus *bus = phy->bus; + struct mii_bus *bus = priv->mii_bus; u16 r1, r2, page; u16 lo, hi; @@ -121,10 +285,9 @@ ar8216_mii_read(struct ar8216_priv *priv, int reg) } static void -ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) +ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val) { - struct phy_device *phy = priv->phy; - struct mii_bus *bus = phy->bus; + struct mii_bus *bus = priv->mii_bus; u16 r1, r2, r3; u16 lo, hi; @@ -136,17 +299,22 @@ ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val) bus->write(bus, 0x18, 0, r3); usleep_range(1000, 2000); /* wait for the page switch to propagate */ - bus->write(bus, 0x10 | r2, r1 + 1, hi); - bus->write(bus, 0x10 | r2, r1, lo); + if (priv->mii_lo_first) { + bus->write(bus, 0x10 | r2, r1, lo); + bus->write(bus, 0x10 | r2, r1 + 1, hi); + } else { + bus->write(bus, 0x10 | r2, r1 + 1, hi); + bus->write(bus, 0x10 | r2, r1, lo); + } mutex_unlock(&bus->mdio_lock); } static void -ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr, +ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, u16 dbg_addr, u16 dbg_data) { - struct mii_bus *bus = priv->phy->bus; + struct mii_bus *bus = priv->mii_bus; mutex_lock(&bus->mdio_lock); bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); @@ -154,8 +322,19 @@ ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr, mutex_unlock(&bus->mdio_lock); } +static 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); +} + static u32 -ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) +ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) { u32 v; @@ -169,8 +348,120 @@ ar8216_rmw(struct ar8216_priv *priv, int reg, u32 mask, u32 val) return v; } +static inline void +ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val) +{ + u32 v; + + lockdep_assert_held(&priv->reg_mutex); + + v = priv->read(priv, reg); + v |= val; + priv->write(priv, reg, v); +} + +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 = priv->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; + int ret; + + lockdep_assert_held(&priv->mib_lock); + + if (chip_is_ar8327(priv)) + mib_func = AR8327_REG_MIB_FUNC; + else + mib_func = AR8216_REG_MIB_FUNC; + + mutex_lock(&priv->reg_mutex); + /* Capture the hardware statistics for all ports */ + ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S)); + mutex_unlock(&priv->reg_mutex); + + /* 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); + + if (chip_is_ar8327(priv)) + base = AR8327_REG_PORT_STATS_BASE(port); + else if (chip_is_ar8236(priv) || + chip_is_ar8316(priv)) + base = AR8236_REG_PORT_STATS_BASE(port); + else + base = AR8216_REG_PORT_STATS_BASE(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 = priv->read(priv, base + mib->offset); + if (mib->size == 2) { + u64 hi; + + hi = priv->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 ar8216_priv *priv, int port, +ar8216_read_port_link(struct ar8xxx_priv *priv, int port, struct switch_port_link *link) { u32 status; @@ -212,79 +503,10 @@ ar8216_read_port_link(struct ar8216_priv *priv, int port, } } -static int -ar8216_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) -{ - struct ar8216_priv *priv = to_ar8216(dev); - priv->vlan = !!val->value.i; - return 0; -} - -static int -ar8216_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) -{ - struct ar8216_priv *priv = to_ar8216(dev); - val->value.i = priv->vlan; - return 0; -} - - -static int -ar8216_set_pvid(struct switch_dev *dev, int port, int vlan) -{ - struct ar8216_priv *priv = to_ar8216(dev); - - /* make sure no invalid PVIDs get set */ - - if (vlan >= dev->vlans) - return -EINVAL; - - priv->pvid[port] = vlan; - return 0; -} - -static int -ar8216_get_pvid(struct switch_dev *dev, int port, int *vlan) -{ - struct ar8216_priv *priv = to_ar8216(dev); - *vlan = priv->pvid[port]; - return 0; -} - -static int -ar8216_set_vid(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) -{ - struct ar8216_priv *priv = to_ar8216(dev); - priv->vlan_id[val->port_vlan] = val->value.i; - return 0; -} - -static int -ar8216_get_vid(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) -{ - struct ar8216_priv *priv = to_ar8216(dev); - val->value.i = priv->vlan_id[val->port_vlan]; - return 0; -} - -static int -ar8216_get_port_link(struct switch_dev *dev, int port, - struct switch_port_link *link) -{ - struct ar8216_priv *priv = to_ar8216(dev); - - ar8216_read_port_link(priv, port, link); - return 0; -} - -static int -ar8216_mangle_tx(struct sk_buff *skb, struct net_device *dev) +static struct sk_buff * +ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb) { - struct ar8216_priv *priv = dev->phy_ptr; + struct ar8xxx_priv *priv = dev->phy_ptr; unsigned char *buf; if (unlikely(!priv)) @@ -303,32 +525,27 @@ ar8216_mangle_tx(struct sk_buff *skb, struct net_device *dev) buf[1] = 0x80; send: - return priv->ndo_old->ndo_start_xmit(skb, dev); + return skb; error: dev_kfree_skb_any(skb); - return 0; + return NULL; } -static int -ar8216_mangle_rx(struct sk_buff *skb, int napi) +static void +ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) { - struct ar8216_priv *priv; - struct net_device *dev; + struct ar8xxx_priv *priv; unsigned char *buf; int port, vlan; - dev = skb->dev; - if (!dev) - goto error; - priv = dev->phy_ptr; if (!priv) - goto error; + return; /* don't strip the header if vlan mode is disabled */ if (!priv->vlan) - goto recv; + return; /* strip header, get vlan id */ buf = skb->data; @@ -336,13 +553,13 @@ ar8216_mangle_rx(struct sk_buff *skb, int napi) /* check for vlan header presence */ if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) - goto recv; + return; port = buf[0] & 0xf; /* no need to fix up packets coming from a tagged source */ if (priv->vlan_tagged & (1 << port)) - goto recv; + return; /* lookup port vid from local table, the switch passes an invalid vlan id */ vlan = priv->vlan_id[priv->pvid[port]]; @@ -350,196 +567,89 @@ ar8216_mangle_rx(struct sk_buff *skb, int napi) buf[14 + 2] &= 0xf0; buf[14 + 2] |= vlan >> 8; buf[15 + 2] = vlan & 0xff; +} + +static int +ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) +{ + int timeout = 20; + u32 t = 0; -recv: - skb->protocol = eth_type_trans(skb, skb->dev); + while (1) { + t = priv->read(priv, reg); + if ((t & mask) == val) + return 0; - if (napi) - return netif_receive_skb(skb); - else - return netif_rx(skb); + if (timeout-- <= 0) + break; -error: - /* no vlan? eat the packet! */ - dev_kfree_skb_any(skb); - return NET_RX_DROP; + udelay(10); + } + + pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n", + (unsigned int) reg, t, mask, val); + return -ETIMEDOUT; } -static int -ar8216_netif_rx(struct sk_buff *skb) +static void +ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) { - return ar8216_mangle_rx(skb, 0); + 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; + priv->write(priv, AR8216_REG_VTU_DATA, val); + } + op |= AR8216_VTU_ACTIVE; + priv->write(priv, AR8216_REG_VTU, op); } -static int -ar8216_netif_receive_skb(struct sk_buff *skb) +static void +ar8216_vtu_flush(struct ar8xxx_priv *priv) { - return ar8216_mangle_rx(skb, 1); + 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; -static struct switch_attr ar8216_globals[] = { - { - .type = SWITCH_TYPE_INT, - .name = "enable_vlan", - .description = "Enable VLAN mode", - .set = ar8216_set_vlan, - .get = ar8216_get_vlan, - .max = 1 - }, -}; - -static struct switch_attr ar8216_port[] = { -}; - -static struct switch_attr ar8216_vlan[] = { - { - .type = SWITCH_TYPE_INT, - .name = "vid", - .description = "VLAN ID (0-4094)", - .set = ar8216_set_vid, - .get = ar8216_get_vid, - .max = 4094, - }, -}; - + op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S); + ar8216_vtu_op(priv, op, port_mask); +} static int -ar8216_get_ports(struct switch_dev *dev, struct switch_val *val) +ar8216_atu_flush(struct ar8xxx_priv *priv) { - struct ar8216_priv *priv = to_ar8216(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; + int ret; - if (!(ports & (1 << i))) - continue; + ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + if (!ret) + priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); - 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; + return ret; } -static int -ar8216_set_ports(struct switch_dev *dev, struct switch_val *val) -{ - struct ar8216_priv *priv = to_ar8216(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 int -ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val) -{ - int timeout = 20; - u32 t = 0; - - while (1) { - t = priv->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 ar8216_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; - priv->write(priv, AR8216_REG_VTU_DATA, val); - } - op |= AR8216_VTU_ACTIVE; - priv->write(priv, AR8216_REG_VTU, op); -} - -static void -ar8216_vtu_flush(struct ar8216_priv *priv) -{ - ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0); -} - -static void -ar8216_vtu_load_vlan(struct ar8216_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 ar8216_priv *priv) -{ - int ret; - - ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); - if (!ret) - priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); - - return ret; -} - -static u32 -ar8216_read_port_status(struct ar8216_priv *priv, int port) +static u32 +ar8216_read_port_status(struct ar8xxx_priv *priv, int port) { return priv->read(priv, AR8216_REG_PORT_STATUS(port)); } static void -ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, +ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid) { u32 header; - if (priv->vlan && port == AR8216_PORT_CPU && priv->chip_type == AR8216) + if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU) header = AR8216_PORT_CTRL_HEADER; else header = 0; - ar8216_rmw(priv, AR8216_REG_PORT_CTRL(port), + 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, @@ -547,7 +657,7 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); - ar8216_rmw(priv, AR8216_REG_PORT_VLAN(port), + 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) | @@ -555,11 +665,69 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); } +static int +ar8216_hw_init(struct ar8xxx_priv *priv) +{ + return 0; +} + +static void +ar8216_init_globals(struct ar8xxx_priv *priv) +{ + /* standard atheros magic */ + priv->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 */ + priv->write(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_LEARN | + (4 << AR8216_PORT_CTRL_STATE_S)); + + priv->write(priv, AR8216_REG_PORT_VLAN(port), 0); + + if (port == AR8216_PORT_CPU) { + priv->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 { + priv->write(priv, AR8216_REG_PORT_STATUS(port), + AR8216_PORT_STATUS_LINK_AUTO); + } +} + +static const struct ar8xxx_chip ar8216_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, + + .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, + + .num_mibs = ARRAY_SIZE(ar8216_mibs), + .mib_decs = ar8216_mibs, +}; + static void -ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, +ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, u32 members, u32 pvid) { - ar8216_rmw(priv, AR8216_REG_PORT_CTRL(port), + 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, @@ -567,11 +735,11 @@ ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, (egress << AR8216_PORT_CTRL_VLAN_MODE_S) | (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S)); - ar8216_rmw(priv, AR8236_REG_PORT_VLAN(port), + ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port), AR8236_PORT_VLAN_DEFAULT_ID, (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S)); - ar8216_rmw(priv, AR8236_REG_PORT_VLAN2(port), + ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port), AR8236_PORT_VLAN2_VLAN_MODE | AR8236_PORT_VLAN2_MEMBER, (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) | @@ -579,80 +747,7 @@ ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress, } static int -ar8216_hw_apply(struct switch_dev *dev) -{ - struct ar8216_priv *priv = to_ar8216(dev); - u8 portmask[AR8216_NUM_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++) { - int egress, ingress; - int pvid; - - if (priv->vlan) { - pvid = priv->vlan_id[priv->pvid[i]]; - if (priv->vlan_tagged & (1 << i)) - egress = AR8216_OUT_ADD_VLAN; - else - egress = AR8216_OUT_STRIP_VLAN; - ingress = AR8216_IN_SECURE; - } else { - pvid = i; - egress = AR8216_OUT_KEEP; - ingress = AR8216_IN_PORT_ONLY; - } - - priv->chip->setup_port(priv, i, egress, ingress, portmask[i], - pvid); - } - mutex_unlock(&priv->reg_mutex); - return 0; -} - -static int -ar8216_hw_init(struct ar8216_priv *priv) -{ - return 0; -} - -static int -ar8236_hw_init(struct ar8216_priv *priv) +ar8236_hw_init(struct ar8xxx_priv *priv) { int i; struct mii_bus *bus; @@ -661,7 +756,7 @@ ar8236_hw_init(struct ar8216_priv *priv) return 0; /* Initialize the PHYs */ - bus = priv->phy->bus; + bus = priv->mii_bus; for (i = 0; i < 5; i++) { mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | @@ -674,167 +769,770 @@ ar8236_hw_init(struct ar8216_priv *priv) return 0; } +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 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 const struct ar8xxx_chip ar8236_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, + .hw_init = ar8236_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, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, +}; + static int -ar8316_hw_init(struct ar8216_priv *priv) +ar8316_hw_init(struct ar8xxx_priv *priv) { int i; u32 val, newval; struct mii_bus *bus; - val = priv->read(priv, 0x8); + val = priv->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; - printk(KERN_INFO "ar8316: Using port 4 as PHY\n"); + pr_info("ar8316: Using port 4 as PHY\n"); } else { newval = 0x01261be2; - printk(KERN_INFO "ar8316: Using port 4 as switch port\n"); + 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 */ - printk(KERN_ERR "ar8316: unsupported mii mode: %d.\n", - priv->phy->interface); + pr_err("ar8316: unsupported mii mode: %d.\n", + priv->phy->interface); return -EINVAL; } if (val == newval) goto out; - priv->write(priv, 0x8, newval); + priv->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); + } /* Initialize the ports */ - bus = priv->phy->bus; + bus = priv->mii_bus; for (i = 0; i < 5; i++) { - if ((i == 4) && priv->port4_phy && - priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { - /* work around for phy4 rgmii mode */ - ar8216_phy_dbg_write(priv, i, 0x12, 0x480c); - /* rx delay */ - ar8216_phy_dbg_write(priv, i, 0x0, 0x824e); - /* tx delay */ - ar8216_phy_dbg_write(priv, i, 0x5, 0x3d47); - msleep(1000); - } - /* initialize the port itself */ mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); - msleep(1000); } + msleep(1000); + out: priv->initialized = true; return 0; } static void -ar8216_init_globals(struct ar8216_priv *priv) +ar8316_init_globals(struct ar8xxx_priv *priv) { /* standard atheros magic */ priv->write(priv, 0x38, 0xc000050e); - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, - AR8216_GCTRL_MTU, 1518 + 8 + 2); -} + /* enable cpu port to receive multicast and broadcast frames */ + priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); -static void -ar8236_init_globals(struct ar8216_priv *priv) -{ /* enable jumbo frames */ - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, + 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); } -static void -ar8316_init_globals(struct ar8216_priv *priv) +static const struct ar8xxx_chip ar8316_chip = { + .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, + .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, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, +}; + +static u32 +ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) { - /* standard atheros magic */ - priv->write(priv, 0x38, 0xc000050e); + u32 t; - /* enable cpu port to receive multicast and broadcast frames */ - priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); + if (!cfg) + return 0; - /* enable jumbo frames */ - ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL, - AR8316_GCTRL_MTU, 9018 + 8 + 2); + t = 0; + switch (cfg->mode) { + case AR8327_PAD_NC: + break; + + case AR8327_PAD_MAC2MAC_MII: + t = AR8327_PAD_MAC_MII_EN; + if (cfg->rxclk_sel) + t |= AR8327_PAD_MAC_MII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_MAC_MII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC2MAC_GMII: + t = AR8327_PAD_MAC_GMII_EN; + if (cfg->rxclk_sel) + t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC_SGMII: + t = AR8327_PAD_SGMII_EN; + + /* + * WAR for the QUalcomm Atheros AP136 board. + * It seems that RGMII TX/RX delay settings needs to be + * applied for SGMII mode as well, The ethernet is not + * reliable without this. + */ + t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; + t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; + if (cfg->rxclk_delay_en) + t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; + if (cfg->txclk_delay_en) + t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; + + if (cfg->sgmii_delay_en) + t |= AR8327_PAD_SGMII_DELAY_EN; + + break; + + case AR8327_PAD_MAC2PHY_MII: + t = AR8327_PAD_PHY_MII_EN; + if (cfg->rxclk_sel) + t |= AR8327_PAD_PHY_MII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_PHY_MII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC2PHY_GMII: + t = AR8327_PAD_PHY_GMII_EN; + if (cfg->pipe_rxclk_sel) + t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; + if (cfg->rxclk_sel) + t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC_RGMII: + t = AR8327_PAD_RGMII_EN; + t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; + t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; + if (cfg->rxclk_delay_en) + t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; + if (cfg->txclk_delay_en) + t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; + break; + + case AR8327_PAD_PHY_GMII: + t = AR8327_PAD_PHYX_GMII_EN; + break; + + case AR8327_PAD_PHY_RGMII: + t = AR8327_PAD_PHYX_RGMII_EN; + break; + + case AR8327_PAD_PHY_MII: + t = AR8327_PAD_PHYX_MII_EN; + break; + } + + return t; } static void -ar8216_init_port(struct ar8216_priv *priv, int port) +ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy) { - /* Enable port learning and tx */ - priv->write(priv, AR8216_REG_PORT_CTRL(port), - AR8216_PORT_CTRL_LEARN | - (4 << AR8216_PORT_CTRL_STATE_S)); + switch (priv->chip_rev) { + case 1: + /* For 100M waveform */ + ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea); + /* Turn on Gigabit clock */ + ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0); + break; - priv->write(priv, AR8216_REG_PORT_VLAN(port), 0); + case 2: + ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c); + ar8xxx_phy_mmd_write(priv, phy, 0x4007, 0x0); + /* fallthrough */ + case 4: + ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d); + ar8xxx_phy_mmd_write(priv, phy, 0x4003, 0x803f); + + ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860); + ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46); + ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000); + break; + } +} - if (port == AR8216_PORT_CPU) { - priv->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 | - ((priv->chip_type == AR8316) ? AR8216_PORT_STATUS_RXFLOW : 0) | - ((priv->chip_type == AR8316) ? AR8216_PORT_STATUS_TXFLOW : 0) | - AR8216_PORT_STATUS_DUPLEX); - } else { - priv->write(priv, AR8216_REG_PORT_STATUS(port), - AR8216_PORT_STATUS_LINK_AUTO); +static u32 +ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) +{ + u32 t; + + if (!cfg->force_link) + return AR8216_PORT_STATUS_LINK_AUTO; + + t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC; + t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0; + t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0; + t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0; + + switch (cfg->speed) { + case AR8327_PORT_SPEED_10: + t |= AR8216_PORT_SPEED_10M; + break; + case AR8327_PORT_SPEED_100: + t |= AR8216_PORT_SPEED_100M; + break; + case AR8327_PORT_SPEED_1000: + t |= AR8216_PORT_SPEED_1000M; + break; } + + return t; } -static const struct ar8xxx_chip ar8216_chip = { - .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, -}; +static int +ar8327_hw_config_pdata(struct ar8xxx_priv *priv, + struct ar8327_platform_data *pdata) +{ + struct ar8327_led_cfg *led_cfg; + struct ar8327_data *data; + u32 pos, new_pos; + u32 t; -static const struct ar8xxx_chip ar8236_chip = { - .hw_init = ar8236_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, + if (!pdata) + return -EINVAL; + + data = &priv->chip_data.ar8327; + + data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg); + data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg); + + t = ar8327_get_pad_cfg(pdata->pad0_cfg); + priv->write(priv, AR8327_REG_PAD0_MODE, t); + t = ar8327_get_pad_cfg(pdata->pad5_cfg); + priv->write(priv, AR8327_REG_PAD5_MODE, t); + t = ar8327_get_pad_cfg(pdata->pad6_cfg); + priv->write(priv, AR8327_REG_PAD6_MODE, t); + + pos = priv->read(priv, AR8327_REG_POWER_ON_STRIP); + new_pos = pos; + + led_cfg = pdata->led_cfg; + if (led_cfg) { + if (led_cfg->open_drain) + new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN; + else + new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN; + + priv->write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0); + priv->write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1); + priv->write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2); + priv->write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3); + } + + if (new_pos != pos) { + new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL; + priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos); + } + + return 0; +} + +#ifdef CONFIG_OF +static int +ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) +{ + const __be32 *paddr; + int len; + int i; + + paddr = of_get_property(np, "qca,ar8327-initvals", &len); + if (!paddr || len < (2 * sizeof(*paddr))) + return -EINVAL; + + len /= sizeof(*paddr); + + for (i = 0; i < len - 1; i += 2) { + u32 reg; + u32 val; + + reg = be32_to_cpup(paddr + i); + val = be32_to_cpup(paddr + i + 1); + + switch (reg) { + case AR8327_REG_PORT_STATUS(0): + priv->chip_data.ar8327.port0_status = val; + break; + case AR8327_REG_PORT_STATUS(6): + priv->chip_data.ar8327.port6_status = val; + break; + default: + priv->write(priv, reg, val); + break; + } + } + + return 0; +} +#else +static inline int +ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) +{ + return -EINVAL; +} +#endif + +static int +ar8327_hw_init(struct ar8xxx_priv *priv) +{ + struct mii_bus *bus; + int ret; + int i; + + if (priv->phy->dev.of_node) + ret = ar8327_hw_config_of(priv, priv->phy->dev.of_node); + else + ret = ar8327_hw_config_pdata(priv, + priv->phy->dev.platform_data); + + if (ret) + return ret; + + bus = priv->mii_bus; + for (i = 0; i < AR8327_NUM_PHYS; i++) { + ar8327_phy_fixup(priv, i); + + /* start aneg on the PHY */ + mdiobus_write(bus, i, MII_ADVERTISE, ADVERTISE_ALL | + ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL); + mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE); + } + + msleep(1000); + + return 0; +} + +static void +ar8327_init_globals(struct ar8xxx_priv *priv) +{ + u32 t; + + /* enable CPU port and disable mirror port */ + t = AR8327_FWD_CTRL0_CPU_PORT_EN | + AR8327_FWD_CTRL0_MIRROR_PORT; + priv->write(priv, AR8327_REG_FWD_CTRL0, t); + + /* forward multicast and broadcast frames to CPU */ + t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) | + (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) | + (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S); + priv->write(priv, AR8327_REG_FWD_CTRL1, t); + + /* setup MTU */ + ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE, + AR8327_MAX_FRAME_SIZE_MTU, 1518 + 8 + 2); + + /* Enable MIB counters */ + ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN, + AR8327_MODULE_EN_MIB); +} + +static void +ar8327_init_port(struct ar8xxx_priv *priv, int port) +{ + u32 t; + + if (port == AR8216_PORT_CPU) + t = priv->chip_data.ar8327.port0_status; + else if (port == 6) + t = priv->chip_data.ar8327.port6_status; + else + t = AR8216_PORT_STATUS_LINK_AUTO; + + priv->write(priv, AR8327_REG_PORT_STATUS(port), t); + priv->write(priv, AR8327_REG_PORT_HEADER(port), 0); + + t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; + t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; + priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); + + t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; + priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); + + t = AR8327_PORT_LOOKUP_LEARN; + t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; + priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t); +} + +static u32 +ar8327_read_port_status(struct ar8xxx_priv *priv, int port) +{ + return priv->read(priv, AR8327_REG_PORT_STATUS(port)); +} + +static int +ar8327_atu_flush(struct ar8xxx_priv *priv) +{ + int ret; + + ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC, + AR8327_ATU_FUNC_BUSY, 0); + if (!ret) + priv->write(priv, AR8327_REG_ATU_FUNC, + AR8327_ATU_FUNC_OP_FLUSH); + + return ret; +} + +static void +ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) +{ + if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1, + AR8327_VTU_FUNC1_BUSY, 0)) + return; + + if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) + priv->write(priv, AR8327_REG_VTU_FUNC0, val); + + op |= AR8327_VTU_FUNC1_BUSY; + priv->write(priv, AR8327_REG_VTU_FUNC1, op); +} + +static void +ar8327_vtu_flush(struct ar8xxx_priv *priv) +{ + ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0); +} + +static void +ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) +{ + u32 op; + u32 val; + int i; + + op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S); + val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; + for (i = 0; i < AR8327_NUM_PORTS; i++) { + u32 mode; + + if ((port_mask & BIT(i)) == 0) + mode = AR8327_VTU_FUNC0_EG_MODE_NOT; + else if (priv->vlan == 0) + mode = AR8327_VTU_FUNC0_EG_MODE_KEEP; + else if (priv->vlan_tagged & BIT(i)) + mode = AR8327_VTU_FUNC0_EG_MODE_TAG; + else + mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; + + val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i); + } + ar8327_vtu_op(priv, op, val); +} + +static void +ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress, + u32 members, u32 pvid) +{ + u32 t; + u32 mode; + + t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; + t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; + priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); + + mode = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; + switch (egress) { + case AR8216_OUT_KEEP: + mode = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; + break; + case AR8216_OUT_STRIP_VLAN: + mode = AR8327_PORT_VLAN1_OUT_MODE_UNTAG; + break; + case AR8216_OUT_ADD_VLAN: + mode = AR8327_PORT_VLAN1_OUT_MODE_TAG; + break; + } + + t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; + t |= mode << AR8327_PORT_VLAN1_OUT_MODE_S; + priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); + + t = members; + t |= AR8327_PORT_LOOKUP_LEARN; + t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S; + t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; + priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t); +} + +static const struct ar8xxx_chip ar8327_chip = { + .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, + .hw_init = ar8327_hw_init, + .init_globals = ar8327_init_globals, + .init_port = ar8327_init_port, + .setup_port = ar8327_setup_port, + .read_port_status = ar8327_read_port_status, + .atu_flush = ar8327_atu_flush, + .vtu_flush = ar8327_vtu_flush, + .vtu_load_vlan = ar8327_vtu_load_vlan, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, }; -static const struct ar8xxx_chip ar8316_chip = { - .caps = AR8XXX_CAP_GIGE, - .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, -}; +static 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; +} + +static 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; +} + + +static 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; +} + +static 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; +} + +static 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 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++) { + int egress, ingress; + int pvid; + + if (priv->vlan) { + pvid = priv->vlan_id[priv->pvid[i]]; + if (priv->vlan_tagged & (1 << i)) + egress = AR8216_OUT_ADD_VLAN; + else + egress = AR8216_OUT_STRIP_VLAN; + ingress = AR8216_IN_SECURE; + } else { + pvid = i; + egress = AR8216_OUT_KEEP; + ingress = AR8216_IN_PORT_ONLY; + } + + priv->chip->setup_port(priv, i, egress, ingress, portmask[i], + pvid); + } + mutex_unlock(&priv->reg_mutex); + return 0; +} static int -ar8216_reset_switch(struct switch_dev *dev) +ar8xxx_sw_reset_switch(struct switch_dev *dev) { - struct ar8216_priv *priv = to_ar8216(dev); + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); int i; mutex_lock(&priv->reg_mutex); - memset(&priv->vlan, 0, sizeof(struct ar8216_priv) - - offsetof(struct ar8216_priv, vlan)); + 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; @@ -846,41 +1544,190 @@ ar8216_reset_switch(struct switch_dev *dev) priv->chip->init_globals(priv); mutex_unlock(&priv->reg_mutex); - return ar8216_hw_apply(dev); + return ar8xxx_sw_hw_apply(dev); +} + +static 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; +} + +static 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; +} + +static 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_dev_ops ar8216_sw_ops = { +static 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, + }, + +}; + +static struct switch_attr ar8xxx_sw_attr_port[] = { + { + .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, + }, +}; + +static struct switch_attr ar8xxx_sw_attr_vlan[] = { + { + .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 = ar8216_globals, - .n_attr = ARRAY_SIZE(ar8216_globals), + .attr = ar8xxx_sw_attr_globals, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals), }, .attr_port = { - .attr = ar8216_port, - .n_attr = ARRAY_SIZE(ar8216_port), + .attr = ar8xxx_sw_attr_port, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port), }, .attr_vlan = { - .attr = ar8216_vlan, - .n_attr = ARRAY_SIZE(ar8216_vlan), + .attr = ar8xxx_sw_attr_vlan, + .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan), }, - .get_port_pvid = ar8216_get_pvid, - .set_port_pvid = ar8216_set_pvid, - .get_vlan_ports = ar8216_get_ports, - .set_vlan_ports = ar8216_set_ports, - .apply_config = ar8216_hw_apply, - .reset_switch = ar8216_reset_switch, - .get_port_link = ar8216_get_port_link, + .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 int -ar8216_id_chip(struct ar8216_priv *priv) +ar8xxx_id_chip(struct ar8xxx_priv *priv) { u32 val; u16 id; int i; - priv->chip_type = UNKNOWN; - - val = ar8216_mii_read(priv, AR8216_REG_CTRL); + val = priv->read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -888,7 +1735,7 @@ ar8216_id_chip(struct ar8216_priv *priv) for (i = 0; i < AR8X16_PROBE_RETRIES; i++) { u16 t; - val = ar8216_mii_read(priv, AR8216_REG_CTRL); + val = priv->read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -897,27 +1744,26 @@ ar8216_id_chip(struct ar8216_priv *priv) return -ENODEV; } - switch (id) { - case 0x0101: - priv->chip_type = AR8216; + 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 0x0301: - priv->chip_type = AR8236; + case AR8XXX_VER_AR8236: priv->chip = &ar8236_chip; break; - case 0x1000: - case 0x1001: - priv->chip_type = AR8316; + case AR8XXX_VER_AR8316: priv->chip = &ar8316_chip; break; + case AR8XXX_VER_AR8327: + priv->mii_lo_first = true; + priv->chip = &ar8327_chip; + break; default: - printk(KERN_DEBUG - "ar8216: Unknown Atheros device [ver=%d, rev=%d, phy_id=%04x%04x]\n", - (int)(id >> AR8216_CTRL_VERSION_S), - (int)(id & AR8216_CTRL_REVISION), - mdiobus_read(priv->phy->bus, priv->phy->addr, 2), - mdiobus_read(priv->phy->bus, priv->phy->addr, 3)); + pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n", + priv->chip_ver, priv->chip_rev); return -ENODEV; } @@ -925,139 +1771,217 @@ ar8216_id_chip(struct ar8216_priv *priv) 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 -ar8216_config_init(struct phy_device *pdev) +ar8xxx_mib_init(struct ar8xxx_priv *priv) { - struct ar8216_priv *priv = pdev->priv; - struct net_device *dev = pdev->attached_dev; - struct switch_dev *swdev; - int ret; + unsigned int len; - if (!priv) { - priv = kzalloc(sizeof(struct ar8216_priv), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - } + if (!ar8xxx_has_mib_counters(priv)) + return 0; - priv->phy = pdev; + BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs); - ret = ar8216_id_chip(priv); - if (ret) - goto err_free_priv; + len = priv->dev.ports * priv->chip->num_mibs * + sizeof(*priv->mib_stats); + priv->mib_stats = kzalloc(len, GFP_KERNEL); - if (pdev->addr != 0) { - if (ar8xxx_has_gige(priv)) { - pdev->supported |= SUPPORTED_1000baseT_Full; - pdev->advertising |= ADVERTISED_1000baseT_Full; - } + if (!priv->mib_stats) + return -ENOMEM; - if (priv->chip_type == AR8316) { - /* check if we're attaching to the switch twice */ - pdev = pdev->bus->phy_map[0]; - if (!pdev) { - kfree(priv); - return 0; - } + return 0; +} - /* switch device has not been initialized, reuse priv */ - if (!pdev->priv) { - priv->port4_phy = true; - pdev->priv = priv; - return 0; - } +static void +ar8xxx_mib_start(struct ar8xxx_priv *priv) +{ + if (!ar8xxx_has_mib_counters(priv)) + return; - kfree(priv); + schedule_delayed_work(&priv->mib_work, + msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); +} - /* switch device has been initialized, reinit */ - priv = pdev->priv; - priv->dev.ports = (AR8216_NUM_PORTS - 1); - priv->initialized = false; - priv->port4_phy = true; - ar8316_hw_init(priv); - return 0; - } +static void +ar8xxx_mib_stop(struct ar8xxx_priv *priv) +{ + if (!ar8xxx_has_mib_counters(priv)) + return; - kfree(priv); - return 0; - } + cancel_delayed_work(&priv->mib_work); +} - printk(KERN_INFO "%s: AR%d switch driver attached.\n", - pdev->attached_dev->name, priv->chip_type); +static struct ar8xxx_priv * +ar8xxx_create(void) +{ + struct ar8xxx_priv *priv; - if (ar8xxx_has_gige(priv)) - pdev->supported = SUPPORTED_1000baseT_Full; - else - pdev->supported = SUPPORTED_100baseT_Full; - pdev->advertising = pdev->supported; + priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; mutex_init(&priv->reg_mutex); - priv->read = ar8216_mii_read; - priv->write = ar8216_mii_write; + mutex_init(&priv->mib_lock); + INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func); - pdev->priv = priv; + return priv; +} + +static void +ar8xxx_free(struct ar8xxx_priv *priv) +{ + kfree(priv->mib_stats); + kfree(priv); +} + +static struct ar8xxx_priv * +ar8xxx_create_mii(struct mii_bus *bus) +{ + struct ar8xxx_priv *priv; + + priv = ar8xxx_create(); + if (priv) { + priv->mii_bus = bus; + priv->read = ar8xxx_mii_read; + priv->write = ar8xxx_mii_write; + } + + return priv; +} + +static int +ar8xxx_probe_switch(struct ar8xxx_priv *priv) +{ + struct switch_dev *swdev; + int ret; + + ret = ar8xxx_id_chip(priv); + if (ret) + return ret; swdev = &priv->dev; swdev->cpu_port = AR8216_PORT_CPU; - swdev->ops = &ar8216_sw_ops; - swdev->ports = AR8216_NUM_PORTS; + swdev->ops = &ar8xxx_sw_ops; - if (priv->chip_type == AR8316) { + if (chip_is_ar8316(priv)) { swdev->name = "Atheros AR8316"; swdev->vlans = AR8X16_MAX_VLANS; - - if (priv->port4_phy) { - /* port 5 connected to the other mac, therefore unusable */ - swdev->ports = (AR8216_NUM_PORTS - 1); - } - } else if (priv->chip_type == AR8236) { + swdev->ports = AR8216_NUM_PORTS; + } else if (chip_is_ar8236(priv)) { swdev->name = "Atheros AR8236"; swdev->vlans = AR8216_NUM_VLANS; swdev->ports = AR8216_NUM_PORTS; + } else if (chip_is_ar8327(priv)) { + swdev->name = "Atheros AR8327"; + swdev->vlans = AR8X16_MAX_VLANS; + swdev->ports = AR8327_NUM_PORTS; } else { swdev->name = "Atheros AR8216"; swdev->vlans = AR8216_NUM_VLANS; + swdev->ports = AR8216_NUM_PORTS; } - ret = register_switch(&priv->dev, pdev->attached_dev); + ret = ar8xxx_mib_init(priv); if (ret) - goto err_free_priv; + 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) - goto err_free_priv; + return ret; - ret = ar8216_reset_switch(&priv->dev); + ret = ar8xxx_sw_reset_switch(&priv->dev); if (ret) - goto err_free_priv; + return ret; - dev->phy_ptr = priv; + priv->init = false; - /* VID fixup only needed on ar8216 */ - if (pdev->addr == 0 && priv->chip_type == 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; + 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 (chip_is_ar8327(priv)) + return 0; + + 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; } - priv->init = false; + ret = ar8xxx_start(priv); + if (ret) + return ret; - return 0; + /* 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; + } -err_free_priv: - kfree(priv); - return ret; + return 0; } static int -ar8216_read_status(struct phy_device *phydev) +ar8xxx_phy_read_status(struct phy_device *phydev) { - struct ar8216_priv *priv = phydev->priv; + struct ar8xxx_priv *priv = phydev->priv; struct switch_port_link link; int ret; @@ -1097,7 +2021,7 @@ ar8216_read_status(struct phy_device *phydev) } static int -ar8216_config_aneg(struct phy_device *phydev) +ar8xxx_phy_config_aneg(struct phy_device *phydev) { if (phydev->addr == 0) return 0; @@ -1105,57 +2029,192 @@ ar8216_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } +static const u32 ar8xxx_phy_ids[] = { + 0x004dd033, + 0x004dd034, + 0x004dd041, + 0x004dd042, +}; + +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 -ar8216_probe(struct phy_device *pdev) +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_mii(phydev->bus); + if (priv == NULL) { + ret = -ENOMEM; + goto unlock; + } + + 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 switch registered on %s\n", + swdev->devname, swdev->name, 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 (chip_is_ar8327(priv)) { + 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 ar8216_priv priv; + struct net_device *dev = phydev->attached_dev; - priv.phy = pdev; - return ar8216_id_chip(&priv); + 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 -ar8216_remove(struct phy_device *pdev) +ar8xxx_phy_remove(struct phy_device *phydev) { - struct ar8216_priv *priv = pdev->priv; - struct net_device *dev = pdev->attached_dev; + struct ar8xxx_priv *priv = phydev->priv; - if (!priv) + if (WARN_ON(!priv)) return; - if (priv->ndo_old && dev) - dev->netdev_ops = priv->ndo_old; - if (pdev->addr == 0) - unregister_switch(&priv->dev); - kfree(priv); + 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); } -static struct phy_driver ar8216_driver = { +static struct phy_driver ar8xxx_phy_driver = { .phy_id = 0x004d0000, .name = "Atheros AR8216/AR8236/AR8316", .phy_id_mask = 0xffff0000, .features = PHY_BASIC_FEATURES, - .probe = ar8216_probe, - .remove = ar8216_remove, - .config_init = &ar8216_config_init, - .config_aneg = &ar8216_config_aneg, - .read_status = &ar8216_read_status, + .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, .driver = { .owner = THIS_MODULE }, }; int __init -ar8216_init(void) +ar8xxx_init(void) { - return phy_driver_register(&ar8216_driver); + return phy_driver_register(&ar8xxx_phy_driver); } void __exit -ar8216_exit(void) +ar8xxx_exit(void) { - phy_driver_unregister(&ar8216_driver); + phy_driver_unregister(&ar8xxx_phy_driver); } -module_init(ar8216_init); -module_exit(ar8216_exit); +module_init(ar8xxx_init); +module_exit(ar8xxx_exit); MODULE_LICENSE("GPL");