summaryrefslogtreecommitdiffstats
path: root/target/linux/generic/files/drivers/net
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/files/drivers/net')
-rw-r--r--target/linux/generic/files/drivers/net/phy/ar8216.c1594
-rw-r--r--target/linux/generic/files/drivers/net/phy/ar8216.h140
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/Kconfig30
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/Makefile9
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_common.c1292
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c401
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c241
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c53
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_priv.h278
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_regs.h308
-rw-r--r--target/linux/generic/files/drivers/net/phy/b53/b53_spi.c329
-rw-r--r--target/linux/generic/files/drivers/net/phy/mvswitch.c13
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366_smi.c76
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366_smi.h3
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366rb.c40
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8366s.c44
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8367.c109
-rw-r--r--target/linux/generic/files/drivers/net/phy/rtl8367b.c1602
-rw-r--r--target/linux/generic/files/drivers/net/phy/swconfig.c129
-rw-r--r--target/linux/generic/files/drivers/net/phy/swconfig_leds.c4
20 files changed, 6298 insertions, 397 deletions
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c
index 4df2dbbdb..b6df6425a 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.c
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.c
@@ -32,6 +32,9 @@
#include <linux/etherdevice.h>
#include <linux/lockdep.h>
#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/of_device.h>
+
#include "ar8216.h"
/* size of the vlan table */
@@ -39,9 +42,12 @@
#define AR8X16_PROBE_RETRIES 10
#define AR8X16_MAX_PORTS 8
-struct ar8216_priv;
+#define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */
+
+struct ar8xxx_priv;
-#define AR8XXX_CAP_GIGE BIT(0)
+#define AR8XXX_CAP_GIGE BIT(0)
+#define AR8XXX_CAP_MIB_COUNTERS BIT(1)
enum {
AR8XXX_VER_AR8216 = 0x01,
@@ -50,69 +56,202 @@ enum {
AR8XXX_VER_AR8327 = 0x12,
};
+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 ar8216_priv {
+struct ar8327_data {
+ u32 port0_status;
+ u32 port6_status;
+};
+
+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;
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[AR8X16_MAX_PORTS];
+
+ /* mirroring */
+ bool mirror_rx;
+ bool mirror_tx;
+ int source_port;
+ int monitor_port;
};
-#define to_ar8216(_dev) container_of(_dev, struct ar8216_priv, dev)
+#define MIB_DESC(_s , _o, _n) \
+ { \
+ .size = (_s), \
+ .offset = (_o), \
+ .name = (_n), \
+ }
-static inline bool ar8xxx_has_gige(struct ar8216_priv *priv)
+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"),
+};
+
+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 ar8xxx_priv *priv)
{
return priv->chip->caps & AR8XXX_CAP_GIGE;
}
-static inline bool chip_is_ar8216(struct ar8216_priv *priv)
+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 ar8216_priv *priv)
+static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8236;
}
-static inline bool chip_is_ar8316(struct ar8216_priv *priv)
+static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8316;
}
-static inline bool chip_is_ar8327(struct ar8216_priv *priv)
+static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
{
return priv->chip_ver == AR8XXX_VER_AR8327;
}
@@ -131,10 +270,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;
@@ -153,10 +291,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;
@@ -180,10 +317,10 @@ ar8216_mii_write(struct ar8216_priv *priv, int reg, u32 val)
}
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);
@@ -192,9 +329,9 @@ ar8216_phy_dbg_write(struct ar8216_priv *priv, int phy_addr,
}
static void
-ar8216_phy_mmd_write(struct ar8216_priv *priv, int phy_addr, u16 addr, u16 data)
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 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_MMD_ADDR, addr);
@@ -203,7 +340,7 @@ ar8216_phy_mmd_write(struct ar8216_priv *priv, int phy_addr, u16 addr, u16 data)
}
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;
@@ -217,8 +354,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
-ar8216_read_port_link(struct ar8216_priv *priv, int port,
+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 ar8xxx_priv *priv, int port,
struct switch_port_link *link)
{
u32 status;
@@ -263,7 +512,7 @@ ar8216_read_port_link(struct ar8216_priv *priv, int port,
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))
@@ -292,7 +541,7 @@ error:
static void
ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
{
- struct ar8216_priv *priv;
+ struct ar8xxx_priv *priv;
unsigned char *buf;
int port, vlan;
@@ -327,7 +576,7 @@ ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
}
static int
-ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val)
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
{
int timeout = 20;
u32 t = 0;
@@ -349,7 +598,7 @@ ar8216_wait_bit(struct ar8216_priv *priv, int reg, u32 mask, u32 val)
}
static void
-ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val)
+ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
{
if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0))
return;
@@ -363,13 +612,13 @@ ar8216_vtu_op(struct ar8216_priv *priv, u32 op, u32 val)
}
static void
-ar8216_vtu_flush(struct ar8216_priv *priv)
+ar8216_vtu_flush(struct ar8xxx_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)
+ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
{
u32 op;
@@ -378,7 +627,7 @@ ar8216_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask)
}
static int
-ar8216_atu_flush(struct ar8216_priv *priv)
+ar8216_atu_flush(struct ar8xxx_priv *priv)
{
int ret;
@@ -390,13 +639,13 @@ ar8216_atu_flush(struct ar8216_priv *priv)
}
static u32
-ar8216_read_port_status(struct ar8216_priv *priv, int port)
+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;
@@ -406,7 +655,7 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress,
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,
@@ -414,7 +663,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) |
@@ -423,23 +672,23 @@ ar8216_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress,
}
static int
-ar8216_hw_init(struct ar8216_priv *priv)
+ar8216_hw_init(struct ar8xxx_priv *priv)
{
return 0;
}
static void
-ar8216_init_globals(struct ar8216_priv *priv)
+ar8216_init_globals(struct ar8xxx_priv *priv)
{
/* standard atheros magic */
priv->write(priv, 0x38, 0xc000050e);
- ar8216_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
AR8216_GCTRL_MTU, 1518 + 8 + 2);
}
static void
-ar8216_init_port(struct ar8216_priv *priv, int port)
+ar8216_init_port(struct ar8xxx_priv *priv, int port)
{
/* Enable port learning and tx */
priv->write(priv, AR8216_REG_PORT_CTRL(port),
@@ -465,6 +714,8 @@ ar8216_init_port(struct ar8216_priv *priv, int port)
}
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,
@@ -473,13 +724,16 @@ static const struct ar8xxx_chip ar8216_chip = {
.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,
@@ -487,11 +741,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) |
@@ -499,7 +753,7 @@ ar8236_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress,
}
static int
-ar8236_hw_init(struct ar8216_priv *priv)
+ar8236_hw_init(struct ar8xxx_priv *priv)
{
int i;
struct mii_bus *bus;
@@ -508,7 +762,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 |
@@ -522,14 +776,20 @@ ar8236_hw_init(struct ar8216_priv *priv)
}
static void
-ar8236_init_globals(struct ar8216_priv *priv)
+ar8236_init_globals(struct ar8xxx_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 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,
@@ -538,70 +798,74 @@ static const struct ar8xxx_chip ar8236_chip = {
.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
-ar8316_init_globals(struct ar8216_priv *priv)
+ar8316_init_globals(struct ar8xxx_priv *priv)
{
/* standard atheros magic */
priv->write(priv, 0x38, 0xc000050e);
@@ -610,12 +874,17 @@ ar8316_init_globals(struct ar8216_priv *priv)
priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f);
/* 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 const struct ar8xxx_chip ar8316_chip = {
- .caps = AR8XXX_CAP_GIGE,
+ .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
.hw_init = ar8316_hw_init,
.init_globals = ar8316_init_globals,
.init_port = ar8216_init_port,
@@ -624,6 +893,9 @@ static const struct ar8xxx_chip ar8316_chip = {
.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
@@ -657,6 +929,23 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
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:
@@ -704,45 +993,76 @@ ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
}
static void
-ar8327_phy_fixup(struct ar8216_priv *priv, int phy)
+ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy)
{
switch (priv->chip_rev) {
case 1:
/* For 100M waveform */
- ar8216_phy_dbg_write(priv, phy, 0, 0x02ea);
+ ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea);
/* Turn on Gigabit clock */
- ar8216_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
+ ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
break;
case 2:
- ar8216_phy_mmd_write(priv, phy, 0x7, 0x3c);
- ar8216_phy_mmd_write(priv, phy, 0x4007, 0x0);
+ ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c);
+ ar8xxx_phy_mmd_write(priv, phy, 0x4007, 0x0);
/* fallthrough */
case 4:
- ar8216_phy_mmd_write(priv, phy, 0x3, 0x800d);
- ar8216_phy_mmd_write(priv, phy, 0x4003, 0x803f);
+ 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;
+ }
+}
+
+static u32
+ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
+{
+ u32 t;
+
+ if (!cfg->force_link)
+ return AR8216_PORT_STATUS_LINK_AUTO;
- ar8216_phy_dbg_write(priv, phy, 0x3d, 0x6860);
- ar8216_phy_dbg_write(priv, phy, 0x5, 0x2c46);
- ar8216_phy_dbg_write(priv, phy, 0x3c, 0x6000);
+ 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 int
-ar8327_hw_init(struct ar8216_priv *priv)
+ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
+ struct ar8327_platform_data *pdata)
{
- struct ar8327_platform_data *pdata;
struct ar8327_led_cfg *led_cfg;
- struct mii_bus *bus;
+ struct ar8327_data *data;
u32 pos, new_pos;
u32 t;
- int i;
- pdata = priv->phy->dev.platform_data;
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);
@@ -771,7 +1091,70 @@ ar8327_hw_init(struct ar8216_priv *priv)
priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
}
- bus = priv->phy->bus;
+ 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);
@@ -789,7 +1172,7 @@ ar8327_hw_init(struct ar8216_priv *priv)
}
static void
-ar8327_init_globals(struct ar8216_priv *priv)
+ar8327_init_globals(struct ar8xxx_priv *priv)
{
u32 t;
@@ -805,62 +1188,32 @@ ar8327_init_globals(struct ar8216_priv *priv)
priv->write(priv, AR8327_REG_FWD_CTRL1, t);
/* setup MTU */
- ar8216_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
+ ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
AR8327_MAX_FRAME_SIZE_MTU, 1518 + 8 + 2);
-}
-
-static void
-ar8327_init_cpuport(struct ar8216_priv *priv)
-{
- struct ar8327_platform_data *pdata;
- struct ar8327_port_cfg *cfg;
- u32 t;
-
- pdata = priv->phy->dev.platform_data;
- if (!pdata)
- return;
-
- cfg = &pdata->cpuport_cfg;
- if (!cfg->force_link) {
- priv->write(priv, AR8327_REG_PORT_STATUS(AR8216_PORT_CPU),
- AR8216_PORT_STATUS_LINK_AUTO);
- return;
- }
-
- 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;
- }
- priv->write(priv, AR8327_REG_PORT_STATUS(AR8216_PORT_CPU), t);
+ /* Enable MIB counters */
+ ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN,
+ AR8327_MODULE_EN_MIB);
}
static void
-ar8327_init_port(struct ar8216_priv *priv, int port)
+ar8327_init_port(struct ar8xxx_priv *priv, int port)
{
u32 t;
- if (port == AR8216_PORT_CPU) {
- ar8327_init_cpuport(priv);
- } else {
+ 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_STATUS(port), t);
priv->write(priv, AR8327_REG_PORT_HEADER(port), 0);
- priv->write(priv, AR8327_REG_PORT_VLAN0(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);
@@ -871,13 +1224,13 @@ ar8327_init_port(struct ar8216_priv *priv, int port)
}
static u32
-ar8327_read_port_status(struct ar8216_priv *priv, int port)
+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 ar8216_priv *priv)
+ar8327_atu_flush(struct ar8xxx_priv *priv)
{
int ret;
@@ -891,7 +1244,7 @@ ar8327_atu_flush(struct ar8216_priv *priv)
}
static void
-ar8327_vtu_op(struct ar8216_priv *priv, u32 op, u32 val)
+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))
@@ -905,13 +1258,13 @@ ar8327_vtu_op(struct ar8216_priv *priv, u32 op, u32 val)
}
static void
-ar8327_vtu_flush(struct ar8216_priv *priv)
+ar8327_vtu_flush(struct ar8xxx_priv *priv)
{
ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0);
}
static void
-ar8327_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask)
+ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
{
u32 op;
u32 val;
@@ -937,7 +1290,7 @@ ar8327_vtu_load_vlan(struct ar8216_priv *priv, u32 vid, u32 port_mask)
}
static void
-ar8327_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress,
+ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 egress, u32 ingress,
u32 members, u32 pvid)
{
u32 t;
@@ -972,7 +1325,7 @@ ar8327_setup_port(struct ar8216_priv *priv, int port, u32 egress, u32 ingress,
}
static const struct ar8xxx_chip ar8327_chip = {
- .caps = AR8XXX_CAP_GIGE,
+ .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
.hw_init = ar8327_hw_init,
.init_globals = ar8327_init_globals,
.init_port = ar8327_init_port,
@@ -981,31 +1334,34 @@ static const struct ar8xxx_chip ar8327_chip = {
.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 int
-ar8216_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
priv->vlan = !!val->value.i;
return 0;
}
static int
-ar8216_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
val->value.i = priv->vlan;
return 0;
}
static int
-ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
/* make sure no invalid PVIDs get set */
@@ -1017,45 +1373,45 @@ ar8216_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
}
static int
-ar8216_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
*vlan = priv->pvid[port];
return 0;
}
static int
-ar8216_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
priv->vlan_id[val->port_vlan] = val->value.i;
return 0;
}
static int
-ar8216_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
struct switch_val *val)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
val->value.i = priv->vlan_id[val->port_vlan];
return 0;
}
static int
-ar8216_sw_get_port_link(struct switch_dev *dev, int port,
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
struct switch_port_link *link)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
ar8216_read_port_link(priv, port, link);
return 0;
}
static int
-ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
u8 ports = priv->vlan_table[val->port_vlan];
int i;
@@ -1077,9 +1433,9 @@ ar8216_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
}
static int
-ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
u8 *vt = &priv->vlan_table[val->port_vlan];
int i, j;
@@ -1107,10 +1463,102 @@ ar8216_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
return 0;
}
+static void
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+ int port;
+
+ /* reset all mirror registers */
+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+ AR8327_FWD_CTRL0_MIRROR_PORT,
+ (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+ for (port = 0; port < AR8327_NUM_PORTS; port++) {
+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(port),
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+ 0);
+
+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(port),
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+ 0);
+ }
+
+ /* now enable mirroring if necessary */
+ if (priv->source_port >= AR8327_NUM_PORTS ||
+ priv->monitor_port >= AR8327_NUM_PORTS ||
+ priv->source_port == priv->monitor_port) {
+ return;
+ }
+
+ ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+ AR8327_FWD_CTRL0_MIRROR_PORT,
+ (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+
+ if (priv->mirror_rx)
+ ar8xxx_rmw(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN,
+ AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+ if (priv->mirror_tx)
+ ar8xxx_rmw(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN,
+ AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+}
+
+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_rmw(priv, AR8216_REG_PORT_CTRL(port),
+ AR8216_PORT_CTRL_MIRROR_RX,
+ 0);
+
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+ AR8216_PORT_CTRL_MIRROR_TX,
+ 0);
+ }
+
+ /* 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_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+ AR8216_PORT_CTRL_MIRROR_RX,
+ AR8216_PORT_CTRL_MIRROR_RX);
+
+ if (priv->mirror_tx)
+ ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+ AR8216_PORT_CTRL_MIRROR_TX,
+ AR8216_PORT_CTRL_MIRROR_TX);
+}
+
+static void
+ar8xxx_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+ if (chip_is_ar8327(priv)) {
+ ar8327_set_mirror_regs(priv);
+ } else {
+ ar8216_set_mirror_regs(priv);
+ }
+}
+
static int
-ar8216_sw_hw_apply(struct switch_dev *dev)
+ar8xxx_sw_hw_apply(struct switch_dev *dev)
{
- struct ar8216_priv *priv = to_ar8216(dev);
+ struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
u8 portmask[AR8X16_MAX_PORTS];
int i, j;
@@ -1170,19 +1618,22 @@ ar8216_sw_hw_apply(struct switch_dev *dev)
priv->chip->setup_port(priv, i, egress, ingress, portmask[i],
pvid);
}
+
+ ar8xxx_set_mirror_regs(priv);
+
mutex_unlock(&priv->reg_mutex);
return 0;
}
static int
-ar8216_sw_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;
@@ -1191,67 +1642,401 @@ ar8216_sw_reset_switch(struct switch_dev *dev)
for (i = 0; i < dev->ports; i++)
priv->chip->init_port(priv, i);
+ priv->mirror_rx = false;
+ priv->mirror_tx = false;
+ priv->source_port = 0;
+ priv->monitor_port = 0;
+
priv->chip->init_globals(priv);
+
+ mutex_unlock(&priv->reg_mutex);
+
+ 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_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;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static 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;
+}
+
+static 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;
+ ar8xxx_set_mirror_regs(priv);
mutex_unlock(&priv->reg_mutex);
- return ar8216_sw_hw_apply(dev);
+ return 0;
+}
+
+static 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;
}
-static struct switch_attr ar8216_globals[] = {
+static 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;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static 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;
+}
+
+static 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;
+ ar8xxx_set_mirror_regs(priv);
+ mutex_unlock(&priv->reg_mutex);
+
+ return 0;
+}
+
+static 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;
+}
+
+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 struct switch_attr ar8xxx_sw_attr_globals[] = {
{
.type = SWITCH_TYPE_INT,
.name = "enable_vlan",
.description = "Enable VLAN mode",
- .set = ar8216_sw_set_vlan,
- .get = ar8216_sw_get_vlan,
+ .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
+ },
+};
+
+static struct switch_attr ar8327_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 = AR8327_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 = AR8327_NUM_PORTS - 1
+ },
};
-static struct switch_attr ar8216_port[] = {
+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 ar8216_vlan[] = {
+static struct switch_attr ar8xxx_sw_attr_vlan[] = {
{
.type = SWITCH_TYPE_INT,
.name = "vid",
.description = "VLAN ID (0-4094)",
- .set = ar8216_sw_set_vid,
- .get = ar8216_sw_get_vid,
+ .set = ar8xxx_sw_set_vid,
+ .get = ar8xxx_sw_get_vid,
.max = 4094,
},
};
-static const struct switch_dev_ops ar8216_sw_ops = {
+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 switch_dev_ops ar8327_sw_ops = {
.attr_global = {
- .attr = ar8216_globals,
- .n_attr = ARRAY_SIZE(ar8216_globals),
+ .attr = ar8327_sw_attr_globals,
+ .n_attr = ARRAY_SIZE(ar8327_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_sw_get_pvid,
- .set_port_pvid = ar8216_sw_set_pvid,
- .get_vlan_ports = ar8216_sw_get_ports,
- .set_vlan_ports = ar8216_sw_set_ports,
- .apply_config = ar8216_sw_hw_apply,
- .reset_switch = ar8216_sw_reset_switch,
- .get_port_link = ar8216_sw_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;
- val = ar8216_mii_read(priv, AR8216_REG_CTRL);
+ val = priv->read(priv, AR8216_REG_CTRL);
if (val == ~0)
return -ENODEV;
@@ -1259,7 +2044,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;
@@ -1286,11 +2071,8 @@ ar8216_id_chip(struct ar8216_priv *priv)
priv->chip = &ar8327_chip;
break;
default:
- printk(KERN_DEBUG
- "ar8216: Unknown Atheros device [ver=%d, rev=%d, phy_id=%04x%04x]\n",
- priv->chip_ver, priv->chip_rev,
- 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;
}
@@ -1298,87 +2080,127 @@ 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 (chip_is_ar8316(priv)) {
- /* 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);
+}
- if (ar8xxx_has_gige(priv))
- pdev->supported = SUPPORTED_1000baseT_Full;
- else
- pdev->supported = SUPPORTED_100baseT_Full;
- pdev->advertising = pdev->supported;
+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);
- 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);
+
+ return priv;
+}
- pdev->priv = 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 (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);
- }
+ swdev->ports = AR8216_NUM_PORTS;
} else if (chip_is_ar8236(priv)) {
swdev->name = "Atheros AR8236";
swdev->vlans = AR8216_NUM_VLANS;
@@ -1387,50 +2209,89 @@ ar8216_config_init(struct phy_device *pdev)
swdev->name = "Atheros AR8327";
swdev->vlans = AR8X16_MAX_VLANS;
swdev->ports = AR8327_NUM_PORTS;
+ swdev->ops = &ar8327_sw_ops;
} 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;
- printk(KERN_INFO "%s: %s switch driver attached.\n",
- pdev->attached_dev->name, swdev->name);
+ 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_sw_reset_switch(&priv->dev);
+ ret = ar8xxx_sw_reset_switch(&priv->dev);
if (ret)
- goto err_free_priv;
+ 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;
- dev->phy_ptr = priv;
+ 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;
+ }
+
+ ret = ar8xxx_start(priv);
+ if (ret)
+ return ret;
/* VID fixup only needed on ar8216 */
- if (chip_is_ar8216(priv) && pdev->addr == 0) {
+ 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;
}
- priv->init = false;
-
return 0;
-
-err_free_priv:
- kfree(priv);
- return ret;
}
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;
@@ -1470,7 +2331,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;
@@ -1478,59 +2339,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 ar8216_priv priv;
+ 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);
- priv.phy = pdev;
- return ar8216_id_chip(&priv);
+ 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
-ar8216_remove(struct phy_device *pdev)
+ar8xxx_phy_detach(struct phy_device *phydev)
{
- struct ar8216_priv *priv = pdev->priv;
- struct net_device *dev = pdev->attached_dev;
+ struct net_device *dev = phydev->attached_dev;
- if (!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;
+}
- if (pdev->addr == 0)
- unregister_switch(&priv->dev);
- kfree(priv);
+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);
}
-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");
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h
index 8948c5205..086d58fba 100644
--- a/target/linux/generic/files/drivers/net/phy/ar8216.h
+++ b/target/linux/generic/files/drivers/net/phy/ar8216.h
@@ -94,6 +94,21 @@
#define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16)
#define AR8216_ATU_CTRL_AGE_TIME_S 0
+#define AR8216_REG_MIB_FUNC 0x0080
+#define AR8216_MIB_TIMER BITS(0, 16)
+#define AR8216_MIB_AT_HALF_EN BIT(16)
+#define AR8216_MIB_BUSY BIT(17)
+#define AR8216_MIB_FUNC BITS(24, 3)
+#define AR8216_MIB_FUNC_S 24
+#define AR8216_MIB_FUNC_NO_OP 0x0
+#define AR8216_MIB_FUNC_FLUSH 0x1
+#define AR8216_MIB_FUNC_CAPTURE 0x3
+#define AR8236_MIB_EN BIT(30)
+
+#define AR8216_REG_GLOBAL_CPUPORT 0x0078
+#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT BITS(4, 4)
+#define AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S 4
+
#define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1))
#define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000)
#define AR8216_PORT_STATUS_SPEED BITS(0,2)
@@ -149,6 +164,45 @@
#define AR8216_REG_PORT_RATE(_i) (AR8216_PORT_OFFSET(_i) + 0x000c)
#define AR8216_REG_PORT_PRIO(_i) (AR8216_PORT_OFFSET(_i) + 0x0010)
+#define AR8216_REG_PORT_STATS_BASE(_i) (0x19000 + (_i) * 0xa0)
+
+#define AR8216_STATS_RXBROAD 0x00
+#define AR8216_STATS_RXPAUSE 0x04
+#define AR8216_STATS_RXMULTI 0x08
+#define AR8216_STATS_RXFCSERR 0x0c
+#define AR8216_STATS_RXALIGNERR 0x10
+#define AR8216_STATS_RXRUNT 0x14
+#define AR8216_STATS_RXFRAGMENT 0x18
+#define AR8216_STATS_RX64BYTE 0x1c
+#define AR8216_STATS_RX128BYTE 0x20
+#define AR8216_STATS_RX256BYTE 0x24
+#define AR8216_STATS_RX512BYTE 0x28
+#define AR8216_STATS_RX1024BYTE 0x2c
+#define AR8216_STATS_RXMAXBYTE 0x30
+#define AR8216_STATS_RXTOOLONG 0x34
+#define AR8216_STATS_RXGOODBYTE 0x38
+#define AR8216_STATS_RXBADBYTE 0x40
+#define AR8216_STATS_RXOVERFLOW 0x48
+#define AR8216_STATS_FILTERED 0x4c
+#define AR8216_STATS_TXBROAD 0x50
+#define AR8216_STATS_TXPAUSE 0x54
+#define AR8216_STATS_TXMULTI 0x58
+#define AR8216_STATS_TXUNDERRUN 0x5c
+#define AR8216_STATS_TX64BYTE 0x60
+#define AR8216_STATS_TX128BYTE 0x64
+#define AR8216_STATS_TX256BYTE 0x68
+#define AR8216_STATS_TX512BYTE 0x6c
+#define AR8216_STATS_TX1024BYTE 0x70
+#define AR8216_STATS_TXMAXBYTE 0x74
+#define AR8216_STATS_TXOVERSIZE 0x78
+#define AR8216_STATS_TXBYTE 0x7c
+#define AR8216_STATS_TXCOLLISION 0x84
+#define AR8216_STATS_TXABORTCOL 0x88
+#define AR8216_STATS_TXMULTICOL 0x8c
+#define AR8216_STATS_TXSINGLECOL 0x90
+#define AR8216_STATS_TXEXCDEFER 0x94
+#define AR8216_STATS_TXDEFER 0x98
+#define AR8216_STATS_TXLATECOL 0x9c
#define AR8236_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET((_i)) + 0x0008)
#define AR8236_PORT_VLAN_DEFAULT_ID BITS(16, 12)
@@ -163,6 +217,78 @@
#define AR8236_PORT_VLAN2_VLAN_MODE BITS(30, 2)
#define AR8236_PORT_VLAN2_VLAN_MODE_S 30
+#define AR8236_REG_PORT_STATS_BASE(_i) (0x20000 + (_i) * 0x100)
+
+#define AR8236_STATS_RXBROAD 0x00
+#define AR8236_STATS_RXPAUSE 0x04
+#define AR8236_STATS_RXMULTI 0x08
+#define AR8236_STATS_RXFCSERR 0x0c
+#define AR8236_STATS_RXALIGNERR 0x10
+#define AR8236_STATS_RXRUNT 0x14
+#define AR8236_STATS_RXFRAGMENT 0x18
+#define AR8236_STATS_RX64BYTE 0x1c
+#define AR8236_STATS_RX128BYTE 0x20
+#define AR8236_STATS_RX256BYTE 0x24
+#define AR8236_STATS_RX512BYTE 0x28
+#define AR8236_STATS_RX1024BYTE 0x2c
+#define AR8236_STATS_RX1518BYTE 0x30
+#define AR8236_STATS_RXMAXBYTE 0x34
+#define AR8236_STATS_RXTOOLONG 0x38
+#define AR8236_STATS_RXGOODBYTE 0x3c
+#define AR8236_STATS_RXBADBYTE 0x44
+#define AR8236_STATS_RXOVERFLOW 0x4c
+#define AR8236_STATS_FILTERED 0x50
+#define AR8236_STATS_TXBROAD 0x54
+#define AR8236_STATS_TXPAUSE 0x58
+#define AR8236_STATS_TXMULTI 0x5c
+#define AR8236_STATS_TXUNDERRUN 0x60
+#define AR8236_STATS_TX64BYTE 0x64
+#define AR8236_STATS_TX128BYTE 0x68
+#define AR8236_STATS_TX256BYTE 0x6c
+#define AR8236_STATS_TX512BYTE 0x70
+#define AR8236_STATS_TX1024BYTE 0x74
+#define AR8236_STATS_TX1518BYTE 0x78
+#define AR8236_STATS_TXMAXBYTE 0x7c
+#define AR8236_STATS_TXOVERSIZE 0x80
+#define AR8236_STATS_TXBYTE 0x84
+#define AR8236_STATS_TXCOLLISION 0x8c
+#define AR8236_STATS_TXABORTCOL 0x90
+#define AR8236_STATS_TXMULTICOL 0x94
+#define AR8236_STATS_TXSINGLECOL 0x98
+#define AR8236_STATS_TXEXCDEFER 0x9c
+#define AR8236_STATS_TXDEFER 0xa0
+#define AR8236_STATS_TXLATECOL 0xa4
+
+#define AR8316_REG_POSTRIP 0x0008
+#define AR8316_POSTRIP_MAC0_GMII_EN BIT(0)
+#define AR8316_POSTRIP_MAC0_RGMII_EN BIT(1)
+#define AR8316_POSTRIP_PHY4_GMII_EN BIT(2)
+#define AR8316_POSTRIP_PHY4_RGMII_EN BIT(3)
+#define AR8316_POSTRIP_MAC0_MAC_MODE BIT(4)
+#define AR8316_POSTRIP_RTL_MODE BIT(5)
+#define AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN BIT(6)
+#define AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN BIT(7)
+#define AR8316_POSTRIP_SERDES_EN BIT(8)
+#define AR8316_POSTRIP_SEL_ANA_RST BIT(9)
+#define AR8316_POSTRIP_GATE_25M_EN BIT(10)
+#define AR8316_POSTRIP_SEL_CLK25M BIT(11)
+#define AR8316_POSTRIP_HIB_PULSE_HW BIT(12)
+#define AR8316_POSTRIP_DBG_MODE_I BIT(13)
+#define AR8316_POSTRIP_MAC5_MAC_MODE BIT(14)
+#define AR8316_POSTRIP_MAC5_PHY_MODE BIT(15)
+#define AR8316_POSTRIP_POWER_DOWN_HW BIT(16)
+#define AR8316_POSTRIP_LPW_STATE_EN BIT(17)
+#define AR8316_POSTRIP_MAN_EN BIT(18)
+#define AR8316_POSTRIP_PHY_PLL_ON BIT(19)
+#define AR8316_POSTRIP_LPW_EXIT BIT(20)
+#define AR8316_POSTRIP_TXDELAY_S0 BIT(21)
+#define AR8316_POSTRIP_TXDELAY_S1 BIT(22)
+#define AR8316_POSTRIP_RXDELAY_S0 BIT(23)
+#define AR8316_POSTRIP_LED_OPEN_EN BIT(24)
+#define AR8316_POSTRIP_SPI_EN BIT(25)
+#define AR8316_POSTRIP_RXDELAY_S1 BIT(26)
+#define AR8316_POSTRIP_POWER_ON_SEL BIT(31)
+
#define AR8327_NUM_PORTS 7
#define AR8327_NUM_PHYS 5
#define AR8327_PORTS_ALL 0x7f
@@ -189,6 +315,7 @@
#define AR8327_PAD_PHYX_GMII_EN BIT(16)
#define AR8327_PAD_PHYX_RGMII_EN BIT(17)
#define AR8327_PAD_PHYX_MII_EN BIT(18)
+#define AR8327_PAD_SGMII_DELAY_EN BIT(19)
#define AR8327_PAD_RGMII_RXCLK_DELAY_SEL BITS(20, 2)
#define AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S 20
#define AR8327_PAD_RGMII_TXCLK_DELAY_SEL BITS(22, 2)
@@ -207,6 +334,13 @@
#define AR8327_REG_INT_STATUS1 0x024
#define AR8327_REG_INT_MASK0 0x028
#define AR8327_REG_INT_MASK1 0x02c
+
+#define AR8327_REG_MODULE_EN 0x030
+#define AR8327_MODULE_EN_MIB BIT(0)
+
+#define AR8327_REG_MIB_FUNC 0x034
+#define AR8327_MIB_CPU_KEEP BIT(20)
+
#define AR8327_REG_SERVICE_TAG 0x048
#define AR8327_REG_LED_CTRL0 0x050
#define AR8327_REG_LED_CTRL1 0x054
@@ -303,9 +437,15 @@
#define AR8327_PORT_LOOKUP_STATE BITS(16, 3)
#define AR8327_PORT_LOOKUP_STATE_S 16
#define AR8327_PORT_LOOKUP_LEARN BIT(20)
+#define AR8327_PORT_LOOKUP_ING_MIRROR_EN BIT(25)
#define AR8327_REG_PORT_PRIO(_i) (0x664 + (_i) * 0xc)
+#define AR8327_REG_PORT_HOL_CTRL1(_i) (0x974 + (_i) * 0x8)
+#define AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN BIT(16)
+
+#define AR8327_REG_PORT_STATS_BASE(_i) (0x1000 + (_i) * 0x100)
+
/* port speed */
enum {
AR8216_PORT_SPEED_10M = 0,
diff --git a/target/linux/generic/files/drivers/net/phy/b53/Kconfig b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
new file mode 100644
index 000000000..3b514fb62
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
@@ -0,0 +1,30 @@
+menuconfig B53
+ tristate "Broadcom bcm53xx managed switch support"
+ depends on SWCONFIG
+ help
+ This driver adds support for Broadcom managed switch chips. It supports
+ BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
+ integrated switches.
+
+config B53_SPI_DRIVER
+ tristate "B53 SPI connected switch driver"
+ depends on B53 && SPI
+ help
+ Select to enable support for registering switches configured through SPI.
+
+config B53_PHY_DRIVER
+ tristate "B53 MDIO connected switch driver"
+ depends on B53
+ select B53_PHY_FIXUP
+ help
+ Select to enable support for registering switches configured through MDIO.
+
+config B53_MMAP_DRIVER
+ tristate "B53 MMAP connected switch driver"
+ depends on B53
+ help
+ Select to enable support for memory-mapped switches like the BCM63XX
+ integrated switches.
+
+config B53_PHY_FIXUP
+ bool
diff --git a/target/linux/generic/files/drivers/net/phy/b53/Makefile b/target/linux/generic/files/drivers/net/phy/b53/Makefile
new file mode 100644
index 000000000..146196e08
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_B53) += b53_common.o
+
+obj-$(CONFIG_B53_PHY_FIXUP) += b53_phy_fixup.o
+
+obj-$(CONFIG_B53_MMAP_DRIVER) += b53_mmap.o
+obj-$(CONFIG_B53_PHY_DRIVER) += b53_mdio.o
+obj-$(CONFIG_B53_SPI_DRIVER) += b53_spi.o
+
+ccflags-y += -Werror
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
new file mode 100644
index 000000000..c74bf7fbc
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
@@ -0,0 +1,1292 @@
+/*
+ * B53 switch driver main logic
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_regs.h"
+#include "b53_priv.h"
+
+/* buffer size needed for displaying all MIBs with max'd values */
+#define B53_BUF_SIZE 1188
+
+struct b53_mib_desc {
+ u8 size;
+ u8 offset;
+ const char *name;
+};
+
+
+/* BCM5365 MIB counters */
+static const struct b53_mib_desc b53_mibs_65[] = {
+ { 8, 0x00, "TxOctets" },
+ { 4, 0x08, "TxDropPkts" },
+ { 4, 0x10, "TxBroadcastPkts" },
+ { 4, 0x14, "TxMulticastPkts" },
+ { 4, 0x18, "TxUnicastPkts" },
+ { 4, 0x1c, "TxCollisions" },
+ { 4, 0x20, "TxSingleCollision" },
+ { 4, 0x24, "TxMultipleCollision" },
+ { 4, 0x28, "TxDeferredTransmit" },
+ { 4, 0x2c, "TxLateCollision" },
+ { 4, 0x30, "TxExcessiveCollision" },
+ { 4, 0x38, "TxPausePkts" },
+ { 8, 0x44, "RxOctets" },
+ { 4, 0x4c, "RxUndersizePkts" },
+ { 4, 0x50, "RxPausePkts" },
+ { 4, 0x54, "Pkts64Octets" },
+ { 4, 0x58, "Pkts65to127Octets" },
+ { 4, 0x5c, "Pkts128to255Octets" },
+ { 4, 0x60, "Pkts256to511Octets" },
+ { 4, 0x64, "Pkts512to1023Octets" },
+ { 4, 0x68, "Pkts1024to1522Octets" },
+ { 4, 0x6c, "RxOversizePkts" },
+ { 4, 0x70, "RxJabbers" },
+ { 4, 0x74, "RxAlignmentErrors" },
+ { 4, 0x78, "RxFCSErrors" },
+ { 8, 0x7c, "RxGoodOctets" },
+ { 4, 0x84, "RxDropPkts" },
+ { 4, 0x88, "RxUnicastPkts" },
+ { 4, 0x8c, "RxMulticastPkts" },
+ { 4, 0x90, "RxBroadcastPkts" },
+ { 4, 0x94, "RxSAChanges" },
+ { 4, 0x98, "RxFragments" },
+ { },
+};
+
+/* BCM63xx MIB counters */
+static const struct b53_mib_desc b53_mibs_63xx[] = {
+ { 8, 0x00, "TxOctets" },
+ { 4, 0x08, "TxDropPkts" },
+ { 4, 0x0c, "TxQoSPkts" },
+ { 4, 0x10, "TxBroadcastPkts" },
+ { 4, 0x14, "TxMulticastPkts" },
+ { 4, 0x18, "TxUnicastPkts" },
+ { 4, 0x1c, "TxCollisions" },
+ { 4, 0x20, "TxSingleCollision" },
+ { 4, 0x24, "TxMultipleCollision" },
+ { 4, 0x28, "TxDeferredTransmit" },
+ { 4, 0x2c, "TxLateCollision" },
+ { 4, 0x30, "TxExcessiveCollision" },
+ { 4, 0x38, "TxPausePkts" },
+ { 8, 0x3c, "TxQoSOctets" },
+ { 8, 0x44, "RxOctets" },
+ { 4, 0x4c, "RxUndersizePkts" },
+ { 4, 0x50, "RxPausePkts" },
+ { 4, 0x54, "Pkts64Octets" },
+ { 4, 0x58, "Pkts65to127Octets" },
+ { 4, 0x5c, "Pkts128to255Octets" },
+ { 4, 0x60, "Pkts256to511Octets" },
+ { 4, 0x64, "Pkts512to1023Octets" },
+ { 4, 0x68, "Pkts1024to1522Octets" },
+ { 4, 0x6c, "RxOversizePkts" },
+ { 4, 0x70, "RxJabbers" },
+ { 4, 0x74, "RxAlignmentErrors" },
+ { 4, 0x78, "RxFCSErrors" },
+ { 8, 0x7c, "RxGoodOctets" },
+ { 4, 0x84, "RxDropPkts" },
+ { 4, 0x88, "RxUnicastPkts" },
+ { 4, 0x8c, "RxMulticastPkts" },
+ { 4, 0x90, "RxBroadcastPkts" },
+ { 4, 0x94, "RxSAChanges" },
+ { 4, 0x98, "RxFragments" },
+ { 4, 0xa0, "RxSymbolErrors" },
+ { 4, 0xa4, "RxQoSPkts" },
+ { 8, 0xa8, "RxQoSOctets" },
+ { 4, 0xb0, "Pkts1523to2047Octets" },
+ { 4, 0xb4, "Pkts2048to4095Octets" },
+ { 4, 0xb8, "Pkts4096to8191Octets" },
+ { 4, 0xbc, "Pkts8192to9728Octets" },
+ { 4, 0xc0, "RxDiscarded" },
+ { }
+};
+
+/* MIB counters */
+static const struct b53_mib_desc b53_mibs[] = {
+ { 8, 0x00, "TxOctets" },
+ { 4, 0x08, "TxDropPkts" },
+ { 4, 0x10, "TxBroadcastPkts" },
+ { 4, 0x14, "TxMulticastPkts" },
+ { 4, 0x18, "TxUnicastPkts" },
+ { 4, 0x1c, "TxCollisions" },
+ { 4, 0x20, "TxSingleCollision" },
+ { 4, 0x24, "TxMultipleCollision" },
+ { 4, 0x28, "TxDeferredTransmit" },
+ { 4, 0x2c, "TxLateCollision" },
+ { 4, 0x30, "TxExcessiveCollision" },
+ { 4, 0x38, "TxPausePkts" },
+ { 8, 0x50, "RxOctets" },
+ { 4, 0x58, "RxUndersizePkts" },
+ { 4, 0x5c, "RxPausePkts" },
+ { 4, 0x60, "Pkts64Octets" },
+ { 4, 0x64, "Pkts65to127Octets" },
+ { 4, 0x68, "Pkts128to255Octets" },
+ { 4, 0x6c, "Pkts256to511Octets" },
+ { 4, 0x70, "Pkts512to1023Octets" },
+ { 4, 0x74, "Pkts1024to1522Octets" },
+ { 4, 0x78, "RxOversizePkts" },
+ { 4, 0x7c, "RxJabbers" },
+ { 4, 0x80, "RxAlignmentErrors" },
+ { 4, 0x84, "RxFCSErrors" },
+ { 8, 0x88, "RxGoodOctets" },
+ { 4, 0x90, "RxDropPkts" },
+ { 4, 0x94, "RxUnicastPkts" },
+ { 4, 0x98, "RxMulticastPkts" },
+ { 4, 0x9c, "RxBroadcastPkts" },
+ { 4, 0xa0, "RxSAChanges" },
+ { 4, 0xa4, "RxFragments" },
+ { 4, 0xa8, "RxJumboPkts" },
+ { 4, 0xac, "RxSymbolErrors" },
+ { 4, 0xc0, "RxDiscarded" },
+ { }
+};
+
+static int b53_do_vlan_op(struct b53_device *dev, u8 op)
+{
+ unsigned int i;
+
+ b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
+
+ for (i = 0; i < 10; i++) {
+ u8 vta;
+
+ b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
+ if (!(vta & VTA_START_CMD))
+ return 0;
+
+ usleep_range(100, 200);
+ }
+
+ return -EIO;
+}
+
+static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
+ u16 untag)
+{
+ if (is5325(dev)) {
+ u32 entry = 0;
+
+ if (members)
+ entry = (untag << VA_UNTAG_S) | members | VA_VALID_25;
+
+ b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
+ VTA_RW_STATE_WR | VTA_RW_OP_EN);
+ } else if (is5365(dev)) {
+ u16 entry = 0;
+
+ if (members)
+ entry = (untag << VA_UNTAG_S) | members | VA_VALID_65;
+
+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
+ VTA_RW_STATE_WR | VTA_RW_OP_EN);
+ } else {
+ b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
+ b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
+ (untag << VTE_UNTAG_S) | members);
+
+ b53_do_vlan_op(dev, VTA_CMD_WRITE);
+ }
+}
+
+void b53_set_forwarding(struct b53_device *dev, int enable)
+{
+ u8 mgmt;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+ if (enable)
+ mgmt |= SM_SW_FWD_EN;
+ else
+ mgmt &= ~SM_SW_FWD_EN;
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static void b53_enable_vlan(struct b53_device *dev, int enable)
+{
+ u8 mgmt, vc0, vc1, vc4 = 0, vc5;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
+
+ if (is5325(dev) || is5365(dev)) {
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
+ } else if (is63xx(dev)) {
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
+ } else {
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
+ }
+
+ mgmt &= ~SM_SW_FWD_MODE;
+
+ if (enable) {
+ vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
+ vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
+ vc4 &= ~VC4_ING_VID_CHECK_MASK;
+ vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
+ vc5 |= VC5_DROP_VTABLE_MISS;
+
+ if (is5325(dev))
+ vc0 &= ~VC0_RESERVED_1;
+
+ if (is5325(dev) || is5365(dev))
+ vc1 |= VC1_RX_MCST_TAG_EN;
+
+ if (!is5325(dev) && !is5365(dev)) {
+ if (dev->allow_vid_4095)
+ vc5 |= VC5_VID_FFF_EN;
+ else
+ vc5 &= ~VC5_VID_FFF_EN;
+ }
+ } else {
+ vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
+ vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
+ vc4 &= ~VC4_ING_VID_CHECK_MASK;
+ vc5 &= ~VC5_DROP_VTABLE_MISS;
+
+ if (is5325(dev) || is5365(dev))
+ vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
+ else
+ vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
+
+ if (is5325(dev) || is5365(dev))
+ vc1 &= ~VC1_RX_MCST_TAG_EN;
+
+ if (!is5325(dev) && !is5365(dev))
+ vc5 &= ~VC5_VID_FFF_EN;
+ }
+
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
+
+ if (is5325(dev) || is5365(dev)) {
+ /* enable the high 8 bit vid check on 5325 */
+ if (is5325(dev) && enable)
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
+ VC3_HIGH_8BIT_EN);
+ else
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
+ } else if (is63xx(dev)) {
+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
+ } else {
+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
+ b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
+ }
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static int b53_set_jumbo(struct b53_device *dev, int enable, int allow_10_100)
+{
+ u32 port_mask = 0;
+ u16 max_size = JMS_MIN_SIZE;
+
+ if (is5325(dev) || is5365(dev))
+ return -EINVAL;
+
+ if (enable) {
+ port_mask = dev->enabled_ports;
+ max_size = JMS_MAX_SIZE;
+ if (allow_10_100)
+ port_mask |= JPM_10_100_JUMBO_EN;
+ }
+
+ b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
+ return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
+}
+
+static int b53_flush_arl(struct b53_device *dev)
+{
+ unsigned int i;
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+ FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
+
+ for (i = 0; i < 10; i++) {
+ u8 fast_age_ctrl;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+ &fast_age_ctrl);
+
+ if (!(fast_age_ctrl & FAST_AGE_DONE))
+ return 0;
+
+ mdelay(1);
+ }
+
+ pr_warn("time out while flushing ARL\n");
+
+ return -EINVAL;
+}
+
+static void b53_enable_ports(struct b53_device *dev)
+{
+ unsigned i;
+
+ b53_for_each_port(dev, i) {
+ u8 port_ctrl;
+ u16 pvlan_mask;
+
+ /*
+ * prevent leaking packets between wan and lan in unmanaged
+ * mode through port vlans.
+ */
+ if (dev->enable_vlan || is_cpu_port(dev, i))
+ pvlan_mask = 0x1ff;
+ else if (is531x5(dev))
+ /* BCM53115 may use a different port as cpu port */
+ pvlan_mask = BIT(dev->sw_dev.cpu_port);
+ else
+ pvlan_mask = BIT(B53_CPU_PORT);
+
+ /* BCM5325 CPU port is at 8 */
+ if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25)
+ i = B53_CPU_PORT;
+
+ if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7))
+ /* disable unused ports 6 & 7 */
+ port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
+ else if (i == B53_CPU_PORT)
+ port_ctrl = PORT_CTRL_RX_BCST_EN |
+ PORT_CTRL_RX_MCST_EN |
+ PORT_CTRL_RX_UCST_EN;
+ else
+ port_ctrl = 0;
+
+ b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i),
+ pvlan_mask);
+
+ /* port state is handled by bcm63xx_enet driver */
+ if (!is63xx(dev))
+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i),
+ port_ctrl);
+ }
+}
+
+static void b53_enable_mib(struct b53_device *dev)
+{
+ u8 gc;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+ gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_GLOBAL_CONFIG, gc);
+}
+
+static int b53_apply(struct b53_device *dev)
+{
+ int i;
+
+ /* clear all vlan entries */
+ if (is5325(dev) || is5365(dev)) {
+ for (i = 1; i < dev->sw_dev.vlans; i++)
+ b53_set_vlan_entry(dev, i, 0, 0);
+ } else {
+ b53_do_vlan_op(dev, VTA_CMD_CLEAR);
+ }
+
+ b53_enable_vlan(dev, dev->enable_vlan);
+
+ /* fill VLAN table */
+ if (dev->enable_vlan) {
+ for (i = 0; i < dev->sw_dev.vlans; i++) {
+ struct b53_vlan *vlan = &dev->vlans[i];
+
+ if (!vlan->members)
+ continue;
+
+ b53_set_vlan_entry(dev, i, vlan->members, vlan->untag);
+ }
+
+ b53_for_each_port(dev, i)
+ b53_write16(dev, B53_VLAN_PAGE,
+ B53_VLAN_PORT_DEF_TAG(i),
+ dev->ports[i].pvid);
+ } else {
+ b53_for_each_port(dev, i)
+ b53_write16(dev, B53_VLAN_PAGE,
+ B53_VLAN_PORT_DEF_TAG(i), 1);
+
+ }
+
+ b53_enable_ports(dev);
+
+ if (!is5325(dev) && !is5365(dev))
+ b53_set_jumbo(dev, dev->enable_jumbo, 1);
+
+ return 0;
+}
+
+static int b53_switch_reset(struct b53_device *dev)
+{
+ u8 mgmt;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+ if (!(mgmt & SM_SW_FWD_EN)) {
+ mgmt &= ~SM_SW_FWD_MODE;
+ mgmt |= SM_SW_FWD_EN;
+
+ b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+ b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+ if (!(mgmt & SM_SW_FWD_EN)) {
+ pr_err("Failed to enable switch!\n");
+ return -EINVAL;
+ }
+ }
+
+ /* enable all ports */
+ b53_enable_ports(dev);
+
+ /* configure MII port if necessary */
+ if (is5325(dev)) {
+ u8 mii_port_override;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+ &mii_port_override);
+ /* reverse mii needs to be enabled */
+ if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+ mii_port_override | PORT_OVERRIDE_RV_MII_25);
+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+ &mii_port_override);
+
+ if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+ pr_err("Failed to enable reverse MII mode\n");
+ return -EINVAL;
+ }
+ }
+ } else if (is531x5(dev) && dev->sw_dev.cpu_port == B53_CPU_PORT) {
+ u8 mii_port_override;
+
+ b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+ &mii_port_override);
+ b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+ mii_port_override | PORT_OVERRIDE_EN |
+ PORT_OVERRIDE_LINK);
+ }
+
+ b53_enable_mib(dev);
+
+ return b53_flush_arl(dev);
+}
+
+/*
+ * Swconfig glue functions
+ */
+
+static int b53_global_get_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ val->value.i = priv->enable_vlan;
+
+ return 0;
+}
+
+static int b53_global_set_vlan_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ priv->enable_vlan = val->value.i;
+
+ return 0;
+}
+
+static int b53_global_get_jumbo_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ val->value.i = priv->enable_jumbo;
+
+ return 0;
+}
+
+static int b53_global_set_jumbo_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ priv->enable_jumbo = val->value.i;
+
+ return 0;
+}
+
+static int b53_global_get_4095_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ val->value.i = priv->allow_vid_4095;
+
+ return 0;
+}
+
+static int b53_global_set_4095_enable(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ priv->allow_vid_4095 = val->value.i;
+
+ return 0;
+}
+
+static int b53_global_get_ports(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x",
+ priv->enabled_ports);
+ val->value.s = priv->buf;
+
+ return 0;
+}
+
+static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ *val = priv->ports[port].pvid;
+
+ return 0;
+}
+
+static int b53_port_set_pvid(struct switch_dev *dev, int port, int val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ if (val > 15 && is5325(priv))
+ return -EINVAL;
+ if (val == 4095 && !priv->allow_vid_4095)
+ return -EINVAL;
+
+ priv->ports[port].pvid = val;
+
+ return 0;
+}
+
+static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+ struct switch_port *port = &val->value.ports[0];
+ struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+ int i;
+
+ val->len = 0;
+
+ if (!vlan->members)
+ return 0;
+
+ for (i = 0; i < dev->ports; i++) {
+ if (!(vlan->members & BIT(i)))
+ continue;
+
+
+ if (!(vlan->untag & BIT(i)))
+ port->flags = BIT(SWITCH_PORT_FLAG_TAGGED);
+ else
+ port->flags = 0;
+
+ port->id = i;
+ val->len++;
+ port++;
+ }
+
+ return 0;
+}
+
+static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+ struct switch_port *port;
+ struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+ int i;
+
+ /* only BCM5325 and BCM5365 supports VID 0 */
+ if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv))
+ return -EINVAL;
+
+ /* VLAN 4095 needs special handling */
+ if (val->port_vlan == 4095 && !priv->allow_vid_4095)
+ return -EINVAL;
+
+ port = &val->value.ports[0];
+ vlan->members = 0;
+ vlan->untag = 0;
+ for (i = 0; i < val->len; i++, port++) {
+ vlan->members |= BIT(port->id);
+
+ if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) {
+ vlan->untag |= BIT(port->id);
+ priv->ports[port->id].pvid = val->port_vlan;
+ };
+ }
+
+ /* ignore disabled ports */
+ vlan->members &= priv->enabled_ports;
+ vlan->untag &= priv->enabled_ports;
+
+ return 0;
+}
+
+static int b53_port_get_link(struct switch_dev *dev, int port,
+ struct switch_port_link *link)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ if (is_cpu_port(priv, port)) {
+ link->link = 1;
+ link->duplex = 1;
+ link->speed = is5325(priv) || is5365(priv) ?
+ SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000;
+ link->aneg = 0;
+ } else if (priv->enabled_ports & BIT(port)) {
+ u32 speed;
+ u16 lnk, duplex;
+
+ b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk);
+ b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex);
+
+ lnk = (lnk >> port) & 1;
+ duplex = (duplex >> port) & 1;
+
+ if (is5325(priv) || is5365(priv)) {
+ u16 tmp;
+
+ b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp);
+ speed = SPEED_PORT_FE(tmp, port);
+ } else {
+ b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed);
+ speed = SPEED_PORT_GE(speed, port);
+ }
+
+ link->link = lnk;
+ if (lnk) {
+ link->duplex = duplex;
+ switch (speed) {
+ case SPEED_STAT_10M:
+ link->speed = SWITCH_PORT_SPEED_10;
+ break;
+ case SPEED_STAT_100M:
+ link->speed = SWITCH_PORT_SPEED_100;
+ break;
+ case SPEED_STAT_1000M:
+ link->speed = SWITCH_PORT_SPEED_1000;
+ break;
+ }
+ }
+
+ link->aneg = 1;
+ } else {
+ link->link = 0;
+ }
+
+ return 0;
+
+}
+
+static int b53_global_reset_switch(struct switch_dev *dev)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ /* reset vlans */
+ priv->enable_vlan = 0;
+ priv->enable_jumbo = 0;
+ priv->allow_vid_4095 = 0;
+
+ memset(priv->vlans, 0, sizeof(priv->vlans) * dev->vlans);
+ memset(priv->ports, 0, sizeof(priv->ports) * dev->ports);
+
+ return b53_switch_reset(priv);
+}
+
+static int b53_global_apply_config(struct switch_dev *dev)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+
+ /* disable switching */
+ b53_set_forwarding(priv, 0);
+
+ b53_apply(priv);
+
+ /* enable switching */
+ b53_set_forwarding(priv, 1);
+
+ return 0;
+}
+
+
+static int b53_global_reset_mib(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *priv = sw_to_b53(dev);
+ u8 gc;
+
+ b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+ b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
+ mdelay(1);
+ b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
+ mdelay(1);
+
+ return 0;
+}
+
+static int b53_port_get_mib(struct switch_dev *sw_dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct b53_device *dev = sw_to_b53(sw_dev);
+ const struct b53_mib_desc *mibs;
+ int port = val->port_vlan;
+ int len = 0;
+
+ if (!(BIT(port) & dev->enabled_ports))
+ return -1;
+
+ if (is5365(dev)) {
+ if (port == 5)
+ port = 8;
+
+ mibs = b53_mibs_65;
+ } else if (is63xx(dev)) {
+ mibs = b53_mibs_63xx;
+ } else {
+ mibs = b53_mibs;
+ }
+
+ dev->buf[0] = 0;
+
+ for (; mibs->size > 0; mibs++) {
+ u64 val;
+
+ if (mibs->size == 8) {
+ b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val);
+ } else {
+ u32 val32;
+
+ b53_read32(dev, B53_MIB_PAGE(port), mibs->offset,
+ &val32);
+ val = val32;
+ }
+
+ len += snprintf(dev->buf + len, B53_BUF_SIZE - len,
+ "%-20s: %llu\n", mibs->name, val);
+ }
+
+ val->len = len;
+ val->value.s = dev->buf;
+
+ return 0;
+}
+
+static struct switch_attr b53_global_ops_25[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = b53_global_set_vlan_enable,
+ .get = b53_global_get_vlan_enable,
+ .max = 1,
+ },
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "ports",
+ .description = "Available ports (as bitmask)",
+ .get = b53_global_get_ports,
+ },
+};
+
+static struct switch_attr b53_global_ops_65[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = b53_global_set_vlan_enable,
+ .get = b53_global_get_vlan_enable,
+ .max = 1,
+ },
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "ports",
+ .description = "Available ports (as bitmask)",
+ .get = b53_global_get_ports,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "reset_mib",
+ .description = "Reset MIB counters",
+ .set = b53_global_reset_mib,
+ },
+};
+
+static struct switch_attr b53_global_ops[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = b53_global_set_vlan_enable,
+ .get = b53_global_get_vlan_enable,
+ .max = 1,
+ },
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "ports",
+ .description = "Available Ports (as bitmask)",
+ .get = b53_global_get_ports,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "reset_mib",
+ .description = "Reset MIB counters",
+ .set = b53_global_reset_mib,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_jumbo",
+ .description = "Enable Jumbo Frames",
+ .set = b53_global_set_jumbo_enable,
+ .get = b53_global_get_jumbo_enable,
+ .max = 1,
+ },
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "allow_vid_4095",
+ .description = "Allow VID 4095",
+ .set = b53_global_set_4095_enable,
+ .get = b53_global_get_4095_enable,
+ .max = 1,
+ },
+};
+
+static struct switch_attr b53_port_ops[] = {
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "mib",
+ .description = "Get port's MIB counters",
+ .get = b53_port_get_mib,
+ },
+};
+
+static struct switch_attr b53_no_ops[] = {
+};
+
+static const struct switch_dev_ops b53_switch_ops_25 = {
+ .attr_global = {
+ .attr = b53_global_ops_25,
+ .n_attr = ARRAY_SIZE(b53_global_ops_25),
+ },
+ .attr_port = {
+ .attr = b53_no_ops,
+ .n_attr = ARRAY_SIZE(b53_no_ops),
+ },
+ .attr_vlan = {
+ .attr = b53_no_ops,
+ .n_attr = ARRAY_SIZE(b53_no_ops),
+ },
+
+ .get_vlan_ports = b53_vlan_get_ports,
+ .set_vlan_ports = b53_vlan_set_ports,
+ .get_port_pvid = b53_port_get_pvid,
+ .set_port_pvid = b53_port_set_pvid,
+ .apply_config = b53_global_apply_config,
+ .reset_switch = b53_global_reset_switch,
+ .get_port_link = b53_port_get_link,
+};
+
+static const struct switch_dev_ops b53_switch_ops_65 = {
+ .attr_global = {
+ .attr = b53_global_ops_65,
+ .n_attr = ARRAY_SIZE(b53_global_ops_65),
+ },
+ .attr_port = {
+ .attr = b53_no_ops,
+ .n_attr = ARRAY_SIZE(b53_port_ops),
+ },
+ .attr_vlan = {
+ .attr = b53_no_ops,
+ .n_attr = ARRAY_SIZE(b53_no_ops),
+ },
+
+ .get_vlan_ports = b53_vlan_get_ports,
+ .set_vlan_ports = b53_vlan_set_ports,
+ .get_port_pvid = b53_port_get_pvid,
+ .set_port_pvid = b53_port_set_pvid,
+ .apply_config = b53_global_apply_config,
+ .reset_switch = b53_global_reset_switch,
+ .get_port_link = b53_port_get_link,
+};
+
+static const struct switch_dev_ops b53_switch_ops = {
+ .attr_global = {
+ .attr = b53_global_ops,
+ .n_attr = ARRAY_SIZE(b53_global_ops),
+ },
+ .attr_port = {
+ .attr = b53_port_ops,
+ .n_attr = ARRAY_SIZE(b53_port_ops),
+ },
+ .attr_vlan = {
+ .attr = b53_no_ops,
+ .n_attr = ARRAY_SIZE(b53_no_ops),
+ },
+
+ .get_vlan_ports = b53_vlan_get_ports,
+ .set_vlan_ports = b53_vlan_set_ports,
+ .get_port_pvid = b53_port_get_pvid,
+ .set_port_pvid = b53_port_set_pvid,
+ .apply_config = b53_global_apply_config,
+ .reset_switch = b53_global_reset_switch,
+ .get_port_link = b53_port_get_link,
+};
+
+struct b53_chip_data {
+ u32 chip_id;
+ const char *dev_name;
+ const char *alias;
+ u16 vlans;
+ u16 enabled_ports;
+ u8 cpu_port;
+ u8 vta_regs[3];
+ u8 duplex_reg;
+ u8 jumbo_pm_reg;
+ u8 jumbo_size_reg;
+ const struct switch_dev_ops *sw_ops;
+};
+
+#define B53_VTA_REGS \
+ { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
+#define B53_VTA_REGS_9798 \
+ { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
+#define B53_VTA_REGS_63XX \
+ { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
+
+static const struct b53_chip_data b53_switch_chips[] = {
+ {
+ .chip_id = BCM5325_DEVICE_ID,
+ .dev_name = "BCM5325",
+ .alias = "bcm5325",
+ .vlans = 16,
+ .enabled_ports = 0x1f,
+ .cpu_port = B53_CPU_PORT_25,
+ .duplex_reg = B53_DUPLEX_STAT_FE,
+ .sw_ops = &b53_switch_ops_25,
+ },
+ {
+ .chip_id = BCM5365_DEVICE_ID,
+ .dev_name = "BCM5365",
+ .alias = "bcm5365",
+ .vlans = 256,
+ .enabled_ports = 0x1f,
+ .cpu_port = B53_CPU_PORT_25,
+ .duplex_reg = B53_DUPLEX_STAT_FE,
+ .sw_ops = &b53_switch_ops_65,
+ },
+ {
+ .chip_id = BCM5395_DEVICE_ID,
+ .dev_name = "BCM5395",
+ .alias = "bcm5395",
+ .vlans = 4096,
+ .enabled_ports = 0x1f,
+ .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .sw_ops = &b53_switch_ops,
+ },
+ {
+ .chip_id = BCM5397_DEVICE_ID,
+ .dev_name = "BCM5397",
+ .alias = "bcm5397",
+ .vlans = 4096,
+ .enabled_ports = 0x1f,
+ .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_9798,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .sw_ops = &b53_switch_ops,
+ },
+ {
+ .chip_id = BCM5398_DEVICE_ID,
+ .dev_name = "BCM5398",
+ .alias = "bcm5398",
+ .vlans = 4096,
+ .enabled_ports = 0x7f,
+ .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_9798,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .sw_ops = &b53_switch_ops,
+ },
+ {
+ .chip_id = BCM53115_DEVICE_ID,
+ .dev_name = "BCM53115",
+ .alias = "bcm53115",
+ .vlans = 4096,
+ .enabled_ports = 0x1f,
+ .vta_regs = B53_VTA_REGS,
+ .cpu_port = B53_CPU_PORT,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .sw_ops = &b53_switch_ops,
+ },
+ {
+ .chip_id = BCM53125_DEVICE_ID,
+ .dev_name = "BCM53125",
+ .alias = "bcm53125",
+ .vlans = 4096,
+ .enabled_ports = 0x1f,
+ .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS,
+ .duplex_reg = B53_DUPLEX_STAT_GE,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+ .sw_ops = &b53_switch_ops,
+ },
+ {
+ .chip_id = BCM63XX_DEVICE_ID,
+ .dev_name = "BCM63xx",
+ .alias = "bcm63xx",
+ .vlans = 4096,
+ .enabled_ports = 0, /* pdata must provide them */
+ .cpu_port = B53_CPU_PORT,
+ .vta_regs = B53_VTA_REGS_63XX,
+ .duplex_reg = B53_DUPLEX_STAT_63XX,
+ .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
+ .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
+ .sw_ops = &b53_switch_ops,
+ },
+};
+
+int b53_switch_init(struct b53_device *dev)
+{
+ struct switch_dev *sw_dev = &dev->sw_dev;
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
+ const struct b53_chip_data *chip = &b53_switch_chips[i];
+
+ if (chip->chip_id == dev->chip_id) {
+ sw_dev->name = chip->dev_name;
+ if (!sw_dev->alias)
+ sw_dev->alias = chip->alias;
+ if (!dev->enabled_ports)
+ dev->enabled_ports = chip->enabled_ports;
+ dev->duplex_reg = chip->duplex_reg;
+ dev->vta_regs[0] = chip->vta_regs[0];
+ dev->vta_regs[1] = chip->vta_regs[1];
+ dev->vta_regs[2] = chip->vta_regs[2];
+ dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+ sw_dev->ops = chip->sw_ops;
+ sw_dev->cpu_port = chip->cpu_port;
+ sw_dev->vlans = chip->vlans;
+ break;
+ }
+ }
+
+ if (!sw_dev->name)
+ return -EINVAL;
+
+ /* check which BCM5325x version we have */
+ if (is5325(dev)) {
+ u8 vc4;
+
+ b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+
+ /* check reserved bits */
+ switch (vc4 & 3) {
+ case 1:
+ /* BCM5325E */
+ break;
+ case 3:
+ /* BCM5325F - do not use port 4 */
+ dev->enabled_ports &= ~BIT(4);
+ break;
+ default:
+ /* BCM5325M */
+ return -EINVAL;
+ }
+ } else if (dev->chip_id == BCM53115_DEVICE_ID) {
+ u64 strap_value;
+
+ b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
+ /* use second IMP port if GMII is enabled */
+ if (strap_value & SV_GMII_CTRL_115)
+ sw_dev->cpu_port = 5;
+ }
+
+ /* cpu port is always last */
+ sw_dev->ports = sw_dev->cpu_port + 1;
+ dev->enabled_ports |= BIT(sw_dev->cpu_port);
+
+ dev->ports = devm_kzalloc(dev->dev,
+ sizeof(struct b53_port) * sw_dev->ports,
+ GFP_KERNEL);
+ if (!dev->ports)
+ return -ENOMEM;
+
+ dev->vlans = devm_kzalloc(dev->dev,
+ sizeof(struct b53_vlan) * sw_dev->vlans,
+ GFP_KERNEL);
+ if (!dev->vlans)
+ return -ENOMEM;
+
+ dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL);
+ if (!dev->buf)
+ return -ENOMEM;
+
+ return b53_switch_reset(dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+ void *priv)
+{
+ struct b53_device *dev;
+
+ dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->dev = base;
+ dev->ops = ops;
+ dev->priv = priv;
+ mutex_init(&dev->reg_mutex);
+
+ return dev;
+}
+EXPORT_SYMBOL(b53_switch_alloc);
+
+int b53_switch_detect(struct b53_device *dev)
+{
+ u32 id32;
+ u16 tmp;
+ u8 id8;
+ int ret;
+
+ ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
+ if (ret)
+ return ret;
+
+ switch (id8) {
+ case 0:
+ /*
+ * BCM5325 and BCM5365 do not have this register so reads
+ * return 0. But the read operation did succeed, so assume
+ * this is one of them.
+ *
+ * Next check if we can write to the 5325's VTA register; for
+ * 5365 it is read only.
+ */
+
+ b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
+ b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
+
+ if (tmp == 0xf)
+ dev->chip_id = BCM5325_DEVICE_ID;
+ else
+ dev->chip_id = BCM5365_DEVICE_ID;
+ break;
+ case BCM5395_DEVICE_ID:
+ case BCM5397_DEVICE_ID:
+ case BCM5398_DEVICE_ID:
+ dev->chip_id = id8;
+ break;
+ default:
+ ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
+ if (ret)
+ return ret;
+
+ switch (id32) {
+ case BCM53115_DEVICE_ID:
+ case BCM53125_DEVICE_ID:
+ dev->chip_id = id32;
+ break;
+ default:
+ pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
+ id8, id32);
+ return -ENODEV;
+ }
+ }
+
+ return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID, &dev->core_rev);
+}
+EXPORT_SYMBOL(b53_switch_detect);
+
+int b53_switch_register(struct b53_device *dev)
+{
+ int ret;
+
+ if (dev->pdata) {
+ dev->chip_id = dev->pdata->chip_id;
+ dev->enabled_ports = dev->pdata->enabled_ports;
+ dev->sw_dev.alias = dev->pdata->alias;
+ }
+
+ if (!dev->chip_id && b53_switch_detect(dev))
+ return -EINVAL;
+
+ ret = b53_switch_init(dev);
+ if (ret)
+ return ret;
+
+ pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev);
+
+ return register_switch(&dev->sw_dev, NULL);
+}
+EXPORT_SYMBOL(b53_switch_register);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 switch library");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
new file mode 100644
index 000000000..1ba05914e
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
@@ -0,0 +1,401 @@
+/*
+ * B53 register access through MII registers
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+
+#include "b53_priv.h"
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+/* MII registers */
+#define REG_MII_PAGE 0x10 /* MII Page register */
+#define REG_MII_ADDR 0x11 /* MII Address register */
+#define REG_MII_DATA0 0x18 /* MII Data register 0 */
+#define REG_MII_DATA1 0x19 /* MII Data register 1 */
+#define REG_MII_DATA2 0x1a /* MII Data register 2 */
+#define REG_MII_DATA3 0x1b /* MII Data register 3 */
+
+#define REG_MII_PAGE_ENABLE BIT(0)
+#define REG_MII_ADDR_WRITE BIT(0)
+#define REG_MII_ADDR_READ BIT(1)
+
+static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
+{
+ int i;
+ u16 v;
+ int ret;
+ struct mii_bus *bus = dev->priv;
+
+ if (dev->current_page != page) {
+ /* set page number */
+ v = (page << 8) | REG_MII_PAGE_ENABLE;
+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
+ if (ret)
+ return ret;
+ dev->current_page = page;
+ }
+
+ /* set register address */
+ v = (reg << 8) | op;
+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
+ if (ret)
+ return ret;
+
+ /* check if operation completed */
+ for (i = 0; i < 5; ++i) {
+ v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
+ if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
+ break;
+ usleep_range(10, 100);
+ }
+
+ if (WARN_ON(i == 5))
+ return -EIO;
+
+ return 0;
+}
+
+static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+ struct mii_bus *bus = dev->priv;
+ int ret;
+
+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
+
+ return 0;
+}
+
+static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+ struct mii_bus *bus = dev->priv;
+ int ret;
+
+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+
+ return 0;
+}
+
+static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+ struct mii_bus *bus = dev->priv;
+ int ret;
+
+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+ *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
+
+ return 0;
+}
+
+static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ struct mii_bus *bus = dev->priv;
+ u64 temp = 0;
+ int i;
+ int ret;
+
+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ for (i = 2; i >= 0; i--) {
+ temp <<= 16;
+ temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+ }
+
+ *val = temp;
+
+ return 0;
+}
+
+static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ struct mii_bus *bus = dev->priv;
+ u64 temp = 0;
+ int i;
+ int ret;
+
+ ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+ if (ret)
+ return ret;
+
+ for (i = 3; i >= 0; i--) {
+ temp <<= 16;
+ temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+ }
+
+ *val = temp;
+
+ return 0;
+}
+
+static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+ struct mii_bus *bus = dev->priv;
+ int ret;
+
+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+ if (ret)
+ return ret;
+
+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
+ u16 value)
+{
+ struct mii_bus *bus = dev->priv;
+ int ret;
+
+ ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+ if (ret)
+ return ret;
+
+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
+ u32 value)
+{
+ struct mii_bus *bus = dev->priv;
+ unsigned int i;
+ u32 temp = value;
+
+ for (i = 0; i < 2; i++) {
+ int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+ temp & 0xffff);
+ if (ret)
+ return ret;
+ temp >>= 16;
+ }
+
+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
+ u64 value)
+{
+ struct mii_bus *bus = dev->priv;
+ unsigned i;
+ u64 temp = value;
+
+ for (i = 0; i < 3; i++) {
+ int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+ temp & 0xffff);
+ if (ret)
+ return ret;
+ temp >>= 16;
+ }
+
+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
+ u64 value)
+{
+ struct mii_bus *bus = dev->priv;
+ unsigned i;
+ u64 temp = value;
+
+ for (i = 0; i < 4; i++) {
+ int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+ temp & 0xffff);
+ if (ret)
+ return ret;
+ temp >>= 16;
+ }
+
+ return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static struct b53_io_ops b53_mdio_ops = {
+ .read8 = b53_mdio_read8,
+ .read16 = b53_mdio_read16,
+ .read32 = b53_mdio_read32,
+ .read48 = b53_mdio_read48,
+ .read64 = b53_mdio_read64,
+ .write8 = b53_mdio_write8,
+ .write16 = b53_mdio_write16,
+ .write32 = b53_mdio_write32,
+ .write48 = b53_mdio_write48,
+ .write64 = b53_mdio_write64,
+};
+
+static int b53_phy_probe(struct phy_device *phydev)
+{
+ struct b53_device dev;
+ int ret;
+
+ /* allow the generic phy driver to take over */
+ if (phydev->addr != B53_PSEUDO_PHY && phydev->addr != 0)
+ return -ENODEV;
+
+ dev.current_page = 0xff;
+ dev.priv = phydev->bus;
+ dev.ops = &b53_mdio_ops;
+ dev.pdata = NULL;
+ mutex_init(&dev.reg_mutex);
+
+ ret = b53_switch_detect(&dev);
+ if (ret)
+ return ret;
+
+ if (is5325(&dev) || is5365(&dev))
+ phydev->supported = SUPPORTED_100baseT_Full;
+ else
+ phydev->supported = SUPPORTED_1000baseT_Full;
+
+ phydev->advertising = phydev->supported;
+
+ return 0;
+}
+
+static int b53_phy_config_init(struct phy_device *phydev)
+{
+ struct b53_device *dev;
+ int ret;
+
+ dev = b53_switch_alloc(&phydev->dev, &b53_mdio_ops, phydev->bus);
+ if (!dev)
+ return -ENOMEM;
+
+ /* we don't use page 0xff, so force a page set */
+ dev->current_page = 0xff;
+ /* force the ethX as alias */
+ dev->sw_dev.alias = phydev->attached_dev->name;
+
+ ret = b53_switch_register(dev);
+ if (ret) {
+ pr_info("failed to register switch: %i\n", ret);
+ return ret;
+ }
+
+ phydev->priv = dev;
+
+ return 0;
+}
+
+static void b53_phy_remove(struct phy_device *phydev)
+{
+ struct b53_device *priv = phydev->priv;
+
+ if (!priv)
+ return;
+
+ b53_switch_remove(priv);
+
+ phydev->priv = NULL;
+}
+
+static int b53_phy_config_aneg(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static int b53_phy_read_status(struct phy_device *phydev)
+{
+ struct b53_device *priv = phydev->priv;
+
+ if (is5325(priv) || is5365(priv))
+ phydev->speed = 100;
+ else
+ phydev->speed = 1000;
+
+ phydev->duplex = DUPLEX_FULL;
+ phydev->link = 1;
+ phydev->state = PHY_RUNNING;
+
+ netif_carrier_on(phydev->attached_dev);
+ phydev->adjust_link(phydev->attached_dev);
+
+ return 0;
+}
+
+/* BCM5325, BCM539x */
+static struct phy_driver b53_phy_driver_id1 = {
+ .phy_id = 0x0143bc00,
+ .name = "Broadcom B53 (1)",
+ .phy_id_mask = 0x1ffffc00,
+ .features = 0,
+ .probe = b53_phy_probe,
+ .remove = b53_phy_remove,
+ .config_aneg = b53_phy_config_aneg,
+ .config_init = b53_phy_config_init,
+ .read_status = b53_phy_read_status,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
+/* BCM53125 */
+static struct phy_driver b53_phy_driver_id2 = {
+ .phy_id = 0x03625c00,
+ .name = "Broadcom B53 (2)",
+ .phy_id_mask = 0x1ffffc00,
+ .features = 0,
+ .probe = b53_phy_probe,
+ .remove = b53_phy_remove,
+ .config_aneg = b53_phy_config_aneg,
+ .config_init = b53_phy_config_init,
+ .read_status = b53_phy_read_status,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init b53_phy_driver_register(void)
+{
+ int ret;
+
+ ret = phy_driver_register(&b53_phy_driver_id1);
+ if (ret)
+ return ret;
+
+ ret = phy_driver_register(&b53_phy_driver_id2);
+ if (ret)
+ phy_driver_unregister(&b53_phy_driver_id1);
+
+ return ret;
+}
+
+void __exit b53_phy_driver_unregister(void)
+{
+ phy_driver_unregister(&b53_phy_driver_id2);
+ phy_driver_unregister(&b53_phy_driver_id1);
+}
+
+module_init(b53_phy_driver_register);
+module_exit(b53_phy_driver_unregister);
+
+MODULE_DESCRIPTION("B53 MDIO access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
new file mode 100644
index 000000000..1fd01581a
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
@@ -0,0 +1,241 @@
+/*
+ * B53 register access through memory mapped registers
+ *
+ * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+ u8 __iomem *regs = dev->priv;
+
+ *val = readb(regs + (page << 8) + reg);
+
+ return 0;
+}
+
+static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 2))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian)
+ *val = readw_be(regs + (page << 8) + reg);
+ else
+ *val = readw(regs + (page << 8) + reg);
+
+ return 0;
+}
+
+static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 4))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian)
+ *val = readl_be(regs + (page << 8) + reg);
+ else
+ *val = readl(regs + (page << 8) + reg);
+
+ return 0;
+}
+
+static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 4))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian) {
+ *val = readl_be(regs + (page << 8) + reg);
+ *val <<= 16;
+ *val |= readw_be(regs + (page << 8) + reg + 4);
+ } else {
+ *val |= readw(regs + (page << 8) + reg + 4);
+ *val <<= 32;
+ *val = readl(regs + (page << 8) + reg);
+ }
+
+ return 0;
+}
+
+static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ u8 __iomem *regs = dev->priv;
+ u32 hi, lo;
+
+ if (WARN_ON(reg % 4))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian) {
+ lo = readl_be(regs + (page << 8) + reg);
+ hi = readl_be(regs + (page << 8) + reg + 4);
+ } else {
+ lo = readl(regs + (page << 8) + reg);
+ hi = readl(regs + (page << 8) + reg + 4);
+ }
+
+ *val = ((u64)hi << 32) | lo;
+
+ return 0;
+}
+
+static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+ u8 __iomem *regs = dev->priv;
+
+ writeb(value, regs + (page << 8) + reg);
+
+ return 0;
+}
+
+static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
+ u16 value)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 2))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian)
+ writew_be(value, regs + (page << 8) + reg);
+ else
+ writew(value, regs + (page << 8) + reg);
+
+ return 0;
+}
+
+static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
+ u32 value)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 4))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian)
+ writel_be(value, regs + (page << 8) + reg);
+ else
+ writel(value, regs + (page << 8) + reg);
+
+ return 0;
+}
+
+static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
+ u64 value)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 4))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian) {
+ writel_be((u32)(value >> 16), regs + (page << 8) + reg);
+ writew_be((u16)value, regs + (page << 8) + reg + 4);
+ } else {
+ writel((u32)value, regs + (page << 8) + reg);
+ writew((u16)(value >> 32), regs + (page << 8) + reg + 4);
+ }
+
+ return 0;
+}
+
+static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
+ u64 value)
+{
+ u8 __iomem *regs = dev->priv;
+
+ if (WARN_ON(reg % 4))
+ return -EINVAL;
+
+ if (dev->pdata && dev->pdata->big_endian) {
+ writel_be((u32)(value >> 32), regs + (page << 8) + reg);
+ writel_be((u32)value, regs + (page << 8) + reg + 4);
+ } else {
+ writel((u32)value, regs + (page << 8) + reg);
+ writel((u32)(value >> 32), regs + (page << 8) + reg + 4);
+ }
+
+ return 0;
+}
+
+static struct b53_io_ops b53_mmap_ops = {
+ .read8 = b53_mmap_read8,
+ .read16 = b53_mmap_read16,
+ .read32 = b53_mmap_read32,
+ .read48 = b53_mmap_read48,
+ .read64 = b53_mmap_read64,
+ .write8 = b53_mmap_write8,
+ .write16 = b53_mmap_write16,
+ .write32 = b53_mmap_write32,
+ .write48 = b53_mmap_write48,
+ .write64 = b53_mmap_write64,
+};
+
+static int b53_mmap_probe(struct platform_device *pdev)
+{
+ struct b53_platform_data *pdata = pdev->dev.platform_data;
+ struct b53_device *dev;
+
+ if (!pdata)
+ return -EINVAL;
+
+ dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
+ if (!dev)
+ return -ENOMEM;
+
+ if (pdata)
+ dev->pdata = pdata;
+
+ pdev->dev.platform_data = dev;
+
+ return b53_switch_register(dev);
+}
+
+static int b53_mmap_remove(struct platform_device *pdev)
+{
+ struct b53_device *dev = pdev->dev.platform_data;
+
+ if (dev) {
+ pdev->dev.platform_data = dev->pdata;
+ b53_switch_remove(dev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver b53_mmap_driver = {
+ .probe = b53_mmap_probe,
+ .remove = b53_mmap_remove,
+ .driver = {
+ .name = "b53-switch",
+ },
+};
+
+module_platform_driver(b53_mmap_driver);
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 MMAP access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c b/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c
new file mode 100644
index 000000000..447f30b64
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c
@@ -0,0 +1,53 @@
+/*
+ * B53 PHY Fixup call
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/phy.h>
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+#define B53_BRCM_OUI_1 0x0143bc00
+#define B53_BRCM_OUI_2 0x03625c00
+
+static int b53_phy_fixup(struct phy_device *dev)
+{
+ u32 phy_id;
+ struct mii_bus *bus = dev->bus;
+
+ if (dev->addr != B53_PSEUDO_PHY)
+ return 0;
+
+ /* read the first port's id */
+ phy_id = mdiobus_read(bus, 0, 2) << 16;
+ phy_id |= mdiobus_read(bus, 0, 3);
+
+ if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
+ (phy_id & 0xfffffc00) == B53_BRCM_OUI_2) {
+ dev->phy_id = phy_id;
+ }
+
+ return 0;
+}
+
+int __init b53_phy_fixup_register(void)
+{
+ return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
+}
+
+subsys_initcall(b53_phy_fixup_register);
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
new file mode 100644
index 000000000..fca74ae4b
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
@@ -0,0 +1,278 @@
+/*
+ * B53 common definitions
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_PRIV_H
+#define __B53_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/switch.h>
+
+struct b53_device;
+
+struct b53_io_ops {
+ int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
+ int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
+ int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
+ int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+ int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+ int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
+ int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
+ int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
+ int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+ int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+};
+
+enum {
+ BCM5325_DEVICE_ID = 0x25,
+ BCM5365_DEVICE_ID = 0x65,
+ BCM5395_DEVICE_ID = 0x95,
+ BCM5397_DEVICE_ID = 0x97,
+ BCM5398_DEVICE_ID = 0x98,
+ BCM53115_DEVICE_ID = 0x53115,
+ BCM53125_DEVICE_ID = 0x53125,
+ BCM63XX_DEVICE_ID = 0x6300,
+};
+
+#define B53_N_PORTS 9
+#define B53_N_PORTS_25 6
+
+struct b53_vlan {
+ unsigned int members:B53_N_PORTS;
+ unsigned int untag:B53_N_PORTS;
+};
+
+struct b53_port {
+ unsigned int pvid:12;
+};
+
+struct b53_device {
+ struct switch_dev sw_dev;
+ struct b53_platform_data *pdata;
+
+ struct mutex reg_mutex;
+ const struct b53_io_ops *ops;
+
+ /* chip specific data */
+ u32 chip_id;
+ u8 core_rev;
+ u8 vta_regs[3];
+ u8 duplex_reg;
+ u8 jumbo_pm_reg;
+ u8 jumbo_size_reg;
+
+ /* used ports mask */
+ u16 enabled_ports;
+
+ /* connect specific data */
+ u8 current_page;
+ struct device *dev;
+ void *priv;
+
+ /* run time configuration */
+ unsigned enable_vlan:1;
+ unsigned enable_jumbo:1;
+ unsigned allow_vid_4095:1;
+
+ struct b53_port *ports;
+ struct b53_vlan *vlans;
+
+ char *buf;
+};
+
+#define b53_for_each_port(dev, i) \
+ for (i = 0; i < B53_N_PORTS; i++) \
+ if (dev->enabled_ports & BIT(i))
+
+
+
+static inline int is5325(struct b53_device *dev)
+{
+ return dev->chip_id == BCM5325_DEVICE_ID;
+}
+
+static inline int is5365(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+ return dev->chip_id == BCM5365_DEVICE_ID;
+#else
+ return 0;
+#endif
+}
+
+static inline int is5397_98(struct b53_device *dev)
+{
+ return dev->chip_id == BCM5397_DEVICE_ID ||
+ dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is531x5(struct b53_device *dev)
+{
+ return dev->chip_id == BCM53115_DEVICE_ID ||
+ dev->chip_id == BCM53125_DEVICE_ID;
+}
+
+static inline int is63xx(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM63XX
+ return dev->chip_id == BCM63XX_DEVICE_ID;
+#else
+ return 0;
+#endif
+}
+
+#define B53_CPU_PORT_25 5
+#define B53_CPU_PORT 8
+
+static inline int is_cpu_port(struct b53_device *dev, int port)
+{
+ return dev->sw_dev.cpu_port == port;
+}
+
+static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
+{
+ return container_of(sw, struct b53_device, sw_dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+ void *priv);
+
+int b53_switch_detect(struct b53_device *dev);
+
+int b53_switch_register(struct b53_device *dev);
+
+static inline void b53_switch_remove(struct b53_device *dev)
+{
+ unregister_switch(&dev->sw_dev);
+}
+
+static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read8(dev, page, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read16(dev, page, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read32(dev, page, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read48(dev, page, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->read64(dev, page, reg, val);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write8(dev, page, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
+ u16 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write16(dev, page, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
+ u32 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write32(dev, page, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
+ u64 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write48(dev, page, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
+ u64 value)
+{
+ int ret;
+
+ mutex_lock(&dev->reg_mutex);
+ ret = dev->ops->write64(dev, page, reg, value);
+ mutex_unlock(&dev->reg_mutex);
+
+ return ret;
+}
+
+#endif
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
new file mode 100644
index 000000000..7b8f539be
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
@@ -0,0 +1,308 @@
+/*
+ * B53 register definitions
+ *
+ * Copyright (C) 2004 Broadcom Corporation
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_REGS_H
+#define __B53_REGS_H
+
+/* Management Port (SMP) Page offsets */
+#define B53_CTRL_PAGE 0x00 /* Control */
+#define B53_STAT_PAGE 0x01 /* Status */
+#define B53_MGMT_PAGE 0x02 /* Management Mode */
+#define B53_MIB_AC_PAGE 0x03 /* MIB Autocast */
+#define B53_ARLCTRL_PAGE 0x04 /* ARL Control */
+#define B53_ARLIO_PAGE 0x05 /* ARL Access */
+#define B53_FRAMEBUF_PAGE 0x06 /* Management frame access */
+#define B53_MEM_ACCESS_PAGE 0x08 /* Memory access */
+
+/* PHY Registers */
+#define B53_PORT_MII_PAGE(i) (0x10 + i) /* Port i MII Registers */
+#define B53_IM_PORT_PAGE 0x18 /* Inverse MII Port (to EMAC) */
+#define B53_ALL_PORT_PAGE 0x19 /* All ports MII (broadcast) */
+
+/* MIB registers */
+#define B53_MIB_PAGE(i) (0x20 + i)
+
+/* Quality of Service (QoS) Registers */
+#define B53_QOS_PAGE 0x30
+
+/* Port VLAN Page */
+#define B53_PVLAN_PAGE 0x31
+
+/* VLAN Registers */
+#define B53_VLAN_PAGE 0x34
+
+/* Jumbo Frame Registers */
+#define B53_JUMBO_PAGE 0x40
+
+/*************************************************************************
+ * Control Page registers
+ *************************************************************************/
+
+/* Port Control Register (8 bit) */
+#define B53_PORT_CTRL(i) (0x00 + i)
+#define PORT_CTRL_RX_DISABLE BIT(0)
+#define PORT_CTRL_TX_DISABLE BIT(1)
+#define PORT_CTRL_RX_BCST_EN BIT(2) /* Broadcast RX (P8 only) */
+#define PORT_CTRL_RX_MCST_EN BIT(3) /* Multicast RX (P8 only) */
+#define PORT_CTRL_RX_UCST_EN BIT(4) /* Unicast RX (P8 only) */
+#define PORT_CTRL_STP_STATE_S 5
+#define PORT_CTRL_STP_STATE_MASK (0x3 << PORT_CTRL_STP_STATE_S)
+
+/* SMP Control Register (8 bit) */
+#define B53_SMP_CTRL 0x0a
+
+/* Switch Mode Control Register (8 bit) */
+#define B53_SWITCH_MODE 0x0b
+#define SM_SW_FWD_MODE BIT(0) /* 1 = Managed Mode */
+#define SM_SW_FWD_EN BIT(1) /* Forwarding Enable */
+
+/* IMP Port state override register (8 bit) */
+#define B53_PORT_OVERRIDE_CTRL 0x0e
+#define PORT_OVERRIDE_LINK BIT(0)
+#define PORT_OVERRIDE_HALF_DUPLEX BIT(1) /* 0 = Full Duplex */
+#define PORT_OVERRIDE_SPEED_S 2
+#define PORT_OVERRIDE_SPEED_10M (0 << PORT_OVERRIDE_SPEED_S)
+#define PORT_OVERRIDE_SPEED_100M (1 << PORT_OVERRIDE_SPEED_S)
+#define PORT_OVERRIDE_SPEED_1000M (2 << PORT_OVERRIDE_SPEED_S)
+#define PORT_OVERRIDE_RV_MII_25 BIT(4) /* BCM5325 only */
+#define PORT_OVERRIDE_RX_FLOW BIT(4)
+#define PORT_OVERRIDE_TX_FLOW BIT(5)
+#define PORT_OVERRIDE_EN BIT(7) /* Use the register contents */
+
+/* Power-down mode control */
+#define B53_PD_MODE_CTRL_25 0x0f
+
+/* IP Multicast control (8 bit) */
+#define B53_IP_MULTICAST_CTRL 0x21
+#define B53_IPMC_FWD_EN BIT(1)
+#define B53_UC_FWD_EN BIT(6)
+#define B53_MC_FWD_EN BIT(7)
+
+/* (16 bit) */
+#define B53_UC_FLOOD_MASK 0x32
+#define B53_MC_FLOOD_MASK 0x34
+#define B53_IPMC_FLOOD_MASK 0x36
+
+/* Software reset register (8 bit) */
+#define B53_SOFTRESET 0x79
+
+/* Fast Aging Control register (8 bit) */
+#define B53_FAST_AGE_CTRL 0x88
+#define FAST_AGE_STATIC BIT(0)
+#define FAST_AGE_DYNAMIC BIT(1)
+#define FAST_AGE_PORT BIT(2)
+#define FAST_AGE_VLAN BIT(3)
+#define FAST_AGE_STP BIT(4)
+#define FAST_AGE_MC BIT(5)
+#define FAST_AGE_DONE BIT(7)
+
+/*************************************************************************
+ * Status Page registers
+ *************************************************************************/
+
+/* Link Status Summary Register (16bit) */
+#define B53_LINK_STAT 0x00
+
+/* Link Status Change Register (16 bit) */
+#define B53_LINK_STAT_CHANGE 0x02
+
+/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
+#define B53_SPEED_STAT 0x04
+#define SPEED_PORT_FE(reg, port) (((reg) >> (port)) & 1)
+#define SPEED_PORT_GE(reg, port) (((reg) >> 2 * (port)) & 3)
+#define SPEED_STAT_10M 0
+#define SPEED_STAT_100M 1
+#define SPEED_STAT_1000M 2
+
+/* Duplex Status Summary (16 bit) */
+#define B53_DUPLEX_STAT_FE 0x06
+#define B53_DUPLEX_STAT_GE 0x08
+#define B53_DUPLEX_STAT_63XX 0x0c
+
+/* Strap Value (48 bit) */
+#define B53_STRAP_VALUE 0x70
+#define SV_GMII_CTRL_115 BIT(27)
+
+/*************************************************************************
+ * Management Mode Page Registers
+ *************************************************************************/
+
+/* Global Management Config Register (8 bit) */
+#define B53_GLOBAL_CONFIG 0x00
+#define GC_RESET_MIB 0x01
+#define GC_RX_BPDU_EN 0x02
+#define GC_MIB_AC_HDR_EN 0x10
+#define GC_MIB_AC_EN 0x20
+#define GC_FRM_MGMT_PORT_M 0xC0
+#define GC_FRM_MGMT_PORT_04 0x00
+#define GC_FRM_MGMT_PORT_MII 0x80
+
+/* Device ID register (8 or 32 bit) */
+#define B53_DEVICE_ID 0x30
+
+/* Revision ID register (8 bit) */
+#define B53_REV_ID 0x40
+
+/*************************************************************************
+ * ARL Access Page Registers
+ *************************************************************************/
+
+/* VLAN Table Access Register (8 bit) */
+#define B53_VT_ACCESS 0x80
+#define B53_VT_ACCESS_9798 0x60 /* for BCM5397/BCM5398 */
+#define B53_VT_ACCESS_63XX 0x60 /* for BCM6328/62/68 */
+#define VTA_CMD_WRITE 0
+#define VTA_CMD_READ 1
+#define VTA_CMD_CLEAR 2
+#define VTA_START_CMD BIT(7)
+
+/* VLAN Table Index Register (16 bit) */
+#define B53_VT_INDEX 0x81
+#define B53_VT_INDEX_9798 0x61
+#define B53_VT_INDEX_63XX 0x62
+
+/* VLAN Table Entry Register (32 bit) */
+#define B53_VT_ENTRY 0x83
+#define B53_VT_ENTRY_9798 0x63
+#define B53_VT_ENTRY_63XX 0x64
+#define VTE_MEMBERS 0x1ff
+#define VTE_UNTAG_S 9
+#define VTE_UNTAG (0x1ff << 9)
+
+/*************************************************************************
+ * Port VLAN Registers
+ *************************************************************************/
+
+/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
+#define B53_PVLAN_PORT_MASK(i) ((i) * 2)
+
+/*************************************************************************
+ * 802.1Q Page Registers
+ *************************************************************************/
+
+/* Global QoS Control (8 bit) */
+#define B53_QOS_GLOBAL_CTL 0x00
+
+/* Enable 802.1Q for individual Ports (16 bit) */
+#define B53_802_1P_EN 0x04
+
+/*************************************************************************
+ * VLAN Page Registers
+ *************************************************************************/
+
+/* VLAN Control 0 (8 bit) */
+#define B53_VLAN_CTRL0 0x00
+#define VC0_8021PF_CTRL_MASK 0x3
+#define VC0_8021PF_CTRL_NONE 0x0
+#define VC0_8021PF_CTRL_CHANGE_PRI 0x1
+#define VC0_8021PF_CTRL_CHANGE_VID 0x2
+#define VC0_8021PF_CTRL_CHANGE_BOTH 0x3
+#define VC0_8021QF_CTRL_MASK 0xc
+#define VC0_8021QF_CTRL_CHANGE_PRI 0x1
+#define VC0_8021QF_CTRL_CHANGE_VID 0x2
+#define VC0_8021QF_CTRL_CHANGE_BOTH 0x3
+#define VC0_RESERVED_1 BIT(1)
+#define VC0_DROP_VID_MISS BIT(4)
+#define VC0_VID_HASH_VID BIT(5)
+#define VC0_VID_CHK_EN BIT(6) /* Use VID,DA or VID,SA */
+#define VC0_VLAN_EN BIT(7) /* 802.1Q VLAN Enabled */
+
+/* VLAN Control 1 (8 bit) */
+#define B53_VLAN_CTRL1 0x01
+#define VC1_RX_MCST_TAG_EN BIT(1)
+#define VC1_RX_MCST_FWD_EN BIT(2)
+#define VC1_RX_MCST_UNTAG_EN BIT(3)
+
+/* VLAN Control 2 (8 bit) */
+#define B53_VLAN_CTRL2 0x02
+
+/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
+#define B53_VLAN_CTRL3 0x03
+#define B53_VLAN_CTRL3_63XX 0x04
+#define VC3_MAXSIZE_1532 BIT(6) /* 5325 only */
+#define VC3_HIGH_8BIT_EN BIT(7) /* 5325 only */
+
+/* VLAN Control 4 (8 bit) */
+#define B53_VLAN_CTRL4 0x05
+#define B53_VLAN_CTRL4_25 0x04
+#define B53_VLAN_CTRL4_63XX 0x06
+#define VC4_ING_VID_CHECK_S 6
+#define VC4_ING_VID_CHECK_MASK (0x3 << VC4_ING_VID_CHECK_S)
+#define VC4_ING_VID_VIO_FWD 0 /* forward, but do not learn */
+#define VC4_ING_VID_VIO_DROP 1 /* drop VID violations */
+#define VC4_NO_ING_VID_CHK 2 /* do not check */
+#define VC4_ING_VID_VIO_TO_IMP 3 /* redirect to MII port */
+
+/* VLAN Control 5 (8 bit) */
+#define B53_VLAN_CTRL5 0x06
+#define B53_VLAN_CTRL5_25 0x05
+#define B53_VLAN_CTRL5_63XX 0x07
+#define VC5_VID_FFF_EN BIT(2)
+#define VC5_DROP_VTABLE_MISS BIT(3)
+
+/* VLAN Control 6 (8 bit) */
+#define B53_VLAN_CTRL6 0x07
+#define B53_VLAN_CTRL6_63XX 0x08
+
+/* VLAN Table Access Register (16 bit) */
+#define B53_VLAN_TABLE_ACCESS_25 0x06 /* BCM5325E/5350 */
+#define B53_VLAN_TABLE_ACCESS_65 0x08 /* BCM5365 */
+#define VTA_VID_LOW_MASK_25 0xf
+#define VTA_VID_LOW_MASK_65 0xff
+#define VTA_VID_HIGH_S_25 4
+#define VTA_VID_HIGH_S_65 8
+#define VTA_VID_HIGH_MASK_25 (0xff << VTA_VID_HIGH_S_25E)
+#define VTA_VID_HIGH_MASK_65 (0xf << VTA_VID_HIGH_S_65)
+#define VTA_RW_STATE BIT(12)
+#define VTA_RW_STATE_RD 0
+#define VTA_RW_STATE_WR BIT(12)
+#define VTA_RW_OP_EN BIT(13)
+
+/* VLAN Read/Write Registers for (16/32 bit) */
+#define B53_VLAN_WRITE_25 0x08
+#define B53_VLAN_WRITE_65 0x0a
+#define B53_VLAN_READ 0x0c
+#define VA_MEMBER_MASK 0x3f
+#define VA_UNTAG_S 6
+#define VA_UNTAG_MASK (0x3f << VA_UNTAG_S)
+#define VA_VID_HIGH_S 12
+#define VA_VID_HIGH_MASK (0xffff << VA_VID_HIGH_S)
+#define VA_VALID_25 BIT(20)
+#define VA_VALID_25_R4 BIT(24)
+#define VA_VALID_65 BIT(14)
+
+/* VLAN Port Default Tag (16 bit) */
+#define B53_VLAN_PORT_DEF_TAG(i) (0x10 + 2 * (i))
+
+/*************************************************************************
+ * Jumbo Frame Page Registers
+ *************************************************************************/
+
+/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
+#define B53_JUMBO_PORT_MASK 0x01
+#define B53_JUMBO_PORT_MASK_63XX 0x04
+#define JPM_10_100_JUMBO_EN BIT(24) /* GigE always enabled */
+
+/* Good Frame Max Size without 802.1Q TAG (16 bit) */
+#define B53_JUMBO_MAX_SIZE 0x05
+#define B53_JUMBO_MAX_SIZE_63XX 0x08
+#define JMS_MIN_SIZE 1518
+#define JMS_MAX_SIZE 9724
+
+#endif /* !__B53_REGS_H */
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c
new file mode 100644
index 000000000..6050fea32
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c
@@ -0,0 +1,329 @@
+/*
+ * B53 register access through SPI
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+#define B53_SPI_DATA 0xf0
+
+#define B53_SPI_STATUS 0xfe
+#define B53_SPI_CMD_SPIF BIT(7)
+#define B53_SPI_CMD_RACK BIT(5)
+
+#define B53_SPI_CMD_READ 0x00
+#define B53_SPI_CMD_WRITE 0x01
+#define B53_SPI_CMD_NORMAL 0x60
+#define B53_SPI_CMD_FAST 0x10
+
+#define B53_SPI_PAGE_SELECT 0xff
+
+static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
+ unsigned len)
+{
+ u8 txbuf[2];
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
+ txbuf[1] = reg;
+
+ return spi_write_then_read(spi, txbuf, 2, val, len);
+}
+
+static inline int b53_spi_clear_status(struct spi_device *spi)
+{
+ unsigned int i;
+ u8 rxbuf;
+ int ret;
+
+ for (i = 0; i < 10; i++) {
+ ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+ if (ret)
+ return ret;
+
+ if (!(rxbuf & B53_SPI_CMD_SPIF))
+ break;
+
+ mdelay(1);
+ }
+
+ if (i == 10)
+ return -EIO;
+
+ return 0;
+}
+
+static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
+{
+ u8 txbuf[3];
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+ txbuf[1] = B53_SPI_PAGE_SELECT;
+ txbuf[2] = page;
+
+ return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
+{
+ int ret = b53_spi_clear_status(spi);
+ if (ret)
+ return ret;
+
+ return b53_spi_set_page(spi, page);
+}
+
+static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
+{
+ u8 rxbuf;
+ int retry_count;
+ int ret;
+
+ ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
+ if (ret)
+ return ret;
+
+ for (retry_count = 0; retry_count < 10; retry_count++) {
+ ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+ if (ret)
+ return ret;
+
+ if (rxbuf & B53_SPI_CMD_RACK)
+ break;
+
+ mdelay(1);
+ }
+
+ if (retry_count == 10)
+ return -EIO;
+
+ return 0;
+}
+
+static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
+ unsigned len)
+{
+ struct spi_device *spi = dev->priv;
+ int ret;
+
+ ret = b53_prepare_reg_access(spi, page);
+ if (ret)
+ return ret;
+
+ ret = b53_spi_prepare_reg_read(spi, reg);
+ if (ret)
+ return ret;
+
+ return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
+}
+
+static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+ return b53_spi_read(dev, page, reg, val, 1);
+}
+
+static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+ int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
+ if (!ret)
+ *val = le16_to_cpu(*val);
+
+ return ret;
+}
+
+static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+ int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
+ if (!ret)
+ *val = le32_to_cpu(*val);
+
+ return ret;
+}
+
+static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ int ret;
+
+ *val = 0;
+ ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
+ if (!ret)
+ *val = le64_to_cpu(*val);
+
+ return ret;
+}
+
+static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+ int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
+ if (!ret)
+ *val = le64_to_cpu(*val);
+
+ return ret;
+}
+
+static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+ struct spi_device *spi = dev->priv;
+ int ret;
+ u8 txbuf[3];
+
+ ret = b53_prepare_reg_access(spi, page);
+ if (ret)
+ return ret;
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+ txbuf[1] = reg;
+ txbuf[2] = value;
+
+ return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
+{
+ struct spi_device *spi = dev->priv;
+ int ret;
+ u8 txbuf[4];
+
+ ret = b53_prepare_reg_access(spi, page);
+ if (ret)
+ return ret;
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+ txbuf[1] = reg;
+ put_unaligned_le16(value, &txbuf[2]);
+
+ return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
+{
+ struct spi_device *spi = dev->priv;
+ int ret;
+ u8 txbuf[6];
+
+ ret = b53_prepare_reg_access(spi, page);
+ if (ret)
+ return ret;
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+ txbuf[1] = reg;
+ put_unaligned_le32(value, &txbuf[2]);
+
+ return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+ struct spi_device *spi = dev->priv;
+ int ret;
+ u8 txbuf[10];
+
+ ret = b53_prepare_reg_access(spi, page);
+ if (ret)
+ return ret;
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+ txbuf[1] = reg;
+ put_unaligned_le64(value, &txbuf[2]);
+
+ return spi_write(spi, txbuf, sizeof(txbuf) - 2);
+}
+
+static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+ struct spi_device *spi = dev->priv;
+ int ret;
+ u8 txbuf[10];
+
+ ret = b53_prepare_reg_access(spi, page);
+ if (ret)
+ return ret;
+
+ txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+ txbuf[1] = reg;
+ put_unaligned_le64(value, &txbuf[2]);
+
+ return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static struct b53_io_ops b53_spi_ops = {
+ .read8 = b53_spi_read8,
+ .read16 = b53_spi_read16,
+ .read32 = b53_spi_read32,
+ .read48 = b53_spi_read48,
+ .read64 = b53_spi_read64,
+ .write8 = b53_spi_write8,
+ .write16 = b53_spi_write16,
+ .write32 = b53_spi_write32,
+ .write48 = b53_spi_write48,
+ .write64 = b53_spi_write64,
+};
+
+static int b53_spi_probe(struct spi_device *spi)
+{
+ struct b53_device *dev;
+ int ret;
+
+ dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);
+ if (!dev)
+ return -ENOMEM;
+
+ if (spi->dev.platform_data)
+ dev->pdata = spi->dev.platform_data;
+
+ ret = b53_switch_register(dev);
+ if (ret)
+ return ret;
+
+ spi->dev.platform_data = dev;
+
+ return 0;
+}
+
+static int b53_spi_remove(struct spi_device *spi)
+{
+ struct b53_device *dev = spi->dev.platform_data;
+
+ if (dev) {
+ struct b53_platform_data *pdata = dev->pdata;
+ b53_switch_remove(dev);
+ spi->dev.platform_data = pdata;
+ }
+
+ return 0;
+}
+
+static struct spi_driver b53_spi_driver = {
+ .driver = {
+ .name = "b53-switch",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = b53_spi_probe,
+ .remove = b53_spi_remove,
+};
+
+module_spi_driver(b53_spi_driver);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 SPI access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/mvswitch.c b/target/linux/generic/files/drivers/net/phy/mvswitch.c
index d754951ac..af73ec22a 100644
--- a/target/linux/generic/files/drivers/net/phy/mvswitch.c
+++ b/target/linux/generic/files/drivers/net/phy/mvswitch.c
@@ -348,16 +348,26 @@ mvswitch_config_aneg(struct phy_device *phydev)
}
static void
-mvswitch_remove(struct phy_device *pdev)
+mvswitch_detach(struct phy_device *pdev)
{
struct mvswitch_priv *priv = to_mvsw(pdev);
struct net_device *dev = pdev->attached_dev;
+ if (!dev)
+ return;
+
dev->phy_ptr = NULL;
dev->eth_mangle_rx = NULL;
dev->eth_mangle_tx = NULL;
dev->features = priv->orig_features;
dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+}
+
+static void
+mvswitch_remove(struct phy_device *pdev)
+{
+ struct mvswitch_priv *priv = to_mvsw(pdev);
+
kfree(priv);
}
@@ -399,6 +409,7 @@ static struct phy_driver mvswitch_driver = {
.features = PHY_BASIC_FEATURES,
.probe = &mvswitch_probe,
.remove = &mvswitch_remove,
+ .detach = &mvswitch_detach,
.config_init = &mvswitch_config_init,
.config_aneg = &mvswitch_config_aneg,
.read_status = &mvswitch_read_status,
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
index be85274e7..e2b4f02ef 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
@@ -15,6 +15,9 @@
#include <linux/gpio.h>
#include <linux/spinlock.h>
#include <linux/skbuff.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include <linux/rtl8366.h>
#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
@@ -1106,6 +1109,7 @@ int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
port = &val->value.ports[0];
for (i = 0; i < val->len; i++, port++) {
+ int pvid = 0;
member |= BIT(port->id);
if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
@@ -1115,9 +1119,14 @@ int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
* To ensure that we have a valid MC entry for this VLAN,
* initialize the port VLAN ID here.
*/
- err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
+ err = rtl8366_get_pvid(smi, port->id, &pvid);
if (err < 0)
return err;
+ if (pvid == 0) {
+ err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
+ if (err < 0)
+ return err;
+ }
}
return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0);
@@ -1370,6 +1379,71 @@ void rtl8366_smi_cleanup(struct rtl8366_smi *smi)
}
EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup);
+#ifdef CONFIG_OF
+int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+ int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0);
+ int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0);
+
+ if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) {
+ dev_err(&pdev->dev, "gpios missing in devictree\n");
+ return -EINVAL;
+ }
+
+ smi->gpio_sda = sda;
+ smi->gpio_sck = sck;
+
+ return 0;
+}
+#else
+static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+ return -ENODEV;
+}
+#endif
+
+int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+ struct rtl8366_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data specified\n");
+ return -EINVAL;
+ }
+
+ smi->gpio_sda = pdata->gpio_sda;
+ smi->gpio_sck = pdata->gpio_sck;
+ smi->hw_reset = pdata->hw_reset;
+
+ return 0;
+}
+
+
+struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev)
+{
+ struct rtl8366_smi *smi;
+ int err;
+
+ smi = rtl8366_smi_alloc(&pdev->dev);
+ if (!smi)
+ return NULL;
+
+ if (pdev->dev.of_node)
+ err = rtl8366_smi_probe_of(pdev, smi);
+ else
+ err = rtl8366_smi_probe_plat(pdev, smi);
+
+ if (err)
+ goto free_smi;
+
+ return smi;
+
+free_smi:
+ kfree(smi);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_probe);
+
MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver");
MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
index 700f83c1a..bd41385be 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
@@ -13,6 +13,7 @@
#include <linux/phy.h>
#include <linux/switch.h>
+#include <linux/platform_device.h>
struct rtl8366_smi_ops;
struct rtl8366_vlan_ops;
@@ -146,4 +147,6 @@ int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
const struct switch_attr *attr,
struct switch_val *val);
+struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
+
#endif /* _RTL8366_SMI_H */
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
index 775949792..d3674afcb 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
@@ -13,7 +13,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/rtl8366.h>
@@ -1168,10 +1170,9 @@ static struct rtl8366_smi_ops rtl8366rb_smi_ops = {
.enable_port = rtl8366rb_enable_port,
};
-static int __devinit rtl8366rb_probe(struct platform_device *pdev)
+static int rtl8366rb_probe(struct platform_device *pdev)
{
static int rtl8366_smi_version_printed;
- struct rtl8366_platform_data *pdata;
struct rtl8366_smi *smi;
int err;
@@ -1179,22 +1180,9 @@ static int __devinit rtl8366rb_probe(struct platform_device *pdev)
printk(KERN_NOTICE RTL8366RB_DRIVER_DESC
" version " RTL8366RB_DRIVER_VER"\n");
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data specified\n");
- err = -EINVAL;
- goto err_out;
- }
-
- smi = rtl8366_smi_alloc(&pdev->dev);
- if (!smi) {
- err = -ENOMEM;
- goto err_out;
- }
-
- smi->gpio_sda = pdata->gpio_sda;
- smi->gpio_sck = pdata->gpio_sck;
- smi->hw_reset = pdata->hw_reset;
+ smi = rtl8366_smi_probe(pdev);
+ if (!smi)
+ return -ENODEV;
smi->clk_delay = 10;
smi->cmd_read = 0xa9;
@@ -1223,11 +1211,10 @@ static int __devinit rtl8366rb_probe(struct platform_device *pdev)
rtl8366_smi_cleanup(smi);
err_free_smi:
kfree(smi);
- err_out:
return err;
}
-static int __devexit rtl8366rb_remove(struct platform_device *pdev)
+static int rtl8366rb_remove(struct platform_device *pdev)
{
struct rtl8366_smi *smi = platform_get_drvdata(pdev);
@@ -1241,13 +1228,22 @@ static int __devexit rtl8366rb_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366rb_match[] = {
+ { .compatible = "rtl8366rb" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366rb_match);
+#endif
+
static struct platform_driver rtl8366rb_driver = {
.driver = {
.name = RTL8366RB_DRIVER_NAME,
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rtl8366rb_match),
},
.probe = rtl8366rb_probe,
- .remove = __devexit_p(rtl8366rb_remove),
+ .remove = rtl8366rb_remove,
};
static int __init rtl8366rb_module_init(void)
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366s.c b/target/linux/generic/files/drivers/net/phy/rtl8366s.c
index 77427d6c9..3dc2a87ae 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8366s.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8366s.c
@@ -12,7 +12,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/rtl8366.h>
@@ -254,7 +256,7 @@ static int rtl8366s_setup(struct rtl8366_smi *smi)
int err;
pdata = smi->parent->platform_data;
- if (pdata->num_initvals && pdata->initvals) {
+ if (pdata && pdata->num_initvals && pdata->initvals) {
unsigned i;
dev_info(smi->parent, "applying initvals\n");
@@ -1048,10 +1050,9 @@ static struct rtl8366_smi_ops rtl8366s_smi_ops = {
.enable_port = rtl8366s_enable_port,
};
-static int __devinit rtl8366s_probe(struct platform_device *pdev)
+static int rtl8366s_probe(struct platform_device *pdev)
{
static int rtl8366_smi_version_printed;
- struct rtl8366_platform_data *pdata;
struct rtl8366_smi *smi;
int err;
@@ -1059,22 +1060,9 @@ static int __devinit rtl8366s_probe(struct platform_device *pdev)
printk(KERN_NOTICE RTL8366S_DRIVER_DESC
" version " RTL8366S_DRIVER_VER"\n");
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data specified\n");
- err = -EINVAL;
- goto err_out;
- }
-
- smi = rtl8366_smi_alloc(&pdev->dev);
- if (!smi) {
- err = -ENOMEM;
- goto err_out;
- }
-
- smi->gpio_sda = pdata->gpio_sda;
- smi->gpio_sck = pdata->gpio_sck;
- smi->hw_reset = pdata->hw_reset;
+ smi = rtl8366_smi_probe(pdev);
+ if (!smi)
+ return -ENODEV;
smi->clk_delay = 10;
smi->cmd_read = 0xa9;
@@ -1103,11 +1091,10 @@ static int __devinit rtl8366s_probe(struct platform_device *pdev)
rtl8366_smi_cleanup(smi);
err_free_smi:
kfree(smi);
- err_out:
return err;
}
-static int __devexit rtl8366s_remove(struct platform_device *pdev)
+static int rtl8366s_remove(struct platform_device *pdev)
{
struct rtl8366_smi *smi = platform_get_drvdata(pdev);
@@ -1121,13 +1108,24 @@ static int __devexit rtl8366s_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366s_match[] = {
+ { .compatible = "realtek,rtl8366s" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366s_match);
+#endif
+
static struct platform_driver rtl8366s_driver = {
.driver = {
.name = RTL8366S_DRIVER_NAME,
.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(rtl8366s_match),
+#endif
},
.probe = rtl8366s_probe,
- .remove = __devexit_p(rtl8366s_remove),
+ .remove = rtl8366s_remove,
};
static int __init rtl8366s_module_init(void)
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367.c b/target/linux/generic/files/drivers/net/phy/rtl8367.c
index ee2a04701..3cd811e75 100644
--- a/target/linux/generic/files/drivers/net/phy/rtl8367.c
+++ b/target/linux/generic/files/drivers/net/phy/rtl8367.c
@@ -11,7 +11,9 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/rtl8367.h>
@@ -1071,6 +1073,51 @@ static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate)
return 0;
}
+#ifdef CONFIG_OF
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+ const char *name)
+{
+ struct rtl8367_extif_config *cfg;
+ const __be32 *prop;
+ int size;
+ int err;
+
+ prop = of_get_property(smi->parent->of_node, name, &size);
+ if (!prop)
+ return rtl8367_extif_init(smi, id, NULL);
+
+ if (size != (9 * sizeof(*prop))) {
+ dev_err(smi->parent, "%s property is invalid\n", name);
+ return -EINVAL;
+ }
+
+ cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->txdelay = be32_to_cpup(prop++);
+ cfg->rxdelay = be32_to_cpup(prop++);
+ cfg->mode = be32_to_cpup(prop++);
+ cfg->ability.force_mode = be32_to_cpup(prop++);
+ cfg->ability.txpause = be32_to_cpup(prop++);
+ cfg->ability.rxpause = be32_to_cpup(prop++);
+ cfg->ability.link = be32_to_cpup(prop++);
+ cfg->ability.duplex = be32_to_cpup(prop++);
+ cfg->ability.speed = be32_to_cpup(prop++);
+
+ err = rtl8367_extif_init(smi, id, cfg);
+ kfree(cfg);
+
+ return err;
+}
+#else
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+ const char *name)
+{
+ return -EINVAL;
+}
+#endif
+
static int rtl8367_setup(struct rtl8366_smi *smi)
{
struct rtl8367_platform_data *pdata;
@@ -1084,13 +1131,23 @@ static int rtl8367_setup(struct rtl8366_smi *smi)
return err;
/* initialize external interfaces */
- err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg);
- if (err)
- return err;
+ if (smi->parent->of_node) {
+ err = rtl8367_extif_init_of(smi, 0, "realtek,extif0");
+ if (err)
+ return err;
- err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg);
- if (err)
- return err;
+ err = rtl8367_extif_init_of(smi, 1, "realtek,extif1");
+ if (err)
+ return err;
+ } else {
+ err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg);
+ if (err)
+ return err;
+
+ err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg);
+ if (err)
+ return err;
+ }
/* set maximum packet length to 1536 bytes */
REG_RMW(smi, RTL8367_SWC0_REG, RTL8367_SWC0_MAX_LENGTH_MASK,
@@ -1671,28 +1728,14 @@ static struct rtl8366_smi_ops rtl8367_smi_ops = {
.enable_port = rtl8367_enable_port,
};
-static int __devinit rtl8367_probe(struct platform_device *pdev)
+static int rtl8367_probe(struct platform_device *pdev)
{
- struct rtl8367_platform_data *pdata;
struct rtl8366_smi *smi;
int err;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data specified\n");
- err = -EINVAL;
- goto err_out;
- }
-
- smi = rtl8366_smi_alloc(&pdev->dev);
- if (!smi) {
- err = -ENOMEM;
- goto err_out;
- }
-
- smi->gpio_sda = pdata->gpio_sda;
- smi->gpio_sck = pdata->gpio_sck;
- smi->hw_reset = pdata->hw_reset;
+ smi = rtl8366_smi_probe(pdev);
+ if (!smi)
+ return -ENODEV;
smi->clk_delay = 1500;
smi->cmd_read = 0xb9;
@@ -1721,11 +1764,10 @@ static int __devinit rtl8367_probe(struct platform_device *pdev)
rtl8366_smi_cleanup(smi);
err_free_smi:
kfree(smi);
- err_out:
return err;
}
-static int __devexit rtl8367_remove(struct platform_device *pdev)
+static int rtl8367_remove(struct platform_device *pdev)
{
struct rtl8366_smi *smi = platform_get_drvdata(pdev);
@@ -1747,13 +1789,24 @@ static void rtl8367_shutdown(struct platform_device *pdev)
rtl8367_reset_chip(smi);
}
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367_match[] = {
+ { .compatible = "realtek,rtl8367" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367_match);
+#endif
+
static struct platform_driver rtl8367_driver = {
.driver = {
.name = RTL8367_DRIVER_NAME,
.owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(rtl8367_match),
+#endif
},
.probe = rtl8367_probe,
- .remove = __devexit_p(rtl8367_remove),
+ .remove = rtl8367_remove,
.shutdown = rtl8367_shutdown,
};
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367b.c b/target/linux/generic/files/drivers/net/phy/rtl8367b.c
new file mode 100644
index 000000000..a82f696d7
--- /dev/null
+++ b/target/linux/generic/files/drivers/net/phy/rtl8367b.c
@@ -0,0 +1,1602 @@
+/*
+ * Platform driver for the Realtek RTL8367R-VB ethernet switches
+ *
+ * Copyright (C) 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 version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367B_RESET_DELAY 1000 /* msecs*/
+
+#define RTL8367B_PHY_ADDR_MAX 8
+#define RTL8367B_PHY_REG_MAX 31
+
+#define RTL8367B_VID_MASK 0x3fff
+#define RTL8367B_FID_MASK 0xf
+#define RTL8367B_UNTAG_MASK 0xff
+#define RTL8367B_MEMBER_MASK 0xff
+
+#define RTL8367B_PORT_MISC_CFG_REG(_p) (0x000e + 0x20 * (_p))
+#define RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT 4
+#define RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK 0x3
+#define RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL 0
+#define RTL8367B_PORT_MISC_CFG_EGRESS_MODE_KEEP 1
+#define RTL8367B_PORT_MISC_CFG_EGRESS_MODE_PRI 2
+#define RTL8367B_PORT_MISC_CFG_EGRESS_MODE_REAL 3
+
+#define RTL8367B_BYPASS_LINE_RATE_REG 0x03f7
+
+#define RTL8367B_TA_CTRL_REG 0x0500 /*GOOD*/
+#define RTL8367B_TA_CTRL_SPA_SHIFT 8
+#define RTL8367B_TA_CTRL_SPA_MASK 0x7
+#define RTL8367B_TA_CTRL_METHOD BIT(4)/*GOOD*/
+#define RTL8367B_TA_CTRL_CMD_SHIFT 3
+#define RTL8367B_TA_CTRL_CMD_READ 0
+#define RTL8367B_TA_CTRL_CMD_WRITE 1
+#define RTL8367B_TA_CTRL_TABLE_SHIFT 0 /*GOOD*/
+#define RTL8367B_TA_CTRL_TABLE_ACLRULE 1
+#define RTL8367B_TA_CTRL_TABLE_ACLACT 2
+#define RTL8367B_TA_CTRL_TABLE_CVLAN 3
+#define RTL8367B_TA_CTRL_TABLE_L2 4
+#define RTL8367B_TA_CTRL_CVLAN_READ \
+ ((RTL8367B_TA_CTRL_CMD_READ << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+ RTL8367B_TA_CTRL_TABLE_CVLAN)
+#define RTL8367B_TA_CTRL_CVLAN_WRITE \
+ ((RTL8367B_TA_CTRL_CMD_WRITE << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+ RTL8367B_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367B_TA_ADDR_REG 0x0501/*GOOD*/
+#define RTL8367B_TA_ADDR_MASK 0x3fff/*GOOD*/
+
+#define RTL8367B_TA_LUT_REG 0x0502/*GOOD*/
+
+#define RTL8367B_TA_WRDATA_REG(_x) (0x0510 + (_x))/*GOOD*/
+#define RTL8367B_TA_VLAN_NUM_WORDS 2
+#define RTL8367B_TA_VLAN_VID_MASK RTL8367B_VID_MASK
+#define RTL8367B_TA_VLAN0_MEMBER_SHIFT 0
+#define RTL8367B_TA_VLAN0_MEMBER_MASK RTL8367B_MEMBER_MASK
+#define RTL8367B_TA_VLAN0_UNTAG_SHIFT 8
+#define RTL8367B_TA_VLAN0_UNTAG_MASK RTL8367B_MEMBER_MASK
+#define RTL8367B_TA_VLAN1_FID_SHIFT 0
+#define RTL8367B_TA_VLAN1_FID_MASK RTL8367B_FID_MASK
+
+#define RTL8367B_TA_RDDATA_REG(_x) (0x0520 + (_x))/*GOOD*/
+
+#define RTL8367B_VLAN_PVID_CTRL_REG(_p) (0x0700 + (_p) / 2) /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_MASK 0x1f /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_SHIFT(_p) (8 * ((_p) % 2)) /*GOOD*/
+
+#define RTL8367B_VLAN_MC_BASE(_x) (0x0728 + (_x) * 4) /*GOOD*/
+#define RTL8367B_VLAN_MC_NUM_WORDS 4 /*GOOD*/
+#define RTL8367B_VLAN_MC0_MEMBER_SHIFT 0/*GOOD*/
+#define RTL8367B_VLAN_MC0_MEMBER_MASK RTL8367B_MEMBER_MASK/*GOOD*/
+#define RTL8367B_VLAN_MC1_FID_SHIFT 0/*GOOD*/
+#define RTL8367B_VLAN_MC1_FID_MASK RTL8367B_FID_MASK/*GOOD*/
+#define RTL8367B_VLAN_MC3_EVID_SHIFT 0/*GOOD*/
+#define RTL8367B_VLAN_MC3_EVID_MASK RTL8367B_VID_MASK/*GOOD*/
+
+#define RTL8367B_VLAN_CTRL_REG 0x07a8 /*GOOD*/
+#define RTL8367B_VLAN_CTRL_ENABLE BIT(0)
+
+#define RTL8367B_VLAN_INGRESS_REG 0x07a9 /*GOOD*/
+
+#define RTL8367B_PORT_ISOLATION_REG(_p) (0x08a2 + (_p)) /*GOOD*/
+
+#define RTL8367B_MIB_COUNTER_REG(_x) (0x1000 + (_x)) /*GOOD*/
+#define RTL8367B_MIB_COUNTER_PORT_OFFSET 0x007c /*GOOD*/
+
+#define RTL8367B_MIB_ADDRESS_REG 0x1004 /*GOOD*/
+
+#define RTL8367B_MIB_CTRL0_REG(_x) (0x1005 + (_x)) /*GOOD*/
+#define RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK BIT(11) /*GOOD*/
+#define RTL8367B_MIB_CTRL0_QM_RESET_MASK BIT(10) /*GOOD*/
+#define RTL8367B_MIB_CTRL0_PORT_RESET_MASK(_p) BIT(2 + (_p)) /*GOOD*/
+#define RTL8367B_MIB_CTRL0_RESET_MASK BIT(1) /*GOOD*/
+#define RTL8367B_MIB_CTRL0_BUSY_MASK BIT(0) /*GOOD*/
+
+#define RTL8367B_SWC0_REG 0x1200/*GOOD*/
+#define RTL8367B_SWC0_MAX_LENGTH_SHIFT 13/*GOOD*/
+#define RTL8367B_SWC0_MAX_LENGTH(_x) ((_x) << 13) /*GOOD*/
+#define RTL8367B_SWC0_MAX_LENGTH_MASK RTL8367B_SWC0_MAX_LENGTH(0x3)
+#define RTL8367B_SWC0_MAX_LENGTH_1522 RTL8367B_SWC0_MAX_LENGTH(0)
+#define RTL8367B_SWC0_MAX_LENGTH_1536 RTL8367B_SWC0_MAX_LENGTH(1)
+#define RTL8367B_SWC0_MAX_LENGTH_1552 RTL8367B_SWC0_MAX_LENGTH(2)
+#define RTL8367B_SWC0_MAX_LENGTH_16000 RTL8367B_SWC0_MAX_LENGTH(3)
+
+#define RTL8367B_CHIP_NUMBER_REG 0x1300/*GOOD*/
+
+#define RTL8367B_CHIP_VER_REG 0x1301/*GOOD*/
+#define RTL8367B_CHIP_VER_RLVID_SHIFT 12/*GOOD*/
+#define RTL8367B_CHIP_VER_RLVID_MASK 0xf/*GOOD*/
+#define RTL8367B_CHIP_VER_MCID_SHIFT 8/*GOOD*/
+#define RTL8367B_CHIP_VER_MCID_MASK 0xf/*GOOD*/
+#define RTL8367B_CHIP_VER_BOID_SHIFT 4/*GOOD*/
+#define RTL8367B_CHIP_VER_BOID_MASK 0xf/*GOOD*/
+#define RTL8367B_CHIP_VER_AFE_SHIFT 0/*GOOD*/
+#define RTL8367B_CHIP_VER_AFE_MASK 0x1/*GOOD*/
+
+#define RTL8367B_CHIP_MODE_REG 0x1302
+#define RTL8367B_CHIP_MODE_MASK 0x7
+
+#define RTL8367B_CHIP_DEBUG0_REG 0x1303
+#define RTL8367B_CHIP_DEBUG0_DUMMY0(_x) BIT(8 + (_x))
+
+#define RTL8367B_CHIP_DEBUG1_REG 0x1304
+
+#define RTL8367B_DIS_REG 0x1305
+#define RTL8367B_DIS_SKIP_MII_RXER(_x) BIT(12 + (_x))
+#define RTL8367B_DIS_RGMII_SHIFT(_x) (4 * (_x))
+#define RTL8367B_DIS_RGMII_MASK 0x7
+
+#define RTL8367B_EXT_RGMXF_REG(_x) (0x1306 + (_x))
+#define RTL8367B_EXT_RGMXF_DUMMY0_SHIFT 5
+#define RTL8367B_EXT_RGMXF_DUMMY0_MASK 0x7ff
+#define RTL8367B_EXT_RGMXF_TXDELAY_SHIFT 3
+#define RTL8367B_EXT_RGMXF_TXDELAY_MASK 1
+#define RTL8367B_EXT_RGMXF_RXDELAY_MASK 0x7
+
+#define RTL8367B_DI_FORCE_REG(_x) (0x1310 + (_x))
+#define RTL8367B_DI_FORCE_MODE BIT(12)
+#define RTL8367B_DI_FORCE_NWAY BIT(7)
+#define RTL8367B_DI_FORCE_TXPAUSE BIT(6)
+#define RTL8367B_DI_FORCE_RXPAUSE BIT(5)
+#define RTL8367B_DI_FORCE_LINK BIT(4)
+#define RTL8367B_DI_FORCE_DUPLEX BIT(2)
+#define RTL8367B_DI_FORCE_SPEED_MASK 3
+#define RTL8367B_DI_FORCE_SPEED_10 0
+#define RTL8367B_DI_FORCE_SPEED_100 1
+#define RTL8367B_DI_FORCE_SPEED_1000 2
+
+#define RTL8367B_MAC_FORCE_REG(_x) (0x1312 + (_x))
+
+#define RTL8367B_CHIP_RESET_REG 0x1322 /*GOOD*/
+#define RTL8367B_CHIP_RESET_SW BIT(1) /*GOOD*/
+#define RTL8367B_CHIP_RESET_HW BIT(0) /*GOOD*/
+
+#define RTL8367B_PORT_STATUS_REG(_p) (0x1352 + (_p)) /*GOOD*/
+#define RTL8367B_PORT_STATUS_EN_1000_SPI BIT(11) /*GOOD*/
+#define RTL8367B_PORT_STATUS_EN_100_SPI BIT(10)/*GOOD*/
+#define RTL8367B_PORT_STATUS_NWAY_FAULT BIT(9)/*GOOD*/
+#define RTL8367B_PORT_STATUS_LINK_MASTER BIT(8)/*GOOD*/
+#define RTL8367B_PORT_STATUS_NWAY BIT(7)/*GOOD*/
+#define RTL8367B_PORT_STATUS_TXPAUSE BIT(6)/*GOOD*/
+#define RTL8367B_PORT_STATUS_RXPAUSE BIT(5)/*GOOD*/
+#define RTL8367B_PORT_STATUS_LINK BIT(4)/*GOOD*/
+#define RTL8367B_PORT_STATUS_DUPLEX BIT(2)/*GOOD*/
+#define RTL8367B_PORT_STATUS_SPEED_MASK 0x0003/*GOOD*/
+#define RTL8367B_PORT_STATUS_SPEED_10 0/*GOOD*/
+#define RTL8367B_PORT_STATUS_SPEED_100 1/*GOOD*/
+#define RTL8367B_PORT_STATUS_SPEED_1000 2/*GOOD*/
+
+#define RTL8367B_RTL_MAGIC_ID_REG 0x13c2
+#define RTL8367B_RTL_MAGIC_ID_VAL 0x0249
+
+#define RTL8367B_IA_CTRL_REG 0x1f00
+#define RTL8367B_IA_CTRL_RW(_x) ((_x) << 1)
+#define RTL8367B_IA_CTRL_RW_READ RTL8367B_IA_CTRL_RW(0)
+#define RTL8367B_IA_CTRL_RW_WRITE RTL8367B_IA_CTRL_RW(1)
+#define RTL8367B_IA_CTRL_CMD_MASK BIT(0)
+
+#define RTL8367B_IA_STATUS_REG 0x1f01
+#define RTL8367B_IA_STATUS_PHY_BUSY BIT(2)
+#define RTL8367B_IA_STATUS_SDS_BUSY BIT(1)
+#define RTL8367B_IA_STATUS_MDX_BUSY BIT(0)
+
+#define RTL8367B_IA_ADDRESS_REG 0x1f02
+#define RTL8367B_IA_WRITE_DATA_REG 0x1f03
+#define RTL8367B_IA_READ_DATA_REG 0x1f04
+
+#define RTL8367B_INTERNAL_PHY_REG(_a, _r) (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367B_NUM_MIB_COUNTERS 58
+
+#define RTL8367B_CPU_PORT_NUM 5
+#define RTL8367B_NUM_PORTS 8
+#define RTL8367B_NUM_VLANS 32
+#define RTL8367B_NUM_VIDS 4096
+#define RTL8367B_PRIORITYMAX 7
+#define RTL8367B_FIDMAX 7
+
+#define RTL8367B_PORT_0 BIT(0)
+#define RTL8367B_PORT_1 BIT(1)
+#define RTL8367B_PORT_2 BIT(2)
+#define RTL8367B_PORT_3 BIT(3)
+#define RTL8367B_PORT_4 BIT(4)
+#define RTL8367B_PORT_E0 BIT(5) /* External port 0 */
+#define RTL8367B_PORT_E1 BIT(6) /* External port 1 */
+#define RTL8367B_PORT_E2 BIT(7) /* External port 2 */
+
+#define RTL8367B_PORTS_ALL \
+ (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 | \
+ RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E0 | \
+ RTL8367B_PORT_E1 | RTL8367B_PORT_E2)
+
+#define RTL8367B_PORTS_ALL_BUT_CPU \
+ (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 | \
+ RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E1 | \
+ RTL8367B_PORT_E2)
+
+struct rtl8367b_initval {
+ u16 reg;
+ u16 val;
+};
+
+static struct rtl8366_mib_counter
+rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
+ {0, 0, 4, "ifInOctets" },
+ {0, 4, 2, "dot3StatsFCSErrors" },
+ {0, 6, 2, "dot3StatsSymbolErrors" },
+ {0, 8, 2, "dot3InPauseFrames" },
+ {0, 10, 2, "dot3ControlInUnknownOpcodes" },
+ {0, 12, 2, "etherStatsFragments" },
+ {0, 14, 2, "etherStatsJabbers" },
+ {0, 16, 2, "ifInUcastPkts" },
+ {0, 18, 2, "etherStatsDropEvents" },
+ {0, 20, 2, "ifInMulticastPkts" },
+ {0, 22, 2, "ifInBroadcastPkts" },
+ {0, 24, 2, "inMldChecksumError" },
+ {0, 26, 2, "inIgmpChecksumError" },
+ {0, 28, 2, "inMldSpecificQuery" },
+ {0, 30, 2, "inMldGeneralQuery" },
+ {0, 32, 2, "inIgmpSpecificQuery" },
+ {0, 34, 2, "inIgmpGeneralQuery" },
+ {0, 36, 2, "inMldLeaves" },
+ {0, 38, 2, "inIgmpLeaves" },
+
+ {0, 40, 4, "etherStatsOctets" },
+ {0, 44, 2, "etherStatsUnderSizePkts" },
+ {0, 46, 2, "etherOversizeStats" },
+ {0, 48, 2, "etherStatsPkts64Octets" },
+ {0, 50, 2, "etherStatsPkts65to127Octets" },
+ {0, 52, 2, "etherStatsPkts128to255Octets" },
+ {0, 54, 2, "etherStatsPkts256to511Octets" },
+ {0, 56, 2, "etherStatsPkts512to1023Octets" },
+ {0, 58, 2, "etherStatsPkts1024to1518Octets" },
+
+ {0, 60, 4, "ifOutOctets" },
+ {0, 64, 2, "dot3StatsSingleCollisionFrames" },
+ {0, 66, 2, "dot3StatMultipleCollisionFrames" },
+ {0, 68, 2, "dot3sDeferredTransmissions" },
+ {0, 70, 2, "dot3StatsLateCollisions" },
+ {0, 72, 2, "etherStatsCollisions" },
+ {0, 74, 2, "dot3StatsExcessiveCollisions" },
+ {0, 76, 2, "dot3OutPauseFrames" },
+ {0, 78, 2, "ifOutDiscards" },
+ {0, 80, 2, "dot1dTpPortInDiscards" },
+ {0, 82, 2, "ifOutUcastPkts" },
+ {0, 84, 2, "ifOutMulticastPkts" },
+ {0, 86, 2, "ifOutBroadcastPkts" },
+ {0, 88, 2, "outOampduPkts" },
+ {0, 90, 2, "inOampduPkts" },
+ {0, 92, 2, "inIgmpJoinsSuccess" },
+ {0, 94, 2, "inIgmpJoinsFail" },
+ {0, 96, 2, "inMldJoinsSuccess" },
+ {0, 98, 2, "inMldJoinsFail" },
+ {0, 100, 2, "inReportSuppressionDrop" },
+ {0, 102, 2, "inLeaveSuppressionDrop" },
+ {0, 104, 2, "outIgmpReports" },
+ {0, 106, 2, "outIgmpLeaves" },
+ {0, 108, 2, "outIgmpGeneralQuery" },
+ {0, 110, 2, "outIgmpSpecificQuery" },
+ {0, 112, 2, "outMldReports" },
+ {0, 114, 2, "outMldLeaves" },
+ {0, 116, 2, "outMldGeneralQuery" },
+ {0, 118, 2, "outMldSpecificQuery" },
+ {0, 120, 2, "inKnownMulticastPkts" },
+};
+
+#define REG_RD(_smi, _reg, _val) \
+ do { \
+ err = rtl8366_smi_read_reg(_smi, _reg, _val); \
+ if (err) \
+ return err; \
+ } while (0)
+
+#define REG_WR(_smi, _reg, _val) \
+ do { \
+ err = rtl8366_smi_write_reg(_smi, _reg, _val); \
+ if (err) \
+ return err; \
+ } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val) \
+ do { \
+ err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val); \
+ if (err) \
+ return err; \
+ } while (0)
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_0[] = {
+ {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x0301, 0x0026}, {0x1722, 0x0E14},
+ {0x205F, 0x0002}, {0x2059, 0x1A00}, {0x205F, 0x0000}, {0x207F, 0x0002},
+ {0x2077, 0x0000}, {0x2078, 0x0000}, {0x2079, 0x0000}, {0x207A, 0x0000},
+ {0x207B, 0x0000}, {0x207F, 0x0000}, {0x205F, 0x0002}, {0x2053, 0x0000},
+ {0x2054, 0x0000}, {0x2055, 0x0000}, {0x2056, 0x0000}, {0x2057, 0x0000},
+ {0x205F, 0x0000}, {0x12A4, 0x110A}, {0x12A6, 0x150A}, {0x13F1, 0x0013},
+ {0x13F4, 0x0010}, {0x13F5, 0x0000}, {0x0018, 0x0F00}, {0x0038, 0x0F00},
+ {0x0058, 0x0F00}, {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x12B6, 0x0C02},
+ {0x12B7, 0x030F}, {0x12B8, 0x11FF}, {0x12BC, 0x0004}, {0x1362, 0x0115},
+ {0x1363, 0x0002}, {0x1363, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E},
+ {0x221F, 0x0007}, {0x221E, 0x002D}, {0x2218, 0xF030}, {0x221F, 0x0007},
+ {0x221E, 0x0023}, {0x2216, 0x0005}, {0x2215, 0x00B9}, {0x2219, 0x0044},
+ {0x2215, 0x00BA}, {0x2219, 0x0020}, {0x2215, 0x00BB}, {0x2219, 0x00C1},
+ {0x2215, 0x0148}, {0x2219, 0x0096}, {0x2215, 0x016E}, {0x2219, 0x0026},
+ {0x2216, 0x0000}, {0x2216, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+ {0x221F, 0x0007}, {0x221E, 0x0020}, {0x2215, 0x0D00}, {0x221F, 0x0000},
+ {0x221F, 0x0000}, {0x2217, 0x2160}, {0x221F, 0x0001}, {0x2210, 0xF25E},
+ {0x221F, 0x0007}, {0x221E, 0x0042}, {0x2215, 0x0F00}, {0x2215, 0x0F00},
+ {0x2216, 0x7408}, {0x2215, 0x0E00}, {0x2215, 0x0F00}, {0x2215, 0x0F01},
+ {0x2216, 0x4000}, {0x2215, 0x0E01}, {0x2215, 0x0F01}, {0x2215, 0x0F02},
+ {0x2216, 0x9400}, {0x2215, 0x0E02}, {0x2215, 0x0F02}, {0x2215, 0x0F03},
+ {0x2216, 0x7408}, {0x2215, 0x0E03}, {0x2215, 0x0F03}, {0x2215, 0x0F04},
+ {0x2216, 0x4008}, {0x2215, 0x0E04}, {0x2215, 0x0F04}, {0x2215, 0x0F05},
+ {0x2216, 0x9400}, {0x2215, 0x0E05}, {0x2215, 0x0F05}, {0x2215, 0x0F06},
+ {0x2216, 0x0803}, {0x2215, 0x0E06}, {0x2215, 0x0F06}, {0x2215, 0x0D00},
+ {0x2215, 0x0100}, {0x221F, 0x0001}, {0x2210, 0xF05E}, {0x221F, 0x0000},
+ {0x2217, 0x2100}, {0x221F, 0x0000}, {0x220D, 0x0003}, {0x220E, 0x0015},
+ {0x220D, 0x4003}, {0x220E, 0x0006}, {0x221F, 0x0000}, {0x2200, 0x1340},
+ {0x133F, 0x0010}, {0x12A0, 0x0058}, {0x12A1, 0x0058}, {0x133E, 0x000E},
+ {0x133F, 0x0030}, {0x221F, 0x0000}, {0x2210, 0x0166}, {0x221F, 0x0000},
+ {0x133E, 0x000E}, {0x133F, 0x0010}, {0x133F, 0x0030}, {0x133E, 0x000E},
+ {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8B6E},
+ {0x2206, 0x0000}, {0x220F, 0x0100}, {0x2205, 0x8000}, {0x2206, 0x0280},
+ {0x2206, 0x28F7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+ {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+ {0x2206, 0x6602}, {0x2206, 0x80B9}, {0x2206, 0xE08B}, {0x2206, 0x8CE1},
+ {0x2206, 0x8B8D}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x8E1E},
+ {0x2206, 0x01A0}, {0x2206, 0x00E7}, {0x2206, 0xAEDB}, {0x2206, 0xEEE0},
+ {0x2206, 0x120E}, {0x2206, 0xEEE0}, {0x2206, 0x1300}, {0x2206, 0xEEE0},
+ {0x2206, 0x2001}, {0x2206, 0xEEE0}, {0x2206, 0x2166}, {0x2206, 0xEEE0},
+ {0x2206, 0xC463}, {0x2206, 0xEEE0}, {0x2206, 0xC5E8}, {0x2206, 0xEEE0},
+ {0x2206, 0xC699}, {0x2206, 0xEEE0}, {0x2206, 0xC7C2}, {0x2206, 0xEEE0},
+ {0x2206, 0xC801}, {0x2206, 0xEEE0}, {0x2206, 0xC913}, {0x2206, 0xEEE0},
+ {0x2206, 0xCA30}, {0x2206, 0xEEE0}, {0x2206, 0xCB3E}, {0x2206, 0xEEE0},
+ {0x2206, 0xDCE1}, {0x2206, 0xEEE0}, {0x2206, 0xDD00}, {0x2206, 0xEEE2},
+ {0x2206, 0x0001}, {0x2206, 0xEEE2}, {0x2206, 0x0100}, {0x2206, 0xEEE4},
+ {0x2206, 0x8860}, {0x2206, 0xEEE4}, {0x2206, 0x8902}, {0x2206, 0xEEE4},
+ {0x2206, 0x8C00}, {0x2206, 0xEEE4}, {0x2206, 0x8D30}, {0x2206, 0xEEEA},
+ {0x2206, 0x1480}, {0x2206, 0xEEEA}, {0x2206, 0x1503}, {0x2206, 0xEEEA},
+ {0x2206, 0xC600}, {0x2206, 0xEEEA}, {0x2206, 0xC706}, {0x2206, 0xEE85},
+ {0x2206, 0xEE00}, {0x2206, 0xEE85}, {0x2206, 0xEF00}, {0x2206, 0xEE8B},
+ {0x2206, 0x6750}, {0x2206, 0xEE8B}, {0x2206, 0x6632}, {0x2206, 0xEE8A},
+ {0x2206, 0xD448}, {0x2206, 0xEE8A}, {0x2206, 0xD548}, {0x2206, 0xEE8A},
+ {0x2206, 0xD649}, {0x2206, 0xEE8A}, {0x2206, 0xD7F8}, {0x2206, 0xEE8B},
+ {0x2206, 0x85E2}, {0x2206, 0xEE8B}, {0x2206, 0x8700}, {0x2206, 0xEEFF},
+ {0x2206, 0xF600}, {0x2206, 0xEEFF}, {0x2206, 0xF7FC}, {0x2206, 0x04F8},
+ {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2023}, {0x2206, 0xF620},
+ {0x2206, 0xE48B}, {0x2206, 0x8E02}, {0x2206, 0x2877}, {0x2206, 0x0225},
+ {0x2206, 0xC702}, {0x2206, 0x26A1}, {0x2206, 0x0281}, {0x2206, 0xB302},
+ {0x2206, 0x8496}, {0x2206, 0x0202}, {0x2206, 0xA102}, {0x2206, 0x27F1},
+ {0x2206, 0x0228}, {0x2206, 0xF902}, {0x2206, 0x2AA0}, {0x2206, 0x0282},
+ {0x2206, 0xB8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD21}, {0x2206, 0x08F6},
+ {0x2206, 0x21E4}, {0x2206, 0x8B8E}, {0x2206, 0x0202}, {0x2206, 0x80E0},
+ {0x2206, 0x8B8E}, {0x2206, 0xAD22}, {0x2206, 0x05F6}, {0x2206, 0x22E4},
+ {0x2206, 0x8B8E}, {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2305},
+ {0x2206, 0xF623}, {0x2206, 0xE48B}, {0x2206, 0x8EE0}, {0x2206, 0x8B8E},
+ {0x2206, 0xAD24}, {0x2206, 0x08F6}, {0x2206, 0x24E4}, {0x2206, 0x8B8E},
+ {0x2206, 0x0227}, {0x2206, 0x6AE0}, {0x2206, 0x8B8E}, {0x2206, 0xAD25},
+ {0x2206, 0x05F6}, {0x2206, 0x25E4}, {0x2206, 0x8B8E}, {0x2206, 0xE08B},
+ {0x2206, 0x8EAD}, {0x2206, 0x260B}, {0x2206, 0xF626}, {0x2206, 0xE48B},
+ {0x2206, 0x8E02}, {0x2206, 0x830D}, {0x2206, 0x021D}, {0x2206, 0x6BE0},
+ {0x2206, 0x8B8E}, {0x2206, 0xAD27}, {0x2206, 0x05F6}, {0x2206, 0x27E4},
+ {0x2206, 0x8B8E}, {0x2206, 0x0281}, {0x2206, 0x4402}, {0x2206, 0x045C},
+ {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B83}, {0x2206, 0xAD23},
+ {0x2206, 0x30E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x2359},
+ {0x2206, 0x02E0}, {0x2206, 0x85EF}, {0x2206, 0xE585}, {0x2206, 0xEFAC},
+ {0x2206, 0x2907}, {0x2206, 0x1F01}, {0x2206, 0x9E51}, {0x2206, 0xAD29},
+ {0x2206, 0x20E0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x06E1},
+ {0x2206, 0x8B84}, {0x2206, 0xAD28}, {0x2206, 0x42E0}, {0x2206, 0x8B85},
+ {0x2206, 0xAD21}, {0x2206, 0x06E1}, {0x2206, 0x8B84}, {0x2206, 0xAD29},
+ {0x2206, 0x36BF}, {0x2206, 0x34BF}, {0x2206, 0x022C}, {0x2206, 0x31AE},
+ {0x2206, 0x2EE0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x10E0},
+ {0x2206, 0x8B84}, {0x2206, 0xF620}, {0x2206, 0xE48B}, {0x2206, 0x84EE},
+ {0x2206, 0x8ADA}, {0x2206, 0x00EE}, {0x2206, 0x8ADB}, {0x2206, 0x00E0},
+ {0x2206, 0x8B85}, {0x2206, 0xAD21}, {0x2206, 0x0CE0}, {0x2206, 0x8B84},
+ {0x2206, 0xF621}, {0x2206, 0xE48B}, {0x2206, 0x84EE}, {0x2206, 0x8B72},
+ {0x2206, 0xFFBF}, {0x2206, 0x34C2}, {0x2206, 0x022C}, {0x2206, 0x31FC},
+ {0x2206, 0x04F8}, {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B85},
+ {0x2206, 0xAD21}, {0x2206, 0x42E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0},
+ {0x2206, 0x2358}, {0x2206, 0xC059}, {0x2206, 0x021E}, {0x2206, 0x01E1},
+ {0x2206, 0x8B72}, {0x2206, 0x1F10}, {0x2206, 0x9E2F}, {0x2206, 0xE48B},
+ {0x2206, 0x72AD}, {0x2206, 0x2123}, {0x2206, 0xE18B}, {0x2206, 0x84F7},
+ {0x2206, 0x29E5}, {0x2206, 0x8B84}, {0x2206, 0xAC27}, {0x2206, 0x10AC},
+ {0x2206, 0x2605}, {0x2206, 0x0205}, {0x2206, 0x23AE}, {0x2206, 0x1602},
+ {0x2206, 0x0535}, {0x2206, 0x0282}, {0x2206, 0x30AE}, {0x2206, 0x0E02},
+ {0x2206, 0x056A}, {0x2206, 0x0282}, {0x2206, 0x75AE}, {0x2206, 0x0602},
+ {0x2206, 0x04DC}, {0x2206, 0x0282}, {0x2206, 0x04EF}, {0x2206, 0x96FE},
+ {0x2206, 0xFC04}, {0x2206, 0xF8F9}, {0x2206, 0xE08B}, {0x2206, 0x87AD},
+ {0x2206, 0x2321}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+ {0x2206, 0xAD26}, {0x2206, 0x18F6}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+ {0x2206, 0xE5EA}, {0x2206, 0x15F6}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+ {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+ {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+ {0x2206, 0xE08B}, {0x2206, 0x87AD}, {0x2206, 0x233A}, {0x2206, 0xAD22},
+ {0x2206, 0x37E0}, {0x2206, 0xE020}, {0x2206, 0xE1E0}, {0x2206, 0x21AC},
+ {0x2206, 0x212E}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+ {0x2206, 0xF627}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+ {0x2206, 0xE2EA}, {0x2206, 0x12E3}, {0x2206, 0xEA13}, {0x2206, 0x5A8F},
+ {0x2206, 0x6A20}, {0x2206, 0xE6EA}, {0x2206, 0x12E7}, {0x2206, 0xEA13},
+ {0x2206, 0xF726}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+ {0x2206, 0xF727}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+ {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B87},
+ {0x2206, 0xAD23}, {0x2206, 0x38AD}, {0x2206, 0x2135}, {0x2206, 0xE0E0},
+ {0x2206, 0x20E1}, {0x2206, 0xE021}, {0x2206, 0xAC21}, {0x2206, 0x2CE0},
+ {0x2206, 0xEA14}, {0x2206, 0xE1EA}, {0x2206, 0x15F6}, {0x2206, 0x27E4},
+ {0x2206, 0xEA14}, {0x2206, 0xE5EA}, {0x2206, 0x15E2}, {0x2206, 0xEA12},
+ {0x2206, 0xE3EA}, {0x2206, 0x135A}, {0x2206, 0x8FE6}, {0x2206, 0xEA12},
+ {0x2206, 0xE7EA}, {0x2206, 0x13F7}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+ {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+ {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+ {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2146},
+ {0x2206, 0xE0E0}, {0x2206, 0x22E1}, {0x2206, 0xE023}, {0x2206, 0x58C0},
+ {0x2206, 0x5902}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x651F},
+ {0x2206, 0x109E}, {0x2206, 0x33E4}, {0x2206, 0x8B65}, {0x2206, 0xAD21},
+ {0x2206, 0x22AD}, {0x2206, 0x272A}, {0x2206, 0xD400}, {0x2206, 0x01BF},
+ {0x2206, 0x34F2}, {0x2206, 0x022C}, {0x2206, 0xA2BF}, {0x2206, 0x34F5},
+ {0x2206, 0x022C}, {0x2206, 0xE0E0}, {0x2206, 0x8B67}, {0x2206, 0x1B10},
+ {0x2206, 0xAA14}, {0x2206, 0xE18B}, {0x2206, 0x660D}, {0x2206, 0x1459},
+ {0x2206, 0x0FAE}, {0x2206, 0x05E1}, {0x2206, 0x8B66}, {0x2206, 0x590F},
+ {0x2206, 0xBF85}, {0x2206, 0x6102}, {0x2206, 0x2CA2}, {0x2206, 0xEF96},
+ {0x2206, 0xFEFC}, {0x2206, 0x04F8}, {0x2206, 0xF9FA}, {0x2206, 0xFBEF},
+ {0x2206, 0x79E2}, {0x2206, 0x8AD2}, {0x2206, 0xAC19}, {0x2206, 0x2DE0},
+ {0x2206, 0xE036}, {0x2206, 0xE1E0}, {0x2206, 0x37EF}, {0x2206, 0x311F},
+ {0x2206, 0x325B}, {0x2206, 0x019E}, {0x2206, 0x1F7A}, {0x2206, 0x0159},
+ {0x2206, 0x019F}, {0x2206, 0x0ABF}, {0x2206, 0x348E}, {0x2206, 0x022C},
+ {0x2206, 0x31F6}, {0x2206, 0x06AE}, {0x2206, 0x0FF6}, {0x2206, 0x0302},
+ {0x2206, 0x0470}, {0x2206, 0xF703}, {0x2206, 0xF706}, {0x2206, 0xBF34},
+ {0x2206, 0x9302}, {0x2206, 0x2C31}, {0x2206, 0xAC1A}, {0x2206, 0x25E0},
+ {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x23EF}, {0x2206, 0x300D},
+ {0x2206, 0x311F}, {0x2206, 0x325B}, {0x2206, 0x029E}, {0x2206, 0x157A},
+ {0x2206, 0x0258}, {0x2206, 0xC4A0}, {0x2206, 0x0408}, {0x2206, 0xBF34},
+ {0x2206, 0x9E02}, {0x2206, 0x2C31}, {0x2206, 0xAE06}, {0x2206, 0xBF34},
+ {0x2206, 0x9C02}, {0x2206, 0x2C31}, {0x2206, 0xAC1B}, {0x2206, 0x4AE0},
+ {0x2206, 0xE012}, {0x2206, 0xE1E0}, {0x2206, 0x13EF}, {0x2206, 0x300D},
+ {0x2206, 0x331F}, {0x2206, 0x325B}, {0x2206, 0x1C9E}, {0x2206, 0x3AEF},
+ {0x2206, 0x325B}, {0x2206, 0x1C9F}, {0x2206, 0x09BF}, {0x2206, 0x3498},
+ {0x2206, 0x022C}, {0x2206, 0x3102}, {0x2206, 0x83C5}, {0x2206, 0x5A03},
+ {0x2206, 0x0D03}, {0x2206, 0x581C}, {0x2206, 0x1E20}, {0x2206, 0x0207},
+ {0x2206, 0xA0A0}, {0x2206, 0x000E}, {0x2206, 0x0284}, {0x2206, 0x17AD},
+ {0x2206, 0x1817}, {0x2206, 0xBF34}, {0x2206, 0x9A02}, {0x2206, 0x2C31},
+ {0x2206, 0xAE0F}, {0x2206, 0xBF34}, {0x2206, 0xC802}, {0x2206, 0x2C31},
+ {0x2206, 0xBF34}, {0x2206, 0xC502}, {0x2206, 0x2C31}, {0x2206, 0x0284},
+ {0x2206, 0x52E6}, {0x2206, 0x8AD2}, {0x2206, 0xEF97}, {0x2206, 0xFFFE},
+ {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xBF34}, {0x2206, 0xDA02},
+ {0x2206, 0x2CE0}, {0x2206, 0xE58A}, {0x2206, 0xD3BF}, {0x2206, 0x34D4},
+ {0x2206, 0x022C}, {0x2206, 0xE00C}, {0x2206, 0x1159}, {0x2206, 0x02E0},
+ {0x2206, 0x8AD3}, {0x2206, 0x1E01}, {0x2206, 0xE48A}, {0x2206, 0xD3D1},
+ {0x2206, 0x00BF}, {0x2206, 0x34DA}, {0x2206, 0x022C}, {0x2206, 0xA2D1},
+ {0x2206, 0x01BF}, {0x2206, 0x34D4}, {0x2206, 0x022C}, {0x2206, 0xA2BF},
+ {0x2206, 0x34CB}, {0x2206, 0x022C}, {0x2206, 0xE0E5}, {0x2206, 0x8ACE},
+ {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CE0}, {0x2206, 0xE58A},
+ {0x2206, 0xCFBF}, {0x2206, 0x8564}, {0x2206, 0x022C}, {0x2206, 0xE0E5},
+ {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6A02}, {0x2206, 0x2CE0},
+ {0x2206, 0xE58A}, {0x2206, 0xD1FC}, {0x2206, 0x04F8}, {0x2206, 0xE18A},
+ {0x2206, 0xD1BF}, {0x2206, 0x856A}, {0x2206, 0x022C}, {0x2206, 0xA2E1},
+ {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+ {0x2206, 0xE18A}, {0x2206, 0xCFBF}, {0x2206, 0x8567}, {0x2206, 0x022C},
+ {0x2206, 0xA2E1}, {0x2206, 0x8ACE}, {0x2206, 0xBF34}, {0x2206, 0xCB02},
+ {0x2206, 0x2CA2}, {0x2206, 0xE18A}, {0x2206, 0xD3BF}, {0x2206, 0x34DA},
+ {0x2206, 0x022C}, {0x2206, 0xA2E1}, {0x2206, 0x8AD3}, {0x2206, 0x0D11},
+ {0x2206, 0xBF34}, {0x2206, 0xD402}, {0x2206, 0x2CA2}, {0x2206, 0xFC04},
+ {0x2206, 0xF9A0}, {0x2206, 0x0405}, {0x2206, 0xE38A}, {0x2206, 0xD4AE},
+ {0x2206, 0x13A0}, {0x2206, 0x0805}, {0x2206, 0xE38A}, {0x2206, 0xD5AE},
+ {0x2206, 0x0BA0}, {0x2206, 0x0C05}, {0x2206, 0xE38A}, {0x2206, 0xD6AE},
+ {0x2206, 0x03E3}, {0x2206, 0x8AD7}, {0x2206, 0xEF13}, {0x2206, 0xBF34},
+ {0x2206, 0xCB02}, {0x2206, 0x2CA2}, {0x2206, 0xEF13}, {0x2206, 0x0D11},
+ {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CA2}, {0x2206, 0xEF13},
+ {0x2206, 0x0D14}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+ {0x2206, 0xEF13}, {0x2206, 0x0D17}, {0x2206, 0xBF85}, {0x2206, 0x6A02},
+ {0x2206, 0x2CA2}, {0x2206, 0xFD04}, {0x2206, 0xF8E0}, {0x2206, 0x8B85},
+ {0x2206, 0xAD27}, {0x2206, 0x2DE0}, {0x2206, 0xE036}, {0x2206, 0xE1E0},
+ {0x2206, 0x37E1}, {0x2206, 0x8B73}, {0x2206, 0x1F10}, {0x2206, 0x9E20},
+ {0x2206, 0xE48B}, {0x2206, 0x73AC}, {0x2206, 0x200B}, {0x2206, 0xAC21},
+ {0x2206, 0x0DAC}, {0x2206, 0x250F}, {0x2206, 0xAC27}, {0x2206, 0x0EAE},
+ {0x2206, 0x0F02}, {0x2206, 0x84CC}, {0x2206, 0xAE0A}, {0x2206, 0x0284},
+ {0x2206, 0xD1AE}, {0x2206, 0x05AE}, {0x2206, 0x0302}, {0x2206, 0x84D8},
+ {0x2206, 0xFC04}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0x0402},
+ {0x2206, 0x84E5}, {0x2206, 0x0285}, {0x2206, 0x2804}, {0x2206, 0x0285},
+ {0x2206, 0x4904}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0xEE8B},
+ {0x2206, 0x6902}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B85},
+ {0x2206, 0xAD26}, {0x2206, 0x38D0}, {0x2206, 0x0B02}, {0x2206, 0x2B4D},
+ {0x2206, 0x5882}, {0x2206, 0x7882}, {0x2206, 0x9F2D}, {0x2206, 0xE08B},
+ {0x2206, 0x68E1}, {0x2206, 0x8B69}, {0x2206, 0x1F10}, {0x2206, 0x9EC8},
+ {0x2206, 0x10E4}, {0x2206, 0x8B68}, {0x2206, 0xE0E0}, {0x2206, 0x00E1},
+ {0x2206, 0xE001}, {0x2206, 0xF727}, {0x2206, 0xE4E0}, {0x2206, 0x00E5},
+ {0x2206, 0xE001}, {0x2206, 0xE2E0}, {0x2206, 0x20E3}, {0x2206, 0xE021},
+ {0x2206, 0xAD30}, {0x2206, 0xF7F6}, {0x2206, 0x27E4}, {0x2206, 0xE000},
+ {0x2206, 0xE5E0}, {0x2206, 0x01FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+ {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2212},
+ {0x2206, 0xE0E0}, {0x2206, 0x14E1}, {0x2206, 0xE015}, {0x2206, 0xAD26},
+ {0x2206, 0x9CE1}, {0x2206, 0x85E0}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+ {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x04F8},
+ {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B86}, {0x2206, 0xAD22},
+ {0x2206, 0x09E1}, {0x2206, 0x85E1}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+ {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x0464},
+ {0x2206, 0xE48C}, {0x2206, 0xFDE4}, {0x2206, 0x80CA}, {0x2206, 0xE480},
+ {0x2206, 0x66E0}, {0x2206, 0x8E70}, {0x2206, 0xE076}, {0x2205, 0xE142},
+ {0x2206, 0x0701}, {0x2205, 0xE140}, {0x2206, 0x0405}, {0x220F, 0x0000},
+ {0x221F, 0x0000}, {0x2200, 0x1340}, {0x133E, 0x000E}, {0x133F, 0x0010},
+ {0x13EB, 0x11BB}
+};
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_1[] = {
+ {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x1305, 0xC000}, {0x121E, 0x03CA},
+ {0x1233, 0x0352}, {0x1234, 0x0064}, {0x1237, 0x0096}, {0x1238, 0x0078},
+ {0x1239, 0x0084}, {0x123A, 0x0030}, {0x205F, 0x0002}, {0x2059, 0x1A00},
+ {0x205F, 0x0000}, {0x207F, 0x0002}, {0x2077, 0x0000}, {0x2078, 0x0000},
+ {0x2079, 0x0000}, {0x207A, 0x0000}, {0x207B, 0x0000}, {0x207F, 0x0000},
+ {0x205F, 0x0002}, {0x2053, 0x0000}, {0x2054, 0x0000}, {0x2055, 0x0000},
+ {0x2056, 0x0000}, {0x2057, 0x0000}, {0x205F, 0x0000}, {0x133F, 0x0030},
+ {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2205, 0x8B86}, {0x2206, 0x800E},
+ {0x221F, 0x0000}, {0x133F, 0x0010}, {0x12A3, 0x2200}, {0x6107, 0xE58B},
+ {0x6103, 0xA970}, {0x0018, 0x0F00}, {0x0038, 0x0F00}, {0x0058, 0x0F00},
+ {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x133F, 0x0030}, {0x133E, 0x000E},
+ {0x221F, 0x0005}, {0x2205, 0x8B6E}, {0x2206, 0x0000}, {0x220F, 0x0100},
+ {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8000}, {0x2206, 0x0280},
+ {0x2206, 0x2BF7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+ {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+ {0x2206, 0x6602}, {0x2206, 0x8044}, {0x2206, 0x0201}, {0x2206, 0x7CE0},
+ {0x2206, 0x8B8C}, {0x2206, 0xE18B}, {0x2206, 0x8D1E}, {0x2206, 0x01E1},
+ {0x2206, 0x8B8E}, {0x2206, 0x1E01}, {0x2206, 0xA000}, {0x2206, 0xE4AE},
+ {0x2206, 0xD8EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE}, {0x2206, 0x85C1},
+ {0x2206, 0x00EE}, {0x2206, 0x8AFC}, {0x2206, 0x07EE}, {0x2206, 0x8AFD},
+ {0x2206, 0x73EE}, {0x2206, 0xFFF6}, {0x2206, 0x00EE}, {0x2206, 0xFFF7},
+ {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD20},
+ {0x2206, 0x0302}, {0x2206, 0x8050}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+ {0x2206, 0xE08B}, {0x2206, 0x85AD}, {0x2206, 0x2548}, {0x2206, 0xE08A},
+ {0x2206, 0xE4E1}, {0x2206, 0x8AE5}, {0x2206, 0x7C00}, {0x2206, 0x009E},
+ {0x2206, 0x35EE}, {0x2206, 0x8AE4}, {0x2206, 0x00EE}, {0x2206, 0x8AE5},
+ {0x2206, 0x00E0}, {0x2206, 0x8AFC}, {0x2206, 0xE18A}, {0x2206, 0xFDE2},
+ {0x2206, 0x85C0}, {0x2206, 0xE385}, {0x2206, 0xC102}, {0x2206, 0x2DAC},
+ {0x2206, 0xAD20}, {0x2206, 0x12EE}, {0x2206, 0x8AE4}, {0x2206, 0x03EE},
+ {0x2206, 0x8AE5}, {0x2206, 0xB7EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE},
+ {0x2206, 0x85C1}, {0x2206, 0x00AE}, {0x2206, 0x1115}, {0x2206, 0xE685},
+ {0x2206, 0xC0E7}, {0x2206, 0x85C1}, {0x2206, 0xAE08}, {0x2206, 0xEE85},
+ {0x2206, 0xC000}, {0x2206, 0xEE85}, {0x2206, 0xC100}, {0x2206, 0xFDFC},
+ {0x2206, 0x0400}, {0x2205, 0xE142}, {0x2206, 0x0701}, {0x2205, 0xE140},
+ {0x2206, 0x0405}, {0x220F, 0x0000}, {0x221F, 0x0000}, {0x133E, 0x000E},
+ {0x133F, 0x0010}, {0x13EB, 0x11BB}, {0x207F, 0x0002}, {0x2073, 0x1D22},
+ {0x207F, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x2200, 0x1340},
+ {0x133E, 0x000E}, {0x133F, 0x0010},
+};
+
+static int rtl8367b_write_initvals(struct rtl8366_smi *smi,
+ const struct rtl8367b_initval *initvals,
+ int count)
+{
+ int err;
+ int i;
+
+ for (i = 0; i < count; i++)
+ REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+ return 0;
+}
+
+static int rtl8367b_read_phy_reg(struct rtl8366_smi *smi,
+ u32 phy_addr, u32 phy_reg, u32 *val)
+{
+ int timeout;
+ u32 data;
+ int err;
+
+ if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+ return -EINVAL;
+
+ if (phy_reg > RTL8367B_PHY_REG_MAX)
+ return -EINVAL;
+
+ REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+ if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+ return -ETIMEDOUT;
+
+ /* prepare address */
+ REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+ RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+ /* send read command */
+ REG_WR(smi, RTL8367B_IA_CTRL_REG,
+ RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_READ);
+
+ timeout = 5;
+ do {
+ REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+ if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+ break;
+
+ if (timeout--) {
+ dev_err(smi->parent, "phy read timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1);
+ } while (1);
+
+ /* read data */
+ REG_RD(smi, RTL8367B_IA_READ_DATA_REG, val);
+
+ dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+ phy_addr, phy_reg, *val);
+ return 0;
+}
+
+static int rtl8367b_write_phy_reg(struct rtl8366_smi *smi,
+ u32 phy_addr, u32 phy_reg, u32 val)
+{
+ int timeout;
+ u32 data;
+ int err;
+
+ dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+ phy_addr, phy_reg, val);
+
+ if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+ return -EINVAL;
+
+ if (phy_reg > RTL8367B_PHY_REG_MAX)
+ return -EINVAL;
+
+ REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+ if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+ return -ETIMEDOUT;
+
+ /* preapre data */
+ REG_WR(smi, RTL8367B_IA_WRITE_DATA_REG, val);
+
+ /* prepare address */
+ REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+ RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+ /* send write command */
+ REG_WR(smi, RTL8367B_IA_CTRL_REG,
+ RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_WRITE);
+
+ timeout = 5;
+ do {
+ REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+ if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+ break;
+
+ if (timeout--) {
+ dev_err(smi->parent, "phy write timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1);
+ } while (1);
+
+ return 0;
+}
+
+static int rtl8367b_init_regs(struct rtl8366_smi *smi)
+{
+ const struct rtl8367b_initval *initvals;
+ u32 chip_ver;
+ u32 rlvid;
+ int count;
+ int err;
+
+ REG_WR(smi, RTL8367B_RTL_MAGIC_ID_REG, RTL8367B_RTL_MAGIC_ID_VAL);
+ REG_RD(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+
+ rlvid = (chip_ver >> RTL8367B_CHIP_VER_RLVID_SHIFT) &
+ RTL8367B_CHIP_VER_RLVID_MASK;
+
+ switch (rlvid) {
+ case 0:
+ initvals = rtl8367r_vb_initvals_0;
+ count = ARRAY_SIZE(rtl8367r_vb_initvals_0);
+ break;
+
+ case 1:
+ initvals = rtl8367r_vb_initvals_1;
+ count = ARRAY_SIZE(rtl8367r_vb_initvals_1);
+ break;
+
+ default:
+ dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+ return -ENODEV;
+ }
+
+ /* TODO: disable RLTP */
+
+ return rtl8367b_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367b_reset_chip(struct rtl8366_smi *smi)
+{
+ int timeout = 10;
+ int err;
+ u32 data;
+
+ REG_WR(smi, RTL8367B_CHIP_RESET_REG, RTL8367B_CHIP_RESET_HW);
+ msleep(RTL8367B_RESET_DELAY);
+
+ do {
+ REG_RD(smi, RTL8367B_CHIP_RESET_REG, &data);
+ if (!(data & RTL8367B_CHIP_RESET_HW))
+ break;
+
+ msleep(1);
+ } while (--timeout);
+
+ if (!timeout) {
+ dev_err(smi->parent, "chip reset timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
+ enum rtl8367_extif_mode mode)
+{
+ int err;
+
+ /* set port mode */
+ switch (mode) {
+ case RTL8367_EXTIF_MODE_RGMII:
+ case RTL8367_EXTIF_MODE_RGMII_33V:
+ REG_WR(smi, RTL8367B_CHIP_DEBUG0_REG, 0x0367);
+ REG_WR(smi, RTL8367B_CHIP_DEBUG1_REG, 0x7777);
+ break;
+
+ case RTL8367_EXTIF_MODE_TMII_MAC:
+ case RTL8367_EXTIF_MODE_TMII_PHY:
+ REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+ BIT((id + 1) % 2), BIT((id + 1) % 2));
+ break;
+
+ case RTL8367_EXTIF_MODE_GMII:
+ REG_RMW(smi, RTL8367B_CHIP_DEBUG0_REG,
+ RTL8367B_CHIP_DEBUG0_DUMMY0(id),
+ RTL8367B_CHIP_DEBUG0_DUMMY0(id));
+ REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+ break;
+
+ case RTL8367_EXTIF_MODE_MII_MAC:
+ case RTL8367_EXTIF_MODE_MII_PHY:
+ case RTL8367_EXTIF_MODE_DISABLED:
+ REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+ BIT((id + 1) % 2), 0);
+ REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), 0);
+ break;
+
+ default:
+ dev_err(smi->parent,
+ "invalid mode for external interface %d\n", id);
+ return -EINVAL;
+ }
+
+ REG_RMW(smi, RTL8367B_DIS_REG,
+ RTL8367B_DIS_RGMII_MASK << RTL8367B_DIS_RGMII_SHIFT(id),
+ mode << RTL8367B_DIS_RGMII_SHIFT(id));
+
+ return 0;
+}
+
+static int rtl8367b_extif_set_force(struct rtl8366_smi *smi, int id,
+ struct rtl8367_port_ability *pa)
+{
+ u32 mask;
+ u32 val;
+ int err;
+
+ mask = (RTL8367B_DI_FORCE_MODE |
+ RTL8367B_DI_FORCE_NWAY |
+ RTL8367B_DI_FORCE_TXPAUSE |
+ RTL8367B_DI_FORCE_RXPAUSE |
+ RTL8367B_DI_FORCE_LINK |
+ RTL8367B_DI_FORCE_DUPLEX |
+ RTL8367B_DI_FORCE_SPEED_MASK);
+
+ val = pa->speed;
+ val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
+ val |= pa->nway ? RTL8367B_DI_FORCE_NWAY : 0;
+ val |= pa->txpause ? RTL8367B_DI_FORCE_TXPAUSE : 0;
+ val |= pa->rxpause ? RTL8367B_DI_FORCE_RXPAUSE : 0;
+ val |= pa->link ? RTL8367B_DI_FORCE_LINK : 0;
+ val |= pa->duplex ? RTL8367B_DI_FORCE_DUPLEX : 0;
+
+ REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
+
+ return 0;
+}
+
+static int rtl8367b_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+ unsigned txdelay, unsigned rxdelay)
+{
+ u32 mask;
+ u32 val;
+ int err;
+
+ mask = (RTL8367B_EXT_RGMXF_RXDELAY_MASK |
+ (RTL8367B_EXT_RGMXF_TXDELAY_MASK <<
+ RTL8367B_EXT_RGMXF_TXDELAY_SHIFT));
+
+ val = rxdelay;
+ val |= txdelay << RTL8367B_EXT_RGMXF_TXDELAY_SHIFT;
+
+ REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), mask, val);
+
+ return 0;
+}
+
+static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id,
+ struct rtl8367_extif_config *cfg)
+{
+ enum rtl8367_extif_mode mode;
+ int err;
+
+ mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+ err = rtl8367b_extif_set_mode(smi, id, mode);
+ if (err)
+ return err;
+
+ if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+ err = rtl8367b_extif_set_force(smi, id, &cfg->ability);
+ if (err)
+ return err;
+
+ err = rtl8367b_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+ cfg->rxdelay);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+ const char *name)
+{
+ struct rtl8367_extif_config *cfg;
+ const __be32 *prop;
+ int size;
+ int err;
+
+ prop = of_get_property(smi->parent->of_node, name, &size);
+ if (!prop)
+ return rtl8367b_extif_init(smi, id, NULL);
+
+ if (size != (9 * sizeof(*prop))) {
+ dev_err(smi->parent, "%s property is invalid\n", name);
+ return -EINVAL;
+ }
+
+ cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+ if (!cfg)
+ return -ENOMEM;
+
+ cfg->txdelay = be32_to_cpup(prop++);
+ cfg->rxdelay = be32_to_cpup(prop++);
+ cfg->mode = be32_to_cpup(prop++);
+ cfg->ability.force_mode = be32_to_cpup(prop++);
+ cfg->ability.txpause = be32_to_cpup(prop++);
+ cfg->ability.rxpause = be32_to_cpup(prop++);
+ cfg->ability.link = be32_to_cpup(prop++);
+ cfg->ability.duplex = be32_to_cpup(prop++);
+ cfg->ability.speed = be32_to_cpup(prop++);
+
+ err = rtl8367b_extif_init(smi, id, cfg);
+ kfree(cfg);
+
+ return err;
+}
+#else
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+ const char *name)
+{
+ return -EINVAL;
+}
+#endif
+
+static int rtl8367b_setup(struct rtl8366_smi *smi)
+{
+ struct rtl8367_platform_data *pdata;
+ int err;
+ int i;
+
+ pdata = smi->parent->platform_data;
+
+ err = rtl8367b_init_regs(smi);
+ if (err)
+ return err;
+
+ /* initialize external interfaces */
+ if (smi->parent->of_node) {
+ err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0");
+ if (err)
+ return err;
+
+ err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1");
+ if (err)
+ return err;
+ } else {
+ err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg);
+ if (err)
+ return err;
+
+ err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg);
+ if (err)
+ return err;
+ }
+
+ /* set maximum packet length to 1536 bytes */
+ REG_RMW(smi, RTL8367B_SWC0_REG, RTL8367B_SWC0_MAX_LENGTH_MASK,
+ RTL8367B_SWC0_MAX_LENGTH_1536);
+
+ /*
+ * discard VLAN tagged packets if the port is not a member of
+ * the VLAN with which the packets is associated.
+ */
+ REG_WR(smi, RTL8367B_VLAN_INGRESS_REG, RTL8367B_PORTS_ALL);
+
+ /*
+ * Setup egress tag mode for each port.
+ */
+ for (i = 0; i < RTL8367B_NUM_PORTS; i++)
+ REG_RMW(smi,
+ RTL8367B_PORT_MISC_CFG_REG(i),
+ RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK <<
+ RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT,
+ RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL <<
+ RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT);
+
+ return 0;
+}
+
+static int rtl8367b_get_mib_counter(struct rtl8366_smi *smi, int counter,
+ int port, unsigned long long *val)
+{
+ struct rtl8366_mib_counter *mib;
+ int offset;
+ int i;
+ int err;
+ u32 addr, data;
+ u64 mibvalue;
+
+ if (port > RTL8367B_NUM_PORTS ||
+ counter >= RTL8367B_NUM_MIB_COUNTERS)
+ return -EINVAL;
+
+ mib = &rtl8367b_mib_counters[counter];
+ addr = RTL8367B_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+ /*
+ * Writing access counter address first
+ * then ASIC will prepare 64bits counter wait for being retrived
+ */
+ REG_WR(smi, RTL8367B_MIB_ADDRESS_REG, addr >> 2);
+
+ /* read MIB control register */
+ REG_RD(smi, RTL8367B_MIB_CTRL0_REG(0), &data);
+
+ if (data & RTL8367B_MIB_CTRL0_BUSY_MASK)
+ return -EBUSY;
+
+ if (data & RTL8367B_MIB_CTRL0_RESET_MASK)
+ return -EIO;
+
+ if (mib->length == 4)
+ offset = 3;
+ else
+ offset = (mib->offset + 1) % 4;
+
+ mibvalue = 0;
+ for (i = 0; i < mib->length; i++) {
+ REG_RD(smi, RTL8367B_MIB_COUNTER_REG(offset - i), &data);
+ mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+ }
+
+ *val = mibvalue;
+ return 0;
+}
+
+static int rtl8367b_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+ struct rtl8366_vlan_4k *vlan4k)
+{
+ u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+ int err;
+ int i;
+
+ memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+ if (vid >= RTL8367B_NUM_VIDS)
+ return -EINVAL;
+
+ /* write VID */
+ REG_WR(smi, RTL8367B_TA_ADDR_REG, vid);
+
+ /* write table access control word */
+ REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_READ);
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ REG_RD(smi, RTL8367B_TA_RDDATA_REG(i), &data[i]);
+
+ vlan4k->vid = vid;
+ vlan4k->member = (data[0] >> RTL8367B_TA_VLAN0_MEMBER_SHIFT) &
+ RTL8367B_TA_VLAN0_MEMBER_MASK;
+ vlan4k->untag = (data[0] >> RTL8367B_TA_VLAN0_UNTAG_SHIFT) &
+ RTL8367B_TA_VLAN0_UNTAG_MASK;
+ vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
+ RTL8367B_TA_VLAN1_FID_MASK;
+
+ return 0;
+}
+
+static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
+ const struct rtl8366_vlan_4k *vlan4k)
+{
+ u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+ int err;
+ int i;
+
+ if (vlan4k->vid >= RTL8367B_NUM_VIDS ||
+ vlan4k->member > RTL8367B_TA_VLAN0_MEMBER_MASK ||
+ vlan4k->untag > RTL8367B_UNTAG_MASK ||
+ vlan4k->fid > RTL8367B_FIDMAX)
+ return -EINVAL;
+
+ memset(data, 0, sizeof(data));
+
+ data[0] = (vlan4k->member & RTL8367B_TA_VLAN0_MEMBER_MASK) <<
+ RTL8367B_TA_VLAN0_MEMBER_SHIFT;
+ data[0] |= (vlan4k->untag & RTL8367B_TA_VLAN0_UNTAG_MASK) <<
+ RTL8367B_TA_VLAN0_UNTAG_SHIFT;
+ data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
+ RTL8367B_TA_VLAN1_FID_SHIFT;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ REG_WR(smi, RTL8367B_TA_WRDATA_REG(i), data[i]);
+
+ /* write VID */
+ REG_WR(smi, RTL8367B_TA_ADDR_REG,
+ vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
+
+ /* write table access control word */
+ REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_WRITE);
+
+ return 0;
+}
+
+static int rtl8367b_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+ struct rtl8366_vlan_mc *vlanmc)
+{
+ u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+ int err;
+ int i;
+
+ memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+ if (index >= RTL8367B_NUM_VLANS)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ REG_RD(smi, RTL8367B_VLAN_MC_BASE(index) + i, &data[i]);
+
+ vlanmc->member = (data[0] >> RTL8367B_VLAN_MC0_MEMBER_SHIFT) &
+ RTL8367B_VLAN_MC0_MEMBER_MASK;
+ vlanmc->fid = (data[1] >> RTL8367B_VLAN_MC1_FID_SHIFT) &
+ RTL8367B_VLAN_MC1_FID_MASK;
+ vlanmc->vid = (data[3] >> RTL8367B_VLAN_MC3_EVID_SHIFT) &
+ RTL8367B_VLAN_MC3_EVID_MASK;
+
+ return 0;
+}
+
+static int rtl8367b_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+ const struct rtl8366_vlan_mc *vlanmc)
+{
+ u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+ int err;
+ int i;
+
+ if (index >= RTL8367B_NUM_VLANS ||
+ vlanmc->vid >= RTL8367B_NUM_VIDS ||
+ vlanmc->priority > RTL8367B_PRIORITYMAX ||
+ vlanmc->member > RTL8367B_VLAN_MC0_MEMBER_MASK ||
+ vlanmc->untag > RTL8367B_UNTAG_MASK ||
+ vlanmc->fid > RTL8367B_FIDMAX)
+ return -EINVAL;
+
+ data[0] = (vlanmc->member & RTL8367B_VLAN_MC0_MEMBER_MASK) <<
+ RTL8367B_VLAN_MC0_MEMBER_SHIFT;
+ data[1] = (vlanmc->fid & RTL8367B_VLAN_MC1_FID_MASK) <<
+ RTL8367B_VLAN_MC1_FID_SHIFT;
+ data[2] = 0;
+ data[3] = (vlanmc->vid & RTL8367B_VLAN_MC3_EVID_MASK) <<
+ RTL8367B_VLAN_MC3_EVID_SHIFT;
+
+ for (i = 0; i < ARRAY_SIZE(data); i++)
+ REG_WR(smi, RTL8367B_VLAN_MC_BASE(index) + i, data[i]);
+
+ return 0;
+}
+
+static int rtl8367b_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+ u32 data;
+ int err;
+
+ if (port >= RTL8367B_NUM_PORTS)
+ return -EINVAL;
+
+ REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
+
+ *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
+ RTL8367B_VLAN_PVID_CTRL_MASK;
+
+ return 0;
+}
+
+static int rtl8367b_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+ if (port >= RTL8367B_NUM_PORTS || index >= RTL8367B_NUM_VLANS)
+ return -EINVAL;
+
+ return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
+ RTL8367B_VLAN_PVID_CTRL_MASK <<
+ RTL8367B_VLAN_PVID_CTRL_SHIFT(port),
+ (index & RTL8367B_VLAN_PVID_CTRL_MASK) <<
+ RTL8367B_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367b_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+ return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_CTRL_REG,
+ RTL8367B_VLAN_CTRL_ENABLE,
+ (enable) ? RTL8367B_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367b_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+ return 0;
+}
+
+static int rtl8367b_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+ unsigned max = RTL8367B_NUM_VLANS;
+
+ if (smi->vlan4k_enabled)
+ max = RTL8367B_NUM_VIDS - 1;
+
+ if (vlan == 0 || vlan >= max)
+ return 0;
+
+ return 1;
+}
+
+static int rtl8367b_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+ int err;
+
+ REG_WR(smi, RTL8367B_PORT_ISOLATION_REG(port),
+ (enable) ? RTL8367B_PORTS_ALL : 0);
+
+ return 0;
+}
+
+static int rtl8367b_sw_reset_mibs(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+ return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(0), 0,
+ RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
+ int port,
+ struct switch_port_link *link)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ u32 data = 0;
+ u32 speed;
+
+ if (port >= RTL8367B_NUM_PORTS)
+ return -EINVAL;
+
+ rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
+
+ link->link = !!(data & RTL8367B_PORT_STATUS_LINK);
+ if (!link->link)
+ return 0;
+
+ link->duplex = !!(data & RTL8367B_PORT_STATUS_DUPLEX);
+ link->rx_flow = !!(data & RTL8367B_PORT_STATUS_RXPAUSE);
+ link->tx_flow = !!(data & RTL8367B_PORT_STATUS_TXPAUSE);
+ link->aneg = !!(data & RTL8367B_PORT_STATUS_NWAY);
+
+ speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
+ switch (speed) {
+ case 0:
+ link->speed = SWITCH_PORT_SPEED_10;
+ break;
+ case 1:
+ link->speed = SWITCH_PORT_SPEED_100;
+ break;
+ case 2:
+ link->speed = SWITCH_PORT_SPEED_1000;
+ break;
+ default:
+ link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+ break;
+ }
+
+ return 0;
+}
+
+static int rtl8367b_sw_get_max_length(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ u32 data;
+
+ rtl8366_smi_read_reg(smi, RTL8367B_SWC0_REG, &data);
+ val->value.i = (data & RTL8367B_SWC0_MAX_LENGTH_MASK) >>
+ RTL8367B_SWC0_MAX_LENGTH_SHIFT;
+
+ return 0;
+}
+
+static int rtl8367b_sw_set_max_length(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ u32 max_len;
+
+ switch (val->value.i) {
+ case 0:
+ max_len = RTL8367B_SWC0_MAX_LENGTH_1522;
+ break;
+ case 1:
+ max_len = RTL8367B_SWC0_MAX_LENGTH_1536;
+ break;
+ case 2:
+ max_len = RTL8367B_SWC0_MAX_LENGTH_1552;
+ break;
+ case 3:
+ max_len = RTL8367B_SWC0_MAX_LENGTH_16000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rtl8366_smi_rmwr(smi, RTL8367B_SWC0_REG,
+ RTL8367B_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367b_sw_reset_port_mibs(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+ int port;
+
+ port = val->port_vlan;
+ if (port >= RTL8367B_NUM_PORTS)
+ return -EINVAL;
+
+ return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(port / 8), 0,
+ RTL8367B_MIB_CTRL0_PORT_RESET_MASK(port % 8));
+}
+
+static struct switch_attr rtl8367b_globals[] = {
+ {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan",
+ .description = "Enable VLAN mode",
+ .set = rtl8366_sw_set_vlan_enable,
+ .get = rtl8366_sw_get_vlan_enable,
+ .max = 1,
+ .ofs = 1
+ }, {
+ .type = SWITCH_TYPE_INT,
+ .name = "enable_vlan4k",
+ .description = "Enable VLAN 4K mode",
+ .set = rtl8366_sw_set_vlan_enable,
+ .get = rtl8366_sw_get_vlan_enable,
+ .max = 1,
+ .ofs = 2
+ }, {
+ .type = SWITCH_TYPE_NOVAL,
+ .name = "reset_mibs",
+ .description = "Reset all MIB counters",
+ .set = rtl8367b_sw_reset_mibs,
+ }, {
+ .type = SWITCH_TYPE_INT,
+ .name = "max_length",
+ .description = "Get/Set the maximum length of valid packets"
+ "(0:1522, 1:1536, 2:1552, 3:16000)",
+ .set = rtl8367b_sw_set_max_length,
+ .get = rtl8367b_sw_get_max_length,
+ .max = 3,
+ }
+};
+
+static struct switch_attr rtl8367b_port[] = {
+ {
+ .type = SWITCH_TYPE_NOVAL,
+ .name = "reset_mib",
+ .description = "Reset single port MIB counters",
+ .set = rtl8367b_sw_reset_port_mibs,
+ }, {
+ .type = SWITCH_TYPE_STRING,
+ .name = "mib",
+ .description = "Get MIB counters for port",
+ .max = 33,
+ .set = NULL,
+ .get = rtl8366_sw_get_port_mib,
+ },
+};
+
+static struct switch_attr rtl8367b_vlan[] = {
+ {
+ .type = SWITCH_TYPE_STRING,
+ .name = "info",
+ .description = "Get vlan information",
+ .max = 1,
+ .set = NULL,
+ .get = rtl8366_sw_get_vlan_info,
+ },
+};
+
+static const struct switch_dev_ops rtl8367b_sw_ops = {
+ .attr_global = {
+ .attr = rtl8367b_globals,
+ .n_attr = ARRAY_SIZE(rtl8367b_globals),
+ },
+ .attr_port = {
+ .attr = rtl8367b_port,
+ .n_attr = ARRAY_SIZE(rtl8367b_port),
+ },
+ .attr_vlan = {
+ .attr = rtl8367b_vlan,
+ .n_attr = ARRAY_SIZE(rtl8367b_vlan),
+ },
+
+ .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+ .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+ .get_port_pvid = rtl8366_sw_get_port_pvid,
+ .set_port_pvid = rtl8366_sw_set_port_pvid,
+ .reset_switch = rtl8366_sw_reset_switch,
+ .get_port_link = rtl8367b_sw_get_port_link,
+};
+
+static int rtl8367b_switch_init(struct rtl8366_smi *smi)
+{
+ struct switch_dev *dev = &smi->sw_dev;
+ int err;
+
+ dev->name = "RTL8367B";
+ dev->cpu_port = RTL8367B_CPU_PORT_NUM;
+ dev->ports = RTL8367B_NUM_PORTS;
+ dev->vlans = RTL8367B_NUM_VIDS;
+ dev->ops = &rtl8367b_sw_ops;
+ dev->alias = dev_name(smi->parent);
+
+ err = register_switch(dev, NULL);
+ if (err)
+ dev_err(smi->parent, "switch registration failed\n");
+
+ return err;
+}
+
+static void rtl8367b_switch_cleanup(struct rtl8366_smi *smi)
+{
+ unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367b_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+ struct rtl8366_smi *smi = bus->priv;
+ u32 val = 0;
+ int err;
+
+ err = rtl8367b_read_phy_reg(smi, addr, reg, &val);
+ if (err)
+ return 0xffff;
+
+ return val;
+}
+
+static int rtl8367b_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+ struct rtl8366_smi *smi = bus->priv;
+ u32 t;
+ int err;
+
+ err = rtl8367b_write_phy_reg(smi, addr, reg, val);
+ if (err)
+ return err;
+
+ /* flush write */
+ (void) rtl8367b_read_phy_reg(smi, addr, reg, &t);
+
+ return err;
+}
+
+static int rtl8367b_detect(struct rtl8366_smi *smi)
+{
+ const char *chip_name;
+ u32 chip_num;
+ u32 chip_ver;
+ u32 chip_mode;
+ int ret;
+
+ /* TODO: improve chip detection */
+ rtl8366_smi_write_reg(smi, RTL8367B_RTL_MAGIC_ID_REG,
+ RTL8367B_RTL_MAGIC_ID_VAL);
+
+ ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_NUMBER_REG, &chip_num);
+ if (ret) {
+ dev_err(smi->parent, "unable to read %s register\n",
+ "chip number");
+ return ret;
+ }
+
+ ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+ if (ret) {
+ dev_err(smi->parent, "unable to read %s register\n",
+ "chip version");
+ return ret;
+ }
+
+ ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_MODE_REG, &chip_mode);
+ if (ret) {
+ dev_err(smi->parent, "unable to read %s register\n",
+ "chip mode");
+ return ret;
+ }
+
+ switch (chip_ver) {
+ case 0x1000:
+ chip_name = "8367RB";
+ break;
+ case 0x1010:
+ chip_name = "8367R-VB";
+ break;
+ default:
+ dev_err(smi->parent,
+ "unknown chip num:%04x ver:%04x, mode:%04x\n",
+ chip_num, chip_ver, chip_mode);
+ return -ENODEV;
+ }
+
+ dev_info(smi->parent, "RTL%s chip found\n", chip_name);
+
+ return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367b_smi_ops = {
+ .detect = rtl8367b_detect,
+ .reset_chip = rtl8367b_reset_chip,
+ .setup = rtl8367b_setup,
+
+ .mii_read = rtl8367b_mii_read,
+ .mii_write = rtl8367b_mii_write,
+
+ .get_vlan_mc = rtl8367b_get_vlan_mc,
+ .set_vlan_mc = rtl8367b_set_vlan_mc,
+ .get_vlan_4k = rtl8367b_get_vlan_4k,
+ .set_vlan_4k = rtl8367b_set_vlan_4k,
+ .get_mc_index = rtl8367b_get_mc_index,
+ .set_mc_index = rtl8367b_set_mc_index,
+ .get_mib_counter = rtl8367b_get_mib_counter,
+ .is_vlan_valid = rtl8367b_is_vlan_valid,
+ .enable_vlan = rtl8367b_enable_vlan,
+ .enable_vlan4k = rtl8367b_enable_vlan4k,
+ .enable_port = rtl8367b_enable_port,
+};
+
+static int rtl8367b_probe(struct platform_device *pdev)
+{
+ struct rtl8366_smi *smi;
+ int err;
+
+ smi = rtl8366_smi_probe(pdev);
+ if (!smi)
+ return -ENODEV;
+
+ smi->clk_delay = 1500;
+ smi->cmd_read = 0xb9;
+ smi->cmd_write = 0xb8;
+ smi->ops = &rtl8367b_smi_ops;
+ smi->cpu_port = RTL8367B_CPU_PORT_NUM;
+ smi->num_ports = RTL8367B_NUM_PORTS;
+ smi->num_vlan_mc = RTL8367B_NUM_VLANS;
+ smi->mib_counters = rtl8367b_mib_counters;
+ smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters);
+
+ err = rtl8366_smi_init(smi);
+ if (err)
+ goto err_free_smi;
+
+ platform_set_drvdata(pdev, smi);
+
+ err = rtl8367b_switch_init(smi);
+ if (err)
+ goto err_clear_drvdata;
+
+ return 0;
+
+ err_clear_drvdata:
+ platform_set_drvdata(pdev, NULL);
+ rtl8366_smi_cleanup(smi);
+ err_free_smi:
+ kfree(smi);
+ return err;
+}
+
+static int rtl8367b_remove(struct platform_device *pdev)
+{
+ struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+ if (smi) {
+ rtl8367b_switch_cleanup(smi);
+ platform_set_drvdata(pdev, NULL);
+ rtl8366_smi_cleanup(smi);
+ kfree(smi);
+ }
+
+ return 0;
+}
+
+static void rtl8367b_shutdown(struct platform_device *pdev)
+{
+ struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+ if (smi)
+ rtl8367b_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367b_match[] = {
+ { .compatible = "realtek,rtl8367b" },
+ { .compatible = "rtl8367b" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367b_match);
+#endif
+
+static struct platform_driver rtl8367b_driver = {
+ .driver = {
+ .name = RTL8367B_DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(rtl8367b_match),
+#endif
+ },
+ .probe = rtl8367b_probe,
+ .remove = rtl8367b_remove,
+ .shutdown = rtl8367b_shutdown,
+};
+
+module_platform_driver(rtl8367b_driver);
+
+MODULE_DESCRIPTION(RTL8367B_DRIVER_DESC);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367B_DRIVER_NAME);
+
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c
index e772c9448..4e6d7136f 100644
--- a/target/linux/generic/files/drivers/net/phy/swconfig.c
+++ b/target/linux/generic/files/drivers/net/phy/swconfig.c
@@ -23,6 +23,7 @@
#include <linux/capability.h>
#include <linux/skbuff.h>
#include <linux/switch.h>
+#include <linux/of.h>
//#define DEBUG 1
#ifdef DEBUG
@@ -376,17 +377,21 @@ swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
int id = cb->args[0];
void *hdr;
- hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam,
+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
if (IS_ERR(hdr))
return -1;
- NLA_PUT_U32(msg, SWITCH_ATTR_OP_ID, id);
- NLA_PUT_U32(msg, SWITCH_ATTR_OP_TYPE, op->type);
- NLA_PUT_STRING(msg, SWITCH_ATTR_OP_NAME, op->name);
+ if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type))
+ goto nla_put_failure;
+ if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name))
+ goto nla_put_failure;
if (op->description)
- NLA_PUT_STRING(msg, SWITCH_ATTR_OP_DESCRIPTION,
- op->description);
+ if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION,
+ op->description))
+ goto nla_put_failure;
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -711,9 +716,12 @@ swconfig_send_port(struct swconfig_callback *cb, void *arg)
if (!p)
goto error;
- NLA_PUT_U32(cb->msg, SWITCH_PORT_ID, port->id);
- if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
- NLA_PUT_FLAG(cb->msg, SWITCH_PORT_FLAG_TAGGED);
+ if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id))
+ goto nla_put_failure;
+ if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+ if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED))
+ goto nla_put_failure;
+ }
nla_nest_end(cb->msg, p);
return 0;
@@ -791,17 +799,19 @@ swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
if (!msg)
goto error;
- hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, &switch_fam,
+ hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
0, cmd);
if (IS_ERR(hdr))
goto nla_put_failure;
switch(attr->type) {
case SWITCH_TYPE_INT:
- NLA_PUT_U32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i);
+ if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i))
+ goto nla_put_failure;
break;
case SWITCH_TYPE_STRING:
- NLA_PUT_STRING(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s);
+ if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s))
+ goto nla_put_failure;
break;
case SWITCH_TYPE_PORTS:
err = swconfig_send_ports(&msg, info,
@@ -835,21 +845,46 @@ static int
swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
const struct switch_dev *dev)
{
+ struct nlattr *p = NULL, *m = NULL;
void *hdr;
+ int i;
hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
SWITCH_CMD_NEW_ATTR);
if (IS_ERR(hdr))
return -1;
- NLA_PUT_U32(msg, SWITCH_ATTR_ID, dev->id);
- NLA_PUT_STRING(msg, SWITCH_ATTR_DEV_NAME, dev->devname);
- NLA_PUT_STRING(msg, SWITCH_ATTR_ALIAS, dev->alias);
- NLA_PUT_STRING(msg, SWITCH_ATTR_NAME, dev->name);
- NLA_PUT_U32(msg, SWITCH_ATTR_VLANS, dev->vlans);
- NLA_PUT_U32(msg, SWITCH_ATTR_PORTS, dev->ports);
- NLA_PUT_U32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port);
+ if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id))
+ goto nla_put_failure;
+ if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname))
+ goto nla_put_failure;
+ if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias))
+ goto nla_put_failure;
+ if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port))
+ goto nla_put_failure;
+ m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP);
+ if (!m)
+ goto nla_put_failure;
+ for (i = 0; i < dev->ports; i++) {
+ p = nla_nest_start(msg, SWITCH_ATTR_PORTS);
+ if (!p)
+ continue;
+ if (dev->portmap[i].s) {
+ if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT, dev->portmap[i].s))
+ goto nla_put_failure;
+ if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT, dev->portmap[i].virt))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, p);
+ }
+ nla_nest_end(msg, m);
return genlmsg_end(msg, hdr);
nla_put_failure:
genlmsg_cancel(msg, hdr);
@@ -867,7 +902,7 @@ static int swconfig_dump_switches(struct sk_buff *skb,
list_for_each_entry(dev, &swdevs, dev_list) {
if (++idx <= start)
continue;
- if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).pid,
+ if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, NLM_F_MULTI,
dev) < 0)
break;
@@ -938,6 +973,49 @@ static struct genl_ops swconfig_ops[] = {
}
};
+#ifdef CONFIG_OF
+void
+of_switch_load_portmap(struct switch_dev *dev)
+{
+ struct device_node *port;
+
+ if (!dev->of_node)
+ return;
+
+ for_each_child_of_node(dev->of_node, port) {
+ const __be32 *prop;
+ const char *segment;
+ int size, phys;
+
+ if (!of_device_is_compatible(port, "swconfig,port"))
+ continue;
+
+ if (of_property_read_string(port, "swconfig,segment", &segment))
+ continue;
+
+ prop = of_get_property(port, "swconfig,portmap", &size);
+ if (!prop)
+ continue;
+
+ if (size != (2 * sizeof(*prop))) {
+ pr_err("%s: failed to parse port mapping\n", port->name);
+ continue;
+ }
+
+ phys = be32_to_cpup(prop++);
+ if ((phys < 0) | (phys >= dev->ports)) {
+ pr_err("%s: physical port index out of range\n", port->name);
+ continue;
+ }
+
+ dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL);
+ dev->portmap[phys].virt = be32_to_cpup(prop);
+ pr_debug("Found port: %s, physical: %d, virtual: %d\n",
+ segment, phys, dev->portmap[phys].virt);
+ }
+}
+#endif
+
int
register_switch(struct switch_dev *dev, struct net_device *netdev)
{
@@ -960,6 +1038,12 @@ register_switch(struct switch_dev *dev, struct net_device *netdev)
GFP_KERNEL);
if (!dev->portbuf)
return -ENOMEM;
+ dev->portmap = kzalloc(sizeof(struct switch_portmap) * dev->ports,
+ GFP_KERNEL);
+ if (!dev->portmap) {
+ kfree(dev->portbuf);
+ return -ENOMEM;
+ }
}
swconfig_defaults_init(dev);
mutex_init(&dev->sw_mutex);
@@ -981,6 +1065,11 @@ register_switch(struct switch_dev *dev, struct net_device *netdev)
return -ENFILE;
}
+#ifdef CONFIG_OF
+ if (dev->ports)
+ of_switch_load_portmap(dev);
+#endif
+
/* fill device name */
snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
index 6f54cc15b..2fc5d554d 100644
--- a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
+++ b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
@@ -46,7 +46,7 @@ static void
swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
enum led_brightness brightness)
{
- led_brightness_set(trig_data->led_cdev, brightness);
+ led_set_brightness(trig_data->led_cdev, brightness);
trig_data->prev_brightness = brightness;
}
@@ -209,7 +209,7 @@ swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
link = !!(sw_trig->port_link & port_mask);
if (!link) {
if (link != trig_data->prev_link)
- led_brightness_set(trig_data->led_cdev, LED_OFF);
+ led_set_brightness(trig_data->led_cdev, LED_OFF);
} else {
unsigned long traffic;
int i;