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

login
register
Submitter Tobias Diedrich
Date 2012-05-06 00:46:39
Message ID <20120506004639.GC2774@yumi.tdiedrich.de>
Download mbox | patch
Permalink /patch/2159/
State Superseded
Delegated to: Gabor Juhos
Headers show

Comments

Tobias Diedrich - 2012-05-06 00:46:39
Third time's a charm.

This patch adds swconfig support for ramips_esw:

Tested on both D-LINK DIR-300 B1 and Sitecom WL-351 (external
rtl8366rb on internal port 5).
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 and verified this
on the WL-351.

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
|        alternate_vlan_disable: 0
|Port 0:
|        disable: 0
|        doubletag: 0
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 2185
|        pvid: 1
|        link: port:0 link:up speed:100baseT full-duplex 
|Port 1:
|        disable: 0
|        doubletag: 0
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 1
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 0
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 1
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 0
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 1
|        link: port:3 link:down
|Port 4:
|        disable: 0
|        doubletag: 0
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 0
|        recv_bad: 0
|        recv_good: 0
|        pvid: 2
|        link: port:4 link:up speed:100baseT full-duplex 
|Port 5:
|        disable: 1
|        doubletag: 0
|        en_vlan: 1
|        untag: 1
|        led: ???
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 1
|        link: port:5 link:down
|Port 6:
|        disable: 0
|        doubletag: 0
|        en_vlan: 1
|        untag: 0
|        led: ???
|        lan: ???
|        recv_bad: ???
|        recv_good: ???
|        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
|        alternate_vlan_disable: 0
|Port 0:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 2381
|        pvid: 0
|        link: port:0 link:up speed:100baseT full-duplex 
|Port 1:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:3 link:down
|Port 4:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 0
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:4 link:up speed:100baseT full-duplex 
|Port 5:
|        disable: 1
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: ???
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:5 link:down
|Port 6:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: ???
|        lan: ???
|        recv_bad: ???
|        recv_good: ???
|        pvid: 0
|        link: port:6 link:up speed:1000baseT full-duplex 
|VLAN 0:
|        ports: 0 1 2 3 4 5 6 

Default on Sitecom WL-351 after boot:
|root@OpenWrt:~# swconfig dev rt305x show
|Global attributes:
|        enable_vlan: 0
|        alternate_vlan_disable: 0
|Port 0:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:0 link:down
|Port 1:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:1 link:down
|Port 2:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:2 link:down
|Port 3:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:3 link:down
|Port 4:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: 0
|        lan: 1
|        recv_bad: 0
|        recv_good: 0
|        pvid: 0
|        link: port:4 link:down
|Port 5:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: ???
|        lan: 1
|        recv_bad: 1
|        recv_good: 65693
|        pvid: 0
|        link: port:5 link:up speed:1000baseT full-duplex 
|Port 6:
|        disable: 0
|        doubletag: 1
|        en_vlan: 1
|        untag: 1
|        led: ???
|        lan: ???
|        recv_bad: ???
|        recv_good: ???
|        pvid: 0
|        link: port:6 link:up speed:1000baseT full-duplex 
|VLAN 0:
|        ports: 0 1 2 3 4 5 6 
|root@OpenWrt:~# swconfig dev rtl8366rb show
|Global attributes:
|        enable_learning: 1
|        enable_vlan: 1
|        enable_vlan4k: 0
|        blinkrate: 0
|        enable_qos: 1
|Port 0:
|        mib: Port 0 MIB counters
[...]
|        led: ???
|        disable: 0
|        rate_in: 1048512
|        rate_out: 1048512
|        pvid: 2
|        link: port:5 link:up speed:1000baseT full-duplex txflow rxflow auto
|VLAN 1:
|        info: VLAN 1: Ports: '01235t', members=002f, untag=000f, fid=0
|        fid: 0
|        ports: 0 1 2 3 5t 
|VLAN 2:
|        info: VLAN 2: Ports: '45t', members=0030, untag=0010, fid=0
|        fid: 0
|        ports: 4 5t 

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

Signed-off-by: Tobias Diedrich <ranma+openwrt@tdiedrich.de>
Daniel Golle - 2012-08-15 11:41:10
I just tested this patch, it works very nice on all systems I got here!

Thanks a lot!

Is there any reason why it's not being committed?


On 06/05/12 03:46, Tobias Diedrich wrote:
> Third time's a charm.
> 
> This patch adds swconfig support for ramips_esw:
> 
> Tested on both D-LINK DIR-300 B1 and Sitecom WL-351 (external
> rtl8366rb on internal port 5).
> 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 and verified this
> on the WL-351.
> 
> 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
> |        alternate_vlan_disable: 0
> |Port 0:
> |        disable: 0
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 2185
> |        pvid: 1
> |        link: port:0 link:up speed:100baseT full-duplex
> |Port 1:
> |        disable: 0
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 1
> |        link: port:1 link:down
> |Port 2:
> |        disable: 0
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 1
> |        link: port:2 link:down
> |Port 3:
> |        disable: 0
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 1
> |        link: port:3 link:down
> |Port 4:
> |        disable: 0
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 0
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 2
> |        link: port:4 link:up speed:100baseT full-duplex
> |Port 5:
> |        disable: 1
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 1
> |        led: ???
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 1
> |        link: port:5 link:down
> |Port 6:
> |        disable: 0
> |        doubletag: 0
> |        en_vlan: 1
> |        untag: 0
> |        led: ???
> |        lan: ???
> |        recv_bad: ???
> |        recv_good: ???
> |        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
> |        alternate_vlan_disable: 0
> |Port 0:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 2381
> |        pvid: 0
> |        link: port:0 link:up speed:100baseT full-duplex
> |Port 1:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:1 link:down
> |Port 2:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:2 link:down
> |Port 3:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:3 link:down
> |Port 4:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 0
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:4 link:up speed:100baseT full-duplex
> |Port 5:
> |        disable: 1
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: ???
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:5 link:down
> |Port 6:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: ???
> |        lan: ???
> |        recv_bad: ???
> |        recv_good: ???
> |        pvid: 0
> |        link: port:6 link:up speed:1000baseT full-duplex
> |VLAN 0:
> |        ports: 0 1 2 3 4 5 6
> 
> Default on Sitecom WL-351 after boot:
> |root@OpenWrt:~# swconfig dev rt305x show
> |Global attributes:
> |        enable_vlan: 0
> |        alternate_vlan_disable: 0
> |Port 0:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:0 link:down
> |Port 1:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:1 link:down
> |Port 2:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:2 link:down
> |Port 3:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:3 link:down
> |Port 4:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: 0
> |        lan: 1
> |        recv_bad: 0
> |        recv_good: 0
> |        pvid: 0
> |        link: port:4 link:down
> |Port 5:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: ???
> |        lan: 1
> |        recv_bad: 1
> |        recv_good: 65693
> |        pvid: 0
> |        link: port:5 link:up speed:1000baseT full-duplex
> |Port 6:
> |        disable: 0
> |        doubletag: 1
> |        en_vlan: 1
> |        untag: 1
> |        led: ???
> |        lan: ???
> |        recv_bad: ???
> |        recv_good: ???
> |        pvid: 0
> |        link: port:6 link:up speed:1000baseT full-duplex
> |VLAN 0:
> |        ports: 0 1 2 3 4 5 6
> |root@OpenWrt:~# swconfig dev rtl8366rb show
> |Global attributes:
> |        enable_learning: 1
> |        enable_vlan: 1
> |        enable_vlan4k: 0
> |        blinkrate: 0
> |        enable_qos: 1
> |Port 0:
> |        mib: Port 0 MIB counters
> [...]
> |        led: ???
> |        disable: 0
> |        rate_in: 1048512
> |        rate_out: 1048512
> |        pvid: 2
> |        link: port:5 link:up speed:1000baseT full-duplex txflow rxflow auto
> |VLAN 1:
> |        info: VLAN 1: Ports: '01235t', members=002f, untag=000f, fid=0
> |        fid: 0
> |        ports: 0 1 2 3 5t
> |VLAN 2:
> |        info: VLAN 2: Ports: '45t', members=0030, untag=0010, fid=0
> |        fid: 0
> |        ports: 4 5t
> 
> Closes: https://dev.openwrt.org/ticket/11327
> 
> Signed-off-by: Tobias Diedrich <ranma+openwrt@tdiedrich.de>
> 
> 
> Index: target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c
> ===================================================================
> --- target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c      (revision 31618)
> +++ 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 31618)
> +++ 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 31618)
> +++ 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 31618)
> +++ target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c  (working copy)
> @@ -1,19 +1,29 @@
>  #include <linux/ioport.h>
> +#include <linux/switch.h>
> 
>  #include <rt305x_regs.h>
>  #include <rt305x_esw_platform.h>
> 
> +/*
> + * HW limitations for this switch:
> + * - no large frame support (PKT_MAX_LEN at most 1536)
> + * - can't have untagged vlan and tagged vlan on one port at the same time,
> + *   though this might be possible using the undocumented PPE
> + */
> +
>  #define RT305X_ESW_REG_FCT0            0x08
>  #define RT305X_ESW_REG_PFC1            0x14
>  #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
> @@ -24,7 +34,30 @@
>  #define RT305X_ESW_REG_P2LED   0xac
>  #define RT305X_ESW_REG_P3LED   0xb0
>  #define RT305X_ESW_REG_P4LED   0xb4
> +#define RT305X_ESW_REG_P0PC    0xe8
> +#define RT305X_ESW_REG_P1PC    0xec
> +#define RT305X_ESW_REG_P2PC    0xf0
> +#define RT305X_ESW_REG_P3PC    0xf4
> +#define RT305X_ESW_REG_P4PC    0xf8
> +#define RT305X_ESW_REG_P5PC    0xfc
> 
> +#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 +80,28 @@
>  #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_M      0xff
> +#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 +110,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 +130,56 @@
>                 (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,
> +       RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
> +       /* port attributes */
> +       RT305X_ESW_ATTR_PORT_DISABLE,
> +       RT305X_ESW_ATTR_PORT_DOUBLETAG,
> +       RT305X_ESW_ATTR_PORT_EN_VLAN,
> +       RT305X_ESW_ATTR_PORT_UNTAG,
> +       RT305X_ESW_ATTR_PORT_LED,
> +       RT305X_ESW_ATTR_PORT_LAN,
> +       RT305X_ESW_ATTR_PORT_RECV_BAD,
> +       RT305X_ESW_ATTR_PORT_RECV_GOOD,
> +};
> +
> +struct rt305x_esw_bools {
> +       u16     reg;
> +       u16     shift;
> +};
> +
> +struct rt305x_esw_port {
> +       bool    disable;
> +       bool    doubletag;
> +       bool    untag;
> +       bool    en_vlan;
> +       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 global_vlan_enable;
> +       bool alt_vlan_disable;
> +       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 +256,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 +281,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 +304,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,6 +328,9 @@
>                        (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)
>  {
> @@ -203,23 +338,30 @@
> 
>         /* 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);
> +       rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2);
> 
>         /* Setup SoC Port control register */
>         rt305x_esw_wr(esw,
> @@ -265,66 +407,609 @@
>         /* 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].en_vlan = 1;
> +               esw->ports[i].untag = i != RT305X_ESW_PORT6;
> +       }
> 
>         switch (esw->pdata->vlan_config) {
>         case RT305X_ESW_VLAN_CONFIG_NONE:
> +               esw->global_vlan_enable = 0;
> +               esw->pmap = RT305X_ESW_PMAP_LLLLL;
>                 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);
> -               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->global_vlan_enable = 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->global_vlan_enable = 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;
> +       u8 disable = 0;
> +       u8 doubletag = 0;
> +       u8 en_vlan = 0;
> +       u8 untag = 0;
> +
> +       for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
> +               u32 vid, vmsc;
> +               if (esw->global_vlan_enable) {
> +                       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;
> +               disable |= esw->ports[i].disable << i;
> +               if (esw->global_vlan_enable) {
> +                       doubletag |= esw->ports[i].doubletag << i;
> +                       en_vlan   |= esw->ports[i].en_vlan   << i;
> +                       untag     |= esw->ports[i].untag     << i;
> +                       pvid       = esw->ports[i].pvid;
> +               } else {
> +                       int x = esw->alt_vlan_disable ? 0 : 1;
> +                       doubletag |= x << i;
> +                       en_vlan   |= x << i;
> +                       untag     |= x << 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_PFC1,
> +                      RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
> +                      en_vlan << RT305X_ESW_PFC1_EN_VLAN_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... */
> +       rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
> +                      RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
> +                      ~esw->pmap << RT305X_ESW_SGC2_LAN_PMAP_S);
> +
> +       if (!esw->global_vlan_enable) {
> +               /* 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->global_vlan_enable = 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->global_vlan_enable;
> +
> +       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->global_vlan_enable = val->value.i != 0;
> +
> +       return 0;
> +}
> +
> +static int
> +rt305x_esw_get_alt_vlan_disable(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->alt_vlan_disable;
> +
> +       return 0;
> +}
> +
> +static int
> +rt305x_esw_set_alt_vlan_disable(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->alt_vlan_disable = 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_EN_VLAN:
> +               reg = RT305X_ESW_REG_PFC1;
> +               shift = RT305X_ESW_PFC1_EN_VLAN_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)
> +                       return -EINVAL;
> +               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_EN_VLAN:
> +               esw->ports[idx].en_vlan = 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_recv_badgood(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;
> +       int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16;
> +
> +       if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
> +               return -EINVAL;
> +
> +       val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0PC + 4*idx) >> shift;
> +
> +       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 ||
> +           idx >= RT305X_ESW_NUM_LEDS)
> +               return -EINVAL;
> +
> +       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 (esw->vlans[i].vid == val->port_vlan &&
> +                   esw->vlans[i].ports != 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 (esw->vlans[i].ports == 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,
> +       },
> +       {
> +               .type = SWITCH_TYPE_INT,
> +               .name = "alternate_vlan_disable",
> +               .description = "Use en_vlan instead of doubletag to disable VLAN mode",
> +               .max = 1,
> +               .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
> +               .get = rt305x_esw_get_alt_vlan_disable,
> +               .set = rt305x_esw_set_alt_vlan_disable,
> +       },
> +};
> +
> +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 = "en_vlan",
> +               .description = "VLAN enabled (1:enabled)",
> +               .max = 1,
> +               .id = RT305X_ESW_ATTR_PORT_EN_VLAN,
> +               .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,
> +       },
> +       {
> +               .type = SWITCH_TYPE_INT,
> +               .name = "recv_bad",
> +               .description = "Receive bad packet counter",
> +               .id = RT305X_ESW_ATTR_PORT_RECV_BAD,
> +               .get = rt305x_esw_get_port_recv_badgood,
> +       },
> +       {
> +               .type = SWITCH_TYPE_INT,
> +               .name = "recv_good",
> +               .description = "Receive good packet counter",
> +               .id = RT305X_ESW_ATTR_PORT_RECV_GOOD,
> +               .get = rt305x_esw_get_port_recv_badgood,
> +       },
> +};
> +
> +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 +1036,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 +1070,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/base-files/etc/uci-defaults/network
> ===================================================================
> --- target/linux/ramips/base-files/etc/uci-defaults/network     (revision 31618)
> +++ 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"
> @@ -70,7 +95,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
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
>
John Crispin - 2012-08-15 11:44:40
On 15/08/12 13:41, Daniel Golle wrote:
> I just tested this patch, it works very nice on all systems I got here!
> 
> Thanks a lot!
> 
> Is there any reason why it's not being committed?
> 
>

i will test it on 2-3 of my ralink units later aswell
Tobias Diedrich - 2012-08-15 18:20:57
No idea, but I've got a slightly updated patch by now, which I
haven't gotten around to submit again. :)

Daniel Golle wrote:
> I just tested this patch, it works very nice on all systems I got here!
> 
> Thanks a lot!
> 
> Is there any reason why it's not being committed?
> 
> 
> On 06/05/12 03:46, Tobias Diedrich wrote:
> > Third time's a charm.
> > 
> > This patch adds swconfig support for ramips_esw:
> > 
> > Tested on both D-LINK DIR-300 B1 and Sitecom WL-351 (external
> > rtl8366rb on internal port 5).
> > 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 and verified this
> > on the WL-351.
> > 
> > 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
> > |        alternate_vlan_disable: 0
> > |Port 0:
> > |        disable: 0
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 2185
> > |        pvid: 1
> > |        link: port:0 link:up speed:100baseT full-duplex
> > |Port 1:
> > |        disable: 0
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 1
> > |        link: port:1 link:down
> > |Port 2:
> > |        disable: 0
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 1
> > |        link: port:2 link:down
> > |Port 3:
> > |        disable: 0
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 1
> > |        link: port:3 link:down
> > |Port 4:
> > |        disable: 0
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 0
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 2
> > |        link: port:4 link:up speed:100baseT full-duplex
> > |Port 5:
> > |        disable: 1
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: ???
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 1
> > |        link: port:5 link:down
> > |Port 6:
> > |        disable: 0
> > |        doubletag: 0
> > |        en_vlan: 1
> > |        untag: 0
> > |        led: ???
> > |        lan: ???
> > |        recv_bad: ???
> > |        recv_good: ???
> > |        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
> > |        alternate_vlan_disable: 0
> > |Port 0:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 2381
> > |        pvid: 0
> > |        link: port:0 link:up speed:100baseT full-duplex
> > |Port 1:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:1 link:down
> > |Port 2:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:2 link:down
> > |Port 3:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:3 link:down
> > |Port 4:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 0
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:4 link:up speed:100baseT full-duplex
> > |Port 5:
> > |        disable: 1
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: ???
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:5 link:down
> > |Port 6:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: ???
> > |        lan: ???
> > |        recv_bad: ???
> > |        recv_good: ???
> > |        pvid: 0
> > |        link: port:6 link:up speed:1000baseT full-duplex
> > |VLAN 0:
> > |        ports: 0 1 2 3 4 5 6
> > 
> > Default on Sitecom WL-351 after boot:
> > |root@OpenWrt:~# swconfig dev rt305x show
> > |Global attributes:
> > |        enable_vlan: 0
> > |        alternate_vlan_disable: 0
> > |Port 0:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:0 link:down
> > |Port 1:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:1 link:down
> > |Port 2:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:2 link:down
> > |Port 3:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:3 link:down
> > |Port 4:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: 0
> > |        lan: 1
> > |        recv_bad: 0
> > |        recv_good: 0
> > |        pvid: 0
> > |        link: port:4 link:down
> > |Port 5:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: ???
> > |        lan: 1
> > |        recv_bad: 1
> > |        recv_good: 65693
> > |        pvid: 0
> > |        link: port:5 link:up speed:1000baseT full-duplex
> > |Port 6:
> > |        disable: 0
> > |        doubletag: 1
> > |        en_vlan: 1
> > |        untag: 1
> > |        led: ???
> > |        lan: ???
> > |        recv_bad: ???
> > |        recv_good: ???
> > |        pvid: 0
> > |        link: port:6 link:up speed:1000baseT full-duplex
> > |VLAN 0:
> > |        ports: 0 1 2 3 4 5 6
> > |root@OpenWrt:~# swconfig dev rtl8366rb show
> > |Global attributes:
> > |        enable_learning: 1
> > |        enable_vlan: 1
> > |        enable_vlan4k: 0
> > |        blinkrate: 0
> > |        enable_qos: 1
> > |Port 0:
> > |        mib: Port 0 MIB counters
> > [...]
> > |        led: ???
> > |        disable: 0
> > |        rate_in: 1048512
> > |        rate_out: 1048512
> > |        pvid: 2
> > |        link: port:5 link:up speed:1000baseT full-duplex txflow rxflow auto
> > |VLAN 1:
> > |        info: VLAN 1: Ports: '01235t', members=002f, untag=000f, fid=0
> > |        fid: 0
> > |        ports: 0 1 2 3 5t
> > |VLAN 2:
> > |        info: VLAN 2: Ports: '45t', members=0030, untag=0010, fid=0
> > |        fid: 0
> > |        ports: 4 5t
> > 
> > Closes: https://dev.openwrt.org/ticket/11327
> > 
> > Signed-off-by: Tobias Diedrich <ranma+openwrt@tdiedrich.de>
> > 
> > 
> > Index: target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c
> > ===================================================================
> > --- target/linux/ramips/files/arch/mips/ralink/rt305x/mach-wl351.c      (revision 31618)
> > +++ 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 31618)
> > +++ 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 31618)
> > +++ 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 31618)
> > +++ target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c  (working copy)
> > @@ -1,19 +1,29 @@
> >  #include <linux/ioport.h>
> > +#include <linux/switch.h>
> > 
> >  #include <rt305x_regs.h>
> >  #include <rt305x_esw_platform.h>
> > 
> > +/*
> > + * HW limitations for this switch:
> > + * - no large frame support (PKT_MAX_LEN at most 1536)
> > + * - can't have untagged vlan and tagged vlan on one port at the same time,
> > + *   though this might be possible using the undocumented PPE
> > + */
> > +
> >  #define RT305X_ESW_REG_FCT0            0x08
> >  #define RT305X_ESW_REG_PFC1            0x14
> >  #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
> > @@ -24,7 +34,30 @@
> >  #define RT305X_ESW_REG_P2LED   0xac
> >  #define RT305X_ESW_REG_P3LED   0xb0
> >  #define RT305X_ESW_REG_P4LED   0xb4
> > +#define RT305X_ESW_REG_P0PC    0xe8
> > +#define RT305X_ESW_REG_P1PC    0xec
> > +#define RT305X_ESW_REG_P2PC    0xf0
> > +#define RT305X_ESW_REG_P3PC    0xf4
> > +#define RT305X_ESW_REG_P4PC    0xf8
> > +#define RT305X_ESW_REG_P5PC    0xfc
> > 
> > +#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 +80,28 @@
> >  #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_M      0xff
> > +#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 +110,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 +130,56 @@
> >                 (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,
> > +       RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
> > +       /* port attributes */
> > +       RT305X_ESW_ATTR_PORT_DISABLE,
> > +       RT305X_ESW_ATTR_PORT_DOUBLETAG,
> > +       RT305X_ESW_ATTR_PORT_EN_VLAN,
> > +       RT305X_ESW_ATTR_PORT_UNTAG,
> > +       RT305X_ESW_ATTR_PORT_LED,
> > +       RT305X_ESW_ATTR_PORT_LAN,
> > +       RT305X_ESW_ATTR_PORT_RECV_BAD,
> > +       RT305X_ESW_ATTR_PORT_RECV_GOOD,
> > +};
> > +
> > +struct rt305x_esw_bools {
> > +       u16     reg;
> > +       u16     shift;
> > +};
> > +
> > +struct rt305x_esw_port {
> > +       bool    disable;
> > +       bool    doubletag;
> > +       bool    untag;
> > +       bool    en_vlan;
> > +       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 global_vlan_enable;
> > +       bool alt_vlan_disable;
> > +       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 +256,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 +281,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 +304,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,6 +328,9 @@
> >                        (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)
> >  {
> > @@ -203,23 +338,30 @@
> > 
> >         /* 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);
> > +       rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2);
> > 
> >         /* Setup SoC Port control register */
> >         rt305x_esw_wr(esw,
> > @@ -265,66 +407,609 @@
> >         /* 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].en_vlan = 1;
> > +               esw->ports[i].untag = i != RT305X_ESW_PORT6;
> > +       }
> > 
> >         switch (esw->pdata->vlan_config) {
> >         case RT305X_ESW_VLAN_CONFIG_NONE:
> > +               esw->global_vlan_enable = 0;
> > +               esw->pmap = RT305X_ESW_PMAP_LLLLL;
> >                 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);
> > -               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->global_vlan_enable = 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->global_vlan_enable = 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;
> > +       u8 disable = 0;
> > +       u8 doubletag = 0;
> > +       u8 en_vlan = 0;
> > +       u8 untag = 0;
> > +
> > +       for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
> > +               u32 vid, vmsc;
> > +               if (esw->global_vlan_enable) {
> > +                       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;
> > +               disable |= esw->ports[i].disable << i;
> > +               if (esw->global_vlan_enable) {
> > +                       doubletag |= esw->ports[i].doubletag << i;
> > +                       en_vlan   |= esw->ports[i].en_vlan   << i;
> > +                       untag     |= esw->ports[i].untag     << i;
> > +                       pvid       = esw->ports[i].pvid;
> > +               } else {
> > +                       int x = esw->alt_vlan_disable ? 0 : 1;
> > +                       doubletag |= x << i;
> > +                       en_vlan   |= x << i;
> > +                       untag     |= x << 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_PFC1,
> > +                      RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
> > +                      en_vlan << RT305X_ESW_PFC1_EN_VLAN_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... */
> > +       rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
> > +                      RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
> > +                      ~esw->pmap << RT305X_ESW_SGC2_LAN_PMAP_S);
> > +
> > +       if (!esw->global_vlan_enable) {
> > +               /* 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->global_vlan_enable = 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->global_vlan_enable;
> > +
> > +       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->global_vlan_enable = val->value.i != 0;
> > +
> > +       return 0;
> > +}
> > +
> > +static int
> > +rt305x_esw_get_alt_vlan_disable(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->alt_vlan_disable;
> > +
> > +       return 0;
> > +}
> > +
> > +static int
> > +rt305x_esw_set_alt_vlan_disable(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->alt_vlan_disable = 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_EN_VLAN:
> > +               reg = RT305X_ESW_REG_PFC1;
> > +               shift = RT305X_ESW_PFC1_EN_VLAN_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)
> > +                       return -EINVAL;
> > +               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_EN_VLAN:
> > +               esw->ports[idx].en_vlan = 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_recv_badgood(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;
> > +       int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16;
> > +
> > +       if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
> > +               return -EINVAL;
> > +
> > +       val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0PC + 4*idx) >> shift;
> > +
> > +       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 ||
> > +           idx >= RT305X_ESW_NUM_LEDS)
> > +               return -EINVAL;
> > +
> > +       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 (esw->vlans[i].vid == val->port_vlan &&
> > +                   esw->vlans[i].ports != 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 (esw->vlans[i].ports == 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,
> > +       },
> > +       {
> > +               .type = SWITCH_TYPE_INT,
> > +               .name = "alternate_vlan_disable",
> > +               .description = "Use en_vlan instead of doubletag to disable VLAN mode",
> > +               .max = 1,
> > +               .id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
> > +               .get = rt305x_esw_get_alt_vlan_disable,
> > +               .set = rt305x_esw_set_alt_vlan_disable,
> > +       },
> > +};
> > +
> > +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 = "en_vlan",
> > +               .description = "VLAN enabled (1:enabled)",
> > +               .max = 1,
> > +               .id = RT305X_ESW_ATTR_PORT_EN_VLAN,
> > +               .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,
> > +       },
> > +       {
> > +               .type = SWITCH_TYPE_INT,
> > +               .name = "recv_bad",
> > +               .description = "Receive bad packet counter",
> > +               .id = RT305X_ESW_ATTR_PORT_RECV_BAD,
> > +               .get = rt305x_esw_get_port_recv_badgood,
> > +       },
> > +       {
> > +               .type = SWITCH_TYPE_INT,
> > +               .name = "recv_good",
> > +               .description = "Receive good packet counter",
> > +               .id = RT305X_ESW_ATTR_PORT_RECV_GOOD,
> > +               .get = rt305x_esw_get_port_recv_badgood,
> > +       },
> > +};
> > +
> > +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 +1036,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 +1070,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/base-files/etc/uci-defaults/network
> > ===================================================================
> > --- target/linux/ramips/base-files/etc/uci-defaults/network     (revision 31618)
> > +++ 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"
> > @@ -70,7 +95,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
> > _______________________________________________
> > openwrt-devel mailing list
> > openwrt-devel@lists.openwrt.org
> > https://lists.openwrt.org/mailman/listinfo/openwrt-devel
> > 
> 
> 
> -- 
> ALLNET GmbH ; Maistr. 2 ; D-82110 Germering ; Germany
> Tel. +49-89-89422217 - Fax +49-89-89422233
> http://www.allnet.de
> email: Daniel Golle <dgolle@allnet.de>
> Schulungs-/Veranstaltungsprogramm: http://www.802lab.de<http://www.802lab.de/>
> Geschäftsführer: Wolfgang Marcus Bauer
> Handelsregister München B 95922 ; UST-ID-Nr. DE 128214294 ;
> St.-Nr.117/115/00164
> WEEE-Reg.-NR. DE 13101093
> Bankverbindung:
> Sparkasse Fürstenfeldbruck KTO: 2774594 ; BLZ: 70053070
> Swift-Code: BYLADEM1FFB ; IBAN: DE61700530700002774594

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 31618)
+++ 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 31618)
+++ 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 31618)
+++ 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 31618)
+++ target/linux/ramips/files/drivers/net/ethernet/ramips/ramips_esw.c	(working copy)
@@ -1,19 +1,29 @@ 
 #include <linux/ioport.h>
+#include <linux/switch.h>
 
 #include <rt305x_regs.h>
 #include <rt305x_esw_platform.h>
 
+/*
+ * HW limitations for this switch:
+ * - no large frame support (PKT_MAX_LEN at most 1536)
+ * - can't have untagged vlan and tagged vlan on one port at the same time,
+ *   though this might be possible using the undocumented PPE
+ */
+
 #define RT305X_ESW_REG_FCT0		0x08
 #define RT305X_ESW_REG_PFC1		0x14
 #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
@@ -24,7 +34,30 @@ 
 #define RT305X_ESW_REG_P2LED	0xac
 #define RT305X_ESW_REG_P3LED	0xb0
 #define RT305X_ESW_REG_P4LED	0xb4
+#define RT305X_ESW_REG_P0PC	0xe8
+#define RT305X_ESW_REG_P1PC	0xec
+#define RT305X_ESW_REG_P2PC	0xf0
+#define RT305X_ESW_REG_P3PC	0xf4
+#define RT305X_ESW_REG_P4PC	0xf8
+#define RT305X_ESW_REG_P5PC	0xfc
 
+#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 +80,28 @@ 
 #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_M	0xff
+#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 +110,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 +130,56 @@ 
 		(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,
+	RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
+	/* port attributes */
+	RT305X_ESW_ATTR_PORT_DISABLE,
+	RT305X_ESW_ATTR_PORT_DOUBLETAG,
+	RT305X_ESW_ATTR_PORT_EN_VLAN,
+	RT305X_ESW_ATTR_PORT_UNTAG,
+	RT305X_ESW_ATTR_PORT_LED,
+	RT305X_ESW_ATTR_PORT_LAN,
+	RT305X_ESW_ATTR_PORT_RECV_BAD,
+	RT305X_ESW_ATTR_PORT_RECV_GOOD,
+};
+
+struct rt305x_esw_bools {
+	u16	reg;
+	u16	shift;
+};
+
+struct rt305x_esw_port {
+	bool	disable;
+	bool	doubletag;
+	bool	untag;
+	bool	en_vlan;
+	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 global_vlan_enable;
+	bool alt_vlan_disable;
+	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 +256,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 +281,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 +304,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,6 +328,9 @@ 
 		       (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)
 {
@@ -203,23 +338,30 @@ 
 
 	/* 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);
+	rt305x_esw_wr(esw, 0x00000000, RT305X_ESW_REG_SGC2);
 
 	/* Setup SoC Port control register */
 	rt305x_esw_wr(esw,
@@ -265,66 +407,609 @@ 
 	/* 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].en_vlan = 1;
+		esw->ports[i].untag = i != RT305X_ESW_PORT6;
+	}
 
 	switch (esw->pdata->vlan_config) {
 	case RT305X_ESW_VLAN_CONFIG_NONE:
+		esw->global_vlan_enable = 0;
+		esw->pmap = RT305X_ESW_PMAP_LLLLL;
 		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);
-		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->global_vlan_enable = 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->global_vlan_enable = 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;
+	u8 disable = 0;
+	u8 doubletag = 0;
+	u8 en_vlan = 0;
+	u8 untag = 0;
+
+	for (i = 0; i < RT305X_ESW_NUM_VLANS; i++) {
+		u32 vid, vmsc;
+		if (esw->global_vlan_enable) {
+			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;
+		disable |= esw->ports[i].disable << i;
+		if (esw->global_vlan_enable) {
+			doubletag |= esw->ports[i].doubletag << i;
+			en_vlan   |= esw->ports[i].en_vlan   << i;
+			untag     |= esw->ports[i].untag     << i;
+			pvid       = esw->ports[i].pvid;
+		} else {
+			int x = esw->alt_vlan_disable ? 0 : 1;
+			doubletag |= x << i;
+			en_vlan   |= x << i;
+			untag     |= x << 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_PFC1,
+	               RT305X_ESW_PFC1_EN_VLAN_M << RT305X_ESW_PFC1_EN_VLAN_S,
+	               en_vlan << RT305X_ESW_PFC1_EN_VLAN_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... */
+	rt305x_esw_rmw(esw, RT305X_ESW_REG_SGC2,
+	               RT305X_ESW_SGC2_LAN_PMAP_M << RT305X_ESW_SGC2_LAN_PMAP_S,
+	               ~esw->pmap << RT305X_ESW_SGC2_LAN_PMAP_S);
+
+	if (!esw->global_vlan_enable) {
+		/* 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->global_vlan_enable = 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->global_vlan_enable;
+
+	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->global_vlan_enable = val->value.i != 0;
+
+	return 0;
+}
+
+static int
+rt305x_esw_get_alt_vlan_disable(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->alt_vlan_disable;
+
+	return 0;
+}
+
+static int
+rt305x_esw_set_alt_vlan_disable(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->alt_vlan_disable = 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_EN_VLAN:
+		reg = RT305X_ESW_REG_PFC1;
+		shift = RT305X_ESW_PFC1_EN_VLAN_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)
+			return -EINVAL;
+		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_EN_VLAN:
+		esw->ports[idx].en_vlan = 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_recv_badgood(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;
+	int shift = attr->id == RT305X_ESW_ATTR_PORT_RECV_GOOD ? 0 : 16;
+
+	if (idx < 0 || idx >= RT305X_ESW_NUM_LANWAN)
+		return -EINVAL;
+
+	val->value.i = rt305x_esw_rr(esw, RT305X_ESW_REG_P0PC + 4*idx) >> shift;
+
+	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 ||
+	    idx >= RT305X_ESW_NUM_LEDS)
+		return -EINVAL;
+
+	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 (esw->vlans[i].vid == val->port_vlan &&
+		    esw->vlans[i].ports != 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 (esw->vlans[i].ports == 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,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "alternate_vlan_disable",
+		.description = "Use en_vlan instead of doubletag to disable VLAN mode",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_ALT_VLAN_DISABLE,
+		.get = rt305x_esw_get_alt_vlan_disable,
+		.set = rt305x_esw_set_alt_vlan_disable,
+	},
+};
+
+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 = "en_vlan",
+		.description = "VLAN enabled (1:enabled)",
+		.max = 1,
+		.id = RT305X_ESW_ATTR_PORT_EN_VLAN,
+		.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,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "recv_bad",
+		.description = "Receive bad packet counter",
+		.id = RT305X_ESW_ATTR_PORT_RECV_BAD,
+		.get = rt305x_esw_get_port_recv_badgood,
+	},
+	{
+		.type = SWITCH_TYPE_INT,
+		.name = "recv_good",
+		.description = "Receive good packet counter",
+		.id = RT305X_ESW_ATTR_PORT_RECV_GOOD,
+		.get = rt305x_esw_get_port_recv_badgood,
+	},
+};
+
+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 +1036,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 +1070,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/base-files/etc/uci-defaults/network
===================================================================
--- target/linux/ramips/base-files/etc/uci-defaults/network	(revision 31618)
+++ 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"
@@ -70,7 +95,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