aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch
diff options
context:
space:
mode:
authorJohn Crispin <john@openwrt.org>2013-10-07 15:02:39 +0000
committerJohn Crispin <john@openwrt.org>2013-10-07 15:02:39 +0000
commitddcb8000185966040ae81cd184012d294cf82310 (patch)
tree341665ea3ce02cea10272c31b11a819afc9a7431 /target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch
parentc077698e38876ecfc28454bcdd495ac36b387c2b (diff)
downloadupstream-ddcb8000185966040ae81cd184012d294cf82310.tar.gz
upstream-ddcb8000185966040ae81cd184012d294cf82310.tar.bz2
upstream-ddcb8000185966040ae81cd184012d294cf82310.zip
ralink: split mt7530 driver out of the gsw driver
Signed-off-by: John Crispin <blogic@openwrt.org> SVN-Revision: 38323
Diffstat (limited to 'target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch')
-rw-r--r--target/linux/ramips/patches-3.10/0111-NET-MIPS-add-ralink-SoC-ethernet-driver.patch1153
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;
++}