From cb7d96499cfef4b6816a2e617ff858a556d64b5a Mon Sep 17 00:00:00 2001 From: Chuanhong Guo Date: Thu, 10 Jan 2019 18:55:27 +0800 Subject: generic: ar8216: add support for ar8229 ar8229 is the builtin switch in ar934x and later chips. There is also a standalone version available and their registers/functions are the same. This commit added support for the builtin ar8229. The only thing missing for standalone ar8229 should be phy modes. Since I don't have a router using that, this commit doesn't add support for other phy modes. Only add its support for mdio-device probing method because the current PHY probing can't return 1G speed when it's a FE switch. Signed-off-by: Chuanhong Guo --- .../linux/generic/files/drivers/net/phy/ar8216.c | 130 ++++++++++++++++++++- .../linux/generic/files/drivers/net/phy/ar8216.h | 19 +++ 2 files changed, 144 insertions(+), 5 deletions(-) (limited to 'target') diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c index d091f60dac..85f7c2ab85 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.c +++ b/target/linux/generic/files/drivers/net/phy/ar8216.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -715,7 +716,8 @@ ar8216_init_globals(struct ar8xxx_priv *priv) } 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 */ ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port), @@ -727,12 +729,11 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) if (port == AR8216_PORT_CPU) { ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port), AR8216_PORT_STATUS_LINK_UP | - (ar8xxx_has_gige(priv) ? - AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) | + (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 { ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port), @@ -740,6 +741,13 @@ ar8216_init_port(struct ar8xxx_priv *priv, int port) } } +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) { @@ -807,6 +815,85 @@ static void ar8216_get_arl_entry(struct ar8xxx_priv *priv, } } +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); + + 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 void ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members) { @@ -1753,6 +1840,36 @@ static const struct ar8xxx_chip ar8216_chip = { .mib_func = AR8216_REG_MIB_FUNC }; +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, + .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 +}; + static const struct ar8xxx_chip ar8236_chip = { .caps = AR8XXX_CAP_MIB_COUNTERS, @@ -2336,6 +2453,9 @@ static struct phy_driver ar8xxx_phy_driver[] = { static const struct of_device_id ar8xxx_mdiodev_of_match[] = { { + .compatible = "qca,ar8229", + .data = &ar8229_chip, + }, { .compatible = "qca,ar8236", .data = &ar8236_chip, }, { diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h index 140c217c7c..4d2d8b5dbd 100644 --- a/target/linux/generic/files/drivers/net/phy/ar8216.h +++ b/target/linux/generic/files/drivers/net/phy/ar8216.h @@ -52,6 +52,8 @@ #define AR8216_REG_FLOOD_MASK 0x002C #define AR8216_FM_UNI_DEST_PORTS BITS(0, 6) #define AR8216_FM_MULTI_DEST_PORTS BITS(16, 6) +#define AR8229_FLOOD_MASK_MC_DP(_p) BIT(16 + (_p)) +#define AR8229_FLOOD_MASK_BC_DP(_p) BIT(25 + (_p)) #define AR8236_FM_CPU_BROADCAST_EN BIT(26) #define AR8236_FM_CPU_BCAST_FWD_EN BIT(25) @@ -127,6 +129,12 @@ #define AR8216_ATU_CTRL_AGE_TIME BITS(0, 16) #define AR8216_ATU_CTRL_AGE_TIME_S 0 #define AR8236_ATU_CTRL_RES BIT(20) +#define AR8216_ATU_CTRL_LEARN_CHANGE BIT(18) + +#define AR8216_REG_TAG_PRIORITY 0x0070 + +#define AR8216_REG_SERVICE_TAG 0x0074 +#define AR8216_SERVICE_TAG_M BITS(0, 16) #define AR8216_REG_MIB_FUNC 0x0080 #define AR8216_MIB_TIMER BITS(0, 16) @@ -142,6 +150,7 @@ #define AR8216_REG_GLOBAL_CPUPORT 0x0078 #define AR8216_GLOBAL_CPUPORT_MIRROR_PORT BITS(4, 4) #define AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S 4 +#define AR8216_GLOBAL_CPUPORT_EN BIT(8) #define AR8216_PORT_OFFSET(_i) (0x0100 * (_i + 1)) #define AR8216_REG_PORT_STATUS(_i) (AR8216_PORT_OFFSET(_i) + 0x0000) @@ -237,6 +246,16 @@ #define AR8216_STATS_TXDEFER 0x98 #define AR8216_STATS_TXLATECOL 0x9c +#define AR8229_REG_OPER_MODE0 0x04 +#define AR8229_OPER_MODE0_MAC_GMII_EN BIT(6) +#define AR8229_OPER_MODE0_PHY_MII_EN BIT(10) + +#define AR8229_REG_OPER_MODE1 0x08 +#define AR8229_REG_OPER_MODE1_PHY4_MII_EN BIT(28) + +#define AR8229_REG_QM_CTRL 0x3c +#define AR8229_QM_CTRL_ARP_EN BIT(15) + #define AR8236_REG_PORT_VLAN(_i) (AR8216_PORT_OFFSET((_i)) + 0x0008) #define AR8236_PORT_VLAN_DEFAULT_ID BITS(16, 12) #define AR8236_PORT_VLAN_DEFAULT_ID_S 16 -- cgit v1.2.3