diff options
author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2013-10-07 15:02:39 +0000 |
---|---|---|
committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2013-10-07 15:02:39 +0000 |
commit | e8cba182523cdb936039c62e67c0590024234239 (patch) | |
tree | 095b7d4b201bff071f0a6d4a1c6eb8978b351f32 /target | |
parent | 0ba61c43274efea2e47589828741ce822c86f977 (diff) |
ralink: split mt7530 driver out of the gsw driver
Signed-off-by: John Crispin <blogic@openwrt.org>
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@38323 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target')
-rw-r--r-- | target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch | 1153 |
1 files changed, 602 insertions, 551 deletions
diff --git a/target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch b/target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch index 4b20d623a3..8bbaa27aa5 100644 --- a/target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch +++ b/target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch @@ -46,8 +46,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> create mode 100644 drivers/net/ethernet/ralink/soc_rt305x.c create mode 100644 drivers/net/ethernet/ralink/soc_rt3883.c ---- /dev/null -+++ b/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h +Index: linux-3.10.13/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h 2013-10-02 21:40:50.192369358 +0200 @@ -0,0 +1,27 @@ +/* + * Ralink RT305x SoC platform device registration @@ -76,9 +78,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +}; + +#endif /* _RT305X_ESW_PLATFORM_H */ ---- a/arch/mips/ralink/rt305x.c -+++ b/arch/mips/ralink/rt305x.c -@@ -221,6 +221,7 @@ void __init ralink_clk_init(void) +Index: linux-3.10.13/arch/mips/ralink/rt305x.c +=================================================================== +--- linux-3.10.13.orig/arch/mips/ralink/rt305x.c 2013-10-02 21:12:59.896297955 +0200 ++++ linux-3.10.13/arch/mips/ralink/rt305x.c 2013-10-06 13:27:24.076279369 +0200 +@@ -221,6 +221,7 @@ } ralink_clk_add("cpu", cpu_rate); @@ -86,9 +90,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> ralink_clk_add("10000b00.spi", sys_rate); ralink_clk_add("10000100.timer", wdt_rate); ralink_clk_add("10000120.watchdog", wdt_rate); ---- a/drivers/net/ethernet/Kconfig -+++ b/drivers/net/ethernet/Kconfig -@@ -135,6 +135,7 @@ config ETHOC +Index: linux-3.10.13/drivers/net/ethernet/Kconfig +=================================================================== +--- linux-3.10.13.orig/drivers/net/ethernet/Kconfig 2013-10-02 21:12:59.896297955 +0200 ++++ linux-3.10.13/drivers/net/ethernet/Kconfig 2013-10-02 21:40:50.192369358 +0200 +@@ -135,6 +135,7 @@ source "drivers/net/ethernet/packetengines/Kconfig" source "drivers/net/ethernet/pasemi/Kconfig" source "drivers/net/ethernet/qlogic/Kconfig" @@ -96,9 +102,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> source "drivers/net/ethernet/realtek/Kconfig" source "drivers/net/ethernet/renesas/Kconfig" source "drivers/net/ethernet/rdc/Kconfig" ---- a/drivers/net/ethernet/Makefile -+++ b/drivers/net/ethernet/Makefile -@@ -53,6 +53,7 @@ obj-$(CONFIG_ETHOC) += ethoc.o +Index: linux-3.10.13/drivers/net/ethernet/Makefile +=================================================================== +--- linux-3.10.13.orig/drivers/net/ethernet/Makefile 2013-10-02 21:12:59.896297955 +0200 ++++ linux-3.10.13/drivers/net/ethernet/Makefile 2013-10-02 21:40:50.192369358 +0200 +@@ -53,6 +53,7 @@ obj-$(CONFIG_NET_PACKET_ENGINE) += packetengines/ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ @@ -106,8 +114,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ obj-$(CONFIG_SH_ETH) += renesas/ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ ---- /dev/null -+++ b/drivers/net/ethernet/ralink/Kconfig +Index: linux-3.10.13/drivers/net/ethernet/ralink/Kconfig +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/Kconfig 2013-10-02 21:40:50.196369356 +0200 @@ -0,0 +1,31 @@ +config NET_RALINK + tristate "Ralink RT288X/RT3X5X/RT3662/RT3883/MT7620 ethernet driver" @@ -140,8 +150,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + select PHYLIB + select SWCONFIG +endif ---- /dev/null -+++ b/drivers/net/ethernet/ralink/Makefile +Index: linux-3.10.13/drivers/net/ethernet/ralink/Makefile +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/Makefile 2013-10-03 23:14:02.777666985 +0200 @@ -0,0 +1,18 @@ +# +# Makefile for the Ralink SoCs built-in ethernet macs @@ -153,7 +165,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +ralink-eth-$(CONFIG_NET_RALINK_MDIO_RT2880) += mdio_rt2880.o + +ralink-eth-$(CONFIG_NET_RALINK_ESW_RT3052) += esw_rt3052.o -+ralink-eth-$(CONFIG_NET_RALINK_GSW_MT7620) += gsw_mt7620a.o ++ralink-eth-$(CONFIG_NET_RALINK_GSW_MT7620) += gsw_mt7620a.o mt7530.o + +ralink-eth-$(CONFIG_SOC_RT288X) += soc_rt2880.o +ralink-eth-$(CONFIG_SOC_RT305X) += soc_rt305x.o @@ -161,8 +173,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +ralink-eth-$(CONFIG_SOC_MT7620) += soc_mt7620.o + +obj-$(CONFIG_NET_RALINK) += ralink-eth.o ---- /dev/null -+++ b/drivers/net/ethernet/ralink/esw_rt3052.c +Index: linux-3.10.13/drivers/net/ethernet/ralink/esw_rt3052.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/esw_rt3052.c 2013-10-02 21:40:50.196369356 +0200 @@ -0,0 +1,1463 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -1627,8 +1641,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +{ + platform_driver_unregister(&esw_driver); +} ---- /dev/null -+++ b/drivers/net/ethernet/ralink/esw_rt3052.h +Index: linux-3.10.13/drivers/net/ethernet/ralink/esw_rt3052.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/esw_rt3052.h 2013-10-02 21:40:50.196369356 +0200 @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -1662,9 +1678,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + +#endif +#endif ---- /dev/null -+++ b/drivers/net/ethernet/ralink/gsw_mt7620a.c -@@ -0,0 +1,1027 @@ +Index: linux-3.10.13/drivers/net/ethernet/ralink/gsw_mt7620a.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/gsw_mt7620a.c 2013-10-04 17:31:46.468886115 +0200 +@@ -0,0 +1,566 @@ +/* + * 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 @@ -1712,6 +1730,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + +#include "ralink_soc_eth.h" +#include "gsw_mt7620a.h" ++#include "mt7530.h" +#include "mdio.h" + +#define GSW_REG_PHY_TIMEOUT (5 * HZ) @@ -1743,29 +1762,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + +#define PORT_IRQ_ST_CHG 0x7f + -+#define GSW_VLAN_VTCR 0x90 -+#define GSW_VLAN_VTCR_VID_M 0xfff -+#define GSW_VLAN_ID(_x) (0x100 + (4 * (_x))) -+#define GSW_VLAN_ID_VID_S 12 -+#define GSW_VLAN_ID_VID_M 0xfff -+ -+#define GSW_VAWD1 0x94 -+#define GSW_VAWD1_VTAG_EN BIT(28) -+#define GSW_VAWD1_PORTM_S 16 -+#define GSW_VAWD1_PORTM_M 0xff -+ -+#define GSW_VAWD2 0x98 -+#define GSW_VAWD2_PORTT_S 16 -+#define GSW_VAWD2_PORTT_M 0xff -+ -+#define GSW_VTIM(_x) (0x100 + (4 * (_x))) -+#define GSW_VTIM_M 0xfff -+#define GSW_VTIM_S 12 -+ -+#define GSW_REG_PCR(x) (0x2004 + (x * 0x100)) -+#define GSW_REG_PCR_EG_TAG_S 28 -+#define GSW_REG_PCR_EG_TAG_M 0x3 -+ +#define SYSCFG1 0x14 + +#define ESW_PHY_POLLING 0x7000 @@ -1799,28 +1795,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + PORT4_EXT, +}; + -+struct gsw_port { -+ bool disable; -+ bool untag; -+ u16 pvid; -+}; -+ -+struct gsw_vlan { -+ u8 ports; -+ u16 vid; -+}; -+ +struct mt7620_gsw { + struct device *dev; + void __iomem *base; + int irq; -+ -+ struct switch_dev swdev; -+ bool global_vlan_enable; -+ struct gsw_vlan vlans[GSW_NUM_VLANS]; -+ struct gsw_port ports[GSW_NUM_PORTS]; -+ long unsigned int autopoll; + int port4; ++ long unsigned int autopoll; +}; + +static inline void gsw_w32(struct mt7620_gsw *gsw, u32 val, unsigned reg) @@ -1868,18 +1848,8 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + return 0; +} + -+int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) ++static u32 _mt7620_mii_read(struct mt7620_gsw *gsw, int phy_addr, int phy_reg) +{ -+ struct fe_priv *priv = bus->priv; -+ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; -+ -+ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val); -+} -+ -+int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) -+{ -+ struct fe_priv *priv = bus->priv; -+ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; + u32 d; + + if (mt7620_mii_busy_wait(gsw)) @@ -1898,6 +1868,22 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + return d; +} + ++int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val) ++{ ++ struct fe_priv *priv = bus->priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ ++ return _mt7620_mii_write(gsw, phy_addr, phy_reg, val); ++} ++ ++int mt7620_mdio_read(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ struct fe_priv *priv = bus->priv; ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ ++ return _mt7620_mii_read(gsw, phy_addr, phy_reg); ++} ++ +static unsigned char *fe_speed_str(int speed) +{ + switch (speed) { @@ -2041,7 +2027,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + mask = 2; + break; + default: -+ dev_err(priv->device, "port %d - invalid phy mode\n", priv->phy->speed[id]); ++ dev_err(priv->device, "port %d - invalid phy mode\n", id); + return; + } + @@ -2056,11 +2042,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + + if (priv->phy->phy_fixed[id]) { + const __be32 *link = priv->phy->phy_fixed[id]; -+ int tx_fc = be32_to_cpup(link++); -+ int rx_fc = be32_to_cpup(link++); ++ int tx_fc, rx_fc; + u32 val = 0; + + priv->phy->speed[id] = be32_to_cpup(link++); ++ tx_fc = be32_to_cpup(link++); ++ rx_fc = be32_to_cpup(link++); + priv->phy->duplex[id] = be32_to_cpup(link++); + priv->link[id] = 1; + @@ -2171,8 +2158,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + /* Set Port6 CPU Port */ + gsw_w32(gsw, 0x7f7f7fe0, 0x0010); + -+// GSW_VAWD2 -+ + /* setup port 4 */ + if (gsw->port4 == PORT4_EPHY) { + u32 val = rt_sysc_r32(SYSCFG1); @@ -2185,435 +2170,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + } +} + -+static int gsw_reset_switch(struct switch_dev *dev) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ -+ gsw->global_vlan_enable = 0; -+ memset(gsw->ports, 0, sizeof(gsw->ports)); -+ memset(gsw->vlans, 0, sizeof(gsw->vlans)); -+ gsw_hw_init(gsw); -+ -+ return 0; -+} -+ -+static int gsw_get_vlan_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ -+ val->value.i = gsw->global_vlan_enable; -+ -+ return 0; -+} -+ -+static int gsw_set_vlan_enable(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ -+ gsw->global_vlan_enable = val->value.i != 0; -+ -+ return 0; -+} -+ -+static unsigned gsw_get_pvid(struct mt7620_gsw *gsw, unsigned port) -+{ -+ unsigned s, val; -+ -+ s = GSW_VTIM_S * (port % 2); -+ val = gsw_r32(gsw, GSW_VTIM(port / 2)); -+ -+ return (val >> s) & GSW_VTIM_M; -+} -+ -+static void gsw_set_pvid(struct mt7620_gsw *gsw, unsigned port, unsigned pvid) -+{ -+ unsigned s, val; -+ -+ s = GSW_VTIM_S * (port % 2); -+ val = gsw_r32(gsw, GSW_VTIM(port / 2)); -+ val &= ~(GSW_VTIM_M << s); -+ val |= (pvid && GSW_VTIM_M) << s; -+ gsw_w32(gsw, val, GSW_VTIM(port / 2)); -+} -+ -+static int gsw_get_port_bool(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int idx = val->port_vlan; -+ -+ if (idx < 0 || idx >= GSW_NUM_PORTS) -+ return -EINVAL; -+ -+ switch (attr->id) { -+ case GSW_ATTR_PORT_UNTAG: -+ return gsw->ports[idx].untag; -+ } -+ -+ return -EINVAL; -+} -+ -+static int gsw_get_port_pvid(struct switch_dev *dev, int port, int *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ -+ if (port >= GSW_NUM_PORTS) -+ return -EINVAL; -+ -+ *val = gsw_get_pvid(gsw, port); -+ -+ return 0; -+} -+ -+static int gsw_set_port_pvid(struct switch_dev *dev, int port, int val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ -+ if (port >= GSW_NUM_PORTS) -+ return -EINVAL; -+ -+ gsw->ports[port].pvid = val; -+ -+ return 0; -+} -+ -+static void gsw_set_vtcr(struct switch_dev *dev, u32 vid) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int retry = 1000; -+ -+ gsw_w32(gsw, 0x80000000 | (BIT(vid) & GSW_VLAN_VTCR_VID_M), GSW_VLAN_VTCR); -+ while (retry-- && (gsw_r32(gsw, GSW_VLAN_VTCR) & 0x80000000)) -+ ; -+} -+ -+static void gsw_apply_vtcr(struct switch_dev *dev, u32 vid) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int retry = 1000; -+ -+ gsw_w32(gsw, 0x80001000 | (BIT(vid) & GSW_VLAN_VTCR_VID_M), GSW_VLAN_VTCR); -+ while (retry-- && (gsw_r32(gsw, GSW_VLAN_VTCR) & 0x80000000)) -+ ; -+} -+ -+static unsigned gsw_get_vlan_id(struct mt7620_gsw *gsw, unsigned vlan) -+{ -+ unsigned s; -+ unsigned val; -+ -+ s = GSW_VLAN_ID_VID_S * (vlan % 2); -+ val = gsw_r32(gsw, GSW_VLAN_ID(vlan / 2)); -+ val = (val >> s) & GSW_VLAN_ID_VID_M; -+ -+ return val; -+} -+ -+static void gsw_set_vlan_id(struct mt7620_gsw *gsw, unsigned vlan, unsigned vid) -+{ -+ unsigned s; -+ unsigned val; -+ -+ s = GSW_VLAN_ID_VID_S * (vlan % 2); -+ val = gsw_r32(gsw, GSW_VLAN_ID(vlan / 2)); -+ val &= ~(GSW_VLAN_ID_VID_M << s); -+ val |= (vid << s); -+ gsw_w32(gsw, val, GSW_VLAN_ID(vlan / 2)); -+} -+ -+static void gsw_vlan_tagging_enable(struct mt7620_gsw *gsw, unsigned vlan, unsigned enable) -+{ -+ unsigned val; -+ -+ val = gsw_r32(gsw, GSW_VAWD1); -+ if (enable) -+ val |= GSW_VAWD1_VTAG_EN; -+ else -+ val &= ~GSW_VAWD1_VTAG_EN; -+ gsw_w32(gsw, val, GSW_VAWD1); -+} -+ -+static unsigned gsw_get_port_member(struct mt7620_gsw *gsw, unsigned vlan) -+{ -+ unsigned val; -+ -+ gsw_set_vtcr(&gsw->swdev, vlan); -+ -+ val = gsw_r32(gsw, GSW_VAWD1); -+ val = (val >> GSW_VAWD1_PORTM_S) & GSW_VAWD1_PORTM_M; -+ -+ return val; -+} -+ -+static void gsw_set_port_member(struct mt7620_gsw *gsw, unsigned vlan, unsigned member) -+{ -+ unsigned val; -+ -+ val = gsw_r32(gsw, GSW_VAWD1); -+ val = ~(GSW_VAWD1_PORTM_M << GSW_VAWD1_PORTM_S); -+ val |= (member & GSW_VAWD1_PORTM_M) << GSW_VAWD1_PORTM_S; -+ gsw_w32(gsw, val, GSW_VAWD1); -+} -+ -+static unsigned gsw_get_port_tag(struct mt7620_gsw *gsw, unsigned port) -+{ -+ unsigned val; -+ -+ val = gsw_r32(gsw, GSW_REG_PCR(port)); -+ val >>= GSW_REG_PCR_EG_TAG_S; -+ val &= GSW_REG_PCR_EG_TAG_M; -+ -+ return !!val; -+} -+ -+static void gsw_set_port_untag(struct mt7620_gsw *gsw, unsigned port, unsigned untag) -+{ -+ unsigned val; -+ -+ val = gsw_r32(gsw, GSW_REG_PCR(port)); -+ if (!untag) -+ untag = 0x2; -+ else -+ untag = 0; -+ val &= ~(GSW_REG_PCR_EG_TAG_M << GSW_REG_PCR_EG_TAG_S); -+ val |= (untag & GSW_REG_PCR_EG_TAG_M) << GSW_REG_PCR_EG_TAG_S; -+ gsw_w32(gsw, val, GSW_REG_PCR(port)); -+} -+ -+static int gsw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int vlan_idx = -1; -+ u32 member; -+ int i; -+ -+ val->len = 0; -+ -+ if (val->port_vlan < 0 || val->port_vlan >= GSW_NUM_VIDS) -+ return -EINVAL; -+ -+ /* valid vlan? */ -+ for (i = 0; i < GSW_NUM_VLANS; i++) { -+ if (gsw_get_vlan_id(gsw, i) != val->port_vlan) -+ continue; -+ member = gsw_get_port_member(gsw, i); -+ vlan_idx = i; -+ break; -+ } -+ -+ if (vlan_idx == -1) -+ return -EINVAL; -+ -+ for (i = 0; i < GSW_NUM_PORTS; i++) { -+ struct switch_port *p; -+ int port_mask = 1 << i; -+ -+ if (!(member & port_mask)) -+ continue; -+ -+ p = &val->value.ports[val->len++]; -+ p->id = i; -+ if (gsw_get_port_tag(gsw, i)) -+ p->flags = 1 << SWITCH_PORT_FLAG_TAGGED; -+ else -+ p->flags = 0; -+ } -+ -+ return 0; -+} -+ -+static int gsw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int ports; -+ int vlan_idx = -1; -+ int i; -+ -+ if (val->port_vlan < 0 || val->port_vlan >= GSW_NUM_VIDS || -+ val->len > GSW_NUM_PORTS) -+ return -EINVAL; -+ -+ /* one of the already defined vlans? */ -+ for (i = 0; i < GSW_NUM_VLANS; i++) { -+ if (gsw->vlans[i].vid == val->port_vlan && -+ gsw->vlans[i].ports) { -+ vlan_idx = i; -+ break; -+ } -+ } -+ -+ /* select a free slot */ -+ for (i = 0; vlan_idx == -1 && i < GSW_NUM_VLANS; i++) { -+ if (!gsw->vlans[i].ports) -+ vlan_idx = i; -+ } -+ -+ /* bail if all slots are in use */ -+ if (vlan_idx == -1) -+ return -EINVAL; -+ -+ ports = 0; -+ for (i = 0; i < val->len; i++) { -+ struct switch_port *p = &val->value.ports[i]; -+ int port_mask = 1 << p->id; -+ bool untagged = !(p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)); -+ -+ if (p->id >= GSW_NUM_PORTS) -+ return -EINVAL; -+ -+ ports |= port_mask; -+ gsw->ports[p->id].untag = untagged; -+ } -+ gsw->vlans[vlan_idx].ports = ports; -+ if (!ports) -+ gsw->vlans[vlan_idx].vid = 0xfff; -+ else -+ gsw->vlans[vlan_idx].vid = val->port_vlan; -+ -+ return 0; -+} -+ -+static int gsw_apply_config(struct switch_dev *dev) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int i; -+ -+ for (i = 0; i < GSW_NUM_VLANS; i++) { -+ gsw_set_vtcr(&gsw->swdev, i); -+ if (gsw->global_vlan_enable) { -+ gsw_set_vlan_id(gsw, i, gsw->vlans[i].vid); -+ gsw_set_port_member(gsw, i, gsw->vlans[i].ports); -+ gsw_vlan_tagging_enable(gsw, i, 1); -+ } else { -+ gsw_set_vlan_id(gsw, i, 0xfff); -+ gsw_set_port_member(gsw, i, 0); -+ gsw_vlan_tagging_enable(gsw, i, 0); -+ } -+ gsw_apply_vtcr(&gsw->swdev, i); -+ } -+ -+ for (i = 0; i < GSW_NUM_PORTS; i++) { -+ if (gsw->global_vlan_enable) { -+ gsw_set_port_untag(gsw, i, !gsw->ports[i].untag); -+ gsw_set_pvid(gsw, i, gsw->ports[i].pvid); -+ } else { -+ gsw_set_port_untag(gsw, i, 0); -+ gsw_set_pvid(gsw, i, 0); -+ } -+ } -+ -+ if (!gsw->global_vlan_enable) -+ gsw_set_vlan_id(gsw, 0, 0); -+ -+ return 0; -+} -+ -+static int gsw_get_port_link(struct switch_dev *dev, -+ int port, -+ struct switch_port_link *link) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ u32 status; -+ -+ if (port < 0 || port >= GSW_NUM_PORTS) -+ return -EINVAL; -+ -+ status = gsw_r32(gsw, GSW_REG_PORT_STATUS(port)); -+ link->link = status & 0x1; -+ link->duplex = (status >> 1) & 1; -+ -+ switch ((status >> 2) & 0x3) { -+ case 0: -+ link->speed = SWITCH_PORT_SPEED_10; -+ break; -+ case 1: -+ link->speed = SWITCH_PORT_SPEED_100; -+ break; -+ case 2: -+ case 3: // forced gige speed can be 2 or 3 -+ link->speed = SWITCH_PORT_SPEED_1000; -+ break; -+ } -+ -+ return 0; -+} -+ -+static int gsw_set_port_bool(struct switch_dev *dev, -+ const struct switch_attr *attr, -+ struct switch_val *val) -+{ -+ struct mt7620_gsw *gsw = container_of(dev, struct mt7620_gsw, swdev); -+ int idx = val->port_vlan; -+ -+ if (idx < 0 || idx >= GSW_NUM_PORTS || -+ val->value.i < 0 || val->value.i > 1) -+ return -EINVAL; -+ -+ switch (attr->id) { -+ case GSW_ATTR_PORT_UNTAG: -+ gsw->ports[idx].untag = val->value.i; -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+static const struct switch_attr gsw_global[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "enable_vlan", -+ .description = "VLAN mode (1:enabled)", -+ .max = 1, -+ .id = GSW_ATTR_ENABLE_VLAN, -+ .get = gsw_get_vlan_enable, -+ .set = gsw_set_vlan_enable, -+ }, -+}; -+ -+static const struct switch_attr gsw_port[] = { -+ { -+ .type = SWITCH_TYPE_INT, -+ .name = "untag", -+ .description = "Untag (1:strip outgoing vlan tag)", -+ .max = 1, -+ .id = GSW_ATTR_PORT_UNTAG, -+ .get = gsw_get_port_bool, -+ .set = gsw_set_port_bool, -+ }, -+}; -+ -+static const struct switch_attr gsw_vlan[] = { -+}; -+ -+static const struct switch_dev_ops gsw_ops = { -+ .attr_global = { -+ .attr = gsw_global, -+ .n_attr = ARRAY_SIZE(gsw_global), -+ }, -+ .attr_port = { -+ .attr = gsw_port, -+ .n_attr = ARRAY_SIZE(gsw_port), -+ }, -+ .attr_vlan = { -+ .attr = gsw_vlan, -+ .n_attr = ARRAY_SIZE(gsw_vlan), -+ }, -+ .get_vlan_ports = gsw_get_vlan_ports, -+ .set_vlan_ports = gsw_set_vlan_ports, -+ .get_port_pvid = gsw_get_port_pvid, -+ .set_port_pvid = gsw_set_port_pvid, -+ .get_port_link = gsw_get_port_link, -+ .apply_config = gsw_apply_config, -+ .reset_switch = gsw_reset_switch, -+}; -+ +void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac) +{ + struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; @@ -2631,11 +2187,23 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + {} +}; + ++int mt7620_gsw_config(struct fe_priv *priv) ++{ ++ struct mt7620_gsw *gsw = (struct mt7620_gsw *) priv->soc->swpriv; ++ ++ /* is the mt7530 internal or external */ ++ if ((_mt7620_mii_read(gsw, 0x1f, 2) == 1) && (_mt7620_mii_read(gsw, 0x1f, 3) == 0xbeef)) ++ mt7530_probe(priv->device, NULL, priv->mii_bus); ++ else ++ mt7530_probe(priv->device, gsw->base, NULL); ++ ++ return 0; ++} ++ +int mt7620_gsw_probe(struct fe_priv *priv) +{ + struct mt7620_gsw *gsw; + struct device_node *np; -+ struct switch_dev *swdev; + const char *port4 = NULL; + + np = of_find_matching_node(NULL, gsw_match); @@ -2660,23 +2228,12 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + gsw->base = of_iomap(np, 0); + if (!gsw->base) { + dev_err(priv->device, "gsw ioremap failed\n"); ++ return -ENOMEM; + } + + gsw->dev = priv->device; + priv->soc->swpriv = gsw; + -+ swdev = &gsw->swdev; -+ swdev->of_node = np; -+ swdev->name = "mt7620a-gsw"; -+ swdev->alias = "mt7620x"; -+ swdev->cpu_port = GSW_PORT6; -+ swdev->ports = GSW_NUM_PORTS; -+ swdev->vlans = GSW_NUM_VLANS; -+ swdev->ops = &gsw_ops; -+ -+ if (register_switch(swdev, NULL)) -+ dev_err(priv->device, "register_switch failed\n"); -+ + of_property_read_string(np, "ralink,port4", &port4); + if (port4 && !strcmp(port4, "ephy")) + gsw->port4 = PORT4_EPHY; @@ -2692,9 +2249,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + + return 0; +} ---- /dev/null -+++ b/drivers/net/ethernet/ralink/gsw_mt7620a.h -@@ -0,0 +1,29 @@ +Index: linux-3.10.13/drivers/net/ethernet/ralink/gsw_mt7620a.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/gsw_mt7620a.h 2013-10-04 02:20:43.226145788 +0200 +@@ -0,0 +1,30 @@ +/* + * 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 @@ -2715,6 +2274,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +#ifndef _RALINK_GSW_MT7620_H__ +#define _RALINK_GSW_MT7620_H__ + ++extern int mt7620_gsw_config(struct fe_priv *priv); +extern int mt7620_gsw_probe(struct fe_priv *priv); +extern void mt7620_set_mac(struct fe_priv *priv, unsigned char *mac); +extern int mt7620_mdio_write(struct mii_bus *bus, int phy_addr, int phy_reg, u16 val); @@ -2724,9 +2284,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +extern int mt7620a_has_carrier(struct fe_priv *priv); + +#endif ---- /dev/null -+++ b/drivers/net/ethernet/ralink/mdio.c -@@ -0,0 +1,245 @@ +Index: linux-3.10.13/drivers/net/ethernet/ralink/mdio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/mdio.c 2013-10-04 01:57:11.854085410 +0200 +@@ -0,0 +1,244 @@ +/* + * 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 @@ -2802,7 +2364,6 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + } + } + } -+ spin_unlock_irqrestore(&priv->phy->lock, flags); +} + +int fe_connect_phy_node(struct fe_priv *priv, struct device_node *phy_node) @@ -2813,7 +2374,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + + _port = of_get_property(phy_node, "reg", NULL); + -+ if (!_port || (be32_to_cpu(*_port) >= 8)) { ++ if (!_port || (be32_to_cpu(*_port) >= 0x20)) { + pr_err("%s: invalid port id\n", phy_node->name); + return -EINVAL; + } @@ -2972,8 +2533,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + of_node_put(priv->mii_bus->dev.of_node); + kfree(priv->mii_bus); +} ---- /dev/null -+++ b/drivers/net/ethernet/ralink/mdio.h +Index: linux-3.10.13/drivers/net/ethernet/ralink/mdio.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/mdio.h 2013-10-02 21:40:50.200369354 +0200 @@ -0,0 +1,29 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -3004,8 +2567,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +static inline void fe_mdio_cleanup(struct fe_priv *priv) {} +#endif +#endif ---- /dev/null -+++ b/drivers/net/ethernet/ralink/mdio_rt2880.c +Index: linux-3.10.13/drivers/net/ethernet/ralink/mdio_rt2880.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/mdio_rt2880.c 2013-10-02 21:40:50.200369354 +0200 @@ -0,0 +1,232 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -3239,8 +2804,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + + return; +} ---- /dev/null -+++ b/drivers/net/ethernet/ralink/mdio_rt2880.h +Index: linux-3.10.13/drivers/net/ethernet/ralink/mdio_rt2880.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/mdio_rt2880.h 2013-10-02 21:40:50.204369354 +0200 @@ -0,0 +1,26 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -3268,9 +2835,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +void rt2880_port_init(struct fe_priv *priv, struct device_node *np); + +#endif ---- /dev/null -+++ b/drivers/net/ethernet/ralink/ralink_soc_eth.c -@@ -0,0 +1,735 @@ +Index: linux-3.10.13/drivers/net/ethernet/ralink/ralink_soc_eth.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/ralink_soc_eth.c 2013-10-04 02:18:27.242139979 +0200 +@@ -0,0 +1,738 @@ +/* + * 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 @@ -3844,13 +3413,16 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + + if (priv->soc->port_init) + for_each_child_of_node(priv->device->of_node, port) -+ if (of_device_is_compatible(port, "ralink,eth-port")) ++ if (of_device_is_compatible(port, "ralink,eth-port") && of_device_is_available(port)) + priv->soc->port_init(priv, port); + + err = fe_hw_init(dev); + if (err) + goto err_phy_disconnect; + ++ if (priv->soc->switch_config) ++ priv->soc->switch_config(priv); ++ + return 0; + +err_phy_disconnect: @@ -4006,9 +3578,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("John Crispin <blogic@openwrt.org>"); +MODULE_DESCRIPTION("Ethernet driver for Ralink SoC"); ---- /dev/null -+++ b/drivers/net/ethernet/ralink/ralink_soc_eth.h -@@ -0,0 +1,374 @@ +Index: linux-3.10.13/drivers/net/ethernet/ralink/ralink_soc_eth.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/ralink_soc_eth.h 2013-10-04 02:18:45.454140650 +0200 +@@ -0,0 +1,375 @@ +/* + * 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 @@ -4333,6 +3907,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + void (*tx_dma)(struct fe_priv *priv, int idx, int len); + void (*rx_dma)(struct fe_priv *priv, int idx, int len); + int (*switch_init)(struct fe_priv *priv); ++ int (*switch_config)(struct fe_priv *priv); + void (*port_init)(struct fe_priv *priv, struct device_node *port); + int (*has_carrier)(struct fe_priv *priv); + int (*mdio_init)(struct fe_priv *priv); @@ -4383,9 +3958,11 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +u32 fe_r32(unsigned reg); + +#endif /* FE_ETH_H */ ---- /dev/null -+++ b/drivers/net/ethernet/ralink/soc_mt7620.c -@@ -0,0 +1,111 @@ +Index: linux-3.10.13/drivers/net/ethernet/ralink/soc_mt7620.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/soc_mt7620.c 2013-10-04 02:21:02.450146612 +0200 +@@ -0,0 +1,112 @@ +/* + * 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 @@ -4478,6 +4055,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + .tx_dma = mt7620_tx_dma, + .rx_dma = mt7620_rx_dma, + .switch_init = mt7620_gsw_probe, ++ .switch_config = mt7620_gsw_config, + .port_init = mt7620_port_init, + .min_pkt_len = 0, + .reg_table = rt5350_reg_table, @@ -4497,8 +4075,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +}; + +MODULE_DEVICE_TABLE(of, of_fe_match); ---- /dev/null -+++ b/drivers/net/ethernet/ralink/soc_rt2880.c +Index: linux-3.10.13/drivers/net/ethernet/ralink/soc_rt2880.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/soc_rt2880.c 2013-10-02 21:40:50.204369354 +0200 @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -4551,8 +4131,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +}; + +MODULE_DEVICE_TABLE(of, of_fe_match); ---- /dev/null -+++ b/drivers/net/ethernet/ralink/soc_rt305x.c +Index: linux-3.10.13/drivers/net/ethernet/ralink/soc_rt305x.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/soc_rt305x.c 2013-10-02 21:40:50.208369356 +0200 @@ -0,0 +1,113 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -4667,8 +4249,10 @@ Signed-off-by: John Crispin <blogic@openwrt.org> +}; + +MODULE_DEVICE_TABLE(of, of_fe_match); ---- /dev/null -+++ b/drivers/net/ethernet/ralink/soc_rt3883.c +Index: linux-3.10.13/drivers/net/ethernet/ralink/soc_rt3883.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/soc_rt3883.c 2013-10-02 21:40:50.208369356 +0200 @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or modify @@ -4730,3 +4314,470 @@ Signed-off-by: John Crispin <blogic@openwrt.org> + +MODULE_DEVICE_TABLE(of, of_fe_match); + +Index: linux-3.10.13/drivers/net/ethernet/ralink/mt7530.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-3.10.13/drivers/net/ethernet/ralink/mt7530.c 2013-10-04 19:37:31.585208659 +0200 +@@ -0,0 +1,462 @@ ++/* ++ * 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. ++ * ++ * Copyright (C) 2013 John Crispin <blogic@openwrt.org> ++ */ ++ ++#include <linux/if.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/if_ether.h> ++#include <linux/skbuff.h> ++#include <linux/netdevice.h> ++#include <linux/netlink.h> ++#include <linux/bitops.h> ++#include <net/genetlink.h> ++#include <linux/switch.h> ++#include <linux/delay.h> ++#include <linux/phy.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/lockdep.h> ++#include <linux/workqueue.h> ++#include <linux/of_device.h> ++ ++#include "mt7530.h" ++ ++#define MT7530_CPU_PORT 6 ++#define MT7530_NUM_PORTS 7 ++#define MT7530_NUM_VLANS 16 ++#define MT7530_NUM_VIDS 16 ++ ++#define REG_ESW_VLAN_VTCR 0x90 ++#define REG_ESW_VLAN_VAWD1 0x94 ++#define REG_ESW_VLAN_VAWD2 0x98 ++ ++enum { ++ /* Global attributes. */ ++ MT7530_ATTR_ENABLE_VLAN, ++}; ++ ++struct mt7530_port { ++ u16 pvid; ++}; ++ ++struct mt7530_vlan { ++ u8 ports; ++}; ++ ++struct mt7530_priv { ++ void __iomem *base; ++ struct mii_bus *bus; ++ struct switch_dev swdev; ++ ++ bool global_vlan_enable; ++ struct mt7530_vlan vlans[MT7530_NUM_VLANS]; ++ struct mt7530_port ports[MT7530_NUM_PORTS]; ++}; ++ ++struct mt7530_mapping { ++ char *name; ++ u8 pvids[6]; ++ u8 vlans[8]; ++} mt7530_defaults[] = { ++ { ++ .name = "llllw", ++ .pvids = { 1, 1, 1, 1, 2, 1 }, ++ .vlans = { 0, 0x4f, 0x50 }, ++ }, { ++ .name = "wllll", ++ .pvids = { 2, 1, 1, 1, 1, 1 }, ++ .vlans = { 0, 0x5e, 0x41 }, ++ }, ++}; ++ ++struct mt7530_mapping* ++mt7530_find_mapping(struct device_node *np) ++{ ++ const char *map; ++ int i; ++ ++ if (of_property_read_string(np, "ralink,port-map", &map)) ++ return NULL; ++ ++ for (i = 0; i < ARRAY_SIZE(mt7530_defaults); i++) ++ if (!strcmp(map, mt7530_defaults[i].name)) ++ return &mt7530_defaults[i]; ++ ++ return NULL; ++} ++ ++static void ++mt7530_apply_mapping(struct mt7530_priv *mt7530, struct mt7530_mapping *map) ++{ ++ int i = 0; ++ ++ mt7530->global_vlan_enable = 1; ++ ++ for (i = 0; i < 6; i++) ++ mt7530->ports[i].pvid = map->pvids[i]; ++ for (i = 0; i < 8; i++) ++ mt7530->vlans[i].ports = map->vlans[i]; ++} ++ ++static int ++mt7530_reset_switch(struct switch_dev *dev) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ ++ memset(priv->ports, 0, sizeof(priv->ports)); ++ memset(priv->vlans, 0, sizeof(priv->vlans)); ++ ++ return 0; ++} ++ ++static int ++mt7530_get_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ ++ val->value.i = priv->global_vlan_enable; ++ ++ return 0; ++} ++ ++static int ++mt7530_set_vlan_enable(struct switch_dev *dev, ++ const struct switch_attr *attr, ++ struct switch_val *val) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ ++ priv->global_vlan_enable = val->value.i != 0; ++ ++ return 0; ++} ++ ++static u32 ++mt7530_r32(struct mt7530_priv *priv, u32 reg) ++{ ++ if (priv->bus) { ++ u16 high, low; ++ ++ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff); ++ low = mdiobus_read(priv->bus, 0x1f, (reg >> 2) & 0xf); ++ high = mdiobus_read(priv->bus, 0x1f, 0x10); ++ ++ return (high << 16) | (low & 0xffff); ++ } ++ ++ return ioread32(priv->base + reg); ++} ++ ++static void ++mt7530_w32(struct mt7530_priv *priv, u32 reg, u32 val) ++{ ++ if (priv->bus) { ++ mdiobus_write(priv->bus, 0x1f, 0x1f, (reg >> 6) & 0x3ff); ++ mdiobus_write(priv->bus, 0x1f, (reg >> 2) & 0xf, val & 0xffff); ++ mdiobus_write(priv->bus, 0x1f, 0x10, val >> 16); ++ return; ++ } ++ ++ iowrite32(val, priv->base + reg); ++} ++ ++static void ++mt7530_vtcr(struct mt7530_priv *priv, u32 cmd, u32 val) ++{ ++ int i; ++ ++ mt7530_w32(priv, REG_ESW_VLAN_VTCR, BIT(31) | (cmd << 12) | val); ++ ++ for (i = 0; i < 20; i++) { ++ u32 val = mt7530_r32(priv, REG_ESW_VLAN_VTCR); ++ ++ if ((val & BIT(31)) == 0) ++ break; ++ ++ udelay(1000); ++ } ++ if (i == 20) ++ printk("mt7530: vtcr timeout\n"); ++} ++ ++static int ++mt7530_get_port_pvid(struct switch_dev *dev, int port, int *val) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ ++ if (port >= MT7530_NUM_PORTS) ++ return -EINVAL; ++ ++ *val = mt7530_r32(priv, 0x2014 + (0x100 * port)); ++ *val &= 0xff; ++ ++ return 0; ++} ++ ++static int ++mt7530_set_port_pvid(struct switch_dev *dev, int port, int pvid) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ ++ if (port >= MT7530_NUM_PORTS) ++ return -1; ++ ++ priv->ports[port].pvid = pvid; ++ ++ return 0; ++} ++ ++static int ++mt7530_get_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ u32 member; ++ int i; ++ ++ val->len = 0; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VIDS) ++ return -EINVAL; ++ ++ mt7530_vtcr(priv, 0, val->port_vlan); ++ member = mt7530_r32(priv, REG_ESW_VLAN_VAWD1); ++ member >>= 16; ++ member &= 0xff; ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) { ++ struct switch_port *p; ++ if (!(member & BIT(i))) ++ continue; ++ ++ p = &val->value.ports[val->len++]; ++ p->id = i; ++ p->flags = 0; ++ } ++ ++ return 0; ++} ++ ++static int ++mt7530_set_vlan_ports(struct switch_dev *dev, struct switch_val *val) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ int ports = 0; ++ int i; ++ ++ if (val->port_vlan < 0 || val->port_vlan >= MT7530_NUM_VIDS || ++ val->len > MT7530_NUM_PORTS) ++ return -EINVAL; ++ ++ for (i = 0; i < val->len; i++) { ++ struct switch_port *p = &val->value.ports[i]; ++ ++ if (p->id >= MT7530_NUM_PORTS) ++ return -EINVAL; ++ ++ ports |= BIT(p->id); ++ } ++ priv->vlans[val->port_vlan].ports = ports; ++ ++ return 0; ++} ++ ++static int ++mt7530_apply_config(struct switch_dev *dev) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ int i; ++ ++ if (!priv->global_vlan_enable) { ++ mt7530_w32(priv, 0x2004, 0xff000); ++ mt7530_w32(priv, 0x2104, 0xff000); ++ mt7530_w32(priv, 0x2204, 0xff000); ++ mt7530_w32(priv, 0x2304, 0xff000); ++ mt7530_w32(priv, 0x2404, 0xff000); ++ mt7530_w32(priv, 0x2504, 0xff000); ++ mt7530_w32(priv, 0x2604, 0xff000); ++ mt7530_w32(priv, 0x2010, 0x810000c); ++ mt7530_w32(priv, 0x2110, 0x810000c); ++ mt7530_w32(priv, 0x2210, 0x810000c); ++ mt7530_w32(priv, 0x2310, 0x810000c); ++ mt7530_w32(priv, 0x2410, 0x810000c); ++ mt7530_w32(priv, 0x2510, 0x810000c); ++ mt7530_w32(priv, 0x2610, 0x810000c); ++ return 0; ++ } ++ ++ // LAN/WAN ports as security mode ++ mt7530_w32(priv, 0x2004, 0xff0003); ++ mt7530_w32(priv, 0x2104, 0xff0003); ++ mt7530_w32(priv, 0x2204, 0xff0003); ++ mt7530_w32(priv, 0x2304, 0xff0003); ++ mt7530_w32(priv, 0x2404, 0xff0003); ++ mt7530_w32(priv, 0x2504, 0xff0003); ++ // LAN/WAN ports as transparent port ++ mt7530_w32(priv, 0x2010, 0x810000c0); ++ mt7530_w32(priv, 0x2110, 0x810000c0); ++ mt7530_w32(priv, 0x2210, 0x810000c0); ++ mt7530_w32(priv, 0x2310, 0x810000c0); ++ mt7530_w32(priv, 0x2410, 0x810000c0); ++ mt7530_w32(priv, 0x2510, 0x810000c0); ++ // set CPU/P7 port as user port ++ mt7530_w32(priv, 0x2610, 0x81000000); ++ mt7530_w32(priv, 0x2710, 0x81000000); ++ ++ mt7530_w32(priv, 0x2604, 0x20ff0003); ++ mt7530_w32(priv, 0x2704, 0x20ff0003); ++ mt7530_w32(priv, 0x2610, 0x81000000); ++ ++ for (i = 0; i < MT7530_NUM_VLANS; i++) { ++ u8 ports = priv->vlans[i].ports; ++ ++ if (ports) ++ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, BIT(30) | (ports << 16) | BIT(0)); ++ else ++ mt7530_w32(priv, REG_ESW_VLAN_VAWD1, 0); ++ ++ mt7530_vtcr(priv, 1, i); ++ } ++ ++ for (i = 0; i < MT7530_NUM_PORTS; i++) ++ mt7530_w32(priv, 0x2014 + (0x100 * i), 0x10000 | priv->ports[i].pvid); ++ ++ return 0; ++} ++ ++static int ++mt7530_get_port_link(struct switch_dev *dev, int port, ++ struct switch_port_link *link) ++{ ++ struct mt7530_priv *priv = container_of(dev, struct mt7530_priv, swdev); ++ u32 speed, pmsr; ++ ++ if (port < 0 || port >= MT7530_NUM_PORTS) ++ return -EINVAL; ++ ++ pmsr = mt7530_r32(priv, 0x3008 + (0x100 * port)); ++ ++ link->link = pmsr & 1; ++ link->duplex = (pmsr >> 1) & 1; ++ speed = (pmsr >> 2) & 3; ++ ++ switch (speed) { ++ case 0: ++ link->speed = SWITCH_PORT_SPEED_10; ++ break; ++ case 1: ++ link->speed = SWITCH_PORT_SPEED_100; ++ break; ++ case 2: ++ case 3: /* forced gige speed can be 2 or 3 */ ++ link->speed = SWITCH_PORT_SPEED_1000; ++ break; ++ default: ++ link->speed = SWITCH_PORT_SPEED_UNKNOWN; ++ break; ++ } ++ ++ return 0; ++} ++ ++static const struct switch_attr mt7530_global[] = { ++ { ++ .type = SWITCH_TYPE_INT, ++ .name = "enable_vlan", ++ .description = "VLAN mode (1:enabled)", ++ .max = 1, ++ .id = MT7530_ATTR_ENABLE_VLAN, ++ .get = mt7530_get_vlan_enable, ++ .set = mt7530_set_vlan_enable, ++ }, ++}; ++ ++static const struct switch_attr mt7530_port[] = { ++}; ++ ++static const struct switch_attr mt7530_vlan[] = { ++}; ++ ++static const struct switch_dev_ops mt7530_ops = { ++ .attr_global = { ++ .attr = mt7530_global, ++ .n_attr = ARRAY_SIZE(mt7530_global), ++ }, ++ .attr_port = { ++ .attr = mt7530_port, ++ .n_attr = ARRAY_SIZE(mt7530_port), ++ }, ++ .attr_vlan = { ++ .attr = mt7530_vlan, ++ .n_attr = ARRAY_SIZE(mt7530_vlan), ++ }, ++ .get_vlan_ports = mt7530_get_vlan_ports, ++ .set_vlan_ports = mt7530_set_vlan_ports, ++ .get_port_pvid = mt7530_get_port_pvid, ++ .set_port_pvid = mt7530_set_port_pvid, ++ .get_port_link = mt7530_get_port_link, ++ .apply_config = mt7530_apply_config, ++ .reset_switch = mt7530_reset_switch, ++}; ++ ++int ++mt7530_probe(struct device *dev, void __iomem *base, struct mii_bus *bus) ++{ ++ struct switch_dev *swdev; ++ struct mt7530_priv *mt7530; ++ struct mt7530_mapping *map; ++ int ret; ++ ++ if (bus && bus->phy_map[0x1f]->phy_id != 0x1beef) ++ return 0; ++ ++ mt7530 = devm_kzalloc(dev, sizeof(struct mt7530_priv), GFP_KERNEL); ++ if (!mt7530) ++ return -ENOMEM; ++ ++ mt7530->base = base; ++ mt7530->bus = bus; ++ mt7530->global_vlan_enable = 1; ++ ++ swdev = &mt7530->swdev; ++ swdev->name = "mt7530"; ++ swdev->alias = "mt7530"; ++ swdev->cpu_port = MT7530_CPU_PORT; ++ swdev->ports = MT7530_NUM_PORTS; ++ swdev->vlans = MT7530_NUM_VLANS; ++ swdev->ops = &mt7530_ops; ++ ++ ret = register_switch(swdev, NULL); ++ if (ret) { ++ dev_err(dev, "failed to register mt7530\n"); ++ return ret; ++ } ++ ++ dev_info(dev, "loaded mt7530 driver\n"); ++ ++ map = mt7530_find_mapping(dev->of_node); ++ if (map) ++ mt7530_apply_mapping(mt7530, map); ++ mt7530_apply_config(swdev); ++ ++ return 0; ++} ++ ++int ++mt7530_probe_mmio(struct device *dev, void __iomem *base) ++{ ++ return 0; ++} |