diff --git a/target/linux/generic/files/drivers/net/phy/yt9215.c b/target/linux/generic/files/drivers/net/phy/yt9215.c new file mode 100644 index 0000000000..8e35fef4f0 --- /dev/null +++ b/target/linux/generic/files/drivers/net/phy/yt9215.c @@ -0,0 +1,834 @@ +#include +#include +#include +#include +#include +#include + + +#define RESET_TIMEOUT_US 100000 +#define INTIF_MDIO_TIMEOUT_US 100000 + + +#define YT9215_FAKE_PHY_ID 0xdeaddead +#define YT9215_FAKE_PHY_ID_MASK 0xffffffff +#define YT9215_CHIP_ID 0x90020000 +#define YT9215_CHIP_ID_MASK 0xffff0000 + + +#define MDIO_ADDR_SWITCHID_MASK GENMASK(3, 2) +#define MDIO_ADDR_SWITCHID(x) FIELD_PREP(MDIO_ADDR_SWITCHID_MASK, (x)) +#define MDIO_ADDR_AD_MASK GENMASK(1, 1) +#define MDIO_ADDR_AD(x) FIELD_PREP(MDIO_ADDR_AD_MASK, (x)) +#define MDIO_ADDR_AD_ADDR MDIO_ADDR_AD(0) +#define MDIO_ADDR_AD_DATA MDIO_ADDR_AD(1) +#define MDIO_ADDR_RW_MASK GENMASK(0, 0) +#define MDIO_ADDR_RW(x) FIELD_PREP(MDIO_ADDR_RW_MASK, (x)) +#define MDIO_ADDR_RW_WRITE MDIO_ADDR_RW(0) +#define MDIO_ADDR_RW_READ MDIO_ADDR_RW(1) + + +#define RESET_CTRL 0x80000 +#define RESET_CTRL_HW BIT(31) +#define RESET_CTRL_SW BIT(1) + +#define FUNC_CTRL 0x80004 +#define FUNC_CTRL_MIB BIT(1) + +#define CHIP_ID 0x80008 + +#define PORT_IGR_LOOKUP_SVLAN 0x80014 + +#define EXTIF_CTRL 0x80028 +#define EXTIF_CTRL_EXTIF1_ENABLE BIT(1) +#define EXTIF_CTRL_EXTIF0_ENABLE BIT(0) + +#define SGMII_CTRL(port) (0x8008c + 4 * (port)) +#define SGMII_CTRL_MODE_MASK GENMASK(9, 7) +#define SGMII_CTRL_MODE(x) FIELD_PREP(SGMII_CTRL_MODE_MASK, (x)) +#define SGMII_CTRL_MODE_SGMII_MAC SGMII_CTRL_MODE(0) +#define SGMII_CTRL_MODE_SGMII_PHY SGMII_CTRL_MODE(1) +#define SGMII_CTRL_MODE_1000BASEX SGMII_CTRL_MODE(2) +#define SGMII_CTRL_MODE_100BASEX SGMII_CTRL_MODE(3) +#define SGMII_CTRL_MODE_2500BASEX SGMII_CTRL_MODE(4) +#define SGMII_CTRL_MODE_BASEX SGMII_CTRL_MODE(5) +#define SGMII_CTRL_MODE_DISABLE SGMII_CTRL_MODE(6) +#define SGMII_CTRL_LINK BIT(4) +#define SGMII_CTRL_DUPLEX_MASK GENMASK(3, 3) +#define SGMII_CTRL_DUPLEX(x) FIELD_PREP(SGMII_CTRL_DUPLEX_MASK, (x)) +#define SGMII_CTRL_DUPLEX_HALF SGMII_CTRL_DUPLEX(0) +#define SGMII_CTRL_DUPLEX_FULL SGMII_CTRL_DUPLEX(1) +#define SGMII_CTRL_SPEED_MASK GENMASK(2, 0) +#define SGMII_CTRL_SPEED(x) FIELD_PREP(SGMII_CTRL_SPEED_MASK, (x)) +#define SGMII_CTRL_SPEED_10 SGMII_CTRL_SPEED(0) +#define SGMII_CTRL_SPEED_100 SGMII_CTRL_SPEED(1) +#define SGMII_CTRL_SPEED_1000 SGMII_CTRL_SPEED(2) +#define SGMII_CTRL_SPEED_2500 SGMII_CTRL_SPEED(4) + +#define PORT_CTRL(port) (0x80100 + 4 * (port)) +#define PORT_CTRL_FLOWCONTROL_AN BIT(10) +#define PORT_CTRL_LINK_AN BIT(9) +#define PORT_CTRL_DUPLEX_MASK GENMASK(7, 7) +#define PORT_CTRL_DUPLEX(x) FIELD_PREP(PORT_CTRL_DUPLEX_MASK, (x)) +#define PORT_CTRL_DUPLEX_HALF PORT_CTRL_DUPLEX(0) +#define PORT_CTRL_DUPLEX_FULL PORT_CTRL_DUPLEX(1) +#define PORT_CTRL_RX_FLOWCONTROL BIT(6) +#define PORT_CTRL_TX_FLOWCONTROL BIT(5) +#define PORT_CTRL_RX_MAC_EN BIT(4) +#define PORT_CTRL_TX_MAC_EN BIT(3) +#define PORT_CTRL_SPEED_MASK GENMASK(2, 0) +#define PORT_CTRL_SPEED(x) FIELD_PREP(PORT_CTRL_SPEED_MASK, (x)) +#define PORT_CTRL_SPEED_10 PORT_CTRL_SPEED(0) +#define PORT_CTRL_SPEED_100 PORT_CTRL_SPEED(1) +#define PORT_CTRL_SPEED_1000 PORT_CTRL_SPEED(2) +#define PORT_CTRL_SPEED_2500 PORT_CTRL_SPEED(4) + +#define PORT_STATUS(port) (0x80200 + 4 * (port)) +#define PORT_STATUS_LINK BIT(8) +#define PORT_STATUS_DUPLEX_MASK GENMASK(7, 7) +#define PORT_STATUS_DUPLEX(x) FIELD_PREP(PORT_STATUS_DUPLEX_MASK, (x)) +#define PORT_STATUS_DUPLEX_HALF PORT_STATUS_DUPLEX(0) +#define PORT_STATUS_DUPLEX_FULL PORT_STATUS_DUPLEX(1) +#define PORT_STATUS_RX_FLOWCONTROL BIT(6) +#define PORT_STATUS_TX_FLOWCONTROL BIT(5) +#define PORT_STATUS_RX_MAC_EN BIT(4) +#define PORT_STATUS_TX_MAC_EN BIT(3) +#define PORT_STATUS_SPEED_MASK GENMASK(2, 0) +#define PORT_STATUS_SPEED(x) FIELD_PREP(PORT_STATUS_SPEED_MASK, (x)) +#define PORT_STATUS_SPEED_10 PORT_STATUS_SPEED(0) +#define PORT_STATUS_SPEED_100 PORT_STATUS_SPEED(1) +#define PORT_STATUS_SPEED_1000 PORT_STATUS_SPEED(2) +#define PORT_STATUS_SPEED_2500 PORT_STATUS_SPEED(4) + +#define EXTIF_SEL 0x80394 +#define EXTIF_SEL_EXTIF0_MASK GENMASK(1, 1) +#define EXTIF_SEL_EXTIF0(x) FIELD_PREP(EXTIF_SEL_EXTIF0_MASK, (x)) +#define EXTIF_SEL_EXTIF0_SERDES EXTIF_SEL_EXTIF0(0) +#define EXTIF_SEL_EXTIF0_XMII EXTIF_SEL_EXTIF0(1) +#define EXTIF_SEL_EXTIF1_MASK GENMASK(0, 0) +#define EXTIF_SEL_EXTIF1(x) FIELD_PREP(EXTIF_SEL_EXTIF1_MASK, (x)) +#define EXTIF_SEL_EXTIF1_SERDES EXTIF_SEL_EXTIF1(0) +#define EXTIF_SEL_EXTIF1_XMII EXTIF_SEL_EXTIF1(1) + +#define MIB_CTRL 0xc0004 +#define MIB_CTRL_OP_CLEAN BIT(30) +#define MIB_CTRL_PORT_MASK GENMASK(6, 3) +#define MIB_CTRL_PORT(port) FIELD_PREP(MIB_CTRL_PORT_MASK, (port)) +#define MIB_CTRL_PORT_SEL_MASK GENMASK(1, 0) +#define MIB_CTRL_PORT_SEL(x) FIELD_PREP(MIB_CTRL_PORT_SEL_MASK, (x)) +#define MIB_CTRL_PORT_SEL_ALL MIB_CTRL_PORT_SEL(0) +#define MIB_CTRL_PORT_SEL_SINGLE MIB_CTRL_PORT_SEL(2) + +#define MIB_DATA(port, offset) (0xc0100 + (0x100 * (port)) + (4 * (offset))) + +#define INTIF_MDIO_OP 0xf0000 +#define INTIF_MDIO_OP_IDLE 0 +#define INTIF_MDIO_OP_DO 1 + +#define INTIF_MDIO_CTRL 0xf0004 +#define INTIF_MDIO_CTRL_ADDR_MASK GENMASK(25, 21) +#define INTIF_MDIO_CTRL_ADDR(x) FIELD_PREP(INTIF_MDIO_CTRL_ADDR_MASK, (x)) +#define INTIF_MDIO_CTRL_REG_MASK GENMASK(20, 16) +#define INTIF_MDIO_CTRL_REG(x) FIELD_PREP(INTIF_MDIO_CTRL_REG_MASK, (x)) +#define INTIF_MDIO_CTRL_OP_MASK GENMASK(3, 2) +#define INTIF_MDIO_CTRL_OP(x) FIELD_PREP(INTIF_MDIO_CTRL_OP_MASK, (x)) +#define INTIF_MDIO_CTRL_OP_WRITE INTIF_MDIO_CTRL_OP(1) +#define INTIF_MDIO_CTRL_OP_READ INTIF_MDIO_CTRL_OP(2) + +#define INTIF_MDIO_WRITE_DATA 0xf0008 + +#define INTIF_MDIO_READ_DATA 0xf000c + +#define PORT_EGR_CTRL(port) (0x100000 + 4 * (port)) +#define PORT_EGR_CTRL_CTAG_TPID_SEL_MASK GENMASK(5, 4) +#define PORT_EGR_CTRL_CTAG_TPID_SEL(x) FIELD_PREP(PORT_EGR_CTRL_CTAG_TPID_SEL_MASK, (x)) +#define PORT_EGR_CTRL_STAG_TPID_SEL_MASK GENMASK(3, 2) +#define PORT_EGR_CTRL_STAG_TPID_SEL(x) FIELD_PREP(PORT_EGR_CTRL_STAG_TPID_SEL_MASK, (x)) + +#define PORT_EGR_VLAN(port) (0x100080 + 4 * (port)) +#define PORT_EGR_VLAN_STAG_KEEP_MASK GENMASK(31, 31) +#define PORT_EGR_VLAN_STAG_KEEP(x) FIELD_PREP(PORT_EGR_VLAN_STAG_KEEP_MASK, (x)) +#define PORT_EGR_VLAN_STAG_KEEP_ALL PORT_EGR_VLAN_STAG_KEEP(0) +#define PORT_EGR_VLAN_STAG_KEEP_TAG_MODE PORT_EGR_VLAN_STAG_KEEP(1) +#define PORT_EGR_VLAN_CTAG_KEEP_MASK GENMASK(30, 30) +#define PORT_EGR_VLAN_CTAG_KEEPK(x) FIELD_PREP(PORT_EGR_VLAN_CTAG_KEEP_MASK, (x)) +#define PORT_EGR_VLAN_CTAG_KEEP_ALL PORT_EGR_VLAN_CTAG_KEEP(0) +#define PORT_EGR_VLAN_CTAG_KEEP_TAG_MODE PORT_EGR_VLAN_CTAG_KEEP(1) +#define PORT_EGR_VLAN_STAG_MODE_MASK GENMASK(29, 27) +#define PORT_EGR_VLAN_STAG_MODE(x) FIELD_PREP(PORT_EGR_VLAN_STAG_MODE_MASK, (x)) +#define PORT_EGR_VLAN_STAG_MODE_UNTAG PORT_EGR_VLAN_STAG_MODE(0) +#define PORT_EGR_VLAN_STAG_MODE_TAG PORT_EGR_VLAN_STAG_MODE(1) +#define PORT_EGR_VLAN_STAG_MODE_TAG_EXCEPT_PVID PORT_EGR_VLAN_STAG_MODE(2) +#define PORT_EGR_VLAN_STAG_MODE_PRIO_TAG PORT_EGR_VLAN_STAG_MODE(3) +#define PORT_EGR_VLAN_STAG_MODE_KEEP PORT_EGR_VLAN_STAG_MODE(4) +#define PORT_EGR_VLAN_STAG_MODE_LOOKUP PORT_EGR_VLAN_STAG_MODE(5) +#define PORT_EGR_VLAN_PVID_SVID_MASK GENMASK(26, 15) +#define PORT_EGR_VLAN_PVID_SVID(x) FIELD_PREP(PORT_EGR_VLAN_PVID_SVID_MASK, (x)) +#define PORT_EGR_VLAN_CTAG_MODE_MASK GENMASK(14, 12) +#define PORT_EGR_VLAN_CTAG_MODE(x) FIELD_PREP(PORT_EGR_VLAN_CTAG_MODE_MASK, (x)) +#define PORT_EGR_VLAN_CTAG_MODE_UNTAG PORT_EGR_VLAN_CTAG_MODE(0) +#define PORT_EGR_VLAN_CTAG_MODE_TAG PORT_EGR_VLAN_CTAG_MODE(1) +#define PORT_EGR_VLAN_CTAG_MODE_TAG_EXCEPT_PVID PORT_EGR_VLAN_CTAG_MODE(2) +#define PORT_EGR_VLAN_CTAG_MODE_PRIO_TAG PORT_EGR_VLAN_CTAG_MODE(3) +#define PORT_EGR_VLAN_CTAG_MODE_KEEP PORT_EGR_VLAN_CTAG_MODE(4) +#define PORT_EGR_VLAN_CTAG_MODE_LOOKUP PORT_EGR_VLAN_CTAG_MODE(5) +#define PORT_EGR_VLAN_PVID_CVID_MASK GENMASK(11, 0) +#define PORT_EGR_VLAN_PVID_CVID(x) FIELD_PREP(PORT_EGR_VLAN_PVID_CVID_MASK, (x)) + +#define EGR_TPID(x) (0x100300 + 4 * (x)) + +#define PORT_IGR_VLAN_FILTER 0x180280 + +#define PORT_EGR_VLAN_FILTER 0x180598 + +#define VLAN_CTRL1(vlan) (0x188000 + 8 * (vlan)) +#define VLAN_CTRL1_MEMBER_MASK GENMASK(17, 7) +#define VLAN_CTRL1_MEMBER(x) FIELD_PREP(VLAN_CTRL1_MEMBER_MASK, (x)) + +#define VLAN_CTRL2(vlan) (0x188004 + 8 * (vlan)) +#define VLAN_CTRL2_UNTAG_MEMBER_MASK GENMASK(18, 8) +#define VLAN_CTRL2_UNTAG_MEMBER(x) FIELD_PREP(VLAN_CTRL2_UNTAG_MEMBER_MASK, (x)) + +#define IGR_TPID(x) (0x210000 + 4 * (x)) + +#define PORT_IGR_TPID(port) (0x210010 + 4 * (port)) +#define PORT_IGR_TPID_STAG_BITMAP_MASK GENMASK(7, 4) +#define PORT_IGR_TPID_STAG_BITMAP(x) FIELD_PREP(PORT_IGR_TPID_STAG_BITMAP_MASK, (x)) +#define PORT_IGR_TPID_CTAG_BITMAP_MASK GENMASK(3, 0) +#define PORT_IGR_TPID_CTAG_BITMAP(x) FIELD_PREP(PORT_IGR_TPID_CTAG_BITMAP_MASK, (x)) + +#define PORT_IGR_PVID(port) (0x230010 + 4 * (port)) +#define PORT_IGR_PVID_SVID_MASK GENMASK(29, 18) +#define PORT_IGR_PVID_SVID(x) FIELD_PREP(PORT_IGR_PVID_SVID_MASK, (x)) +#define PORT_IGR_PVID_CVID_MASK GENMASK(17, 6) +#define PORT_IGR_PVID_CVID(x) FIELD_PREP(PORT_IGR_PVID_CVID_MASK, (x)) + + +struct yt9215_priv { + struct mdio_device *mdiodev; + int switchid; + + struct mii_bus *int_mdio_bus; + + struct switch_dev swdev; +}; + + +static u32 yt9215_smi_read(struct yt9215_priv *priv, int reg) +{ + struct mii_bus *bus = priv->mdiodev->bus; + int addr = priv->mdiodev->addr; + u32 val; + + mutex_lock(&bus->mdio_lock); + bus->write(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_ADDR | MDIO_ADDR_RW_READ), ((reg >> 16) & 0xffff)); + bus->write(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_ADDR | MDIO_ADDR_RW_READ), ((reg >> 0) & 0xffff)); + val = (bus->read(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_DATA | MDIO_ADDR_RW_READ)) & 0xffff) << 16; + val |= bus->read(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_DATA | MDIO_ADDR_RW_READ)) & 0xffff; + mutex_unlock(&bus->mdio_lock); + + return val; +} + +static int yt9215_smi_write(struct yt9215_priv *priv, int reg, u32 val) +{ + struct mii_bus *bus = priv->mdiodev->bus; + int addr = priv->mdiodev->addr; + + mutex_lock(&bus->mdio_lock); + bus->write(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_ADDR | MDIO_ADDR_RW_WRITE), ((reg >> 16) & 0xffff)); + bus->write(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_ADDR | MDIO_ADDR_RW_WRITE), ((reg >> 0) & 0xffff)); + bus->write(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_DATA | MDIO_ADDR_RW_WRITE), ((val >> 16) & 0xffff)); + bus->write(bus, addr, (MDIO_ADDR_SWITCHID(priv->switchid) | MDIO_ADDR_AD_DATA | MDIO_ADDR_RW_WRITE), ((val >> 0) & 0xffff)); + mutex_unlock(&bus->mdio_lock); + + return 0; +} + +static int yt9215_smi_rmw(struct yt9215_priv *priv, int reg, u32 mask, u32 val) +{ + u32 v; + v = yt9215_smi_read(priv, reg); + v &= ~mask; + v |= val; + return yt9215_smi_write(priv, reg, v); +} + + +static int yt9215_int_mdio_wait(struct yt9215_priv *priv) +{ + u32 val; + return read_poll_timeout(yt9215_smi_read, val, + val == INTIF_MDIO_OP_IDLE, + 1000, INTIF_MDIO_TIMEOUT_US, false, + priv, INTIF_MDIO_OP); +} + +static int yt9215_int_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct yt9215_priv *priv = bus->priv; + int ret; + + ret = yt9215_int_mdio_wait(priv); + if(ret) + return ret; + + yt9215_smi_rmw(priv, INTIF_MDIO_CTRL, + (INTIF_MDIO_CTRL_OP_MASK | INTIF_MDIO_CTRL_ADDR_MASK + | INTIF_MDIO_CTRL_REG_MASK), + (INTIF_MDIO_CTRL_OP_READ | INTIF_MDIO_CTRL_ADDR(addr) + | INTIF_MDIO_CTRL_REG(regnum))); + + yt9215_smi_write(priv, INTIF_MDIO_OP, INTIF_MDIO_OP_DO); + ret = yt9215_int_mdio_wait(priv); + if(ret) + return ret; + + return yt9215_smi_read(priv, INTIF_MDIO_READ_DATA); +} + +static int yt9215_int_mdio_write(struct mii_bus *bus, int addr, int regnum, u16 val) +{ + struct yt9215_priv *priv = bus->priv; + int ret; + + ret = yt9215_int_mdio_wait(priv); + if(ret) + return ret; + + yt9215_smi_rmw(priv, INTIF_MDIO_CTRL, + (INTIF_MDIO_CTRL_OP_MASK | INTIF_MDIO_CTRL_ADDR_MASK + | INTIF_MDIO_CTRL_REG_MASK), + (INTIF_MDIO_CTRL_OP_WRITE | INTIF_MDIO_CTRL_ADDR(addr) + | INTIF_MDIO_CTRL_REG(regnum))); + + yt9215_smi_write(priv, INTIF_MDIO_WRITE_DATA, val); + + yt9215_smi_write(priv, INTIF_MDIO_OP, INTIF_MDIO_OP_DO); + ret = yt9215_int_mdio_wait(priv); + if(ret) + return ret; + + return 0; +} + + +static void yt9215_init_ports(struct yt9215_priv *priv) +{ + struct device_node *ports, *port, *fixed; + struct device *dev = &priv->mdiodev->dev; + phy_interface_t mode; + u32 reg, speed, full_duplex; + u32 val; + + ports = of_get_child_by_name(dev_of_node(dev), "ports"); + if (!ports) + return; + + for_each_available_child_of_node(ports, port) { + speed = 0; + full_duplex = 0; + + if (of_property_read_u32(port, "reg", ®)) + continue; + + if (reg > priv->swdev.ports) + continue; + + val = PORT_CTRL_RX_MAC_EN | PORT_CTRL_TX_MAC_EN; + fixed = of_get_child_by_name(port, "fixed-link"); + if (fixed) { + if (!of_property_read_u32(fixed, "speed", &speed)) { + switch (speed) { + case 10: + val |= PORT_CTRL_SPEED_10; + break; + case 100: + val |= PORT_CTRL_SPEED_100; + break; + case 1000: + val |= PORT_CTRL_SPEED_1000; + break; + case 2500: + val |= PORT_CTRL_SPEED_2500; + break; + } + } + if (of_property_read_bool(fixed, "full-duplex")) { + full_duplex = 1; + val |= PORT_CTRL_DUPLEX_FULL; + } else + val |= PORT_CTRL_DUPLEX_HALF; + if (of_property_read_bool(fixed, "pause")) + val |= PORT_CTRL_RX_FLOWCONTROL; + if (of_property_read_bool(fixed, "asym-pause")) + val |= PORT_CTRL_TX_FLOWCONTROL; + + /* TODO: phy setting */ + } else { /* !fixed */ + val |= PORT_CTRL_LINK_AN | PORT_CTRL_FLOWCONTROL_AN; + } + yt9215_smi_write(priv, PORT_CTRL(reg), val); + + of_get_phy_mode(port, &mode); + switch(mode) { + case PHY_INTERFACE_MODE_INTERNAL: + break; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_2500BASEX: + if (reg != 8) + break; + + yt9215_smi_rmw(priv, EXTIF_CTRL, + EXTIF_CTRL_EXTIF0_ENABLE, + EXTIF_CTRL_EXTIF0_ENABLE); + yt9215_smi_rmw(priv, EXTIF_SEL, + EXTIF_SEL_EXTIF1_MASK, + EXTIF_SEL_EXTIF1_XMII); + yt9215_smi_rmw(priv, EXTIF_SEL, + EXTIF_SEL_EXTIF0_MASK, + EXTIF_SEL_EXTIF0_SERDES); + + val = SGMII_CTRL_LINK; + switch (mode) { + case PHY_INTERFACE_MODE_SGMII: + val |= SGMII_CTRL_MODE_SGMII_PHY; + break; + case PHY_INTERFACE_MODE_2500BASEX: + val |= SGMII_CTRL_MODE_2500BASEX; + break; + default: /* avoid warning */ + break; + } + switch (speed) { + case 10: + val |= SGMII_CTRL_SPEED_10; + break; + case 100: + val |= SGMII_CTRL_SPEED_100; + break; + case 1000: + val |= SGMII_CTRL_SPEED_1000; + break; + case 2500: + val |= SGMII_CTRL_SPEED_2500; + break; + } + if (full_duplex) + val |= SGMII_CTRL_DUPLEX_FULL; + else + val |= SGMII_CTRL_DUPLEX_HALF; + yt9215_smi_write(priv, SGMII_CTRL(0), val); + + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + /* TODO */ + break; + default: + break; + } + } +} + +static int yt9215_config(struct yt9215_priv *priv) +{ + u32 val; + int i; + + /* Reset */ + yt9215_smi_write(priv, RESET_CTRL, RESET_CTRL_HW); + if (read_poll_timeout(yt9215_smi_read, val, val == 0, + 10000, RESET_TIMEOUT_US, false, + priv, RESET_CTRL)) + dev_err(&priv->mdiodev->dev, "reset timeout"); + + /* RESET_CTRL_HW is almost same as GPIO hard reset. + * So we need this delay. + */ + msleep(10); + + /* MIB */ + yt9215_smi_rmw(priv, FUNC_CTRL, FUNC_CTRL_MIB, FUNC_CTRL_MIB); + yt9215_smi_write(priv, MIB_CTRL, MIB_CTRL_PORT_SEL_ALL | MIB_CTRL_OP_CLEAN); + + /* VLAN */ + yt9215_smi_write(priv, EGR_TPID(0), ETH_P_8021Q); + yt9215_smi_write(priv, PORT_EGR_VLAN_FILTER, GENMASK(10, 0)); + + yt9215_smi_write(priv, IGR_TPID(0), ETH_P_8021Q); + yt9215_smi_write(priv, IGR_TPID(1), ETH_P_8021AD); + yt9215_smi_write(priv, PORT_IGR_LOOKUP_SVLAN, 0x00000); + yt9215_smi_write(priv, PORT_IGR_VLAN_FILTER, GENMASK(10, 0)); + + yt9215_smi_write(priv, VLAN_CTRL1(1), + VLAN_CTRL1_MEMBER(GENMASK(10, 0))); + yt9215_smi_write(priv, VLAN_CTRL2(1), + VLAN_CTRL2_UNTAG_MEMBER(GENMASK(10, 0))); + + for(i = 0; i < 11; i++) { + yt9215_smi_write(priv, PORT_EGR_CTRL(i), + PORT_EGR_CTRL_CTAG_TPID_SEL(0)); + yt9215_smi_write(priv, PORT_EGR_VLAN(i), + (PORT_EGR_VLAN_STAG_MODE_UNTAG | + PORT_EGR_VLAN_CTAG_MODE_LOOKUP)); + + /* only care about the outer VID */ + yt9215_smi_write(priv, PORT_IGR_TPID(i), + (PORT_IGR_TPID_STAG_BITMAP(0) | + PORT_IGR_TPID_CTAG_BITMAP(BIT(0) | BIT(1)))); + yt9215_smi_write(priv, PORT_IGR_PVID(i), + PORT_IGR_PVID_CVID(1)); + } + + /* MAC */ + yt9215_init_ports(priv); + + return 0; +} + + +static int yt9215_get_pvid(struct switch_dev *dev, int port, int *vlan) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + *vlan = FIELD_GET(PORT_IGR_PVID_CVID_MASK, + yt9215_smi_read(priv, PORT_IGR_PVID(port))); + return 0; +} + +static int yt9215_set_pvid(struct switch_dev *dev, int port, int vlan) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + dev_info(&priv->mdiodev->dev, "Set Port%d default VLAN to %d", port, vlan); + yt9215_smi_rmw(priv, PORT_IGR_PVID(port), + PORT_IGR_PVID_CVID_MASK, + PORT_IGR_PVID_CVID(vlan)); + return 0; +} + +static int yt9215_get_vlan(struct switch_dev *dev, struct switch_val *val) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + u32 members, untag_members; + int i, num; + + if (val->port_vlan >= dev->vlans || val->port_vlan < 0) + return -EINVAL; + + members = FIELD_GET(VLAN_CTRL1_MEMBER_MASK, + yt9215_smi_read(priv, VLAN_CTRL1(val->port_vlan))); + untag_members = FIELD_GET(VLAN_CTRL2_UNTAG_MEMBER_MASK, + yt9215_smi_read(priv, VLAN_CTRL2(val->port_vlan))); + + num = 0; + for(i = 0; i < dev->ports; i++) + if(members & (1 << i)) { + val->value.ports[num].id = i; + val->value.ports[num].flags = (!(untag_members & (1 << i))) << SWITCH_PORT_FLAG_TAGGED; + num++; + } + val->len = num; + + return 0; +} + +static int yt9215_set_vlan(struct switch_dev *dev, struct switch_val *val) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + u32 members, untag_members; + int i; + + dev_info(&priv->mdiodev->dev, "Set VLAN%d members:", val->port_vlan); + + if (val->port_vlan >= dev->vlans || val->port_vlan < 0) + return -EINVAL; + + members = 0; + untag_members = 0; + for(i = 0; i < val->len; i++) { + members |= 1 << val->value.ports[i].id; + if (!(val->value.ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED))) + untag_members |= 1 << val->value.ports[i].id; + + dev_info(&priv->mdiodev->dev, "\t%d: %s", val->value.ports[i].id, + ((val->value.ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "tag" : "untag")); + } + + yt9215_smi_write(priv, VLAN_CTRL1(val->port_vlan), + VLAN_CTRL1_MEMBER(members)); + yt9215_smi_write(priv, VLAN_CTRL2(val->port_vlan), + VLAN_CTRL2_UNTAG_MEMBER(untag_members)); + + return 0; +} + +// static int yt9215_apply(struct switch_dev *dev) +// { +// struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); +// dev_info(&priv->mdiodev->dev, "%s", __FUNCTION__); +// return 0; +// } + +static int yt9215_reset_switch(struct switch_dev *dev) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + return yt9215_config(priv); +} + +static int yt9215_get_link(struct switch_dev *dev, int port, + struct switch_port_link *link) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + u32 val; + + val = yt9215_smi_read(priv, PORT_STATUS(port)); + link->aneg = true; + link->link = (val & PORT_STATUS_RX_MAC_EN) && (val & PORT_STATUS_TX_MAC_EN); + link->duplex = ((val & PORT_STATUS_DUPLEX_MASK) == PORT_STATUS_DUPLEX_FULL); + link->tx_flow = !!(val & PORT_STATUS_TX_FLOWCONTROL); + link->rx_flow = !!(val & PORT_STATUS_RX_FLOWCONTROL); + + if((val & PORT_STATUS_SPEED_MASK) == PORT_STATUS_SPEED_10) + link->speed = SWITCH_PORT_SPEED_10; + else if((val & PORT_STATUS_SPEED_MASK) == PORT_STATUS_SPEED_100) + link->speed = SWITCH_PORT_SPEED_100; + else if((val & PORT_STATUS_SPEED_MASK) == PORT_STATUS_SPEED_1000) + link->speed = SWITCH_PORT_SPEED_1000; + else if((val & PORT_STATUS_SPEED_MASK) == PORT_STATUS_SPEED_2500) + link->speed = SWITCH_PORT_SPEED_2500; + else + link->speed = SWITCH_PORT_SPEED_UNKNOWN; + + return 0; +} + +static int yt9215_get_stats(struct switch_dev *dev, int port, + struct switch_port_stats *stats) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + stats->tx_bytes = (unsigned long long) yt9215_smi_read(priv, MIB_DATA(port, 33)); + stats->tx_bytes |= (unsigned long long) yt9215_smi_read(priv, MIB_DATA(port, 34)) << 32; + stats->rx_bytes = (unsigned long long) yt9215_smi_read(priv, MIB_DATA(port, 15)); + stats->rx_bytes |= (unsigned long long) yt9215_smi_read(priv, MIB_DATA(port, 16)) << 32; + return 0; +} + + +static int yt9215_port_get_mib(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + val->value.i = yt9215_smi_read(priv, MIB_DATA(val->port_vlan, attr->ofs)); + return 0; +} + +static int yt9215_port_get_mib_dword(struct switch_dev *dev, const struct switch_attr *attr, + struct switch_val *val) +{ + struct yt9215_priv *priv = container_of(dev, struct yt9215_priv, swdev); + static char buf[64]; + u64 v; + + v = (u64) yt9215_smi_read(priv, MIB_DATA(val->port_vlan, attr->ofs)); + v |= (u64) yt9215_smi_read(priv, MIB_DATA(val->port_vlan, attr->ofs + 1)) << 32; + + val->len = snprintf(buf, sizeof(buf), "%llu", v); + val->value.s = buf; + return 0; +} + +static struct switch_attr yt9215_global_attrs[] = { + // { + // .type = SWITCH_TYPE_INT, + // .name = "enable_vlan", + // .description = "Enable VLANs", + // .set = yt9215_set_enable_vlan, + // .get = yt9215_get_enable_vlan, + // }, +}; + +#define YT9215_PORT_ATTR_MIB(attr, offset) \ + { \ + .type = SWITCH_TYPE_INT, \ + .name = attr, \ + .description = "Get port's " attr " counters", \ + .get = yt9215_port_get_mib, \ + .set = NULL, \ + .ofs = offset, \ + } + +/* switch_attr doesn't support 64bit int. So we need to use string. */ +#define YT9215_PORT_ATTR_MIB_DWORD(attr, offset) \ + { \ + .type = SWITCH_TYPE_STRING, \ + .name = attr, \ + .description = "Get port's " attr " counters", \ + .get = yt9215_port_get_mib_dword, \ + .set = NULL, \ + .ofs = offset, \ + } + +static struct switch_attr yt9215_port_attrs[] = { + YT9215_PORT_ATTR_MIB("RX_BROADCAST", 0), + YT9215_PORT_ATTR_MIB("RX_PAUSE", 1), + YT9215_PORT_ATTR_MIB("RX_MULTICAST", 2), + YT9215_PORT_ATTR_MIB("RX_FCS_ERR", 3), + YT9215_PORT_ATTR_MIB("RX_ALIGNMENT_ERR", 4), + YT9215_PORT_ATTR_MIB("RX_UNDERSIZE", 5), + YT9215_PORT_ATTR_MIB("RX_FRAGMENT", 6), + YT9215_PORT_ATTR_MIB("RX_64B", 7), + YT9215_PORT_ATTR_MIB("RX_65_127B", 8), + YT9215_PORT_ATTR_MIB("RX_128_255B", 9), + YT9215_PORT_ATTR_MIB("RX_256_511B", 10), + YT9215_PORT_ATTR_MIB("RX_512_1023B", 11), + YT9215_PORT_ATTR_MIB("RX_1024_1518B", 12), + YT9215_PORT_ATTR_MIB("RX_JUMBO", 13), + YT9215_PORT_ATTR_MIB_DWORD("RX_OKBYTE", 15), + YT9215_PORT_ATTR_MIB_DWORD("RX_NOT_OKBYTE", 17), + YT9215_PORT_ATTR_MIB("RX_OVERSIZE", 19), + YT9215_PORT_ATTR_MIB("RX_DISCARD", 20), + YT9215_PORT_ATTR_MIB("TX_BROADCAST", 21), + YT9215_PORT_ATTR_MIB("TX_PAUSE", 22), + YT9215_PORT_ATTR_MIB("TX_MULTICAST", 23), + YT9215_PORT_ATTR_MIB("TX_UNDERSIZE", 24), + YT9215_PORT_ATTR_MIB("TX_64B", 25), + YT9215_PORT_ATTR_MIB("TX_65_127B", 26), + YT9215_PORT_ATTR_MIB("TX_128_255B", 27), + YT9215_PORT_ATTR_MIB("TX_256_511B", 28), + YT9215_PORT_ATTR_MIB("TX_512_1023B", 29), + YT9215_PORT_ATTR_MIB("TX_1024_1518B", 30), + YT9215_PORT_ATTR_MIB("TX_JUMBO", 31), + YT9215_PORT_ATTR_MIB_DWORD("TX_OKBYTE", 33), + YT9215_PORT_ATTR_MIB("TX_COLLISION", 35), + YT9215_PORT_ATTR_MIB("TX_EXCESSIVE_COLLISION", 36), + YT9215_PORT_ATTR_MIB("TX_MULTI_COLLISION", 37), + YT9215_PORT_ATTR_MIB("TX_SINGLE_COLLISION", 38), + YT9215_PORT_ATTR_MIB("TX_OK_PKT", 39), + YT9215_PORT_ATTR_MIB("TX_DEFER", 40), + YT9215_PORT_ATTR_MIB("TX_LATE_COLLISION", 41), + YT9215_PORT_ATTR_MIB("RX_OAM_COUNTER", 42), + YT9215_PORT_ATTR_MIB("TX_OAM_COUNTER", 43), +}; + +static struct switch_attr yt9215_vlan_attrs[] = { +}; + +static const struct switch_dev_ops yt9215_ops = { + .attr_global = { + .attr = yt9215_global_attrs, + .n_attr = ARRAY_SIZE(yt9215_global_attrs), + }, + .attr_port = { + .attr = yt9215_port_attrs, + .n_attr = ARRAY_SIZE(yt9215_port_attrs), + }, + .attr_vlan = { + .attr = yt9215_vlan_attrs, + .n_attr = ARRAY_SIZE(yt9215_vlan_attrs), + }, + .get_port_pvid = yt9215_get_pvid, + .set_port_pvid = yt9215_set_pvid, + .get_vlan_ports = yt9215_get_vlan, + .set_vlan_ports = yt9215_set_vlan, + // .apply_config = yt9215_apply, + .reset_switch = yt9215_reset_switch, + .get_port_link = yt9215_get_link, + // .set_port_link = yt9215_set_link, + .get_port_stats = yt9215_get_stats, +}; + +static int yt9215_smi_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct yt9215_priv *priv; + struct switch_dev *swdev; + static int index; + u32 chipid; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if(!priv) + return -ENOMEM; + + dev_set_drvdata(&mdiodev->dev, priv); + priv->mdiodev = mdiodev; + if (!of_property_read_u32(dev_of_node(dev), "switchid", &priv->switchid)) + priv->switchid = 0; + + chipid = yt9215_smi_read(priv, CHIP_ID); + if ((chipid & YT9215_CHIP_ID_MASK) != YT9215_CHIP_ID) + dev_err(dev, "chipid incorrect: %08x", chipid); + else + dev_info(dev, "detected chipid: %08x", chipid); + + priv->int_mdio_bus = devm_mdiobus_alloc(dev); + if (priv->int_mdio_bus == NULL) + goto out_free_priv; + + priv->int_mdio_bus->name = "YT9215 internal mdio"; + priv->int_mdio_bus->read = yt9215_int_mdio_read; + priv->int_mdio_bus->write = yt9215_int_mdio_write; + snprintf(priv->int_mdio_bus->id, MII_BUS_ID_SIZE, "YT9215-%d-int", index); + priv->int_mdio_bus->parent = dev; + priv->int_mdio_bus->phy_mask = (u32) ~GENMASK(7, 0); + priv->int_mdio_bus->priv = priv; + + ret = mdiobus_register(priv->int_mdio_bus); + if(ret < 0) + goto out_free_priv; + + swdev = &priv->swdev; + swdev->name = "YT9215"; + swdev->cpu_port = 8; + swdev->ports = 11; + swdev->vlans = 4096; + swdev->ops = &yt9215_ops; + swdev->alias = dev_name(dev); + + ret = register_switch(swdev, NULL); + if(ret < 0) + goto out_unregister_mdio; + + dev_info(dev, "Registered %s as %s\n", swdev->name, swdev->devname); + + ret = yt9215_config(priv); + if(ret < 0) + goto out_free_switch; + + index++; + return 0; + +out_free_switch: + unregister_switch(&priv->swdev); +out_unregister_mdio: + mdiobus_unregister(priv->int_mdio_bus); +out_free_priv: + kfree(priv); + return ret; +} + +static void yt9215_smi_remove(struct mdio_device *mdiodev) +{ + struct yt9215_priv *priv = dev_get_drvdata(&mdiodev->dev); + unregister_switch(&priv->swdev); + mdiobus_unregister(priv->int_mdio_bus); + kfree(priv); + return; +} + +static const struct of_device_id yt9215_of_match[] = { + { .compatible = "motorcomm,yt9215-smi" }, + {}, +}; + +static struct mdio_driver yt9215_mdio_driver = { + .probe = yt9215_smi_probe, + .remove = yt9215_smi_remove, + .mdiodrv.driver = { + .name = "yt9215-smi", + .of_match_table = yt9215_of_match, + }, +}; +mdio_module_driver(yt9215_mdio_driver); diff --git a/target/linux/generic/hack-5.15/700-swconfig_switch_drivers.patch b/target/linux/generic/hack-5.15/700-swconfig_switch_drivers.patch index 9d77efaca6..9af99aa71d 100644 --- a/target/linux/generic/hack-5.15/700-swconfig_switch_drivers.patch +++ b/target/linux/generic/hack-5.15/700-swconfig_switch_drivers.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig -@@ -62,6 +62,80 @@ config SFP +@@ -62,6 +62,84 @@ config SFP depends on HWMON || HWMON=n select MDIO_I2C @@ -89,13 +89,17 @@ Signed-off-by: Felix Fietkau + select SWCONFIG + +endif # RTL8366_SMI ++ ++config YT9215_PHY ++ tristate "Motorcomm YT9215 switch" ++ select SWCONFIG + comment "MII PHY device drivers" config AMD_PHY --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile -@@ -24,6 +24,21 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ +@@ -24,6 +24,22 @@ libphy-$(CONFIG_LED_TRIGGER_PHY) += phy_ obj-$(CONFIG_PHYLINK) += phylink.o obj-$(CONFIG_PHYLIB) += libphy.o @@ -113,6 +117,7 @@ Signed-off-by: Felix Fietkau +obj-$(CONFIG_RTL8366RB_PHY) += rtl8366rb.o +obj-$(CONFIG_RTL8367_PHY) += rtl8367.o +obj-$(CONFIG_RTL8367B_PHY) += rtl8367b.o ++obj-$(CONFIG_YT9215_PHY) += yt9215.o + obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o