diff options
author | Felix Fietkau <nbd@openwrt.org> | 2015-01-05 13:03:07 +0000 |
---|---|---|
committer | Felix Fietkau <nbd@openwrt.org> | 2015-01-05 13:03:07 +0000 |
commit | dbe77b94a09e9965c4ef031b79b5276096bac3f6 (patch) | |
tree | 274a5bff11e1b94cbcb2cb0380e78ac83c65a9de /target/linux/generic/files/drivers/net/phy | |
parent | 724b3b940a5230098800deb9718fa0e1deef698f (diff) | |
download | upstream-dbe77b94a09e9965c4ef031b79b5276096bac3f6.tar.gz upstream-dbe77b94a09e9965c4ef031b79b5276096bac3f6.tar.bz2 upstream-dbe77b94a09e9965c4ef031b79b5276096bac3f6.zip |
ar8216: factor out AR8327/AR8337-specific driver code into ar8327.c
Move all AR8327/AR8337-specific driver code into a separate source file
ar8327.c and adjust patches so that ar8327.c is compiled if
CONFIG_AR8216_PHY is set.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@43845 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/generic/files/drivers/net/phy')
-rw-r--r-- | target/linux/generic/files/drivers/net/phy/ar8216.c | 1059 | ||||
-rw-r--r-- | target/linux/generic/files/drivers/net/phy/ar8216.h | 76 | ||||
-rw-r--r-- | target/linux/generic/files/drivers/net/phy/ar8327.c | 1029 |
3 files changed, 1134 insertions, 1030 deletions
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index d5a900a32c..b88e9ec459 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -33,13 +33,12 @@ #include <linux/lockdep.h> #include <linux/ar8216_platform.h> #include <linux/workqueue.h> -#include <linux/of_device.h> -#include <linux/leds.h> -#include <linux/gpio.h> #include <linux/version.h> #include "ar8216.h" -#include "ar8327.h" + +extern const struct ar8xxx_chip ar8327_chip; +extern const struct ar8xxx_chip ar8337_chip; #define AR8XXX_MIB_WORK_DELAY 2000 /* msecs */ @@ -90,7 +89,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"), @@ -196,7 +195,7 @@ ar8xxx_phy_check_aneg(struct phy_device *phydev) return phy_write(phydev, MII_BMCR, ret); } -static void +void ar8xxx_phy_init(struct ar8xxx_priv *priv) { int i; @@ -249,7 +248,7 @@ mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val) } } -static u32 +u32 ar8xxx_read(struct ar8xxx_priv *priv, int reg) { struct mii_bus *bus = priv->mii_bus; @@ -269,7 +268,7 @@ ar8xxx_read(struct ar8xxx_priv *priv, int reg) return val; } -static void +void ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val) { struct mii_bus *bus = priv->mii_bus; @@ -286,7 +285,7 @@ ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val) mutex_unlock(&bus->mdio_lock); } -static u32 +u32 ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) { struct mii_bus *bus = priv->mii_bus; @@ -310,7 +309,7 @@ ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val) return ret; } -static void +void ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, u16 dbg_addr, u16 dbg_data) { @@ -322,7 +321,7 @@ ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, mutex_unlock(&bus->mdio_lock); } -static void +void ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data) { struct mii_bus *bus = priv->mii_bus; @@ -333,12 +332,6 @@ ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data) mutex_unlock(&bus->mdio_lock); } -static inline void -ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val) -{ - ar8xxx_rmw(priv, reg, 0, val); -} - static int ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val, unsigned timeout) @@ -545,7 +538,7 @@ 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; @@ -831,780 +824,7 @@ ar8316_init_globals(struct ar8xxx_priv *priv) AR8236_MIB_EN); } -static u32 -ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) -{ - u32 t; - - if (!cfg) - return 0; - - t = 0; - switch (cfg->mode) { - case AR8327_PAD_NC: - break; - - case AR8327_PAD_MAC2MAC_MII: - t = AR8327_PAD_MAC_MII_EN; - if (cfg->rxclk_sel) - t |= AR8327_PAD_MAC_MII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_MAC_MII_TXCLK_SEL; - break; - - case AR8327_PAD_MAC2MAC_GMII: - t = AR8327_PAD_MAC_GMII_EN; - if (cfg->rxclk_sel) - t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; - break; - - case AR8327_PAD_MAC_SGMII: - t = AR8327_PAD_SGMII_EN; - - /* - * WAR for the QUalcomm Atheros AP136 board. - * It seems that RGMII TX/RX delay settings needs to be - * applied for SGMII mode as well, The ethernet is not - * reliable without this. - */ - t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; - t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; - if (cfg->rxclk_delay_en) - t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; - if (cfg->txclk_delay_en) - t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; - - if (cfg->sgmii_delay_en) - t |= AR8327_PAD_SGMII_DELAY_EN; - - break; - - case AR8327_PAD_MAC2PHY_MII: - t = AR8327_PAD_PHY_MII_EN; - if (cfg->rxclk_sel) - t |= AR8327_PAD_PHY_MII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_PHY_MII_TXCLK_SEL; - break; - - case AR8327_PAD_MAC2PHY_GMII: - t = AR8327_PAD_PHY_GMII_EN; - if (cfg->pipe_rxclk_sel) - t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; - if (cfg->rxclk_sel) - t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; - if (cfg->txclk_sel) - t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; - break; - - case AR8327_PAD_MAC_RGMII: - t = AR8327_PAD_RGMII_EN; - t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; - t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; - if (cfg->rxclk_delay_en) - t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; - if (cfg->txclk_delay_en) - t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; - break; - - case AR8327_PAD_PHY_GMII: - t = AR8327_PAD_PHYX_GMII_EN; - break; - - case AR8327_PAD_PHY_RGMII: - t = AR8327_PAD_PHYX_RGMII_EN; - break; - - case AR8327_PAD_PHY_MII: - t = AR8327_PAD_PHYX_MII_EN; - break; - } - - return t; -} - -static void -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; - - ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t); - t = ar8327_get_pad_cfg(pdata->pad5_cfg); - ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t); - t = ar8327_get_pad_cfg(pdata->pad6_cfg); - ar8xxx_write(priv, AR8327_REG_PAD6_MODE, t); - - pos = ar8xxx_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; - - ar8xxx_write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0); - ar8xxx_write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1); - ar8xxx_write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2); - ar8xxx_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); - - ar8xxx_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; - } - - ar8xxx_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: - ar8xxx_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; - ar8xxx_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); - ar8xxx_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 = ar8xxx_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); - ar8xxx_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; - - ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t); - ar8xxx_write(priv, AR8327_REG_PORT_HEADER(port), 0); - - t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; - t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; - ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t); - - t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; - ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t); - - t = AR8327_PORT_LOOKUP_LEARN; - t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; - ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t); -} - -static u32 -ar8327_read_port_status(struct ar8xxx_priv *priv, int port) -{ - return ar8xxx_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) - ar8xxx_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) - ar8xxx_write(priv, AR8327_REG_VTU_FUNC0, val); - - op |= AR8327_VTU_FUNC1_BUSY; - ar8xxx_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; - ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t); - - t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; - t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S; - ar8xxx_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; - ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t); -} - -static int +int ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { @@ -1613,7 +833,7 @@ ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, return 0; } -static int +int ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val) { @@ -1623,7 +843,7 @@ ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, } -static int +int ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); @@ -1637,7 +857,7 @@ ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int 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); @@ -1663,7 +883,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) { @@ -1698,30 +918,6 @@ ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val) } 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; - - 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)) || (priv->pvid[i] != val->port_vlan)) - p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); - else - p->flags = 0; - } - return 0; -} - -static int ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); @@ -1752,72 +948,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) { @@ -1859,7 +989,7 @@ ar8216_set_mirror_regs(struct ar8xxx_priv *priv) AR8216_PORT_CTRL_MIRROR_TX); } -static int +int ar8xxx_sw_hw_apply(struct switch_dev *dev) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); @@ -1912,7 +1042,7 @@ ar8xxx_sw_hw_apply(struct switch_dev *dev) return 0; } -static int +int ar8xxx_sw_reset_switch(struct switch_dev *dev) { struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev); @@ -1941,7 +1071,7 @@ ar8xxx_sw_reset_switch(struct switch_dev *dev) return ar8xxx_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) @@ -1969,7 +1099,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) @@ -1984,7 +1114,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) @@ -1994,7 +1124,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) @@ -2009,7 +1139,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) @@ -2019,7 +1149,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) @@ -2034,7 +1164,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) @@ -2044,7 +1174,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) @@ -2059,7 +1189,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) @@ -2196,56 +1326,7 @@ static struct switch_attr ar8xxx_sw_attr_globals[] = { }, }; -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 ar8xxx_sw_attr_port[] = { +struct switch_attr ar8xxx_sw_attr_port[2] = { { .type = SWITCH_TYPE_NOVAL, .name = "reset_mib", @@ -2261,7 +1342,7 @@ static struct switch_attr ar8xxx_sw_attr_port[] = { }, }; -static struct switch_attr ar8xxx_sw_attr_vlan[] = { +struct switch_attr ar8xxx_sw_attr_vlan[1] = { { .type = SWITCH_TYPE_INT, .name = "vid", @@ -2294,28 +1375,6 @@ static const struct switch_dev_ops ar8xxx_sw_ops = { .get_port_link = ar8xxx_sw_get_port_link, }; -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 ar8216_chip = { .caps = AR8XXX_CAP_MIB_COUNTERS, @@ -2394,66 +1453,6 @@ static const struct ar8xxx_chip ar8316_chip = { .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 -}; - static int ar8xxx_id_chip(struct ar8xxx_priv *priv) { diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index 3d2e6e4bd9..50f225258f 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -419,6 +419,76 @@ struct ar8xxx_priv { int monitor_port; }; +u32 +ar8xxx_read(struct ar8xxx_priv *priv, int reg); +void +ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val); +u32 +ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val); + +void +ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr, + u16 dbg_addr, u16 dbg_data); +void +ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 data); +void +ar8xxx_phy_init(struct ar8xxx_priv *priv); +int +ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_set_reset_mibs(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev, + const struct switch_attr *attr, + struct switch_val *val); +int +ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan); +int +ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan); +int +ar8xxx_sw_hw_apply(struct switch_dev *dev); +int +ar8xxx_sw_reset_switch(struct switch_dev *dev); +int +ar8xxx_sw_get_port_link(struct switch_dev *dev, int port, + struct switch_port_link *link); +int +ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val); + static inline struct ar8xxx_priv * swdev_to_ar8xxx(struct switch_dev *swdev) { @@ -460,4 +530,10 @@ static inline bool chip_is_ar8337(struct ar8xxx_priv *priv) return priv->chip_ver == AR8XXX_VER_AR8337; } +static inline void +ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val) +{ + ar8xxx_rmw(priv, reg, 0, val); +} + #endif diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.c b/target/linux/generic/files/drivers/net/phy/ar8327.c new file mode 100644 index 0000000000..77ee4bf809 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/ar8327.c @@ -0,0 +1,1029 @@ +/* + * ar8327.c: AR8216 switch driver + * + * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/list.h> +#include <linux/bitops.h> +#include <linux/switch.h> +#include <linux/delay.h> +#include <linux/phy.h> +#include <linux/lockdep.h> +#include <linux/ar8216_platform.h> +#include <linux/workqueue.h> +#include <linux/of_device.h> +#include <linux/leds.h> + +#include "ar8216.h" +#include "ar8327.h" + +extern const struct ar8xxx_mib_desc ar8236_mibs[39]; +extern struct switch_attr ar8xxx_sw_attr_port[2]; +extern struct switch_attr ar8xxx_sw_attr_vlan[1]; + +static u32 +ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg) +{ + u32 t; + + if (!cfg) + return 0; + + t = 0; + switch (cfg->mode) { + case AR8327_PAD_NC: + break; + + case AR8327_PAD_MAC2MAC_MII: + t = AR8327_PAD_MAC_MII_EN; + if (cfg->rxclk_sel) + t |= AR8327_PAD_MAC_MII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_MAC_MII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC2MAC_GMII: + t = AR8327_PAD_MAC_GMII_EN; + if (cfg->rxclk_sel) + t |= AR8327_PAD_MAC_GMII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_MAC_GMII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC_SGMII: + t = AR8327_PAD_SGMII_EN; + + /* + * WAR for the QUalcomm Atheros AP136 board. + * It seems that RGMII TX/RX delay settings needs to be + * applied for SGMII mode as well, The ethernet is not + * reliable without this. + */ + t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; + t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; + if (cfg->rxclk_delay_en) + t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; + if (cfg->txclk_delay_en) + t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; + + if (cfg->sgmii_delay_en) + t |= AR8327_PAD_SGMII_DELAY_EN; + + break; + + case AR8327_PAD_MAC2PHY_MII: + t = AR8327_PAD_PHY_MII_EN; + if (cfg->rxclk_sel) + t |= AR8327_PAD_PHY_MII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_PHY_MII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC2PHY_GMII: + t = AR8327_PAD_PHY_GMII_EN; + if (cfg->pipe_rxclk_sel) + t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL; + if (cfg->rxclk_sel) + t |= AR8327_PAD_PHY_GMII_RXCLK_SEL; + if (cfg->txclk_sel) + t |= AR8327_PAD_PHY_GMII_TXCLK_SEL; + break; + + case AR8327_PAD_MAC_RGMII: + t = AR8327_PAD_RGMII_EN; + t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S; + t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S; + if (cfg->rxclk_delay_en) + t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN; + if (cfg->txclk_delay_en) + t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN; + break; + + case AR8327_PAD_PHY_GMII: + t = AR8327_PAD_PHYX_GMII_EN; + break; + + case AR8327_PAD_PHY_RGMII: + t = AR8327_PAD_PHYX_RGMII_EN; + break; + + case AR8327_PAD_PHY_MII: + t = AR8327_PAD_PHYX_MII_EN; + break; + } + + return t; +} + +static void +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; + + ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t); + t = ar8327_get_pad_cfg(pdata->pad5_cfg); + ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t); + t = ar8327_get_pad_cfg(pdata->pad6_cfg); + ar8xxx_write(priv, AR8327_REG_PAD6_MODE, t); + + pos = ar8xxx_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; + + ar8xxx_write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0); + ar8xxx_write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1); + ar8xxx_write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2); + ar8xxx_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); + + ar8xxx_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; + } + + ar8xxx_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: + ar8xxx_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; + ar8xxx_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); + ar8xxx_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 = ar8xxx_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); + ar8xxx_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; + + ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t); + ar8xxx_write(priv, AR8327_REG_PORT_HEADER(port), 0); + + t = 1 << AR8327_PORT_VLAN0_DEF_SVID_S; + t |= 1 << AR8327_PORT_VLAN0_DEF_CVID_S; + ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t); + + t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S; + ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t); + + t = AR8327_PORT_LOOKUP_LEARN; + t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S; + ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t); +} + +static u32 +ar8327_read_port_status(struct ar8xxx_priv *priv, int port) +{ + return ar8xxx_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) + ar8xxx_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) + ar8xxx_write(priv, AR8327_REG_VTU_FUNC0, val); + + op |= AR8327_VTU_FUNC1_BUSY; + ar8xxx_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; + ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t); + + t = AR8327_PORT_VLAN1_PORT_VLAN_PROP; + t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S; + ar8xxx_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; + ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t); +} + +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; + + 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)) || (priv->pvid[i] != val->port_vlan)) + p->flags = (1 << SWITCH_PORT_FLAG_TAGGED); + else + p->flags = 0; + } + 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 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 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, +}; + +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 +}; + +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 +}; + |