X-Git-Url: http://git.openwrt.org/?p=openwrt%2Fopenwrt.git;a=blobdiff_plain;f=target%2Flinux%2Fgeneric%2Ffiles%2Fdrivers%2Fnet%2Fphy%2Far8216.c;h=a2c42d18e6c3570ff39132cb7e1299d52c391c53;hp=14627c1c7906148c4d149933a0f4be92f91fcb10;hb=47eef3a5ce981c6754836c8c996b4cc724617604;hpb=45a494b808e2b4778346146acf6dc65c6fad21e9 diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index 14627c1c79..a2c42d18e6 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -1,7 +1,7 @@ /* * ar8216.c: AR8216 switch driver * - * Copyright (C) 2009 Felix Fietkau + * Copyright (C) 2009 Felix Fietkau * Copyright (C) 2011-2012 Gabor Juhos * * This program is free software; you can redistribute it and/or @@ -23,165 +23,27 @@ #include #include #include +#include +#include +#include #include #include #include #include #include -#include #include #include #include #include -#include -#include -#include #include #include "ar8216.h" -/* size of the vlan table */ -#define AR8X16_MAX_VLANS 128 -#define AR8X16_PROBE_RETRIES 10 -#define AR8X16_MAX_PORTS 8 +extern const struct ar8xxx_chip ar8327_chip; +extern const struct ar8xxx_chip ar8337_chip; #define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */ -struct ar8xxx_priv; - -#define AR8XXX_CAP_GIGE BIT(0) -#define AR8XXX_CAP_MIB_COUNTERS BIT(1) - -#define AR8XXX_NUM_PHYS 5 - -enum { - AR8XXX_VER_AR8216 = 0x01, - AR8XXX_VER_AR8236 = 0x03, - AR8XXX_VER_AR8316 = 0x10, - AR8XXX_VER_AR8327 = 0x12, - AR8XXX_VER_AR8337 = 0x13, -}; - -struct ar8xxx_mib_desc { - unsigned int size; - unsigned int offset; - const char *name; -}; - -struct ar8xxx_chip { - unsigned long caps; - bool config_at_probe; - bool mii_lo_first; - - /* parameters to calculate REG_PORT_STATS_BASE */ - unsigned reg_port_stats_start; - unsigned reg_port_stats_length; - - int (*hw_init)(struct ar8xxx_priv *priv); - void (*cleanup)(struct ar8xxx_priv *priv); - - const char *name; - int vlans; - int ports; - const struct switch_dev_ops *swops; - - 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 members); - 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); - void (*phy_fixup)(struct ar8xxx_priv *priv, int phy); - void (*set_mirror_regs)(struct ar8xxx_priv *priv); - - const struct ar8xxx_mib_desc *mib_decs; - unsigned num_mibs; - unsigned mib_func; -}; - -enum ar8327_led_pattern { - AR8327_LED_PATTERN_OFF = 0, - AR8327_LED_PATTERN_BLINK, - AR8327_LED_PATTERN_ON, - AR8327_LED_PATTERN_RULE, -}; - -struct ar8327_led_entry { - unsigned reg; - unsigned shift; -}; - -struct ar8327_led { - struct led_classdev cdev; - struct ar8xxx_priv *sw_priv; - - char *name; - bool active_low; - u8 led_num; - enum ar8327_led_mode mode; - - struct mutex mutex; - spinlock_t lock; - struct work_struct led_work; - bool enable_hw_mode; - enum ar8327_led_pattern pattern; -}; - -struct ar8327_data { - u32 port0_status; - u32 port6_status; - - struct ar8327_led **leds; - unsigned int num_leds; -}; - -struct ar8xxx_priv { - struct switch_dev dev; - struct mii_bus *mii_bus; - struct phy_device *phy; - - u32 (*read)(struct ar8xxx_priv *priv, int reg); - void (*write)(struct ar8xxx_priv *priv, int reg, u32 val); - u32 (*rmw)(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val); - - int (*get_port_link)(unsigned port); - - 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; - void *chip_data; - bool initialized; - bool port4_phy; - char buf[2048]; - - bool init; - - 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 MIB_DESC(_s , _o, _n) \ { \ .size = (_s), \ @@ -229,7 +91,7 @@ static const struct ar8xxx_mib_desc ar8216_mibs[] = { MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"), }; -static const struct ar8xxx_mib_desc ar8236_mibs[] = { +const struct ar8xxx_mib_desc ar8236_mibs[39] = { MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"), MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"), MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"), @@ -274,60 +136,6 @@ static const struct ar8xxx_mib_desc ar8236_mibs[] = { 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 ar8xxx_has_mib_counters(struct ar8xxx_priv *priv) -{ - return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS; -} - -static inline bool chip_is_ar8216(struct ar8xxx_priv *priv) -{ - return priv->chip_ver == AR8XXX_VER_AR8216; -} - -static inline bool chip_is_ar8236(struct ar8xxx_priv *priv) -{ - return priv->chip_ver == AR8XXX_VER_AR8236; -} - -static inline bool chip_is_ar8316(struct ar8xxx_priv *priv) -{ - return priv->chip_ver == AR8XXX_VER_AR8316; -} - -static inline bool chip_is_ar8327(struct ar8xxx_priv *priv) -{ - return priv->chip_ver == AR8XXX_VER_AR8327; -} - -static inline bool chip_is_ar8337(struct ar8xxx_priv *priv) -{ - return priv->chip_ver == AR8XXX_VER_AR8337; -} - -static inline void -split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page) -{ - regaddr >>= 1; - *r1 = regaddr & 0x1e; - - regaddr >>= 5; - *r2 = regaddr & 0x7; - - regaddr >>= 3; - *page = regaddr & 0x1ff; -} - /* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */ static int ar8xxx_phy_poll_reset(struct mii_bus *bus) @@ -371,18 +179,18 @@ ar8xxx_phy_check_aneg(struct phy_device *phydev) if (ret & BMCR_ANENABLE) return 0; - dev_info(&phydev->dev, "ANEG disabled, re-enabling ...\n"); + dev_info(&phydev->mdio.dev, "ANEG disabled, re-enabling ...\n"); ret |= BMCR_ANENABLE | BMCR_ANRESTART; return phy_write(phydev, MII_BMCR, ret); } -static void +void ar8xxx_phy_init(struct ar8xxx_priv *priv) { int i; struct mii_bus *bus; - bus = priv->mii_bus; + bus = priv->sw_mii_bus ?: priv->mii_bus; for (i = 0; i < AR8XXX_NUM_PHYS; i++) { if (priv->chip->phy_fixup) priv->chip->phy_fixup(priv, i); @@ -398,8 +206,8 @@ ar8xxx_phy_init(struct ar8xxx_priv *priv) ar8xxx_phy_poll_reset(bus); } -static u32 -mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum) +u32 +ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum) { struct mii_bus *bus = priv->mii_bus; u16 lo, hi; @@ -410,8 +218,8 @@ mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum) return (hi << 16) | lo; } -static void -mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val) +void +ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val) { struct mii_bus *bus = priv->mii_bus; u16 lo, hi; @@ -429,8 +237,8 @@ mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val) } } -static u32 -ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg) +u32 +ar8xxx_read(struct ar8xxx_priv *priv, int reg) { struct mii_bus *bus = priv->mii_bus; u16 r1, r2, page; @@ -441,16 +249,16 @@ ar8xxx_mii_read(struct ar8xxx_priv *priv, int reg) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - usleep_range(1000, 2000); /* wait for the page switch to propagate */ - val = mii_read32(priv, 0x10 | r2, r1); + wait_for_page_switch(); + val = ar8xxx_mii_read32(priv, 0x10 | r2, r1); mutex_unlock(&bus->mdio_lock); return val; } -static void -ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val) +void +ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val) { struct mii_bus *bus = priv->mii_bus; u16 r1, r2, page; @@ -460,14 +268,14 @@ ar8xxx_mii_write(struct ar8xxx_priv *priv, int reg, u32 val) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - usleep_range(1000, 2000); /* wait for the page switch to propagate */ - mii_write32(priv, 0x10 | r2, r1, val); + wait_for_page_switch(); + ar8xxx_mii_write32(priv, 0x10 | r2, r1, val); mutex_unlock(&bus->mdio_lock); } -static u32 -ar8xxx_mii_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) +u32 +ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) { struct mii_bus *bus = priv->mii_bus; u16 r1, r2, page; @@ -478,19 +286,30 @@ ar8xxx_mii_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) mutex_lock(&bus->mdio_lock); bus->write(bus, 0x18, 0, page); - usleep_range(1000, 2000); /* wait for the page switch to propagate */ + wait_for_page_switch(); - ret = mii_read32(priv, 0x10 | r2, r1); + ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1); ret &= ~mask; ret |= val; - mii_write32(priv, 0x10 | r2, r1, ret); + ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret); mutex_unlock(&bus->mdio_lock); return ret; } +void +ar8xxx_phy_dbg_read(struct ar8xxx_priv *priv, int phy_addr, + u16 dbg_addr, u16 *dbg_data) +{ + struct mii_bus *bus = priv->mii_bus; -static void + mutex_lock(&bus->mdio_lock); + bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr); + *dbg_data = bus->read(bus, phy_addr, MII_ATH_DBG_DATA); + mutex_unlock(&bus->mdio_lock); +} + +void ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, u16 dbg_addr, u16 dbg_data) { @@ -502,27 +321,37 @@ ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, mutex_unlock(&bus->mdio_lock); } -static void -ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data) +static inline void +ar8xxx_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg) +{ + bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr); + bus->write(bus, phy_addr, MII_ATH_MMD_DATA, reg); + bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000); +} + +void +ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data) { struct mii_bus *bus = priv->mii_bus; mutex_lock(&bus->mdio_lock); - bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr); + ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg); bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data); mutex_unlock(&bus->mdio_lock); } -static inline u32 -ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) +u16 +ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg) { - return priv->rmw(priv, reg, mask, val); -} + struct mii_bus *bus = priv->mii_bus; + u16 data; -static inline void -ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val) -{ - priv->rmw(priv, reg, 0, val); + mutex_lock(&bus->mdio_lock); + ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg); + data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA); + mutex_unlock(&bus->mdio_lock); + + return data; } static int @@ -534,11 +363,12 @@ ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, for (i = 0; i < timeout; i++) { u32 t; - t = priv->read(priv, reg); + t = ar8xxx_read(priv, reg); if ((t & mask) == val) return 0; usleep_range(1000, 2000); + cond_resched(); } return -ETIMEDOUT; @@ -598,11 +428,11 @@ ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush) u64 t; mib = &priv->chip->mib_decs[i]; - t = priv->read(priv, base + mib->offset); + t = ar8xxx_read(priv, base + mib->offset); if (mib->size == 2) { u64 hi; - hi = priv->read(priv, base + mib->offset + 4); + hi = ar8xxx_read(priv, base + mib->offset + 4); t |= hi << 32; } @@ -610,6 +440,7 @@ ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush) mib_stats[i] = 0; else mib_stats[i] += t; + cond_resched(); } } @@ -646,6 +477,9 @@ ar8216_read_port_link(struct ar8xxx_priv *priv, int port, link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW); link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW); + if (link->aneg && link->duplex && priv->chip->read_port_eee_status) + link->eee = priv->chip->read_port_eee_status(priv, port); + speed = (status & AR8216_PORT_STATUS_SPEED) >> AR8216_PORT_STATUS_SPEED_S; @@ -717,7 +551,7 @@ ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00)) return; - port = buf[0] & 0xf; + port = buf[0] & 0x7; /* no need to fix up packets coming from a tagged source */ if (priv->vlan_tagged & (1 << port)) @@ -731,14 +565,14 @@ ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb) buf[15 + 2] = vlan & 0xff; } -static int +int ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) { int timeout = 20; u32 t = 0; while (1) { - t = priv->read(priv, reg); + t = ar8xxx_read(priv, reg); if ((t & mask) == val) return 0; @@ -746,6 +580,7 @@ ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) break; udelay(10); + cond_resched(); } pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n", @@ -761,10 +596,10 @@ ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) { val &= AR8216_VTUDATA_MEMBER; val |= AR8216_VTUDATA_VALID; - priv->write(priv, AR8216_REG_VTU_DATA, val); + ar8xxx_write(priv, AR8216_REG_VTU_DATA, val); } op |= AR8216_VTU_ACTIVE; - priv->write(priv, AR8216_REG_VTU, op); + ar8xxx_write(priv, AR8216_REG_VTU, op); } static void @@ -787,9 +622,26 @@ ar8216_atu_flush(struct ar8xxx_priv *priv) { int ret; - ret = ar8216_wait_bit(priv, AR8216_REG_ATU, AR8216_ATU_ACTIVE, 0); + ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0); if (!ret) - priv->write(priv, AR8216_REG_ATU, AR8216_ATU_OP_FLUSH); + ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH | + AR8216_ATU_ACTIVE); + + return ret; +} + +static int +ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port) +{ + u32 t; + int ret; + + ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0); + if (!ret) { + t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT; + t |= AR8216_ATU_ACTIVE; + ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t); + } return ret; } @@ -797,11 +649,12 @@ ar8216_atu_flush(struct ar8xxx_priv *priv) static u32 ar8216_read_port_status(struct ar8xxx_priv *priv, int port) { - return priv->read(priv, AR8216_REG_PORT_STATUS(port)); + return ar8xxx_read(priv, AR8216_REG_PORT_STATUS(port)); } static void -ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) +__ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members, + bool ath_hdr_en) { u32 header; u32 egress, ingress; @@ -820,10 +673,7 @@ ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) ingress = AR8216_IN_PORT_ONLY; } - if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU) - header = AR8216_PORT_CTRL_HEADER; - else - header = 0; + header = ath_hdr_en ? AR8216_PORT_CTRL_HEADER : 0; ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE | @@ -841,12 +691,23 @@ ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S)); } +static void +ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members) +{ + return __ar8216_setup_port(priv, port, members, + chip_is_ar8216(priv) && priv->vlan && + port == AR8216_PORT_CPU); +} + static int ar8216_hw_init(struct ar8xxx_priv *priv) { if (priv->initialized) return 0; + ar8xxx_write(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET); + ar8xxx_reg_wait(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET, 0, 1000); + ar8xxx_phy_init(priv); priv->initialized = true; @@ -857,38 +718,299 @@ static void ar8216_init_globals(struct ar8xxx_priv *priv) { /* standard atheros magic */ - priv->write(priv, 0x38, 0xc000050e); + ar8xxx_write(priv, 0x38, 0xc000050e); ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, AR8216_GCTRL_MTU, 1518 + 8 + 2); } static void -ar8216_init_port(struct ar8xxx_priv *priv, int port) +__ar8216_init_port(struct ar8xxx_priv *priv, int port, + bool cpu_ge, bool flow_en) { /* Enable port learning and tx */ - priv->write(priv, AR8216_REG_PORT_CTRL(port), + ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port), AR8216_PORT_CTRL_LEARN | (4 << AR8216_PORT_CTRL_STATE_S)); - priv->write(priv, AR8216_REG_PORT_VLAN(port), 0); + ar8xxx_write(priv, AR8216_REG_PORT_VLAN(port), 0); if (port == AR8216_PORT_CPU) { - priv->write(priv, AR8216_REG_PORT_STATUS(port), + ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port), AR8216_PORT_STATUS_LINK_UP | - (ar8xxx_has_gige(priv) ? - AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | + (cpu_ge ? AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC | - (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) | - (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) | + (flow_en ? AR8216_PORT_STATUS_RXFLOW : 0) | + (flow_en ? AR8216_PORT_STATUS_TXFLOW : 0) | AR8216_PORT_STATUS_DUPLEX); } else { - priv->write(priv, AR8216_REG_PORT_STATUS(port), + ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port), AR8216_PORT_STATUS_LINK_AUTO); } } +static void +ar8216_init_port(struct ar8xxx_priv *priv, int port) +{ + __ar8216_init_port(priv, port, ar8xxx_has_gige(priv), + chip_is_ar8316(priv)); +} + +static void +ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1) +{ + int timeout = 20; + + while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) { + udelay(10); + cond_resched(); + } + + if (!timeout) + pr_err("ar8216: timeout waiting for atu to become ready\n"); +} + +static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, + struct arl_entry *a, u32 *status, enum arl_op op) +{ + struct mii_bus *bus = priv->mii_bus; + u16 r2, page; + u16 r1_func0, r1_func1, r1_func2; + u32 t, val0, val1, val2; + + split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page); + r2 |= 0x10; + + r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e; + r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e; + + switch (op) { + case AR8XXX_ARL_INITIALIZE: + /* all ATU registers are on the same page + * therefore set page only once + */ + bus->write(bus, 0x18, 0, page); + wait_for_page_switch(); + + ar8216_wait_atu_ready(priv, r2, r1_func0); + + ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT); + ar8xxx_mii_write32(priv, r2, r1_func1, 0); + ar8xxx_mii_write32(priv, r2, r1_func2, 0); + break; + case AR8XXX_ARL_GET_NEXT: + t = ar8xxx_mii_read32(priv, r2, r1_func0); + t |= AR8216_ATU_ACTIVE; + ar8xxx_mii_write32(priv, r2, r1_func0, t); + ar8216_wait_atu_ready(priv, r2, r1_func0); + + val0 = ar8xxx_mii_read32(priv, r2, r1_func0); + val1 = ar8xxx_mii_read32(priv, r2, r1_func1); + val2 = ar8xxx_mii_read32(priv, r2, r1_func2); + + *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S; + if (!*status) + break; + + a->portmap = (val2 & AR8216_ATU_PORTS) >> AR8216_ATU_PORTS_S; + a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S; + a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S; + a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S; + a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S; + a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S; + a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S; + break; + } +} + +static int +ar8216_phy_read(struct ar8xxx_priv *priv, int addr, int regnum) +{ + u32 t, val = 0xffff; + int err; + + if (addr >= AR8216_NUM_PORTS) + return 0xffff; + t = (regnum << AR8216_MDIO_CTRL_REG_ADDR_S) | + (addr << AR8216_MDIO_CTRL_PHY_ADDR_S) | + AR8216_MDIO_CTRL_MASTER_EN | + AR8216_MDIO_CTRL_BUSY | + AR8216_MDIO_CTRL_CMD_READ; + + ar8xxx_write(priv, AR8216_REG_MDIO_CTRL, t); + err = ar8xxx_reg_wait(priv, AR8216_REG_MDIO_CTRL, + AR8216_MDIO_CTRL_BUSY, 0, 5); + if (!err) + val = ar8xxx_read(priv, AR8216_REG_MDIO_CTRL); + + return val & AR8216_MDIO_CTRL_DATA_M; +} + +static int +ar8216_phy_write(struct ar8xxx_priv *priv, int addr, int regnum, u16 val) +{ + u32 t; + int ret; + + if (addr >= AR8216_NUM_PORTS) + return -EINVAL; + + t = (addr << AR8216_MDIO_CTRL_PHY_ADDR_S) | + (regnum << AR8216_MDIO_CTRL_REG_ADDR_S) | + AR8216_MDIO_CTRL_MASTER_EN | + AR8216_MDIO_CTRL_BUSY | + AR8216_MDIO_CTRL_CMD_WRITE | + val; + + ar8xxx_write(priv, AR8216_REG_MDIO_CTRL, t); + ret = ar8xxx_reg_wait(priv, AR8216_REG_MDIO_CTRL, + AR8216_MDIO_CTRL_BUSY, 0, 5); + + return ret; +} + +static int +ar8229_hw_init(struct ar8xxx_priv *priv) +{ + int phy_if_mode; + + if (priv->initialized) + return 0; + + ar8xxx_write(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET); + ar8xxx_reg_wait(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET, 0, 1000); + + phy_if_mode = of_get_phy_mode(priv->pdev->of_node); + + if (phy_if_mode == PHY_INTERFACE_MODE_GMII) { + ar8xxx_write(priv, AR8229_REG_OPER_MODE0, + AR8229_OPER_MODE0_MAC_GMII_EN); + } else if (phy_if_mode == PHY_INTERFACE_MODE_MII) { + ar8xxx_write(priv, AR8229_REG_OPER_MODE0, + AR8229_OPER_MODE0_PHY_MII_EN); + } else { + pr_err("ar8229: unsupported mii mode\n"); + return -EINVAL; + } + + if (priv->port4_phy) { + ar8xxx_write(priv, AR8229_REG_OPER_MODE1, + AR8229_REG_OPER_MODE1_PHY4_MII_EN); + /* disable port5 to prevent mii conflict */ + ar8xxx_write(priv, AR8216_REG_PORT_STATUS(5), 0); + } + + ar8xxx_phy_init(priv); + + priv->initialized = true; + return 0; +} + +static void +ar8229_init_globals(struct ar8xxx_priv *priv) +{ + + /* Enable CPU port, and disable mirror port */ + ar8xxx_write(priv, AR8216_REG_GLOBAL_CPUPORT, + AR8216_GLOBAL_CPUPORT_EN | + (15 << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); + + /* Setup TAG priority mapping */ + ar8xxx_write(priv, AR8216_REG_TAG_PRIORITY, 0xfa50); + + /* Enable aging, MAC replacing */ + ar8xxx_write(priv, AR8216_REG_ATU_CTRL, + 0x2b /* 5 min age time */ | + AR8216_ATU_CTRL_AGE_EN | + AR8216_ATU_CTRL_LEARN_CHANGE); + + /* Enable ARP frame acknowledge */ + ar8xxx_reg_set(priv, AR8229_REG_QM_CTRL, + AR8229_QM_CTRL_ARP_EN); + + /* Enable Broadcast/Multicast frames transmitted to the CPU */ + ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK, + AR8229_FLOOD_MASK_BC_DP(0) | + AR8229_FLOOD_MASK_MC_DP(0)); + + /* setup MTU */ + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8236_GCTRL_MTU, AR8236_GCTRL_MTU); + + /* Enable MIB counters */ + ar8xxx_reg_set(priv, AR8216_REG_MIB_FUNC, + AR8236_MIB_EN); + + /* setup Service TAG */ + ar8xxx_rmw(priv, AR8216_REG_SERVICE_TAG, AR8216_SERVICE_TAG_M, 0); +} + +static void +ar8229_init_port(struct ar8xxx_priv *priv, int port) +{ + __ar8216_init_port(priv, port, true, true); +} + + +static int +ar7240sw_hw_init(struct ar8xxx_priv *priv) +{ + if (priv->initialized) + return 0; + + ar8xxx_write(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET); + ar8xxx_reg_wait(priv, AR8216_REG_CTRL, AR8216_CTRL_RESET, 0, 1000); + + priv->port4_phy = 1; + /* disable port5 to prevent mii conflict */ + ar8xxx_write(priv, AR8216_REG_PORT_STATUS(5), 0); + + ar8xxx_phy_init(priv); + + priv->initialized = true; + return 0; +} + +static void +ar7240sw_init_globals(struct ar8xxx_priv *priv) +{ + + /* Enable CPU port, and disable mirror port */ + ar8xxx_write(priv, AR8216_REG_GLOBAL_CPUPORT, + AR8216_GLOBAL_CPUPORT_EN | + (15 << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S)); + + /* Setup TAG priority mapping */ + ar8xxx_write(priv, AR8216_REG_TAG_PRIORITY, 0xfa50); + + /* Enable ARP frame acknowledge, aging, MAC replacing */ + ar8xxx_write(priv, AR8216_REG_ATU_CTRL, + AR8216_ATU_CTRL_RESERVED | + 0x2b /* 5 min age time */ | + AR8216_ATU_CTRL_AGE_EN | + AR8216_ATU_CTRL_ARP_EN | + AR8216_ATU_CTRL_LEARN_CHANGE); + + /* Enable Broadcast frames transmitted to the CPU */ + ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK, + AR8236_FM_CPU_BROADCAST_EN); + + /* setup MTU */ + ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, + AR8216_GCTRL_MTU, + AR8216_GCTRL_MTU); + + /* setup Service TAG */ + ar8xxx_rmw(priv, AR8216_REG_SERVICE_TAG, AR8216_SERVICE_TAG_M, 0); +} + +static void +ar7240sw_setup_port(struct ar8xxx_priv *priv, int port, u32 members) +{ + return __ar8216_setup_port(priv, port, members, false); +} + static void ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { @@ -935,12 +1057,11 @@ ar8236_init_globals(struct ar8xxx_priv *priv) AR8316_GCTRL_MTU, 9018 + 8 + 2); /* enable cpu port to receive arp frames */ - ar8xxx_rmw(priv, AR8216_REG_ATU_CTRL, - AR8236_ATU_CTRL_RES, AR8236_ATU_CTRL_RES); + ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL, + AR8236_ATU_CTRL_RES); /* enable cpu port to receive multicast and broadcast frames */ - ar8xxx_rmw(priv, AR8216_REG_FLOOD_MASK, - AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN, + ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK, AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN); /* Enable MIB counters */ @@ -954,7 +1075,7 @@ ar8316_hw_init(struct ar8xxx_priv *priv) { u32 val, newval; - val = priv->read(priv, AR8316_REG_POSTRIP); + val = ar8xxx_read(priv, AR8316_REG_POSTRIP); if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { if (priv->port4_phy) { @@ -978,7 +1099,7 @@ ar8316_hw_init(struct ar8xxx_priv *priv) if (val == newval) goto out; - priv->write(priv, AR8316_REG_POSTRIP, newval); + ar8xxx_write(priv, AR8316_REG_POSTRIP, newval); if (priv->port4_phy && priv->phy->interface == PHY_INTERFACE_MODE_RGMII) { @@ -1002,10 +1123,10 @@ static void ar8316_init_globals(struct ar8xxx_priv *priv) { /* standard atheros magic */ - priv->write(priv, 0x38, 0xc000050e); + ar8xxx_write(priv, 0x38, 0xc000050e); /* enable cpu port to receive multicast and broadcast frames */ - priv->write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); + ar8xxx_write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f); /* enable jumbo frames */ ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL, @@ -1017,816 +1138,48 @@ ar8316_init_globals(struct ar8xxx_priv *priv) AR8236_MIB_EN); } -static u32 -ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) +int +ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) { - u32 t; - - if (!cfg) - return 0; + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + priv->vlan = !!val->value.i; + return 0; +} - t = 0; - switch (cfg->mode) { - case AR8327_PAD_NC: - break; +int +ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->vlan; + return 0; +} - case AR8327_PAD_MAC2MAC_MII: - t = AR8327_PAD_MAC_MII_EN; - if (cfg->rxclk_sel) - t |= AR8327_PAD_MAC_MII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_MAC_MII_TXCLK_SEL; - break; - case AR8327_PAD_MAC2MAC_GMII: - t = AR8327_PAD_MAC_GMII_EN; - if (cfg->rxclk_sel) - t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; - break; +int +ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - case AR8327_PAD_MAC_SGMII: - t = AR8327_PAD_SGMII_EN; + /* make sure no invalid PVIDs get set */ - /* - * 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 (vlan < 0 || vlan >= dev->vlans || + port < 0 || port >= AR8X16_MAX_PORTS) + return -EINVAL; - if (cfg->sgmii_delay_en) - t |= AR8327_PAD_SGMII_DELAY_EN; + priv->pvid[port] = vlan; + return 0; +} - break; - - case AR8327_PAD_MAC2PHY_MII: - t = AR8327_PAD_PHY_MII_EN; - if (cfg->rxclk_sel) - t |= AR8327_PAD_PHY_MII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_PHY_MII_TXCLK_SEL; - break; - - case AR8327_PAD_MAC2PHY_GMII: - t = AR8327_PAD_PHY_GMII_EN; - if (cfg->pipe_rxclk_sel) - t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; - if (cfg->rxclk_sel) - t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; - break; - - case AR8327_PAD_MAC_RGMII: - t = AR8327_PAD_RGMII_EN; - t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; - t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; - if (cfg->rxclk_delay_en) - t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; - if (cfg->txclk_delay_en) - t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; - break; - - case AR8327_PAD_PHY_GMII: - t = AR8327_PAD_PHYX_GMII_EN; - break; - - case AR8327_PAD_PHY_RGMII: - t = AR8327_PAD_PHYX_RGMII_EN; - break; - - case AR8327_PAD_PHY_MII: - t = AR8327_PAD_PHYX_MII_EN; - break; - } - - return t; -} - -static void -ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy) -{ - switch (priv->chip_rev) { - case 1: - /* For 100M waveform */ - ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea); - /* Turn on Gigabit clock */ - ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0); - break; - - case 2: - ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c); - ar8xxx_phy_mmd_write(priv, phy, 0x4007, 0x0); - /* fallthrough */ - case 4: - ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d); - ar8xxx_phy_mmd_write(priv, phy, 0x4003, 0x803f); - - ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860); - ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46); - ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000); - break; - } -} - -static u32 -ar8327_get_port_init_status(struct ar8327_port_cfg *cfg) -{ - u32 t; - - if (!cfg->force_link) - return AR8216_PORT_STATUS_LINK_AUTO; - - t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC; - t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0; - t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0; - t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0; - - switch (cfg->speed) { - case AR8327_PORT_SPEED_10: - t |= AR8216_PORT_SPEED_10M; - break; - case AR8327_PORT_SPEED_100: - t |= AR8216_PORT_SPEED_100M; - break; - case AR8327_PORT_SPEED_1000: - t |= AR8216_PORT_SPEED_1000M; - break; - } - - return t; -} - -#define AR8327_LED_ENTRY(_num, _reg, _shift) \ - [_num] = { .reg = (_reg), .shift = (_shift) } - -static const struct ar8327_led_entry -ar8327_led_map[AR8327_NUM_LEDS] = { - AR8327_LED_ENTRY(AR8327_LED_PHY0_0, 0, 14), - AR8327_LED_ENTRY(AR8327_LED_PHY0_1, 1, 14), - AR8327_LED_ENTRY(AR8327_LED_PHY0_2, 2, 14), - - AR8327_LED_ENTRY(AR8327_LED_PHY1_0, 3, 8), - AR8327_LED_ENTRY(AR8327_LED_PHY1_1, 3, 10), - AR8327_LED_ENTRY(AR8327_LED_PHY1_2, 3, 12), - - AR8327_LED_ENTRY(AR8327_LED_PHY2_0, 3, 14), - AR8327_LED_ENTRY(AR8327_LED_PHY2_1, 3, 16), - AR8327_LED_ENTRY(AR8327_LED_PHY2_2, 3, 18), - - AR8327_LED_ENTRY(AR8327_LED_PHY3_0, 3, 20), - AR8327_LED_ENTRY(AR8327_LED_PHY3_1, 3, 22), - AR8327_LED_ENTRY(AR8327_LED_PHY3_2, 3, 24), - - AR8327_LED_ENTRY(AR8327_LED_PHY4_0, 0, 30), - AR8327_LED_ENTRY(AR8327_LED_PHY4_1, 1, 30), - AR8327_LED_ENTRY(AR8327_LED_PHY4_2, 2, 30), -}; - -static void -ar8327_set_led_pattern(struct ar8xxx_priv *priv, unsigned int led_num, - enum ar8327_led_pattern pattern) -{ - const struct ar8327_led_entry *entry; - - entry = &ar8327_led_map[led_num]; - ar8xxx_rmw(priv, AR8327_REG_LED_CTRL(entry->reg), - (3 << entry->shift), pattern << entry->shift); -} - -static void -ar8327_led_work_func(struct work_struct *work) -{ - struct ar8327_led *aled; - u8 pattern; - - aled = container_of(work, struct ar8327_led, led_work); - - spin_lock(&aled->lock); - pattern = aled->pattern; - spin_unlock(&aled->lock); - - ar8327_set_led_pattern(aled->sw_priv, aled->led_num, - pattern); -} - -static void -ar8327_led_schedule_change(struct ar8327_led *aled, u8 pattern) -{ - if (aled->pattern == pattern) - return; - - aled->pattern = pattern; - schedule_work(&aled->led_work); -} - -static inline struct ar8327_led * -led_cdev_to_ar8327_led(struct led_classdev *led_cdev) -{ - return container_of(led_cdev, struct ar8327_led, cdev); -} - -static int -ar8327_led_blink_set(struct led_classdev *led_cdev, - unsigned long *delay_on, - unsigned long *delay_off) -{ - struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev); - - if (*delay_on == 0 && *delay_off == 0) { - *delay_on = 125; - *delay_off = 125; - } - - if (*delay_on != 125 || *delay_off != 125) { - /* - * The hardware only supports blinking at 4Hz. Fall back - * to software implementation in other cases. - */ - return -EINVAL; - } - - spin_lock(&aled->lock); - - aled->enable_hw_mode = false; - ar8327_led_schedule_change(aled, AR8327_LED_PATTERN_BLINK); - - spin_unlock(&aled->lock); - - return 0; -} - -static void -ar8327_led_set_brightness(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev); - u8 pattern; - bool active; - - active = (brightness != LED_OFF); - active ^= aled->active_low; - - pattern = (active) ? AR8327_LED_PATTERN_ON : - AR8327_LED_PATTERN_OFF; - - spin_lock(&aled->lock); - - aled->enable_hw_mode = false; - ar8327_led_schedule_change(aled, pattern); - - spin_unlock(&aled->lock); -} - -static ssize_t -ar8327_led_enable_hw_mode_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev); - ssize_t ret = 0; - - spin_lock(&aled->lock); - ret += sprintf(buf, "%d\n", aled->enable_hw_mode); - spin_unlock(&aled->lock); - - return ret; -} - -static ssize_t -ar8327_led_enable_hw_mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t size) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev); - u8 pattern; - u8 value; - int ret; - - ret = kstrtou8(buf, 10, &value); - if (ret < 0) - return -EINVAL; - - spin_lock(&aled->lock); - - aled->enable_hw_mode = !!value; - if (aled->enable_hw_mode) - pattern = AR8327_LED_PATTERN_RULE; - else - pattern = AR8327_LED_PATTERN_OFF; - - ar8327_led_schedule_change(aled, pattern); - - spin_unlock(&aled->lock); - - return size; -} - -static DEVICE_ATTR(enable_hw_mode, S_IRUGO | S_IWUSR, - ar8327_led_enable_hw_mode_show, - ar8327_led_enable_hw_mode_store); - -static int -ar8327_led_register(struct ar8327_led *aled) -{ - int ret; - - ret = led_classdev_register(NULL, &aled->cdev); - if (ret < 0) - return ret; - - if (aled->mode == AR8327_LED_MODE_HW) { - ret = device_create_file(aled->cdev.dev, - &dev_attr_enable_hw_mode); - if (ret) - goto err_unregister; - } - - return 0; - -err_unregister: - led_classdev_unregister(&aled->cdev); - return ret; -} - -static void -ar8327_led_unregister(struct ar8327_led *aled) -{ - if (aled->mode == AR8327_LED_MODE_HW) - device_remove_file(aled->cdev.dev, &dev_attr_enable_hw_mode); - - led_classdev_unregister(&aled->cdev); - cancel_work_sync(&aled->led_work); -} - -static int -ar8327_led_create(struct ar8xxx_priv *priv, - const struct ar8327_led_info *led_info) -{ - struct ar8327_data *data = priv->chip_data; - struct ar8327_led *aled; - int ret; - - if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS)) - return 0; - - if (!led_info->name) - return -EINVAL; - - if (led_info->led_num >= AR8327_NUM_LEDS) - return -EINVAL; - - aled = kzalloc(sizeof(*aled) + strlen(led_info->name) + 1, - GFP_KERNEL); - if (!aled) - return -ENOMEM; - - aled->sw_priv = priv; - aled->led_num = led_info->led_num; - aled->active_low = led_info->active_low; - aled->mode = led_info->mode; - - if (aled->mode == AR8327_LED_MODE_HW) - aled->enable_hw_mode = true; - - aled->name = (char *)(aled + 1); - strcpy(aled->name, led_info->name); - - aled->cdev.name = aled->name; - aled->cdev.brightness_set = ar8327_led_set_brightness; - aled->cdev.blink_set = ar8327_led_blink_set; - aled->cdev.default_trigger = led_info->default_trigger; - - spin_lock_init(&aled->lock); - mutex_init(&aled->mutex); - INIT_WORK(&aled->led_work, ar8327_led_work_func); - - ret = ar8327_led_register(aled); - if (ret) - goto err_free; - - data->leds[data->num_leds++] = aled; - - return 0; - -err_free: - kfree(aled); - return ret; -} - -static void -ar8327_led_destroy(struct ar8327_led *aled) -{ - ar8327_led_unregister(aled); - kfree(aled); -} - -static void -ar8327_leds_init(struct ar8xxx_priv *priv) -{ - struct ar8327_data *data = priv->chip_data; - unsigned i; - - if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS)) - return; - - for (i = 0; i < data->num_leds; i++) { - struct ar8327_led *aled; - - aled = data->leds[i]; - - if (aled->enable_hw_mode) - aled->pattern = AR8327_LED_PATTERN_RULE; - else - aled->pattern = AR8327_LED_PATTERN_OFF; - - ar8327_set_led_pattern(priv, aled->led_num, aled->pattern); - } -} - -static void -ar8327_leds_cleanup(struct ar8xxx_priv *priv) -{ - struct ar8327_data *data = priv->chip_data; - unsigned i; - - if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS)) - return; - - for (i = 0; i < data->num_leds; i++) { - struct ar8327_led *aled; - - aled = data->leds[i]; - ar8327_led_destroy(aled); - } - - kfree(data->leds); -} - -static int -ar8327_hw_config_pdata(struct ar8xxx_priv *priv, - struct ar8327_platform_data *pdata) -{ - struct ar8327_led_cfg *led_cfg; - struct ar8327_data *data = priv->chip_data; - u32 pos, new_pos; - u32 t; - - if (!pdata) - return -EINVAL; - - priv->get_port_link = pdata->get_port_link; - - 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); - if (chip_is_ar8337(priv)) - t |= AR8337_PAD_MAC06_EXCHANGE_EN; - - priv->write(priv, AR8327_REG_PAD0_MODE, t); - t = ar8327_get_pad_cfg(pdata->pad5_cfg); - priv->write(priv, AR8327_REG_PAD5_MODE, t); - t = ar8327_get_pad_cfg(pdata->pad6_cfg); - priv->write(priv, AR8327_REG_PAD6_MODE, t); - - pos = priv->read(priv, AR8327_REG_POWER_ON_STRIP); - new_pos = pos; - - led_cfg = pdata->led_cfg; - if (led_cfg) { - if (led_cfg->open_drain) - new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN; - else - new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN; - - priv->write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0); - priv->write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1); - priv->write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2); - priv->write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3); - - if (new_pos != pos) - new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL; - } - - if (pdata->sgmii_cfg) { - t = pdata->sgmii_cfg->sgmii_ctrl; - if (priv->chip_rev == 1) - t |= AR8327_SGMII_CTRL_EN_PLL | - AR8327_SGMII_CTRL_EN_RX | - AR8327_SGMII_CTRL_EN_TX; - else - t &= ~(AR8327_SGMII_CTRL_EN_PLL | - AR8327_SGMII_CTRL_EN_RX | - AR8327_SGMII_CTRL_EN_TX); - - priv->write(priv, AR8327_REG_SGMII_CTRL, t); - - if (pdata->sgmii_cfg->serdes_aen) - new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN; - else - new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN; - } - - priv->write(priv, AR8327_REG_POWER_ON_STRIP, new_pos); - - if (pdata->leds && pdata->num_leds) { - int i; - - data->leds = kzalloc(pdata->num_leds * sizeof(void *), - GFP_KERNEL); - if (!data->leds) - return -ENOMEM; - - for (i = 0; i < pdata->num_leds; i++) - ar8327_led_create(priv, &pdata->leds[i]); - } - - return 0; -} - -#ifdef CONFIG_OF -static int -ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np) -{ - struct ar8327_data *data = priv->chip_data; - 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): - data->port0_status = val; - break; - case AR8327_REG_PORT_STATUS(6): - data->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) -{ - int ret; - - priv->chip_data = kzalloc(sizeof(struct ar8327_data), GFP_KERNEL); - if (!priv->chip_data) - return -ENOMEM; - - 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; - - ar8327_leds_init(priv); - - ar8xxx_phy_init(priv); - - return 0; -} - -static void -ar8327_cleanup(struct ar8xxx_priv *priv) -{ - ar8327_leds_cleanup(priv); -} - -static void -ar8327_init_globals(struct ar8xxx_priv *priv) -{ - u32 t; - - /* enable CPU port and disable mirror port */ - t = AR8327_FWD_CTRL0_CPU_PORT_EN | - AR8327_FWD_CTRL0_MIRROR_PORT; - priv->write(priv, AR8327_REG_FWD_CTRL0, t); - - /* forward multicast and broadcast frames to CPU */ - t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) | - (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) | - (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S); - priv->write(priv, AR8327_REG_FWD_CTRL1, t); - - /* enable jumbo frames */ - ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE, - AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2); - - /* Enable MIB counters */ - ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN, - AR8327_MODULE_EN_MIB); - - /* Disable EEE on all ports due to stability issues */ - t = priv->read(priv, AR8327_REG_EEE_CTRL); - t |= AR8327_EEE_CTRL_DISABLE_PHY(0) | - AR8327_EEE_CTRL_DISABLE_PHY(1) | - AR8327_EEE_CTRL_DISABLE_PHY(2) | - AR8327_EEE_CTRL_DISABLE_PHY(3) | - AR8327_EEE_CTRL_DISABLE_PHY(4); - priv->write(priv, AR8327_REG_EEE_CTRL, t); -} - -static void -ar8327_init_port(struct ar8xxx_priv *priv, int port) -{ - struct ar8327_data *data = priv->chip_data; - u32 t; - - if (port == AR8216_PORT_CPU) - t = data->port0_status; - else if (port == 6) - t = data->port6_status; - else - t = AR8216_PORT_STATUS_LINK_AUTO; - - priv->write(priv, AR8327_REG_PORT_STATUS(port), t); - priv->write(priv, AR8327_REG_PORT_HEADER(port), 0); - - t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; - t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; - priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); - - t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; - priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); - - t = AR8327_PORT_LOOKUP_LEARN; - t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; - priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t); -} - -static u32 -ar8327_read_port_status(struct ar8xxx_priv *priv, int port) -{ - return priv->read(priv, AR8327_REG_PORT_STATUS(port)); -} - -static int -ar8327_atu_flush(struct ar8xxx_priv *priv) -{ - int ret; - - ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC, - AR8327_ATU_FUNC_BUSY, 0); - if (!ret) - priv->write(priv, AR8327_REG_ATU_FUNC, - AR8327_ATU_FUNC_OP_FLUSH); - - return ret; -} - -static void -ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val) -{ - if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1, - AR8327_VTU_FUNC1_BUSY, 0)) - return; - - if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD) - priv->write(priv, AR8327_REG_VTU_FUNC0, val); - - op |= AR8327_VTU_FUNC1_BUSY; - priv->write(priv, AR8327_REG_VTU_FUNC1, op); -} - -static void -ar8327_vtu_flush(struct ar8xxx_priv *priv) -{ - ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0); -} - -static void -ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask) -{ - u32 op; - u32 val; - int i; - - op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S); - val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL; - for (i = 0; i < AR8327_NUM_PORTS; i++) { - u32 mode; - - if ((port_mask & BIT(i)) == 0) - mode = AR8327_VTU_FUNC0_EG_MODE_NOT; - else if (priv->vlan == 0) - mode = AR8327_VTU_FUNC0_EG_MODE_KEEP; - else if ((priv->vlan_tagged & BIT(i)) || (priv->vlan_id[priv->pvid[i]] != vid)) - mode = AR8327_VTU_FUNC0_EG_MODE_TAG; - else - mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG; - - val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i); - } - ar8327_vtu_op(priv, op, val); -} - -static void -ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members) -{ - u32 t; - u32 egress, ingress; - u32 pvid = priv->vlan_id[priv->pvid[port]]; - - if (priv->vlan) { - egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD; - ingress = AR8216_IN_SECURE; - } else { - egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH; - ingress = AR8216_IN_PORT_ONLY; - } - - t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S; - t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S; - priv->write(priv, AR8327_REG_PORT_VLAN0(port), t); - - t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; - t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S; - priv->write(priv, AR8327_REG_PORT_VLAN1(port), t); - - t = members; - t |= AR8327_PORT_LOOKUP_LEARN; - t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S; - t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; - priv->write(priv, AR8327_REG_PORT_LOOKUP(port), t); -} - -static int -ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) -{ - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - priv->vlan = !!val->value.i; - return 0; -} - -static int -ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, - struct switch_val *val) -{ - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - val->value.i = priv->vlan; - return 0; -} - - -static int -ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan) -{ - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - - /* make sure no invalid PVIDs get set */ - - if (vlan >= dev->vlans) - return -EINVAL; - - priv->pvid[port] = vlan; - return 0; -} - -static int +int ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + if (port < 0 || port >= AR8X16_MAX_PORTS) + return -EINVAL; + *vlan = priv->pvid[port]; return 0; } @@ -1836,6 +1189,10 @@ ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + + if (val->port_vlan >= AR8X16_MAX_VLANS) + return -EINVAL; + priv->vlan_id[val->port_vlan] = val->value.i; return 0; } @@ -1849,7 +1206,7 @@ ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr, return 0; } -static int +int ar8xxx_sw_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link) { @@ -1863,33 +1220,13 @@ static int ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - u8 ports = priv->vlan_table[val->port_vlan]; + u8 ports; int i; - val->len = 0; - for (i = 0; i < dev->ports; i++) { - struct switch_port *p; - - if (!(ports & (1 << i))) - continue; - - p = &val->value.ports[val->len++]; - p->id = i; - if (priv->vlan_tagged & (1 << i)) - p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); - else - p->flags = 0; - } - return 0; -} - -static int -ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val) -{ - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - u8 ports = priv->vlan_table[val->port_vlan]; - int i; + if (val->port_vlan >= AR8X16_MAX_VLANS) + return -EINVAL; + ports = priv->vlan_table[val->port_vlan]; val->len = 0; for (i = 0; i < dev->ports; i++) { struct switch_port *p; @@ -1899,7 +1236,7 @@ ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val) p = &val->value.ports[val->len++]; p->id = i; - if ((priv->vlan_tagged & (1 << i)) || (priv->pvid[i] != val->port_vlan)) + if (priv->vlan_tagged & (1 << i)) p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); else p->flags = 0; @@ -1938,72 +1275,6 @@ ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val) return 0; } -static int -ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val) -{ - struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); - u8 *vt = &priv->vlan_table[val->port_vlan]; - int i; - - *vt = 0; - for (i = 0; i < val->len; i++) { - struct switch_port *p = &val->value.ports[i]; - - if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) { - if (val->port_vlan == priv->pvid[p->id]) { - priv->vlan_tagged |= (1 << p->id); - } - } else { - priv->vlan_tagged &= ~(1 << p->id); - priv->pvid[p->id] = val->port_vlan; - } - - *vt |= 1 << p->id; - } - 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) { @@ -2014,13 +1285,11 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv) 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_reg_clear(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_MIRROR_RX); - ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port), - AR8216_PORT_CTRL_MIRROR_TX, - 0); + ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port), + AR8216_PORT_CTRL_MIRROR_TX); } /* now enable mirroring if necessary */ @@ -2035,20 +1304,33 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv) (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, + ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port), AR8216_PORT_CTRL_MIRROR_RX); if (priv->mirror_tx) - ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(priv->source_port), - AR8216_PORT_CTRL_MIRROR_TX, + ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port), AR8216_PORT_CTRL_MIRROR_TX); } -static int +static inline u32 +ar8xxx_age_time_val(int age_time) +{ + return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) / + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS; +} + +static inline void +ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg) +{ + u32 age_time = ar8xxx_age_time_val(priv->arl_age_time); + ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S); +} + +int ar8xxx_sw_hw_apply(struct switch_dev *dev) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + const struct ar8xxx_chip *chip = priv->chip; u8 portmask[AR8X16_MAX_PORTS]; int i, j; @@ -2072,8 +1354,8 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) portmask[i] |= vp & ~mask; } - priv->chip->vtu_load_vlan(priv, priv->vlan_id[j], - priv->vlan_table[j]); + chip->vtu_load_vlan(priv, priv->vlan_id[j], + priv->vlan_table[j]); } } else { /* vlan disabled: @@ -2089,19 +1371,24 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) /* update the port destination mask registers and tag settings */ for (i = 0; i < dev->ports; i++) { - priv->chip->setup_port(priv, i, portmask[i]); + chip->setup_port(priv, i, portmask[i]); } - priv->chip->set_mirror_regs(priv); + chip->set_mirror_regs(priv); + + /* set age time */ + if (chip->reg_arl_ctrl) + ar8xxx_set_age_time(priv, chip->reg_arl_ctrl); mutex_unlock(&priv->reg_mutex); return 0; } -static int +int ar8xxx_sw_reset_switch(struct switch_dev *dev) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + const struct ar8xxx_chip *chip = priv->chip; int i; mutex_lock(&priv->reg_mutex); @@ -2113,21 +1400,23 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev) /* Configure all ports */ for (i = 0; i < dev->ports; i++) - priv->chip->init_port(priv, i); + chip->init_port(priv, i); priv->mirror_rx = false; priv->mirror_tx = false; priv->source_port = 0; priv->monitor_port = 0; + priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME; - priv->chip->init_globals(priv); + chip->init_globals(priv); + chip->atu_flush(priv); mutex_unlock(&priv->reg_mutex); - return ar8xxx_sw_hw_apply(dev); + return chip->sw_hw_apply(dev); } -static int +int ar8xxx_sw_set_reset_mibs(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2155,7 +1444,7 @@ unlock: return ret; } -static int +int ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2170,7 +1459,7 @@ ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2180,7 +1469,7 @@ ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2195,7 +1484,7 @@ ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2205,7 +1494,7 @@ ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2220,7 +1509,7 @@ ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2230,7 +1519,7 @@ ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2245,7 +1534,7 @@ ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2255,7 +1544,7 @@ ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev, return 0; } -static int +int ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) @@ -2285,18 +1574,46 @@ unlock: return ret; } -static int +static void +ar8xxx_byte_to_str(char *buf, int len, u64 byte) +{ + unsigned long b; + const char *unit; + + if (byte >= 0x40000000) { /* 1 GiB */ + b = byte * 10 / 0x40000000; + unit = "GiB"; + } else if (byte >= 0x100000) { /* 1 MiB */ + b = byte * 10 / 0x100000; + unit = "MiB"; + } else if (byte >= 0x400) { /* 1 KiB */ + b = byte * 10 / 0x400; + unit = "KiB"; + } else { + b = byte; + unit = "Byte"; + } + if (strcmp(unit, "Byte")) + snprintf(buf, len, "%lu.%lu %s", b / 10, b % 10, unit); + else + snprintf(buf, len, "%lu %s", b, unit); +} + +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; + u64 *mib_stats, mib_data; + unsigned int port; int ret; char *buf = priv->buf; + char buf1[64]; + const char *mib_name; int i, len = 0; + bool mib_stats_empty = true; if (!ar8xxx_has_mib_counters(priv)) return -EOPNOTSUPP; @@ -2313,15 +1630,28 @@ ar8xxx_sw_get_port_mib(struct switch_dev *dev, ar8xxx_mib_fetch_port_stat(priv, port, false); len += snprintf(buf + len, sizeof(priv->buf) - len, - "Port %d MIB counters\n", - port); + "MIB counters\n"); mib_stats = &priv->mib_stats[port * chip->num_mibs]; - for (i = 0; i < chip->num_mibs; i++) + for (i = 0; i < chip->num_mibs; i++) { + mib_name = chip->mib_decs[i].name; + mib_data = mib_stats[i]; len += snprintf(buf + len, sizeof(priv->buf) - len, - "%-12s: %llu\n", - chip->mib_decs[i].name, - mib_stats[i]); + "%-12s: %llu\n", mib_name, mib_data); + if ((!strcmp(mib_name, "TxByte") || + !strcmp(mib_name, "RxGoodByte")) && + mib_data >= 1024) { + ar8xxx_byte_to_str(buf1, sizeof(buf1), mib_data); + --len; /* discard newline at the end of buf */ + len += snprintf(buf + len, sizeof(priv->buf) - len, + " (%s)\n", buf1); + } + if (mib_stats_empty && mib_data) + mib_stats_empty = false; + } + + if (mib_stats_empty) + len = snprintf(buf, sizeof(priv->buf), "No MIB data"); val->value.s = buf; val->len = len; @@ -2333,56 +1663,186 @@ unlock: return ret; } -static struct switch_attr ar8xxx_sw_attr_globals[] = { - { - .type = SWITCH_TYPE_INT, - .name = "enable_vlan", - .description = "Enable VLAN mode", - .set = ar8xxx_sw_set_vlan, - .get = ar8xxx_sw_get_vlan, - .max = 1 - }, - { - .type = SWITCH_TYPE_NOVAL, - .name = "reset_mibs", - .description = "Reset all MIB counters", - .set = ar8xxx_sw_set_reset_mibs, - }, - { - .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 - }, -}; +int +ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int age_time = val->value.i; + u32 age_time_val; + + if (age_time < 0) + return -EINVAL; + + age_time_val = ar8xxx_age_time_val(age_time); + if (age_time_val == 0 || age_time_val > 0xffff) + return -EINVAL; + + priv->arl_age_time = age_time; + return 0; +} + +int +ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + val->value.i = priv->arl_age_time; + return 0; +} + +int +ar8xxx_sw_get_arl_table(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + struct mii_bus *bus = priv->mii_bus; + const struct ar8xxx_chip *chip = priv->chip; + char *buf = priv->arl_buf; + int i, j, k, len = 0; + struct arl_entry *a, *a1; + u32 status; + + if (!chip->get_arl_entry) + return -EOPNOTSUPP; + + mutex_lock(&priv->reg_mutex); + mutex_lock(&bus->mdio_lock); + + chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE); + + for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) { + a = &priv->arl_table[i]; + duplicate: + chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT); + + if (!status) + break; + + /* avoid duplicates + * ARL table can include multiple valid entries + * per MAC, just with differing status codes + */ + for (j = 0; j < i; ++j) { + a1 = &priv->arl_table[j]; + if (!memcmp(a->mac, a1->mac, sizeof(a->mac))) { + /* ignore ports already seen in former entry */ + a->portmap &= ~a1->portmap; + if (!a->portmap) + goto duplicate; + } + } + } + + mutex_unlock(&bus->mdio_lock); + + len += snprintf(buf + len, sizeof(priv->arl_buf) - len, + "address resolution table\n"); + + if (i == AR8XXX_NUM_ARL_RECORDS) + len += snprintf(buf + len, sizeof(priv->arl_buf) - len, + "Too many entries found, displaying the first %d only!\n", + AR8XXX_NUM_ARL_RECORDS); + + for (j = 0; j < priv->dev.ports; ++j) { + for (k = 0; k < i; ++k) { + a = &priv->arl_table[k]; + if (!(a->portmap & BIT(j))) + continue; + len += snprintf(buf + len, sizeof(priv->arl_buf) - len, + "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n", + j, + a->mac[5], a->mac[4], a->mac[3], + a->mac[2], a->mac[1], a->mac[0]); + } + } + + val->value.s = buf; + val->len = len; + + mutex_unlock(&priv->reg_mutex); + + return 0; +} + +int +ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int ret; + + mutex_lock(&priv->reg_mutex); + ret = priv->chip->atu_flush(priv); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +int +ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + int port, ret; + + port = val->port_vlan; + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->reg_mutex); + ret = priv->chip->atu_flush_port(priv, port); + mutex_unlock(&priv->reg_mutex); + + return ret; +} + +int +ar8xxx_sw_get_port_stats(struct switch_dev *dev, int port, + struct switch_port_stats *stats) +{ + struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); + u64 *mib_stats; + + if (!ar8xxx_has_mib_counters(priv)) + return -EOPNOTSUPP; + + if (!(priv->chip->mib_rxb_id || priv->chip->mib_txb_id)) + return -EOPNOTSUPP; + + if (port >= dev->ports) + return -EINVAL; + + mutex_lock(&priv->mib_lock); + + mib_stats = &priv->mib_stats[port * priv->chip->num_mibs]; + + stats->tx_bytes = mib_stats[priv->chip->mib_txb_id]; + stats->rx_bytes = mib_stats[priv->chip->mib_rxb_id]; + + mutex_unlock(&priv->mib_lock); + return 0; +} + +static int +ar8xxx_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr) +{ + struct ar8xxx_priv *priv = bus->priv; + return priv->chip->phy_read(priv, phy_addr, reg_addr); +} + +static int +ar8xxx_phy_write(struct mii_bus *bus, int phy_addr, int reg_addr, + u16 reg_val) +{ + struct ar8xxx_priv *priv = bus->priv; + return priv->chip->phy_write(priv, phy_addr, reg_addr, reg_val); +} -static struct switch_attr ar8327_sw_attr_globals[] = { +static const struct switch_attr ar8xxx_sw_attr_globals[] = { { .type = SWITCH_TYPE_INT, .name = "enable_vlan", @@ -2419,7 +1879,7 @@ static struct switch_attr ar8327_sw_attr_globals[] = { .description = "Mirror monitor port", .set = ar8xxx_sw_set_mirror_monitor_port, .get = ar8xxx_sw_get_mirror_monitor_port, - .max = AR8327_NUM_PORTS - 1 + .max = AR8216_NUM_PORTS - 1 }, { .type = SWITCH_TYPE_INT, @@ -2427,11 +1887,24 @@ static struct switch_attr ar8327_sw_attr_globals[] = { .description = "Mirror source port", .set = ar8xxx_sw_set_mirror_source_port, .get = ar8xxx_sw_get_mirror_source_port, - .max = AR8327_NUM_PORTS - 1 + .max = AR8216_NUM_PORTS - 1 }, + { + .type = SWITCH_TYPE_STRING, + .name = "arl_table", + .description = "Get ARL table", + .set = NULL, + .get = ar8xxx_sw_get_arl_table, + }, + { + .type = SWITCH_TYPE_NOVAL, + .name = "flush_arl_table", + .description = "Flush ARL table", + .set = ar8xxx_sw_set_flush_arl_table, + }, }; -static struct switch_attr ar8xxx_sw_attr_port[] = { +const struct switch_attr ar8xxx_sw_attr_port[] = { { .type = SWITCH_TYPE_NOVAL, .name = "reset_mib", @@ -2445,9 +1918,15 @@ static struct switch_attr ar8xxx_sw_attr_port[] = { .set = NULL, .get = ar8xxx_sw_get_port_mib, }, + { + .type = SWITCH_TYPE_NOVAL, + .name = "flush_arl_table", + .description = "Flush port's ARL table entries", + .set = ar8xxx_sw_set_flush_port_arl_table, + }, }; -static struct switch_attr ar8xxx_sw_attr_vlan[] = { +const struct switch_attr ar8xxx_sw_attr_vlan[1] = { { .type = SWITCH_TYPE_INT, .name = "vid", @@ -2478,28 +1957,41 @@ static const struct switch_dev_ops ar8xxx_sw_ops = { .apply_config = ar8xxx_sw_hw_apply, .reset_switch = ar8xxx_sw_reset_switch, .get_port_link = ar8xxx_sw_get_port_link, + .get_port_stats = ar8xxx_sw_get_port_stats, }; -static const struct switch_dev_ops ar8327_sw_ops = { - .attr_global = { - .attr = ar8327_sw_attr_globals, - .n_attr = ARRAY_SIZE(ar8327_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 = ar8327_sw_get_ports, - .set_vlan_ports = ar8327_sw_set_ports, - .apply_config = ar8xxx_sw_hw_apply, - .reset_switch = ar8xxx_sw_reset_switch, - .get_port_link = ar8xxx_sw_get_port_link, +static const struct ar8xxx_chip ar7240sw_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, + + .reg_port_stats_start = 0x20000, + .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, + + .name = "Atheros AR724X/AR933X built-in", + .ports = AR7240SW_NUM_PORTS, + .vlans = AR8216_NUM_VLANS, + .swops = &ar8xxx_sw_ops, + + .hw_init = ar7240sw_hw_init, + .init_globals = ar7240sw_init_globals, + .init_port = ar8229_init_port, + .phy_read = ar8216_phy_read, + .phy_write = ar8216_phy_write, + .setup_port = ar7240sw_setup_port, + .read_port_status = ar8216_read_port_status, + .atu_flush = ar8216_atu_flush, + .atu_flush_port = ar8216_atu_flush_port, + .vtu_flush = ar8216_vtu_flush, + .vtu_load_vlan = ar8216_vtu_load_vlan, + .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, + .sw_hw_apply = ar8xxx_sw_hw_apply, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8216_chip = { @@ -2507,6 +1999,7 @@ static const struct ar8xxx_chip ar8216_chip = { .reg_port_stats_start = 0x19000, .reg_port_stats_length = 0xa0, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, .name = "Atheros AR8216", .ports = AR8216_NUM_PORTS, @@ -2519,13 +2012,52 @@ static const struct ar8xxx_chip ar8216_chip = { .setup_port = ar8216_setup_port, .read_port_status = ar8216_read_port_status, .atu_flush = ar8216_atu_flush, + .atu_flush_port = ar8216_atu_flush_port, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, + .sw_hw_apply = ar8xxx_sw_hw_apply, .num_mibs = ARRAY_SIZE(ar8216_mibs), .mib_decs = ar8216_mibs, - .mib_func = AR8216_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8216_MIB_RXB_ID, + .mib_txb_id = AR8216_MIB_TXB_ID, +}; + +static const struct ar8xxx_chip ar8229_chip = { + .caps = AR8XXX_CAP_MIB_COUNTERS, + + .reg_port_stats_start = 0x20000, + .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, + + .name = "Atheros AR8229", + .ports = AR8216_NUM_PORTS, + .vlans = AR8216_NUM_VLANS, + .swops = &ar8xxx_sw_ops, + + .hw_init = ar8229_hw_init, + .init_globals = ar8229_init_globals, + .init_port = ar8229_init_port, + .phy_read = ar8216_phy_read, + .phy_write = ar8216_phy_write, + .setup_port = ar8236_setup_port, + .read_port_status = ar8216_read_port_status, + .atu_flush = ar8216_atu_flush, + .atu_flush_port = ar8216_atu_flush_port, + .vtu_flush = ar8216_vtu_flush, + .vtu_load_vlan = ar8216_vtu_load_vlan, + .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, + .sw_hw_apply = ar8xxx_sw_hw_apply, + + .num_mibs = ARRAY_SIZE(ar8236_mibs), + .mib_decs = ar8236_mibs, + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8236_chip = { @@ -2533,6 +2065,7 @@ static const struct ar8xxx_chip ar8236_chip = { .reg_port_stats_start = 0x20000, .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, .name = "Atheros AR8236", .ports = AR8216_NUM_PORTS, @@ -2545,13 +2078,18 @@ static const struct ar8xxx_chip ar8236_chip = { .setup_port = ar8236_setup_port, .read_port_status = ar8216_read_port_status, .atu_flush = ar8216_atu_flush, + .atu_flush_port = ar8216_atu_flush_port, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, + .sw_hw_apply = ar8xxx_sw_hw_apply, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, - .mib_func = AR8216_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static const struct ar8xxx_chip ar8316_chip = { @@ -2559,6 +2097,7 @@ static const struct ar8xxx_chip ar8316_chip = { .reg_port_stats_start = 0x20000, .reg_port_stats_length = 0x100, + .reg_arl_ctrl = AR8216_REG_ATU_CTRL, .name = "Atheros AR8316", .ports = AR8216_NUM_PORTS, @@ -2571,83 +2110,28 @@ static const struct ar8xxx_chip ar8316_chip = { .setup_port = ar8216_setup_port, .read_port_status = ar8216_read_port_status, .atu_flush = ar8216_atu_flush, + .atu_flush_port = ar8216_atu_flush_port, .vtu_flush = ar8216_vtu_flush, .vtu_load_vlan = ar8216_vtu_load_vlan, .set_mirror_regs = ar8216_set_mirror_regs, + .get_arl_entry = ar8216_get_arl_entry, + .sw_hw_apply = ar8xxx_sw_hw_apply, .num_mibs = ARRAY_SIZE(ar8236_mibs), .mib_decs = ar8236_mibs, - .mib_func = AR8216_REG_MIB_FUNC -}; - -static const struct ar8xxx_chip ar8327_chip = { - .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, - .config_at_probe = true, - .mii_lo_first = true, - - .name = "Atheros AR8327", - .ports = AR8327_NUM_PORTS, - .vlans = AR8X16_MAX_VLANS, - .swops = &ar8327_sw_ops, - - .reg_port_stats_start = 0x1000, - .reg_port_stats_length = 0x100, - - .hw_init = ar8327_hw_init, - .cleanup = ar8327_cleanup, - .init_globals = ar8327_init_globals, - .init_port = ar8327_init_port, - .setup_port = ar8327_setup_port, - .read_port_status = ar8327_read_port_status, - .atu_flush = ar8327_atu_flush, - .vtu_flush = ar8327_vtu_flush, - .vtu_load_vlan = ar8327_vtu_load_vlan, - .phy_fixup = ar8327_phy_fixup, - .set_mirror_regs = ar8327_set_mirror_regs, - - .num_mibs = ARRAY_SIZE(ar8236_mibs), - .mib_decs = ar8236_mibs, - .mib_func = AR8327_REG_MIB_FUNC -}; - -static const struct ar8xxx_chip ar8337_chip = { - .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS, - .config_at_probe = true, - .mii_lo_first = true, - - .name = "Atheros AR8337", - .ports = AR8327_NUM_PORTS, - .vlans = AR8X16_MAX_VLANS, - .swops = &ar8327_sw_ops, - - .reg_port_stats_start = 0x1000, - .reg_port_stats_length = 0x100, - - .hw_init = ar8327_hw_init, - .cleanup = ar8327_cleanup, - .init_globals = ar8327_init_globals, - .init_port = ar8327_init_port, - .setup_port = ar8327_setup_port, - .read_port_status = ar8327_read_port_status, - .atu_flush = ar8327_atu_flush, - .vtu_flush = ar8327_vtu_flush, - .vtu_load_vlan = ar8327_vtu_load_vlan, - .phy_fixup = ar8327_phy_fixup, - .set_mirror_regs = ar8327_set_mirror_regs, - - .num_mibs = ARRAY_SIZE(ar8236_mibs), - .mib_decs = ar8236_mibs, - .mib_func = AR8327_REG_MIB_FUNC + .mib_func = AR8216_REG_MIB_FUNC, + .mib_rxb_id = AR8236_MIB_RXB_ID, + .mib_txb_id = AR8236_MIB_TXB_ID, }; static int -ar8xxx_id_chip(struct ar8xxx_priv *priv) +ar8xxx_read_id(struct ar8xxx_priv *priv) { u32 val; u16 id; int i; - val = priv->read(priv, AR8216_REG_CTRL); + val = ar8xxx_read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -2655,7 +2139,7 @@ ar8xxx_id_chip(struct ar8xxx_priv *priv) for (i = 0; i < AR8X16_PROBE_RETRIES; i++) { u16 t; - val = priv->read(priv, AR8216_REG_CTRL); + val = ar8xxx_read(priv, AR8216_REG_CTRL); if (val == ~0) return -ENODEV; @@ -2666,6 +2150,17 @@ ar8xxx_id_chip(struct ar8xxx_priv *priv) priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S; priv->chip_rev = (id & AR8216_CTRL_REVISION); + return 0; +} + +static int +ar8xxx_id_chip(struct ar8xxx_priv *priv) +{ + int ret; + + ret = ar8xxx_read_id(priv); + if(ret) + return ret; switch (priv->chip_ver) { case AR8XXX_VER_AR8216: @@ -2697,7 +2192,7 @@ static void ar8xxx_mib_work_func(struct work_struct *work) { struct ar8xxx_priv *priv; - int err; + int err, i; priv = container_of(work, struct ar8xxx_priv, mib_work.work); @@ -2705,18 +2200,15 @@ ar8xxx_mib_work_func(struct work_struct *work) err = ar8xxx_mib_capture(priv); if (err) - goto next_port; + goto next_attempt; - 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; + for (i = 0; i < priv->dev.ports; i++) + ar8xxx_mib_fetch_port_stat(priv, i, false); +next_attempt: mutex_unlock(&priv->mib_lock); schedule_delayed_work(&priv->mib_work, - msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); + msecs_to_jiffies(priv->mib_poll_interval)); } static int @@ -2746,7 +2238,7 @@ ar8xxx_mib_start(struct ar8xxx_priv *priv) return; schedule_delayed_work(&priv->mib_work, - msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY)); + msecs_to_jiffies(priv->mib_poll_interval)); } static void @@ -2755,7 +2247,7 @@ ar8xxx_mib_stop(struct ar8xxx_priv *priv) if (!ar8xxx_has_mib_counters(priv)) return; - cancel_delayed_work(&priv->mib_work); + cancel_delayed_work_sync(&priv->mib_work); } static struct ar8xxx_priv * @@ -2785,22 +2277,6 @@ ar8xxx_free(struct ar8xxx_priv *priv) 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; - priv->rmw = ar8xxx_mii_rmw; - } - - return priv; -} - static int ar8xxx_probe_switch(struct ar8xxx_priv *priv) { @@ -2808,10 +2284,6 @@ ar8xxx_probe_switch(struct ar8xxx_priv *priv) struct switch_dev *swdev; int ret; - ret = ar8xxx_id_chip(priv); - if (ret) - return ret; - chip = priv->chip; swdev = &priv->dev; @@ -2865,7 +2337,7 @@ ar8xxx_phy_config_init(struct phy_device *phydev) priv->phy = phydev; - if (phydev->addr != 0) { + if (phydev->mdio.addr != 0) { if (chip_is_ar8316(priv)) { /* switch device has been initialized, reinit */ priv->dev.ports = (AR8216_NUM_PORTS - 1); @@ -2893,17 +2365,49 @@ ar8xxx_phy_config_init(struct phy_device *phydev) return 0; } +static bool +ar8xxx_check_link_states(struct ar8xxx_priv *priv) +{ + bool link_new, changed = false; + u32 status; + int i; + + mutex_lock(&priv->reg_mutex); + + for (i = 0; i < priv->dev.ports; i++) { + status = priv->chip->read_port_status(priv, i); + link_new = !!(status & AR8216_PORT_STATUS_LINK_UP); + if (link_new == priv->link_up[i]) + continue; + + priv->link_up[i] = link_new; + changed = true; + /* flush ARL entries for this port if it went down*/ + if (!link_new) + priv->chip->atu_flush_port(priv, i); + dev_info(&priv->phy->mdio.dev, "Port %d is %s\n", + i, link_new ? "up" : "down"); + } + + mutex_unlock(&priv->reg_mutex); + + return changed; +} + static int ar8xxx_phy_read_status(struct phy_device *phydev) { struct ar8xxx_priv *priv = phydev->priv; struct switch_port_link link; - int ret; - if (phydev->addr != 0) + /* check for switch port link changes */ + if (phydev->state == PHY_CHANGELINK) + ar8xxx_check_link_states(priv); + + if (phydev->mdio.addr != 0) return genphy_read_status(phydev); - ar8216_read_port_link(priv, phydev->addr, &link); + ar8216_read_port_link(priv, phydev->mdio.addr, &link); phydev->link = !!link.link; if (!phydev->link) return 0; @@ -2923,22 +2427,18 @@ ar8xxx_phy_read_status(struct phy_device *phydev) } phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF; - /* flush the address translation unit */ - mutex_lock(&priv->reg_mutex); - ret = priv->chip->atu_flush(priv); - mutex_unlock(&priv->reg_mutex); - phydev->state = PHY_RUNNING; netif_carrier_on(phydev->attached_dev); - phydev->adjust_link(phydev->attached_dev); + if (phydev->adjust_link) + phydev->adjust_link(phydev->attached_dev); - return ret; + return 0; } static int ar8xxx_phy_config_aneg(struct phy_device *phydev) { - if (phydev->addr == 0) + if (phydev->mdio.addr == 0) return 0; return genphy_config_aneg(phydev); @@ -2968,21 +2468,21 @@ ar8xxx_phy_match(u32 phy_id) static bool ar8xxx_is_possible(struct mii_bus *bus) { - unsigned i; + unsigned int i, found_phys = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < 5; i++) { u32 phy_id; phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16; phy_id |= mdiobus_read(bus, i, MII_PHYSID2); - if (!ar8xxx_phy_match(phy_id)) { + if (ar8xxx_phy_match(phy_id)) { + found_phys++; + } else if (phy_id) { pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n", dev_name(&bus->dev), i, phy_id); - return false; } } - - return true; + return !!found_phys; } static int @@ -2993,23 +2493,35 @@ ar8xxx_phy_probe(struct phy_device *phydev) int ret; /* skip PHYs at unused adresses */ - if (phydev->addr != 0 && phydev->addr != 4) + if (phydev->mdio.addr != 0 && phydev->mdio.addr != 3 && phydev->mdio.addr != 4) return -ENODEV; - if (!ar8xxx_is_possible(phydev->bus)) + if (!ar8xxx_is_possible(phydev->mdio.bus)) return -ENODEV; mutex_lock(&ar8xxx_dev_list_lock); list_for_each_entry(priv, &ar8xxx_dev_list, list) - if (priv->mii_bus == phydev->bus) + if (priv->mii_bus == phydev->mdio.bus) goto found; - priv = ar8xxx_create_mii(phydev->bus); + priv = ar8xxx_create(); if (priv == NULL) { ret = -ENOMEM; goto unlock; } + priv->mii_bus = phydev->mdio.bus; + priv->pdev = &phydev->mdio.dev; + + ret = of_property_read_u32(priv->pdev->of_node, "qca,mib-poll-interval", + &priv->mib_poll_interval); + if (ret) + priv->mib_poll_interval = AR8XXX_MIB_WORK_DELAY; + + ret = ar8xxx_id_chip(priv); + if (ret) + goto free_priv; + ret = ar8xxx_probe_switch(priv); if (ret) goto free_priv; @@ -3024,10 +2536,12 @@ ar8xxx_phy_probe(struct phy_device *phydev) swdev->devname, swdev->name, priv->chip_rev, dev_name(&priv->mii_bus->dev)); + list_add(&priv->list, &ar8xxx_dev_list); + found: priv->use_count++; - if (phydev->addr == 0) { + if (phydev->mdio.addr == 0) { if (ar8xxx_has_gige(priv)) { phydev->supported = SUPPORTED_1000baseT_Full; phydev->advertising = ADVERTISED_1000baseT_Full; @@ -3048,12 +2562,12 @@ found: phydev->supported |= SUPPORTED_1000baseT_Full; phydev->advertising |= ADVERTISED_1000baseT_Full; } + if (priv->chip->phy_rgmii_set) + priv->chip->phy_rgmii_set(priv, phydev); } phydev->priv = priv; - list_add(&priv->list, &ar8xxx_dev_list); - mutex_unlock(&ar8xxx_dev_list_lock); return 0; @@ -3094,10 +2608,14 @@ ar8xxx_phy_remove(struct phy_device *phydev) return; phydev->priv = NULL; - if (--priv->use_count > 0) - return; mutex_lock(&ar8xxx_dev_list_lock); + + if (--priv->use_count > 0) { + mutex_unlock(&ar8xxx_dev_list_lock); + return; + } + list_del(&priv->list); mutex_unlock(&ar8xxx_dev_list_lock); @@ -3106,45 +2624,195 @@ ar8xxx_phy_remove(struct phy_device *phydev) ar8xxx_free(priv); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) static int ar8xxx_phy_soft_reset(struct phy_device *phydev) { /* we don't need an extra reset */ return 0; } -#endif - -static struct phy_driver ar8xxx_phy_driver = { - .phy_id = 0x004d0000, - .name = "Atheros AR8216/AR8236/AR8316", - .phy_id_mask = 0xffff0000, - .features = PHY_BASIC_FEATURES, - .probe = ar8xxx_phy_probe, - .remove = ar8xxx_phy_remove, - .detach = ar8xxx_phy_detach, - .config_init = ar8xxx_phy_config_init, - .config_aneg = ar8xxx_phy_config_aneg, - .read_status = ar8xxx_phy_read_status, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) - .soft_reset = ar8xxx_phy_soft_reset, -#endif - .driver = { .owner = THIS_MODULE }, + +static struct phy_driver ar8xxx_phy_driver[] = { + { + .phy_id = 0x004d0000, + .name = "Atheros AR8216/AR8236/AR8316", + .phy_id_mask = 0xffff0000, + .features = PHY_BASIC_FEATURES, + .probe = ar8xxx_phy_probe, + .remove = ar8xxx_phy_remove, + .detach = ar8xxx_phy_detach, + .config_init = ar8xxx_phy_config_init, + .config_aneg = ar8xxx_phy_config_aneg, + .read_status = ar8xxx_phy_read_status, + .soft_reset = ar8xxx_phy_soft_reset, + } +}; + +static const struct of_device_id ar8xxx_mdiodev_of_match[] = { + { + .compatible = "qca,ar7240sw", + .data = &ar7240sw_chip, + }, { + .compatible = "qca,ar8229", + .data = &ar8229_chip, + }, { + .compatible = "qca,ar8236", + .data = &ar8236_chip, + }, { + .compatible = "qca,ar8327", + .data = &ar8327_chip, + }, + { /* sentinel */ }, +}; + +static int +ar8xxx_mdiodev_probe(struct mdio_device *mdiodev) +{ + const struct of_device_id *match; + struct ar8xxx_priv *priv; + struct switch_dev *swdev; + struct device_node *mdio_node; + int ret; + + match = of_match_device(ar8xxx_mdiodev_of_match, &mdiodev->dev); + if (!match) + return -EINVAL; + + priv = ar8xxx_create(); + if (priv == NULL) + return -ENOMEM; + + priv->mii_bus = mdiodev->bus; + priv->pdev = &mdiodev->dev; + priv->chip = (const struct ar8xxx_chip *) match->data; + + ret = of_property_read_u32(priv->pdev->of_node, "qca,mib-poll-interval", + &priv->mib_poll_interval); + if (ret) + priv->mib_poll_interval = AR8XXX_MIB_WORK_DELAY; + + ret = ar8xxx_read_id(priv); + if (ret) + goto free_priv; + + ret = ar8xxx_probe_switch(priv); + if (ret) + goto free_priv; + + if (priv->chip->phy_read && priv->chip->phy_write) { + priv->sw_mii_bus = devm_mdiobus_alloc(&mdiodev->dev); + priv->sw_mii_bus->name = "ar8xxx-mdio"; + priv->sw_mii_bus->read = ar8xxx_phy_read; + priv->sw_mii_bus->write = ar8xxx_phy_write; + priv->sw_mii_bus->priv = priv; + priv->sw_mii_bus->parent = &mdiodev->dev; + snprintf(priv->sw_mii_bus->id, MII_BUS_ID_SIZE, "%s", + dev_name(&mdiodev->dev)); + mdio_node = of_get_child_by_name(priv->pdev->of_node, "mdio-bus"); + ret = of_mdiobus_register(priv->sw_mii_bus, mdio_node); + if (ret) + goto free_priv; + } + + swdev = &priv->dev; + swdev->alias = dev_name(&mdiodev->dev); + + if (of_property_read_bool(priv->pdev->of_node, "qca,phy4-mii-enable")) { + priv->port4_phy = true; + swdev->ports--; + } + + ret = register_switch(swdev, NULL); + if (ret) + goto free_priv; + + pr_info("%s: %s rev. %u switch registered on %s\n", + swdev->devname, swdev->name, priv->chip_rev, + dev_name(&priv->mii_bus->dev)); + + mutex_lock(&ar8xxx_dev_list_lock); + list_add(&priv->list, &ar8xxx_dev_list); + mutex_unlock(&ar8xxx_dev_list_lock); + + priv->use_count++; + + ret = ar8xxx_start(priv); + if (ret) + goto err_unregister_switch; + + dev_set_drvdata(&mdiodev->dev, priv); + + return 0; + +err_unregister_switch: + if (--priv->use_count) + return ret; + + unregister_switch(&priv->dev); + +free_priv: + ar8xxx_free(priv); + return ret; +} + +static void +ar8xxx_mdiodev_remove(struct mdio_device *mdiodev) +{ + struct ar8xxx_priv *priv = dev_get_drvdata(&mdiodev->dev); + + if (WARN_ON(!priv)) + return; + + mutex_lock(&ar8xxx_dev_list_lock); + + if (--priv->use_count > 0) { + mutex_unlock(&ar8xxx_dev_list_lock); + return; + } + + list_del(&priv->list); + mutex_unlock(&ar8xxx_dev_list_lock); + + unregister_switch(&priv->dev); + ar8xxx_mib_stop(priv); + if(priv->sw_mii_bus) + mdiobus_unregister(priv->sw_mii_bus); + ar8xxx_free(priv); +} + +static struct mdio_driver ar8xxx_mdio_driver = { + .probe = ar8xxx_mdiodev_probe, + .remove = ar8xxx_mdiodev_remove, + .mdiodrv.driver = { + .name = "ar8xxx-switch", + .of_match_table = ar8xxx_mdiodev_of_match, + }, }; -int __init -ar8xxx_init(void) +static int __init ar8216_init(void) { - return phy_driver_register(&ar8xxx_phy_driver); + int ret; + + ret = phy_drivers_register(ar8xxx_phy_driver, + ARRAY_SIZE(ar8xxx_phy_driver), + THIS_MODULE); + if (ret) + return ret; + + ret = mdio_driver_register(&ar8xxx_mdio_driver); + if (ret) + phy_drivers_unregister(ar8xxx_phy_driver, + ARRAY_SIZE(ar8xxx_phy_driver)); + + return ret; } +module_init(ar8216_init); -void __exit -ar8xxx_exit(void) +static void __exit ar8216_exit(void) { - phy_driver_unregister(&ar8xxx_phy_driver); + mdio_driver_unregister(&ar8xxx_mdio_driver); + phy_drivers_unregister(ar8xxx_phy_driver, + ARRAY_SIZE(ar8xxx_phy_driver)); } +module_exit(ar8216_exit); -module_init(ar8xxx_init); -module_exit(ar8xxx_exit); MODULE_LICENSE("GPL"); -