Patchwork [OpenWrt-Devel] ramips: Updated patch for ramips_esw.c swconfig supoprt

login
register
Submitter Tobias Diedrich
Date 2012-05-05 20:26:58
Message ID <20120505202658.GO31393@yumi.tdiedrich.de>
Download mbox | patch
Permalink /patch/2156/
State Superseded
Headers show

Comments

Tobias Diedrich - 2012-05-05 20:26:58
This patch adds swconfig support for ramips_esw:

So far I've developed and tested the patch mostly on the D-LINK DIR-300 B1.
I've made sure that in the enable_vlan=0 case it behaves like a dumb switch,
so external switches should work fine with vlans.

Also I've taken some more care than before to make sure it doesn't change the
pre-existing behaviour in case swconfig is not available or an external switch
is used.

The current state shown by swconfig is always read directly from HW registers,
new settings only show after 'swconfig dev rt305x set apply'.

Note that I have renamed RT305X_ESW_REG_POC[123] to RT305X_ESW_REG_POC[012] to
better match the datasheet (which has a typo on POC0, which probably lead to
the confusing POC[123] naming).

Default on D-LINK DIR-300 B1 after first boot:
|root@OpenWrt:/# swconfig dev rt305x show
|Global attributes:
|        enable_vlan: 1
|Port 0:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:0 link:down
|Port 1:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 1
|        link: port:3 link:up speed:100baseT full-duplex 
|Port 4:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 0
|        pvid: 2
|        link: port:4 link:up speed:100baseT full-duplex 
|Port 5:
|        disable: 1
|        doubletag: 0
|        untag: 1
|        led: -1
|        lan: 1
|        pvid: 1
|        link: port:5 link:down
|Port 6:
|        disable: 0
|        doubletag: 0
|        untag: 0
|        led: -1
|        lan: -1
|        pvid: 1
|        link: port:6 link:up speed:1000baseT full-duplex 
|VLAN 1:
|        ports: 0 1 2 3 6t 
|VLAN 2:
|        ports: 4 6t 

With enable_vlan=0:
|Global attributes:
|        enable_vlan: 0
|Port 0:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:0 link:down
|Port 1:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 1
|        pvid: 0
|        link: port:3 link:up speed:100baseT full-duplex 
|Port 4:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: 0
|        lan: 0
|        pvid: 0
|        link: port:4 link:up speed:100baseT full-duplex 
|Port 5:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: -1
|        lan: 1
|        pvid: 0
|        link: port:5 link:down
|Port 6:
|        disable: 0
|        doubletag: 0
|        untag: 1
|        led: -1
|        lan: -1
|        pvid: 0
|        link: port:6 link:up speed:1000baseT full-duplex 
|VLAN 0:
|        ports: 0 1 2 3 4 5 6 

Closes: https://dev.openwrt.org/ticket/11327

Signed-off-by: Tobias Diedrich <ranma+openwrt@tdiedrich.de>

Patch

Index: target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c
===================================================================
--- target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c	(revision 31538)
+++ target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c	(working copy)
@@ -98,7 +98,7 @@ 
 				     ARRAY_SIZE(wl351_gpio_buttons),
 				     wl351_gpio_buttons);
 	// external rtl8366rb
-	rt305x_esw_data.vlan_config = RT305X_ESW_VLAN_CONFIG_BYPASS;
+	rt305x_esw_data.vlan_config = RT305X_ESW_VLAN_CONFIG_NONE;
 	rt305x_esw_data.reg_initval_fct2 = 0x0002500c;
 	rt305x_esw_data.reg_initval_fpa2 = 0x1f003fff;
 	rt305x_register_ethernet();
Index: target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h
===================================================================
--- target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h	(revision 31538)
+++ target/linux/ramips/files/arch/mips/include/asm/mach-ralink/rt305x_esw_platform.h	(working copy)
@@ -13,7 +13,6 @@ 
 
 enum {
 	RT305X_ESW_VLAN_CONFIG_NONE = 0,
-	RT305X_ESW_VLAN_CONFIG_BYPASS,
 	RT305X_ESW_VLAN_CONFIG_LLLLW,
 	RT305X_ESW_VLAN_CONFIG_WLLLL,
 };
Index: target/linux/ramips/files/drivers/net/ethernet/ramips/Kconfig
===================================================================
--- target/linux/ramips/files/drivers/net/ethernet/ramips/Kconfig	(revision 31538)
+++ target/linux/ramips/files/drivers/net/ethernet/ramips/Kconfig	(working copy)
@@ -2,6 +2,7 @@ 
        tristate "Ralink RT288X/RT3X5X/RT3662/RT3883 ethernet driver"
        depends on MIPS_RALINK
        select PHYLIB if (SOC_RT288X || SOC_RT3883)
+       select SWCONFIG
        help
          This driver supports the etehrnet mac inside the ralink wisocs
 
Index: target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c
===================================================================
--- target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c	(revision 31538)
+++ target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c	(working copy)
@@ -1,4 +1,5 @@ 
 #include <linux/ioport.h>
+#include <linux/switch.h>
 
 #include <rt305x_regs.h>
 #include <rt305x_esw_platform.h>
@@ -8,12 +9,14 @@ 
 #define RT305X_ESW_REG_PVIDC(_n)	(0x40 + 4 * (_n))
 #define RT305X_ESW_REG_VLANI(_n)	(0x50 + 4 * (_n))
 #define RT305X_ESW_REG_VMSC(_n)		(0x70 + 4 * (_n))
+#define RT305X_ESW_REG_POA		0x80
 #define RT305X_ESW_REG_FPA		0x84
 #define RT305X_ESW_REG_SOCPC		0x8c
-#define RT305X_ESW_REG_POC1		0x90
-#define RT305X_ESW_REG_POC2		0x94
-#define RT305X_ESW_REG_POC3		0x98
+#define RT305X_ESW_REG_POC0		0x90
+#define RT305X_ESW_REG_POC1		0x94
+#define RT305X_ESW_REG_POC2		0x98
 #define RT305X_ESW_REG_SGC		0x9c
+#define RT305X_ESW_REG_STRT		0xa0
 #define RT305X_ESW_REG_PCR0		0xc0
 #define RT305X_ESW_REG_PCR1		0xc4
 #define RT305X_ESW_REG_FPA2		0xc8
@@ -25,6 +28,23 @@ 
 #define RT305X_ESW_REG_P3LED	0xb0
 #define RT305X_ESW_REG_P4LED	0xb4
 
+#define RT305X_ESW_LED_LINK		0
+#define RT305X_ESW_LED_100M		1
+#define RT305X_ESW_LED_DUPLEX		2
+#define RT305X_ESW_LED_ACTIVITY		3
+#define RT305X_ESW_LED_COLLISION	4
+#define RT305X_ESW_LED_LINKACT		5
+#define RT305X_ESW_LED_DUPLCOLL		6
+#define RT305X_ESW_LED_10MACT		7
+#define RT305X_ESW_LED_100MACT		8
+// Additional led states not in datasheet:
+#define RT305X_ESW_LED_BLINK		10
+#define RT305X_ESW_LED_ON		12
+
+#define RT305X_ESW_LINK_S	25
+#define RT305X_ESW_DUPLEX_S	9
+#define RT305X_ESW_SPD_S	0
+
 #define RT305X_ESW_PCR0_WT_NWAY_DATA_S	16
 #define RT305X_ESW_PCR0_WT_PHY_CMD	BIT(13)
 #define RT305X_ESW_PCR0_CPU_PHY_REG_S	8
@@ -47,15 +67,27 @@ 
 #define RT305X_ESW_SOCPC_DISBC2CPU_S	16
 #define RT305X_ESW_SOCPC_CRC_PADDING	BIT(25)
 
-#define RT305X_ESW_POC1_EN_BP_S		0
-#define RT305X_ESW_POC1_EN_FC_S		8
-#define RT305X_ESW_POC1_DIS_RMC2CPU_S	16
-#define RT305X_ESW_POC1_DIS_PORT_S	23
+#define RT305X_ESW_POC0_EN_BP_S		0
+#define RT305X_ESW_POC0_EN_FC_S		8
+#define RT305X_ESW_POC0_DIS_RMC2CPU_S	16
+#define RT305X_ESW_POC0_DIS_PORT_M	0x7f
+#define RT305X_ESW_POC0_DIS_PORT_S	23
 
-#define RT305X_ESW_POC3_UNTAG_EN_S	0
-#define RT305X_ESW_POC3_ENAGING_S	8
-#define RT305X_ESW_POC3_DIS_UC_PAUSE_S	16
+#define RT305X_ESW_POC2_UNTAG_EN_M	0xff
+#define RT305X_ESW_POC2_UNTAG_EN_S	0
+#define RT305X_ESW_POC2_ENAGING_S	8
+#define RT305X_ESW_POC2_DIS_UC_PAUSE_S	16
 
+#define RT305X_ESW_SGC2_DOUBLE_TAG_M	0x7f
+#define RT305X_ESW_SGC2_DOUBLE_TAG_S	0
+#define RT305X_ESW_SGC2_LAN_PMAP_M	0x3f
+#define RT305X_ESW_SGC2_LAN_PMAP_S	24
+
+#define RT305X_ESW_PFC1_EN_VLAN_S	16
+#define RT305X_ESW_PFC1_EN_TOS_S	24
+
+#define RT305X_ESW_VLAN_NONE		0xfff
+
 #define RT305X_ESW_PORT0		0
 #define RT305X_ESW_PORT1		1
 #define RT305X_ESW_PORT2		2
@@ -64,6 +96,12 @@ 
 #define RT305X_ESW_PORT5		5
 #define RT305X_ESW_PORT6		6
 
+#define RT305X_ESW_PORTS_NONE		0
+
+#define RT305X_ESW_PMAP_LLLLL		0x00
+#define RT305X_ESW_PMAP_LLLLW		0x10
+#define RT305X_ESW_PMAP_WLLLL		0x01
+
 #define RT305X_ESW_PORTS_INTERNAL					\
 		(BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) |	\
 		 BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) |	\
@@ -78,12 +116,50 @@ 
 		(RT305X_ESW_PORTS_NOCPU | RT305X_ESW_PORTS_CPU)
 
 #define RT305X_ESW_NUM_VLANS	16
+#define RT305X_ESW_NUM_VIDS	4096
 #define RT305X_ESW_NUM_PORTS	7
+#define RT305X_ESW_NUM_LANWAN	6
+#define RT305X_ESW_NUM_LEDS	5
 
+enum {
+	/* global attributes */
+	RT305X_ESW_ATTR_ENABLE_VLAN,
+	/* port attributes */
+	RT305X_ESW_ATTR_PORT_DISABLE,
+	RT305X_ESW_ATTR_PORT_DOUBLETAG,
+	RT305X_ESW_ATTR_PORT_UNTAG,
+	RT305X_ESW_ATTR_PORT_LED,
+	RT305X_ESW_ATTR_PORT_LAN,
+};
+
+struct rt305x_esw_bools {
+	u16	reg;
+	u16	shift;
+};
+
+struct rt305x_esw_port {
+	bool	disable;
+	bool	doubletag;
+	bool	untag;
+	u8	led;
+	u16	pvid;
+};
+
+struct rt305x_esw_vlan {
+	u8	ports;
+	u16	vid;
+};
+
 struct rt305x_esw {
+	struct switch_dev swdev;
 	void __iomem *base;
 	struct rt305x_esw_platform_data *pdata;
 	spinlock_t reg_rw_lock;
+
+	bool enable_vlan;
+	u8 pmap;
+	struct rt305x_esw_vlan vlans[RT305X_ESW_NUM_VLANS];
+	struct rt305x_esw_port ports[RT305X_ESW_NUM_PORTS];
 };
 
 static inline void
@@ -160,6 +236,19 @@ 
 	return ret;
 }
 
+static unsigned
+rt305x_esw_get_vlan_id(struct rt305x_esw *esw, unsigned vlan)
+{
+	unsigned s;
+	unsigned val;
+
+	s = RT305X_ESW_VLANI_VID_S * (vlan % 2);
+	val = rt305x_esw_rr(esw, RT305X_ESW_REG_VLANI(vlan / 2));
+	val = (val >> s) & RT305X_ESW_VLANI_VID_M;
+
+	return val;
+}
+
 static void
 rt305x_esw_set_vlan_id(struct rt305x_esw *esw, unsigned vlan, unsigned vid)
 {
@@ -172,6 +261,17 @@ 
 		       (vid & RT305X_ESW_VLANI_VID_M) << s);
 }
 
+static unsigned
+rt305x_esw_get_pvid(struct rt305x_esw *esw, unsigned port)
+{
+	unsigned s, val;
+
+	s = RT305X_ESW_PVIDC_PVID_S * (port % 2);
+	val = rt305x_esw_rr(esw,
+		       RT305X_ESW_REG_PVIDC(port / 2));
+	return (val >> s) & RT305X_ESW_PVIDC_PVID_M; 
+}
+
 static void
 rt305x_esw_set_pvid(struct rt305x_esw *esw, unsigned port, unsigned pvid)
 {
@@ -184,6 +284,18 @@ 
 		       (pvid & RT305X_ESW_PVIDC_PVID_M) << s);
 }
 
+static unsigned
+rt305x_esw_get_vmsc(struct rt305x_esw *esw, unsigned vlan)
+{
+	unsigned s, val;
+
+	s = RT305X_ESW_VMSC_MSC_S * (vlan % 4);
+	val = rt305x_esw_rr(esw, RT305X_ESW_REG_VMSC(vlan / 4));
+	val = (val >> s) & RT305X_ESW_VMSC_MSC_M;
+
+	return val;
+}
+
 static void
 rt305x_esw_set_vmsc(struct rt305x_esw *esw, unsigned vlan, unsigned msc)
 {
@@ -196,29 +308,39 @@ 
 		       (msc & RT305X_ESW_VMSC_MSC_M) << s);
 }
 
+static int
+rt305x_esw_apply_config(struct switch_dev *dev);
+
 static void
 rt305x_esw_hw_init(struct rt305x_esw *esw)
 {
 	int i;
+	u32 sgc2 = 0;
 
 	/* vodoo from original driver */
 	rt305x_esw_wr(esw, 0xC8A07850, RT305X_ESW_REG_FCT0);
-	rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2);
-	rt305x_esw_wr(esw, 0x00405555, RT305X_ESW_REG_PFC1);
+	/* port priority 1 for all ports, vlan enabled */
+	rt305x_esw_wr(esw, 0x00005555 |
+		      (RT305X_ESW_PORTS_ALL << RT305X_ESW_PFC1_EN_VLAN_S),
+		      RT305X_ESW_REG_PFC1);
+	/* Enable Aging, and VLAN TAG removal */
+	rt305x_esw_wr(esw,
+		      ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC2_ENAGING_S) |
+		       (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC2_UNTAG_EN_S)),
+		      RT305X_ESW_REG_POC2);
 
 	/* Enable Back Pressure, and Flow Control */
 	rt305x_esw_wr(esw,
-		      ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC1_EN_BP_S) |
-		       (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC1_EN_FC_S)),
-		      RT305X_ESW_REG_POC1);
+		      ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_BP_S) |
+		       (RT305X_ESW_PORTS_ALL << RT305X_ESW_POC0_EN_FC_S)),
+		      RT305X_ESW_REG_POC0);
 
-	/* Enable Aging, and VLAN TAG removal */
-	rt305x_esw_wr(esw,
-		      ((RT305X_ESW_PORTS_ALL << RT305X_ESW_POC3_ENAGING_S) |
-		       (RT305X_ESW_PORTS_NOCPU << RT305X_ESW_POC3_UNTAG_EN_S)),
-		      RT305X_ESW_REG_POC3);
+	rt305x_esw_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2);
 
-	rt305x_esw_wr(esw, esw->pdata->reg_initval_fct2, RT305X_ESW_REG_FCT2);
+	/* 300s aging timer, max packet len 1536, broadcast storm prevention disabled,
+	 * disable collision abort, mac xor48 hash, 10 packet back pressure jam,
+	 * GMII disable was_transmit, back pressure disabled, 30ms led flash,
+	 * unmatched IGMP as broadcast, rmc tb fault to all ports */
 	rt305x_esw_wr(esw, 0x0008a301, RT305X_ESW_REG_SGC);
 
 	/* Setup SoC Port control register */
@@ -265,66 +387,525 @@ 
 	/* select local register */
 	rt305x_mii_write(esw, 0, 31, 0x8000);
 
+	/* set up logical config and apply */
 	for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
-		rt305x_esw_set_vlan_id(esw, i, 0);
-		rt305x_esw_set_vmsc(esw, i, 0);
+		esw->vlans[i].vid = RT305X_ESW_VLAN_NONE;
+		esw->vlans[i].ports = RT305X_ESW_PORTS_NONE;
 	}
 
-	for (i = 0; i < RT305X_ESW_NUM_PORTS; i++)
-		rt305x_esw_set_pvid(esw, i, 1);
+	for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+		esw->ports[i].pvid = 1;
+		esw->ports[i].untag = i != RT305X_ESW_PORT6;
+	}
 
 	switch (esw->pdata->vlan_config) {
 	case RT305X_ESW_VLAN_CONFIG_NONE:
-		break;
-
 	case RT305X_ESW_VLAN_CONFIG_BYPASS:
-		/* Pass all vlan tags to all ports */
-		for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
-			rt305x_esw_set_vlan_id(esw, i, i+1);
-			rt305x_esw_set_vmsc(esw, i, RT305X_ESW_PORTS_ALL);
-		}
-		/* Disable VLAN TAG removal, keep aging on. */
-		rt305x_esw_wr(esw,
-			      RT305X_ESW_PORTS_ALL << RT305X_ESW_POC3_ENAGING_S,
-			      RT305X_ESW_REG_POC3);
+		esw->enable_vlan = 0;
+		esw->pmap = RT305X_ESW_PMAP_LLLLL;
 		break;
 
 	case RT305X_ESW_VLAN_CONFIG_LLLLW:
-		rt305x_esw_set_vlan_id(esw, 0, 1);
-		rt305x_esw_set_vlan_id(esw, 1, 2);
-		rt305x_esw_set_pvid(esw, RT305X_ESW_PORT4, 2);
-
-		rt305x_esw_set_vmsc(esw, 0,
+		esw->enable_vlan = 1;
+		esw->pmap = RT305X_ESW_PMAP_LLLLW;
+		esw->vlans[0].vid = 1;
+		esw->vlans[1].vid = 2;
+		esw->ports[4].pvid = 2;
+		esw->ports[5].disable = 1;
+		esw->vlans[0].ports =
 				BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT1) |
 				BIT(RT305X_ESW_PORT2) | BIT(RT305X_ESW_PORT3) |
-				BIT(RT305X_ESW_PORT6));
-		rt305x_esw_set_vmsc(esw, 1,
-				BIT(RT305X_ESW_PORT4) | BIT(RT305X_ESW_PORT6));
+				BIT(RT305X_ESW_PORT6);
+		esw->vlans[1].ports =
+				BIT(RT305X_ESW_PORT4) | BIT(RT305X_ESW_PORT6);
 		break;
 
 	case RT305X_ESW_VLAN_CONFIG_WLLLL:
-		rt305x_esw_set_vlan_id(esw, 0, 1);
-		rt305x_esw_set_vlan_id(esw, 1, 2);
-		rt305x_esw_set_pvid(esw, RT305X_ESW_PORT0, 2);
-
-		rt305x_esw_set_vmsc(esw, 0,
-				BIT(RT305X_ESW_PORT1) | BIT(RT305X_ESW_PORT2) |
-				BIT(RT305X_ESW_PORT3) | BIT(RT305X_ESW_PORT4) |
-				BIT(RT305X_ESW_PORT6));
-		rt305x_esw_set_vmsc(esw, 1,
-				BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT6));
+		esw->enable_vlan = 1;
+		esw->pmap = RT305X_ESW_PMAP_WLLLL;
+		esw->vlans[0].vid = 1;
+		esw->vlans[1].vid = 2;
+		esw->ports[0].pvid = 2;
+		esw->ports[5].disable = 1;
+		esw->vlans[0].ports =
+			BIT(RT305X_ESW_PORT1) | BIT(RT305X_ESW_PORT2) |
+			BIT(RT305X_ESW_PORT3) | BIT(RT305X_ESW_PORT4) |
+			BIT(RT305X_ESW_PORT6);
+		esw->vlans[1].ports =
+				BIT(RT305X_ESW_PORT0) | BIT(RT305X_ESW_PORT6);
 		break;
 
 	default:
 		BUG();
 	}
+
+	rt305x_esw_apply_config(&esw->swdev);
 }
 
 static int
+rt305x_esw_apply_config(struct switch_dev *dev)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	int i;
+	u32 sgc2;
+	u8 doubletag = 0;
+	u8 disable = 0;
+	u8 untag = 0;
+
+	for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+		u32 vid, vmsc;
+		if (esw->enable_vlan) {
+			vid = esw->vlans[i].vid;
+			vmsc = esw->vlans[i].ports;
+		} else {
+			vid = RT305X_ESW_VLAN_NONE;
+			vmsc = RT305X_ESW_PORTS_NONE;
+		}
+		rt305x_esw_set_vlan_id(esw, i, vid);
+		rt305x_esw_set_vmsc(esw, i, vmsc);
+	}
+
+	for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+		u32 pvid;
+		if (esw->enable_vlan) {
+			doubletag |= esw->ports[i].doubletag << i;
+			untag     |= esw->ports[i].untag     << i;
+			disable   |= esw->ports[i].disable   << i;
+			pvid       = esw->ports[i].pvid;
+		} else {
+			doubletag |= 1 << i;
+			untag     |= 1 << i;
+			pvid       = 0;
+		}
+		rt305x_esw_set_pvid(esw, i, pvid);
+		if (i < RT305X_ESW_NUM_LEDS)
+			rt305x_esw_wr(esw, esw->ports[i].led,
+			              RT305X_ESW_REG_P0LED + 4*i);
+	}
+
+	rt305x_esw_rmw(esw, RT305X_ESW_REG_POC0,
+	               RT305X_ESW_POC0_DIS_PORT_M << RT305X_ESW_POC0_DIS_PORT_S,
+	               disable << RT305X_ESW_POC0_DIS_PORT_S);
+	rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
+	               RT305X_ESW_SGC2_DOUBLE_TAG_M << RT305X_ESW_SGC2_DOUBLE_TAG_S,
+	               doubletag << RT305X_ESW_SGC2_DOUBLE_TAG_S);
+	rt305x_esw_rmw(esw, RT305X_ESW_REG_POC2,
+	               RT305X_ESW_POC2_UNTAG_EN_M << RT305X_ESW_POC2_UNTAG_EN_S,
+	               untag << RT305X_ESW_POC2_UNTAG_EN_S);
+	/* unused feature, but still nice to be consistent here... */
+	sgc2 = (~esw->pmap & RT305X_ESW_SGC2_LAN_PMAP_M) << RT305X_ESW_SGC2_LAN_PMAP_S;
+	rt305x_esw_wr(esw, sgc2, RT305X_ESW_REG_SGC2);
+
+	if (!esw->enable_vlan) {
+		/* Still need to put all ports into vlan 0 or they'll be isolated */
+		/* NOTE: vlan 0 is special: No vlan tag is prepended */
+		rt305x_esw_set_vlan_id(esw, 0, 0);
+		rt305x_esw_set_vmsc(esw, 0, RT305X_ESW_PORTS_ALL);
+	}
+
+	return 0;
+}
+
+static int
+rt305x_esw_reset_switch(struct switch_dev *dev)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	esw->enable_vlan = 0;
+	memset(esw->ports, 0, sizeof(esw->ports));
+	memset(esw->vlans, 0, sizeof(esw->vlans));
+	rt305x_esw_hw_init(esw);
+
+	return 0;
+}
+
+static int
+rt305x_esw_get_vlan_enable(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+	val->value.i = esw->enable_vlan;
+
+	return 0;
+}
+
+static int
+rt305x_esw_set_vlan_enable(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+	esw->enable_vlan = val->value.i != 0;
+
+	return 0;
+}
+
+static int
+rt305x_esw_get_port_link(struct switch_dev *dev,
+                         int port,
+                         struct switch_port_link *link)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	u32 speed, poa;
+
+	if (port < 0 || port >= RT305X_ESW_NUM_PORTS)
+		return -EINVAL;
+
+	poa = rt305x_esw_rr(esw, RT305X_ESW_REG_POA) >> port;
+
+	link->link = (poa >> RT305X_ESW_LINK_S) & 1;
+	link->duplex = (poa >> RT305X_ESW_DUPLEX_S) & 1;
+	if (port < RT305X_ESW_NUM_LEDS) {
+		speed = (poa >> RT305X_ESW_SPD_S) & 1;
+	} else {
+		if(port == RT305X_ESW_NUM_PORTS - 1)
+			poa >>= 1;
+		speed = (poa >> RT305X_ESW_SPD_S) & 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 int
+rt305x_esw_get_port_bool(struct switch_dev *dev,
+                         const struct switch_attr *attr,
+                         struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	int idx = val->port_vlan;
+	u32 x, reg, shift;
+
+	if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS)
+		return -EINVAL;
+
+	switch (attr->id) {
+	case RT305X_ESW_ATTR_PORT_DISABLE:
+		reg = RT305X_ESW_REG_POC0;
+		shift = RT305X_ESW_POC0_DIS_PORT_S;
+		break;
+	case RT305X_ESW_ATTR_PORT_DOUBLETAG:
+		reg = RT305X_ESW_REG_SGC2;
+		shift = RT305X_ESW_SGC2_DOUBLE_TAG_S;
+		break;
+	case RT305X_ESW_ATTR_PORT_UNTAG:
+		reg = RT305X_ESW_REG_POC2;
+		shift = RT305X_ESW_POC2_UNTAG_EN_S;
+		break;
+	case RT305X_ESW_ATTR_PORT_LAN:
+		reg = RT305X_ESW_REG_SGC2;
+		shift = RT305X_ESW_SGC2_LAN_PMAP_S;
+		if (idx >= RT305X_ESW_NUM_LANWAN) {
+			val->value.i = -1;
+			return 0;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	x = rt305x_esw_rr(esw, reg);
+	val->value.i = (x >> (idx + shift)) & 1;
+
+	return 0;
+}
+
+static int
+rt305x_esw_set_port_bool(struct switch_dev *dev,
+                         const struct switch_attr *attr,
+                         struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	int idx = val->port_vlan;
+
+	if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS ||
+	    val->value.i < 0 || val->value.i > 1)
+		return -EINVAL;
+
+	switch (attr->id) {
+	case RT305X_ESW_ATTR_PORT_DISABLE:
+		esw->ports[idx].disable = val->value.i;
+		break;
+	case RT305X_ESW_ATTR_PORT_DOUBLETAG:
+		esw->ports[idx].doubletag = val->value.i;
+		break;
+	case RT305X_ESW_ATTR_PORT_UNTAG:
+		esw->ports[idx].untag = val->value.i;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+rt305x_esw_get_port_led(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	int idx = val->port_vlan;
+
+	if (idx < 0 || idx >= RT305X_ESW_NUM_PORTS)
+		return -EINVAL;
+
+	if (idx >= RT305X_ESW_NUM_LEDS)
+		val->value.i = -1;
+	else
+		val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0LED + 4*idx);
+
+	return 0;
+}
+
+static int
+rt305x_esw_set_port_led(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	int idx = val->port_vlan;
+
+	if (idx < 0 || idx >= RT305X_ESW_NUM_LEDS)
+		return -EINVAL;
+
+	esw->ports[idx].led = val->value.i;
+
+	return 0;
+}
+
+static int
+rt305x_esw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+	if (port >= RT305X_ESW_NUM_PORTS)
+		return -EINVAL;
+
+	*val = rt305x_esw_get_pvid(esw, port);
+
+	return 0;
+}
+
+static int
+rt305x_esw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+
+	if (port >= RT305X_ESW_NUM_PORTS)
+		return -EINVAL;
+
+	esw->ports[port].pvid = val;
+
+	return 0;
+}
+
+static int
+rt305x_esw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	u32 vmsc, poc2;
+	int vlan_idx = -1;
+	int i;
+
+	val->len = 0;
+
+	if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS)
+		return -EINVAL;
+
+	// valid vlan?
+	for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+		if (rt305x_esw_get_vlan_id(esw, i) == val->port_vlan &&
+		    rt305x_esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) {
+			vlan_idx = i;
+			break;
+		}
+	}
+
+	if (vlan_idx == -1)
+		return -EINVAL;
+
+	vmsc = rt305x_esw_get_vmsc(esw, vlan_idx);
+	poc2 = rt305x_esw_rr(esw, RT305X_ESW_REG_POC2);
+
+	for (i = 0; i < RT305X_ESW_NUM_PORTS; i++) {
+		struct switch_port *p;
+		int port_mask = 1 << i;
+
+		if (!(vmsc & port_mask))
+			continue;
+
+		p = &val->value.ports[val->len++];
+		p->id = i;
+		if (poc2 & (port_mask << RT305X_ESW_POC2_UNTAG_EN_S))
+			p->flags = 0;
+		else
+			p->flags = 1 << SWITCH_PORT_FLAG_TAGGED;
+	}
+
+	return 0;
+}
+
+static int
+rt305x_esw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+	struct rt305x_esw *esw = container_of(dev, struct rt305x_esw, swdev);
+	int ports;
+	int vlan_idx = -1;
+	int i;
+
+	if (val->port_vlan < 0 || val->port_vlan >= RT305X_ESW_NUM_VIDS ||
+	    val->len > RT305X_ESW_NUM_PORTS)
+		return -EINVAL;
+
+	// one of the already defined vlans?
+	for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+		if (rt305x_esw_get_vlan_id(esw, i) == val->port_vlan &&
+		    rt305x_esw_get_vmsc(esw, i) != RT305X_ESW_PORTS_NONE) {
+			vlan_idx = i;
+			break;
+		}
+	}
+
+	// select a free slot
+	for (i = 0; vlan_idx == -1 && i < RT305X_ESW_NUM_VLANS; i++) {
+		if (rt305x_esw_get_vmsc(esw, i) == RT305X_ESW_PORTS_NONE)
+			vlan_idx = i;
+	}
+
+	// bail if all slots are in use
+	if (vlan_idx == -1)
+		return -EINVAL;
+
+	ports = RT305X_ESW_PORTS_NONE;
+	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 >= RT305X_ESW_NUM_PORTS)
+			return -EINVAL;
+
+		ports |= port_mask;
+		esw->ports[p->id].untag = untagged;
+	}
+	esw->vlans[vlan_idx].ports = ports;
+	if (ports == RT305X_ESW_PORTS_NONE)
+		esw->vlans[vlan_idx].vid = RT305X_ESW_VLAN_NONE;
+	else
+		esw->vlans[vlan_idx].vid = val->port_vlan;
+
+	return 0;
+}
+
+static const struct switch_attr rt305x_esw_global[] = {
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "enable_vlan",
+		.description = "VLAN mode (1:enabled)",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_ENABLE_VLAN,
+		.get = rt305x_esw_get_vlan_enable,
+		.set = rt305x_esw_set_vlan_enable,
+	},
+};
+
+static const struct switch_attr rt305x_esw_port[] = {
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "disable",
+		.description = "Port state (1:disabled)",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_PORT_DISABLE,
+		.get = rt305x_esw_get_port_bool,
+		.set = rt305x_esw_set_port_bool,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "doubletag",
+		.description = "Double tagging for incoming vlan packets (1:enabled)",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_PORT_DOUBLETAG,
+		.get = rt305x_esw_get_port_bool,
+		.set = rt305x_esw_set_port_bool,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "untag",
+		.description = "Untag (1:strip outgoing vlan tag)",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_PORT_UNTAG,
+		.get = rt305x_esw_get_port_bool,
+		.set = rt305x_esw_set_port_bool,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "led",
+		.description = "LED mode (0:link, 1:100m, 2:duplex, 3:activity, "
+		               "4:collision, 5:linkact, 6:duplcoll, 7:10mact, "
+		               "8:100mact, 10:blink, 12:on)",
+		.max = 15,
+		.id = RT305X_ESW_ATTR_PORT_LED,
+		.get = rt305x_esw_get_port_led,
+		.set = rt305x_esw_set_port_led,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "lan",
+		.description = "HW port group (0:wan, 1:lan)",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_PORT_LAN,
+		.get = rt305x_esw_get_port_bool,
+	},
+};
+
+static const struct switch_attr rt305x_esw_vlan[] = {
+};
+
+static const struct switch_dev_ops rt305x_esw_ops = {
+	.attr_global = {
+		.attr = rt305x_esw_global,
+		.n_attr = ARRAY_SIZE(rt305x_esw_global),
+	},
+	.attr_port = {
+		.attr = rt305x_esw_port,
+		.n_attr = ARRAY_SIZE(rt305x_esw_port),
+	},
+	.attr_vlan = {
+		.attr = rt305x_esw_vlan,
+		.n_attr = ARRAY_SIZE(rt305x_esw_vlan),
+	},
+	.get_vlan_ports = rt305x_esw_get_vlan_ports,
+	.set_vlan_ports = rt305x_esw_set_vlan_ports,
+	.get_port_pvid = rt305x_esw_get_port_pvid,
+	.set_port_pvid = rt305x_esw_set_port_pvid,
+	.get_port_link = rt305x_esw_get_port_link,
+	.apply_config = rt305x_esw_apply_config,
+	.reset_switch = rt305x_esw_reset_switch,
+};
+
+static int
 rt305x_esw_probe(struct platform_device *pdev)
 {
 	struct rt305x_esw_platform_data *pdata;
 	struct rt305x_esw *esw;
+	struct switch_dev *swdev;
 	struct resource *res;
 	int err;
 
@@ -351,6 +932,20 @@ 
 		goto free_esw;
 	}
 
+	swdev = &esw->swdev;
+	swdev->name = "rt305x-esw";
+	swdev->alias = "rt305x";
+	swdev->cpu_port = RT305X_ESW_PORT6;
+	swdev->ports = RT305X_ESW_NUM_PORTS;
+	swdev->vlans = RT305X_ESW_NUM_VIDS;
+	swdev->ops = &rt305x_esw_ops;
+
+	err = register_switch(swdev, NULL);
+	if (err < 0) {
+		dev_err(&pdev->dev, "register_switch failed\n");
+		goto free_esw;
+	}
+
 	platform_set_drvdata(pdev, esw);
 
 	esw->pdata = pdata;
@@ -371,6 +966,7 @@ 
 
 	esw = platform_get_drvdata(pdev);
 	if (esw) {
+		unregister_switch(&esw->swdev);
 		platform_set_drvdata(pdev, NULL);
 		iounmap(esw->base);
 		kfree(esw);
Index: target/linux/ramips/image/Makefile
===================================================================
--- target/linux/ramips/image/Makefile	(revision 31538)
+++ target/linux/ramips/image/Makefile	(working copy)
@@ -158,14 +158,14 @@ 
 mtd_dir300b1_kernel_part_size=917504
 mtd_dir300b1_rootfs_part_size=2949120
 define BuildFirmware/DIR300B1
-	$(call BuildFirmware/Generic,$(1),$(2),board=$(3) $(call mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
+	$(call BuildFirmware/Generic,$(1),$(2),$(call mkcmdline,$(3),ttyS1,57600) $(call mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
 	mkwrgimg -s $(4) -d /dev/mtdblock/2 \
 		-i $(call sysupname,$(1),$(2)) \
 		-o $(call imgname,$(1),$(2))-factory.bin
 endef
 
 define BuildFirmware/DIR300B1/initramfs
-	$(call BuildFirmware/Generic/initramfs,$(1),$(2),board=$(3) $(call mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
+	$(call BuildFirmware/Generic/initramfs,$(1),$(2),$(call mkcmdline,$(3),ttyS1,57600) $(call mkmtd/phys,$(mtdlayout_dir300b1)),$(mtd_dir300b1_kernel_part_size),$(mtd_dir300b1_rootfs_part_size))
 endef
 
 define BuildFirmware/Edimax
Index: target/linux/ramips/base-files/etc/uci-defaults/network
===================================================================
--- target/linux/ramips/base-files/etc/uci-defaults/network	(revision 31538)
+++ target/linux/ramips/base-files/etc/uci-defaults/network	(working copy)
@@ -9,6 +9,31 @@ 
 	return
 fi
 
+ramips_setup_rt3x5x_vlans()
+{
+	if [ ! -x /sbin/swconfig ]; then
+		# legacy default
+		ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
+		return
+	fi
+	local wanports=""
+	local lanports=""
+	for port in 5 4 3 2 1 0; do
+		if [ `swconfig dev rt305x port $port get disable` = "1" ]; then
+			continue
+		fi
+		if [ `swconfig dev rt305x port $port get lan` = "0" ]; then
+			wanports="$port $wanports"
+		else
+			lanports="$port $lanports"
+		fi
+	done
+	ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
+	ucidef_add_switch "rt305x" "1" "1"
+	ucidef_add_switch_vlan "rt305x" "1" "$lanports 6t"
+	ucidef_add_switch_vlan "rt305x" "2" "$wanports 6t"
+}
+
 ramips_setup_interfaces()
 {
 	local board="$1"
@@ -71,7 +96,7 @@ 
 	*)
 		RT3X5X=`cat /proc/cpuinfo | grep RT3.5`
 		if [ -n "${RT3X5X}" ]; then
-			ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
+			ramips_setup_rt3x5x_vlans
 		else
 			ucidef_set_interfaces_lan_wan "eth0" "eth1"
 		fi