generic: split files according to kernel versions
authorMarko Ratkaj <marko.ratkaj@sartura.hr>
Tue, 18 Sep 2018 14:33:22 +0000 (16:33 +0200)
committerZoltan HERPAI <wigyori@uid0.hu>
Thu, 4 Oct 2018 15:31:27 +0000 (17:31 +0200)
Signed-off-by: Marko Ratkaj <marko.ratkaj@sartura.hr>
260 files changed:
target/linux/generic/files-3.18/Documentation/networking/adm6996.txt [new file with mode: 0644]
target/linux/generic/files-3.18/arch/mips/fw/myloader/Makefile [new file with mode: 0644]
target/linux/generic/files-3.18/arch/mips/fw/myloader/myloader.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/leds/ledtrig-netdev.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/misc/owl-loader.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/Kconfig [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/Makefile [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_brnimage.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_eva.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_fit.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_jimage.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_lzma.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_minor.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_seama.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_squashfs.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_tplink.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_trx.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_uimage.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_wrgg.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/mtd/myloader.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/adm6996.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/adm6996.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/ar8216.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/ar8216.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/ar8327.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/ar8327.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/Kconfig [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/Makefile [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_common.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_mdio.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_mmap.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_phy_fixup.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_priv.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_regs.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_spi.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/b53/b53_srab.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/ip17xx.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/mvsw61xx.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/mvsw61xx.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/mvswitch.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/mvswitch.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/psb6970.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8306.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8366_smi.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8366_smi.h [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8366rb.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8366s.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8367.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/rtl8367b.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/swconfig.c [new file with mode: 0644]
target/linux/generic/files-3.18/drivers/net/phy/swconfig_leds.c [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/ar8216_platform.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/ath5k_platform.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/ath9k_platform.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/myloader.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/platform_data/adm6996-gpio.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/platform_data/b53.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/routerboot.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/rt2x00_platform.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/rtl8366.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/rtl8367.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/linux/switch.h [new file with mode: 0644]
target/linux/generic/files-3.18/include/uapi/linux/switch.h [new file with mode: 0644]
target/linux/generic/files-4.14/Documentation/networking/adm6996.txt [new file with mode: 0644]
target/linux/generic/files-4.14/arch/mips/fw/myloader/Makefile [new file with mode: 0644]
target/linux/generic/files-4.14/arch/mips/fw/myloader/myloader.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/leds/ledtrig-netdev.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/misc/owl-loader.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/Kconfig [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/Makefile [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_brnimage.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_eva.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_fit.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_jimage.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_lzma.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_minor.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_seama.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_squashfs.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_tplink.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_trx.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_uimage.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_wrgg.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/mtd/myloader.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/adm6996.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/adm6996.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/ar8216.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/ar8216.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/ar8327.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/ar8327.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/Kconfig [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/Makefile [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_common.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_mdio.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_mmap.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_phy_fixup.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_priv.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_regs.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_spi.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/b53/b53_srab.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/ip17xx.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/mvsw61xx.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/mvsw61xx.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/mvswitch.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/mvswitch.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/psb6970.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8306.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8366_smi.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8366_smi.h [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8366rb.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8366s.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8367.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/rtl8367b.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/swconfig.c [new file with mode: 0644]
target/linux/generic/files-4.14/drivers/net/phy/swconfig_leds.c [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/ar8216_platform.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/ath5k_platform.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/ath9k_platform.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/myloader.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/platform_data/adm6996-gpio.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/platform_data/b53.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/routerboot.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/rt2x00_platform.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/rtl8366.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/rtl8367.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/linux/switch.h [new file with mode: 0644]
target/linux/generic/files-4.14/include/uapi/linux/switch.h [new file with mode: 0644]
target/linux/generic/files-4.9/Documentation/networking/adm6996.txt [new file with mode: 0644]
target/linux/generic/files-4.9/arch/mips/fw/myloader/Makefile [new file with mode: 0644]
target/linux/generic/files-4.9/arch/mips/fw/myloader/myloader.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/leds/ledtrig-netdev.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/misc/owl-loader.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/Kconfig [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/Makefile [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_brnimage.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_eva.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_fit.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_jimage.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_lzma.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_minor.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_seama.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_squashfs.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_tplink.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_trx.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_uimage.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_wrgg.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/mtd/myloader.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/adm6996.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/adm6996.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/ar8216.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/ar8216.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/ar8327.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/ar8327.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/Kconfig [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/Makefile [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_common.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_mdio.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_mmap.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_phy_fixup.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_priv.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_regs.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_spi.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/b53/b53_srab.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/ip17xx.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/mvsw61xx.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/mvsw61xx.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/mvswitch.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/mvswitch.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/psb6970.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8306.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8366_smi.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8366_smi.h [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8366rb.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8366s.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8367.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/rtl8367b.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/swconfig.c [new file with mode: 0644]
target/linux/generic/files-4.9/drivers/net/phy/swconfig_leds.c [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/ar8216_platform.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/ath5k_platform.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/ath9k_platform.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/myloader.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/platform_data/adm6996-gpio.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/platform_data/b53.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/routerboot.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/rt2x00_platform.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/rtl8366.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/rtl8367.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/linux/switch.h [new file with mode: 0644]
target/linux/generic/files-4.9/include/uapi/linux/switch.h [new file with mode: 0644]
target/linux/generic/files/Documentation/networking/adm6996.txt [deleted file]
target/linux/generic/files/arch/mips/fw/myloader/Makefile [deleted file]
target/linux/generic/files/arch/mips/fw/myloader/myloader.c [deleted file]
target/linux/generic/files/drivers/leds/ledtrig-netdev.c [deleted file]
target/linux/generic/files/drivers/misc/owl-loader.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/Makefile [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.h [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_brnimage.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_eva.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_jimage.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_lzma.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seama.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_tplink.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_trx.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c [deleted file]
target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_wrgg.c [deleted file]
target/linux/generic/files/drivers/mtd/myloader.c [deleted file]
target/linux/generic/files/drivers/net/phy/adm6996.c [deleted file]
target/linux/generic/files/drivers/net/phy/adm6996.h [deleted file]
target/linux/generic/files/drivers/net/phy/ar8216.c [deleted file]
target/linux/generic/files/drivers/net/phy/ar8216.h [deleted file]
target/linux/generic/files/drivers/net/phy/ar8327.c [deleted file]
target/linux/generic/files/drivers/net/phy/ar8327.h [deleted file]
target/linux/generic/files/drivers/net/phy/b53/Kconfig [deleted file]
target/linux/generic/files/drivers/net/phy/b53/Makefile [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_common.c [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_priv.h [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_regs.h [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_spi.c [deleted file]
target/linux/generic/files/drivers/net/phy/b53/b53_srab.c [deleted file]
target/linux/generic/files/drivers/net/phy/ip17xx.c [deleted file]
target/linux/generic/files/drivers/net/phy/mvsw61xx.c [deleted file]
target/linux/generic/files/drivers/net/phy/mvsw61xx.h [deleted file]
target/linux/generic/files/drivers/net/phy/mvswitch.c [deleted file]
target/linux/generic/files/drivers/net/phy/mvswitch.h [deleted file]
target/linux/generic/files/drivers/net/phy/psb6970.c [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8306.c [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8366_smi.c [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8366_smi.h [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8366rb.c [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8366s.c [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8367.c [deleted file]
target/linux/generic/files/drivers/net/phy/rtl8367b.c [deleted file]
target/linux/generic/files/drivers/net/phy/swconfig.c [deleted file]
target/linux/generic/files/drivers/net/phy/swconfig_leds.c [deleted file]
target/linux/generic/files/include/linux/ar8216_platform.h [deleted file]
target/linux/generic/files/include/linux/ath5k_platform.h [deleted file]
target/linux/generic/files/include/linux/ath9k_platform.h [deleted file]
target/linux/generic/files/include/linux/myloader.h [deleted file]
target/linux/generic/files/include/linux/platform_data/adm6996-gpio.h [deleted file]
target/linux/generic/files/include/linux/platform_data/b53.h [deleted file]
target/linux/generic/files/include/linux/routerboot.h [deleted file]
target/linux/generic/files/include/linux/rt2x00_platform.h [deleted file]
target/linux/generic/files/include/linux/rtl8366.h [deleted file]
target/linux/generic/files/include/linux/rtl8367.h [deleted file]
target/linux/generic/files/include/linux/switch.h [deleted file]
target/linux/generic/files/include/uapi/linux/switch.h [deleted file]

diff --git a/target/linux/generic/files-3.18/Documentation/networking/adm6996.txt b/target/linux/generic/files-3.18/Documentation/networking/adm6996.txt
new file mode 100644 (file)
index 0000000..ab59f1d
--- /dev/null
@@ -0,0 +1,110 @@
+------- 
+
+ADM6996FC / ADM6996M switch chip driver
+
+
+1. General information
+
+  This driver supports the FC and M models only. The ADM6996F and L are
+  completely different chips.
+  
+  Support for the FC model is extremely limited at the moment. There is no VLAN
+  support as of yet. The driver will not offer an swconfig interface for the FC
+  chip.
+1.1 VLAN IDs
+
+  It is possible to define 16 different VLANs. Every VLAN has an identifier, its
+  VLAN ID. It is easiest if you use at most VLAN IDs 0-15. In that case, the
+  swconfig based configuration is very straightforward. To define two VLANs with
+  IDs 4 and 5, you can invoke, for example:
+  
+      # swconfig dev ethX vlan 4 set ports '0 1t 2 5t' 
+      # swconfig dev ethX vlan 5 set ports '0t 1t 5t'
+  
+  The swconfig framework will automatically invoke 'port Y set pvid Z' for every
+  port that is an untagged member of VLAN Y, setting its Primary VLAN ID. In
+  this example, ports 0 and 2 would get "pvid 4". The Primary VLAN ID of a port
+  is the VLAN ID associated with untagged packets coming in on that port.
+  
+  But if you wish to use VLAN IDs outside the range 0-15, this automatic
+  behaviour of the swconfig framework becomes a problem. The 16 VLANs that
+  swconfig can configure on the ADM6996 also have a "vid" setting. By default,
+  this is the same as the number of the VLAN entry, to make the simple behaviour
+  above possible. To still support a VLAN with a VLAN ID higher than 15
+  (presumably because you are in a network where such VLAN IDs are already in
+  use), you can change the "vid" setting of the VLAN to anything in the range
+  0-1023. But suppose you did the following:
+  
+      # swconfig dev ethX vlan 0 set vid 998 
+      # swconfig dev ethX vlan 0 set ports '0 2 5t'
+  Now the swconfig framework will issue 'port 0 set pvid 0' and 'port 2 set pvid
+  0'. But the "pvid" should be set to 998, so you are responsible for manually
+  fixing this!
+
+1.2 VLAN filtering
+
+  The switch is configured to apply source port filtering. This means that
+  packets are only accepted when the port the packets came in on is a member of
+  the VLAN the packet should go to.
+
+  Only membership of a VLAN is tested, it does not matter whether it is a tagged
+  or untagged membership.
+
+  For untagged packets, the destination VLAN is the Primary VLAN ID of the
+  incoming port. So if the PVID of a port is 0, but that port is not a member of
+  the VLAN with ID 0, this means that untagged packets on that port are dropped.
+  This can be used as a roundabout way of dropping untagged packets from a port,
+  a mode often referred to as "Admit only tagged packets".
+
+1.3 Reset
+
+  The two supported chip models do not have a sofware-initiated reset. When the
+  driver is initialised, as well as when the 'reset' swconfig option is invoked,
+  the driver will set those registers it knows about and supports to the correct
+  default value. But there are a lot of registers in the chip that the driver
+  does not support. If something changed those registers, invoking 'reset' or
+  performing a warm reboot might still leave the chip in a "broken" state. Only
+  a hardware reset will bring it back in the default state.
+
+2. Technical details on PHYs and the ADM6996
+
+  From the viewpoint of the Linux kernel, it is common that an Ethernet adapter
+  can be seen as a separate MAC entity and a separate PHY entity. The PHY entity
+  can be queried and set through registers accessible via an MDIO bus. A PHY
+  normally has a single address on that bus, in the range 0 through 31.
+
+  The ADM6996 has special-purpose registers in the range of PHYs 0 through 10.
+  Even though all these registers control a single ADM6996 chip, the Linux
+  kernel treats this as 11 separate PHYs.  The driver will bind to these
+  addresses to prevent a different PHY driver from binding and corrupting these
+  registers.
+
+  What Linux sees as the PHY on address 0 is meant for the Ethernet MAC
+  connected to the CPU port of the ADM6996 switch chip (port 5). This is the
+  Ethernet MAC you will use to send and receive data through the switch.
+
+  The PHYs at addresses 16 through 20 map to the PHYs on ports 0 through 4 of
+  the switch chip. These can be accessed with the Generic PHY driver, as the
+  registers have the common layout.
+
+  If a second Ethernet MAC on your board is wired to the port 4 PHY, that MAC
+  needs to bind to PHY address 20 for the port to work correctly.
+
+  The ADM6996 switch driver will reset the ports 0 through 3 on startup and when
+  'reset' is invoked. This could clash with a different PHY driver if the kernel
+  binds a PHY driver to address 16 through 19.
+
+  If Linux binds a PHY on addresses 1 through 10 to an Ethernet MAC, the ADM6996
+  driver will simply always report a connected 100 Mbit/s full-duplex link for
+  that PHY, and provide no other functionality. This is most likely not what you
+  want. So if you see a message in your log
+
+       ethX: PHY overlaps ADM6996, providing fixed PHY yy.
+
+  This is most likely an indication that ethX will not work properly, and your
+  kernel needs to be configured to attach a different PHY to that Ethernet MAC.
+
+  Controlling the mapping between MACs and PHYs is usually done in platform- or
+  board-specific fixup code. The ADM6996 driver has no influence over this.
diff --git a/target/linux/generic/files-3.18/arch/mips/fw/myloader/Makefile b/target/linux/generic/files-3.18/arch/mips/fw/myloader/Makefile
new file mode 100644 (file)
index 0000000..34acfd0
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the Compex's MyLoader support on MIPS architecture
+#
+
+lib-y += myloader.o
diff --git a/target/linux/generic/files-3.18/arch/mips/fw/myloader/myloader.c b/target/linux/generic/files-3.18/arch/mips/fw/myloader/myloader.c
new file mode 100644 (file)
index 0000000..a26f9ad
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Compex's MyLoader specific prom routines
+ *
+ *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/addrspace.h>
+#include <asm/fw/myloader/myloader.h>
+
+#define SYS_PARAMS_ADDR                KSEG1ADDR(0x80000800)
+#define BOARD_PARAMS_ADDR      KSEG1ADDR(0x80000A00)
+#define PART_TABLE_ADDR                KSEG1ADDR(0x80000C00)
+#define BOOT_PARAMS_ADDR       KSEG1ADDR(0x80000E00)
+
+static struct myloader_info myloader_info __initdata;
+static int myloader_found __initdata;
+
+struct myloader_info * __init myloader_get_info(void)
+{
+       struct mylo_system_params *sysp;
+       struct mylo_board_params *boardp;
+       struct mylo_partition_table *parts;
+
+       if (myloader_found)
+               return &myloader_info;
+
+       sysp = (struct mylo_system_params *)(SYS_PARAMS_ADDR);
+       boardp = (struct mylo_board_params *)(BOARD_PARAMS_ADDR);
+       parts = (struct mylo_partition_table *)(PART_TABLE_ADDR);
+
+       printk(KERN_DEBUG "MyLoader: sysp=%08x, boardp=%08x, parts=%08x\n",
+               sysp->magic, boardp->magic, parts->magic);
+
+       /* Check for some magic numbers */
+       if (sysp->magic != MYLO_MAGIC_SYS_PARAMS ||
+           boardp->magic != MYLO_MAGIC_BOARD_PARAMS ||
+           le32_to_cpu(parts->magic) != MYLO_MAGIC_PARTITIONS)
+               return NULL;
+
+       printk(KERN_DEBUG "MyLoader: id=%04x:%04x, sub_id=%04x:%04x\n",
+               sysp->vid, sysp->did, sysp->svid, sysp->sdid);
+
+       myloader_info.vid = sysp->vid;
+       myloader_info.did = sysp->did;
+       myloader_info.svid = sysp->svid;
+       myloader_info.sdid = sysp->sdid;
+
+       memcpy(myloader_info.macs, boardp->addr, sizeof(myloader_info.macs));
+
+       myloader_found = 1;
+
+       return &myloader_info;
+}
diff --git a/target/linux/generic/files-3.18/drivers/leds/ledtrig-netdev.c b/target/linux/generic/files-3.18/drivers/leds/ledtrig-netdev.c
new file mode 100644 (file)
index 0000000..8d32490
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * LED Kernel Netdev Trigger
+ *
+ * Toggles the LED to reflect the link and traffic state of a named net device
+ *
+ * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
+ *
+ * Derived from ledtrig-timer.c which is:
+ *  Copyright 2005-2006 Openedhand Ltd.
+ *  Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+
+#include "leds.h"
+
+/*
+ * Configurable sysfs attributes:
+ *
+ * device_name - network device name to monitor
+ *
+ * interval - duration of LED blink, in milliseconds
+ *
+ * mode - either "none" (LED is off) or a space separated list of one or more of:
+ *   link: LED's normal state reflects whether the link is up (has carrier) or not
+ *   tx:   LED blinks on transmitted data
+ *   rx:   LED blinks on receive data
+ *
+ * Some suggestions:
+ *
+ *  Simple link status LED:
+ *  $ echo netdev >someled/trigger
+ *  $ echo eth0 >someled/device_name
+ *  $ echo link >someled/mode
+ *
+ *  Ethernet-style link/activity LED:
+ *  $ echo netdev >someled/trigger
+ *  $ echo eth0 >someled/device_name
+ *  $ echo "link tx rx" >someled/mode
+ *
+ *  Modem-style tx/rx LEDs:
+ *  $ echo netdev >led1/trigger
+ *  $ echo ppp0 >led1/device_name
+ *  $ echo tx >led1/mode
+ *  $ echo netdev >led2/trigger
+ *  $ echo ppp0 >led2/device_name
+ *  $ echo rx >led2/mode
+ *
+ */
+
+#define MODE_LINK 1
+#define MODE_TX   2
+#define MODE_RX   4
+
+struct led_netdev_data {
+       spinlock_t lock;
+
+       struct delayed_work work;
+       struct notifier_block notifier;
+
+       struct led_classdev *led_cdev;
+       struct net_device *net_dev;
+
+       char device_name[IFNAMSIZ];
+       unsigned interval;
+       unsigned mode;
+       unsigned link_up;
+       unsigned last_activity;
+};
+
+static void set_baseline_state(struct led_netdev_data *trigger_data)
+{
+       if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
+               led_set_brightness(trigger_data->led_cdev, LED_FULL);
+       else
+               led_set_brightness(trigger_data->led_cdev, LED_OFF);
+
+       if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
+               schedule_delayed_work(&trigger_data->work, trigger_data->interval);
+}
+
+static ssize_t led_device_name_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+       sprintf(buf, "%s\n", trigger_data->device_name);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf) + 1;
+}
+
+static ssize_t led_device_name_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       if (size >= IFNAMSIZ)
+               return -EINVAL;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+
+       strcpy(trigger_data->device_name, buf);
+       if (size > 0 && trigger_data->device_name[size-1] == '\n')
+               trigger_data->device_name[size-1] = 0;
+       trigger_data->link_up = 0;
+       trigger_data->last_activity = 0;
+
+       if (trigger_data->device_name[0] != 0) {
+               /* check for existing device to update from */
+               trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
+               if (trigger_data->net_dev != NULL)
+                       trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
+       }
+
+       set_baseline_state(trigger_data);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
+
+static ssize_t led_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+
+       if (trigger_data->mode == 0) {
+               strcpy(buf, "none\n");
+       } else {
+               if (trigger_data->mode & MODE_LINK)
+                       strcat(buf, "link ");
+               if (trigger_data->mode & MODE_TX)
+                       strcat(buf, "tx ");
+               if (trigger_data->mode & MODE_RX)
+                       strcat(buf, "rx ");
+               strcat(buf, "\n");
+       }
+
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf)+1;
+}
+
+static ssize_t led_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+       char copybuf[128];
+       int new_mode = -1;
+       char *p, *token;
+
+       /* take a copy since we don't want to trash the inbound buffer when using strsep */
+       strncpy(copybuf, buf, sizeof(copybuf));
+       copybuf[sizeof(copybuf) - 1] = 0;
+       p = copybuf;
+
+       while ((token = strsep(&p, " \t\n")) != NULL) {
+               if (!*token)
+                       continue;
+
+               if (new_mode == -1)
+                       new_mode = 0;
+
+               if (!strcmp(token, "none"))
+                       new_mode = 0;
+               else if (!strcmp(token, "tx"))
+                       new_mode |= MODE_TX;
+               else if (!strcmp(token, "rx"))
+                       new_mode |= MODE_RX;
+               else if (!strcmp(token, "link"))
+                       new_mode |= MODE_LINK;
+               else
+                       return -EINVAL;
+       }
+
+       if (new_mode == -1)
+               return -EINVAL;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+       trigger_data->mode = new_mode;
+       set_baseline_state(trigger_data);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
+
+static ssize_t led_interval_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+       sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf) + 1;
+}
+
+static ssize_t led_interval_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+       int ret = -EINVAL;
+       char *after;
+       unsigned long value = simple_strtoul(buf, &after, 10);
+       size_t count = after - buf;
+
+       if (isspace(*after))
+               count++;
+
+       /* impose some basic bounds on the timer interval */
+       if (count == size && value >= 5 && value <= 10000) {
+               cancel_delayed_work_sync(&trigger_data->work);
+
+               spin_lock_bh(&trigger_data->lock);
+               trigger_data->interval = msecs_to_jiffies(value);
+               set_baseline_state(trigger_data); /* resets timer */
+               spin_unlock_bh(&trigger_data->lock);
+
+               ret = count;
+       }
+
+       return ret;
+}
+
+static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
+
+static int netdev_trig_notify(struct notifier_block *nb,
+                             unsigned long evt,
+                             void *dv)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
+       struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
+
+       if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER && evt != NETDEV_CHANGENAME)
+               return NOTIFY_DONE;
+
+       if (strcmp(dev->name, trigger_data->device_name))
+               return NOTIFY_DONE;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+
+       if (evt == NETDEV_REGISTER || evt == NETDEV_CHANGENAME) {
+               if (trigger_data->net_dev != NULL)
+                       dev_put(trigger_data->net_dev);
+
+               dev_hold(dev);
+               trigger_data->net_dev = dev;
+               trigger_data->link_up = 0;
+               goto done;
+       }
+
+       if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
+               dev_put(trigger_data->net_dev);
+               trigger_data->net_dev = NULL;
+               goto done;
+       }
+
+       /* UP / DOWN / CHANGE */
+
+       trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
+       set_baseline_state(trigger_data);
+
+done:
+       spin_unlock_bh(&trigger_data->lock);
+       return NOTIFY_DONE;
+}
+
+/* here's the real work! */
+static void netdev_trig_work(struct work_struct *work)
+{
+       struct led_netdev_data *trigger_data = container_of(work, struct led_netdev_data, work.work);
+       struct rtnl_link_stats64 *dev_stats;
+       unsigned new_activity;
+       struct rtnl_link_stats64 temp;
+
+       if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
+               /* we don't need to do timer work, just reflect link state. */
+               led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
+               return;
+       }
+
+       dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
+       new_activity =
+               ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
+               ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
+
+       if (trigger_data->mode & MODE_LINK) {
+               /* base state is ON (link present) */
+               /* if there's no link, we don't get this far and the LED is off */
+
+               /* OFF -> ON always */
+               /* ON -> OFF on activity */
+               if (trigger_data->led_cdev->brightness == LED_OFF) {
+                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
+               } else if (trigger_data->last_activity != new_activity) {
+                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
+               }
+       } else {
+               /* base state is OFF */
+               /* ON -> OFF always */
+               /* OFF -> ON on activity */
+               if (trigger_data->led_cdev->brightness == LED_FULL) {
+                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
+               } else if (trigger_data->last_activity != new_activity) {
+                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
+               }
+       }
+
+       trigger_data->last_activity = new_activity;
+       schedule_delayed_work(&trigger_data->work, trigger_data->interval);
+}
+
+static void netdev_trig_activate(struct led_classdev *led_cdev)
+{
+       struct led_netdev_data *trigger_data;
+       int rc;
+
+       trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
+       if (!trigger_data)
+               return;
+
+       spin_lock_init(&trigger_data->lock);
+
+       trigger_data->notifier.notifier_call = netdev_trig_notify;
+       trigger_data->notifier.priority = 10;
+
+       INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
+
+       trigger_data->led_cdev = led_cdev;
+       trigger_data->net_dev = NULL;
+       trigger_data->device_name[0] = 0;
+
+       trigger_data->mode = 0;
+       trigger_data->interval = msecs_to_jiffies(50);
+       trigger_data->link_up = 0;
+       trigger_data->last_activity = 0;
+
+       led_cdev->trigger_data = trigger_data;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
+       if (rc)
+               goto err_out;
+       rc = device_create_file(led_cdev->dev, &dev_attr_mode);
+       if (rc)
+               goto err_out_device_name;
+       rc = device_create_file(led_cdev->dev, &dev_attr_interval);
+       if (rc)
+               goto err_out_mode;
+
+       register_netdevice_notifier(&trigger_data->notifier);
+       return;
+
+err_out_mode:
+       device_remove_file(led_cdev->dev, &dev_attr_mode);
+err_out_device_name:
+       device_remove_file(led_cdev->dev, &dev_attr_device_name);
+err_out:
+       led_cdev->trigger_data = NULL;
+       kfree(trigger_data);
+}
+
+static void netdev_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       if (trigger_data) {
+               unregister_netdevice_notifier(&trigger_data->notifier);
+
+               device_remove_file(led_cdev->dev, &dev_attr_device_name);
+               device_remove_file(led_cdev->dev, &dev_attr_mode);
+               device_remove_file(led_cdev->dev, &dev_attr_interval);
+
+               cancel_delayed_work_sync(&trigger_data->work);
+
+               spin_lock_bh(&trigger_data->lock);
+
+               if (trigger_data->net_dev) {
+                       dev_put(trigger_data->net_dev);
+                       trigger_data->net_dev = NULL;
+               }
+
+               spin_unlock_bh(&trigger_data->lock);
+
+               kfree(trigger_data);
+       }
+}
+
+static struct led_trigger netdev_led_trigger = {
+       .name     = "netdev",
+       .activate = netdev_trig_activate,
+       .deactivate = netdev_trig_deactivate,
+};
+
+static int __init netdev_trig_init(void)
+{
+       return led_trigger_register(&netdev_led_trigger);
+}
+
+static void __exit netdev_trig_exit(void)
+{
+       led_trigger_unregister(&netdev_led_trigger);
+}
+
+module_init(netdev_trig_init);
+module_exit(netdev_trig_exit);
+
+MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
+MODULE_DESCRIPTION("Netdev LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-3.18/drivers/misc/owl-loader.c b/target/linux/generic/files-3.18/drivers/misc/owl-loader.c
new file mode 100644 (file)
index 0000000..f11cb2b
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Initialize Owl Emulation Devices
+ *
+ * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
+ * need to be able to initialize the PCIe wifi device. Normally, this is done
+ * during the early stages of booting linux, because the necessary init code
+ * is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
+ * However,this isn't possible for devices which have the init code for the
+ * Atheros chip stored on NAND. Hence, this module can be used to initialze
+ * the chip when the user-space is ready to extract the init code.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/completion.h>
+#include <linux/etherdevice.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+
+struct owl_ctx {
+       struct completion eeprom_load;
+};
+
+#define EEPROM_FILENAME_LEN 100
+
+#define AR5416_EEPROM_MAGIC 0xa55a
+
+static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
+                          size_t cal_len)
+{
+       void __iomem *mem;
+       const void *cal_end = (void *)cal_data + cal_len;
+       const struct {
+               __be16 reg;
+               __be16 low_val;
+               __be16 high_val;
+       } __packed *data;
+       u16 cmd;
+       u32 bar0;
+       bool swap_needed = false;
+
+       if (*cal_data != AR5416_EEPROM_MAGIC) {
+               if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
+                       dev_err(&pdev->dev, "invalid calibration data\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(&pdev->dev, "calibration data needs swapping\n");
+               swap_needed = true;
+       }
+
+       dev_info(&pdev->dev, "fixup device configuration\n");
+
+       mem = pcim_iomap(pdev, 0, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "ioremap error\n");
+               return -EINVAL;
+       }
+
+       pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
+       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
+                              pci_resource_start(pdev, 0));
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+       /* set pointer to first reg address */
+       for (data = (const void *) (cal_data + 3);
+            (const void *) data <= cal_end && data->reg != cpu_to_be16(~0);
+            data++) {
+               u32 val;
+               u16 reg;
+
+               reg = data->reg;
+               val = data->low_val;
+               val |= data->high_val << 16;
+
+               if (swap_needed) {
+                       reg = swab16(reg);
+                       val = swahb32(val);
+               }
+
+#ifdef CONFIG_LANTIQ
+               val = swab32(val);
+#endif
+
+               __raw_writel(val, mem + reg);
+               udelay(100);
+       }
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
+       pcim_iounmap(pdev, mem);
+
+       pci_disable_device(pdev);
+
+       return 0;
+}
+
+static void owl_fw_cb(const struct firmware *fw, void *context)
+{
+       struct pci_dev *pdev = (struct pci_dev *) context;
+       struct owl_ctx *ctx = (struct owl_ctx *) pci_get_drvdata(pdev);
+       struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct pci_bus *bus;
+
+       complete(&ctx->eeprom_load);
+
+       if (!fw) {
+               dev_err(&pdev->dev, "no eeprom data received.\n");
+               goto release;
+       }
+
+       /* also note that we are doing *u16 operations on the file */
+       if (fw->size > sizeof(pdata->eeprom_data) || fw->size < 0x200 ||
+           (fw->size & 1) == 1) {
+               dev_err(&pdev->dev, "eeprom file has an invalid size.\n");
+               goto release;
+       }
+
+       if (pdata) {
+               memcpy(pdata->eeprom_data, fw->data, fw->size);
+
+               /*
+                * eeprom has been successfully loaded - pass the data to ath9k
+                * but remove the eeprom_name, so it doesn't try to load it too.
+                */
+               pdata->eeprom_name = NULL;
+       }
+
+       if (ath9k_pci_fixup(pdev, (const u16 *) fw->data, fw->size))
+               goto release;
+
+       pci_lock_rescan_remove();
+       bus = pdev->bus;
+       pci_stop_and_remove_bus_device(pdev);
+       /*
+        * the device should come back with the proper
+        * ProductId. But we have to initiate a rescan.
+        */
+       pci_rescan_bus(bus);
+       pci_unlock_rescan_remove();
+
+release:
+       release_firmware(fw);
+}
+
+static const char *owl_get_eeprom_name(struct pci_dev *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct ath9k_platform_data *pdata;
+       char *eeprom_name;
+
+       /* try the existing platform data first */
+       pdata = dev_get_platdata(dev);
+       if (pdata && pdata->eeprom_name)
+               return pdata->eeprom_name;
+
+       dev_dbg(dev, "using auto-generated eeprom filename\n");
+
+       eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
+       if (!eeprom_name)
+               return NULL;
+
+       /* this should match the pattern used in ath9k/init.c */
+       scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
+                 dev_name(dev));
+
+       return eeprom_name;
+}
+
+static int owl_probe(struct pci_dev *pdev,
+                   const struct pci_device_id *id)
+{
+       struct owl_ctx *ctx;
+       const char *eeprom_name;
+       int err = 0;
+
+       if (pcim_enable_device(pdev))
+               return -EIO;
+
+       pcim_pin_device(pdev);
+
+       eeprom_name = owl_get_eeprom_name(pdev);
+       if (!eeprom_name) {
+               dev_err(&pdev->dev, "no eeprom filename found.\n");
+               return -ENODEV;
+       }
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               dev_err(&pdev->dev, "failed to alloc device context.\n");
+               return -ENOMEM;
+       }
+       init_completion(&ctx->eeprom_load);
+
+       pci_set_drvdata(pdev, ctx);
+       err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
+                                     &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
+       if (err) {
+               dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
+               kfree(ctx);
+       }
+       return err;
+}
+
+static void owl_remove(struct pci_dev *pdev)
+{
+       struct owl_ctx *ctx = pci_get_drvdata(pdev);
+
+       if (ctx) {
+               wait_for_completion(&ctx->eeprom_load);
+               pci_set_drvdata(pdev, NULL);
+               kfree(ctx);
+       }
+}
+
+static const struct pci_device_id owl_pci_table[] = {
+       { PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
+       { PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
+       { },
+};
+MODULE_DEVICE_TABLE(pci, owl_pci_table);
+
+static struct pci_driver owl_driver = {
+       .name           = "owl-loader",
+       .id_table       = owl_pci_table,
+       .probe          = owl_probe,
+       .remove         = owl_remove,
+};
+module_pci_driver(owl_driver);
+MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
+MODULE_DESCRIPTION("Initializes Atheros' Owl Emulation devices");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/Kconfig
new file mode 100644 (file)
index 0000000..81ece43
--- /dev/null
@@ -0,0 +1,76 @@
+config MTD_SPLIT
+       def_bool n
+       help
+         Generic MTD split support.
+
+config MTD_SPLIT_SUPPORT
+       def_bool MTD = y
+
+comment "Rootfs partition parsers"
+
+config MTD_SPLIT_SQUASHFS_ROOT
+       bool "Squashfs based root partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+       default n
+       help
+         This provides a parsing function which allows to detect the
+         offset and size of the unused portion of a rootfs partition
+         containing a squashfs.
+
+comment "Firmware partition parsers"
+
+config MTD_SPLIT_SEAMA_FW
+       bool "Seama firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_WRGG_FW
+       bool "WRGG firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_UIMAGE_FW
+       bool "uImage based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_FIT_FW
+       bool "FIT based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_LZMA_FW
+       bool "LZMA compressed kernel based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_TPLINK_FW
+       bool "TP-Link firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_TRX_FW
+       bool "TRX image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_BRNIMAGE_FW
+       bool "brnImage (brnboot image) firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_EVA_FW
+       bool "EVA image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_MINOR_FW
+       bool "Mikrotik NOR image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_JIMAGE_FW
+       bool "JBOOT Image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/Makefile
new file mode 100644 (file)
index 0000000..206e754
--- /dev/null
@@ -0,0 +1,13 @@
+obj-$(CONFIG_MTD_SPLIT)                += mtdsplit.o
+obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
+obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o
+obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o
+obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o
+obj-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o
+obj-$(CONFIG_MTD_SPLIT_TPLINK_FW) += mtdsplit_tplink.o
+obj-$(CONFIG_MTD_SPLIT_TRX_FW) += mtdsplit_trx.o
+obj-$(CONFIG_MTD_SPLIT_BRNIMAGE_FW) += mtdsplit_brnimage.o
+obj-$(CONFIG_MTD_SPLIT_EVA_FW) += mtdsplit_eva.o
+obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o
+obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
+obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit.c
new file mode 100644 (file)
index 0000000..b2e51dc
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    "mtdsplit: " fmt
+
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/magic.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define UBI_EC_MAGIC                   0x55424923      /* UBI# */
+
+struct squashfs_super_block {
+       __le32 s_magic;
+       __le32 pad0[9];
+       __le64 bytes_used;
+};
+
+int mtd_get_squashfs_len(struct mtd_info *master,
+                        size_t offset,
+                        size_t *squashfs_len)
+{
+       struct squashfs_super_block sb;
+       size_t retlen;
+       int err;
+
+       err = mtd_read(master, offset, sizeof(sb), &retlen, (void *)&sb);
+       if (err || (retlen != sizeof(sb))) {
+               pr_alert("error occured while reading from \"%s\"\n",
+                        master->name);
+               return -EIO;
+       }
+
+       if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC) {
+               pr_alert("no squashfs found in \"%s\"\n", master->name);
+               return -EINVAL;
+       }
+
+       retlen = le64_to_cpu(sb.bytes_used);
+       if (retlen <= 0) {
+               pr_alert("squashfs is empty in \"%s\"\n", master->name);
+               return -ENODEV;
+       }
+
+       if (offset + retlen > master->size) {
+               pr_alert("squashfs has invalid size in \"%s\"\n",
+                        master->name);
+               return -EINVAL;
+       }
+
+       *squashfs_len = retlen;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtd_get_squashfs_len);
+
+static ssize_t mtd_next_eb(struct mtd_info *mtd, size_t offset)
+{
+       return mtd_rounddown_to_eb(offset, mtd) + mtd->erasesize;
+}
+
+int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                          enum mtdsplit_part_type *type)
+{
+       u32 magic;
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
+                      (unsigned char *) &magic);
+       if (ret)
+               return ret;
+
+       if (retlen != sizeof(magic))
+               return -EIO;
+
+       if (le32_to_cpu(magic) == SQUASHFS_MAGIC) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_SQUASHFS;
+               return 0;
+       } else if (magic == 0x19852003) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_JFFS2;
+               return 0;
+       } else if (be32_to_cpu(magic) == UBI_EC_MAGIC) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_UBI;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtd_check_rootfs_magic);
+
+int mtd_find_rootfs_from(struct mtd_info *mtd,
+                        size_t from,
+                        size_t limit,
+                        size_t *ret_offset,
+                        enum mtdsplit_part_type *type)
+{
+       size_t offset;
+       int err;
+
+       for (offset = from; offset < limit;
+            offset = mtd_next_eb(mtd, offset)) {
+               err = mtd_check_rootfs_magic(mtd, offset, type);
+               if (err)
+                       continue;
+
+               *ret_offset = offset;
+               return 0;
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(mtd_find_rootfs_from);
+
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit.h b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit.h
new file mode 100644 (file)
index 0000000..71d62a8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MTDSPLIT_H
+#define _MTDSPLIT_H
+
+#define KERNEL_PART_NAME       "kernel"
+#define ROOTFS_PART_NAME       "rootfs"
+#define UBI_PART_NAME          "ubi"
+
+#define ROOTFS_SPLIT_NAME      "rootfs_data"
+
+enum mtdsplit_part_type {
+       MTDSPLIT_PART_TYPE_UNK = 0,
+       MTDSPLIT_PART_TYPE_SQUASHFS,
+       MTDSPLIT_PART_TYPE_JFFS2,
+       MTDSPLIT_PART_TYPE_UBI,
+};
+
+#ifdef CONFIG_MTD_SPLIT
+int mtd_get_squashfs_len(struct mtd_info *master,
+                        size_t offset,
+                        size_t *squashfs_len);
+
+int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                          enum mtdsplit_part_type *type);
+
+int mtd_find_rootfs_from(struct mtd_info *mtd,
+                        size_t from,
+                        size_t limit,
+                        size_t *ret_offset,
+                        enum mtdsplit_part_type *type);
+
+#else
+static inline int mtd_get_squashfs_len(struct mtd_info *master,
+                                      size_t offset,
+                                      size_t *squashfs_len)
+{
+       return -ENODEV;
+}
+
+static inline int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                                        enum mtdsplit_part_type *type)
+{
+       return -EINVAL;
+}
+
+static inline int mtd_find_rootfs_from(struct mtd_info *mtd,
+                                      size_t from,
+                                      size_t limit,
+                                      size_t *ret_offset,
+                                      enum mtdsplit_part_type *type)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_MTD_SPLIT */
+
+#endif /* _MTDSPLIT_H */
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_brnimage.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_brnimage.c
new file mode 100644 (file)
index 0000000..3f2d796
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define BRNIMAGE_NR_PARTS      2
+
+#define BRNIMAGE_ALIGN_BYTES   0x400
+#define BRNIMAGE_FOOTER_SIZE   12
+
+#define BRNIMAGE_MIN_OVERHEAD  (BRNIMAGE_FOOTER_SIZE)
+#define BRNIMAGE_MAX_OVERHEAD  (BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
+
+static int mtdsplit_parse_brnimage(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       uint32_t buf;
+       unsigned long rootfs_offset, rootfs_size, kernel_size;
+       size_t len;
+       int ret = 0;
+
+       for (rootfs_offset = 0; rootfs_offset < master->size;
+            rootfs_offset += BRNIMAGE_ALIGN_BYTES) {
+               ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+               if (!ret)
+                       break;
+       }
+
+       if (ret)
+               return ret;
+
+       if (rootfs_offset >= master->size)
+               return -EINVAL;
+
+       ret = mtd_read(master, rootfs_offset - BRNIMAGE_FOOTER_SIZE, 4, &len,
+                       (void *)&buf);
+       if (ret)
+               return ret;
+
+       if (len != 4)
+               return -EIO;
+
+       kernel_size = le32_to_cpu(buf);
+
+       if (kernel_size > (rootfs_offset - BRNIMAGE_MIN_OVERHEAD))
+               return -EINVAL;
+
+       if (kernel_size < (rootfs_offset - BRNIMAGE_MAX_OVERHEAD))
+               return -EINVAL;
+
+       /*
+        * The footer must be untouched as it contains the checksum of the
+        * original brnImage (kernel + squashfs)!
+        */
+       rootfs_size = master->size - rootfs_offset - BRNIMAGE_FOOTER_SIZE;
+
+       parts = kzalloc(BRNIMAGE_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return BRNIMAGE_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_brnimage_parser = {
+       .owner = THIS_MODULE,
+       .name = "brnimage-fw",
+       .parse_fn = mtdsplit_parse_brnimage,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_brnimage_init(void)
+{
+       register_mtd_parser(&mtdsplit_brnimage_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_brnimage_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_eva.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_eva.c
new file mode 100644 (file)
index 0000000..746944e
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define EVA_NR_PARTS           2
+#define EVA_MAGIC              0xfeed1281
+#define EVA_FOOTER_SIZE                0x18
+#define EVA_DUMMY_SQUASHFS_SIZE        0x100
+
+struct eva_image_header {
+       uint32_t        magic;
+       uint32_t        size;
+};
+
+static int mtdsplit_parse_eva(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       struct eva_image_header hdr;
+       size_t retlen;
+       unsigned long kernel_size, rootfs_offset;
+       int err;
+
+       err = mtd_read(master, 0, sizeof(hdr), &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != sizeof(hdr))
+               return -EIO;
+
+       if (le32_to_cpu(hdr.magic) != EVA_MAGIC)
+               return -EINVAL;
+
+       kernel_size = le32_to_cpu(hdr.size) + EVA_FOOTER_SIZE;
+
+       /* rootfs starts at the next 0x10000 boundary: */
+       rootfs_offset = round_up(kernel_size, 0x10000);
+
+       /* skip the dummy EVA squashfs partition (with wrong endianness): */
+       rootfs_offset += EVA_DUMMY_SQUASHFS_SIZE;
+
+       if (rootfs_offset >= master->size)
+               return -EINVAL;
+
+       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(EVA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return EVA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_eva_parser = {
+       .owner = THIS_MODULE,
+       .name = "eva-fw",
+       .parse_fn = mtdsplit_parse_eva,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_eva_init(void)
+{
+       register_mtd_parser(&mtdsplit_eva_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_eva_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_fit.c
new file mode 100644 (file)
index 0000000..f356adc
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 The Linux Foundation
+ * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/types.h>
+#include <linux/byteorder/generic.h>
+#include <linux/slab.h>
+#include <linux/of_fdt.h>
+
+#include "mtdsplit.h"
+
+struct fdt_header {
+       uint32_t magic;                  /* magic word FDT_MAGIC */
+       uint32_t totalsize;              /* total size of DT block */
+       uint32_t off_dt_struct;          /* offset to structure */
+       uint32_t off_dt_strings;         /* offset to strings */
+       uint32_t off_mem_rsvmap;         /* offset to memory reserve map */
+       uint32_t version;                /* format version */
+       uint32_t last_comp_version;      /* last compatible version */
+
+       /* version 2 fields below */
+       uint32_t boot_cpuid_phys;        /* Which physical CPU id we're
+                                           booting on */
+       /* version 3 fields below */
+       uint32_t size_dt_strings;        /* size of the strings block */
+
+       /* version 17 fields below */
+       uint32_t size_dt_struct;         /* size of the structure block */
+};
+
+static int
+mtdsplit_fit_parse(struct mtd_info *mtd,
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data)
+{
+       struct fdt_header hdr;
+       size_t hdr_len, retlen;
+       size_t offset;
+       size_t fit_offset, fit_size;
+       size_t rootfs_offset, rootfs_size;
+       struct mtd_partition *parts;
+       int ret;
+
+       hdr_len = sizeof(struct fdt_header);
+
+       /* Parse the MTD device & search for the FIT image location */
+       for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
+               ret = mtd_read(mtd, 0, hdr_len, &retlen, (void*) &hdr);
+               if (ret) {
+                       pr_err("read error in \"%s\" at offset 0x%llx\n",
+                              mtd->name, (unsigned long long) offset);
+                       return ret;
+               }
+
+               if (retlen != hdr_len) {
+                       pr_err("short read in \"%s\"\n", mtd->name);
+                       return -EIO;
+               }
+
+               /* Check the magic - see if this is a FIT image */
+               if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
+                       pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
+                                mtd->name, (unsigned long long) offset);
+                       continue;
+               }
+
+               /* We found a FIT image. Let's keep going */
+               break;
+       }
+
+       fit_offset = offset;
+       fit_size = be32_to_cpu(hdr.totalsize);
+
+       if (fit_size == 0) {
+               pr_err("FIT image in \"%s\" at offset %llx has null size\n",
+                      mtd->name, (unsigned long long) fit_offset);
+               return -ENODEV;
+       }
+
+       /* Search for the rootfs partition after the FIT image */
+       ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
+                                  &rootfs_offset, NULL);
+       if (ret) {
+               pr_info("no rootfs found after FIT image in \"%s\"\n",
+                       mtd->name);
+               return ret;
+       }
+
+       rootfs_size = mtd->size - rootfs_offset;
+
+       parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = fit_offset;
+       parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return 2;
+}
+
+static struct mtd_part_parser uimage_parser = {
+       .owner = THIS_MODULE,
+       .name = "fit-fw",
+       .parse_fn = mtdsplit_fit_parse,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_fit_init(void)
+{
+       register_mtd_parser(&uimage_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_fit_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_jimage.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_jimage.c
new file mode 100644 (file)
index 0000000..51544a7
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ *  Copyright (C) 2018 PaweÅ‚ Dembicki <paweldembicki@gmail.com> 
+ *
+ *  Based on: mtdsplit_uimage.c
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE )
+
+#define STAG_SIZE 16
+#define STAG_ID 0x04
+#define STAG_MAGIC 0x2B24
+
+#define SCH2_SIZE 40
+#define SCH2_MAGIC 0x2124
+#define SCH2_VER 0x02
+
+/*
+ * Jboot image header,
+ * all data in little endian.
+ */
+
+struct jimage_header           //stag + sch2 jboot joined headers
+{
+       uint8_t stag_cmark;             // in factory 0xFF , in sysupgrade must be the same as stag_id
+       uint8_t stag_id;                // 0x04
+       uint16_t stag_magic;            //magic 0x2B24
+       uint32_t stag_time_stamp;       // timestamp calculated in jboot way
+       uint32_t stag_image_length;     // lentgh of kernel + sch2 header
+       uint16_t stag_image_checksum;   // negated jboot_checksum of sch2 + kernel
+       uint16_t stag_tag_checksum;     // negated jboot_checksum of stag header data
+       uint16_t sch2_magic;            // magic 0x2124
+       uint8_t sch2_cp_type;   // 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma
+       uint8_t sch2_version;   // 0x02 for sch2
+       uint32_t sch2_ram_addr; // ram entry address
+       uint32_t sch2_image_len;        // kernel image length
+       uint32_t sch2_image_crc32;      // kernel image crc
+       uint32_t sch2_start_addr;       // ram start address
+       uint32_t sch2_rootfs_addr;      // rootfs flash address
+       uint32_t sch2_rootfs_len;       // rootfls length
+       uint32_t sch2_rootfs_crc32;     // rootfs crc32
+       uint32_t sch2_header_crc32;     // sch2 header crc32, durring calculation this area is replaced by zero
+       uint16_t sch2_header_length;    // sch2 header length: 0x28
+       uint16_t sch2_cmd_line_length;  // cmd line length, known zeros
+};
+
+static int
+read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
+                  size_t header_len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts
+ *
+ * @find_header: function to call for a block of data that will return offset
+ *      of a valid jImage header if found
+ */
+static int __mtdsplit_parse_jimage(struct mtd_info *master,
+                                  const struct mtd_partition **pparts,
+                                  struct mtd_part_parser_data *data,
+                                  ssize_t (*find_header)(u_char *buf, size_t len))
+{
+       struct mtd_partition *parts;
+       u_char *buf;
+       int nr_parts;
+       size_t offset;
+       size_t jimage_offset;
+       size_t jimage_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int jimage_part, rf_part;
+       int ret;
+       enum mtdsplit_part_type type;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       buf = vmalloc(MAX_HEADER_LEN);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_free_parts;
+       }
+
+       /* find jImage on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               struct jimage_header *header;
+
+               jimage_size = 0;
+
+               ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN);
+               if (ret)
+                       continue;
+
+               ret = find_header(buf, MAX_HEADER_LEN);
+               if (ret < 0) {
+                       pr_debug("no valid jImage found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+               header = (struct jimage_header *)(buf + ret);
+
+               jimage_size = sizeof(*header) + header->sch2_image_len + ret;
+               if ((offset + jimage_size) > master->size) {
+                       pr_debug("jImage exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (jimage_size == 0) {
+               pr_debug("no jImage found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       jimage_offset = offset;
+
+       if (jimage_offset == 0) {
+               jimage_part = 0;
+               rf_part = 1;
+
+               /* find the roots after the jImage */
+               ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size,
+                                          master->size, &rootfs_offset, &type);
+               if (ret) {
+                       pr_debug("no rootfs after jImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_size = master->size - rootfs_offset;
+               jimage_size = rootfs_offset - jimage_offset;
+       } else {
+               rf_part = 0;
+               jimage_part = 1;
+
+               /* check rootfs presence at offset 0 */
+               ret = mtd_check_rootfs_magic(master, 0, &type);
+               if (ret) {
+                       pr_debug("no rootfs before jImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_offset = 0;
+               rootfs_size = jimage_offset;
+       }
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       parts[jimage_part].name = KERNEL_PART_NAME;
+       parts[jimage_part].offset = jimage_offset;
+       parts[jimage_part].size = jimage_size;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[rf_part].name = UBI_PART_NAME;
+       else
+               parts[rf_part].name = ROOTFS_PART_NAME;
+       parts[rf_part].offset = rootfs_offset;
+       parts[rf_part].size = rootfs_size;
+
+       vfree(buf);
+
+       *pparts = parts;
+       return nr_parts;
+
+err_free_buf:
+       vfree(buf);
+
+err_free_parts:
+       kfree(parts);
+       return ret;
+}
+
+static ssize_t jimage_verify_default(u_char *buf, size_t len)
+{
+       struct jimage_header *header = (struct jimage_header *)buf;
+
+       /* default sanity checks */
+       if (header->stag_magic != STAG_MAGIC) {
+               pr_debug("invalid jImage stag header magic: %04x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->sch2_magic != SCH2_MAGIC) {
+               pr_debug("invalid jImage sch2 header magic: %04x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->stag_cmark != header->stag_id) {
+               pr_debug("invalid jImage stag header cmark: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->stag_id != STAG_ID) {
+               pr_debug("invalid jImage stag header id: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->sch2_version != SCH2_VER) {
+               pr_debug("invalid jImage sch2 header version: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_jimage_parse_generic(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_jimage(master, pparts, data,
+                                     jimage_verify_default);
+}
+
+static struct mtd_part_parser jimage_generic_parser = {
+       .owner = THIS_MODULE,
+       .name = "jimage-fw",
+       .parse_fn = mtdsplit_jimage_parse_generic,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_jimage_init(void)
+{
+       register_mtd_parser(&jimage_generic_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_jimage_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_lzma.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_lzma.c
new file mode 100644 (file)
index 0000000..b7f044a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/unaligned.h>
+
+#include "mtdsplit.h"
+
+#define LZMA_NR_PARTS          2
+#define LZMA_PROPERTIES_SIZE   5
+
+struct lzma_header {
+       u8 props[LZMA_PROPERTIES_SIZE];
+       u8 size_low[4];
+       u8 size_high[4];
+};
+
+static int mtdsplit_parse_lzma(struct mtd_info *master,
+                              const struct mtd_partition **pparts,
+                              struct mtd_part_parser_data *data)
+{
+       struct lzma_header hdr;
+       size_t hdr_len, retlen;
+       size_t rootfs_offset;
+       u32 t;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* verify LZMA properties */
+       if (hdr.props[0] >= (9 * 5 * 5))
+               return -EINVAL;
+
+       t = get_unaligned_le32(&hdr.props[1]);
+       if (!is_power_of_2(t))
+               return -EINVAL;
+
+       t = get_unaligned_le32(&hdr.size_high);
+       if (t)
+               return -EINVAL;
+
+       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
+                                  &rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(LZMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return LZMA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_lzma_parser = {
+       .owner = THIS_MODULE,
+       .name = "lzma-fw",
+       .parse_fn = mtdsplit_parse_lzma,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_lzma_init(void)
+{
+       register_mtd_parser(&mtdsplit_lzma_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_lzma_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_minor.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_minor.c
new file mode 100644 (file)
index 0000000..f971f0a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  MTD splitter for MikroTik NOR devices
+ *
+ *  Copyright (C) 2017 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  The rootfs is expected at erase-block boundary due to the use of
+ *  mtd_find_rootfs_from(). We use a trimmed down version of the yaffs header
+ *  for two main reasons:
+ *  - the original header uses weakly defined types (int, enum...) which can
+ *    vary in length depending on build host (and the struct is not packed),
+ *    and the name field can have a different total length depending on
+ *    whether or not the yaffs code was _built_ with unicode support.
+ *  - the only field that could be of real use here (file_size_low) contains
+ *    invalid data in the header generated by kernel2minor, so we cannot use
+ *    it to infer the exact position of the rootfs and do away with
+ *    mtd_find_rootfs_from() (and thus have non-EB-aligned rootfs).
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/string.h>
+
+#include "mtdsplit.h"
+
+#define YAFFS_OBJECT_TYPE_FILE 0x1
+#define YAFFS_OBJECTID_ROOT    0x1
+#define YAFFS_SUM_UNUSED       0xFFFF
+#define YAFFS_NAME             "kernel"
+
+#define MINOR_NR_PARTS         2
+
+/*
+ * This structure is based on yaffs_obj_hdr from yaffs_guts.h
+ * The weak types match upstream. The fields have cpu-endianness
+ */
+struct minor_header {
+       int yaffs_type;
+       int yaffs_obj_id;
+       u16 yaffs_sum_unused;
+       char yaffs_name[sizeof(YAFFS_NAME)];
+};
+
+static int mtdsplit_parse_minor(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct minor_header hdr;
+       size_t hdr_len, retlen;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* match header */
+       if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE)
+               return -EINVAL;
+
+       if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT)
+               return -EINVAL;
+
+       if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED)
+               return -EINVAL;
+
+       if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME)))
+               return -EINVAL;
+
+       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
+                                  &rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return MINOR_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_minor_parser = {
+       .owner = THIS_MODULE,
+       .name = "minor-fw",
+       .parse_fn = mtdsplit_parse_minor,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_minor_init(void)
+{
+       register_mtd_parser(&mtdsplit_minor_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_minor_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_seama.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_seama.c
new file mode 100644 (file)
index 0000000..f8556e0
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define SEAMA_MAGIC            0x5EA3A417
+#define SEAMA_NR_PARTS         2
+#define SEAMA_MIN_ROOTFS_OFFS  0x80000 /* 512KiB */
+
+struct seama_header {
+       __be32  magic;          /* should always be SEAMA_MAGIC. */
+       __be16  reserved;       /* reserved for  */
+       __be16  metasize;       /* size of the META data */
+       __be32  size;           /* size of the image */
+       u8      md5[16];        /* digest */
+};
+
+static int mtdsplit_parse_seama(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct seama_header hdr;
+       size_t hdr_len, retlen, kernel_ent_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       enum mtdsplit_part_type type;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* sanity checks */
+       if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC)
+               return -EINVAL;
+
+       kernel_ent_size = hdr_len + be32_to_cpu(hdr.size) +
+                         be16_to_cpu(hdr.metasize);
+       if (kernel_ent_size > master->size)
+               return -EINVAL;
+
+       /* Check for the rootfs right after Seama entity with a kernel. */
+       err = mtd_check_rootfs_magic(master, kernel_ent_size, &type);
+       if (!err) {
+               rootfs_offset = kernel_ent_size;
+       } else {
+               /*
+                * On some devices firmware entity might contain both: kernel
+                * and rootfs. We can't determine kernel size so we just have to
+                * look for rootfs magic.
+                * Start the search from an arbitrary offset.
+                */
+               err = mtd_find_rootfs_from(master, SEAMA_MIN_ROOTFS_OFFS,
+                                          master->size, &rootfs_offset, &type);
+               if (err)
+                       return err;
+       }
+
+       parts = kzalloc(SEAMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = sizeof hdr + be16_to_cpu(hdr.metasize);
+       parts[0].size = rootfs_offset - parts[0].offset;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[1].name = UBI_PART_NAME;
+       else
+               parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return SEAMA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_seama_parser = {
+       .owner = THIS_MODULE,
+       .name = "seama-fw",
+       .parse_fn = mtdsplit_parse_seama,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_seama_init(void)
+{
+       register_mtd_parser(&mtdsplit_seama_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_seama_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_squashfs.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_squashfs.c
new file mode 100644 (file)
index 0000000..79e1f73
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (C) 2013 Felix Fietkau <nbd@nbd.name>
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/magic.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+static int
+mtdsplit_parse_squashfs(struct mtd_info *master,
+                       const struct mtd_partition **pparts,
+                       struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *part;
+       struct mtd_info *parent_mtd;
+       size_t part_offset;
+       size_t squashfs_len;
+       int err;
+
+       err = mtd_get_squashfs_len(master, 0, &squashfs_len);
+       if (err)
+               return err;
+
+       parent_mtd = mtdpart_get_master(master);
+       part_offset = mtdpart_get_offset(master);
+
+       part = kzalloc(sizeof(*part), GFP_KERNEL);
+       if (!part) {
+               pr_alert("unable to allocate memory for \"%s\" partition\n",
+                        ROOTFS_SPLIT_NAME);
+               return -ENOMEM;
+       }
+
+       part->name = ROOTFS_SPLIT_NAME;
+       part->offset = mtd_roundup_to_eb(part_offset + squashfs_len,
+                                        parent_mtd) - part_offset;
+       part->size = mtd_rounddown_to_eb(master->size - part->offset, master);
+
+       *pparts = part;
+       return 1;
+}
+
+static struct mtd_part_parser mtdsplit_squashfs_parser = {
+       .owner = THIS_MODULE,
+       .name = "squashfs-split",
+       .parse_fn = mtdsplit_parse_squashfs,
+       .type = MTD_PARSER_TYPE_ROOTFS,
+};
+
+static int __init mtdsplit_squashfs_init(void)
+{
+       register_mtd_parser(&mtdsplit_squashfs_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_squashfs_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_tplink.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_tplink.c
new file mode 100644 (file)
index 0000000..c346aa8
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define TPLINK_NR_PARTS                2
+#define TPLINK_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
+
+#define MD5SUM_LEN  16
+
+struct fw_v1 {
+       char            vendor_name[24];
+       char            fw_version[36];
+       uint32_t        hw_id;          /* hardware id */
+       uint32_t        hw_rev;         /* hardware revision */
+       uint32_t        unk1;
+       uint8_t         md5sum1[MD5SUM_LEN];
+       uint32_t        unk2;
+       uint8_t         md5sum2[MD5SUM_LEN];
+       uint32_t        unk3;
+       uint32_t        kernel_la;      /* kernel load address */
+       uint32_t        kernel_ep;      /* kernel entry point */
+       uint32_t        fw_length;      /* total length of the firmware */
+       uint32_t        kernel_ofs;     /* kernel data offset */
+       uint32_t        kernel_len;     /* kernel data length */
+       uint32_t        rootfs_ofs;     /* rootfs data offset */
+       uint32_t        rootfs_len;     /* rootfs data length */
+       uint32_t        boot_ofs;       /* bootloader data offset */
+       uint32_t        boot_len;       /* bootloader data length */
+       uint8_t         pad[360];
+} __attribute__ ((packed));
+
+struct fw_v2 {
+       char            fw_version[48]; /* 0x04: fw version string */
+       uint32_t        hw_id;          /* 0x34: hardware id */
+       uint32_t        hw_rev;         /* 0x38: FIXME: hardware revision? */
+       uint32_t        unk1;           /* 0x3c: 0x00000000 */
+       uint8_t         md5sum1[MD5SUM_LEN]; /* 0x40 */
+       uint32_t        unk2;           /* 0x50: 0x00000000 */
+       uint8_t         md5sum2[MD5SUM_LEN]; /* 0x54 */
+       uint32_t        unk3;           /* 0x64: 0xffffffff */
+
+       uint32_t        kernel_la;      /* 0x68: kernel load address */
+       uint32_t        kernel_ep;      /* 0x6c: kernel entry point */
+       uint32_t        fw_length;      /* 0x70: total length of the image */
+       uint32_t        kernel_ofs;     /* 0x74: kernel data offset */
+       uint32_t        kernel_len;     /* 0x78: kernel data length */
+       uint32_t        rootfs_ofs;     /* 0x7c: rootfs data offset */
+       uint32_t        rootfs_len;     /* 0x80: rootfs data length */
+       uint32_t        boot_ofs;       /* 0x84: FIXME: seems to be unused */
+       uint32_t        boot_len;       /* 0x88: FIXME: seems to be unused */
+       uint16_t        unk4;           /* 0x8c: 0x55aa */
+       uint8_t         sver_hi;        /* 0x8e */
+       uint8_t         sver_lo;        /* 0x8f */
+       uint8_t         unk5;           /* 0x90: magic: 0xa5 */
+       uint8_t         ver_hi;         /* 0x91 */
+       uint8_t         ver_mid;        /* 0x92 */
+       uint8_t         ver_lo;         /* 0x93 */
+       uint8_t         pad[364];
+} __attribute__ ((packed));
+
+struct tplink_fw_header {
+       uint32_t version;
+       union {
+               struct fw_v1 v1;
+               struct fw_v2 v2;
+       };
+};
+
+static int mtdsplit_parse_tplink(struct mtd_info *master,
+                                const struct mtd_partition **pparts,
+                                struct mtd_part_parser_data *data)
+{
+       struct tplink_fw_header hdr;
+       size_t hdr_len, retlen, kernel_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       switch (le32_to_cpu(hdr.version)) {
+       case 1:
+               if (be32_to_cpu(hdr.v1.kernel_ofs) != sizeof(hdr))
+                       return -EINVAL;
+
+               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v1.kernel_len);
+               rootfs_offset = be32_to_cpu(hdr.v1.rootfs_ofs);
+               break;
+       case 2:
+       case 3:
+               if (be32_to_cpu(hdr.v2.kernel_ofs) != sizeof(hdr))
+                       return -EINVAL;
+
+               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v2.kernel_len);
+               rootfs_offset = be32_to_cpu(hdr.v2.rootfs_ofs);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (kernel_size > master->size)
+               return -EINVAL;
+
+       /* Find the rootfs */
+       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+       if (err) {
+               /*
+                * The size in the header might cover the rootfs as well.
+                * Start the search from an arbitrary offset.
+                */
+               err = mtd_find_rootfs_from(master, TPLINK_MIN_ROOTFS_OFFS,
+                                          master->size, &rootfs_offset, NULL);
+               if (err)
+                       return err;
+       }
+
+       parts = kzalloc(TPLINK_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return TPLINK_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_tplink_parser = {
+       .owner = THIS_MODULE,
+       .name = "tplink-fw",
+       .parse_fn = mtdsplit_parse_tplink,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_tplink_init(void)
+{
+       register_mtd_parser(&mtdsplit_tplink_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_tplink_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_trx.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_trx.c
new file mode 100644 (file)
index 0000000..53aebc5
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define TRX_MAGIC   0x30524448  /* "HDR0" */
+
+struct trx_header {
+       __le32 magic;
+       __le32 len;
+       __le32 crc32;
+       __le32 flag_version;
+       __le32 offset[4];
+};
+
+static int
+read_trx_header(struct mtd_info *mtd, size_t offset,
+                  struct trx_header *header)
+{
+       size_t header_len;
+       size_t retlen;
+       int ret;
+
+       header_len = sizeof(*header);
+       ret = mtd_read(mtd, offset, header_len, &retlen,
+                      (unsigned char *) header);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_parse_trx(struct mtd_info *master,
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       struct trx_header hdr;
+       int nr_parts;
+       size_t offset;
+       size_t trx_offset;
+       size_t trx_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int ret;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       /* find trx image on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               trx_size = 0;
+
+               ret = read_trx_header(master, offset, &hdr);
+               if (ret)
+                       continue;
+
+               if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
+                       pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+
+               trx_size = le32_to_cpu(hdr.len);
+               if ((offset + trx_size) > master->size) {
+                       pr_debug("trx image exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (trx_size == 0) {
+               pr_debug("no trx header found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       trx_offset = offset + hdr.offset[0];
+       rootfs_offset = offset + hdr.offset[1];
+       rootfs_size = master->size - rootfs_offset;
+       trx_size = rootfs_offset - trx_offset;
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = trx_offset;
+       parts[0].size = trx_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return nr_parts;
+
+err:
+       kfree(parts);
+       return ret;
+}
+
+static struct mtd_part_parser trx_parser = {
+       .owner = THIS_MODULE,
+       .name = "trx-fw",
+       .parse_fn = mtdsplit_parse_trx,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_trx_init(void)
+{
+       register_mtd_parser(&trx_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_trx_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_uimage.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_uimage.c
new file mode 100644 (file)
index 0000000..bd1c723
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+/*
+ * uimage_header itself is only 64B, but it may be prepended with another data.
+ * Currently the biggest size is for Edimax devices: 20B + 64B
+ */
+#define MAX_HEADER_LEN         84
+
+#define IH_MAGIC       0x27051956      /* Image Magic Number           */
+#define IH_NMLEN               32      /* Image Name Length            */
+
+#define IH_OS_LINUX            5       /* Linux        */
+
+#define IH_TYPE_KERNEL         2       /* OS Kernel Image              */
+#define IH_TYPE_FILESYSTEM     7       /* Filesystem Image             */
+
+/*
+ * Legacy format image header,
+ * all data in network byte order (aka natural aka bigendian).
+ */
+struct uimage_header {
+       uint32_t        ih_magic;       /* Image Header Magic Number    */
+       uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
+       uint32_t        ih_time;        /* Image Creation Timestamp     */
+       uint32_t        ih_size;        /* Image Data Size              */
+       uint32_t        ih_load;        /* Data  Load  Address          */
+       uint32_t        ih_ep;          /* Entry Point Address          */
+       uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
+       uint8_t         ih_os;          /* Operating System             */
+       uint8_t         ih_arch;        /* CPU architecture             */
+       uint8_t         ih_type;        /* Image Type                   */
+       uint8_t         ih_comp;        /* Compression Type             */
+       uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
+};
+
+static int
+read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
+                  size_t header_len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
+ *
+ * @find_header: function to call for a block of data that will return offset
+ *      of a valid uImage header if found
+ */
+static int __mtdsplit_parse_uimage(struct mtd_info *master,
+                                  const struct mtd_partition **pparts,
+                                  struct mtd_part_parser_data *data,
+                                  ssize_t (*find_header)(u_char *buf, size_t len))
+{
+       struct mtd_partition *parts;
+       u_char *buf;
+       int nr_parts;
+       size_t offset;
+       size_t uimage_offset;
+       size_t uimage_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int uimage_part, rf_part;
+       int ret;
+       enum mtdsplit_part_type type;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       buf = vmalloc(MAX_HEADER_LEN);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_free_parts;
+       }
+
+       /* find uImage on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               struct uimage_header *header;
+
+               uimage_size = 0;
+
+               ret = read_uimage_header(master, offset, buf, MAX_HEADER_LEN);
+               if (ret)
+                       continue;
+
+               ret = find_header(buf, MAX_HEADER_LEN);
+               if (ret < 0) {
+                       pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+               header = (struct uimage_header *)(buf + ret);
+
+               uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
+               if ((offset + uimage_size) > master->size) {
+                       pr_debug("uImage exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (uimage_size == 0) {
+               pr_debug("no uImage found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       uimage_offset = offset;
+
+       if (uimage_offset == 0) {
+               uimage_part = 0;
+               rf_part = 1;
+
+               /* find the roots after the uImage */
+               ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
+                                          master->size, &rootfs_offset, &type);
+               if (ret) {
+                       pr_debug("no rootfs after uImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_size = master->size - rootfs_offset;
+               uimage_size = rootfs_offset - uimage_offset;
+       } else {
+               rf_part = 0;
+               uimage_part = 1;
+
+               /* check rootfs presence at offset 0 */
+               ret = mtd_check_rootfs_magic(master, 0, &type);
+               if (ret) {
+                       pr_debug("no rootfs before uImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_offset = 0;
+               rootfs_size = uimage_offset;
+       }
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       parts[uimage_part].name = KERNEL_PART_NAME;
+       parts[uimage_part].offset = uimage_offset;
+       parts[uimage_part].size = uimage_size;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[rf_part].name = UBI_PART_NAME;
+       else
+               parts[rf_part].name = ROOTFS_PART_NAME;
+       parts[rf_part].offset = rootfs_offset;
+       parts[rf_part].size = rootfs_size;
+
+       vfree(buf);
+
+       *pparts = parts;
+       return nr_parts;
+
+err_free_buf:
+       vfree(buf);
+
+err_free_parts:
+       kfree(parts);
+       return ret;
+}
+
+static ssize_t uimage_verify_default(u_char *buf, size_t len)
+{
+       struct uimage_header *header = (struct uimage_header *)buf;
+
+       /* default sanity checks */
+       if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
+               pr_debug("invalid uImage magic: %08x\n",
+                        be32_to_cpu(header->ih_magic));
+               return -EINVAL;
+       }
+
+       if (header->ih_os != IH_OS_LINUX) {
+               pr_debug("invalid uImage OS: %08x\n",
+                        be32_to_cpu(header->ih_os));
+               return -EINVAL;
+       }
+
+       if (header->ih_type != IH_TYPE_KERNEL) {
+               pr_debug("invalid uImage type: %08x\n",
+                        be32_to_cpu(header->ih_type));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_generic(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                     uimage_verify_default);
+}
+
+static struct mtd_part_parser uimage_generic_parser = {
+       .owner = THIS_MODULE,
+       .name = "uimage-fw",
+       .parse_fn = mtdsplit_uimage_parse_generic,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+#define FW_MAGIC_WNR2000V1     0x32303031
+#define FW_MAGIC_WNR2000V3     0x32303033
+#define FW_MAGIC_WNR2000V4     0x32303034
+#define FW_MAGIC_WNR2200       0x32323030
+#define FW_MAGIC_WNR612V2      0x32303631
+#define FW_MAGIC_WNR1000V2     0x31303031
+#define FW_MAGIC_WNR1000V2_VC  0x31303030
+#define FW_MAGIC_WNDR3700      0x33373030
+#define FW_MAGIC_WNDR3700V2    0x33373031
+#define FW_MAGIC_WPN824N       0x31313030
+
+static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
+{
+       struct uimage_header *header = (struct uimage_header *)buf;
+       uint8_t expected_type = IH_TYPE_FILESYSTEM;
+
+       switch (be32_to_cpu(header->ih_magic)) {
+       case FW_MAGIC_WNR612V2:
+       case FW_MAGIC_WNR1000V2:
+       case FW_MAGIC_WNR1000V2_VC:
+       case FW_MAGIC_WNR2000V1:
+       case FW_MAGIC_WNR2000V3:
+       case FW_MAGIC_WNR2200:
+       case FW_MAGIC_WNDR3700:
+       case FW_MAGIC_WNDR3700V2:
+       case FW_MAGIC_WPN824N:
+               break;
+       case FW_MAGIC_WNR2000V4:
+               expected_type = IH_TYPE_KERNEL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (header->ih_os != IH_OS_LINUX ||
+           header->ih_type != expected_type)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_netgear(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                     uimage_verify_wndr3700);
+}
+
+static struct mtd_part_parser uimage_netgear_parser = {
+       .owner = THIS_MODULE,
+       .name = "netgear-fw",
+       .parse_fn = mtdsplit_uimage_parse_netgear,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Edimax
+ **************************************************/
+
+#define FW_EDIMAX_OFFSET       20
+#define FW_MAGIC_EDIMAX                0x43535953
+
+static ssize_t uimage_find_edimax(u_char *buf, size_t len)
+{
+       u32 *magic;
+
+       if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
+               pr_err("Buffer too small for checking Edimax header\n");
+               return -ENOSPC;
+       }
+
+       magic = (u32 *)buf;
+       if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
+               return -EINVAL;
+
+       if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
+               return FW_EDIMAX_OFFSET;
+
+       return -EINVAL;
+}
+
+static int
+mtdsplit_uimage_parse_edimax(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                      uimage_find_edimax);
+}
+
+static struct mtd_part_parser uimage_edimax_parser = {
+       .owner = THIS_MODULE,
+       .name = "edimax-fw",
+       .parse_fn = mtdsplit_uimage_parse_edimax,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_uimage_init(void)
+{
+       register_mtd_parser(&uimage_generic_parser);
+       register_mtd_parser(&uimage_netgear_parser);
+       register_mtd_parser(&uimage_edimax_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_uimage_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_wrgg.c b/target/linux/generic/files-3.18/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
new file mode 100644 (file)
index 0000000..16ebd51
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *  Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define WRGG_NR_PARTS          2
+#define WRGG_MIN_ROOTFS_OFFS   0x80000 /* 512KiB */
+#define WRGG03_MAGIC           0x20080321
+#define WRG_MAGIC              0x20040220
+
+struct wrgg03_header {
+       char            signature[32];
+       uint32_t        magic1;
+       uint32_t        magic2;
+       char            version[16];
+       char            model[16];
+       uint32_t        flag[2];
+       uint32_t        reserve[2];
+       char            buildno[16];
+       uint32_t        size;
+       uint32_t        offset;
+       char            devname[32];
+       char            digest[16];
+} __attribute__ ((packed));
+
+struct wrg_header {
+       char            signature[32];
+       uint32_t        magic1;
+       uint32_t        magic2;
+       uint32_t        size;
+       uint32_t        offset;
+       char            devname[32];
+       char            digest[16];
+} __attribute__ ((packed));
+
+
+static int mtdsplit_parse_wrgg(struct mtd_info *master,
+                              const struct mtd_partition **pparts,
+                              struct mtd_part_parser_data *data)
+{
+       struct wrgg03_header hdr;
+       size_t hdr_len, retlen, kernel_ent_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       enum mtdsplit_part_type type;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* sanity checks */
+       if (le32_to_cpu(hdr.magic1) == WRGG03_MAGIC) {
+               kernel_ent_size = hdr_len + be32_to_cpu(hdr.size);
+       } else if (le32_to_cpu(hdr.magic1) == WRG_MAGIC) {
+               kernel_ent_size = sizeof(struct wrg_header) + le32_to_cpu(
+                                 ((struct wrg_header*)&hdr)->size);
+       } else {
+               return -EINVAL;
+       }
+
+       if (kernel_ent_size > master->size)
+               return -EINVAL;
+
+       /*
+        * The size in the header covers the rootfs as well.
+        * Start the search from an arbitrary offset.
+        */
+       err = mtd_find_rootfs_from(master, WRGG_MIN_ROOTFS_OFFS,
+                                  master->size, &rootfs_offset, &type);
+       if (err)
+               return err;
+
+       parts = kzalloc(WRGG_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return WRGG_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_wrgg_parser = {
+       .owner = THIS_MODULE,
+       .name = "wrgg-fw",
+       .parse_fn = mtdsplit_parse_wrgg,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_wrgg_init(void)
+{
+       register_mtd_parser(&mtdsplit_wrgg_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_wrgg_init);
diff --git a/target/linux/generic/files-3.18/drivers/mtd/myloader.c b/target/linux/generic/files-3.18/drivers/mtd/myloader.c
new file mode 100644 (file)
index 0000000..7532d45
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *  Parse MyLoader-style flash partition tables and produce a Linux partition
+ *  array to match.
+ *
+ *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This file was based on drivers/mtd/redboot.c
+ *  Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+#include <linux/myloader.h>
+
+#define BLOCK_LEN_MIN          0x10000
+#define PART_NAME_LEN          32
+
+struct part_data {
+       struct mylo_partition_table     tab;
+       char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
+};
+
+static int myloader_parse_partitions(struct mtd_info *master,
+                                    const struct mtd_partition **pparts,
+                                    struct mtd_part_parser_data *data)
+{
+       struct part_data *buf;
+       struct mylo_partition_table *tab;
+       struct mylo_partition *part;
+       struct mtd_partition *mtd_parts;
+       struct mtd_partition *mtd_part;
+       int num_parts;
+       int ret, i;
+       size_t retlen;
+       char *names;
+       unsigned long offset;
+       unsigned long blocklen;
+
+       buf = vmalloc(sizeof(*buf));
+       if (!buf) {
+               return -ENOMEM;
+               goto out;
+       }
+       tab = &buf->tab;
+
+       blocklen = master->erasesize;
+       if (blocklen < BLOCK_LEN_MIN)
+               blocklen = BLOCK_LEN_MIN;
+
+       offset = blocklen;
+
+       /* Find the partition table */
+       for (i = 0; i < 4; i++, offset += blocklen) {
+               printk(KERN_DEBUG "%s: searching for MyLoader partition table"
+                               " at offset 0x%lx\n", master->name, offset);
+
+               ret = mtd_read(master, offset, sizeof(*buf), &retlen,
+                              (void *)buf);
+               if (ret)
+                       goto out_free_buf;
+
+               if (retlen != sizeof(*buf)) {
+                       ret = -EIO;
+                       goto out_free_buf;
+               }
+
+               /* Check for Partition Table magic number */
+               if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
+                       break;
+
+       }
+
+       if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
+               printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
+                       master->name);
+               ret = 0;
+               goto out_free_buf;
+       }
+
+       /* The MyLoader and the Partition Table is always present */
+       num_parts = 2;
+
+       /* Detect number of used partitions */
+       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+               part = &tab->partitions[i];
+
+               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+                       continue;
+
+               num_parts++;
+       }
+
+       mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
+                               num_parts * PART_NAME_LEN), GFP_KERNEL);
+
+       if (!mtd_parts) {
+               ret = -ENOMEM;
+               goto out_free_buf;
+       }
+
+       mtd_part = mtd_parts;
+       names = (char *)&mtd_parts[num_parts];
+
+       strncpy(names, "myloader", PART_NAME_LEN);
+       mtd_part->name = names;
+       mtd_part->offset = 0;
+       mtd_part->size = offset;
+       mtd_part->mask_flags = MTD_WRITEABLE;
+       mtd_part++;
+       names += PART_NAME_LEN;
+
+       strncpy(names, "partition_table", PART_NAME_LEN);
+       mtd_part->name = names;
+       mtd_part->offset = offset;
+       mtd_part->size = blocklen;
+       mtd_part->mask_flags = MTD_WRITEABLE;
+       mtd_part++;
+       names += PART_NAME_LEN;
+
+       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+               part = &tab->partitions[i];
+
+               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+                       continue;
+
+               if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
+                       strncpy(names, buf->names[i], PART_NAME_LEN);
+               else
+                       snprintf(names, PART_NAME_LEN, "partition%d", i);
+
+               mtd_part->offset = le32_to_cpu(part->addr);
+               mtd_part->size = le32_to_cpu(part->size);
+               mtd_part->name = names;
+               mtd_part++;
+               names += PART_NAME_LEN;
+       }
+
+       *pparts = mtd_parts;
+       ret = num_parts;
+
+ out_free_buf:
+       vfree(buf);
+ out:
+       return ret;
+}
+
+static struct mtd_part_parser myloader_mtd_parser = {
+       .owner          = THIS_MODULE,
+       .parse_fn       = myloader_parse_partitions,
+       .name           = "MyLoader",
+};
+
+static int __init myloader_mtd_parser_init(void)
+{
+       register_mtd_parser(&myloader_mtd_parser);
+
+       return 0;
+}
+
+static void __exit myloader_mtd_parser_exit(void)
+{
+       deregister_mtd_parser(&myloader_mtd_parser);
+}
+
+module_init(myloader_mtd_parser_init);
+module_exit(myloader_mtd_parser_exit);
+
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/adm6996.c b/target/linux/generic/files-3.18/drivers/net/phy/adm6996.c
new file mode 100644 (file)
index 0000000..42928ba
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ * ADM6996 switch driver
+ *
+ * swconfig interface based on ar8216.c
+ *
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ * VLAN support Copyright (c) 2010, 2011 Peter Lebbing <peter@digitalbrains.com>
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2014 Matti Laakso <malaakso@elisanet.fi>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/*#define DEBUG 1*/
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/adm6996-gpio.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/switch.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "adm6996.h"
+
+MODULE_DESCRIPTION("Infineon ADM6996 Switch");
+MODULE_AUTHOR("Felix Fietkau, Peter Lebbing <peter@digitalbrains.com>");
+MODULE_LICENSE("GPL");
+
+static const char * const adm6996_model_name[] =
+{
+       NULL,
+       "ADM6996FC",
+       "ADM6996M",
+       "ADM6996L"
+};
+
+struct adm6996_mib_desc {
+       unsigned int offset;
+       const char *name;
+};
+
+struct adm6996_priv {
+       struct switch_dev dev;
+       void *priv;
+
+       u8 eecs;
+       u8 eesk;
+       u8 eedi;
+
+       enum adm6996_model model;
+
+       bool enable_vlan;
+       bool vlan_enabled;      /* Current hardware state */
+
+#ifdef DEBUG
+       u16 addr;               /* Debugging: register address to operate on */
+#endif
+
+       u16 pvid[ADM_NUM_PORTS];        /* Primary VLAN ID */
+       u8 tagged_ports;
+
+       u16 vlan_id[ADM_NUM_VLANS];
+       u8 vlan_table[ADM_NUM_VLANS];   /* bitmap, 1 = port is member */
+       u8 vlan_tagged[ADM_NUM_VLANS];  /* bitmap, 1 = tagged member */
+       
+       struct mutex mib_lock;
+       char buf[2048];
+
+       struct mutex reg_mutex;
+
+       /* use abstraction for regops, we want to add gpio support in the future */
+       u16 (*read)(struct adm6996_priv *priv, enum admreg reg);
+       void (*write)(struct adm6996_priv *priv, enum admreg reg, u16 val);
+};
+
+#define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev)
+#define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
+
+#define MIB_DESC(_o, _n)       \
+       {                       \
+               .offset = (_o), \
+               .name = (_n),   \
+       }
+
+static const struct adm6996_mib_desc adm6996_mibs[] = {
+       MIB_DESC(ADM_CL0, "RxPacket"),
+       MIB_DESC(ADM_CL6, "RxByte"),
+       MIB_DESC(ADM_CL12, "TxPacket"),
+       MIB_DESC(ADM_CL18, "TxByte"),
+       MIB_DESC(ADM_CL24, "Collision"),
+       MIB_DESC(ADM_CL30, "Error"),
+};
+
+#define ADM6996_MIB_RXB_ID     1
+#define ADM6996_MIB_TXB_ID     3
+
+static inline u16
+r16(struct adm6996_priv *priv, enum admreg reg)
+{
+       return priv->read(priv, reg);
+}
+
+static inline void
+w16(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       priv->write(priv, reg, val);
+}
+
+/* Minimum timing constants */
+#define EECK_EDGE_TIME  3   /* 3us - max(adm 2.5us, 93c 1us) */
+#define EEDI_SETUP_TIME 1   /* 1us - max(adm 10ns, 93c 400ns) */
+#define EECS_SETUP_TIME 1   /* 1us - max(adm no, 93c 200ns) */
+
+static void adm6996_gpio_write(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
+{
+       int i, len = (bits + 7) / 8;
+       u8 mask;
+
+       gpio_set_value(priv->eecs, cs);
+       udelay(EECK_EDGE_TIME);
+
+       /* Byte assemble from MSB to LSB */
+       for (i = 0; i < len; i++) {
+               /* Bit bang from MSB to LSB */
+               for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
+                       /* Clock low */
+                       gpio_set_value(priv->eesk, 0);
+                       udelay(EECK_EDGE_TIME);
+
+                       /* Output on rising edge */
+                       gpio_set_value(priv->eedi, (mask & buf[i]));
+                       udelay(EEDI_SETUP_TIME);
+
+                       /* Clock high */
+                       gpio_set_value(priv->eesk, 1);
+                       udelay(EECK_EDGE_TIME);
+               }
+       }
+
+       /* Clock low */
+       gpio_set_value(priv->eesk, 0);
+       udelay(EECK_EDGE_TIME);
+
+       if (cs)
+               gpio_set_value(priv->eecs, 0);
+}
+
+static void adm6996_gpio_read(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
+{
+       int i, len = (bits + 7) / 8;
+       u8 mask;
+
+       gpio_set_value(priv->eecs, cs);
+       udelay(EECK_EDGE_TIME);
+
+       /* Byte assemble from MSB to LSB */
+       for (i = 0; i < len; i++) {
+               u8 byte;
+
+               /* Bit bang from MSB to LSB */
+               for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) {
+                       u8 gp;
+
+                       /* Clock low */
+                       gpio_set_value(priv->eesk, 0);
+                       udelay(EECK_EDGE_TIME);
+
+                       /* Input on rising edge */
+                       gp = gpio_get_value(priv->eedi);
+                       if (gp)
+                               byte |= mask;
+
+                       /* Clock high */
+                       gpio_set_value(priv->eesk, 1);
+                       udelay(EECK_EDGE_TIME);
+               }
+
+               *buf++ = byte;
+       }
+
+       /* Clock low */
+       gpio_set_value(priv->eesk, 0);
+       udelay(EECK_EDGE_TIME);
+
+       if (cs)
+               gpio_set_value(priv->eecs, 0);
+}
+
+/* Advance clock(s) */
+static void adm6996_gpio_adclk(struct adm6996_priv *priv, int clocks)
+{
+       int i;
+       for (i = 0; i < clocks; i++) {
+               /* Clock high */
+               gpio_set_value(priv->eesk, 1);
+               udelay(EECK_EDGE_TIME);
+
+               /* Clock low */
+               gpio_set_value(priv->eesk, 0);
+               udelay(EECK_EDGE_TIME);
+       }
+}
+
+static u16
+adm6996_read_gpio_reg(struct adm6996_priv *priv, enum admreg reg)
+{
+       /* cmd: 01 10 T DD R RRRRRR */
+       u8 bits[6] = {
+               0xFF, 0xFF, 0xFF, 0xFF,
+               (0x06 << 4) | ((0 & 0x01) << 3 | (reg&64)>>6),
+               ((reg&63)<<2)
+       };
+
+       u8 rbits[4];
+
+       /* Enable GPIO outputs with all pins to 0 */
+       gpio_direction_output(priv->eecs, 0);
+       gpio_direction_output(priv->eesk, 0);
+       gpio_direction_output(priv->eedi, 0);
+
+       adm6996_gpio_write(priv, 0, bits, 46);
+       gpio_direction_input(priv->eedi);
+       adm6996_gpio_adclk(priv, 2);
+       adm6996_gpio_read(priv, 0, rbits, 32);
+
+       /* Extra clock(s) required per datasheet */
+       adm6996_gpio_adclk(priv, 2);
+
+       /* Disable GPIO outputs */
+       gpio_direction_input(priv->eecs);
+       gpio_direction_input(priv->eesk);
+
+        /* EEPROM has 16-bit registers, but pumps out two registers in one request */
+       return (reg & 0x01 ?  (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3]));
+}
+
+/* Write chip configuration register */
+/* Follow 93c66 timing and chip's min EEPROM timing requirement */
+static void
+adm6996_write_gpio_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
+       u8 bits[4] = {
+               (0x05 << 5) | (reg >> 3),
+               (reg << 5) | (u8)(val >> 11),
+               (u8)(val >> 3),
+               (u8)(val << 5)
+       };
+
+       /* Enable GPIO outputs with all pins to 0 */
+       gpio_direction_output(priv->eecs, 0);
+       gpio_direction_output(priv->eesk, 0);
+       gpio_direction_output(priv->eedi, 0);
+
+       /* Write cmd. Total 27 bits */
+       adm6996_gpio_write(priv, 1, bits, 27);
+
+       /* Extra clock(s) required per datasheet */
+       adm6996_gpio_adclk(priv, 2);
+
+       /* Disable GPIO outputs */
+       gpio_direction_input(priv->eecs);
+       gpio_direction_input(priv->eesk);
+       gpio_direction_input(priv->eedi);
+}
+
+static u16
+adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg)
+{
+       struct phy_device *phydev = priv->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, PHYADDR(reg));
+}
+
+static void
+adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       struct phy_device *phydev = priv->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, PHYADDR(reg), val);
+}
+
+static int
+adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 1)
+               return -EINVAL;
+
+       priv->enable_vlan = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = priv->enable_vlan;
+
+       return 0;
+};
+
+#ifdef DEBUG
+
+static int
+adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 1023)
+               return -EINVAL;
+
+       priv->addr = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = priv->addr;
+
+       return 0;
+};
+
+static int
+adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 65535)
+               return -EINVAL;
+
+       w16(priv, priv->addr, val->value.i);
+
+       return 0;
+};
+
+static int
+adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = r16(priv, priv->addr);
+
+       return 0;
+};
+
+#endif /* def DEBUG */
+
+static int
+adm6996_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("set_pvid port %d vlan %d\n", port, vlan);
+
+       if (vlan > ADM_VLAN_MAX_ID)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+
+       return 0;
+}
+
+static int
+adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("get_pvid port %d\n", port);
+       *vlan = priv->pvid[port];
+
+       return 0;
+}
+
+static int
+adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("set_vid port %d vid %d\n", val->port_vlan, val->value.i);
+
+       if (val->value.i > ADM_VLAN_MAX_ID)
+               return -EINVAL;
+
+       priv->vlan_id[val->port_vlan] = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("get_vid port %d\n", val->port_vlan);
+
+       val->value.i = priv->vlan_id[val->port_vlan];
+
+       return 0;
+};
+
+static int
+adm6996_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       u8 tagged = priv->vlan_tagged[val->port_vlan];
+       int i;
+
+       pr_devel("get_ports port_vlan %d\n", val->port_vlan);
+
+       val->len = 0;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+
+       return 0;
+};
+
+static int
+adm6996_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       u8 *ports = &priv->vlan_table[val->port_vlan];
+       u8 *tagged = &priv->vlan_tagged[val->port_vlan];
+       int i;
+
+       pr_devel("set_ports port_vlan %d ports", val->port_vlan);
+
+       *ports = 0;
+       *tagged = 0;
+
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+#ifdef DEBUG
+               pr_cont(" %d%s", p->id,
+                      ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" :
+                       ""));
+#endif
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       *tagged |= (1 << p->id);
+                       priv->tagged_ports |= (1 << p->id);
+               }
+
+               *ports |= (1 << p->id);
+       }
+
+#ifdef DEBUG
+       pr_cont("\n");
+#endif
+
+       return 0;
+};
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_enable_vlan(struct adm6996_priv *priv)
+{
+       u16 reg;
+
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg &= ~(ADM_OTBE_MASK);
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = r16(priv, ADM_IFNTE);
+       reg &= ~(ADM_IFNTE_MASK);
+       w16(priv, ADM_IFNTE, reg);
+       reg = r16(priv, ADM_VID_CHECK);
+       reg |= ADM_VID_CHECK_MASK;
+       w16(priv, ADM_VID_CHECK, reg);
+       reg = r16(priv, ADM_SYSC0);
+       reg |= ADM_NTTE;
+       reg &= ~(ADM_RVID1);
+       w16(priv, ADM_SYSC0, reg);
+       reg = r16(priv, ADM_SYSC3);
+       reg |= ADM_TBV;
+       w16(priv, ADM_SYSC3, reg);
+}
+
+static void
+adm6996_enable_vlan_6996l(struct adm6996_priv *priv)
+{
+       u16 reg;
+
+       reg = r16(priv, ADM_SYSC3);
+       reg |= ADM_TBV;
+       reg |= ADM_MAC_CLONE;
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Disable VLANs
+ *
+ * Sets VLAN mapping for port-based VLAN with all ports connected to
+ * eachother (this is also the power-on default).
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_disable_vlan(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               reg = ADM_VLAN_FILT_MEMBER_MASK;
+               w16(priv, ADM_VLAN_FILT_L(i), reg);
+               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1);
+               w16(priv, ADM_VLAN_FILT_H(i), reg);
+       }
+
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg |= ADM_OTBE_MASK;
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = r16(priv, ADM_IFNTE);
+       reg |= ADM_IFNTE_MASK;
+       w16(priv, ADM_IFNTE, reg);
+       reg = r16(priv, ADM_VID_CHECK);
+       reg &= ~(ADM_VID_CHECK_MASK);
+       w16(priv, ADM_VID_CHECK, reg);
+       reg = r16(priv, ADM_SYSC0);
+       reg &= ~(ADM_NTTE);
+       reg |= ADM_RVID1;
+       w16(priv, ADM_SYSC0, reg);
+       reg = r16(priv, ADM_SYSC3);
+       reg &= ~(ADM_TBV);
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Disable VLANs
+ *
+ * Sets VLAN mapping for port-based VLAN with all ports connected to
+ * eachother (this is also the power-on default).
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_disable_vlan_6996l(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               w16(priv, ADM_VLAN_MAP(i), 0);
+       }
+
+       reg = r16(priv, ADM_SYSC3);
+       reg &= ~(ADM_TBV);
+       reg &= ~(ADM_MAC_CLONE);
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_apply_port_pvids(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               reg = r16(priv, adm_portcfg[i]);
+               reg &= ~(ADM_PORTCFG_PVID_MASK);
+               reg |= ADM_PORTCFG_PVID(priv->pvid[i]);
+               if (priv->model == ADM6996L) {
+                       if (priv->tagged_ports & (1 << i))
+                               reg |= (1 << 4);
+                       else
+                               reg &= ~(1 << 4);
+               }
+               w16(priv, adm_portcfg[i], reg);
+       }
+
+       w16(priv, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0]));
+       w16(priv, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1]));
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg &= ~(ADM_P2_PVID_MASK);
+       reg |= ADM_P2_PVID_VAL(priv->pvid[2]);
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = ADM_P3_PVID_VAL(priv->pvid[3]);
+       reg |= ADM_P4_PVID_VAL(priv->pvid[4]);
+       w16(priv, ADM_P3_P4_PVID, reg);
+       reg = r16(priv, ADM_P5_PVID);
+       reg &= ~(ADM_P2_PVID_MASK);
+       reg |= ADM_P5_PVID_VAL(priv->pvid[5]);
+       w16(priv, ADM_P5_PVID, reg);
+}
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_apply_vlan_filters(struct adm6996_priv *priv)
+{
+       u8 ports, tagged;
+       u16 vid, reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               vid = priv->vlan_id[i];
+               ports = priv->vlan_table[i];
+               tagged = priv->vlan_tagged[i];
+
+               if (ports == 0) {
+                       /* Disable VLAN entry */
+                       w16(priv, ADM_VLAN_FILT_H(i), 0);
+                       w16(priv, ADM_VLAN_FILT_L(i), 0);
+                       continue;
+               }
+
+               reg = ADM_VLAN_FILT_MEMBER(ports);
+               reg |= ADM_VLAN_FILT_TAGGED(tagged);
+               w16(priv, ADM_VLAN_FILT_L(i), reg);
+               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid);
+               w16(priv, ADM_VLAN_FILT_H(i), reg);
+       }
+}
+
+static void
+adm6996_apply_vlan_filters_6996l(struct adm6996_priv *priv)
+{
+       u8 ports;
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               ports = priv->vlan_table[i];
+
+               if (ports == 0) {
+                       /* Disable VLAN entry */
+                       w16(priv, ADM_VLAN_MAP(i), 0);
+                       continue;
+               } else {
+                       reg = ADM_VLAN_FILT(ports);
+                       w16(priv, ADM_VLAN_MAP(i), reg);
+               }
+       }
+}
+
+static int
+adm6996_hw_apply(struct switch_dev *dev)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("hw_apply\n");
+
+       mutex_lock(&priv->reg_mutex);
+
+       if (!priv->enable_vlan) {
+               if (priv->vlan_enabled) {
+                       if (priv->model == ADM6996L)
+                               adm6996_disable_vlan_6996l(priv);
+                       else
+                               adm6996_disable_vlan(priv);
+                       priv->vlan_enabled = 0;
+               }
+               goto out;
+       }
+
+       if (!priv->vlan_enabled) {
+               if (priv->model == ADM6996L)
+                       adm6996_enable_vlan_6996l(priv);
+               else
+                       adm6996_enable_vlan(priv);
+               priv->vlan_enabled = 1;
+       }
+
+       adm6996_apply_port_pvids(priv);
+       if (priv->model == ADM6996L)
+               adm6996_apply_vlan_filters_6996l(priv);
+       else
+               adm6996_apply_vlan_filters(priv);
+
+out:
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+/*
+ * Reset the switch
+ *
+ * The ADM6996 can't do a software-initiated reset, so we just initialise the
+ * registers we support in this driver.
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_perform_reset (struct adm6996_priv *priv)
+{
+       int i;
+
+       /* initialize port and vlan settings */
+       for (i = 0; i < ADM_NUM_PORTS - 1; i++) {
+               w16(priv, adm_portcfg[i], ADM_PORTCFG_INIT |
+                       ADM_PORTCFG_PVID(0));
+       }
+       w16(priv, adm_portcfg[5], ADM_PORTCFG_CPU);
+
+       if (priv->model == ADM6996M || priv->model == ADM6996FC) {
+               /* reset all PHY ports */
+               for (i = 0; i < ADM_PHY_PORTS; i++) {
+                       w16(priv, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
+               }
+       }
+
+       priv->enable_vlan = 0;
+       priv->vlan_enabled = 0;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               priv->pvid[i] = 0;
+       }
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               priv->vlan_id[i] = i;
+               priv->vlan_table[i] = 0;
+               priv->vlan_tagged[i] = 0;
+       }
+
+       if (priv->model == ADM6996M) {
+               /* Clear VLAN priority map so prio's are unused */
+               w16 (priv, ADM_VLAN_PRIOMAP, 0);
+
+               adm6996_disable_vlan(priv);
+               adm6996_apply_port_pvids(priv);
+       } else if (priv->model == ADM6996L) {
+               /* Clear VLAN priority map so prio's are unused */
+               w16 (priv, ADM_VLAN_PRIOMAP, 0);
+
+               adm6996_disable_vlan_6996l(priv);
+               adm6996_apply_port_pvids(priv);
+       }
+}
+
+static int
+adm6996_reset_switch(struct switch_dev *dev)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("reset\n");
+
+       mutex_lock(&priv->reg_mutex);
+       adm6996_perform_reset (priv);
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+static int
+adm6996_get_port_link(struct switch_dev *dev, int port,
+               struct switch_port_link *link)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       
+       u16 reg = 0;
+       
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+       
+       switch (port) {
+       case 0:
+               reg = r16(priv, ADM_PS0);
+               break;
+       case 1:
+               reg = r16(priv, ADM_PS0);
+               reg = reg >> 8;
+               break;
+       case 2:
+               reg = r16(priv, ADM_PS1);
+               break;
+       case 3:
+               reg = r16(priv, ADM_PS1);
+               reg = reg >> 8;
+               break;
+       case 4:
+               reg = r16(priv, ADM_PS1);
+               reg = reg >> 12;
+               break;
+       case 5:
+               reg = r16(priv, ADM_PS2);
+               /* Bits 0, 1, 3 and 4. */
+               reg = (reg & 3) | ((reg & 24) >> 1);
+               break;
+       default:
+               return -EINVAL;
+       }
+       
+       link->link = reg & ADM_PS_LS;
+       if (!link->link)
+               return 0;
+       link->aneg = true;
+       link->duplex = reg & ADM_PS_DS;
+       link->tx_flow = reg & ADM_PS_FCS;
+       link->rx_flow = reg & ADM_PS_FCS;
+       if (reg & ADM_PS_SS)
+               link->speed = SWITCH_PORT_SPEED_100;
+       else
+               link->speed = SWITCH_PORT_SPEED_10;
+
+       return 0;
+}
+
+static int
+adm6996_sw_get_port_mib(struct switch_dev *dev,
+                      const struct switch_attr *attr,
+                      struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       int port;
+       char *buf = priv->buf;
+       int i, len = 0;
+       u32 reg = 0;
+
+       port = val->port_vlan;
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+
+       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                       "Port %d MIB counters\n",
+                       port);
+
+       for (i = 0; i < ARRAY_SIZE(adm6996_mibs); i++) {
+               reg = r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port));
+               reg += r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+               len += snprintf(buf + len, sizeof(priv->buf) - len,
+                               "%-12s: %u\n",
+                               adm6996_mibs[i].name,
+                               reg);
+       }
+
+       mutex_unlock(&priv->mib_lock);
+
+       val->value.s = buf;
+       val->len = len;
+
+       return 0;
+}
+
+static int
+adm6996_get_port_stats(struct switch_dev *dev, int port,
+                       struct switch_port_stats *stats)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       int id;
+       u32 reg = 0;
+
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+
+       id = ADM6996_MIB_TXB_ID;
+       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
+       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+       stats->tx_bytes = reg;
+
+       id = ADM6996_MIB_RXB_ID;
+       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
+       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+       stats->rx_bytes = reg;
+
+       mutex_unlock(&priv->mib_lock);
+
+       return 0;
+}
+
+static struct switch_attr adm6996_globals[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "enable_vlan",
+        .description = "Enable VLANs",
+        .set = adm6996_set_enable_vlan,
+        .get = adm6996_get_enable_vlan,
+       },
+#ifdef DEBUG
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "addr",
+        .description =
+        "Direct register access: set register address (0 - 1023)",
+        .set = adm6996_set_addr,
+        .get = adm6996_get_addr,
+        },
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "data",
+        .description =
+        "Direct register access: read/write to register (0 - 65535)",
+        .set = adm6996_set_data,
+        .get = adm6996_get_data,
+        },
+#endif /* def DEBUG */
+};
+
+static struct switch_attr adm6996_port[] = {
+       {
+        .type = SWITCH_TYPE_STRING,
+        .name = "mib",
+        .description = "Get port's MIB counters",
+        .set = NULL,
+        .get = adm6996_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr adm6996_vlan[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "vid",
+        .description = "VLAN ID",
+        .set = adm6996_set_vid,
+        .get = adm6996_get_vid,
+        },
+};
+
+static struct switch_dev_ops adm6996_ops = {
+       .attr_global = {
+                       .attr = adm6996_globals,
+                       .n_attr = ARRAY_SIZE(adm6996_globals),
+                       },
+       .attr_port = {
+                     .attr = adm6996_port,
+                     .n_attr = ARRAY_SIZE(adm6996_port),
+                     },
+       .attr_vlan = {
+                     .attr = adm6996_vlan,
+                     .n_attr = ARRAY_SIZE(adm6996_vlan),
+                     },
+       .get_port_pvid = adm6996_get_pvid,
+       .set_port_pvid = adm6996_set_pvid,
+       .get_vlan_ports = adm6996_get_ports,
+       .set_vlan_ports = adm6996_set_ports,
+       .apply_config = adm6996_hw_apply,
+       .reset_switch = adm6996_reset_switch,
+       .get_port_link = adm6996_get_port_link,
+       .get_port_stats = adm6996_get_port_stats,
+};
+
+static int adm6996_switch_init(struct adm6996_priv *priv, const char *alias, struct net_device *netdev)
+{
+       struct switch_dev *swdev;
+       u16 test, old;
+
+       if (!priv->model) {
+               /* Detect type of chip */
+               old = r16(priv, ADM_VID_CHECK);
+               test = old ^ (1 << 12);
+               w16(priv, ADM_VID_CHECK, test);
+               test ^= r16(priv, ADM_VID_CHECK);
+               if (test & (1 << 12)) {
+                       /* 
+                        * Bit 12 of this register is read-only. 
+                        * This is the FC model. 
+                        */
+                       priv->model = ADM6996FC;
+               } else {
+                       /* Bit 12 is read-write. This is the M model. */
+                       priv->model = ADM6996M;
+                       w16(priv, ADM_VID_CHECK, old);
+               }
+       }
+
+       swdev = &priv->dev;
+       swdev->name = (adm6996_model_name[priv->model]);
+       swdev->cpu_port = ADM_CPU_PORT;
+       swdev->ports = ADM_NUM_PORTS;
+       swdev->vlans = ADM_NUM_VLANS;
+       swdev->ops = &adm6996_ops;
+       swdev->alias = alias;
+
+       /* The ADM6996L connected through GPIOs does not support any switch
+          status calls */
+       if (priv->model == ADM6996L) {
+               adm6996_ops.attr_port.n_attr = 0;
+               adm6996_ops.get_port_link = NULL;
+       }
+
+       pr_info ("%s: %s model PHY found.\n", alias, swdev->name);
+
+       mutex_lock(&priv->reg_mutex);
+       adm6996_perform_reset (priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       if (priv->model == ADM6996M || priv->model == ADM6996L) {
+               return register_switch(swdev, netdev);
+       }
+
+       return -ENODEV;
+}
+
+static int adm6996_config_init(struct phy_device *pdev)
+{
+       struct adm6996_priv *priv;
+       int ret;
+
+       pdev->supported = ADVERTISED_100baseT_Full;
+       pdev->advertising = ADVERTISED_100baseT_Full;
+
+       if (pdev->mdio.addr != 0) {
+               pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n"
+                               , pdev->attached_dev->name, pdev->mdio.addr);
+               return 0;
+       }
+
+       priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+       priv->priv = pdev;
+       priv->read = adm6996_read_mii_reg;
+       priv->write = adm6996_write_mii_reg;
+
+       ret = adm6996_switch_init(priv, pdev->attached_dev->name, pdev->attached_dev);
+       if (ret < 0)
+               return ret;
+
+       pdev->priv = priv;
+
+       return 0;
+}
+
+/*
+ * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
+ */
+static int adm6996_read_status(struct phy_device *phydev)
+{
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+/*
+ * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
+ */
+static int adm6996_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int adm6996_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       /* Our custom registers are at PHY addresses 0-10. Claim those. */
+       if (dev->mdio.addr > 10)
+               return 0;
+
+       /* look for the switch on the bus */
+       reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
+       if (reg != ADM_SIG0_VAL)
+               return 0;
+
+       reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
+       if (reg != ADM_SIG1_VAL)
+               return 0;
+
+       dev->phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL;
+
+       return 0;
+}
+
+static int adm6996_probe(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static void adm6996_remove(struct phy_device *pdev)
+{
+       struct adm6996_priv *priv = phy_to_adm(pdev);
+
+       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
+               unregister_switch(&priv->dev);
+}
+
+static int adm6996_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+
+static struct phy_driver adm6996_phy_driver = {
+       .name           = "Infineon ADM6996",
+       .phy_id         = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = adm6996_probe,
+       .remove         = adm6996_remove,
+       .config_init    = &adm6996_config_init,
+       .config_aneg    = &adm6996_config_aneg,
+       .read_status    = &adm6996_read_status,
+       .soft_reset     = adm6996_soft_reset,
+};
+
+static int adm6996_gpio_probe(struct platform_device *pdev)
+{
+       struct adm6996_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct adm6996_priv *priv;
+       int ret;
+
+       if (!pdata)
+               return -EINVAL;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+
+       priv->eecs = pdata->eecs;
+       priv->eedi = pdata->eedi;
+       priv->eesk = pdata->eesk;
+
+       priv->model = pdata->model;
+       priv->read = adm6996_read_gpio_reg;
+       priv->write = adm6996_write_gpio_reg;
+
+       ret = devm_gpio_request(&pdev->dev, priv->eecs, "adm_eecs");
+       if (ret)
+               return ret;
+       ret = devm_gpio_request(&pdev->dev, priv->eedi, "adm_eedi");
+       if (ret)
+               return ret;
+       ret = devm_gpio_request(&pdev->dev, priv->eesk, "adm_eesk");
+       if (ret)
+               return ret;
+
+       ret = adm6996_switch_init(priv, dev_name(&pdev->dev), NULL);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+}
+
+static int adm6996_gpio_remove(struct platform_device *pdev)
+{
+       struct adm6996_priv *priv = platform_get_drvdata(pdev);
+
+       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
+               unregister_switch(&priv->dev);
+
+       return 0;
+}
+
+static struct platform_driver adm6996_gpio_driver = {
+       .probe = adm6996_gpio_probe,
+       .remove = adm6996_gpio_remove,
+       .driver = {
+               .name = "adm6996_gpio",
+       },
+};
+
+static int __init adm6996_init(void)
+{
+       int err;
+
+       phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup);
+       err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE);
+       if (err)
+               return err;
+
+       err = platform_driver_register(&adm6996_gpio_driver);
+       if (err)
+               phy_driver_unregister(&adm6996_phy_driver);
+
+       return err;
+}
+
+static void __exit adm6996_exit(void)
+{
+       platform_driver_unregister(&adm6996_gpio_driver);
+       phy_driver_unregister(&adm6996_phy_driver);
+}
+
+module_init(adm6996_init);
+module_exit(adm6996_exit);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/adm6996.h b/target/linux/generic/files-3.18/drivers/net/phy/adm6996.h
new file mode 100644 (file)
index 0000000..6fd460a
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * ADM6996 switch driver
+ *
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ * Copyright (c) 2010,2011 Peter Lebbing <peter@digitalbrains.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#ifndef __ADM6996_H
+#define __ADM6996_H
+
+/*
+ * ADM_PHY_PORTS: Number of ports with a PHY.
+ * We only control ports 0 to 3, because if 4 is connected, it is most likely
+ * not connected to the switch but to a separate MII and MAC for the WAN port.
+ */
+#define ADM_PHY_PORTS  4
+#define ADM_NUM_PORTS  6
+#define ADM_CPU_PORT   5
+
+#define ADM_NUM_VLANS 16
+#define ADM_VLAN_MAX_ID 4094
+
+enum admreg {
+       ADM_EEPROM_BASE         = 0x0,
+               ADM_P0_CFG              = ADM_EEPROM_BASE + 1,
+               ADM_P1_CFG              = ADM_EEPROM_BASE + 3,
+               ADM_P2_CFG              = ADM_EEPROM_BASE + 5,
+               ADM_P3_CFG              = ADM_EEPROM_BASE + 7,
+               ADM_P4_CFG              = ADM_EEPROM_BASE + 8,
+               ADM_P5_CFG              = ADM_EEPROM_BASE + 9,
+               ADM_SYSC0               = ADM_EEPROM_BASE + 0xa,
+               ADM_VLAN_PRIOMAP        = ADM_EEPROM_BASE + 0xe,
+               ADM_SYSC3               = ADM_EEPROM_BASE + 0x11,
+               /* Input Force No Tag Enable */
+               ADM_IFNTE               = ADM_EEPROM_BASE + 0x20,
+               ADM_VID_CHECK           = ADM_EEPROM_BASE + 0x26,
+               ADM_P0_PVID             = ADM_EEPROM_BASE + 0x28,
+               ADM_P1_PVID             = ADM_EEPROM_BASE + 0x29,
+               /* Output Tag Bypass Enable and P2 PVID */
+               ADM_OTBE_P2_PVID        = ADM_EEPROM_BASE + 0x2a,
+               ADM_P3_P4_PVID          = ADM_EEPROM_BASE + 0x2b,
+               ADM_P5_PVID             = ADM_EEPROM_BASE + 0x2c,
+       ADM_EEPROM_EXT_BASE     = 0x40,
+#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n))
+#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n))
+#define ADM_VLAN_MAP(n) (ADM_EEPROM_BASE + 0x13 + n)
+       ADM_COUNTER_BASE        = 0xa0,
+               ADM_SIG0                = ADM_COUNTER_BASE + 0,
+               ADM_SIG1                = ADM_COUNTER_BASE + 1,
+               ADM_PS0         = ADM_COUNTER_BASE + 2,
+               ADM_PS1         = ADM_COUNTER_BASE + 3,
+               ADM_PS2         = ADM_COUNTER_BASE + 4,
+               ADM_CL0         = ADM_COUNTER_BASE + 8, /* RxPacket */
+               ADM_CL6         = ADM_COUNTER_BASE + 0x1a, /* RxByte */
+               ADM_CL12                = ADM_COUNTER_BASE + 0x2c, /* TxPacket */
+               ADM_CL18                = ADM_COUNTER_BASE + 0x3e, /* TxByte */
+               ADM_CL24                = ADM_COUNTER_BASE + 0x50, /* Coll */
+               ADM_CL30                = ADM_COUNTER_BASE + 0x62, /* Err */
+#define ADM_OFFSET_PORT(n) ((n * 4) - (n / 4) * 2 - (n / 5) * 2)
+       ADM_PHY_BASE            = 0x200,
+#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
+};
+
+/* Chip identification patterns */
+#define        ADM_SIG0_MASK   0xffff
+#define ADM_SIG0_VAL   0x1023
+#define ADM_SIG1_MASK  0xffff
+#define ADM_SIG1_VAL   0x0007
+
+enum {
+       ADM_PHYCFG_COLTST     = (1 << 7),       /* Enable collision test */
+       ADM_PHYCFG_DPLX       = (1 << 8),       /* Enable full duplex */
+       ADM_PHYCFG_ANEN_RST   = (1 << 9),       /* Restart auto negotiation (self clear) */
+       ADM_PHYCFG_ISO        = (1 << 10),      /* Isolate PHY */
+       ADM_PHYCFG_PDN        = (1 << 11),      /* Power down PHY */
+       ADM_PHYCFG_ANEN       = (1 << 12),      /* Enable auto negotiation */
+       ADM_PHYCFG_SPEED_100  = (1 << 13),      /* Enable 100 Mbit/s */
+       ADM_PHYCFG_LPBK       = (1 << 14),      /* Enable loopback operation */
+       ADM_PHYCFG_RST        = (1 << 15),      /* Reset the port (self clear) */
+       ADM_PHYCFG_INIT = (
+               ADM_PHYCFG_RST |
+               ADM_PHYCFG_SPEED_100 |
+               ADM_PHYCFG_ANEN |
+               ADM_PHYCFG_ANEN_RST
+       )
+};
+
+enum {
+       ADM_PORTCFG_FC        = (1 << 0),       /* Enable 802.x flow control */
+       ADM_PORTCFG_AN        = (1 << 1),       /* Enable auto-negotiation */
+       ADM_PORTCFG_SPEED_100 = (1 << 2),       /* Enable 100 Mbit/s */
+       ADM_PORTCFG_DPLX      = (1 << 3),       /* Enable full duplex */
+       ADM_PORTCFG_OT        = (1 << 4),       /* Output tagged packets */
+       ADM_PORTCFG_PD        = (1 << 5),       /* Port disable */
+       ADM_PORTCFG_TV_PRIO   = (1 << 6),       /* 0 = VLAN based priority
+                                                * 1 = TOS based priority */
+       ADM_PORTCFG_PPE       = (1 << 7),       /* Port based priority enable */
+       ADM_PORTCFG_PP_S      = (1 << 8),       /* Port based priority, 2 bits */
+       ADM_PORTCFG_PVID_BASE = (1 << 10),      /* Primary VLAN id, 4 bits */
+       ADM_PORTCFG_FSE       = (1 << 14),      /* Fx select enable */
+       ADM_PORTCFG_CAM       = (1 << 15),      /* Crossover Auto MDIX */
+
+       ADM_PORTCFG_INIT = (
+               ADM_PORTCFG_FC |
+               ADM_PORTCFG_AN |
+               ADM_PORTCFG_SPEED_100 |
+               ADM_PORTCFG_DPLX |
+               ADM_PORTCFG_CAM
+       ),
+       ADM_PORTCFG_CPU = (
+               ADM_PORTCFG_FC |
+               ADM_PORTCFG_SPEED_100 |
+               ADM_PORTCFG_OT |
+               ADM_PORTCFG_DPLX
+       ),
+};
+
+#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8)
+#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
+#define ADM_PORTCFG_PVID_MASK (0xf << 10)
+
+#define ADM_IFNTE_MASK (0x3f << 9)
+#define ADM_VID_CHECK_MASK (0x3f << 6)
+
+#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8)
+#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P2_PVID_MASK 0xff
+
+#define ADM_OTBE(n) (((n) & 0x3f) << 8)
+#define ADM_OTBE_MASK (0x3f << 8)
+
+/* ADM_SYSC0 */
+enum {
+       ADM_NTTE        = (1 << 2),     /* New Tag Transmit Enable */
+       ADM_RVID1       = (1 << 8)      /* Replace VLAN ID 1 */
+};
+
+/* Tag Based VLAN in ADM_SYSC3 */
+#define ADM_MAC_CLONE  BIT(4)
+#define ADM_TBV                BIT(5)
+
+static const u8 adm_portcfg[] = {
+       [0] = ADM_P0_CFG,
+       [1] = ADM_P1_CFG,
+       [2] = ADM_P2_CFG,
+       [3] = ADM_P3_CFG,
+       [4] = ADM_P4_CFG,
+       [5] = ADM_P5_CFG,
+};
+
+/* Fields in ADM_VLAN_FILT_L(x) */
+#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12)
+#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6)
+#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0)
+#define ADM_VLAN_FILT_MEMBER_MASK 0x3f
+/* Fields in ADM_VLAN_FILT_H(x) */
+#define ADM_VLAN_FILT_VALID (1 << 15)
+#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0)
+
+/* Convert ports to a form for ADM6996L VLAN map */
+#define ADM_VLAN_FILT(ports) ((ports & 0x01) | ((ports & 0x02) << 1) | \
+                       ((ports & 0x04) << 2) | ((ports & 0x08) << 3) | \
+                       ((ports & 0x10) << 3) | ((ports & 0x20) << 3))
+
+/* Port status register */
+enum {
+       ADM_PS_LS = (1 << 0),   /* Link status */
+       ADM_PS_SS = (1 << 1),   /* Speed status */
+       ADM_PS_DS = (1 << 2),   /* Duplex status */
+       ADM_PS_FCS = (1 << 3)   /* Flow control status */
+};
+
+/*
+ * Split the register address in phy id and register
+ * it will get combined again by the mdio bus op
+ */
+#define PHYADDR(_reg)  ((_reg >> 5) & 0xff), (_reg & 0x1f)
+
+#endif
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/ar8216.c b/target/linux/generic/files-3.18/drivers/net/phy/ar8216.c
new file mode 100644 (file)
index 0000000..7512ee1
--- /dev/null
@@ -0,0 +1,2313 @@
+/*
+ * ar8216.c: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/bitops.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/lockdep.h>
+#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/version.h>
+
+#include "ar8216.h"
+
+extern const struct ar8xxx_chip ar8327_chip;
+extern const struct ar8xxx_chip ar8337_chip;
+
+#define AR8XXX_MIB_WORK_DELAY  2000 /* msecs */
+
+#define MIB_DESC(_s , _o, _n)  \
+       {                       \
+               .size = (_s),   \
+               .offset = (_o), \
+               .name = (_n),   \
+       }
+
+static const struct ar8xxx_mib_desc ar8216_mibs[] = {
+       MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"),
+       MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"),
+       MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"),
+       MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"),
+       MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"),
+       MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"),
+       MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"),
+       MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"),
+       MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"),
+       MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"),
+       MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"),
+       MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"),
+       MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"),
+       MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"),
+       MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"),
+       MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"),
+       MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"),
+       MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"),
+       MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"),
+       MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"),
+       MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"),
+       MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"),
+       MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"),
+       MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"),
+       MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"),
+       MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"),
+       MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"),
+       MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"),
+       MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"),
+       MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"),
+       MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"),
+       MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"),
+       MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"),
+       MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"),
+       MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"),
+       MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"),
+       MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"),
+};
+
+const struct ar8xxx_mib_desc ar8236_mibs[39] = {
+       MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"),
+       MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"),
+       MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"),
+       MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"),
+       MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"),
+       MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"),
+       MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"),
+       MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"),
+       MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"),
+       MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"),
+       MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"),
+       MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"),
+       MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"),
+       MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"),
+       MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"),
+       MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"),
+       MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"),
+       MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"),
+       MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"),
+       MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"),
+       MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"),
+       MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"),
+       MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"),
+       MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"),
+       MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"),
+       MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"),
+       MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"),
+       MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"),
+       MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"),
+       MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"),
+       MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"),
+       MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"),
+       MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"),
+       MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"),
+       MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"),
+       MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"),
+       MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"),
+       MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"),
+       MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"),
+};
+
+static DEFINE_MUTEX(ar8xxx_dev_list_lock);
+static LIST_HEAD(ar8xxx_dev_list);
+
+/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
+static int
+ar8xxx_phy_poll_reset(struct mii_bus *bus)
+{
+        unsigned int sleep_msecs = 20;
+        int ret, elapsed, i;
+
+        for (elapsed = sleep_msecs; elapsed <= 600;
+            elapsed += sleep_msecs) {
+                msleep(sleep_msecs);
+                for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+                        ret = mdiobus_read(bus, i, MII_BMCR);
+                        if (ret < 0)
+                               return ret;
+                        if (ret & BMCR_RESET)
+                               break;
+                        if (i == AR8XXX_NUM_PHYS - 1) {
+                                usleep_range(1000, 2000);
+                                return 0;
+                        }
+                }
+        }
+        return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_phy_check_aneg(struct phy_device *phydev)
+{
+       int ret;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return 0;
+       /*
+        * BMCR_ANENABLE might have been cleared
+        * by phy_init_hw in certain kernel versions
+        * therefore check for it
+        */
+       ret = phy_read(phydev, MII_BMCR);
+       if (ret < 0)
+               return ret;
+       if (ret & BMCR_ANENABLE)
+               return 0;
+
+       dev_info(&phydev->mdio.dev, "ANEG disabled, re-enabling ...\n");
+       ret |= BMCR_ANENABLE | BMCR_ANRESTART;
+       return phy_write(phydev, MII_BMCR, ret);
+}
+
+void
+ar8xxx_phy_init(struct ar8xxx_priv *priv)
+{
+       int i;
+       struct mii_bus *bus;
+
+       bus = priv->mii_bus;
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+               if (priv->chip->phy_fixup)
+                       priv->chip->phy_fixup(priv, i);
+
+               /* initialize the port itself */
+               mdiobus_write(bus, i, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+               if (ar8xxx_has_gige(priv))
+                       mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
+               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
+       }
+
+       ar8xxx_phy_poll_reset(bus);
+}
+
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 lo, hi;
+
+       lo = bus->read(bus, phy_id, regnum);
+       hi = bus->read(bus, phy_id, regnum + 1);
+
+       return (hi << 16) | lo;
+}
+
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 lo, hi;
+
+       lo = val & 0xffff;
+       hi = (u16) (val >> 16);
+
+       if (priv->chip->mii_lo_first)
+       {
+               bus->write(bus, phy_id, regnum, lo);
+               bus->write(bus, phy_id, regnum + 1, hi);
+       } else {
+               bus->write(bus, phy_id, regnum + 1, hi);
+               bus->write(bus, phy_id, regnum, lo);
+       }
+}
+
+u32
+ar8xxx_read(struct ar8xxx_priv *priv, int reg)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+       u32 val;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+       val = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
+
+       mutex_unlock(&bus->mdio_lock);
+
+       return val;
+}
+
+void
+ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, val);
+
+       mutex_unlock(&bus->mdio_lock);
+}
+
+u32
+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+       u32 ret;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+
+       ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
+       ret &= ~mask;
+       ret |= val;
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret);
+
+       mutex_unlock(&bus->mdio_lock);
+
+       return ret;
+}
+
+void
+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
+                    u16 dbg_addr, u16 dbg_data)
+{
+       struct mii_bus *bus = priv->mii_bus;
+
+       mutex_lock(&bus->mdio_lock);
+       bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
+       bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
+       mutex_unlock(&bus->mdio_lock);
+}
+
+static inline void
+ar8xxx_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg)
+{
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, reg);
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
+}
+
+void
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data)
+{
+       struct mii_bus *bus = priv->mii_bus;
+
+       mutex_lock(&bus->mdio_lock);
+       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
+       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data);
+       mutex_unlock(&bus->mdio_lock);
+}
+
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 data;
+
+       mutex_lock(&bus->mdio_lock);
+       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
+       data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
+       mutex_unlock(&bus->mdio_lock);
+
+       return data;
+}
+
+static int
+ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
+               unsigned timeout)
+{
+       int i;
+
+       for (i = 0; i < timeout; i++) {
+               u32 t;
+
+               t = ar8xxx_read(priv, reg);
+               if ((t & mask) == val)
+                       return 0;
+
+               usleep_range(1000, 2000);
+               cond_resched();
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
+{
+       unsigned mib_func = priv->chip->mib_func;
+       int ret;
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       /* Capture the hardware statistics for all ports */
+       ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S));
+
+       /* Wait for the capturing to complete. */
+       ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+out:
+       return ret;
+}
+
+static int
+ar8xxx_mib_capture(struct ar8xxx_priv *priv)
+{
+       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE);
+}
+
+static int
+ar8xxx_mib_flush(struct ar8xxx_priv *priv)
+{
+       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH);
+}
+
+static void
+ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush)
+{
+       unsigned int base;
+       u64 *mib_stats;
+       int i;
+
+       WARN_ON(port >= priv->dev.ports);
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       base = priv->chip->reg_port_stats_start +
+              priv->chip->reg_port_stats_length * port;
+
+       mib_stats = &priv->mib_stats[port * priv->chip->num_mibs];
+       for (i = 0; i < priv->chip->num_mibs; i++) {
+               const struct ar8xxx_mib_desc *mib;
+               u64 t;
+
+               mib = &priv->chip->mib_decs[i];
+               t = ar8xxx_read(priv, base + mib->offset);
+               if (mib->size == 2) {
+                       u64 hi;
+
+                       hi = ar8xxx_read(priv, base + mib->offset + 4);
+                       t |= hi << 32;
+               }
+
+               if (flush)
+                       mib_stats[i] = 0;
+               else
+                       mib_stats[i] += t;
+               cond_resched();
+       }
+}
+
+static void
+ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
+                     struct switch_port_link *link)
+{
+       u32 status;
+       u32 speed;
+
+       memset(link, '\0', sizeof(*link));
+
+       status = priv->chip->read_port_status(priv, port);
+
+       link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO);
+       if (link->aneg) {
+               link->link = !!(status & AR8216_PORT_STATUS_LINK_UP);
+       } else {
+               link->link = true;
+
+               if (priv->get_port_link) {
+                       int err;
+
+                       err = priv->get_port_link(port);
+                       if (err >= 0)
+                               link->link = !!err;
+               }
+       }
+
+       if (!link->link)
+               return;
+
+       link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX);
+       link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
+       link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
+
+       if (link->aneg && link->duplex && priv->chip->read_port_eee_status)
+               link->eee = priv->chip->read_port_eee_status(priv, port);
+
+       speed = (status & AR8216_PORT_STATUS_SPEED) >>
+                AR8216_PORT_STATUS_SPEED_S;
+
+       switch (speed) {
+       case AR8216_PORT_SPEED_10M:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case AR8216_PORT_SPEED_100M:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case AR8216_PORT_SPEED_1000M:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+}
+
+static struct sk_buff *
+ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ar8xxx_priv *priv = dev->phy_ptr;
+       unsigned char *buf;
+
+       if (unlikely(!priv))
+               goto error;
+
+       if (!priv->vlan)
+               goto send;
+
+       if (unlikely(skb_headroom(skb) < 2)) {
+               if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0)
+                       goto error;
+       }
+
+       buf = skb_push(skb, 2);
+       buf[0] = 0x10;
+       buf[1] = 0x80;
+
+send:
+       return skb;
+
+error:
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static void
+ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ar8xxx_priv *priv;
+       unsigned char *buf;
+       int port, vlan;
+
+       priv = dev->phy_ptr;
+       if (!priv)
+               return;
+
+       /* don't strip the header if vlan mode is disabled */
+       if (!priv->vlan)
+               return;
+
+       /* strip header, get vlan id */
+       buf = skb->data;
+       skb_pull(skb, 2);
+
+       /* check for vlan header presence */
+       if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00))
+               return;
+
+       port = buf[0] & 0x7;
+
+       /* no need to fix up packets coming from a tagged source */
+       if (priv->vlan_tagged & (1 << port))
+               return;
+
+       /* lookup port vid from local table, the switch passes an invalid vlan id */
+       vlan = priv->vlan_id[priv->pvid[port]];
+
+       buf[14 + 2] &= 0xf0;
+       buf[14 + 2] |= vlan >> 8;
+       buf[15 + 2] = vlan & 0xff;
+}
+
+int
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
+{
+       int timeout = 20;
+       u32 t = 0;
+
+       while (1) {
+               t = ar8xxx_read(priv, reg);
+               if ((t & mask) == val)
+                       return 0;
+
+               if (timeout-- <= 0)
+                       break;
+
+               udelay(10);
+               cond_resched();
+       }
+
+       pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n",
+              (unsigned int) reg, t, mask, val);
+       return -ETIMEDOUT;
+}
+
+static void
+ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
+{
+       if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0))
+               return;
+       if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) {
+               val &= AR8216_VTUDATA_MEMBER;
+               val |= AR8216_VTUDATA_VALID;
+               ar8xxx_write(priv, AR8216_REG_VTU_DATA, val);
+       }
+       op |= AR8216_VTU_ACTIVE;
+       ar8xxx_write(priv, AR8216_REG_VTU, op);
+}
+
+static void
+ar8216_vtu_flush(struct ar8xxx_priv *priv)
+{
+       ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
+}
+
+static void
+ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
+{
+       u32 op;
+
+       op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S);
+       ar8216_vtu_op(priv, op, port_mask);
+}
+
+static int
+ar8216_atu_flush(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret)
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH |
+                                                        AR8216_ATU_ACTIVE);
+
+       return ret;
+}
+
+static int
+ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret) {
+               t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT;
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t);
+       }
+
+       return ret;
+}
+
+static u32
+ar8216_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+       return ar8xxx_read(priv, AR8216_REG_PORT_STATUS(port));
+}
+
+static void
+ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 header;
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU)
+               header = AR8216_PORT_CTRL_HEADER;
+       else
+               header = 0;
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
+                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
+                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
+                  AR8216_PORT_CTRL_LEARN | header |
+                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
+                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port),
+                  AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE |
+                  AR8216_PORT_VLAN_DEFAULT_ID,
+                  (members << AR8216_PORT_VLAN_DEST_PORTS_S) |
+                  (ingress << AR8216_PORT_VLAN_MODE_S) |
+                  (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S));
+}
+
+static int
+ar8216_hw_init(struct ar8xxx_priv *priv)
+{
+       if (priv->initialized)
+               return 0;
+
+       ar8xxx_phy_init(priv);
+
+       priv->initialized = true;
+       return 0;
+}
+
+static void
+ar8216_init_globals(struct ar8xxx_priv *priv)
+{
+       /* standard atheros magic */
+       ar8xxx_write(priv, 0x38, 0xc000050e);
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8216_GCTRL_MTU, 1518 + 8 + 2);
+}
+
+static void
+ar8216_init_port(struct ar8xxx_priv *priv, int port)
+{
+       /* Enable port learning and tx */
+       ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port),
+               AR8216_PORT_CTRL_LEARN |
+               (4 << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_write(priv, AR8216_REG_PORT_VLAN(port), 0);
+
+       if (port == AR8216_PORT_CPU) {
+               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
+                       AR8216_PORT_STATUS_LINK_UP |
+                       (ar8xxx_has_gige(priv) ?
+                                AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) |
+                       AR8216_PORT_STATUS_TXMAC |
+                       AR8216_PORT_STATUS_RXMAC |
+                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) |
+                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) |
+                       AR8216_PORT_STATUS_DUPLEX);
+       } else {
+               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
+                       AR8216_PORT_STATUS_LINK_AUTO);
+       }
+}
+
+static void
+ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) {
+               udelay(10);
+               cond_resched();
+       }
+
+       if (!timeout)
+               pr_err("ar8216: timeout waiting for atu to become ready\n");
+}
+
+static void ar8216_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_func0, r1_func1, r1_func2;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e;
+       r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT);
+               ar8xxx_mii_write32(priv, r2, r1_func1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_func2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               t = ar8xxx_mii_read32(priv, r2, r1_func0);
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_mii_write32(priv, r2, r1_func0, t);
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_func0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_func1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_func2);
+
+               *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8216_ATU_PORT0;
+               while (!(val2 & t) && ++i < priv->dev.ports)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S;
+               a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S;
+               a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S;
+               a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S;
+               a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S;
+               a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S;
+               break;
+       }
+}
+
+static void
+ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
+                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
+                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
+                  AR8216_PORT_CTRL_LEARN |
+                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
+                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port),
+                  AR8236_PORT_VLAN_DEFAULT_ID,
+                  (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S));
+
+       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port),
+                  AR8236_PORT_VLAN2_VLAN_MODE |
+                  AR8236_PORT_VLAN2_MEMBER,
+                  (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) |
+                  (members << AR8236_PORT_VLAN2_MEMBER_S));
+}
+
+static void
+ar8236_init_globals(struct ar8xxx_priv *priv)
+{
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
+
+       /* enable cpu port to receive arp frames */
+       ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL,
+                  AR8236_ATU_CTRL_RES);
+
+       /* enable cpu port to receive multicast and broadcast frames */
+       ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK,
+                  AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN);
+
+       /* Enable MIB counters */
+       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
+                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
+                  AR8236_MIB_EN);
+}
+
+static int
+ar8316_hw_init(struct ar8xxx_priv *priv)
+{
+       u32 val, newval;
+
+       val = ar8xxx_read(priv, AR8316_REG_POSTRIP);
+
+       if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
+               if (priv->port4_phy) {
+                       /* value taken from Ubiquiti RouterStation Pro */
+                       newval = 0x81461bea;
+                       pr_info("ar8316: Using port 4 as PHY\n");
+               } else {
+                       newval = 0x01261be2;
+                       pr_info("ar8316: Using port 4 as switch port\n");
+               }
+       } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) {
+               /* value taken from AVM Fritz!Box 7390 sources */
+               newval = 0x010e5b71;
+       } else {
+               /* no known value for phy interface */
+               pr_err("ar8316: unsupported mii mode: %d.\n",
+                      priv->phy->interface);
+               return -EINVAL;
+       }
+
+       if (val == newval)
+               goto out;
+
+       ar8xxx_write(priv, AR8316_REG_POSTRIP, newval);
+
+       if (priv->port4_phy &&
+           priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
+               /* work around for phy4 rgmii mode */
+               ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c);
+               /* rx delay */
+               ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e);
+               /* tx delay */
+               ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47);
+               msleep(1000);
+       }
+
+       ar8xxx_phy_init(priv);
+
+out:
+       priv->initialized = true;
+       return 0;
+}
+
+static void
+ar8316_init_globals(struct ar8xxx_priv *priv)
+{
+       /* standard atheros magic */
+       ar8xxx_write(priv, 0x38, 0xc000050e);
+
+       /* enable cpu port to receive multicast and broadcast frames */
+       ar8xxx_write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f);
+
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
+
+       /* Enable MIB counters */
+       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
+                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
+                  AR8236_MIB_EN);
+}
+
+int
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       priv->vlan = !!val->value.i;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->vlan;
+       return 0;
+}
+
+
+int
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       /* make sure no invalid PVIDs get set */
+
+       if (vlan < 0 || vlan >= dev->vlans ||
+           port < 0 || port >= AR8X16_MAX_PORTS)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       if (port < 0 || port >= AR8X16_MAX_PORTS)
+               return -EINVAL;
+
+       *vlan = priv->pvid[port];
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       if (val->port_vlan >= AR8X16_MAX_VLANS)
+               return -EINVAL;
+
+       priv->vlan_id[val->port_vlan] = val->value.i;
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->vlan_id[val->port_vlan];
+       return 0;
+}
+
+int
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       ar8216_read_port_link(priv, port, link);
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports;
+       int i;
+
+       if (val->port_vlan >= AR8X16_MAX_VLANS)
+               return -EINVAL;
+
+       ports = priv->vlan_table[val->port_vlan];
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (priv->vlan_tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i, j;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       priv->vlan_tagged |= (1 << p->id);
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+
+                       /* make sure that an untagged port does not
+                        * appear in other vlans */
+                       for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+                               if (j == val->port_vlan)
+                                       continue;
+                               priv->vlan_table[j] &= ~(1 << p->id);
+                       }
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static void
+ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+       for (port = 0; port < AR8216_NUM_PORTS; port++) {
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8216_NUM_PORTS ||
+           priv->monitor_port >= AR8216_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+       if (priv->mirror_tx)
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
+}
+
+static inline u32
+ar8xxx_age_time_val(int age_time)
+{
+       return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) /
+              AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS;
+}
+
+static inline void
+ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg)
+{
+       u32 age_time = ar8xxx_age_time_val(priv->arl_age_time);
+       ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S);
+}
+
+int
+ar8xxx_sw_hw_apply(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       u8 portmask[AR8X16_MAX_PORTS];
+       int i, j;
+
+       mutex_lock(&priv->reg_mutex);
+       /* flush all vlan translation unit entries */
+       priv->chip->vtu_flush(priv);
+
+       memset(portmask, 0, sizeof(portmask));
+       if (!priv->init) {
+               /* calculate the port destination masks and load vlans
+                * into the vlan translation unit */
+               for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+                       u8 vp = priv->vlan_table[j];
+
+                       if (!vp)
+                               continue;
+
+                       for (i = 0; i < dev->ports; i++) {
+                               u8 mask = (1 << i);
+                               if (vp & mask)
+                                       portmask[i] |= vp & ~mask;
+                       }
+
+                       chip->vtu_load_vlan(priv, priv->vlan_id[j],
+                                           priv->vlan_table[j]);
+               }
+       } else {
+               /* vlan disabled:
+                * isolate all ports, but connect them to the cpu port */
+               for (i = 0; i < dev->ports; i++) {
+                       if (i == AR8216_PORT_CPU)
+                               continue;
+
+                       portmask[i] = 1 << AR8216_PORT_CPU;
+                       portmask[AR8216_PORT_CPU] |= (1 << i);
+               }
+       }
+
+       /* update the port destination mask registers and tag settings */
+       for (i = 0; i < dev->ports; i++) {
+               chip->setup_port(priv, i, portmask[i]);
+       }
+
+       chip->set_mirror_regs(priv);
+
+       /* set age time */
+       if (chip->reg_arl_ctrl)
+               ar8xxx_set_age_time(priv, chip->reg_arl_ctrl);
+
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+int
+ar8xxx_sw_reset_switch(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+       memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) -
+               offsetof(struct ar8xxx_priv, vlan));
+
+       for (i = 0; i < AR8X16_MAX_VLANS; i++)
+               priv->vlan_id[i] = i;
+
+       /* Configure all ports */
+       for (i = 0; i < dev->ports; i++)
+               chip->init_port(priv, i);
+
+       priv->mirror_rx = false;
+       priv->mirror_tx = false;
+       priv->source_port = 0;
+       priv->monitor_port = 0;
+       priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME;
+
+       chip->init_globals(priv);
+       chip->atu_flush(priv);
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return chip->sw_hw_apply(dev);
+}
+
+int
+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       unsigned int len;
+       int ret;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->mib_lock);
+
+       len = priv->dev.ports * priv->chip->num_mibs *
+             sizeof(*priv->mib_stats);
+       memset(priv->mib_stats, '\0', len);
+       ret = ar8xxx_mib_flush(priv);
+       if (ret)
+               goto unlock;
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_rx = !!val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_rx;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_tx = !!val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_tx;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->monitor_port = val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->monitor_port;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->source_port = val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->source_port;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port;
+       int ret;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+       ret = ar8xxx_mib_capture(priv);
+       if (ret)
+               goto unlock;
+
+       ar8xxx_mib_fetch_port_stat(priv, port, true);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+static void
+ar8xxx_byte_to_str(char *buf, int len, u64 byte)
+{
+       unsigned long b;
+       const char *unit;
+
+       if (byte >= 0x40000000) { /* 1 GiB */
+               b = byte * 10 / 0x40000000;
+               unit = "GiB";
+       } else if (byte >= 0x100000) { /* 1 MiB */
+               b = byte * 10 / 0x100000;
+               unit = "MiB";
+       } else if (byte >= 0x400) { /* 1 KiB */
+               b = byte * 10 / 0x400;
+               unit = "KiB";
+       } else {
+               b = byte;
+               unit = "Byte";
+       }
+       if (strcmp(unit, "Byte"))
+               snprintf(buf, len, "%lu.%lu %s", b / 10, b % 10, unit);
+       else
+               snprintf(buf, len, "%lu %s", b, unit);
+}
+
+int
+ar8xxx_sw_get_port_mib(struct switch_dev *dev,
+                      const struct switch_attr *attr,
+                      struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       u64 *mib_stats, mib_data;
+       unsigned int port;
+       int ret;
+       char *buf = priv->buf;
+       char buf1[64];
+       const char *mib_name;
+       int i, len = 0;
+       bool mib_stats_empty = true;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+       ret = ar8xxx_mib_capture(priv);
+       if (ret)
+               goto unlock;
+
+       ar8xxx_mib_fetch_port_stat(priv, port, false);
+
+       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                       "MIB counters\n");
+
+       mib_stats = &priv->mib_stats[port * chip->num_mibs];
+       for (i = 0; i < chip->num_mibs; i++) {
+               mib_name = chip->mib_decs[i].name;
+               mib_data = mib_stats[i];
+               len += snprintf(buf + len, sizeof(priv->buf) - len,
+                               "%-12s: %llu\n", mib_name, mib_data);
+               if ((!strcmp(mib_name, "TxByte") ||
+                   !strcmp(mib_name, "RxGoodByte")) &&
+                   mib_data >= 1024) {
+                       ar8xxx_byte_to_str(buf1, sizeof(buf1), mib_data);
+                       --len; /* discard newline at the end of buf */
+                       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                                       " (%s)\n", buf1);
+               }
+               if (mib_stats_empty && mib_data)
+                       mib_stats_empty = false;
+       }
+
+       if (mib_stats_empty)
+               len = snprintf(buf, sizeof(priv->buf), "No MIB data");
+
+       val->value.s = buf;
+       val->len = len;
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+                          struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int age_time = val->value.i;
+       u32 age_time_val;
+
+       if (age_time < 0)
+               return -EINVAL;
+
+       age_time_val = ar8xxx_age_time_val(age_time);
+       if (age_time_val == 0 || age_time_val > 0xffff)
+               return -EINVAL;
+
+       priv->arl_age_time = age_time;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+                   struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->arl_age_time;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct mii_bus *bus = priv->mii_bus;
+       const struct ar8xxx_chip *chip = priv->chip;
+       char *buf = priv->arl_buf;
+       int i, j, k, len = 0;
+       struct arl_entry *a, *a1;
+       u32 status;
+
+       if (!chip->get_arl_entry)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->reg_mutex);
+       mutex_lock(&bus->mdio_lock);
+
+       chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE);
+
+       for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) {
+               a = &priv->arl_table[i];
+               duplicate:
+               chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT);
+
+               if (!status)
+                       break;
+
+               /* avoid duplicates
+                * ARL table can include multiple valid entries
+                * per MAC, just with differing status codes
+                */
+               for (j = 0; j < i; ++j) {
+                       a1 = &priv->arl_table[j];
+                       if (a->port == a1->port && !memcmp(a->mac, a1->mac, sizeof(a->mac)))
+                               goto duplicate;
+               }
+       }
+
+       mutex_unlock(&bus->mdio_lock);
+
+       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                        "address resolution table\n");
+
+       if (i == AR8XXX_NUM_ARL_RECORDS)
+               len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                               "Too many entries found, displaying the first %d only!\n",
+                               AR8XXX_NUM_ARL_RECORDS);
+
+       for (j = 0; j < priv->dev.ports; ++j) {
+               for (k = 0; k < i; ++k) {
+                       a = &priv->arl_table[k];
+                       if (a->port != j)
+                               continue;
+                       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                                       "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                       j,
+                                       a->mac[5], a->mac[4], a->mac[3],
+                                       a->mac[2], a->mac[1], a->mac[0]);
+               }
+       }
+
+       val->value.s = buf;
+       val->len = len;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int ret;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port, ret;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush_port(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+static const struct switch_attr ar8xxx_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
+};
+
+const struct switch_attr ar8xxx_sw_attr_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = ar8xxx_sw_set_port_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .set = NULL,
+               .get = ar8xxx_sw_get_port_mib,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
+};
+
+const struct switch_attr ar8xxx_sw_attr_vlan[1] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "vid",
+               .description = "VLAN ID (0-4094)",
+               .set = ar8xxx_sw_set_vid,
+               .get = ar8xxx_sw_get_vid,
+               .max = 4094,
+       },
+};
+
+static const struct switch_dev_ops ar8xxx_sw_ops = {
+       .attr_global = {
+               .attr = ar8xxx_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8xxx_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8xxx_sw_get_ports,
+       .set_vlan_ports = ar8xxx_sw_set_ports,
+       .apply_config = ar8xxx_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+/* The following op is disabled as it hogs the CPU and degrades performance.
+   An implementation has been attempted in 4d8a66d but reading MIB data is slow
+   on ar8xxx switches.
+
+   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
+   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
+   the request to update the MIB counter is cleared. */
+#if 0
+       .get_port_stats = ar8xxx_sw_get_port_stats,
+#endif
+};
+
+static const struct ar8xxx_chip ar8216_chip = {
+       .caps = AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x19000,
+       .reg_port_stats_length = 0xa0,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8216",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8216_NUM_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8216_hw_init,
+       .init_globals = ar8216_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8216_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8216_mibs),
+       .mib_decs = ar8216_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static const struct ar8xxx_chip ar8236_chip = {
+       .caps = AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x20000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8236",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8216_NUM_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8216_hw_init,
+       .init_globals = ar8236_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8236_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static const struct ar8xxx_chip ar8316_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x20000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8316",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8316_hw_init,
+       .init_globals = ar8316_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8216_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static int
+ar8xxx_id_chip(struct ar8xxx_priv *priv)
+{
+       u32 val;
+       u16 id;
+       int i;
+
+       val = ar8xxx_read(priv, AR8216_REG_CTRL);
+       if (val == ~0)
+               return -ENODEV;
+
+       id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
+       for (i = 0; i < AR8X16_PROBE_RETRIES; i++) {
+               u16 t;
+
+               val = ar8xxx_read(priv, AR8216_REG_CTRL);
+               if (val == ~0)
+                       return -ENODEV;
+
+               t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
+               if (t != id)
+                       return -ENODEV;
+       }
+
+       priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S;
+       priv->chip_rev = (id & AR8216_CTRL_REVISION);
+
+       switch (priv->chip_ver) {
+       case AR8XXX_VER_AR8216:
+               priv->chip = &ar8216_chip;
+               break;
+       case AR8XXX_VER_AR8236:
+               priv->chip = &ar8236_chip;
+               break;
+       case AR8XXX_VER_AR8316:
+               priv->chip = &ar8316_chip;
+               break;
+       case AR8XXX_VER_AR8327:
+               priv->chip = &ar8327_chip;
+               break;
+       case AR8XXX_VER_AR8337:
+               priv->chip = &ar8337_chip;
+               break;
+       default:
+               pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n",
+                      priv->chip_ver, priv->chip_rev);
+
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void
+ar8xxx_mib_work_func(struct work_struct *work)
+{
+       struct ar8xxx_priv *priv;
+       int err;
+
+       priv = container_of(work, struct ar8xxx_priv, mib_work.work);
+
+       mutex_lock(&priv->mib_lock);
+
+       err = ar8xxx_mib_capture(priv);
+       if (err)
+               goto next_port;
+
+       ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false);
+
+next_port:
+       priv->mib_next_port++;
+       if (priv->mib_next_port >= priv->dev.ports)
+               priv->mib_next_port = 0;
+
+       mutex_unlock(&priv->mib_lock);
+       schedule_delayed_work(&priv->mib_work,
+                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
+}
+
+static int
+ar8xxx_mib_init(struct ar8xxx_priv *priv)
+{
+       unsigned int len;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return 0;
+
+       BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs);
+
+       len = priv->dev.ports * priv->chip->num_mibs *
+             sizeof(*priv->mib_stats);
+       priv->mib_stats = kzalloc(len, GFP_KERNEL);
+
+       if (!priv->mib_stats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void
+ar8xxx_mib_start(struct ar8xxx_priv *priv)
+{
+       if (!ar8xxx_has_mib_counters(priv))
+               return;
+
+       schedule_delayed_work(&priv->mib_work,
+                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
+}
+
+static void
+ar8xxx_mib_stop(struct ar8xxx_priv *priv)
+{
+       if (!ar8xxx_has_mib_counters(priv))
+               return;
+
+       cancel_delayed_work_sync(&priv->mib_work);
+}
+
+static struct ar8xxx_priv *
+ar8xxx_create(void)
+{
+       struct ar8xxx_priv *priv;
+
+       priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+       INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func);
+
+       return priv;
+}
+
+static void
+ar8xxx_free(struct ar8xxx_priv *priv)
+{
+       if (priv->chip && priv->chip->cleanup)
+               priv->chip->cleanup(priv);
+
+       kfree(priv->chip_data);
+       kfree(priv->mib_stats);
+       kfree(priv);
+}
+
+static int
+ar8xxx_probe_switch(struct ar8xxx_priv *priv)
+{
+       const struct ar8xxx_chip *chip;
+       struct switch_dev *swdev;
+       int ret;
+
+       ret = ar8xxx_id_chip(priv);
+       if (ret)
+               return ret;
+
+       chip = priv->chip;
+
+       swdev = &priv->dev;
+       swdev->cpu_port = AR8216_PORT_CPU;
+       swdev->name = chip->name;
+       swdev->vlans = chip->vlans;
+       swdev->ports = chip->ports;
+       swdev->ops = chip->swops;
+
+       ret = ar8xxx_mib_init(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int
+ar8xxx_start(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->init = true;
+
+       ret = priv->chip->hw_init(priv);
+       if (ret)
+               return ret;
+
+       ret = ar8xxx_sw_reset_switch(&priv->dev);
+       if (ret)
+               return ret;
+
+       priv->init = false;
+
+       ar8xxx_mib_start(priv);
+
+       return 0;
+}
+
+static int
+ar8xxx_phy_config_init(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+       struct net_device *dev = phydev->attached_dev;
+       int ret;
+
+       if (WARN_ON(!priv))
+               return -ENODEV;
+
+       if (priv->chip->config_at_probe)
+               return ar8xxx_phy_check_aneg(phydev);
+
+       priv->phy = phydev;
+
+       if (phydev->mdio.addr != 0) {
+               if (chip_is_ar8316(priv)) {
+                       /* switch device has been initialized, reinit */
+                       priv->dev.ports = (AR8216_NUM_PORTS - 1);
+                       priv->initialized = false;
+                       priv->port4_phy = true;
+                       ar8316_hw_init(priv);
+                       return 0;
+               }
+
+               return 0;
+       }
+
+       ret = ar8xxx_start(priv);
+       if (ret)
+               return ret;
+
+       /* VID fixup only needed on ar8216 */
+       if (chip_is_ar8216(priv)) {
+               dev->phy_ptr = priv;
+               dev->priv_flags |= IFF_NO_IP_ALIGN;
+               dev->eth_mangle_rx = ar8216_mangle_rx;
+               dev->eth_mangle_tx = ar8216_mangle_tx;
+       }
+
+       return 0;
+}
+
+static bool
+ar8xxx_check_link_states(struct ar8xxx_priv *priv)
+{
+       bool link_new, changed = false;
+       u32 status;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       for (i = 0; i < priv->dev.ports; i++) {
+               status = priv->chip->read_port_status(priv, i);
+               link_new = !!(status & AR8216_PORT_STATUS_LINK_UP);
+               if (link_new == priv->link_up[i])
+                       continue;
+
+               priv->link_up[i] = link_new;
+               changed = true;
+               /* flush ARL entries for this port if it went down*/
+               if (!link_new)
+                       priv->chip->atu_flush_port(priv, i);
+               dev_info(&priv->phy->mdio.dev, "Port %d is %s\n",
+                        i, link_new ? "up" : "down");
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return changed;
+}
+
+static int
+ar8xxx_phy_read_status(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+       struct switch_port_link link;
+
+       /* check for switch port link changes */
+       if (phydev->state == PHY_CHANGELINK)
+               ar8xxx_check_link_states(priv);
+
+       if (phydev->mdio.addr != 0)
+               return genphy_read_status(phydev);
+
+       ar8216_read_port_link(priv, phydev->mdio.addr, &link);
+       phydev->link = !!link.link;
+       if (!phydev->link)
+               return 0;
+
+       switch (link.speed) {
+       case SWITCH_PORT_SPEED_10:
+               phydev->speed = SPEED_10;
+               break;
+       case SWITCH_PORT_SPEED_100:
+               phydev->speed = SPEED_100;
+               break;
+       case SWITCH_PORT_SPEED_1000:
+               phydev->speed = SPEED_1000;
+               break;
+       default:
+               phydev->speed = 0;
+       }
+       phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+static int
+ar8xxx_phy_config_aneg(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 0)
+               return 0;
+
+       return genphy_config_aneg(phydev);
+}
+
+static const u32 ar8xxx_phy_ids[] = {
+       0x004dd033,
+       0x004dd034, /* AR8327 */
+       0x004dd036, /* AR8337 */
+       0x004dd041,
+       0x004dd042,
+       0x004dd043, /* AR8236 */
+};
+
+static bool
+ar8xxx_phy_match(u32 phy_id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++)
+               if (phy_id == ar8xxx_phy_ids[i])
+                       return true;
+
+       return false;
+}
+
+static bool
+ar8xxx_is_possible(struct mii_bus *bus)
+{
+       unsigned int i, found_phys = 0;
+
+       for (i = 0; i < 5; i++) {
+               u32 phy_id;
+
+               phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16;
+               phy_id |= mdiobus_read(bus, i, MII_PHYSID2);
+               if (ar8xxx_phy_match(phy_id)) {
+                       found_phys++;
+               } else if (phy_id) {
+                       pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n",
+                                dev_name(&bus->dev), i, phy_id);
+               }
+       }
+       return !!found_phys;
+}
+
+static int
+ar8xxx_phy_probe(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv;
+       struct switch_dev *swdev;
+       int ret;
+
+       /* skip PHYs at unused adresses */
+       if (phydev->mdio.addr != 0 && phydev->mdio.addr != 4)
+               return -ENODEV;
+
+       if (!ar8xxx_is_possible(phydev->mdio.bus))
+               return -ENODEV;
+
+       mutex_lock(&ar8xxx_dev_list_lock);
+       list_for_each_entry(priv, &ar8xxx_dev_list, list)
+               if (priv->mii_bus == phydev->mdio.bus)
+                       goto found;
+
+       priv = ar8xxx_create();
+       if (priv == NULL) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+
+       priv->mii_bus = phydev->mdio.bus;
+
+       ret = ar8xxx_probe_switch(priv);
+       if (ret)
+               goto free_priv;
+
+       swdev = &priv->dev;
+       swdev->alias = dev_name(&priv->mii_bus->dev);
+       ret = register_switch(swdev, NULL);
+       if (ret)
+               goto free_priv;
+
+       pr_info("%s: %s rev. %u switch registered on %s\n",
+               swdev->devname, swdev->name, priv->chip_rev,
+               dev_name(&priv->mii_bus->dev));
+
+       list_add(&priv->list, &ar8xxx_dev_list);
+
+found:
+       priv->use_count++;
+
+       if (phydev->mdio.addr == 0) {
+               if (ar8xxx_has_gige(priv)) {
+                       phydev->supported = SUPPORTED_1000baseT_Full;
+                       phydev->advertising = ADVERTISED_1000baseT_Full;
+               } else {
+                       phydev->supported = SUPPORTED_100baseT_Full;
+                       phydev->advertising = ADVERTISED_100baseT_Full;
+               }
+
+               if (priv->chip->config_at_probe) {
+                       priv->phy = phydev;
+
+                       ret = ar8xxx_start(priv);
+                       if (ret)
+                               goto err_unregister_switch;
+               }
+       } else {
+               if (ar8xxx_has_gige(priv)) {
+                       phydev->supported |= SUPPORTED_1000baseT_Full;
+                       phydev->advertising |= ADVERTISED_1000baseT_Full;
+               }
+       }
+
+       phydev->priv = priv;
+
+       mutex_unlock(&ar8xxx_dev_list_lock);
+
+       return 0;
+
+err_unregister_switch:
+       if (--priv->use_count)
+               goto unlock;
+
+       unregister_switch(&priv->dev);
+
+free_priv:
+       ar8xxx_free(priv);
+unlock:
+       mutex_unlock(&ar8xxx_dev_list_lock);
+       return ret;
+}
+
+static void
+ar8xxx_phy_detach(struct phy_device *phydev)
+{
+       struct net_device *dev = phydev->attached_dev;
+
+       if (!dev)
+               return;
+
+       dev->phy_ptr = NULL;
+       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+       dev->eth_mangle_rx = NULL;
+       dev->eth_mangle_tx = NULL;
+}
+
+static void
+ar8xxx_phy_remove(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+
+       if (WARN_ON(!priv))
+               return;
+
+       phydev->priv = NULL;
+
+       mutex_lock(&ar8xxx_dev_list_lock);
+
+       if (--priv->use_count > 0) {
+               mutex_unlock(&ar8xxx_dev_list_lock);
+               return;
+       }
+
+       list_del(&priv->list);
+       mutex_unlock(&ar8xxx_dev_list_lock);
+
+       unregister_switch(&priv->dev);
+       ar8xxx_mib_stop(priv);
+       ar8xxx_free(priv);
+}
+
+static int
+ar8xxx_phy_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+
+static struct phy_driver ar8xxx_phy_driver[] = {
+       {
+               .phy_id         = 0x004d0000,
+               .name           = "Atheros AR8216/AR8236/AR8316",
+               .phy_id_mask    = 0xffff0000,
+               .features       = PHY_BASIC_FEATURES,
+               .probe          = ar8xxx_phy_probe,
+               .remove         = ar8xxx_phy_remove,
+               .detach         = ar8xxx_phy_detach,
+               .config_init    = ar8xxx_phy_config_init,
+               .config_aneg    = ar8xxx_phy_config_aneg,
+               .read_status    = ar8xxx_phy_read_status,
+               .soft_reset     = ar8xxx_phy_soft_reset,
+       }
+};
+
+module_phy_driver(ar8xxx_phy_driver);
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/ar8216.h b/target/linux/generic/files-3.18/drivers/net/phy/ar8216.h
new file mode 100644 (file)
index 0000000..ba0e0dd
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * ar8216.h: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AR8216_H
+#define __AR8216_H
+
+#define BITS(_s, _n)   (((1UL << (_n)) - 1) << _s)
+
+#define AR8XXX_CAP_GIGE                        BIT(0)
+#define AR8XXX_CAP_MIB_COUNTERS                BIT(1)
+
+#define AR8XXX_NUM_PHYS        5
+#define AR8216_PORT_CPU        0
+#define AR8216_NUM_PORTS       6
+#define AR8216_NUM_VLANS       16
+#define AR8316_NUM_VLANS       4096
+
+/* size of the vlan table */
+#define AR8X16_MAX_VLANS       128
+#define AR8X16_PROBE_RETRIES   10
+#define AR8X16_MAX_PORTS       8
+
+#define AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS      7
+#define AR8XXX_DEFAULT_ARL_AGE_TIME            300
+
+/* Atheros specific MII registers */
+#define MII_ATH_MMD_ADDR               0x0d
+#define MII_ATH_MMD_DATA               0x0e
+#define MII_ATH_DBG_ADDR               0x1d
+#define MII_ATH_DBG_DATA               0x1e
+
+#define AR8216_REG_CTRL                        0x0000
+#define   AR8216_CTRL_REVISION         BITS(0, 8)
+#define   AR8216_CTRL_REVISION_S       0
+#define   AR8216_CTRL_VERSION          BITS(8, 8)
+#define   AR8216_CTRL_VERSION_S                8
+#define   AR8216_CTRL_RESET            BIT(31)
+
+#define AR8216_REG_FLOOD_MASK          0x002C
+#define   AR8216_FM_UNI_DEST_PORTS     BITS(0, 6)
+#define   AR8216_FM_MULTI_DEST_PORTS   BITS(16, 6)
+#define   AR8236_FM_CPU_BROADCAST_EN   BIT(26)
+#define   AR8236_FM_CPU_BCAST_FWD_EN   BIT(25)
+
+#define AR8216_REG_GLOBAL_CTRL         0x0030
+#define   AR8216_GCTRL_MTU             BITS(0, 11)
+#define   AR8236_GCTRL_MTU             BITS(0, 14)
+#define   AR8316_GCTRL_MTU             BITS(0, 14)
+
+#define AR8216_REG_VTU                 0x0040
+#define   AR8216_VTU_OP                        BITS(0, 3)
+#define   AR8216_VTU_OP_NOOP           0x0
+#define   AR8216_VTU_OP_FLUSH          0x1
+#define   AR8216_VTU_OP_LOAD           0x2
+#define   AR8216_VTU_OP_PURGE          0x3
+#define   AR8216_VTU_OP_REMOVE_PORT    0x4
+#define   AR8216_VTU_ACTIVE            BIT(3)
+#define   AR8216_VTU_FULL              BIT(4)
+#define   AR8216_VTU_PORT              BITS(8, 4)
+#define   AR8216_VTU_PORT_S            8
+#define   AR8216_VTU_VID               BITS(16, 12)
+#define   AR8216_VTU_VID_S             16
+#define   AR8216_VTU_PRIO              BITS(28, 3)
+#define   AR8216_VTU_PRIO_S            28
+#define   AR8216_VTU_PRIO_EN           BIT(31)
+
+#define AR8216_REG_VTU_DATA            0x0044
+#define   AR8216_VTUDATA_MEMBER                BITS(0, 10)
+#define   AR8236_VTUDATA_MEMBER                BITS(0, 7)
+#define   AR8216_VTUDATA_VALID         BIT(11)
+
+#define AR8216_REG_ATU_FUNC0           0x0050
+#define   AR8216_ATU_OP                        BITS(0, 3)
+#define   AR8216_ATU_OP_NOOP           0x0
+#define   AR8216_ATU_OP_FLUSH          0x1
+#define   AR8216_ATU_OP_LOAD           0x2
+#define   AR8216_ATU_OP_PURGE          0x3
+#define   AR8216_ATU_OP_FLUSH_UNLOCKED 0x4
+#define   AR8216_ATU_OP_FLUSH_PORT     0x5
+#define   AR8216_ATU_OP_GET_NEXT       0x6
+#define   AR8216_ATU_ACTIVE            BIT(3)
+#define   AR8216_ATU_PORT_NUM          BITS(8, 4)
+#define   AR8216_ATU_PORT_NUM_S                8
+#define   AR8216_ATU_FULL_VIO          BIT(12)
+#define   AR8216_ATU_ADDR5             BITS(16, 8)
+#define   AR8216_ATU_ADDR5_S           16
+#define   AR8216_ATU_ADDR4             BITS(24, 8)
+#define   AR8216_ATU_ADDR4_S           24
+
+#define AR8216_REG_ATU_FUNC1           0x0054
+#define   AR8216_ATU_ADDR3             BITS(0, 8)
+#define   AR8216_ATU_ADDR3_S           0
+#define   AR8216_ATU_ADDR2             BITS(8, 8)
+#define   AR8216_ATU_ADDR2_S           8
+#define   AR8216_ATU_ADDR1             BITS(16, 8)
+#define   AR8216_ATU_ADDR1_S           16
+#define   AR8216_ATU_ADDR0             BITS(24, 8)
+#define   AR8216_ATU_ADDR0_S           24
+
+#define AR8216_REG_ATU_FUNC2           0x0058
+#define   AR8216_ATU_PORTS             BITS(0, 6)
+#define   AR8216_ATU_PORT0             BIT(0)
+#define   AR8216_ATU_PORT1             BIT(1)
+#define   AR8216_ATU_PORT2             BIT(2)
+#define   AR8216_ATU_PORT3             BIT(3)
+#define   AR8216_ATU_PORT4             BIT(4)
+#define   AR8216_ATU_PORT5             BIT(5)
+#define   AR8216_ATU_STATUS            BITS(16, 4)
+#define   AR8216_ATU_STATUS_S          16
+
+#define AR8216_REG_ATU_CTRL            0x005C
+#define   AR8216_ATU_CTRL_AGE_EN       BIT(17)
+#define   AR8216_ATU_CTRL_AGE_TIME     BITS(0, 16)
+#define   AR8216_ATU_CTRL_AGE_TIME_S   0
+#define   AR8236_ATU_CTRL_RES          BIT(20)
+
+#define AR8216_REG_MIB_FUNC            0x0080
+#define   AR8216_MIB_TIMER             BITS(0, 16)
+#define   AR8216_MIB_AT_HALF_EN                BIT(16)
+#define   AR8216_MIB_BUSY              BIT(17)
+#define   AR8216_MIB_FUNC              BITS(24, 3)
+#define   AR8216_MIB_FUNC_S            24
+#define   AR8216_MIB_FUNC_NO_OP                0x0
+#define   AR8216_MIB_FUNC_FLUSH                0x1
+#define   AR8216_MIB_FUNC_CAPTURE      0x3
+#define   AR8236_MIB_EN                        BIT(30)
+
+#define AR8216_REG_GLOBAL_CPUPORT              0x0078
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT    BITS(4, 4)
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S  4
+
+#define AR8216_PORT_OFFSET(_i)         (0x0100 * (_i + 1))
+#define AR8216_REG_PORT_STATUS(_i)     (AR8216_PORT_OFFSET(_i) + 0x0000)
+#define   AR8216_PORT_STATUS_SPEED     BITS(0,2)
+#define   AR8216_PORT_STATUS_SPEED_S   0
+#define   AR8216_PORT_STATUS_TXMAC     BIT(2)
+#define   AR8216_PORT_STATUS_RXMAC     BIT(3)
+#define   AR8216_PORT_STATUS_TXFLOW    BIT(4)
+#define   AR8216_PORT_STATUS_RXFLOW    BIT(5)
+#define   AR8216_PORT_STATUS_DUPLEX    BIT(6)
+#define   AR8216_PORT_STATUS_LINK_UP   BIT(8)
+#define   AR8216_PORT_STATUS_LINK_AUTO BIT(9)
+#define   AR8216_PORT_STATUS_LINK_PAUSE        BIT(10)
+#define   AR8216_PORT_STATUS_FLOW_CONTROL  BIT(12)
+
+#define AR8216_REG_PORT_CTRL(_i)       (AR8216_PORT_OFFSET(_i) + 0x0004)
+
+/* port forwarding state */
+#define   AR8216_PORT_CTRL_STATE       BITS(0, 3)
+#define   AR8216_PORT_CTRL_STATE_S     0
+
+#define   AR8216_PORT_CTRL_LEARN_LOCK  BIT(7)
+
+/* egress 802.1q mode */
+#define   AR8216_PORT_CTRL_VLAN_MODE   BITS(8, 2)
+#define   AR8216_PORT_CTRL_VLAN_MODE_S 8
+
+#define   AR8216_PORT_CTRL_IGMP_SNOOP  BIT(10)
+#define   AR8216_PORT_CTRL_HEADER      BIT(11)
+#define   AR8216_PORT_CTRL_MAC_LOOP    BIT(12)
+#define   AR8216_PORT_CTRL_SINGLE_VLAN BIT(13)
+#define   AR8216_PORT_CTRL_LEARN       BIT(14)
+#define   AR8216_PORT_CTRL_MIRROR_TX   BIT(16)
+#define   AR8216_PORT_CTRL_MIRROR_RX   BIT(17)
+
+#define AR8216_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET(_i) + 0x0008)
+
+#define   AR8216_PORT_VLAN_DEFAULT_ID  BITS(0, 12)
+#define   AR8216_PORT_VLAN_DEFAULT_ID_S        0
+
+#define   AR8216_PORT_VLAN_DEST_PORTS  BITS(16, 9)
+#define   AR8216_PORT_VLAN_DEST_PORTS_S        16
+
+/* bit0 added to the priority field of egress frames */
+#define   AR8216_PORT_VLAN_TX_PRIO     BIT(27)
+
+/* port default priority */
+#define   AR8216_PORT_VLAN_PRIORITY    BITS(28, 2)
+#define   AR8216_PORT_VLAN_PRIORITY_S  28
+
+/* ingress 802.1q mode */
+#define   AR8216_PORT_VLAN_MODE                BITS(30, 2)
+#define   AR8216_PORT_VLAN_MODE_S      30
+
+#define AR8216_REG_PORT_RATE(_i)       (AR8216_PORT_OFFSET(_i) + 0x000c)
+#define AR8216_REG_PORT_PRIO(_i)       (AR8216_PORT_OFFSET(_i) + 0x0010)
+
+#define AR8216_STATS_RXBROAD           0x00
+#define AR8216_STATS_RXPAUSE           0x04
+#define AR8216_STATS_RXMULTI           0x08
+#define AR8216_STATS_RXFCSERR          0x0c
+#define AR8216_STATS_RXALIGNERR                0x10
+#define AR8216_STATS_RXRUNT            0x14
+#define AR8216_STATS_RXFRAGMENT                0x18
+#define AR8216_STATS_RX64BYTE          0x1c
+#define AR8216_STATS_RX128BYTE         0x20
+#define AR8216_STATS_RX256BYTE         0x24
+#define AR8216_STATS_RX512BYTE         0x28
+#define AR8216_STATS_RX1024BYTE                0x2c
+#define AR8216_STATS_RXMAXBYTE         0x30
+#define AR8216_STATS_RXTOOLONG         0x34
+#define AR8216_STATS_RXGOODBYTE                0x38
+#define AR8216_STATS_RXBADBYTE         0x40
+#define AR8216_STATS_RXOVERFLOW                0x48
+#define AR8216_STATS_FILTERED          0x4c
+#define AR8216_STATS_TXBROAD           0x50
+#define AR8216_STATS_TXPAUSE           0x54
+#define AR8216_STATS_TXMULTI           0x58
+#define AR8216_STATS_TXUNDERRUN                0x5c
+#define AR8216_STATS_TX64BYTE          0x60
+#define AR8216_STATS_TX128BYTE         0x64
+#define AR8216_STATS_TX256BYTE         0x68
+#define AR8216_STATS_TX512BYTE         0x6c
+#define AR8216_STATS_TX1024BYTE                0x70
+#define AR8216_STATS_TXMAXBYTE         0x74
+#define AR8216_STATS_TXOVERSIZE                0x78
+#define AR8216_STATS_TXBYTE            0x7c
+#define AR8216_STATS_TXCOLLISION       0x84
+#define AR8216_STATS_TXABORTCOL                0x88
+#define AR8216_STATS_TXMULTICOL                0x8c
+#define AR8216_STATS_TXSINGLECOL       0x90
+#define AR8216_STATS_TXEXCDEFER                0x94
+#define AR8216_STATS_TXDEFER           0x98
+#define AR8216_STATS_TXLATECOL         0x9c
+
+#define AR8236_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET((_i)) + 0x0008)
+#define   AR8236_PORT_VLAN_DEFAULT_ID  BITS(16, 12)
+#define   AR8236_PORT_VLAN_DEFAULT_ID_S        16
+#define   AR8236_PORT_VLAN_PRIORITY    BITS(29, 3)
+#define   AR8236_PORT_VLAN_PRIORITY_S  28
+
+#define AR8236_REG_PORT_VLAN2(_i)      (AR8216_PORT_OFFSET((_i)) + 0x000c)
+#define   AR8236_PORT_VLAN2_MEMBER     BITS(16, 7)
+#define   AR8236_PORT_VLAN2_MEMBER_S   16
+#define   AR8236_PORT_VLAN2_TX_PRIO    BIT(23)
+#define   AR8236_PORT_VLAN2_VLAN_MODE  BITS(30, 2)
+#define   AR8236_PORT_VLAN2_VLAN_MODE_S        30
+
+#define AR8236_STATS_RXBROAD           0x00
+#define AR8236_STATS_RXPAUSE           0x04
+#define AR8236_STATS_RXMULTI           0x08
+#define AR8236_STATS_RXFCSERR          0x0c
+#define AR8236_STATS_RXALIGNERR                0x10
+#define AR8236_STATS_RXRUNT            0x14
+#define AR8236_STATS_RXFRAGMENT                0x18
+#define AR8236_STATS_RX64BYTE          0x1c
+#define AR8236_STATS_RX128BYTE         0x20
+#define AR8236_STATS_RX256BYTE         0x24
+#define AR8236_STATS_RX512BYTE         0x28
+#define AR8236_STATS_RX1024BYTE                0x2c
+#define AR8236_STATS_RX1518BYTE                0x30
+#define AR8236_STATS_RXMAXBYTE         0x34
+#define AR8236_STATS_RXTOOLONG         0x38
+#define AR8236_STATS_RXGOODBYTE                0x3c
+#define AR8236_STATS_RXBADBYTE         0x44
+#define AR8236_STATS_RXOVERFLOW                0x4c
+#define AR8236_STATS_FILTERED          0x50
+#define AR8236_STATS_TXBROAD           0x54
+#define AR8236_STATS_TXPAUSE           0x58
+#define AR8236_STATS_TXMULTI           0x5c
+#define AR8236_STATS_TXUNDERRUN                0x60
+#define AR8236_STATS_TX64BYTE          0x64
+#define AR8236_STATS_TX128BYTE         0x68
+#define AR8236_STATS_TX256BYTE         0x6c
+#define AR8236_STATS_TX512BYTE         0x70
+#define AR8236_STATS_TX1024BYTE                0x74
+#define AR8236_STATS_TX1518BYTE                0x78
+#define AR8236_STATS_TXMAXBYTE         0x7c
+#define AR8236_STATS_TXOVERSIZE                0x80
+#define AR8236_STATS_TXBYTE            0x84
+#define AR8236_STATS_TXCOLLISION       0x8c
+#define AR8236_STATS_TXABORTCOL                0x90
+#define AR8236_STATS_TXMULTICOL                0x94
+#define AR8236_STATS_TXSINGLECOL       0x98
+#define AR8236_STATS_TXEXCDEFER                0x9c
+#define AR8236_STATS_TXDEFER           0xa0
+#define AR8236_STATS_TXLATECOL         0xa4
+
+#define AR8316_REG_POSTRIP                     0x0008
+#define   AR8316_POSTRIP_MAC0_GMII_EN          BIT(0)
+#define   AR8316_POSTRIP_MAC0_RGMII_EN         BIT(1)
+#define   AR8316_POSTRIP_PHY4_GMII_EN          BIT(2)
+#define   AR8316_POSTRIP_PHY4_RGMII_EN         BIT(3)
+#define   AR8316_POSTRIP_MAC0_MAC_MODE         BIT(4)
+#define   AR8316_POSTRIP_RTL_MODE              BIT(5)
+#define   AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN  BIT(6)
+#define   AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN  BIT(7)
+#define   AR8316_POSTRIP_SERDES_EN             BIT(8)
+#define   AR8316_POSTRIP_SEL_ANA_RST           BIT(9)
+#define   AR8316_POSTRIP_GATE_25M_EN           BIT(10)
+#define   AR8316_POSTRIP_SEL_CLK25M            BIT(11)
+#define   AR8316_POSTRIP_HIB_PULSE_HW          BIT(12)
+#define   AR8316_POSTRIP_DBG_MODE_I            BIT(13)
+#define   AR8316_POSTRIP_MAC5_MAC_MODE         BIT(14)
+#define   AR8316_POSTRIP_MAC5_PHY_MODE         BIT(15)
+#define   AR8316_POSTRIP_POWER_DOWN_HW         BIT(16)
+#define   AR8316_POSTRIP_LPW_STATE_EN          BIT(17)
+#define   AR8316_POSTRIP_MAN_EN                        BIT(18)
+#define   AR8316_POSTRIP_PHY_PLL_ON            BIT(19)
+#define   AR8316_POSTRIP_LPW_EXIT              BIT(20)
+#define   AR8316_POSTRIP_TXDELAY_S0            BIT(21)
+#define   AR8316_POSTRIP_TXDELAY_S1            BIT(22)
+#define   AR8316_POSTRIP_RXDELAY_S0            BIT(23)
+#define   AR8316_POSTRIP_LED_OPEN_EN           BIT(24)
+#define   AR8316_POSTRIP_SPI_EN                        BIT(25)
+#define   AR8316_POSTRIP_RXDELAY_S1            BIT(26)
+#define   AR8316_POSTRIP_POWER_ON_SEL          BIT(31)
+
+/* port speed */
+enum {
+        AR8216_PORT_SPEED_10M = 0,
+        AR8216_PORT_SPEED_100M = 1,
+        AR8216_PORT_SPEED_1000M = 2,
+        AR8216_PORT_SPEED_ERR = 3,
+};
+
+/* ingress 802.1q mode */
+enum {
+       AR8216_IN_PORT_ONLY = 0,
+       AR8216_IN_PORT_FALLBACK = 1,
+       AR8216_IN_VLAN_ONLY = 2,
+       AR8216_IN_SECURE = 3
+};
+
+/* egress 802.1q mode */
+enum {
+       AR8216_OUT_KEEP = 0,
+       AR8216_OUT_STRIP_VLAN = 1,
+       AR8216_OUT_ADD_VLAN = 2
+};
+
+/* port forwarding state */
+enum {
+       AR8216_PORT_STATE_DISABLED = 0,
+       AR8216_PORT_STATE_BLOCK = 1,
+       AR8216_PORT_STATE_LISTEN = 2,
+       AR8216_PORT_STATE_LEARN = 3,
+       AR8216_PORT_STATE_FORWARD = 4
+};
+
+enum {
+       AR8XXX_VER_AR8216 = 0x01,
+       AR8XXX_VER_AR8236 = 0x03,
+       AR8XXX_VER_AR8316 = 0x10,
+       AR8XXX_VER_AR8327 = 0x12,
+       AR8XXX_VER_AR8337 = 0x13,
+};
+
+#define AR8XXX_NUM_ARL_RECORDS 100
+
+enum arl_op {
+       AR8XXX_ARL_INITIALIZE,
+       AR8XXX_ARL_GET_NEXT
+};
+
+struct arl_entry {
+       u8 port;
+       u8 mac[6];
+};
+
+struct ar8xxx_priv;
+
+struct ar8xxx_mib_desc {
+       unsigned int size;
+       unsigned int offset;
+       const char *name;
+};
+
+struct ar8xxx_chip {
+       unsigned long caps;
+       bool config_at_probe;
+       bool mii_lo_first;
+
+       /* parameters to calculate REG_PORT_STATS_BASE */
+       unsigned reg_port_stats_start;
+       unsigned reg_port_stats_length;
+
+       unsigned reg_arl_ctrl;
+
+       int (*hw_init)(struct ar8xxx_priv *priv);
+       void (*cleanup)(struct ar8xxx_priv *priv);
+
+       const char *name;
+       int vlans;
+       int ports;
+       const struct switch_dev_ops *swops;
+
+       void (*init_globals)(struct ar8xxx_priv *priv);
+       void (*init_port)(struct ar8xxx_priv *priv, int port);
+       void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
+       u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
+       u32 (*read_port_eee_status)(struct ar8xxx_priv *priv, int port);
+       int (*atu_flush)(struct ar8xxx_priv *priv);
+       int (*atu_flush_port)(struct ar8xxx_priv *priv, int port);
+       void (*vtu_flush)(struct ar8xxx_priv *priv);
+       void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
+       void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
+       void (*set_mirror_regs)(struct ar8xxx_priv *priv);
+       void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
+                             u32 *status, enum arl_op op);
+       int (*sw_hw_apply)(struct switch_dev *dev);
+
+       const struct ar8xxx_mib_desc *mib_decs;
+       unsigned num_mibs;
+       unsigned mib_func;
+};
+
+struct ar8xxx_priv {
+       struct switch_dev dev;
+       struct mii_bus *mii_bus;
+       struct phy_device *phy;
+
+       int (*get_port_link)(unsigned port);
+
+       const struct net_device_ops *ndo_old;
+       struct net_device_ops ndo;
+       struct mutex reg_mutex;
+       u8 chip_ver;
+       u8 chip_rev;
+       const struct ar8xxx_chip *chip;
+       void *chip_data;
+       bool initialized;
+       bool port4_phy;
+       char buf[2048];
+       struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
+       char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
+       bool link_up[AR8X16_MAX_PORTS];
+
+       bool init;
+
+       struct mutex mib_lock;
+       struct delayed_work mib_work;
+       int mib_next_port;
+       u64 *mib_stats;
+
+       struct list_head list;
+       unsigned int use_count;
+
+       /* all fields below are cleared on reset */
+       bool vlan;
+       u16 vlan_id[AR8X16_MAX_VLANS];
+       u8 vlan_table[AR8X16_MAX_VLANS];
+       u8 vlan_tagged;
+       u16 pvid[AR8X16_MAX_PORTS];
+       int arl_age_time;
+
+       /* mirroring */
+       bool mirror_rx;
+       bool mirror_tx;
+       int source_port;
+       int monitor_port;
+       u8 port_vlan_prio[AR8X16_MAX_PORTS];
+};
+
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
+u32
+ar8xxx_read(struct ar8xxx_priv *priv, int reg);
+void
+ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val);
+u32
+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
+
+void
+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
+                    u16 dbg_addr, u16 dbg_data);
+void
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data);
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg);
+void
+ar8xxx_phy_init(struct ar8xxx_priv *priv);
+int
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val);
+int
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val);
+int
+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val);
+int
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan);
+int
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan);
+int
+ar8xxx_sw_hw_apply(struct switch_dev *dev);
+int
+ar8xxx_sw_reset_switch(struct switch_dev *dev);
+int
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link);
+int
+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val);
+int
+ar8xxx_sw_get_port_mib(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val);
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev,
+                          const struct switch_attr *attr,
+                          struct switch_val *val);
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev,
+                          const struct switch_attr *attr,
+                          struct switch_val *val);
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val);
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val);
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val);
+int
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
+
+static inline struct ar8xxx_priv *
+swdev_to_ar8xxx(struct switch_dev *swdev)
+{
+       return container_of(swdev, struct ar8xxx_priv, dev);
+}
+
+static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv)
+{
+       return priv->chip->caps & AR8XXX_CAP_GIGE;
+}
+
+static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv)
+{
+       return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS;
+}
+
+static inline bool chip_is_ar8216(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8216;
+}
+
+static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8236;
+}
+
+static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8316;
+}
+
+static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8327;
+}
+
+static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8337;
+}
+
+static inline void
+ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       ar8xxx_rmw(priv, reg, 0, val);
+}
+
+static inline void
+ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       ar8xxx_rmw(priv, reg, val, 0);
+}
+
+static inline void
+split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+{
+       regaddr >>= 1;
+       *r1 = regaddr & 0x1e;
+
+       regaddr >>= 5;
+       *r2 = regaddr & 0x7;
+
+       regaddr >>= 3;
+       *page = regaddr & 0x1ff;
+}
+
+static inline void
+wait_for_page_switch(void)
+{
+       udelay(5);
+}
+
+#endif
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/ar8327.c b/target/linux/generic/files-3.18/drivers/net/phy/ar8327.c
new file mode 100644 (file)
index 0000000..74f0a08
--- /dev/null
@@ -0,0 +1,1503 @@
+/*
+ * ar8327.c: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/lockdep.h>
+#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/of_device.h>
+#include <linux/leds.h>
+#include <linux/mdio.h>
+
+#include "ar8216.h"
+#include "ar8327.h"
+
+extern const struct ar8xxx_mib_desc ar8236_mibs[39];
+extern const struct switch_attr ar8xxx_sw_attr_vlan[1];
+
+static u32
+ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
+{
+       u32 t;
+
+       if (!cfg)
+               return 0;
+
+       t = 0;
+       switch (cfg->mode) {
+       case AR8327_PAD_NC:
+               break;
+
+       case AR8327_PAD_MAC2MAC_MII:
+               t = AR8327_PAD_MAC_MII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC2MAC_GMII:
+               t = AR8327_PAD_MAC_GMII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC_SGMII:
+               t = AR8327_PAD_SGMII_EN;
+
+               /*
+                * WAR for the QUalcomm Atheros AP136 board.
+                * It seems that RGMII TX/RX delay settings needs to be
+                * applied for SGMII mode as well, The ethernet is not
+                * reliable without this.
+                */
+               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+               if (cfg->rxclk_delay_en)
+                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+               if (cfg->txclk_delay_en)
+                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+
+               if (cfg->sgmii_delay_en)
+                       t |= AR8327_PAD_SGMII_DELAY_EN;
+
+               break;
+
+       case AR8327_PAD_MAC2PHY_MII:
+               t = AR8327_PAD_PHY_MII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC2PHY_GMII:
+               t = AR8327_PAD_PHY_GMII_EN;
+               if (cfg->pipe_rxclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC_RGMII:
+               t = AR8327_PAD_RGMII_EN;
+               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+               if (cfg->rxclk_delay_en)
+                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+               if (cfg->txclk_delay_en)
+                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+               break;
+
+       case AR8327_PAD_PHY_GMII:
+               t = AR8327_PAD_PHYX_GMII_EN;
+               break;
+
+       case AR8327_PAD_PHY_RGMII:
+               t = AR8327_PAD_PHYX_RGMII_EN;
+               break;
+
+       case AR8327_PAD_PHY_MII:
+               t = AR8327_PAD_PHYX_MII_EN;
+               break;
+       }
+
+       return t;
+}
+
+static void
+ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy)
+{
+       switch (priv->chip_rev) {
+       case 1:
+               /* For 100M waveform */
+               ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea);
+               /* Turn on Gigabit clock */
+               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
+               break;
+
+       case 2:
+               ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0);
+               /* fallthrough */
+       case 4:
+               ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f);
+               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860);
+               ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46);
+               ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000);
+               break;
+       }
+}
+
+static u32
+ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
+{
+       u32 t;
+
+       if (!cfg->force_link)
+               return AR8216_PORT_STATUS_LINK_AUTO;
+
+       t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC;
+       t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0;
+       t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0;
+       t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0;
+
+       switch (cfg->speed) {
+       case AR8327_PORT_SPEED_10:
+               t |= AR8216_PORT_SPEED_10M;
+               break;
+       case AR8327_PORT_SPEED_100:
+               t |= AR8216_PORT_SPEED_100M;
+               break;
+       case AR8327_PORT_SPEED_1000:
+               t |= AR8216_PORT_SPEED_1000M;
+               break;
+       }
+
+       return t;
+}
+
+#define AR8327_LED_ENTRY(_num, _reg, _shift) \
+       [_num] = { .reg = (_reg), .shift = (_shift) }
+
+static const struct ar8327_led_entry
+ar8327_led_map[AR8327_NUM_LEDS] = {
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_0, 0, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_1, 1, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_2, 2, 14),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_0, 3, 8),
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_1, 3, 10),
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_2, 3, 12),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_0, 3, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_1, 3, 16),
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_2, 3, 18),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_0, 3, 20),
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_1, 3, 22),
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_2, 3, 24),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_0, 0, 30),
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_1, 1, 30),
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_2, 2, 30),
+};
+
+static void
+ar8327_set_led_pattern(struct ar8xxx_priv *priv, unsigned int led_num,
+                      enum ar8327_led_pattern pattern)
+{
+       const struct ar8327_led_entry *entry;
+
+       entry = &ar8327_led_map[led_num];
+       ar8xxx_rmw(priv, AR8327_REG_LED_CTRL(entry->reg),
+                  (3 << entry->shift), pattern << entry->shift);
+}
+
+static void
+ar8327_led_work_func(struct work_struct *work)
+{
+       struct ar8327_led *aled;
+       u8 pattern;
+
+       aled = container_of(work, struct ar8327_led, led_work);
+
+       pattern = aled->pattern;
+
+       ar8327_set_led_pattern(aled->sw_priv, aled->led_num,
+                              pattern);
+}
+
+static void
+ar8327_led_schedule_change(struct ar8327_led *aled, u8 pattern)
+{
+       if (aled->pattern == pattern)
+               return;
+
+       aled->pattern = pattern;
+       schedule_work(&aled->led_work);
+}
+
+static inline struct ar8327_led *
+led_cdev_to_ar8327_led(struct led_classdev *led_cdev)
+{
+       return container_of(led_cdev, struct ar8327_led, cdev);
+}
+
+static int
+ar8327_led_blink_set(struct led_classdev *led_cdev,
+                    unsigned long *delay_on,
+                    unsigned long *delay_off)
+{
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+
+       if (*delay_on == 0 && *delay_off == 0) {
+               *delay_on = 125;
+               *delay_off = 125;
+       }
+
+       if (*delay_on != 125 || *delay_off != 125) {
+               /*
+                * The hardware only supports blinking at 4Hz. Fall back
+                * to software implementation in other cases.
+                */
+               return -EINVAL;
+       }
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = false;
+       ar8327_led_schedule_change(aled, AR8327_LED_PATTERN_BLINK);
+
+       spin_unlock(&aled->lock);
+
+       return 0;
+}
+
+static void
+ar8327_led_set_brightness(struct led_classdev *led_cdev,
+                         enum led_brightness brightness)
+{
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       u8 pattern;
+       bool active;
+
+       active = (brightness != LED_OFF);
+       active ^= aled->active_low;
+
+       pattern = (active) ? AR8327_LED_PATTERN_ON :
+                            AR8327_LED_PATTERN_OFF;
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = false;
+       ar8327_led_schedule_change(aled, pattern);
+
+       spin_unlock(&aled->lock);
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       ssize_t ret = 0;
+
+       ret += scnprintf(buf, PAGE_SIZE, "%d\n", aled->enable_hw_mode);
+
+       return ret;
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf,
+                               size_t size)
+{
+        struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       u8 pattern;
+       u8 value;
+       int ret;
+
+       ret = kstrtou8(buf, 10, &value);
+       if (ret < 0)
+               return -EINVAL;
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = !!value;
+       if (aled->enable_hw_mode)
+               pattern = AR8327_LED_PATTERN_RULE;
+       else
+               pattern = AR8327_LED_PATTERN_OFF;
+
+       ar8327_led_schedule_change(aled, pattern);
+
+       spin_unlock(&aled->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(enable_hw_mode,  S_IRUGO | S_IWUSR,
+                  ar8327_led_enable_hw_mode_show,
+                  ar8327_led_enable_hw_mode_store);
+
+static int
+ar8327_led_register(struct ar8327_led *aled)
+{
+       int ret;
+
+       ret = led_classdev_register(NULL, &aled->cdev);
+       if (ret < 0)
+               return ret;
+
+       if (aled->mode == AR8327_LED_MODE_HW) {
+               ret = device_create_file(aled->cdev.dev,
+                                        &dev_attr_enable_hw_mode);
+               if (ret)
+                       goto err_unregister;
+       }
+
+       return 0;
+
+err_unregister:
+       led_classdev_unregister(&aled->cdev);
+       return ret;
+}
+
+static void
+ar8327_led_unregister(struct ar8327_led *aled)
+{
+       if (aled->mode == AR8327_LED_MODE_HW)
+               device_remove_file(aled->cdev.dev, &dev_attr_enable_hw_mode);
+
+       led_classdev_unregister(&aled->cdev);
+       cancel_work_sync(&aled->led_work);
+}
+
+static int
+ar8327_led_create(struct ar8xxx_priv *priv,
+                 const struct ar8327_led_info *led_info)
+{
+       struct ar8327_data *data = priv->chip_data;
+       struct ar8327_led *aled;
+       int ret;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return 0;
+
+       if (!led_info->name)
+               return -EINVAL;
+
+       if (led_info->led_num >= AR8327_NUM_LEDS)
+               return -EINVAL;
+
+       aled = kzalloc(sizeof(*aled) + strlen(led_info->name) + 1,
+                      GFP_KERNEL);
+       if (!aled)
+               return -ENOMEM;
+
+       aled->sw_priv = priv;
+       aled->led_num = led_info->led_num;
+       aled->active_low = led_info->active_low;
+       aled->mode = led_info->mode;
+
+       if (aled->mode == AR8327_LED_MODE_HW)
+               aled->enable_hw_mode = true;
+
+       aled->name = (char *)(aled + 1);
+       strcpy(aled->name, led_info->name);
+
+       aled->cdev.name = aled->name;
+       aled->cdev.brightness_set = ar8327_led_set_brightness;
+       aled->cdev.blink_set = ar8327_led_blink_set;
+       aled->cdev.default_trigger = led_info->default_trigger;
+
+       spin_lock_init(&aled->lock);
+       mutex_init(&aled->mutex);
+       INIT_WORK(&aled->led_work, ar8327_led_work_func);
+
+       ret = ar8327_led_register(aled);
+       if (ret)
+               goto err_free;
+
+       data->leds[data->num_leds++] = aled;
+
+       return 0;
+
+err_free:
+       kfree(aled);
+       return ret;
+}
+
+static void
+ar8327_led_destroy(struct ar8327_led *aled)
+{
+       ar8327_led_unregister(aled);
+       kfree(aled);
+}
+
+static void
+ar8327_leds_init(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       unsigned i;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return;
+
+       for (i = 0; i < data->num_leds; i++) {
+               struct ar8327_led *aled;
+
+               aled = data->leds[i];
+
+               if (aled->enable_hw_mode)
+                       aled->pattern = AR8327_LED_PATTERN_RULE;
+               else
+                       aled->pattern = AR8327_LED_PATTERN_OFF;
+
+               ar8327_set_led_pattern(priv, aled->led_num, aled->pattern);
+       }
+}
+
+static void
+ar8327_leds_cleanup(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       unsigned i;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return;
+
+       for (i = 0; i < data->num_leds; i++) {
+               struct ar8327_led *aled;
+
+               aled = data->leds[i];
+               ar8327_led_destroy(aled);
+       }
+
+       kfree(data->leds);
+}
+
+static int
+ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
+                      struct ar8327_platform_data *pdata)
+{
+       struct ar8327_led_cfg *led_cfg;
+       struct ar8327_data *data = priv->chip_data;
+       u32 pos, new_pos;
+       u32 t;
+
+       if (!pdata)
+               return -EINVAL;
+
+       priv->get_port_link = pdata->get_port_link;
+
+       data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg);
+       data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
+
+       t = ar8327_get_pad_cfg(pdata->pad0_cfg);
+       if (chip_is_ar8337(priv) && !pdata->pad0_cfg->mac06_exchange_dis)
+           t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+       ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t);
+
+       t = ar8327_get_pad_cfg(pdata->pad5_cfg);
+       ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t);
+       t = ar8327_get_pad_cfg(pdata->pad6_cfg);
+       ar8xxx_write(priv, AR8327_REG_PAD6_MODE, t);
+
+       pos = ar8xxx_read(priv, AR8327_REG_POWER_ON_STRIP);
+       new_pos = pos;
+
+       led_cfg = pdata->led_cfg;
+       if (led_cfg) {
+               if (led_cfg->open_drain)
+                       new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+               else
+                       new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3);
+
+               if (new_pos != pos)
+                       new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
+       }
+
+       if (pdata->sgmii_cfg) {
+               t = pdata->sgmii_cfg->sgmii_ctrl;
+               if (priv->chip_rev == 1)
+                       t |= AR8327_SGMII_CTRL_EN_PLL |
+                            AR8327_SGMII_CTRL_EN_RX |
+                            AR8327_SGMII_CTRL_EN_TX;
+               else
+                       t &= ~(AR8327_SGMII_CTRL_EN_PLL |
+                              AR8327_SGMII_CTRL_EN_RX |
+                              AR8327_SGMII_CTRL_EN_TX);
+
+               ar8xxx_write(priv, AR8327_REG_SGMII_CTRL, t);
+
+               if (pdata->sgmii_cfg->serdes_aen)
+                       new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
+               else
+                       new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
+       }
+
+       ar8xxx_write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
+
+       if (pdata->leds && pdata->num_leds) {
+               int i;
+
+               data->leds = kzalloc(pdata->num_leds * sizeof(void *),
+                                    GFP_KERNEL);
+               if (!data->leds)
+                       return -ENOMEM;
+
+               for (i = 0; i < pdata->num_leds; i++)
+                       ar8327_led_create(priv, &pdata->leds[i]);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       struct ar8327_data *data = priv->chip_data;
+       const __be32 *paddr;
+       int len;
+       int i;
+
+       paddr = of_get_property(np, "qca,ar8327-initvals", &len);
+       if (!paddr || len < (2 * sizeof(*paddr)))
+               return -EINVAL;
+
+       len /= sizeof(*paddr);
+
+       for (i = 0; i < len - 1; i += 2) {
+               u32 reg;
+               u32 val;
+
+               reg = be32_to_cpup(paddr + i);
+               val = be32_to_cpup(paddr + i + 1);
+
+               switch (reg) {
+               case AR8327_REG_PORT_STATUS(0):
+                       data->port0_status = val;
+                       break;
+               case AR8327_REG_PORT_STATUS(6):
+                       data->port6_status = val;
+                       break;
+               default:
+                       ar8xxx_write(priv, reg, val);
+                       break;
+               }
+       }
+
+       return 0;
+}
+#else
+static inline int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       return -EINVAL;
+}
+#endif
+
+static int
+ar8327_hw_init(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->chip_data = kzalloc(sizeof(struct ar8327_data), GFP_KERNEL);
+       if (!priv->chip_data)
+               return -ENOMEM;
+
+       if (priv->phy->mdio.dev.of_node)
+               ret = ar8327_hw_config_of(priv, priv->phy->mdio.dev.of_node);
+       else
+               ret = ar8327_hw_config_pdata(priv,
+                                            priv->phy->mdio.dev.platform_data);
+
+       if (ret)
+               return ret;
+
+       ar8327_leds_init(priv);
+
+       ar8xxx_phy_init(priv);
+
+       return 0;
+}
+
+static void
+ar8327_cleanup(struct ar8xxx_priv *priv)
+{
+       ar8327_leds_cleanup(priv);
+}
+
+static void
+ar8327_init_globals(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       u32 t;
+       int i;
+
+       /* enable CPU port and disable mirror port */
+       t = AR8327_FWD_CTRL0_CPU_PORT_EN |
+           AR8327_FWD_CTRL0_MIRROR_PORT;
+       ar8xxx_write(priv, AR8327_REG_FWD_CTRL0, t);
+
+       /* forward multicast and broadcast frames to CPU */
+       t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
+           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
+           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
+       ar8xxx_write(priv, AR8327_REG_FWD_CTRL1, t);
+
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
+                  AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
+
+       /* Enable MIB counters */
+       ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN,
+                      AR8327_MODULE_EN_MIB);
+
+       /* Disable EEE on all phy's due to stability issues */
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++)
+               data->eee[i] = false;
+}
+
+static void
+ar8327_init_port(struct ar8xxx_priv *priv, int port)
+{
+       struct ar8327_data *data = priv->chip_data;
+       u32 t;
+
+       if (port == AR8216_PORT_CPU)
+               t = data->port0_status;
+       else if (port == 6)
+               t = data->port6_status;
+       else
+               t = AR8216_PORT_STATUS_LINK_AUTO;
+
+       if (port != AR8216_PORT_CPU && port != 6) {
+               /*hw limitation:if configure mac when there is traffic,
+               port MAC may work abnormal. Need disable lan&wan mac at fisrt*/
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), 0);
+               msleep(100);
+               t |= AR8216_PORT_STATUS_FLOW_CONTROL;
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
+       } else {
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
+       }
+
+       ar8xxx_write(priv, AR8327_REG_PORT_HEADER(port), 0);
+
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), 0);
+
+       t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+       t = AR8327_PORT_LOOKUP_LEARN;
+       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static u32
+ar8327_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+
+       t = ar8xxx_read(priv, AR8327_REG_PORT_STATUS(port));
+       /* map the flow control autoneg result bits to the flow control bits
+        * used in forced mode to allow ar8216_read_port_link detect
+        * flow control properly if autoneg is used
+        */
+       if (t & AR8216_PORT_STATUS_LINK_UP &&
+           t & AR8216_PORT_STATUS_LINK_AUTO) {
+               t &= ~(AR8216_PORT_STATUS_TXFLOW | AR8216_PORT_STATUS_RXFLOW);
+               if (t & AR8327_PORT_STATUS_TXFLOW_AUTO)
+                       t |= AR8216_PORT_STATUS_TXFLOW;
+               if (t & AR8327_PORT_STATUS_RXFLOW_AUTO)
+                       t |= AR8216_PORT_STATUS_RXFLOW;
+       }
+
+       return t;
+}
+
+static u32
+ar8327_read_port_eee_status(struct ar8xxx_priv *priv, int port)
+{
+       int phy;
+       u16 t;
+
+       if (port >= priv->dev.ports)
+               return 0;
+
+       if (port == 0 || port == 6)
+               return 0;
+
+       phy = port - 1;
+
+       /* EEE Ability Auto-negotiation Result */
+       t = ar8xxx_phy_mmd_read(priv, phy, 0x7, 0x8000);
+
+       return mmd_eee_adv_to_ethtool_adv_t(t);
+}
+
+static int
+ar8327_atu_flush(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+                             AR8327_ATU_FUNC_BUSY, 0);
+       if (!ret)
+               ar8xxx_write(priv, AR8327_REG_ATU_FUNC,
+                            AR8327_ATU_FUNC_OP_FLUSH |
+                            AR8327_ATU_FUNC_BUSY);
+
+       return ret;
+}
+
+static int
+ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+                             AR8327_ATU_FUNC_BUSY, 0);
+       if (!ret) {
+               t = (port << AR8327_ATU_PORT_NUM_S);
+               t |= AR8327_ATU_FUNC_OP_FLUSH_PORT;
+               t |= AR8327_ATU_FUNC_BUSY;
+               ar8xxx_write(priv, AR8327_REG_ATU_FUNC, t);
+       }
+
+       return ret;
+}
+
+static int
+ar8327_get_port_igmp(struct ar8xxx_priv *priv, int port)
+{
+       u32 fwd_ctrl, frame_ack;
+
+       fwd_ctrl = (BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
+       frame_ack = ((AR8327_FRAME_ACK_CTRL_IGMP_MLD |
+                     AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
+                     AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
+                    AR8327_FRAME_ACK_CTRL_S(port));
+
+       return (ar8xxx_read(priv, AR8327_REG_FWD_CTRL1) &
+                       fwd_ctrl) == fwd_ctrl &&
+               (ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL(port)) &
+                       frame_ack) == frame_ack;
+}
+
+static void
+ar8327_set_port_igmp(struct ar8xxx_priv *priv, int port, int enable)
+{
+       int reg_frame_ack = AR8327_REG_FRAME_ACK_CTRL(port);
+       u32 val_frame_ack = (AR8327_FRAME_ACK_CTRL_IGMP_MLD |
+                         AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
+                         AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
+                        AR8327_FRAME_ACK_CTRL_S(port);
+
+       if (enable) {
+               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
+                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S,
+                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
+               ar8xxx_reg_set(priv, reg_frame_ack, val_frame_ack);
+       } else {
+               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
+                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S,
+                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S);
+               ar8xxx_reg_clear(priv, reg_frame_ack, val_frame_ack);
+       }
+}
+
+static void
+ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
+{
+       if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1,
+                           AR8327_VTU_FUNC1_BUSY, 0))
+               return;
+
+       if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD)
+               ar8xxx_write(priv, AR8327_REG_VTU_FUNC0, val);
+
+       op |= AR8327_VTU_FUNC1_BUSY;
+       ar8xxx_write(priv, AR8327_REG_VTU_FUNC1, op);
+}
+
+static void
+ar8327_vtu_flush(struct ar8xxx_priv *priv)
+{
+       ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0);
+}
+
+static void
+ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
+{
+       u32 op;
+       u32 val;
+       int i;
+
+       op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S);
+       val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL;
+       for (i = 0; i < AR8327_NUM_PORTS; i++) {
+               u32 mode;
+
+               if ((port_mask & BIT(i)) == 0)
+                       mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
+               else if (priv->vlan == 0)
+                       mode = AR8327_VTU_FUNC0_EG_MODE_KEEP;
+               else if ((priv->vlan_tagged & BIT(i)) || (priv->vlan_id[priv->pvid[i]] != vid))
+                       mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
+               else
+                       mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
+
+               val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i);
+       }
+       ar8327_vtu_op(priv, op, val);
+}
+
+static void
+ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 t;
+       u32 egress, ingress;
+       u32 pvid = priv->vlan_id[priv->pvid[port]];
+
+       if (priv->vlan) {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
+       t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
+       if (priv->vlan && priv->port_vlan_prio[port]) {
+               u32 prio = priv->port_vlan_prio[port];
+
+               t |= prio << AR8327_PORT_VLAN0_DEF_SPRI_S;
+               t |= prio << AR8327_PORT_VLAN0_DEF_CPRI_S;
+       }
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t);
+
+       t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
+       t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
+       if (priv->vlan && priv->port_vlan_prio[port])
+               t |= AR8327_PORT_VLAN1_VLAN_PRI_PROP;
+
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+       t = members;
+       t |= AR8327_PORT_LOOKUP_LEARN;
+       t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S;
+       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static int
+ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if ((priv->vlan_tagged & (1 << i)) || (priv->pvid[i] != val->port_vlan))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
+ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       if (val->port_vlan == priv->pvid[p->id]) {
+                               priv->vlan_tagged |= (1 << p->id);
+                       }
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static void
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+       for (port = 0; port < AR8327_NUM_PORTS; port++) {
+               ar8xxx_reg_clear(priv, AR8327_REG_PORT_LOOKUP(port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+               ar8xxx_reg_clear(priv, AR8327_REG_PORT_HOL_CTRL1(port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8327_NUM_PORTS ||
+           priv->monitor_port >= AR8327_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_reg_set(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+       if (priv->mirror_tx)
+               ar8xxx_reg_set(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+}
+
+static int
+ar8327_sw_set_eee(struct switch_dev *dev,
+                 const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct ar8327_data *data = priv->chip_data;
+       int port = val->port_vlan;
+       int phy;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+
+       phy = port - 1;
+
+       data->eee[phy] = !!(val->value.i);
+
+       return 0;
+}
+
+static int
+ar8327_sw_get_eee(struct switch_dev *dev,
+                 const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8327_data *data = priv->chip_data;
+       int port = val->port_vlan;
+       int phy;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+
+       phy = port - 1;
+
+       val->value.i = data->eee[phy];
+
+       return 0;
+}
+
+static void
+ar8327_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8327_ATU_FUNC_BUSY && --timeout) {
+               udelay(10);
+               cond_resched();
+       }
+
+       if (!timeout)
+               pr_err("ar8327: timeout waiting for atu to become ready\n");
+}
+
+static void ar8327_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_data0, r1_data1, r1_data2, r1_func;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8327_REG_ATU_DATA0, &r1_data0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_data1 = (AR8327_REG_ATU_DATA1 >> 1) & 0x1e;
+       r1_data2 = (AR8327_REG_ATU_DATA2 >> 1) & 0x1e;
+       r1_func  = (AR8327_REG_ATU_FUNC >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               ar8xxx_mii_write32(priv, r2, r1_data0, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               ar8xxx_mii_write32(priv, r2, r1_func,
+                                  AR8327_ATU_FUNC_OP_GET_NEXT |
+                                  AR8327_ATU_FUNC_BUSY);
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_data0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_data1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_data2);
+
+               *status = val2 & AR8327_ATU_STATUS;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8327_ATU_PORT0;
+               while (!(val1 & t) && ++i < AR8327_NUM_PORTS)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8327_ATU_ADDR0) >> AR8327_ATU_ADDR0_S;
+               a->mac[1] = (val0 & AR8327_ATU_ADDR1) >> AR8327_ATU_ADDR1_S;
+               a->mac[2] = (val0 & AR8327_ATU_ADDR2) >> AR8327_ATU_ADDR2_S;
+               a->mac[3] = (val0 & AR8327_ATU_ADDR3) >> AR8327_ATU_ADDR3_S;
+               a->mac[4] = (val1 & AR8327_ATU_ADDR4) >> AR8327_ATU_ADDR4_S;
+               a->mac[5] = (val1 & AR8327_ATU_ADDR5) >> AR8327_ATU_ADDR5_S;
+               break;
+       }
+}
+
+static int
+ar8327_sw_hw_apply(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8327_data *data = priv->chip_data;
+       int ret, i;
+
+       ret = ar8xxx_sw_hw_apply(dev);
+       if (ret)
+               return ret;
+
+       for (i=0; i < AR8XXX_NUM_PHYS; i++) {
+               if (data->eee[i])
+                       ar8xxx_reg_clear(priv, AR8327_REG_EEE_CTRL,
+                              AR8327_EEE_CTRL_DISABLE_PHY(i));
+               else
+                       ar8xxx_reg_set(priv, AR8327_REG_EEE_CTRL,
+                              AR8327_EEE_CTRL_DISABLE_PHY(i));
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_get_port_igmp_snooping(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       val->value.i = ar8327_get_port_igmp(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_set_port_igmp_snooping(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ar8327_set_port_igmp(priv, port, val->value.i);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_get_igmp_snooping(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       int port;
+
+       for (port = 0; port < dev->ports; port++) {
+               val->port_vlan = port;
+               if (ar8327_sw_get_port_igmp_snooping(dev, attr, val) ||
+                   !val->value.i)
+                       break;
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_set_igmp_snooping(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       int port;
+
+       for (port = 0; port < dev->ports; port++) {
+               val->port_vlan = port;
+               if (ar8327_sw_set_port_igmp_snooping(dev, attr, val))
+                       break;
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_get_igmp_v3(struct switch_dev *dev,
+                     const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u32 val_reg;
+
+       mutex_lock(&priv->reg_mutex);
+       val_reg = ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL1);
+       val->value.i = ((val_reg & AR8327_FRAME_ACK_CTRL_IGMP_V3_EN) != 0);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_set_igmp_v3(struct switch_dev *dev,
+                     const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       if (val->value.i)
+               ar8xxx_reg_set(priv, AR8327_REG_FRAME_ACK_CTRL1,
+                              AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
+       else
+               ar8xxx_reg_clear(priv, AR8327_REG_FRAME_ACK_CTRL1,
+                                AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8327_sw_set_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+       if (val->value.i < 0 || val->value.i > 7)
+               return -EINVAL;
+
+       priv->port_vlan_prio[port] = val->value.i;
+
+       return 0;
+}
+
+static int
+ar8327_sw_get_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       val->value.i = priv->port_vlan_prio[port];
+
+       return 0;
+}
+
+static const struct switch_attr ar8327_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "arl_age_time",
+               .description = "ARL age time (secs)",
+               .set = ar8xxx_sw_set_arl_age_time,
+               .get = ar8xxx_sw_get_arl_age_time,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_snooping",
+               .description = "Enable IGMP Snooping",
+               .set = ar8327_sw_set_igmp_snooping,
+               .get = ar8327_sw_get_igmp_snooping,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_v3",
+               .description = "Enable IGMPv3 support",
+               .set = ar8327_sw_set_igmp_v3,
+               .get = ar8327_sw_get_igmp_v3,
+               .max = 1
+       },
+};
+
+static const struct switch_attr ar8327_sw_attr_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = ar8xxx_sw_set_port_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .set = NULL,
+               .get = ar8xxx_sw_get_port_mib,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_eee",
+               .description = "Enable EEE PHY sleep mode",
+               .set = ar8327_sw_set_eee,
+               .get = ar8327_sw_get_eee,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_snooping",
+               .description = "Enable port's IGMP Snooping",
+               .set = ar8327_sw_set_port_igmp_snooping,
+               .get = ar8327_sw_get_port_igmp_snooping,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "vlan_prio",
+               .description = "Port VLAN default priority (VLAN PCP) (0-7)",
+               .set = ar8327_sw_set_port_vlan_prio,
+               .get = ar8327_sw_get_port_vlan_prio,
+               .max = 7,
+       },
+};
+
+static const struct switch_dev_ops ar8327_sw_ops = {
+       .attr_global = {
+               .attr = ar8327_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8327_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8327_sw_get_ports,
+       .set_vlan_ports = ar8327_sw_set_ports,
+       .apply_config = ar8327_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+/* The following op is disabled as it hogs the CPU and degrades performance.
+   An implementation has been attempted in 4d8a66d but reading MIB data is slow
+   on ar8xxx switches.
+
+   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
+   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
+   the request to update the MIB counter is cleared. */
+#if 0
+       .get_port_stats = ar8xxx_sw_get_port_stats,
+#endif
+};
+
+const struct ar8xxx_chip ar8327_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
+       .mii_lo_first = true,
+
+       .name = "Atheros AR8327",
+       .ports = AR8327_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8327_sw_ops,
+
+       .reg_port_stats_start = 0x1000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
+
+       .hw_init = ar8327_hw_init,
+       .cleanup = ar8327_cleanup,
+       .init_globals = ar8327_init_globals,
+       .init_port = ar8327_init_port,
+       .setup_port = ar8327_setup_port,
+       .read_port_status = ar8327_read_port_status,
+       .read_port_eee_status = ar8327_read_port_eee_status,
+       .atu_flush = ar8327_atu_flush,
+       .atu_flush_port = ar8327_atu_flush_port,
+       .vtu_flush = ar8327_vtu_flush,
+       .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
+       .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
+       .sw_hw_apply = ar8327_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
+};
+
+const struct ar8xxx_chip ar8337_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
+       .mii_lo_first = true,
+
+       .name = "Atheros AR8337",
+       .ports = AR8327_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8327_sw_ops,
+
+       .reg_port_stats_start = 0x1000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
+
+       .hw_init = ar8327_hw_init,
+       .cleanup = ar8327_cleanup,
+       .init_globals = ar8327_init_globals,
+       .init_port = ar8327_init_port,
+       .setup_port = ar8327_setup_port,
+       .read_port_status = ar8327_read_port_status,
+       .read_port_eee_status = ar8327_read_port_eee_status,
+       .atu_flush = ar8327_atu_flush,
+       .atu_flush_port = ar8327_atu_flush_port,
+       .vtu_flush = ar8327_vtu_flush,
+       .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
+       .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
+       .sw_hw_apply = ar8327_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
+};
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/ar8327.h b/target/linux/generic/files-3.18/drivers/net/phy/ar8327.h
new file mode 100644 (file)
index 0000000..d53ef88
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * ar8327.h: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AR8327_H
+#define __AR8327_H
+
+#define AR8327_NUM_PORTS       7
+#define AR8327_NUM_LEDS                15
+#define AR8327_PORTS_ALL       0x7f
+#define AR8327_NUM_LED_CTRL_REGS       4
+
+#define AR8327_REG_MASK                                0x000
+
+#define AR8327_REG_PAD0_MODE                   0x004
+#define AR8327_REG_PAD5_MODE                   0x008
+#define AR8327_REG_PAD6_MODE                   0x00c
+#define   AR8327_PAD_MAC_MII_RXCLK_SEL         BIT(0)
+#define   AR8327_PAD_MAC_MII_TXCLK_SEL         BIT(1)
+#define   AR8327_PAD_MAC_MII_EN                        BIT(2)
+#define   AR8327_PAD_MAC_GMII_RXCLK_SEL                BIT(4)
+#define   AR8327_PAD_MAC_GMII_TXCLK_SEL                BIT(5)
+#define   AR8327_PAD_MAC_GMII_EN               BIT(6)
+#define   AR8327_PAD_SGMII_EN                  BIT(7)
+#define   AR8327_PAD_PHY_MII_RXCLK_SEL         BIT(8)
+#define   AR8327_PAD_PHY_MII_TXCLK_SEL         BIT(9)
+#define   AR8327_PAD_PHY_MII_EN                        BIT(10)
+#define   AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL   BIT(11)
+#define   AR8327_PAD_PHY_GMII_RXCLK_SEL                BIT(12)
+#define   AR8327_PAD_PHY_GMII_TXCLK_SEL                BIT(13)
+#define   AR8327_PAD_PHY_GMII_EN               BIT(14)
+#define   AR8327_PAD_PHYX_GMII_EN              BIT(16)
+#define   AR8327_PAD_PHYX_RGMII_EN             BIT(17)
+#define   AR8327_PAD_PHYX_MII_EN               BIT(18)
+#define   AR8327_PAD_SGMII_DELAY_EN            BIT(19)
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL     BITS(20, 2)
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S   20
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL     BITS(22, 2)
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S   22
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_EN      BIT(24)
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_EN      BIT(25)
+#define   AR8327_PAD_RGMII_EN                  BIT(26)
+
+#define AR8327_REG_POWER_ON_STRIP              0x010
+#define   AR8327_POWER_ON_STRIP_POWER_ON_SEL   BIT(31)
+#define   AR8327_POWER_ON_STRIP_LED_OPEN_EN    BIT(24)
+#define   AR8327_POWER_ON_STRIP_SERDES_AEN     BIT(7)
+
+#define AR8327_REG_INT_STATUS0                 0x020
+#define   AR8327_INT0_VT_DONE                  BIT(20)
+
+#define AR8327_REG_INT_STATUS1                 0x024
+#define AR8327_REG_INT_MASK0                   0x028
+#define AR8327_REG_INT_MASK1                   0x02c
+
+#define AR8327_REG_MODULE_EN                   0x030
+#define   AR8327_MODULE_EN_MIB                 BIT(0)
+
+#define AR8327_REG_MIB_FUNC                    0x034
+#define   AR8327_MIB_CPU_KEEP                  BIT(20)
+
+#define AR8327_REG_SERVICE_TAG                 0x048
+#define AR8327_REG_LED_CTRL(_i)                        (0x050 + (_i) * 4)
+#define AR8327_REG_LED_CTRL0                   0x050
+#define AR8327_REG_LED_CTRL1                   0x054
+#define AR8327_REG_LED_CTRL2                   0x058
+#define AR8327_REG_LED_CTRL3                   0x05c
+#define AR8327_REG_MAC_ADDR0                   0x060
+#define AR8327_REG_MAC_ADDR1                   0x064
+
+#define AR8327_REG_MAX_FRAME_SIZE              0x078
+#define   AR8327_MAX_FRAME_SIZE_MTU            BITS(0, 14)
+
+#define AR8327_REG_PORT_STATUS(_i)             (0x07c + (_i) * 4)
+#define   AR8327_PORT_STATUS_TXFLOW_AUTO       BIT(10)
+#define   AR8327_PORT_STATUS_RXFLOW_AUTO       BIT(11)
+
+#define AR8327_REG_HEADER_CTRL                 0x098
+#define AR8327_REG_PORT_HEADER(_i)             (0x09c + (_i) * 4)
+
+#define AR8327_REG_SGMII_CTRL                  0x0e0
+#define   AR8327_SGMII_CTRL_EN_PLL             BIT(1)
+#define   AR8327_SGMII_CTRL_EN_RX              BIT(2)
+#define   AR8327_SGMII_CTRL_EN_TX              BIT(3)
+
+#define AR8327_REG_EEE_CTRL                    0x100
+#define   AR8327_EEE_CTRL_DISABLE_PHY(_i)      BIT(4 + (_i) * 2)
+
+#define AR8327_REG_FRAME_ACK_CTRL0             0x210
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0   BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0  BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0 BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN0      BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN0       BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0    BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0    BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1   BIT(8)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1  BIT(9)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1 BIT(10)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN1      BIT(11)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN1       BIT(12)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1    BIT(13)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1    BIT(14)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2   BIT(16)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2  BIT(17)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2 BIT(18)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN2      BIT(19)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN2       BIT(20)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2    BIT(21)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2    BIT(22)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3   BIT(24)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3  BIT(25)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3 BIT(26)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN3      BIT(27)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN3       BIT(28)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3    BIT(29)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3    BIT(30)
+
+#define AR8327_REG_FRAME_ACK_CTRL1             0x214
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4   BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4  BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4 BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN4      BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN4       BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4    BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4    BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5   BIT(8)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5  BIT(9)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5 BIT(10)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN5      BIT(11)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN5       BIT(12)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5    BIT(13)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5    BIT(14)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6   BIT(16)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6  BIT(17)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6 BIT(18)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN6      BIT(19)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN6       BIT(20)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6    BIT(21)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6    BIT(22)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_V3_EN     BIT(24)
+#define   AR8327_FRAME_ACK_CTRL_PPPOE_EN       BIT(25)
+
+#define AR8327_REG_FRAME_ACK_CTRL(_i)          (0x210 + ((_i) / 4) * 0x4)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD       BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN      BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE     BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL          BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP           BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK                BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ                BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_S(_i)          (((_i) % 4) * 8)
+
+#define AR8327_REG_PORT_VLAN0(_i)              (0x420 + (_i) * 0x8)
+#define   AR8327_PORT_VLAN0_DEF_PRI_MASK       BITS(0, 3)
+#define   AR8327_PORT_VLAN0_DEF_SVID           BITS(0, 12)
+#define   AR8327_PORT_VLAN0_DEF_SVID_S         0
+#define   AR8327_PORT_VLAN0_DEF_SPRI           BITS(13, 3)
+#define   AR8327_PORT_VLAN0_DEF_SPRI_S         13
+#define   AR8327_PORT_VLAN0_DEF_CVID           BITS(16, 12)
+#define   AR8327_PORT_VLAN0_DEF_CVID_S         16
+#define   AR8327_PORT_VLAN0_DEF_CPRI           BITS(29, 3)
+#define   AR8327_PORT_VLAN0_DEF_CPRI_S         29
+
+#define AR8327_REG_PORT_VLAN1(_i)              (0x424 + (_i) * 0x8)
+#define   AR8327_PORT_VLAN1_VLAN_PRI_PROP      BIT(4)
+#define   AR8327_PORT_VLAN1_PORT_VLAN_PROP     BIT(6)
+#define   AR8327_PORT_VLAN1_OUT_MODE           BITS(12, 2)
+#define   AR8327_PORT_VLAN1_OUT_MODE_S         12
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNMOD     0
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNTAG     1
+#define   AR8327_PORT_VLAN1_OUT_MODE_TAG       2
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH   3
+
+#define AR8327_REG_ATU_DATA0                   0x600
+#define   AR8327_ATU_ADDR0                     BITS(0, 8)
+#define   AR8327_ATU_ADDR0_S                   0
+#define   AR8327_ATU_ADDR1                     BITS(8, 8)
+#define   AR8327_ATU_ADDR1_S                   8
+#define   AR8327_ATU_ADDR2                     BITS(16, 8)
+#define   AR8327_ATU_ADDR2_S                   16
+#define   AR8327_ATU_ADDR3                     BITS(24, 8)
+#define   AR8327_ATU_ADDR3_S                   24
+#define AR8327_REG_ATU_DATA1                   0x604
+#define   AR8327_ATU_ADDR4                     BITS(0, 8)
+#define   AR8327_ATU_ADDR4_S                   0
+#define   AR8327_ATU_ADDR5                     BITS(8, 8)
+#define   AR8327_ATU_ADDR5_S                   8
+#define   AR8327_ATU_PORTS                     BITS(16, 7)
+#define   AR8327_ATU_PORT0                     BIT(16)
+#define   AR8327_ATU_PORT1                     BIT(17)
+#define   AR8327_ATU_PORT2                     BIT(18)
+#define   AR8327_ATU_PORT3                     BIT(19)
+#define   AR8327_ATU_PORT4                     BIT(20)
+#define   AR8327_ATU_PORT5                     BIT(21)
+#define   AR8327_ATU_PORT6                     BIT(22)
+#define AR8327_REG_ATU_DATA2                   0x608
+#define   AR8327_ATU_STATUS                    BITS(0, 4)
+
+#define AR8327_REG_ATU_FUNC                    0x60c
+#define   AR8327_ATU_FUNC_OP                   BITS(0, 4)
+#define   AR8327_ATU_FUNC_OP_NOOP              0x0
+#define   AR8327_ATU_FUNC_OP_FLUSH             0x1
+#define   AR8327_ATU_FUNC_OP_LOAD              0x2
+#define   AR8327_ATU_FUNC_OP_PURGE             0x3
+#define   AR8327_ATU_FUNC_OP_FLUSH_UNLOCKED    0x4
+#define   AR8327_ATU_FUNC_OP_FLUSH_PORT                0x5
+#define   AR8327_ATU_FUNC_OP_GET_NEXT          0x6
+#define   AR8327_ATU_FUNC_OP_SEARCH_MAC                0x7
+#define   AR8327_ATU_FUNC_OP_CHANGE_TRUNK      0x8
+#define   AR8327_ATU_PORT_NUM                  BITS(8, 4)
+#define   AR8327_ATU_PORT_NUM_S                        8
+#define   AR8327_ATU_FUNC_BUSY                 BIT(31)
+
+#define AR8327_REG_VTU_FUNC0                   0x0610
+#define   AR8327_VTU_FUNC0_EG_MODE             BITS(4, 14)
+#define   AR8327_VTU_FUNC0_EG_MODE_S(_i)       (4 + (_i) * 2)
+#define   AR8327_VTU_FUNC0_EG_MODE_KEEP                0
+#define   AR8327_VTU_FUNC0_EG_MODE_UNTAG       1
+#define   AR8327_VTU_FUNC0_EG_MODE_TAG         2
+#define   AR8327_VTU_FUNC0_EG_MODE_NOT         3
+#define   AR8327_VTU_FUNC0_IVL                 BIT(19)
+#define   AR8327_VTU_FUNC0_VALID               BIT(20)
+
+#define AR8327_REG_VTU_FUNC1                   0x0614
+#define   AR8327_VTU_FUNC1_OP                  BITS(0, 3)
+#define   AR8327_VTU_FUNC1_OP_NOOP             0
+#define   AR8327_VTU_FUNC1_OP_FLUSH            1
+#define   AR8327_VTU_FUNC1_OP_LOAD             2
+#define   AR8327_VTU_FUNC1_OP_PURGE            3
+#define   AR8327_VTU_FUNC1_OP_REMOVE_PORT      4
+#define   AR8327_VTU_FUNC1_OP_GET_NEXT         5
+#define   AR8327_VTU_FUNC1_OP_GET_ONE          6
+#define   AR8327_VTU_FUNC1_FULL                        BIT(4)
+#define   AR8327_VTU_FUNC1_PORT                        BIT(8, 4)
+#define   AR8327_VTU_FUNC1_PORT_S              8
+#define   AR8327_VTU_FUNC1_VID                 BIT(16, 12)
+#define   AR8327_VTU_FUNC1_VID_S               16
+#define   AR8327_VTU_FUNC1_BUSY                        BIT(31)
+
+#define AR8327_REG_ARL_CTRL                    0x0618
+
+#define AR8327_REG_FWD_CTRL0                   0x620
+#define   AR8327_FWD_CTRL0_CPU_PORT_EN         BIT(10)
+#define   AR8327_FWD_CTRL0_MIRROR_PORT         BITS(4, 4)
+#define   AR8327_FWD_CTRL0_MIRROR_PORT_S       4
+
+#define AR8327_REG_FWD_CTRL1                   0x624
+#define   AR8327_FWD_CTRL1_UC_FLOOD            BITS(0, 7)
+#define   AR8327_FWD_CTRL1_UC_FLOOD_S          0
+#define   AR8327_FWD_CTRL1_MC_FLOOD            BITS(8, 7)
+#define   AR8327_FWD_CTRL1_MC_FLOOD_S          8
+#define   AR8327_FWD_CTRL1_BC_FLOOD            BITS(16, 7)
+#define   AR8327_FWD_CTRL1_BC_FLOOD_S          16
+#define   AR8327_FWD_CTRL1_IGMP                        BITS(24, 7)
+#define   AR8327_FWD_CTRL1_IGMP_S              24
+
+#define AR8327_REG_PORT_LOOKUP(_i)             (0x660 + (_i) * 0xc)
+#define   AR8327_PORT_LOOKUP_MEMBER            BITS(0, 7)
+#define   AR8327_PORT_LOOKUP_IN_MODE           BITS(8, 2)
+#define   AR8327_PORT_LOOKUP_IN_MODE_S         8
+#define   AR8327_PORT_LOOKUP_STATE             BITS(16, 3)
+#define   AR8327_PORT_LOOKUP_STATE_S           16
+#define   AR8327_PORT_LOOKUP_LEARN             BIT(20)
+#define   AR8327_PORT_LOOKUP_ING_MIRROR_EN     BIT(25)
+
+#define AR8327_REG_PORT_PRIO(_i)               (0x664 + (_i) * 0xc)
+
+#define AR8327_REG_PORT_HOL_CTRL1(_i)          (0x974 + (_i) * 0x8)
+#define   AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN   BIT(16)
+
+#define AR8337_PAD_MAC06_EXCHANGE_EN           BIT(31)
+
+enum ar8327_led_pattern {
+       AR8327_LED_PATTERN_OFF = 0,
+       AR8327_LED_PATTERN_BLINK,
+       AR8327_LED_PATTERN_ON,
+       AR8327_LED_PATTERN_RULE,
+};
+
+struct ar8327_led_entry {
+       unsigned reg;
+       unsigned shift;
+};
+
+struct ar8327_led {
+       struct led_classdev cdev;
+       struct ar8xxx_priv *sw_priv;
+
+       char *name;
+       bool active_low;
+       u8 led_num;
+       enum ar8327_led_mode mode;
+
+       struct mutex mutex;
+       spinlock_t lock;
+       struct work_struct led_work;
+       bool enable_hw_mode;
+       enum ar8327_led_pattern pattern;
+};
+
+struct ar8327_data {
+       u32 port0_status;
+       u32 port6_status;
+
+       struct ar8327_led **leds;
+       unsigned int num_leds;
+
+       /* all fields below are cleared on reset */
+       bool eee[AR8XXX_NUM_PHYS];
+};
+
+#endif
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/Kconfig b/target/linux/generic/files-3.18/drivers/net/phy/b53/Kconfig
new file mode 100644 (file)
index 0000000..08287e7
--- /dev/null
@@ -0,0 +1,37 @@
+menuconfig SWCONFIG_B53
+       tristate "Broadcom bcm53xx managed switch support"
+       depends on SWCONFIG
+       help
+         This driver adds support for Broadcom managed switch chips. It supports
+         BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
+         integrated switches.
+
+config SWCONFIG_B53_SPI_DRIVER
+       tristate "B53 SPI connected switch driver"
+       depends on SWCONFIG_B53 && SPI
+       help
+         Select to enable support for registering switches configured through SPI.
+
+config SWCONFIG_B53_PHY_DRIVER
+       tristate "B53 MDIO connected switch driver"
+       depends on SWCONFIG_B53
+       select SWCONFIG_B53_PHY_FIXUP
+       help
+         Select to enable support for registering switches configured through MDIO.
+
+config SWCONFIG_B53_MMAP_DRIVER
+       tristate "B53 MMAP connected switch driver"
+       depends on SWCONFIG_B53
+       help
+         Select to enable support for memory-mapped switches like the BCM63XX
+         integrated switches.
+
+config SWCONFIG_B53_SRAB_DRIVER
+       tristate "B53 SRAB connected switch driver"
+       depends on SWCONFIG_B53
+       help
+         Select to enable support for memory-mapped Switch Register Access
+         Bridge Registers (SRAB) like it is found on the BCM53010
+
+config SWCONFIG_B53_PHY_FIXUP
+       bool
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/Makefile b/target/linux/generic/files-3.18/drivers/net/phy/b53/Makefile
new file mode 100644 (file)
index 0000000..13ff366
--- /dev/null
@@ -0,0 +1,10 @@
+obj-$(CONFIG_SWCONFIG_B53)             += b53_common.o
+
+obj-$(CONFIG_SWCONFIG_B53_PHY_FIXUP)   += b53_phy_fixup.o
+
+obj-$(CONFIG_SWCONFIG_B53_MMAP_DRIVER) += b53_mmap.o
+obj-$(CONFIG_SWCONFIG_B53_SRAB_DRIVER) += b53_srab.o
+obj-$(CONFIG_SWCONFIG_B53_PHY_DRIVER)  += b53_mdio.o
+obj-$(CONFIG_SWCONFIG_B53_SPI_DRIVER)  += b53_spi.o
+
+ccflags-y                              += -Werror
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_common.c
new file mode 100644 (file)
index 0000000..670588c
--- /dev/null
@@ -0,0 +1,1722 @@
+/*
+ * B53 switch driver main logic
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_regs.h"
+#include "b53_priv.h"
+
+/* buffer size needed for displaying all MIBs with max'd values */
+#define B53_BUF_SIZE   1188
+
+struct b53_mib_desc {
+       u8 size;
+       u8 offset;
+       const char *name;
+};
+
+/* BCM5365 MIB counters */
+static const struct b53_mib_desc b53_mibs_65[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x44, "RxOctets" },
+       { 4, 0x4c, "RxUndersizePkts" },
+       { 4, 0x50, "RxPausePkts" },
+       { 4, 0x54, "Pkts64Octets" },
+       { 4, 0x58, "Pkts65to127Octets" },
+       { 4, 0x5c, "Pkts128to255Octets" },
+       { 4, 0x60, "Pkts256to511Octets" },
+       { 4, 0x64, "Pkts512to1023Octets" },
+       { 4, 0x68, "Pkts1024to1522Octets" },
+       { 4, 0x6c, "RxOversizePkts" },
+       { 4, 0x70, "RxJabbers" },
+       { 4, 0x74, "RxAlignmentErrors" },
+       { 4, 0x78, "RxFCSErrors" },
+       { 8, 0x7c, "RxGoodOctets" },
+       { 4, 0x84, "RxDropPkts" },
+       { 4, 0x88, "RxUnicastPkts" },
+       { 4, 0x8c, "RxMulticastPkts" },
+       { 4, 0x90, "RxBroadcastPkts" },
+       { 4, 0x94, "RxSAChanges" },
+       { 4, 0x98, "RxFragments" },
+       { },
+};
+
+#define B63XX_MIB_TXB_ID       0       /* TxOctets */
+#define B63XX_MIB_RXB_ID       14      /* RxOctets */
+
+/* BCM63xx MIB counters */
+static const struct b53_mib_desc b53_mibs_63xx[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x0c, "TxQoSPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x3c, "TxQoSOctets" },
+       { 8, 0x44, "RxOctets" },
+       { 4, 0x4c, "RxUndersizePkts" },
+       { 4, 0x50, "RxPausePkts" },
+       { 4, 0x54, "Pkts64Octets" },
+       { 4, 0x58, "Pkts65to127Octets" },
+       { 4, 0x5c, "Pkts128to255Octets" },
+       { 4, 0x60, "Pkts256to511Octets" },
+       { 4, 0x64, "Pkts512to1023Octets" },
+       { 4, 0x68, "Pkts1024to1522Octets" },
+       { 4, 0x6c, "RxOversizePkts" },
+       { 4, 0x70, "RxJabbers" },
+       { 4, 0x74, "RxAlignmentErrors" },
+       { 4, 0x78, "RxFCSErrors" },
+       { 8, 0x7c, "RxGoodOctets" },
+       { 4, 0x84, "RxDropPkts" },
+       { 4, 0x88, "RxUnicastPkts" },
+       { 4, 0x8c, "RxMulticastPkts" },
+       { 4, 0x90, "RxBroadcastPkts" },
+       { 4, 0x94, "RxSAChanges" },
+       { 4, 0x98, "RxFragments" },
+       { 4, 0xa0, "RxSymbolErrors" },
+       { 4, 0xa4, "RxQoSPkts" },
+       { 8, 0xa8, "RxQoSOctets" },
+       { 4, 0xb0, "Pkts1523to2047Octets" },
+       { 4, 0xb4, "Pkts2048to4095Octets" },
+       { 4, 0xb8, "Pkts4096to8191Octets" },
+       { 4, 0xbc, "Pkts8192to9728Octets" },
+       { 4, 0xc0, "RxDiscarded" },
+       { }
+};
+
+#define B53XX_MIB_TXB_ID       0       /* TxOctets */
+#define B53XX_MIB_RXB_ID       12      /* RxOctets */
+
+/* MIB counters */
+static const struct b53_mib_desc b53_mibs[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x50, "RxOctets" },
+       { 4, 0x58, "RxUndersizePkts" },
+       { 4, 0x5c, "RxPausePkts" },
+       { 4, 0x60, "Pkts64Octets" },
+       { 4, 0x64, "Pkts65to127Octets" },
+       { 4, 0x68, "Pkts128to255Octets" },
+       { 4, 0x6c, "Pkts256to511Octets" },
+       { 4, 0x70, "Pkts512to1023Octets" },
+       { 4, 0x74, "Pkts1024to1522Octets" },
+       { 4, 0x78, "RxOversizePkts" },
+       { 4, 0x7c, "RxJabbers" },
+       { 4, 0x80, "RxAlignmentErrors" },
+       { 4, 0x84, "RxFCSErrors" },
+       { 8, 0x88, "RxGoodOctets" },
+       { 4, 0x90, "RxDropPkts" },
+       { 4, 0x94, "RxUnicastPkts" },
+       { 4, 0x98, "RxMulticastPkts" },
+       { 4, 0x9c, "RxBroadcastPkts" },
+       { 4, 0xa0, "RxSAChanges" },
+       { 4, 0xa4, "RxFragments" },
+       { 4, 0xa8, "RxJumboPkts" },
+       { 4, 0xac, "RxSymbolErrors" },
+       { 4, 0xc0, "RxDiscarded" },
+       { }
+};
+
+static int b53_do_vlan_op(struct b53_device *dev, u8 op)
+{
+       unsigned int i;
+
+       b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
+
+       for (i = 0; i < 10; i++) {
+               u8 vta;
+
+               b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
+               if (!(vta & VTA_START_CMD))
+                       return 0;
+
+               usleep_range(100, 200);
+       }
+
+       return -EIO;
+}
+
+static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
+                              u16 untag)
+{
+       if (is5325(dev)) {
+               u32 entry = 0;
+
+               if (members) {
+                       entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) |
+                               members;
+                       if (dev->core_rev >= 3)
+                               entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S;
+                       else
+                               entry |= VA_VALID_25;
+               }
+
+               b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
+                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
+       } else if (is5365(dev)) {
+               u16 entry = 0;
+
+               if (members)
+                       entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) |
+                               members | VA_VALID_65;
+
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
+                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
+       } else {
+               b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
+               b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
+                           (untag << VTE_UNTAG_S) | members);
+
+               b53_do_vlan_op(dev, VTA_CMD_WRITE);
+       }
+}
+
+void b53_set_forwarding(struct b53_device *dev, int enable)
+{
+       u8 mgmt;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (enable)
+               mgmt |= SM_SW_FWD_EN;
+       else
+               mgmt &= ~SM_SW_FWD_EN;
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static void b53_enable_vlan(struct b53_device *dev, int enable)
+{
+       u8 mgmt, vc0, vc1, vc4 = 0, vc5;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
+       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
+
+       if (is5325(dev) || is5365(dev)) {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
+       } else if (is63xx(dev)) {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
+       } else {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
+       }
+
+       mgmt &= ~SM_SW_FWD_MODE;
+
+       if (enable) {
+               vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
+               vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
+               vc4 &= ~VC4_ING_VID_CHECK_MASK;
+               vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
+               vc5 |= VC5_DROP_VTABLE_MISS;
+
+               if (is5325(dev))
+                       vc0 &= ~VC0_RESERVED_1;
+
+               if (is5325(dev) || is5365(dev))
+                       vc1 |= VC1_RX_MCST_TAG_EN;
+
+               if (!is5325(dev) && !is5365(dev)) {
+                       if (dev->allow_vid_4095)
+                               vc5 |= VC5_VID_FFF_EN;
+                       else
+                               vc5 &= ~VC5_VID_FFF_EN;
+               }
+       } else {
+               vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
+               vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
+               vc4 &= ~VC4_ING_VID_CHECK_MASK;
+               vc5 &= ~VC5_DROP_VTABLE_MISS;
+
+               if (is5325(dev) || is5365(dev))
+                       vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
+               else
+                       vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
+
+               if (is5325(dev) || is5365(dev))
+                       vc1 &= ~VC1_RX_MCST_TAG_EN;
+
+               if (!is5325(dev) && !is5365(dev))
+                       vc5 &= ~VC5_VID_FFF_EN;
+       }
+
+       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
+       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
+
+       if (is5325(dev) || is5365(dev)) {
+               /* enable the high 8 bit vid check on 5325 */
+               if (is5325(dev) && enable)
+                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
+                                  VC3_HIGH_8BIT_EN);
+               else
+                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
+       } else if (is63xx(dev)) {
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
+       } else {
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
+       }
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static int b53_set_jumbo(struct b53_device *dev, int enable, int allow_10_100)
+{
+       u32 port_mask = 0;
+       u16 max_size = JMS_MIN_SIZE;
+
+       if (is5325(dev) || is5365(dev))
+               return -EINVAL;
+
+       if (enable) {
+               port_mask = dev->enabled_ports;
+               max_size = JMS_MAX_SIZE;
+               if (allow_10_100)
+                       port_mask |= JPM_10_100_JUMBO_EN;
+       }
+
+       b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
+       return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
+}
+
+static int b53_flush_arl(struct b53_device *dev)
+{
+       unsigned int i;
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+                  FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
+
+       for (i = 0; i < 10; i++) {
+               u8 fast_age_ctrl;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+                         &fast_age_ctrl);
+
+               if (!(fast_age_ctrl & FAST_AGE_DONE))
+                       return 0;
+
+               mdelay(1);
+       }
+
+       pr_warn("time out while flushing ARL\n");
+
+       return -EINVAL;
+}
+
+static void b53_enable_ports(struct b53_device *dev)
+{
+       unsigned i;
+
+       b53_for_each_port(dev, i) {
+               u8 port_ctrl;
+               u16 pvlan_mask;
+
+               /*
+                * prevent leaking packets between wan and lan in unmanaged
+                * mode through port vlans.
+                */
+               if (dev->enable_vlan || is_cpu_port(dev, i))
+                       pvlan_mask = 0x1ff;
+               else if (is531x5(dev) || is5301x(dev))
+                       /* BCM53115 may use a different port as cpu port */
+                       pvlan_mask = BIT(dev->sw_dev.cpu_port);
+               else
+                       pvlan_mask = BIT(B53_CPU_PORT);
+
+               /* BCM5325 CPU port is at 8 */
+               if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25)
+                       i = B53_CPU_PORT;
+
+               if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7))
+                       /* disable unused ports 6 & 7 */
+                       port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
+               else if (i == B53_CPU_PORT)
+                       port_ctrl = PORT_CTRL_RX_BCST_EN |
+                                   PORT_CTRL_RX_MCST_EN |
+                                   PORT_CTRL_RX_UCST_EN;
+               else
+                       port_ctrl = 0;
+
+               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i),
+                           pvlan_mask);
+
+               /* port state is handled by bcm63xx_enet driver */
+               if (!is63xx(dev) && !(is5301x(dev) && i == 6))
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i),
+                                  port_ctrl);
+       }
+}
+
+static void b53_enable_mib(struct b53_device *dev)
+{
+       u8 gc;
+
+       b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+       gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
+
+       b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
+}
+
+static int b53_apply(struct b53_device *dev)
+{
+       int i;
+
+       /* clear all vlan entries */
+       if (is5325(dev) || is5365(dev)) {
+               for (i = 1; i < dev->sw_dev.vlans; i++)
+                       b53_set_vlan_entry(dev, i, 0, 0);
+       } else {
+               b53_do_vlan_op(dev, VTA_CMD_CLEAR);
+       }
+
+       b53_enable_vlan(dev, dev->enable_vlan);
+
+       /* fill VLAN table */
+       if (dev->enable_vlan) {
+               for (i = 0; i < dev->sw_dev.vlans; i++) {
+                       struct b53_vlan *vlan = &dev->vlans[i];
+
+                       if (!vlan->members)
+                               continue;
+
+                       b53_set_vlan_entry(dev, i, vlan->members, vlan->untag);
+               }
+
+               b53_for_each_port(dev, i)
+                       b53_write16(dev, B53_VLAN_PAGE,
+                                   B53_VLAN_PORT_DEF_TAG(i),
+                                   dev->ports[i].pvid);
+       } else {
+               b53_for_each_port(dev, i)
+                       b53_write16(dev, B53_VLAN_PAGE,
+                                   B53_VLAN_PORT_DEF_TAG(i), 1);
+
+       }
+
+       b53_enable_ports(dev);
+
+       if (!is5325(dev) && !is5365(dev))
+               b53_set_jumbo(dev, dev->enable_jumbo, 1);
+
+       return 0;
+}
+
+static void b53_switch_reset_gpio(struct b53_device *dev)
+{
+       int gpio = dev->reset_gpio;
+
+       if (gpio < 0)
+               return;
+
+       /*
+        * Reset sequence: RESET low(50ms)->high(20ms)
+        */
+       gpio_set_value(gpio, 0);
+       mdelay(50);
+
+       gpio_set_value(gpio, 1);
+       mdelay(20);
+
+       dev->current_page = 0xff;
+}
+
+static int b53_configure_ports_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       u32 port_num;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+
+       for_each_available_child_of_node(dn, pn) {
+               struct device_node *fixed_link;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               fixed_link = of_get_child_by_name(pn, "fixed-link");
+               if (fixed_link) {
+                       u32 spd;
+                       u8 po = GMII_PO_LINK;
+                       int mode = of_get_phy_mode(pn);
+
+                       if (!of_property_read_u32(fixed_link, "speed", &spd)) {
+                               switch (spd) {
+                               case 10:
+                                       po |= GMII_PO_SPEED_10M;
+                                       break;
+                               case 100:
+                                       po |= GMII_PO_SPEED_100M;
+                                       break;
+                               case 2000:
+                                       if (is_imp_port(dev, port_num))
+                                               po |= PORT_OVERRIDE_SPEED_2000M;
+                                       else
+                                               po |= GMII_PO_SPEED_2000M;
+                                       /* fall through */
+                               case 1000:
+                                       po |= GMII_PO_SPEED_1000M;
+                                       break;
+                               }
+                       }
+
+                       if (of_property_read_bool(fixed_link, "full-duplex"))
+                               po |= PORT_OVERRIDE_FULL_DUPLEX;
+                       if (of_property_read_bool(fixed_link, "pause"))
+                               po |= GMII_PO_RX_FLOW;
+                       if (of_property_read_bool(fixed_link, "asym-pause"))
+                               po |= GMII_PO_TX_FLOW;
+
+                       if (is_imp_port(dev, port_num)) {
+                               po |= PORT_OVERRIDE_EN;
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII)
+                                       po |= PORT_OVERRIDE_RV_MII_25;
+
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_PORT_OVERRIDE_CTRL, po);
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII) {
+                                       b53_read8(dev, B53_CTRL_PAGE,
+                                                 B53_PORT_OVERRIDE_CTRL, &po);
+                                       if (!(po & PORT_OVERRIDE_RV_MII_25))
+                                       pr_err("Failed to enable reverse MII mode\n");
+                                       return -EINVAL;
+                               }
+                       } else {
+                               po |= GMII_PO_EN;
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_GMII_PORT_OVERRIDE_CTRL(port_num),
+                                          po);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int b53_configure_ports(struct b53_device *dev)
+{
+       u8 cpu_port = dev->sw_dev.cpu_port;
+
+       /* configure MII port if necessary */
+       if (is5325(dev)) {
+               u8 mii_port_override;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                         &mii_port_override);
+               /* reverse mii needs to be enabled */
+               if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                  mii_port_override | PORT_OVERRIDE_RV_MII_25);
+                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                 &mii_port_override);
+
+                       if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+                               pr_err("Failed to enable reverse MII mode\n");
+                               return -EINVAL;
+                       }
+               }
+       } else if (is531x5(dev) && cpu_port == B53_CPU_PORT) {
+               u8 mii_port_override;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                         &mii_port_override);
+               b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                          mii_port_override | PORT_OVERRIDE_EN |
+                          PORT_OVERRIDE_LINK);
+
+               /* BCM47189 has another interface connected to the port 5 */
+               if (dev->enabled_ports & BIT(5)) {
+                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(5);
+                       u8 gmii_po;
+
+                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
+                       gmii_po |= GMII_PO_LINK |
+                                  GMII_PO_RX_FLOW |
+                                  GMII_PO_TX_FLOW |
+                                  GMII_PO_EN;
+                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
+               }
+       } else if (is5301x(dev)) {
+               if (cpu_port == 8) {
+                       u8 mii_port_override;
+
+                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                 &mii_port_override);
+                       mii_port_override |= PORT_OVERRIDE_LINK |
+                                            PORT_OVERRIDE_RX_FLOW |
+                                            PORT_OVERRIDE_TX_FLOW |
+                                            PORT_OVERRIDE_SPEED_2000M |
+                                            PORT_OVERRIDE_EN;
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                  mii_port_override);
+
+                       /* TODO: Ports 5 & 7 require some extra handling */
+               } else {
+                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(cpu_port);
+                       u8 gmii_po;
+
+                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
+                       gmii_po |= GMII_PO_LINK |
+                                  GMII_PO_RX_FLOW |
+                                  GMII_PO_TX_FLOW |
+                                  GMII_PO_EN |
+                                  GMII_PO_SPEED_2000M;
+                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
+               }
+       }
+
+       return 0;
+}
+
+static int b53_switch_reset(struct b53_device *dev)
+{
+       int ret = 0;
+       u8 mgmt;
+
+       b53_switch_reset_gpio(dev);
+
+       if (is539x(dev)) {
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
+       }
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (!(mgmt & SM_SW_FWD_EN)) {
+               mgmt &= ~SM_SW_FWD_MODE;
+               mgmt |= SM_SW_FWD_EN;
+
+               b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+               b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+               if (!(mgmt & SM_SW_FWD_EN)) {
+                       pr_err("Failed to enable switch!\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* enable all ports */
+       b53_enable_ports(dev);
+
+       if (dev->dev->of_node)
+               ret = b53_configure_ports_of(dev);
+       else
+               ret = b53_configure_ports(dev);
+
+       if (ret)
+               return ret;
+
+       b53_enable_mib(dev);
+
+       return b53_flush_arl(dev);
+}
+
+/*
+ * Swconfig glue functions
+ */
+
+static int b53_global_get_vlan_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->enable_vlan;
+
+       return 0;
+}
+
+static int b53_global_set_vlan_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->enable_vlan = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_jumbo_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->enable_jumbo;
+
+       return 0;
+}
+
+static int b53_global_set_jumbo_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->enable_jumbo = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_4095_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->allow_vid_4095;
+
+       return 0;
+}
+
+static int b53_global_set_4095_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->allow_vid_4095 = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_ports(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x",
+                           priv->enabled_ports);
+       val->value.s = priv->buf;
+
+       return 0;
+}
+
+static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       *val = priv->ports[port].pvid;
+
+       return 0;
+}
+
+static int b53_port_set_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (val > 15 && is5325(priv))
+               return -EINVAL;
+       if (val == 4095 && !priv->allow_vid_4095)
+               return -EINVAL;
+
+       priv->ports[port].pvid = val;
+
+       return 0;
+}
+
+static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       struct switch_port *port = &val->value.ports[0];
+       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+       int i;
+
+       val->len = 0;
+
+       if (!vlan->members)
+               return 0;
+
+       for (i = 0; i < dev->ports; i++) {
+               if (!(vlan->members & BIT(i)))
+                       continue;
+
+
+               if (!(vlan->untag & BIT(i)))
+                       port->flags = BIT(SWITCH_PORT_FLAG_TAGGED);
+               else
+                       port->flags = 0;
+
+               port->id = i;
+               val->len++;
+               port++;
+       }
+
+       return 0;
+}
+
+static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       struct switch_port *port;
+       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+       int i;
+
+       /* only BCM5325 and BCM5365 supports VID 0 */
+       if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv))
+               return -EINVAL;
+
+       /* VLAN 4095 needs special handling */
+       if (val->port_vlan == 4095 && !priv->allow_vid_4095)
+               return -EINVAL;
+
+       port = &val->value.ports[0];
+       vlan->members = 0;
+       vlan->untag = 0;
+       for (i = 0; i < val->len; i++, port++) {
+               vlan->members |= BIT(port->id);
+
+               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) {
+                       vlan->untag |= BIT(port->id);
+                       priv->ports[port->id].pvid = val->port_vlan;
+               };
+       }
+
+       /* ignore disabled ports */
+       vlan->members &= priv->enabled_ports;
+       vlan->untag &= priv->enabled_ports;
+
+       return 0;
+}
+
+static int b53_port_get_link(struct switch_dev *dev, int port,
+                            struct switch_port_link *link)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (is_cpu_port(priv, port)) {
+               link->link = 1;
+               link->duplex = 1;
+               link->speed = is5325(priv) || is5365(priv) ?
+                               SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000;
+               link->aneg = 0;
+       } else if (priv->enabled_ports & BIT(port)) {
+               u32 speed;
+               u16 lnk, duplex;
+
+               b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk);
+               b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex);
+
+               lnk = (lnk >> port) & 1;
+               duplex = (duplex >> port) & 1;
+
+               if (is5325(priv) || is5365(priv)) {
+                       u16 tmp;
+
+                       b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp);
+                       speed = SPEED_PORT_FE(tmp, port);
+               } else {
+                       b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed);
+                       speed = SPEED_PORT_GE(speed, port);
+               }
+
+               link->link = lnk;
+               if (lnk) {
+                       link->duplex = duplex;
+                       switch (speed) {
+                       case SPEED_STAT_10M:
+                               link->speed = SWITCH_PORT_SPEED_10;
+                               break;
+                       case SPEED_STAT_100M:
+                               link->speed = SWITCH_PORT_SPEED_100;
+                               break;
+                       case SPEED_STAT_1000M:
+                               link->speed = SWITCH_PORT_SPEED_1000;
+                               break;
+                       }
+               }
+
+               link->aneg = 1;
+       } else {
+               link->link = 0;
+       }
+
+       return 0;
+
+}
+
+static int b53_port_set_link(struct switch_dev *sw_dev, int port,
+                            struct switch_port_link *link)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+
+       /*
+        * TODO: BCM63XX requires special handling as it can have external phys
+        * and ports might be GE or only FE
+        */
+       if (is63xx(dev))
+               return -ENOTSUPP;
+
+       if (port == sw_dev->cpu_port)
+               return -EINVAL;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -EINVAL;
+
+       if (link->speed == SWITCH_PORT_SPEED_1000 &&
+           (is5325(dev) || is5365(dev)))
+               return -EINVAL;
+
+       if (link->speed == SWITCH_PORT_SPEED_1000 && !link->duplex)
+               return -EINVAL;
+
+       return switch_generic_set_link(sw_dev, port, link);
+}
+
+static int b53_phy_read16(struct switch_dev *dev, int addr, u8 reg, u16 *value)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (priv->ops->phy_read16)
+               return priv->ops->phy_read16(priv, addr, reg, value);
+
+       return b53_read16(priv, B53_PORT_MII_PAGE(addr), reg, value);
+}
+
+static int b53_phy_write16(struct switch_dev *dev, int addr, u8 reg, u16 value)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (priv->ops->phy_write16)
+               return priv->ops->phy_write16(priv, addr, reg, value);
+
+       return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg, value);
+}
+
+static int b53_global_reset_switch(struct switch_dev *dev)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       /* reset vlans */
+       priv->enable_vlan = 0;
+       priv->enable_jumbo = 0;
+       priv->allow_vid_4095 = 0;
+
+       memset(priv->vlans, 0, sizeof(*priv->vlans) * dev->vlans);
+       memset(priv->ports, 0, sizeof(*priv->ports) * dev->ports);
+
+       return b53_switch_reset(priv);
+}
+
+static int b53_global_apply_config(struct switch_dev *dev)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       /* disable switching */
+       b53_set_forwarding(priv, 0);
+
+       b53_apply(priv);
+
+       /* enable switching */
+       b53_set_forwarding(priv, 1);
+
+       return 0;
+}
+
+
+static int b53_global_reset_mib(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       u8 gc;
+
+       b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
+       mdelay(1);
+       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
+       mdelay(1);
+
+       return 0;
+}
+
+static int b53_port_get_mib(struct switch_dev *sw_dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+       const struct b53_mib_desc *mibs;
+       int port = val->port_vlan;
+       int len = 0;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -1;
+
+       if (is5365(dev)) {
+               if (port == 5)
+                       port = 8;
+
+               mibs = b53_mibs_65;
+       } else if (is63xx(dev)) {
+               mibs = b53_mibs_63xx;
+       } else {
+               mibs = b53_mibs;
+       }
+
+       dev->buf[0] = 0;
+
+       for (; mibs->size > 0; mibs++) {
+               u64 val;
+
+               if (mibs->size == 8) {
+                       b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val);
+               } else {
+                       u32 val32;
+
+                       b53_read32(dev, B53_MIB_PAGE(port), mibs->offset,
+                                  &val32);
+                       val = val32;
+               }
+
+               len += snprintf(dev->buf + len, B53_BUF_SIZE - len,
+                               "%-20s: %llu\n", mibs->name, val);
+       }
+
+       val->len = len;
+       val->value.s = dev->buf;
+
+       return 0;
+}
+
+static int b53_port_get_stats(struct switch_dev *sw_dev, int port,
+                               struct switch_port_stats *stats)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+       const struct b53_mib_desc *mibs;
+       int txb_id, rxb_id;
+       u64 rxb, txb;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -EINVAL;
+
+       txb_id = B53XX_MIB_TXB_ID;
+       rxb_id = B53XX_MIB_RXB_ID;
+
+       if (is5365(dev)) {
+               if (port == 5)
+                       port = 8;
+
+               mibs = b53_mibs_65;
+       } else if (is63xx(dev)) {
+               mibs = b53_mibs_63xx;
+               txb_id = B63XX_MIB_TXB_ID;
+               rxb_id = B63XX_MIB_RXB_ID;
+       } else {
+               mibs = b53_mibs;
+       }
+
+       dev->buf[0] = 0;
+
+       if (mibs->size == 8) {
+               b53_read64(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &txb);
+               b53_read64(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &rxb);
+       } else {
+               u32 val32;
+
+               b53_read32(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &val32);
+               txb = val32;
+
+               b53_read32(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &val32);
+               rxb = val32;
+       }
+
+       stats->tx_bytes = txb;
+       stats->rx_bytes = rxb;
+
+       return 0;
+}
+
+static struct switch_attr b53_global_ops_25[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+};
+
+static struct switch_attr b53_global_ops_65[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "reset_mib",
+               .description = "Reset MIB counters",
+               .set = b53_global_reset_mib,
+       },
+};
+
+static struct switch_attr b53_global_ops[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available Ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "reset_mib",
+               .description = "Reset MIB counters",
+               .set = b53_global_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_jumbo",
+               .description = "Enable Jumbo Frames",
+               .set = b53_global_set_jumbo_enable,
+               .get = b53_global_get_jumbo_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "allow_vid_4095",
+               .description = "Allow VID 4095",
+               .set = b53_global_set_4095_enable,
+               .get = b53_global_get_4095_enable,
+               .max = 1,
+       },
+};
+
+static struct switch_attr b53_port_ops[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .get = b53_port_get_mib,
+       },
+};
+
+static struct switch_attr b53_no_ops[] = {
+};
+
+static const struct switch_dev_ops b53_switch_ops_25 = {
+       .attr_global = {
+               .attr = b53_global_ops_25,
+               .n_attr = ARRAY_SIZE(b53_global_ops_25),
+       },
+       .attr_port = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+static const struct switch_dev_ops b53_switch_ops_65 = {
+       .attr_global = {
+               .attr = b53_global_ops_65,
+               .n_attr = ARRAY_SIZE(b53_global_ops_65),
+       },
+       .attr_port = {
+               .attr = b53_port_ops,
+               .n_attr = ARRAY_SIZE(b53_port_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+static const struct switch_dev_ops b53_switch_ops = {
+       .attr_global = {
+               .attr = b53_global_ops,
+               .n_attr = ARRAY_SIZE(b53_global_ops),
+       },
+       .attr_port = {
+               .attr = b53_port_ops,
+               .n_attr = ARRAY_SIZE(b53_port_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+struct b53_chip_data {
+       u32 chip_id;
+       const char *dev_name;
+       const char *alias;
+       u16 vlans;
+       u16 enabled_ports;
+       u8 cpu_port;
+       u8 vta_regs[3];
+       u8 duplex_reg;
+       u8 jumbo_pm_reg;
+       u8 jumbo_size_reg;
+       const struct switch_dev_ops *sw_ops;
+};
+
+#define B53_VTA_REGS   \
+       { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
+#define B53_VTA_REGS_9798 \
+       { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
+#define B53_VTA_REGS_63XX \
+       { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
+
+static const struct b53_chip_data b53_switch_chips[] = {
+       {
+               .chip_id = BCM5325_DEVICE_ID,
+               .dev_name = "BCM5325",
+               .alias = "bcm5325",
+               .vlans = 16,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25,
+               .duplex_reg = B53_DUPLEX_STAT_FE,
+               .sw_ops = &b53_switch_ops_25,
+       },
+       {
+               .chip_id = BCM5365_DEVICE_ID,
+               .dev_name = "BCM5365",
+               .alias = "bcm5365",
+               .vlans = 256,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25,
+               .duplex_reg = B53_DUPLEX_STAT_FE,
+               .sw_ops = &b53_switch_ops_65,
+       },
+       {
+               .chip_id = BCM5395_DEVICE_ID,
+               .dev_name = "BCM5395",
+               .alias = "bcm5395",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM5397_DEVICE_ID,
+               .dev_name = "BCM5397",
+               .alias = "bcm5397",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_9798,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM5398_DEVICE_ID,
+               .dev_name = "BCM5398",
+               .alias = "bcm5398",
+               .vlans = 4096,
+               .enabled_ports = 0x7f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_9798,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53115_DEVICE_ID,
+               .dev_name = "BCM53115",
+               .alias = "bcm53115",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .vta_regs = B53_VTA_REGS,
+               .cpu_port = B53_CPU_PORT,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53125_DEVICE_ID,
+               .dev_name = "BCM53125",
+               .alias = "bcm53125",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53128_DEVICE_ID,
+               .dev_name = "BCM53128",
+               .alias = "bcm53128",
+               .vlans = 4096,
+               .enabled_ports = 0x1ff,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM63XX_DEVICE_ID,
+               .dev_name = "BCM63xx",
+               .alias = "bcm63xx",
+               .vlans = 4096,
+               .enabled_ports = 0, /* pdata must provide them */
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_63XX,
+               .duplex_reg = B53_DUPLEX_STAT_63XX,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53010_DEVICE_ID,
+               .dev_name = "BCM53010",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53011_DEVICE_ID,
+               .dev_name = "BCM53011",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53012_DEVICE_ID,
+               .dev_name = "BCM53012",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53018_DEVICE_ID,
+               .dev_name = "BCM53018",
+               .alias = "bcm53018",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53019_DEVICE_ID,
+               .dev_name = "BCM53019",
+               .alias = "bcm53019",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+};
+
+static int b53_switch_init_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       const char *alias;
+       u32 port_num;
+       u16 ports = 0;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+       if (!dn)
+               return -EINVAL;
+
+       for_each_available_child_of_node(dn, pn) {
+               const char *label;
+               int len;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               ports |= BIT(port_num);
+
+               label = of_get_property(pn, "label", &len);
+               if (label && !strcmp(label, "cpu"))
+                       dev->sw_dev.cpu_port = port_num;
+       }
+
+       dev->enabled_ports = ports;
+
+       if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias",
+                                                &alias))
+               dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL);
+
+       return 0;
+}
+
+static int b53_switch_init(struct b53_device *dev)
+{
+       struct switch_dev *sw_dev = &dev->sw_dev;
+       unsigned i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
+               const struct b53_chip_data *chip = &b53_switch_chips[i];
+
+               if (chip->chip_id == dev->chip_id) {
+                       sw_dev->name = chip->dev_name;
+                       if (!sw_dev->alias)
+                               sw_dev->alias = chip->alias;
+                       if (!dev->enabled_ports)
+                               dev->enabled_ports = chip->enabled_ports;
+                       dev->duplex_reg = chip->duplex_reg;
+                       dev->vta_regs[0] = chip->vta_regs[0];
+                       dev->vta_regs[1] = chip->vta_regs[1];
+                       dev->vta_regs[2] = chip->vta_regs[2];
+                       dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+                       sw_dev->ops = chip->sw_ops;
+                       sw_dev->cpu_port = chip->cpu_port;
+                       sw_dev->vlans = chip->vlans;
+                       break;
+               }
+       }
+
+       if (!sw_dev->name)
+               return -EINVAL;
+
+       /* check which BCM5325x version we have */
+       if (is5325(dev)) {
+               u8 vc4;
+
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+
+               /* check reserved bits */
+               switch (vc4 & 3) {
+               case 1:
+                       /* BCM5325E */
+                       break;
+               case 3:
+                       /* BCM5325F - do not use port 4 */
+                       dev->enabled_ports &= ~BIT(4);
+                       break;
+               default:
+/* On the BCM47XX SoCs this is the supported internal switch.*/
+#ifndef CONFIG_BCM47XX
+                       /* BCM5325M */
+                       return -EINVAL;
+#else
+                       break;
+#endif
+               }
+       } else if (dev->chip_id == BCM53115_DEVICE_ID) {
+               u64 strap_value;
+
+               b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
+               /* use second IMP port if GMII is enabled */
+               if (strap_value & SV_GMII_CTRL_115)
+                       sw_dev->cpu_port = 5;
+       }
+
+       if (dev_of_node(dev->dev)) {
+               ret = b53_switch_init_of(dev);
+               if (ret)
+                       return ret;
+       }
+
+       dev->enabled_ports |= BIT(sw_dev->cpu_port);
+       sw_dev->ports = fls(dev->enabled_ports);
+
+       dev->ports = devm_kzalloc(dev->dev,
+                                 sizeof(struct b53_port) * sw_dev->ports,
+                                 GFP_KERNEL);
+       if (!dev->ports)
+               return -ENOMEM;
+
+       dev->vlans = devm_kzalloc(dev->dev,
+                                 sizeof(struct b53_vlan) * sw_dev->vlans,
+                                 GFP_KERNEL);
+       if (!dev->vlans)
+               return -ENOMEM;
+
+       dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL);
+       if (!dev->buf)
+               return -ENOMEM;
+
+       dev->reset_gpio = b53_switch_get_reset_gpio(dev);
+       if (dev->reset_gpio >= 0) {
+               ret = devm_gpio_request_one(dev->dev, dev->reset_gpio,
+                                           GPIOF_OUT_INIT_HIGH, "robo_reset");
+               if (ret)
+                       return ret;
+       }
+
+       return b53_switch_reset(dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+                                   void *priv)
+{
+       struct b53_device *dev;
+
+       dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       dev->dev = base;
+       dev->ops = ops;
+       dev->priv = priv;
+       mutex_init(&dev->reg_mutex);
+
+       return dev;
+}
+EXPORT_SYMBOL(b53_switch_alloc);
+
+int b53_switch_detect(struct b53_device *dev)
+{
+       u32 id32;
+       u16 tmp;
+       u8 id8;
+       int ret;
+
+       ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
+       if (ret)
+               return ret;
+
+       switch (id8) {
+       case 0:
+               /*
+                * BCM5325 and BCM5365 do not have this register so reads
+                * return 0. But the read operation did succeed, so assume
+                * this is one of them.
+                *
+                * Next check if we can write to the 5325's VTA register; for
+                * 5365 it is read only.
+                */
+
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
+               b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
+
+               if (tmp == 0xf)
+                       dev->chip_id = BCM5325_DEVICE_ID;
+               else
+                       dev->chip_id = BCM5365_DEVICE_ID;
+               break;
+       case BCM5395_DEVICE_ID:
+       case BCM5397_DEVICE_ID:
+       case BCM5398_DEVICE_ID:
+               dev->chip_id = id8;
+               break;
+       default:
+               ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
+               if (ret)
+                       return ret;
+
+               switch (id32) {
+               case BCM53115_DEVICE_ID:
+               case BCM53125_DEVICE_ID:
+               case BCM53128_DEVICE_ID:
+               case BCM53010_DEVICE_ID:
+               case BCM53011_DEVICE_ID:
+               case BCM53012_DEVICE_ID:
+               case BCM53018_DEVICE_ID:
+               case BCM53019_DEVICE_ID:
+                       dev->chip_id = id32;
+                       break;
+               default:
+                       pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
+                              id8, id32);
+                       return -ENODEV;
+               }
+       }
+
+       if (dev->chip_id == BCM5325_DEVICE_ID)
+               return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25,
+                                &dev->core_rev);
+       else
+               return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
+                                &dev->core_rev);
+}
+EXPORT_SYMBOL(b53_switch_detect);
+
+int b53_switch_register(struct b53_device *dev)
+{
+       int ret;
+
+       if (dev->pdata) {
+               dev->chip_id = dev->pdata->chip_id;
+               dev->enabled_ports = dev->pdata->enabled_ports;
+               dev->sw_dev.alias = dev->pdata->alias;
+       }
+
+       if (!dev->chip_id && b53_switch_detect(dev))
+               return -EINVAL;
+
+       ret = b53_switch_init(dev);
+       if (ret)
+               return ret;
+
+       pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev);
+
+       return register_switch(&dev->sw_dev, NULL);
+}
+EXPORT_SYMBOL(b53_switch_register);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 switch library");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_mdio.c
new file mode 100644 (file)
index 0000000..75bb4d9
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * B53 register access through MII registers
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+
+#include "b53_priv.h"
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+/* MII registers */
+#define REG_MII_PAGE    0x10    /* MII Page register */
+#define REG_MII_ADDR    0x11    /* MII Address register */
+#define REG_MII_DATA0   0x18    /* MII Data register 0 */
+#define REG_MII_DATA1   0x19    /* MII Data register 1 */
+#define REG_MII_DATA2   0x1a    /* MII Data register 2 */
+#define REG_MII_DATA3   0x1b    /* MII Data register 3 */
+
+#define REG_MII_PAGE_ENABLE     BIT(0)
+#define REG_MII_ADDR_WRITE      BIT(0)
+#define REG_MII_ADDR_READ       BIT(1)
+
+static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
+{
+       int i;
+       u16 v;
+       int ret;
+       struct mii_bus *bus = dev->priv;
+
+       if (dev->current_page != page) {
+               /* set page number */
+               v = (page << 8) | REG_MII_PAGE_ENABLE;
+               ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
+               if (ret)
+                       return ret;
+               dev->current_page = page;
+       }
+
+       /* set register address */
+       v = (reg << 8) | op;
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
+       if (ret)
+               return ret;
+
+       /* check if operation completed */
+       for (i = 0; i < 5; ++i) {
+               v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
+               if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
+                       break;
+               usleep_range(10, 100);
+       }
+
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
+
+       return 0;
+}
+
+static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+
+       return 0;
+}
+
+static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+       *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
+
+       return 0;
+}
+
+static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       u64 temp = 0;
+       int i;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       for (i = 2; i >= 0; i--) {
+               temp <<= 16;
+               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+       }
+
+       *val = temp;
+
+       return 0;
+}
+
+static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       u64 temp = 0;
+       int i;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       for (i = 3; i >= 0; i--) {
+               temp <<= 16;
+               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+       }
+
+       *val = temp;
+
+       return 0;
+}
+
+static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+       if (ret)
+               return ret;
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+       if (ret)
+               return ret;
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned int i;
+       u32 temp = value;
+
+       for (i = 0; i < 2; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned i;
+       u64 temp = value;
+
+       for (i = 0; i < 3; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned i;
+       u64 temp = value;
+
+       for (i = 0; i < 4; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg,
+                              u16 *value)
+{
+       struct mii_bus *bus = dev->priv;
+
+       *value = mdiobus_read(bus, addr, reg);
+
+       return 0;
+}
+
+static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg,
+                               u16 value)
+{
+       struct mii_bus *bus = dev->priv;
+
+       return mdiobus_write(bus, addr, reg, value);
+}
+
+static struct b53_io_ops b53_mdio_ops = {
+       .read8 = b53_mdio_read8,
+       .read16 = b53_mdio_read16,
+       .read32 = b53_mdio_read32,
+       .read48 = b53_mdio_read48,
+       .read64 = b53_mdio_read64,
+       .write8 = b53_mdio_write8,
+       .write16 = b53_mdio_write16,
+       .write32 = b53_mdio_write32,
+       .write48 = b53_mdio_write48,
+       .write64 = b53_mdio_write64,
+       .phy_read16 = b53_mdio_phy_read16,
+       .phy_write16 = b53_mdio_phy_write16,
+};
+
+static int b53_phy_probe(struct phy_device *phydev)
+{
+       struct b53_device dev;
+       int ret;
+
+       /* allow the generic phy driver to take over */
+       if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
+               return -ENODEV;
+
+       dev.current_page = 0xff;
+       dev.priv = phydev->mdio.bus;
+       dev.ops = &b53_mdio_ops;
+       dev.pdata = NULL;
+       mutex_init(&dev.reg_mutex);
+
+       ret = b53_switch_detect(&dev);
+       if (ret)
+               return ret;
+
+       if (is5325(&dev) || is5365(&dev))
+               phydev->supported = SUPPORTED_100baseT_Full;
+       else
+               phydev->supported = SUPPORTED_1000baseT_Full;
+
+       phydev->advertising = phydev->supported;
+
+       return 0;
+}
+
+static int b53_phy_config_init(struct phy_device *phydev)
+{
+       struct b53_device *dev;
+       int ret;
+
+       dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
+       if (!dev)
+               return -ENOMEM;
+
+       /* we don't use page 0xff, so force a page set */
+       dev->current_page = 0xff;
+       /* force the ethX as alias */
+       dev->sw_dev.alias = phydev->attached_dev->name;
+
+       ret = b53_switch_register(dev);
+       if (ret) {
+               dev_err(dev->dev, "failed to register switch: %i\n", ret);
+               return ret;
+       }
+
+       phydev->priv = dev;
+
+       return 0;
+}
+
+static void b53_phy_remove(struct phy_device *phydev)
+{
+       struct b53_device *priv = phydev->priv;
+
+       if (!priv)
+               return;
+
+       b53_switch_remove(priv);
+
+       phydev->priv = NULL;
+}
+
+static int b53_phy_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int b53_phy_read_status(struct phy_device *phydev)
+{
+       struct b53_device *priv = phydev->priv;
+
+       if (is5325(priv) || is5365(priv))
+               phydev->speed = 100;
+       else
+               phydev->speed = 1000;
+
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+       phydev->state = PHY_RUNNING;
+
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+/* BCM5325, BCM539x */
+static struct phy_driver b53_phy_driver_id1 = {
+       .phy_id         = 0x0143bc00,
+       .name           = "Broadcom B53 (1)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+/* BCM53125, BCM53128 */
+static struct phy_driver b53_phy_driver_id2 = {
+       .phy_id         = 0x03625c00,
+       .name           = "Broadcom B53 (2)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+/* BCM5365 */
+static struct phy_driver b53_phy_driver_id3 = {
+       .phy_id         = 0x00406000,
+       .name           = "Broadcom B53 (3)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+int __init b53_phy_driver_register(void)
+{
+       int ret;
+
+       ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE);
+       if (ret)
+               return ret;
+
+       ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE);
+       if (ret)
+               goto err1;
+
+       ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE);
+       if (!ret)
+               return 0;
+
+       phy_driver_unregister(&b53_phy_driver_id2);
+err1:
+       phy_driver_unregister(&b53_phy_driver_id1);
+       return ret;
+}
+
+void __exit b53_phy_driver_unregister(void)
+{
+       phy_driver_unregister(&b53_phy_driver_id3);
+       phy_driver_unregister(&b53_phy_driver_id2);
+       phy_driver_unregister(&b53_phy_driver_id1);
+}
+
+module_init(b53_phy_driver_register);
+module_exit(b53_phy_driver_unregister);
+
+MODULE_DESCRIPTION("B53 MDIO access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_mmap.c
new file mode 100644 (file)
index 0000000..ab1895e
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * B53 register access through memory mapped registers
+ *
+ * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       *val = readb(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               *val = readw_be(regs + (page << 8) + reg);
+       else
+               *val = readw(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               *val = readl_be(regs + (page << 8) + reg);
+       else
+               *val = readl(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (reg % 4) {
+               u16 lo;
+               u32 hi;
+
+               b53_mmap_read16(dev, page, reg, &lo);
+               b53_mmap_read32(dev, page, reg + 2, &hi);
+
+               *val = ((u64)hi << 16) | lo;
+       } else {
+               u32 lo;
+               u16 hi;
+
+               b53_mmap_read32(dev, page, reg, &lo);
+               b53_mmap_read16(dev, page, reg + 4, &hi);
+
+               *val = ((u64)hi << 32) | lo;
+       }
+
+       return 0;
+}
+
+static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u32 hi, lo;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       b53_mmap_read32(dev, page, reg, &lo);
+       b53_mmap_read32(dev, page, reg + 4, &hi);
+
+       *val = ((u64)hi << 32) | lo;
+
+       return 0;
+}
+
+static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       writeb(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               writew_be(value, regs + (page << 8) + reg);
+       else
+               writew(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               writel_be(value, regs + (page << 8) + reg);
+       else
+               writel(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (reg % 4) {
+               u32 hi = (u32)(value >> 16);
+               u16 lo = (u16)value;
+
+               b53_mmap_write16(dev, page, reg, lo);
+               b53_mmap_write32(dev, page, reg + 2, hi);
+       } else {
+               u16 hi = (u16)(value >> 32);
+               u32 lo = (u32)value;
+
+               b53_mmap_write32(dev, page, reg, lo);
+               b53_mmap_write16(dev, page, reg + 4, hi);
+       }
+
+       return 0;
+}
+
+static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       u32 hi, lo;
+
+       hi = (u32)(value >> 32);
+       lo = (u32)value;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       b53_mmap_write32(dev, page, reg, lo);
+       b53_mmap_write32(dev, page, reg + 4, hi);
+
+       return 0;
+}
+
+static struct b53_io_ops b53_mmap_ops = {
+       .read8 = b53_mmap_read8,
+       .read16 = b53_mmap_read16,
+       .read32 = b53_mmap_read32,
+       .read48 = b53_mmap_read48,
+       .read64 = b53_mmap_read64,
+       .write8 = b53_mmap_write8,
+       .write16 = b53_mmap_write16,
+       .write32 = b53_mmap_write32,
+       .write48 = b53_mmap_write48,
+       .write64 = b53_mmap_write64,
+};
+
+static int b53_mmap_probe(struct platform_device *pdev)
+{
+       struct b53_platform_data *pdata = pdev->dev.platform_data;
+       struct b53_device *dev;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
+       if (!dev)
+               return -ENOMEM;
+
+       if (pdata)
+               dev->pdata = pdata;
+
+       platform_set_drvdata(pdev, dev);
+
+       return b53_switch_register(dev);
+}
+
+static int b53_mmap_remove(struct platform_device *pdev)
+{
+       struct b53_device *dev = platform_get_drvdata(pdev);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static struct platform_driver b53_mmap_driver = {
+       .probe = b53_mmap_probe,
+       .remove = b53_mmap_remove,
+       .driver = {
+               .name = "b53-switch",
+       },
+};
+
+module_platform_driver(b53_mmap_driver);
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 MMAP access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_phy_fixup.c b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_phy_fixup.c
new file mode 100644 (file)
index 0000000..e2f8a39
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * B53 PHY Fixup call
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/phy.h>
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+#define B53_BRCM_OUI_1 0x0143bc00
+#define B53_BRCM_OUI_2 0x03625c00
+#define B53_BRCM_OUI_3 0x00406000
+
+static int b53_phy_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u32 phy_id;
+
+       if (dev->mdio.addr != B53_PSEUDO_PHY)
+               return 0;
+
+       /* read the first port's id */
+       phy_id = mdiobus_read(bus, 0, 2) << 16;
+       phy_id |= mdiobus_read(bus, 0, 3);
+
+       if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
+           (phy_id & 0xfffffc00) == B53_BRCM_OUI_2 ||
+           (phy_id & 0xfffffc00) == B53_BRCM_OUI_3) {
+               dev->phy_id = phy_id;
+       }
+
+       return 0;
+}
+
+int __init b53_phy_fixup_register(void)
+{
+       return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
+}
+
+subsys_initcall(b53_phy_fixup_register);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_priv.h
new file mode 100644 (file)
index 0000000..a9296c9
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * B53 common definitions
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_PRIV_H
+#define __B53_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/switch.h>
+
+struct b53_device;
+
+struct b53_io_ops {
+       int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
+       int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
+       int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
+       int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+       int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+       int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
+       int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
+       int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
+       int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+       int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+       int (*phy_read16)(struct b53_device *dev, int addr, u8 reg, u16 *value);
+       int (*phy_write16)(struct b53_device *dev, int addr, u8 reg, u16 value);
+};
+
+enum {
+       BCM5325_DEVICE_ID = 0x25,
+       BCM5365_DEVICE_ID = 0x65,
+       BCM5395_DEVICE_ID = 0x95,
+       BCM5397_DEVICE_ID = 0x97,
+       BCM5398_DEVICE_ID = 0x98,
+       BCM53115_DEVICE_ID = 0x53115,
+       BCM53125_DEVICE_ID = 0x53125,
+       BCM53128_DEVICE_ID = 0x53128,
+       BCM63XX_DEVICE_ID = 0x6300,
+       BCM53010_DEVICE_ID = 0x53010,
+       BCM53011_DEVICE_ID = 0x53011,
+       BCM53012_DEVICE_ID = 0x53012,
+       BCM53018_DEVICE_ID = 0x53018,
+       BCM53019_DEVICE_ID = 0x53019,
+};
+
+#define B53_N_PORTS    9
+#define B53_N_PORTS_25 6
+
+struct b53_vlan {
+       unsigned int    members:B53_N_PORTS;
+       unsigned int    untag:B53_N_PORTS;
+};
+
+struct b53_port {
+       unsigned int    pvid:12;
+};
+
+struct b53_device {
+       struct switch_dev sw_dev;
+       struct b53_platform_data *pdata;
+
+       struct mutex reg_mutex;
+       const struct b53_io_ops *ops;
+
+       /* chip specific data */
+       u32 chip_id;
+       u8 core_rev;
+       u8 vta_regs[3];
+       u8 duplex_reg;
+       u8 jumbo_pm_reg;
+       u8 jumbo_size_reg;
+       int reset_gpio;
+
+       /* used ports mask */
+       u16 enabled_ports;
+
+       /* connect specific data */
+       u8 current_page;
+       struct device *dev;
+       void *priv;
+
+       /* run time configuration */
+       unsigned enable_vlan:1;
+       unsigned enable_jumbo:1;
+       unsigned allow_vid_4095:1;
+
+       struct b53_port *ports;
+       struct b53_vlan *vlans;
+
+       char *buf;
+};
+
+#define b53_for_each_port(dev, i) \
+       for (i = 0; i < B53_N_PORTS; i++) \
+               if (dev->enabled_ports & BIT(i))
+
+
+
+static inline int is5325(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5325_DEVICE_ID;
+}
+
+static inline int is5365(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+       return dev->chip_id == BCM5365_DEVICE_ID;
+#else
+       return 0;
+#endif
+}
+
+static inline int is5397_98(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5397_DEVICE_ID ||
+               dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is539x(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5395_DEVICE_ID ||
+               dev->chip_id == BCM5397_DEVICE_ID ||
+               dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is531x5(struct b53_device *dev)
+{
+       return dev->chip_id == BCM53115_DEVICE_ID ||
+               dev->chip_id == BCM53125_DEVICE_ID ||
+               dev->chip_id == BCM53128_DEVICE_ID;
+}
+
+static inline int is63xx(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM63XX
+       return dev->chip_id == BCM63XX_DEVICE_ID;
+#else
+       return 0;
+#endif
+}
+
+static inline int is5301x(struct b53_device *dev)
+{
+       return dev->chip_id == BCM53010_DEVICE_ID ||
+               dev->chip_id == BCM53011_DEVICE_ID ||
+               dev->chip_id == BCM53012_DEVICE_ID ||
+               dev->chip_id == BCM53018_DEVICE_ID ||
+               dev->chip_id == BCM53019_DEVICE_ID;
+}
+
+#define B53_CPU_PORT_25        5
+#define B53_CPU_PORT   8
+
+static inline int is_cpu_port(struct b53_device *dev, int port)
+{
+       return dev->sw_dev.cpu_port == port;
+}
+
+static inline int is_imp_port(struct b53_device *dev, int port)
+{
+       if (is5325(dev) || is5365(dev))
+               return port == B53_CPU_PORT_25;
+       else
+               return port == B53_CPU_PORT;
+}
+
+static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
+{
+       return container_of(sw, struct b53_device, sw_dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+                                   void *priv);
+
+int b53_switch_detect(struct b53_device *dev);
+
+int b53_switch_register(struct b53_device *dev);
+
+static inline void b53_switch_remove(struct b53_device *dev)
+{
+       unregister_switch(&dev->sw_dev);
+}
+
+static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read8(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read16(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read32(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read48(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read64(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write8(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
+                             u16 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write16(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
+                             u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write32(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
+                             u64 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write48(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
+                              u64 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write64(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+#ifdef CONFIG_BCM47XX
+#include <bcm47xx_board.h>
+#endif
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+#include <linux/bcm47xx_nvram.h>
+#endif
+static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+       enum bcm47xx_board board = bcm47xx_board_get();
+
+       switch (board) {
+       case BCM47XX_BOARD_LINKSYS_WRT300NV11:
+       case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+               return 8;
+       default:
+               break;
+       }
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+       return bcm47xx_nvram_gpio_pin("robo_reset");
+#else
+       return -ENOENT;
+#endif
+}
+
+#endif
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_regs.h
new file mode 100644 (file)
index 0000000..f0bf674
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * B53 register definitions
+ *
+ * Copyright (C) 2004 Broadcom Corporation
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_REGS_H
+#define __B53_REGS_H
+
+/* Management Port (SMP) Page offsets */
+#define B53_CTRL_PAGE                  0x00 /* Control */
+#define B53_STAT_PAGE                  0x01 /* Status */
+#define B53_MGMT_PAGE                  0x02 /* Management Mode */
+#define B53_MIB_AC_PAGE                        0x03 /* MIB Autocast */
+#define B53_ARLCTRL_PAGE               0x04 /* ARL Control */
+#define B53_ARLIO_PAGE                 0x05 /* ARL Access */
+#define B53_FRAMEBUF_PAGE              0x06 /* Management frame access */
+#define B53_MEM_ACCESS_PAGE            0x08 /* Memory access */
+
+/* PHY Registers */
+#define B53_PORT_MII_PAGE(i)           (0x10 + (i)) /* Port i MII Registers */
+#define B53_IM_PORT_PAGE               0x18 /* Inverse MII Port (to EMAC) */
+#define B53_ALL_PORT_PAGE              0x19 /* All ports MII (broadcast) */
+
+/* MIB registers */
+#define B53_MIB_PAGE(i)                        (0x20 + (i))
+
+/* Quality of Service (QoS) Registers */
+#define B53_QOS_PAGE                   0x30
+
+/* Port VLAN Page */
+#define B53_PVLAN_PAGE                 0x31
+
+/* VLAN Registers */
+#define B53_VLAN_PAGE                  0x34
+
+/* Jumbo Frame Registers */
+#define B53_JUMBO_PAGE                 0x40
+
+/* CFP Configuration Registers Page */
+#define B53_CFP_PAGE                   0xa1
+
+/*************************************************************************
+ * Control Page registers
+ *************************************************************************/
+
+/* Port Control Register (8 bit) */
+#define B53_PORT_CTRL(i)               (0x00 + (i))
+#define   PORT_CTRL_RX_DISABLE         BIT(0)
+#define   PORT_CTRL_TX_DISABLE         BIT(1)
+#define   PORT_CTRL_RX_BCST_EN         BIT(2) /* Broadcast RX (P8 only) */
+#define   PORT_CTRL_RX_MCST_EN         BIT(3) /* Multicast RX (P8 only) */
+#define   PORT_CTRL_RX_UCST_EN         BIT(4) /* Unicast RX (P8 only) */
+#define          PORT_CTRL_STP_STATE_S         5
+#define   PORT_CTRL_STP_STATE_MASK     (0x7 << PORT_CTRL_STP_STATE_S)
+
+/* SMP Control Register (8 bit) */
+#define B53_SMP_CTRL                   0x0a
+
+/* Switch Mode Control Register (8 bit) */
+#define B53_SWITCH_MODE                        0x0b
+#define   SM_SW_FWD_MODE               BIT(0)  /* 1 = Managed Mode */
+#define   SM_SW_FWD_EN                 BIT(1)  /* Forwarding Enable */
+
+/* IMP Port state override register (8 bit) */
+#define B53_PORT_OVERRIDE_CTRL         0x0e
+#define   PORT_OVERRIDE_LINK           BIT(0)
+#define   PORT_OVERRIDE_FULL_DUPLEX    BIT(1) /* 0 = Half Duplex */
+#define   PORT_OVERRIDE_SPEED_S                2
+#define   PORT_OVERRIDE_SPEED_10M      (0 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_SPEED_100M     (1 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_SPEED_1000M    (2 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_RV_MII_25      BIT(4) /* BCM5325 only */
+#define   PORT_OVERRIDE_RX_FLOW                BIT(4)
+#define   PORT_OVERRIDE_TX_FLOW                BIT(5)
+#define   PORT_OVERRIDE_SPEED_2000M    BIT(6) /* BCM5301X only, requires setting 1000M */
+#define   PORT_OVERRIDE_EN             BIT(7) /* Use the register contents */
+
+/* Power-down mode control */
+#define B53_PD_MODE_CTRL_25            0x0f
+
+/* IP Multicast control (8 bit) */
+#define B53_IP_MULTICAST_CTRL          0x21
+#define  B53_IPMC_FWD_EN               BIT(1)
+#define  B53_UC_FWD_EN                 BIT(6)
+#define  B53_MC_FWD_EN                 BIT(7)
+
+/* (16 bit) */
+#define B53_UC_FLOOD_MASK              0x32
+#define B53_MC_FLOOD_MASK              0x34
+#define B53_IPMC_FLOOD_MASK            0x36
+
+/*
+ * Override Ports 0-7 State on devices with xMII interfaces (8 bit)
+ *
+ * For port 8 still use B53_PORT_OVERRIDE_CTRL
+ * Please note that not all ports are available on every hardware, e.g. BCM5301X
+ * don't include overriding port 6, BCM63xx also have some limitations.
+ */
+#define B53_GMII_PORT_OVERRIDE_CTRL(i) (0x58 + (i))
+#define   GMII_PO_LINK                 BIT(0)
+#define   GMII_PO_FULL_DUPLEX          BIT(1) /* 0 = Half Duplex */
+#define   GMII_PO_SPEED_S              2
+#define   GMII_PO_SPEED_10M            (0 << GMII_PO_SPEED_S)
+#define   GMII_PO_SPEED_100M           (1 << GMII_PO_SPEED_S)
+#define   GMII_PO_SPEED_1000M          (2 << GMII_PO_SPEED_S)
+#define   GMII_PO_RX_FLOW              BIT(4)
+#define   GMII_PO_TX_FLOW              BIT(5)
+#define   GMII_PO_EN                   BIT(6) /* Use the register contents */
+#define   GMII_PO_SPEED_2000M          BIT(7) /* BCM5301X only, requires setting 1000M */
+
+/* Software reset register (8 bit) */
+#define B53_SOFTRESET                  0x79
+
+/* Fast Aging Control register (8 bit) */
+#define B53_FAST_AGE_CTRL              0x88
+#define   FAST_AGE_STATIC              BIT(0)
+#define   FAST_AGE_DYNAMIC             BIT(1)
+#define   FAST_AGE_PORT                        BIT(2)
+#define   FAST_AGE_VLAN                        BIT(3)
+#define   FAST_AGE_STP                 BIT(4)
+#define   FAST_AGE_MC                  BIT(5)
+#define   FAST_AGE_DONE                        BIT(7)
+
+/*************************************************************************
+ * Status Page registers
+ *************************************************************************/
+
+/* Link Status Summary Register (16bit) */
+#define B53_LINK_STAT                  0x00
+
+/* Link Status Change Register (16 bit) */
+#define B53_LINK_STAT_CHANGE           0x02
+
+/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
+#define B53_SPEED_STAT                 0x04
+#define  SPEED_PORT_FE(reg, port)      (((reg) >> (port)) & 1)
+#define  SPEED_PORT_GE(reg, port)      (((reg) >> 2 * (port)) & 3)
+#define  SPEED_STAT_10M                        0
+#define  SPEED_STAT_100M               1
+#define  SPEED_STAT_1000M              2
+
+/* Duplex Status Summary (16 bit) */
+#define B53_DUPLEX_STAT_FE             0x06
+#define B53_DUPLEX_STAT_GE             0x08
+#define B53_DUPLEX_STAT_63XX           0x0c
+
+/* Revision ID register for BCM5325 */
+#define B53_REV_ID_25                  0x50
+
+/* Strap Value (48 bit) */
+#define B53_STRAP_VALUE                        0x70
+#define   SV_GMII_CTRL_115             BIT(27)
+
+/*************************************************************************
+ * Management Mode Page Registers
+ *************************************************************************/
+
+/* Global Management Config Register (8 bit) */
+#define B53_GLOBAL_CONFIG              0x00
+#define   GC_RESET_MIB                 0x01
+#define   GC_RX_BPDU_EN                        0x02
+#define   GC_MIB_AC_HDR_EN             0x10
+#define   GC_MIB_AC_EN                 0x20
+#define   GC_FRM_MGMT_PORT_M           0xC0
+#define   GC_FRM_MGMT_PORT_04          0x00
+#define   GC_FRM_MGMT_PORT_MII         0x80
+
+/* Broadcom Header control register (8 bit) */
+#define B53_BRCM_HDR                   0x03
+#define   BRCM_HDR_P8_EN               BIT(0) /* Enable tagging on port 8 */
+#define   BRCM_HDR_P5_EN               BIT(1) /* Enable tagging on port 5 */
+
+/* Device ID register (8 or 32 bit) */
+#define B53_DEVICE_ID                  0x30
+
+/* Revision ID register (8 bit) */
+#define B53_REV_ID                     0x40
+
+/*************************************************************************
+ * ARL Access Page Registers
+ *************************************************************************/
+
+/* VLAN Table Access Register (8 bit) */
+#define B53_VT_ACCESS                  0x80
+#define B53_VT_ACCESS_9798             0x60 /* for BCM5397/BCM5398 */
+#define B53_VT_ACCESS_63XX             0x60 /* for BCM6328/62/68 */
+#define   VTA_CMD_WRITE                        0
+#define   VTA_CMD_READ                 1
+#define   VTA_CMD_CLEAR                        2
+#define   VTA_START_CMD                        BIT(7)
+
+/* VLAN Table Index Register (16 bit) */
+#define B53_VT_INDEX                   0x81
+#define B53_VT_INDEX_9798              0x61
+#define B53_VT_INDEX_63XX              0x62
+
+/* VLAN Table Entry Register (32 bit) */
+#define B53_VT_ENTRY                   0x83
+#define B53_VT_ENTRY_9798              0x63
+#define B53_VT_ENTRY_63XX              0x64
+#define   VTE_MEMBERS                  0x1ff
+#define   VTE_UNTAG_S                  9
+#define   VTE_UNTAG                    (0x1ff << 9)
+
+/*************************************************************************
+ * Port VLAN Registers
+ *************************************************************************/
+
+/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
+#define B53_PVLAN_PORT_MASK(i)         ((i) * 2)
+
+/*************************************************************************
+ * 802.1Q Page Registers
+ *************************************************************************/
+
+/* Global QoS Control (8 bit) */
+#define B53_QOS_GLOBAL_CTL             0x00
+
+/* Enable 802.1Q for individual Ports (16 bit) */
+#define B53_802_1P_EN                  0x04
+
+/*************************************************************************
+ * VLAN Page Registers
+ *************************************************************************/
+
+/* VLAN Control 0 (8 bit) */
+#define B53_VLAN_CTRL0                 0x00
+#define   VC0_8021PF_CTRL_MASK         0x3
+#define   VC0_8021PF_CTRL_NONE         0x0
+#define   VC0_8021PF_CTRL_CHANGE_PRI   0x1
+#define   VC0_8021PF_CTRL_CHANGE_VID   0x2
+#define   VC0_8021PF_CTRL_CHANGE_BOTH  0x3
+#define   VC0_8021QF_CTRL_MASK         0xc
+#define   VC0_8021QF_CTRL_CHANGE_PRI   0x1
+#define   VC0_8021QF_CTRL_CHANGE_VID   0x2
+#define   VC0_8021QF_CTRL_CHANGE_BOTH  0x3
+#define   VC0_RESERVED_1               BIT(1)
+#define   VC0_DROP_VID_MISS            BIT(4)
+#define   VC0_VID_HASH_VID             BIT(5)
+#define   VC0_VID_CHK_EN               BIT(6)  /* Use VID,DA or VID,SA */
+#define   VC0_VLAN_EN                  BIT(7)  /* 802.1Q VLAN Enabled */
+
+/* VLAN Control 1 (8 bit) */
+#define B53_VLAN_CTRL1                 0x01
+#define   VC1_RX_MCST_TAG_EN           BIT(1)
+#define   VC1_RX_MCST_FWD_EN           BIT(2)
+#define   VC1_RX_MCST_UNTAG_EN         BIT(3)
+
+/* VLAN Control 2 (8 bit) */
+#define B53_VLAN_CTRL2                 0x02
+
+/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
+#define B53_VLAN_CTRL3                 0x03
+#define B53_VLAN_CTRL3_63XX            0x04
+#define   VC3_MAXSIZE_1532             BIT(6) /* 5325 only */
+#define   VC3_HIGH_8BIT_EN             BIT(7) /* 5325 only */
+
+/* VLAN Control 4 (8 bit) */
+#define B53_VLAN_CTRL4                 0x05
+#define B53_VLAN_CTRL4_25              0x04
+#define B53_VLAN_CTRL4_63XX            0x06
+#define   VC4_ING_VID_CHECK_S          6
+#define   VC4_ING_VID_CHECK_MASK       (0x3 << VC4_ING_VID_CHECK_S)
+#define   VC4_ING_VID_VIO_FWD          0 /* forward, but do not learn */
+#define   VC4_ING_VID_VIO_DROP         1 /* drop VID violations */
+#define   VC4_NO_ING_VID_CHK           2 /* do not check */
+#define   VC4_ING_VID_VIO_TO_IMP       3 /* redirect to MII port */
+
+/* VLAN Control 5 (8 bit) */
+#define B53_VLAN_CTRL5                 0x06
+#define B53_VLAN_CTRL5_25              0x05
+#define B53_VLAN_CTRL5_63XX            0x07
+#define   VC5_VID_FFF_EN               BIT(2)
+#define   VC5_DROP_VTABLE_MISS         BIT(3)
+
+/* VLAN Control 6 (8 bit) */
+#define B53_VLAN_CTRL6                 0x07
+#define B53_VLAN_CTRL6_63XX            0x08
+
+/* VLAN Table Access Register (16 bit) */
+#define B53_VLAN_TABLE_ACCESS_25       0x06    /* BCM5325E/5350 */
+#define B53_VLAN_TABLE_ACCESS_65       0x08    /* BCM5365 */
+#define   VTA_VID_LOW_MASK_25          0xf
+#define   VTA_VID_LOW_MASK_65          0xff
+#define   VTA_VID_HIGH_S_25            4
+#define   VTA_VID_HIGH_S_65            8
+#define   VTA_VID_HIGH_MASK_25         (0xff << VTA_VID_HIGH_S_25E)
+#define   VTA_VID_HIGH_MASK_65         (0xf << VTA_VID_HIGH_S_65)
+#define   VTA_RW_STATE                 BIT(12)
+#define   VTA_RW_STATE_RD              0
+#define   VTA_RW_STATE_WR              BIT(12)
+#define   VTA_RW_OP_EN                 BIT(13)
+
+/* VLAN Read/Write Registers for (16/32 bit) */
+#define B53_VLAN_WRITE_25              0x08
+#define B53_VLAN_WRITE_65              0x0a
+#define B53_VLAN_READ                  0x0c
+#define   VA_MEMBER_MASK               0x3f
+#define   VA_UNTAG_S_25                        6
+#define   VA_UNTAG_MASK_25             0x3f
+#define   VA_UNTAG_S_65                        7
+#define   VA_UNTAG_MASK_65             0x1f
+#define   VA_VID_HIGH_S                        12
+#define   VA_VID_HIGH_MASK             (0xffff << VA_VID_HIGH_S)
+#define   VA_VALID_25                  BIT(20)
+#define   VA_VALID_25_R4               BIT(24)
+#define   VA_VALID_65                  BIT(14)
+
+/* VLAN Port Default Tag (16 bit) */
+#define B53_VLAN_PORT_DEF_TAG(i)       (0x10 + 2 * (i))
+
+/*************************************************************************
+ * Jumbo Frame Page Registers
+ *************************************************************************/
+
+/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
+#define B53_JUMBO_PORT_MASK            0x01
+#define B53_JUMBO_PORT_MASK_63XX       0x04
+#define   JPM_10_100_JUMBO_EN          BIT(24) /* GigE always enabled */
+
+/* Good Frame Max Size without 802.1Q TAG (16 bit) */
+#define B53_JUMBO_MAX_SIZE             0x05
+#define B53_JUMBO_MAX_SIZE_63XX                0x08
+#define   JMS_MIN_SIZE                 1518
+#define   JMS_MAX_SIZE                 9724
+
+/*************************************************************************
+ * CFP Configuration Page Registers
+ *************************************************************************/
+
+/* CFP Control Register with ports map (8 bit) */
+#define B53_CFP_CTRL                   0x00
+
+#endif /* !__B53_REGS_H */
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_spi.c
new file mode 100644 (file)
index 0000000..efc8f7e
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * B53 register access through SPI
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+#define B53_SPI_DATA           0xf0
+
+#define B53_SPI_STATUS         0xfe
+#define B53_SPI_CMD_SPIF       BIT(7)
+#define B53_SPI_CMD_RACK       BIT(5)
+
+#define B53_SPI_CMD_READ       0x00
+#define B53_SPI_CMD_WRITE      0x01
+#define B53_SPI_CMD_NORMAL     0x60
+#define B53_SPI_CMD_FAST       0x10
+
+#define B53_SPI_PAGE_SELECT    0xff
+
+static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
+                                    unsigned len)
+{
+       u8 txbuf[2];
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
+       txbuf[1] = reg;
+
+       return spi_write_then_read(spi, txbuf, 2, val, len);
+}
+
+static inline int b53_spi_clear_status(struct spi_device *spi)
+{
+       unsigned int i;
+       u8 rxbuf;
+       int ret;
+
+       for (i = 0; i < 10; i++) {
+               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+               if (ret)
+                       return ret;
+
+               if (!(rxbuf & B53_SPI_CMD_SPIF))
+                       break;
+
+               mdelay(1);
+       }
+
+       if (i == 10)
+               return -EIO;
+
+       return 0;
+}
+
+static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
+{
+       u8 txbuf[3];
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = B53_SPI_PAGE_SELECT;
+       txbuf[2] = page;
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
+{
+       int ret = b53_spi_clear_status(spi);
+
+       if (ret)
+               return ret;
+
+       return b53_spi_set_page(spi, page);
+}
+
+static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
+{
+       u8 rxbuf;
+       int retry_count;
+       int ret;
+
+       ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
+       if (ret)
+               return ret;
+
+       for (retry_count = 0; retry_count < 10; retry_count++) {
+               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+               if (ret)
+                       return ret;
+
+               if (rxbuf & B53_SPI_CMD_RACK)
+                       break;
+
+               mdelay(1);
+       }
+
+       if (retry_count == 10)
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
+                       unsigned len)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       ret = b53_spi_prepare_reg_read(spi, reg);
+       if (ret)
+               return ret;
+
+       return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
+}
+
+static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       return b53_spi_read(dev, page, reg, val, 1);
+}
+
+static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
+
+       if (!ret)
+               *val = le16_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
+
+       if (!ret)
+               *val = le32_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       *val = 0;
+       ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
+       if (!ret)
+               *val = le64_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
+
+       if (!ret)
+               *val = le64_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[3];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       txbuf[2] = value;
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[4];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le16(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[6];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le32(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[10];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le64(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf) - 2);
+}
+
+static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[10];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le64(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static struct b53_io_ops b53_spi_ops = {
+       .read8 = b53_spi_read8,
+       .read16 = b53_spi_read16,
+       .read32 = b53_spi_read32,
+       .read48 = b53_spi_read48,
+       .read64 = b53_spi_read64,
+       .write8 = b53_spi_write8,
+       .write16 = b53_spi_write16,
+       .write32 = b53_spi_write32,
+       .write48 = b53_spi_write48,
+       .write64 = b53_spi_write64,
+};
+
+static int b53_spi_probe(struct spi_device *spi)
+{
+       struct b53_device *dev;
+       int ret;
+
+       dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);
+       if (!dev)
+               return -ENOMEM;
+
+       if (spi->dev.platform_data)
+               dev->pdata = spi->dev.platform_data;
+
+       ret = b53_switch_register(dev);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, dev);
+
+       return 0;
+}
+
+static int b53_spi_remove(struct spi_device *spi)
+{
+       struct b53_device *dev = spi_get_drvdata(spi);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static const struct of_device_id b53_of_match[] = {
+       { .compatible = "brcm,bcm5325" },
+       { .compatible = "brcm,bcm53115" },
+       { .compatible = "brcm,bcm53125" },
+       { .compatible = "brcm,bcm53128" },
+       { .compatible = "brcm,bcm5365" },
+       { .compatible = "brcm,bcm5395" },
+       { .compatible = "brcm,bcm5397" },
+       { .compatible = "brcm,bcm5398" },
+       { /* sentinel */ },
+};
+
+static struct spi_driver b53_spi_driver = {
+       .driver = {
+               .name   = "b53-switch",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+               .of_match_table = b53_of_match,
+       },
+       .probe  = b53_spi_probe,
+       .remove = b53_spi_remove,
+};
+
+module_spi_driver(b53_spi_driver);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 SPI access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_srab.c b/target/linux/generic/files-3.18/drivers/net/phy/b53/b53_srab.c
new file mode 100644 (file)
index 0000000..012daa3
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * B53 register access through Switch Register Access Bridge Registers
+ *
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+/* command and status register of the SRAB */
+#define B53_SRAB_CMDSTAT               0x2c
+#define  B53_SRAB_CMDSTAT_RST          BIT(2)
+#define  B53_SRAB_CMDSTAT_WRITE                BIT(1)
+#define  B53_SRAB_CMDSTAT_GORDYN       BIT(0)
+#define  B53_SRAB_CMDSTAT_PAGE         24
+#define  B53_SRAB_CMDSTAT_REG          16
+
+/* high order word of write data to switch registe */
+#define B53_SRAB_WD_H                  0x30
+
+/* low order word of write data to switch registe */
+#define B53_SRAB_WD_L                  0x34
+
+/* high order word of read data from switch register */
+#define B53_SRAB_RD_H                  0x38
+
+/* low order word of read data from switch register */
+#define B53_SRAB_RD_L                  0x3c
+
+/* command and status register of the SRAB */
+#define B53_SRAB_CTRLS                 0x40
+#define  B53_SRAB_CTRLS_RCAREQ         BIT(3)
+#define  B53_SRAB_CTRLS_RCAGNT         BIT(4)
+#define  B53_SRAB_CTRLS_SW_INIT_DONE   BIT(6)
+
+/* the register captures interrupt pulses from the switch */
+#define B53_SRAB_INTR                  0x44
+
+static int b53_srab_request_grant(struct b53_device *dev)
+{
+       u8 __iomem *regs = dev->priv;
+       u32 ctrls;
+       int i;
+
+       ctrls = readl(regs + B53_SRAB_CTRLS);
+       ctrls |= B53_SRAB_CTRLS_RCAREQ;
+       writel(ctrls, regs + B53_SRAB_CTRLS);
+
+       for (i = 0; i < 20; i++) {
+               ctrls = readl(regs + B53_SRAB_CTRLS);
+               if (ctrls & B53_SRAB_CTRLS_RCAGNT)
+                       break;
+               usleep_range(10, 100);
+       }
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static void b53_srab_release_grant(struct b53_device *dev)
+{
+       u8 __iomem *regs = dev->priv;
+       u32 ctrls;
+
+       ctrls = readl(regs + B53_SRAB_CTRLS);
+       ctrls &= ~B53_SRAB_CTRLS_RCAREQ;
+       writel(ctrls, regs + B53_SRAB_CTRLS);
+}
+
+static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)
+{
+       int i;
+       u32 cmdstat;
+       u8 __iomem *regs = dev->priv;
+
+       /* set register address */
+       cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |
+                 (reg << B53_SRAB_CMDSTAT_REG) |
+                 B53_SRAB_CMDSTAT_GORDYN |
+                 op;
+       writel(cmdstat, regs + B53_SRAB_CMDSTAT);
+
+       /* check if operation completed */
+       for (i = 0; i < 5; ++i) {
+               cmdstat = readl(regs + B53_SRAB_CMDSTAT);
+               if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))
+                       break;
+               usleep_range(10, 100);
+       }
+
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L) & 0xff;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L) & 0xffff;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+       *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+       *val += (u64)readl(regs + B53_SRAB_RD_H) << 32;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+
+}
+
+static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel((u32)value, regs + B53_SRAB_WD_L);
+       writel((u16)(value >> 32), regs + B53_SRAB_WD_H);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+
+}
+
+static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel((u32)value, regs + B53_SRAB_WD_L);
+       writel((u32)(value >> 32), regs + B53_SRAB_WD_H);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static struct b53_io_ops b53_srab_ops = {
+       .read8 = b53_srab_read8,
+       .read16 = b53_srab_read16,
+       .read32 = b53_srab_read32,
+       .read48 = b53_srab_read48,
+       .read64 = b53_srab_read64,
+       .write8 = b53_srab_write8,
+       .write16 = b53_srab_write16,
+       .write32 = b53_srab_write32,
+       .write48 = b53_srab_write48,
+       .write64 = b53_srab_write64,
+};
+
+static int b53_srab_probe(struct platform_device *pdev)
+{
+       struct b53_platform_data *pdata = pdev->dev.platform_data;
+       struct b53_device *dev;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
+       if (!dev)
+               return -ENOMEM;
+
+       if (pdata)
+               dev->pdata = pdata;
+
+       platform_set_drvdata(pdev, dev);
+
+       return b53_switch_register(dev);
+}
+
+static int b53_srab_remove(struct platform_device *pdev)
+{
+       struct b53_device *dev = platform_get_drvdata(pdev);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static struct platform_driver b53_srab_driver = {
+       .probe = b53_srab_probe,
+       .remove = b53_srab_remove,
+       .driver = {
+               .name = "b53-srab-switch",
+       },
+};
+
+module_platform_driver(b53_srab_driver);
+MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
+MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/ip17xx.c b/target/linux/generic/files-3.18/drivers/net/phy/ip17xx.c
new file mode 100644 (file)
index 0000000..85a9617
--- /dev/null
@@ -0,0 +1,1377 @@
+/*
+ * ip17xx.c: Swconfig configuration for IC+ IP17xx switch family
+ *
+ * Copyright (C) 2008 Patrick Horn <patrick.horn@gmail.com>
+ * Copyright (C) 2008, 2010 Martin Mares <mj@ucw.cz>
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/device.h>
+
+#define MAX_VLANS 16
+#define MAX_PORTS 9
+#undef DUMP_MII_IO
+
+typedef struct ip17xx_reg {
+       u16 p;                  // phy
+       u16 m;                  // mii
+} reg;
+typedef char bitnum;
+
+#define NOTSUPPORTED {-1,-1}
+
+#define REG_SUPP(x) (((x).m != ((u16)-1)) && ((x).p != (u16)-1))
+
+struct ip17xx_state;
+
+/*********** CONSTANTS ***********/
+struct register_mappings {
+       char *NAME;
+       u16 MODEL_NO;                   // Compare to bits 4-9 of MII register 0,3.
+       bitnum NUM_PORTS;
+       bitnum CPU_PORT;
+
+/* The default VLAN for each port.
+        Default: 0x0001 for Ports 0,1,2,3
+                 0x0002 for Ports 4,5 */
+       reg VLAN_DEFAULT_TAG_REG[MAX_PORTS];
+
+/* These ports are tagged.
+        Default: 0x00 */
+       reg ADD_TAG_REG;
+       reg REMOVE_TAG_REG;
+       bitnum ADD_TAG_BIT[MAX_PORTS];
+/* These ports are untagged.
+        Default: 0x00 (i.e. do not alter any VLAN tags...)
+        Maybe set to 0 if user disables VLANs. */
+       bitnum REMOVE_TAG_BIT[MAX_PORTS];
+
+/* Port M and Port N are on the same VLAN.
+        Default: All ports on all VLANs. */
+// Use register {29, 19+N/2}
+       reg VLAN_LOOKUP_REG;
+// Port 5 uses register {30, 18} but same as odd bits.
+       reg VLAN_LOOKUP_REG_5;          // in a different register on IP175C.
+       bitnum VLAN_LOOKUP_EVEN_BIT[MAX_PORTS];
+       bitnum VLAN_LOOKUP_ODD_BIT[MAX_PORTS];
+
+/* This VLAN corresponds to which ports.
+        Default: 0x2f,0x30,0x3f,0x3f... */
+       reg TAG_VLAN_MASK_REG;
+       bitnum TAG_VLAN_MASK_EVEN_BIT[MAX_PORTS];
+       bitnum TAG_VLAN_MASK_ODD_BIT[MAX_PORTS];
+
+       int RESET_VAL;
+       reg RESET_REG;
+
+       reg MODE_REG;
+       int MODE_VAL;
+
+/* General flags */
+       reg ROUTER_CONTROL_REG;
+       reg VLAN_CONTROL_REG;
+       bitnum TAG_VLAN_BIT;
+       bitnum ROUTER_EN_BIT;
+       bitnum NUMLAN_GROUPS_MAX;
+       bitnum NUMLAN_GROUPS_BIT;
+
+       reg MII_REGISTER_EN;
+       bitnum MII_REGISTER_EN_BIT;
+
+       // set to 1 for 178C, 0 for 175C.
+       bitnum SIMPLE_VLAN_REGISTERS;   // 175C has two vlans per register but 178C has only one.
+
+       // Pointers to functions which manipulate hardware state
+       int (*update_state)(struct ip17xx_state *state);
+       int (*set_vlan_mode)(struct ip17xx_state *state);
+       int (*reset)(struct ip17xx_state *state);
+};
+
+static int ip175c_update_state(struct ip17xx_state *state);
+static int ip175c_set_vlan_mode(struct ip17xx_state *state);
+static int ip175c_reset(struct ip17xx_state *state);
+
+static const struct register_mappings IP178C = {
+       .NAME = "IP178C",
+       .MODEL_NO = 0x18,
+       .VLAN_DEFAULT_TAG_REG = {
+               {30,3},{30,4},{30,5},{30,6},{30,7},{30,8},
+               {30,9},{30,10},{30,11},
+       },
+
+       .ADD_TAG_REG = {30,12},
+       .ADD_TAG_BIT = {0,1,2,3,4,5,6,7,8},
+       .REMOVE_TAG_REG = {30,13},
+       .REMOVE_TAG_BIT = {4,5,6,7,8,9,10,11,12},
+
+       .SIMPLE_VLAN_REGISTERS = 1,
+
+       .VLAN_LOOKUP_REG = {31,0},// +N
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, // not used with SIMPLE_VLAN_REGISTERS
+       .VLAN_LOOKUP_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,5,6,7,8},
+
+       .TAG_VLAN_MASK_REG = {30,14}, // +N
+       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
+       .TAG_VLAN_MASK_ODD_BIT = {0,1,2,3,4,5,6,7,8},
+
+       .RESET_VAL = 0x55AA,
+       .RESET_REG = {30,0},
+       .MODE_VAL = 0,
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = {30,30},
+       .ROUTER_EN_BIT = 11,
+       .NUMLAN_GROUPS_MAX = 8,
+       .NUMLAN_GROUPS_BIT = 8, // {0-2}
+
+       .VLAN_CONTROL_REG = {30,13},
+       .TAG_VLAN_BIT = 3,
+
+       .CPU_PORT = 8,
+       .NUM_PORTS = 9,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+static const struct register_mappings IP175C = {
+       .NAME = "IP175C",
+       .MODEL_NO = 0x18,
+       .VLAN_DEFAULT_TAG_REG = {
+               {29,24},{29,25},{29,26},{29,27},{29,28},{29,30},
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
+       },
+
+       .ADD_TAG_REG = {29,23},
+       .REMOVE_TAG_REG = {29,23},
+       .ADD_TAG_BIT = {11,12,13,14,15,1,-1,-1,-1},
+       .REMOVE_TAG_BIT = {6,7,8,9,10,0,-1,-1,-1},
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       .VLAN_LOOKUP_REG = {29,19},// +N/2
+       .VLAN_LOOKUP_REG_5 = {30,18},
+       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,15,-1,-1,-1},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,7,-1,-1,-1},
+
+       .TAG_VLAN_MASK_REG = {30,1}, // +N/2
+       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,-1,-1,-1},
+       .TAG_VLAN_MASK_ODD_BIT = {8,9,10,11,12,13,-1,-1,-1},
+
+       .RESET_VAL = 0x175C,
+       .RESET_REG = {30,0},
+       .MODE_VAL = 0x175C,
+       .MODE_REG = {29,31},
+
+       .ROUTER_CONTROL_REG = {30,9},
+       .ROUTER_EN_BIT = 3,
+       .NUMLAN_GROUPS_MAX = 8,
+       .NUMLAN_GROUPS_BIT = 0, // {0-2}
+
+       .VLAN_CONTROL_REG = {30,9},
+       .TAG_VLAN_BIT = 7,
+
+       .NUM_PORTS = 6,
+       .CPU_PORT = 5,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+static const struct register_mappings IP175A = {
+       .NAME = "IP175A",
+       .MODEL_NO = 0x05,
+       .VLAN_DEFAULT_TAG_REG = {
+               {0,24},{0,25},{0,26},{0,27},{0,28},NOTSUPPORTED,
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
+       },
+
+       .ADD_TAG_REG = {0,23},
+       .REMOVE_TAG_REG = {0,23},
+       .ADD_TAG_BIT = {11,12,13,14,15,-1,-1,-1,-1},
+       .REMOVE_TAG_BIT = {6,7,8,9,10,-1,-1,-1,-1},
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       // Only programmable via EEPROM
+       .VLAN_LOOKUP_REG = NOTSUPPORTED,// +N/2
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
+       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,-1,-1,-1,-1},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,-1,-1,-1,-1},
+
+       .TAG_VLAN_MASK_REG = NOTSUPPORTED, // +N/2,
+       .TAG_VLAN_MASK_EVEN_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
+       .TAG_VLAN_MASK_ODD_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
+
+       .RESET_VAL = -1,
+       .RESET_REG = NOTSUPPORTED,
+       .MODE_VAL = 0,
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = NOTSUPPORTED,
+       .VLAN_CONTROL_REG = NOTSUPPORTED,
+       .TAG_VLAN_BIT = -1,
+       .ROUTER_EN_BIT = -1,
+       .NUMLAN_GROUPS_MAX = -1,
+       .NUMLAN_GROUPS_BIT = -1, // {0-2}
+
+       .NUM_PORTS = 5,
+       .CPU_PORT = 4,
+
+       .MII_REGISTER_EN = {0, 18},
+       .MII_REGISTER_EN_BIT = 7,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+
+static int ip175d_update_state(struct ip17xx_state *state);
+static int ip175d_set_vlan_mode(struct ip17xx_state *state);
+static int ip175d_reset(struct ip17xx_state *state);
+
+static const struct register_mappings IP175D = {
+       .NAME = "IP175D",
+       .MODEL_NO = 0x18,
+
+       // The IP175D has a completely different interface, so we leave most
+       // of the registers undefined and switch to different code paths.
+
+       .VLAN_DEFAULT_TAG_REG = {
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
+       },
+
+       .ADD_TAG_REG = NOTSUPPORTED,
+       .REMOVE_TAG_REG = NOTSUPPORTED,
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       .VLAN_LOOKUP_REG = NOTSUPPORTED,
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
+       .TAG_VLAN_MASK_REG = NOTSUPPORTED,
+
+       .RESET_VAL = 0x175D,
+       .RESET_REG = {20,2},
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = NOTSUPPORTED,
+       .ROUTER_EN_BIT = -1,
+       .NUMLAN_GROUPS_BIT = -1,
+
+       .VLAN_CONTROL_REG = NOTSUPPORTED,
+       .TAG_VLAN_BIT = -1,
+
+       .NUM_PORTS = 6,
+       .CPU_PORT = 5,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175d_update_state,
+       .set_vlan_mode = ip175d_set_vlan_mode,
+       .reset = ip175d_reset,
+};
+
+struct ip17xx_state {
+       struct switch_dev dev;
+       struct mii_bus *mii_bus;
+       bool registered;
+
+       int router_mode;                // ROUTER_EN
+       int vlan_enabled;               // TAG_VLAN_EN
+       struct port_state {
+               u16 pvid;
+               unsigned int shareports;
+       } ports[MAX_PORTS];
+       unsigned int add_tag;
+       unsigned int remove_tag;
+       int num_vlans;
+       struct vlan_state {
+               unsigned int ports;
+               unsigned int tag;       // VLAN tag (IP175D only)
+       } vlans[MAX_VLANS];
+       const struct register_mappings *regs;
+       reg proc_mii;   // phy/reg for the low level register access via swconfig
+
+       char buf[80];
+};
+
+#define get_state(_dev) container_of((_dev), struct ip17xx_state, dev)
+
+static int ip_phy_read(struct ip17xx_state *state, int port, int reg)
+{
+       int val = mdiobus_read(state->mii_bus, port, reg);
+       if (val < 0)
+               pr_warning("IP17xx: Unable to get MII register %d,%d: error %d\n", port, reg, -val);
+#ifdef DUMP_MII_IO
+       else
+               pr_debug("IP17xx: Read MII(%d,%d) -> %04x\n", port, reg, val);
+#endif
+       return val;
+}
+
+static int ip_phy_write(struct ip17xx_state *state, int port, int reg, u16 val)
+{
+       int err;
+
+#ifdef DUMP_MII_IO
+       pr_debug("IP17xx: Write MII(%d,%d) <- %04x\n", port, reg, val);
+#endif
+       err = mdiobus_write(state->mii_bus, port, reg, val);
+       if (err < 0)
+               pr_warning("IP17xx: Unable to write MII register %d,%d: error %d\n", port, reg, -err);
+       return err;
+}
+
+static int ip_phy_write_masked(struct ip17xx_state *state, int port, int reg, unsigned int mask, unsigned int data)
+{
+       int val = ip_phy_read(state, port, reg);
+       if (val < 0)
+               return 0;
+       return ip_phy_write(state, port, reg, (val & ~mask) | data);
+}
+
+static int getPhy(struct ip17xx_state *state, reg mii)
+{
+       if (!REG_SUPP(mii))
+               return -EFAULT;
+       return ip_phy_read(state, mii.p, mii.m);
+}
+
+static int setPhy(struct ip17xx_state *state, reg mii, u16 value)
+{
+       int err;
+
+       if (!REG_SUPP(mii))
+               return -EFAULT;
+       err = ip_phy_write(state, mii.p, mii.m, value);
+       if (err < 0)
+               return err;
+       mdelay(2);
+       getPhy(state, mii);
+       return 0;
+}
+
+
+/**
+ * These two macros are to simplify the mapping of logical bits to the bits in hardware.
+ * NOTE: these macros will return if there is an error!
+ */
+
+#define GET_PORT_BITS(state, bits, addr, bit_lookup)           \
+       do {                                                    \
+               int i, val = getPhy((state), (addr));           \
+               if (val < 0)                                    \
+                       return val;                             \
+               (bits) = 0;                                     \
+               for (i = 0; i < MAX_PORTS; i++) {               \
+                       if ((bit_lookup)[i] == -1) continue;    \
+                       if (val & (1<<(bit_lookup)[i]))         \
+                               (bits) |= (1<<i);               \
+               }                                               \
+       } while (0)
+
+#define SET_PORT_BITS(state, bits, addr, bit_lookup)           \
+       do {                                                    \
+               int i, val = getPhy((state), (addr));           \
+               if (val < 0)                                    \
+                       return val;                             \
+               for (i = 0; i < MAX_PORTS; i++) {               \
+                       unsigned int newmask = ((bits)&(1<<i)); \
+                       if ((bit_lookup)[i] == -1) continue;    \
+                       val &= ~(1<<(bit_lookup)[i]);           \
+                       val |= ((newmask>>i)<<(bit_lookup)[i]); \
+               }                                               \
+               val = setPhy((state), (addr), val);             \
+               if (val < 0)                                    \
+                       return val;                             \
+       } while (0)
+
+
+static int get_model(struct ip17xx_state *state)
+{
+       int id1, id2;
+       int oui_id, model_no, rev_no, chip_no;
+
+       id1 = ip_phy_read(state, 0, 2);
+       id2 = ip_phy_read(state, 0, 3);
+       oui_id = (id1 << 6) | ((id2 >> 10) & 0x3f);
+       model_no = (id2 >> 4) & 0x3f;
+       rev_no = id2 & 0xf;
+       pr_debug("IP17xx: Identified oui=%06x model=%02x rev=%X\n", oui_id, model_no, rev_no);
+
+       if (oui_id != 0x0090c3)  // No other oui_id should have reached us anyway
+               return -ENODEV;
+
+       if (model_no == IP175A.MODEL_NO) {
+               state->regs = &IP175A;
+       } else if (model_no == IP175C.MODEL_NO) {
+               /*
+                *  Several models share the same model_no:
+                *  178C has more PHYs, so we try whether the device responds to a read from PHY5
+                *  175D has a new chip ID register
+                *  175C has neither
+                */
+               if (ip_phy_read(state, 5, 2) == 0x0243) {
+                       state->regs = &IP178C;
+               } else {
+                       chip_no = ip_phy_read(state, 20, 0);
+                       pr_debug("IP17xx: Chip ID register reads %04x\n", chip_no);
+                       if (chip_no == 0x175d) {
+                               state->regs = &IP175D;
+                       } else {
+                               state->regs = &IP175C;
+                       }
+               }
+       } else {
+               pr_warning("IP17xx: Found an unknown IC+ switch with model number %02x, revision %X.\n", model_no, rev_no);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/*** Low-level functions for the older models ***/
+
+/** Only set vlan and router flags in the switch **/
+static int ip175c_set_flags(struct ip17xx_state *state)
+{
+       int val;
+
+       if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) {
+               return 0;
+       }
+
+       val = getPhy(state, state->regs->ROUTER_CONTROL_REG);
+       if (val < 0) {
+               return val;
+       }
+       if (state->regs->ROUTER_EN_BIT >= 0) {
+               if (state->router_mode) {
+                       val |= (1<<state->regs->ROUTER_EN_BIT);
+               } else {
+                       val &= (~(1<<state->regs->ROUTER_EN_BIT));
+               }
+       }
+       if (state->regs->TAG_VLAN_BIT >= 0) {
+               if (state->vlan_enabled) {
+                       val |= (1<<state->regs->TAG_VLAN_BIT);
+               } else {
+                       val &= (~(1<<state->regs->TAG_VLAN_BIT));
+               }
+       }
+       if (state->regs->NUMLAN_GROUPS_BIT >= 0) {
+               val &= (~((state->regs->NUMLAN_GROUPS_MAX-1)<<state->regs->NUMLAN_GROUPS_BIT));
+               if (state->num_vlans > state->regs->NUMLAN_GROUPS_MAX) {
+                       val |= state->regs->NUMLAN_GROUPS_MAX << state->regs->NUMLAN_GROUPS_BIT;
+               } else if (state->num_vlans >= 1) {
+                       val |= (state->num_vlans-1) << state->regs->NUMLAN_GROUPS_BIT;
+               }
+       }
+       return setPhy(state, state->regs->ROUTER_CONTROL_REG, val);
+}
+
+/** Set all VLAN and port state.  Usually you should call "correct_vlan_state" first. **/
+static int ip175c_set_state(struct ip17xx_state *state)
+{
+       int j;
+       int i;
+       SET_PORT_BITS(state, state->add_tag,
+                                 state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT);
+       SET_PORT_BITS(state, state->remove_tag,
+                                 state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT);
+
+       if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) {
+               for (j=0; j<state->regs->NUM_PORTS; j++) {
+                       reg addr;
+                       const bitnum *bit_lookup = (j%2==0)?
+                               state->regs->VLAN_LOOKUP_EVEN_BIT:
+                               state->regs->VLAN_LOOKUP_ODD_BIT;
+
+                       addr = state->regs->VLAN_LOOKUP_REG;
+                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
+                               addr.m += j;
+                       } else {
+                               switch (j) {
+                               case 0:
+                               case 1:
+                                       break;
+                               case 2:
+                               case 3:
+                                       addr.m+=1;
+                                       break;
+                               case 4:
+                                       addr.m+=2;
+                                       break;
+                               case 5:
+                                       addr = state->regs->VLAN_LOOKUP_REG_5;
+                                       break;
+                               default:
+                                       addr.m = -1; // shouldn't get here, but...
+                                       break;
+                               }
+                       }
+                       //printf("shareports for %d is %02X\n",j,state->ports[j].shareports);
+                       if (REG_SUPP(addr)) {
+                               SET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup);
+                       }
+               }
+       }
+       if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) {
+               for (j=0; j<MAX_VLANS; j++) {
+                       reg addr = state->regs->TAG_VLAN_MASK_REG;
+                       const bitnum *bit_lookup = (j%2==0)?
+                               state->regs->TAG_VLAN_MASK_EVEN_BIT:
+                               state->regs->TAG_VLAN_MASK_ODD_BIT;
+                       unsigned int vlan_mask;
+                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
+                               addr.m += j;
+                       } else {
+                               addr.m += j/2;
+                       }
+                       vlan_mask = state->vlans[j].ports;
+                       SET_PORT_BITS(state, vlan_mask, addr, bit_lookup);
+               }
+       }
+
+       for (i=0; i<MAX_PORTS; i++) {
+               if (REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[i])) {
+                       int err = setPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i],
+                                       state->ports[i].pvid);
+                       if (err < 0) {
+                               return err;
+                       }
+               }
+       }
+
+       return ip175c_set_flags(state);
+}
+
+/**
+ *  Uses only the VLAN port mask and the add tag mask to generate the other fields:
+ *  which ports are part of the same VLAN, removing vlan tags, and VLAN tag ids.
+ */
+static void ip175c_correct_vlan_state(struct ip17xx_state *state)
+{
+       int i, j;
+       state->num_vlans = 0;
+       for (i=0; i<MAX_VLANS; i++) {
+               if (state->vlans[i].ports != 0) {
+                       state->num_vlans = i+1; // Hack -- we need to store the "set" vlans somewhere...
+               }
+       }
+
+       for (i=0; i<state->regs->NUM_PORTS; i++) {
+               unsigned int portmask = (1<<i);
+               if (!state->vlan_enabled) {
+                       // Share with everybody!
+                       state->ports[i].shareports = (1<<state->regs->NUM_PORTS)-1;
+                       continue;
+               }
+               state->ports[i].shareports = portmask;
+               for (j=0; j<MAX_VLANS; j++) {
+                       if (state->vlans[j].ports & portmask)
+                               state->ports[i].shareports |= state->vlans[j].ports;
+               }
+       }
+}
+
+static int ip175c_update_state(struct ip17xx_state *state)
+{
+       ip175c_correct_vlan_state(state);
+       return ip175c_set_state(state);
+}
+
+static int ip175c_set_vlan_mode(struct ip17xx_state *state)
+{
+       return ip175c_update_state(state);
+}
+
+static int ip175c_reset(struct ip17xx_state *state)
+{
+       int err;
+
+       if (REG_SUPP(state->regs->MODE_REG)) {
+               err = setPhy(state, state->regs->MODE_REG, state->regs->MODE_VAL);
+               if (err < 0)
+                       return err;
+               err = getPhy(state, state->regs->MODE_REG);
+               if (err < 0)
+                       return err;
+       }
+
+       return ip175c_update_state(state);
+}
+
+/*** Low-level functions for IP175D ***/
+
+static int ip175d_update_state(struct ip17xx_state *state)
+{
+       unsigned int filter_mask = 0;
+       unsigned int ports[16], add[16], rem[16];
+       int i, j;
+       int err = 0;
+
+       for (i = 0; i < 16; i++) {
+               ports[i] = 0;
+               add[i] = 0;
+               rem[i] = 0;
+               if (!state->vlan_enabled) {
+                       err |= ip_phy_write(state, 22, 14+i, i+1);      // default tags
+                       ports[i] = 0x3f;
+                       continue;
+               }
+               if (!state->vlans[i].tag) {
+                       // Reset the filter
+                       err |= ip_phy_write(state, 22, 14+i, 0);        // tag
+                       continue;
+               }
+               filter_mask |= 1 << i;
+               err |= ip_phy_write(state, 22, 14+i, state->vlans[i].tag);
+               ports[i] = state->vlans[i].ports;
+               for (j = 0; j < 6; j++) {
+                       if (ports[i] & (1 << j)) {
+                               if (state->add_tag & (1 << j))
+                                       add[i] |= 1 << j;
+                               if (state->remove_tag & (1 << j))
+                                       rem[i] |= 1 << j;
+                       }
+               }
+       }
+
+       // Port masks, tag adds and removals
+       for (i = 0; i < 8; i++) {
+               err |= ip_phy_write(state, 23, i, ports[2*i] | (ports[2*i+1] << 8));
+               err |= ip_phy_write(state, 23, 8+i, add[2*i] | (add[2*i+1] << 8));
+               err |= ip_phy_write(state, 23, 16+i, rem[2*i] | (rem[2*i+1] << 8));
+       }
+       err |= ip_phy_write(state, 22, 10, filter_mask);
+
+       // Default VLAN tag for each port
+       for (i = 0; i < 6; i++)
+               err |= ip_phy_write(state, 22, 4+i, state->vlans[state->ports[i].pvid].tag);
+
+       return (err ? -EIO : 0);
+}
+
+static int ip175d_set_vlan_mode(struct ip17xx_state *state)
+{
+       int i;
+       int err = 0;
+
+       if (state->vlan_enabled) {
+               // VLAN classification rules: tag-based VLANs, use VID to classify,
+               // drop packets that cannot be classified.
+               err |= ip_phy_write_masked(state, 22, 0, 0x3fff, 0x003f);
+
+               // Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed,
+               // VID=0xfff discarded, admin both tagged and untagged, ingress
+               // filters enabled.
+               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
+
+               // Egress rules: IGMP processing off, keep VLAN header off
+               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
+       } else {
+               // VLAN classification rules: everything off & clear table
+               err |= ip_phy_write_masked(state, 22, 0, 0xbfff, 0x8000);
+
+               // Ingress and egress rules: set to defaults
+               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
+               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
+       }
+
+       // Reset default VLAN for each port to 0
+       for (i = 0; i < 6; i++)
+               state->ports[i].pvid = 0;
+
+       err |= ip175d_update_state(state);
+
+       return (err ? -EIO : 0);
+}
+
+static int ip175d_reset(struct ip17xx_state *state)
+{
+       int err = 0;
+
+       // Disable the special tagging mode
+       err |= ip_phy_write_masked(state, 21, 22, 0x0003, 0x0000);
+
+       // Set 802.1q protocol type
+       err |= ip_phy_write(state, 22, 3, 0x8100);
+
+       state->vlan_enabled = 0;
+       err |= ip175d_set_vlan_mode(state);
+
+       return (err ? -EIO : 0);
+}
+
+/*** High-level functions ***/
+
+static int ip17xx_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->vlan_enabled;
+       return 0;
+}
+
+static void ip17xx_reset_vlan_config(struct ip17xx_state *state)
+{
+       int i;
+
+       state->remove_tag = (state->vlan_enabled ? ((1<<state->regs->NUM_PORTS)-1) : 0x0000);
+       state->add_tag = 0x0000;
+       for (i = 0; i < MAX_VLANS; i++) {
+               state->vlans[i].ports = 0x0000;
+               state->vlans[i].tag = (i ? i : 16);
+       }
+       for (i = 0; i < MAX_PORTS; i++)
+               state->ports[i].pvid = 0;
+}
+
+static int ip17xx_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int enable;
+
+       enable = val->value.i;
+       if (state->vlan_enabled == enable) {
+               // Do not change any state.
+               return 0;
+       }
+       state->vlan_enabled = enable;
+
+       // Otherwise, if we are switching state, set fields to a known default.
+       ip17xx_reset_vlan_config(state);
+
+       return state->regs->set_vlan_mode(state);
+}
+
+static int ip17xx_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int b;
+       int ind;
+       unsigned int ports;
+
+       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
+               return -EINVAL;
+
+       ports = state->vlans[val->port_vlan].ports;
+       b = 0;
+       ind = 0;
+       while (b < MAX_PORTS) {
+               if (ports&1) {
+                       int istagged = ((state->add_tag >> b) & 1);
+                       val->value.ports[ind].id = b;
+                       val->value.ports[ind].flags = (istagged << SWITCH_PORT_FLAG_TAGGED);
+                       ind++;
+               }
+               b++;
+               ports >>= 1;
+       }
+       val->len = ind;
+
+       return 0;
+}
+
+static int ip17xx_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int i;
+
+       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
+               return -EINVAL;
+
+       state->vlans[val->port_vlan].ports = 0;
+       for (i = 0; i < val->len; i++) {
+               unsigned int bitmask = (1<<val->value.ports[i].id);
+               state->vlans[val->port_vlan].ports |= bitmask;
+               if (val->value.ports[i].flags & (1<<SWITCH_PORT_FLAG_TAGGED)) {
+                       state->add_tag |= bitmask;
+                       state->remove_tag &= (~bitmask);
+               } else {
+                       state->add_tag &= (~bitmask);
+                       state->remove_tag |= bitmask;
+               }
+       }
+
+       return state->regs->update_state(state);
+}
+
+static int ip17xx_apply(struct switch_dev *dev)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (REG_SUPP(state->regs->MII_REGISTER_EN)) {
+               int val = getPhy(state, state->regs->MII_REGISTER_EN);
+               if (val < 0) {
+                       return val;
+               }
+               val |= (1<<state->regs->MII_REGISTER_EN_BIT);
+               return setPhy(state, state->regs->MII_REGISTER_EN, val);
+       }
+       return 0;
+}
+
+static int ip17xx_reset(struct switch_dev *dev)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int i, err;
+
+       if (REG_SUPP(state->regs->RESET_REG)) {
+               err = setPhy(state, state->regs->RESET_REG, state->regs->RESET_VAL);
+               if (err < 0)
+                       return err;
+               err = getPhy(state, state->regs->RESET_REG);
+
+               /*
+                *  Data sheet specifies reset period to be 2 msec.
+                *  (I don't see any mention of the 2ms delay in the IP178C spec, only
+                *  in IP175C, but it can't hurt.)
+                */
+               mdelay(2);
+       }
+
+       /* reset switch ports */
+       for (i = 0; i < state->regs->NUM_PORTS-1; i++) {
+               err = ip_phy_write(state, i, MII_BMCR, BMCR_RESET);
+               if (err < 0)
+                       return err;
+       }
+
+       state->router_mode = 0;
+       state->vlan_enabled = 0;
+       ip17xx_reset_vlan_config(state);
+
+       return state->regs->reset(state);
+}
+
+static int ip17xx_get_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (state->add_tag & (1<<val->port_vlan)) {
+               if (state->remove_tag & (1<<val->port_vlan))
+                       val->value.i = 3; // shouldn't ever happen.
+               else
+                       val->value.i = 1;
+       } else {
+               if (state->remove_tag & (1<<val->port_vlan))
+                       val->value.i = 0;
+               else
+                       val->value.i = 2;
+       }
+       return 0;
+}
+
+static int ip17xx_set_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       state->add_tag &= ~(1<<val->port_vlan);
+       state->remove_tag &= ~(1<<val->port_vlan);
+
+       if (val->value.i == 0)
+               state->remove_tag |= (1<<val->port_vlan);
+       if (val->value.i == 1)
+               state->add_tag |= (1<<val->port_vlan);
+
+       return state->regs->update_state(state);
+}
+
+/** Get the current phy address */
+static int ip17xx_get_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->proc_mii.p;
+       return 0;
+}
+
+/** Set a new phy address for low level access to registers */
+static int ip17xx_set_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int new_reg = val->value.i;
+
+       if (new_reg < 0 || new_reg > 31)
+               state->proc_mii.p = (u16)-1;
+       else
+               state->proc_mii.p = (u16)new_reg;
+       return 0;
+}
+
+/** Get the current register number */
+static int ip17xx_get_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->proc_mii.m;
+       return 0;
+}
+
+/** Set a new register address for low level access to registers */
+static int ip17xx_set_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int new_reg = val->value.i;
+
+       if (new_reg < 0 || new_reg > 31)
+               state->proc_mii.m = (u16)-1;
+       else
+               state->proc_mii.m = (u16)new_reg;
+       return 0;
+}
+
+/** Get the register content of state->proc_mii */
+static int ip17xx_get_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int retval = -EINVAL;
+       if (REG_SUPP(state->proc_mii))
+               retval = getPhy(state, state->proc_mii);
+
+       if (retval < 0) {
+               return retval;
+       } else {
+               val->value.i = retval;
+               return 0;
+       }
+}
+
+/** Write a value to the register defined by phy/reg above */
+static int ip17xx_set_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int myval, err = -EINVAL;
+
+       myval = val->value.i;
+       if (myval <= 0xffff && myval >= 0 && REG_SUPP(state->proc_mii)) {
+               err = setPhy(state, state->proc_mii, (u16)myval);
+       }
+       return err;
+}
+
+static int ip17xx_read_name(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       val->value.s = state->regs->NAME; // Just a const pointer, won't be freed by swconfig.
+       return 0;
+}
+
+static int ip17xx_get_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int vlan = val->port_vlan;
+
+       if (vlan < 0 || vlan >= MAX_VLANS)
+               return -EINVAL;
+
+       val->value.i = state->vlans[vlan].tag;
+       return 0;
+}
+
+static int ip17xx_set_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int vlan = val->port_vlan;
+       int tag = val->value.i;
+
+       if (vlan < 0 || vlan >= MAX_VLANS)
+               return -EINVAL;
+
+       if (tag < 0 || tag > 4095)
+               return -EINVAL;
+
+       state->vlans[vlan].tag = tag;
+       return state->regs->update_state(state);
+}
+
+static int ip17xx_set_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int nr = val->port_vlan;
+       int ctrl;
+       int autoneg;
+       int speed;
+       if (val->value.i == 100) {
+               speed = 1;
+               autoneg = 0;
+       } else if (val->value.i == 10) {
+               speed = 0;
+               autoneg = 0;
+       } else {
+               autoneg = 1;
+               speed = 1;
+       }
+
+       /* Can't set speed for cpu port */
+       if (nr == state->regs->CPU_PORT)
+               return -EINVAL;
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       ctrl = ip_phy_read(state, nr, 0);
+       if (ctrl < 0)
+               return -EIO;
+
+       ctrl &= (~(1<<12));
+       ctrl &= (~(1<<13));
+       ctrl |= (autoneg<<12);
+       ctrl |= (speed<<13);
+
+       return ip_phy_write(state, nr, 0, ctrl);
+}
+
+static int ip17xx_get_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int nr = val->port_vlan;
+       int speed, status;
+
+       if (nr == state->regs->CPU_PORT) {
+               val->value.i = 100;
+               return 0;
+       }
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       status = ip_phy_read(state, nr, 1);
+       speed = ip_phy_read(state, nr, 18);
+       if (status < 0 || speed < 0)
+               return -EIO;
+
+       if (status & 4)
+               val->value.i = ((speed & (1<<11)) ? 100 : 10);
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int ip17xx_get_port_status(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int ctrl, speed, status;
+       int nr = val->port_vlan;
+       int len;
+       char *buf = state->buf; // fixed-length at 80.
+
+       if (nr == state->regs->CPU_PORT) {
+               sprintf(buf, "up, 100 Mbps, cpu port");
+               val->value.s = buf;
+               return 0;
+       }
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       ctrl = ip_phy_read(state, nr, 0);
+       status = ip_phy_read(state, nr, 1);
+       speed = ip_phy_read(state, nr, 18);
+       if (ctrl < 0 || status < 0 || speed < 0)
+               return -EIO;
+
+       if (status & 4)
+               len = sprintf(buf, "up, %d Mbps, %s duplex",
+                       ((speed & (1<<11)) ? 100 : 10),
+                       ((speed & (1<<10)) ? "full" : "half"));
+       else
+               len = sprintf(buf, "down");
+
+       if (ctrl & (1<<12)) {
+               len += sprintf(buf+len, ", auto-negotiate");
+               if (!(status & (1<<5)))
+                       len += sprintf(buf+len, " (in progress)");
+       } else {
+               len += sprintf(buf+len, ", fixed speed (%d)",
+                       ((ctrl & (1<<13)) ? 100 : 10));
+       }
+
+       buf[len] = '\0';
+       val->value.s = buf;
+       return 0;
+}
+
+static int ip17xx_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       *val = state->ports[port].pvid;
+       return 0;
+}
+
+static int ip17xx_set_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (val < 0 || val >= MAX_VLANS)
+               return -EINVAL;
+
+       state->ports[port].pvid = val;
+       return state->regs->update_state(state);
+}
+
+
+enum Ports {
+       IP17XX_PORT_STATUS,
+       IP17XX_PORT_LINK,
+       IP17XX_PORT_TAGGED,
+       IP17XX_PORT_PVID,
+};
+
+enum Globals {
+       IP17XX_ENABLE_VLAN,
+       IP17XX_GET_NAME,
+       IP17XX_REGISTER_PHY,
+       IP17XX_REGISTER_MII,
+       IP17XX_REGISTER_VALUE,
+       IP17XX_REGISTER_ERRNO,
+};
+
+enum Vlans {
+       IP17XX_VLAN_TAG,
+};
+
+static const struct switch_attr ip17xx_global[] = {
+       [IP17XX_ENABLE_VLAN] = {
+               .id = IP17XX_ENABLE_VLAN,
+               .type = SWITCH_TYPE_INT,
+               .name  = "enable_vlan",
+               .description = "Flag to enable or disable VLANs and tagging",
+               .get  = ip17xx_get_enable_vlan,
+               .set = ip17xx_set_enable_vlan,
+       },
+       [IP17XX_GET_NAME] = {
+               .id = IP17XX_GET_NAME,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Returns the type of IC+ chip.",
+               .name  = "name",
+               .get  = ip17xx_read_name,
+               .set = NULL,
+       },
+       /* jal: added for low level debugging etc. */
+       [IP17XX_REGISTER_PHY] = {
+               .id = IP17XX_REGISTER_PHY,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: set PHY (0-4, or 29,30,31)",
+               .name  = "phy",
+               .get  = ip17xx_get_phy,
+               .set = ip17xx_set_phy,
+       },
+       [IP17XX_REGISTER_MII] = {
+               .id = IP17XX_REGISTER_MII,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: set MII register number (0-31)",
+               .name  = "reg",
+               .get  = ip17xx_get_reg,
+               .set = ip17xx_set_reg,
+       },
+       [IP17XX_REGISTER_VALUE] = {
+               .id = IP17XX_REGISTER_VALUE,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: read/write to register (0-65535)",
+               .name  = "val",
+               .get  = ip17xx_get_val,
+               .set = ip17xx_set_val,
+       },
+};
+
+static const struct switch_attr ip17xx_vlan[] = {
+       [IP17XX_VLAN_TAG] = {
+               .id = IP17XX_VLAN_TAG,
+               .type = SWITCH_TYPE_INT,
+               .description = "VLAN ID (0-4095) [IP175D only]",
+               .name = "vid",
+               .get = ip17xx_get_tag,
+               .set = ip17xx_set_tag,
+       }
+};
+
+static const struct switch_attr ip17xx_port[] = {
+       [IP17XX_PORT_STATUS] = {
+               .id = IP17XX_PORT_STATUS,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Returns Detailed port status",
+               .name  = "status",
+               .get  = ip17xx_get_port_status,
+               .set = NULL,
+       },
+       [IP17XX_PORT_LINK] = {
+               .id = IP17XX_PORT_LINK,
+               .type = SWITCH_TYPE_INT,
+               .description = "Link speed. Can write 0 for auto-negotiate, or 10 or 100",
+               .name  = "link",
+               .get  = ip17xx_get_port_speed,
+               .set = ip17xx_set_port_speed,
+       },
+       [IP17XX_PORT_TAGGED] = {
+               .id = IP17XX_PORT_LINK,
+               .type = SWITCH_TYPE_INT,
+               .description = "0 = untag, 1 = add tags, 2 = do not alter (This value is reset if vlans are altered)",
+               .name  = "tagged",
+               .get  = ip17xx_get_tagged,
+               .set = ip17xx_set_tagged,
+       },
+};
+
+static const struct switch_dev_ops ip17xx_ops = {
+       .attr_global = {
+               .attr = ip17xx_global,
+               .n_attr = ARRAY_SIZE(ip17xx_global),
+       },
+       .attr_port = {
+               .attr = ip17xx_port,
+               .n_attr = ARRAY_SIZE(ip17xx_port),
+       },
+       .attr_vlan = {
+               .attr = ip17xx_vlan,
+               .n_attr = ARRAY_SIZE(ip17xx_vlan),
+       },
+
+       .get_port_pvid = ip17xx_get_pvid,
+       .set_port_pvid = ip17xx_set_pvid,
+       .get_vlan_ports = ip17xx_get_ports,
+       .set_vlan_ports = ip17xx_set_ports,
+       .apply_config = ip17xx_apply,
+       .reset_switch = ip17xx_reset,
+};
+
+static int ip17xx_probe(struct phy_device *pdev)
+{
+       struct ip17xx_state *state;
+       struct switch_dev *dev;
+       int err;
+
+       /* We only attach to PHY 0, but use all available PHYs */
+       if (pdev->mdio.addr != 0)
+               return -ENODEV;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       dev = &state->dev;
+
+       pdev->priv = state;
+       state->mii_bus = pdev->mdio.bus;
+
+       err = get_model(state);
+       if (err < 0)
+               goto error;
+
+       dev->vlans = MAX_VLANS;
+       dev->cpu_port = state->regs->CPU_PORT;
+       dev->ports = state->regs->NUM_PORTS;
+       dev->name = state->regs->NAME;
+       dev->ops = &ip17xx_ops;
+
+       pr_info("IP17xx: Found %s at %s\n", dev->name, dev_name(&pdev->mdio.dev));
+       return 0;
+
+error:
+       kfree(state);
+       return err;
+}
+
+static int ip17xx_config_init(struct phy_device *pdev)
+{
+       struct ip17xx_state *state = pdev->priv;
+       struct net_device *dev = pdev->attached_dev;
+       int err;
+
+       err = register_switch(&state->dev, dev);
+       if (err < 0)
+               return err;
+
+       state->registered = true;
+       ip17xx_reset(&state->dev);
+       return 0;
+}
+
+static void ip17xx_remove(struct phy_device *pdev)
+{
+       struct ip17xx_state *state = pdev->priv;
+
+       if (state->registered)
+               unregister_switch(&state->dev);
+       kfree(state);
+}
+
+static int ip17xx_config_aneg(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static int ip17xx_aneg_done(struct phy_device *pdev)
+{
+       return 1;       /* Return any positive value */
+}
+
+static int ip17xx_update_link(struct phy_device *pdev)
+{
+       pdev->link = 1;
+       return 0;
+}
+
+static int ip17xx_read_status(struct phy_device *pdev)
+{
+       pdev->speed = SPEED_100;
+       pdev->duplex = DUPLEX_FULL;
+       pdev->pause = pdev->asym_pause = 0;
+       pdev->link = 1;
+
+       return 0;
+}
+
+static struct phy_driver ip17xx_driver[] = {
+       {
+               .name           = "IC+ IP17xx",
+               .phy_id         = 0x02430c00,
+               .phy_id_mask    = 0x0ffffc00,
+               .features       = PHY_BASIC_FEATURES,
+               .probe          = ip17xx_probe,
+               .remove         = ip17xx_remove,
+               .config_init    = ip17xx_config_init,
+               .config_aneg    = ip17xx_config_aneg,
+               .aneg_done      = ip17xx_aneg_done,
+               .update_link    = ip17xx_update_link,
+               .read_status    = ip17xx_read_status,
+       }
+};
+
+module_phy_driver(ip17xx_driver);
+
+MODULE_AUTHOR("Patrick Horn <patrick.horn@gmail.com>");
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_AUTHOR("Martin Mares <mj@ucw.cz>");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/mvsw61xx.c b/target/linux/generic/files-3.18/drivers/net/phy/mvsw61xx.c
new file mode 100644 (file)
index 0000000..9a689e6
--- /dev/null
@@ -0,0 +1,947 @@
+/*
+ * Marvell 88E61xx switch driver
+ *
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
+ *
+ * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "mvsw61xx.h"
+
+MODULE_DESCRIPTION("Marvell 88E61xx Switch driver");
+MODULE_AUTHOR("Claudio Leite <leitec@staticky.com>");
+MODULE_AUTHOR("Nikita Nazarenko <nnazarenko@radiofid.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mvsw61xx");
+
+/*
+ * Register access is done through direct or indirect addressing,
+ * depending on how the switch is physically connected.
+ *
+ * Direct addressing: all port and global registers directly
+ *   accessible via an address/register pair
+ *
+ * Indirect addressing: switch is mapped at a single address,
+ *   port and global registers accessible via a single command/data
+ *   register pair
+ */
+
+static int
+mvsw61xx_wait_mask_raw(struct mii_bus *bus, int addr,
+               int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = bus->read(bus, addr, reg);
+               if ((r & mask) == val)
+                       return 0;
+       } while (--i > 0);
+
+       return -ETIMEDOUT;
+}
+
+static u16
+r16(struct mii_bus *bus, bool indirect, int base_addr, int addr, int reg)
+{
+       u16 ind_addr;
+
+       if (!indirect)
+               return bus->read(bus, addr, reg);
+
+       /* Indirect read: First, make sure switch is free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load address and request read */
+       ind_addr = MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg;
+       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       ind_addr);
+
+       /* Wait until it's ready */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Read the requested data */
+       return bus->read(bus, base_addr, MV_INDIRECT_REG_DATA);
+}
+
+static void
+w16(struct mii_bus *bus, bool indirect, int base_addr, int addr,
+               int reg, u16 val)
+{
+       u16 ind_addr;
+
+       if (!indirect) {
+               bus->write(bus, addr, reg, val);
+               return;
+       }
+
+       /* Indirect write: First, make sure switch is free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load the data to be written */
+       bus->write(bus, base_addr, MV_INDIRECT_REG_DATA, val);
+
+       /* Wait again for switch to be free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load address, and issue write command */
+       ind_addr = MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg;
+       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       ind_addr);
+}
+
+/* swconfig support */
+
+static inline u16
+sr16(struct switch_dev *dev, int addr, int reg)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       return r16(state->bus, state->is_indirect, state->base_addr, addr, reg);
+}
+
+static inline void
+sw16(struct switch_dev *dev, int addr, int reg, u16 val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       w16(state->bus, state->is_indirect, state->base_addr, addr, reg, val);
+}
+
+static int
+mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr,
+               int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = sr16(dev, addr, reg) & mask;
+               if (r == val)
+                       return 0;
+       } while (--i > 0);
+
+       return -ETIMEDOUT;
+}
+
+static int
+mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg)
+{
+       sw16(dev, MV_GLOBAL2REG(SMI_OP),
+            MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg);
+
+       if (mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
+                                MV_INDIRECT_INPROGRESS, 0) < 0)
+               return -ETIMEDOUT;
+
+       return sr16(dev, MV_GLOBAL2REG(SMI_DATA));
+}
+
+static int
+mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val)
+{
+       sw16(dev, MV_GLOBAL2REG(SMI_DATA), val);
+
+       sw16(dev, MV_GLOBAL2REG(SMI_OP),
+            MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg);
+
+       return mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
+                                   MV_INDIRECT_INPROGRESS, 0) < 0;
+}
+
+static int
+mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg)
+{
+       int ret;
+
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
+       ret = mvsw61xx_mdio_read(dev, port, reg);
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
+
+       return ret;
+}
+
+static void
+mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg,
+                        u16 val)
+{
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
+       mvsw61xx_mdio_write(dev, port, reg, val);
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
+}
+
+static int
+mvsw61xx_get_port_mask(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       char *buf = state->buf;
+       int port, len, i;
+       u16 reg;
+
+       port = val->port_vlan;
+       reg = sr16(dev, MV_PORTREG(VLANMAP, port)) & MV_PORTS_MASK;
+
+       len = sprintf(buf, "0x%04x: ", reg);
+
+       for (i = 0; i < MV_PORTS; i++) {
+               if (reg & (1 << i))
+                       len += sprintf(buf + len, "%d ", i);
+               else if (i == port)
+                       len += sprintf(buf + len, "(%d) ", i);
+       }
+
+       val->value.s = buf;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_qmode(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->ports[val->port_vlan].qmode;
+
+       return 0;
+}
+
+static int
+mvsw61xx_set_port_qmode(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->ports[val->port_vlan].qmode = val->value.i;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       *val = state->ports[port].pvid;
+
+       return 0;
+}
+
+static int
+mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       if (val < 0 || val >= MV_VLANS)
+               return -EINVAL;
+
+       state->ports[port].pvid = (u16)val;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_link(struct switch_dev *dev, int port,
+               struct switch_port_link *link)
+{
+       u16 status, speed;
+
+       status = sr16(dev, MV_PORTREG(STATUS, port));
+
+       link->link = status & MV_PORT_STATUS_LINK;
+       if (!link->link)
+               return 0;
+
+       link->duplex = status & MV_PORT_STATUS_FDX;
+
+       speed = (status & MV_PORT_STATUS_SPEED_MASK) >>
+                       MV_PORT_STATUS_SPEED_SHIFT;
+
+       switch (speed) {
+       case MV_PORT_STATUS_SPEED_10:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case MV_PORT_STATUS_SPEED_100:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case MV_PORT_STATUS_SPEED_1000:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       }
+
+       return 0;
+}
+
+static int mvsw61xx_get_vlan_ports(struct switch_dev *dev,
+               struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, j, mode, vno;
+
+       vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       for (i = 0, j = 0; i < dev->ports; i++) {
+               if (state->vlans[vno].mask & (1 << i)) {
+                       val->value.ports[j].id = i;
+
+                       mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
+                       if (mode == MV_VTUCTL_EGRESS_TAGGED)
+                               val->value.ports[j].flags =
+                                       (1 << SWITCH_PORT_FLAG_TAGGED);
+                       else
+                               val->value.ports[j].flags = 0;
+
+                       j++;
+               }
+       }
+
+       val->len = j;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vlan_ports(struct switch_dev *dev,
+               struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, mode, pno, vno;
+
+       vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       state->vlans[vno].mask = 0;
+       state->vlans[vno].port_mode = 0;
+       state->vlans[vno].port_sstate = 0;
+
+       if(state->vlans[vno].vid == 0)
+               state->vlans[vno].vid = vno;
+
+       for (i = 0; i < val->len; i++) {
+               pno = val->value.ports[i].id;
+
+               state->vlans[vno].mask |= (1 << pno);
+               if (val->value.ports[i].flags &
+                               (1 << SWITCH_PORT_FLAG_TAGGED))
+                       mode = MV_VTUCTL_EGRESS_TAGGED;
+               else
+                       mode = MV_VTUCTL_EGRESS_UNTAGGED;
+
+               state->vlans[vno].port_mode |= mode << (pno * 4);
+               state->vlans[vno].port_sstate |=
+                       MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2);
+       }
+
+       /*
+        * DISCARD is nonzero, so it must be explicitly
+        * set on ports not in the VLAN.
+        */
+       for (i = 0; i < dev->ports; i++)
+               if (!(state->vlans[vno].mask & (1 << i)))
+                       state->vlans[vno].port_mode |=
+                               MV_VTUCTL_DISCARD << (i * 4);
+
+       return 0;
+}
+
+static int mvsw61xx_get_vlan_port_based(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       if (state->vlans[vno].port_based)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vlan_port_based(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       if (val->value.i == 1)
+               state->vlans[vno].port_based = true;
+       else
+               state->vlans[vno].port_based = false;
+
+       return 0;
+}
+
+static int mvsw61xx_get_vid(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       val->value.i = state->vlans[vno].vid;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vid(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       state->vlans[vno].vid = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_get_enable_vlan(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->vlan_enabled;
+
+       return 0;
+}
+
+static int mvsw61xx_set_enable_vlan(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->vlan_enabled = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_vtu_program(struct switch_dev *dev)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       u16 v1, v2, s1, s2;
+       int i;
+
+       /* Flush */
+       mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                       MV_VTUOP_INPROGRESS, 0);
+       sw16(dev, MV_GLOBALREG(VTU_OP),
+                       MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE);
+
+       /* Write VLAN table */
+       for (i = 1; i < dev->vlans; i++) {
+               if (state->vlans[i].mask == 0 ||
+                               state->vlans[i].vid == 0 ||
+                               state->vlans[i].port_based == true)
+                       continue;
+
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+
+               /* Write per-VLAN port state into STU */
+               s1 = (u16) (state->vlans[i].port_sstate & 0xffff);
+               s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff);
+
+               sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID);
+               sw16(dev, MV_GLOBALREG(VTU_SID), i);
+               sw16(dev, MV_GLOBALREG(VTU_DATA1), s1);
+               sw16(dev, MV_GLOBALREG(VTU_DATA2), s2);
+               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
+
+               sw16(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD);
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+
+               /* Write VLAN information into VTU */
+               v1 = (u16) (state->vlans[i].port_mode & 0xffff);
+               v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff);
+
+               sw16(dev, MV_GLOBALREG(VTU_VID),
+                               MV_VTU_VID_VALID | state->vlans[i].vid);
+               sw16(dev, MV_GLOBALREG(VTU_SID), i);
+               sw16(dev, MV_GLOBALREG(VTU_FID), i);
+               sw16(dev, MV_GLOBALREG(VTU_DATA1), v1);
+               sw16(dev, MV_GLOBALREG(VTU_DATA2), v2);
+               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
+
+               sw16(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD);
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+       }
+
+       return 0;
+}
+
+static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, mode;
+
+       for (i = 0; i < dev->ports; i++) {
+               if (!(state->vlans[vno].mask & (1 << i)))
+                       continue;
+
+               mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
+
+               if(mode != MV_VTUCTL_EGRESS_TAGGED)
+                       state->ports[i].pvid = state->vlans[vno].vid;
+
+               if (state->vlans[vno].port_based) {
+                       state->ports[i].mask |= state->vlans[vno].mask;
+                       state->ports[i].fdb = vno;
+               }
+               else
+                       state->ports[i].qmode = MV_8021Q_MODE_SECURE;
+       }
+}
+
+static int mvsw61xx_update_state(struct switch_dev *dev)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i;
+       u16 reg;
+
+       if (!state->registered)
+               return -EINVAL;
+
+       /*
+        * Set 802.1q-only mode if vlan_enabled is true.
+        *
+        * Without this, even if 802.1q is enabled for
+        * a port/VLAN, it still depends on the port-based
+        * VLAN mask being set.
+        *
+        * With this setting, port-based VLANs are still
+        * functional, provided the VID is not in the VTU.
+        */
+       reg = sr16(dev, MV_GLOBAL2REG(SDET_POLARITY));
+
+       if (state->vlan_enabled)
+               reg |= MV_8021Q_VLAN_ONLY;
+       else
+               reg &= ~MV_8021Q_VLAN_ONLY;
+
+       sw16(dev, MV_GLOBAL2REG(SDET_POLARITY), reg);
+
+       /*
+        * Set port-based VLAN masks on each port
+        * based only on VLAN definitions known to
+        * the driver (i.e. in state).
+        *
+        * This means any pre-existing port mapping is
+        * wiped out once our driver is initialized.
+        */
+       for (i = 0; i < dev->ports; i++) {
+               state->ports[i].mask = 0;
+               state->ports[i].qmode = MV_8021Q_MODE_DISABLE;
+       }
+
+       for (i = 0; i < dev->vlans; i++)
+               mvsw61xx_vlan_port_config(dev, i);
+
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(VLANID, i)) & ~MV_PVID_MASK;
+               reg |= state->ports[i].pvid;
+               sw16(dev, MV_PORTREG(VLANID, i), reg);
+
+               state->ports[i].mask &= ~(1 << i);
+
+               /* set default forwarding DB number and port mask */
+               reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK;
+               reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) &
+                       MV_FDB_HI_MASK;
+               sw16(dev, MV_PORTREG(CONTROL1, i), reg);
+
+               reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) |
+                       state->ports[i].mask;
+               sw16(dev, MV_PORTREG(VLANMAP, i), reg);
+
+               reg = sr16(dev, MV_PORTREG(CONTROL2, i)) &
+                       ~MV_8021Q_MODE_MASK;
+               reg |= state->ports[i].qmode << MV_8021Q_MODE_SHIFT;
+               sw16(dev, MV_PORTREG(CONTROL2, i), reg);
+       }
+
+       mvsw61xx_vtu_program(dev);
+
+       return 0;
+}
+
+static int mvsw61xx_apply(struct switch_dev *dev)
+{
+       return mvsw61xx_update_state(dev);
+}
+
+static void mvsw61xx_enable_serdes(struct switch_dev *dev)
+{
+       int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES,
+                                          MV_PAGE_FIBER_SERDES, MII_BMCR);
+       if (bmcr < 0)
+               return;
+
+       if (bmcr & BMCR_PDOWN)
+               mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES,
+                                        MV_PAGE_FIBER_SERDES, MII_BMCR,
+                                        bmcr & ~BMCR_PDOWN);
+}
+
+static int _mvsw61xx_reset(struct switch_dev *dev, bool full)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i;
+       u16 reg;
+
+       /* Disable all ports before reset */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL, i)) &
+                       ~MV_PORTCTRL_FORWARDING;
+               sw16(dev, MV_PORTREG(CONTROL, i), reg);
+       }
+
+       reg = sr16(dev, MV_GLOBALREG(CONTROL)) | MV_CONTROL_RESET;
+
+       sw16(dev, MV_GLOBALREG(CONTROL), reg);
+       if (mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(CONTROL),
+                               MV_CONTROL_RESET, 0) < 0)
+               return -ETIMEDOUT;
+
+       for (i = 0; i < dev->ports; i++) {
+               state->ports[i].fdb = 0;
+               state->ports[i].qmode = 0;
+               state->ports[i].mask = 0;
+               state->ports[i].pvid = 0;
+
+               /* Force flow control off */
+               reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK;
+               reg |= MV_PHYCTL_FC_DISABLE;
+               sw16(dev, MV_PORTREG(PHYCTL, i), reg);
+
+               /* Set port association vector */
+               sw16(dev, MV_PORTREG(ASSOC, i), (1 << i));
+
+               /* power up phys */
+               if (full && i < 5) {
+                       mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL,
+                                           MV_SPEC_MDI_CROSS_AUTO |
+                                           MV_SPEC_ENERGY_DETECT |
+                                           MV_SPEC_DOWNSHIFT_COUNTER);
+                       mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET |
+                                           BMCR_ANENABLE | BMCR_FULLDPLX |
+                                           BMCR_SPEED1000);
+               }
+
+               /* enable SerDes if necessary */
+               if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) {
+                       u16 sts = sr16(dev, MV_PORTREG(STATUS, i));
+                       u16 mode = sts & MV_PORT_STATUS_CMODE_MASK;
+
+                       if (mode == MV_PORT_STATUS_CMODE_100BASE_X ||
+                           mode == MV_PORT_STATUS_CMODE_1000BASE_X ||
+                           mode == MV_PORT_STATUS_CMODE_SGMII) {
+                               mvsw61xx_enable_serdes(dev);
+                       }
+               }
+       }
+
+       for (i = 0; i < dev->vlans; i++) {
+               state->vlans[i].port_based = false;
+               state->vlans[i].mask = 0;
+               state->vlans[i].vid = 0;
+               state->vlans[i].port_mode = 0;
+               state->vlans[i].port_sstate = 0;
+       }
+
+       state->vlan_enabled = 0;
+
+       mvsw61xx_update_state(dev);
+
+       /* Re-enable ports */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL, i)) |
+                       MV_PORTCTRL_FORWARDING;
+               sw16(dev, MV_PORTREG(CONTROL, i), reg);
+       }
+
+       return 0;
+}
+
+static int mvsw61xx_reset(struct switch_dev *dev)
+{
+       return _mvsw61xx_reset(dev, false);
+}
+
+enum {
+       MVSW61XX_ENABLE_VLAN,
+};
+
+enum {
+       MVSW61XX_VLAN_PORT_BASED,
+       MVSW61XX_VLAN_ID,
+};
+
+enum {
+       MVSW61XX_PORT_MASK,
+       MVSW61XX_PORT_QMODE,
+};
+
+static const struct switch_attr mvsw61xx_global[] = {
+       [MVSW61XX_ENABLE_VLAN] = {
+               .id = MVSW61XX_ENABLE_VLAN,
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable 802.1q VLAN support",
+               .get = mvsw61xx_get_enable_vlan,
+               .set = mvsw61xx_set_enable_vlan,
+       },
+};
+
+static const struct switch_attr mvsw61xx_vlan[] = {
+       [MVSW61XX_VLAN_PORT_BASED] = {
+               .id = MVSW61XX_VLAN_PORT_BASED,
+               .type = SWITCH_TYPE_INT,
+               .name = "port_based",
+               .description = "Use port-based (non-802.1q) VLAN only",
+               .get = mvsw61xx_get_vlan_port_based,
+               .set = mvsw61xx_set_vlan_port_based,
+       },
+       [MVSW61XX_VLAN_ID] = {
+               .id = MVSW61XX_VLAN_ID,
+               .type = SWITCH_TYPE_INT,
+               .name = "vid",
+               .description = "Get/set VLAN ID",
+               .get = mvsw61xx_get_vid,
+               .set = mvsw61xx_set_vid,
+       },
+};
+
+static const struct switch_attr mvsw61xx_port[] = {
+       [MVSW61XX_PORT_MASK] = {
+               .id = MVSW61XX_PORT_MASK,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Port-based VLAN mask",
+               .name = "mask",
+               .get = mvsw61xx_get_port_mask,
+               .set = NULL,
+       },
+       [MVSW61XX_PORT_QMODE] = {
+               .id = MVSW61XX_PORT_QMODE,
+               .type = SWITCH_TYPE_INT,
+               .description = "802.1q mode: 0=off/1=fallback/2=check/3=secure",
+               .name = "qmode",
+               .get = mvsw61xx_get_port_qmode,
+               .set = mvsw61xx_set_port_qmode,
+       },
+};
+
+static const struct switch_dev_ops mvsw61xx_ops = {
+       .attr_global = {
+               .attr = mvsw61xx_global,
+               .n_attr = ARRAY_SIZE(mvsw61xx_global),
+       },
+       .attr_vlan = {
+               .attr = mvsw61xx_vlan,
+               .n_attr = ARRAY_SIZE(mvsw61xx_vlan),
+       },
+       .attr_port = {
+               .attr = mvsw61xx_port,
+               .n_attr = ARRAY_SIZE(mvsw61xx_port),
+       },
+       .get_port_link = mvsw61xx_get_port_link,
+       .get_port_pvid = mvsw61xx_get_port_pvid,
+       .set_port_pvid = mvsw61xx_set_port_pvid,
+       .get_vlan_ports = mvsw61xx_get_vlan_ports,
+       .set_vlan_ports = mvsw61xx_set_vlan_ports,
+       .apply_config = mvsw61xx_apply,
+       .reset_switch = mvsw61xx_reset,
+};
+
+/* end swconfig stuff */
+
+static int mvsw61xx_probe(struct platform_device *pdev)
+{
+       struct mvsw61xx_state *state;
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *mdio;
+       char *model_str;
+       u32 val;
+       int err;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       mdio = of_parse_phandle(np, "mii-bus", 0);
+       if (!mdio) {
+               dev_err(&pdev->dev, "Couldn't get MII bus handle\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->bus = of_mdio_find_bus(mdio);
+       if (!state->bus) {
+               dev_err(&pdev->dev, "Couldn't find MII bus from handle\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->is_indirect = of_property_read_bool(np, "is-indirect");
+
+       if (state->is_indirect) {
+               if (of_property_read_u32(np, "reg", &val)) {
+                       dev_err(&pdev->dev, "Switch address not specified\n");
+                       err = -ENODEV;
+                       goto out_err;
+               }
+
+               state->base_addr = val;
+       } else {
+               state->base_addr = MV_BASE;
+       }
+
+       state->model = r16(state->bus, state->is_indirect, state->base_addr,
+                               MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
+
+       switch(state->model) {
+       case MV_IDENT_VALUE_6171:
+               model_str = MV_IDENT_STR_6171;
+               break;
+       case MV_IDENT_VALUE_6172:
+               model_str = MV_IDENT_STR_6172;
+               break;
+       case MV_IDENT_VALUE_6176:
+               model_str = MV_IDENT_STR_6176;
+               break;
+       case MV_IDENT_VALUE_6352:
+               model_str = MV_IDENT_STR_6352;
+               break;
+       default:
+               dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n",
+                               state->base_addr);
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       platform_set_drvdata(pdev, state);
+       dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str,
+                       state->bus->id, state->base_addr);
+
+       dev_info(&pdev->dev, "Using %sdirect addressing\n",
+                       (state->is_indirect ? "in" : ""));
+
+       if (of_property_read_u32(np, "cpu-port-0", &val)) {
+               dev_err(&pdev->dev, "CPU port not set\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->cpu_port0 = val;
+
+       if (!of_property_read_u32(np, "cpu-port-1", &val))
+               state->cpu_port1 = val;
+       else
+               state->cpu_port1 = -1;
+
+       state->dev.vlans = MV_VLANS;
+       state->dev.cpu_port = state->cpu_port0;
+       state->dev.ports = MV_PORTS;
+       state->dev.name = model_str;
+       state->dev.ops = &mvsw61xx_ops;
+       state->dev.alias = dev_name(&pdev->dev);
+
+       _mvsw61xx_reset(&state->dev, true);
+
+       err = register_switch(&state->dev, NULL);
+       if (err < 0)
+               goto out_err;
+
+       state->registered = true;
+
+       return 0;
+out_err:
+       kfree(state);
+       return err;
+}
+
+static int
+mvsw61xx_remove(struct platform_device *pdev)
+{
+       struct mvsw61xx_state *state = platform_get_drvdata(pdev);
+
+       if (state->registered)
+               unregister_switch(&state->dev);
+
+       kfree(state);
+
+       return 0;
+}
+
+static const struct of_device_id mvsw61xx_match[] = {
+       { .compatible = "marvell,88e6171" },
+       { .compatible = "marvell,88e6172" },
+       { .compatible = "marvell,88e6176" },
+       { .compatible = "marvell,88e6352" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mvsw61xx_match);
+
+static struct platform_driver mvsw61xx_driver = {
+       .probe = mvsw61xx_probe,
+       .remove = mvsw61xx_remove,
+       .driver = {
+               .name = "mvsw61xx",
+               .of_match_table = of_match_ptr(mvsw61xx_match),
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init mvsw61xx_module_init(void)
+{
+       return platform_driver_register(&mvsw61xx_driver);
+}
+late_initcall(mvsw61xx_module_init);
+
+static void __exit mvsw61xx_module_exit(void)
+{
+       platform_driver_unregister(&mvsw61xx_driver);
+}
+module_exit(mvsw61xx_module_exit);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/mvsw61xx.h b/target/linux/generic/files-3.18/drivers/net/phy/mvsw61xx.h
new file mode 100644 (file)
index 0000000..a07b09c
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Marvell 88E61xx switch driver
+ *
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
+ *
+ * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#ifndef __MVSW61XX_H
+#define __MVSW61XX_H
+
+#define MV_PORTS                       7
+#define MV_PORTS_MASK                  ((1 << MV_PORTS) - 1)
+
+#define MV_BASE                                0x10
+
+#define MV_SWITCHPORT_BASE             0x10
+#define MV_SWITCHPORT(_n)              (MV_SWITCHPORT_BASE + (_n))
+#define MV_SWITCHREGS                  (MV_BASE + 0xb)
+
+#define MV_VLANS                       64
+
+enum {
+       MV_PORT_STATUS                  = 0x00,
+       MV_PORT_PHYCTL                  = 0x01,
+       MV_PORT_JAMCTL                  = 0x02,
+       MV_PORT_IDENT                   = 0x03,
+       MV_PORT_CONTROL                 = 0x04,
+       MV_PORT_CONTROL1                = 0x05,
+       MV_PORT_VLANMAP                 = 0x06,
+       MV_PORT_VLANID                  = 0x07,
+       MV_PORT_CONTROL2                = 0x08,
+       MV_PORT_ASSOC                   = 0x0b,
+       MV_PORT_RX_DISCARD_LOW          = 0x10,
+       MV_PORT_RX_DISCARD_HIGH         = 0x11,
+       MV_PORT_IN_FILTERED             = 0x12,
+       MV_PORT_OUT_ACCEPTED            = 0x13,
+};
+#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
+
+enum {
+       MV_PORT_STATUS_FDX              = (1 << 10),
+       MV_PORT_STATUS_LINK             = (1 << 11),
+};
+
+enum {
+       MV_PORT_STATUS_CMODE_100BASE_X  = 0x8,
+       MV_PORT_STATUS_CMODE_1000BASE_X = 0x9,
+       MV_PORT_STATUS_CMODE_SGMII      = 0xa,
+};
+
+#define MV_PORT_STATUS_CMODE_MASK      0xf
+
+enum {
+       MV_PORT_STATUS_SPEED_10         = 0x00,
+       MV_PORT_STATUS_SPEED_100        = 0x01,
+       MV_PORT_STATUS_SPEED_1000       = 0x02,
+};
+#define MV_PORT_STATUS_SPEED_SHIFT     8
+#define MV_PORT_STATUS_SPEED_MASK      (3 << 8)
+
+enum {
+       MV_PORTCTRL_DISABLED            = (0 << 0),
+       MV_PORTCTRL_BLOCKING            = (1 << 0),
+       MV_PORTCTRL_LEARNING            = (2 << 0),
+       MV_PORTCTRL_FORWARDING          = (3 << 0),
+       MV_PORTCTRL_VLANTUN             = (1 << 7),
+       MV_PORTCTRL_EGRESS              = (1 << 12),
+};
+
+#define MV_PHYCTL_FC_MASK              (3 << 6)
+
+enum {
+       MV_PHYCTL_FC_ENABLE             = (3 << 6),
+       MV_PHYCTL_FC_DISABLE            = (1 << 6),
+};
+
+enum {
+       MV_8021Q_EGRESS_UNMODIFIED      = 0x00,
+       MV_8021Q_EGRESS_UNTAGGED        = 0x01,
+       MV_8021Q_EGRESS_TAGGED          = 0x02,
+       MV_8021Q_EGRESS_ADDTAG          = 0x03,
+};
+
+#define MV_8021Q_MODE_SHIFT            10
+#define MV_8021Q_MODE_MASK             (0x3 << MV_8021Q_MODE_SHIFT)
+
+enum {
+       MV_8021Q_MODE_DISABLE           = 0x00,
+       MV_8021Q_MODE_FALLBACK          = 0x01,
+       MV_8021Q_MODE_CHECK             = 0x02,
+       MV_8021Q_MODE_SECURE            = 0x03,
+};
+
+enum {
+       MV_8021Q_VLAN_ONLY              = (1 << 15),
+};
+
+#define MV_PORTASSOC_MONITOR           (1 << 15)
+
+enum {
+       MV_SWITCH_ATU_FID0              = 0x01,
+       MV_SWITCH_ATU_FID1              = 0x02,
+       MV_SWITCH_ATU_SID               = 0x03,
+       MV_SWITCH_CTRL                  = 0x04,
+       MV_SWITCH_ATU_CTRL              = 0x0a,
+       MV_SWITCH_ATU_OP                = 0x0b,
+       MV_SWITCH_ATU_DATA              = 0x0c,
+       MV_SWITCH_ATU_MAC0              = 0x0d,
+       MV_SWITCH_ATU_MAC1              = 0x0e,
+       MV_SWITCH_ATU_MAC2              = 0x0f,
+       MV_SWITCH_GLOBAL                = 0x1b,
+       MV_SWITCH_GLOBAL2               = 0x1c,
+};
+#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
+
+enum {
+       MV_SWITCHCTL_EEIE               = (1 << 0),
+       MV_SWITCHCTL_PHYIE              = (1 << 1),
+       MV_SWITCHCTL_ATUDONE            = (1 << 2),
+       MV_SWITCHCTL_ATUIE              = (1 << 3),
+       MV_SWITCHCTL_CTRMODE            = (1 << 8),
+       MV_SWITCHCTL_RELOAD             = (1 << 9),
+       MV_SWITCHCTL_MSIZE              = (1 << 10),
+       MV_SWITCHCTL_DROP               = (1 << 13),
+};
+
+enum {
+#define MV_ATUCTL_AGETIME_MIN          16
+#define MV_ATUCTL_AGETIME_MAX          4080
+#define MV_ATUCTL_AGETIME(_n)          ((((_n) / 16) & 0xff) << 4)
+       MV_ATUCTL_ATU_256               = (0 << 12),
+       MV_ATUCTL_ATU_512               = (1 << 12),
+       MV_ATUCTL_ATU_1K                = (2 << 12),
+       MV_ATUCTL_ATUMASK               = (3 << 12),
+       MV_ATUCTL_NO_LEARN              = (1 << 14),
+       MV_ATUCTL_RESET                 = (1 << 15),
+};
+
+enum {
+#define MV_ATUOP_DBNUM(_n)             ((_n) & 0x0f)
+       MV_ATUOP_NOOP                   = (0 << 12),
+       MV_ATUOP_FLUSH_ALL              = (1 << 12),
+       MV_ATUOP_FLUSH_U                = (2 << 12),
+       MV_ATUOP_LOAD_DB                = (3 << 12),
+       MV_ATUOP_GET_NEXT               = (4 << 12),
+       MV_ATUOP_FLUSH_DB               = (5 << 12),
+       MV_ATUOP_FLUSH_DB_UU            = (6 << 12),
+       MV_ATUOP_INPROGRESS             = (1 << 15),
+};
+
+enum {
+       MV_GLOBAL_STATUS                = 0x00,
+       MV_GLOBAL_ATU_FID               = 0x01,
+       MV_GLOBAL_VTU_FID               = 0x02,
+       MV_GLOBAL_VTU_SID               = 0x03,
+       MV_GLOBAL_CONTROL               = 0x04,
+       MV_GLOBAL_VTU_OP                = 0x05,
+       MV_GLOBAL_VTU_VID               = 0x06,
+       MV_GLOBAL_VTU_DATA1             = 0x07,
+       MV_GLOBAL_VTU_DATA2             = 0x08,
+       MV_GLOBAL_VTU_DATA3             = 0x09,
+       MV_GLOBAL_CONTROL2              = 0x1c,
+};
+#define MV_GLOBALREG(_type) MV_SWITCH_GLOBAL, MV_GLOBAL_##_type
+
+enum {
+       MV_GLOBAL2_SMI_OP               = 0x18,
+       MV_GLOBAL2_SMI_DATA             = 0x19,
+       MV_GLOBAL2_SDET_POLARITY        = 0x1d,
+};
+#define MV_GLOBAL2REG(_type) MV_SWITCH_GLOBAL2, MV_GLOBAL2_##_type
+
+enum {
+       MV_VTU_VID_VALID                = (1 << 12),
+};
+
+enum {
+       MV_VTUOP_PURGE                  = (1 << 12),
+       MV_VTUOP_LOAD                   = (3 << 12),
+       MV_VTUOP_INPROGRESS             = (1 << 15),
+       MV_VTUOP_STULOAD                = (5 << 12),
+       MV_VTUOP_VTU_GET_NEXT           = (4 << 12),
+       MV_VTUOP_STU_GET_NEXT           = (6 << 12),
+       MV_VTUOP_GET_VIOLATION          = (7 << 12),
+};
+
+enum {
+       MV_CONTROL_RESET                = (1 << 15),
+       MV_CONTROL_PPU_ENABLE           = (1 << 14),
+};
+
+enum {
+       MV_VTUCTL_EGRESS_UNMODIFIED     = (0 << 0),
+       MV_VTUCTL_EGRESS_UNTAGGED       = (1 << 0),
+       MV_VTUCTL_EGRESS_TAGGED         = (2 << 0),
+       MV_VTUCTL_DISCARD               = (3 << 0),
+};
+
+enum {
+       MV_STUCTL_STATE_DISABLED        = (0 << 0),
+       MV_STUCTL_STATE_BLOCKING        = (1 << 0),
+       MV_STUCTL_STATE_LEARNING        = (2 << 0),
+       MV_STUCTL_STATE_FORWARDING      = (3 << 0),
+};
+
+enum {
+       MV_INDIRECT_REG_CMD             = 0,
+       MV_INDIRECT_REG_DATA            = 1,
+};
+
+enum {
+       MV_INDIRECT_INPROGRESS          = 0x8000,
+       MV_INDIRECT_WRITE               = 0x9400,
+       MV_INDIRECT_READ                = 0x9800,
+};
+#define MV_INDIRECT_ADDR_S             5
+
+#define MV_IDENT_MASK                  0xfff0
+
+#define MV_IDENT_VALUE_6171            0x1710
+#define MV_IDENT_STR_6171              "MV88E6171"
+
+#define MV_IDENT_VALUE_6172            0x1720
+#define MV_IDENT_STR_6172              "MV88E6172"
+
+#define MV_IDENT_VALUE_6176            0x1760
+#define MV_IDENT_STR_6176              "MV88E6176"
+
+#define MV_IDENT_VALUE_6352            0x3520
+#define MV_IDENT_STR_6352              "MV88E6352"
+
+#define MV_PVID_MASK                   0x0fff
+
+#define MV_FDB_HI_MASK                 0x00ff
+#define MV_FDB_LO_MASK                 0xf000
+#define MV_FDB_HI_SHIFT                        4
+#define MV_FDB_LO_SHIFT                        12
+
+/* Marvell Specific PHY register */
+#define MII_MV_SPEC_CTRL               16
+enum {
+       MV_SPEC_MDI_CROSS_AUTO          = (0x6 << 4),
+       MV_SPEC_ENERGY_DETECT           = (0x3 << 8),
+       MV_SPEC_DOWNSHIFT_COUNTER       = (0x3 << 12),
+};
+
+#define MII_MV_PAGE                    22
+
+#define MV_REG_FIBER_SERDES            0xf
+#define MV_PAGE_FIBER_SERDES           0x1
+
+struct mvsw61xx_state {
+       struct switch_dev dev;
+       struct mii_bus *bus;
+       int base_addr;
+       u16 model;
+
+       bool registered;
+       bool is_indirect;
+
+       int cpu_port0;
+       int cpu_port1;
+
+       int vlan_enabled;
+       struct port_state {
+               u16 fdb;
+               u16 pvid;
+               u16 mask;
+               u8 qmode;
+       } ports[MV_PORTS];
+
+       struct vlan_state {
+               bool port_based;
+
+               u16 mask;
+               u16 vid;
+               u32 port_mode;
+               u32 port_sstate;
+       } vlans[MV_VLANS];
+
+       char buf[128];
+};
+
+#define get_state(_dev) container_of((_dev), struct mvsw61xx_state, dev)
+
+#endif
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/mvswitch.c b/target/linux/generic/files-3.18/drivers/net/phy/mvswitch.c
new file mode 100644 (file)
index 0000000..043978f
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Marvell 88E6060 switch driver
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/if_vlan.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "mvswitch.h"
+
+/* Undefine this to use trailer mode instead.
+ * I don't know if header mode works with all chips */
+#define HEADER_MODE    1
+
+MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
+MODULE_AUTHOR("Felix Fietkau");
+MODULE_LICENSE("GPL");
+
+#define MVSWITCH_MAGIC 0x88E6060
+
+struct mvswitch_priv {
+       netdev_features_t orig_features;
+       u8 vlans[16];
+};
+
+#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
+
+static inline u16
+r16(struct phy_device *phydev, int addr, int reg)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, addr, reg);
+}
+
+static inline void
+w16(struct phy_device *phydev, int addr, int reg, u16 val)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, addr, reg, val);
+}
+
+
+static struct sk_buff *
+mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct mvswitch_priv *priv;
+       char *buf = NULL;
+       u16 vid;
+
+       priv = dev->phy_ptr;
+       if (unlikely(!priv))
+               goto error;
+
+       if (unlikely(skb->len < 16))
+               goto error;
+
+#ifdef HEADER_MODE
+       if (__vlan_hwaccel_get_tag(skb, &vid))
+               goto error;
+
+       if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
+               if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
+                       goto error_expand;
+               if (skb->len < 62)
+                       skb->len = 62;
+       }
+       buf = skb_push(skb, MV_HEADER_SIZE);
+#else
+       if (__vlan_get_tag(skb, &vid))
+               goto error;
+
+       if (unlikely((vid > 15 || !priv->vlans[vid])))
+               goto error;
+
+       if (skb->len <= 64) {
+               if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
+                       goto error_expand;
+
+               buf = skb->data + 64;
+               skb->len = 64 + MV_TRAILER_SIZE;
+       } else {
+               if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
+                       if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
+                               goto error_expand;
+               }
+               buf = skb_put(skb, 4);
+       }
+
+       /* move the ethernet header 4 bytes forward, overwriting the vlan tag */
+       memmove(skb->data + 4, skb->data, 12);
+       skb->data += 4;
+       skb->len -= 4;
+       skb->mac_header += 4;
+#endif
+
+       if (!buf)
+               goto error;
+
+
+#ifdef HEADER_MODE
+       /* prepend the tag */
+       *((__be16 *) buf) = cpu_to_be16(
+               ((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
+               ((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
+       );
+#else
+       /* append the tag */
+       *((__be32 *) buf) = cpu_to_be32((
+               (MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
+               ((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
+       ));
+#endif
+
+       return skb;
+
+error_expand:
+       if (net_ratelimit())
+               printk("%s: failed to expand/update skb for the switch\n", dev->name);
+
+error:
+       /* any errors? drop the packet! */
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static void
+mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct mvswitch_priv *priv;
+       unsigned char *buf;
+       int vlan = -1;
+       int i;
+
+       priv = dev->phy_ptr;
+       if (WARN_ON_ONCE(!priv))
+               return;
+
+#ifdef HEADER_MODE
+       buf = skb->data;
+       skb_pull(skb, MV_HEADER_SIZE);
+#else
+       buf = skb->data + skb->len - MV_TRAILER_SIZE;
+       if (buf[0] != 0x80)
+               return;
+#endif
+
+       /* look for the vlan matching the incoming port */
+       for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
+               if ((1 << buf[1]) & priv->vlans[i])
+                       vlan = i;
+       }
+
+       if (vlan == -1)
+               return;
+
+       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
+}
+
+
+static int
+mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = r16(pdev, addr, reg) & mask;
+               if (r == val)
+                       return 0;
+       } while(--i > 0);
+       return -ETIMEDOUT;
+}
+
+static int
+mvswitch_config_init(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+       struct net_device *dev = pdev->attached_dev;
+       u8 vlmap = 0;
+       int i;
+
+       if (!dev)
+               return -EINVAL;
+
+       printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
+       pdev->supported = ADVERTISED_100baseT_Full;
+       pdev->advertising = ADVERTISED_100baseT_Full;
+       dev->phy_ptr = priv;
+       pdev->irq = PHY_POLL;
+#ifdef HEADER_MODE
+       dev->flags |= IFF_PROMISC;
+#endif
+
+       /* initialize default vlans */
+       for (i = 0; i < MV_PORTS; i++)
+               priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i);
+
+       /* before entering reset, disable all ports */
+       for (i = 0; i < MV_PORTS; i++)
+               w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
+
+       msleep(2); /* wait for the status change to settle in */
+
+       /* put the ATU in reset */
+       w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
+
+       i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
+       if (i < 0) {
+               printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
+               return i;
+       }
+
+       /* set the ATU flags */
+       w16(pdev, MV_SWITCHREG(ATU_CTRL),
+               MV_ATUCTL_NO_LEARN |
+               MV_ATUCTL_ATU_1K |
+               MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
+       );
+
+       /* initialize the cpu port */
+       w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
+#ifdef HEADER_MODE
+               MV_PORTCTRL_HEADER |
+#else
+               MV_PORTCTRL_RXTR |
+               MV_PORTCTRL_TXTR |
+#endif
+               MV_PORTCTRL_ENABLED
+       );
+       /* wait for the phy change to settle in */
+       msleep(2);
+       for (i = 0; i < MV_PORTS; i++) {
+               u8 pvid = 0;
+               int j;
+
+               vlmap = 0;
+
+               /* look for the matching vlan */
+               for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
+                       if (priv->vlans[j] & (1 << i)) {
+                               vlmap = priv->vlans[j];
+                               pvid = j;
+                       }
+               }
+               /* leave port unconfigured if it's not part of a vlan */
+               if (!vlmap)
+                       continue;
+
+               /* add the cpu port to the allowed destinations list */
+               vlmap |= (1 << MV_CPUPORT);
+
+               /* take port out of its own vlan destination map */
+               vlmap &= ~(1 << i);
+
+               /* apply vlan settings */
+               w16(pdev, MV_PORTREG(VLANMAP, i),
+                       MV_PORTVLAN_PORTS(vlmap) |
+                       MV_PORTVLAN_ID(i)
+               );
+
+               /* re-enable port */
+               w16(pdev, MV_PORTREG(CONTROL, i),
+                       MV_PORTCTRL_ENABLED
+               );
+       }
+
+       w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
+               MV_PORTVLAN_ID(MV_CPUPORT)
+       );
+
+       /* set the port association vector */
+       for (i = 0; i <= MV_PORTS; i++) {
+               w16(pdev, MV_PORTREG(ASSOC, i),
+                       MV_PORTASSOC_PORTS(1 << i)
+               );
+       }
+
+       /* init switch control */
+       w16(pdev, MV_SWITCHREG(CTRL),
+               MV_SWITCHCTL_MSIZE |
+               MV_SWITCHCTL_DROP
+       );
+
+       dev->eth_mangle_rx = mvswitch_mangle_rx;
+       dev->eth_mangle_tx = mvswitch_mangle_tx;
+       priv->orig_features = dev->features;
+
+#ifdef HEADER_MODE
+       dev->priv_flags |= IFF_NO_IP_ALIGN;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
+#else
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+#endif
+
+       return 0;
+}
+
+static int
+mvswitch_read_status(struct phy_device *pdev)
+{
+       pdev->speed = SPEED_100;
+       pdev->duplex = DUPLEX_FULL;
+       pdev->link = 1;
+
+       /* XXX ugly workaround: we can't force the switch
+        * to gracefully handle hosts moving from one port to another,
+        * so we have to regularly clear the ATU database */
+
+       /* wait for the ATU to become available */
+       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
+
+       /* flush the ATU */
+       w16(pdev, MV_SWITCHREG(ATU_OP),
+               MV_ATUOP_INPROGRESS |
+               MV_ATUOP_FLUSH_ALL
+       );
+
+       /* wait for operation to complete */
+       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
+
+       return 0;
+}
+
+static int
+mvswitch_aneg_done(struct phy_device *phydev)
+{
+       return 1;       /* Return any positive value */
+}
+
+static int
+mvswitch_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static void
+mvswitch_detach(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+       struct net_device *dev = pdev->attached_dev;
+
+       if (!dev)
+               return;
+
+       dev->phy_ptr = NULL;
+       dev->eth_mangle_rx = NULL;
+       dev->eth_mangle_tx = NULL;
+       dev->features = priv->orig_features;
+       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+}
+
+static void
+mvswitch_remove(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+
+       kfree(priv);
+}
+
+static int
+mvswitch_probe(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv;
+
+       priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       pdev->priv = priv;
+
+       return 0;
+}
+
+static int
+mvswitch_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       if (dev->mdio.addr != 0x10)
+               return 0;
+
+       reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
+       if (reg != MV_IDENT_VALUE)
+               return 0;
+
+       dev->phy_id = MVSWITCH_MAGIC;
+       return 0;
+}
+
+
+static struct phy_driver mvswitch_driver = {
+       .name           = "Marvell 88E6060",
+       .phy_id         = MVSWITCH_MAGIC,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = &mvswitch_probe,
+       .remove         = &mvswitch_remove,
+       .detach         = &mvswitch_detach,
+       .config_init    = &mvswitch_config_init,
+       .config_aneg    = &mvswitch_config_aneg,
+       .aneg_done      = &mvswitch_aneg_done,
+       .read_status    = &mvswitch_read_status,
+};
+
+static int __init
+mvswitch_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
+       return phy_driver_register(&mvswitch_driver, THIS_MODULE);
+}
+
+static void __exit
+mvswitch_exit(void)
+{
+       phy_driver_unregister(&mvswitch_driver);
+}
+
+module_init(mvswitch_init);
+module_exit(mvswitch_exit);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/mvswitch.h b/target/linux/generic/files-3.18/drivers/net/phy/mvswitch.h
new file mode 100644 (file)
index 0000000..ab2a1a1
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Marvell 88E6060 switch driver
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#ifndef __MVSWITCH_H
+#define __MVSWITCH_H
+
+#define MV_HEADER_SIZE 2
+#define MV_HEADER_PORTS_M      0x001f
+#define MV_HEADER_PORTS_S      0
+#define MV_HEADER_VLAN_M       0xf000
+#define MV_HEADER_VLAN_S       12
+
+#define MV_TRAILER_SIZE        4
+#define MV_TRAILER_PORTS_M     0x1f
+#define MV_TRAILER_PORTS_S     16
+#define MV_TRAILER_FLAGS_S     24
+#define MV_TRAILER_OVERRIDE    0x80
+
+
+#define MV_PORTS       5
+#define MV_WANPORT     4
+#define MV_CPUPORT     5
+
+#define MV_BASE                0x10
+
+#define MV_PHYPORT_BASE                (MV_BASE + 0x0)
+#define MV_PHYPORT(_n)         (MV_PHYPORT_BASE + (_n))
+#define MV_SWITCHPORT_BASE     (MV_BASE + 0x8)
+#define MV_SWITCHPORT(_n)      (MV_SWITCHPORT_BASE + (_n))
+#define MV_SWITCHREGS          (MV_BASE + 0xf)
+
+enum {
+       MV_PHY_CONTROL      = 0x00,
+       MV_PHY_STATUS       = 0x01,
+       MV_PHY_IDENT0       = 0x02,
+       MV_PHY_IDENT1       = 0x03,
+       MV_PHY_ANEG         = 0x04,
+       MV_PHY_LINK_ABILITY = 0x05,
+       MV_PHY_ANEG_EXPAND  = 0x06,
+       MV_PHY_XMIT_NEXTP   = 0x07,
+       MV_PHY_LINK_NEXTP   = 0x08,
+       MV_PHY_CONTROL1     = 0x10,
+       MV_PHY_STATUS1      = 0x11,
+       MV_PHY_INTR_EN      = 0x12,
+       MV_PHY_INTR_STATUS  = 0x13,
+       MV_PHY_INTR_PORT    = 0x14,
+       MV_PHY_RECV_COUNTER = 0x16,
+       MV_PHY_LED_PARALLEL = 0x16,
+       MV_PHY_LED_STREAM   = 0x17,
+       MV_PHY_LED_CTRL     = 0x18,
+       MV_PHY_LED_OVERRIDE = 0x19,
+       MV_PHY_VCT_CTRL     = 0x1a,
+       MV_PHY_VCT_STATUS   = 0x1b,
+       MV_PHY_CONTROL2     = 0x1e
+};
+#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
+
+enum {
+       MV_PORT_STATUS      = 0x00,
+       MV_PORT_IDENT       = 0x03,
+       MV_PORT_CONTROL     = 0x04,
+       MV_PORT_VLANMAP     = 0x06,
+       MV_PORT_ASSOC       = 0x0b,
+       MV_PORT_RXCOUNT     = 0x10,
+       MV_PORT_TXCOUNT     = 0x11,
+};
+#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
+
+enum {
+       MV_PORTCTRL_BLOCK   =  (1 << 0),
+       MV_PORTCTRL_LEARN   =  (2 << 0),
+       MV_PORTCTRL_ENABLED =  (3 << 0),
+       MV_PORTCTRL_VLANTUN =  (1 << 7),        /* Enforce VLANs on packets */
+       MV_PORTCTRL_RXTR    =  (1 << 8),        /* Enable Marvell packet trailer for ingress */
+       MV_PORTCTRL_HEADER      = (1 << 11),    /* Enable Marvell packet header mode for port */
+       MV_PORTCTRL_TXTR    = (1 << 14),        /* Enable Marvell packet trailer for egress */
+       MV_PORTCTRL_FORCEFL = (1 << 15),        /* force flow control */
+};
+
+#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
+#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
+
+#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
+#define MV_PORTASSOC_MONITOR   (1 << 15)
+
+enum {
+       MV_SWITCH_MAC0      = 0x01,
+       MV_SWITCH_MAC1      = 0x02,
+       MV_SWITCH_MAC2      = 0x03,
+       MV_SWITCH_CTRL      = 0x04,
+       MV_SWITCH_ATU_CTRL  = 0x0a,
+       MV_SWITCH_ATU_OP    = 0x0b,
+       MV_SWITCH_ATU_DATA  = 0x0c,
+       MV_SWITCH_ATU_MAC0  = 0x0d,
+       MV_SWITCH_ATU_MAC1  = 0x0e,
+       MV_SWITCH_ATU_MAC2  = 0x0f,
+};
+#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
+
+enum {
+       MV_SWITCHCTL_EEIE   =  (1 << 0),        /* EEPROM interrupt enable */
+       MV_SWITCHCTL_PHYIE  =  (1 << 1),        /* PHY interrupt enable */
+       MV_SWITCHCTL_ATUDONE=  (1 << 2),        /* ATU done interrupt enable */
+       MV_SWITCHCTL_ATUIE  =  (1 << 3),        /* ATU interrupt enable */
+       MV_SWITCHCTL_CTRMODE=  (1 << 8),        /* statistics for rx and tx errors */
+       MV_SWITCHCTL_RELOAD =  (1 << 9),        /* reload registers from eeprom */
+       MV_SWITCHCTL_MSIZE  = (1 << 10),        /* increase maximum frame size */
+       MV_SWITCHCTL_DROP   = (1 << 13),        /* discard frames with excessive collisions */
+};
+
+enum {
+#define MV_ATUCTL_AGETIME_MIN  16
+#define MV_ATUCTL_AGETIME_MAX  4080
+#define MV_ATUCTL_AGETIME(_n)  ((((_n) / 16) & 0xff) << 4)
+       MV_ATUCTL_ATU_256   = (0 << 12),
+       MV_ATUCTL_ATU_512   = (1 << 12),
+       MV_ATUCTL_ATU_1K        = (2 << 12),
+       MV_ATUCTL_ATUMASK   = (3 << 12),
+       MV_ATUCTL_NO_LEARN  = (1 << 14),
+       MV_ATUCTL_RESET     = (1 << 15),
+};
+
+enum {
+#define MV_ATUOP_DBNUM(_n)     ((_n) & 0x0f)
+
+       MV_ATUOP_NOOP       = (0 << 12),
+       MV_ATUOP_FLUSH_ALL  = (1 << 12),
+       MV_ATUOP_FLUSH_U    = (2 << 12),
+       MV_ATUOP_LOAD_DB    = (3 << 12),
+       MV_ATUOP_GET_NEXT   = (4 << 12),
+       MV_ATUOP_FLUSH_DB   = (5 << 12),
+       MV_ATUOP_FLUSH_DB_UU= (6 << 12),
+
+       MV_ATUOP_INPROGRESS = (1 << 15),
+};
+
+#define MV_IDENT_MASK          0xfff0
+#define MV_IDENT_VALUE         0x0600
+
+#endif
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/psb6970.c b/target/linux/generic/files-3.18/drivers/net/phy/psb6970.c
new file mode 100644 (file)
index 0000000..c1a381c
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Lantiq PSB6970 (Tantos) Switch driver
+ *
+ * Copyright (c) 2009,2010 Team Embedded.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation.
+ *
+ * The switch programming done in this driver follows the 
+ * "Ethernet Traffic Separation using VLAN" Application Note as
+ * published by Lantiq.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/switch.h>
+#include <linux/phy.h>
+
+#define PSB6970_MAX_VLANS              16
+#define PSB6970_NUM_PORTS              7
+#define PSB6970_DEFAULT_PORT_CPU       6
+#define PSB6970_IS_CPU_PORT(x)         ((x) > 4)
+
+#define PHYADDR(_reg)          ((_reg >> 5) & 0xff), (_reg & 0x1f)
+
+/* --- Identification --- */
+#define PSB6970_CI0            0x0100
+#define PSB6970_CI0_MASK       0x000f
+#define PSB6970_CI1            0x0101
+#define PSB6970_CI1_VAL                0x2599
+#define PSB6970_CI1_MASK       0xffff
+
+/* --- VLAN filter table --- */
+#define PSB6970_VFxL(i)                ((i)*2+0x10)    /* VLAN Filter Low */
+#define PSB6970_VFxL_VV                (1 << 15)       /* VLAN_Valid */
+
+#define PSB6970_VFxH(i)                ((i)*2+0x11)    /* VLAN Filter High */
+#define PSB6970_VFxH_TM_SHIFT  7               /* Tagged Member */
+
+/* --- Port registers --- */
+#define PSB6970_EC(p)          ((p)*0x20+2)    /* Extended Control */
+#define PSB6970_EC_IFNTE       (1 << 1)        /* Input Force No Tag Enable */
+
+#define PSB6970_PBVM(p)                ((p)*0x20+3)    /* Port Base VLAN Map */
+#define PSB6970_PBVM_VMCE      (1 << 8)
+#define PSB6970_PBVM_AOVTP     (1 << 9)
+#define PSB6970_PBVM_VSD       (1 << 10)
+#define PSB6970_PBVM_VC                (1 << 11)       /* VID Check with VID table */
+#define PSB6970_PBVM_TBVE      (1 << 13)       /* Tag-Based VLAN enable */
+
+#define PSB6970_DVID(p)                ((p)*0x20+4)    /* Default VLAN ID & Priority */
+
+struct psb6970_priv {
+       struct switch_dev dev;
+       struct phy_device *phy;
+       u16 (*read) (struct phy_device* phydev, int reg);
+       void (*write) (struct phy_device* phydev, int reg, u16 val);
+       struct mutex reg_mutex;
+
+       /* all fields below are cleared on reset */
+       bool vlan;
+       u16 vlan_id[PSB6970_MAX_VLANS];
+       u8 vlan_table[PSB6970_MAX_VLANS];
+       u8 vlan_tagged;
+       u16 pvid[PSB6970_NUM_PORTS];
+};
+
+#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
+
+static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, PHYADDR(reg));
+}
+
+static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, PHYADDR(reg), val);
+}
+
+static int
+psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       priv->vlan = !!val->value.i;
+       return 0;
+}
+
+static int
+psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       val->value.i = priv->vlan;
+       return 0;
+}
+
+static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+
+       /* make sure no invalid PVIDs get set */
+       if (vlan >= dev->vlans)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+       return 0;
+}
+
+static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       *vlan = priv->pvid[port];
+       return 0;
+}
+
+static int
+psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       priv->vlan_id[val->port_vlan] = val->value.i;
+       return 0;
+}
+
+static int
+psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       val->value.i = priv->vlan_id[val->port_vlan];
+       return 0;
+}
+
+static struct switch_attr psb6970_globals[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "enable_vlan",
+        .description = "Enable VLAN mode",
+        .set = psb6970_set_vlan,
+        .get = psb6970_get_vlan,
+        .max = 1},
+};
+
+static struct switch_attr psb6970_port[] = {
+};
+
+static struct switch_attr psb6970_vlan[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "vid",
+        .description = "VLAN ID (0-4094)",
+        .set = psb6970_set_vid,
+        .get = psb6970_get_vid,
+        .max = 4094,
+        },
+};
+
+static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (priv->vlan_tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i, j;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
+                       priv->vlan_tagged |= (1 << p->id);
+               else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+
+                       /* make sure that an untagged port does not
+                        * appear in other vlans */
+                       for (j = 0; j < PSB6970_MAX_VLANS; j++) {
+                               if (j == val->port_vlan)
+                                       continue;
+                               priv->vlan_table[j] &= ~(1 << p->id);
+                       }
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static int psb6970_hw_apply(struct switch_dev *dev)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       int i, j;
+
+       mutex_lock(&priv->reg_mutex);
+
+       if (priv->vlan) {
+               /* into the vlan translation unit */
+               for (j = 0; j < PSB6970_MAX_VLANS; j++) {
+                       u8 vp = priv->vlan_table[j];
+
+                       if (vp) {
+                               priv->write(priv->phy, PSB6970_VFxL(j),
+                                           PSB6970_VFxL_VV | priv->vlan_id[j]);
+                               priv->write(priv->phy, PSB6970_VFxH(j),
+                                           ((vp & priv->
+                                             vlan_tagged) <<
+                                            PSB6970_VFxH_TM_SHIFT) | vp);
+                       } else  /* clear VLAN Valid flag for unused vlans */
+                               priv->write(priv->phy, PSB6970_VFxL(j), 0);
+
+               }
+       }
+
+       /* update the port destination mask registers and tag settings */
+       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
+               int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
+
+               if (priv->vlan) {
+                       ec = PSB6970_EC_IFNTE;
+                       dvid = priv->vlan_id[priv->pvid[i]];
+                       pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
+
+                       if ((i << 1) & priv->vlan_tagged)
+                               pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
+               }
+
+               priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
+
+               if (!PSB6970_IS_CPU_PORT(i)) {
+                       priv->write(priv->phy, PSB6970_EC(i), ec);
+                       priv->write(priv->phy, PSB6970_DVID(i), dvid);
+               }
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+static int psb6970_reset_switch(struct switch_dev *dev)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       memset(&priv->vlan, 0, sizeof(struct psb6970_priv) -
+              offsetof(struct psb6970_priv, vlan));
+
+       for (i = 0; i < PSB6970_MAX_VLANS; i++)
+               priv->vlan_id[i] = i;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return psb6970_hw_apply(dev);
+}
+
+static const struct switch_dev_ops psb6970_ops = {
+       .attr_global = {
+                       .attr = psb6970_globals,
+                       .n_attr = ARRAY_SIZE(psb6970_globals),
+                       },
+       .attr_port = {
+                     .attr = psb6970_port,
+                     .n_attr = ARRAY_SIZE(psb6970_port),
+                     },
+       .attr_vlan = {
+                     .attr = psb6970_vlan,
+                     .n_attr = ARRAY_SIZE(psb6970_vlan),
+                     },
+       .get_port_pvid = psb6970_get_pvid,
+       .set_port_pvid = psb6970_set_pvid,
+       .get_vlan_ports = psb6970_get_ports,
+       .set_vlan_ports = psb6970_set_ports,
+       .apply_config = psb6970_hw_apply,
+       .reset_switch = psb6970_reset_switch,
+};
+
+static int psb6970_config_init(struct phy_device *pdev)
+{
+       struct psb6970_priv *priv;
+       struct net_device *dev = pdev->attached_dev;
+       struct switch_dev *swdev;
+       int ret;
+
+       priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       priv->phy = pdev;
+
+       if (pdev->mdio.addr == 0)
+               printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
+                      pdev->attached_dev->name);
+
+       if (pdev->mdio.addr != 0) {
+               kfree(priv);
+               return 0;
+       }
+
+       pdev->supported = pdev->advertising = SUPPORTED_100baseT_Full;
+
+       mutex_init(&priv->reg_mutex);
+       priv->read = psb6970_mii_read;
+       priv->write = psb6970_mii_write;
+
+       pdev->priv = priv;
+
+       swdev = &priv->dev;
+       swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
+       swdev->ops = &psb6970_ops;
+
+       swdev->name = "Lantiq PSB6970";
+       swdev->vlans = PSB6970_MAX_VLANS;
+       swdev->ports = PSB6970_NUM_PORTS;
+
+       if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
+               kfree(priv);
+               goto done;
+       }
+
+       ret = psb6970_reset_switch(&priv->dev);
+       if (ret) {
+               kfree(priv);
+               goto done;
+       }
+
+       dev->phy_ptr = priv;
+
+done:
+       return ret;
+}
+
+static int psb6970_read_status(struct phy_device *phydev)
+{
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+static int psb6970_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int psb6970_probe(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static void psb6970_remove(struct phy_device *pdev)
+{
+       struct psb6970_priv *priv = pdev->priv;
+
+       if (!priv)
+               return;
+
+       if (pdev->mdio.addr == 0)
+               unregister_switch(&priv->dev);
+       kfree(priv);
+}
+
+static int psb6970_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       /* look for the switch on the bus */
+       reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
+       if (reg != PSB6970_CI1_VAL)
+               return 0;
+
+       dev->phy_id = (reg << 16);
+       dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
+
+       return 0;
+}
+
+static struct phy_driver psb6970_driver = {
+       .name = "Lantiq PSB6970",
+       .phy_id = PSB6970_CI1_VAL << 16,
+       .phy_id_mask = 0xffff0000,
+       .features = PHY_BASIC_FEATURES,
+       .probe = psb6970_probe,
+       .remove = psb6970_remove,
+       .config_init = &psb6970_config_init,
+       .config_aneg = &psb6970_config_aneg,
+       .read_status = &psb6970_read_status,
+};
+
+int __init psb6970_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
+       return phy_driver_register(&psb6970_driver, THIS_MODULE);
+}
+
+module_init(psb6970_init);
+
+void __exit psb6970_exit(void)
+{
+       phy_driver_unregister(&psb6970_driver);
+}
+
+module_exit(psb6970_exit);
+
+MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
+MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8306.c b/target/linux/generic/files-3.18/drivers/net/phy/rtl8306.c
new file mode 100644 (file)
index 0000000..6d09c10
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * rtl8306.c: RTL8306S switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/version.h>
+
+//#define DEBUG 1
+
+/* Global (PHY0) */
+#define RTL8306_REG_PAGE               16
+#define RTL8306_REG_PAGE_LO            (1 << 15)
+#define RTL8306_REG_PAGE_HI            (1 << 1) /* inverted */
+
+#define RTL8306_NUM_VLANS              16
+#define RTL8306_NUM_PORTS              6
+#define RTL8306_PORT_CPU               5
+#define RTL8306_NUM_PAGES              4
+#define RTL8306_NUM_REGS               32
+
+#define RTL_NAME_S          "RTL8306S"
+#define RTL_NAME_SD         "RTL8306SD"
+#define RTL_NAME_SDM        "RTL8306SDM"
+#define RTL_NAME_UNKNOWN    "RTL8306(unknown)"
+
+#define RTL8306_MAGIC  0x8306
+
+static LIST_HEAD(phydevs);
+
+struct rtl_priv {
+       struct list_head list;
+       struct switch_dev dev;
+       int page;
+       int type;
+       int do_cpu;
+       struct mii_bus *bus;
+       char hwname[sizeof(RTL_NAME_UNKNOWN)];
+       bool fixup;
+};
+
+struct rtl_phyregs {
+       int nway;
+       int speed;
+       int duplex;
+};
+
+#define to_rtl(_dev) container_of(_dev, struct rtl_priv, dev)
+
+enum {
+       RTL_TYPE_S,
+       RTL_TYPE_SD,
+       RTL_TYPE_SDM,
+};
+
+struct rtl_reg {
+       int page;
+       int phy;
+       int reg;
+       int bits;
+       int shift;
+       int inverted;
+};
+
+#define RTL_VLAN_REGOFS(name) \
+       (RTL_REG_VLAN1_##name - RTL_REG_VLAN0_##name)
+
+#define RTL_PORT_REGOFS(name) \
+       (RTL_REG_PORT1_##name - RTL_REG_PORT0_##name)
+
+#define RTL_PORT_REG(id, reg) \
+       (RTL_REG_PORT0_##reg + (id * RTL_PORT_REGOFS(reg)))
+
+#define RTL_VLAN_REG(id, reg) \
+       (RTL_REG_VLAN0_##reg + (id * RTL_VLAN_REGOFS(reg)))
+
+#define RTL_GLOBAL_REGATTR(reg) \
+       .id = RTL_REG_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = 0, \
+       .set = rtl_attr_set_int, \
+       .get = rtl_attr_get_int
+
+#define RTL_PORT_REGATTR(reg) \
+       .id = RTL_REG_PORT0_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = RTL_PORT_REGOFS(reg), \
+       .set = rtl_attr_set_port_int, \
+       .get = rtl_attr_get_port_int
+
+#define RTL_VLAN_REGATTR(reg) \
+       .id = RTL_REG_VLAN0_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = RTL_VLAN_REGOFS(reg), \
+       .set = rtl_attr_set_vlan_int, \
+       .get = rtl_attr_get_vlan_int
+
+enum rtl_regidx {
+       RTL_REG_CHIPID,
+       RTL_REG_CHIPVER,
+       RTL_REG_CHIPTYPE,
+       RTL_REG_CPUPORT,
+
+       RTL_REG_EN_CPUPORT,
+       RTL_REG_EN_TAG_OUT,
+       RTL_REG_EN_TAG_CLR,
+       RTL_REG_EN_TAG_IN,
+       RTL_REG_TRAP_CPU,
+       RTL_REG_CPU_LINKUP,
+       RTL_REG_TRUNK_PORTSEL,
+       RTL_REG_EN_TRUNK,
+       RTL_REG_RESET,
+
+       RTL_REG_VLAN_ENABLE,
+       RTL_REG_VLAN_FILTER,
+       RTL_REG_VLAN_TAG_ONLY,
+       RTL_REG_VLAN_TAG_AWARE,
+#define RTL_VLAN_ENUM(id) \
+       RTL_REG_VLAN##id##_VID, \
+       RTL_REG_VLAN##id##_PORTMASK
+       RTL_VLAN_ENUM(0),
+       RTL_VLAN_ENUM(1),
+       RTL_VLAN_ENUM(2),
+       RTL_VLAN_ENUM(3),
+       RTL_VLAN_ENUM(4),
+       RTL_VLAN_ENUM(5),
+       RTL_VLAN_ENUM(6),
+       RTL_VLAN_ENUM(7),
+       RTL_VLAN_ENUM(8),
+       RTL_VLAN_ENUM(9),
+       RTL_VLAN_ENUM(10),
+       RTL_VLAN_ENUM(11),
+       RTL_VLAN_ENUM(12),
+       RTL_VLAN_ENUM(13),
+       RTL_VLAN_ENUM(14),
+       RTL_VLAN_ENUM(15),
+#define RTL_PORT_ENUM(id) \
+       RTL_REG_PORT##id##_PVID, \
+       RTL_REG_PORT##id##_NULL_VID_REPLACE, \
+       RTL_REG_PORT##id##_NON_PVID_DISCARD, \
+       RTL_REG_PORT##id##_VID_INSERT, \
+       RTL_REG_PORT##id##_TAG_INSERT, \
+       RTL_REG_PORT##id##_LINK, \
+       RTL_REG_PORT##id##_SPEED, \
+       RTL_REG_PORT##id##_NWAY, \
+       RTL_REG_PORT##id##_NRESTART, \
+       RTL_REG_PORT##id##_DUPLEX, \
+       RTL_REG_PORT##id##_RXEN, \
+       RTL_REG_PORT##id##_TXEN
+       RTL_PORT_ENUM(0),
+       RTL_PORT_ENUM(1),
+       RTL_PORT_ENUM(2),
+       RTL_PORT_ENUM(3),
+       RTL_PORT_ENUM(4),
+       RTL_PORT_ENUM(5),
+};
+
+static const struct rtl_reg rtl_regs[] = {
+       [RTL_REG_CHIPID]         = { 0, 4, 30, 16,  0, 0 },
+       [RTL_REG_CHIPVER]        = { 0, 4, 31,  8,  0, 0 },
+       [RTL_REG_CHIPTYPE]       = { 0, 4, 31,  2,  8, 0 },
+
+       /* CPU port number */
+       [RTL_REG_CPUPORT]        = { 2, 4, 21,  3,  0, 0 },
+       /* Enable CPU port function */
+       [RTL_REG_EN_CPUPORT]     = { 3, 2, 21,  1, 15, 1 },
+       /* Enable CPU port tag insertion */
+       [RTL_REG_EN_TAG_OUT]     = { 3, 2, 21,  1, 12, 0 },
+       /* Enable CPU port tag removal */
+       [RTL_REG_EN_TAG_CLR]     = { 3, 2, 21,  1, 11, 0 },
+       /* Enable CPU port tag checking */
+       [RTL_REG_EN_TAG_IN]      = { 0, 4, 21,  1,  7, 0 },
+       [RTL_REG_EN_TRUNK]       = { 0, 0, 19,  1, 11, 1 },
+       [RTL_REG_TRUNK_PORTSEL]  = { 0, 0, 16,  1,  6, 1 },
+       [RTL_REG_RESET]          = { 0, 0, 16,  1, 12, 0 },
+
+       [RTL_REG_TRAP_CPU]       = { 3, 2, 22,  1,  6, 0 },
+       [RTL_REG_CPU_LINKUP]     = { 0, 6, 22,  1, 15, 0 },
+
+       [RTL_REG_VLAN_TAG_ONLY]  = { 0, 0, 16,  1,  8, 1 },
+       [RTL_REG_VLAN_FILTER]    = { 0, 0, 16,  1,  9, 1 },
+       [RTL_REG_VLAN_TAG_AWARE] = { 0, 0, 16,  1, 10, 1 },
+       [RTL_REG_VLAN_ENABLE]    = { 0, 0, 18,  1,  8, 1 },
+
+#define RTL_VLAN_REGS(id, phy, page, regofs) \
+       [RTL_REG_VLAN##id##_VID] = { page, phy, 25 + regofs, 12, 0, 0 }, \
+       [RTL_REG_VLAN##id##_PORTMASK] = { page, phy, 24 + regofs, 6, 0, 0 }
+       RTL_VLAN_REGS( 0, 0, 0, 0),
+       RTL_VLAN_REGS( 1, 1, 0, 0),
+       RTL_VLAN_REGS( 2, 2, 0, 0),
+       RTL_VLAN_REGS( 3, 3, 0, 0),
+       RTL_VLAN_REGS( 4, 4, 0, 0),
+       RTL_VLAN_REGS( 5, 0, 1, 2),
+       RTL_VLAN_REGS( 6, 1, 1, 2),
+       RTL_VLAN_REGS( 7, 2, 1, 2),
+       RTL_VLAN_REGS( 8, 3, 1, 2),
+       RTL_VLAN_REGS( 9, 4, 1, 2),
+       RTL_VLAN_REGS(10, 0, 1, 4),
+       RTL_VLAN_REGS(11, 1, 1, 4),
+       RTL_VLAN_REGS(12, 2, 1, 4),
+       RTL_VLAN_REGS(13, 3, 1, 4),
+       RTL_VLAN_REGS(14, 4, 1, 4),
+       RTL_VLAN_REGS(15, 0, 1, 6),
+
+#define REG_PORT_SETTING(port, phy) \
+       [RTL_REG_PORT##port##_SPEED] = { 0, phy, 0, 1, 13, 0 }, \
+       [RTL_REG_PORT##port##_NWAY] = { 0, phy, 0, 1, 12, 0 }, \
+       [RTL_REG_PORT##port##_NRESTART] = { 0, phy, 0, 1, 9, 0 }, \
+       [RTL_REG_PORT##port##_DUPLEX] = { 0, phy, 0, 1, 8, 0 }, \
+       [RTL_REG_PORT##port##_TXEN] = { 0, phy, 24, 1, 11, 0 }, \
+       [RTL_REG_PORT##port##_RXEN] = { 0, phy, 24, 1, 10, 0 }, \
+       [RTL_REG_PORT##port##_LINK] = { 0, phy, 1, 1, 2, 0 }, \
+       [RTL_REG_PORT##port##_NULL_VID_REPLACE] = { 0, phy, 22, 1, 12, 0 }, \
+       [RTL_REG_PORT##port##_NON_PVID_DISCARD] = { 0, phy, 22, 1, 11, 0 }, \
+       [RTL_REG_PORT##port##_VID_INSERT] = { 0, phy, 22, 2, 9, 0 }, \
+       [RTL_REG_PORT##port##_TAG_INSERT] = { 0, phy, 22, 2, 0, 0 }
+
+       REG_PORT_SETTING(0, 0),
+       REG_PORT_SETTING(1, 1),
+       REG_PORT_SETTING(2, 2),
+       REG_PORT_SETTING(3, 3),
+       REG_PORT_SETTING(4, 4),
+       REG_PORT_SETTING(5, 6),
+
+#define REG_PORT_PVID(phy, page, regofs) \
+       { page, phy, 24 + regofs, 4, 12, 0 }
+       [RTL_REG_PORT0_PVID] = REG_PORT_PVID(0, 0, 0),
+       [RTL_REG_PORT1_PVID] = REG_PORT_PVID(1, 0, 0),
+       [RTL_REG_PORT2_PVID] = REG_PORT_PVID(2, 0, 0),
+       [RTL_REG_PORT3_PVID] = REG_PORT_PVID(3, 0, 0),
+       [RTL_REG_PORT4_PVID] = REG_PORT_PVID(4, 0, 0),
+       [RTL_REG_PORT5_PVID] = REG_PORT_PVID(0, 1, 2),
+};
+
+
+static inline void
+rtl_set_page(struct rtl_priv *priv, unsigned int page)
+{
+       struct mii_bus *bus = priv->bus;
+       u16 pgsel;
+
+       if (priv->fixup)
+               return;
+
+       if (priv->page == page)
+               return;
+
+       BUG_ON(page > RTL8306_NUM_PAGES);
+       pgsel = bus->read(bus, 0, RTL8306_REG_PAGE);
+       pgsel &= ~(RTL8306_REG_PAGE_LO | RTL8306_REG_PAGE_HI);
+       if (page & (1 << 0))
+               pgsel |= RTL8306_REG_PAGE_LO;
+       if (!(page & (1 << 1))) /* bit is inverted */
+               pgsel |= RTL8306_REG_PAGE_HI;
+       bus->write(bus, 0, RTL8306_REG_PAGE, pgsel);
+}
+
+static inline int
+rtl_w16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+
+       rtl_set_page(priv, page);
+       bus->write(bus, phy, reg, val);
+       bus->read(bus, phy, reg); /* flush */
+       return 0;
+}
+
+static inline int
+rtl_r16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+
+       rtl_set_page(priv, page);
+       return bus->read(bus, phy, reg);
+}
+
+static inline u16
+rtl_rmw(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 mask, u16 val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+       u16 r;
+
+       rtl_set_page(priv, page);
+       r = bus->read(bus, phy, reg);
+       r &= ~mask;
+       r |= val;
+       bus->write(bus, phy, reg, r);
+       return bus->read(bus, phy, reg); /* flush */
+}
+
+
+static inline int
+rtl_get(struct switch_dev *dev, enum rtl_regidx s)
+{
+       const struct rtl_reg *r = &rtl_regs[s];
+       u16 val;
+
+       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
+       if (r->bits == 0) /* unimplemented */
+               return 0;
+
+       val = rtl_r16(dev, r->page, r->phy, r->reg);
+
+       if (r->shift > 0)
+               val >>= r->shift;
+
+       if (r->inverted)
+               val = ~val;
+
+       val &= (1 << r->bits) - 1;
+
+       return val;
+}
+
+static int
+rtl_set(struct switch_dev *dev, enum rtl_regidx s, unsigned int val)
+{
+       const struct rtl_reg *r = &rtl_regs[s];
+       u16 mask = 0xffff;
+
+       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
+
+       if (r->bits == 0) /* unimplemented */
+               return 0;
+
+       if (r->shift > 0)
+               val <<= r->shift;
+
+       if (r->inverted)
+               val = ~val;
+
+       if (r->bits != 16) {
+               mask = (1 << r->bits) - 1;
+               mask <<= r->shift;
+       }
+       val &= mask;
+       return rtl_rmw(dev, r->page, r->phy, r->reg, mask, val);
+}
+
+static void
+rtl_phy_save(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
+{
+       regs->nway = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+       regs->speed = rtl_get(dev, RTL_PORT_REG(port, SPEED));
+       regs->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+}
+
+static void
+rtl_phy_restore(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
+{
+       rtl_set(dev, RTL_PORT_REG(port, NWAY), regs->nway);
+       rtl_set(dev, RTL_PORT_REG(port, SPEED), regs->speed);
+       rtl_set(dev, RTL_PORT_REG(port, DUPLEX), regs->duplex);
+}
+
+static void
+rtl_port_set_enable(struct switch_dev *dev, int port, int enabled)
+{
+       rtl_set(dev, RTL_PORT_REG(port, RXEN), enabled);
+       rtl_set(dev, RTL_PORT_REG(port, TXEN), enabled);
+
+       if ((port >= 5) || !enabled)
+               return;
+
+       /* restart autonegotiation if enabled */
+       rtl_set(dev, RTL_PORT_REG(port, NRESTART), 1);
+}
+
+static int
+rtl_hw_apply(struct switch_dev *dev)
+{
+       int i;
+       int trunk_en, trunk_psel;
+       struct rtl_phyregs port5;
+
+       rtl_phy_save(dev, 5, &port5);
+
+       /* disable rx/tx from PHYs */
+       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
+               rtl_port_set_enable(dev, i, 0);
+       }
+
+       /* save trunking status */
+       trunk_en = rtl_get(dev, RTL_REG_EN_TRUNK);
+       trunk_psel = rtl_get(dev, RTL_REG_TRUNK_PORTSEL);
+
+       /* trunk port 3 and 4
+        * XXX: Big WTF, but RealTek seems to do it */
+       rtl_set(dev, RTL_REG_EN_TRUNK, 1);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 1);
+
+       /* execute the software reset */
+       rtl_set(dev, RTL_REG_RESET, 1);
+
+       /* wait for the reset to complete,
+        * but don't wait for too long */
+       for (i = 0; i < 10; i++) {
+               if (rtl_get(dev, RTL_REG_RESET) == 0)
+                       break;
+
+               msleep(1);
+       }
+
+       /* enable rx/tx from PHYs */
+       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
+               rtl_port_set_enable(dev, i, 1);
+       }
+
+       /* restore trunking settings */
+       rtl_set(dev, RTL_REG_EN_TRUNK, trunk_en);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, trunk_psel);
+       rtl_phy_restore(dev, 5, &port5);
+
+       rtl_set(dev, RTL_REG_CPU_LINKUP, 1);
+
+       return 0;
+}
+
+static void
+rtl_hw_init(struct switch_dev *dev)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       int cpu_mask = 1 << dev->cpu_port;
+       int i;
+
+       rtl_set(dev, RTL_REG_VLAN_ENABLE, 0);
+       rtl_set(dev, RTL_REG_VLAN_FILTER, 0);
+       rtl_set(dev, RTL_REG_EN_TRUNK, 0);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 0);
+
+       /* initialize cpu port settings */
+       if (priv->do_cpu) {
+               rtl_set(dev, RTL_REG_CPUPORT, dev->cpu_port);
+               rtl_set(dev, RTL_REG_EN_CPUPORT, 1);
+       } else {
+               rtl_set(dev, RTL_REG_CPUPORT, 7);
+               rtl_set(dev, RTL_REG_EN_CPUPORT, 0);
+       }
+       rtl_set(dev, RTL_REG_EN_TAG_OUT, 0);
+       rtl_set(dev, RTL_REG_EN_TAG_IN, 0);
+       rtl_set(dev, RTL_REG_EN_TAG_CLR, 0);
+
+       /* reset all vlans */
+       for (i = 0; i < RTL8306_NUM_VLANS; i++) {
+               rtl_set(dev, RTL_VLAN_REG(i, VID), i);
+               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), 0);
+       }
+
+       /* default to port isolation */
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               unsigned long mask;
+
+               if ((1 << i) == cpu_mask)
+                       mask = ((1 << RTL8306_NUM_PORTS) - 1) & ~cpu_mask; /* all bits set */
+               else
+                       mask = cpu_mask | (1 << i);
+
+               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), mask);
+               rtl_set(dev, RTL_PORT_REG(i, PVID), i);
+               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
+               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), 1);
+               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), 3);
+       }
+       rtl_hw_apply(dev);
+}
+
+#ifdef DEBUG
+static int
+rtl_set_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       priv->do_cpu = val->value.i;
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_get_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       val->value.i = priv->do_cpu;
+       return 0;
+}
+
+static int
+rtl_set_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       dev->cpu_port = val->value.i;
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_get_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       val->value.i = dev->cpu_port;
+       return 0;
+}
+#endif
+
+static int
+rtl_reset(struct switch_dev *dev)
+{
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_attr_set_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       int idx = attr->id + (val->port_vlan * attr->ofs);
+       struct rtl_phyregs port;
+
+       if (attr->id >= ARRAY_SIZE(rtl_regs))
+               return -EINVAL;
+
+       if ((attr->max > 0) && (val->value.i > attr->max))
+               return -EINVAL;
+
+       /* access to phy register 22 on port 4/5
+        * needs phy status save/restore */
+       if ((val->port_vlan > 3) &&
+               (rtl_regs[idx].reg == 22) &&
+               (rtl_regs[idx].page == 0)) {
+
+               rtl_phy_save(dev, val->port_vlan, &port);
+               rtl_set(dev, idx, val->value.i);
+               rtl_phy_restore(dev, val->port_vlan, &port);
+       } else {
+               rtl_set(dev, idx, val->value.i);
+       }
+
+       return 0;
+}
+
+static int
+rtl_attr_get_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       int idx = attr->id + (val->port_vlan * attr->ofs);
+
+       if (idx >= ARRAY_SIZE(rtl_regs))
+               return -EINVAL;
+
+       val->value.i = rtl_get(dev, idx);
+       return 0;
+}
+
+static int
+rtl_attr_set_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl_attr_set_int(dev, attr, val);
+}
+
+static int
+rtl_attr_get_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+       return rtl_attr_get_int(dev, attr, val);
+}
+
+static int 
+rtl_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link)
+{
+       if (port >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+
+       /* in case the link changes from down to up, the register is only updated on read */
+       link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
+       if (!link->link)
+               link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
+
+       if (!link->link)
+               return 0;
+
+       link->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+       link->aneg = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+
+       if (rtl_get(dev, RTL_PORT_REG(port, SPEED)))
+               link->speed = SWITCH_PORT_SPEED_100;
+       else
+               link->speed = SWITCH_PORT_SPEED_10;
+
+       return 0;
+}
+
+static int
+rtl_attr_set_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       return rtl_attr_set_int(dev, attr, val);
+}
+
+static int
+rtl_attr_get_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       return rtl_attr_get_int(dev, attr, val);
+}
+
+static int
+rtl_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       unsigned int i, mask;
+
+       mask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               struct switch_port *port;
+
+               if (!(mask & (1 << i)))
+                       continue;
+
+               port = &val->value.ports[val->len];
+               port->id = i;
+               if (rtl_get(dev, RTL_PORT_REG(i, TAG_INSERT)) == 2 || i == dev->cpu_port)
+                       port->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+       }
+
+       return 0;
+}
+
+static int
+rtl_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct rtl_phyregs port;
+       int en = val->value.i;
+       int i;
+
+       rtl_set(dev, RTL_REG_EN_TAG_OUT, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_EN_TAG_IN, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_EN_TAG_CLR, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_VLAN_TAG_AWARE, en);
+       if (en)
+               rtl_set(dev, RTL_REG_VLAN_FILTER, en);
+
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               if (i > 3)
+                       rtl_phy_save(dev, val->port_vlan, &port);
+               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
+               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), (en ? (i == dev->cpu_port ? 0 : 1) : 1));
+               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), (en ? (i == dev->cpu_port ? 2 : 1) : 3));
+               if (i > 3)
+                       rtl_phy_restore(dev, val->port_vlan, &port);
+       }
+       rtl_set(dev, RTL_REG_VLAN_ENABLE, en);
+
+       return 0;
+}
+
+static int
+rtl_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       val->value.i = rtl_get(dev, RTL_REG_VLAN_ENABLE);
+       return 0;
+}
+
+static int
+rtl_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       unsigned int mask = 0;
+       unsigned int oldmask;
+       int i;
+
+       for(i = 0; i < val->len; i++)
+       {
+               struct switch_port *port = &val->value.ports[i];
+               bool tagged = false;
+
+               mask |= (1 << port->id);
+
+               if (port->id == dev->cpu_port)
+                       continue;
+
+               if ((i == dev->cpu_port) ||
+                       (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+                       tagged = true;
+
+               /* fix up PVIDs for added ports */
+               if (!tagged)
+                       rtl_set(dev, RTL_PORT_REG(port->id, PVID), val->port_vlan);
+
+               rtl_set(dev, RTL_PORT_REG(port->id, NON_PVID_DISCARD), (tagged ? 0 : 1));
+               rtl_set(dev, RTL_PORT_REG(port->id, VID_INSERT), (tagged ? 0 : 1));
+               rtl_set(dev, RTL_PORT_REG(port->id, TAG_INSERT), (tagged ? 2 : 1));
+       }
+
+       oldmask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
+       rtl_set(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK), mask);
+
+       /* fix up PVIDs for removed ports, default to last vlan */
+       oldmask &= ~mask;
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               if (!(oldmask & (1 << i)))
+                       continue;
+
+               if (i == dev->cpu_port)
+                       continue;
+
+               if (rtl_get(dev, RTL_PORT_REG(i, PVID)) == val->port_vlan)
+                       rtl_set(dev, RTL_PORT_REG(i, PVID), dev->vlans - 1);
+       }
+
+       return 0;
+}
+
+static struct switch_attr rtl_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .max = 1,
+               .set = rtl_set_vlan,
+               .get = rtl_get_vlan,
+       },
+       {
+               RTL_GLOBAL_REGATTR(EN_TRUNK),
+               .name = "trunk",
+               .description = "Enable port trunking",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(TRUNK_PORTSEL),
+               .name = "trunk_sel",
+               .description = "Select ports for trunking (0: 0,1 - 1: 3,4)",
+               .max = 1,
+       },
+#ifdef DEBUG
+       {
+               RTL_GLOBAL_REGATTR(VLAN_FILTER),
+               .name = "vlan_filter",
+               .description = "Filter incoming packets for allowed VLANS",
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "cpuport",
+               .description = "CPU Port",
+               .set = rtl_set_cpuport,
+               .get = rtl_get_cpuport,
+               .max = RTL8306_NUM_PORTS,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "use_cpuport",
+               .description = "CPU Port handling flag",
+               .set = rtl_set_use_cpuport,
+               .get = rtl_get_use_cpuport,
+               .max = RTL8306_NUM_PORTS,
+       },
+       {
+               RTL_GLOBAL_REGATTR(TRAP_CPU),
+               .name = "trap_cpu",
+               .description = "VLAN trap to CPU",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(VLAN_TAG_AWARE),
+               .name = "vlan_tag_aware",
+               .description = "Enable VLAN tag awareness",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(VLAN_TAG_ONLY),
+               .name = "tag_only",
+               .description = "Only accept tagged packets",
+               .max = 1,
+       },
+#endif
+};
+static struct switch_attr rtl_port[] = {
+       {
+               RTL_PORT_REGATTR(PVID),
+               .name = "pvid",
+               .description = "Port VLAN ID",
+               .max = RTL8306_NUM_VLANS - 1,
+       },
+#ifdef DEBUG
+       {
+               RTL_PORT_REGATTR(NULL_VID_REPLACE),
+               .name = "null_vid",
+               .description = "NULL VID gets replaced by port default vid",
+               .max = 1,
+       },
+       {
+               RTL_PORT_REGATTR(NON_PVID_DISCARD),
+               .name = "non_pvid_discard",
+               .description = "discard packets with VID != PVID",
+               .max = 1,
+       },
+       {
+               RTL_PORT_REGATTR(VID_INSERT),
+               .name = "vid_insert_remove",
+               .description = "how should the switch insert and remove vids ?",
+               .max = 3,
+       },
+       {
+               RTL_PORT_REGATTR(TAG_INSERT),
+               .name = "tag_insert",
+               .description = "tag insertion handling",
+               .max = 3,
+       },
+#endif
+};
+
+static struct switch_attr rtl_vlan[] = {
+       {
+               RTL_VLAN_REGATTR(VID),
+               .name = "vid",
+               .description = "VLAN ID (1-4095)",
+               .max = 4095,
+       },
+};
+
+static const struct switch_dev_ops rtl8306_ops = {
+       .attr_global = {
+               .attr = rtl_globals,
+               .n_attr = ARRAY_SIZE(rtl_globals),
+       },
+       .attr_port = {
+               .attr = rtl_port,
+               .n_attr = ARRAY_SIZE(rtl_port),
+       },
+       .attr_vlan = {
+               .attr = rtl_vlan,
+               .n_attr = ARRAY_SIZE(rtl_vlan),
+       },
+
+       .get_vlan_ports = rtl_get_ports,
+       .set_vlan_ports = rtl_set_ports,
+       .apply_config = rtl_hw_apply,
+       .reset_switch = rtl_reset,
+       .get_port_link = rtl_get_port_link,
+};
+
+static int
+rtl8306_config_init(struct phy_device *pdev)
+{
+       struct net_device *netdev = pdev->attached_dev;
+       struct rtl_priv *priv = pdev->priv;
+       struct switch_dev *dev = &priv->dev;
+       struct switch_val val;
+       unsigned int chipid, chipver, chiptype;
+       int err;
+
+       /* Only init the switch for the primary PHY */
+       if (pdev->mdio.addr != 0)
+               return 0;
+
+       val.value.i = 1;
+       priv->dev.cpu_port = RTL8306_PORT_CPU;
+       priv->dev.ports = RTL8306_NUM_PORTS;
+       priv->dev.vlans = RTL8306_NUM_VLANS;
+       priv->dev.ops = &rtl8306_ops;
+       priv->do_cpu = 0;
+       priv->page = -1;
+       priv->bus = pdev->mdio.bus;
+
+       chipid = rtl_get(dev, RTL_REG_CHIPID);
+       chipver = rtl_get(dev, RTL_REG_CHIPVER);
+       chiptype = rtl_get(dev, RTL_REG_CHIPTYPE);
+       switch(chiptype) {
+       case 0:
+       case 2:
+               strncpy(priv->hwname, RTL_NAME_S, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_S;
+               break;
+       case 1:
+               strncpy(priv->hwname, RTL_NAME_SD, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_SD;
+               break;
+       case 3:
+               strncpy(priv->hwname, RTL_NAME_SDM, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_SDM;
+               break;
+       default:
+               strncpy(priv->hwname, RTL_NAME_UNKNOWN, sizeof(priv->hwname));
+               break;
+       }
+
+       dev->name = priv->hwname;
+       rtl_hw_init(dev);
+
+       printk(KERN_INFO "Registering %s switch with Chip ID: 0x%04x, version: 0x%04x\n", priv->hwname, chipid, chipver);
+
+       err = register_switch(dev, netdev);
+       if (err < 0) {
+               kfree(priv);
+               return err;
+       }
+
+       return 0;
+}
+
+
+static int
+rtl8306_fixup(struct phy_device *pdev)
+{
+       struct rtl_priv priv;
+       u16 chipid;
+
+       /* Attach to primary LAN port and WAN port */
+       if (pdev->mdio.addr != 0 && pdev->mdio.addr != 4)
+               return 0;
+
+       memset(&priv, 0, sizeof(priv));
+       priv.fixup = true;
+       priv.page = -1;
+       priv.bus = pdev->mdio.bus;
+       chipid = rtl_get(&priv.dev, RTL_REG_CHIPID);
+       if (chipid == 0x5988)
+               pdev->phy_id = RTL8306_MAGIC;
+
+       return 0;
+}
+
+static int
+rtl8306_probe(struct phy_device *pdev)
+{
+       struct rtl_priv *priv;
+
+       list_for_each_entry(priv, &phydevs, list) {
+               /*
+                * share one rtl_priv instance between virtual phy
+                * devices on the same bus
+                */
+               if (priv->bus == pdev->mdio.bus)
+                       goto found;
+       }
+       priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->bus = pdev->mdio.bus;
+
+found:
+       pdev->priv = priv;
+       return 0;
+}
+
+static void
+rtl8306_remove(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+       unregister_switch(&priv->dev);
+       kfree(priv);
+}
+
+static int
+rtl8306_config_aneg(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+
+       /* Only for WAN */
+       if (pdev->mdio.addr == 0)
+               return 0;
+
+       /* Restart autonegotiation */
+       rtl_set(&priv->dev, RTL_PORT_REG(4, NWAY), 1);
+       rtl_set(&priv->dev, RTL_PORT_REG(4, NRESTART), 1);
+
+       return 0;
+}
+
+static int
+rtl8306_read_status(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+       struct switch_dev *dev = &priv->dev;
+
+       if (pdev->mdio.addr == 4) {
+               /* WAN */
+               pdev->speed = rtl_get(dev, RTL_PORT_REG(4, SPEED)) ? SPEED_100 : SPEED_10;
+               pdev->duplex = rtl_get(dev, RTL_PORT_REG(4, DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF;
+               pdev->link = !!rtl_get(dev, RTL_PORT_REG(4, LINK));
+       } else {
+               /* LAN */
+               pdev->speed = SPEED_100;
+               pdev->duplex = DUPLEX_FULL;
+               pdev->link = 1;
+       }
+
+       /*
+        * Bypass generic PHY status read,
+        * it doesn't work with this switch
+        */
+       if (pdev->link) {
+               pdev->state = PHY_RUNNING;
+               netif_carrier_on(pdev->attached_dev);
+               pdev->adjust_link(pdev->attached_dev);
+       } else {
+               pdev->state = PHY_NOLINK;
+               netif_carrier_off(pdev->attached_dev);
+               pdev->adjust_link(pdev->attached_dev);
+       }
+
+       return 0;
+}
+
+
+static struct phy_driver rtl8306_driver = {
+       .name           = "Realtek RTL8306S",
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0))
+       .flags          = PHY_HAS_MAGICANEG,
+#endif
+       .phy_id         = RTL8306_MAGIC,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = &rtl8306_probe,
+       .remove         = &rtl8306_remove,
+       .config_init    = &rtl8306_config_init,
+       .config_aneg    = &rtl8306_config_aneg,
+       .read_status    = &rtl8306_read_status,
+};
+
+
+static int __init
+rtl_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, rtl8306_fixup);
+       return phy_driver_register(&rtl8306_driver, THIS_MODULE);
+}
+
+static void __exit
+rtl_exit(void)
+{
+       phy_driver_unregister(&rtl8306_driver);
+}
+
+module_init(rtl_init);
+module_exit(rtl_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files-3.18/drivers/net/phy/rtl8366_smi.c
new file mode 100644 (file)
index 0000000..c0cb680
--- /dev/null
@@ -0,0 +1,1628 @@
+/*
+ * Realtek RTL8366 SMI interface driver
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/rtl8366.h>
+#include <linux/version.h>
+#include <linux/of_mdio.h>
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#include "rtl8366_smi.h"
+
+#define RTL8366_SMI_ACK_RETRY_COUNT         5
+
+#define RTL8366_SMI_HW_STOP_DELAY              25      /* msecs */
+#define RTL8366_SMI_HW_START_DELAY             100     /* msecs */
+
+static inline void rtl8366_smi_clk_delay(struct rtl8366_smi *smi)
+{
+       ndelay(smi->clk_delay);
+}
+
+static void rtl8366_smi_start(struct rtl8366_smi *smi)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       /*
+        * Set GPIO pins to output mode, with initial state:
+        * SCK = 0, SDA = 1
+        */
+       gpio_direction_output(sck, 0);
+       gpio_direction_output(sda, 1);
+       rtl8366_smi_clk_delay(smi);
+
+       /* CLK 1: 0 -> 1, 1 -> 0 */
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+
+       /* CLK 2: */
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 1);
+}
+
+static void rtl8366_smi_stop(struct rtl8366_smi *smi)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 0);
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+
+       /* add a click */
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+
+       /* set GPIO pins to input mode */
+       gpio_direction_input(sda);
+       gpio_direction_input(sck);
+}
+
+static void rtl8366_smi_write_bits(struct rtl8366_smi *smi, u32 data, u32 len)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       for (; len > 0; len--) {
+               rtl8366_smi_clk_delay(smi);
+
+               /* prepare data */
+               gpio_set_value(sda, !!(data & ( 1 << (len - 1))));
+               rtl8366_smi_clk_delay(smi);
+
+               /* clocking */
+               gpio_set_value(sck, 1);
+               rtl8366_smi_clk_delay(smi);
+               gpio_set_value(sck, 0);
+       }
+}
+
+static void rtl8366_smi_read_bits(struct rtl8366_smi *smi, u32 len, u32 *data)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       gpio_direction_input(sda);
+
+       for (*data = 0; len > 0; len--) {
+               u32 u;
+
+               rtl8366_smi_clk_delay(smi);
+
+               /* clocking */
+               gpio_set_value(sck, 1);
+               rtl8366_smi_clk_delay(smi);
+               u = !!gpio_get_value(sda);
+               gpio_set_value(sck, 0);
+
+               *data |= (u << (len - 1));
+       }
+
+       gpio_direction_output(sda, 0);
+}
+
+static int rtl8366_smi_wait_for_ack(struct rtl8366_smi *smi)
+{
+       int retry_cnt;
+
+       retry_cnt = 0;
+       do {
+               u32 ack;
+
+               rtl8366_smi_read_bits(smi, 1, &ack);
+               if (ack == 0)
+                       break;
+
+               if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT) {
+                       dev_err(smi->parent, "ACK timeout\n");
+                       return -ETIMEDOUT;
+               }
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8366_smi_write_byte(struct rtl8366_smi *smi, u8 data)
+{
+       rtl8366_smi_write_bits(smi, data, 8);
+       return rtl8366_smi_wait_for_ack(smi);
+}
+
+static int rtl8366_smi_write_byte_noack(struct rtl8366_smi *smi, u8 data)
+{
+       rtl8366_smi_write_bits(smi, data, 8);
+       return 0;
+}
+
+static int rtl8366_smi_read_byte0(struct rtl8366_smi *smi, u8 *data)
+{
+       u32 t;
+
+       /* read data */
+       rtl8366_smi_read_bits(smi, 8, &t);
+       *data = (t & 0xff);
+
+       /* send an ACK */
+       rtl8366_smi_write_bits(smi, 0x00, 1);
+
+       return 0;
+}
+
+static int rtl8366_smi_read_byte1(struct rtl8366_smi *smi, u8 *data)
+{
+       u32 t;
+
+       /* read data */
+       rtl8366_smi_read_bits(smi, 8, &t);
+       *data = (t & 0xff);
+
+       /* send an ACK */
+       rtl8366_smi_write_bits(smi, 0x01, 1);
+
+       return 0;
+}
+
+static int __rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       unsigned long flags;
+       u8 lo = 0;
+       u8 hi = 0;
+       int ret;
+
+       spin_lock_irqsave(&smi->lock, flags);
+
+       rtl8366_smi_start(smi);
+
+       /* send READ command */
+       ret = rtl8366_smi_write_byte(smi, smi->cmd_read);
+       if (ret)
+               goto out;
+
+       /* set ADDR[7:0] */
+       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
+       if (ret)
+               goto out;
+
+       /* set ADDR[15:8] */
+       ret = rtl8366_smi_write_byte(smi, addr >> 8);
+       if (ret)
+               goto out;
+
+       /* read DATA[7:0] */
+       rtl8366_smi_read_byte0(smi, &lo);
+       /* read DATA[15:8] */
+       rtl8366_smi_read_byte1(smi, &hi);
+
+       *data = ((u32) lo) | (((u32) hi) << 8);
+
+       ret = 0;
+
+ out:
+       rtl8366_smi_stop(smi);
+       spin_unlock_irqrestore(&smi->lock, flags);
+
+       return ret;
+}
+/* Read/write via mdiobus */
+#define MDC_MDIO_CTRL0_REG             31
+#define MDC_MDIO_START_REG             29
+#define MDC_MDIO_CTRL1_REG             21
+#define MDC_MDIO_ADDRESS_REG           23
+#define MDC_MDIO_DATA_WRITE_REG                24
+#define MDC_MDIO_DATA_READ_REG         25
+
+#define MDC_MDIO_START_OP              0xFFFF
+#define MDC_MDIO_ADDR_OP               0x000E
+#define MDC_MDIO_READ_OP               0x0001
+#define MDC_MDIO_WRITE_OP              0x0003
+#define MDC_REALTEK_PHY_ADDR           0x0
+
+int __rtl8366_mdio_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       u32 phy_id = MDC_REALTEK_PHY_ADDR;
+       struct mii_bus *mbus = smi->ext_mbus;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock(&mbus->mdio_lock);
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address control code to register 31 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address to register 23 */
+       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write read control code to register 21 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_READ_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(smi->ext_mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Read data from register 25 */
+       *data = mbus->read(mbus, phy_id, MDC_MDIO_DATA_READ_REG);
+
+       mutex_unlock(&mbus->mdio_lock);
+
+       return 0;
+}
+
+static int __rtl8366_mdio_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       u32 phy_id = MDC_REALTEK_PHY_ADDR;
+       struct mii_bus *mbus = smi->ext_mbus;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock(&mbus->mdio_lock);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address control code to register 31 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address to register 23 */
+       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write data to register 24 */
+       mbus->write(mbus, phy_id, MDC_MDIO_DATA_WRITE_REG, data);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write data control code to register 21 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_WRITE_OP);
+
+       mutex_unlock(&mbus->mdio_lock);
+       return 0;
+}
+
+int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       if (smi->ext_mbus)
+               return __rtl8366_mdio_read_reg(smi, addr, data);
+       else
+               return __rtl8366_smi_read_reg(smi, addr, data);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_read_reg);
+
+static int __rtl8366_smi_write_reg(struct rtl8366_smi *smi,
+                                  u32 addr, u32 data, bool ack)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&smi->lock, flags);
+
+       rtl8366_smi_start(smi);
+
+       /* send WRITE command */
+       ret = rtl8366_smi_write_byte(smi, smi->cmd_write);
+       if (ret)
+               goto out;
+
+       /* set ADDR[7:0] */
+       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
+       if (ret)
+               goto out;
+
+       /* set ADDR[15:8] */
+       ret = rtl8366_smi_write_byte(smi, addr >> 8);
+       if (ret)
+               goto out;
+
+       /* write DATA[7:0] */
+       ret = rtl8366_smi_write_byte(smi, data & 0xff);
+       if (ret)
+               goto out;
+
+       /* write DATA[15:8] */
+       if (ack)
+               ret = rtl8366_smi_write_byte(smi, data >> 8);
+       else
+               ret = rtl8366_smi_write_byte_noack(smi, data >> 8);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+ out:
+       rtl8366_smi_stop(smi);
+       spin_unlock_irqrestore(&smi->lock, flags);
+
+       return ret;
+}
+
+int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       if (smi->ext_mbus)
+               return __rtl8366_mdio_write_reg(smi, addr, data);
+       else
+               return __rtl8366_smi_write_reg(smi, addr, data, true);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg);
+
+int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       return __rtl8366_smi_write_reg(smi, addr, data, false);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg_noack);
+
+int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data)
+{
+       u32 t;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, addr, &t);
+       if (err)
+               return err;
+
+       err = rtl8366_smi_write_reg(smi, addr, (t & ~mask) | data);
+       return err;
+
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_rmwr);
+
+static int rtl8366_reset(struct rtl8366_smi *smi)
+{
+       if (smi->hw_reset) {
+               smi->hw_reset(smi, true);
+               msleep(RTL8366_SMI_HW_STOP_DELAY);
+               smi->hw_reset(smi, false);
+               msleep(RTL8366_SMI_HW_START_DELAY);
+               return 0;
+       }
+
+       return smi->ops->reset_chip(smi);
+}
+
+static int rtl8366_mc_is_used(struct rtl8366_smi *smi, int mc_index, int *used)
+{
+       int err;
+       int i;
+
+       *used = 0;
+       for (i = 0; i < smi->num_ports; i++) {
+               int index = 0;
+
+               err = smi->ops->get_mc_index(smi, i, &index);
+               if (err)
+                       return err;
+
+               if (mc_index == index) {
+                       *used = 1;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member,
+                           u32 untag, u32 fid)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       int err;
+       int i;
+
+       /* Update the 4K table */
+       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+       if (err)
+               return err;
+
+       vlan4k.member = member;
+       vlan4k.untag = untag;
+       vlan4k.fid = fid;
+       err = smi->ops->set_vlan_4k(smi, &vlan4k);
+       if (err)
+               return err;
+
+       /* Try to find an existing MC entry for this VID */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               struct rtl8366_vlan_mc vlanmc;
+
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vid == vlanmc.vid) {
+                       /* update the MC entry */
+                       vlanmc.member = member;
+                       vlanmc.untag = untag;
+                       vlanmc.fid = fid;
+
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       int err;
+       int index;
+
+       err = smi->ops->get_mc_index(smi, port, &index);
+       if (err)
+               return err;
+
+       err = smi->ops->get_vlan_mc(smi, index, &vlanmc);
+       if (err)
+               return err;
+
+       *val = vlanmc.vid;
+       return 0;
+}
+
+static int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port,
+                           unsigned vid)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       struct rtl8366_vlan_4k vlan4k;
+       int err;
+       int i;
+
+       /* Try to find an existing MC entry for this VID */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vid == vlanmc.vid) {
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       /* We have no MC entry for this VID, try to find an empty one */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vlanmc.vid == 0 && vlanmc.member == 0) {
+                       /* Update the entry from the 4K table */
+                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+                       if (err)
+                               return err;
+
+                       vlanmc.vid = vid;
+                       vlanmc.member = vlan4k.member;
+                       vlanmc.untag = vlan4k.untag;
+                       vlanmc.fid = vlan4k.fid;
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       /* MC table is full, try to find an unused entry and replace it */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               int used;
+
+               err = rtl8366_mc_is_used(smi, i, &used);
+               if (err)
+                       return err;
+
+               if (!used) {
+                       /* Update the entry from the 4K table */
+                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+                       if (err)
+                               return err;
+
+                       vlanmc.vid = vid;
+                       vlanmc.member = vlan4k.member;
+                       vlanmc.untag = vlan4k.untag;
+                       vlanmc.fid = vlan4k.fid;
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       dev_err(smi->parent,
+               "all VLAN member configurations are in use\n");
+
+       return -ENOSPC;
+}
+
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+
+       err = smi->ops->enable_vlan(smi, enable);
+       if (err)
+               return err;
+
+       smi->vlan_enabled = enable;
+
+       if (!enable) {
+               smi->vlan4k_enabled = 0;
+               err = smi->ops->enable_vlan4k(smi, enable);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
+
+static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+
+       if (enable) {
+               err = smi->ops->enable_vlan(smi, enable);
+               if (err)
+                       return err;
+
+               smi->vlan_enabled = enable;
+       }
+
+       err = smi->ops->enable_vlan4k(smi, enable);
+       if (err)
+               return err;
+
+       smi->vlan4k_enabled = enable;
+       return 0;
+}
+
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable)
+{
+       int port;
+       int err;
+
+       for (port = 0; port < smi->num_ports; port++) {
+               err = smi->ops->enable_port(smi, port, enable);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports);
+
+int rtl8366_reset_vlan(struct rtl8366_smi *smi)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       int err;
+       int i;
+
+       rtl8366_enable_vlan(smi, 0);
+       rtl8366_enable_vlan4k(smi, 0);
+
+       /* clear VLAN member configurations */
+       vlanmc.vid = 0;
+       vlanmc.priority = 0;
+       vlanmc.member = 0;
+       vlanmc.untag = 0;
+       vlanmc.fid = 0;
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
+
+static int rtl8366_init_vlan(struct rtl8366_smi *smi)
+{
+       int port;
+       int err;
+
+       err = rtl8366_reset_vlan(smi);
+       if (err)
+               return err;
+
+       for (port = 0; port < smi->num_ports; port++) {
+               u32 mask;
+
+               if (port == smi->cpu_port)
+                       mask = (1 << smi->num_ports) - 1;
+               else
+                       mask = (1 << port) | (1 << smi->cpu_port);
+
+               err = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
+               if (err)
+                       return err;
+
+               err = rtl8366_set_pvid(smi, port, (port + 1));
+               if (err)
+                       return err;
+       }
+
+       return rtl8366_enable_vlan(smi, 1);
+}
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+int rtl8366_debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_debugfs_open);
+
+static ssize_t rtl8366_read_debugfs_vlan_mc(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       int i, len = 0;
+       char *buf = smi->buf;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "%2s %6s %4s %6s %6s %3s\n",
+                       "id", "vid","prio", "member", "untag", "fid");
+
+       for (i = 0; i < smi->num_vlan_mc; ++i) {
+               struct rtl8366_vlan_mc vlanmc;
+
+               smi->ops->get_vlan_mc(smi, i, &vlanmc);
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%2d %6d %4d 0x%04x 0x%04x %3d\n",
+                               i, vlanmc.vid, vlanmc.priority,
+                               vlanmc.member, vlanmc.untag, vlanmc.fid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+#define RTL8366_VLAN4K_PAGE_SIZE       64
+#define RTL8366_VLAN4K_NUM_PAGES       (4096 / RTL8366_VLAN4K_PAGE_SIZE)
+
+static ssize_t rtl8366_read_debugfs_vlan_4k(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       int i, len = 0;
+       int offset;
+       char *buf = smi->buf;
+
+       if (smi->dbg_vlan_4k_page >= RTL8366_VLAN4K_NUM_PAGES) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "invalid page: %u\n", smi->dbg_vlan_4k_page);
+               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       }
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "%4s %6s %6s %3s\n",
+                       "vid", "member", "untag", "fid");
+
+       offset = RTL8366_VLAN4K_PAGE_SIZE * smi->dbg_vlan_4k_page;
+       for (i = 0; i < RTL8366_VLAN4K_PAGE_SIZE; i++) {
+               struct rtl8366_vlan_4k vlan4k;
+
+               smi->ops->get_vlan_4k(smi, offset + i, &vlan4k);
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d 0x%04x 0x%04x %3d\n",
+                               vlan4k.vid, vlan4k.member,
+                               vlan4k.untag, vlan4k.fid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_pvid(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       char *buf = smi->buf;
+       int len = 0;
+       int i;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "%4s %4s\n",
+                       "port", "pvid");
+
+       for (i = 0; i < smi->num_ports; i++) {
+               int pvid;
+               int err;
+
+               err = rtl8366_get_pvid(smi, i, &pvid);
+               if (err)
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d error\n", i);
+               else
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d %4d\n", i, pvid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_reg(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       u32 t, reg = smi->dbg_reg;
+       int err, len = 0;
+       char *buf = smi->buf;
+
+       memset(buf, '\0', sizeof(smi->buf));
+
+       err = rtl8366_smi_read_reg(smi, reg, &t);
+       if (err) {
+               len += snprintf(buf, sizeof(smi->buf),
+                               "Read failed (reg: 0x%04x)\n", reg);
+               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       }
+
+       len += snprintf(buf, sizeof(smi->buf), "reg = 0x%04x, val = 0x%04x\n",
+                       reg, t);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_write_debugfs_reg(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       unsigned long data;
+       u32 reg = smi->dbg_reg;
+       int err;
+       size_t len;
+       char *buf = smi->buf;
+
+       len = min(count, sizeof(smi->buf) - 1);
+       if (copy_from_user(buf, user_buf, len)) {
+               dev_err(smi->parent, "copy from user failed\n");
+               return -EFAULT;
+       }
+
+       buf[len] = '\0';
+       if (len > 0 && buf[len - 1] == '\n')
+               buf[len - 1] = '\0';
+
+
+       if (kstrtoul(buf, 16, &data)) {
+               dev_err(smi->parent, "Invalid reg value %s\n", buf);
+       } else {
+               err = rtl8366_smi_write_reg(smi, reg, data);
+               if (err) {
+                       dev_err(smi->parent,
+                               "writing reg 0x%04x val 0x%04lx failed\n",
+                               reg, data);
+               }
+       }
+
+       return count;
+}
+
+static ssize_t rtl8366_read_debugfs_mibs(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = file->private_data;
+       int i, j, len = 0;
+       char *buf = smi->buf;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s",
+                       "Counter");
+
+       for (i = 0; i < smi->num_ports; i++) {
+               char port_buf[10];
+
+               snprintf(port_buf, sizeof(port_buf), "Port %d", i);
+               len += snprintf(buf + len, sizeof(smi->buf) - len, " %12s",
+                               port_buf);
+       }
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
+
+       for (i = 0; i < smi->num_mib_counters; i++) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s ",
+                               smi->mib_counters[i].name);
+               for (j = 0; j < smi->num_ports; j++) {
+                       unsigned long long counter = 0;
+
+                       if (!smi->ops->get_mib_counter(smi, i, j, &counter))
+                               len += snprintf(buf + len,
+                                               sizeof(smi->buf) - len,
+                                               "%12llu ", counter);
+                       else
+                               len += snprintf(buf + len,
+                                               sizeof(smi->buf) - len,
+                                               "%12s ", "error");
+               }
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_rtl8366_regs = {
+       .read   = rtl8366_read_debugfs_reg,
+       .write  = rtl8366_write_debugfs_reg,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_vlan_mc = {
+       .read   = rtl8366_read_debugfs_vlan_mc,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_vlan_4k = {
+       .read   = rtl8366_read_debugfs_vlan_4k,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_pvid = {
+       .read   = rtl8366_read_debugfs_pvid,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_mibs = {
+       .read = rtl8366_read_debugfs_mibs,
+       .open = rtl8366_debugfs_open,
+       .owner = THIS_MODULE
+};
+
+static void rtl8366_debugfs_init(struct rtl8366_smi *smi)
+{
+       struct dentry *node;
+       struct dentry *root;
+
+       if (!smi->debugfs_root)
+               smi->debugfs_root = debugfs_create_dir(dev_name(smi->parent),
+                                                      NULL);
+
+       if (!smi->debugfs_root) {
+               dev_err(smi->parent, "Unable to create debugfs dir\n");
+               return;
+       }
+       root = smi->debugfs_root;
+
+       node = debugfs_create_x16("reg", S_IRUGO | S_IWUSR, root,
+                                 &smi->dbg_reg);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "reg");
+               return;
+       }
+
+       node = debugfs_create_file("val", S_IRUGO | S_IWUSR, root, smi,
+                                  &fops_rtl8366_regs);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "val");
+               return;
+       }
+
+       node = debugfs_create_file("vlan_mc", S_IRUSR, root, smi,
+                                  &fops_rtl8366_vlan_mc);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_mc");
+               return;
+       }
+
+       node = debugfs_create_u8("vlan_4k_page", S_IRUGO | S_IWUSR, root,
+                                 &smi->dbg_vlan_4k_page);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_4k_page");
+               return;
+       }
+
+       node = debugfs_create_file("vlan_4k", S_IRUSR, root, smi,
+                                  &fops_rtl8366_vlan_4k);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_4k");
+               return;
+       }
+
+       node = debugfs_create_file("pvid", S_IRUSR, root, smi,
+                                  &fops_rtl8366_pvid);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "pvid");
+               return;
+       }
+
+       node = debugfs_create_file("mibs", S_IRUSR, smi->debugfs_root, smi,
+                                  &fops_rtl8366_mibs);
+       if (!node)
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "mibs");
+}
+
+static void rtl8366_debugfs_remove(struct rtl8366_smi *smi)
+{
+       if (smi->debugfs_root) {
+               debugfs_remove_recursive(smi->debugfs_root);
+               smi->debugfs_root = NULL;
+       }
+}
+#else
+static inline void rtl8366_debugfs_init(struct rtl8366_smi *smi) {}
+static inline void rtl8366_debugfs_remove(struct rtl8366_smi *smi) {}
+#endif /* CONFIG_RTL8366_SMI_DEBUG_FS */
+
+static int rtl8366_smi_mii_init(struct rtl8366_smi *smi)
+{
+       int ret;
+
+#ifdef CONFIG_OF
+       struct device_node *np = NULL;
+
+       np = of_get_child_by_name(smi->parent->of_node, "mdio-bus");
+#endif
+
+       smi->mii_bus = mdiobus_alloc();
+       if (smi->mii_bus == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       smi->mii_bus->priv = (void *) smi;
+       smi->mii_bus->name = dev_name(smi->parent);
+       smi->mii_bus->read = smi->ops->mii_read;
+       smi->mii_bus->write = smi->ops->mii_write;
+       snprintf(smi->mii_bus->id, MII_BUS_ID_SIZE, "%s",
+                dev_name(smi->parent));
+       smi->mii_bus->parent = smi->parent;
+       smi->mii_bus->phy_mask = ~(0x1f);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
+       {
+               int i;
+               smi->mii_bus->irq = smi->mii_irq;
+               for (i = 0; i < PHY_MAX_ADDR; i++)
+                       smi->mii_irq[i] = PHY_POLL;
+       }
+#endif
+
+#ifdef CONFIG_OF
+       if (np)
+               ret = of_mdiobus_register(smi->mii_bus, np);
+       else
+#endif
+               ret = mdiobus_register(smi->mii_bus);
+
+       if (ret)
+               goto err_free;
+
+       return 0;
+
+ err_free:
+       mdiobus_free(smi->mii_bus);
+ err:
+       return ret;
+}
+
+static void rtl8366_smi_mii_cleanup(struct rtl8366_smi *smi)
+{
+       mdiobus_unregister(smi->mii_bus);
+       mdiobus_free(smi->mii_bus);
+}
+
+int rtl8366_sw_reset_switch(struct switch_dev *dev)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       err = rtl8366_reset(smi);
+       if (err)
+               return err;
+
+       err = smi->ops->setup(smi);
+       if (err)
+               return err;
+
+       err = rtl8366_reset_vlan(smi);
+       if (err)
+               return err;
+
+       err = rtl8366_enable_vlan(smi, 1);
+       if (err)
+               return err;
+
+       return rtl8366_enable_all_ports(smi, 1);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_reset_switch);
+
+int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366_get_pvid(smi, port, val);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_pvid);
+
+int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366_set_pvid(smi, port, val);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid);
+
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int i, len = 0;
+       unsigned long long counter = 0;
+       char *buf = smi->buf;
+
+       if (val->port_vlan >= smi->num_ports)
+               return -EINVAL;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "Port %d MIB counters\n",
+                       val->port_vlan);
+
+       for (i = 0; i < smi->num_mib_counters; ++i) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%-36s: ", smi->mib_counters[i].name);
+               if (!smi->ops->get_mib_counter(smi, i, val->port_vlan,
+                                              &counter))
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                                       "%llu\n", counter);
+               else
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                                       "%s\n", "error");
+       }
+
+       val->value.s = buf;
+       val->len = len;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_mib);
+
+int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
+                               struct switch_port_stats *stats,
+                               int txb_id, int rxb_id)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       unsigned long long counter = 0;
+       int ret;
+
+       if (port >= smi->num_ports)
+               return -EINVAL;
+
+       ret = smi->ops->get_mib_counter(smi, txb_id, port, &counter);
+       if (ret)
+               return ret;
+
+       stats->tx_bytes = counter;
+
+       ret = smi->ops->get_mib_counter(smi, rxb_id, port, &counter);
+       if (ret)
+               return ret;
+
+       stats->rx_bytes = counter;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_stats);
+
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       int i;
+       u32 len = 0;
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       char *buf = smi->buf;
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       memset(buf, '\0', sizeof(smi->buf));
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "VLAN %d: Ports: '", vlan4k.vid);
+
+       for (i = 0; i < smi->num_ports; i++) {
+               if (!(vlan4k.member & (1 << i)))
+                       continue;
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "%d%s", i,
+                               (vlan4k.untag & (1 << i)) ? "" : "t");
+       }
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "', members=%04x, untag=%04x, fid=%u",
+                       vlan4k.member, vlan4k.untag, vlan4k.fid);
+
+       val->value.s = buf;
+       val->len = len;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_info);
+
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       struct switch_port *port;
+       struct rtl8366_vlan_4k vlan4k;
+       int i;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+
+       port = &val->value.ports[0];
+       val->len = 0;
+       for (i = 0; i < smi->num_ports; i++) {
+               if (!(vlan4k.member & BIT(i)))
+                       continue;
+
+               port->id = i;
+               port->flags = (vlan4k.untag & BIT(i)) ?
+                                       0 : BIT(SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+               port++;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_ports);
+
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       struct switch_port *port;
+       u32 member = 0;
+       u32 untag = 0;
+       int err;
+       int i;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       port = &val->value.ports[0];
+       for (i = 0; i < val->len; i++, port++) {
+               int pvid = 0;
+               member |= BIT(port->id);
+
+               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
+                       untag |= BIT(port->id);
+
+               /*
+                * To ensure that we have a valid MC entry for this VLAN,
+                * initialize the port VLAN ID here.
+                */
+               err = rtl8366_get_pvid(smi, port->id, &pvid);
+               if (err < 0)
+                       return err;
+               if (pvid == 0) {
+                       err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_ports);
+
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       val->value.i = vlan4k.fid;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_fid);
+
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       if (val->value.i < 0 || val->value.i > attr->max)
+               return -EINVAL;
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       return rtl8366_set_vlan(smi, val->port_vlan,
+                               vlan4k.member,
+                               vlan4k.untag,
+                               val->value.i);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_fid);
+
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (attr->ofs > 2)
+               return -EINVAL;
+
+       if (attr->ofs == 1)
+               val->value.i = smi->vlan_enabled;
+       else
+               val->value.i = smi->vlan4k_enabled;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_enable);
+
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (attr->ofs > 2)
+               return -EINVAL;
+
+       if (attr->ofs == 1)
+               err = rtl8366_enable_vlan(smi, val->value.i);
+       else
+               err = rtl8366_enable_vlan4k(smi, val->value.i);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_enable);
+
+struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent)
+{
+       struct rtl8366_smi *smi;
+
+       BUG_ON(!parent);
+
+       smi = kzalloc(sizeof(*smi), GFP_KERNEL);
+       if (!smi) {
+               dev_err(parent, "no memory for private data\n");
+               return NULL;
+       }
+
+       smi->parent = parent;
+       return smi;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_alloc);
+
+static int __rtl8366_smi_init(struct rtl8366_smi *smi, const char *name)
+{
+       int err;
+
+       if (!smi->ext_mbus) {
+               err = gpio_request(smi->gpio_sda, name);
+               if (err) {
+                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
+                               smi->gpio_sda, err);
+                       goto err_out;
+               }
+
+               err = gpio_request(smi->gpio_sck, name);
+               if (err) {
+                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
+                               smi->gpio_sck, err);
+                       goto err_free_sda;
+               }
+       }
+
+       spin_lock_init(&smi->lock);
+
+       /* start the switch */
+       if (smi->hw_reset) {
+               smi->hw_reset(smi, false);
+               msleep(RTL8366_SMI_HW_START_DELAY);
+       }
+
+       return 0;
+
+ err_free_sda:
+       gpio_free(smi->gpio_sda);
+ err_out:
+       return err;
+}
+
+static void __rtl8366_smi_cleanup(struct rtl8366_smi *smi)
+{
+       if (smi->hw_reset)
+               smi->hw_reset(smi, true);
+
+       if (!smi->ext_mbus) {
+               gpio_free(smi->gpio_sck);
+               gpio_free(smi->gpio_sda);
+       }
+}
+
+enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata)
+{
+       static struct rtl8366_smi smi;
+       enum rtl8366_type type = RTL8366_TYPE_UNKNOWN;
+       u32 reg = 0;
+
+       memset(&smi, 0, sizeof(smi));
+       smi.gpio_sda = pdata->gpio_sda;
+       smi.gpio_sck = pdata->gpio_sck;
+       smi.clk_delay = 10;
+       smi.cmd_read  = 0xa9;
+       smi.cmd_write = 0xa8;
+
+       if (__rtl8366_smi_init(&smi, "rtl8366"))
+               goto out;
+
+       if (rtl8366_smi_read_reg(&smi, 0x5c, &reg))
+               goto cleanup;
+
+       switch(reg) {
+       case 0x6027:
+               printk("Found an RTL8366S switch\n");
+               type = RTL8366_TYPE_S;
+               break;
+       case 0x5937:
+               printk("Found an RTL8366RB switch\n");
+               type = RTL8366_TYPE_RB;
+               break;
+       default:
+               printk("Found an Unknown RTL8366 switch (id=0x%04x)\n", reg);
+               break;
+       }
+
+cleanup:
+       __rtl8366_smi_cleanup(&smi);
+out:
+       return type;
+}
+
+int rtl8366_smi_init(struct rtl8366_smi *smi)
+{
+       int err;
+
+       if (!smi->ops)
+               return -EINVAL;
+
+       err = __rtl8366_smi_init(smi, dev_name(smi->parent));
+       if (err)
+               goto err_out;
+
+       if (!smi->ext_mbus)
+               dev_info(smi->parent, "using GPIO pins %u (SDA) and %u (SCK)\n",
+                        smi->gpio_sda, smi->gpio_sck);
+       else
+               dev_info(smi->parent, "using MDIO bus '%s'\n", smi->ext_mbus->name);
+
+       err = smi->ops->detect(smi);
+       if (err) {
+               dev_err(smi->parent, "chip detection failed, err=%d\n", err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_reset(smi);
+       if (err)
+               goto err_free_sck;
+
+       err = smi->ops->setup(smi);
+       if (err) {
+               dev_err(smi->parent, "chip setup failed, err=%d\n", err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_init_vlan(smi);
+       if (err) {
+               dev_err(smi->parent, "VLAN initialization failed, err=%d\n",
+                       err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_enable_all_ports(smi, 1);
+       if (err)
+               goto err_free_sck;
+
+       err = rtl8366_smi_mii_init(smi);
+       if (err)
+               goto err_free_sck;
+
+       rtl8366_debugfs_init(smi);
+
+       return 0;
+
+ err_free_sck:
+       __rtl8366_smi_cleanup(smi);
+ err_out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_init);
+
+void rtl8366_smi_cleanup(struct rtl8366_smi *smi)
+{
+       rtl8366_debugfs_remove(smi);
+       rtl8366_smi_mii_cleanup(smi);
+       __rtl8366_smi_cleanup(smi);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup);
+
+#ifdef CONFIG_OF
+static void rtl8366_smi_reset(struct rtl8366_smi *smi, bool active)
+{
+       if (active)
+               reset_control_assert(smi->reset);
+       else
+               reset_control_deassert(smi->reset);
+}
+
+int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0);
+       int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0);
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *mdio_node;
+
+       mdio_node = of_parse_phandle(np, "mii-bus", 0);
+       if (!mdio_node) {
+               dev_err(&pdev->dev, "cannot find mdio node phandle");
+               goto try_gpio;
+       }
+
+       smi->ext_mbus = of_mdio_find_bus(mdio_node);
+       if (!smi->ext_mbus) {
+               dev_err(&pdev->dev,
+                       "cannot find mdio bus from bus handle");
+               goto try_gpio;
+       }
+
+       return 0;
+
+try_gpio:
+       if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) {
+               dev_err(&pdev->dev, "gpios missing in devictree\n");
+               return -EINVAL;
+       }
+
+       smi->gpio_sda = sda;
+       smi->gpio_sck = sck;
+       smi->reset = devm_reset_control_get(&pdev->dev, "switch");
+       if (!IS_ERR(smi->reset))
+               smi->hw_reset = rtl8366_smi_reset;
+
+       return 0;
+}
+#else
+static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       return -ENODEV;
+}
+#endif
+
+int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata = pdev->dev.platform_data;
+
+       if (!pdev->dev.platform_data) {
+               dev_err(&pdev->dev, "no platform data specified\n");
+               return -EINVAL;
+       }
+
+       smi->gpio_sda = pdata->gpio_sda;
+       smi->gpio_sck = pdata->gpio_sck;
+       smi->hw_reset = pdata->hw_reset;
+
+       return 0;
+}
+
+
+struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_alloc(&pdev->dev);
+       if (!smi)
+               return NULL;
+
+       if (pdev->dev.of_node)
+               err = rtl8366_smi_probe_of(pdev, smi);
+       else
+               err = rtl8366_smi_probe_plat(pdev, smi);
+
+       if (err)
+               goto free_smi;
+
+       return smi;
+
+free_smi:
+       kfree(smi);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_probe);
+
+MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files-3.18/drivers/net/phy/rtl8366_smi.h
new file mode 100644 (file)
index 0000000..d1d988a
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Realtek RTL8366 SMI interface driver defines
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8366_SMI_H
+#define _RTL8366_SMI_H
+
+#include <linux/phy.h>
+#include <linux/switch.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct rtl8366_smi_ops;
+struct rtl8366_vlan_ops;
+struct mii_bus;
+struct dentry;
+struct inode;
+struct file;
+
+struct rtl8366_mib_counter {
+       unsigned        base;
+       unsigned        offset;
+       unsigned        length;
+       const char      *name;
+};
+
+struct rtl8366_smi {
+       struct device           *parent;
+       unsigned int            gpio_sda;
+       unsigned int            gpio_sck;
+       void                    (*hw_reset)(struct rtl8366_smi *smi, bool active);
+       unsigned int            clk_delay;      /* ns */
+       u8                      cmd_read;
+       u8                      cmd_write;
+       spinlock_t              lock;
+       struct mii_bus          *mii_bus;
+       int                     mii_irq[PHY_MAX_ADDR];
+       struct switch_dev       sw_dev;
+
+       unsigned int            cpu_port;
+       unsigned int            num_ports;
+       unsigned int            num_vlan_mc;
+       unsigned int            num_mib_counters;
+       struct rtl8366_mib_counter *mib_counters;
+
+       struct rtl8366_smi_ops  *ops;
+
+       int                     vlan_enabled;
+       int                     vlan4k_enabled;
+
+       char                    buf[4096];
+
+       struct reset_control    *reset;
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+       struct dentry           *debugfs_root;
+       u16                     dbg_reg;
+       u8                      dbg_vlan_4k_page;
+#endif
+       struct mii_bus          *ext_mbus;
+};
+
+struct rtl8366_vlan_mc {
+       u16     vid;
+       u16     untag;
+       u16     member;
+       u8      fid;
+       u8      priority;
+};
+
+struct rtl8366_vlan_4k {
+       u16     vid;
+       u16     untag;
+       u16     member;
+       u8      fid;
+};
+
+struct rtl8366_smi_ops {
+       int     (*detect)(struct rtl8366_smi *smi);
+       int     (*reset_chip)(struct rtl8366_smi *smi);
+       int     (*setup)(struct rtl8366_smi *smi);
+
+       int     (*mii_read)(struct mii_bus *bus, int addr, int reg);
+       int     (*mii_write)(struct mii_bus *bus, int addr, int reg, u16 val);
+
+       int     (*get_vlan_mc)(struct rtl8366_smi *smi, u32 index,
+                              struct rtl8366_vlan_mc *vlanmc);
+       int     (*set_vlan_mc)(struct rtl8366_smi *smi, u32 index,
+                              const struct rtl8366_vlan_mc *vlanmc);
+       int     (*get_vlan_4k)(struct rtl8366_smi *smi, u32 vid,
+                              struct rtl8366_vlan_4k *vlan4k);
+       int     (*set_vlan_4k)(struct rtl8366_smi *smi,
+                              const struct rtl8366_vlan_4k *vlan4k);
+       int     (*get_mc_index)(struct rtl8366_smi *smi, int port, int *val);
+       int     (*set_mc_index)(struct rtl8366_smi *smi, int port, int index);
+       int     (*get_mib_counter)(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val);
+       int     (*is_vlan_valid)(struct rtl8366_smi *smi, unsigned vlan);
+       int     (*enable_vlan)(struct rtl8366_smi *smi, int enable);
+       int     (*enable_vlan4k)(struct rtl8366_smi *smi, int enable);
+       int     (*enable_port)(struct rtl8366_smi *smi, int port, int enable);
+};
+
+struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent);
+int rtl8366_smi_init(struct rtl8366_smi *smi);
+void rtl8366_smi_cleanup(struct rtl8366_smi *smi);
+int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data);
+int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data);
+int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data);
+int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data);
+
+int rtl8366_reset_vlan(struct rtl8366_smi *smi);
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable);
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable);
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+int rtl8366_debugfs_open(struct inode *inode, struct file *file);
+#endif
+
+static inline struct rtl8366_smi *sw_to_rtl8366_smi(struct switch_dev *sw)
+{
+       return container_of(sw, struct rtl8366_smi, sw_dev);
+}
+
+int rtl8366_sw_reset_switch(struct switch_dev *dev);
+int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
+int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val);
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val);
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val);
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
+                               struct switch_port_stats *stats,
+                               int txb_id, int rxb_id);
+
+struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
+
+#endif /*  _RTL8366_SMI_H */
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files-3.18/drivers/net/phy/rtl8366rb.c
new file mode 100644 (file)
index 0000000..dc394c0
--- /dev/null
@@ -0,0 +1,1532 @@
+/*
+ * Platform driver for the Realtek RTL8366RB ethernet switch
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
+ * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8366.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8366RB_DRIVER_DESC  "Realtek RTL8366RB ethernet switch driver"
+#define RTL8366RB_DRIVER_VER   "0.2.4"
+
+#define RTL8366RB_PHY_NO_MAX   4
+#define RTL8366RB_PHY_PAGE_MAX 7
+#define RTL8366RB_PHY_ADDR_MAX 31
+
+/* Switch Global Configuration register */
+#define RTL8366RB_SGCR                         0x0000
+#define RTL8366RB_SGCR_EN_BC_STORM_CTRL                BIT(0)
+#define RTL8366RB_SGCR_MAX_LENGTH(_x)          (_x << 4)
+#define RTL8366RB_SGCR_MAX_LENGTH_MASK         RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_MAX_LENGTH_1522         RTL8366RB_SGCR_MAX_LENGTH(0x0)
+#define RTL8366RB_SGCR_MAX_LENGTH_1536         RTL8366RB_SGCR_MAX_LENGTH(0x1)
+#define RTL8366RB_SGCR_MAX_LENGTH_1552         RTL8366RB_SGCR_MAX_LENGTH(0x2)
+#define RTL8366RB_SGCR_MAX_LENGTH_9216         RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_EN_VLAN                 BIT(13)
+#define RTL8366RB_SGCR_EN_VLAN_4KTB            BIT(14)
+
+/* Port Enable Control register */
+#define RTL8366RB_PECR                         0x0001
+
+/* Port Mirror Control Register */
+#define RTL8366RB_PMCR                         0x0007
+#define RTL8366RB_PMCR_SOURCE_PORT(_x)         (_x)
+#define RTL8366RB_PMCR_SOURCE_PORT_MASK                0x000f
+#define RTL8366RB_PMCR_MONITOR_PORT(_x)                ((_x) << 4)
+#define RTL8366RB_PMCR_MONITOR_PORT_MASK       0x00f0
+#define RTL8366RB_PMCR_MIRROR_RX               BIT(8)
+#define RTL8366RB_PMCR_MIRROR_TX               BIT(9)
+#define RTL8366RB_PMCR_MIRROR_SPC              BIT(10)
+#define RTL8366RB_PMCR_MIRROR_ISO              BIT(11)
+
+/* Switch Security Control registers */
+#define RTL8366RB_SSCR0                                0x0002
+#define RTL8366RB_SSCR1                                0x0003
+#define RTL8366RB_SSCR2                                0x0004
+#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA                BIT(0)
+
+#define RTL8366RB_RESET_CTRL_REG               0x0100
+#define RTL8366RB_CHIP_CTRL_RESET_HW           1
+#define RTL8366RB_CHIP_CTRL_RESET_SW           (1 << 1)
+
+#define RTL8366RB_CHIP_VERSION_CTRL_REG                0x050A
+#define RTL8366RB_CHIP_VERSION_MASK            0xf
+#define RTL8366RB_CHIP_ID_REG                  0x0509
+#define RTL8366RB_CHIP_ID_8366                 0x5937
+
+/* PHY registers control */
+#define RTL8366RB_PHY_ACCESS_CTRL_REG          0x8000
+#define RTL8366RB_PHY_ACCESS_DATA_REG          0x8002
+
+#define RTL8366RB_PHY_CTRL_READ                        1
+#define RTL8366RB_PHY_CTRL_WRITE               0
+
+#define RTL8366RB_PHY_REG_MASK                 0x1f
+#define RTL8366RB_PHY_PAGE_OFFSET              5
+#define RTL8366RB_PHY_PAGE_MASK                        (0xf << 5)
+#define RTL8366RB_PHY_NO_OFFSET                        9
+#define RTL8366RB_PHY_NO_MASK                  (0x1f << 9)
+
+#define RTL8366RB_VLAN_INGRESS_CTRL2_REG       0x037f
+
+/* LED control registers */
+#define RTL8366RB_LED_BLINKRATE_REG            0x0430
+#define RTL8366RB_LED_BLINKRATE_BIT            0
+#define RTL8366RB_LED_BLINKRATE_MASK           0x0007
+
+#define RTL8366RB_LED_CTRL_REG                 0x0431
+#define RTL8366RB_LED_0_1_CTRL_REG             0x0432
+#define RTL8366RB_LED_2_3_CTRL_REG             0x0433
+
+#define RTL8366RB_MIB_COUNT                    33
+#define RTL8366RB_GLOBAL_MIB_COUNT             1
+#define RTL8366RB_MIB_COUNTER_PORT_OFFSET      0x0050
+#define RTL8366RB_MIB_COUNTER_BASE             0x1000
+#define RTL8366RB_MIB_CTRL_REG                 0x13F0
+#define RTL8366RB_MIB_CTRL_USER_MASK           0x0FFC
+#define RTL8366RB_MIB_CTRL_BUSY_MASK           BIT(0)
+#define RTL8366RB_MIB_CTRL_RESET_MASK          BIT(1)
+#define RTL8366RB_MIB_CTRL_PORT_RESET(_p)      BIT(2 + (_p))
+#define RTL8366RB_MIB_CTRL_GLOBAL_RESET                BIT(11)
+
+#define RTL8366RB_PORT_VLAN_CTRL_BASE          0x0063
+#define RTL8366RB_PORT_VLAN_CTRL_REG(_p)  \
+               (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366RB_PORT_VLAN_CTRL_MASK          0xf
+#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p)     (4 * ((_p) % 4))
+
+
+#define RTL8366RB_VLAN_TABLE_READ_BASE         0x018C
+#define RTL8366RB_VLAN_TABLE_WRITE_BASE                0x0185
+
+
+#define RTL8366RB_TABLE_ACCESS_CTRL_REG                0x0180
+#define RTL8366RB_TABLE_VLAN_READ_CTRL         0x0E01
+#define RTL8366RB_TABLE_VLAN_WRITE_CTRL                0x0F01
+
+#define RTL8366RB_VLAN_MC_BASE(_x)             (0x0020 + (_x) * 3)
+
+
+#define RTL8366RB_PORT_LINK_STATUS_BASE                0x0014
+#define RTL8366RB_PORT_STATUS_SPEED_MASK       0x0003
+#define RTL8366RB_PORT_STATUS_DUPLEX_MASK      0x0004
+#define RTL8366RB_PORT_STATUS_LINK_MASK                0x0010
+#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK     0x0020
+#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK     0x0040
+#define RTL8366RB_PORT_STATUS_AN_MASK          0x0080
+
+
+#define RTL8366RB_PORT_NUM_CPU         5
+#define RTL8366RB_NUM_PORTS            6
+#define RTL8366RB_NUM_VLANS            16
+#define RTL8366RB_NUM_LEDGROUPS                4
+#define RTL8366RB_NUM_VIDS             4096
+#define RTL8366RB_PRIORITYMAX          7
+#define RTL8366RB_FIDMAX               7
+
+
+#define RTL8366RB_PORT_1               (1 << 0) /* In userspace port 0 */
+#define RTL8366RB_PORT_2               (1 << 1) /* In userspace port 1 */
+#define RTL8366RB_PORT_3               (1 << 2) /* In userspace port 2 */
+#define RTL8366RB_PORT_4               (1 << 3) /* In userspace port 3 */
+#define RTL8366RB_PORT_5               (1 << 4) /* In userspace port 4 */
+
+#define RTL8366RB_PORT_CPU             (1 << 5) /* CPU port */
+
+#define RTL8366RB_PORT_ALL             (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4 |     \
+                                        RTL8366RB_PORT_5 |     \
+                                        RTL8366RB_PORT_CPU)
+
+#define RTL8366RB_PORT_ALL_BUT_CPU     (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4 |     \
+                                        RTL8366RB_PORT_5)
+
+#define RTL8366RB_PORT_ALL_EXTERNAL    (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4)
+
+#define RTL8366RB_PORT_ALL_INTERNAL     RTL8366RB_PORT_CPU
+
+#define RTL8366RB_VLAN_VID_MASK                0xfff
+#define RTL8366RB_VLAN_PRIORITY_SHIFT  12
+#define RTL8366RB_VLAN_PRIORITY_MASK   0x7
+#define RTL8366RB_VLAN_UNTAG_SHIFT     8
+#define RTL8366RB_VLAN_UNTAG_MASK      0xff
+#define RTL8366RB_VLAN_MEMBER_MASK     0xff
+#define RTL8366RB_VLAN_FID_MASK                0x7
+
+
+/* Port ingress bandwidth control */
+#define RTL8366RB_IB_BASE              0x0200
+#define RTL8366RB_IB_REG(pnum)         (RTL8366RB_IB_BASE + pnum)
+#define RTL8366RB_IB_BDTH_MASK         0x3fff
+#define RTL8366RB_IB_PREIFG_OFFSET     14
+#define RTL8366RB_IB_PREIFG_MASK       (1 << RTL8366RB_IB_PREIFG_OFFSET)
+
+/* Port egress bandwidth control */
+#define RTL8366RB_EB_BASE              0x02d1
+#define RTL8366RB_EB_REG(pnum)         (RTL8366RB_EB_BASE + pnum)
+#define RTL8366RB_EB_BDTH_MASK         0x3fff
+#define RTL8366RB_EB_PREIFG_REG        0x02f8
+#define RTL8366RB_EB_PREIFG_OFFSET     9
+#define RTL8366RB_EB_PREIFG_MASK       (1 << RTL8366RB_EB_PREIFG_OFFSET)
+
+#define RTL8366RB_BDTH_SW_MAX          1048512
+#define RTL8366RB_BDTH_UNIT            64
+#define RTL8366RB_BDTH_REG_DEFAULT     16383
+
+/* QOS */
+#define RTL8366RB_QOS_BIT              15
+#define RTL8366RB_QOS_MASK             (1 << RTL8366RB_QOS_BIT)
+/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */
+#define RTL8366RB_QOS_DEFAULT_PREIFG   1
+
+
+#define RTL8366RB_MIB_RXB_ID           0       /* IfInOctets */
+#define RTL8366RB_MIB_TXB_ID           20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 4, "EtherStatsOctets"                          },
+       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 10, 2, "EtherFragments"                            },
+       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 24, 2, "EtherOversizeStats"                        },
+       { 0, 26, 2, "EtherStatsJabbers"                         },
+       { 0, 28, 2, "IfInUcastPkts"                             },
+       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
+       { 0, 34, 2, "EtherStatsDropEvents"                      },
+       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
+       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
+       { 0, 40, 2, "Dot3InPauseFrames"                         },
+       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 44, 4, "IfOutOctets"                               },
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
+       { 0, 66, 2, "IfOutUcastPkts"                            },
+       { 0, 68, 2, "IfOutMulticastPkts"                        },
+       { 0, 70, 2, "IfOutBroadcastPkts"                        },
+};
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int rtl8366rb_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       u32 data;
+
+       rtl8366_smi_write_reg_noack(smi, RTL8366RB_RESET_CTRL_REG,
+                                   RTL8366RB_CHIP_CTRL_RESET_HW);
+       do {
+               msleep(1);
+               if (rtl8366_smi_read_reg(smi, RTL8366RB_RESET_CTRL_REG, &data))
+                       return -EIO;
+
+               if (!(data & RTL8366RB_CHIP_CTRL_RESET_HW))
+                       break;
+       } while (--timeout);
+
+       if (!timeout) {
+               printk("Timeout waiting for the switch to reset\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_setup(struct rtl8366_smi *smi)
+{
+       int err;
+#ifdef CONFIG_OF
+       unsigned i;
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK,
+               RTL8366RB_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366RB_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366RB_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366RB_VLAN_INGRESS_CTRL2_REG, RTL8366RB_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
+static int rtl8366rb_read_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_no, u32 page, u32 addr, u32 *data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366RB_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366RB_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                   RTL8366RB_PHY_CTRL_READ);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
+             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
+             (addr & RTL8366RB_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, 0);
+       if (ret)
+               return ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_PHY_ACCESS_DATA_REG, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366rb_write_phy_reg(struct rtl8366_smi *smi,
+                                 u32 phy_no, u32 page, u32 addr, u32 data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366RB_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366RB_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                   RTL8366RB_PHY_CTRL_WRITE);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
+             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
+             (addr & RTL8366RB_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366rb_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                    int port, unsigned long long *val)
+{
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8366RB_NUM_PORTS || counter >= RTL8366RB_MIB_COUNT)
+               return -EINVAL;
+
+       addr = RTL8366RB_MIB_COUNTER_BASE +
+              RTL8366RB_MIB_COUNTER_PORT_OFFSET * (port) +
+              rtl8366rb_mib_counters[counter].offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       data = 0; /* writing data will be discard by ASIC */
+       err = rtl8366_smi_write_reg(smi, addr, data);
+       if (err)
+               return err;
+
+       /* read MIB control register */
+       err =  rtl8366_smi_read_reg(smi, RTL8366RB_MIB_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       if (data & RTL8366RB_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8366RB_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       mibvalue = 0;
+       for (i = rtl8366rb_mib_counters[counter].length; i > 0; i--) {
+               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
+               if (err)
+                       return err;
+
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8366rb_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                                struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8366RB_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_VLAN_TABLE_WRITE_BASE,
+                                   vid & RTL8366RB_VLAN_VID_MASK);
+       if (err)
+               return err;
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366RB_TABLE_VLAN_READ_CTRL);
+       if (err)
+               return err;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366RB_VLAN_TABLE_READ_BASE + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlan4k->vid = vid;
+       vlan4k->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
+                       RTL8366RB_VLAN_UNTAG_MASK;
+       vlan4k->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
+       vlan4k->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_set_vlan_4k(struct rtl8366_smi *smi,
+                                const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8366RB_NUM_VIDS ||
+           vlan4k->member > RTL8366RB_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8366RB_VLAN_UNTAG_MASK ||
+           vlan4k->fid > RTL8366RB_FIDMAX)
+               return -EINVAL;
+
+       data[0] = vlan4k->vid & RTL8366RB_VLAN_VID_MASK;
+       data[1] = (vlan4k->member & RTL8366RB_VLAN_MEMBER_MASK) |
+                 ((vlan4k->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
+                       RTL8366RB_VLAN_UNTAG_SHIFT);
+       data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366RB_VLAN_TABLE_WRITE_BASE + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366RB_TABLE_VLAN_WRITE_CTRL);
+
+       return err;
+}
+
+static int rtl8366rb_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                                struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8366RB_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366RB_VLAN_MC_BASE(index) + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlanmc->vid = data[0] & RTL8366RB_VLAN_VID_MASK;
+       vlanmc->priority = (data[0] >> RTL8366RB_VLAN_PRIORITY_SHIFT) &
+                          RTL8366RB_VLAN_PRIORITY_MASK;
+       vlanmc->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
+                       RTL8366RB_VLAN_UNTAG_MASK;
+       vlanmc->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
+       vlanmc->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                                const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       if (index >= RTL8366RB_NUM_VLANS ||
+           vlanmc->vid >= RTL8366RB_NUM_VIDS ||
+           vlanmc->priority > RTL8366RB_PRIORITYMAX ||
+           vlanmc->member > RTL8366RB_VLAN_MEMBER_MASK ||
+           vlanmc->untag > RTL8366RB_VLAN_UNTAG_MASK ||
+           vlanmc->fid > RTL8366RB_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->vid & RTL8366RB_VLAN_VID_MASK) |
+                 ((vlanmc->priority & RTL8366RB_VLAN_PRIORITY_MASK) <<
+                       RTL8366RB_VLAN_PRIORITY_SHIFT);
+       data[1] = (vlanmc->member & RTL8366RB_VLAN_MEMBER_MASK) |
+                 ((vlanmc->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
+                       RTL8366RB_VLAN_UNTAG_SHIFT);
+       data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366RB_VLAN_MC_BASE(index) + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+                                  &data);
+       if (err)
+               return err;
+
+       *val = (data >> RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)) &
+              RTL8366RB_PORT_VLAN_CTRL_MASK;
+
+       return 0;
+
+}
+
+static int rtl8366rb_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8366RB_NUM_PORTS || index >= RTL8366RB_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+                               RTL8366RB_PORT_VLAN_CTRL_MASK <<
+                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port),
+                               (index & RTL8366RB_PORT_VLAN_CTRL_MASK) <<
+                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366rb_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8366RB_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8366RB_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8366rb_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN,
+                               (enable) ? RTL8366RB_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366rb_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR,
+                               RTL8366RB_SGCR_EN_VLAN_4KTB,
+                               (enable) ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0);
+}
+
+static int rtl8366rb_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, (1 << port),
+                               (enable) ? 0 : (1 << port));
+}
+
+static int rtl8366rb_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
+                               RTL8366RB_MIB_CTRL_GLOBAL_RESET);
+}
+
+static int rtl8366rb_sw_get_blinkrate(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_LED_BLINKRATE_REG, &data);
+
+       val->value.i = (data & (RTL8366RB_LED_BLINKRATE_MASK));
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_blinkrate(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->value.i >= 6)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_LED_BLINKRATE_REG,
+                               RTL8366RB_LED_BLINKRATE_MASK,
+                               val->value.i);
+}
+
+static int rtl8366rb_sw_get_learning_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_SSCR0, &data);
+       val->value.i = !data;
+
+       return 0;
+}
+
+
+static int rtl8366rb_sw_set_learning_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 portmask = 0;
+       int err = 0;
+
+       if (!val->value.i)
+               portmask = RTL8366RB_PORT_ALL;
+
+       /* set learning for all ports */
+       REG_WR(smi, RTL8366RB_SSCR0, portmask);
+
+       /* set auto ageing for all ports */
+       REG_WR(smi, RTL8366RB_SSCR1, portmask);
+
+       return 0;
+}
+
+static int rtl8366rb_sw_get_port_link(struct switch_dev *dev,
+                                    int port,
+                                    struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PORT_LINK_STATUS_BASE + (port / 2),
+                            &data);
+
+       if (port % 2)
+               data = data >> 8;
+
+       link->link = !!(data & RTL8366RB_PORT_STATUS_LINK_MASK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8366RB_PORT_STATUS_DUPLEX_MASK);
+       link->rx_flow = !!(data & RTL8366RB_PORT_STATUS_RXPAUSE_MASK);
+       link->tx_flow = !!(data & RTL8366RB_PORT_STATUS_TXPAUSE_MASK);
+       link->aneg = !!(data & RTL8366RB_PORT_STATUS_AN_MASK);
+
+       speed = (data & RTL8366RB_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       u32 mask;
+       u32 reg;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       if (val->port_vlan == RTL8366RB_PORT_NUM_CPU) {
+               reg = RTL8366RB_LED_BLINKRATE_REG;
+               mask = 0xF << 4;
+               data = val->value.i << 4;
+       } else {
+               reg = RTL8366RB_LED_CTRL_REG;
+               mask = 0xF << (val->port_vlan * 4),
+               data = val->value.i << (val->port_vlan * 4);
+       }
+
+       return rtl8366_smi_rmwr(smi, reg, mask, data);
+}
+
+static int rtl8366rb_sw_get_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+
+       if (val->port_vlan >= RTL8366RB_NUM_LEDGROUPS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_LED_CTRL_REG, &data);
+       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_disable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 mask, data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       mask = 1 << val->port_vlan ;
+       if (val->value.i)
+               data = mask;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, mask, data);
+}
+
+static int rtl8366rb_sw_get_port_disable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PECR, &data);
+       if (data & (1 << val->port_vlan))
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_rate_in(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
+               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
+       else
+               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_IB_REG(val->port_vlan),
+               RTL8366RB_IB_BDTH_MASK | RTL8366RB_IB_PREIFG_MASK,
+               val->value.i |
+               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_IB_PREIFG_OFFSET));
+
+}
+
+static int rtl8366rb_sw_get_port_rate_in(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_IB_REG(val->port_vlan), &data);
+       data &= RTL8366RB_IB_BDTH_MASK;
+       if (data < RTL8366RB_IB_BDTH_MASK)
+               data += 1;
+
+       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_rate_out(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_rmwr(smi, RTL8366RB_EB_PREIFG_REG,
+               RTL8366RB_EB_PREIFG_MASK,
+               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_EB_PREIFG_OFFSET));
+
+       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
+               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
+       else
+               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_EB_REG(val->port_vlan),
+                       RTL8366RB_EB_BDTH_MASK, val->value.i );
+
+}
+
+static int rtl8366rb_sw_get_port_rate_out(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_EB_REG(val->port_vlan), &data);
+       data &= RTL8366RB_EB_BDTH_MASK;
+       if (data < RTL8366RB_EB_BDTH_MASK)
+               data += 1;
+
+       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_qos_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_QOS_MASK;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_QOS_MASK, data);
+}
+
+static int rtl8366rb_sw_get_qos_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_SGCR, &data);
+       if (data & RTL8366RB_QOS_MASK)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_RX;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_RX, data);
+}
+
+static int rtl8366rb_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_RX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_TX;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_TX, data);
+}
+
+static int rtl8366rb_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_TX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_monitor_isolation_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_ISO;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_ISO, data);
+}
+
+static int rtl8366rb_sw_get_monitor_isolation_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_ISO)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_pause_frames_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_SPC;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_SPC, data);
+}
+
+static int rtl8366rb_sw_get_mirror_pause_frames_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_SPC)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       data = RTL8366RB_PMCR_MONITOR_PORT(val->value.i);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MONITOR_PORT_MASK, data);
+}
+
+static int rtl8366rb_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       val->value.i = (data & RTL8366RB_PMCR_MONITOR_PORT_MASK) >> 4;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_source_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       data = RTL8366RB_PMCR_SOURCE_PORT(val->value.i);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_SOURCE_PORT_MASK, data);
+}
+
+static int rtl8366rb_sw_get_mirror_source_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       val->value.i = data & RTL8366RB_PMCR_SOURCE_PORT_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
+                               RTL8366RB_MIB_CTRL_PORT_RESET(val->port_vlan));
+}
+
+static int rtl8366rb_sw_get_port_stats(struct switch_dev *dev, int port,
+                                       struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8366RB_MIB_TXB_ID, RTL8366RB_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8366rb_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_learning",
+               .description = "Enable learning, enable aging",
+               .set = rtl8366rb_sw_set_learning_enable,
+               .get = rtl8366rb_sw_get_learning_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8366rb_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "blinkrate",
+               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
+               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
+               .set = rtl8366rb_sw_set_blinkrate,
+               .get = rtl8366rb_sw_get_blinkrate,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_qos",
+               .description = "Enable QOS",
+               .set = rtl8366rb_sw_set_qos_enable,
+               .get = rtl8366rb_sw_get_qos_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = rtl8366rb_sw_set_mirror_rx_enable,
+               .get = rtl8366rb_sw_get_mirror_rx_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = rtl8366rb_sw_set_mirror_tx_enable,
+               .get = rtl8366rb_sw_get_mirror_tx_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_monitor_isolation",
+               .description = "Enable isolation of monitor port (TX packets will be dropped)",
+               .set = rtl8366rb_sw_set_monitor_isolation_enable,
+               .get = rtl8366rb_sw_get_monitor_isolation_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_pause_frames",
+               .description = "Enable mirroring of RX pause frames",
+               .set = rtl8366rb_sw_set_mirror_pause_frames_enable,
+               .get = rtl8366rb_sw_get_mirror_pause_frames_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = rtl8366rb_sw_set_mirror_monitor_port,
+               .get = rtl8366rb_sw_get_mirror_monitor_port,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = rtl8366rb_sw_set_mirror_source_port,
+               .get = rtl8366rb_sw_get_mirror_source_port,
+               .max = 5
+       },
+};
+
+static struct switch_attr rtl8366rb_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8366rb_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
+               .max = 15,
+               .set = rtl8366rb_sw_set_port_led,
+               .get = rtl8366rb_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "disable",
+               .description = "Get/Set port state (enabled or disabled)",
+               .max = 1,
+               .set = rtl8366rb_sw_set_port_disable,
+               .get = rtl8366rb_sw_get_port_disable,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "rate_in",
+               .description = "Get/Set port ingress (incoming) bandwidth limit in kbps",
+               .max = RTL8366RB_BDTH_SW_MAX,
+               .set = rtl8366rb_sw_set_port_rate_in,
+               .get = rtl8366rb_sw_get_port_rate_in,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "rate_out",
+               .description = "Get/Set port egress (outgoing) bandwidth limit in kbps",
+               .max = RTL8366RB_BDTH_SW_MAX,
+               .set = rtl8366rb_sw_set_port_rate_out,
+               .get = rtl8366rb_sw_get_port_rate_out,
+       },
+};
+
+static struct switch_attr rtl8366rb_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8366RB_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8366_ops = {
+       .attr_global = {
+               .attr = rtl8366rb_globals,
+               .n_attr = ARRAY_SIZE(rtl8366rb_globals),
+       },
+       .attr_port = {
+               .attr = rtl8366rb_port,
+               .n_attr = ARRAY_SIZE(rtl8366rb_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8366rb_vlan,
+               .n_attr = ARRAY_SIZE(rtl8366rb_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366rb_sw_get_port_link,
+       .get_port_stats = rtl8366rb_sw_get_port_stats,
+};
+
+static int rtl8366rb_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8366RB";
+       dev->cpu_port = RTL8366RB_PORT_NUM_CPU;
+       dev->ports = RTL8366RB_NUM_PORTS;
+       dev->vlans = RTL8366RB_NUM_VIDS;
+       dev->ops = &rtl8366_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8366rb_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8366rb_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8366rb_read_phy_reg(smi, addr, 0, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8366rb_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8366rb_write_phy_reg(smi, addr, 0, reg, val);
+       /* flush write */
+       (void) rtl8366rb_read_phy_reg(smi, addr, 0, reg, &t);
+
+       return err;
+}
+
+static int rtl8366rb_detect(struct rtl8366_smi *smi)
+{
+       u32 chip_id = 0;
+       u32 chip_ver = 0;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_ID_REG, &chip_id);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip id\n");
+               return ret;
+       }
+
+       switch (chip_id) {
+       case RTL8366RB_CHIP_ID_8366:
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_VERSION_CTRL_REG,
+                                  &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
+                chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8366rb_smi_ops = {
+       .detect         = rtl8366rb_detect,
+       .reset_chip     = rtl8366rb_reset_chip,
+       .setup          = rtl8366rb_setup,
+
+       .mii_read       = rtl8366rb_mii_read,
+       .mii_write      = rtl8366rb_mii_write,
+
+       .get_vlan_mc    = rtl8366rb_get_vlan_mc,
+       .set_vlan_mc    = rtl8366rb_set_vlan_mc,
+       .get_vlan_4k    = rtl8366rb_get_vlan_4k,
+       .set_vlan_4k    = rtl8366rb_set_vlan_4k,
+       .get_mc_index   = rtl8366rb_get_mc_index,
+       .set_mc_index   = rtl8366rb_set_mc_index,
+       .get_mib_counter = rtl8366rb_get_mib_counter,
+       .is_vlan_valid  = rtl8366rb_is_vlan_valid,
+       .enable_vlan    = rtl8366rb_enable_vlan,
+       .enable_vlan4k  = rtl8366rb_enable_vlan4k,
+       .enable_port    = rtl8366rb_enable_port,
+};
+
+static int rtl8366rb_probe(struct platform_device *pdev)
+{
+       static int rtl8366_smi_version_printed;
+       struct rtl8366_smi *smi;
+       int err;
+
+       if (!rtl8366_smi_version_printed++)
+               printk(KERN_NOTICE RTL8366RB_DRIVER_DESC
+                      " version " RTL8366RB_DRIVER_VER"\n");
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 10;
+       smi->cmd_read = 0xa9;
+       smi->cmd_write = 0xa8;
+       smi->ops = &rtl8366rb_smi_ops;
+       smi->cpu_port = RTL8366RB_PORT_NUM_CPU;
+       smi->num_ports = RTL8366RB_NUM_PORTS;
+       smi->num_vlan_mc = RTL8366RB_NUM_VLANS;
+       smi->mib_counters = rtl8366rb_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8366rb_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8366rb_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8366rb_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366rb_match[] = {
+       { .compatible = "realtek,rtl8366rb" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366rb_match);
+#endif
+
+static struct platform_driver rtl8366rb_driver = {
+       .driver = {
+               .name           = RTL8366RB_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(rtl8366rb_match),
+       },
+       .probe          = rtl8366rb_probe,
+       .remove         = rtl8366rb_remove,
+};
+
+static int __init rtl8366rb_module_init(void)
+{
+       return platform_driver_register(&rtl8366rb_driver);
+}
+module_init(rtl8366rb_module_init);
+
+static void __exit rtl8366rb_module_exit(void)
+{
+       platform_driver_unregister(&rtl8366rb_driver);
+}
+module_exit(rtl8366rb_module_exit);
+
+MODULE_DESCRIPTION(RTL8366RB_DRIVER_DESC);
+MODULE_VERSION(RTL8366RB_DRIVER_VER);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
+MODULE_AUTHOR("Roman Yeryomin <roman@advem.lv>");
+MODULE_AUTHOR("Colin Leitner <colin.leitner@googlemail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8366RB_DRIVER_NAME);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8366s.c b/target/linux/generic/files-3.18/drivers/net/phy/rtl8366s.c
new file mode 100644 (file)
index 0000000..3f458f9
--- /dev/null
@@ -0,0 +1,1320 @@
+/*
+ * Platform driver for the Realtek RTL8366S ethernet switch
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8366.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8366S_DRIVER_DESC   "Realtek RTL8366S ethernet switch driver"
+#define RTL8366S_DRIVER_VER    "0.2.2"
+
+#define RTL8366S_PHY_NO_MAX    4
+#define RTL8366S_PHY_PAGE_MAX  7
+#define RTL8366S_PHY_ADDR_MAX  31
+
+/* Switch Global Configuration register */
+#define RTL8366S_SGCR                          0x0000
+#define RTL8366S_SGCR_EN_BC_STORM_CTRL         BIT(0)
+#define RTL8366S_SGCR_MAX_LENGTH(_x)           (_x << 4)
+#define RTL8366S_SGCR_MAX_LENGTH_MASK          RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_MAX_LENGTH_1522          RTL8366S_SGCR_MAX_LENGTH(0x0)
+#define RTL8366S_SGCR_MAX_LENGTH_1536          RTL8366S_SGCR_MAX_LENGTH(0x1)
+#define RTL8366S_SGCR_MAX_LENGTH_1552          RTL8366S_SGCR_MAX_LENGTH(0x2)
+#define RTL8366S_SGCR_MAX_LENGTH_16000         RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_EN_VLAN                  BIT(13)
+
+/* Port Enable Control register */
+#define RTL8366S_PECR                          0x0001
+
+/* Green Ethernet Feature (based on GPL_BELKIN_F5D8235-4_v1000 v1.01.24) */
+#define RTL8366S_GREEN_ETHERNET_CTRL_REG       0x000a
+#define RTL8366S_GREEN_ETHERNET_CTRL_MASK      0x0018
+#define RTL8366S_GREEN_ETHERNET_TX_BIT         (1 << 3)
+#define RTL8366S_GREEN_ETHERNET_RX_BIT         (1 << 4)
+
+/* Switch Security Control registers */
+#define RTL8366S_SSCR0                         0x0002
+#define RTL8366S_SSCR1                         0x0003
+#define RTL8366S_SSCR2                         0x0004
+#define RTL8366S_SSCR2_DROP_UNKNOWN_DA         BIT(0)
+
+#define RTL8366S_RESET_CTRL_REG                        0x0100
+#define RTL8366S_CHIP_CTRL_RESET_HW            1
+#define RTL8366S_CHIP_CTRL_RESET_SW            (1 << 1)
+
+#define RTL8366S_CHIP_VERSION_CTRL_REG         0x0104
+#define RTL8366S_CHIP_VERSION_MASK             0xf
+#define RTL8366S_CHIP_ID_REG                   0x0105
+#define RTL8366S_CHIP_ID_8366                  0x8366
+
+/* PHY registers control */
+#define RTL8366S_PHY_ACCESS_CTRL_REG           0x8028
+#define RTL8366S_PHY_ACCESS_DATA_REG           0x8029
+
+#define RTL8366S_PHY_CTRL_READ                 1
+#define RTL8366S_PHY_CTRL_WRITE                        0
+
+#define RTL8366S_PHY_REG_MASK                  0x1f
+#define RTL8366S_PHY_PAGE_OFFSET               5
+#define RTL8366S_PHY_PAGE_MASK                 (0x7 << 5)
+#define RTL8366S_PHY_NO_OFFSET                 9
+#define RTL8366S_PHY_NO_MASK                   (0x1f << 9)
+
+/* Green Ethernet Feature for PHY ports */
+#define RTL8366S_PHY_POWER_SAVING_CTRL_REG     12
+#define RTL8366S_PHY_POWER_SAVING_MASK         0x1000
+
+/* LED control registers */
+#define RTL8366S_LED_BLINKRATE_REG             0x0420
+#define RTL8366S_LED_BLINKRATE_BIT             0
+#define RTL8366S_LED_BLINKRATE_MASK            0x0007
+
+#define RTL8366S_LED_CTRL_REG                  0x0421
+#define RTL8366S_LED_0_1_CTRL_REG              0x0422
+#define RTL8366S_LED_2_3_CTRL_REG              0x0423
+
+#define RTL8366S_MIB_COUNT                     33
+#define RTL8366S_GLOBAL_MIB_COUNT              1
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET       0x0040
+#define RTL8366S_MIB_COUNTER_BASE              0x1000
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET2      0x0008
+#define RTL8366S_MIB_COUNTER_BASE2             0x1180
+#define RTL8366S_MIB_CTRL_REG                  0x11F0
+#define RTL8366S_MIB_CTRL_USER_MASK            0x01FF
+#define RTL8366S_MIB_CTRL_BUSY_MASK            0x0001
+#define RTL8366S_MIB_CTRL_RESET_MASK           0x0002
+
+#define RTL8366S_MIB_CTRL_GLOBAL_RESET_MASK    0x0004
+#define RTL8366S_MIB_CTRL_PORT_RESET_BIT       0x0003
+#define RTL8366S_MIB_CTRL_PORT_RESET_MASK      0x01FC
+
+
+#define RTL8366S_PORT_VLAN_CTRL_BASE           0x0058
+#define RTL8366S_PORT_VLAN_CTRL_REG(_p)  \
+               (RTL8366S_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366S_PORT_VLAN_CTRL_MASK           0xf
+#define RTL8366S_PORT_VLAN_CTRL_SHIFT(_p)      (4 * ((_p) % 4))
+
+
+#define RTL8366S_VLAN_TABLE_READ_BASE          0x018B
+#define RTL8366S_VLAN_TABLE_WRITE_BASE         0x0185
+
+#define RTL8366S_VLAN_TB_CTRL_REG              0x010F
+
+#define RTL8366S_TABLE_ACCESS_CTRL_REG         0x0180
+#define RTL8366S_TABLE_VLAN_READ_CTRL          0x0E01
+#define RTL8366S_TABLE_VLAN_WRITE_CTRL         0x0F01
+
+#define RTL8366S_VLAN_MC_BASE(_x)              (0x0016 + (_x) * 2)
+
+#define RTL8366S_VLAN_MEMBERINGRESS_REG                0x0379
+
+#define RTL8366S_PORT_LINK_STATUS_BASE         0x0060
+#define RTL8366S_PORT_STATUS_SPEED_MASK                0x0003
+#define RTL8366S_PORT_STATUS_DUPLEX_MASK       0x0004
+#define RTL8366S_PORT_STATUS_LINK_MASK         0x0010
+#define RTL8366S_PORT_STATUS_TXPAUSE_MASK      0x0020
+#define RTL8366S_PORT_STATUS_RXPAUSE_MASK      0x0040
+#define RTL8366S_PORT_STATUS_AN_MASK           0x0080
+
+
+#define RTL8366S_PORT_NUM_CPU          5
+#define RTL8366S_NUM_PORTS             6
+#define RTL8366S_NUM_VLANS             16
+#define RTL8366S_NUM_LEDGROUPS         4
+#define RTL8366S_NUM_VIDS              4096
+#define RTL8366S_PRIORITYMAX           7
+#define RTL8366S_FIDMAX                        7
+
+
+#define RTL8366S_PORT_1                        (1 << 0) /* In userspace port 0 */
+#define RTL8366S_PORT_2                        (1 << 1) /* In userspace port 1 */
+#define RTL8366S_PORT_3                        (1 << 2) /* In userspace port 2 */
+#define RTL8366S_PORT_4                        (1 << 3) /* In userspace port 3 */
+
+#define RTL8366S_PORT_UNKNOWN          (1 << 4) /* No known connection */
+#define RTL8366S_PORT_CPU              (1 << 5) /* CPU port */
+
+#define RTL8366S_PORT_ALL              (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4 |      \
+                                        RTL8366S_PORT_UNKNOWN | \
+                                        RTL8366S_PORT_CPU)
+
+#define RTL8366S_PORT_ALL_BUT_CPU      (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4 |      \
+                                        RTL8366S_PORT_UNKNOWN)
+
+#define RTL8366S_PORT_ALL_EXTERNAL     (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4)
+
+#define RTL8366S_PORT_ALL_INTERNAL     (RTL8366S_PORT_UNKNOWN | \
+                                        RTL8366S_PORT_CPU)
+
+#define RTL8366S_VLAN_VID_MASK         0xfff
+#define RTL8366S_VLAN_PRIORITY_SHIFT   12
+#define RTL8366S_VLAN_PRIORITY_MASK    0x7
+#define RTL8366S_VLAN_MEMBER_MASK      0x3f
+#define RTL8366S_VLAN_UNTAG_SHIFT      6
+#define RTL8366S_VLAN_UNTAG_MASK       0x3f
+#define RTL8366S_VLAN_FID_SHIFT                12
+#define RTL8366S_VLAN_FID_MASK         0x7
+
+#define RTL8366S_MIB_RXB_ID            0       /* IfInOctets */
+#define RTL8366S_MIB_TXB_ID            20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366s_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 4, "EtherStatsOctets"                          },
+       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 10, 2, "EtherFragments"                            },
+       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 24, 2, "EtherOversizeStats"                        },
+       { 0, 26, 2, "EtherStatsJabbers"                         },
+       { 0, 28, 2, "IfInUcastPkts"                             },
+       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
+       { 0, 34, 2, "EtherStatsDropEvents"                      },
+       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
+       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
+       { 0, 40, 2, "Dot3InPauseFrames"                         },
+       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 44, 4, "IfOutOctets"                               },
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+
+       /*
+        * The following counters are accessible at a different
+        * base address.
+        */
+       { 1,  0, 2, "Dot1dTpPortInDiscards"                     },
+       { 1,  2, 2, "IfOutUcastPkts"                            },
+       { 1,  4, 2, "IfOutMulticastPkts"                        },
+       { 1,  6, 2, "IfOutBroadcastPkts"                        },
+};
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int rtl8366s_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       u32 data;
+
+       rtl8366_smi_write_reg_noack(smi, RTL8366S_RESET_CTRL_REG,
+                                   RTL8366S_CHIP_CTRL_RESET_HW);
+       do {
+               msleep(1);
+               if (rtl8366_smi_read_reg(smi, RTL8366S_RESET_CTRL_REG, &data))
+                       return -EIO;
+
+               if (!(data & RTL8366S_CHIP_CTRL_RESET_HW))
+                       break;
+       } while (--timeout);
+
+       if (!timeout) {
+               printk("Timeout waiting for the switch to reset\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_read_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_no, u32 page, u32 addr, u32 *data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366S_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366S_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366S_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
+                                   RTL8366S_PHY_CTRL_READ);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
+             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
+             (addr & RTL8366S_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, 0);
+       if (ret)
+               return ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_PHY_ACCESS_DATA_REG, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366s_write_phy_reg(struct rtl8366_smi *smi,
+                                 u32 phy_no, u32 page, u32 addr, u32 data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366S_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366S_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366S_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
+                                   RTL8366S_PHY_CTRL_WRITE);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
+             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
+             (addr & RTL8366S_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366s_set_green_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+       u32 phyData;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       if (enable)
+               phyData |= RTL8366S_PHY_POWER_SAVING_MASK;
+       else
+               phyData &= ~RTL8366S_PHY_POWER_SAVING_MASK;
+
+       err = rtl8366s_write_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, phyData);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8366s_set_green(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+       unsigned i;
+       u32 data = 0;
+
+       if (!enable) {
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       rtl8366s_set_green_port(smi, i, 0);
+               }
+       }
+
+       if (enable)
+               data = (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT);
+
+       REG_RMW(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, RTL8366S_GREEN_ETHERNET_CTRL_MASK, data);
+
+       return 0;
+}
+
+static int rtl8366s_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata;
+       int err;
+       unsigned i;
+#ifdef CONFIG_OF
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+#endif
+
+       pdata = smi->parent->platform_data;
+       if (pdata && pdata->num_initvals && pdata->initvals) {
+               dev_info(smi->parent, "applying initvals\n");
+               for (i = 0; i < pdata->num_initvals; i++)
+                       REG_WR(smi, pdata->initvals[i].reg,
+                              pdata->initvals[i].val);
+       }
+
+#ifdef CONFIG_OF
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+
+       if (of_property_read_bool(np, "realtek,green-ethernet-features")) {
+               dev_info(smi->parent, "activating Green Ethernet features\n");
+
+               err = rtl8366s_set_green(smi, 1);
+               if (err)
+                       return err;
+
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       err = rtl8366s_set_green_port(smi, i, 1);
+                       if (err)
+                               return err;
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366S_SGCR, RTL8366S_SGCR_MAX_LENGTH_MASK,
+               RTL8366S_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366S_VLAN_MEMBERINGRESS_REG, RTL8366S_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366S_SSCR2, RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
+static int rtl8366_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val)
+{
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8366S_NUM_PORTS || counter >= RTL8366S_MIB_COUNT)
+               return -EINVAL;
+
+       switch (rtl8366s_mib_counters[counter].base) {
+       case 0:
+               addr = RTL8366S_MIB_COUNTER_BASE +
+                      RTL8366S_MIB_COUNTER_PORT_OFFSET * port;
+               break;
+
+       case 1:
+               addr = RTL8366S_MIB_COUNTER_BASE2 +
+                       RTL8366S_MIB_COUNTER_PORT_OFFSET2 * port;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       addr += rtl8366s_mib_counters[counter].offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       data = 0; /* writing data will be discard by ASIC */
+       err = rtl8366_smi_write_reg(smi, addr, data);
+       if (err)
+               return err;
+
+       /* read MIB control register */
+       err =  rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       if (data & RTL8366S_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8366S_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       mibvalue = 0;
+       for (i = rtl8366s_mib_counters[counter].length; i > 0; i--) {
+               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
+               if (err)
+                       return err;
+
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8366s_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8366S_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE,
+                                   vid & RTL8366S_VLAN_VID_MASK);
+       if (err)
+               return err;
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366S_TABLE_VLAN_READ_CTRL);
+       if (err)
+               return err;
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366S_VLAN_TABLE_READ_BASE + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlan4k->vid = vid;
+       vlan4k->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+                       RTL8366S_VLAN_UNTAG_MASK;
+       vlan4k->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+       vlan4k->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+                       RTL8366S_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8366S_NUM_VIDS ||
+           vlan4k->member > RTL8366S_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8366S_VLAN_UNTAG_MASK ||
+           vlan4k->fid > RTL8366S_FIDMAX)
+               return -EINVAL;
+
+       data[0] = vlan4k->vid & RTL8366S_VLAN_VID_MASK;
+       data[1] = (vlan4k->member & RTL8366S_VLAN_MEMBER_MASK) |
+                 ((vlan4k->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+                       RTL8366S_VLAN_UNTAG_SHIFT) |
+                 ((vlan4k->fid & RTL8366S_VLAN_FID_MASK) <<
+                       RTL8366S_VLAN_FID_SHIFT);
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366S_VLAN_TABLE_WRITE_BASE + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366S_TABLE_VLAN_WRITE_CTRL);
+
+       return err;
+}
+
+static int rtl8366s_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8366S_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366S_VLAN_MC_BASE(index) + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlanmc->vid = data[0] & RTL8366S_VLAN_VID_MASK;
+       vlanmc->priority = (data[0] >> RTL8366S_VLAN_PRIORITY_SHIFT) &
+                          RTL8366S_VLAN_PRIORITY_MASK;
+       vlanmc->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+                       RTL8366S_VLAN_UNTAG_MASK;
+       vlanmc->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+                     RTL8366S_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       if (index >= RTL8366S_NUM_VLANS ||
+           vlanmc->vid >= RTL8366S_NUM_VIDS ||
+           vlanmc->priority > RTL8366S_PRIORITYMAX ||
+           vlanmc->member > RTL8366S_VLAN_MEMBER_MASK ||
+           vlanmc->untag > RTL8366S_VLAN_UNTAG_MASK ||
+           vlanmc->fid > RTL8366S_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->vid & RTL8366S_VLAN_VID_MASK) |
+                 ((vlanmc->priority & RTL8366S_VLAN_PRIORITY_MASK) <<
+                       RTL8366S_VLAN_PRIORITY_SHIFT);
+       data[1] = (vlanmc->member & RTL8366S_VLAN_MEMBER_MASK) |
+                 ((vlanmc->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+                       RTL8366S_VLAN_UNTAG_SHIFT) |
+                 ((vlanmc->fid & RTL8366S_VLAN_FID_MASK) <<
+                       RTL8366S_VLAN_FID_SHIFT);
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366S_VLAN_MC_BASE(index) + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
+                                  &data);
+       if (err)
+               return err;
+
+       *val = (data >> RTL8366S_PORT_VLAN_CTRL_SHIFT(port)) &
+              RTL8366S_PORT_VLAN_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8366S_NUM_PORTS || index >= RTL8366S_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
+                               RTL8366S_PORT_VLAN_CTRL_MASK <<
+                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port),
+                               (index & RTL8366S_PORT_VLAN_CTRL_MASK) <<
+                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366s_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR, RTL8366S_SGCR_EN_VLAN,
+                               (enable) ? RTL8366S_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366s_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_VLAN_TB_CTRL_REG,
+                               1, (enable) ? 1 : 0);
+}
+
+static int rtl8366s_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8366S_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8366S_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8366s_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_PECR, (1 << port),
+                               (enable) ? 0 : (1 << port));
+}
+
+static int rtl8366s_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG, 0, (1 << 2));
+}
+
+static int rtl8366s_sw_get_blinkrate(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_LED_BLINKRATE_REG, &data);
+
+       val->value.i = (data & (RTL8366S_LED_BLINKRATE_MASK));
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_blinkrate(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->value.i >= 6)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_LED_BLINKRATE_REG,
+                               RTL8366S_LED_BLINKRATE_MASK,
+                               val->value.i);
+}
+
+static int rtl8366s_sw_get_max_length(struct switch_dev *dev,
+                                       const struct switch_attr *attr,
+                                       struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_SGCR, &data);
+
+       val->value.i = ((data & (RTL8366S_SGCR_MAX_LENGTH_MASK)) >> 4);
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_max_length(struct switch_dev *dev,
+                                       const struct switch_attr *attr,
+                                       struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       char length_code;
+
+       switch (val->value.i) {
+               case 0:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1522;
+                       break;
+               case 1:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1536;
+                       break;
+               case 2:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1552;
+                       break;
+               case 3:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_16000;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR,
+                       RTL8366S_SGCR_MAX_LENGTH_MASK,
+                       length_code);
+}
+
+static int rtl8366s_sw_get_learning_enable(struct switch_dev *dev,
+                                          const struct switch_attr *attr,
+                                          struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi,RTL8366S_SSCR0, &data);
+       val->value.i = !data;
+
+       return 0;
+}
+
+
+static int rtl8366s_sw_set_learning_enable(struct switch_dev *dev,
+                                          const struct switch_attr *attr,
+                                          struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 portmask = 0;
+       int err = 0;
+
+       if (!val->value.i)
+               portmask = RTL8366S_PORT_ALL;
+
+       /* set learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, portmask);
+
+       /* set auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, portmask);
+
+       return 0;
+}
+
+static int rtl8366s_sw_get_green(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       val->value.i = ((data & (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT)) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366s_set_green(smi, val->value.i);
+}
+
+static int rtl8366s_sw_get_port_link(struct switch_dev *dev,
+                                    int port,
+                                    struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_PORT_LINK_STATUS_BASE + (port / 2),
+                            &data);
+
+       if (port % 2)
+               data = data >> 8;
+
+       link->link = !!(data & RTL8366S_PORT_STATUS_LINK_MASK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8366S_PORT_STATUS_DUPLEX_MASK);
+       link->rx_flow = !!(data & RTL8366S_PORT_STATUS_RXPAUSE_MASK);
+       link->tx_flow = !!(data & RTL8366S_PORT_STATUS_TXPAUSE_MASK);
+       link->aneg = !!(data & RTL8366S_PORT_STATUS_AN_MASK);
+
+       speed = (data & RTL8366S_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       u32 mask;
+       u32 reg;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS ||
+           (1 << val->port_vlan) == RTL8366S_PORT_UNKNOWN)
+               return -EINVAL;
+
+       if (val->port_vlan == RTL8366S_PORT_NUM_CPU) {
+               reg = RTL8366S_LED_BLINKRATE_REG;
+               mask = 0xF << 4;
+               data = val->value.i << 4;
+       } else {
+               reg = RTL8366S_LED_CTRL_REG;
+               mask = 0xF << (val->port_vlan * 4),
+               data = val->value.i << (val->port_vlan * 4);
+       }
+
+       return rtl8366_smi_rmwr(smi, reg, mask, data);
+}
+
+static int rtl8366s_sw_get_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+
+       if (val->port_vlan >= RTL8366S_NUM_LEDGROUPS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_LED_CTRL_REG, &data);
+       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
+
+       return 0;
+}
+
+static int rtl8366s_sw_get_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+       u32 phyData;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, val->port_vlan, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       val->value.i = ((phyData & RTL8366S_PHY_POWER_SAVING_MASK) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366s_set_green_port(smi, val->port_vlan, val->value.i);
+}
+
+static int rtl8366s_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG,
+                               0, (1 << (val->port_vlan + 3)));
+}
+
+static int rtl8366s_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8366S_MIB_TXB_ID, RTL8366S_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8366s_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_learning",
+               .description = "Enable learning, enable aging",
+               .set = rtl8366s_sw_set_learning_enable,
+               .get = rtl8366s_sw_get_learning_enable,
+               .max = 1,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8366s_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "blinkrate",
+               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
+               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
+               .set = rtl8366s_sw_set_blinkrate,
+               .get = rtl8366s_sw_get_blinkrate,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+               " (0 = 1522, 1 = 1536, 2 = 1552, 3 = 16000 (9216?))",
+               .set = rtl8366s_sw_set_max_length,
+               .get = rtl8366s_sw_get_max_length,
+               .max = 3,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_mode",
+               .description = "Get/Set the router green feature",
+               .set = rtl8366s_sw_set_green,
+               .get = rtl8366s_sw_get_green,
+               .max = 1,
+       },
+};
+
+static struct switch_attr rtl8366s_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8366s_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
+               .max = 15,
+               .set = rtl8366s_sw_set_port_led,
+               .get = rtl8366s_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_port",
+               .description = "Get/Set port green feature (0 - 1)",
+               .max = 1,
+               .set = rtl8366s_sw_set_green_port,
+               .get = rtl8366s_sw_get_green_port,
+       },
+};
+
+static struct switch_attr rtl8366s_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8366S_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8366_ops = {
+       .attr_global = {
+               .attr = rtl8366s_globals,
+               .n_attr = ARRAY_SIZE(rtl8366s_globals),
+       },
+       .attr_port = {
+               .attr = rtl8366s_port,
+               .n_attr = ARRAY_SIZE(rtl8366s_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8366s_vlan,
+               .n_attr = ARRAY_SIZE(rtl8366s_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366s_sw_get_port_link,
+       .get_port_stats = rtl8366s_sw_get_port_stats,
+};
+
+static int rtl8366s_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8366S";
+       dev->cpu_port = RTL8366S_PORT_NUM_CPU;
+       dev->ports = RTL8366S_NUM_PORTS;
+       dev->vlans = RTL8366S_NUM_VIDS;
+       dev->ops = &rtl8366_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8366s_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8366s_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8366s_read_phy_reg(smi, addr, 0, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8366s_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8366s_write_phy_reg(smi, addr, 0, reg, val);
+       /* flush write */
+       (void) rtl8366s_read_phy_reg(smi, addr, 0, reg, &t);
+
+       return err;
+}
+
+static int rtl8366s_detect(struct rtl8366_smi *smi)
+{
+       u32 chip_id = 0;
+       u32 chip_ver = 0;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_ID_REG, &chip_id);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip id\n");
+               return ret;
+       }
+
+       switch (chip_id) {
+       case RTL8366S_CHIP_ID_8366:
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_VERSION_CTRL_REG,
+                                  &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
+                chip_id, chip_ver & RTL8366S_CHIP_VERSION_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8366s_smi_ops = {
+       .detect         = rtl8366s_detect,
+       .reset_chip     = rtl8366s_reset_chip,
+       .setup          = rtl8366s_setup,
+
+       .mii_read       = rtl8366s_mii_read,
+       .mii_write      = rtl8366s_mii_write,
+
+       .get_vlan_mc    = rtl8366s_get_vlan_mc,
+       .set_vlan_mc    = rtl8366s_set_vlan_mc,
+       .get_vlan_4k    = rtl8366s_get_vlan_4k,
+       .set_vlan_4k    = rtl8366s_set_vlan_4k,
+       .get_mc_index   = rtl8366s_get_mc_index,
+       .set_mc_index   = rtl8366s_set_mc_index,
+       .get_mib_counter = rtl8366_get_mib_counter,
+       .is_vlan_valid  = rtl8366s_is_vlan_valid,
+       .enable_vlan    = rtl8366s_enable_vlan,
+       .enable_vlan4k  = rtl8366s_enable_vlan4k,
+       .enable_port    = rtl8366s_enable_port,
+};
+
+static int rtl8366s_probe(struct platform_device *pdev)
+{
+       static int rtl8366_smi_version_printed;
+       struct rtl8366_smi *smi;
+       int err;
+
+       if (!rtl8366_smi_version_printed++)
+               printk(KERN_NOTICE RTL8366S_DRIVER_DESC
+                      " version " RTL8366S_DRIVER_VER"\n");
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 10;
+       smi->cmd_read = 0xa9;
+       smi->cmd_write = 0xa8;
+       smi->ops = &rtl8366s_smi_ops;
+       smi->cpu_port = RTL8366S_PORT_NUM_CPU;
+       smi->num_ports = RTL8366S_NUM_PORTS;
+       smi->num_vlan_mc = RTL8366S_NUM_VLANS;
+       smi->mib_counters = rtl8366s_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8366s_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8366s_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8366s_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8366s_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366s_match[] = {
+       { .compatible = "realtek,rtl8366s" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366s_match);
+#endif
+
+static struct platform_driver rtl8366s_driver = {
+       .driver = {
+               .name           = RTL8366S_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8366s_match),
+#endif
+       },
+       .probe          = rtl8366s_probe,
+       .remove         = rtl8366s_remove,
+};
+
+static int __init rtl8366s_module_init(void)
+{
+       return platform_driver_register(&rtl8366s_driver);
+}
+module_init(rtl8366s_module_init);
+
+static void __exit rtl8366s_module_exit(void)
+{
+       platform_driver_unregister(&rtl8366s_driver);
+}
+module_exit(rtl8366s_module_exit);
+
+MODULE_DESCRIPTION(RTL8366S_DRIVER_DESC);
+MODULE_VERSION(RTL8366S_DRIVER_VER);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8366S_DRIVER_NAME);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8367.c b/target/linux/generic/files-3.18/drivers/net/phy/rtl8367.c
new file mode 100644 (file)
index 0000000..9549961
--- /dev/null
@@ -0,0 +1,1846 @@
+/*
+ * Platform driver for the Realtek RTL8367R/M ethernet switches
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367_RESET_DELAY    1000    /* msecs*/
+
+#define RTL8367_PHY_ADDR_MAX   8
+#define RTL8367_PHY_REG_MAX    31
+
+#define RTL8367_VID_MASK       0xffff
+#define RTL8367_FID_MASK       0xfff
+#define RTL8367_UNTAG_MASK     0xffff
+#define RTL8367_MEMBER_MASK    0xffff
+
+#define RTL8367_PORT_CFG_REG(_p)               (0x000e + 0x20 * (_p))
+#define   RTL8367_PORT_CFG_EGRESS_MODE_SHIFT   4
+#define   RTL8367_PORT_CFG_EGRESS_MODE_MASK    0x3
+#define   RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL        0
+#define   RTL8367_PORT_CFG_EGRESS_MODE_KEEP    1
+#define   RTL8367_PORT_CFG_EGRESS_MODE_PRI     2
+#define   RTL8367_PORT_CFG_EGRESS_MODE_REAL    3
+
+#define RTL8367_BYPASS_LINE_RATE_REG           0x03f7
+
+#define RTL8367_TA_CTRL_REG                    0x0500
+#define   RTL8367_TA_CTRL_STATUS               BIT(12)
+#define   RTL8367_TA_CTRL_METHOD               BIT(5)
+#define   RTL8367_TA_CTRL_CMD_SHIFT            4
+#define   RTL8367_TA_CTRL_CMD_READ             0
+#define   RTL8367_TA_CTRL_CMD_WRITE            1
+#define   RTL8367_TA_CTRL_TABLE_SHIFT          0
+#define   RTL8367_TA_CTRL_TABLE_ACLRULE                1
+#define   RTL8367_TA_CTRL_TABLE_ACLACT         2
+#define   RTL8367_TA_CTRL_TABLE_CVLAN          3
+#define   RTL8367_TA_CTRL_TABLE_L2             4
+#define   RTL8367_TA_CTRL_CVLAN_READ \
+               ((RTL8367_TA_CTRL_CMD_READ << RTL8367_TA_CTRL_CMD_SHIFT) | \
+                RTL8367_TA_CTRL_TABLE_CVLAN)
+#define   RTL8367_TA_CTRL_CVLAN_WRITE \
+               ((RTL8367_TA_CTRL_CMD_WRITE << RTL8367_TA_CTRL_CMD_SHIFT) | \
+                RTL8367_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367_TA_ADDR_REG                    0x0501
+#define   RTL8367_TA_ADDR_MASK                 0x3fff
+
+#define RTL8367_TA_DATA_REG(_x)                        (0x0503 + (_x))
+#define   RTL8367_TA_VLAN_DATA_SIZE            4
+#define   RTL8367_TA_VLAN_VID_MASK             RTL8367_VID_MASK
+#define   RTL8367_TA_VLAN_MEMBER_SHIFT         0
+#define   RTL8367_TA_VLAN_MEMBER_MASK          RTL8367_MEMBER_MASK
+#define   RTL8367_TA_VLAN_FID_SHIFT            0
+#define   RTL8367_TA_VLAN_FID_MASK             RTL8367_FID_MASK
+#define   RTL8367_TA_VLAN_UNTAG1_SHIFT         14
+#define   RTL8367_TA_VLAN_UNTAG1_MASK          0x3
+#define   RTL8367_TA_VLAN_UNTAG2_SHIFT         0
+#define   RTL8367_TA_VLAN_UNTAG2_MASK          0x3fff
+
+#define RTL8367_VLAN_PVID_CTRL_REG(_p)         (0x0700 + (_p) / 2)
+#define RTL8367_VLAN_PVID_CTRL_MASK            0x1f
+#define RTL8367_VLAN_PVID_CTRL_SHIFT(_p)       (8 * ((_p) % 2))
+
+#define RTL8367_VLAN_MC_BASE(_x)               (0x0728 + (_x) * 4)
+#define   RTL8367_VLAN_MC_DATA_SIZE            4
+#define   RTL8367_VLAN_MC_MEMBER_SHIFT         0
+#define   RTL8367_VLAN_MC_MEMBER_MASK          RTL8367_MEMBER_MASK
+#define   RTL8367_VLAN_MC_FID_SHIFT            0
+#define   RTL8367_VLAN_MC_FID_MASK             RTL8367_FID_MASK
+#define   RTL8367_VLAN_MC_EVID_SHIFT           0
+#define   RTL8367_VLAN_MC_EVID_MASK            RTL8367_VID_MASK
+
+#define RTL8367_VLAN_CTRL_REG                  0x07a8
+#define   RTL8367_VLAN_CTRL_ENABLE             BIT(0)
+
+#define RTL8367_VLAN_INGRESS_REG               0x07a9
+
+#define RTL8367_PORT_ISOLATION_REG(_p)         (0x08a2 + (_p))
+
+#define RTL8367_MIB_COUNTER_REG(_x)            (0x1000 + (_x))
+
+#define RTL8367_MIB_ADDRESS_REG                        0x1004
+
+#define RTL8367_MIB_CTRL_REG(_x)               (0x1005 + (_x))
+#define   RTL8367_MIB_CTRL_GLOBAL_RESET_MASK   BIT(11)
+#define   RTL8367_MIB_CTRL_QM_RESET_MASK       BIT(10)
+#define   RTL8367_MIB_CTRL_PORT_RESET_MASK(_p) BIT(2 + (_p))
+#define   RTL8367_MIB_CTRL_RESET_MASK          BIT(1)
+#define   RTL8367_MIB_CTRL_BUSY_MASK           BIT(0)
+
+#define RTL8367_MIB_COUNT                      36
+#define RTL8367_MIB_COUNTER_PORT_OFFSET                0x0050
+
+#define RTL8367_SWC0_REG                       0x1200
+#define   RTL8367_SWC0_MAX_LENGTH_SHIFT                13
+#define   RTL8367_SWC0_MAX_LENGTH(_x)          ((_x) << 13)
+#define   RTL8367_SWC0_MAX_LENGTH_MASK         RTL8367_SWC0_MAX_LENGTH(0x3)
+#define   RTL8367_SWC0_MAX_LENGTH_1522         RTL8367_SWC0_MAX_LENGTH(0)
+#define   RTL8367_SWC0_MAX_LENGTH_1536         RTL8367_SWC0_MAX_LENGTH(1)
+#define   RTL8367_SWC0_MAX_LENGTH_1552         RTL8367_SWC0_MAX_LENGTH(2)
+#define   RTL8367_SWC0_MAX_LENGTH_16000                RTL8367_SWC0_MAX_LENGTH(3)
+
+#define RTL8367_CHIP_NUMBER_REG                        0x1300
+
+#define RTL8367_CHIP_VER_REG                   0x1301
+#define   RTL8367_CHIP_VER_RLVID_SHIFT         12
+#define   RTL8367_CHIP_VER_RLVID_MASK          0xf
+#define   RTL8367_CHIP_VER_MCID_SHIFT          8
+#define   RTL8367_CHIP_VER_MCID_MASK           0xf
+#define   RTL8367_CHIP_VER_BOID_SHIFT          4
+#define   RTL8367_CHIP_VER_BOID_MASK           0xf
+
+#define RTL8367_CHIP_MODE_REG                  0x1302
+#define   RTL8367_CHIP_MODE_MASK               0x7
+
+#define RTL8367_CHIP_DEBUG0_REG                        0x1303
+#define   RTL8367_CHIP_DEBUG0_DUMMY0(_x)       BIT(8 + (_x))
+
+#define RTL8367_CHIP_DEBUG1_REG                        0x1304
+
+#define RTL8367_DIS_REG                                0x1305
+#define   RTL8367_DIS_SKIP_MII_RXER(_x)                BIT(12 + (_x))
+#define   RTL8367_DIS_RGMII_SHIFT(_x)          (4 * (_x))
+#define   RTL8367_DIS_RGMII_MASK               0x7
+
+#define RTL8367_EXT_RGMXF_REG(_x)              (0x1306 + (_x))
+#define   RTL8367_EXT_RGMXF_DUMMY0_SHIFT       5
+#define   RTL8367_EXT_RGMXF_DUMMY0_MASK        0x7ff
+#define   RTL8367_EXT_RGMXF_TXDELAY_SHIFT      3
+#define   RTL8367_EXT_RGMXF_TXDELAY_MASK       1
+#define   RTL8367_EXT_RGMXF_RXDELAY_MASK       0x7
+
+#define RTL8367_DI_FORCE_REG(_x)               (0x1310 + (_x))
+#define   RTL8367_DI_FORCE_MODE                        BIT(12)
+#define   RTL8367_DI_FORCE_NWAY                        BIT(7)
+#define   RTL8367_DI_FORCE_TXPAUSE             BIT(6)
+#define   RTL8367_DI_FORCE_RXPAUSE             BIT(5)
+#define   RTL8367_DI_FORCE_LINK                        BIT(4)
+#define   RTL8367_DI_FORCE_DUPLEX              BIT(2)
+#define   RTL8367_DI_FORCE_SPEED_MASK          3
+#define   RTL8367_DI_FORCE_SPEED_10            0
+#define   RTL8367_DI_FORCE_SPEED_100           1
+#define   RTL8367_DI_FORCE_SPEED_1000          2
+
+#define RTL8367_MAC_FORCE_REG(_x)              (0x1312 + (_x))
+
+#define RTL8367_CHIP_RESET_REG                 0x1322
+#define   RTL8367_CHIP_RESET_SW                        BIT(1)
+#define   RTL8367_CHIP_RESET_HW                        BIT(0)
+
+#define RTL8367_PORT_STATUS_REG(_p)            (0x1352 + (_p))
+#define   RTL8367_PORT_STATUS_NWAY             BIT(7)
+#define   RTL8367_PORT_STATUS_TXPAUSE          BIT(6)
+#define   RTL8367_PORT_STATUS_RXPAUSE          BIT(5)
+#define   RTL8367_PORT_STATUS_LINK             BIT(4)
+#define   RTL8367_PORT_STATUS_DUPLEX           BIT(2)
+#define   RTL8367_PORT_STATUS_SPEED_MASK       0x0003
+#define   RTL8367_PORT_STATUS_SPEED_10         0
+#define   RTL8367_PORT_STATUS_SPEED_100                1
+#define   RTL8367_PORT_STATUS_SPEED_1000       2
+
+#define RTL8367_RTL_NO_REG                     0x13c0
+#define   RTL8367_RTL_NO_8367R                 0x3670
+#define   RTL8367_RTL_NO_8367M                 0x3671
+
+#define RTL8367_RTL_VER_REG                    0x13c1
+#define   RTL8367_RTL_VER_MASK                 0xf
+
+#define RTL8367_RTL_MAGIC_ID_REG               0x13c2
+#define   RTL8367_RTL_MAGIC_ID_VAL             0x0249
+
+#define RTL8367_LED_SYS_CONFIG_REG             0x1b00
+#define RTL8367_LED_MODE_REG                   0x1b02
+#define   RTL8367_LED_MODE_RATE_M              0x7
+#define   RTL8367_LED_MODE_RATE_S              1
+
+#define RTL8367_LED_CONFIG_REG                 0x1b03
+#define   RTL8367_LED_CONFIG_DATA_S            12
+#define   RTL8367_LED_CONFIG_DATA_M            0x3
+#define   RTL8367_LED_CONFIG_SEL               BIT(14)
+#define   RTL8367_LED_CONFIG_LED_CFG_M         0xf
+
+#define RTL8367_PARA_LED_IO_EN1_REG            0x1b24
+#define RTL8367_PARA_LED_IO_EN2_REG            0x1b25
+#define   RTL8367_PARA_LED_IO_EN_PMASK         0xff
+
+#define RTL8367_IA_CTRL_REG                    0x1f00
+#define   RTL8367_IA_CTRL_RW(_x)               ((_x) << 1)
+#define   RTL8367_IA_CTRL_RW_READ              RTL8367_IA_CTRL_RW(0)
+#define   RTL8367_IA_CTRL_RW_WRITE             RTL8367_IA_CTRL_RW(1)
+#define   RTL8367_IA_CTRL_CMD_MASK             BIT(0)
+
+#define RTL8367_IA_STATUS_REG                  0x1f01
+#define   RTL8367_IA_STATUS_PHY_BUSY           BIT(2)
+#define   RTL8367_IA_STATUS_SDS_BUSY           BIT(1)
+#define   RTL8367_IA_STATUS_MDX_BUSY           BIT(0)
+
+#define RTL8367_IA_ADDRESS_REG                 0x1f02
+
+#define RTL8367_IA_WRITE_DATA_REG              0x1f03
+#define RTL8367_IA_READ_DATA_REG               0x1f04
+
+#define RTL8367_INTERNAL_PHY_REG(_a, _r)       (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367_CPU_PORT_NUM           9
+#define RTL8367_NUM_PORTS              10
+#define RTL8367_NUM_VLANS              32
+#define RTL8367_NUM_LEDGROUPS          4
+#define RTL8367_NUM_VIDS               4096
+#define RTL8367_PRIORITYMAX            7
+#define RTL8367_FIDMAX                 7
+
+#define RTL8367_PORT_0                 BIT(0)
+#define RTL8367_PORT_1                 BIT(1)
+#define RTL8367_PORT_2                 BIT(2)
+#define RTL8367_PORT_3                 BIT(3)
+#define RTL8367_PORT_4                 BIT(4)
+#define RTL8367_PORT_5                 BIT(5)
+#define RTL8367_PORT_6                 BIT(6)
+#define RTL8367_PORT_7                 BIT(7)
+#define RTL8367_PORT_E1                        BIT(8)  /* external port 1 */
+#define RTL8367_PORT_E0                        BIT(9)  /* external port 0 */
+
+#define RTL8367_PORTS_ALL                                      \
+       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
+        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
+        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1 |    \
+        RTL8367_PORT_E0)
+
+#define RTL8367_PORTS_ALL_BUT_CPU                              \
+       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
+        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
+        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1)
+
+struct rtl8367_initval {
+       u16 reg;
+       u16 val;
+};
+
+#define RTL8367_MIB_RXB_ID             0       /* IfInOctets */
+#define RTL8367_MIB_TXB_ID             20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8367_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 2, "Dot3StatsFCSErrors"                        },
+       { 0,  6, 2, "Dot3StatsSymbolErrors"                     },
+       { 0,  8, 2, "Dot3InPauseFrames"                         },
+       { 0, 10, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 12, 2, "EtherStatsFragments"                       },
+       { 0, 14, 2, "EtherStatsJabbers"                         },
+       { 0, 16, 2, "IfInUcastPkts"                             },
+       { 0, 18, 2, "EtherStatsDropEvents"                      },
+       { 0, 20, 4, "EtherStatsOctets"                          },
+
+       { 0, 24, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 26, 2, "EtherOversizeStats"                        },
+       { 0, 28, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 30, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 32, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 34, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 36, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 38, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 40, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 42, 2, "EtherStatsBroadcastPkts"                   },
+
+       { 0, 44, 4, "IfOutOctets"                               },
+
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
+       { 0, 66, 2, "IfOutUcastPkts"                            },
+       { 0, 68, 2, "IfOutMulticastPkts"                        },
+       { 0, 70, 2, "IfOutBroadcastPkts"                        },
+       { 0, 72, 2, "OutOampduPkts"                             },
+       { 0, 74, 2, "InOampduPkts"                              },
+       { 0, 76, 2, "PktgenPkts"                                },
+};
+
+#define REG_RD(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static const struct rtl8367_initval rtl8367_initvals_0_0[] = {
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
+       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
+       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
+       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
+       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
+       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
+       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
+       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
+       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
+       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
+       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
+       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
+       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
+       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
+       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
+       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
+       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
+       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
+       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
+       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
+       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
+       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1006}, {0x121e, 0x03e8},
+       {0x121f, 0x02b3}, {0x1220, 0x028f}, {0x1221, 0x029b}, {0x1222, 0x0277},
+       {0x1223, 0x02b3}, {0x1224, 0x028f}, {0x1225, 0x029b}, {0x1226, 0x0277},
+       {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0}, {0x1230, 0x00b4},
+       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
+       {0x0219, 0x0032}, {0x0200, 0x03e8}, {0x0201, 0x03e8}, {0x0202, 0x03e8},
+       {0x0203, 0x03e8}, {0x0204, 0x03e8}, {0x0205, 0x03e8}, {0x0206, 0x03e8},
+       {0x0207, 0x03e8}, {0x0218, 0x0032}, {0x0208, 0x029b}, {0x0209, 0x029b},
+       {0x020a, 0x029b}, {0x020b, 0x029b}, {0x020c, 0x029b}, {0x020d, 0x029b},
+       {0x020e, 0x029b}, {0x020f, 0x029b}, {0x0210, 0x029b}, {0x0211, 0x029b},
+       {0x0212, 0x029b}, {0x0213, 0x029b}, {0x0214, 0x029b}, {0x0215, 0x029b},
+       {0x0216, 0x029b}, {0x0217, 0x029b}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
+       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x1700, 0x014C}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
+       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
+       {0x133f, 0x0010}, {0x20A0, 0x1940}, {0x20C0, 0x1940}, {0x20E0, 0x1940},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_0_1[] = {
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
+       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
+       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
+       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
+       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
+       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
+       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
+       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
+       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
+       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
+       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
+       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
+       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
+       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
+       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
+       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
+       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
+       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
+       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
+       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
+       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
+       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1b06}, {0x121e, 0x07f0},
+       {0x121f, 0x0438}, {0x1220, 0x040f}, {0x1221, 0x040f}, {0x1222, 0x03eb},
+       {0x1223, 0x0438}, {0x1224, 0x040f}, {0x1225, 0x040f}, {0x1226, 0x03eb},
+       {0x1227, 0x0144}, {0x1228, 0x0138}, {0x122f, 0x0144}, {0x1230, 0x0138},
+       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
+       {0x0219, 0x0032}, {0x0200, 0x07d0}, {0x0201, 0x07d0}, {0x0202, 0x07d0},
+       {0x0203, 0x07d0}, {0x0204, 0x07d0}, {0x0205, 0x07d0}, {0x0206, 0x07d0},
+       {0x0207, 0x07d0}, {0x0218, 0x0032}, {0x0208, 0x0190}, {0x0209, 0x0190},
+       {0x020a, 0x0190}, {0x020b, 0x0190}, {0x020c, 0x0190}, {0x020d, 0x0190},
+       {0x020e, 0x0190}, {0x020f, 0x0190}, {0x0210, 0x0190}, {0x0211, 0x0190},
+       {0x0212, 0x0190}, {0x0213, 0x0190}, {0x0214, 0x0190}, {0x0215, 0x0190},
+       {0x0216, 0x0190}, {0x0217, 0x0190}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
+       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x1700, 0x0125}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
+       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
+       {0x133f, 0x0010},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_1_0[] = {
+       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
+       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
+       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
+       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
+       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
+       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
+       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
+       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
+       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
+       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
+       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
+       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
+       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
+       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
+       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
+       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
+       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
+       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
+       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
+       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
+       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
+       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
+       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
+       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
+       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
+       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
+       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
+       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
+       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
+       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
+       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
+       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
+       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
+       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
+       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
+       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
+       {0x121D, 0x7D16}, {0x121E, 0x03E8}, {0x121F, 0x024E}, {0x1220, 0x0230},
+       {0x1221, 0x0244}, {0x1222, 0x0226}, {0x1223, 0x024E}, {0x1224, 0x0230},
+       {0x1225, 0x0244}, {0x1226, 0x0226}, {0x1227, 0x00C0}, {0x1228, 0x00B4},
+       {0x122F, 0x00C0}, {0x1230, 0x00B4}, {0x0208, 0x03E8}, {0x0209, 0x03E8},
+       {0x020A, 0x03E8}, {0x020B, 0x03E8}, {0x020C, 0x03E8}, {0x020D, 0x03E8},
+       {0x020E, 0x03E8}, {0x020F, 0x03E8}, {0x0210, 0x03E8}, {0x0211, 0x03E8},
+       {0x0212, 0x03E8}, {0x0213, 0x03E8}, {0x0214, 0x03E8}, {0x0215, 0x03E8},
+       {0x0216, 0x03E8}, {0x0217, 0x03E8}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087B, 0x0000},
+       {0x087C, 0xFF00}, {0x087D, 0x0000}, {0x087E, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040},
+       {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
+       {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000}, {0x2200, 0x1340},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x20A0, 0x1940},
+       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_1_1[] = {
+       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
+       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
+       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
+       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
+       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
+       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
+       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
+       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
+       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
+       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
+       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
+       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
+       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
+       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
+       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
+       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
+       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
+       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
+       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
+       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
+       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
+       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
+       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
+       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
+       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
+       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
+       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
+       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
+       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
+       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
+       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
+       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
+       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
+       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
+       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
+       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
+       {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000},
+       {0x0865, 0x3210}, {0x087B, 0x0000}, {0x087C, 0xFF00}, {0x087D, 0x0000},
+       {0x087E, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040},
+       {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040},
+       {0x0A25, 0x2040}, {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040},
+       {0x0A29, 0x2040}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000},
+       {0x2200, 0x1340}, {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE},
+       {0x1B03, 0x0876},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_2_0[] = {
+       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
+       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
+       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
+       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
+       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
+       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
+       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
+       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
+       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
+       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
+       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
+       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
+       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
+       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
+       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
+       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
+       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
+       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
+       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
+       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
+       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
+       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
+       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
+       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
+       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
+       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
+       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
+       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
+       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
+       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
+       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
+       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
+       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
+       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
+       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
+       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x7D16},
+       {0x121e, 0x03e8}, {0x121f, 0x024e}, {0x1220, 0x0230}, {0x1221, 0x0244},
+       {0x1222, 0x0226}, {0x1223, 0x024e}, {0x1224, 0x0230}, {0x1225, 0x0244},
+       {0x1226, 0x0226}, {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0},
+       {0x1230, 0x00b4}, {0x0208, 0x03e8}, {0x0209, 0x03e8}, {0x020a, 0x03e8},
+       {0x020b, 0x03e8}, {0x020c, 0x03e8}, {0x020d, 0x03e8}, {0x020e, 0x03e8},
+       {0x020f, 0x03e8}, {0x0210, 0x03e8}, {0x0211, 0x03e8}, {0x0212, 0x03e8},
+       {0x0213, 0x03e8}, {0x0214, 0x03e8}, {0x0215, 0x03e8}, {0x0216, 0x03e8},
+       {0x0217, 0x03e8}, {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000},
+       {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000}, {0x087c, 0xff00},
+       {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100},
+       {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040},
+       {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040}, {0x20A0, 0x1940},
+       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_2_1[] = {
+       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
+       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
+       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
+       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
+       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
+       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
+       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
+       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
+       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
+       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
+       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
+       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
+       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
+       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
+       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
+       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
+       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
+       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
+       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
+       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
+       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
+       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
+       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
+       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
+       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
+       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
+       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
+       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
+       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
+       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
+       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
+       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
+       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
+       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
+       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
+       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x0900, 0x0000},
+       {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210},
+       {0x087b, 0x0000}, {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000},
+       {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040},
+       {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A25, 0x2040},
+       {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
+       {0x130c, 0x0050},
+};
+
+static int rtl8367_write_initvals(struct rtl8366_smi *smi,
+                                 const struct rtl8367_initval *initvals,
+                                 int count)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < count; i++)
+               REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+       return 0;
+}
+
+static int rtl8367_read_phy_reg(struct rtl8366_smi *smi,
+                               u32 phy_addr, u32 phy_reg, u32 *val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       if (phy_addr > RTL8367_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+       if (data & RTL8367_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* prepare address */
+       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
+              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send read command */
+       REG_WR(smi, RTL8367_IA_CTRL_REG,
+              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_READ);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy read timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       /* read data */
+       REG_RD(smi, RTL8367_IA_READ_DATA_REG, val);
+
+       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, *val);
+       return 0;
+}
+
+static int rtl8367_write_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_addr, u32 phy_reg, u32 val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, val);
+
+       if (phy_addr > RTL8367_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+       if (data & RTL8367_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* preapre data */
+       REG_WR(smi, RTL8367_IA_WRITE_DATA_REG, val);
+
+       /* prepare address */
+       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
+              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send write command */
+       REG_WR(smi, RTL8367_IA_CTRL_REG,
+              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_WRITE);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy write timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8367_init_regs0(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+       int err;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_0_0;
+               count = ARRAY_SIZE(rtl8367_initvals_0_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_0_1;
+               count = ARRAY_SIZE(rtl8367_initvals_0_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       err = rtl8367_write_initvals(smi, initvals, count);
+       if (err)
+               return err;
+
+       /* TODO: complete this */
+
+       return 0;
+}
+
+static int rtl8367_init_regs1(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_1_0;
+               count = ARRAY_SIZE(rtl8367_initvals_1_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_1_1;
+               count = ARRAY_SIZE(rtl8367_initvals_1_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       return rtl8367_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367_init_regs2(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_2_0;
+               count = ARRAY_SIZE(rtl8367_initvals_2_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_2_1;
+               count = ARRAY_SIZE(rtl8367_initvals_2_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       return rtl8367_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367_init_regs(struct rtl8366_smi *smi)
+{
+       u32 data;
+       u32 rlvid;
+       u32 mode;
+       int err;
+
+       REG_WR(smi, RTL8367_RTL_MAGIC_ID_REG, RTL8367_RTL_MAGIC_ID_VAL);
+
+       REG_RD(smi, RTL8367_CHIP_VER_REG, &data);
+       rlvid = (data >> RTL8367_CHIP_VER_RLVID_SHIFT) &
+               RTL8367_CHIP_VER_RLVID_MASK;
+
+       REG_RD(smi, RTL8367_CHIP_MODE_REG, &data);
+       mode = data & RTL8367_CHIP_MODE_MASK;
+
+       switch (rlvid) {
+       case 0:
+               err = rtl8367_init_regs0(smi, mode);
+               break;
+
+       case 1:
+               err = rtl8367_write_phy_reg(smi, 0, 31, 5);
+               if (err)
+                       break;
+
+               err = rtl8367_write_phy_reg(smi, 0, 5, 0x3ffe);
+               if (err)
+                       break;
+
+               err = rtl8367_read_phy_reg(smi, 0, 6, &data);
+               if (err)
+                       break;
+
+               if (data == 0x94eb) {
+                       err = rtl8367_init_regs1(smi, mode);
+               } else if (data == 0x2104) {
+                       err = rtl8367_init_regs2(smi, mode);
+               } else {
+                       dev_err(smi->parent, "unknow phy data %04x\n", data);
+                       return -ENODEV;
+               }
+
+               break;
+
+       default:
+               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+               err = -ENODEV;
+               break;
+       }
+
+       return err;
+}
+
+static int rtl8367_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       int err;
+       u32 data;
+
+       REG_WR(smi, RTL8367_CHIP_RESET_REG, RTL8367_CHIP_RESET_HW);
+       msleep(RTL8367_RESET_DELAY);
+
+       do {
+               REG_RD(smi, RTL8367_CHIP_RESET_REG, &data);
+               if (!(data & RTL8367_CHIP_RESET_HW))
+                       break;
+
+               msleep(1);
+       } while (--timeout);
+
+       if (!timeout) {
+               dev_err(smi->parent, "chip reset timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtl8367_extif_set_mode(struct rtl8366_smi *smi, int id,
+                                 enum rtl8367_extif_mode mode)
+{
+       int err;
+
+       /* set port mode */
+       switch (mode) {
+       case RTL8367_EXTIF_MODE_RGMII:
+       case RTL8367_EXTIF_MODE_RGMII_33V:
+               REG_WR(smi, RTL8367_CHIP_DEBUG0_REG, 0x0367);
+               REG_WR(smi, RTL8367_CHIP_DEBUG1_REG, 0x7777);
+               break;
+
+       case RTL8367_EXTIF_MODE_TMII_MAC:
+       case RTL8367_EXTIF_MODE_TMII_PHY:
+               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), BIT((id + 1) % 2));
+               break;
+
+       case RTL8367_EXTIF_MODE_GMII:
+               REG_RMW(smi, RTL8367_CHIP_DEBUG0_REG,
+                       RTL8367_CHIP_DEBUG0_DUMMY0(id),
+                       RTL8367_CHIP_DEBUG0_DUMMY0(id));
+               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+               break;
+
+       case RTL8367_EXTIF_MODE_MII_MAC:
+       case RTL8367_EXTIF_MODE_MII_PHY:
+       case RTL8367_EXTIF_MODE_DISABLED:
+               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), 0);
+               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), 0);
+               break;
+
+       default:
+               dev_err(smi->parent,
+                       "invalid mode for external interface %d\n", id);
+               return -EINVAL;
+       }
+
+       REG_RMW(smi, RTL8367_DIS_REG,
+               RTL8367_DIS_RGMII_MASK << RTL8367_DIS_RGMII_SHIFT(id),
+               mode << RTL8367_DIS_RGMII_SHIFT(id));
+
+       return 0;
+}
+
+static int rtl8367_extif_set_force(struct rtl8366_smi *smi, int id,
+                                  struct rtl8367_port_ability *pa)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367_DI_FORCE_MODE |
+               RTL8367_DI_FORCE_NWAY |
+               RTL8367_DI_FORCE_TXPAUSE |
+               RTL8367_DI_FORCE_RXPAUSE |
+               RTL8367_DI_FORCE_LINK |
+               RTL8367_DI_FORCE_DUPLEX |
+               RTL8367_DI_FORCE_SPEED_MASK);
+
+       val = pa->speed;
+       val |= pa->force_mode ? RTL8367_DI_FORCE_MODE : 0;
+       val |= pa->nway ? RTL8367_DI_FORCE_NWAY : 0;
+       val |= pa->txpause ? RTL8367_DI_FORCE_TXPAUSE : 0;
+       val |= pa->rxpause ? RTL8367_DI_FORCE_RXPAUSE : 0;
+       val |= pa->link ? RTL8367_DI_FORCE_LINK : 0;
+       val |= pa->duplex ? RTL8367_DI_FORCE_DUPLEX : 0;
+
+       REG_RMW(smi, RTL8367_DI_FORCE_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+                                        unsigned txdelay, unsigned rxdelay)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367_EXT_RGMXF_RXDELAY_MASK |
+               (RTL8367_EXT_RGMXF_TXDELAY_MASK <<
+                       RTL8367_EXT_RGMXF_TXDELAY_SHIFT));
+
+       val = rxdelay;
+       val |= txdelay << RTL8367_EXT_RGMXF_TXDELAY_SHIFT;
+
+       REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367_extif_init(struct rtl8366_smi *smi, int id,
+                             struct rtl8367_extif_config *cfg)
+{
+       enum rtl8367_extif_mode mode;
+       int err;
+
+       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+       err = rtl8367_extif_set_mode(smi, id, mode);
+       if (err)
+               return err;
+
+       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+               err = rtl8367_extif_set_force(smi, id, &cfg->ability);
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+                                                    cfg->rxdelay);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_ports(struct rtl8366_smi *smi,
+                                      unsigned int group, u16 port_mask)
+{
+       u32 reg;
+       u32 s;
+       int err;
+
+       port_mask &= RTL8367_PARA_LED_IO_EN_PMASK;
+       s = (group % 2) * 8;
+       reg = RTL8367_PARA_LED_IO_EN1_REG + (group / 2);
+
+       REG_RMW(smi, reg, (RTL8367_PARA_LED_IO_EN_PMASK << s), port_mask << s);
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_mode(struct rtl8366_smi *smi,
+                                     unsigned int mode)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mode &= RTL8367_LED_CONFIG_DATA_M;
+
+       mask = (RTL8367_LED_CONFIG_DATA_M << RTL8367_LED_CONFIG_DATA_S) |
+               RTL8367_LED_CONFIG_SEL;
+       set = (mode << RTL8367_LED_CONFIG_DATA_S) | RTL8367_LED_CONFIG_SEL;
+
+       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_config(struct rtl8366_smi *smi,
+                                       unsigned int led, unsigned int cfg)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mask = (RTL8367_LED_CONFIG_LED_CFG_M << (led * 4)) |
+               RTL8367_LED_CONFIG_SEL;
+       set = (cfg & RTL8367_LED_CONFIG_LED_CFG_M) << (led * 4);
+
+       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
+       return 0;
+}
+
+static int rtl8367_led_op_select_parallel(struct rtl8366_smi *smi)
+{
+       int err;
+
+       REG_WR(smi, RTL8367_LED_SYS_CONFIG_REG, 0x1472);
+       return 0;
+}
+
+static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mask = RTL8367_LED_MODE_RATE_M << RTL8367_LED_MODE_RATE_S;
+       set = (rate & RTL8367_LED_MODE_RATE_M) << RTL8367_LED_MODE_RATE_S;
+       REG_RMW(smi, RTL8367_LED_MODE_REG, mask, set);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+                                const char *name)
+{
+       struct rtl8367_extif_config *cfg;
+       const __be32 *prop;
+       int size;
+       int err;
+
+       prop = of_get_property(smi->parent->of_node, name, &size);
+       if (!prop)
+               return rtl8367_extif_init(smi, id, NULL);
+
+       if (size != (9 * sizeof(*prop))) {
+               dev_err(smi->parent, "%s property is invalid\n", name);
+               return -EINVAL;
+       }
+
+       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->txdelay = be32_to_cpup(prop++);
+       cfg->rxdelay = be32_to_cpup(prop++);
+       cfg->mode = be32_to_cpup(prop++);
+       cfg->ability.force_mode = be32_to_cpup(prop++);
+       cfg->ability.txpause = be32_to_cpup(prop++);
+       cfg->ability.rxpause = be32_to_cpup(prop++);
+       cfg->ability.link = be32_to_cpup(prop++);
+       cfg->ability.duplex = be32_to_cpup(prop++);
+       cfg->ability.speed = be32_to_cpup(prop++);
+
+       err = rtl8367_extif_init(smi, id, cfg);
+       kfree(cfg);
+
+       return err;
+}
+#else
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+                                const char *name)
+{
+       return -EINVAL;
+}
+#endif
+
+static int rtl8367_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8367_platform_data *pdata;
+       int err;
+       int i;
+
+       pdata = smi->parent->platform_data;
+
+       err = rtl8367_init_regs(smi);
+       if (err)
+               return err;
+
+       /* initialize external interfaces */
+       if (smi->parent->of_node) {
+               err = rtl8367_extif_init_of(smi, 0, "realtek,extif0");
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_init_of(smi, 1, "realtek,extif1");
+               if (err)
+                       return err;
+       } else {
+               err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg);
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg);
+               if (err)
+                       return err;
+       }
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8367_SWC0_REG, RTL8367_SWC0_MAX_LENGTH_MASK,
+               RTL8367_SWC0_MAX_LENGTH_1536);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8367_VLAN_INGRESS_REG, RTL8367_PORTS_ALL);
+
+       /*
+        * Setup egress tag mode for each port.
+        */
+       for (i = 0; i < RTL8367_NUM_PORTS; i++)
+               REG_RMW(smi,
+                       RTL8367_PORT_CFG_REG(i),
+                       RTL8367_PORT_CFG_EGRESS_MODE_MASK <<
+                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT,
+                       RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL <<
+                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT);
+
+       /* setup LEDs */
+       err = rtl8367_led_group_set_ports(smi, 0, RTL8367_PORTS_ALL);
+       if (err)
+               return err;
+
+       err = rtl8367_led_group_set_mode(smi, 0);
+       if (err)
+               return err;
+
+       err = rtl8367_led_op_select_parallel(smi);
+       if (err)
+               return err;
+
+       err = rtl8367_led_blinkrate_set(smi, 1);
+       if (err)
+               return err;
+
+       err = rtl8367_led_group_set_config(smi, 0, 2);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8367_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val)
+{
+       struct rtl8366_mib_counter *mib;
+       int offset;
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8367_NUM_PORTS || counter >= RTL8367_MIB_COUNT)
+               return -EINVAL;
+
+       mib = &rtl8367_mib_counters[counter];
+       addr = RTL8367_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       REG_WR(smi, RTL8367_MIB_ADDRESS_REG, addr >> 2);
+
+       /* read MIB control register */
+       REG_RD(smi, RTL8367_MIB_CTRL_REG(0), &data);
+
+       if (data & RTL8367_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8367_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       if (mib->length == 4)
+               offset = 3;
+       else
+               offset = (mib->offset + 1) % 4;
+
+       mibvalue = 0;
+       for (i = 0; i < mib->length; i++) {
+               REG_RD(smi, RTL8367_MIB_COUNTER_REG(offset - i), &data);
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8367_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8367_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       REG_WR(smi, RTL8367_TA_ADDR_REG, vid);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_READ);
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367_TA_DATA_REG(i), &data[i]);
+
+       vlan4k->vid = vid;
+       vlan4k->member = (data[0] >> RTL8367_TA_VLAN_MEMBER_SHIFT) &
+                        RTL8367_TA_VLAN_MEMBER_MASK;
+       vlan4k->fid = (data[1] >> RTL8367_TA_VLAN_FID_SHIFT) &
+                     RTL8367_TA_VLAN_FID_MASK;
+       vlan4k->untag = (data[2] >> RTL8367_TA_VLAN_UNTAG1_SHIFT) &
+                       RTL8367_TA_VLAN_UNTAG1_MASK;
+       vlan4k->untag |= ((data[3] >> RTL8367_TA_VLAN_UNTAG2_SHIFT) &
+                         RTL8367_TA_VLAN_UNTAG2_MASK) << 2;
+
+       return 0;
+}
+
+static int rtl8367_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8367_NUM_VIDS ||
+           vlan4k->member > RTL8367_TA_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8367_UNTAG_MASK ||
+           vlan4k->fid > RTL8367_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlan4k->member & RTL8367_TA_VLAN_MEMBER_MASK) <<
+                 RTL8367_TA_VLAN_MEMBER_SHIFT;
+       data[1] = (vlan4k->fid & RTL8367_TA_VLAN_FID_MASK) <<
+                 RTL8367_TA_VLAN_FID_SHIFT;
+       data[2] = (vlan4k->untag & RTL8367_TA_VLAN_UNTAG1_MASK) <<
+                 RTL8367_TA_VLAN_UNTAG1_SHIFT;
+       data[3] = ((vlan4k->untag >> 2) & RTL8367_TA_VLAN_UNTAG2_MASK) <<
+                 RTL8367_TA_VLAN_UNTAG2_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367_TA_DATA_REG(i), data[i]);
+
+       /* write VID */
+       REG_WR(smi, RTL8367_TA_ADDR_REG,
+              vlan4k->vid & RTL8367_TA_VLAN_VID_MASK);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_WRITE);
+
+       return 0;
+}
+
+static int rtl8367_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8367_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367_VLAN_MC_BASE(index) + i, &data[i]);
+
+       vlanmc->member = (data[0] >> RTL8367_VLAN_MC_MEMBER_SHIFT) &
+                        RTL8367_VLAN_MC_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8367_VLAN_MC_FID_SHIFT) &
+                     RTL8367_VLAN_MC_FID_MASK;
+       vlanmc->vid = (data[3] >> RTL8367_VLAN_MC_EVID_SHIFT) &
+                     RTL8367_VLAN_MC_EVID_MASK;
+
+       return 0;
+}
+
+static int rtl8367_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
+       int err;
+       int i;
+
+       if (index >= RTL8367_NUM_VLANS ||
+           vlanmc->vid >= RTL8367_NUM_VIDS ||
+           vlanmc->priority > RTL8367_PRIORITYMAX ||
+           vlanmc->member > RTL8367_VLAN_MC_MEMBER_MASK ||
+           vlanmc->untag > RTL8367_UNTAG_MASK ||
+           vlanmc->fid > RTL8367_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->member & RTL8367_VLAN_MC_MEMBER_MASK) <<
+                 RTL8367_VLAN_MC_MEMBER_SHIFT;
+       data[1] = (vlanmc->fid & RTL8367_VLAN_MC_FID_MASK) <<
+                 RTL8367_VLAN_MC_FID_SHIFT;
+       data[2] = 0;
+       data[3] = (vlanmc->vid & RTL8367_VLAN_MC_EVID_MASK) <<
+                  RTL8367_VLAN_MC_EVID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367_VLAN_MC_BASE(index) + i, data[i]);
+
+       return 0;
+}
+
+static int rtl8367_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_VLAN_PVID_CTRL_REG(port), &data);
+
+       *val = (data >> RTL8367_VLAN_PVID_CTRL_SHIFT(port)) &
+              RTL8367_VLAN_PVID_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8367_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8367_NUM_PORTS || index >= RTL8367_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_PVID_CTRL_REG(port),
+                               RTL8367_VLAN_PVID_CTRL_MASK <<
+                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port),
+                               (index & RTL8367_VLAN_PVID_CTRL_MASK) <<
+                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_CTRL_REG,
+                               RTL8367_VLAN_CTRL_ENABLE,
+                               (enable) ? RTL8367_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return 0;
+}
+
+static int rtl8367_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8367_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8367_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8367_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+
+       REG_WR(smi, RTL8367_PORT_ISOLATION_REG(port),
+              (enable) ? RTL8367_PORTS_ALL : 0);
+
+       return 0;
+}
+
+static int rtl8367_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(0), 0,
+                               RTL8367_MIB_CTRL_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367_sw_get_port_link(struct switch_dev *dev,
+                                   int port,
+                                   struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8367_PORT_STATUS_REG(port), &data);
+
+       link->link = !!(data & RTL8367_PORT_STATUS_LINK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8367_PORT_STATUS_DUPLEX);
+       link->rx_flow = !!(data & RTL8367_PORT_STATUS_RXPAUSE);
+       link->tx_flow = !!(data & RTL8367_PORT_STATUS_TXPAUSE);
+       link->aneg = !!(data & RTL8367_PORT_STATUS_NWAY);
+
+       speed = (data & RTL8367_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8367_sw_get_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8367_SWC0_REG, &data);
+       val->value.i = (data & RTL8367_SWC0_MAX_LENGTH_MASK) >>
+                       RTL8367_SWC0_MAX_LENGTH_SHIFT;
+
+       return 0;
+}
+
+static int rtl8367_sw_set_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 max_len;
+
+       switch (val->value.i) {
+       case 0:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1522;
+               break;
+       case 1:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1536;
+               break;
+       case 2:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1552;
+               break;
+       case 3:
+               max_len = RTL8367_SWC0_MAX_LENGTH_16000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8367_SWC0_REG,
+                               RTL8367_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int port;
+
+       port = val->port_vlan;
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(port / 8), 0,
+                               RTL8367_MIB_CTRL_PORT_RESET_MASK(port % 8));
+}
+
+static int rtl8367_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8367_MIB_TXB_ID, RTL8367_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8367_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8367_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+                              "(0:1522, 1:1536, 2:1552, 3:16000)",
+               .set = rtl8367_sw_set_max_length,
+               .get = rtl8367_sw_get_max_length,
+               .max = 3,
+       }
+};
+
+static struct switch_attr rtl8367_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8367_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr rtl8367_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8367_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8367_sw_ops = {
+       .attr_global = {
+               .attr = rtl8367_globals,
+               .n_attr = ARRAY_SIZE(rtl8367_globals),
+       },
+       .attr_port = {
+               .attr = rtl8367_port,
+               .n_attr = ARRAY_SIZE(rtl8367_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8367_vlan,
+               .n_attr = ARRAY_SIZE(rtl8367_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8367_sw_get_port_link,
+       .get_port_stats = rtl8367_sw_get_port_stats,
+};
+
+static int rtl8367_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8367";
+       dev->cpu_port = RTL8367_CPU_PORT_NUM;
+       dev->ports = RTL8367_NUM_PORTS;
+       dev->vlans = RTL8367_NUM_VIDS;
+       dev->ops = &rtl8367_sw_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8367_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8367_read_phy_reg(smi, addr, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8367_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8367_write_phy_reg(smi, addr, reg, val);
+       if (err)
+               return err;
+
+       /* flush write */
+       (void) rtl8367_read_phy_reg(smi, addr, reg, &t);
+
+       return err;
+}
+
+static int rtl8367_detect(struct rtl8366_smi *smi)
+{
+       u32 rtl_no = 0;
+       u32 rtl_ver = 0;
+       char *chip_name;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_NO_REG, &rtl_no);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip number\n");
+               return ret;
+       }
+
+       switch (rtl_no) {
+       case RTL8367_RTL_NO_8367R:
+               chip_name = "8367R";
+               break;
+       case RTL8367_RTL_NO_8367M:
+               chip_name = "8367M";
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip number (%04x)\n", rtl_no);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_VER_REG, &rtl_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%s ver. %u chip found\n",
+                chip_name, rtl_ver & RTL8367_RTL_VER_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367_smi_ops = {
+       .detect         = rtl8367_detect,
+       .reset_chip     = rtl8367_reset_chip,
+       .setup          = rtl8367_setup,
+
+       .mii_read       = rtl8367_mii_read,
+       .mii_write      = rtl8367_mii_write,
+
+       .get_vlan_mc    = rtl8367_get_vlan_mc,
+       .set_vlan_mc    = rtl8367_set_vlan_mc,
+       .get_vlan_4k    = rtl8367_get_vlan_4k,
+       .set_vlan_4k    = rtl8367_set_vlan_4k,
+       .get_mc_index   = rtl8367_get_mc_index,
+       .set_mc_index   = rtl8367_set_mc_index,
+       .get_mib_counter = rtl8367_get_mib_counter,
+       .is_vlan_valid  = rtl8367_is_vlan_valid,
+       .enable_vlan    = rtl8367_enable_vlan,
+       .enable_vlan4k  = rtl8367_enable_vlan4k,
+       .enable_port    = rtl8367_enable_port,
+};
+
+static int rtl8367_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 1500;
+       smi->cmd_read = 0xb9;
+       smi->cmd_write = 0xb8;
+       smi->ops = &rtl8367_smi_ops;
+       smi->cpu_port = RTL8367_CPU_PORT_NUM;
+       smi->num_ports = RTL8367_NUM_PORTS;
+       smi->num_vlan_mc = RTL8367_NUM_VLANS;
+       smi->mib_counters = rtl8367_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8367_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8367_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8367_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8367_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+static void rtl8367_shutdown(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi)
+               rtl8367_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367_match[] = {
+       { .compatible = "realtek,rtl8367" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367_match);
+#endif
+
+static struct platform_driver rtl8367_driver = {
+       .driver = {
+               .name           = RTL8367_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8367_match),
+#endif
+       },
+       .probe          = rtl8367_probe,
+       .remove         = rtl8367_remove,
+       .shutdown       = rtl8367_shutdown,
+};
+
+static int __init rtl8367_module_init(void)
+{
+       return platform_driver_register(&rtl8367_driver);
+}
+module_init(rtl8367_module_init);
+
+static void __exit rtl8367_module_exit(void)
+{
+       platform_driver_unregister(&rtl8367_driver);
+}
+module_exit(rtl8367_module_exit);
+
+MODULE_DESCRIPTION("Realtek RTL8367 ethernet switch driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367_DRIVER_NAME);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/rtl8367b.c b/target/linux/generic/files-3.18/drivers/net/phy/rtl8367b.c
new file mode 100644 (file)
index 0000000..e6ea650
--- /dev/null
@@ -0,0 +1,1612 @@
+/*
+ * Platform driver for the Realtek RTL8367R-VB ethernet switches
+ *
+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367B_RESET_DELAY   1000    /* msecs*/
+
+#define RTL8367B_PHY_ADDR_MAX  8
+#define RTL8367B_PHY_REG_MAX   31
+
+#define RTL8367B_VID_MASK      0x3fff
+#define RTL8367B_FID_MASK      0xf
+#define RTL8367B_UNTAG_MASK    0xff
+#define RTL8367B_MEMBER_MASK   0xff
+
+#define RTL8367B_PORT_MISC_CFG_REG(_p)         (0x000e + 0x20 * (_p))
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT     4
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK      0x3
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL  0
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_KEEP      1
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_PRI       2
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_REAL      3
+
+#define RTL8367B_BYPASS_LINE_RATE_REG          0x03f7
+
+#define RTL8367B_TA_CTRL_REG                   0x0500 /*GOOD*/
+#define   RTL8367B_TA_CTRL_SPA_SHIFT           8
+#define   RTL8367B_TA_CTRL_SPA_MASK            0x7
+#define   RTL8367B_TA_CTRL_METHOD              BIT(4)/*GOOD*/
+#define   RTL8367B_TA_CTRL_CMD_SHIFT           3
+#define   RTL8367B_TA_CTRL_CMD_READ            0
+#define   RTL8367B_TA_CTRL_CMD_WRITE           1
+#define   RTL8367B_TA_CTRL_TABLE_SHIFT         0 /*GOOD*/
+#define   RTL8367B_TA_CTRL_TABLE_ACLRULE       1
+#define   RTL8367B_TA_CTRL_TABLE_ACLACT                2
+#define   RTL8367B_TA_CTRL_TABLE_CVLAN         3
+#define   RTL8367B_TA_CTRL_TABLE_L2            4
+#define   RTL8367B_TA_CTRL_CVLAN_READ \
+               ((RTL8367B_TA_CTRL_CMD_READ << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+                RTL8367B_TA_CTRL_TABLE_CVLAN)
+#define   RTL8367B_TA_CTRL_CVLAN_WRITE \
+               ((RTL8367B_TA_CTRL_CMD_WRITE << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+                RTL8367B_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367B_TA_ADDR_REG                   0x0501/*GOOD*/
+#define   RTL8367B_TA_ADDR_MASK                        0x3fff/*GOOD*/
+
+#define RTL8367B_TA_LUT_REG                    0x0502/*GOOD*/
+
+#define RTL8367B_TA_WRDATA_REG(_x)             (0x0510 + (_x))/*GOOD*/
+#define   RTL8367B_TA_VLAN_NUM_WORDS           2
+#define   RTL8367B_TA_VLAN_VID_MASK            RTL8367B_VID_MASK
+#define   RTL8367B_TA_VLAN0_MEMBER_SHIFT       0
+#define   RTL8367B_TA_VLAN0_MEMBER_MASK                RTL8367B_MEMBER_MASK
+#define   RTL8367B_TA_VLAN0_UNTAG_SHIFT                8
+#define   RTL8367B_TA_VLAN0_UNTAG_MASK         RTL8367B_MEMBER_MASK
+#define   RTL8367B_TA_VLAN1_FID_SHIFT          0
+#define   RTL8367B_TA_VLAN1_FID_MASK           RTL8367B_FID_MASK
+
+#define RTL8367B_TA_RDDATA_REG(_x)             (0x0520 + (_x))/*GOOD*/
+
+#define RTL8367B_VLAN_PVID_CTRL_REG(_p)                (0x0700 + (_p) / 2) /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_MASK           0x1f /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_SHIFT(_p)      (8 * ((_p) % 2)) /*GOOD*/
+
+#define RTL8367B_VLAN_MC_BASE(_x)              (0x0728 + (_x) * 4) /*GOOD*/
+#define   RTL8367B_VLAN_MC_NUM_WORDS           4 /*GOOD*/
+#define   RTL8367B_VLAN_MC0_MEMBER_SHIFT       0/*GOOD*/
+#define   RTL8367B_VLAN_MC0_MEMBER_MASK                RTL8367B_MEMBER_MASK/*GOOD*/
+#define   RTL8367B_VLAN_MC1_FID_SHIFT          0/*GOOD*/
+#define   RTL8367B_VLAN_MC1_FID_MASK           RTL8367B_FID_MASK/*GOOD*/
+#define   RTL8367B_VLAN_MC3_EVID_SHIFT         0/*GOOD*/
+#define   RTL8367B_VLAN_MC3_EVID_MASK          RTL8367B_VID_MASK/*GOOD*/
+
+#define RTL8367B_VLAN_CTRL_REG                 0x07a8 /*GOOD*/
+#define   RTL8367B_VLAN_CTRL_ENABLE            BIT(0)
+
+#define RTL8367B_VLAN_INGRESS_REG              0x07a9 /*GOOD*/
+
+#define RTL8367B_PORT_ISOLATION_REG(_p)                (0x08a2 + (_p)) /*GOOD*/
+
+#define RTL8367B_MIB_COUNTER_REG(_x)           (0x1000 + (_x)) /*GOOD*/
+#define RTL8367B_MIB_COUNTER_PORT_OFFSET       0x007c /*GOOD*/
+
+#define RTL8367B_MIB_ADDRESS_REG               0x1004 /*GOOD*/
+
+#define RTL8367B_MIB_CTRL0_REG(_x)             (0x1005 + (_x)) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK BIT(11) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_QM_RESET_MASK     BIT(10) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_PORT_RESET_MASK(_p) BIT(2 + (_p)) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_RESET_MASK                BIT(1) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_BUSY_MASK         BIT(0) /*GOOD*/
+
+#define RTL8367B_SWC0_REG                      0x1200/*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH_SHIFT       13/*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH(_x)         ((_x) << 13) /*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH_MASK                RTL8367B_SWC0_MAX_LENGTH(0x3)
+#define   RTL8367B_SWC0_MAX_LENGTH_1522                RTL8367B_SWC0_MAX_LENGTH(0)
+#define   RTL8367B_SWC0_MAX_LENGTH_1536                RTL8367B_SWC0_MAX_LENGTH(1)
+#define   RTL8367B_SWC0_MAX_LENGTH_1552                RTL8367B_SWC0_MAX_LENGTH(2)
+#define   RTL8367B_SWC0_MAX_LENGTH_16000       RTL8367B_SWC0_MAX_LENGTH(3)
+
+#define RTL8367B_CHIP_NUMBER_REG               0x1300/*GOOD*/
+
+#define RTL8367B_CHIP_VER_REG                  0x1301/*GOOD*/
+#define   RTL8367B_CHIP_VER_RLVID_SHIFT                12/*GOOD*/
+#define   RTL8367B_CHIP_VER_RLVID_MASK         0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_MCID_SHIFT         8/*GOOD*/
+#define   RTL8367B_CHIP_VER_MCID_MASK          0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_BOID_SHIFT         4/*GOOD*/
+#define   RTL8367B_CHIP_VER_BOID_MASK          0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_AFE_SHIFT          0/*GOOD*/
+#define   RTL8367B_CHIP_VER_AFE_MASK           0x1/*GOOD*/
+
+#define RTL8367B_CHIP_MODE_REG                 0x1302
+#define   RTL8367B_CHIP_MODE_MASK              0x7
+
+#define RTL8367B_CHIP_DEBUG0_REG               0x1303
+#define   RTL8367B_CHIP_DEBUG0_DUMMY0(_x)      BIT(8 + (_x))
+
+#define RTL8367B_CHIP_DEBUG1_REG               0x1304
+
+#define RTL8367B_DIS_REG                       0x1305
+#define   RTL8367B_DIS_SKIP_MII_RXER(_x)       BIT(12 + (_x))
+#define   RTL8367B_DIS_RGMII_SHIFT(_x)         (4 * (_x))
+#define   RTL8367B_DIS_RGMII_MASK              0x7
+
+#define RTL8367B_EXT_RGMXF_REG(_x)             (0x1306 + (_x))
+#define   RTL8367B_EXT_RGMXF_DUMMY0_SHIFT      5
+#define   RTL8367B_EXT_RGMXF_DUMMY0_MASK       0x7ff
+#define   RTL8367B_EXT_RGMXF_TXDELAY_SHIFT     3
+#define   RTL8367B_EXT_RGMXF_TXDELAY_MASK      1
+#define   RTL8367B_EXT_RGMXF_RXDELAY_MASK      0x7
+
+#define RTL8367B_DI_FORCE_REG(_x)              (0x1310 + (_x))
+#define   RTL8367B_DI_FORCE_MODE               BIT(12)
+#define   RTL8367B_DI_FORCE_NWAY               BIT(7)
+#define   RTL8367B_DI_FORCE_TXPAUSE            BIT(6)
+#define   RTL8367B_DI_FORCE_RXPAUSE            BIT(5)
+#define   RTL8367B_DI_FORCE_LINK               BIT(4)
+#define   RTL8367B_DI_FORCE_DUPLEX             BIT(2)
+#define   RTL8367B_DI_FORCE_SPEED_MASK         3
+#define   RTL8367B_DI_FORCE_SPEED_10           0
+#define   RTL8367B_DI_FORCE_SPEED_100          1
+#define   RTL8367B_DI_FORCE_SPEED_1000         2
+
+#define RTL8367B_MAC_FORCE_REG(_x)             (0x1312 + (_x))
+
+#define RTL8367B_CHIP_RESET_REG                        0x1322 /*GOOD*/
+#define   RTL8367B_CHIP_RESET_SW               BIT(1) /*GOOD*/
+#define   RTL8367B_CHIP_RESET_HW               BIT(0) /*GOOD*/
+
+#define RTL8367B_PORT_STATUS_REG(_p)           (0x1352 + (_p)) /*GOOD*/
+#define   RTL8367B_PORT_STATUS_EN_1000_SPI     BIT(11) /*GOOD*/
+#define   RTL8367B_PORT_STATUS_EN_100_SPI      BIT(10)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_NWAY_FAULT      BIT(9)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_LINK_MASTER     BIT(8)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_NWAY            BIT(7)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_TXPAUSE         BIT(6)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_RXPAUSE         BIT(5)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_LINK            BIT(4)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_DUPLEX          BIT(2)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_MASK      0x0003/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_10                0/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_100       1/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_1000      2/*GOOD*/
+
+#define RTL8367B_RTL_MAGIC_ID_REG              0x13c2
+#define   RTL8367B_RTL_MAGIC_ID_VAL            0x0249
+
+#define RTL8367B_IA_CTRL_REG                   0x1f00
+#define   RTL8367B_IA_CTRL_RW(_x)              ((_x) << 1)
+#define   RTL8367B_IA_CTRL_RW_READ             RTL8367B_IA_CTRL_RW(0)
+#define   RTL8367B_IA_CTRL_RW_WRITE            RTL8367B_IA_CTRL_RW(1)
+#define   RTL8367B_IA_CTRL_CMD_MASK            BIT(0)
+
+#define RTL8367B_IA_STATUS_REG                 0x1f01
+#define   RTL8367B_IA_STATUS_PHY_BUSY          BIT(2)
+#define   RTL8367B_IA_STATUS_SDS_BUSY          BIT(1)
+#define   RTL8367B_IA_STATUS_MDX_BUSY          BIT(0)
+
+#define RTL8367B_IA_ADDRESS_REG                        0x1f02
+#define RTL8367B_IA_WRITE_DATA_REG             0x1f03
+#define RTL8367B_IA_READ_DATA_REG              0x1f04
+
+#define RTL8367B_INTERNAL_PHY_REG(_a, _r)      (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367B_NUM_MIB_COUNTERS      58
+
+#define RTL8367B_CPU_PORT_NUM          5
+#define RTL8367B_NUM_PORTS             8
+#define RTL8367B_NUM_VLANS             32
+#define RTL8367B_NUM_VIDS              4096
+#define RTL8367B_PRIORITYMAX           7
+#define RTL8367B_FIDMAX                        7
+
+#define RTL8367B_PORT_0                        BIT(0)
+#define RTL8367B_PORT_1                        BIT(1)
+#define RTL8367B_PORT_2                        BIT(2)
+#define RTL8367B_PORT_3                        BIT(3)
+#define RTL8367B_PORT_4                        BIT(4)
+#define RTL8367B_PORT_E0               BIT(5)  /* External port 0 */
+#define RTL8367B_PORT_E1               BIT(6)  /* External port 1 */
+#define RTL8367B_PORT_E2               BIT(7)  /* External port 2 */
+
+#define RTL8367B_PORTS_ALL                                     \
+       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
+        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E0 | \
+        RTL8367B_PORT_E1 | RTL8367B_PORT_E2)
+
+#define RTL8367B_PORTS_ALL_BUT_CPU                             \
+       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
+        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E1 | \
+        RTL8367B_PORT_E2)
+
+struct rtl8367b_initval {
+       u16 reg;
+       u16 val;
+};
+
+#define RTL8367B_MIB_RXB_ID            0       /* IfInOctets */
+#define RTL8367B_MIB_TXB_ID            28      /* IfOutOctets */
+
+static struct rtl8366_mib_counter
+rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
+       {0,   0, 4, "ifInOctets"                        },
+       {0,   4, 2, "dot3StatsFCSErrors"                },
+       {0,   6, 2, "dot3StatsSymbolErrors"             },
+       {0,   8, 2, "dot3InPauseFrames"                 },
+       {0,  10, 2, "dot3ControlInUnknownOpcodes"       },
+       {0,  12, 2, "etherStatsFragments"               },
+       {0,  14, 2, "etherStatsJabbers"                 },
+       {0,  16, 2, "ifInUcastPkts"                     },
+       {0,  18, 2, "etherStatsDropEvents"              },
+       {0,  20, 2, "ifInMulticastPkts"                 },
+       {0,  22, 2, "ifInBroadcastPkts"                 },
+       {0,  24, 2, "inMldChecksumError"                },
+       {0,  26, 2, "inIgmpChecksumError"               },
+       {0,  28, 2, "inMldSpecificQuery"                },
+       {0,  30, 2, "inMldGeneralQuery"                 },
+       {0,  32, 2, "inIgmpSpecificQuery"               },
+       {0,  34, 2, "inIgmpGeneralQuery"                },
+       {0,  36, 2, "inMldLeaves"                       },
+       {0,  38, 2, "inIgmpLeaves"                      },
+
+       {0,  40, 4, "etherStatsOctets"                  },
+       {0,  44, 2, "etherStatsUnderSizePkts"           },
+       {0,  46, 2, "etherOversizeStats"                },
+       {0,  48, 2, "etherStatsPkts64Octets"            },
+       {0,  50, 2, "etherStatsPkts65to127Octets"       },
+       {0,  52, 2, "etherStatsPkts128to255Octets"      },
+       {0,  54, 2, "etherStatsPkts256to511Octets"      },
+       {0,  56, 2, "etherStatsPkts512to1023Octets"     },
+       {0,  58, 2, "etherStatsPkts1024to1518Octets"    },
+
+       {0,  60, 4, "ifOutOctets"                       },
+       {0,  64, 2, "dot3StatsSingleCollisionFrames"    },
+       {0,  66, 2, "dot3StatMultipleCollisionFrames"   },
+       {0,  68, 2, "dot3sDeferredTransmissions"        },
+       {0,  70, 2, "dot3StatsLateCollisions"           },
+       {0,  72, 2, "etherStatsCollisions"              },
+       {0,  74, 2, "dot3StatsExcessiveCollisions"      },
+       {0,  76, 2, "dot3OutPauseFrames"                },
+       {0,  78, 2, "ifOutDiscards"                     },
+       {0,  80, 2, "dot1dTpPortInDiscards"             },
+       {0,  82, 2, "ifOutUcastPkts"                    },
+       {0,  84, 2, "ifOutMulticastPkts"                },
+       {0,  86, 2, "ifOutBroadcastPkts"                },
+       {0,  88, 2, "outOampduPkts"                     },
+       {0,  90, 2, "inOampduPkts"                      },
+       {0,  92, 2, "inIgmpJoinsSuccess"                },
+       {0,  94, 2, "inIgmpJoinsFail"                   },
+       {0,  96, 2, "inMldJoinsSuccess"                 },
+       {0,  98, 2, "inMldJoinsFail"                    },
+       {0, 100, 2, "inReportSuppressionDrop"           },
+       {0, 102, 2, "inLeaveSuppressionDrop"            },
+       {0, 104, 2, "outIgmpReports"                    },
+       {0, 106, 2, "outIgmpLeaves"                     },
+       {0, 108, 2, "outIgmpGeneralQuery"               },
+       {0, 110, 2, "outIgmpSpecificQuery"              },
+       {0, 112, 2, "outMldReports"                     },
+       {0, 114, 2, "outMldLeaves"                      },
+       {0, 116, 2, "outMldGeneralQuery"                },
+       {0, 118, 2, "outMldSpecificQuery"               },
+       {0, 120, 2, "inKnownMulticastPkts"              },
+};
+
+#define REG_RD(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_0[] = {
+       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x0301, 0x0026}, {0x1722, 0x0E14},
+       {0x205F, 0x0002}, {0x2059, 0x1A00}, {0x205F, 0x0000}, {0x207F, 0x0002},
+       {0x2077, 0x0000}, {0x2078, 0x0000}, {0x2079, 0x0000}, {0x207A, 0x0000},
+       {0x207B, 0x0000}, {0x207F, 0x0000}, {0x205F, 0x0002}, {0x2053, 0x0000},
+       {0x2054, 0x0000}, {0x2055, 0x0000}, {0x2056, 0x0000}, {0x2057, 0x0000},
+       {0x205F, 0x0000}, {0x12A4, 0x110A}, {0x12A6, 0x150A}, {0x13F1, 0x0013},
+       {0x13F4, 0x0010}, {0x13F5, 0x0000}, {0x0018, 0x0F00}, {0x0038, 0x0F00},
+       {0x0058, 0x0F00}, {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x12B6, 0x0C02},
+       {0x12B7, 0x030F}, {0x12B8, 0x11FF}, {0x12BC, 0x0004}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0007}, {0x221E, 0x002D}, {0x2218, 0xF030}, {0x221F, 0x0007},
+       {0x221E, 0x0023}, {0x2216, 0x0005}, {0x2215, 0x00B9}, {0x2219, 0x0044},
+       {0x2215, 0x00BA}, {0x2219, 0x0020}, {0x2215, 0x00BB}, {0x2219, 0x00C1},
+       {0x2215, 0x0148}, {0x2219, 0x0096}, {0x2215, 0x016E}, {0x2219, 0x0026},
+       {0x2216, 0x0000}, {0x2216, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221F, 0x0007}, {0x221E, 0x0020}, {0x2215, 0x0D00}, {0x221F, 0x0000},
+       {0x221F, 0x0000}, {0x2217, 0x2160}, {0x221F, 0x0001}, {0x2210, 0xF25E},
+       {0x221F, 0x0007}, {0x221E, 0x0042}, {0x2215, 0x0F00}, {0x2215, 0x0F00},
+       {0x2216, 0x7408}, {0x2215, 0x0E00}, {0x2215, 0x0F00}, {0x2215, 0x0F01},
+       {0x2216, 0x4000}, {0x2215, 0x0E01}, {0x2215, 0x0F01}, {0x2215, 0x0F02},
+       {0x2216, 0x9400}, {0x2215, 0x0E02}, {0x2215, 0x0F02}, {0x2215, 0x0F03},
+       {0x2216, 0x7408}, {0x2215, 0x0E03}, {0x2215, 0x0F03}, {0x2215, 0x0F04},
+       {0x2216, 0x4008}, {0x2215, 0x0E04}, {0x2215, 0x0F04}, {0x2215, 0x0F05},
+       {0x2216, 0x9400}, {0x2215, 0x0E05}, {0x2215, 0x0F05}, {0x2215, 0x0F06},
+       {0x2216, 0x0803}, {0x2215, 0x0E06}, {0x2215, 0x0F06}, {0x2215, 0x0D00},
+       {0x2215, 0x0100}, {0x221F, 0x0001}, {0x2210, 0xF05E}, {0x221F, 0x0000},
+       {0x2217, 0x2100}, {0x221F, 0x0000}, {0x220D, 0x0003}, {0x220E, 0x0015},
+       {0x220D, 0x4003}, {0x220E, 0x0006}, {0x221F, 0x0000}, {0x2200, 0x1340},
+       {0x133F, 0x0010}, {0x12A0, 0x0058}, {0x12A1, 0x0058}, {0x133E, 0x000E},
+       {0x133F, 0x0030}, {0x221F, 0x0000}, {0x2210, 0x0166}, {0x221F, 0x0000},
+       {0x133E, 0x000E}, {0x133F, 0x0010}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8B6E},
+       {0x2206, 0x0000}, {0x220F, 0x0100}, {0x2205, 0x8000}, {0x2206, 0x0280},
+       {0x2206, 0x28F7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+       {0x2206, 0x6602}, {0x2206, 0x80B9}, {0x2206, 0xE08B}, {0x2206, 0x8CE1},
+       {0x2206, 0x8B8D}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x8E1E},
+       {0x2206, 0x01A0}, {0x2206, 0x00E7}, {0x2206, 0xAEDB}, {0x2206, 0xEEE0},
+       {0x2206, 0x120E}, {0x2206, 0xEEE0}, {0x2206, 0x1300}, {0x2206, 0xEEE0},
+       {0x2206, 0x2001}, {0x2206, 0xEEE0}, {0x2206, 0x2166}, {0x2206, 0xEEE0},
+       {0x2206, 0xC463}, {0x2206, 0xEEE0}, {0x2206, 0xC5E8}, {0x2206, 0xEEE0},
+       {0x2206, 0xC699}, {0x2206, 0xEEE0}, {0x2206, 0xC7C2}, {0x2206, 0xEEE0},
+       {0x2206, 0xC801}, {0x2206, 0xEEE0}, {0x2206, 0xC913}, {0x2206, 0xEEE0},
+       {0x2206, 0xCA30}, {0x2206, 0xEEE0}, {0x2206, 0xCB3E}, {0x2206, 0xEEE0},
+       {0x2206, 0xDCE1}, {0x2206, 0xEEE0}, {0x2206, 0xDD00}, {0x2206, 0xEEE2},
+       {0x2206, 0x0001}, {0x2206, 0xEEE2}, {0x2206, 0x0100}, {0x2206, 0xEEE4},
+       {0x2206, 0x8860}, {0x2206, 0xEEE4}, {0x2206, 0x8902}, {0x2206, 0xEEE4},
+       {0x2206, 0x8C00}, {0x2206, 0xEEE4}, {0x2206, 0x8D30}, {0x2206, 0xEEEA},
+       {0x2206, 0x1480}, {0x2206, 0xEEEA}, {0x2206, 0x1503}, {0x2206, 0xEEEA},
+       {0x2206, 0xC600}, {0x2206, 0xEEEA}, {0x2206, 0xC706}, {0x2206, 0xEE85},
+       {0x2206, 0xEE00}, {0x2206, 0xEE85}, {0x2206, 0xEF00}, {0x2206, 0xEE8B},
+       {0x2206, 0x6750}, {0x2206, 0xEE8B}, {0x2206, 0x6632}, {0x2206, 0xEE8A},
+       {0x2206, 0xD448}, {0x2206, 0xEE8A}, {0x2206, 0xD548}, {0x2206, 0xEE8A},
+       {0x2206, 0xD649}, {0x2206, 0xEE8A}, {0x2206, 0xD7F8}, {0x2206, 0xEE8B},
+       {0x2206, 0x85E2}, {0x2206, 0xEE8B}, {0x2206, 0x8700}, {0x2206, 0xEEFF},
+       {0x2206, 0xF600}, {0x2206, 0xEEFF}, {0x2206, 0xF7FC}, {0x2206, 0x04F8},
+       {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2023}, {0x2206, 0xF620},
+       {0x2206, 0xE48B}, {0x2206, 0x8E02}, {0x2206, 0x2877}, {0x2206, 0x0225},
+       {0x2206, 0xC702}, {0x2206, 0x26A1}, {0x2206, 0x0281}, {0x2206, 0xB302},
+       {0x2206, 0x8496}, {0x2206, 0x0202}, {0x2206, 0xA102}, {0x2206, 0x27F1},
+       {0x2206, 0x0228}, {0x2206, 0xF902}, {0x2206, 0x2AA0}, {0x2206, 0x0282},
+       {0x2206, 0xB8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD21}, {0x2206, 0x08F6},
+       {0x2206, 0x21E4}, {0x2206, 0x8B8E}, {0x2206, 0x0202}, {0x2206, 0x80E0},
+       {0x2206, 0x8B8E}, {0x2206, 0xAD22}, {0x2206, 0x05F6}, {0x2206, 0x22E4},
+       {0x2206, 0x8B8E}, {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2305},
+       {0x2206, 0xF623}, {0x2206, 0xE48B}, {0x2206, 0x8EE0}, {0x2206, 0x8B8E},
+       {0x2206, 0xAD24}, {0x2206, 0x08F6}, {0x2206, 0x24E4}, {0x2206, 0x8B8E},
+       {0x2206, 0x0227}, {0x2206, 0x6AE0}, {0x2206, 0x8B8E}, {0x2206, 0xAD25},
+       {0x2206, 0x05F6}, {0x2206, 0x25E4}, {0x2206, 0x8B8E}, {0x2206, 0xE08B},
+       {0x2206, 0x8EAD}, {0x2206, 0x260B}, {0x2206, 0xF626}, {0x2206, 0xE48B},
+       {0x2206, 0x8E02}, {0x2206, 0x830D}, {0x2206, 0x021D}, {0x2206, 0x6BE0},
+       {0x2206, 0x8B8E}, {0x2206, 0xAD27}, {0x2206, 0x05F6}, {0x2206, 0x27E4},
+       {0x2206, 0x8B8E}, {0x2206, 0x0281}, {0x2206, 0x4402}, {0x2206, 0x045C},
+       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B83}, {0x2206, 0xAD23},
+       {0x2206, 0x30E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x2359},
+       {0x2206, 0x02E0}, {0x2206, 0x85EF}, {0x2206, 0xE585}, {0x2206, 0xEFAC},
+       {0x2206, 0x2907}, {0x2206, 0x1F01}, {0x2206, 0x9E51}, {0x2206, 0xAD29},
+       {0x2206, 0x20E0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x06E1},
+       {0x2206, 0x8B84}, {0x2206, 0xAD28}, {0x2206, 0x42E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD21}, {0x2206, 0x06E1}, {0x2206, 0x8B84}, {0x2206, 0xAD29},
+       {0x2206, 0x36BF}, {0x2206, 0x34BF}, {0x2206, 0x022C}, {0x2206, 0x31AE},
+       {0x2206, 0x2EE0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x10E0},
+       {0x2206, 0x8B84}, {0x2206, 0xF620}, {0x2206, 0xE48B}, {0x2206, 0x84EE},
+       {0x2206, 0x8ADA}, {0x2206, 0x00EE}, {0x2206, 0x8ADB}, {0x2206, 0x00E0},
+       {0x2206, 0x8B85}, {0x2206, 0xAD21}, {0x2206, 0x0CE0}, {0x2206, 0x8B84},
+       {0x2206, 0xF621}, {0x2206, 0xE48B}, {0x2206, 0x84EE}, {0x2206, 0x8B72},
+       {0x2206, 0xFFBF}, {0x2206, 0x34C2}, {0x2206, 0x022C}, {0x2206, 0x31FC},
+       {0x2206, 0x04F8}, {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD21}, {0x2206, 0x42E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0},
+       {0x2206, 0x2358}, {0x2206, 0xC059}, {0x2206, 0x021E}, {0x2206, 0x01E1},
+       {0x2206, 0x8B72}, {0x2206, 0x1F10}, {0x2206, 0x9E2F}, {0x2206, 0xE48B},
+       {0x2206, 0x72AD}, {0x2206, 0x2123}, {0x2206, 0xE18B}, {0x2206, 0x84F7},
+       {0x2206, 0x29E5}, {0x2206, 0x8B84}, {0x2206, 0xAC27}, {0x2206, 0x10AC},
+       {0x2206, 0x2605}, {0x2206, 0x0205}, {0x2206, 0x23AE}, {0x2206, 0x1602},
+       {0x2206, 0x0535}, {0x2206, 0x0282}, {0x2206, 0x30AE}, {0x2206, 0x0E02},
+       {0x2206, 0x056A}, {0x2206, 0x0282}, {0x2206, 0x75AE}, {0x2206, 0x0602},
+       {0x2206, 0x04DC}, {0x2206, 0x0282}, {0x2206, 0x04EF}, {0x2206, 0x96FE},
+       {0x2206, 0xFC04}, {0x2206, 0xF8F9}, {0x2206, 0xE08B}, {0x2206, 0x87AD},
+       {0x2206, 0x2321}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+       {0x2206, 0xAD26}, {0x2206, 0x18F6}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F6}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+       {0x2206, 0xE08B}, {0x2206, 0x87AD}, {0x2206, 0x233A}, {0x2206, 0xAD22},
+       {0x2206, 0x37E0}, {0x2206, 0xE020}, {0x2206, 0xE1E0}, {0x2206, 0x21AC},
+       {0x2206, 0x212E}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+       {0x2206, 0xF627}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xE2EA}, {0x2206, 0x12E3}, {0x2206, 0xEA13}, {0x2206, 0x5A8F},
+       {0x2206, 0x6A20}, {0x2206, 0xE6EA}, {0x2206, 0x12E7}, {0x2206, 0xEA13},
+       {0x2206, 0xF726}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xF727}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B87},
+       {0x2206, 0xAD23}, {0x2206, 0x38AD}, {0x2206, 0x2135}, {0x2206, 0xE0E0},
+       {0x2206, 0x20E1}, {0x2206, 0xE021}, {0x2206, 0xAC21}, {0x2206, 0x2CE0},
+       {0x2206, 0xEA14}, {0x2206, 0xE1EA}, {0x2206, 0x15F6}, {0x2206, 0x27E4},
+       {0x2206, 0xEA14}, {0x2206, 0xE5EA}, {0x2206, 0x15E2}, {0x2206, 0xEA12},
+       {0x2206, 0xE3EA}, {0x2206, 0x135A}, {0x2206, 0x8FE6}, {0x2206, 0xEA12},
+       {0x2206, 0xE7EA}, {0x2206, 0x13F7}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2146},
+       {0x2206, 0xE0E0}, {0x2206, 0x22E1}, {0x2206, 0xE023}, {0x2206, 0x58C0},
+       {0x2206, 0x5902}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x651F},
+       {0x2206, 0x109E}, {0x2206, 0x33E4}, {0x2206, 0x8B65}, {0x2206, 0xAD21},
+       {0x2206, 0x22AD}, {0x2206, 0x272A}, {0x2206, 0xD400}, {0x2206, 0x01BF},
+       {0x2206, 0x34F2}, {0x2206, 0x022C}, {0x2206, 0xA2BF}, {0x2206, 0x34F5},
+       {0x2206, 0x022C}, {0x2206, 0xE0E0}, {0x2206, 0x8B67}, {0x2206, 0x1B10},
+       {0x2206, 0xAA14}, {0x2206, 0xE18B}, {0x2206, 0x660D}, {0x2206, 0x1459},
+       {0x2206, 0x0FAE}, {0x2206, 0x05E1}, {0x2206, 0x8B66}, {0x2206, 0x590F},
+       {0x2206, 0xBF85}, {0x2206, 0x6102}, {0x2206, 0x2CA2}, {0x2206, 0xEF96},
+       {0x2206, 0xFEFC}, {0x2206, 0x04F8}, {0x2206, 0xF9FA}, {0x2206, 0xFBEF},
+       {0x2206, 0x79E2}, {0x2206, 0x8AD2}, {0x2206, 0xAC19}, {0x2206, 0x2DE0},
+       {0x2206, 0xE036}, {0x2206, 0xE1E0}, {0x2206, 0x37EF}, {0x2206, 0x311F},
+       {0x2206, 0x325B}, {0x2206, 0x019E}, {0x2206, 0x1F7A}, {0x2206, 0x0159},
+       {0x2206, 0x019F}, {0x2206, 0x0ABF}, {0x2206, 0x348E}, {0x2206, 0x022C},
+       {0x2206, 0x31F6}, {0x2206, 0x06AE}, {0x2206, 0x0FF6}, {0x2206, 0x0302},
+       {0x2206, 0x0470}, {0x2206, 0xF703}, {0x2206, 0xF706}, {0x2206, 0xBF34},
+       {0x2206, 0x9302}, {0x2206, 0x2C31}, {0x2206, 0xAC1A}, {0x2206, 0x25E0},
+       {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x23EF}, {0x2206, 0x300D},
+       {0x2206, 0x311F}, {0x2206, 0x325B}, {0x2206, 0x029E}, {0x2206, 0x157A},
+       {0x2206, 0x0258}, {0x2206, 0xC4A0}, {0x2206, 0x0408}, {0x2206, 0xBF34},
+       {0x2206, 0x9E02}, {0x2206, 0x2C31}, {0x2206, 0xAE06}, {0x2206, 0xBF34},
+       {0x2206, 0x9C02}, {0x2206, 0x2C31}, {0x2206, 0xAC1B}, {0x2206, 0x4AE0},
+       {0x2206, 0xE012}, {0x2206, 0xE1E0}, {0x2206, 0x13EF}, {0x2206, 0x300D},
+       {0x2206, 0x331F}, {0x2206, 0x325B}, {0x2206, 0x1C9E}, {0x2206, 0x3AEF},
+       {0x2206, 0x325B}, {0x2206, 0x1C9F}, {0x2206, 0x09BF}, {0x2206, 0x3498},
+       {0x2206, 0x022C}, {0x2206, 0x3102}, {0x2206, 0x83C5}, {0x2206, 0x5A03},
+       {0x2206, 0x0D03}, {0x2206, 0x581C}, {0x2206, 0x1E20}, {0x2206, 0x0207},
+       {0x2206, 0xA0A0}, {0x2206, 0x000E}, {0x2206, 0x0284}, {0x2206, 0x17AD},
+       {0x2206, 0x1817}, {0x2206, 0xBF34}, {0x2206, 0x9A02}, {0x2206, 0x2C31},
+       {0x2206, 0xAE0F}, {0x2206, 0xBF34}, {0x2206, 0xC802}, {0x2206, 0x2C31},
+       {0x2206, 0xBF34}, {0x2206, 0xC502}, {0x2206, 0x2C31}, {0x2206, 0x0284},
+       {0x2206, 0x52E6}, {0x2206, 0x8AD2}, {0x2206, 0xEF97}, {0x2206, 0xFFFE},
+       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xBF34}, {0x2206, 0xDA02},
+       {0x2206, 0x2CE0}, {0x2206, 0xE58A}, {0x2206, 0xD3BF}, {0x2206, 0x34D4},
+       {0x2206, 0x022C}, {0x2206, 0xE00C}, {0x2206, 0x1159}, {0x2206, 0x02E0},
+       {0x2206, 0x8AD3}, {0x2206, 0x1E01}, {0x2206, 0xE48A}, {0x2206, 0xD3D1},
+       {0x2206, 0x00BF}, {0x2206, 0x34DA}, {0x2206, 0x022C}, {0x2206, 0xA2D1},
+       {0x2206, 0x01BF}, {0x2206, 0x34D4}, {0x2206, 0x022C}, {0x2206, 0xA2BF},
+       {0x2206, 0x34CB}, {0x2206, 0x022C}, {0x2206, 0xE0E5}, {0x2206, 0x8ACE},
+       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CE0}, {0x2206, 0xE58A},
+       {0x2206, 0xCFBF}, {0x2206, 0x8564}, {0x2206, 0x022C}, {0x2206, 0xE0E5},
+       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6A02}, {0x2206, 0x2CE0},
+       {0x2206, 0xE58A}, {0x2206, 0xD1FC}, {0x2206, 0x04F8}, {0x2206, 0xE18A},
+       {0x2206, 0xD1BF}, {0x2206, 0x856A}, {0x2206, 0x022C}, {0x2206, 0xA2E1},
+       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+       {0x2206, 0xE18A}, {0x2206, 0xCFBF}, {0x2206, 0x8567}, {0x2206, 0x022C},
+       {0x2206, 0xA2E1}, {0x2206, 0x8ACE}, {0x2206, 0xBF34}, {0x2206, 0xCB02},
+       {0x2206, 0x2CA2}, {0x2206, 0xE18A}, {0x2206, 0xD3BF}, {0x2206, 0x34DA},
+       {0x2206, 0x022C}, {0x2206, 0xA2E1}, {0x2206, 0x8AD3}, {0x2206, 0x0D11},
+       {0x2206, 0xBF34}, {0x2206, 0xD402}, {0x2206, 0x2CA2}, {0x2206, 0xFC04},
+       {0x2206, 0xF9A0}, {0x2206, 0x0405}, {0x2206, 0xE38A}, {0x2206, 0xD4AE},
+       {0x2206, 0x13A0}, {0x2206, 0x0805}, {0x2206, 0xE38A}, {0x2206, 0xD5AE},
+       {0x2206, 0x0BA0}, {0x2206, 0x0C05}, {0x2206, 0xE38A}, {0x2206, 0xD6AE},
+       {0x2206, 0x03E3}, {0x2206, 0x8AD7}, {0x2206, 0xEF13}, {0x2206, 0xBF34},
+       {0x2206, 0xCB02}, {0x2206, 0x2CA2}, {0x2206, 0xEF13}, {0x2206, 0x0D11},
+       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CA2}, {0x2206, 0xEF13},
+       {0x2206, 0x0D14}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+       {0x2206, 0xEF13}, {0x2206, 0x0D17}, {0x2206, 0xBF85}, {0x2206, 0x6A02},
+       {0x2206, 0x2CA2}, {0x2206, 0xFD04}, {0x2206, 0xF8E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD27}, {0x2206, 0x2DE0}, {0x2206, 0xE036}, {0x2206, 0xE1E0},
+       {0x2206, 0x37E1}, {0x2206, 0x8B73}, {0x2206, 0x1F10}, {0x2206, 0x9E20},
+       {0x2206, 0xE48B}, {0x2206, 0x73AC}, {0x2206, 0x200B}, {0x2206, 0xAC21},
+       {0x2206, 0x0DAC}, {0x2206, 0x250F}, {0x2206, 0xAC27}, {0x2206, 0x0EAE},
+       {0x2206, 0x0F02}, {0x2206, 0x84CC}, {0x2206, 0xAE0A}, {0x2206, 0x0284},
+       {0x2206, 0xD1AE}, {0x2206, 0x05AE}, {0x2206, 0x0302}, {0x2206, 0x84D8},
+       {0x2206, 0xFC04}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0x0402},
+       {0x2206, 0x84E5}, {0x2206, 0x0285}, {0x2206, 0x2804}, {0x2206, 0x0285},
+       {0x2206, 0x4904}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0xEE8B},
+       {0x2206, 0x6902}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD26}, {0x2206, 0x38D0}, {0x2206, 0x0B02}, {0x2206, 0x2B4D},
+       {0x2206, 0x5882}, {0x2206, 0x7882}, {0x2206, 0x9F2D}, {0x2206, 0xE08B},
+       {0x2206, 0x68E1}, {0x2206, 0x8B69}, {0x2206, 0x1F10}, {0x2206, 0x9EC8},
+       {0x2206, 0x10E4}, {0x2206, 0x8B68}, {0x2206, 0xE0E0}, {0x2206, 0x00E1},
+       {0x2206, 0xE001}, {0x2206, 0xF727}, {0x2206, 0xE4E0}, {0x2206, 0x00E5},
+       {0x2206, 0xE001}, {0x2206, 0xE2E0}, {0x2206, 0x20E3}, {0x2206, 0xE021},
+       {0x2206, 0xAD30}, {0x2206, 0xF7F6}, {0x2206, 0x27E4}, {0x2206, 0xE000},
+       {0x2206, 0xE5E0}, {0x2206, 0x01FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2212},
+       {0x2206, 0xE0E0}, {0x2206, 0x14E1}, {0x2206, 0xE015}, {0x2206, 0xAD26},
+       {0x2206, 0x9CE1}, {0x2206, 0x85E0}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x04F8},
+       {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B86}, {0x2206, 0xAD22},
+       {0x2206, 0x09E1}, {0x2206, 0x85E1}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x0464},
+       {0x2206, 0xE48C}, {0x2206, 0xFDE4}, {0x2206, 0x80CA}, {0x2206, 0xE480},
+       {0x2206, 0x66E0}, {0x2206, 0x8E70}, {0x2206, 0xE076}, {0x2205, 0xE142},
+       {0x2206, 0x0701}, {0x2205, 0xE140}, {0x2206, 0x0405}, {0x220F, 0x0000},
+       {0x221F, 0x0000}, {0x2200, 0x1340}, {0x133E, 0x000E}, {0x133F, 0x0010},
+       {0x13EB, 0x11BB}
+};
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_1[] = {
+       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x1305, 0xC000}, {0x121E, 0x03CA},
+       {0x1233, 0x0352}, {0x1234, 0x0064}, {0x1237, 0x0096}, {0x1238, 0x0078},
+       {0x1239, 0x0084}, {0x123A, 0x0030}, {0x205F, 0x0002}, {0x2059, 0x1A00},
+       {0x205F, 0x0000}, {0x207F, 0x0002}, {0x2077, 0x0000}, {0x2078, 0x0000},
+       {0x2079, 0x0000}, {0x207A, 0x0000}, {0x207B, 0x0000}, {0x207F, 0x0000},
+       {0x205F, 0x0002}, {0x2053, 0x0000}, {0x2054, 0x0000}, {0x2055, 0x0000},
+       {0x2056, 0x0000}, {0x2057, 0x0000}, {0x205F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2205, 0x8B86}, {0x2206, 0x800E},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x12A3, 0x2200}, {0x6107, 0xE58B},
+       {0x6103, 0xA970}, {0x0018, 0x0F00}, {0x0038, 0x0F00}, {0x0058, 0x0F00},
+       {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0005}, {0x2205, 0x8B6E}, {0x2206, 0x0000}, {0x220F, 0x0100},
+       {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8000}, {0x2206, 0x0280},
+       {0x2206, 0x2BF7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+       {0x2206, 0x6602}, {0x2206, 0x8044}, {0x2206, 0x0201}, {0x2206, 0x7CE0},
+       {0x2206, 0x8B8C}, {0x2206, 0xE18B}, {0x2206, 0x8D1E}, {0x2206, 0x01E1},
+       {0x2206, 0x8B8E}, {0x2206, 0x1E01}, {0x2206, 0xA000}, {0x2206, 0xE4AE},
+       {0x2206, 0xD8EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE}, {0x2206, 0x85C1},
+       {0x2206, 0x00EE}, {0x2206, 0x8AFC}, {0x2206, 0x07EE}, {0x2206, 0x8AFD},
+       {0x2206, 0x73EE}, {0x2206, 0xFFF6}, {0x2206, 0x00EE}, {0x2206, 0xFFF7},
+       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD20},
+       {0x2206, 0x0302}, {0x2206, 0x8050}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+       {0x2206, 0xE08B}, {0x2206, 0x85AD}, {0x2206, 0x2548}, {0x2206, 0xE08A},
+       {0x2206, 0xE4E1}, {0x2206, 0x8AE5}, {0x2206, 0x7C00}, {0x2206, 0x009E},
+       {0x2206, 0x35EE}, {0x2206, 0x8AE4}, {0x2206, 0x00EE}, {0x2206, 0x8AE5},
+       {0x2206, 0x00E0}, {0x2206, 0x8AFC}, {0x2206, 0xE18A}, {0x2206, 0xFDE2},
+       {0x2206, 0x85C0}, {0x2206, 0xE385}, {0x2206, 0xC102}, {0x2206, 0x2DAC},
+       {0x2206, 0xAD20}, {0x2206, 0x12EE}, {0x2206, 0x8AE4}, {0x2206, 0x03EE},
+       {0x2206, 0x8AE5}, {0x2206, 0xB7EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE},
+       {0x2206, 0x85C1}, {0x2206, 0x00AE}, {0x2206, 0x1115}, {0x2206, 0xE685},
+       {0x2206, 0xC0E7}, {0x2206, 0x85C1}, {0x2206, 0xAE08}, {0x2206, 0xEE85},
+       {0x2206, 0xC000}, {0x2206, 0xEE85}, {0x2206, 0xC100}, {0x2206, 0xFDFC},
+       {0x2206, 0x0400}, {0x2205, 0xE142}, {0x2206, 0x0701}, {0x2205, 0xE140},
+       {0x2206, 0x0405}, {0x220F, 0x0000}, {0x221F, 0x0000}, {0x133E, 0x000E},
+       {0x133F, 0x0010}, {0x13EB, 0x11BB}, {0x207F, 0x0002}, {0x2073, 0x1D22},
+       {0x207F, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x2200, 0x1340},
+       {0x133E, 0x000E}, {0x133F, 0x0010},
+};
+
+static int rtl8367b_write_initvals(struct rtl8366_smi *smi,
+                                 const struct rtl8367b_initval *initvals,
+                                 int count)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < count; i++)
+               REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+       return 0;
+}
+
+static int rtl8367b_read_phy_reg(struct rtl8366_smi *smi,
+                               u32 phy_addr, u32 phy_reg, u32 *val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367B_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* prepare address */
+       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send read command */
+       REG_WR(smi, RTL8367B_IA_CTRL_REG,
+              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_READ);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy read timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       /* read data */
+       REG_RD(smi, RTL8367B_IA_READ_DATA_REG, val);
+
+       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, *val);
+       return 0;
+}
+
+static int rtl8367b_write_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_addr, u32 phy_reg, u32 val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, val);
+
+       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367B_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* preapre data */
+       REG_WR(smi, RTL8367B_IA_WRITE_DATA_REG, val);
+
+       /* prepare address */
+       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send write command */
+       REG_WR(smi, RTL8367B_IA_CTRL_REG,
+              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_WRITE);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy write timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8367b_init_regs(struct rtl8366_smi *smi)
+{
+       const struct rtl8367b_initval *initvals;
+       u32 chip_ver;
+       u32 rlvid;
+       int count;
+       int err;
+
+       REG_WR(smi, RTL8367B_RTL_MAGIC_ID_REG, RTL8367B_RTL_MAGIC_ID_VAL);
+       REG_RD(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+
+       rlvid = (chip_ver >> RTL8367B_CHIP_VER_RLVID_SHIFT) &
+               RTL8367B_CHIP_VER_RLVID_MASK;
+
+       switch (rlvid) {
+       case 0:
+               initvals = rtl8367r_vb_initvals_0;
+               count = ARRAY_SIZE(rtl8367r_vb_initvals_0);
+               break;
+
+       case 1:
+               initvals = rtl8367r_vb_initvals_1;
+               count = ARRAY_SIZE(rtl8367r_vb_initvals_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+               return -ENODEV;
+       }
+
+       /* TODO: disable RLTP */
+
+       return rtl8367b_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367b_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       int err;
+       u32 data;
+
+       REG_WR(smi, RTL8367B_CHIP_RESET_REG, RTL8367B_CHIP_RESET_HW);
+       msleep(RTL8367B_RESET_DELAY);
+
+       do {
+               REG_RD(smi, RTL8367B_CHIP_RESET_REG, &data);
+               if (!(data & RTL8367B_CHIP_RESET_HW))
+                       break;
+
+               msleep(1);
+       } while (--timeout);
+
+       if (!timeout) {
+               dev_err(smi->parent, "chip reset timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
+                                  enum rtl8367_extif_mode mode)
+{
+       int err;
+
+       /* set port mode */
+       switch (mode) {
+       case RTL8367_EXTIF_MODE_RGMII:
+       case RTL8367_EXTIF_MODE_RGMII_33V:
+               REG_WR(smi, RTL8367B_CHIP_DEBUG0_REG, 0x0367);
+               REG_WR(smi, RTL8367B_CHIP_DEBUG1_REG, 0x7777);
+               break;
+
+       case RTL8367_EXTIF_MODE_TMII_MAC:
+       case RTL8367_EXTIF_MODE_TMII_PHY:
+               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), BIT((id + 1) % 2));
+               break;
+
+       case RTL8367_EXTIF_MODE_GMII:
+               REG_RMW(smi, RTL8367B_CHIP_DEBUG0_REG,
+                       RTL8367B_CHIP_DEBUG0_DUMMY0(id),
+                       RTL8367B_CHIP_DEBUG0_DUMMY0(id));
+               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+               break;
+
+       case RTL8367_EXTIF_MODE_MII_MAC:
+       case RTL8367_EXTIF_MODE_MII_PHY:
+       case RTL8367_EXTIF_MODE_DISABLED:
+               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), 0);
+               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), 0);
+               break;
+
+       default:
+               dev_err(smi->parent,
+                       "invalid mode for external interface %d\n", id);
+               return -EINVAL;
+       }
+
+       REG_RMW(smi, RTL8367B_DIS_REG,
+               RTL8367B_DIS_RGMII_MASK << RTL8367B_DIS_RGMII_SHIFT(id),
+               mode << RTL8367B_DIS_RGMII_SHIFT(id));
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_force(struct rtl8366_smi *smi, int id,
+                                   struct rtl8367_port_ability *pa)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367B_DI_FORCE_MODE |
+               RTL8367B_DI_FORCE_NWAY |
+               RTL8367B_DI_FORCE_TXPAUSE |
+               RTL8367B_DI_FORCE_RXPAUSE |
+               RTL8367B_DI_FORCE_LINK |
+               RTL8367B_DI_FORCE_DUPLEX |
+               RTL8367B_DI_FORCE_SPEED_MASK);
+
+       val = pa->speed;
+       val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
+       val |= pa->nway ? RTL8367B_DI_FORCE_NWAY : 0;
+       val |= pa->txpause ? RTL8367B_DI_FORCE_TXPAUSE : 0;
+       val |= pa->rxpause ? RTL8367B_DI_FORCE_RXPAUSE : 0;
+       val |= pa->link ? RTL8367B_DI_FORCE_LINK : 0;
+       val |= pa->duplex ? RTL8367B_DI_FORCE_DUPLEX : 0;
+
+       REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+                                        unsigned txdelay, unsigned rxdelay)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367B_EXT_RGMXF_RXDELAY_MASK |
+               (RTL8367B_EXT_RGMXF_TXDELAY_MASK <<
+                       RTL8367B_EXT_RGMXF_TXDELAY_SHIFT));
+
+       val = rxdelay;
+       val |= txdelay << RTL8367B_EXT_RGMXF_TXDELAY_SHIFT;
+
+       REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id,
+                              struct rtl8367_extif_config *cfg)
+{
+       enum rtl8367_extif_mode mode;
+       int err;
+
+       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+       err = rtl8367b_extif_set_mode(smi, id, mode);
+       if (err)
+               return err;
+
+       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+               err = rtl8367b_extif_set_force(smi, id, &cfg->ability);
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+                                                    cfg->rxdelay);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+                                 const char *name)
+{
+       struct rtl8367_extif_config *cfg;
+       const __be32 *prop;
+       int size;
+       int err;
+
+       prop = of_get_property(smi->parent->of_node, name, &size);
+       if (!prop)
+               return rtl8367b_extif_init(smi, id, NULL);
+
+       if (size != (9 * sizeof(*prop))) {
+               dev_err(smi->parent, "%s property is invalid\n", name);
+               return -EINVAL;
+       }
+
+       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->txdelay = be32_to_cpup(prop++);
+       cfg->rxdelay = be32_to_cpup(prop++);
+       cfg->mode = be32_to_cpup(prop++);
+       cfg->ability.force_mode = be32_to_cpup(prop++);
+       cfg->ability.txpause = be32_to_cpup(prop++);
+       cfg->ability.rxpause = be32_to_cpup(prop++);
+       cfg->ability.link = be32_to_cpup(prop++);
+       cfg->ability.duplex = be32_to_cpup(prop++);
+       cfg->ability.speed = be32_to_cpup(prop++);
+
+       err = rtl8367b_extif_init(smi, id, cfg);
+       kfree(cfg);
+
+       return err;
+}
+#else
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+                                 const char *name)
+{
+       return -EINVAL;
+}
+#endif
+
+static int rtl8367b_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8367_platform_data *pdata;
+       int err;
+       int i;
+
+       pdata = smi->parent->platform_data;
+
+       err = rtl8367b_init_regs(smi);
+       if (err)
+               return err;
+
+       /* initialize external interfaces */
+       if (smi->parent->of_node) {
+               err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0");
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1");
+               if (err)
+                       return err;
+       } else {
+               err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg);
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg);
+               if (err)
+                       return err;
+       }
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8367B_SWC0_REG, RTL8367B_SWC0_MAX_LENGTH_MASK,
+               RTL8367B_SWC0_MAX_LENGTH_1536);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8367B_VLAN_INGRESS_REG, RTL8367B_PORTS_ALL);
+
+       /*
+        * Setup egress tag mode for each port.
+        */
+       for (i = 0; i < RTL8367B_NUM_PORTS; i++)
+               REG_RMW(smi,
+                       RTL8367B_PORT_MISC_CFG_REG(i),
+                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK <<
+                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT,
+                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL <<
+                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT);
+
+       return 0;
+}
+
+static int rtl8367b_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                   int port, unsigned long long *val)
+{
+       struct rtl8366_mib_counter *mib;
+       int offset;
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8367B_NUM_PORTS ||
+           counter >= RTL8367B_NUM_MIB_COUNTERS)
+               return -EINVAL;
+
+       mib = &rtl8367b_mib_counters[counter];
+       addr = RTL8367B_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       REG_WR(smi, RTL8367B_MIB_ADDRESS_REG, addr >> 2);
+
+       /* read MIB control register */
+       REG_RD(smi, RTL8367B_MIB_CTRL0_REG(0), &data);
+
+       if (data & RTL8367B_MIB_CTRL0_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8367B_MIB_CTRL0_RESET_MASK)
+               return -EIO;
+
+       if (mib->length == 4)
+               offset = 3;
+       else
+               offset = (mib->offset + 1) % 4;
+
+       mibvalue = 0;
+       for (i = 0; i < mib->length; i++) {
+               REG_RD(smi, RTL8367B_MIB_COUNTER_REG(offset - i), &data);
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8367b_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8367B_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       REG_WR(smi, RTL8367B_TA_ADDR_REG, vid);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_READ);
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367B_TA_RDDATA_REG(i), &data[i]);
+
+       vlan4k->vid = vid;
+       vlan4k->member = (data[0] >> RTL8367B_TA_VLAN0_MEMBER_SHIFT) &
+                        RTL8367B_TA_VLAN0_MEMBER_MASK;
+       vlan4k->untag = (data[0] >> RTL8367B_TA_VLAN0_UNTAG_SHIFT) &
+                       RTL8367B_TA_VLAN0_UNTAG_MASK;
+       vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
+                     RTL8367B_TA_VLAN1_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8367B_NUM_VIDS ||
+           vlan4k->member > RTL8367B_TA_VLAN0_MEMBER_MASK ||
+           vlan4k->untag > RTL8367B_UNTAG_MASK ||
+           vlan4k->fid > RTL8367B_FIDMAX)
+               return -EINVAL;
+
+       memset(data, 0, sizeof(data));
+
+       data[0] = (vlan4k->member & RTL8367B_TA_VLAN0_MEMBER_MASK) <<
+                 RTL8367B_TA_VLAN0_MEMBER_SHIFT;
+       data[0] |= (vlan4k->untag & RTL8367B_TA_VLAN0_UNTAG_MASK) <<
+                  RTL8367B_TA_VLAN0_UNTAG_SHIFT;
+       data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
+                 RTL8367B_TA_VLAN1_FID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367B_TA_WRDATA_REG(i), data[i]);
+
+       /* write VID */
+       REG_WR(smi, RTL8367B_TA_ADDR_REG,
+              vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_WRITE);
+
+       return 0;
+}
+
+static int rtl8367b_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8367B_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367B_VLAN_MC_BASE(index) + i, &data[i]);
+
+       vlanmc->member = (data[0] >> RTL8367B_VLAN_MC0_MEMBER_SHIFT) &
+                        RTL8367B_VLAN_MC0_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8367B_VLAN_MC1_FID_SHIFT) &
+                     RTL8367B_VLAN_MC1_FID_MASK;
+       vlanmc->vid = (data[3] >> RTL8367B_VLAN_MC3_EVID_SHIFT) &
+                     RTL8367B_VLAN_MC3_EVID_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+       int err;
+       int i;
+
+       if (index >= RTL8367B_NUM_VLANS ||
+           vlanmc->vid >= RTL8367B_NUM_VIDS ||
+           vlanmc->priority > RTL8367B_PRIORITYMAX ||
+           vlanmc->member > RTL8367B_VLAN_MC0_MEMBER_MASK ||
+           vlanmc->untag > RTL8367B_UNTAG_MASK ||
+           vlanmc->fid > RTL8367B_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->member & RTL8367B_VLAN_MC0_MEMBER_MASK) <<
+                 RTL8367B_VLAN_MC0_MEMBER_SHIFT;
+       data[1] = (vlanmc->fid & RTL8367B_VLAN_MC1_FID_MASK) <<
+                 RTL8367B_VLAN_MC1_FID_SHIFT;
+       data[2] = 0;
+       data[3] = (vlanmc->vid & RTL8367B_VLAN_MC3_EVID_MASK) <<
+                  RTL8367B_VLAN_MC3_EVID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367B_VLAN_MC_BASE(index) + i, data[i]);
+
+       return 0;
+}
+
+static int rtl8367b_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
+
+       *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
+              RTL8367B_VLAN_PVID_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8367B_NUM_PORTS || index >= RTL8367B_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
+                               RTL8367B_VLAN_PVID_CTRL_MASK <<
+                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port),
+                               (index & RTL8367B_VLAN_PVID_CTRL_MASK) <<
+                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367b_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_CTRL_REG,
+                               RTL8367B_VLAN_CTRL_ENABLE,
+                               (enable) ? RTL8367B_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367b_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return 0;
+}
+
+static int rtl8367b_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8367B_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8367B_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8367b_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+
+       REG_WR(smi, RTL8367B_PORT_ISOLATION_REG(port),
+              (enable) ? RTL8367B_PORTS_ALL : 0);
+
+       return 0;
+}
+
+static int rtl8367b_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(0), 0,
+                               RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
+                                   int port,
+                                   struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
+
+       link->link = !!(data & RTL8367B_PORT_STATUS_LINK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8367B_PORT_STATUS_DUPLEX);
+       link->rx_flow = !!(data & RTL8367B_PORT_STATUS_RXPAUSE);
+       link->tx_flow = !!(data & RTL8367B_PORT_STATUS_TXPAUSE);
+       link->aneg = !!(data & RTL8367B_PORT_STATUS_NWAY);
+
+       speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8367b_sw_get_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8367B_SWC0_REG, &data);
+       val->value.i = (data & RTL8367B_SWC0_MAX_LENGTH_MASK) >>
+                       RTL8367B_SWC0_MAX_LENGTH_SHIFT;
+
+       return 0;
+}
+
+static int rtl8367b_sw_set_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 max_len;
+
+       switch (val->value.i) {
+       case 0:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1522;
+               break;
+       case 1:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1536;
+               break;
+       case 2:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1552;
+               break;
+       case 3:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_16000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_SWC0_REG,
+                               RTL8367B_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367b_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int port;
+
+       port = val->port_vlan;
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(port / 8), 0,
+                               RTL8367B_MIB_CTRL0_PORT_RESET_MASK(port % 8));
+}
+
+static int rtl8367b_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8367B_MIB_TXB_ID, RTL8367B_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8367b_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8367b_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+                              "(0:1522, 1:1536, 2:1552, 3:16000)",
+               .set = rtl8367b_sw_set_max_length,
+               .get = rtl8367b_sw_get_max_length,
+               .max = 3,
+       }
+};
+
+static struct switch_attr rtl8367b_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8367b_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr rtl8367b_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       },
+};
+
+static const struct switch_dev_ops rtl8367b_sw_ops = {
+       .attr_global = {
+               .attr = rtl8367b_globals,
+               .n_attr = ARRAY_SIZE(rtl8367b_globals),
+       },
+       .attr_port = {
+               .attr = rtl8367b_port,
+               .n_attr = ARRAY_SIZE(rtl8367b_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8367b_vlan,
+               .n_attr = ARRAY_SIZE(rtl8367b_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8367b_sw_get_port_link,
+       .get_port_stats = rtl8367b_sw_get_port_stats,
+};
+
+static int rtl8367b_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8367B";
+       dev->cpu_port = RTL8367B_CPU_PORT_NUM;
+       dev->ports = RTL8367B_NUM_PORTS;
+       dev->vlans = RTL8367B_NUM_VIDS;
+       dev->ops = &rtl8367b_sw_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8367b_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367b_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8367b_read_phy_reg(smi, addr, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8367b_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8367b_write_phy_reg(smi, addr, reg, val);
+       if (err)
+               return err;
+
+       /* flush write */
+       (void) rtl8367b_read_phy_reg(smi, addr, reg, &t);
+
+       return err;
+}
+
+static int rtl8367b_detect(struct rtl8366_smi *smi)
+{
+       const char *chip_name;
+       u32 chip_num;
+       u32 chip_ver;
+       u32 chip_mode;
+       int ret;
+
+       /* TODO: improve chip detection */
+       rtl8366_smi_write_reg(smi, RTL8367B_RTL_MAGIC_ID_REG,
+                             RTL8367B_RTL_MAGIC_ID_VAL);
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_NUMBER_REG, &chip_num);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip number");
+               return ret;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip version");
+               return ret;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_MODE_REG, &chip_mode);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip mode");
+               return ret;
+       }
+
+       switch (chip_ver) {
+       case 0x1000:
+               chip_name = "8367RB";
+               break;
+       case 0x1010:
+               chip_name = "8367R-VB";
+               break;
+       default:
+               dev_err(smi->parent,
+                       "unknown chip num:%04x ver:%04x, mode:%04x\n",
+                       chip_num, chip_ver, chip_mode);
+               return -ENODEV;
+       }
+
+       dev_info(smi->parent, "RTL%s chip found\n", chip_name);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367b_smi_ops = {
+       .detect         = rtl8367b_detect,
+       .reset_chip     = rtl8367b_reset_chip,
+       .setup          = rtl8367b_setup,
+
+       .mii_read       = rtl8367b_mii_read,
+       .mii_write      = rtl8367b_mii_write,
+
+       .get_vlan_mc    = rtl8367b_get_vlan_mc,
+       .set_vlan_mc    = rtl8367b_set_vlan_mc,
+       .get_vlan_4k    = rtl8367b_get_vlan_4k,
+       .set_vlan_4k    = rtl8367b_set_vlan_4k,
+       .get_mc_index   = rtl8367b_get_mc_index,
+       .set_mc_index   = rtl8367b_set_mc_index,
+       .get_mib_counter = rtl8367b_get_mib_counter,
+       .is_vlan_valid  = rtl8367b_is_vlan_valid,
+       .enable_vlan    = rtl8367b_enable_vlan,
+       .enable_vlan4k  = rtl8367b_enable_vlan4k,
+       .enable_port    = rtl8367b_enable_port,
+};
+
+static int  rtl8367b_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 1500;
+       smi->cmd_read = 0xb9;
+       smi->cmd_write = 0xb8;
+       smi->ops = &rtl8367b_smi_ops;
+       smi->cpu_port = RTL8367B_CPU_PORT_NUM;
+       smi->num_ports = RTL8367B_NUM_PORTS;
+       smi->num_vlan_mc = RTL8367B_NUM_VLANS;
+       smi->mib_counters = rtl8367b_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8367b_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8367b_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8367b_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+static void rtl8367b_shutdown(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi)
+               rtl8367b_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367b_match[] = {
+       { .compatible = "realtek,rtl8367b" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367b_match);
+#endif
+
+static struct platform_driver rtl8367b_driver = {
+       .driver = {
+               .name           = RTL8367B_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8367b_match),
+#endif
+       },
+       .probe          = rtl8367b_probe,
+       .remove         = rtl8367b_remove,
+       .shutdown       = rtl8367b_shutdown,
+};
+
+module_platform_driver(rtl8367b_driver);
+
+MODULE_DESCRIPTION("Realtek RTL8367B ethernet switch driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367B_DRIVER_NAME);
+
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/swconfig.c b/target/linux/generic/files-3.18/drivers/net/phy/swconfig.c
new file mode 100644 (file)
index 0000000..e8a6847
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+ * swconfig.c: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/capability.h>
+#include <linux/skbuff.h>
+#include <linux/switch.h>
+#include <linux/of.h>
+#include <linux/version.h>
+#include <uapi/linux/mii.h>
+
+#define SWCONFIG_DEVNAME       "switch%d"
+
+#include "swconfig_leds.c"
+
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_LICENSE("GPL");
+
+static int swdev_id;
+static struct list_head swdevs;
+static DEFINE_MUTEX(swdevs_lock);
+struct swconfig_callback;
+
+struct swconfig_callback {
+       struct sk_buff *msg;
+       struct genlmsghdr *hdr;
+       struct genl_info *info;
+       int cmd;
+
+       /* callback for filling in the message data */
+       int (*fill)(struct swconfig_callback *cb, void *arg);
+
+       /* callback for closing the message before sending it */
+       int (*close)(struct swconfig_callback *cb, void *arg);
+
+       struct nlattr *nest[4];
+       int args[4];
+};
+
+/* defaults */
+
+static int
+swconfig_get_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       int ret;
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       if (!dev->ops->get_vlan_ports)
+               return -EOPNOTSUPP;
+
+       ret = dev->ops->get_vlan_ports(dev, val);
+       return ret;
+}
+
+static int
+swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct switch_port *ports = val->value.ports;
+       const struct switch_dev_ops *ops = dev->ops;
+       int i;
+
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       /* validate ports */
+       if (val->len > dev->ports)
+               return -EINVAL;
+
+       if (!ops->set_vlan_ports)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < val->len; i++) {
+               if (ports[i].id >= dev->ports)
+                       return -EINVAL;
+
+               if (ops->set_port_pvid &&
+                   !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+                       ops->set_port_pvid(dev, ports[i].id, val->port_vlan);
+       }
+
+       return ops->set_vlan_ports(dev, val);
+}
+
+static int
+swconfig_set_pvid(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->set_port_pvid)
+               return -EOPNOTSUPP;
+
+       return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i);
+}
+
+static int
+swconfig_get_pvid(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->get_port_pvid)
+               return -EOPNOTSUPP;
+
+       return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i);
+}
+
+static int
+swconfig_set_link(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (!dev->ops->set_port_link)
+               return -EOPNOTSUPP;
+
+       return dev->ops->set_port_link(dev, val->port_vlan, val->value.link);
+}
+
+static int
+swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct switch_port_link *link = val->value.link;
+
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->get_port_link)
+               return -EOPNOTSUPP;
+
+       memset(link, 0, sizeof(*link));
+       return dev->ops->get_port_link(dev, val->port_vlan, link);
+}
+
+static int
+swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->apply_config)
+               return 0;
+
+       return dev->ops->apply_config(dev);
+}
+
+static int
+swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->reset_switch)
+               return 0;
+
+       return dev->ops->reset_switch(dev);
+}
+
+enum global_defaults {
+       GLOBAL_APPLY,
+       GLOBAL_RESET,
+};
+
+enum vlan_defaults {
+       VLAN_PORTS,
+};
+
+enum port_defaults {
+       PORT_PVID,
+       PORT_LINK,
+};
+
+static struct switch_attr default_global[] = {
+       [GLOBAL_APPLY] = {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "apply",
+               .description = "Activate changes in the hardware",
+               .set = swconfig_apply_config,
+       },
+       [GLOBAL_RESET] = {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset",
+               .description = "Reset the switch",
+               .set = swconfig_reset_switch,
+       }
+};
+
+static struct switch_attr default_port[] = {
+       [PORT_PVID] = {
+               .type = SWITCH_TYPE_INT,
+               .name = "pvid",
+               .description = "Primary VLAN ID",
+               .set = swconfig_set_pvid,
+               .get = swconfig_get_pvid,
+       },
+       [PORT_LINK] = {
+               .type = SWITCH_TYPE_LINK,
+               .name = "link",
+               .description = "Get port link information",
+               .set = swconfig_set_link,
+               .get = swconfig_get_link,
+       }
+};
+
+static struct switch_attr default_vlan[] = {
+       [VLAN_PORTS] = {
+               .type = SWITCH_TYPE_PORTS,
+               .name = "ports",
+               .description = "VLAN port mapping",
+               .set = swconfig_set_vlan_ports,
+               .get = swconfig_get_vlan_ports,
+       },
+};
+
+static const struct switch_attr *
+swconfig_find_attr_by_name(const struct switch_attrlist *alist,
+                               const char *name)
+{
+       int i;
+
+       for (i = 0; i < alist->n_attr; i++)
+               if (strcmp(name, alist->attr[i].name) == 0)
+                       return &alist->attr[i];
+
+       return NULL;
+}
+
+static void swconfig_defaults_init(struct switch_dev *dev)
+{
+       const struct switch_dev_ops *ops = dev->ops;
+
+       dev->def_global = 0;
+       dev->def_vlan = 0;
+       dev->def_port = 0;
+
+       if (ops->get_vlan_ports || ops->set_vlan_ports)
+               set_bit(VLAN_PORTS, &dev->def_vlan);
+
+       if (ops->get_port_pvid || ops->set_port_pvid)
+               set_bit(PORT_PVID, &dev->def_port);
+
+       if (ops->get_port_link &&
+           !swconfig_find_attr_by_name(&ops->attr_port, "link"))
+               set_bit(PORT_LINK, &dev->def_port);
+
+       /* always present, can be no-op */
+       set_bit(GLOBAL_APPLY, &dev->def_global);
+       set_bit(GLOBAL_RESET, &dev->def_global);
+}
+
+
+static struct genl_family switch_fam;
+
+static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = {
+       [SWITCH_ATTR_ID] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING },
+       [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED },
+       [SWITCH_ATTR_TYPE] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = {
+       [SWITCH_PORT_ID] = { .type = NLA_U32 },
+       [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
+};
+
+static struct nla_policy link_policy[SWITCH_LINK_ATTR_MAX] = {
+       [SWITCH_LINK_FLAG_DUPLEX] = { .type = NLA_FLAG },
+       [SWITCH_LINK_FLAG_ANEG] = { .type = NLA_FLAG },
+       [SWITCH_LINK_SPEED] = { .type = NLA_U32 },
+};
+
+static inline void
+swconfig_lock(void)
+{
+       mutex_lock(&swdevs_lock);
+}
+
+static inline void
+swconfig_unlock(void)
+{
+       mutex_unlock(&swdevs_lock);
+}
+
+static struct switch_dev *
+swconfig_get_dev(struct genl_info *info)
+{
+       struct switch_dev *dev = NULL;
+       struct switch_dev *p;
+       int id;
+
+       if (!info->attrs[SWITCH_ATTR_ID])
+               goto done;
+
+       id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]);
+       swconfig_lock();
+       list_for_each_entry(p, &swdevs, dev_list) {
+               if (id != p->id)
+                       continue;
+
+               dev = p;
+               break;
+       }
+       if (dev)
+               mutex_lock(&dev->sw_mutex);
+       else
+               pr_debug("device %d not found\n", id);
+       swconfig_unlock();
+done:
+       return dev;
+}
+
+static inline void
+swconfig_put_dev(struct switch_dev *dev)
+{
+       mutex_unlock(&dev->sw_mutex);
+}
+
+static int
+swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
+{
+       struct switch_attr *op = arg;
+       struct genl_info *info = cb->info;
+       struct sk_buff *msg = cb->msg;
+       int id = cb->args[0];
+       void *hdr;
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
+                       NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
+       if (IS_ERR(hdr))
+               return -1;
+
+       if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name))
+               goto nla_put_failure;
+       if (op->description)
+               if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION,
+                       op->description))
+                       goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return msg->len;
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+/* spread multipart messages across multiple message buffers */
+static int
+swconfig_send_multipart(struct swconfig_callback *cb, void *arg)
+{
+       struct genl_info *info = cb->info;
+       int restart = 0;
+       int err;
+
+       do {
+               if (!cb->msg) {
+                       cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+                       if (cb->msg == NULL)
+                               goto error;
+               }
+
+               if (!(cb->fill(cb, arg) < 0))
+                       break;
+
+               /* fill failed, check if this was already the second attempt */
+               if (restart)
+                       goto error;
+
+               /* try again in a new message, send the current one */
+               restart = 1;
+               if (cb->close) {
+                       if (cb->close(cb, arg) < 0)
+                               goto error;
+               }
+               err = genlmsg_reply(cb->msg, info);
+               cb->msg = NULL;
+               if (err < 0)
+                       goto error;
+
+       } while (restart);
+
+       return 0;
+
+error:
+       if (cb->msg)
+               nlmsg_free(cb->msg);
+       return -1;
+}
+
+static int
+swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attrlist *alist;
+       struct switch_dev *dev;
+       struct swconfig_callback cb;
+       int err = -EINVAL;
+       int i;
+
+       /* defaults */
+       struct switch_attr *def_list;
+       unsigned long *def_active;
+       int n_def;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       switch (hdr->cmd) {
+       case SWITCH_CMD_LIST_GLOBAL:
+               alist = &dev->ops->attr_global;
+               def_list = default_global;
+               def_active = &dev->def_global;
+               n_def = ARRAY_SIZE(default_global);
+               break;
+       case SWITCH_CMD_LIST_VLAN:
+               alist = &dev->ops->attr_vlan;
+               def_list = default_vlan;
+               def_active = &dev->def_vlan;
+               n_def = ARRAY_SIZE(default_vlan);
+               break;
+       case SWITCH_CMD_LIST_PORT:
+               alist = &dev->ops->attr_port;
+               def_list = default_port;
+               def_active = &dev->def_port;
+               n_def = ARRAY_SIZE(default_port);
+               break;
+       default:
+               WARN_ON(1);
+               goto out;
+       }
+
+       memset(&cb, 0, sizeof(cb));
+       cb.info = info;
+       cb.fill = swconfig_dump_attr;
+       for (i = 0; i < alist->n_attr; i++) {
+               if (alist->attr[i].disabled)
+                       continue;
+               cb.args[0] = i;
+               err = swconfig_send_multipart(&cb, (void *) &alist->attr[i]);
+               if (err < 0)
+                       goto error;
+       }
+
+       /* defaults */
+       for (i = 0; i < n_def; i++) {
+               if (!test_bit(i, def_active))
+                       continue;
+               cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i;
+               err = swconfig_send_multipart(&cb, (void *) &def_list[i]);
+               if (err < 0)
+                       goto error;
+       }
+       swconfig_put_dev(dev);
+
+       if (!cb.msg)
+               return 0;
+
+       return genlmsg_reply(cb.msg, info);
+
+error:
+       if (cb.msg)
+               nlmsg_free(cb.msg);
+out:
+       swconfig_put_dev(dev);
+       return err;
+}
+
+static const struct switch_attr *
+swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info,
+               struct switch_val *val)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attrlist *alist;
+       const struct switch_attr *attr = NULL;
+       unsigned int attr_id;
+
+       /* defaults */
+       struct switch_attr *def_list;
+       unsigned long *def_active;
+       int n_def;
+
+       if (!info->attrs[SWITCH_ATTR_OP_ID])
+               goto done;
+
+       switch (hdr->cmd) {
+       case SWITCH_CMD_SET_GLOBAL:
+       case SWITCH_CMD_GET_GLOBAL:
+               alist = &dev->ops->attr_global;
+               def_list = default_global;
+               def_active = &dev->def_global;
+               n_def = ARRAY_SIZE(default_global);
+               break;
+       case SWITCH_CMD_SET_VLAN:
+       case SWITCH_CMD_GET_VLAN:
+               alist = &dev->ops->attr_vlan;
+               def_list = default_vlan;
+               def_active = &dev->def_vlan;
+               n_def = ARRAY_SIZE(default_vlan);
+               if (!info->attrs[SWITCH_ATTR_OP_VLAN])
+                       goto done;
+               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]);
+               if (val->port_vlan >= dev->vlans)
+                       goto done;
+               break;
+       case SWITCH_CMD_SET_PORT:
+       case SWITCH_CMD_GET_PORT:
+               alist = &dev->ops->attr_port;
+               def_list = default_port;
+               def_active = &dev->def_port;
+               n_def = ARRAY_SIZE(default_port);
+               if (!info->attrs[SWITCH_ATTR_OP_PORT])
+                       goto done;
+               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]);
+               if (val->port_vlan >= dev->ports)
+                       goto done;
+               break;
+       default:
+               WARN_ON(1);
+               goto done;
+       }
+
+       if (!alist)
+               goto done;
+
+       attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
+       if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) {
+               attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET;
+               if (attr_id >= n_def)
+                       goto done;
+               if (!test_bit(attr_id, def_active))
+                       goto done;
+               attr = &def_list[attr_id];
+       } else {
+               if (attr_id >= alist->n_attr)
+                       goto done;
+               attr = &alist->attr[attr_id];
+       }
+
+       if (attr->disabled)
+               attr = NULL;
+
+done:
+       if (!attr)
+               pr_debug("attribute lookup failed\n");
+       val->attr = attr;
+       return attr;
+}
+
+static int
+swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head,
+               struct switch_val *val, int max)
+{
+       struct nlattr *nla;
+       int rem;
+
+       val->len = 0;
+       nla_for_each_nested(nla, head, rem) {
+               struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
+               struct switch_port *port;
+
+               if (val->len >= max)
+                       return -EINVAL;
+
+               port = &val->value.ports[val->len];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
+               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
+                               port_policy))
+#else
+               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
+                               port_policy, NULL))
+#endif
+                       return -EINVAL;
+
+               if (!tb[SWITCH_PORT_ID])
+                       return -EINVAL;
+
+               port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
+               if (tb[SWITCH_PORT_FLAG_TAGGED])
+                       port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+       }
+
+       return 0;
+}
+
+static int
+swconfig_parse_link(struct sk_buff *msg, struct nlattr *nla,
+                   struct switch_port_link *link)
+{
+       struct nlattr *tb[SWITCH_LINK_ATTR_MAX + 1];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
+       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy))
+#else
+       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy, NULL))
+#endif
+               return -EINVAL;
+
+       link->duplex = !!tb[SWITCH_LINK_FLAG_DUPLEX];
+       link->aneg = !!tb[SWITCH_LINK_FLAG_ANEG];
+       link->speed = nla_get_u32(tb[SWITCH_LINK_SPEED]);
+
+       return 0;
+}
+
+static int
+swconfig_set_attr(struct sk_buff *skb, struct genl_info *info)
+{
+       const struct switch_attr *attr;
+       struct switch_dev *dev;
+       struct switch_val val;
+       int err = -EINVAL;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       memset(&val, 0, sizeof(val));
+       attr = swconfig_lookup_attr(dev, info, &val);
+       if (!attr || !attr->set)
+               goto error;
+
+       val.attr = attr;
+       switch (attr->type) {
+       case SWITCH_TYPE_NOVAL:
+               break;
+       case SWITCH_TYPE_INT:
+               if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT])
+                       goto error;
+               val.value.i =
+                       nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]);
+               break;
+       case SWITCH_TYPE_STRING:
+               if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR])
+                       goto error;
+               val.value.s =
+                       nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]);
+               break;
+       case SWITCH_TYPE_PORTS:
+               val.value.ports = dev->portbuf;
+               memset(dev->portbuf, 0,
+                       sizeof(struct switch_port) * dev->ports);
+
+               /* TODO: implement multipart? */
+               if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) {
+                       err = swconfig_parse_ports(skb,
+                               info->attrs[SWITCH_ATTR_OP_VALUE_PORTS],
+                               &val, dev->ports);
+                       if (err < 0)
+                               goto error;
+               } else {
+                       val.len = 0;
+                       err = 0;
+               }
+               break;
+       case SWITCH_TYPE_LINK:
+               val.value.link = &dev->linkbuf;
+               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
+
+               if (info->attrs[SWITCH_ATTR_OP_VALUE_LINK]) {
+                       err = swconfig_parse_link(skb,
+                                                 info->attrs[SWITCH_ATTR_OP_VALUE_LINK],
+                                                 val.value.link);
+                       if (err < 0)
+                               goto error;
+               } else {
+                       val.len = 0;
+                       err = 0;
+               }
+               break;
+       default:
+               goto error;
+       }
+
+       err = attr->set(dev, attr, &val);
+error:
+       swconfig_put_dev(dev);
+       return err;
+}
+
+static int
+swconfig_close_portlist(struct swconfig_callback *cb, void *arg)
+{
+       if (cb->nest[0])
+               nla_nest_end(cb->msg, cb->nest[0]);
+       return 0;
+}
+
+static int
+swconfig_send_port(struct swconfig_callback *cb, void *arg)
+{
+       const struct switch_port *port = arg;
+       struct nlattr *p = NULL;
+
+       if (!cb->nest[0]) {
+               cb->nest[0] = nla_nest_start(cb->msg, cb->cmd);
+               if (!cb->nest[0])
+                       return -1;
+       }
+
+       p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT);
+       if (!p)
+               goto error;
+
+       if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id))
+               goto nla_put_failure;
+       if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+               if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED))
+                       goto nla_put_failure;
+       }
+
+       nla_nest_end(cb->msg, p);
+       return 0;
+
+nla_put_failure:
+               nla_nest_cancel(cb->msg, p);
+error:
+       nla_nest_cancel(cb->msg, cb->nest[0]);
+       return -1;
+}
+
+static int
+swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr,
+               const struct switch_val *val)
+{
+       struct swconfig_callback cb;
+       int err = 0;
+       int i;
+
+       if (!val->value.ports)
+               return -EINVAL;
+
+       memset(&cb, 0, sizeof(cb));
+       cb.cmd = attr;
+       cb.msg = *msg;
+       cb.info = info;
+       cb.fill = swconfig_send_port;
+       cb.close = swconfig_close_portlist;
+
+       cb.nest[0] = nla_nest_start(cb.msg, cb.cmd);
+       for (i = 0; i < val->len; i++) {
+               err = swconfig_send_multipart(&cb, &val->value.ports[i]);
+               if (err)
+                       goto done;
+       }
+       err = val->len;
+       swconfig_close_portlist(&cb, NULL);
+       *msg = cb.msg;
+
+done:
+       return err;
+}
+
+static int
+swconfig_send_link(struct sk_buff *msg, struct genl_info *info, int attr,
+                  const struct switch_port_link *link)
+{
+       struct nlattr *p = NULL;
+       int err = 0;
+
+       p = nla_nest_start(msg, attr);
+       if (link->link) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_LINK))
+                       goto nla_put_failure;
+       }
+       if (link->duplex) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_DUPLEX))
+                       goto nla_put_failure;
+       }
+       if (link->aneg) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_ANEG))
+                       goto nla_put_failure;
+       }
+       if (link->tx_flow) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_TX_FLOW))
+                       goto nla_put_failure;
+       }
+       if (link->rx_flow) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_RX_FLOW))
+                       goto nla_put_failure;
+       }
+       if (nla_put_u32(msg, SWITCH_LINK_SPEED, link->speed))
+               goto nla_put_failure;
+       if (link->eee & ADVERTISED_100baseT_Full) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_100BASET))
+                       goto nla_put_failure;
+       }
+       if (link->eee & ADVERTISED_1000baseT_Full) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_1000BASET))
+                       goto nla_put_failure;
+       }
+       nla_nest_end(msg, p);
+
+       return err;
+
+nla_put_failure:
+       nla_nest_cancel(msg, p);
+       return -1;
+}
+
+static int
+swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attr *attr;
+       struct switch_dev *dev;
+       struct sk_buff *msg = NULL;
+       struct switch_val val;
+       int err = -EINVAL;
+       int cmd = hdr->cmd;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       memset(&val, 0, sizeof(val));
+       attr = swconfig_lookup_attr(dev, info, &val);
+       if (!attr || !attr->get)
+               goto error;
+
+       if (attr->type == SWITCH_TYPE_PORTS) {
+               val.value.ports = dev->portbuf;
+               memset(dev->portbuf, 0,
+                       sizeof(struct switch_port) * dev->ports);
+       } else if (attr->type == SWITCH_TYPE_LINK) {
+               val.value.link = &dev->linkbuf;
+               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
+       }
+
+       err = attr->get(dev, attr, &val);
+       if (err)
+               goto error;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               goto error;
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
+                       0, cmd);
+       if (IS_ERR(hdr))
+               goto nla_put_failure;
+
+       switch (attr->type) {
+       case SWITCH_TYPE_INT:
+               if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i))
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_STRING:
+               if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s))
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_PORTS:
+               err = swconfig_send_ports(&msg, info,
+                               SWITCH_ATTR_OP_VALUE_PORTS, &val);
+               if (err < 0)
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_LINK:
+               err = swconfig_send_link(msg, info,
+                                        SWITCH_ATTR_OP_VALUE_LINK, val.value.link);
+               if (err < 0)
+                       goto nla_put_failure;
+               break;
+       default:
+               pr_debug("invalid type in attribute\n");
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+       genlmsg_end(msg, hdr);
+       err = msg->len;
+       if (err < 0)
+               goto nla_put_failure;
+
+       swconfig_put_dev(dev);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       if (msg)
+               nlmsg_free(msg);
+error:
+       swconfig_put_dev(dev);
+       if (!err)
+               err = -ENOMEM;
+       return err;
+}
+
+static int
+swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+               const struct switch_dev *dev)
+{
+       struct nlattr *p = NULL, *m = NULL;
+       void *hdr;
+       int i;
+
+       hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
+                       SWITCH_CMD_NEW_ATTR);
+       if (IS_ERR(hdr))
+               return -1;
+
+       if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port))
+               goto nla_put_failure;
+
+       m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP);
+       if (!m)
+               goto nla_put_failure;
+       for (i = 0; i < dev->ports; i++) {
+               p = nla_nest_start(msg, SWITCH_ATTR_PORTS);
+               if (!p)
+                       continue;
+               if (dev->portmap[i].s) {
+                       if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT,
+                                               dev->portmap[i].s))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT,
+                                               dev->portmap[i].virt))
+                               goto nla_put_failure;
+               }
+               nla_nest_end(msg, p);
+       }
+       nla_nest_end(msg, m);
+       genlmsg_end(msg, hdr);
+       return msg->len;
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int swconfig_dump_switches(struct sk_buff *skb,
+               struct netlink_callback *cb)
+{
+       struct switch_dev *dev;
+       int start = cb->args[0];
+       int idx = 0;
+
+       swconfig_lock();
+       list_for_each_entry(dev, &swdevs, dev_list) {
+               if (++idx <= start)
+                       continue;
+               if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid,
+                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                               dev) < 0)
+                       break;
+       }
+       swconfig_unlock();
+       cb->args[0] = idx;
+
+       return skb->len;
+}
+
+static int
+swconfig_done(struct netlink_callback *cb)
+{
+       return 0;
+}
+
+static struct genl_ops swconfig_ops[] = {
+       {
+               .cmd = SWITCH_CMD_LIST_GLOBAL,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_LIST_VLAN,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_LIST_PORT,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_GLOBAL,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_VLAN,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_PORT,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_GLOBAL,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_VLAN,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_PORT,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_SWITCH,
+               .dumpit = swconfig_dump_switches,
+               .policy = switch_policy,
+               .done = swconfig_done,
+       }
+};
+
+static struct genl_family switch_fam = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+       .id = GENL_ID_GENERATE,
+#endif
+       .name = "switch",
+       .hdrsize = 0,
+       .version = 1,
+       .maxattr = SWITCH_ATTR_MAX,
+       .module = THIS_MODULE,
+       .ops = swconfig_ops,
+       .n_ops = ARRAY_SIZE(swconfig_ops),
+};
+
+#ifdef CONFIG_OF
+void
+of_switch_load_portmap(struct switch_dev *dev)
+{
+       struct device_node *port;
+
+       if (!dev->of_node)
+               return;
+
+       for_each_child_of_node(dev->of_node, port) {
+               const __be32 *prop;
+               const char *segment;
+               int size, phys;
+
+               if (!of_device_is_compatible(port, "swconfig,port"))
+                       continue;
+
+               if (of_property_read_string(port, "swconfig,segment", &segment))
+                       continue;
+
+               prop = of_get_property(port, "swconfig,portmap", &size);
+               if (!prop)
+                       continue;
+
+               if (size != (2 * sizeof(*prop))) {
+                       pr_err("%s: failed to parse port mapping\n",
+                                       port->name);
+                       continue;
+               }
+
+               phys = be32_to_cpup(prop++);
+               if ((phys < 0) | (phys >= dev->ports)) {
+                       pr_err("%s: physical port index out of range\n",
+                                       port->name);
+                       continue;
+               }
+
+               dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL);
+               dev->portmap[phys].virt = be32_to_cpup(prop);
+               pr_debug("Found port: %s, physical: %d, virtual: %d\n",
+                       segment, phys, dev->portmap[phys].virt);
+       }
+}
+#endif
+
+int
+register_switch(struct switch_dev *dev, struct net_device *netdev)
+{
+       struct switch_dev *sdev;
+       const int max_switches = 8 * sizeof(unsigned long);
+       unsigned long in_use = 0;
+       int err;
+       int i;
+
+       INIT_LIST_HEAD(&dev->dev_list);
+       if (netdev) {
+               dev->netdev = netdev;
+               if (!dev->alias)
+                       dev->alias = netdev->name;
+       }
+       BUG_ON(!dev->alias);
+
+       /* Make sure swdev_id doesn't overflow */
+       if (swdev_id == INT_MAX) {
+               return -ENOMEM;
+       }
+
+       if (dev->ports > 0) {
+               dev->portbuf = kzalloc(sizeof(struct switch_port) *
+                               dev->ports, GFP_KERNEL);
+               if (!dev->portbuf)
+                       return -ENOMEM;
+               dev->portmap = kzalloc(sizeof(struct switch_portmap) *
+                               dev->ports, GFP_KERNEL);
+               if (!dev->portmap) {
+                       kfree(dev->portbuf);
+                       return -ENOMEM;
+               }
+       }
+       swconfig_defaults_init(dev);
+       mutex_init(&dev->sw_mutex);
+       swconfig_lock();
+       dev->id = ++swdev_id;
+
+       list_for_each_entry(sdev, &swdevs, dev_list) {
+               if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i))
+                       continue;
+               if (i < 0 || i > max_switches)
+                       continue;
+
+               set_bit(i, &in_use);
+       }
+       i = find_first_zero_bit(&in_use, max_switches);
+
+       if (i == max_switches) {
+               swconfig_unlock();
+               return -ENFILE;
+       }
+
+#ifdef CONFIG_OF
+       if (dev->ports)
+               of_switch_load_portmap(dev);
+#endif
+
+       /* fill device name */
+       snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
+
+       list_add_tail(&dev->dev_list, &swdevs);
+       swconfig_unlock();
+
+       err = swconfig_create_led_trigger(dev);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(register_switch);
+
+void
+unregister_switch(struct switch_dev *dev)
+{
+       swconfig_destroy_led_trigger(dev);
+       kfree(dev->portbuf);
+       mutex_lock(&dev->sw_mutex);
+       swconfig_lock();
+       list_del(&dev->dev_list);
+       swconfig_unlock();
+       mutex_unlock(&dev->sw_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_switch);
+
+int
+switch_generic_set_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link)
+{
+       if (WARN_ON(!dev->ops->phy_write16))
+               return -ENOTSUPP;
+
+       /* Generic implementation */
+       if (link->aneg) {
+               dev->ops->phy_write16(dev, port, MII_BMCR, 0x0000);
+               dev->ops->phy_write16(dev, port, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+       } else {
+               u16 bmcr = 0;
+
+               if (link->duplex)
+                       bmcr |= BMCR_FULLDPLX;
+
+               switch (link->speed) {
+               case SWITCH_PORT_SPEED_10:
+                       break;
+               case SWITCH_PORT_SPEED_100:
+                       bmcr |= BMCR_SPEED100;
+                       break;
+               case SWITCH_PORT_SPEED_1000:
+                       bmcr |= BMCR_SPEED1000;
+                       break;
+               default:
+                       return -ENOTSUPP;
+               }
+
+               dev->ops->phy_write16(dev, port, MII_BMCR, bmcr);
+       }
+
+       return 0;
+}
+
+static int __init
+swconfig_init(void)
+{
+       INIT_LIST_HEAD(&swdevs);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+       return genl_register_family_with_ops(&switch_fam, swconfig_ops);
+#else
+       return genl_register_family(&switch_fam);
+#endif
+}
+
+static void __exit
+swconfig_exit(void)
+{
+       genl_unregister_family(&switch_fam);
+}
+
+module_init(swconfig_init);
+module_exit(swconfig_exit);
diff --git a/target/linux/generic/files-3.18/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files-3.18/drivers/net/phy/swconfig_leds.c
new file mode 100644 (file)
index 0000000..91824b7
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * swconfig_led.c: LED trigger support for the switch configuration API
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ */
+
+#ifdef CONFIG_SWCONFIG_LEDS
+
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+
+#define SWCONFIG_LED_TIMER_INTERVAL    (HZ / 10)
+#define SWCONFIG_LED_NUM_PORTS         32
+
+#define SWCONFIG_LED_PORT_SPEED_NA     0x01    /* unknown speed */
+#define SWCONFIG_LED_PORT_SPEED_10     0x02    /* 10 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_100    0x04    /* 100 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_1000   0x08    /* 1000 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_ALL    (SWCONFIG_LED_PORT_SPEED_NA | \
+                                        SWCONFIG_LED_PORT_SPEED_10 | \
+                                        SWCONFIG_LED_PORT_SPEED_100 | \
+                                        SWCONFIG_LED_PORT_SPEED_1000)
+
+#define SWCONFIG_LED_MODE_LINK         0x01
+#define SWCONFIG_LED_MODE_TX           0x02
+#define SWCONFIG_LED_MODE_RX           0x04
+#define SWCONFIG_LED_MODE_TXRX         (SWCONFIG_LED_MODE_TX   | \
+                                        SWCONFIG_LED_MODE_RX)
+#define SWCONFIG_LED_MODE_ALL          (SWCONFIG_LED_MODE_LINK | \
+                                        SWCONFIG_LED_MODE_TX   | \
+                                        SWCONFIG_LED_MODE_RX)
+
+struct switch_led_trigger {
+       struct led_trigger trig;
+       struct switch_dev *swdev;
+
+       struct delayed_work sw_led_work;
+       u32 port_mask;
+       u32 port_link;
+       unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
+       unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
+       u8 link_speed[SWCONFIG_LED_NUM_PORTS];
+};
+
+struct swconfig_trig_data {
+       struct led_classdev *led_cdev;
+       struct switch_dev *swdev;
+
+       rwlock_t lock;
+       u32 port_mask;
+
+       bool prev_link;
+       unsigned long prev_traffic;
+       enum led_brightness prev_brightness;
+       u8 mode;
+       u8 speed_mask;
+};
+
+static void
+swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
+                            enum led_brightness brightness)
+{
+       led_set_brightness(trig_data->led_cdev, brightness);
+       trig_data->prev_brightness = brightness;
+}
+
+static void
+swconfig_trig_update_port_mask(struct led_trigger *trigger)
+{
+       struct list_head *entry;
+       struct switch_led_trigger *sw_trig;
+       u32 port_mask;
+
+       if (!trigger)
+               return;
+
+       sw_trig = (void *) trigger;
+
+       port_mask = 0;
+       read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_cdevs) {
+               struct led_classdev *led_cdev;
+               struct swconfig_trig_data *trig_data;
+
+               led_cdev = list_entry(entry, struct led_classdev, trig_list);
+               trig_data = led_cdev->trigger_data;
+               if (trig_data) {
+                       read_lock(&trig_data->lock);
+                       port_mask |= trig_data->port_mask;
+                       read_unlock(&trig_data->lock);
+               }
+       }
+       read_unlock(&trigger->leddev_list_lock);
+
+       sw_trig->port_mask = port_mask;
+
+       if (port_mask)
+               schedule_delayed_work(&sw_trig->sw_led_work,
+                                     SWCONFIG_LED_TIMER_INTERVAL);
+       else
+               cancel_delayed_work_sync(&sw_trig->sw_led_work);
+}
+
+static ssize_t
+swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       unsigned long port_mask;
+       int ret;
+       bool changed;
+
+       ret = kstrtoul(buf, 0, &port_mask);
+       if (ret)
+               return ret;
+
+       write_lock(&trig_data->lock);
+       changed = (trig_data->port_mask != port_mask);
+       trig_data->port_mask = port_mask;
+       write_unlock(&trig_data->lock);
+
+       if (changed) {
+               if (port_mask == 0)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF);
+
+               swconfig_trig_update_port_mask(led_cdev->trigger);
+       }
+
+       return size;
+}
+
+static ssize_t
+swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u32 port_mask;
+
+       read_lock(&trig_data->lock);
+       port_mask = trig_data->port_mask;
+       read_unlock(&trig_data->lock);
+
+       sprintf(buf, "%#x\n", port_mask);
+
+       return strlen(buf) + 1;
+}
+
+static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
+                  swconfig_trig_port_mask_store);
+
+/* speed_mask file handler - display value */
+static ssize_t swconfig_trig_speed_mask_show(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 speed_mask;
+
+       read_lock(&trig_data->lock);
+       speed_mask = trig_data->speed_mask;
+       read_unlock(&trig_data->lock);
+
+       sprintf(buf, "%#x\n", speed_mask);
+
+       return strlen(buf) + 1;
+}
+
+/* speed_mask file handler - store value */
+static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
+                                             struct device_attribute *attr,
+                                             const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 speed_mask;
+       int ret;
+
+       ret = kstrtou8(buf, 0, &speed_mask);
+       if (ret)
+               return ret;
+
+       write_lock(&trig_data->lock);
+       trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
+       write_unlock(&trig_data->lock);
+
+       return size;
+}
+
+/* speed_mask special file */
+static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
+                  swconfig_trig_speed_mask_store);
+
+static ssize_t swconfig_trig_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 mode;
+
+       read_lock(&trig_data->lock);
+       mode = trig_data->mode;
+       read_unlock(&trig_data->lock);
+
+       if (mode == 0) {
+               strcpy(buf, "none\n");
+       } else {
+               if (mode & SWCONFIG_LED_MODE_LINK)
+                       strcat(buf, "link ");
+               if (mode & SWCONFIG_LED_MODE_TX)
+                       strcat(buf, "tx ");
+               if (mode & SWCONFIG_LED_MODE_RX)
+                       strcat(buf, "rx ");
+               strcat(buf, "\n");
+       }
+
+       return strlen(buf)+1;
+}
+
+static ssize_t swconfig_trig_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       char copybuf[128];
+       int new_mode = -1;
+       char *p, *token;
+
+       /* take a copy since we don't want to trash the inbound buffer when using strsep */
+       strncpy(copybuf, buf, sizeof(copybuf));
+       copybuf[sizeof(copybuf) - 1] = 0;
+       p = copybuf;
+
+       while ((token = strsep(&p, " \t\n")) != NULL) {
+               if (!*token)
+                       continue;
+
+               if (new_mode < 0)
+                       new_mode = 0;
+
+               if (!strcmp(token, "none"))
+                       new_mode = 0;
+               else if (!strcmp(token, "tx"))
+                       new_mode |= SWCONFIG_LED_MODE_TX;
+               else if (!strcmp(token, "rx"))
+                       new_mode |= SWCONFIG_LED_MODE_RX;
+               else if (!strcmp(token, "link"))
+                       new_mode |= SWCONFIG_LED_MODE_LINK;
+               else
+                       return -EINVAL;
+       }
+
+       if (new_mode < 0)
+               return -EINVAL;
+
+       write_lock(&trig_data->lock);
+       trig_data->mode = (u8)new_mode;
+       write_unlock(&trig_data->lock);
+
+       return size;
+}
+
+/* mode special file */
+static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
+                  swconfig_trig_mode_store);
+
+static void
+swconfig_trig_activate(struct led_classdev *led_cdev)
+{
+       struct switch_led_trigger *sw_trig;
+       struct swconfig_trig_data *trig_data;
+       int err;
+
+       if (led_cdev->trigger->activate != swconfig_trig_activate)
+               return;
+
+       trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
+       if (!trig_data)
+               return;
+
+       sw_trig = (void *) led_cdev->trigger;
+
+       rwlock_init(&trig_data->lock);
+       trig_data->led_cdev = led_cdev;
+       trig_data->swdev = sw_trig->swdev;
+       trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
+       trig_data->mode = SWCONFIG_LED_MODE_ALL;
+       led_cdev->trigger_data = trig_data;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
+       if (err)
+               goto err_free;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
+       if (err)
+               goto err_dev_free;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_mode);
+       if (err)
+               goto err_mode_free;
+
+       return;
+
+err_mode_free:
+       device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+
+err_dev_free:
+       device_remove_file(led_cdev->dev, &dev_attr_port_mask);
+
+err_free:
+       led_cdev->trigger_data = NULL;
+       kfree(trig_data);
+}
+
+static void
+swconfig_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct swconfig_trig_data *trig_data;
+
+       swconfig_trig_update_port_mask(led_cdev->trigger);
+
+       trig_data = (void *) led_cdev->trigger_data;
+       if (trig_data) {
+               device_remove_file(led_cdev->dev, &dev_attr_port_mask);
+               device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+               device_remove_file(led_cdev->dev, &dev_attr_mode);
+               kfree(trig_data);
+       }
+}
+
+/*
+ * link off -> led off (can't be any other reason to turn it on)
+ * link on:
+ *     mode link: led on by default only if speed matches, else off
+ *     mode txrx: blink only if speed matches, else off
+ */
+static void
+swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
+                       struct led_classdev *led_cdev)
+{
+       struct swconfig_trig_data *trig_data;
+       u32 port_mask;
+       bool link;
+       u8 speed_mask, mode;
+       enum led_brightness led_base, led_blink;
+
+       trig_data = led_cdev->trigger_data;
+       if (!trig_data)
+               return;
+
+       read_lock(&trig_data->lock);
+       port_mask = trig_data->port_mask;
+       speed_mask = trig_data->speed_mask;
+       mode = trig_data->mode;
+       read_unlock(&trig_data->lock);
+
+       link = !!(sw_trig->port_link & port_mask);
+       if (!link) {
+               if (trig_data->prev_brightness != LED_OFF)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
+       }
+       else {
+               unsigned long traffic;
+               int speedok;    /* link speed flag */
+               int i;
+
+               led_base = LED_FULL;
+               led_blink = LED_OFF;
+               traffic = 0;
+               speedok = 0;
+               for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+                       if (port_mask & (1 << i)) {
+                               if (sw_trig->link_speed[i] & speed_mask) {
+                                       traffic += ((mode & SWCONFIG_LED_MODE_TX) ?
+                                                   sw_trig->port_tx_traffic[i] : 0) +
+                                               ((mode & SWCONFIG_LED_MODE_RX) ?
+                                                sw_trig->port_rx_traffic[i] : 0);
+                                       speedok = 1;
+                               }
+                       }
+               }
+
+               if (speedok) {
+                       /* At least one port speed matches speed_mask */
+                       if (!(mode & SWCONFIG_LED_MODE_LINK)) {
+                               led_base = LED_OFF;
+                               led_blink = LED_FULL;
+                       }
+
+                       if (trig_data->prev_brightness != led_base)
+                               swconfig_trig_set_brightness(trig_data,
+                                                            led_base);
+                       else if (traffic != trig_data->prev_traffic)
+                               swconfig_trig_set_brightness(trig_data,
+                                                            led_blink);
+               } else if (trig_data->prev_brightness != LED_OFF)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF);
+
+               trig_data->prev_traffic = traffic;
+       }
+
+       trig_data->prev_link = link;
+}
+
+static void
+swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
+{
+       struct list_head *entry;
+       struct led_trigger *trigger;
+
+       trigger = &sw_trig->trig;
+       read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_cdevs) {
+               struct led_classdev *led_cdev;
+
+               led_cdev = list_entry(entry, struct led_classdev, trig_list);
+               swconfig_trig_led_event(sw_trig, led_cdev);
+       }
+       read_unlock(&trigger->leddev_list_lock);
+}
+
+static void
+swconfig_led_work_func(struct work_struct *work)
+{
+       struct switch_led_trigger *sw_trig;
+       struct switch_dev *swdev;
+       u32 port_mask;
+       u32 link;
+       int i;
+
+       sw_trig = container_of(work, struct switch_led_trigger,
+                              sw_led_work.work);
+
+       port_mask = sw_trig->port_mask;
+       swdev = sw_trig->swdev;
+
+       link = 0;
+       for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+               u32 port_bit;
+
+               sw_trig->link_speed[i] = 0;
+
+               port_bit = BIT(i);
+               if ((port_mask & port_bit) == 0)
+                       continue;
+
+               if (swdev->ops->get_port_link) {
+                       struct switch_port_link port_link;
+
+                       memset(&port_link, '\0', sizeof(port_link));
+                       swdev->ops->get_port_link(swdev, i, &port_link);
+
+                       if (port_link.link) {
+                               link |= port_bit;
+                               switch (port_link.speed) {
+                               case SWITCH_PORT_SPEED_UNKNOWN:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_NA;
+                                       break;
+                               case SWITCH_PORT_SPEED_10:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_10;
+                                       break;
+                               case SWITCH_PORT_SPEED_100:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_100;
+                                       break;
+                               case SWITCH_PORT_SPEED_1000:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_1000;
+                                       break;
+                               }
+                       }
+               }
+
+               if (swdev->ops->get_port_stats) {
+                       struct switch_port_stats port_stats;
+
+                       memset(&port_stats, '\0', sizeof(port_stats));
+                       swdev->ops->get_port_stats(swdev, i, &port_stats);
+                       sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
+                       sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
+               }
+       }
+
+       sw_trig->port_link = link;
+
+       swconfig_trig_update_leds(sw_trig);
+
+       schedule_delayed_work(&sw_trig->sw_led_work,
+                             SWCONFIG_LED_TIMER_INTERVAL);
+}
+
+static int
+swconfig_create_led_trigger(struct switch_dev *swdev)
+{
+       struct switch_led_trigger *sw_trig;
+       int err;
+
+       if (!swdev->ops->get_port_link)
+               return 0;
+
+       sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL);
+       if (!sw_trig)
+               return -ENOMEM;
+
+       sw_trig->swdev = swdev;
+       sw_trig->trig.name = swdev->devname;
+       sw_trig->trig.activate = swconfig_trig_activate;
+       sw_trig->trig.deactivate = swconfig_trig_deactivate;
+
+       INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func);
+
+       err = led_trigger_register(&sw_trig->trig);
+       if (err)
+               goto err_free;
+
+       swdev->led_trigger = sw_trig;
+
+       return 0;
+
+err_free:
+       kfree(sw_trig);
+       return err;
+}
+
+static void
+swconfig_destroy_led_trigger(struct switch_dev *swdev)
+{
+       struct switch_led_trigger *sw_trig;
+
+       sw_trig = swdev->led_trigger;
+       if (sw_trig) {
+               cancel_delayed_work_sync(&sw_trig->sw_led_work);
+               led_trigger_unregister(&sw_trig->trig);
+               kfree(sw_trig);
+       }
+}
+
+#else /* SWCONFIG_LEDS */
+static inline int
+swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; }
+
+static inline void
+swconfig_destroy_led_trigger(struct switch_dev *swdev) { }
+#endif /* CONFIG_SWCONFIG_LEDS */
diff --git a/target/linux/generic/files-3.18/include/linux/ar8216_platform.h b/target/linux/generic/files-3.18/include/linux/ar8216_platform.h
new file mode 100644 (file)
index 0000000..24bc442
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * AR8216 switch driver platform data
+ *
+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AR8216_PLATFORM_H
+#define AR8216_PLATFORM_H
+
+enum ar8327_pad_mode {
+       AR8327_PAD_NC = 0,
+       AR8327_PAD_MAC2MAC_MII,
+       AR8327_PAD_MAC2MAC_GMII,
+       AR8327_PAD_MAC_SGMII,
+       AR8327_PAD_MAC2PHY_MII,
+       AR8327_PAD_MAC2PHY_GMII,
+       AR8327_PAD_MAC_RGMII,
+       AR8327_PAD_PHY_GMII,
+       AR8327_PAD_PHY_RGMII,
+       AR8327_PAD_PHY_MII,
+};
+
+enum ar8327_clk_delay_sel {
+       AR8327_CLK_DELAY_SEL0 = 0,
+       AR8327_CLK_DELAY_SEL1,
+       AR8327_CLK_DELAY_SEL2,
+       AR8327_CLK_DELAY_SEL3,
+};
+
+struct ar8327_pad_cfg {
+       enum ar8327_pad_mode mode;
+       bool rxclk_sel;
+       bool txclk_sel;
+       bool pipe_rxclk_sel;
+       bool txclk_delay_en;
+       bool rxclk_delay_en;
+       bool sgmii_delay_en;
+       enum ar8327_clk_delay_sel txclk_delay_sel;
+       enum ar8327_clk_delay_sel rxclk_delay_sel;
+       bool mac06_exchange_dis;
+};
+
+enum ar8327_port_speed {
+       AR8327_PORT_SPEED_10 = 0,
+       AR8327_PORT_SPEED_100,
+       AR8327_PORT_SPEED_1000,
+};
+
+struct ar8327_port_cfg {
+       int force_link:1;
+       enum ar8327_port_speed speed;
+       int txpause:1;
+       int rxpause:1;
+       int duplex:1;
+};
+
+struct ar8327_sgmii_cfg {
+       u32 sgmii_ctrl;
+       bool serdes_aen;
+};
+
+struct ar8327_led_cfg {
+       u32 led_ctrl0;
+       u32 led_ctrl1;
+       u32 led_ctrl2;
+       u32 led_ctrl3;
+       bool open_drain;
+};
+
+enum ar8327_led_num {
+       AR8327_LED_PHY0_0 = 0,
+       AR8327_LED_PHY0_1,
+       AR8327_LED_PHY0_2,
+       AR8327_LED_PHY1_0,
+       AR8327_LED_PHY1_1,
+       AR8327_LED_PHY1_2,
+       AR8327_LED_PHY2_0,
+       AR8327_LED_PHY2_1,
+       AR8327_LED_PHY2_2,
+       AR8327_LED_PHY3_0,
+       AR8327_LED_PHY3_1,
+       AR8327_LED_PHY3_2,
+       AR8327_LED_PHY4_0,
+       AR8327_LED_PHY4_1,
+       AR8327_LED_PHY4_2,
+};
+
+enum ar8327_led_mode {
+       AR8327_LED_MODE_HW = 0,
+       AR8327_LED_MODE_SW,
+};
+
+struct ar8327_led_info {
+       const char *name;
+       const char *default_trigger;
+       bool active_low;
+       enum ar8327_led_num led_num;
+       enum ar8327_led_mode mode;
+};
+
+#define AR8327_LED_INFO(_led, _mode, _name) {  \
+       .name = (_name),                        \
+       .led_num = AR8327_LED_ ## _led,         \
+       .mode = AR8327_LED_MODE_ ## _mode       \
+}
+
+struct ar8327_platform_data {
+       struct ar8327_pad_cfg *pad0_cfg;
+       struct ar8327_pad_cfg *pad5_cfg;
+       struct ar8327_pad_cfg *pad6_cfg;
+       struct ar8327_sgmii_cfg *sgmii_cfg;
+       struct ar8327_port_cfg port0_cfg;
+       struct ar8327_port_cfg port6_cfg;
+       struct ar8327_led_cfg *led_cfg;
+
+       int (*get_port_link)(unsigned port);
+
+       unsigned num_leds;
+       const struct ar8327_led_info *leds;
+};
+
+#endif /* AR8216_PLATFORM_H */
+
diff --git a/target/linux/generic/files-3.18/include/linux/ath5k_platform.h b/target/linux/generic/files-3.18/include/linux/ath5k_platform.h
new file mode 100644 (file)
index 0000000..ec85224
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_ATH5K_PLATFORM_H
+#define _LINUX_ATH5K_PLATFORM_H
+
+#define ATH5K_PLAT_EEP_MAX_WORDS       2048
+
+struct ath5k_platform_data {
+       u16 *eeprom_data;
+       u8 *macaddr;
+};
+
+#endif /* _LINUX_ATH5K_PLATFORM_H */
diff --git a/target/linux/generic/files-3.18/include/linux/ath9k_platform.h b/target/linux/generic/files-3.18/include/linux/ath9k_platform.h
new file mode 100644 (file)
index 0000000..f1f2ad4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_ATH9K_PLATFORM_H
+#define _LINUX_ATH9K_PLATFORM_H
+
+#define ATH9K_PLAT_EEP_MAX_WORDS       2048
+
+struct ath9k_platform_data {
+       const char *eeprom_name;
+
+       u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
+       u8 *macaddr;
+
+       int led_pin;
+       u32 gpio_mask;
+       u32 gpio_val;
+
+       u32 bt_active_pin;
+       u32 bt_priority_pin;
+       u32 wlan_active_pin;
+
+       bool endian_check;
+       bool is_clk_25mhz;
+       bool tx_gain_buffalo;
+       bool disable_2ghz;
+       bool disable_5ghz;
+       bool led_active_high;
+
+       int (*get_mac_revision)(void);
+       int (*external_reset)(void);
+
+       bool use_eeprom;
+
+       int num_leds;
+       const struct gpio_led *leds;
+
+       unsigned num_btns;
+       const struct gpio_keys_button *btns;
+       unsigned btn_poll_interval;
+
+       bool ubnt_hsr;
+};
+
+#endif /* _LINUX_ATH9K_PLATFORM_H */
diff --git a/target/linux/generic/files-3.18/include/linux/myloader.h b/target/linux/generic/files-3.18/include/linux/myloader.h
new file mode 100644 (file)
index 0000000..d89e415
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  Compex's MyLoader specific definitions
+ *
+ *  Copyright (C) 2006-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MYLOADER_H_
+#define _MYLOADER_H_
+
+/* Myloader specific magic numbers */
+#define MYLO_MAGIC_SYS_PARAMS  0x20021107
+#define MYLO_MAGIC_PARTITIONS  0x20021103
+#define MYLO_MAGIC_BOARD_PARAMS        0x20021103
+
+/* Vendor ID's (seems to be same as the PCI vendor ID's) */
+#define VENID_COMPEX           0x11F6
+
+/* Devices based on the ADM5120 */
+#define DEVID_COMPEX_NP27G     0x0078
+#define DEVID_COMPEX_NP28G     0x044C
+#define DEVID_COMPEX_NP28GHS   0x044E
+#define DEVID_COMPEX_WP54Gv1C  0x0514
+#define DEVID_COMPEX_WP54G     0x0515
+#define DEVID_COMPEX_WP54AG    0x0546
+#define DEVID_COMPEX_WPP54AG   0x0550
+#define DEVID_COMPEX_WPP54G    0x0555
+
+/* Devices based on the Atheros AR2317 */
+#define DEVID_COMPEX_NP25G     0x05E6
+#define DEVID_COMPEX_WPE53G    0x05DC
+
+/* Devices based on the Atheros AR71xx */
+#define DEVID_COMPEX_WP543     0x0640
+#define DEVID_COMPEX_WPE72     0x0672
+
+/* Devices based on the IXP422 */
+#define DEVID_COMPEX_WP18      0x047E
+#define DEVID_COMPEX_NP18A     0x0489
+
+/* Other devices */
+#define DEVID_COMPEX_NP26G8M   0x03E8
+#define DEVID_COMPEX_NP26G16M  0x03E9
+
+struct mylo_partition {
+       uint16_t        flags;  /* partition flags */
+       uint16_t        type;   /* type of the partition */
+       uint32_t        addr;   /* relative address of the partition from the
+                                  flash start */
+       uint32_t        size;   /* size of the partition in bytes */
+       uint32_t        param;  /* if this is the active partition, the
+                                  MyLoader load code to this address */
+};
+
+#define PARTITION_FLAG_ACTIVE  0x8000 /* this is the active partition,
+                                       * MyLoader loads firmware from here */
+#define PARTITION_FLAG_ISRAM   0x2000 /* FIXME: this is a RAM partition? */
+#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */
+#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM
+                                       * before decompression */
+#define PARTITION_FLAG_LZMA    0x0100 /* partition data compressed by LZMA */
+#define PARTITION_FLAG_HAVEHDR  0x0002 /* the partition data have a header */
+
+#define PARTITION_TYPE_FREE    0
+#define PARTITION_TYPE_USED    1
+
+#define MYLO_MAX_PARTITIONS    8       /* maximum number of partitions in the
+                                          partition table */
+
+struct mylo_partition_table {
+       uint32_t        magic;          /* must be MYLO_MAGIC_PARTITIONS */
+       uint32_t        res0;           /* unknown/unused */
+       uint32_t        res1;           /* unknown/unused */
+       uint32_t        res2;           /* unknown/unused */
+       struct mylo_partition partitions[MYLO_MAX_PARTITIONS];
+};
+
+struct mylo_partition_header {
+       uint32_t        len;            /* length of the partition data */
+       uint32_t        crc;            /* CRC value of the partition data */
+};
+
+struct mylo_system_params {
+       uint32_t        magic;          /* must be MYLO_MAGIC_SYS_PARAMS */
+       uint32_t        res0;
+       uint32_t        res1;
+       uint32_t        mylo_ver;
+       uint16_t        vid;            /* Vendor ID */
+       uint16_t        did;            /* Device ID */
+       uint16_t        svid;           /* Sub Vendor ID */
+       uint16_t        sdid;           /* Sub Device ID */
+       uint32_t        rev;            /* device revision */
+       uint32_t        fwhi;
+       uint32_t        fwlo;
+       uint32_t        tftp_addr;
+       uint32_t        prog_start;
+       uint32_t        flash_size;     /* size of boot FLASH in bytes */
+       uint32_t        dram_size;      /* size of onboard RAM in bytes */
+};
+
+struct mylo_eth_addr {
+       uint8_t mac[6];
+       uint8_t csum[2];
+};
+
+#define MYLO_ETHADDR_COUNT     8       /* maximum number of ethernet address
+                                          in the board parameters */
+
+struct mylo_board_params {
+       uint32_t        magic;  /* must be MYLO_MAGIC_BOARD_PARAMS */
+       uint32_t        res0;
+       uint32_t        res1;
+       uint32_t        res2;
+       struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT];
+};
+
+#endif /* _MYLOADER_H_*/
diff --git a/target/linux/generic/files-3.18/include/linux/platform_data/adm6996-gpio.h b/target/linux/generic/files-3.18/include/linux/platform_data/adm6996-gpio.h
new file mode 100644 (file)
index 0000000..d5af9bb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * ADM6996 GPIO platform data
+ *
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#ifndef __PLATFORM_ADM6996_GPIO_H
+#define __PLATFORM_ADM6996_GPIO_H
+
+#include <linux/kernel.h>
+
+enum adm6996_model {
+       ADM6996FC = 1,
+       ADM6996M = 2,
+       ADM6996L = 3,
+};
+
+struct adm6996_gpio_platform_data {
+       u8 eecs;
+       u8 eesk;
+       u8 eedi;
+       enum adm6996_model model;
+};
+
+#endif
diff --git a/target/linux/generic/files-3.18/include/linux/platform_data/b53.h b/target/linux/generic/files-3.18/include/linux/platform_data/b53.h
new file mode 100644 (file)
index 0000000..7842741
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * B53 platform data
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_H
+#define __B53_H
+
+#include <linux/kernel.h>
+
+struct b53_platform_data {
+       u32 chip_id;
+       u16 enabled_ports;
+
+       /* allow to specify an ethX alias */
+       const char *alias;
+
+       /* only used by MMAP'd driver */
+       unsigned big_endian:1;
+       void __iomem *regs;
+};
+
+#endif
diff --git a/target/linux/generic/files-3.18/include/linux/routerboot.h b/target/linux/generic/files-3.18/include/linux/routerboot.h
new file mode 100644 (file)
index 0000000..3cda858
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Mikrotik's RouterBOOT definitions
+ *
+ *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef _ROUTERBOOT_H
+#define _ROUTERBOOT_H
+
+#define RB_MAC_SIZE            6
+
+/*
+ * Magic numbers
+ */
+#define RB_MAGIC_HARD  0x64726148 /* "Hard" */
+#define RB_MAGIC_SOFT  0x74666F53 /* "Soft" */
+#define RB_MAGIC_DAWN  0x6E776144 /* "Dawn" */
+
+#define RB_ID_TERMINATOR       0
+
+/*
+ * ID values for Hardware settings
+ */
+#define RB_ID_HARD_01          1
+#define RB_ID_HARD_02          2
+#define RB_ID_FLASH_INFO       3
+#define RB_ID_MAC_ADDRESS_PACK 4
+#define RB_ID_BOARD_NAME       5
+#define RB_ID_BIOS_VERSION     6
+#define RB_ID_HARD_07          7
+#define RB_ID_SDRAM_TIMINGS    8
+#define RB_ID_DEVICE_TIMINGS   9
+#define RB_ID_SOFTWARE_ID      10
+#define RB_ID_SERIAL_NUMBER    11
+#define RB_ID_HARD_12          12
+#define RB_ID_MEMORY_SIZE      13
+#define RB_ID_MAC_ADDRESS_COUNT        14
+#define RB_ID_HW_OPTIONS       21
+#define RB_ID_WLAN_DATA                22
+
+/*
+ * ID values for Software settings
+ */
+#define RB_ID_UART_SPEED       1
+#define RB_ID_BOOT_DELAY       2
+#define RB_ID_BOOT_DEVICE      3
+#define RB_ID_BOOT_KEY         4
+#define RB_ID_CPU_MODE         5
+#define RB_ID_FW_VERSION       6
+#define RB_ID_SOFT_07          7
+#define RB_ID_SOFT_08          8
+#define RB_ID_BOOT_PROTOCOL    9
+#define RB_ID_SOFT_10          10
+#define RB_ID_SOFT_11          11
+
+/*
+ * UART_SPEED values
+ */
+#define RB_UART_SPEED_115200   0
+#define RB_UART_SPEED_57600    1
+#define RB_UART_SPEED_38400    2
+#define RB_UART_SPEED_19200    3
+#define RB_UART_SPEED_9600     4
+#define RB_UART_SPEED_4800     5
+#define RB_UART_SPEED_2400     6
+#define RB_UART_SPEED_1200     7
+
+/*
+ * BOOT_DELAY values
+ */
+#define RB_BOOT_DELAY_0SEC     0
+#define RB_BOOT_DELAY_1SEC     1
+#define RB_BOOT_DELAY_2SEC     2
+
+/*
+ * BOOT_DEVICE values
+ */
+#define RB_BOOT_DEVICE_ETHER   0
+#define RB_BOOT_DEVICE_NANDETH 1
+#define RB_BOOT_DEVICE_ETHONCE 2
+#define RB_BOOT_DEVICE_NANDONLY        3
+
+/*
+ * BOOT_KEY values
+ */
+#define RB_BOOT_KEY_ANY                0
+#define RB_BOOT_KEY_DEL                1
+
+/*
+ * CPU_MODE values
+ */
+#define RB_CPU_MODE_POWERSAVE  0
+#define RB_CPU_MODE_REGULAR    1
+
+/*
+ * BOOT_PROTOCOL values
+ */
+#define RB_BOOT_PROTOCOL_BOOTP 0
+#define RB_BOOT_PROTOCOL_DHCP  1
+
+#endif /* _ROUTERBOOT_H */
diff --git a/target/linux/generic/files-3.18/include/linux/rt2x00_platform.h b/target/linux/generic/files-3.18/include/linux/rt2x00_platform.h
new file mode 100644 (file)
index 0000000..e10377e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Platform data definition for the rt2x00 driver
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef _RT2X00_PLATFORM_H
+#define _RT2X00_PLATFORM_H
+
+struct rt2x00_platform_data {
+       char *eeprom_file_name;
+       const u8 *mac_address;
+
+       int disable_2ghz;
+       int disable_5ghz;
+};
+
+#endif /* _RT2X00_PLATFORM_H */
diff --git a/target/linux/generic/files-3.18/include/linux/rtl8366.h b/target/linux/generic/files-3.18/include/linux/rtl8366.h
new file mode 100644 (file)
index 0000000..e3ce8f5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Platform data definition for the Realtek RTL8366RB/S ethernet switch driver
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8366_H
+#define _RTL8366_H
+
+#define RTL8366_DRIVER_NAME    "rtl8366"
+#define RTL8366S_DRIVER_NAME   "rtl8366s"
+#define RTL8366RB_DRIVER_NAME  "rtl8366rb"
+
+struct rtl8366_smi;
+
+enum rtl8366_type {
+       RTL8366_TYPE_UNKNOWN,
+       RTL8366_TYPE_S,
+       RTL8366_TYPE_RB,
+};
+
+struct rtl8366_initval {
+       unsigned        reg;
+       u16             val;
+};
+
+struct rtl8366_platform_data {
+       unsigned        gpio_sda;
+       unsigned        gpio_sck;
+       void            (*hw_reset)(struct rtl8366_smi *smi, bool active);
+
+       unsigned        num_initvals;
+       struct rtl8366_initval *initvals;
+};
+
+enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata);
+
+#endif /*  _RTL8366_H */
diff --git a/target/linux/generic/files-3.18/include/linux/rtl8367.h b/target/linux/generic/files-3.18/include/linux/rtl8367.h
new file mode 100644 (file)
index 0000000..855de6a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Platform data definition for the Realtek RTL8367 ethernet switch driver
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8367_H
+#define _RTL8367_H
+
+#define RTL8367_DRIVER_NAME    "rtl8367"
+#define RTL8367B_DRIVER_NAME   "rtl8367b"
+
+enum rtl8367_port_speed {
+       RTL8367_PORT_SPEED_10 = 0,
+       RTL8367_PORT_SPEED_100,
+       RTL8367_PORT_SPEED_1000,
+};
+
+struct rtl8367_port_ability {
+       int force_mode;
+       int nway;
+       int txpause;
+       int rxpause;
+       int link;
+       int duplex;
+       enum rtl8367_port_speed speed;
+};
+
+enum rtl8367_extif_mode {
+       RTL8367_EXTIF_MODE_DISABLED = 0,
+       RTL8367_EXTIF_MODE_RGMII,
+       RTL8367_EXTIF_MODE_MII_MAC,
+       RTL8367_EXTIF_MODE_MII_PHY,
+       RTL8367_EXTIF_MODE_TMII_MAC,
+       RTL8367_EXTIF_MODE_TMII_PHY,
+       RTL8367_EXTIF_MODE_GMII,
+       RTL8367_EXTIF_MODE_RGMII_33V,
+};
+
+struct rtl8367_extif_config {
+       unsigned int txdelay;
+       unsigned int rxdelay;
+       enum rtl8367_extif_mode mode;
+       struct rtl8367_port_ability ability;
+};
+
+struct rtl8367_platform_data {
+       unsigned gpio_sda;
+       unsigned gpio_sck;
+       void (*hw_reset)(bool active);
+
+       struct rtl8367_extif_config *extif0_cfg;
+       struct rtl8367_extif_config *extif1_cfg;
+};
+
+#endif /*  _RTL8367_H */
diff --git a/target/linux/generic/files-3.18/include/linux/switch.h b/target/linux/generic/files-3.18/include/linux/switch.h
new file mode 100644 (file)
index 0000000..4e62384
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * switch.h: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _LINUX_SWITCH_H
+#define _LINUX_SWITCH_H
+
+#include <net/genetlink.h>
+#include <uapi/linux/switch.h>
+
+struct switch_dev;
+struct switch_op;
+struct switch_val;
+struct switch_attr;
+struct switch_attrlist;
+struct switch_led_trigger;
+
+int register_switch(struct switch_dev *dev, struct net_device *netdev);
+void unregister_switch(struct switch_dev *dev);
+
+/**
+ * struct switch_attrlist - attribute list
+ *
+ * @n_attr: number of attributes
+ * @attr: pointer to the attributes array
+ */
+struct switch_attrlist {
+       int n_attr;
+       const struct switch_attr *attr;
+};
+
+enum switch_port_speed {
+       SWITCH_PORT_SPEED_UNKNOWN = 0,
+       SWITCH_PORT_SPEED_10 = 10,
+       SWITCH_PORT_SPEED_100 = 100,
+       SWITCH_PORT_SPEED_1000 = 1000,
+};
+
+struct switch_port_link {
+       bool link;
+       bool duplex;
+       bool aneg;
+       bool tx_flow;
+       bool rx_flow;
+       enum switch_port_speed speed;
+       /* in ethtool adv_t format */
+       u32 eee;
+};
+
+struct switch_port_stats {
+       unsigned long long tx_bytes;
+       unsigned long long rx_bytes;
+};
+
+/**
+ * struct switch_dev_ops - switch driver operations
+ *
+ * @attr_global: global switch attribute list
+ * @attr_port: port attribute list
+ * @attr_vlan: vlan attribute list
+ *
+ * Callbacks:
+ *
+ * @get_vlan_ports: read the port list of a VLAN
+ * @set_vlan_ports: set the port list of a VLAN
+ *
+ * @get_port_pvid: get the primary VLAN ID of a port
+ * @set_port_pvid: set the primary VLAN ID of a port
+ *
+ * @apply_config: apply all changed settings to the switch
+ * @reset_switch: resetting the switch
+ */
+struct switch_dev_ops {
+       struct switch_attrlist attr_global, attr_port, attr_vlan;
+
+       int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
+       int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
+
+       int (*get_port_pvid)(struct switch_dev *dev, int port, int *val);
+       int (*set_port_pvid)(struct switch_dev *dev, int port, int val);
+
+       int (*apply_config)(struct switch_dev *dev);
+       int (*reset_switch)(struct switch_dev *dev);
+
+       int (*get_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*set_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*get_port_stats)(struct switch_dev *dev, int port,
+                             struct switch_port_stats *stats);
+
+       int (*phy_read16)(struct switch_dev *dev, int addr, u8 reg, u16 *value);
+       int (*phy_write16)(struct switch_dev *dev, int addr, u8 reg, u16 value);
+};
+
+struct switch_dev {
+       struct device_node *of_node;
+       const struct switch_dev_ops *ops;
+       /* will be automatically filled */
+       char devname[IFNAMSIZ];
+
+       const char *name;
+       /* NB: either alias or netdev must be set */
+       const char *alias;
+       struct net_device *netdev;
+
+       unsigned int ports;
+       unsigned int vlans;
+       unsigned int cpu_port;
+
+       /* the following fields are internal for swconfig */
+       unsigned int id;
+       struct list_head dev_list;
+       unsigned long def_global, def_port, def_vlan;
+
+       struct mutex sw_mutex;
+       struct switch_port *portbuf;
+       struct switch_portmap *portmap;
+       struct switch_port_link linkbuf;
+
+       char buf[128];
+
+#ifdef CONFIG_SWCONFIG_LEDS
+       struct switch_led_trigger *led_trigger;
+#endif
+};
+
+struct switch_port {
+       u32 id;
+       u32 flags;
+};
+
+struct switch_portmap {
+       u32 virt;
+       const char *s;
+};
+
+struct switch_val {
+       const struct switch_attr *attr;
+       unsigned int port_vlan;
+       unsigned int len;
+       union {
+               const char *s;
+               u32 i;
+               struct switch_port *ports;
+               struct switch_port_link *link;
+       } value;
+};
+
+struct switch_attr {
+       int disabled;
+       int type;
+       const char *name;
+       const char *description;
+
+       int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
+       int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
+
+       /* for driver internal use */
+       int id;
+       int ofs;
+       int max;
+};
+
+int switch_generic_set_link(struct switch_dev *dev, int port,
+                           struct switch_port_link *link);
+
+#endif /* _LINUX_SWITCH_H */
diff --git a/target/linux/generic/files-3.18/include/uapi/linux/switch.h b/target/linux/generic/files-3.18/include/uapi/linux/switch.h
new file mode 100644 (file)
index 0000000..ea44965
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * switch.h: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_LINUX_SWITCH_H
+#define _UAPI_LINUX_SWITCH_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#ifndef __KERNEL__
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#endif
+
+/* main attributes */
+enum {
+       SWITCH_ATTR_UNSPEC,
+       /* global */
+       SWITCH_ATTR_TYPE,
+       /* device */
+       SWITCH_ATTR_ID,
+       SWITCH_ATTR_DEV_NAME,
+       SWITCH_ATTR_ALIAS,
+       SWITCH_ATTR_NAME,
+       SWITCH_ATTR_VLANS,
+       SWITCH_ATTR_PORTS,
+       SWITCH_ATTR_PORTMAP,
+       SWITCH_ATTR_CPU_PORT,
+       /* attributes */
+       SWITCH_ATTR_OP_ID,
+       SWITCH_ATTR_OP_TYPE,
+       SWITCH_ATTR_OP_NAME,
+       SWITCH_ATTR_OP_PORT,
+       SWITCH_ATTR_OP_VLAN,
+       SWITCH_ATTR_OP_VALUE_INT,
+       SWITCH_ATTR_OP_VALUE_STR,
+       SWITCH_ATTR_OP_VALUE_PORTS,
+       SWITCH_ATTR_OP_VALUE_LINK,
+       SWITCH_ATTR_OP_DESCRIPTION,
+       /* port lists */
+       SWITCH_ATTR_PORT,
+       SWITCH_ATTR_MAX
+};
+
+enum {
+       /* port map */
+       SWITCH_PORTMAP_PORTS,
+       SWITCH_PORTMAP_SEGMENT,
+       SWITCH_PORTMAP_VIRT,
+       SWITCH_PORTMAP_MAX
+};
+
+/* commands */
+enum {
+       SWITCH_CMD_UNSPEC,
+       SWITCH_CMD_GET_SWITCH,
+       SWITCH_CMD_NEW_ATTR,
+       SWITCH_CMD_LIST_GLOBAL,
+       SWITCH_CMD_GET_GLOBAL,
+       SWITCH_CMD_SET_GLOBAL,
+       SWITCH_CMD_LIST_PORT,
+       SWITCH_CMD_GET_PORT,
+       SWITCH_CMD_SET_PORT,
+       SWITCH_CMD_LIST_VLAN,
+       SWITCH_CMD_GET_VLAN,
+       SWITCH_CMD_SET_VLAN
+};
+
+/* data types */
+enum switch_val_type {
+       SWITCH_TYPE_UNSPEC,
+       SWITCH_TYPE_INT,
+       SWITCH_TYPE_STRING,
+       SWITCH_TYPE_PORTS,
+       SWITCH_TYPE_LINK,
+       SWITCH_TYPE_NOVAL,
+};
+
+/* port nested attributes */
+enum {
+       SWITCH_PORT_UNSPEC,
+       SWITCH_PORT_ID,
+       SWITCH_PORT_FLAG_TAGGED,
+       SWITCH_PORT_ATTR_MAX
+};
+
+/* link nested attributes */
+enum {
+       SWITCH_LINK_UNSPEC,
+       SWITCH_LINK_FLAG_LINK,
+       SWITCH_LINK_FLAG_DUPLEX,
+       SWITCH_LINK_FLAG_ANEG,
+       SWITCH_LINK_FLAG_TX_FLOW,
+       SWITCH_LINK_FLAG_RX_FLOW,
+       SWITCH_LINK_SPEED,
+       SWITCH_LINK_FLAG_EEE_100BASET,
+       SWITCH_LINK_FLAG_EEE_1000BASET,
+       SWITCH_LINK_ATTR_MAX,
+};
+
+#define SWITCH_ATTR_DEFAULTS_OFFSET    0x1000
+
+
+#endif /* _UAPI_LINUX_SWITCH_H */
diff --git a/target/linux/generic/files-4.14/Documentation/networking/adm6996.txt b/target/linux/generic/files-4.14/Documentation/networking/adm6996.txt
new file mode 100644 (file)
index 0000000..ab59f1d
--- /dev/null
@@ -0,0 +1,110 @@
+------- 
+
+ADM6996FC / ADM6996M switch chip driver
+
+
+1. General information
+
+  This driver supports the FC and M models only. The ADM6996F and L are
+  completely different chips.
+  
+  Support for the FC model is extremely limited at the moment. There is no VLAN
+  support as of yet. The driver will not offer an swconfig interface for the FC
+  chip.
+1.1 VLAN IDs
+
+  It is possible to define 16 different VLANs. Every VLAN has an identifier, its
+  VLAN ID. It is easiest if you use at most VLAN IDs 0-15. In that case, the
+  swconfig based configuration is very straightforward. To define two VLANs with
+  IDs 4 and 5, you can invoke, for example:
+  
+      # swconfig dev ethX vlan 4 set ports '0 1t 2 5t' 
+      # swconfig dev ethX vlan 5 set ports '0t 1t 5t'
+  
+  The swconfig framework will automatically invoke 'port Y set pvid Z' for every
+  port that is an untagged member of VLAN Y, setting its Primary VLAN ID. In
+  this example, ports 0 and 2 would get "pvid 4". The Primary VLAN ID of a port
+  is the VLAN ID associated with untagged packets coming in on that port.
+  
+  But if you wish to use VLAN IDs outside the range 0-15, this automatic
+  behaviour of the swconfig framework becomes a problem. The 16 VLANs that
+  swconfig can configure on the ADM6996 also have a "vid" setting. By default,
+  this is the same as the number of the VLAN entry, to make the simple behaviour
+  above possible. To still support a VLAN with a VLAN ID higher than 15
+  (presumably because you are in a network where such VLAN IDs are already in
+  use), you can change the "vid" setting of the VLAN to anything in the range
+  0-1023. But suppose you did the following:
+  
+      # swconfig dev ethX vlan 0 set vid 998 
+      # swconfig dev ethX vlan 0 set ports '0 2 5t'
+  Now the swconfig framework will issue 'port 0 set pvid 0' and 'port 2 set pvid
+  0'. But the "pvid" should be set to 998, so you are responsible for manually
+  fixing this!
+
+1.2 VLAN filtering
+
+  The switch is configured to apply source port filtering. This means that
+  packets are only accepted when the port the packets came in on is a member of
+  the VLAN the packet should go to.
+
+  Only membership of a VLAN is tested, it does not matter whether it is a tagged
+  or untagged membership.
+
+  For untagged packets, the destination VLAN is the Primary VLAN ID of the
+  incoming port. So if the PVID of a port is 0, but that port is not a member of
+  the VLAN with ID 0, this means that untagged packets on that port are dropped.
+  This can be used as a roundabout way of dropping untagged packets from a port,
+  a mode often referred to as "Admit only tagged packets".
+
+1.3 Reset
+
+  The two supported chip models do not have a sofware-initiated reset. When the
+  driver is initialised, as well as when the 'reset' swconfig option is invoked,
+  the driver will set those registers it knows about and supports to the correct
+  default value. But there are a lot of registers in the chip that the driver
+  does not support. If something changed those registers, invoking 'reset' or
+  performing a warm reboot might still leave the chip in a "broken" state. Only
+  a hardware reset will bring it back in the default state.
+
+2. Technical details on PHYs and the ADM6996
+
+  From the viewpoint of the Linux kernel, it is common that an Ethernet adapter
+  can be seen as a separate MAC entity and a separate PHY entity. The PHY entity
+  can be queried and set through registers accessible via an MDIO bus. A PHY
+  normally has a single address on that bus, in the range 0 through 31.
+
+  The ADM6996 has special-purpose registers in the range of PHYs 0 through 10.
+  Even though all these registers control a single ADM6996 chip, the Linux
+  kernel treats this as 11 separate PHYs.  The driver will bind to these
+  addresses to prevent a different PHY driver from binding and corrupting these
+  registers.
+
+  What Linux sees as the PHY on address 0 is meant for the Ethernet MAC
+  connected to the CPU port of the ADM6996 switch chip (port 5). This is the
+  Ethernet MAC you will use to send and receive data through the switch.
+
+  The PHYs at addresses 16 through 20 map to the PHYs on ports 0 through 4 of
+  the switch chip. These can be accessed with the Generic PHY driver, as the
+  registers have the common layout.
+
+  If a second Ethernet MAC on your board is wired to the port 4 PHY, that MAC
+  needs to bind to PHY address 20 for the port to work correctly.
+
+  The ADM6996 switch driver will reset the ports 0 through 3 on startup and when
+  'reset' is invoked. This could clash with a different PHY driver if the kernel
+  binds a PHY driver to address 16 through 19.
+
+  If Linux binds a PHY on addresses 1 through 10 to an Ethernet MAC, the ADM6996
+  driver will simply always report a connected 100 Mbit/s full-duplex link for
+  that PHY, and provide no other functionality. This is most likely not what you
+  want. So if you see a message in your log
+
+       ethX: PHY overlaps ADM6996, providing fixed PHY yy.
+
+  This is most likely an indication that ethX will not work properly, and your
+  kernel needs to be configured to attach a different PHY to that Ethernet MAC.
+
+  Controlling the mapping between MACs and PHYs is usually done in platform- or
+  board-specific fixup code. The ADM6996 driver has no influence over this.
diff --git a/target/linux/generic/files-4.14/arch/mips/fw/myloader/Makefile b/target/linux/generic/files-4.14/arch/mips/fw/myloader/Makefile
new file mode 100644 (file)
index 0000000..34acfd0
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the Compex's MyLoader support on MIPS architecture
+#
+
+lib-y += myloader.o
diff --git a/target/linux/generic/files-4.14/arch/mips/fw/myloader/myloader.c b/target/linux/generic/files-4.14/arch/mips/fw/myloader/myloader.c
new file mode 100644 (file)
index 0000000..a26f9ad
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Compex's MyLoader specific prom routines
+ *
+ *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/addrspace.h>
+#include <asm/fw/myloader/myloader.h>
+
+#define SYS_PARAMS_ADDR                KSEG1ADDR(0x80000800)
+#define BOARD_PARAMS_ADDR      KSEG1ADDR(0x80000A00)
+#define PART_TABLE_ADDR                KSEG1ADDR(0x80000C00)
+#define BOOT_PARAMS_ADDR       KSEG1ADDR(0x80000E00)
+
+static struct myloader_info myloader_info __initdata;
+static int myloader_found __initdata;
+
+struct myloader_info * __init myloader_get_info(void)
+{
+       struct mylo_system_params *sysp;
+       struct mylo_board_params *boardp;
+       struct mylo_partition_table *parts;
+
+       if (myloader_found)
+               return &myloader_info;
+
+       sysp = (struct mylo_system_params *)(SYS_PARAMS_ADDR);
+       boardp = (struct mylo_board_params *)(BOARD_PARAMS_ADDR);
+       parts = (struct mylo_partition_table *)(PART_TABLE_ADDR);
+
+       printk(KERN_DEBUG "MyLoader: sysp=%08x, boardp=%08x, parts=%08x\n",
+               sysp->magic, boardp->magic, parts->magic);
+
+       /* Check for some magic numbers */
+       if (sysp->magic != MYLO_MAGIC_SYS_PARAMS ||
+           boardp->magic != MYLO_MAGIC_BOARD_PARAMS ||
+           le32_to_cpu(parts->magic) != MYLO_MAGIC_PARTITIONS)
+               return NULL;
+
+       printk(KERN_DEBUG "MyLoader: id=%04x:%04x, sub_id=%04x:%04x\n",
+               sysp->vid, sysp->did, sysp->svid, sysp->sdid);
+
+       myloader_info.vid = sysp->vid;
+       myloader_info.did = sysp->did;
+       myloader_info.svid = sysp->svid;
+       myloader_info.sdid = sysp->sdid;
+
+       memcpy(myloader_info.macs, boardp->addr, sizeof(myloader_info.macs));
+
+       myloader_found = 1;
+
+       return &myloader_info;
+}
diff --git a/target/linux/generic/files-4.14/drivers/leds/ledtrig-netdev.c b/target/linux/generic/files-4.14/drivers/leds/ledtrig-netdev.c
new file mode 100644 (file)
index 0000000..8d32490
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * LED Kernel Netdev Trigger
+ *
+ * Toggles the LED to reflect the link and traffic state of a named net device
+ *
+ * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
+ *
+ * Derived from ledtrig-timer.c which is:
+ *  Copyright 2005-2006 Openedhand Ltd.
+ *  Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+
+#include "leds.h"
+
+/*
+ * Configurable sysfs attributes:
+ *
+ * device_name - network device name to monitor
+ *
+ * interval - duration of LED blink, in milliseconds
+ *
+ * mode - either "none" (LED is off) or a space separated list of one or more of:
+ *   link: LED's normal state reflects whether the link is up (has carrier) or not
+ *   tx:   LED blinks on transmitted data
+ *   rx:   LED blinks on receive data
+ *
+ * Some suggestions:
+ *
+ *  Simple link status LED:
+ *  $ echo netdev >someled/trigger
+ *  $ echo eth0 >someled/device_name
+ *  $ echo link >someled/mode
+ *
+ *  Ethernet-style link/activity LED:
+ *  $ echo netdev >someled/trigger
+ *  $ echo eth0 >someled/device_name
+ *  $ echo "link tx rx" >someled/mode
+ *
+ *  Modem-style tx/rx LEDs:
+ *  $ echo netdev >led1/trigger
+ *  $ echo ppp0 >led1/device_name
+ *  $ echo tx >led1/mode
+ *  $ echo netdev >led2/trigger
+ *  $ echo ppp0 >led2/device_name
+ *  $ echo rx >led2/mode
+ *
+ */
+
+#define MODE_LINK 1
+#define MODE_TX   2
+#define MODE_RX   4
+
+struct led_netdev_data {
+       spinlock_t lock;
+
+       struct delayed_work work;
+       struct notifier_block notifier;
+
+       struct led_classdev *led_cdev;
+       struct net_device *net_dev;
+
+       char device_name[IFNAMSIZ];
+       unsigned interval;
+       unsigned mode;
+       unsigned link_up;
+       unsigned last_activity;
+};
+
+static void set_baseline_state(struct led_netdev_data *trigger_data)
+{
+       if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
+               led_set_brightness(trigger_data->led_cdev, LED_FULL);
+       else
+               led_set_brightness(trigger_data->led_cdev, LED_OFF);
+
+       if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
+               schedule_delayed_work(&trigger_data->work, trigger_data->interval);
+}
+
+static ssize_t led_device_name_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+       sprintf(buf, "%s\n", trigger_data->device_name);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf) + 1;
+}
+
+static ssize_t led_device_name_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       if (size >= IFNAMSIZ)
+               return -EINVAL;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+
+       strcpy(trigger_data->device_name, buf);
+       if (size > 0 && trigger_data->device_name[size-1] == '\n')
+               trigger_data->device_name[size-1] = 0;
+       trigger_data->link_up = 0;
+       trigger_data->last_activity = 0;
+
+       if (trigger_data->device_name[0] != 0) {
+               /* check for existing device to update from */
+               trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
+               if (trigger_data->net_dev != NULL)
+                       trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
+       }
+
+       set_baseline_state(trigger_data);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
+
+static ssize_t led_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+
+       if (trigger_data->mode == 0) {
+               strcpy(buf, "none\n");
+       } else {
+               if (trigger_data->mode & MODE_LINK)
+                       strcat(buf, "link ");
+               if (trigger_data->mode & MODE_TX)
+                       strcat(buf, "tx ");
+               if (trigger_data->mode & MODE_RX)
+                       strcat(buf, "rx ");
+               strcat(buf, "\n");
+       }
+
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf)+1;
+}
+
+static ssize_t led_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+       char copybuf[128];
+       int new_mode = -1;
+       char *p, *token;
+
+       /* take a copy since we don't want to trash the inbound buffer when using strsep */
+       strncpy(copybuf, buf, sizeof(copybuf));
+       copybuf[sizeof(copybuf) - 1] = 0;
+       p = copybuf;
+
+       while ((token = strsep(&p, " \t\n")) != NULL) {
+               if (!*token)
+                       continue;
+
+               if (new_mode == -1)
+                       new_mode = 0;
+
+               if (!strcmp(token, "none"))
+                       new_mode = 0;
+               else if (!strcmp(token, "tx"))
+                       new_mode |= MODE_TX;
+               else if (!strcmp(token, "rx"))
+                       new_mode |= MODE_RX;
+               else if (!strcmp(token, "link"))
+                       new_mode |= MODE_LINK;
+               else
+                       return -EINVAL;
+       }
+
+       if (new_mode == -1)
+               return -EINVAL;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+       trigger_data->mode = new_mode;
+       set_baseline_state(trigger_data);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
+
+static ssize_t led_interval_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+       sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf) + 1;
+}
+
+static ssize_t led_interval_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+       int ret = -EINVAL;
+       char *after;
+       unsigned long value = simple_strtoul(buf, &after, 10);
+       size_t count = after - buf;
+
+       if (isspace(*after))
+               count++;
+
+       /* impose some basic bounds on the timer interval */
+       if (count == size && value >= 5 && value <= 10000) {
+               cancel_delayed_work_sync(&trigger_data->work);
+
+               spin_lock_bh(&trigger_data->lock);
+               trigger_data->interval = msecs_to_jiffies(value);
+               set_baseline_state(trigger_data); /* resets timer */
+               spin_unlock_bh(&trigger_data->lock);
+
+               ret = count;
+       }
+
+       return ret;
+}
+
+static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
+
+static int netdev_trig_notify(struct notifier_block *nb,
+                             unsigned long evt,
+                             void *dv)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
+       struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
+
+       if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER && evt != NETDEV_CHANGENAME)
+               return NOTIFY_DONE;
+
+       if (strcmp(dev->name, trigger_data->device_name))
+               return NOTIFY_DONE;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+
+       if (evt == NETDEV_REGISTER || evt == NETDEV_CHANGENAME) {
+               if (trigger_data->net_dev != NULL)
+                       dev_put(trigger_data->net_dev);
+
+               dev_hold(dev);
+               trigger_data->net_dev = dev;
+               trigger_data->link_up = 0;
+               goto done;
+       }
+
+       if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
+               dev_put(trigger_data->net_dev);
+               trigger_data->net_dev = NULL;
+               goto done;
+       }
+
+       /* UP / DOWN / CHANGE */
+
+       trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
+       set_baseline_state(trigger_data);
+
+done:
+       spin_unlock_bh(&trigger_data->lock);
+       return NOTIFY_DONE;
+}
+
+/* here's the real work! */
+static void netdev_trig_work(struct work_struct *work)
+{
+       struct led_netdev_data *trigger_data = container_of(work, struct led_netdev_data, work.work);
+       struct rtnl_link_stats64 *dev_stats;
+       unsigned new_activity;
+       struct rtnl_link_stats64 temp;
+
+       if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
+               /* we don't need to do timer work, just reflect link state. */
+               led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
+               return;
+       }
+
+       dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
+       new_activity =
+               ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
+               ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
+
+       if (trigger_data->mode & MODE_LINK) {
+               /* base state is ON (link present) */
+               /* if there's no link, we don't get this far and the LED is off */
+
+               /* OFF -> ON always */
+               /* ON -> OFF on activity */
+               if (trigger_data->led_cdev->brightness == LED_OFF) {
+                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
+               } else if (trigger_data->last_activity != new_activity) {
+                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
+               }
+       } else {
+               /* base state is OFF */
+               /* ON -> OFF always */
+               /* OFF -> ON on activity */
+               if (trigger_data->led_cdev->brightness == LED_FULL) {
+                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
+               } else if (trigger_data->last_activity != new_activity) {
+                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
+               }
+       }
+
+       trigger_data->last_activity = new_activity;
+       schedule_delayed_work(&trigger_data->work, trigger_data->interval);
+}
+
+static void netdev_trig_activate(struct led_classdev *led_cdev)
+{
+       struct led_netdev_data *trigger_data;
+       int rc;
+
+       trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
+       if (!trigger_data)
+               return;
+
+       spin_lock_init(&trigger_data->lock);
+
+       trigger_data->notifier.notifier_call = netdev_trig_notify;
+       trigger_data->notifier.priority = 10;
+
+       INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
+
+       trigger_data->led_cdev = led_cdev;
+       trigger_data->net_dev = NULL;
+       trigger_data->device_name[0] = 0;
+
+       trigger_data->mode = 0;
+       trigger_data->interval = msecs_to_jiffies(50);
+       trigger_data->link_up = 0;
+       trigger_data->last_activity = 0;
+
+       led_cdev->trigger_data = trigger_data;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
+       if (rc)
+               goto err_out;
+       rc = device_create_file(led_cdev->dev, &dev_attr_mode);
+       if (rc)
+               goto err_out_device_name;
+       rc = device_create_file(led_cdev->dev, &dev_attr_interval);
+       if (rc)
+               goto err_out_mode;
+
+       register_netdevice_notifier(&trigger_data->notifier);
+       return;
+
+err_out_mode:
+       device_remove_file(led_cdev->dev, &dev_attr_mode);
+err_out_device_name:
+       device_remove_file(led_cdev->dev, &dev_attr_device_name);
+err_out:
+       led_cdev->trigger_data = NULL;
+       kfree(trigger_data);
+}
+
+static void netdev_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       if (trigger_data) {
+               unregister_netdevice_notifier(&trigger_data->notifier);
+
+               device_remove_file(led_cdev->dev, &dev_attr_device_name);
+               device_remove_file(led_cdev->dev, &dev_attr_mode);
+               device_remove_file(led_cdev->dev, &dev_attr_interval);
+
+               cancel_delayed_work_sync(&trigger_data->work);
+
+               spin_lock_bh(&trigger_data->lock);
+
+               if (trigger_data->net_dev) {
+                       dev_put(trigger_data->net_dev);
+                       trigger_data->net_dev = NULL;
+               }
+
+               spin_unlock_bh(&trigger_data->lock);
+
+               kfree(trigger_data);
+       }
+}
+
+static struct led_trigger netdev_led_trigger = {
+       .name     = "netdev",
+       .activate = netdev_trig_activate,
+       .deactivate = netdev_trig_deactivate,
+};
+
+static int __init netdev_trig_init(void)
+{
+       return led_trigger_register(&netdev_led_trigger);
+}
+
+static void __exit netdev_trig_exit(void)
+{
+       led_trigger_unregister(&netdev_led_trigger);
+}
+
+module_init(netdev_trig_init);
+module_exit(netdev_trig_exit);
+
+MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
+MODULE_DESCRIPTION("Netdev LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.14/drivers/misc/owl-loader.c b/target/linux/generic/files-4.14/drivers/misc/owl-loader.c
new file mode 100644 (file)
index 0000000..f11cb2b
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Initialize Owl Emulation Devices
+ *
+ * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
+ * need to be able to initialize the PCIe wifi device. Normally, this is done
+ * during the early stages of booting linux, because the necessary init code
+ * is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
+ * However,this isn't possible for devices which have the init code for the
+ * Atheros chip stored on NAND. Hence, this module can be used to initialze
+ * the chip when the user-space is ready to extract the init code.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/completion.h>
+#include <linux/etherdevice.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+
+struct owl_ctx {
+       struct completion eeprom_load;
+};
+
+#define EEPROM_FILENAME_LEN 100
+
+#define AR5416_EEPROM_MAGIC 0xa55a
+
+static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
+                          size_t cal_len)
+{
+       void __iomem *mem;
+       const void *cal_end = (void *)cal_data + cal_len;
+       const struct {
+               __be16 reg;
+               __be16 low_val;
+               __be16 high_val;
+       } __packed *data;
+       u16 cmd;
+       u32 bar0;
+       bool swap_needed = false;
+
+       if (*cal_data != AR5416_EEPROM_MAGIC) {
+               if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
+                       dev_err(&pdev->dev, "invalid calibration data\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(&pdev->dev, "calibration data needs swapping\n");
+               swap_needed = true;
+       }
+
+       dev_info(&pdev->dev, "fixup device configuration\n");
+
+       mem = pcim_iomap(pdev, 0, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "ioremap error\n");
+               return -EINVAL;
+       }
+
+       pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
+       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
+                              pci_resource_start(pdev, 0));
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+       /* set pointer to first reg address */
+       for (data = (const void *) (cal_data + 3);
+            (const void *) data <= cal_end && data->reg != cpu_to_be16(~0);
+            data++) {
+               u32 val;
+               u16 reg;
+
+               reg = data->reg;
+               val = data->low_val;
+               val |= data->high_val << 16;
+
+               if (swap_needed) {
+                       reg = swab16(reg);
+                       val = swahb32(val);
+               }
+
+#ifdef CONFIG_LANTIQ
+               val = swab32(val);
+#endif
+
+               __raw_writel(val, mem + reg);
+               udelay(100);
+       }
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
+       pcim_iounmap(pdev, mem);
+
+       pci_disable_device(pdev);
+
+       return 0;
+}
+
+static void owl_fw_cb(const struct firmware *fw, void *context)
+{
+       struct pci_dev *pdev = (struct pci_dev *) context;
+       struct owl_ctx *ctx = (struct owl_ctx *) pci_get_drvdata(pdev);
+       struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct pci_bus *bus;
+
+       complete(&ctx->eeprom_load);
+
+       if (!fw) {
+               dev_err(&pdev->dev, "no eeprom data received.\n");
+               goto release;
+       }
+
+       /* also note that we are doing *u16 operations on the file */
+       if (fw->size > sizeof(pdata->eeprom_data) || fw->size < 0x200 ||
+           (fw->size & 1) == 1) {
+               dev_err(&pdev->dev, "eeprom file has an invalid size.\n");
+               goto release;
+       }
+
+       if (pdata) {
+               memcpy(pdata->eeprom_data, fw->data, fw->size);
+
+               /*
+                * eeprom has been successfully loaded - pass the data to ath9k
+                * but remove the eeprom_name, so it doesn't try to load it too.
+                */
+               pdata->eeprom_name = NULL;
+       }
+
+       if (ath9k_pci_fixup(pdev, (const u16 *) fw->data, fw->size))
+               goto release;
+
+       pci_lock_rescan_remove();
+       bus = pdev->bus;
+       pci_stop_and_remove_bus_device(pdev);
+       /*
+        * the device should come back with the proper
+        * ProductId. But we have to initiate a rescan.
+        */
+       pci_rescan_bus(bus);
+       pci_unlock_rescan_remove();
+
+release:
+       release_firmware(fw);
+}
+
+static const char *owl_get_eeprom_name(struct pci_dev *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct ath9k_platform_data *pdata;
+       char *eeprom_name;
+
+       /* try the existing platform data first */
+       pdata = dev_get_platdata(dev);
+       if (pdata && pdata->eeprom_name)
+               return pdata->eeprom_name;
+
+       dev_dbg(dev, "using auto-generated eeprom filename\n");
+
+       eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
+       if (!eeprom_name)
+               return NULL;
+
+       /* this should match the pattern used in ath9k/init.c */
+       scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
+                 dev_name(dev));
+
+       return eeprom_name;
+}
+
+static int owl_probe(struct pci_dev *pdev,
+                   const struct pci_device_id *id)
+{
+       struct owl_ctx *ctx;
+       const char *eeprom_name;
+       int err = 0;
+
+       if (pcim_enable_device(pdev))
+               return -EIO;
+
+       pcim_pin_device(pdev);
+
+       eeprom_name = owl_get_eeprom_name(pdev);
+       if (!eeprom_name) {
+               dev_err(&pdev->dev, "no eeprom filename found.\n");
+               return -ENODEV;
+       }
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               dev_err(&pdev->dev, "failed to alloc device context.\n");
+               return -ENOMEM;
+       }
+       init_completion(&ctx->eeprom_load);
+
+       pci_set_drvdata(pdev, ctx);
+       err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
+                                     &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
+       if (err) {
+               dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
+               kfree(ctx);
+       }
+       return err;
+}
+
+static void owl_remove(struct pci_dev *pdev)
+{
+       struct owl_ctx *ctx = pci_get_drvdata(pdev);
+
+       if (ctx) {
+               wait_for_completion(&ctx->eeprom_load);
+               pci_set_drvdata(pdev, NULL);
+               kfree(ctx);
+       }
+}
+
+static const struct pci_device_id owl_pci_table[] = {
+       { PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
+       { PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
+       { },
+};
+MODULE_DEVICE_TABLE(pci, owl_pci_table);
+
+static struct pci_driver owl_driver = {
+       .name           = "owl-loader",
+       .id_table       = owl_pci_table,
+       .probe          = owl_probe,
+       .remove         = owl_remove,
+};
+module_pci_driver(owl_driver);
+MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
+MODULE_DESCRIPTION("Initializes Atheros' Owl Emulation devices");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/Kconfig
new file mode 100644 (file)
index 0000000..81ece43
--- /dev/null
@@ -0,0 +1,76 @@
+config MTD_SPLIT
+       def_bool n
+       help
+         Generic MTD split support.
+
+config MTD_SPLIT_SUPPORT
+       def_bool MTD = y
+
+comment "Rootfs partition parsers"
+
+config MTD_SPLIT_SQUASHFS_ROOT
+       bool "Squashfs based root partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+       default n
+       help
+         This provides a parsing function which allows to detect the
+         offset and size of the unused portion of a rootfs partition
+         containing a squashfs.
+
+comment "Firmware partition parsers"
+
+config MTD_SPLIT_SEAMA_FW
+       bool "Seama firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_WRGG_FW
+       bool "WRGG firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_UIMAGE_FW
+       bool "uImage based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_FIT_FW
+       bool "FIT based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_LZMA_FW
+       bool "LZMA compressed kernel based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_TPLINK_FW
+       bool "TP-Link firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_TRX_FW
+       bool "TRX image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_BRNIMAGE_FW
+       bool "brnImage (brnboot image) firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_EVA_FW
+       bool "EVA image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_MINOR_FW
+       bool "Mikrotik NOR image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_JIMAGE_FW
+       bool "JBOOT Image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/Makefile
new file mode 100644 (file)
index 0000000..206e754
--- /dev/null
@@ -0,0 +1,13 @@
+obj-$(CONFIG_MTD_SPLIT)                += mtdsplit.o
+obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
+obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o
+obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o
+obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o
+obj-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o
+obj-$(CONFIG_MTD_SPLIT_TPLINK_FW) += mtdsplit_tplink.o
+obj-$(CONFIG_MTD_SPLIT_TRX_FW) += mtdsplit_trx.o
+obj-$(CONFIG_MTD_SPLIT_BRNIMAGE_FW) += mtdsplit_brnimage.o
+obj-$(CONFIG_MTD_SPLIT_EVA_FW) += mtdsplit_eva.o
+obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o
+obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
+obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit.c
new file mode 100644 (file)
index 0000000..b2e51dc
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    "mtdsplit: " fmt
+
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/magic.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define UBI_EC_MAGIC                   0x55424923      /* UBI# */
+
+struct squashfs_super_block {
+       __le32 s_magic;
+       __le32 pad0[9];
+       __le64 bytes_used;
+};
+
+int mtd_get_squashfs_len(struct mtd_info *master,
+                        size_t offset,
+                        size_t *squashfs_len)
+{
+       struct squashfs_super_block sb;
+       size_t retlen;
+       int err;
+
+       err = mtd_read(master, offset, sizeof(sb), &retlen, (void *)&sb);
+       if (err || (retlen != sizeof(sb))) {
+               pr_alert("error occured while reading from \"%s\"\n",
+                        master->name);
+               return -EIO;
+       }
+
+       if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC) {
+               pr_alert("no squashfs found in \"%s\"\n", master->name);
+               return -EINVAL;
+       }
+
+       retlen = le64_to_cpu(sb.bytes_used);
+       if (retlen <= 0) {
+               pr_alert("squashfs is empty in \"%s\"\n", master->name);
+               return -ENODEV;
+       }
+
+       if (offset + retlen > master->size) {
+               pr_alert("squashfs has invalid size in \"%s\"\n",
+                        master->name);
+               return -EINVAL;
+       }
+
+       *squashfs_len = retlen;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtd_get_squashfs_len);
+
+static ssize_t mtd_next_eb(struct mtd_info *mtd, size_t offset)
+{
+       return mtd_rounddown_to_eb(offset, mtd) + mtd->erasesize;
+}
+
+int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                          enum mtdsplit_part_type *type)
+{
+       u32 magic;
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
+                      (unsigned char *) &magic);
+       if (ret)
+               return ret;
+
+       if (retlen != sizeof(magic))
+               return -EIO;
+
+       if (le32_to_cpu(magic) == SQUASHFS_MAGIC) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_SQUASHFS;
+               return 0;
+       } else if (magic == 0x19852003) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_JFFS2;
+               return 0;
+       } else if (be32_to_cpu(magic) == UBI_EC_MAGIC) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_UBI;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtd_check_rootfs_magic);
+
+int mtd_find_rootfs_from(struct mtd_info *mtd,
+                        size_t from,
+                        size_t limit,
+                        size_t *ret_offset,
+                        enum mtdsplit_part_type *type)
+{
+       size_t offset;
+       int err;
+
+       for (offset = from; offset < limit;
+            offset = mtd_next_eb(mtd, offset)) {
+               err = mtd_check_rootfs_magic(mtd, offset, type);
+               if (err)
+                       continue;
+
+               *ret_offset = offset;
+               return 0;
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(mtd_find_rootfs_from);
+
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit.h b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit.h
new file mode 100644 (file)
index 0000000..71d62a8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MTDSPLIT_H
+#define _MTDSPLIT_H
+
+#define KERNEL_PART_NAME       "kernel"
+#define ROOTFS_PART_NAME       "rootfs"
+#define UBI_PART_NAME          "ubi"
+
+#define ROOTFS_SPLIT_NAME      "rootfs_data"
+
+enum mtdsplit_part_type {
+       MTDSPLIT_PART_TYPE_UNK = 0,
+       MTDSPLIT_PART_TYPE_SQUASHFS,
+       MTDSPLIT_PART_TYPE_JFFS2,
+       MTDSPLIT_PART_TYPE_UBI,
+};
+
+#ifdef CONFIG_MTD_SPLIT
+int mtd_get_squashfs_len(struct mtd_info *master,
+                        size_t offset,
+                        size_t *squashfs_len);
+
+int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                          enum mtdsplit_part_type *type);
+
+int mtd_find_rootfs_from(struct mtd_info *mtd,
+                        size_t from,
+                        size_t limit,
+                        size_t *ret_offset,
+                        enum mtdsplit_part_type *type);
+
+#else
+static inline int mtd_get_squashfs_len(struct mtd_info *master,
+                                      size_t offset,
+                                      size_t *squashfs_len)
+{
+       return -ENODEV;
+}
+
+static inline int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                                        enum mtdsplit_part_type *type)
+{
+       return -EINVAL;
+}
+
+static inline int mtd_find_rootfs_from(struct mtd_info *mtd,
+                                      size_t from,
+                                      size_t limit,
+                                      size_t *ret_offset,
+                                      enum mtdsplit_part_type *type)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_MTD_SPLIT */
+
+#endif /* _MTDSPLIT_H */
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_brnimage.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_brnimage.c
new file mode 100644 (file)
index 0000000..3f2d796
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define BRNIMAGE_NR_PARTS      2
+
+#define BRNIMAGE_ALIGN_BYTES   0x400
+#define BRNIMAGE_FOOTER_SIZE   12
+
+#define BRNIMAGE_MIN_OVERHEAD  (BRNIMAGE_FOOTER_SIZE)
+#define BRNIMAGE_MAX_OVERHEAD  (BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
+
+static int mtdsplit_parse_brnimage(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       uint32_t buf;
+       unsigned long rootfs_offset, rootfs_size, kernel_size;
+       size_t len;
+       int ret = 0;
+
+       for (rootfs_offset = 0; rootfs_offset < master->size;
+            rootfs_offset += BRNIMAGE_ALIGN_BYTES) {
+               ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+               if (!ret)
+                       break;
+       }
+
+       if (ret)
+               return ret;
+
+       if (rootfs_offset >= master->size)
+               return -EINVAL;
+
+       ret = mtd_read(master, rootfs_offset - BRNIMAGE_FOOTER_SIZE, 4, &len,
+                       (void *)&buf);
+       if (ret)
+               return ret;
+
+       if (len != 4)
+               return -EIO;
+
+       kernel_size = le32_to_cpu(buf);
+
+       if (kernel_size > (rootfs_offset - BRNIMAGE_MIN_OVERHEAD))
+               return -EINVAL;
+
+       if (kernel_size < (rootfs_offset - BRNIMAGE_MAX_OVERHEAD))
+               return -EINVAL;
+
+       /*
+        * The footer must be untouched as it contains the checksum of the
+        * original brnImage (kernel + squashfs)!
+        */
+       rootfs_size = master->size - rootfs_offset - BRNIMAGE_FOOTER_SIZE;
+
+       parts = kzalloc(BRNIMAGE_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return BRNIMAGE_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_brnimage_parser = {
+       .owner = THIS_MODULE,
+       .name = "brnimage-fw",
+       .parse_fn = mtdsplit_parse_brnimage,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_brnimage_init(void)
+{
+       register_mtd_parser(&mtdsplit_brnimage_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_brnimage_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_eva.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_eva.c
new file mode 100644 (file)
index 0000000..746944e
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define EVA_NR_PARTS           2
+#define EVA_MAGIC              0xfeed1281
+#define EVA_FOOTER_SIZE                0x18
+#define EVA_DUMMY_SQUASHFS_SIZE        0x100
+
+struct eva_image_header {
+       uint32_t        magic;
+       uint32_t        size;
+};
+
+static int mtdsplit_parse_eva(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       struct eva_image_header hdr;
+       size_t retlen;
+       unsigned long kernel_size, rootfs_offset;
+       int err;
+
+       err = mtd_read(master, 0, sizeof(hdr), &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != sizeof(hdr))
+               return -EIO;
+
+       if (le32_to_cpu(hdr.magic) != EVA_MAGIC)
+               return -EINVAL;
+
+       kernel_size = le32_to_cpu(hdr.size) + EVA_FOOTER_SIZE;
+
+       /* rootfs starts at the next 0x10000 boundary: */
+       rootfs_offset = round_up(kernel_size, 0x10000);
+
+       /* skip the dummy EVA squashfs partition (with wrong endianness): */
+       rootfs_offset += EVA_DUMMY_SQUASHFS_SIZE;
+
+       if (rootfs_offset >= master->size)
+               return -EINVAL;
+
+       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(EVA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return EVA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_eva_parser = {
+       .owner = THIS_MODULE,
+       .name = "eva-fw",
+       .parse_fn = mtdsplit_parse_eva,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_eva_init(void)
+{
+       register_mtd_parser(&mtdsplit_eva_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_eva_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_fit.c
new file mode 100644 (file)
index 0000000..f356adc
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 The Linux Foundation
+ * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/types.h>
+#include <linux/byteorder/generic.h>
+#include <linux/slab.h>
+#include <linux/of_fdt.h>
+
+#include "mtdsplit.h"
+
+struct fdt_header {
+       uint32_t magic;                  /* magic word FDT_MAGIC */
+       uint32_t totalsize;              /* total size of DT block */
+       uint32_t off_dt_struct;          /* offset to structure */
+       uint32_t off_dt_strings;         /* offset to strings */
+       uint32_t off_mem_rsvmap;         /* offset to memory reserve map */
+       uint32_t version;                /* format version */
+       uint32_t last_comp_version;      /* last compatible version */
+
+       /* version 2 fields below */
+       uint32_t boot_cpuid_phys;        /* Which physical CPU id we're
+                                           booting on */
+       /* version 3 fields below */
+       uint32_t size_dt_strings;        /* size of the strings block */
+
+       /* version 17 fields below */
+       uint32_t size_dt_struct;         /* size of the structure block */
+};
+
+static int
+mtdsplit_fit_parse(struct mtd_info *mtd,
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data)
+{
+       struct fdt_header hdr;
+       size_t hdr_len, retlen;
+       size_t offset;
+       size_t fit_offset, fit_size;
+       size_t rootfs_offset, rootfs_size;
+       struct mtd_partition *parts;
+       int ret;
+
+       hdr_len = sizeof(struct fdt_header);
+
+       /* Parse the MTD device & search for the FIT image location */
+       for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
+               ret = mtd_read(mtd, 0, hdr_len, &retlen, (void*) &hdr);
+               if (ret) {
+                       pr_err("read error in \"%s\" at offset 0x%llx\n",
+                              mtd->name, (unsigned long long) offset);
+                       return ret;
+               }
+
+               if (retlen != hdr_len) {
+                       pr_err("short read in \"%s\"\n", mtd->name);
+                       return -EIO;
+               }
+
+               /* Check the magic - see if this is a FIT image */
+               if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
+                       pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
+                                mtd->name, (unsigned long long) offset);
+                       continue;
+               }
+
+               /* We found a FIT image. Let's keep going */
+               break;
+       }
+
+       fit_offset = offset;
+       fit_size = be32_to_cpu(hdr.totalsize);
+
+       if (fit_size == 0) {
+               pr_err("FIT image in \"%s\" at offset %llx has null size\n",
+                      mtd->name, (unsigned long long) fit_offset);
+               return -ENODEV;
+       }
+
+       /* Search for the rootfs partition after the FIT image */
+       ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
+                                  &rootfs_offset, NULL);
+       if (ret) {
+               pr_info("no rootfs found after FIT image in \"%s\"\n",
+                       mtd->name);
+               return ret;
+       }
+
+       rootfs_size = mtd->size - rootfs_offset;
+
+       parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = fit_offset;
+       parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return 2;
+}
+
+static struct mtd_part_parser uimage_parser = {
+       .owner = THIS_MODULE,
+       .name = "fit-fw",
+       .parse_fn = mtdsplit_fit_parse,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_fit_init(void)
+{
+       register_mtd_parser(&uimage_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_fit_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_jimage.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_jimage.c
new file mode 100644 (file)
index 0000000..51544a7
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ *  Copyright (C) 2018 PaweÅ‚ Dembicki <paweldembicki@gmail.com> 
+ *
+ *  Based on: mtdsplit_uimage.c
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE )
+
+#define STAG_SIZE 16
+#define STAG_ID 0x04
+#define STAG_MAGIC 0x2B24
+
+#define SCH2_SIZE 40
+#define SCH2_MAGIC 0x2124
+#define SCH2_VER 0x02
+
+/*
+ * Jboot image header,
+ * all data in little endian.
+ */
+
+struct jimage_header           //stag + sch2 jboot joined headers
+{
+       uint8_t stag_cmark;             // in factory 0xFF , in sysupgrade must be the same as stag_id
+       uint8_t stag_id;                // 0x04
+       uint16_t stag_magic;            //magic 0x2B24
+       uint32_t stag_time_stamp;       // timestamp calculated in jboot way
+       uint32_t stag_image_length;     // lentgh of kernel + sch2 header
+       uint16_t stag_image_checksum;   // negated jboot_checksum of sch2 + kernel
+       uint16_t stag_tag_checksum;     // negated jboot_checksum of stag header data
+       uint16_t sch2_magic;            // magic 0x2124
+       uint8_t sch2_cp_type;   // 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma
+       uint8_t sch2_version;   // 0x02 for sch2
+       uint32_t sch2_ram_addr; // ram entry address
+       uint32_t sch2_image_len;        // kernel image length
+       uint32_t sch2_image_crc32;      // kernel image crc
+       uint32_t sch2_start_addr;       // ram start address
+       uint32_t sch2_rootfs_addr;      // rootfs flash address
+       uint32_t sch2_rootfs_len;       // rootfls length
+       uint32_t sch2_rootfs_crc32;     // rootfs crc32
+       uint32_t sch2_header_crc32;     // sch2 header crc32, durring calculation this area is replaced by zero
+       uint16_t sch2_header_length;    // sch2 header length: 0x28
+       uint16_t sch2_cmd_line_length;  // cmd line length, known zeros
+};
+
+static int
+read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
+                  size_t header_len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts
+ *
+ * @find_header: function to call for a block of data that will return offset
+ *      of a valid jImage header if found
+ */
+static int __mtdsplit_parse_jimage(struct mtd_info *master,
+                                  const struct mtd_partition **pparts,
+                                  struct mtd_part_parser_data *data,
+                                  ssize_t (*find_header)(u_char *buf, size_t len))
+{
+       struct mtd_partition *parts;
+       u_char *buf;
+       int nr_parts;
+       size_t offset;
+       size_t jimage_offset;
+       size_t jimage_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int jimage_part, rf_part;
+       int ret;
+       enum mtdsplit_part_type type;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       buf = vmalloc(MAX_HEADER_LEN);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_free_parts;
+       }
+
+       /* find jImage on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               struct jimage_header *header;
+
+               jimage_size = 0;
+
+               ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN);
+               if (ret)
+                       continue;
+
+               ret = find_header(buf, MAX_HEADER_LEN);
+               if (ret < 0) {
+                       pr_debug("no valid jImage found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+               header = (struct jimage_header *)(buf + ret);
+
+               jimage_size = sizeof(*header) + header->sch2_image_len + ret;
+               if ((offset + jimage_size) > master->size) {
+                       pr_debug("jImage exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (jimage_size == 0) {
+               pr_debug("no jImage found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       jimage_offset = offset;
+
+       if (jimage_offset == 0) {
+               jimage_part = 0;
+               rf_part = 1;
+
+               /* find the roots after the jImage */
+               ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size,
+                                          master->size, &rootfs_offset, &type);
+               if (ret) {
+                       pr_debug("no rootfs after jImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_size = master->size - rootfs_offset;
+               jimage_size = rootfs_offset - jimage_offset;
+       } else {
+               rf_part = 0;
+               jimage_part = 1;
+
+               /* check rootfs presence at offset 0 */
+               ret = mtd_check_rootfs_magic(master, 0, &type);
+               if (ret) {
+                       pr_debug("no rootfs before jImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_offset = 0;
+               rootfs_size = jimage_offset;
+       }
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       parts[jimage_part].name = KERNEL_PART_NAME;
+       parts[jimage_part].offset = jimage_offset;
+       parts[jimage_part].size = jimage_size;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[rf_part].name = UBI_PART_NAME;
+       else
+               parts[rf_part].name = ROOTFS_PART_NAME;
+       parts[rf_part].offset = rootfs_offset;
+       parts[rf_part].size = rootfs_size;
+
+       vfree(buf);
+
+       *pparts = parts;
+       return nr_parts;
+
+err_free_buf:
+       vfree(buf);
+
+err_free_parts:
+       kfree(parts);
+       return ret;
+}
+
+static ssize_t jimage_verify_default(u_char *buf, size_t len)
+{
+       struct jimage_header *header = (struct jimage_header *)buf;
+
+       /* default sanity checks */
+       if (header->stag_magic != STAG_MAGIC) {
+               pr_debug("invalid jImage stag header magic: %04x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->sch2_magic != SCH2_MAGIC) {
+               pr_debug("invalid jImage sch2 header magic: %04x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->stag_cmark != header->stag_id) {
+               pr_debug("invalid jImage stag header cmark: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->stag_id != STAG_ID) {
+               pr_debug("invalid jImage stag header id: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->sch2_version != SCH2_VER) {
+               pr_debug("invalid jImage sch2 header version: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_jimage_parse_generic(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_jimage(master, pparts, data,
+                                     jimage_verify_default);
+}
+
+static struct mtd_part_parser jimage_generic_parser = {
+       .owner = THIS_MODULE,
+       .name = "jimage-fw",
+       .parse_fn = mtdsplit_jimage_parse_generic,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_jimage_init(void)
+{
+       register_mtd_parser(&jimage_generic_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_jimage_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_lzma.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_lzma.c
new file mode 100644 (file)
index 0000000..b7f044a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/unaligned.h>
+
+#include "mtdsplit.h"
+
+#define LZMA_NR_PARTS          2
+#define LZMA_PROPERTIES_SIZE   5
+
+struct lzma_header {
+       u8 props[LZMA_PROPERTIES_SIZE];
+       u8 size_low[4];
+       u8 size_high[4];
+};
+
+static int mtdsplit_parse_lzma(struct mtd_info *master,
+                              const struct mtd_partition **pparts,
+                              struct mtd_part_parser_data *data)
+{
+       struct lzma_header hdr;
+       size_t hdr_len, retlen;
+       size_t rootfs_offset;
+       u32 t;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* verify LZMA properties */
+       if (hdr.props[0] >= (9 * 5 * 5))
+               return -EINVAL;
+
+       t = get_unaligned_le32(&hdr.props[1]);
+       if (!is_power_of_2(t))
+               return -EINVAL;
+
+       t = get_unaligned_le32(&hdr.size_high);
+       if (t)
+               return -EINVAL;
+
+       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
+                                  &rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(LZMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return LZMA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_lzma_parser = {
+       .owner = THIS_MODULE,
+       .name = "lzma-fw",
+       .parse_fn = mtdsplit_parse_lzma,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_lzma_init(void)
+{
+       register_mtd_parser(&mtdsplit_lzma_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_lzma_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_minor.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_minor.c
new file mode 100644 (file)
index 0000000..f971f0a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  MTD splitter for MikroTik NOR devices
+ *
+ *  Copyright (C) 2017 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  The rootfs is expected at erase-block boundary due to the use of
+ *  mtd_find_rootfs_from(). We use a trimmed down version of the yaffs header
+ *  for two main reasons:
+ *  - the original header uses weakly defined types (int, enum...) which can
+ *    vary in length depending on build host (and the struct is not packed),
+ *    and the name field can have a different total length depending on
+ *    whether or not the yaffs code was _built_ with unicode support.
+ *  - the only field that could be of real use here (file_size_low) contains
+ *    invalid data in the header generated by kernel2minor, so we cannot use
+ *    it to infer the exact position of the rootfs and do away with
+ *    mtd_find_rootfs_from() (and thus have non-EB-aligned rootfs).
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/string.h>
+
+#include "mtdsplit.h"
+
+#define YAFFS_OBJECT_TYPE_FILE 0x1
+#define YAFFS_OBJECTID_ROOT    0x1
+#define YAFFS_SUM_UNUSED       0xFFFF
+#define YAFFS_NAME             "kernel"
+
+#define MINOR_NR_PARTS         2
+
+/*
+ * This structure is based on yaffs_obj_hdr from yaffs_guts.h
+ * The weak types match upstream. The fields have cpu-endianness
+ */
+struct minor_header {
+       int yaffs_type;
+       int yaffs_obj_id;
+       u16 yaffs_sum_unused;
+       char yaffs_name[sizeof(YAFFS_NAME)];
+};
+
+static int mtdsplit_parse_minor(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct minor_header hdr;
+       size_t hdr_len, retlen;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* match header */
+       if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE)
+               return -EINVAL;
+
+       if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT)
+               return -EINVAL;
+
+       if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED)
+               return -EINVAL;
+
+       if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME)))
+               return -EINVAL;
+
+       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
+                                  &rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return MINOR_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_minor_parser = {
+       .owner = THIS_MODULE,
+       .name = "minor-fw",
+       .parse_fn = mtdsplit_parse_minor,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_minor_init(void)
+{
+       register_mtd_parser(&mtdsplit_minor_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_minor_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_seama.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_seama.c
new file mode 100644 (file)
index 0000000..f8556e0
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define SEAMA_MAGIC            0x5EA3A417
+#define SEAMA_NR_PARTS         2
+#define SEAMA_MIN_ROOTFS_OFFS  0x80000 /* 512KiB */
+
+struct seama_header {
+       __be32  magic;          /* should always be SEAMA_MAGIC. */
+       __be16  reserved;       /* reserved for  */
+       __be16  metasize;       /* size of the META data */
+       __be32  size;           /* size of the image */
+       u8      md5[16];        /* digest */
+};
+
+static int mtdsplit_parse_seama(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct seama_header hdr;
+       size_t hdr_len, retlen, kernel_ent_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       enum mtdsplit_part_type type;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* sanity checks */
+       if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC)
+               return -EINVAL;
+
+       kernel_ent_size = hdr_len + be32_to_cpu(hdr.size) +
+                         be16_to_cpu(hdr.metasize);
+       if (kernel_ent_size > master->size)
+               return -EINVAL;
+
+       /* Check for the rootfs right after Seama entity with a kernel. */
+       err = mtd_check_rootfs_magic(master, kernel_ent_size, &type);
+       if (!err) {
+               rootfs_offset = kernel_ent_size;
+       } else {
+               /*
+                * On some devices firmware entity might contain both: kernel
+                * and rootfs. We can't determine kernel size so we just have to
+                * look for rootfs magic.
+                * Start the search from an arbitrary offset.
+                */
+               err = mtd_find_rootfs_from(master, SEAMA_MIN_ROOTFS_OFFS,
+                                          master->size, &rootfs_offset, &type);
+               if (err)
+                       return err;
+       }
+
+       parts = kzalloc(SEAMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = sizeof hdr + be16_to_cpu(hdr.metasize);
+       parts[0].size = rootfs_offset - parts[0].offset;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[1].name = UBI_PART_NAME;
+       else
+               parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return SEAMA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_seama_parser = {
+       .owner = THIS_MODULE,
+       .name = "seama-fw",
+       .parse_fn = mtdsplit_parse_seama,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_seama_init(void)
+{
+       register_mtd_parser(&mtdsplit_seama_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_seama_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_squashfs.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_squashfs.c
new file mode 100644 (file)
index 0000000..79e1f73
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (C) 2013 Felix Fietkau <nbd@nbd.name>
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/magic.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+static int
+mtdsplit_parse_squashfs(struct mtd_info *master,
+                       const struct mtd_partition **pparts,
+                       struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *part;
+       struct mtd_info *parent_mtd;
+       size_t part_offset;
+       size_t squashfs_len;
+       int err;
+
+       err = mtd_get_squashfs_len(master, 0, &squashfs_len);
+       if (err)
+               return err;
+
+       parent_mtd = mtdpart_get_master(master);
+       part_offset = mtdpart_get_offset(master);
+
+       part = kzalloc(sizeof(*part), GFP_KERNEL);
+       if (!part) {
+               pr_alert("unable to allocate memory for \"%s\" partition\n",
+                        ROOTFS_SPLIT_NAME);
+               return -ENOMEM;
+       }
+
+       part->name = ROOTFS_SPLIT_NAME;
+       part->offset = mtd_roundup_to_eb(part_offset + squashfs_len,
+                                        parent_mtd) - part_offset;
+       part->size = mtd_rounddown_to_eb(master->size - part->offset, master);
+
+       *pparts = part;
+       return 1;
+}
+
+static struct mtd_part_parser mtdsplit_squashfs_parser = {
+       .owner = THIS_MODULE,
+       .name = "squashfs-split",
+       .parse_fn = mtdsplit_parse_squashfs,
+       .type = MTD_PARSER_TYPE_ROOTFS,
+};
+
+static int __init mtdsplit_squashfs_init(void)
+{
+       register_mtd_parser(&mtdsplit_squashfs_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_squashfs_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_tplink.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_tplink.c
new file mode 100644 (file)
index 0000000..c346aa8
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define TPLINK_NR_PARTS                2
+#define TPLINK_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
+
+#define MD5SUM_LEN  16
+
+struct fw_v1 {
+       char            vendor_name[24];
+       char            fw_version[36];
+       uint32_t        hw_id;          /* hardware id */
+       uint32_t        hw_rev;         /* hardware revision */
+       uint32_t        unk1;
+       uint8_t         md5sum1[MD5SUM_LEN];
+       uint32_t        unk2;
+       uint8_t         md5sum2[MD5SUM_LEN];
+       uint32_t        unk3;
+       uint32_t        kernel_la;      /* kernel load address */
+       uint32_t        kernel_ep;      /* kernel entry point */
+       uint32_t        fw_length;      /* total length of the firmware */
+       uint32_t        kernel_ofs;     /* kernel data offset */
+       uint32_t        kernel_len;     /* kernel data length */
+       uint32_t        rootfs_ofs;     /* rootfs data offset */
+       uint32_t        rootfs_len;     /* rootfs data length */
+       uint32_t        boot_ofs;       /* bootloader data offset */
+       uint32_t        boot_len;       /* bootloader data length */
+       uint8_t         pad[360];
+} __attribute__ ((packed));
+
+struct fw_v2 {
+       char            fw_version[48]; /* 0x04: fw version string */
+       uint32_t        hw_id;          /* 0x34: hardware id */
+       uint32_t        hw_rev;         /* 0x38: FIXME: hardware revision? */
+       uint32_t        unk1;           /* 0x3c: 0x00000000 */
+       uint8_t         md5sum1[MD5SUM_LEN]; /* 0x40 */
+       uint32_t        unk2;           /* 0x50: 0x00000000 */
+       uint8_t         md5sum2[MD5SUM_LEN]; /* 0x54 */
+       uint32_t        unk3;           /* 0x64: 0xffffffff */
+
+       uint32_t        kernel_la;      /* 0x68: kernel load address */
+       uint32_t        kernel_ep;      /* 0x6c: kernel entry point */
+       uint32_t        fw_length;      /* 0x70: total length of the image */
+       uint32_t        kernel_ofs;     /* 0x74: kernel data offset */
+       uint32_t        kernel_len;     /* 0x78: kernel data length */
+       uint32_t        rootfs_ofs;     /* 0x7c: rootfs data offset */
+       uint32_t        rootfs_len;     /* 0x80: rootfs data length */
+       uint32_t        boot_ofs;       /* 0x84: FIXME: seems to be unused */
+       uint32_t        boot_len;       /* 0x88: FIXME: seems to be unused */
+       uint16_t        unk4;           /* 0x8c: 0x55aa */
+       uint8_t         sver_hi;        /* 0x8e */
+       uint8_t         sver_lo;        /* 0x8f */
+       uint8_t         unk5;           /* 0x90: magic: 0xa5 */
+       uint8_t         ver_hi;         /* 0x91 */
+       uint8_t         ver_mid;        /* 0x92 */
+       uint8_t         ver_lo;         /* 0x93 */
+       uint8_t         pad[364];
+} __attribute__ ((packed));
+
+struct tplink_fw_header {
+       uint32_t version;
+       union {
+               struct fw_v1 v1;
+               struct fw_v2 v2;
+       };
+};
+
+static int mtdsplit_parse_tplink(struct mtd_info *master,
+                                const struct mtd_partition **pparts,
+                                struct mtd_part_parser_data *data)
+{
+       struct tplink_fw_header hdr;
+       size_t hdr_len, retlen, kernel_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       switch (le32_to_cpu(hdr.version)) {
+       case 1:
+               if (be32_to_cpu(hdr.v1.kernel_ofs) != sizeof(hdr))
+                       return -EINVAL;
+
+               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v1.kernel_len);
+               rootfs_offset = be32_to_cpu(hdr.v1.rootfs_ofs);
+               break;
+       case 2:
+       case 3:
+               if (be32_to_cpu(hdr.v2.kernel_ofs) != sizeof(hdr))
+                       return -EINVAL;
+
+               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v2.kernel_len);
+               rootfs_offset = be32_to_cpu(hdr.v2.rootfs_ofs);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (kernel_size > master->size)
+               return -EINVAL;
+
+       /* Find the rootfs */
+       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+       if (err) {
+               /*
+                * The size in the header might cover the rootfs as well.
+                * Start the search from an arbitrary offset.
+                */
+               err = mtd_find_rootfs_from(master, TPLINK_MIN_ROOTFS_OFFS,
+                                          master->size, &rootfs_offset, NULL);
+               if (err)
+                       return err;
+       }
+
+       parts = kzalloc(TPLINK_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return TPLINK_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_tplink_parser = {
+       .owner = THIS_MODULE,
+       .name = "tplink-fw",
+       .parse_fn = mtdsplit_parse_tplink,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_tplink_init(void)
+{
+       register_mtd_parser(&mtdsplit_tplink_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_tplink_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_trx.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_trx.c
new file mode 100644 (file)
index 0000000..53aebc5
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define TRX_MAGIC   0x30524448  /* "HDR0" */
+
+struct trx_header {
+       __le32 magic;
+       __le32 len;
+       __le32 crc32;
+       __le32 flag_version;
+       __le32 offset[4];
+};
+
+static int
+read_trx_header(struct mtd_info *mtd, size_t offset,
+                  struct trx_header *header)
+{
+       size_t header_len;
+       size_t retlen;
+       int ret;
+
+       header_len = sizeof(*header);
+       ret = mtd_read(mtd, offset, header_len, &retlen,
+                      (unsigned char *) header);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_parse_trx(struct mtd_info *master,
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       struct trx_header hdr;
+       int nr_parts;
+       size_t offset;
+       size_t trx_offset;
+       size_t trx_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int ret;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       /* find trx image on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               trx_size = 0;
+
+               ret = read_trx_header(master, offset, &hdr);
+               if (ret)
+                       continue;
+
+               if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
+                       pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+
+               trx_size = le32_to_cpu(hdr.len);
+               if ((offset + trx_size) > master->size) {
+                       pr_debug("trx image exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (trx_size == 0) {
+               pr_debug("no trx header found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       trx_offset = offset + hdr.offset[0];
+       rootfs_offset = offset + hdr.offset[1];
+       rootfs_size = master->size - rootfs_offset;
+       trx_size = rootfs_offset - trx_offset;
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = trx_offset;
+       parts[0].size = trx_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return nr_parts;
+
+err:
+       kfree(parts);
+       return ret;
+}
+
+static struct mtd_part_parser trx_parser = {
+       .owner = THIS_MODULE,
+       .name = "trx-fw",
+       .parse_fn = mtdsplit_parse_trx,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_trx_init(void)
+{
+       register_mtd_parser(&trx_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_trx_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_uimage.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_uimage.c
new file mode 100644 (file)
index 0000000..bd1c723
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+/*
+ * uimage_header itself is only 64B, but it may be prepended with another data.
+ * Currently the biggest size is for Edimax devices: 20B + 64B
+ */
+#define MAX_HEADER_LEN         84
+
+#define IH_MAGIC       0x27051956      /* Image Magic Number           */
+#define IH_NMLEN               32      /* Image Name Length            */
+
+#define IH_OS_LINUX            5       /* Linux        */
+
+#define IH_TYPE_KERNEL         2       /* OS Kernel Image              */
+#define IH_TYPE_FILESYSTEM     7       /* Filesystem Image             */
+
+/*
+ * Legacy format image header,
+ * all data in network byte order (aka natural aka bigendian).
+ */
+struct uimage_header {
+       uint32_t        ih_magic;       /* Image Header Magic Number    */
+       uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
+       uint32_t        ih_time;        /* Image Creation Timestamp     */
+       uint32_t        ih_size;        /* Image Data Size              */
+       uint32_t        ih_load;        /* Data  Load  Address          */
+       uint32_t        ih_ep;          /* Entry Point Address          */
+       uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
+       uint8_t         ih_os;          /* Operating System             */
+       uint8_t         ih_arch;        /* CPU architecture             */
+       uint8_t         ih_type;        /* Image Type                   */
+       uint8_t         ih_comp;        /* Compression Type             */
+       uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
+};
+
+static int
+read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
+                  size_t header_len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
+ *
+ * @find_header: function to call for a block of data that will return offset
+ *      of a valid uImage header if found
+ */
+static int __mtdsplit_parse_uimage(struct mtd_info *master,
+                                  const struct mtd_partition **pparts,
+                                  struct mtd_part_parser_data *data,
+                                  ssize_t (*find_header)(u_char *buf, size_t len))
+{
+       struct mtd_partition *parts;
+       u_char *buf;
+       int nr_parts;
+       size_t offset;
+       size_t uimage_offset;
+       size_t uimage_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int uimage_part, rf_part;
+       int ret;
+       enum mtdsplit_part_type type;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       buf = vmalloc(MAX_HEADER_LEN);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_free_parts;
+       }
+
+       /* find uImage on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               struct uimage_header *header;
+
+               uimage_size = 0;
+
+               ret = read_uimage_header(master, offset, buf, MAX_HEADER_LEN);
+               if (ret)
+                       continue;
+
+               ret = find_header(buf, MAX_HEADER_LEN);
+               if (ret < 0) {
+                       pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+               header = (struct uimage_header *)(buf + ret);
+
+               uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
+               if ((offset + uimage_size) > master->size) {
+                       pr_debug("uImage exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (uimage_size == 0) {
+               pr_debug("no uImage found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       uimage_offset = offset;
+
+       if (uimage_offset == 0) {
+               uimage_part = 0;
+               rf_part = 1;
+
+               /* find the roots after the uImage */
+               ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
+                                          master->size, &rootfs_offset, &type);
+               if (ret) {
+                       pr_debug("no rootfs after uImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_size = master->size - rootfs_offset;
+               uimage_size = rootfs_offset - uimage_offset;
+       } else {
+               rf_part = 0;
+               uimage_part = 1;
+
+               /* check rootfs presence at offset 0 */
+               ret = mtd_check_rootfs_magic(master, 0, &type);
+               if (ret) {
+                       pr_debug("no rootfs before uImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_offset = 0;
+               rootfs_size = uimage_offset;
+       }
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       parts[uimage_part].name = KERNEL_PART_NAME;
+       parts[uimage_part].offset = uimage_offset;
+       parts[uimage_part].size = uimage_size;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[rf_part].name = UBI_PART_NAME;
+       else
+               parts[rf_part].name = ROOTFS_PART_NAME;
+       parts[rf_part].offset = rootfs_offset;
+       parts[rf_part].size = rootfs_size;
+
+       vfree(buf);
+
+       *pparts = parts;
+       return nr_parts;
+
+err_free_buf:
+       vfree(buf);
+
+err_free_parts:
+       kfree(parts);
+       return ret;
+}
+
+static ssize_t uimage_verify_default(u_char *buf, size_t len)
+{
+       struct uimage_header *header = (struct uimage_header *)buf;
+
+       /* default sanity checks */
+       if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
+               pr_debug("invalid uImage magic: %08x\n",
+                        be32_to_cpu(header->ih_magic));
+               return -EINVAL;
+       }
+
+       if (header->ih_os != IH_OS_LINUX) {
+               pr_debug("invalid uImage OS: %08x\n",
+                        be32_to_cpu(header->ih_os));
+               return -EINVAL;
+       }
+
+       if (header->ih_type != IH_TYPE_KERNEL) {
+               pr_debug("invalid uImage type: %08x\n",
+                        be32_to_cpu(header->ih_type));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_generic(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                     uimage_verify_default);
+}
+
+static struct mtd_part_parser uimage_generic_parser = {
+       .owner = THIS_MODULE,
+       .name = "uimage-fw",
+       .parse_fn = mtdsplit_uimage_parse_generic,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+#define FW_MAGIC_WNR2000V1     0x32303031
+#define FW_MAGIC_WNR2000V3     0x32303033
+#define FW_MAGIC_WNR2000V4     0x32303034
+#define FW_MAGIC_WNR2200       0x32323030
+#define FW_MAGIC_WNR612V2      0x32303631
+#define FW_MAGIC_WNR1000V2     0x31303031
+#define FW_MAGIC_WNR1000V2_VC  0x31303030
+#define FW_MAGIC_WNDR3700      0x33373030
+#define FW_MAGIC_WNDR3700V2    0x33373031
+#define FW_MAGIC_WPN824N       0x31313030
+
+static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
+{
+       struct uimage_header *header = (struct uimage_header *)buf;
+       uint8_t expected_type = IH_TYPE_FILESYSTEM;
+
+       switch (be32_to_cpu(header->ih_magic)) {
+       case FW_MAGIC_WNR612V2:
+       case FW_MAGIC_WNR1000V2:
+       case FW_MAGIC_WNR1000V2_VC:
+       case FW_MAGIC_WNR2000V1:
+       case FW_MAGIC_WNR2000V3:
+       case FW_MAGIC_WNR2200:
+       case FW_MAGIC_WNDR3700:
+       case FW_MAGIC_WNDR3700V2:
+       case FW_MAGIC_WPN824N:
+               break;
+       case FW_MAGIC_WNR2000V4:
+               expected_type = IH_TYPE_KERNEL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (header->ih_os != IH_OS_LINUX ||
+           header->ih_type != expected_type)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_netgear(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                     uimage_verify_wndr3700);
+}
+
+static struct mtd_part_parser uimage_netgear_parser = {
+       .owner = THIS_MODULE,
+       .name = "netgear-fw",
+       .parse_fn = mtdsplit_uimage_parse_netgear,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Edimax
+ **************************************************/
+
+#define FW_EDIMAX_OFFSET       20
+#define FW_MAGIC_EDIMAX                0x43535953
+
+static ssize_t uimage_find_edimax(u_char *buf, size_t len)
+{
+       u32 *magic;
+
+       if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
+               pr_err("Buffer too small for checking Edimax header\n");
+               return -ENOSPC;
+       }
+
+       magic = (u32 *)buf;
+       if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
+               return -EINVAL;
+
+       if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
+               return FW_EDIMAX_OFFSET;
+
+       return -EINVAL;
+}
+
+static int
+mtdsplit_uimage_parse_edimax(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                      uimage_find_edimax);
+}
+
+static struct mtd_part_parser uimage_edimax_parser = {
+       .owner = THIS_MODULE,
+       .name = "edimax-fw",
+       .parse_fn = mtdsplit_uimage_parse_edimax,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_uimage_init(void)
+{
+       register_mtd_parser(&uimage_generic_parser);
+       register_mtd_parser(&uimage_netgear_parser);
+       register_mtd_parser(&uimage_edimax_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_uimage_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_wrgg.c b/target/linux/generic/files-4.14/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
new file mode 100644 (file)
index 0000000..16ebd51
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *  Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define WRGG_NR_PARTS          2
+#define WRGG_MIN_ROOTFS_OFFS   0x80000 /* 512KiB */
+#define WRGG03_MAGIC           0x20080321
+#define WRG_MAGIC              0x20040220
+
+struct wrgg03_header {
+       char            signature[32];
+       uint32_t        magic1;
+       uint32_t        magic2;
+       char            version[16];
+       char            model[16];
+       uint32_t        flag[2];
+       uint32_t        reserve[2];
+       char            buildno[16];
+       uint32_t        size;
+       uint32_t        offset;
+       char            devname[32];
+       char            digest[16];
+} __attribute__ ((packed));
+
+struct wrg_header {
+       char            signature[32];
+       uint32_t        magic1;
+       uint32_t        magic2;
+       uint32_t        size;
+       uint32_t        offset;
+       char            devname[32];
+       char            digest[16];
+} __attribute__ ((packed));
+
+
+static int mtdsplit_parse_wrgg(struct mtd_info *master,
+                              const struct mtd_partition **pparts,
+                              struct mtd_part_parser_data *data)
+{
+       struct wrgg03_header hdr;
+       size_t hdr_len, retlen, kernel_ent_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       enum mtdsplit_part_type type;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* sanity checks */
+       if (le32_to_cpu(hdr.magic1) == WRGG03_MAGIC) {
+               kernel_ent_size = hdr_len + be32_to_cpu(hdr.size);
+       } else if (le32_to_cpu(hdr.magic1) == WRG_MAGIC) {
+               kernel_ent_size = sizeof(struct wrg_header) + le32_to_cpu(
+                                 ((struct wrg_header*)&hdr)->size);
+       } else {
+               return -EINVAL;
+       }
+
+       if (kernel_ent_size > master->size)
+               return -EINVAL;
+
+       /*
+        * The size in the header covers the rootfs as well.
+        * Start the search from an arbitrary offset.
+        */
+       err = mtd_find_rootfs_from(master, WRGG_MIN_ROOTFS_OFFS,
+                                  master->size, &rootfs_offset, &type);
+       if (err)
+               return err;
+
+       parts = kzalloc(WRGG_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return WRGG_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_wrgg_parser = {
+       .owner = THIS_MODULE,
+       .name = "wrgg-fw",
+       .parse_fn = mtdsplit_parse_wrgg,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_wrgg_init(void)
+{
+       register_mtd_parser(&mtdsplit_wrgg_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_wrgg_init);
diff --git a/target/linux/generic/files-4.14/drivers/mtd/myloader.c b/target/linux/generic/files-4.14/drivers/mtd/myloader.c
new file mode 100644 (file)
index 0000000..7532d45
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *  Parse MyLoader-style flash partition tables and produce a Linux partition
+ *  array to match.
+ *
+ *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This file was based on drivers/mtd/redboot.c
+ *  Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+#include <linux/myloader.h>
+
+#define BLOCK_LEN_MIN          0x10000
+#define PART_NAME_LEN          32
+
+struct part_data {
+       struct mylo_partition_table     tab;
+       char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
+};
+
+static int myloader_parse_partitions(struct mtd_info *master,
+                                    const struct mtd_partition **pparts,
+                                    struct mtd_part_parser_data *data)
+{
+       struct part_data *buf;
+       struct mylo_partition_table *tab;
+       struct mylo_partition *part;
+       struct mtd_partition *mtd_parts;
+       struct mtd_partition *mtd_part;
+       int num_parts;
+       int ret, i;
+       size_t retlen;
+       char *names;
+       unsigned long offset;
+       unsigned long blocklen;
+
+       buf = vmalloc(sizeof(*buf));
+       if (!buf) {
+               return -ENOMEM;
+               goto out;
+       }
+       tab = &buf->tab;
+
+       blocklen = master->erasesize;
+       if (blocklen < BLOCK_LEN_MIN)
+               blocklen = BLOCK_LEN_MIN;
+
+       offset = blocklen;
+
+       /* Find the partition table */
+       for (i = 0; i < 4; i++, offset += blocklen) {
+               printk(KERN_DEBUG "%s: searching for MyLoader partition table"
+                               " at offset 0x%lx\n", master->name, offset);
+
+               ret = mtd_read(master, offset, sizeof(*buf), &retlen,
+                              (void *)buf);
+               if (ret)
+                       goto out_free_buf;
+
+               if (retlen != sizeof(*buf)) {
+                       ret = -EIO;
+                       goto out_free_buf;
+               }
+
+               /* Check for Partition Table magic number */
+               if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
+                       break;
+
+       }
+
+       if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
+               printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
+                       master->name);
+               ret = 0;
+               goto out_free_buf;
+       }
+
+       /* The MyLoader and the Partition Table is always present */
+       num_parts = 2;
+
+       /* Detect number of used partitions */
+       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+               part = &tab->partitions[i];
+
+               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+                       continue;
+
+               num_parts++;
+       }
+
+       mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
+                               num_parts * PART_NAME_LEN), GFP_KERNEL);
+
+       if (!mtd_parts) {
+               ret = -ENOMEM;
+               goto out_free_buf;
+       }
+
+       mtd_part = mtd_parts;
+       names = (char *)&mtd_parts[num_parts];
+
+       strncpy(names, "myloader", PART_NAME_LEN);
+       mtd_part->name = names;
+       mtd_part->offset = 0;
+       mtd_part->size = offset;
+       mtd_part->mask_flags = MTD_WRITEABLE;
+       mtd_part++;
+       names += PART_NAME_LEN;
+
+       strncpy(names, "partition_table", PART_NAME_LEN);
+       mtd_part->name = names;
+       mtd_part->offset = offset;
+       mtd_part->size = blocklen;
+       mtd_part->mask_flags = MTD_WRITEABLE;
+       mtd_part++;
+       names += PART_NAME_LEN;
+
+       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+               part = &tab->partitions[i];
+
+               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+                       continue;
+
+               if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
+                       strncpy(names, buf->names[i], PART_NAME_LEN);
+               else
+                       snprintf(names, PART_NAME_LEN, "partition%d", i);
+
+               mtd_part->offset = le32_to_cpu(part->addr);
+               mtd_part->size = le32_to_cpu(part->size);
+               mtd_part->name = names;
+               mtd_part++;
+               names += PART_NAME_LEN;
+       }
+
+       *pparts = mtd_parts;
+       ret = num_parts;
+
+ out_free_buf:
+       vfree(buf);
+ out:
+       return ret;
+}
+
+static struct mtd_part_parser myloader_mtd_parser = {
+       .owner          = THIS_MODULE,
+       .parse_fn       = myloader_parse_partitions,
+       .name           = "MyLoader",
+};
+
+static int __init myloader_mtd_parser_init(void)
+{
+       register_mtd_parser(&myloader_mtd_parser);
+
+       return 0;
+}
+
+static void __exit myloader_mtd_parser_exit(void)
+{
+       deregister_mtd_parser(&myloader_mtd_parser);
+}
+
+module_init(myloader_mtd_parser_init);
+module_exit(myloader_mtd_parser_exit);
+
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/adm6996.c b/target/linux/generic/files-4.14/drivers/net/phy/adm6996.c
new file mode 100644 (file)
index 0000000..42928ba
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ * ADM6996 switch driver
+ *
+ * swconfig interface based on ar8216.c
+ *
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ * VLAN support Copyright (c) 2010, 2011 Peter Lebbing <peter@digitalbrains.com>
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2014 Matti Laakso <malaakso@elisanet.fi>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/*#define DEBUG 1*/
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/adm6996-gpio.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/switch.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "adm6996.h"
+
+MODULE_DESCRIPTION("Infineon ADM6996 Switch");
+MODULE_AUTHOR("Felix Fietkau, Peter Lebbing <peter@digitalbrains.com>");
+MODULE_LICENSE("GPL");
+
+static const char * const adm6996_model_name[] =
+{
+       NULL,
+       "ADM6996FC",
+       "ADM6996M",
+       "ADM6996L"
+};
+
+struct adm6996_mib_desc {
+       unsigned int offset;
+       const char *name;
+};
+
+struct adm6996_priv {
+       struct switch_dev dev;
+       void *priv;
+
+       u8 eecs;
+       u8 eesk;
+       u8 eedi;
+
+       enum adm6996_model model;
+
+       bool enable_vlan;
+       bool vlan_enabled;      /* Current hardware state */
+
+#ifdef DEBUG
+       u16 addr;               /* Debugging: register address to operate on */
+#endif
+
+       u16 pvid[ADM_NUM_PORTS];        /* Primary VLAN ID */
+       u8 tagged_ports;
+
+       u16 vlan_id[ADM_NUM_VLANS];
+       u8 vlan_table[ADM_NUM_VLANS];   /* bitmap, 1 = port is member */
+       u8 vlan_tagged[ADM_NUM_VLANS];  /* bitmap, 1 = tagged member */
+       
+       struct mutex mib_lock;
+       char buf[2048];
+
+       struct mutex reg_mutex;
+
+       /* use abstraction for regops, we want to add gpio support in the future */
+       u16 (*read)(struct adm6996_priv *priv, enum admreg reg);
+       void (*write)(struct adm6996_priv *priv, enum admreg reg, u16 val);
+};
+
+#define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev)
+#define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
+
+#define MIB_DESC(_o, _n)       \
+       {                       \
+               .offset = (_o), \
+               .name = (_n),   \
+       }
+
+static const struct adm6996_mib_desc adm6996_mibs[] = {
+       MIB_DESC(ADM_CL0, "RxPacket"),
+       MIB_DESC(ADM_CL6, "RxByte"),
+       MIB_DESC(ADM_CL12, "TxPacket"),
+       MIB_DESC(ADM_CL18, "TxByte"),
+       MIB_DESC(ADM_CL24, "Collision"),
+       MIB_DESC(ADM_CL30, "Error"),
+};
+
+#define ADM6996_MIB_RXB_ID     1
+#define ADM6996_MIB_TXB_ID     3
+
+static inline u16
+r16(struct adm6996_priv *priv, enum admreg reg)
+{
+       return priv->read(priv, reg);
+}
+
+static inline void
+w16(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       priv->write(priv, reg, val);
+}
+
+/* Minimum timing constants */
+#define EECK_EDGE_TIME  3   /* 3us - max(adm 2.5us, 93c 1us) */
+#define EEDI_SETUP_TIME 1   /* 1us - max(adm 10ns, 93c 400ns) */
+#define EECS_SETUP_TIME 1   /* 1us - max(adm no, 93c 200ns) */
+
+static void adm6996_gpio_write(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
+{
+       int i, len = (bits + 7) / 8;
+       u8 mask;
+
+       gpio_set_value(priv->eecs, cs);
+       udelay(EECK_EDGE_TIME);
+
+       /* Byte assemble from MSB to LSB */
+       for (i = 0; i < len; i++) {
+               /* Bit bang from MSB to LSB */
+               for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
+                       /* Clock low */
+                       gpio_set_value(priv->eesk, 0);
+                       udelay(EECK_EDGE_TIME);
+
+                       /* Output on rising edge */
+                       gpio_set_value(priv->eedi, (mask & buf[i]));
+                       udelay(EEDI_SETUP_TIME);
+
+                       /* Clock high */
+                       gpio_set_value(priv->eesk, 1);
+                       udelay(EECK_EDGE_TIME);
+               }
+       }
+
+       /* Clock low */
+       gpio_set_value(priv->eesk, 0);
+       udelay(EECK_EDGE_TIME);
+
+       if (cs)
+               gpio_set_value(priv->eecs, 0);
+}
+
+static void adm6996_gpio_read(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
+{
+       int i, len = (bits + 7) / 8;
+       u8 mask;
+
+       gpio_set_value(priv->eecs, cs);
+       udelay(EECK_EDGE_TIME);
+
+       /* Byte assemble from MSB to LSB */
+       for (i = 0; i < len; i++) {
+               u8 byte;
+
+               /* Bit bang from MSB to LSB */
+               for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) {
+                       u8 gp;
+
+                       /* Clock low */
+                       gpio_set_value(priv->eesk, 0);
+                       udelay(EECK_EDGE_TIME);
+
+                       /* Input on rising edge */
+                       gp = gpio_get_value(priv->eedi);
+                       if (gp)
+                               byte |= mask;
+
+                       /* Clock high */
+                       gpio_set_value(priv->eesk, 1);
+                       udelay(EECK_EDGE_TIME);
+               }
+
+               *buf++ = byte;
+       }
+
+       /* Clock low */
+       gpio_set_value(priv->eesk, 0);
+       udelay(EECK_EDGE_TIME);
+
+       if (cs)
+               gpio_set_value(priv->eecs, 0);
+}
+
+/* Advance clock(s) */
+static void adm6996_gpio_adclk(struct adm6996_priv *priv, int clocks)
+{
+       int i;
+       for (i = 0; i < clocks; i++) {
+               /* Clock high */
+               gpio_set_value(priv->eesk, 1);
+               udelay(EECK_EDGE_TIME);
+
+               /* Clock low */
+               gpio_set_value(priv->eesk, 0);
+               udelay(EECK_EDGE_TIME);
+       }
+}
+
+static u16
+adm6996_read_gpio_reg(struct adm6996_priv *priv, enum admreg reg)
+{
+       /* cmd: 01 10 T DD R RRRRRR */
+       u8 bits[6] = {
+               0xFF, 0xFF, 0xFF, 0xFF,
+               (0x06 << 4) | ((0 & 0x01) << 3 | (reg&64)>>6),
+               ((reg&63)<<2)
+       };
+
+       u8 rbits[4];
+
+       /* Enable GPIO outputs with all pins to 0 */
+       gpio_direction_output(priv->eecs, 0);
+       gpio_direction_output(priv->eesk, 0);
+       gpio_direction_output(priv->eedi, 0);
+
+       adm6996_gpio_write(priv, 0, bits, 46);
+       gpio_direction_input(priv->eedi);
+       adm6996_gpio_adclk(priv, 2);
+       adm6996_gpio_read(priv, 0, rbits, 32);
+
+       /* Extra clock(s) required per datasheet */
+       adm6996_gpio_adclk(priv, 2);
+
+       /* Disable GPIO outputs */
+       gpio_direction_input(priv->eecs);
+       gpio_direction_input(priv->eesk);
+
+        /* EEPROM has 16-bit registers, but pumps out two registers in one request */
+       return (reg & 0x01 ?  (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3]));
+}
+
+/* Write chip configuration register */
+/* Follow 93c66 timing and chip's min EEPROM timing requirement */
+static void
+adm6996_write_gpio_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
+       u8 bits[4] = {
+               (0x05 << 5) | (reg >> 3),
+               (reg << 5) | (u8)(val >> 11),
+               (u8)(val >> 3),
+               (u8)(val << 5)
+       };
+
+       /* Enable GPIO outputs with all pins to 0 */
+       gpio_direction_output(priv->eecs, 0);
+       gpio_direction_output(priv->eesk, 0);
+       gpio_direction_output(priv->eedi, 0);
+
+       /* Write cmd. Total 27 bits */
+       adm6996_gpio_write(priv, 1, bits, 27);
+
+       /* Extra clock(s) required per datasheet */
+       adm6996_gpio_adclk(priv, 2);
+
+       /* Disable GPIO outputs */
+       gpio_direction_input(priv->eecs);
+       gpio_direction_input(priv->eesk);
+       gpio_direction_input(priv->eedi);
+}
+
+static u16
+adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg)
+{
+       struct phy_device *phydev = priv->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, PHYADDR(reg));
+}
+
+static void
+adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       struct phy_device *phydev = priv->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, PHYADDR(reg), val);
+}
+
+static int
+adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 1)
+               return -EINVAL;
+
+       priv->enable_vlan = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = priv->enable_vlan;
+
+       return 0;
+};
+
+#ifdef DEBUG
+
+static int
+adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 1023)
+               return -EINVAL;
+
+       priv->addr = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = priv->addr;
+
+       return 0;
+};
+
+static int
+adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 65535)
+               return -EINVAL;
+
+       w16(priv, priv->addr, val->value.i);
+
+       return 0;
+};
+
+static int
+adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = r16(priv, priv->addr);
+
+       return 0;
+};
+
+#endif /* def DEBUG */
+
+static int
+adm6996_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("set_pvid port %d vlan %d\n", port, vlan);
+
+       if (vlan > ADM_VLAN_MAX_ID)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+
+       return 0;
+}
+
+static int
+adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("get_pvid port %d\n", port);
+       *vlan = priv->pvid[port];
+
+       return 0;
+}
+
+static int
+adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("set_vid port %d vid %d\n", val->port_vlan, val->value.i);
+
+       if (val->value.i > ADM_VLAN_MAX_ID)
+               return -EINVAL;
+
+       priv->vlan_id[val->port_vlan] = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("get_vid port %d\n", val->port_vlan);
+
+       val->value.i = priv->vlan_id[val->port_vlan];
+
+       return 0;
+};
+
+static int
+adm6996_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       u8 tagged = priv->vlan_tagged[val->port_vlan];
+       int i;
+
+       pr_devel("get_ports port_vlan %d\n", val->port_vlan);
+
+       val->len = 0;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+
+       return 0;
+};
+
+static int
+adm6996_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       u8 *ports = &priv->vlan_table[val->port_vlan];
+       u8 *tagged = &priv->vlan_tagged[val->port_vlan];
+       int i;
+
+       pr_devel("set_ports port_vlan %d ports", val->port_vlan);
+
+       *ports = 0;
+       *tagged = 0;
+
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+#ifdef DEBUG
+               pr_cont(" %d%s", p->id,
+                      ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" :
+                       ""));
+#endif
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       *tagged |= (1 << p->id);
+                       priv->tagged_ports |= (1 << p->id);
+               }
+
+               *ports |= (1 << p->id);
+       }
+
+#ifdef DEBUG
+       pr_cont("\n");
+#endif
+
+       return 0;
+};
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_enable_vlan(struct adm6996_priv *priv)
+{
+       u16 reg;
+
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg &= ~(ADM_OTBE_MASK);
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = r16(priv, ADM_IFNTE);
+       reg &= ~(ADM_IFNTE_MASK);
+       w16(priv, ADM_IFNTE, reg);
+       reg = r16(priv, ADM_VID_CHECK);
+       reg |= ADM_VID_CHECK_MASK;
+       w16(priv, ADM_VID_CHECK, reg);
+       reg = r16(priv, ADM_SYSC0);
+       reg |= ADM_NTTE;
+       reg &= ~(ADM_RVID1);
+       w16(priv, ADM_SYSC0, reg);
+       reg = r16(priv, ADM_SYSC3);
+       reg |= ADM_TBV;
+       w16(priv, ADM_SYSC3, reg);
+}
+
+static void
+adm6996_enable_vlan_6996l(struct adm6996_priv *priv)
+{
+       u16 reg;
+
+       reg = r16(priv, ADM_SYSC3);
+       reg |= ADM_TBV;
+       reg |= ADM_MAC_CLONE;
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Disable VLANs
+ *
+ * Sets VLAN mapping for port-based VLAN with all ports connected to
+ * eachother (this is also the power-on default).
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_disable_vlan(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               reg = ADM_VLAN_FILT_MEMBER_MASK;
+               w16(priv, ADM_VLAN_FILT_L(i), reg);
+               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1);
+               w16(priv, ADM_VLAN_FILT_H(i), reg);
+       }
+
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg |= ADM_OTBE_MASK;
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = r16(priv, ADM_IFNTE);
+       reg |= ADM_IFNTE_MASK;
+       w16(priv, ADM_IFNTE, reg);
+       reg = r16(priv, ADM_VID_CHECK);
+       reg &= ~(ADM_VID_CHECK_MASK);
+       w16(priv, ADM_VID_CHECK, reg);
+       reg = r16(priv, ADM_SYSC0);
+       reg &= ~(ADM_NTTE);
+       reg |= ADM_RVID1;
+       w16(priv, ADM_SYSC0, reg);
+       reg = r16(priv, ADM_SYSC3);
+       reg &= ~(ADM_TBV);
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Disable VLANs
+ *
+ * Sets VLAN mapping for port-based VLAN with all ports connected to
+ * eachother (this is also the power-on default).
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_disable_vlan_6996l(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               w16(priv, ADM_VLAN_MAP(i), 0);
+       }
+
+       reg = r16(priv, ADM_SYSC3);
+       reg &= ~(ADM_TBV);
+       reg &= ~(ADM_MAC_CLONE);
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_apply_port_pvids(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               reg = r16(priv, adm_portcfg[i]);
+               reg &= ~(ADM_PORTCFG_PVID_MASK);
+               reg |= ADM_PORTCFG_PVID(priv->pvid[i]);
+               if (priv->model == ADM6996L) {
+                       if (priv->tagged_ports & (1 << i))
+                               reg |= (1 << 4);
+                       else
+                               reg &= ~(1 << 4);
+               }
+               w16(priv, adm_portcfg[i], reg);
+       }
+
+       w16(priv, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0]));
+       w16(priv, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1]));
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg &= ~(ADM_P2_PVID_MASK);
+       reg |= ADM_P2_PVID_VAL(priv->pvid[2]);
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = ADM_P3_PVID_VAL(priv->pvid[3]);
+       reg |= ADM_P4_PVID_VAL(priv->pvid[4]);
+       w16(priv, ADM_P3_P4_PVID, reg);
+       reg = r16(priv, ADM_P5_PVID);
+       reg &= ~(ADM_P2_PVID_MASK);
+       reg |= ADM_P5_PVID_VAL(priv->pvid[5]);
+       w16(priv, ADM_P5_PVID, reg);
+}
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_apply_vlan_filters(struct adm6996_priv *priv)
+{
+       u8 ports, tagged;
+       u16 vid, reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               vid = priv->vlan_id[i];
+               ports = priv->vlan_table[i];
+               tagged = priv->vlan_tagged[i];
+
+               if (ports == 0) {
+                       /* Disable VLAN entry */
+                       w16(priv, ADM_VLAN_FILT_H(i), 0);
+                       w16(priv, ADM_VLAN_FILT_L(i), 0);
+                       continue;
+               }
+
+               reg = ADM_VLAN_FILT_MEMBER(ports);
+               reg |= ADM_VLAN_FILT_TAGGED(tagged);
+               w16(priv, ADM_VLAN_FILT_L(i), reg);
+               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid);
+               w16(priv, ADM_VLAN_FILT_H(i), reg);
+       }
+}
+
+static void
+adm6996_apply_vlan_filters_6996l(struct adm6996_priv *priv)
+{
+       u8 ports;
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               ports = priv->vlan_table[i];
+
+               if (ports == 0) {
+                       /* Disable VLAN entry */
+                       w16(priv, ADM_VLAN_MAP(i), 0);
+                       continue;
+               } else {
+                       reg = ADM_VLAN_FILT(ports);
+                       w16(priv, ADM_VLAN_MAP(i), reg);
+               }
+       }
+}
+
+static int
+adm6996_hw_apply(struct switch_dev *dev)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("hw_apply\n");
+
+       mutex_lock(&priv->reg_mutex);
+
+       if (!priv->enable_vlan) {
+               if (priv->vlan_enabled) {
+                       if (priv->model == ADM6996L)
+                               adm6996_disable_vlan_6996l(priv);
+                       else
+                               adm6996_disable_vlan(priv);
+                       priv->vlan_enabled = 0;
+               }
+               goto out;
+       }
+
+       if (!priv->vlan_enabled) {
+               if (priv->model == ADM6996L)
+                       adm6996_enable_vlan_6996l(priv);
+               else
+                       adm6996_enable_vlan(priv);
+               priv->vlan_enabled = 1;
+       }
+
+       adm6996_apply_port_pvids(priv);
+       if (priv->model == ADM6996L)
+               adm6996_apply_vlan_filters_6996l(priv);
+       else
+               adm6996_apply_vlan_filters(priv);
+
+out:
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+/*
+ * Reset the switch
+ *
+ * The ADM6996 can't do a software-initiated reset, so we just initialise the
+ * registers we support in this driver.
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_perform_reset (struct adm6996_priv *priv)
+{
+       int i;
+
+       /* initialize port and vlan settings */
+       for (i = 0; i < ADM_NUM_PORTS - 1; i++) {
+               w16(priv, adm_portcfg[i], ADM_PORTCFG_INIT |
+                       ADM_PORTCFG_PVID(0));
+       }
+       w16(priv, adm_portcfg[5], ADM_PORTCFG_CPU);
+
+       if (priv->model == ADM6996M || priv->model == ADM6996FC) {
+               /* reset all PHY ports */
+               for (i = 0; i < ADM_PHY_PORTS; i++) {
+                       w16(priv, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
+               }
+       }
+
+       priv->enable_vlan = 0;
+       priv->vlan_enabled = 0;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               priv->pvid[i] = 0;
+       }
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               priv->vlan_id[i] = i;
+               priv->vlan_table[i] = 0;
+               priv->vlan_tagged[i] = 0;
+       }
+
+       if (priv->model == ADM6996M) {
+               /* Clear VLAN priority map so prio's are unused */
+               w16 (priv, ADM_VLAN_PRIOMAP, 0);
+
+               adm6996_disable_vlan(priv);
+               adm6996_apply_port_pvids(priv);
+       } else if (priv->model == ADM6996L) {
+               /* Clear VLAN priority map so prio's are unused */
+               w16 (priv, ADM_VLAN_PRIOMAP, 0);
+
+               adm6996_disable_vlan_6996l(priv);
+               adm6996_apply_port_pvids(priv);
+       }
+}
+
+static int
+adm6996_reset_switch(struct switch_dev *dev)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("reset\n");
+
+       mutex_lock(&priv->reg_mutex);
+       adm6996_perform_reset (priv);
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+static int
+adm6996_get_port_link(struct switch_dev *dev, int port,
+               struct switch_port_link *link)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       
+       u16 reg = 0;
+       
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+       
+       switch (port) {
+       case 0:
+               reg = r16(priv, ADM_PS0);
+               break;
+       case 1:
+               reg = r16(priv, ADM_PS0);
+               reg = reg >> 8;
+               break;
+       case 2:
+               reg = r16(priv, ADM_PS1);
+               break;
+       case 3:
+               reg = r16(priv, ADM_PS1);
+               reg = reg >> 8;
+               break;
+       case 4:
+               reg = r16(priv, ADM_PS1);
+               reg = reg >> 12;
+               break;
+       case 5:
+               reg = r16(priv, ADM_PS2);
+               /* Bits 0, 1, 3 and 4. */
+               reg = (reg & 3) | ((reg & 24) >> 1);
+               break;
+       default:
+               return -EINVAL;
+       }
+       
+       link->link = reg & ADM_PS_LS;
+       if (!link->link)
+               return 0;
+       link->aneg = true;
+       link->duplex = reg & ADM_PS_DS;
+       link->tx_flow = reg & ADM_PS_FCS;
+       link->rx_flow = reg & ADM_PS_FCS;
+       if (reg & ADM_PS_SS)
+               link->speed = SWITCH_PORT_SPEED_100;
+       else
+               link->speed = SWITCH_PORT_SPEED_10;
+
+       return 0;
+}
+
+static int
+adm6996_sw_get_port_mib(struct switch_dev *dev,
+                      const struct switch_attr *attr,
+                      struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       int port;
+       char *buf = priv->buf;
+       int i, len = 0;
+       u32 reg = 0;
+
+       port = val->port_vlan;
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+
+       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                       "Port %d MIB counters\n",
+                       port);
+
+       for (i = 0; i < ARRAY_SIZE(adm6996_mibs); i++) {
+               reg = r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port));
+               reg += r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+               len += snprintf(buf + len, sizeof(priv->buf) - len,
+                               "%-12s: %u\n",
+                               adm6996_mibs[i].name,
+                               reg);
+       }
+
+       mutex_unlock(&priv->mib_lock);
+
+       val->value.s = buf;
+       val->len = len;
+
+       return 0;
+}
+
+static int
+adm6996_get_port_stats(struct switch_dev *dev, int port,
+                       struct switch_port_stats *stats)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       int id;
+       u32 reg = 0;
+
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+
+       id = ADM6996_MIB_TXB_ID;
+       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
+       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+       stats->tx_bytes = reg;
+
+       id = ADM6996_MIB_RXB_ID;
+       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
+       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+       stats->rx_bytes = reg;
+
+       mutex_unlock(&priv->mib_lock);
+
+       return 0;
+}
+
+static struct switch_attr adm6996_globals[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "enable_vlan",
+        .description = "Enable VLANs",
+        .set = adm6996_set_enable_vlan,
+        .get = adm6996_get_enable_vlan,
+       },
+#ifdef DEBUG
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "addr",
+        .description =
+        "Direct register access: set register address (0 - 1023)",
+        .set = adm6996_set_addr,
+        .get = adm6996_get_addr,
+        },
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "data",
+        .description =
+        "Direct register access: read/write to register (0 - 65535)",
+        .set = adm6996_set_data,
+        .get = adm6996_get_data,
+        },
+#endif /* def DEBUG */
+};
+
+static struct switch_attr adm6996_port[] = {
+       {
+        .type = SWITCH_TYPE_STRING,
+        .name = "mib",
+        .description = "Get port's MIB counters",
+        .set = NULL,
+        .get = adm6996_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr adm6996_vlan[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "vid",
+        .description = "VLAN ID",
+        .set = adm6996_set_vid,
+        .get = adm6996_get_vid,
+        },
+};
+
+static struct switch_dev_ops adm6996_ops = {
+       .attr_global = {
+                       .attr = adm6996_globals,
+                       .n_attr = ARRAY_SIZE(adm6996_globals),
+                       },
+       .attr_port = {
+                     .attr = adm6996_port,
+                     .n_attr = ARRAY_SIZE(adm6996_port),
+                     },
+       .attr_vlan = {
+                     .attr = adm6996_vlan,
+                     .n_attr = ARRAY_SIZE(adm6996_vlan),
+                     },
+       .get_port_pvid = adm6996_get_pvid,
+       .set_port_pvid = adm6996_set_pvid,
+       .get_vlan_ports = adm6996_get_ports,
+       .set_vlan_ports = adm6996_set_ports,
+       .apply_config = adm6996_hw_apply,
+       .reset_switch = adm6996_reset_switch,
+       .get_port_link = adm6996_get_port_link,
+       .get_port_stats = adm6996_get_port_stats,
+};
+
+static int adm6996_switch_init(struct adm6996_priv *priv, const char *alias, struct net_device *netdev)
+{
+       struct switch_dev *swdev;
+       u16 test, old;
+
+       if (!priv->model) {
+               /* Detect type of chip */
+               old = r16(priv, ADM_VID_CHECK);
+               test = old ^ (1 << 12);
+               w16(priv, ADM_VID_CHECK, test);
+               test ^= r16(priv, ADM_VID_CHECK);
+               if (test & (1 << 12)) {
+                       /* 
+                        * Bit 12 of this register is read-only. 
+                        * This is the FC model. 
+                        */
+                       priv->model = ADM6996FC;
+               } else {
+                       /* Bit 12 is read-write. This is the M model. */
+                       priv->model = ADM6996M;
+                       w16(priv, ADM_VID_CHECK, old);
+               }
+       }
+
+       swdev = &priv->dev;
+       swdev->name = (adm6996_model_name[priv->model]);
+       swdev->cpu_port = ADM_CPU_PORT;
+       swdev->ports = ADM_NUM_PORTS;
+       swdev->vlans = ADM_NUM_VLANS;
+       swdev->ops = &adm6996_ops;
+       swdev->alias = alias;
+
+       /* The ADM6996L connected through GPIOs does not support any switch
+          status calls */
+       if (priv->model == ADM6996L) {
+               adm6996_ops.attr_port.n_attr = 0;
+               adm6996_ops.get_port_link = NULL;
+       }
+
+       pr_info ("%s: %s model PHY found.\n", alias, swdev->name);
+
+       mutex_lock(&priv->reg_mutex);
+       adm6996_perform_reset (priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       if (priv->model == ADM6996M || priv->model == ADM6996L) {
+               return register_switch(swdev, netdev);
+       }
+
+       return -ENODEV;
+}
+
+static int adm6996_config_init(struct phy_device *pdev)
+{
+       struct adm6996_priv *priv;
+       int ret;
+
+       pdev->supported = ADVERTISED_100baseT_Full;
+       pdev->advertising = ADVERTISED_100baseT_Full;
+
+       if (pdev->mdio.addr != 0) {
+               pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n"
+                               , pdev->attached_dev->name, pdev->mdio.addr);
+               return 0;
+       }
+
+       priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+       priv->priv = pdev;
+       priv->read = adm6996_read_mii_reg;
+       priv->write = adm6996_write_mii_reg;
+
+       ret = adm6996_switch_init(priv, pdev->attached_dev->name, pdev->attached_dev);
+       if (ret < 0)
+               return ret;
+
+       pdev->priv = priv;
+
+       return 0;
+}
+
+/*
+ * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
+ */
+static int adm6996_read_status(struct phy_device *phydev)
+{
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+/*
+ * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
+ */
+static int adm6996_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int adm6996_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       /* Our custom registers are at PHY addresses 0-10. Claim those. */
+       if (dev->mdio.addr > 10)
+               return 0;
+
+       /* look for the switch on the bus */
+       reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
+       if (reg != ADM_SIG0_VAL)
+               return 0;
+
+       reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
+       if (reg != ADM_SIG1_VAL)
+               return 0;
+
+       dev->phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL;
+
+       return 0;
+}
+
+static int adm6996_probe(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static void adm6996_remove(struct phy_device *pdev)
+{
+       struct adm6996_priv *priv = phy_to_adm(pdev);
+
+       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
+               unregister_switch(&priv->dev);
+}
+
+static int adm6996_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+
+static struct phy_driver adm6996_phy_driver = {
+       .name           = "Infineon ADM6996",
+       .phy_id         = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = adm6996_probe,
+       .remove         = adm6996_remove,
+       .config_init    = &adm6996_config_init,
+       .config_aneg    = &adm6996_config_aneg,
+       .read_status    = &adm6996_read_status,
+       .soft_reset     = adm6996_soft_reset,
+};
+
+static int adm6996_gpio_probe(struct platform_device *pdev)
+{
+       struct adm6996_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct adm6996_priv *priv;
+       int ret;
+
+       if (!pdata)
+               return -EINVAL;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+
+       priv->eecs = pdata->eecs;
+       priv->eedi = pdata->eedi;
+       priv->eesk = pdata->eesk;
+
+       priv->model = pdata->model;
+       priv->read = adm6996_read_gpio_reg;
+       priv->write = adm6996_write_gpio_reg;
+
+       ret = devm_gpio_request(&pdev->dev, priv->eecs, "adm_eecs");
+       if (ret)
+               return ret;
+       ret = devm_gpio_request(&pdev->dev, priv->eedi, "adm_eedi");
+       if (ret)
+               return ret;
+       ret = devm_gpio_request(&pdev->dev, priv->eesk, "adm_eesk");
+       if (ret)
+               return ret;
+
+       ret = adm6996_switch_init(priv, dev_name(&pdev->dev), NULL);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+}
+
+static int adm6996_gpio_remove(struct platform_device *pdev)
+{
+       struct adm6996_priv *priv = platform_get_drvdata(pdev);
+
+       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
+               unregister_switch(&priv->dev);
+
+       return 0;
+}
+
+static struct platform_driver adm6996_gpio_driver = {
+       .probe = adm6996_gpio_probe,
+       .remove = adm6996_gpio_remove,
+       .driver = {
+               .name = "adm6996_gpio",
+       },
+};
+
+static int __init adm6996_init(void)
+{
+       int err;
+
+       phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup);
+       err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE);
+       if (err)
+               return err;
+
+       err = platform_driver_register(&adm6996_gpio_driver);
+       if (err)
+               phy_driver_unregister(&adm6996_phy_driver);
+
+       return err;
+}
+
+static void __exit adm6996_exit(void)
+{
+       platform_driver_unregister(&adm6996_gpio_driver);
+       phy_driver_unregister(&adm6996_phy_driver);
+}
+
+module_init(adm6996_init);
+module_exit(adm6996_exit);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/adm6996.h b/target/linux/generic/files-4.14/drivers/net/phy/adm6996.h
new file mode 100644 (file)
index 0000000..6fd460a
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * ADM6996 switch driver
+ *
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ * Copyright (c) 2010,2011 Peter Lebbing <peter@digitalbrains.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#ifndef __ADM6996_H
+#define __ADM6996_H
+
+/*
+ * ADM_PHY_PORTS: Number of ports with a PHY.
+ * We only control ports 0 to 3, because if 4 is connected, it is most likely
+ * not connected to the switch but to a separate MII and MAC for the WAN port.
+ */
+#define ADM_PHY_PORTS  4
+#define ADM_NUM_PORTS  6
+#define ADM_CPU_PORT   5
+
+#define ADM_NUM_VLANS 16
+#define ADM_VLAN_MAX_ID 4094
+
+enum admreg {
+       ADM_EEPROM_BASE         = 0x0,
+               ADM_P0_CFG              = ADM_EEPROM_BASE + 1,
+               ADM_P1_CFG              = ADM_EEPROM_BASE + 3,
+               ADM_P2_CFG              = ADM_EEPROM_BASE + 5,
+               ADM_P3_CFG              = ADM_EEPROM_BASE + 7,
+               ADM_P4_CFG              = ADM_EEPROM_BASE + 8,
+               ADM_P5_CFG              = ADM_EEPROM_BASE + 9,
+               ADM_SYSC0               = ADM_EEPROM_BASE + 0xa,
+               ADM_VLAN_PRIOMAP        = ADM_EEPROM_BASE + 0xe,
+               ADM_SYSC3               = ADM_EEPROM_BASE + 0x11,
+               /* Input Force No Tag Enable */
+               ADM_IFNTE               = ADM_EEPROM_BASE + 0x20,
+               ADM_VID_CHECK           = ADM_EEPROM_BASE + 0x26,
+               ADM_P0_PVID             = ADM_EEPROM_BASE + 0x28,
+               ADM_P1_PVID             = ADM_EEPROM_BASE + 0x29,
+               /* Output Tag Bypass Enable and P2 PVID */
+               ADM_OTBE_P2_PVID        = ADM_EEPROM_BASE + 0x2a,
+               ADM_P3_P4_PVID          = ADM_EEPROM_BASE + 0x2b,
+               ADM_P5_PVID             = ADM_EEPROM_BASE + 0x2c,
+       ADM_EEPROM_EXT_BASE     = 0x40,
+#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n))
+#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n))
+#define ADM_VLAN_MAP(n) (ADM_EEPROM_BASE + 0x13 + n)
+       ADM_COUNTER_BASE        = 0xa0,
+               ADM_SIG0                = ADM_COUNTER_BASE + 0,
+               ADM_SIG1                = ADM_COUNTER_BASE + 1,
+               ADM_PS0         = ADM_COUNTER_BASE + 2,
+               ADM_PS1         = ADM_COUNTER_BASE + 3,
+               ADM_PS2         = ADM_COUNTER_BASE + 4,
+               ADM_CL0         = ADM_COUNTER_BASE + 8, /* RxPacket */
+               ADM_CL6         = ADM_COUNTER_BASE + 0x1a, /* RxByte */
+               ADM_CL12                = ADM_COUNTER_BASE + 0x2c, /* TxPacket */
+               ADM_CL18                = ADM_COUNTER_BASE + 0x3e, /* TxByte */
+               ADM_CL24                = ADM_COUNTER_BASE + 0x50, /* Coll */
+               ADM_CL30                = ADM_COUNTER_BASE + 0x62, /* Err */
+#define ADM_OFFSET_PORT(n) ((n * 4) - (n / 4) * 2 - (n / 5) * 2)
+       ADM_PHY_BASE            = 0x200,
+#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
+};
+
+/* Chip identification patterns */
+#define        ADM_SIG0_MASK   0xffff
+#define ADM_SIG0_VAL   0x1023
+#define ADM_SIG1_MASK  0xffff
+#define ADM_SIG1_VAL   0x0007
+
+enum {
+       ADM_PHYCFG_COLTST     = (1 << 7),       /* Enable collision test */
+       ADM_PHYCFG_DPLX       = (1 << 8),       /* Enable full duplex */
+       ADM_PHYCFG_ANEN_RST   = (1 << 9),       /* Restart auto negotiation (self clear) */
+       ADM_PHYCFG_ISO        = (1 << 10),      /* Isolate PHY */
+       ADM_PHYCFG_PDN        = (1 << 11),      /* Power down PHY */
+       ADM_PHYCFG_ANEN       = (1 << 12),      /* Enable auto negotiation */
+       ADM_PHYCFG_SPEED_100  = (1 << 13),      /* Enable 100 Mbit/s */
+       ADM_PHYCFG_LPBK       = (1 << 14),      /* Enable loopback operation */
+       ADM_PHYCFG_RST        = (1 << 15),      /* Reset the port (self clear) */
+       ADM_PHYCFG_INIT = (
+               ADM_PHYCFG_RST |
+               ADM_PHYCFG_SPEED_100 |
+               ADM_PHYCFG_ANEN |
+               ADM_PHYCFG_ANEN_RST
+       )
+};
+
+enum {
+       ADM_PORTCFG_FC        = (1 << 0),       /* Enable 802.x flow control */
+       ADM_PORTCFG_AN        = (1 << 1),       /* Enable auto-negotiation */
+       ADM_PORTCFG_SPEED_100 = (1 << 2),       /* Enable 100 Mbit/s */
+       ADM_PORTCFG_DPLX      = (1 << 3),       /* Enable full duplex */
+       ADM_PORTCFG_OT        = (1 << 4),       /* Output tagged packets */
+       ADM_PORTCFG_PD        = (1 << 5),       /* Port disable */
+       ADM_PORTCFG_TV_PRIO   = (1 << 6),       /* 0 = VLAN based priority
+                                                * 1 = TOS based priority */
+       ADM_PORTCFG_PPE       = (1 << 7),       /* Port based priority enable */
+       ADM_PORTCFG_PP_S      = (1 << 8),       /* Port based priority, 2 bits */
+       ADM_PORTCFG_PVID_BASE = (1 << 10),      /* Primary VLAN id, 4 bits */
+       ADM_PORTCFG_FSE       = (1 << 14),      /* Fx select enable */
+       ADM_PORTCFG_CAM       = (1 << 15),      /* Crossover Auto MDIX */
+
+       ADM_PORTCFG_INIT = (
+               ADM_PORTCFG_FC |
+               ADM_PORTCFG_AN |
+               ADM_PORTCFG_SPEED_100 |
+               ADM_PORTCFG_DPLX |
+               ADM_PORTCFG_CAM
+       ),
+       ADM_PORTCFG_CPU = (
+               ADM_PORTCFG_FC |
+               ADM_PORTCFG_SPEED_100 |
+               ADM_PORTCFG_OT |
+               ADM_PORTCFG_DPLX
+       ),
+};
+
+#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8)
+#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
+#define ADM_PORTCFG_PVID_MASK (0xf << 10)
+
+#define ADM_IFNTE_MASK (0x3f << 9)
+#define ADM_VID_CHECK_MASK (0x3f << 6)
+
+#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8)
+#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P2_PVID_MASK 0xff
+
+#define ADM_OTBE(n) (((n) & 0x3f) << 8)
+#define ADM_OTBE_MASK (0x3f << 8)
+
+/* ADM_SYSC0 */
+enum {
+       ADM_NTTE        = (1 << 2),     /* New Tag Transmit Enable */
+       ADM_RVID1       = (1 << 8)      /* Replace VLAN ID 1 */
+};
+
+/* Tag Based VLAN in ADM_SYSC3 */
+#define ADM_MAC_CLONE  BIT(4)
+#define ADM_TBV                BIT(5)
+
+static const u8 adm_portcfg[] = {
+       [0] = ADM_P0_CFG,
+       [1] = ADM_P1_CFG,
+       [2] = ADM_P2_CFG,
+       [3] = ADM_P3_CFG,
+       [4] = ADM_P4_CFG,
+       [5] = ADM_P5_CFG,
+};
+
+/* Fields in ADM_VLAN_FILT_L(x) */
+#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12)
+#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6)
+#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0)
+#define ADM_VLAN_FILT_MEMBER_MASK 0x3f
+/* Fields in ADM_VLAN_FILT_H(x) */
+#define ADM_VLAN_FILT_VALID (1 << 15)
+#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0)
+
+/* Convert ports to a form for ADM6996L VLAN map */
+#define ADM_VLAN_FILT(ports) ((ports & 0x01) | ((ports & 0x02) << 1) | \
+                       ((ports & 0x04) << 2) | ((ports & 0x08) << 3) | \
+                       ((ports & 0x10) << 3) | ((ports & 0x20) << 3))
+
+/* Port status register */
+enum {
+       ADM_PS_LS = (1 << 0),   /* Link status */
+       ADM_PS_SS = (1 << 1),   /* Speed status */
+       ADM_PS_DS = (1 << 2),   /* Duplex status */
+       ADM_PS_FCS = (1 << 3)   /* Flow control status */
+};
+
+/*
+ * Split the register address in phy id and register
+ * it will get combined again by the mdio bus op
+ */
+#define PHYADDR(_reg)  ((_reg >> 5) & 0xff), (_reg & 0x1f)
+
+#endif
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/ar8216.c b/target/linux/generic/files-4.14/drivers/net/phy/ar8216.c
new file mode 100644 (file)
index 0000000..7512ee1
--- /dev/null
@@ -0,0 +1,2313 @@
+/*
+ * ar8216.c: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/bitops.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/lockdep.h>
+#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/version.h>
+
+#include "ar8216.h"
+
+extern const struct ar8xxx_chip ar8327_chip;
+extern const struct ar8xxx_chip ar8337_chip;
+
+#define AR8XXX_MIB_WORK_DELAY  2000 /* msecs */
+
+#define MIB_DESC(_s , _o, _n)  \
+       {                       \
+               .size = (_s),   \
+               .offset = (_o), \
+               .name = (_n),   \
+       }
+
+static const struct ar8xxx_mib_desc ar8216_mibs[] = {
+       MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"),
+       MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"),
+       MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"),
+       MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"),
+       MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"),
+       MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"),
+       MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"),
+       MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"),
+       MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"),
+       MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"),
+       MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"),
+       MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"),
+       MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"),
+       MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"),
+       MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"),
+       MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"),
+       MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"),
+       MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"),
+       MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"),
+       MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"),
+       MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"),
+       MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"),
+       MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"),
+       MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"),
+       MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"),
+       MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"),
+       MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"),
+       MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"),
+       MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"),
+       MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"),
+       MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"),
+       MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"),
+       MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"),
+       MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"),
+       MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"),
+       MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"),
+       MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"),
+};
+
+const struct ar8xxx_mib_desc ar8236_mibs[39] = {
+       MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"),
+       MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"),
+       MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"),
+       MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"),
+       MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"),
+       MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"),
+       MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"),
+       MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"),
+       MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"),
+       MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"),
+       MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"),
+       MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"),
+       MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"),
+       MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"),
+       MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"),
+       MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"),
+       MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"),
+       MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"),
+       MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"),
+       MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"),
+       MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"),
+       MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"),
+       MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"),
+       MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"),
+       MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"),
+       MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"),
+       MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"),
+       MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"),
+       MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"),
+       MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"),
+       MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"),
+       MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"),
+       MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"),
+       MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"),
+       MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"),
+       MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"),
+       MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"),
+       MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"),
+       MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"),
+};
+
+static DEFINE_MUTEX(ar8xxx_dev_list_lock);
+static LIST_HEAD(ar8xxx_dev_list);
+
+/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
+static int
+ar8xxx_phy_poll_reset(struct mii_bus *bus)
+{
+        unsigned int sleep_msecs = 20;
+        int ret, elapsed, i;
+
+        for (elapsed = sleep_msecs; elapsed <= 600;
+            elapsed += sleep_msecs) {
+                msleep(sleep_msecs);
+                for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+                        ret = mdiobus_read(bus, i, MII_BMCR);
+                        if (ret < 0)
+                               return ret;
+                        if (ret & BMCR_RESET)
+                               break;
+                        if (i == AR8XXX_NUM_PHYS - 1) {
+                                usleep_range(1000, 2000);
+                                return 0;
+                        }
+                }
+        }
+        return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_phy_check_aneg(struct phy_device *phydev)
+{
+       int ret;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return 0;
+       /*
+        * BMCR_ANENABLE might have been cleared
+        * by phy_init_hw in certain kernel versions
+        * therefore check for it
+        */
+       ret = phy_read(phydev, MII_BMCR);
+       if (ret < 0)
+               return ret;
+       if (ret & BMCR_ANENABLE)
+               return 0;
+
+       dev_info(&phydev->mdio.dev, "ANEG disabled, re-enabling ...\n");
+       ret |= BMCR_ANENABLE | BMCR_ANRESTART;
+       return phy_write(phydev, MII_BMCR, ret);
+}
+
+void
+ar8xxx_phy_init(struct ar8xxx_priv *priv)
+{
+       int i;
+       struct mii_bus *bus;
+
+       bus = priv->mii_bus;
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+               if (priv->chip->phy_fixup)
+                       priv->chip->phy_fixup(priv, i);
+
+               /* initialize the port itself */
+               mdiobus_write(bus, i, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+               if (ar8xxx_has_gige(priv))
+                       mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
+               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
+       }
+
+       ar8xxx_phy_poll_reset(bus);
+}
+
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 lo, hi;
+
+       lo = bus->read(bus, phy_id, regnum);
+       hi = bus->read(bus, phy_id, regnum + 1);
+
+       return (hi << 16) | lo;
+}
+
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 lo, hi;
+
+       lo = val & 0xffff;
+       hi = (u16) (val >> 16);
+
+       if (priv->chip->mii_lo_first)
+       {
+               bus->write(bus, phy_id, regnum, lo);
+               bus->write(bus, phy_id, regnum + 1, hi);
+       } else {
+               bus->write(bus, phy_id, regnum + 1, hi);
+               bus->write(bus, phy_id, regnum, lo);
+       }
+}
+
+u32
+ar8xxx_read(struct ar8xxx_priv *priv, int reg)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+       u32 val;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+       val = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
+
+       mutex_unlock(&bus->mdio_lock);
+
+       return val;
+}
+
+void
+ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, val);
+
+       mutex_unlock(&bus->mdio_lock);
+}
+
+u32
+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+       u32 ret;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+
+       ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
+       ret &= ~mask;
+       ret |= val;
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret);
+
+       mutex_unlock(&bus->mdio_lock);
+
+       return ret;
+}
+
+void
+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
+                    u16 dbg_addr, u16 dbg_data)
+{
+       struct mii_bus *bus = priv->mii_bus;
+
+       mutex_lock(&bus->mdio_lock);
+       bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
+       bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
+       mutex_unlock(&bus->mdio_lock);
+}
+
+static inline void
+ar8xxx_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg)
+{
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, reg);
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
+}
+
+void
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data)
+{
+       struct mii_bus *bus = priv->mii_bus;
+
+       mutex_lock(&bus->mdio_lock);
+       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
+       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data);
+       mutex_unlock(&bus->mdio_lock);
+}
+
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 data;
+
+       mutex_lock(&bus->mdio_lock);
+       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
+       data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
+       mutex_unlock(&bus->mdio_lock);
+
+       return data;
+}
+
+static int
+ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
+               unsigned timeout)
+{
+       int i;
+
+       for (i = 0; i < timeout; i++) {
+               u32 t;
+
+               t = ar8xxx_read(priv, reg);
+               if ((t & mask) == val)
+                       return 0;
+
+               usleep_range(1000, 2000);
+               cond_resched();
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
+{
+       unsigned mib_func = priv->chip->mib_func;
+       int ret;
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       /* Capture the hardware statistics for all ports */
+       ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S));
+
+       /* Wait for the capturing to complete. */
+       ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+out:
+       return ret;
+}
+
+static int
+ar8xxx_mib_capture(struct ar8xxx_priv *priv)
+{
+       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE);
+}
+
+static int
+ar8xxx_mib_flush(struct ar8xxx_priv *priv)
+{
+       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH);
+}
+
+static void
+ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush)
+{
+       unsigned int base;
+       u64 *mib_stats;
+       int i;
+
+       WARN_ON(port >= priv->dev.ports);
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       base = priv->chip->reg_port_stats_start +
+              priv->chip->reg_port_stats_length * port;
+
+       mib_stats = &priv->mib_stats[port * priv->chip->num_mibs];
+       for (i = 0; i < priv->chip->num_mibs; i++) {
+               const struct ar8xxx_mib_desc *mib;
+               u64 t;
+
+               mib = &priv->chip->mib_decs[i];
+               t = ar8xxx_read(priv, base + mib->offset);
+               if (mib->size == 2) {
+                       u64 hi;
+
+                       hi = ar8xxx_read(priv, base + mib->offset + 4);
+                       t |= hi << 32;
+               }
+
+               if (flush)
+                       mib_stats[i] = 0;
+               else
+                       mib_stats[i] += t;
+               cond_resched();
+       }
+}
+
+static void
+ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
+                     struct switch_port_link *link)
+{
+       u32 status;
+       u32 speed;
+
+       memset(link, '\0', sizeof(*link));
+
+       status = priv->chip->read_port_status(priv, port);
+
+       link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO);
+       if (link->aneg) {
+               link->link = !!(status & AR8216_PORT_STATUS_LINK_UP);
+       } else {
+               link->link = true;
+
+               if (priv->get_port_link) {
+                       int err;
+
+                       err = priv->get_port_link(port);
+                       if (err >= 0)
+                               link->link = !!err;
+               }
+       }
+
+       if (!link->link)
+               return;
+
+       link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX);
+       link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
+       link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
+
+       if (link->aneg && link->duplex && priv->chip->read_port_eee_status)
+               link->eee = priv->chip->read_port_eee_status(priv, port);
+
+       speed = (status & AR8216_PORT_STATUS_SPEED) >>
+                AR8216_PORT_STATUS_SPEED_S;
+
+       switch (speed) {
+       case AR8216_PORT_SPEED_10M:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case AR8216_PORT_SPEED_100M:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case AR8216_PORT_SPEED_1000M:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+}
+
+static struct sk_buff *
+ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ar8xxx_priv *priv = dev->phy_ptr;
+       unsigned char *buf;
+
+       if (unlikely(!priv))
+               goto error;
+
+       if (!priv->vlan)
+               goto send;
+
+       if (unlikely(skb_headroom(skb) < 2)) {
+               if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0)
+                       goto error;
+       }
+
+       buf = skb_push(skb, 2);
+       buf[0] = 0x10;
+       buf[1] = 0x80;
+
+send:
+       return skb;
+
+error:
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static void
+ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ar8xxx_priv *priv;
+       unsigned char *buf;
+       int port, vlan;
+
+       priv = dev->phy_ptr;
+       if (!priv)
+               return;
+
+       /* don't strip the header if vlan mode is disabled */
+       if (!priv->vlan)
+               return;
+
+       /* strip header, get vlan id */
+       buf = skb->data;
+       skb_pull(skb, 2);
+
+       /* check for vlan header presence */
+       if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00))
+               return;
+
+       port = buf[0] & 0x7;
+
+       /* no need to fix up packets coming from a tagged source */
+       if (priv->vlan_tagged & (1 << port))
+               return;
+
+       /* lookup port vid from local table, the switch passes an invalid vlan id */
+       vlan = priv->vlan_id[priv->pvid[port]];
+
+       buf[14 + 2] &= 0xf0;
+       buf[14 + 2] |= vlan >> 8;
+       buf[15 + 2] = vlan & 0xff;
+}
+
+int
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
+{
+       int timeout = 20;
+       u32 t = 0;
+
+       while (1) {
+               t = ar8xxx_read(priv, reg);
+               if ((t & mask) == val)
+                       return 0;
+
+               if (timeout-- <= 0)
+                       break;
+
+               udelay(10);
+               cond_resched();
+       }
+
+       pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n",
+              (unsigned int) reg, t, mask, val);
+       return -ETIMEDOUT;
+}
+
+static void
+ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
+{
+       if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0))
+               return;
+       if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) {
+               val &= AR8216_VTUDATA_MEMBER;
+               val |= AR8216_VTUDATA_VALID;
+               ar8xxx_write(priv, AR8216_REG_VTU_DATA, val);
+       }
+       op |= AR8216_VTU_ACTIVE;
+       ar8xxx_write(priv, AR8216_REG_VTU, op);
+}
+
+static void
+ar8216_vtu_flush(struct ar8xxx_priv *priv)
+{
+       ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
+}
+
+static void
+ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
+{
+       u32 op;
+
+       op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S);
+       ar8216_vtu_op(priv, op, port_mask);
+}
+
+static int
+ar8216_atu_flush(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret)
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH |
+                                                        AR8216_ATU_ACTIVE);
+
+       return ret;
+}
+
+static int
+ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret) {
+               t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT;
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t);
+       }
+
+       return ret;
+}
+
+static u32
+ar8216_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+       return ar8xxx_read(priv, AR8216_REG_PORT_STATUS(port));
+}
+
+static void
+ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 header;
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU)
+               header = AR8216_PORT_CTRL_HEADER;
+       else
+               header = 0;
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
+                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
+                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
+                  AR8216_PORT_CTRL_LEARN | header |
+                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
+                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port),
+                  AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE |
+                  AR8216_PORT_VLAN_DEFAULT_ID,
+                  (members << AR8216_PORT_VLAN_DEST_PORTS_S) |
+                  (ingress << AR8216_PORT_VLAN_MODE_S) |
+                  (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S));
+}
+
+static int
+ar8216_hw_init(struct ar8xxx_priv *priv)
+{
+       if (priv->initialized)
+               return 0;
+
+       ar8xxx_phy_init(priv);
+
+       priv->initialized = true;
+       return 0;
+}
+
+static void
+ar8216_init_globals(struct ar8xxx_priv *priv)
+{
+       /* standard atheros magic */
+       ar8xxx_write(priv, 0x38, 0xc000050e);
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8216_GCTRL_MTU, 1518 + 8 + 2);
+}
+
+static void
+ar8216_init_port(struct ar8xxx_priv *priv, int port)
+{
+       /* Enable port learning and tx */
+       ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port),
+               AR8216_PORT_CTRL_LEARN |
+               (4 << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_write(priv, AR8216_REG_PORT_VLAN(port), 0);
+
+       if (port == AR8216_PORT_CPU) {
+               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
+                       AR8216_PORT_STATUS_LINK_UP |
+                       (ar8xxx_has_gige(priv) ?
+                                AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) |
+                       AR8216_PORT_STATUS_TXMAC |
+                       AR8216_PORT_STATUS_RXMAC |
+                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) |
+                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) |
+                       AR8216_PORT_STATUS_DUPLEX);
+       } else {
+               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
+                       AR8216_PORT_STATUS_LINK_AUTO);
+       }
+}
+
+static void
+ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) {
+               udelay(10);
+               cond_resched();
+       }
+
+       if (!timeout)
+               pr_err("ar8216: timeout waiting for atu to become ready\n");
+}
+
+static void ar8216_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_func0, r1_func1, r1_func2;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e;
+       r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT);
+               ar8xxx_mii_write32(priv, r2, r1_func1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_func2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               t = ar8xxx_mii_read32(priv, r2, r1_func0);
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_mii_write32(priv, r2, r1_func0, t);
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_func0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_func1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_func2);
+
+               *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8216_ATU_PORT0;
+               while (!(val2 & t) && ++i < priv->dev.ports)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S;
+               a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S;
+               a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S;
+               a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S;
+               a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S;
+               a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S;
+               break;
+       }
+}
+
+static void
+ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
+                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
+                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
+                  AR8216_PORT_CTRL_LEARN |
+                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
+                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port),
+                  AR8236_PORT_VLAN_DEFAULT_ID,
+                  (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S));
+
+       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port),
+                  AR8236_PORT_VLAN2_VLAN_MODE |
+                  AR8236_PORT_VLAN2_MEMBER,
+                  (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) |
+                  (members << AR8236_PORT_VLAN2_MEMBER_S));
+}
+
+static void
+ar8236_init_globals(struct ar8xxx_priv *priv)
+{
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
+
+       /* enable cpu port to receive arp frames */
+       ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL,
+                  AR8236_ATU_CTRL_RES);
+
+       /* enable cpu port to receive multicast and broadcast frames */
+       ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK,
+                  AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN);
+
+       /* Enable MIB counters */
+       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
+                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
+                  AR8236_MIB_EN);
+}
+
+static int
+ar8316_hw_init(struct ar8xxx_priv *priv)
+{
+       u32 val, newval;
+
+       val = ar8xxx_read(priv, AR8316_REG_POSTRIP);
+
+       if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
+               if (priv->port4_phy) {
+                       /* value taken from Ubiquiti RouterStation Pro */
+                       newval = 0x81461bea;
+                       pr_info("ar8316: Using port 4 as PHY\n");
+               } else {
+                       newval = 0x01261be2;
+                       pr_info("ar8316: Using port 4 as switch port\n");
+               }
+       } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) {
+               /* value taken from AVM Fritz!Box 7390 sources */
+               newval = 0x010e5b71;
+       } else {
+               /* no known value for phy interface */
+               pr_err("ar8316: unsupported mii mode: %d.\n",
+                      priv->phy->interface);
+               return -EINVAL;
+       }
+
+       if (val == newval)
+               goto out;
+
+       ar8xxx_write(priv, AR8316_REG_POSTRIP, newval);
+
+       if (priv->port4_phy &&
+           priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
+               /* work around for phy4 rgmii mode */
+               ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c);
+               /* rx delay */
+               ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e);
+               /* tx delay */
+               ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47);
+               msleep(1000);
+       }
+
+       ar8xxx_phy_init(priv);
+
+out:
+       priv->initialized = true;
+       return 0;
+}
+
+static void
+ar8316_init_globals(struct ar8xxx_priv *priv)
+{
+       /* standard atheros magic */
+       ar8xxx_write(priv, 0x38, 0xc000050e);
+
+       /* enable cpu port to receive multicast and broadcast frames */
+       ar8xxx_write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f);
+
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
+
+       /* Enable MIB counters */
+       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
+                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
+                  AR8236_MIB_EN);
+}
+
+int
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       priv->vlan = !!val->value.i;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->vlan;
+       return 0;
+}
+
+
+int
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       /* make sure no invalid PVIDs get set */
+
+       if (vlan < 0 || vlan >= dev->vlans ||
+           port < 0 || port >= AR8X16_MAX_PORTS)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       if (port < 0 || port >= AR8X16_MAX_PORTS)
+               return -EINVAL;
+
+       *vlan = priv->pvid[port];
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       if (val->port_vlan >= AR8X16_MAX_VLANS)
+               return -EINVAL;
+
+       priv->vlan_id[val->port_vlan] = val->value.i;
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->vlan_id[val->port_vlan];
+       return 0;
+}
+
+int
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       ar8216_read_port_link(priv, port, link);
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports;
+       int i;
+
+       if (val->port_vlan >= AR8X16_MAX_VLANS)
+               return -EINVAL;
+
+       ports = priv->vlan_table[val->port_vlan];
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (priv->vlan_tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i, j;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       priv->vlan_tagged |= (1 << p->id);
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+
+                       /* make sure that an untagged port does not
+                        * appear in other vlans */
+                       for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+                               if (j == val->port_vlan)
+                                       continue;
+                               priv->vlan_table[j] &= ~(1 << p->id);
+                       }
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static void
+ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+       for (port = 0; port < AR8216_NUM_PORTS; port++) {
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8216_NUM_PORTS ||
+           priv->monitor_port >= AR8216_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+       if (priv->mirror_tx)
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
+}
+
+static inline u32
+ar8xxx_age_time_val(int age_time)
+{
+       return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) /
+              AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS;
+}
+
+static inline void
+ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg)
+{
+       u32 age_time = ar8xxx_age_time_val(priv->arl_age_time);
+       ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S);
+}
+
+int
+ar8xxx_sw_hw_apply(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       u8 portmask[AR8X16_MAX_PORTS];
+       int i, j;
+
+       mutex_lock(&priv->reg_mutex);
+       /* flush all vlan translation unit entries */
+       priv->chip->vtu_flush(priv);
+
+       memset(portmask, 0, sizeof(portmask));
+       if (!priv->init) {
+               /* calculate the port destination masks and load vlans
+                * into the vlan translation unit */
+               for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+                       u8 vp = priv->vlan_table[j];
+
+                       if (!vp)
+                               continue;
+
+                       for (i = 0; i < dev->ports; i++) {
+                               u8 mask = (1 << i);
+                               if (vp & mask)
+                                       portmask[i] |= vp & ~mask;
+                       }
+
+                       chip->vtu_load_vlan(priv, priv->vlan_id[j],
+                                           priv->vlan_table[j]);
+               }
+       } else {
+               /* vlan disabled:
+                * isolate all ports, but connect them to the cpu port */
+               for (i = 0; i < dev->ports; i++) {
+                       if (i == AR8216_PORT_CPU)
+                               continue;
+
+                       portmask[i] = 1 << AR8216_PORT_CPU;
+                       portmask[AR8216_PORT_CPU] |= (1 << i);
+               }
+       }
+
+       /* update the port destination mask registers and tag settings */
+       for (i = 0; i < dev->ports; i++) {
+               chip->setup_port(priv, i, portmask[i]);
+       }
+
+       chip->set_mirror_regs(priv);
+
+       /* set age time */
+       if (chip->reg_arl_ctrl)
+               ar8xxx_set_age_time(priv, chip->reg_arl_ctrl);
+
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+int
+ar8xxx_sw_reset_switch(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+       memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) -
+               offsetof(struct ar8xxx_priv, vlan));
+
+       for (i = 0; i < AR8X16_MAX_VLANS; i++)
+               priv->vlan_id[i] = i;
+
+       /* Configure all ports */
+       for (i = 0; i < dev->ports; i++)
+               chip->init_port(priv, i);
+
+       priv->mirror_rx = false;
+       priv->mirror_tx = false;
+       priv->source_port = 0;
+       priv->monitor_port = 0;
+       priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME;
+
+       chip->init_globals(priv);
+       chip->atu_flush(priv);
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return chip->sw_hw_apply(dev);
+}
+
+int
+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       unsigned int len;
+       int ret;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->mib_lock);
+
+       len = priv->dev.ports * priv->chip->num_mibs *
+             sizeof(*priv->mib_stats);
+       memset(priv->mib_stats, '\0', len);
+       ret = ar8xxx_mib_flush(priv);
+       if (ret)
+               goto unlock;
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_rx = !!val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_rx;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_tx = !!val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_tx;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->monitor_port = val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->monitor_port;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->source_port = val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->source_port;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port;
+       int ret;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+       ret = ar8xxx_mib_capture(priv);
+       if (ret)
+               goto unlock;
+
+       ar8xxx_mib_fetch_port_stat(priv, port, true);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+static void
+ar8xxx_byte_to_str(char *buf, int len, u64 byte)
+{
+       unsigned long b;
+       const char *unit;
+
+       if (byte >= 0x40000000) { /* 1 GiB */
+               b = byte * 10 / 0x40000000;
+               unit = "GiB";
+       } else if (byte >= 0x100000) { /* 1 MiB */
+               b = byte * 10 / 0x100000;
+               unit = "MiB";
+       } else if (byte >= 0x400) { /* 1 KiB */
+               b = byte * 10 / 0x400;
+               unit = "KiB";
+       } else {
+               b = byte;
+               unit = "Byte";
+       }
+       if (strcmp(unit, "Byte"))
+               snprintf(buf, len, "%lu.%lu %s", b / 10, b % 10, unit);
+       else
+               snprintf(buf, len, "%lu %s", b, unit);
+}
+
+int
+ar8xxx_sw_get_port_mib(struct switch_dev *dev,
+                      const struct switch_attr *attr,
+                      struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       u64 *mib_stats, mib_data;
+       unsigned int port;
+       int ret;
+       char *buf = priv->buf;
+       char buf1[64];
+       const char *mib_name;
+       int i, len = 0;
+       bool mib_stats_empty = true;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+       ret = ar8xxx_mib_capture(priv);
+       if (ret)
+               goto unlock;
+
+       ar8xxx_mib_fetch_port_stat(priv, port, false);
+
+       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                       "MIB counters\n");
+
+       mib_stats = &priv->mib_stats[port * chip->num_mibs];
+       for (i = 0; i < chip->num_mibs; i++) {
+               mib_name = chip->mib_decs[i].name;
+               mib_data = mib_stats[i];
+               len += snprintf(buf + len, sizeof(priv->buf) - len,
+                               "%-12s: %llu\n", mib_name, mib_data);
+               if ((!strcmp(mib_name, "TxByte") ||
+                   !strcmp(mib_name, "RxGoodByte")) &&
+                   mib_data >= 1024) {
+                       ar8xxx_byte_to_str(buf1, sizeof(buf1), mib_data);
+                       --len; /* discard newline at the end of buf */
+                       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                                       " (%s)\n", buf1);
+               }
+               if (mib_stats_empty && mib_data)
+                       mib_stats_empty = false;
+       }
+
+       if (mib_stats_empty)
+               len = snprintf(buf, sizeof(priv->buf), "No MIB data");
+
+       val->value.s = buf;
+       val->len = len;
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+                          struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int age_time = val->value.i;
+       u32 age_time_val;
+
+       if (age_time < 0)
+               return -EINVAL;
+
+       age_time_val = ar8xxx_age_time_val(age_time);
+       if (age_time_val == 0 || age_time_val > 0xffff)
+               return -EINVAL;
+
+       priv->arl_age_time = age_time;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+                   struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->arl_age_time;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct mii_bus *bus = priv->mii_bus;
+       const struct ar8xxx_chip *chip = priv->chip;
+       char *buf = priv->arl_buf;
+       int i, j, k, len = 0;
+       struct arl_entry *a, *a1;
+       u32 status;
+
+       if (!chip->get_arl_entry)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->reg_mutex);
+       mutex_lock(&bus->mdio_lock);
+
+       chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE);
+
+       for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) {
+               a = &priv->arl_table[i];
+               duplicate:
+               chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT);
+
+               if (!status)
+                       break;
+
+               /* avoid duplicates
+                * ARL table can include multiple valid entries
+                * per MAC, just with differing status codes
+                */
+               for (j = 0; j < i; ++j) {
+                       a1 = &priv->arl_table[j];
+                       if (a->port == a1->port && !memcmp(a->mac, a1->mac, sizeof(a->mac)))
+                               goto duplicate;
+               }
+       }
+
+       mutex_unlock(&bus->mdio_lock);
+
+       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                        "address resolution table\n");
+
+       if (i == AR8XXX_NUM_ARL_RECORDS)
+               len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                               "Too many entries found, displaying the first %d only!\n",
+                               AR8XXX_NUM_ARL_RECORDS);
+
+       for (j = 0; j < priv->dev.ports; ++j) {
+               for (k = 0; k < i; ++k) {
+                       a = &priv->arl_table[k];
+                       if (a->port != j)
+                               continue;
+                       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                                       "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                       j,
+                                       a->mac[5], a->mac[4], a->mac[3],
+                                       a->mac[2], a->mac[1], a->mac[0]);
+               }
+       }
+
+       val->value.s = buf;
+       val->len = len;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int ret;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port, ret;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush_port(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+static const struct switch_attr ar8xxx_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
+};
+
+const struct switch_attr ar8xxx_sw_attr_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = ar8xxx_sw_set_port_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .set = NULL,
+               .get = ar8xxx_sw_get_port_mib,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
+};
+
+const struct switch_attr ar8xxx_sw_attr_vlan[1] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "vid",
+               .description = "VLAN ID (0-4094)",
+               .set = ar8xxx_sw_set_vid,
+               .get = ar8xxx_sw_get_vid,
+               .max = 4094,
+       },
+};
+
+static const struct switch_dev_ops ar8xxx_sw_ops = {
+       .attr_global = {
+               .attr = ar8xxx_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8xxx_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8xxx_sw_get_ports,
+       .set_vlan_ports = ar8xxx_sw_set_ports,
+       .apply_config = ar8xxx_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+/* The following op is disabled as it hogs the CPU and degrades performance.
+   An implementation has been attempted in 4d8a66d but reading MIB data is slow
+   on ar8xxx switches.
+
+   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
+   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
+   the request to update the MIB counter is cleared. */
+#if 0
+       .get_port_stats = ar8xxx_sw_get_port_stats,
+#endif
+};
+
+static const struct ar8xxx_chip ar8216_chip = {
+       .caps = AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x19000,
+       .reg_port_stats_length = 0xa0,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8216",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8216_NUM_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8216_hw_init,
+       .init_globals = ar8216_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8216_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8216_mibs),
+       .mib_decs = ar8216_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static const struct ar8xxx_chip ar8236_chip = {
+       .caps = AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x20000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8236",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8216_NUM_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8216_hw_init,
+       .init_globals = ar8236_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8236_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static const struct ar8xxx_chip ar8316_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x20000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8316",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8316_hw_init,
+       .init_globals = ar8316_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8216_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static int
+ar8xxx_id_chip(struct ar8xxx_priv *priv)
+{
+       u32 val;
+       u16 id;
+       int i;
+
+       val = ar8xxx_read(priv, AR8216_REG_CTRL);
+       if (val == ~0)
+               return -ENODEV;
+
+       id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
+       for (i = 0; i < AR8X16_PROBE_RETRIES; i++) {
+               u16 t;
+
+               val = ar8xxx_read(priv, AR8216_REG_CTRL);
+               if (val == ~0)
+                       return -ENODEV;
+
+               t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
+               if (t != id)
+                       return -ENODEV;
+       }
+
+       priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S;
+       priv->chip_rev = (id & AR8216_CTRL_REVISION);
+
+       switch (priv->chip_ver) {
+       case AR8XXX_VER_AR8216:
+               priv->chip = &ar8216_chip;
+               break;
+       case AR8XXX_VER_AR8236:
+               priv->chip = &ar8236_chip;
+               break;
+       case AR8XXX_VER_AR8316:
+               priv->chip = &ar8316_chip;
+               break;
+       case AR8XXX_VER_AR8327:
+               priv->chip = &ar8327_chip;
+               break;
+       case AR8XXX_VER_AR8337:
+               priv->chip = &ar8337_chip;
+               break;
+       default:
+               pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n",
+                      priv->chip_ver, priv->chip_rev);
+
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void
+ar8xxx_mib_work_func(struct work_struct *work)
+{
+       struct ar8xxx_priv *priv;
+       int err;
+
+       priv = container_of(work, struct ar8xxx_priv, mib_work.work);
+
+       mutex_lock(&priv->mib_lock);
+
+       err = ar8xxx_mib_capture(priv);
+       if (err)
+               goto next_port;
+
+       ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false);
+
+next_port:
+       priv->mib_next_port++;
+       if (priv->mib_next_port >= priv->dev.ports)
+               priv->mib_next_port = 0;
+
+       mutex_unlock(&priv->mib_lock);
+       schedule_delayed_work(&priv->mib_work,
+                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
+}
+
+static int
+ar8xxx_mib_init(struct ar8xxx_priv *priv)
+{
+       unsigned int len;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return 0;
+
+       BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs);
+
+       len = priv->dev.ports * priv->chip->num_mibs *
+             sizeof(*priv->mib_stats);
+       priv->mib_stats = kzalloc(len, GFP_KERNEL);
+
+       if (!priv->mib_stats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void
+ar8xxx_mib_start(struct ar8xxx_priv *priv)
+{
+       if (!ar8xxx_has_mib_counters(priv))
+               return;
+
+       schedule_delayed_work(&priv->mib_work,
+                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
+}
+
+static void
+ar8xxx_mib_stop(struct ar8xxx_priv *priv)
+{
+       if (!ar8xxx_has_mib_counters(priv))
+               return;
+
+       cancel_delayed_work_sync(&priv->mib_work);
+}
+
+static struct ar8xxx_priv *
+ar8xxx_create(void)
+{
+       struct ar8xxx_priv *priv;
+
+       priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+       INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func);
+
+       return priv;
+}
+
+static void
+ar8xxx_free(struct ar8xxx_priv *priv)
+{
+       if (priv->chip && priv->chip->cleanup)
+               priv->chip->cleanup(priv);
+
+       kfree(priv->chip_data);
+       kfree(priv->mib_stats);
+       kfree(priv);
+}
+
+static int
+ar8xxx_probe_switch(struct ar8xxx_priv *priv)
+{
+       const struct ar8xxx_chip *chip;
+       struct switch_dev *swdev;
+       int ret;
+
+       ret = ar8xxx_id_chip(priv);
+       if (ret)
+               return ret;
+
+       chip = priv->chip;
+
+       swdev = &priv->dev;
+       swdev->cpu_port = AR8216_PORT_CPU;
+       swdev->name = chip->name;
+       swdev->vlans = chip->vlans;
+       swdev->ports = chip->ports;
+       swdev->ops = chip->swops;
+
+       ret = ar8xxx_mib_init(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int
+ar8xxx_start(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->init = true;
+
+       ret = priv->chip->hw_init(priv);
+       if (ret)
+               return ret;
+
+       ret = ar8xxx_sw_reset_switch(&priv->dev);
+       if (ret)
+               return ret;
+
+       priv->init = false;
+
+       ar8xxx_mib_start(priv);
+
+       return 0;
+}
+
+static int
+ar8xxx_phy_config_init(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+       struct net_device *dev = phydev->attached_dev;
+       int ret;
+
+       if (WARN_ON(!priv))
+               return -ENODEV;
+
+       if (priv->chip->config_at_probe)
+               return ar8xxx_phy_check_aneg(phydev);
+
+       priv->phy = phydev;
+
+       if (phydev->mdio.addr != 0) {
+               if (chip_is_ar8316(priv)) {
+                       /* switch device has been initialized, reinit */
+                       priv->dev.ports = (AR8216_NUM_PORTS - 1);
+                       priv->initialized = false;
+                       priv->port4_phy = true;
+                       ar8316_hw_init(priv);
+                       return 0;
+               }
+
+               return 0;
+       }
+
+       ret = ar8xxx_start(priv);
+       if (ret)
+               return ret;
+
+       /* VID fixup only needed on ar8216 */
+       if (chip_is_ar8216(priv)) {
+               dev->phy_ptr = priv;
+               dev->priv_flags |= IFF_NO_IP_ALIGN;
+               dev->eth_mangle_rx = ar8216_mangle_rx;
+               dev->eth_mangle_tx = ar8216_mangle_tx;
+       }
+
+       return 0;
+}
+
+static bool
+ar8xxx_check_link_states(struct ar8xxx_priv *priv)
+{
+       bool link_new, changed = false;
+       u32 status;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       for (i = 0; i < priv->dev.ports; i++) {
+               status = priv->chip->read_port_status(priv, i);
+               link_new = !!(status & AR8216_PORT_STATUS_LINK_UP);
+               if (link_new == priv->link_up[i])
+                       continue;
+
+               priv->link_up[i] = link_new;
+               changed = true;
+               /* flush ARL entries for this port if it went down*/
+               if (!link_new)
+                       priv->chip->atu_flush_port(priv, i);
+               dev_info(&priv->phy->mdio.dev, "Port %d is %s\n",
+                        i, link_new ? "up" : "down");
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return changed;
+}
+
+static int
+ar8xxx_phy_read_status(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+       struct switch_port_link link;
+
+       /* check for switch port link changes */
+       if (phydev->state == PHY_CHANGELINK)
+               ar8xxx_check_link_states(priv);
+
+       if (phydev->mdio.addr != 0)
+               return genphy_read_status(phydev);
+
+       ar8216_read_port_link(priv, phydev->mdio.addr, &link);
+       phydev->link = !!link.link;
+       if (!phydev->link)
+               return 0;
+
+       switch (link.speed) {
+       case SWITCH_PORT_SPEED_10:
+               phydev->speed = SPEED_10;
+               break;
+       case SWITCH_PORT_SPEED_100:
+               phydev->speed = SPEED_100;
+               break;
+       case SWITCH_PORT_SPEED_1000:
+               phydev->speed = SPEED_1000;
+               break;
+       default:
+               phydev->speed = 0;
+       }
+       phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+static int
+ar8xxx_phy_config_aneg(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 0)
+               return 0;
+
+       return genphy_config_aneg(phydev);
+}
+
+static const u32 ar8xxx_phy_ids[] = {
+       0x004dd033,
+       0x004dd034, /* AR8327 */
+       0x004dd036, /* AR8337 */
+       0x004dd041,
+       0x004dd042,
+       0x004dd043, /* AR8236 */
+};
+
+static bool
+ar8xxx_phy_match(u32 phy_id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++)
+               if (phy_id == ar8xxx_phy_ids[i])
+                       return true;
+
+       return false;
+}
+
+static bool
+ar8xxx_is_possible(struct mii_bus *bus)
+{
+       unsigned int i, found_phys = 0;
+
+       for (i = 0; i < 5; i++) {
+               u32 phy_id;
+
+               phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16;
+               phy_id |= mdiobus_read(bus, i, MII_PHYSID2);
+               if (ar8xxx_phy_match(phy_id)) {
+                       found_phys++;
+               } else if (phy_id) {
+                       pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n",
+                                dev_name(&bus->dev), i, phy_id);
+               }
+       }
+       return !!found_phys;
+}
+
+static int
+ar8xxx_phy_probe(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv;
+       struct switch_dev *swdev;
+       int ret;
+
+       /* skip PHYs at unused adresses */
+       if (phydev->mdio.addr != 0 && phydev->mdio.addr != 4)
+               return -ENODEV;
+
+       if (!ar8xxx_is_possible(phydev->mdio.bus))
+               return -ENODEV;
+
+       mutex_lock(&ar8xxx_dev_list_lock);
+       list_for_each_entry(priv, &ar8xxx_dev_list, list)
+               if (priv->mii_bus == phydev->mdio.bus)
+                       goto found;
+
+       priv = ar8xxx_create();
+       if (priv == NULL) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+
+       priv->mii_bus = phydev->mdio.bus;
+
+       ret = ar8xxx_probe_switch(priv);
+       if (ret)
+               goto free_priv;
+
+       swdev = &priv->dev;
+       swdev->alias = dev_name(&priv->mii_bus->dev);
+       ret = register_switch(swdev, NULL);
+       if (ret)
+               goto free_priv;
+
+       pr_info("%s: %s rev. %u switch registered on %s\n",
+               swdev->devname, swdev->name, priv->chip_rev,
+               dev_name(&priv->mii_bus->dev));
+
+       list_add(&priv->list, &ar8xxx_dev_list);
+
+found:
+       priv->use_count++;
+
+       if (phydev->mdio.addr == 0) {
+               if (ar8xxx_has_gige(priv)) {
+                       phydev->supported = SUPPORTED_1000baseT_Full;
+                       phydev->advertising = ADVERTISED_1000baseT_Full;
+               } else {
+                       phydev->supported = SUPPORTED_100baseT_Full;
+                       phydev->advertising = ADVERTISED_100baseT_Full;
+               }
+
+               if (priv->chip->config_at_probe) {
+                       priv->phy = phydev;
+
+                       ret = ar8xxx_start(priv);
+                       if (ret)
+                               goto err_unregister_switch;
+               }
+       } else {
+               if (ar8xxx_has_gige(priv)) {
+                       phydev->supported |= SUPPORTED_1000baseT_Full;
+                       phydev->advertising |= ADVERTISED_1000baseT_Full;
+               }
+       }
+
+       phydev->priv = priv;
+
+       mutex_unlock(&ar8xxx_dev_list_lock);
+
+       return 0;
+
+err_unregister_switch:
+       if (--priv->use_count)
+               goto unlock;
+
+       unregister_switch(&priv->dev);
+
+free_priv:
+       ar8xxx_free(priv);
+unlock:
+       mutex_unlock(&ar8xxx_dev_list_lock);
+       return ret;
+}
+
+static void
+ar8xxx_phy_detach(struct phy_device *phydev)
+{
+       struct net_device *dev = phydev->attached_dev;
+
+       if (!dev)
+               return;
+
+       dev->phy_ptr = NULL;
+       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+       dev->eth_mangle_rx = NULL;
+       dev->eth_mangle_tx = NULL;
+}
+
+static void
+ar8xxx_phy_remove(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+
+       if (WARN_ON(!priv))
+               return;
+
+       phydev->priv = NULL;
+
+       mutex_lock(&ar8xxx_dev_list_lock);
+
+       if (--priv->use_count > 0) {
+               mutex_unlock(&ar8xxx_dev_list_lock);
+               return;
+       }
+
+       list_del(&priv->list);
+       mutex_unlock(&ar8xxx_dev_list_lock);
+
+       unregister_switch(&priv->dev);
+       ar8xxx_mib_stop(priv);
+       ar8xxx_free(priv);
+}
+
+static int
+ar8xxx_phy_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+
+static struct phy_driver ar8xxx_phy_driver[] = {
+       {
+               .phy_id         = 0x004d0000,
+               .name           = "Atheros AR8216/AR8236/AR8316",
+               .phy_id_mask    = 0xffff0000,
+               .features       = PHY_BASIC_FEATURES,
+               .probe          = ar8xxx_phy_probe,
+               .remove         = ar8xxx_phy_remove,
+               .detach         = ar8xxx_phy_detach,
+               .config_init    = ar8xxx_phy_config_init,
+               .config_aneg    = ar8xxx_phy_config_aneg,
+               .read_status    = ar8xxx_phy_read_status,
+               .soft_reset     = ar8xxx_phy_soft_reset,
+       }
+};
+
+module_phy_driver(ar8xxx_phy_driver);
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/ar8216.h b/target/linux/generic/files-4.14/drivers/net/phy/ar8216.h
new file mode 100644 (file)
index 0000000..ba0e0dd
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * ar8216.h: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AR8216_H
+#define __AR8216_H
+
+#define BITS(_s, _n)   (((1UL << (_n)) - 1) << _s)
+
+#define AR8XXX_CAP_GIGE                        BIT(0)
+#define AR8XXX_CAP_MIB_COUNTERS                BIT(1)
+
+#define AR8XXX_NUM_PHYS        5
+#define AR8216_PORT_CPU        0
+#define AR8216_NUM_PORTS       6
+#define AR8216_NUM_VLANS       16
+#define AR8316_NUM_VLANS       4096
+
+/* size of the vlan table */
+#define AR8X16_MAX_VLANS       128
+#define AR8X16_PROBE_RETRIES   10
+#define AR8X16_MAX_PORTS       8
+
+#define AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS      7
+#define AR8XXX_DEFAULT_ARL_AGE_TIME            300
+
+/* Atheros specific MII registers */
+#define MII_ATH_MMD_ADDR               0x0d
+#define MII_ATH_MMD_DATA               0x0e
+#define MII_ATH_DBG_ADDR               0x1d
+#define MII_ATH_DBG_DATA               0x1e
+
+#define AR8216_REG_CTRL                        0x0000
+#define   AR8216_CTRL_REVISION         BITS(0, 8)
+#define   AR8216_CTRL_REVISION_S       0
+#define   AR8216_CTRL_VERSION          BITS(8, 8)
+#define   AR8216_CTRL_VERSION_S                8
+#define   AR8216_CTRL_RESET            BIT(31)
+
+#define AR8216_REG_FLOOD_MASK          0x002C
+#define   AR8216_FM_UNI_DEST_PORTS     BITS(0, 6)
+#define   AR8216_FM_MULTI_DEST_PORTS   BITS(16, 6)
+#define   AR8236_FM_CPU_BROADCAST_EN   BIT(26)
+#define   AR8236_FM_CPU_BCAST_FWD_EN   BIT(25)
+
+#define AR8216_REG_GLOBAL_CTRL         0x0030
+#define   AR8216_GCTRL_MTU             BITS(0, 11)
+#define   AR8236_GCTRL_MTU             BITS(0, 14)
+#define   AR8316_GCTRL_MTU             BITS(0, 14)
+
+#define AR8216_REG_VTU                 0x0040
+#define   AR8216_VTU_OP                        BITS(0, 3)
+#define   AR8216_VTU_OP_NOOP           0x0
+#define   AR8216_VTU_OP_FLUSH          0x1
+#define   AR8216_VTU_OP_LOAD           0x2
+#define   AR8216_VTU_OP_PURGE          0x3
+#define   AR8216_VTU_OP_REMOVE_PORT    0x4
+#define   AR8216_VTU_ACTIVE            BIT(3)
+#define   AR8216_VTU_FULL              BIT(4)
+#define   AR8216_VTU_PORT              BITS(8, 4)
+#define   AR8216_VTU_PORT_S            8
+#define   AR8216_VTU_VID               BITS(16, 12)
+#define   AR8216_VTU_VID_S             16
+#define   AR8216_VTU_PRIO              BITS(28, 3)
+#define   AR8216_VTU_PRIO_S            28
+#define   AR8216_VTU_PRIO_EN           BIT(31)
+
+#define AR8216_REG_VTU_DATA            0x0044
+#define   AR8216_VTUDATA_MEMBER                BITS(0, 10)
+#define   AR8236_VTUDATA_MEMBER                BITS(0, 7)
+#define   AR8216_VTUDATA_VALID         BIT(11)
+
+#define AR8216_REG_ATU_FUNC0           0x0050
+#define   AR8216_ATU_OP                        BITS(0, 3)
+#define   AR8216_ATU_OP_NOOP           0x0
+#define   AR8216_ATU_OP_FLUSH          0x1
+#define   AR8216_ATU_OP_LOAD           0x2
+#define   AR8216_ATU_OP_PURGE          0x3
+#define   AR8216_ATU_OP_FLUSH_UNLOCKED 0x4
+#define   AR8216_ATU_OP_FLUSH_PORT     0x5
+#define   AR8216_ATU_OP_GET_NEXT       0x6
+#define   AR8216_ATU_ACTIVE            BIT(3)
+#define   AR8216_ATU_PORT_NUM          BITS(8, 4)
+#define   AR8216_ATU_PORT_NUM_S                8
+#define   AR8216_ATU_FULL_VIO          BIT(12)
+#define   AR8216_ATU_ADDR5             BITS(16, 8)
+#define   AR8216_ATU_ADDR5_S           16
+#define   AR8216_ATU_ADDR4             BITS(24, 8)
+#define   AR8216_ATU_ADDR4_S           24
+
+#define AR8216_REG_ATU_FUNC1           0x0054
+#define   AR8216_ATU_ADDR3             BITS(0, 8)
+#define   AR8216_ATU_ADDR3_S           0
+#define   AR8216_ATU_ADDR2             BITS(8, 8)
+#define   AR8216_ATU_ADDR2_S           8
+#define   AR8216_ATU_ADDR1             BITS(16, 8)
+#define   AR8216_ATU_ADDR1_S           16
+#define   AR8216_ATU_ADDR0             BITS(24, 8)
+#define   AR8216_ATU_ADDR0_S           24
+
+#define AR8216_REG_ATU_FUNC2           0x0058
+#define   AR8216_ATU_PORTS             BITS(0, 6)
+#define   AR8216_ATU_PORT0             BIT(0)
+#define   AR8216_ATU_PORT1             BIT(1)
+#define   AR8216_ATU_PORT2             BIT(2)
+#define   AR8216_ATU_PORT3             BIT(3)
+#define   AR8216_ATU_PORT4             BIT(4)
+#define   AR8216_ATU_PORT5             BIT(5)
+#define   AR8216_ATU_STATUS            BITS(16, 4)
+#define   AR8216_ATU_STATUS_S          16
+
+#define AR8216_REG_ATU_CTRL            0x005C
+#define   AR8216_ATU_CTRL_AGE_EN       BIT(17)
+#define   AR8216_ATU_CTRL_AGE_TIME     BITS(0, 16)
+#define   AR8216_ATU_CTRL_AGE_TIME_S   0
+#define   AR8236_ATU_CTRL_RES          BIT(20)
+
+#define AR8216_REG_MIB_FUNC            0x0080
+#define   AR8216_MIB_TIMER             BITS(0, 16)
+#define   AR8216_MIB_AT_HALF_EN                BIT(16)
+#define   AR8216_MIB_BUSY              BIT(17)
+#define   AR8216_MIB_FUNC              BITS(24, 3)
+#define   AR8216_MIB_FUNC_S            24
+#define   AR8216_MIB_FUNC_NO_OP                0x0
+#define   AR8216_MIB_FUNC_FLUSH                0x1
+#define   AR8216_MIB_FUNC_CAPTURE      0x3
+#define   AR8236_MIB_EN                        BIT(30)
+
+#define AR8216_REG_GLOBAL_CPUPORT              0x0078
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT    BITS(4, 4)
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S  4
+
+#define AR8216_PORT_OFFSET(_i)         (0x0100 * (_i + 1))
+#define AR8216_REG_PORT_STATUS(_i)     (AR8216_PORT_OFFSET(_i) + 0x0000)
+#define   AR8216_PORT_STATUS_SPEED     BITS(0,2)
+#define   AR8216_PORT_STATUS_SPEED_S   0
+#define   AR8216_PORT_STATUS_TXMAC     BIT(2)
+#define   AR8216_PORT_STATUS_RXMAC     BIT(3)
+#define   AR8216_PORT_STATUS_TXFLOW    BIT(4)
+#define   AR8216_PORT_STATUS_RXFLOW    BIT(5)
+#define   AR8216_PORT_STATUS_DUPLEX    BIT(6)
+#define   AR8216_PORT_STATUS_LINK_UP   BIT(8)
+#define   AR8216_PORT_STATUS_LINK_AUTO BIT(9)
+#define   AR8216_PORT_STATUS_LINK_PAUSE        BIT(10)
+#define   AR8216_PORT_STATUS_FLOW_CONTROL  BIT(12)
+
+#define AR8216_REG_PORT_CTRL(_i)       (AR8216_PORT_OFFSET(_i) + 0x0004)
+
+/* port forwarding state */
+#define   AR8216_PORT_CTRL_STATE       BITS(0, 3)
+#define   AR8216_PORT_CTRL_STATE_S     0
+
+#define   AR8216_PORT_CTRL_LEARN_LOCK  BIT(7)
+
+/* egress 802.1q mode */
+#define   AR8216_PORT_CTRL_VLAN_MODE   BITS(8, 2)
+#define   AR8216_PORT_CTRL_VLAN_MODE_S 8
+
+#define   AR8216_PORT_CTRL_IGMP_SNOOP  BIT(10)
+#define   AR8216_PORT_CTRL_HEADER      BIT(11)
+#define   AR8216_PORT_CTRL_MAC_LOOP    BIT(12)
+#define   AR8216_PORT_CTRL_SINGLE_VLAN BIT(13)
+#define   AR8216_PORT_CTRL_LEARN       BIT(14)
+#define   AR8216_PORT_CTRL_MIRROR_TX   BIT(16)
+#define   AR8216_PORT_CTRL_MIRROR_RX   BIT(17)
+
+#define AR8216_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET(_i) + 0x0008)
+
+#define   AR8216_PORT_VLAN_DEFAULT_ID  BITS(0, 12)
+#define   AR8216_PORT_VLAN_DEFAULT_ID_S        0
+
+#define   AR8216_PORT_VLAN_DEST_PORTS  BITS(16, 9)
+#define   AR8216_PORT_VLAN_DEST_PORTS_S        16
+
+/* bit0 added to the priority field of egress frames */
+#define   AR8216_PORT_VLAN_TX_PRIO     BIT(27)
+
+/* port default priority */
+#define   AR8216_PORT_VLAN_PRIORITY    BITS(28, 2)
+#define   AR8216_PORT_VLAN_PRIORITY_S  28
+
+/* ingress 802.1q mode */
+#define   AR8216_PORT_VLAN_MODE                BITS(30, 2)
+#define   AR8216_PORT_VLAN_MODE_S      30
+
+#define AR8216_REG_PORT_RATE(_i)       (AR8216_PORT_OFFSET(_i) + 0x000c)
+#define AR8216_REG_PORT_PRIO(_i)       (AR8216_PORT_OFFSET(_i) + 0x0010)
+
+#define AR8216_STATS_RXBROAD           0x00
+#define AR8216_STATS_RXPAUSE           0x04
+#define AR8216_STATS_RXMULTI           0x08
+#define AR8216_STATS_RXFCSERR          0x0c
+#define AR8216_STATS_RXALIGNERR                0x10
+#define AR8216_STATS_RXRUNT            0x14
+#define AR8216_STATS_RXFRAGMENT                0x18
+#define AR8216_STATS_RX64BYTE          0x1c
+#define AR8216_STATS_RX128BYTE         0x20
+#define AR8216_STATS_RX256BYTE         0x24
+#define AR8216_STATS_RX512BYTE         0x28
+#define AR8216_STATS_RX1024BYTE                0x2c
+#define AR8216_STATS_RXMAXBYTE         0x30
+#define AR8216_STATS_RXTOOLONG         0x34
+#define AR8216_STATS_RXGOODBYTE                0x38
+#define AR8216_STATS_RXBADBYTE         0x40
+#define AR8216_STATS_RXOVERFLOW                0x48
+#define AR8216_STATS_FILTERED          0x4c
+#define AR8216_STATS_TXBROAD           0x50
+#define AR8216_STATS_TXPAUSE           0x54
+#define AR8216_STATS_TXMULTI           0x58
+#define AR8216_STATS_TXUNDERRUN                0x5c
+#define AR8216_STATS_TX64BYTE          0x60
+#define AR8216_STATS_TX128BYTE         0x64
+#define AR8216_STATS_TX256BYTE         0x68
+#define AR8216_STATS_TX512BYTE         0x6c
+#define AR8216_STATS_TX1024BYTE                0x70
+#define AR8216_STATS_TXMAXBYTE         0x74
+#define AR8216_STATS_TXOVERSIZE                0x78
+#define AR8216_STATS_TXBYTE            0x7c
+#define AR8216_STATS_TXCOLLISION       0x84
+#define AR8216_STATS_TXABORTCOL                0x88
+#define AR8216_STATS_TXMULTICOL                0x8c
+#define AR8216_STATS_TXSINGLECOL       0x90
+#define AR8216_STATS_TXEXCDEFER                0x94
+#define AR8216_STATS_TXDEFER           0x98
+#define AR8216_STATS_TXLATECOL         0x9c
+
+#define AR8236_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET((_i)) + 0x0008)
+#define   AR8236_PORT_VLAN_DEFAULT_ID  BITS(16, 12)
+#define   AR8236_PORT_VLAN_DEFAULT_ID_S        16
+#define   AR8236_PORT_VLAN_PRIORITY    BITS(29, 3)
+#define   AR8236_PORT_VLAN_PRIORITY_S  28
+
+#define AR8236_REG_PORT_VLAN2(_i)      (AR8216_PORT_OFFSET((_i)) + 0x000c)
+#define   AR8236_PORT_VLAN2_MEMBER     BITS(16, 7)
+#define   AR8236_PORT_VLAN2_MEMBER_S   16
+#define   AR8236_PORT_VLAN2_TX_PRIO    BIT(23)
+#define   AR8236_PORT_VLAN2_VLAN_MODE  BITS(30, 2)
+#define   AR8236_PORT_VLAN2_VLAN_MODE_S        30
+
+#define AR8236_STATS_RXBROAD           0x00
+#define AR8236_STATS_RXPAUSE           0x04
+#define AR8236_STATS_RXMULTI           0x08
+#define AR8236_STATS_RXFCSERR          0x0c
+#define AR8236_STATS_RXALIGNERR                0x10
+#define AR8236_STATS_RXRUNT            0x14
+#define AR8236_STATS_RXFRAGMENT                0x18
+#define AR8236_STATS_RX64BYTE          0x1c
+#define AR8236_STATS_RX128BYTE         0x20
+#define AR8236_STATS_RX256BYTE         0x24
+#define AR8236_STATS_RX512BYTE         0x28
+#define AR8236_STATS_RX1024BYTE                0x2c
+#define AR8236_STATS_RX1518BYTE                0x30
+#define AR8236_STATS_RXMAXBYTE         0x34
+#define AR8236_STATS_RXTOOLONG         0x38
+#define AR8236_STATS_RXGOODBYTE                0x3c
+#define AR8236_STATS_RXBADBYTE         0x44
+#define AR8236_STATS_RXOVERFLOW                0x4c
+#define AR8236_STATS_FILTERED          0x50
+#define AR8236_STATS_TXBROAD           0x54
+#define AR8236_STATS_TXPAUSE           0x58
+#define AR8236_STATS_TXMULTI           0x5c
+#define AR8236_STATS_TXUNDERRUN                0x60
+#define AR8236_STATS_TX64BYTE          0x64
+#define AR8236_STATS_TX128BYTE         0x68
+#define AR8236_STATS_TX256BYTE         0x6c
+#define AR8236_STATS_TX512BYTE         0x70
+#define AR8236_STATS_TX1024BYTE                0x74
+#define AR8236_STATS_TX1518BYTE                0x78
+#define AR8236_STATS_TXMAXBYTE         0x7c
+#define AR8236_STATS_TXOVERSIZE                0x80
+#define AR8236_STATS_TXBYTE            0x84
+#define AR8236_STATS_TXCOLLISION       0x8c
+#define AR8236_STATS_TXABORTCOL                0x90
+#define AR8236_STATS_TXMULTICOL                0x94
+#define AR8236_STATS_TXSINGLECOL       0x98
+#define AR8236_STATS_TXEXCDEFER                0x9c
+#define AR8236_STATS_TXDEFER           0xa0
+#define AR8236_STATS_TXLATECOL         0xa4
+
+#define AR8316_REG_POSTRIP                     0x0008
+#define   AR8316_POSTRIP_MAC0_GMII_EN          BIT(0)
+#define   AR8316_POSTRIP_MAC0_RGMII_EN         BIT(1)
+#define   AR8316_POSTRIP_PHY4_GMII_EN          BIT(2)
+#define   AR8316_POSTRIP_PHY4_RGMII_EN         BIT(3)
+#define   AR8316_POSTRIP_MAC0_MAC_MODE         BIT(4)
+#define   AR8316_POSTRIP_RTL_MODE              BIT(5)
+#define   AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN  BIT(6)
+#define   AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN  BIT(7)
+#define   AR8316_POSTRIP_SERDES_EN             BIT(8)
+#define   AR8316_POSTRIP_SEL_ANA_RST           BIT(9)
+#define   AR8316_POSTRIP_GATE_25M_EN           BIT(10)
+#define   AR8316_POSTRIP_SEL_CLK25M            BIT(11)
+#define   AR8316_POSTRIP_HIB_PULSE_HW          BIT(12)
+#define   AR8316_POSTRIP_DBG_MODE_I            BIT(13)
+#define   AR8316_POSTRIP_MAC5_MAC_MODE         BIT(14)
+#define   AR8316_POSTRIP_MAC5_PHY_MODE         BIT(15)
+#define   AR8316_POSTRIP_POWER_DOWN_HW         BIT(16)
+#define   AR8316_POSTRIP_LPW_STATE_EN          BIT(17)
+#define   AR8316_POSTRIP_MAN_EN                        BIT(18)
+#define   AR8316_POSTRIP_PHY_PLL_ON            BIT(19)
+#define   AR8316_POSTRIP_LPW_EXIT              BIT(20)
+#define   AR8316_POSTRIP_TXDELAY_S0            BIT(21)
+#define   AR8316_POSTRIP_TXDELAY_S1            BIT(22)
+#define   AR8316_POSTRIP_RXDELAY_S0            BIT(23)
+#define   AR8316_POSTRIP_LED_OPEN_EN           BIT(24)
+#define   AR8316_POSTRIP_SPI_EN                        BIT(25)
+#define   AR8316_POSTRIP_RXDELAY_S1            BIT(26)
+#define   AR8316_POSTRIP_POWER_ON_SEL          BIT(31)
+
+/* port speed */
+enum {
+        AR8216_PORT_SPEED_10M = 0,
+        AR8216_PORT_SPEED_100M = 1,
+        AR8216_PORT_SPEED_1000M = 2,
+        AR8216_PORT_SPEED_ERR = 3,
+};
+
+/* ingress 802.1q mode */
+enum {
+       AR8216_IN_PORT_ONLY = 0,
+       AR8216_IN_PORT_FALLBACK = 1,
+       AR8216_IN_VLAN_ONLY = 2,
+       AR8216_IN_SECURE = 3
+};
+
+/* egress 802.1q mode */
+enum {
+       AR8216_OUT_KEEP = 0,
+       AR8216_OUT_STRIP_VLAN = 1,
+       AR8216_OUT_ADD_VLAN = 2
+};
+
+/* port forwarding state */
+enum {
+       AR8216_PORT_STATE_DISABLED = 0,
+       AR8216_PORT_STATE_BLOCK = 1,
+       AR8216_PORT_STATE_LISTEN = 2,
+       AR8216_PORT_STATE_LEARN = 3,
+       AR8216_PORT_STATE_FORWARD = 4
+};
+
+enum {
+       AR8XXX_VER_AR8216 = 0x01,
+       AR8XXX_VER_AR8236 = 0x03,
+       AR8XXX_VER_AR8316 = 0x10,
+       AR8XXX_VER_AR8327 = 0x12,
+       AR8XXX_VER_AR8337 = 0x13,
+};
+
+#define AR8XXX_NUM_ARL_RECORDS 100
+
+enum arl_op {
+       AR8XXX_ARL_INITIALIZE,
+       AR8XXX_ARL_GET_NEXT
+};
+
+struct arl_entry {
+       u8 port;
+       u8 mac[6];
+};
+
+struct ar8xxx_priv;
+
+struct ar8xxx_mib_desc {
+       unsigned int size;
+       unsigned int offset;
+       const char *name;
+};
+
+struct ar8xxx_chip {
+       unsigned long caps;
+       bool config_at_probe;
+       bool mii_lo_first;
+
+       /* parameters to calculate REG_PORT_STATS_BASE */
+       unsigned reg_port_stats_start;
+       unsigned reg_port_stats_length;
+
+       unsigned reg_arl_ctrl;
+
+       int (*hw_init)(struct ar8xxx_priv *priv);
+       void (*cleanup)(struct ar8xxx_priv *priv);
+
+       const char *name;
+       int vlans;
+       int ports;
+       const struct switch_dev_ops *swops;
+
+       void (*init_globals)(struct ar8xxx_priv *priv);
+       void (*init_port)(struct ar8xxx_priv *priv, int port);
+       void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
+       u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
+       u32 (*read_port_eee_status)(struct ar8xxx_priv *priv, int port);
+       int (*atu_flush)(struct ar8xxx_priv *priv);
+       int (*atu_flush_port)(struct ar8xxx_priv *priv, int port);
+       void (*vtu_flush)(struct ar8xxx_priv *priv);
+       void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
+       void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
+       void (*set_mirror_regs)(struct ar8xxx_priv *priv);
+       void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
+                             u32 *status, enum arl_op op);
+       int (*sw_hw_apply)(struct switch_dev *dev);
+
+       const struct ar8xxx_mib_desc *mib_decs;
+       unsigned num_mibs;
+       unsigned mib_func;
+};
+
+struct ar8xxx_priv {
+       struct switch_dev dev;
+       struct mii_bus *mii_bus;
+       struct phy_device *phy;
+
+       int (*get_port_link)(unsigned port);
+
+       const struct net_device_ops *ndo_old;
+       struct net_device_ops ndo;
+       struct mutex reg_mutex;
+       u8 chip_ver;
+       u8 chip_rev;
+       const struct ar8xxx_chip *chip;
+       void *chip_data;
+       bool initialized;
+       bool port4_phy;
+       char buf[2048];
+       struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
+       char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
+       bool link_up[AR8X16_MAX_PORTS];
+
+       bool init;
+
+       struct mutex mib_lock;
+       struct delayed_work mib_work;
+       int mib_next_port;
+       u64 *mib_stats;
+
+       struct list_head list;
+       unsigned int use_count;
+
+       /* all fields below are cleared on reset */
+       bool vlan;
+       u16 vlan_id[AR8X16_MAX_VLANS];
+       u8 vlan_table[AR8X16_MAX_VLANS];
+       u8 vlan_tagged;
+       u16 pvid[AR8X16_MAX_PORTS];
+       int arl_age_time;
+
+       /* mirroring */
+       bool mirror_rx;
+       bool mirror_tx;
+       int source_port;
+       int monitor_port;
+       u8 port_vlan_prio[AR8X16_MAX_PORTS];
+};
+
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
+u32
+ar8xxx_read(struct ar8xxx_priv *priv, int reg);
+void
+ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val);
+u32
+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
+
+void
+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
+                    u16 dbg_addr, u16 dbg_data);
+void
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data);
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg);
+void
+ar8xxx_phy_init(struct ar8xxx_priv *priv);
+int
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val);
+int
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val);
+int
+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val);
+int
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan);
+int
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan);
+int
+ar8xxx_sw_hw_apply(struct switch_dev *dev);
+int
+ar8xxx_sw_reset_switch(struct switch_dev *dev);
+int
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link);
+int
+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val);
+int
+ar8xxx_sw_get_port_mib(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val);
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev,
+                          const struct switch_attr *attr,
+                          struct switch_val *val);
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev,
+                          const struct switch_attr *attr,
+                          struct switch_val *val);
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val);
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val);
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val);
+int
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
+
+static inline struct ar8xxx_priv *
+swdev_to_ar8xxx(struct switch_dev *swdev)
+{
+       return container_of(swdev, struct ar8xxx_priv, dev);
+}
+
+static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv)
+{
+       return priv->chip->caps & AR8XXX_CAP_GIGE;
+}
+
+static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv)
+{
+       return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS;
+}
+
+static inline bool chip_is_ar8216(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8216;
+}
+
+static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8236;
+}
+
+static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8316;
+}
+
+static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8327;
+}
+
+static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8337;
+}
+
+static inline void
+ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       ar8xxx_rmw(priv, reg, 0, val);
+}
+
+static inline void
+ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       ar8xxx_rmw(priv, reg, val, 0);
+}
+
+static inline void
+split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+{
+       regaddr >>= 1;
+       *r1 = regaddr & 0x1e;
+
+       regaddr >>= 5;
+       *r2 = regaddr & 0x7;
+
+       regaddr >>= 3;
+       *page = regaddr & 0x1ff;
+}
+
+static inline void
+wait_for_page_switch(void)
+{
+       udelay(5);
+}
+
+#endif
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/ar8327.c b/target/linux/generic/files-4.14/drivers/net/phy/ar8327.c
new file mode 100644 (file)
index 0000000..74f0a08
--- /dev/null
@@ -0,0 +1,1503 @@
+/*
+ * ar8327.c: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/lockdep.h>
+#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/of_device.h>
+#include <linux/leds.h>
+#include <linux/mdio.h>
+
+#include "ar8216.h"
+#include "ar8327.h"
+
+extern const struct ar8xxx_mib_desc ar8236_mibs[39];
+extern const struct switch_attr ar8xxx_sw_attr_vlan[1];
+
+static u32
+ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
+{
+       u32 t;
+
+       if (!cfg)
+               return 0;
+
+       t = 0;
+       switch (cfg->mode) {
+       case AR8327_PAD_NC:
+               break;
+
+       case AR8327_PAD_MAC2MAC_MII:
+               t = AR8327_PAD_MAC_MII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC2MAC_GMII:
+               t = AR8327_PAD_MAC_GMII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC_SGMII:
+               t = AR8327_PAD_SGMII_EN;
+
+               /*
+                * WAR for the QUalcomm Atheros AP136 board.
+                * It seems that RGMII TX/RX delay settings needs to be
+                * applied for SGMII mode as well, The ethernet is not
+                * reliable without this.
+                */
+               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+               if (cfg->rxclk_delay_en)
+                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+               if (cfg->txclk_delay_en)
+                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+
+               if (cfg->sgmii_delay_en)
+                       t |= AR8327_PAD_SGMII_DELAY_EN;
+
+               break;
+
+       case AR8327_PAD_MAC2PHY_MII:
+               t = AR8327_PAD_PHY_MII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC2PHY_GMII:
+               t = AR8327_PAD_PHY_GMII_EN;
+               if (cfg->pipe_rxclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC_RGMII:
+               t = AR8327_PAD_RGMII_EN;
+               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+               if (cfg->rxclk_delay_en)
+                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+               if (cfg->txclk_delay_en)
+                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+               break;
+
+       case AR8327_PAD_PHY_GMII:
+               t = AR8327_PAD_PHYX_GMII_EN;
+               break;
+
+       case AR8327_PAD_PHY_RGMII:
+               t = AR8327_PAD_PHYX_RGMII_EN;
+               break;
+
+       case AR8327_PAD_PHY_MII:
+               t = AR8327_PAD_PHYX_MII_EN;
+               break;
+       }
+
+       return t;
+}
+
+static void
+ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy)
+{
+       switch (priv->chip_rev) {
+       case 1:
+               /* For 100M waveform */
+               ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea);
+               /* Turn on Gigabit clock */
+               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
+               break;
+
+       case 2:
+               ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0);
+               /* fallthrough */
+       case 4:
+               ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f);
+               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860);
+               ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46);
+               ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000);
+               break;
+       }
+}
+
+static u32
+ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
+{
+       u32 t;
+
+       if (!cfg->force_link)
+               return AR8216_PORT_STATUS_LINK_AUTO;
+
+       t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC;
+       t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0;
+       t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0;
+       t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0;
+
+       switch (cfg->speed) {
+       case AR8327_PORT_SPEED_10:
+               t |= AR8216_PORT_SPEED_10M;
+               break;
+       case AR8327_PORT_SPEED_100:
+               t |= AR8216_PORT_SPEED_100M;
+               break;
+       case AR8327_PORT_SPEED_1000:
+               t |= AR8216_PORT_SPEED_1000M;
+               break;
+       }
+
+       return t;
+}
+
+#define AR8327_LED_ENTRY(_num, _reg, _shift) \
+       [_num] = { .reg = (_reg), .shift = (_shift) }
+
+static const struct ar8327_led_entry
+ar8327_led_map[AR8327_NUM_LEDS] = {
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_0, 0, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_1, 1, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_2, 2, 14),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_0, 3, 8),
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_1, 3, 10),
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_2, 3, 12),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_0, 3, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_1, 3, 16),
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_2, 3, 18),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_0, 3, 20),
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_1, 3, 22),
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_2, 3, 24),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_0, 0, 30),
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_1, 1, 30),
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_2, 2, 30),
+};
+
+static void
+ar8327_set_led_pattern(struct ar8xxx_priv *priv, unsigned int led_num,
+                      enum ar8327_led_pattern pattern)
+{
+       const struct ar8327_led_entry *entry;
+
+       entry = &ar8327_led_map[led_num];
+       ar8xxx_rmw(priv, AR8327_REG_LED_CTRL(entry->reg),
+                  (3 << entry->shift), pattern << entry->shift);
+}
+
+static void
+ar8327_led_work_func(struct work_struct *work)
+{
+       struct ar8327_led *aled;
+       u8 pattern;
+
+       aled = container_of(work, struct ar8327_led, led_work);
+
+       pattern = aled->pattern;
+
+       ar8327_set_led_pattern(aled->sw_priv, aled->led_num,
+                              pattern);
+}
+
+static void
+ar8327_led_schedule_change(struct ar8327_led *aled, u8 pattern)
+{
+       if (aled->pattern == pattern)
+               return;
+
+       aled->pattern = pattern;
+       schedule_work(&aled->led_work);
+}
+
+static inline struct ar8327_led *
+led_cdev_to_ar8327_led(struct led_classdev *led_cdev)
+{
+       return container_of(led_cdev, struct ar8327_led, cdev);
+}
+
+static int
+ar8327_led_blink_set(struct led_classdev *led_cdev,
+                    unsigned long *delay_on,
+                    unsigned long *delay_off)
+{
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+
+       if (*delay_on == 0 && *delay_off == 0) {
+               *delay_on = 125;
+               *delay_off = 125;
+       }
+
+       if (*delay_on != 125 || *delay_off != 125) {
+               /*
+                * The hardware only supports blinking at 4Hz. Fall back
+                * to software implementation in other cases.
+                */
+               return -EINVAL;
+       }
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = false;
+       ar8327_led_schedule_change(aled, AR8327_LED_PATTERN_BLINK);
+
+       spin_unlock(&aled->lock);
+
+       return 0;
+}
+
+static void
+ar8327_led_set_brightness(struct led_classdev *led_cdev,
+                         enum led_brightness brightness)
+{
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       u8 pattern;
+       bool active;
+
+       active = (brightness != LED_OFF);
+       active ^= aled->active_low;
+
+       pattern = (active) ? AR8327_LED_PATTERN_ON :
+                            AR8327_LED_PATTERN_OFF;
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = false;
+       ar8327_led_schedule_change(aled, pattern);
+
+       spin_unlock(&aled->lock);
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       ssize_t ret = 0;
+
+       ret += scnprintf(buf, PAGE_SIZE, "%d\n", aled->enable_hw_mode);
+
+       return ret;
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf,
+                               size_t size)
+{
+        struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       u8 pattern;
+       u8 value;
+       int ret;
+
+       ret = kstrtou8(buf, 10, &value);
+       if (ret < 0)
+               return -EINVAL;
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = !!value;
+       if (aled->enable_hw_mode)
+               pattern = AR8327_LED_PATTERN_RULE;
+       else
+               pattern = AR8327_LED_PATTERN_OFF;
+
+       ar8327_led_schedule_change(aled, pattern);
+
+       spin_unlock(&aled->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(enable_hw_mode,  S_IRUGO | S_IWUSR,
+                  ar8327_led_enable_hw_mode_show,
+                  ar8327_led_enable_hw_mode_store);
+
+static int
+ar8327_led_register(struct ar8327_led *aled)
+{
+       int ret;
+
+       ret = led_classdev_register(NULL, &aled->cdev);
+       if (ret < 0)
+               return ret;
+
+       if (aled->mode == AR8327_LED_MODE_HW) {
+               ret = device_create_file(aled->cdev.dev,
+                                        &dev_attr_enable_hw_mode);
+               if (ret)
+                       goto err_unregister;
+       }
+
+       return 0;
+
+err_unregister:
+       led_classdev_unregister(&aled->cdev);
+       return ret;
+}
+
+static void
+ar8327_led_unregister(struct ar8327_led *aled)
+{
+       if (aled->mode == AR8327_LED_MODE_HW)
+               device_remove_file(aled->cdev.dev, &dev_attr_enable_hw_mode);
+
+       led_classdev_unregister(&aled->cdev);
+       cancel_work_sync(&aled->led_work);
+}
+
+static int
+ar8327_led_create(struct ar8xxx_priv *priv,
+                 const struct ar8327_led_info *led_info)
+{
+       struct ar8327_data *data = priv->chip_data;
+       struct ar8327_led *aled;
+       int ret;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return 0;
+
+       if (!led_info->name)
+               return -EINVAL;
+
+       if (led_info->led_num >= AR8327_NUM_LEDS)
+               return -EINVAL;
+
+       aled = kzalloc(sizeof(*aled) + strlen(led_info->name) + 1,
+                      GFP_KERNEL);
+       if (!aled)
+               return -ENOMEM;
+
+       aled->sw_priv = priv;
+       aled->led_num = led_info->led_num;
+       aled->active_low = led_info->active_low;
+       aled->mode = led_info->mode;
+
+       if (aled->mode == AR8327_LED_MODE_HW)
+               aled->enable_hw_mode = true;
+
+       aled->name = (char *)(aled + 1);
+       strcpy(aled->name, led_info->name);
+
+       aled->cdev.name = aled->name;
+       aled->cdev.brightness_set = ar8327_led_set_brightness;
+       aled->cdev.blink_set = ar8327_led_blink_set;
+       aled->cdev.default_trigger = led_info->default_trigger;
+
+       spin_lock_init(&aled->lock);
+       mutex_init(&aled->mutex);
+       INIT_WORK(&aled->led_work, ar8327_led_work_func);
+
+       ret = ar8327_led_register(aled);
+       if (ret)
+               goto err_free;
+
+       data->leds[data->num_leds++] = aled;
+
+       return 0;
+
+err_free:
+       kfree(aled);
+       return ret;
+}
+
+static void
+ar8327_led_destroy(struct ar8327_led *aled)
+{
+       ar8327_led_unregister(aled);
+       kfree(aled);
+}
+
+static void
+ar8327_leds_init(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       unsigned i;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return;
+
+       for (i = 0; i < data->num_leds; i++) {
+               struct ar8327_led *aled;
+
+               aled = data->leds[i];
+
+               if (aled->enable_hw_mode)
+                       aled->pattern = AR8327_LED_PATTERN_RULE;
+               else
+                       aled->pattern = AR8327_LED_PATTERN_OFF;
+
+               ar8327_set_led_pattern(priv, aled->led_num, aled->pattern);
+       }
+}
+
+static void
+ar8327_leds_cleanup(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       unsigned i;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return;
+
+       for (i = 0; i < data->num_leds; i++) {
+               struct ar8327_led *aled;
+
+               aled = data->leds[i];
+               ar8327_led_destroy(aled);
+       }
+
+       kfree(data->leds);
+}
+
+static int
+ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
+                      struct ar8327_platform_data *pdata)
+{
+       struct ar8327_led_cfg *led_cfg;
+       struct ar8327_data *data = priv->chip_data;
+       u32 pos, new_pos;
+       u32 t;
+
+       if (!pdata)
+               return -EINVAL;
+
+       priv->get_port_link = pdata->get_port_link;
+
+       data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg);
+       data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
+
+       t = ar8327_get_pad_cfg(pdata->pad0_cfg);
+       if (chip_is_ar8337(priv) && !pdata->pad0_cfg->mac06_exchange_dis)
+           t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+       ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t);
+
+       t = ar8327_get_pad_cfg(pdata->pad5_cfg);
+       ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t);
+       t = ar8327_get_pad_cfg(pdata->pad6_cfg);
+       ar8xxx_write(priv, AR8327_REG_PAD6_MODE, t);
+
+       pos = ar8xxx_read(priv, AR8327_REG_POWER_ON_STRIP);
+       new_pos = pos;
+
+       led_cfg = pdata->led_cfg;
+       if (led_cfg) {
+               if (led_cfg->open_drain)
+                       new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+               else
+                       new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3);
+
+               if (new_pos != pos)
+                       new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
+       }
+
+       if (pdata->sgmii_cfg) {
+               t = pdata->sgmii_cfg->sgmii_ctrl;
+               if (priv->chip_rev == 1)
+                       t |= AR8327_SGMII_CTRL_EN_PLL |
+                            AR8327_SGMII_CTRL_EN_RX |
+                            AR8327_SGMII_CTRL_EN_TX;
+               else
+                       t &= ~(AR8327_SGMII_CTRL_EN_PLL |
+                              AR8327_SGMII_CTRL_EN_RX |
+                              AR8327_SGMII_CTRL_EN_TX);
+
+               ar8xxx_write(priv, AR8327_REG_SGMII_CTRL, t);
+
+               if (pdata->sgmii_cfg->serdes_aen)
+                       new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
+               else
+                       new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
+       }
+
+       ar8xxx_write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
+
+       if (pdata->leds && pdata->num_leds) {
+               int i;
+
+               data->leds = kzalloc(pdata->num_leds * sizeof(void *),
+                                    GFP_KERNEL);
+               if (!data->leds)
+                       return -ENOMEM;
+
+               for (i = 0; i < pdata->num_leds; i++)
+                       ar8327_led_create(priv, &pdata->leds[i]);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       struct ar8327_data *data = priv->chip_data;
+       const __be32 *paddr;
+       int len;
+       int i;
+
+       paddr = of_get_property(np, "qca,ar8327-initvals", &len);
+       if (!paddr || len < (2 * sizeof(*paddr)))
+               return -EINVAL;
+
+       len /= sizeof(*paddr);
+
+       for (i = 0; i < len - 1; i += 2) {
+               u32 reg;
+               u32 val;
+
+               reg = be32_to_cpup(paddr + i);
+               val = be32_to_cpup(paddr + i + 1);
+
+               switch (reg) {
+               case AR8327_REG_PORT_STATUS(0):
+                       data->port0_status = val;
+                       break;
+               case AR8327_REG_PORT_STATUS(6):
+                       data->port6_status = val;
+                       break;
+               default:
+                       ar8xxx_write(priv, reg, val);
+                       break;
+               }
+       }
+
+       return 0;
+}
+#else
+static inline int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       return -EINVAL;
+}
+#endif
+
+static int
+ar8327_hw_init(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->chip_data = kzalloc(sizeof(struct ar8327_data), GFP_KERNEL);
+       if (!priv->chip_data)
+               return -ENOMEM;
+
+       if (priv->phy->mdio.dev.of_node)
+               ret = ar8327_hw_config_of(priv, priv->phy->mdio.dev.of_node);
+       else
+               ret = ar8327_hw_config_pdata(priv,
+                                            priv->phy->mdio.dev.platform_data);
+
+       if (ret)
+               return ret;
+
+       ar8327_leds_init(priv);
+
+       ar8xxx_phy_init(priv);
+
+       return 0;
+}
+
+static void
+ar8327_cleanup(struct ar8xxx_priv *priv)
+{
+       ar8327_leds_cleanup(priv);
+}
+
+static void
+ar8327_init_globals(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       u32 t;
+       int i;
+
+       /* enable CPU port and disable mirror port */
+       t = AR8327_FWD_CTRL0_CPU_PORT_EN |
+           AR8327_FWD_CTRL0_MIRROR_PORT;
+       ar8xxx_write(priv, AR8327_REG_FWD_CTRL0, t);
+
+       /* forward multicast and broadcast frames to CPU */
+       t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
+           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
+           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
+       ar8xxx_write(priv, AR8327_REG_FWD_CTRL1, t);
+
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
+                  AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
+
+       /* Enable MIB counters */
+       ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN,
+                      AR8327_MODULE_EN_MIB);
+
+       /* Disable EEE on all phy's due to stability issues */
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++)
+               data->eee[i] = false;
+}
+
+static void
+ar8327_init_port(struct ar8xxx_priv *priv, int port)
+{
+       struct ar8327_data *data = priv->chip_data;
+       u32 t;
+
+       if (port == AR8216_PORT_CPU)
+               t = data->port0_status;
+       else if (port == 6)
+               t = data->port6_status;
+       else
+               t = AR8216_PORT_STATUS_LINK_AUTO;
+
+       if (port != AR8216_PORT_CPU && port != 6) {
+               /*hw limitation:if configure mac when there is traffic,
+               port MAC may work abnormal. Need disable lan&wan mac at fisrt*/
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), 0);
+               msleep(100);
+               t |= AR8216_PORT_STATUS_FLOW_CONTROL;
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
+       } else {
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
+       }
+
+       ar8xxx_write(priv, AR8327_REG_PORT_HEADER(port), 0);
+
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), 0);
+
+       t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+       t = AR8327_PORT_LOOKUP_LEARN;
+       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static u32
+ar8327_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+
+       t = ar8xxx_read(priv, AR8327_REG_PORT_STATUS(port));
+       /* map the flow control autoneg result bits to the flow control bits
+        * used in forced mode to allow ar8216_read_port_link detect
+        * flow control properly if autoneg is used
+        */
+       if (t & AR8216_PORT_STATUS_LINK_UP &&
+           t & AR8216_PORT_STATUS_LINK_AUTO) {
+               t &= ~(AR8216_PORT_STATUS_TXFLOW | AR8216_PORT_STATUS_RXFLOW);
+               if (t & AR8327_PORT_STATUS_TXFLOW_AUTO)
+                       t |= AR8216_PORT_STATUS_TXFLOW;
+               if (t & AR8327_PORT_STATUS_RXFLOW_AUTO)
+                       t |= AR8216_PORT_STATUS_RXFLOW;
+       }
+
+       return t;
+}
+
+static u32
+ar8327_read_port_eee_status(struct ar8xxx_priv *priv, int port)
+{
+       int phy;
+       u16 t;
+
+       if (port >= priv->dev.ports)
+               return 0;
+
+       if (port == 0 || port == 6)
+               return 0;
+
+       phy = port - 1;
+
+       /* EEE Ability Auto-negotiation Result */
+       t = ar8xxx_phy_mmd_read(priv, phy, 0x7, 0x8000);
+
+       return mmd_eee_adv_to_ethtool_adv_t(t);
+}
+
+static int
+ar8327_atu_flush(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+                             AR8327_ATU_FUNC_BUSY, 0);
+       if (!ret)
+               ar8xxx_write(priv, AR8327_REG_ATU_FUNC,
+                            AR8327_ATU_FUNC_OP_FLUSH |
+                            AR8327_ATU_FUNC_BUSY);
+
+       return ret;
+}
+
+static int
+ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+                             AR8327_ATU_FUNC_BUSY, 0);
+       if (!ret) {
+               t = (port << AR8327_ATU_PORT_NUM_S);
+               t |= AR8327_ATU_FUNC_OP_FLUSH_PORT;
+               t |= AR8327_ATU_FUNC_BUSY;
+               ar8xxx_write(priv, AR8327_REG_ATU_FUNC, t);
+       }
+
+       return ret;
+}
+
+static int
+ar8327_get_port_igmp(struct ar8xxx_priv *priv, int port)
+{
+       u32 fwd_ctrl, frame_ack;
+
+       fwd_ctrl = (BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
+       frame_ack = ((AR8327_FRAME_ACK_CTRL_IGMP_MLD |
+                     AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
+                     AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
+                    AR8327_FRAME_ACK_CTRL_S(port));
+
+       return (ar8xxx_read(priv, AR8327_REG_FWD_CTRL1) &
+                       fwd_ctrl) == fwd_ctrl &&
+               (ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL(port)) &
+                       frame_ack) == frame_ack;
+}
+
+static void
+ar8327_set_port_igmp(struct ar8xxx_priv *priv, int port, int enable)
+{
+       int reg_frame_ack = AR8327_REG_FRAME_ACK_CTRL(port);
+       u32 val_frame_ack = (AR8327_FRAME_ACK_CTRL_IGMP_MLD |
+                         AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
+                         AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
+                        AR8327_FRAME_ACK_CTRL_S(port);
+
+       if (enable) {
+               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
+                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S,
+                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
+               ar8xxx_reg_set(priv, reg_frame_ack, val_frame_ack);
+       } else {
+               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
+                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S,
+                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S);
+               ar8xxx_reg_clear(priv, reg_frame_ack, val_frame_ack);
+       }
+}
+
+static void
+ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
+{
+       if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1,
+                           AR8327_VTU_FUNC1_BUSY, 0))
+               return;
+
+       if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD)
+               ar8xxx_write(priv, AR8327_REG_VTU_FUNC0, val);
+
+       op |= AR8327_VTU_FUNC1_BUSY;
+       ar8xxx_write(priv, AR8327_REG_VTU_FUNC1, op);
+}
+
+static void
+ar8327_vtu_flush(struct ar8xxx_priv *priv)
+{
+       ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0);
+}
+
+static void
+ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
+{
+       u32 op;
+       u32 val;
+       int i;
+
+       op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S);
+       val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL;
+       for (i = 0; i < AR8327_NUM_PORTS; i++) {
+               u32 mode;
+
+               if ((port_mask & BIT(i)) == 0)
+                       mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
+               else if (priv->vlan == 0)
+                       mode = AR8327_VTU_FUNC0_EG_MODE_KEEP;
+               else if ((priv->vlan_tagged & BIT(i)) || (priv->vlan_id[priv->pvid[i]] != vid))
+                       mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
+               else
+                       mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
+
+               val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i);
+       }
+       ar8327_vtu_op(priv, op, val);
+}
+
+static void
+ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 t;
+       u32 egress, ingress;
+       u32 pvid = priv->vlan_id[priv->pvid[port]];
+
+       if (priv->vlan) {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
+       t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
+       if (priv->vlan && priv->port_vlan_prio[port]) {
+               u32 prio = priv->port_vlan_prio[port];
+
+               t |= prio << AR8327_PORT_VLAN0_DEF_SPRI_S;
+               t |= prio << AR8327_PORT_VLAN0_DEF_CPRI_S;
+       }
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t);
+
+       t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
+       t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
+       if (priv->vlan && priv->port_vlan_prio[port])
+               t |= AR8327_PORT_VLAN1_VLAN_PRI_PROP;
+
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+       t = members;
+       t |= AR8327_PORT_LOOKUP_LEARN;
+       t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S;
+       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static int
+ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if ((priv->vlan_tagged & (1 << i)) || (priv->pvid[i] != val->port_vlan))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
+ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       if (val->port_vlan == priv->pvid[p->id]) {
+                               priv->vlan_tagged |= (1 << p->id);
+                       }
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static void
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+       for (port = 0; port < AR8327_NUM_PORTS; port++) {
+               ar8xxx_reg_clear(priv, AR8327_REG_PORT_LOOKUP(port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+               ar8xxx_reg_clear(priv, AR8327_REG_PORT_HOL_CTRL1(port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8327_NUM_PORTS ||
+           priv->monitor_port >= AR8327_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_reg_set(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+       if (priv->mirror_tx)
+               ar8xxx_reg_set(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+}
+
+static int
+ar8327_sw_set_eee(struct switch_dev *dev,
+                 const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct ar8327_data *data = priv->chip_data;
+       int port = val->port_vlan;
+       int phy;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+
+       phy = port - 1;
+
+       data->eee[phy] = !!(val->value.i);
+
+       return 0;
+}
+
+static int
+ar8327_sw_get_eee(struct switch_dev *dev,
+                 const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8327_data *data = priv->chip_data;
+       int port = val->port_vlan;
+       int phy;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+
+       phy = port - 1;
+
+       val->value.i = data->eee[phy];
+
+       return 0;
+}
+
+static void
+ar8327_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8327_ATU_FUNC_BUSY && --timeout) {
+               udelay(10);
+               cond_resched();
+       }
+
+       if (!timeout)
+               pr_err("ar8327: timeout waiting for atu to become ready\n");
+}
+
+static void ar8327_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_data0, r1_data1, r1_data2, r1_func;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8327_REG_ATU_DATA0, &r1_data0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_data1 = (AR8327_REG_ATU_DATA1 >> 1) & 0x1e;
+       r1_data2 = (AR8327_REG_ATU_DATA2 >> 1) & 0x1e;
+       r1_func  = (AR8327_REG_ATU_FUNC >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               ar8xxx_mii_write32(priv, r2, r1_data0, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               ar8xxx_mii_write32(priv, r2, r1_func,
+                                  AR8327_ATU_FUNC_OP_GET_NEXT |
+                                  AR8327_ATU_FUNC_BUSY);
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_data0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_data1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_data2);
+
+               *status = val2 & AR8327_ATU_STATUS;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8327_ATU_PORT0;
+               while (!(val1 & t) && ++i < AR8327_NUM_PORTS)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8327_ATU_ADDR0) >> AR8327_ATU_ADDR0_S;
+               a->mac[1] = (val0 & AR8327_ATU_ADDR1) >> AR8327_ATU_ADDR1_S;
+               a->mac[2] = (val0 & AR8327_ATU_ADDR2) >> AR8327_ATU_ADDR2_S;
+               a->mac[3] = (val0 & AR8327_ATU_ADDR3) >> AR8327_ATU_ADDR3_S;
+               a->mac[4] = (val1 & AR8327_ATU_ADDR4) >> AR8327_ATU_ADDR4_S;
+               a->mac[5] = (val1 & AR8327_ATU_ADDR5) >> AR8327_ATU_ADDR5_S;
+               break;
+       }
+}
+
+static int
+ar8327_sw_hw_apply(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8327_data *data = priv->chip_data;
+       int ret, i;
+
+       ret = ar8xxx_sw_hw_apply(dev);
+       if (ret)
+               return ret;
+
+       for (i=0; i < AR8XXX_NUM_PHYS; i++) {
+               if (data->eee[i])
+                       ar8xxx_reg_clear(priv, AR8327_REG_EEE_CTRL,
+                              AR8327_EEE_CTRL_DISABLE_PHY(i));
+               else
+                       ar8xxx_reg_set(priv, AR8327_REG_EEE_CTRL,
+                              AR8327_EEE_CTRL_DISABLE_PHY(i));
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_get_port_igmp_snooping(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       val->value.i = ar8327_get_port_igmp(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_set_port_igmp_snooping(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ar8327_set_port_igmp(priv, port, val->value.i);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_get_igmp_snooping(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       int port;
+
+       for (port = 0; port < dev->ports; port++) {
+               val->port_vlan = port;
+               if (ar8327_sw_get_port_igmp_snooping(dev, attr, val) ||
+                   !val->value.i)
+                       break;
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_set_igmp_snooping(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       int port;
+
+       for (port = 0; port < dev->ports; port++) {
+               val->port_vlan = port;
+               if (ar8327_sw_set_port_igmp_snooping(dev, attr, val))
+                       break;
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_get_igmp_v3(struct switch_dev *dev,
+                     const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u32 val_reg;
+
+       mutex_lock(&priv->reg_mutex);
+       val_reg = ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL1);
+       val->value.i = ((val_reg & AR8327_FRAME_ACK_CTRL_IGMP_V3_EN) != 0);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_set_igmp_v3(struct switch_dev *dev,
+                     const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       if (val->value.i)
+               ar8xxx_reg_set(priv, AR8327_REG_FRAME_ACK_CTRL1,
+                              AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
+       else
+               ar8xxx_reg_clear(priv, AR8327_REG_FRAME_ACK_CTRL1,
+                                AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8327_sw_set_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+       if (val->value.i < 0 || val->value.i > 7)
+               return -EINVAL;
+
+       priv->port_vlan_prio[port] = val->value.i;
+
+       return 0;
+}
+
+static int
+ar8327_sw_get_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       val->value.i = priv->port_vlan_prio[port];
+
+       return 0;
+}
+
+static const struct switch_attr ar8327_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "arl_age_time",
+               .description = "ARL age time (secs)",
+               .set = ar8xxx_sw_set_arl_age_time,
+               .get = ar8xxx_sw_get_arl_age_time,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_snooping",
+               .description = "Enable IGMP Snooping",
+               .set = ar8327_sw_set_igmp_snooping,
+               .get = ar8327_sw_get_igmp_snooping,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_v3",
+               .description = "Enable IGMPv3 support",
+               .set = ar8327_sw_set_igmp_v3,
+               .get = ar8327_sw_get_igmp_v3,
+               .max = 1
+       },
+};
+
+static const struct switch_attr ar8327_sw_attr_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = ar8xxx_sw_set_port_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .set = NULL,
+               .get = ar8xxx_sw_get_port_mib,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_eee",
+               .description = "Enable EEE PHY sleep mode",
+               .set = ar8327_sw_set_eee,
+               .get = ar8327_sw_get_eee,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_snooping",
+               .description = "Enable port's IGMP Snooping",
+               .set = ar8327_sw_set_port_igmp_snooping,
+               .get = ar8327_sw_get_port_igmp_snooping,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "vlan_prio",
+               .description = "Port VLAN default priority (VLAN PCP) (0-7)",
+               .set = ar8327_sw_set_port_vlan_prio,
+               .get = ar8327_sw_get_port_vlan_prio,
+               .max = 7,
+       },
+};
+
+static const struct switch_dev_ops ar8327_sw_ops = {
+       .attr_global = {
+               .attr = ar8327_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8327_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8327_sw_get_ports,
+       .set_vlan_ports = ar8327_sw_set_ports,
+       .apply_config = ar8327_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+/* The following op is disabled as it hogs the CPU and degrades performance.
+   An implementation has been attempted in 4d8a66d but reading MIB data is slow
+   on ar8xxx switches.
+
+   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
+   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
+   the request to update the MIB counter is cleared. */
+#if 0
+       .get_port_stats = ar8xxx_sw_get_port_stats,
+#endif
+};
+
+const struct ar8xxx_chip ar8327_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
+       .mii_lo_first = true,
+
+       .name = "Atheros AR8327",
+       .ports = AR8327_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8327_sw_ops,
+
+       .reg_port_stats_start = 0x1000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
+
+       .hw_init = ar8327_hw_init,
+       .cleanup = ar8327_cleanup,
+       .init_globals = ar8327_init_globals,
+       .init_port = ar8327_init_port,
+       .setup_port = ar8327_setup_port,
+       .read_port_status = ar8327_read_port_status,
+       .read_port_eee_status = ar8327_read_port_eee_status,
+       .atu_flush = ar8327_atu_flush,
+       .atu_flush_port = ar8327_atu_flush_port,
+       .vtu_flush = ar8327_vtu_flush,
+       .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
+       .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
+       .sw_hw_apply = ar8327_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
+};
+
+const struct ar8xxx_chip ar8337_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
+       .mii_lo_first = true,
+
+       .name = "Atheros AR8337",
+       .ports = AR8327_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8327_sw_ops,
+
+       .reg_port_stats_start = 0x1000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
+
+       .hw_init = ar8327_hw_init,
+       .cleanup = ar8327_cleanup,
+       .init_globals = ar8327_init_globals,
+       .init_port = ar8327_init_port,
+       .setup_port = ar8327_setup_port,
+       .read_port_status = ar8327_read_port_status,
+       .read_port_eee_status = ar8327_read_port_eee_status,
+       .atu_flush = ar8327_atu_flush,
+       .atu_flush_port = ar8327_atu_flush_port,
+       .vtu_flush = ar8327_vtu_flush,
+       .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
+       .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
+       .sw_hw_apply = ar8327_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
+};
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/ar8327.h b/target/linux/generic/files-4.14/drivers/net/phy/ar8327.h
new file mode 100644 (file)
index 0000000..d53ef88
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * ar8327.h: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AR8327_H
+#define __AR8327_H
+
+#define AR8327_NUM_PORTS       7
+#define AR8327_NUM_LEDS                15
+#define AR8327_PORTS_ALL       0x7f
+#define AR8327_NUM_LED_CTRL_REGS       4
+
+#define AR8327_REG_MASK                                0x000
+
+#define AR8327_REG_PAD0_MODE                   0x004
+#define AR8327_REG_PAD5_MODE                   0x008
+#define AR8327_REG_PAD6_MODE                   0x00c
+#define   AR8327_PAD_MAC_MII_RXCLK_SEL         BIT(0)
+#define   AR8327_PAD_MAC_MII_TXCLK_SEL         BIT(1)
+#define   AR8327_PAD_MAC_MII_EN                        BIT(2)
+#define   AR8327_PAD_MAC_GMII_RXCLK_SEL                BIT(4)
+#define   AR8327_PAD_MAC_GMII_TXCLK_SEL                BIT(5)
+#define   AR8327_PAD_MAC_GMII_EN               BIT(6)
+#define   AR8327_PAD_SGMII_EN                  BIT(7)
+#define   AR8327_PAD_PHY_MII_RXCLK_SEL         BIT(8)
+#define   AR8327_PAD_PHY_MII_TXCLK_SEL         BIT(9)
+#define   AR8327_PAD_PHY_MII_EN                        BIT(10)
+#define   AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL   BIT(11)
+#define   AR8327_PAD_PHY_GMII_RXCLK_SEL                BIT(12)
+#define   AR8327_PAD_PHY_GMII_TXCLK_SEL                BIT(13)
+#define   AR8327_PAD_PHY_GMII_EN               BIT(14)
+#define   AR8327_PAD_PHYX_GMII_EN              BIT(16)
+#define   AR8327_PAD_PHYX_RGMII_EN             BIT(17)
+#define   AR8327_PAD_PHYX_MII_EN               BIT(18)
+#define   AR8327_PAD_SGMII_DELAY_EN            BIT(19)
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL     BITS(20, 2)
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S   20
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL     BITS(22, 2)
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S   22
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_EN      BIT(24)
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_EN      BIT(25)
+#define   AR8327_PAD_RGMII_EN                  BIT(26)
+
+#define AR8327_REG_POWER_ON_STRIP              0x010
+#define   AR8327_POWER_ON_STRIP_POWER_ON_SEL   BIT(31)
+#define   AR8327_POWER_ON_STRIP_LED_OPEN_EN    BIT(24)
+#define   AR8327_POWER_ON_STRIP_SERDES_AEN     BIT(7)
+
+#define AR8327_REG_INT_STATUS0                 0x020
+#define   AR8327_INT0_VT_DONE                  BIT(20)
+
+#define AR8327_REG_INT_STATUS1                 0x024
+#define AR8327_REG_INT_MASK0                   0x028
+#define AR8327_REG_INT_MASK1                   0x02c
+
+#define AR8327_REG_MODULE_EN                   0x030
+#define   AR8327_MODULE_EN_MIB                 BIT(0)
+
+#define AR8327_REG_MIB_FUNC                    0x034
+#define   AR8327_MIB_CPU_KEEP                  BIT(20)
+
+#define AR8327_REG_SERVICE_TAG                 0x048
+#define AR8327_REG_LED_CTRL(_i)                        (0x050 + (_i) * 4)
+#define AR8327_REG_LED_CTRL0                   0x050
+#define AR8327_REG_LED_CTRL1                   0x054
+#define AR8327_REG_LED_CTRL2                   0x058
+#define AR8327_REG_LED_CTRL3                   0x05c
+#define AR8327_REG_MAC_ADDR0                   0x060
+#define AR8327_REG_MAC_ADDR1                   0x064
+
+#define AR8327_REG_MAX_FRAME_SIZE              0x078
+#define   AR8327_MAX_FRAME_SIZE_MTU            BITS(0, 14)
+
+#define AR8327_REG_PORT_STATUS(_i)             (0x07c + (_i) * 4)
+#define   AR8327_PORT_STATUS_TXFLOW_AUTO       BIT(10)
+#define   AR8327_PORT_STATUS_RXFLOW_AUTO       BIT(11)
+
+#define AR8327_REG_HEADER_CTRL                 0x098
+#define AR8327_REG_PORT_HEADER(_i)             (0x09c + (_i) * 4)
+
+#define AR8327_REG_SGMII_CTRL                  0x0e0
+#define   AR8327_SGMII_CTRL_EN_PLL             BIT(1)
+#define   AR8327_SGMII_CTRL_EN_RX              BIT(2)
+#define   AR8327_SGMII_CTRL_EN_TX              BIT(3)
+
+#define AR8327_REG_EEE_CTRL                    0x100
+#define   AR8327_EEE_CTRL_DISABLE_PHY(_i)      BIT(4 + (_i) * 2)
+
+#define AR8327_REG_FRAME_ACK_CTRL0             0x210
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0   BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0  BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0 BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN0      BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN0       BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0    BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0    BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1   BIT(8)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1  BIT(9)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1 BIT(10)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN1      BIT(11)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN1       BIT(12)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1    BIT(13)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1    BIT(14)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2   BIT(16)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2  BIT(17)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2 BIT(18)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN2      BIT(19)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN2       BIT(20)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2    BIT(21)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2    BIT(22)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3   BIT(24)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3  BIT(25)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3 BIT(26)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN3      BIT(27)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN3       BIT(28)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3    BIT(29)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3    BIT(30)
+
+#define AR8327_REG_FRAME_ACK_CTRL1             0x214
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4   BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4  BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4 BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN4      BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN4       BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4    BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4    BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5   BIT(8)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5  BIT(9)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5 BIT(10)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN5      BIT(11)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN5       BIT(12)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5    BIT(13)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5    BIT(14)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6   BIT(16)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6  BIT(17)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6 BIT(18)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN6      BIT(19)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN6       BIT(20)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6    BIT(21)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6    BIT(22)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_V3_EN     BIT(24)
+#define   AR8327_FRAME_ACK_CTRL_PPPOE_EN       BIT(25)
+
+#define AR8327_REG_FRAME_ACK_CTRL(_i)          (0x210 + ((_i) / 4) * 0x4)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD       BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN      BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE     BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL          BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP           BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK                BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ                BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_S(_i)          (((_i) % 4) * 8)
+
+#define AR8327_REG_PORT_VLAN0(_i)              (0x420 + (_i) * 0x8)
+#define   AR8327_PORT_VLAN0_DEF_PRI_MASK       BITS(0, 3)
+#define   AR8327_PORT_VLAN0_DEF_SVID           BITS(0, 12)
+#define   AR8327_PORT_VLAN0_DEF_SVID_S         0
+#define   AR8327_PORT_VLAN0_DEF_SPRI           BITS(13, 3)
+#define   AR8327_PORT_VLAN0_DEF_SPRI_S         13
+#define   AR8327_PORT_VLAN0_DEF_CVID           BITS(16, 12)
+#define   AR8327_PORT_VLAN0_DEF_CVID_S         16
+#define   AR8327_PORT_VLAN0_DEF_CPRI           BITS(29, 3)
+#define   AR8327_PORT_VLAN0_DEF_CPRI_S         29
+
+#define AR8327_REG_PORT_VLAN1(_i)              (0x424 + (_i) * 0x8)
+#define   AR8327_PORT_VLAN1_VLAN_PRI_PROP      BIT(4)
+#define   AR8327_PORT_VLAN1_PORT_VLAN_PROP     BIT(6)
+#define   AR8327_PORT_VLAN1_OUT_MODE           BITS(12, 2)
+#define   AR8327_PORT_VLAN1_OUT_MODE_S         12
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNMOD     0
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNTAG     1
+#define   AR8327_PORT_VLAN1_OUT_MODE_TAG       2
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH   3
+
+#define AR8327_REG_ATU_DATA0                   0x600
+#define   AR8327_ATU_ADDR0                     BITS(0, 8)
+#define   AR8327_ATU_ADDR0_S                   0
+#define   AR8327_ATU_ADDR1                     BITS(8, 8)
+#define   AR8327_ATU_ADDR1_S                   8
+#define   AR8327_ATU_ADDR2                     BITS(16, 8)
+#define   AR8327_ATU_ADDR2_S                   16
+#define   AR8327_ATU_ADDR3                     BITS(24, 8)
+#define   AR8327_ATU_ADDR3_S                   24
+#define AR8327_REG_ATU_DATA1                   0x604
+#define   AR8327_ATU_ADDR4                     BITS(0, 8)
+#define   AR8327_ATU_ADDR4_S                   0
+#define   AR8327_ATU_ADDR5                     BITS(8, 8)
+#define   AR8327_ATU_ADDR5_S                   8
+#define   AR8327_ATU_PORTS                     BITS(16, 7)
+#define   AR8327_ATU_PORT0                     BIT(16)
+#define   AR8327_ATU_PORT1                     BIT(17)
+#define   AR8327_ATU_PORT2                     BIT(18)
+#define   AR8327_ATU_PORT3                     BIT(19)
+#define   AR8327_ATU_PORT4                     BIT(20)
+#define   AR8327_ATU_PORT5                     BIT(21)
+#define   AR8327_ATU_PORT6                     BIT(22)
+#define AR8327_REG_ATU_DATA2                   0x608
+#define   AR8327_ATU_STATUS                    BITS(0, 4)
+
+#define AR8327_REG_ATU_FUNC                    0x60c
+#define   AR8327_ATU_FUNC_OP                   BITS(0, 4)
+#define   AR8327_ATU_FUNC_OP_NOOP              0x0
+#define   AR8327_ATU_FUNC_OP_FLUSH             0x1
+#define   AR8327_ATU_FUNC_OP_LOAD              0x2
+#define   AR8327_ATU_FUNC_OP_PURGE             0x3
+#define   AR8327_ATU_FUNC_OP_FLUSH_UNLOCKED    0x4
+#define   AR8327_ATU_FUNC_OP_FLUSH_PORT                0x5
+#define   AR8327_ATU_FUNC_OP_GET_NEXT          0x6
+#define   AR8327_ATU_FUNC_OP_SEARCH_MAC                0x7
+#define   AR8327_ATU_FUNC_OP_CHANGE_TRUNK      0x8
+#define   AR8327_ATU_PORT_NUM                  BITS(8, 4)
+#define   AR8327_ATU_PORT_NUM_S                        8
+#define   AR8327_ATU_FUNC_BUSY                 BIT(31)
+
+#define AR8327_REG_VTU_FUNC0                   0x0610
+#define   AR8327_VTU_FUNC0_EG_MODE             BITS(4, 14)
+#define   AR8327_VTU_FUNC0_EG_MODE_S(_i)       (4 + (_i) * 2)
+#define   AR8327_VTU_FUNC0_EG_MODE_KEEP                0
+#define   AR8327_VTU_FUNC0_EG_MODE_UNTAG       1
+#define   AR8327_VTU_FUNC0_EG_MODE_TAG         2
+#define   AR8327_VTU_FUNC0_EG_MODE_NOT         3
+#define   AR8327_VTU_FUNC0_IVL                 BIT(19)
+#define   AR8327_VTU_FUNC0_VALID               BIT(20)
+
+#define AR8327_REG_VTU_FUNC1                   0x0614
+#define   AR8327_VTU_FUNC1_OP                  BITS(0, 3)
+#define   AR8327_VTU_FUNC1_OP_NOOP             0
+#define   AR8327_VTU_FUNC1_OP_FLUSH            1
+#define   AR8327_VTU_FUNC1_OP_LOAD             2
+#define   AR8327_VTU_FUNC1_OP_PURGE            3
+#define   AR8327_VTU_FUNC1_OP_REMOVE_PORT      4
+#define   AR8327_VTU_FUNC1_OP_GET_NEXT         5
+#define   AR8327_VTU_FUNC1_OP_GET_ONE          6
+#define   AR8327_VTU_FUNC1_FULL                        BIT(4)
+#define   AR8327_VTU_FUNC1_PORT                        BIT(8, 4)
+#define   AR8327_VTU_FUNC1_PORT_S              8
+#define   AR8327_VTU_FUNC1_VID                 BIT(16, 12)
+#define   AR8327_VTU_FUNC1_VID_S               16
+#define   AR8327_VTU_FUNC1_BUSY                        BIT(31)
+
+#define AR8327_REG_ARL_CTRL                    0x0618
+
+#define AR8327_REG_FWD_CTRL0                   0x620
+#define   AR8327_FWD_CTRL0_CPU_PORT_EN         BIT(10)
+#define   AR8327_FWD_CTRL0_MIRROR_PORT         BITS(4, 4)
+#define   AR8327_FWD_CTRL0_MIRROR_PORT_S       4
+
+#define AR8327_REG_FWD_CTRL1                   0x624
+#define   AR8327_FWD_CTRL1_UC_FLOOD            BITS(0, 7)
+#define   AR8327_FWD_CTRL1_UC_FLOOD_S          0
+#define   AR8327_FWD_CTRL1_MC_FLOOD            BITS(8, 7)
+#define   AR8327_FWD_CTRL1_MC_FLOOD_S          8
+#define   AR8327_FWD_CTRL1_BC_FLOOD            BITS(16, 7)
+#define   AR8327_FWD_CTRL1_BC_FLOOD_S          16
+#define   AR8327_FWD_CTRL1_IGMP                        BITS(24, 7)
+#define   AR8327_FWD_CTRL1_IGMP_S              24
+
+#define AR8327_REG_PORT_LOOKUP(_i)             (0x660 + (_i) * 0xc)
+#define   AR8327_PORT_LOOKUP_MEMBER            BITS(0, 7)
+#define   AR8327_PORT_LOOKUP_IN_MODE           BITS(8, 2)
+#define   AR8327_PORT_LOOKUP_IN_MODE_S         8
+#define   AR8327_PORT_LOOKUP_STATE             BITS(16, 3)
+#define   AR8327_PORT_LOOKUP_STATE_S           16
+#define   AR8327_PORT_LOOKUP_LEARN             BIT(20)
+#define   AR8327_PORT_LOOKUP_ING_MIRROR_EN     BIT(25)
+
+#define AR8327_REG_PORT_PRIO(_i)               (0x664 + (_i) * 0xc)
+
+#define AR8327_REG_PORT_HOL_CTRL1(_i)          (0x974 + (_i) * 0x8)
+#define   AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN   BIT(16)
+
+#define AR8337_PAD_MAC06_EXCHANGE_EN           BIT(31)
+
+enum ar8327_led_pattern {
+       AR8327_LED_PATTERN_OFF = 0,
+       AR8327_LED_PATTERN_BLINK,
+       AR8327_LED_PATTERN_ON,
+       AR8327_LED_PATTERN_RULE,
+};
+
+struct ar8327_led_entry {
+       unsigned reg;
+       unsigned shift;
+};
+
+struct ar8327_led {
+       struct led_classdev cdev;
+       struct ar8xxx_priv *sw_priv;
+
+       char *name;
+       bool active_low;
+       u8 led_num;
+       enum ar8327_led_mode mode;
+
+       struct mutex mutex;
+       spinlock_t lock;
+       struct work_struct led_work;
+       bool enable_hw_mode;
+       enum ar8327_led_pattern pattern;
+};
+
+struct ar8327_data {
+       u32 port0_status;
+       u32 port6_status;
+
+       struct ar8327_led **leds;
+       unsigned int num_leds;
+
+       /* all fields below are cleared on reset */
+       bool eee[AR8XXX_NUM_PHYS];
+};
+
+#endif
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/Kconfig b/target/linux/generic/files-4.14/drivers/net/phy/b53/Kconfig
new file mode 100644 (file)
index 0000000..08287e7
--- /dev/null
@@ -0,0 +1,37 @@
+menuconfig SWCONFIG_B53
+       tristate "Broadcom bcm53xx managed switch support"
+       depends on SWCONFIG
+       help
+         This driver adds support for Broadcom managed switch chips. It supports
+         BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
+         integrated switches.
+
+config SWCONFIG_B53_SPI_DRIVER
+       tristate "B53 SPI connected switch driver"
+       depends on SWCONFIG_B53 && SPI
+       help
+         Select to enable support for registering switches configured through SPI.
+
+config SWCONFIG_B53_PHY_DRIVER
+       tristate "B53 MDIO connected switch driver"
+       depends on SWCONFIG_B53
+       select SWCONFIG_B53_PHY_FIXUP
+       help
+         Select to enable support for registering switches configured through MDIO.
+
+config SWCONFIG_B53_MMAP_DRIVER
+       tristate "B53 MMAP connected switch driver"
+       depends on SWCONFIG_B53
+       help
+         Select to enable support for memory-mapped switches like the BCM63XX
+         integrated switches.
+
+config SWCONFIG_B53_SRAB_DRIVER
+       tristate "B53 SRAB connected switch driver"
+       depends on SWCONFIG_B53
+       help
+         Select to enable support for memory-mapped Switch Register Access
+         Bridge Registers (SRAB) like it is found on the BCM53010
+
+config SWCONFIG_B53_PHY_FIXUP
+       bool
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/Makefile b/target/linux/generic/files-4.14/drivers/net/phy/b53/Makefile
new file mode 100644 (file)
index 0000000..13ff366
--- /dev/null
@@ -0,0 +1,10 @@
+obj-$(CONFIG_SWCONFIG_B53)             += b53_common.o
+
+obj-$(CONFIG_SWCONFIG_B53_PHY_FIXUP)   += b53_phy_fixup.o
+
+obj-$(CONFIG_SWCONFIG_B53_MMAP_DRIVER) += b53_mmap.o
+obj-$(CONFIG_SWCONFIG_B53_SRAB_DRIVER) += b53_srab.o
+obj-$(CONFIG_SWCONFIG_B53_PHY_DRIVER)  += b53_mdio.o
+obj-$(CONFIG_SWCONFIG_B53_SPI_DRIVER)  += b53_spi.o
+
+ccflags-y                              += -Werror
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_common.c
new file mode 100644 (file)
index 0000000..670588c
--- /dev/null
@@ -0,0 +1,1722 @@
+/*
+ * B53 switch driver main logic
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_regs.h"
+#include "b53_priv.h"
+
+/* buffer size needed for displaying all MIBs with max'd values */
+#define B53_BUF_SIZE   1188
+
+struct b53_mib_desc {
+       u8 size;
+       u8 offset;
+       const char *name;
+};
+
+/* BCM5365 MIB counters */
+static const struct b53_mib_desc b53_mibs_65[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x44, "RxOctets" },
+       { 4, 0x4c, "RxUndersizePkts" },
+       { 4, 0x50, "RxPausePkts" },
+       { 4, 0x54, "Pkts64Octets" },
+       { 4, 0x58, "Pkts65to127Octets" },
+       { 4, 0x5c, "Pkts128to255Octets" },
+       { 4, 0x60, "Pkts256to511Octets" },
+       { 4, 0x64, "Pkts512to1023Octets" },
+       { 4, 0x68, "Pkts1024to1522Octets" },
+       { 4, 0x6c, "RxOversizePkts" },
+       { 4, 0x70, "RxJabbers" },
+       { 4, 0x74, "RxAlignmentErrors" },
+       { 4, 0x78, "RxFCSErrors" },
+       { 8, 0x7c, "RxGoodOctets" },
+       { 4, 0x84, "RxDropPkts" },
+       { 4, 0x88, "RxUnicastPkts" },
+       { 4, 0x8c, "RxMulticastPkts" },
+       { 4, 0x90, "RxBroadcastPkts" },
+       { 4, 0x94, "RxSAChanges" },
+       { 4, 0x98, "RxFragments" },
+       { },
+};
+
+#define B63XX_MIB_TXB_ID       0       /* TxOctets */
+#define B63XX_MIB_RXB_ID       14      /* RxOctets */
+
+/* BCM63xx MIB counters */
+static const struct b53_mib_desc b53_mibs_63xx[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x0c, "TxQoSPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x3c, "TxQoSOctets" },
+       { 8, 0x44, "RxOctets" },
+       { 4, 0x4c, "RxUndersizePkts" },
+       { 4, 0x50, "RxPausePkts" },
+       { 4, 0x54, "Pkts64Octets" },
+       { 4, 0x58, "Pkts65to127Octets" },
+       { 4, 0x5c, "Pkts128to255Octets" },
+       { 4, 0x60, "Pkts256to511Octets" },
+       { 4, 0x64, "Pkts512to1023Octets" },
+       { 4, 0x68, "Pkts1024to1522Octets" },
+       { 4, 0x6c, "RxOversizePkts" },
+       { 4, 0x70, "RxJabbers" },
+       { 4, 0x74, "RxAlignmentErrors" },
+       { 4, 0x78, "RxFCSErrors" },
+       { 8, 0x7c, "RxGoodOctets" },
+       { 4, 0x84, "RxDropPkts" },
+       { 4, 0x88, "RxUnicastPkts" },
+       { 4, 0x8c, "RxMulticastPkts" },
+       { 4, 0x90, "RxBroadcastPkts" },
+       { 4, 0x94, "RxSAChanges" },
+       { 4, 0x98, "RxFragments" },
+       { 4, 0xa0, "RxSymbolErrors" },
+       { 4, 0xa4, "RxQoSPkts" },
+       { 8, 0xa8, "RxQoSOctets" },
+       { 4, 0xb0, "Pkts1523to2047Octets" },
+       { 4, 0xb4, "Pkts2048to4095Octets" },
+       { 4, 0xb8, "Pkts4096to8191Octets" },
+       { 4, 0xbc, "Pkts8192to9728Octets" },
+       { 4, 0xc0, "RxDiscarded" },
+       { }
+};
+
+#define B53XX_MIB_TXB_ID       0       /* TxOctets */
+#define B53XX_MIB_RXB_ID       12      /* RxOctets */
+
+/* MIB counters */
+static const struct b53_mib_desc b53_mibs[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x50, "RxOctets" },
+       { 4, 0x58, "RxUndersizePkts" },
+       { 4, 0x5c, "RxPausePkts" },
+       { 4, 0x60, "Pkts64Octets" },
+       { 4, 0x64, "Pkts65to127Octets" },
+       { 4, 0x68, "Pkts128to255Octets" },
+       { 4, 0x6c, "Pkts256to511Octets" },
+       { 4, 0x70, "Pkts512to1023Octets" },
+       { 4, 0x74, "Pkts1024to1522Octets" },
+       { 4, 0x78, "RxOversizePkts" },
+       { 4, 0x7c, "RxJabbers" },
+       { 4, 0x80, "RxAlignmentErrors" },
+       { 4, 0x84, "RxFCSErrors" },
+       { 8, 0x88, "RxGoodOctets" },
+       { 4, 0x90, "RxDropPkts" },
+       { 4, 0x94, "RxUnicastPkts" },
+       { 4, 0x98, "RxMulticastPkts" },
+       { 4, 0x9c, "RxBroadcastPkts" },
+       { 4, 0xa0, "RxSAChanges" },
+       { 4, 0xa4, "RxFragments" },
+       { 4, 0xa8, "RxJumboPkts" },
+       { 4, 0xac, "RxSymbolErrors" },
+       { 4, 0xc0, "RxDiscarded" },
+       { }
+};
+
+static int b53_do_vlan_op(struct b53_device *dev, u8 op)
+{
+       unsigned int i;
+
+       b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
+
+       for (i = 0; i < 10; i++) {
+               u8 vta;
+
+               b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
+               if (!(vta & VTA_START_CMD))
+                       return 0;
+
+               usleep_range(100, 200);
+       }
+
+       return -EIO;
+}
+
+static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
+                              u16 untag)
+{
+       if (is5325(dev)) {
+               u32 entry = 0;
+
+               if (members) {
+                       entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) |
+                               members;
+                       if (dev->core_rev >= 3)
+                               entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S;
+                       else
+                               entry |= VA_VALID_25;
+               }
+
+               b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
+                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
+       } else if (is5365(dev)) {
+               u16 entry = 0;
+
+               if (members)
+                       entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) |
+                               members | VA_VALID_65;
+
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
+                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
+       } else {
+               b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
+               b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
+                           (untag << VTE_UNTAG_S) | members);
+
+               b53_do_vlan_op(dev, VTA_CMD_WRITE);
+       }
+}
+
+void b53_set_forwarding(struct b53_device *dev, int enable)
+{
+       u8 mgmt;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (enable)
+               mgmt |= SM_SW_FWD_EN;
+       else
+               mgmt &= ~SM_SW_FWD_EN;
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static void b53_enable_vlan(struct b53_device *dev, int enable)
+{
+       u8 mgmt, vc0, vc1, vc4 = 0, vc5;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
+       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
+
+       if (is5325(dev) || is5365(dev)) {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
+       } else if (is63xx(dev)) {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
+       } else {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
+       }
+
+       mgmt &= ~SM_SW_FWD_MODE;
+
+       if (enable) {
+               vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
+               vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
+               vc4 &= ~VC4_ING_VID_CHECK_MASK;
+               vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
+               vc5 |= VC5_DROP_VTABLE_MISS;
+
+               if (is5325(dev))
+                       vc0 &= ~VC0_RESERVED_1;
+
+               if (is5325(dev) || is5365(dev))
+                       vc1 |= VC1_RX_MCST_TAG_EN;
+
+               if (!is5325(dev) && !is5365(dev)) {
+                       if (dev->allow_vid_4095)
+                               vc5 |= VC5_VID_FFF_EN;
+                       else
+                               vc5 &= ~VC5_VID_FFF_EN;
+               }
+       } else {
+               vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
+               vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
+               vc4 &= ~VC4_ING_VID_CHECK_MASK;
+               vc5 &= ~VC5_DROP_VTABLE_MISS;
+
+               if (is5325(dev) || is5365(dev))
+                       vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
+               else
+                       vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
+
+               if (is5325(dev) || is5365(dev))
+                       vc1 &= ~VC1_RX_MCST_TAG_EN;
+
+               if (!is5325(dev) && !is5365(dev))
+                       vc5 &= ~VC5_VID_FFF_EN;
+       }
+
+       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
+       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
+
+       if (is5325(dev) || is5365(dev)) {
+               /* enable the high 8 bit vid check on 5325 */
+               if (is5325(dev) && enable)
+                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
+                                  VC3_HIGH_8BIT_EN);
+               else
+                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
+       } else if (is63xx(dev)) {
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
+       } else {
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
+       }
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static int b53_set_jumbo(struct b53_device *dev, int enable, int allow_10_100)
+{
+       u32 port_mask = 0;
+       u16 max_size = JMS_MIN_SIZE;
+
+       if (is5325(dev) || is5365(dev))
+               return -EINVAL;
+
+       if (enable) {
+               port_mask = dev->enabled_ports;
+               max_size = JMS_MAX_SIZE;
+               if (allow_10_100)
+                       port_mask |= JPM_10_100_JUMBO_EN;
+       }
+
+       b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
+       return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
+}
+
+static int b53_flush_arl(struct b53_device *dev)
+{
+       unsigned int i;
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+                  FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
+
+       for (i = 0; i < 10; i++) {
+               u8 fast_age_ctrl;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+                         &fast_age_ctrl);
+
+               if (!(fast_age_ctrl & FAST_AGE_DONE))
+                       return 0;
+
+               mdelay(1);
+       }
+
+       pr_warn("time out while flushing ARL\n");
+
+       return -EINVAL;
+}
+
+static void b53_enable_ports(struct b53_device *dev)
+{
+       unsigned i;
+
+       b53_for_each_port(dev, i) {
+               u8 port_ctrl;
+               u16 pvlan_mask;
+
+               /*
+                * prevent leaking packets between wan and lan in unmanaged
+                * mode through port vlans.
+                */
+               if (dev->enable_vlan || is_cpu_port(dev, i))
+                       pvlan_mask = 0x1ff;
+               else if (is531x5(dev) || is5301x(dev))
+                       /* BCM53115 may use a different port as cpu port */
+                       pvlan_mask = BIT(dev->sw_dev.cpu_port);
+               else
+                       pvlan_mask = BIT(B53_CPU_PORT);
+
+               /* BCM5325 CPU port is at 8 */
+               if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25)
+                       i = B53_CPU_PORT;
+
+               if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7))
+                       /* disable unused ports 6 & 7 */
+                       port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
+               else if (i == B53_CPU_PORT)
+                       port_ctrl = PORT_CTRL_RX_BCST_EN |
+                                   PORT_CTRL_RX_MCST_EN |
+                                   PORT_CTRL_RX_UCST_EN;
+               else
+                       port_ctrl = 0;
+
+               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i),
+                           pvlan_mask);
+
+               /* port state is handled by bcm63xx_enet driver */
+               if (!is63xx(dev) && !(is5301x(dev) && i == 6))
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i),
+                                  port_ctrl);
+       }
+}
+
+static void b53_enable_mib(struct b53_device *dev)
+{
+       u8 gc;
+
+       b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+       gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
+
+       b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
+}
+
+static int b53_apply(struct b53_device *dev)
+{
+       int i;
+
+       /* clear all vlan entries */
+       if (is5325(dev) || is5365(dev)) {
+               for (i = 1; i < dev->sw_dev.vlans; i++)
+                       b53_set_vlan_entry(dev, i, 0, 0);
+       } else {
+               b53_do_vlan_op(dev, VTA_CMD_CLEAR);
+       }
+
+       b53_enable_vlan(dev, dev->enable_vlan);
+
+       /* fill VLAN table */
+       if (dev->enable_vlan) {
+               for (i = 0; i < dev->sw_dev.vlans; i++) {
+                       struct b53_vlan *vlan = &dev->vlans[i];
+
+                       if (!vlan->members)
+                               continue;
+
+                       b53_set_vlan_entry(dev, i, vlan->members, vlan->untag);
+               }
+
+               b53_for_each_port(dev, i)
+                       b53_write16(dev, B53_VLAN_PAGE,
+                                   B53_VLAN_PORT_DEF_TAG(i),
+                                   dev->ports[i].pvid);
+       } else {
+               b53_for_each_port(dev, i)
+                       b53_write16(dev, B53_VLAN_PAGE,
+                                   B53_VLAN_PORT_DEF_TAG(i), 1);
+
+       }
+
+       b53_enable_ports(dev);
+
+       if (!is5325(dev) && !is5365(dev))
+               b53_set_jumbo(dev, dev->enable_jumbo, 1);
+
+       return 0;
+}
+
+static void b53_switch_reset_gpio(struct b53_device *dev)
+{
+       int gpio = dev->reset_gpio;
+
+       if (gpio < 0)
+               return;
+
+       /*
+        * Reset sequence: RESET low(50ms)->high(20ms)
+        */
+       gpio_set_value(gpio, 0);
+       mdelay(50);
+
+       gpio_set_value(gpio, 1);
+       mdelay(20);
+
+       dev->current_page = 0xff;
+}
+
+static int b53_configure_ports_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       u32 port_num;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+
+       for_each_available_child_of_node(dn, pn) {
+               struct device_node *fixed_link;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               fixed_link = of_get_child_by_name(pn, "fixed-link");
+               if (fixed_link) {
+                       u32 spd;
+                       u8 po = GMII_PO_LINK;
+                       int mode = of_get_phy_mode(pn);
+
+                       if (!of_property_read_u32(fixed_link, "speed", &spd)) {
+                               switch (spd) {
+                               case 10:
+                                       po |= GMII_PO_SPEED_10M;
+                                       break;
+                               case 100:
+                                       po |= GMII_PO_SPEED_100M;
+                                       break;
+                               case 2000:
+                                       if (is_imp_port(dev, port_num))
+                                               po |= PORT_OVERRIDE_SPEED_2000M;
+                                       else
+                                               po |= GMII_PO_SPEED_2000M;
+                                       /* fall through */
+                               case 1000:
+                                       po |= GMII_PO_SPEED_1000M;
+                                       break;
+                               }
+                       }
+
+                       if (of_property_read_bool(fixed_link, "full-duplex"))
+                               po |= PORT_OVERRIDE_FULL_DUPLEX;
+                       if (of_property_read_bool(fixed_link, "pause"))
+                               po |= GMII_PO_RX_FLOW;
+                       if (of_property_read_bool(fixed_link, "asym-pause"))
+                               po |= GMII_PO_TX_FLOW;
+
+                       if (is_imp_port(dev, port_num)) {
+                               po |= PORT_OVERRIDE_EN;
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII)
+                                       po |= PORT_OVERRIDE_RV_MII_25;
+
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_PORT_OVERRIDE_CTRL, po);
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII) {
+                                       b53_read8(dev, B53_CTRL_PAGE,
+                                                 B53_PORT_OVERRIDE_CTRL, &po);
+                                       if (!(po & PORT_OVERRIDE_RV_MII_25))
+                                       pr_err("Failed to enable reverse MII mode\n");
+                                       return -EINVAL;
+                               }
+                       } else {
+                               po |= GMII_PO_EN;
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_GMII_PORT_OVERRIDE_CTRL(port_num),
+                                          po);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int b53_configure_ports(struct b53_device *dev)
+{
+       u8 cpu_port = dev->sw_dev.cpu_port;
+
+       /* configure MII port if necessary */
+       if (is5325(dev)) {
+               u8 mii_port_override;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                         &mii_port_override);
+               /* reverse mii needs to be enabled */
+               if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                  mii_port_override | PORT_OVERRIDE_RV_MII_25);
+                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                 &mii_port_override);
+
+                       if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+                               pr_err("Failed to enable reverse MII mode\n");
+                               return -EINVAL;
+                       }
+               }
+       } else if (is531x5(dev) && cpu_port == B53_CPU_PORT) {
+               u8 mii_port_override;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                         &mii_port_override);
+               b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                          mii_port_override | PORT_OVERRIDE_EN |
+                          PORT_OVERRIDE_LINK);
+
+               /* BCM47189 has another interface connected to the port 5 */
+               if (dev->enabled_ports & BIT(5)) {
+                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(5);
+                       u8 gmii_po;
+
+                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
+                       gmii_po |= GMII_PO_LINK |
+                                  GMII_PO_RX_FLOW |
+                                  GMII_PO_TX_FLOW |
+                                  GMII_PO_EN;
+                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
+               }
+       } else if (is5301x(dev)) {
+               if (cpu_port == 8) {
+                       u8 mii_port_override;
+
+                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                 &mii_port_override);
+                       mii_port_override |= PORT_OVERRIDE_LINK |
+                                            PORT_OVERRIDE_RX_FLOW |
+                                            PORT_OVERRIDE_TX_FLOW |
+                                            PORT_OVERRIDE_SPEED_2000M |
+                                            PORT_OVERRIDE_EN;
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                  mii_port_override);
+
+                       /* TODO: Ports 5 & 7 require some extra handling */
+               } else {
+                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(cpu_port);
+                       u8 gmii_po;
+
+                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
+                       gmii_po |= GMII_PO_LINK |
+                                  GMII_PO_RX_FLOW |
+                                  GMII_PO_TX_FLOW |
+                                  GMII_PO_EN |
+                                  GMII_PO_SPEED_2000M;
+                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
+               }
+       }
+
+       return 0;
+}
+
+static int b53_switch_reset(struct b53_device *dev)
+{
+       int ret = 0;
+       u8 mgmt;
+
+       b53_switch_reset_gpio(dev);
+
+       if (is539x(dev)) {
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
+       }
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (!(mgmt & SM_SW_FWD_EN)) {
+               mgmt &= ~SM_SW_FWD_MODE;
+               mgmt |= SM_SW_FWD_EN;
+
+               b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+               b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+               if (!(mgmt & SM_SW_FWD_EN)) {
+                       pr_err("Failed to enable switch!\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* enable all ports */
+       b53_enable_ports(dev);
+
+       if (dev->dev->of_node)
+               ret = b53_configure_ports_of(dev);
+       else
+               ret = b53_configure_ports(dev);
+
+       if (ret)
+               return ret;
+
+       b53_enable_mib(dev);
+
+       return b53_flush_arl(dev);
+}
+
+/*
+ * Swconfig glue functions
+ */
+
+static int b53_global_get_vlan_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->enable_vlan;
+
+       return 0;
+}
+
+static int b53_global_set_vlan_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->enable_vlan = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_jumbo_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->enable_jumbo;
+
+       return 0;
+}
+
+static int b53_global_set_jumbo_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->enable_jumbo = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_4095_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->allow_vid_4095;
+
+       return 0;
+}
+
+static int b53_global_set_4095_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->allow_vid_4095 = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_ports(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x",
+                           priv->enabled_ports);
+       val->value.s = priv->buf;
+
+       return 0;
+}
+
+static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       *val = priv->ports[port].pvid;
+
+       return 0;
+}
+
+static int b53_port_set_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (val > 15 && is5325(priv))
+               return -EINVAL;
+       if (val == 4095 && !priv->allow_vid_4095)
+               return -EINVAL;
+
+       priv->ports[port].pvid = val;
+
+       return 0;
+}
+
+static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       struct switch_port *port = &val->value.ports[0];
+       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+       int i;
+
+       val->len = 0;
+
+       if (!vlan->members)
+               return 0;
+
+       for (i = 0; i < dev->ports; i++) {
+               if (!(vlan->members & BIT(i)))
+                       continue;
+
+
+               if (!(vlan->untag & BIT(i)))
+                       port->flags = BIT(SWITCH_PORT_FLAG_TAGGED);
+               else
+                       port->flags = 0;
+
+               port->id = i;
+               val->len++;
+               port++;
+       }
+
+       return 0;
+}
+
+static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       struct switch_port *port;
+       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+       int i;
+
+       /* only BCM5325 and BCM5365 supports VID 0 */
+       if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv))
+               return -EINVAL;
+
+       /* VLAN 4095 needs special handling */
+       if (val->port_vlan == 4095 && !priv->allow_vid_4095)
+               return -EINVAL;
+
+       port = &val->value.ports[0];
+       vlan->members = 0;
+       vlan->untag = 0;
+       for (i = 0; i < val->len; i++, port++) {
+               vlan->members |= BIT(port->id);
+
+               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) {
+                       vlan->untag |= BIT(port->id);
+                       priv->ports[port->id].pvid = val->port_vlan;
+               };
+       }
+
+       /* ignore disabled ports */
+       vlan->members &= priv->enabled_ports;
+       vlan->untag &= priv->enabled_ports;
+
+       return 0;
+}
+
+static int b53_port_get_link(struct switch_dev *dev, int port,
+                            struct switch_port_link *link)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (is_cpu_port(priv, port)) {
+               link->link = 1;
+               link->duplex = 1;
+               link->speed = is5325(priv) || is5365(priv) ?
+                               SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000;
+               link->aneg = 0;
+       } else if (priv->enabled_ports & BIT(port)) {
+               u32 speed;
+               u16 lnk, duplex;
+
+               b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk);
+               b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex);
+
+               lnk = (lnk >> port) & 1;
+               duplex = (duplex >> port) & 1;
+
+               if (is5325(priv) || is5365(priv)) {
+                       u16 tmp;
+
+                       b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp);
+                       speed = SPEED_PORT_FE(tmp, port);
+               } else {
+                       b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed);
+                       speed = SPEED_PORT_GE(speed, port);
+               }
+
+               link->link = lnk;
+               if (lnk) {
+                       link->duplex = duplex;
+                       switch (speed) {
+                       case SPEED_STAT_10M:
+                               link->speed = SWITCH_PORT_SPEED_10;
+                               break;
+                       case SPEED_STAT_100M:
+                               link->speed = SWITCH_PORT_SPEED_100;
+                               break;
+                       case SPEED_STAT_1000M:
+                               link->speed = SWITCH_PORT_SPEED_1000;
+                               break;
+                       }
+               }
+
+               link->aneg = 1;
+       } else {
+               link->link = 0;
+       }
+
+       return 0;
+
+}
+
+static int b53_port_set_link(struct switch_dev *sw_dev, int port,
+                            struct switch_port_link *link)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+
+       /*
+        * TODO: BCM63XX requires special handling as it can have external phys
+        * and ports might be GE or only FE
+        */
+       if (is63xx(dev))
+               return -ENOTSUPP;
+
+       if (port == sw_dev->cpu_port)
+               return -EINVAL;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -EINVAL;
+
+       if (link->speed == SWITCH_PORT_SPEED_1000 &&
+           (is5325(dev) || is5365(dev)))
+               return -EINVAL;
+
+       if (link->speed == SWITCH_PORT_SPEED_1000 && !link->duplex)
+               return -EINVAL;
+
+       return switch_generic_set_link(sw_dev, port, link);
+}
+
+static int b53_phy_read16(struct switch_dev *dev, int addr, u8 reg, u16 *value)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (priv->ops->phy_read16)
+               return priv->ops->phy_read16(priv, addr, reg, value);
+
+       return b53_read16(priv, B53_PORT_MII_PAGE(addr), reg, value);
+}
+
+static int b53_phy_write16(struct switch_dev *dev, int addr, u8 reg, u16 value)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (priv->ops->phy_write16)
+               return priv->ops->phy_write16(priv, addr, reg, value);
+
+       return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg, value);
+}
+
+static int b53_global_reset_switch(struct switch_dev *dev)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       /* reset vlans */
+       priv->enable_vlan = 0;
+       priv->enable_jumbo = 0;
+       priv->allow_vid_4095 = 0;
+
+       memset(priv->vlans, 0, sizeof(*priv->vlans) * dev->vlans);
+       memset(priv->ports, 0, sizeof(*priv->ports) * dev->ports);
+
+       return b53_switch_reset(priv);
+}
+
+static int b53_global_apply_config(struct switch_dev *dev)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       /* disable switching */
+       b53_set_forwarding(priv, 0);
+
+       b53_apply(priv);
+
+       /* enable switching */
+       b53_set_forwarding(priv, 1);
+
+       return 0;
+}
+
+
+static int b53_global_reset_mib(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       u8 gc;
+
+       b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
+       mdelay(1);
+       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
+       mdelay(1);
+
+       return 0;
+}
+
+static int b53_port_get_mib(struct switch_dev *sw_dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+       const struct b53_mib_desc *mibs;
+       int port = val->port_vlan;
+       int len = 0;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -1;
+
+       if (is5365(dev)) {
+               if (port == 5)
+                       port = 8;
+
+               mibs = b53_mibs_65;
+       } else if (is63xx(dev)) {
+               mibs = b53_mibs_63xx;
+       } else {
+               mibs = b53_mibs;
+       }
+
+       dev->buf[0] = 0;
+
+       for (; mibs->size > 0; mibs++) {
+               u64 val;
+
+               if (mibs->size == 8) {
+                       b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val);
+               } else {
+                       u32 val32;
+
+                       b53_read32(dev, B53_MIB_PAGE(port), mibs->offset,
+                                  &val32);
+                       val = val32;
+               }
+
+               len += snprintf(dev->buf + len, B53_BUF_SIZE - len,
+                               "%-20s: %llu\n", mibs->name, val);
+       }
+
+       val->len = len;
+       val->value.s = dev->buf;
+
+       return 0;
+}
+
+static int b53_port_get_stats(struct switch_dev *sw_dev, int port,
+                               struct switch_port_stats *stats)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+       const struct b53_mib_desc *mibs;
+       int txb_id, rxb_id;
+       u64 rxb, txb;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -EINVAL;
+
+       txb_id = B53XX_MIB_TXB_ID;
+       rxb_id = B53XX_MIB_RXB_ID;
+
+       if (is5365(dev)) {
+               if (port == 5)
+                       port = 8;
+
+               mibs = b53_mibs_65;
+       } else if (is63xx(dev)) {
+               mibs = b53_mibs_63xx;
+               txb_id = B63XX_MIB_TXB_ID;
+               rxb_id = B63XX_MIB_RXB_ID;
+       } else {
+               mibs = b53_mibs;
+       }
+
+       dev->buf[0] = 0;
+
+       if (mibs->size == 8) {
+               b53_read64(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &txb);
+               b53_read64(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &rxb);
+       } else {
+               u32 val32;
+
+               b53_read32(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &val32);
+               txb = val32;
+
+               b53_read32(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &val32);
+               rxb = val32;
+       }
+
+       stats->tx_bytes = txb;
+       stats->rx_bytes = rxb;
+
+       return 0;
+}
+
+static struct switch_attr b53_global_ops_25[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+};
+
+static struct switch_attr b53_global_ops_65[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "reset_mib",
+               .description = "Reset MIB counters",
+               .set = b53_global_reset_mib,
+       },
+};
+
+static struct switch_attr b53_global_ops[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available Ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "reset_mib",
+               .description = "Reset MIB counters",
+               .set = b53_global_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_jumbo",
+               .description = "Enable Jumbo Frames",
+               .set = b53_global_set_jumbo_enable,
+               .get = b53_global_get_jumbo_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "allow_vid_4095",
+               .description = "Allow VID 4095",
+               .set = b53_global_set_4095_enable,
+               .get = b53_global_get_4095_enable,
+               .max = 1,
+       },
+};
+
+static struct switch_attr b53_port_ops[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .get = b53_port_get_mib,
+       },
+};
+
+static struct switch_attr b53_no_ops[] = {
+};
+
+static const struct switch_dev_ops b53_switch_ops_25 = {
+       .attr_global = {
+               .attr = b53_global_ops_25,
+               .n_attr = ARRAY_SIZE(b53_global_ops_25),
+       },
+       .attr_port = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+static const struct switch_dev_ops b53_switch_ops_65 = {
+       .attr_global = {
+               .attr = b53_global_ops_65,
+               .n_attr = ARRAY_SIZE(b53_global_ops_65),
+       },
+       .attr_port = {
+               .attr = b53_port_ops,
+               .n_attr = ARRAY_SIZE(b53_port_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+static const struct switch_dev_ops b53_switch_ops = {
+       .attr_global = {
+               .attr = b53_global_ops,
+               .n_attr = ARRAY_SIZE(b53_global_ops),
+       },
+       .attr_port = {
+               .attr = b53_port_ops,
+               .n_attr = ARRAY_SIZE(b53_port_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+struct b53_chip_data {
+       u32 chip_id;
+       const char *dev_name;
+       const char *alias;
+       u16 vlans;
+       u16 enabled_ports;
+       u8 cpu_port;
+       u8 vta_regs[3];
+       u8 duplex_reg;
+       u8 jumbo_pm_reg;
+       u8 jumbo_size_reg;
+       const struct switch_dev_ops *sw_ops;
+};
+
+#define B53_VTA_REGS   \
+       { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
+#define B53_VTA_REGS_9798 \
+       { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
+#define B53_VTA_REGS_63XX \
+       { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
+
+static const struct b53_chip_data b53_switch_chips[] = {
+       {
+               .chip_id = BCM5325_DEVICE_ID,
+               .dev_name = "BCM5325",
+               .alias = "bcm5325",
+               .vlans = 16,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25,
+               .duplex_reg = B53_DUPLEX_STAT_FE,
+               .sw_ops = &b53_switch_ops_25,
+       },
+       {
+               .chip_id = BCM5365_DEVICE_ID,
+               .dev_name = "BCM5365",
+               .alias = "bcm5365",
+               .vlans = 256,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25,
+               .duplex_reg = B53_DUPLEX_STAT_FE,
+               .sw_ops = &b53_switch_ops_65,
+       },
+       {
+               .chip_id = BCM5395_DEVICE_ID,
+               .dev_name = "BCM5395",
+               .alias = "bcm5395",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM5397_DEVICE_ID,
+               .dev_name = "BCM5397",
+               .alias = "bcm5397",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_9798,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM5398_DEVICE_ID,
+               .dev_name = "BCM5398",
+               .alias = "bcm5398",
+               .vlans = 4096,
+               .enabled_ports = 0x7f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_9798,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53115_DEVICE_ID,
+               .dev_name = "BCM53115",
+               .alias = "bcm53115",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .vta_regs = B53_VTA_REGS,
+               .cpu_port = B53_CPU_PORT,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53125_DEVICE_ID,
+               .dev_name = "BCM53125",
+               .alias = "bcm53125",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53128_DEVICE_ID,
+               .dev_name = "BCM53128",
+               .alias = "bcm53128",
+               .vlans = 4096,
+               .enabled_ports = 0x1ff,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM63XX_DEVICE_ID,
+               .dev_name = "BCM63xx",
+               .alias = "bcm63xx",
+               .vlans = 4096,
+               .enabled_ports = 0, /* pdata must provide them */
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_63XX,
+               .duplex_reg = B53_DUPLEX_STAT_63XX,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53010_DEVICE_ID,
+               .dev_name = "BCM53010",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53011_DEVICE_ID,
+               .dev_name = "BCM53011",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53012_DEVICE_ID,
+               .dev_name = "BCM53012",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53018_DEVICE_ID,
+               .dev_name = "BCM53018",
+               .alias = "bcm53018",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53019_DEVICE_ID,
+               .dev_name = "BCM53019",
+               .alias = "bcm53019",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+};
+
+static int b53_switch_init_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       const char *alias;
+       u32 port_num;
+       u16 ports = 0;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+       if (!dn)
+               return -EINVAL;
+
+       for_each_available_child_of_node(dn, pn) {
+               const char *label;
+               int len;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               ports |= BIT(port_num);
+
+               label = of_get_property(pn, "label", &len);
+               if (label && !strcmp(label, "cpu"))
+                       dev->sw_dev.cpu_port = port_num;
+       }
+
+       dev->enabled_ports = ports;
+
+       if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias",
+                                                &alias))
+               dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL);
+
+       return 0;
+}
+
+static int b53_switch_init(struct b53_device *dev)
+{
+       struct switch_dev *sw_dev = &dev->sw_dev;
+       unsigned i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
+               const struct b53_chip_data *chip = &b53_switch_chips[i];
+
+               if (chip->chip_id == dev->chip_id) {
+                       sw_dev->name = chip->dev_name;
+                       if (!sw_dev->alias)
+                               sw_dev->alias = chip->alias;
+                       if (!dev->enabled_ports)
+                               dev->enabled_ports = chip->enabled_ports;
+                       dev->duplex_reg = chip->duplex_reg;
+                       dev->vta_regs[0] = chip->vta_regs[0];
+                       dev->vta_regs[1] = chip->vta_regs[1];
+                       dev->vta_regs[2] = chip->vta_regs[2];
+                       dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+                       sw_dev->ops = chip->sw_ops;
+                       sw_dev->cpu_port = chip->cpu_port;
+                       sw_dev->vlans = chip->vlans;
+                       break;
+               }
+       }
+
+       if (!sw_dev->name)
+               return -EINVAL;
+
+       /* check which BCM5325x version we have */
+       if (is5325(dev)) {
+               u8 vc4;
+
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+
+               /* check reserved bits */
+               switch (vc4 & 3) {
+               case 1:
+                       /* BCM5325E */
+                       break;
+               case 3:
+                       /* BCM5325F - do not use port 4 */
+                       dev->enabled_ports &= ~BIT(4);
+                       break;
+               default:
+/* On the BCM47XX SoCs this is the supported internal switch.*/
+#ifndef CONFIG_BCM47XX
+                       /* BCM5325M */
+                       return -EINVAL;
+#else
+                       break;
+#endif
+               }
+       } else if (dev->chip_id == BCM53115_DEVICE_ID) {
+               u64 strap_value;
+
+               b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
+               /* use second IMP port if GMII is enabled */
+               if (strap_value & SV_GMII_CTRL_115)
+                       sw_dev->cpu_port = 5;
+       }
+
+       if (dev_of_node(dev->dev)) {
+               ret = b53_switch_init_of(dev);
+               if (ret)
+                       return ret;
+       }
+
+       dev->enabled_ports |= BIT(sw_dev->cpu_port);
+       sw_dev->ports = fls(dev->enabled_ports);
+
+       dev->ports = devm_kzalloc(dev->dev,
+                                 sizeof(struct b53_port) * sw_dev->ports,
+                                 GFP_KERNEL);
+       if (!dev->ports)
+               return -ENOMEM;
+
+       dev->vlans = devm_kzalloc(dev->dev,
+                                 sizeof(struct b53_vlan) * sw_dev->vlans,
+                                 GFP_KERNEL);
+       if (!dev->vlans)
+               return -ENOMEM;
+
+       dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL);
+       if (!dev->buf)
+               return -ENOMEM;
+
+       dev->reset_gpio = b53_switch_get_reset_gpio(dev);
+       if (dev->reset_gpio >= 0) {
+               ret = devm_gpio_request_one(dev->dev, dev->reset_gpio,
+                                           GPIOF_OUT_INIT_HIGH, "robo_reset");
+               if (ret)
+                       return ret;
+       }
+
+       return b53_switch_reset(dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+                                   void *priv)
+{
+       struct b53_device *dev;
+
+       dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       dev->dev = base;
+       dev->ops = ops;
+       dev->priv = priv;
+       mutex_init(&dev->reg_mutex);
+
+       return dev;
+}
+EXPORT_SYMBOL(b53_switch_alloc);
+
+int b53_switch_detect(struct b53_device *dev)
+{
+       u32 id32;
+       u16 tmp;
+       u8 id8;
+       int ret;
+
+       ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
+       if (ret)
+               return ret;
+
+       switch (id8) {
+       case 0:
+               /*
+                * BCM5325 and BCM5365 do not have this register so reads
+                * return 0. But the read operation did succeed, so assume
+                * this is one of them.
+                *
+                * Next check if we can write to the 5325's VTA register; for
+                * 5365 it is read only.
+                */
+
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
+               b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
+
+               if (tmp == 0xf)
+                       dev->chip_id = BCM5325_DEVICE_ID;
+               else
+                       dev->chip_id = BCM5365_DEVICE_ID;
+               break;
+       case BCM5395_DEVICE_ID:
+       case BCM5397_DEVICE_ID:
+       case BCM5398_DEVICE_ID:
+               dev->chip_id = id8;
+               break;
+       default:
+               ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
+               if (ret)
+                       return ret;
+
+               switch (id32) {
+               case BCM53115_DEVICE_ID:
+               case BCM53125_DEVICE_ID:
+               case BCM53128_DEVICE_ID:
+               case BCM53010_DEVICE_ID:
+               case BCM53011_DEVICE_ID:
+               case BCM53012_DEVICE_ID:
+               case BCM53018_DEVICE_ID:
+               case BCM53019_DEVICE_ID:
+                       dev->chip_id = id32;
+                       break;
+               default:
+                       pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
+                              id8, id32);
+                       return -ENODEV;
+               }
+       }
+
+       if (dev->chip_id == BCM5325_DEVICE_ID)
+               return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25,
+                                &dev->core_rev);
+       else
+               return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
+                                &dev->core_rev);
+}
+EXPORT_SYMBOL(b53_switch_detect);
+
+int b53_switch_register(struct b53_device *dev)
+{
+       int ret;
+
+       if (dev->pdata) {
+               dev->chip_id = dev->pdata->chip_id;
+               dev->enabled_ports = dev->pdata->enabled_ports;
+               dev->sw_dev.alias = dev->pdata->alias;
+       }
+
+       if (!dev->chip_id && b53_switch_detect(dev))
+               return -EINVAL;
+
+       ret = b53_switch_init(dev);
+       if (ret)
+               return ret;
+
+       pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev);
+
+       return register_switch(&dev->sw_dev, NULL);
+}
+EXPORT_SYMBOL(b53_switch_register);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 switch library");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_mdio.c
new file mode 100644 (file)
index 0000000..75bb4d9
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * B53 register access through MII registers
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+
+#include "b53_priv.h"
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+/* MII registers */
+#define REG_MII_PAGE    0x10    /* MII Page register */
+#define REG_MII_ADDR    0x11    /* MII Address register */
+#define REG_MII_DATA0   0x18    /* MII Data register 0 */
+#define REG_MII_DATA1   0x19    /* MII Data register 1 */
+#define REG_MII_DATA2   0x1a    /* MII Data register 2 */
+#define REG_MII_DATA3   0x1b    /* MII Data register 3 */
+
+#define REG_MII_PAGE_ENABLE     BIT(0)
+#define REG_MII_ADDR_WRITE      BIT(0)
+#define REG_MII_ADDR_READ       BIT(1)
+
+static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
+{
+       int i;
+       u16 v;
+       int ret;
+       struct mii_bus *bus = dev->priv;
+
+       if (dev->current_page != page) {
+               /* set page number */
+               v = (page << 8) | REG_MII_PAGE_ENABLE;
+               ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
+               if (ret)
+                       return ret;
+               dev->current_page = page;
+       }
+
+       /* set register address */
+       v = (reg << 8) | op;
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
+       if (ret)
+               return ret;
+
+       /* check if operation completed */
+       for (i = 0; i < 5; ++i) {
+               v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
+               if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
+                       break;
+               usleep_range(10, 100);
+       }
+
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
+
+       return 0;
+}
+
+static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+
+       return 0;
+}
+
+static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+       *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
+
+       return 0;
+}
+
+static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       u64 temp = 0;
+       int i;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       for (i = 2; i >= 0; i--) {
+               temp <<= 16;
+               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+       }
+
+       *val = temp;
+
+       return 0;
+}
+
+static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       u64 temp = 0;
+       int i;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       for (i = 3; i >= 0; i--) {
+               temp <<= 16;
+               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+       }
+
+       *val = temp;
+
+       return 0;
+}
+
+static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+       if (ret)
+               return ret;
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+       if (ret)
+               return ret;
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned int i;
+       u32 temp = value;
+
+       for (i = 0; i < 2; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned i;
+       u64 temp = value;
+
+       for (i = 0; i < 3; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned i;
+       u64 temp = value;
+
+       for (i = 0; i < 4; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg,
+                              u16 *value)
+{
+       struct mii_bus *bus = dev->priv;
+
+       *value = mdiobus_read(bus, addr, reg);
+
+       return 0;
+}
+
+static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg,
+                               u16 value)
+{
+       struct mii_bus *bus = dev->priv;
+
+       return mdiobus_write(bus, addr, reg, value);
+}
+
+static struct b53_io_ops b53_mdio_ops = {
+       .read8 = b53_mdio_read8,
+       .read16 = b53_mdio_read16,
+       .read32 = b53_mdio_read32,
+       .read48 = b53_mdio_read48,
+       .read64 = b53_mdio_read64,
+       .write8 = b53_mdio_write8,
+       .write16 = b53_mdio_write16,
+       .write32 = b53_mdio_write32,
+       .write48 = b53_mdio_write48,
+       .write64 = b53_mdio_write64,
+       .phy_read16 = b53_mdio_phy_read16,
+       .phy_write16 = b53_mdio_phy_write16,
+};
+
+static int b53_phy_probe(struct phy_device *phydev)
+{
+       struct b53_device dev;
+       int ret;
+
+       /* allow the generic phy driver to take over */
+       if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
+               return -ENODEV;
+
+       dev.current_page = 0xff;
+       dev.priv = phydev->mdio.bus;
+       dev.ops = &b53_mdio_ops;
+       dev.pdata = NULL;
+       mutex_init(&dev.reg_mutex);
+
+       ret = b53_switch_detect(&dev);
+       if (ret)
+               return ret;
+
+       if (is5325(&dev) || is5365(&dev))
+               phydev->supported = SUPPORTED_100baseT_Full;
+       else
+               phydev->supported = SUPPORTED_1000baseT_Full;
+
+       phydev->advertising = phydev->supported;
+
+       return 0;
+}
+
+static int b53_phy_config_init(struct phy_device *phydev)
+{
+       struct b53_device *dev;
+       int ret;
+
+       dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
+       if (!dev)
+               return -ENOMEM;
+
+       /* we don't use page 0xff, so force a page set */
+       dev->current_page = 0xff;
+       /* force the ethX as alias */
+       dev->sw_dev.alias = phydev->attached_dev->name;
+
+       ret = b53_switch_register(dev);
+       if (ret) {
+               dev_err(dev->dev, "failed to register switch: %i\n", ret);
+               return ret;
+       }
+
+       phydev->priv = dev;
+
+       return 0;
+}
+
+static void b53_phy_remove(struct phy_device *phydev)
+{
+       struct b53_device *priv = phydev->priv;
+
+       if (!priv)
+               return;
+
+       b53_switch_remove(priv);
+
+       phydev->priv = NULL;
+}
+
+static int b53_phy_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int b53_phy_read_status(struct phy_device *phydev)
+{
+       struct b53_device *priv = phydev->priv;
+
+       if (is5325(priv) || is5365(priv))
+               phydev->speed = 100;
+       else
+               phydev->speed = 1000;
+
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+       phydev->state = PHY_RUNNING;
+
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+/* BCM5325, BCM539x */
+static struct phy_driver b53_phy_driver_id1 = {
+       .phy_id         = 0x0143bc00,
+       .name           = "Broadcom B53 (1)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+/* BCM53125, BCM53128 */
+static struct phy_driver b53_phy_driver_id2 = {
+       .phy_id         = 0x03625c00,
+       .name           = "Broadcom B53 (2)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+/* BCM5365 */
+static struct phy_driver b53_phy_driver_id3 = {
+       .phy_id         = 0x00406000,
+       .name           = "Broadcom B53 (3)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+int __init b53_phy_driver_register(void)
+{
+       int ret;
+
+       ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE);
+       if (ret)
+               return ret;
+
+       ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE);
+       if (ret)
+               goto err1;
+
+       ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE);
+       if (!ret)
+               return 0;
+
+       phy_driver_unregister(&b53_phy_driver_id2);
+err1:
+       phy_driver_unregister(&b53_phy_driver_id1);
+       return ret;
+}
+
+void __exit b53_phy_driver_unregister(void)
+{
+       phy_driver_unregister(&b53_phy_driver_id3);
+       phy_driver_unregister(&b53_phy_driver_id2);
+       phy_driver_unregister(&b53_phy_driver_id1);
+}
+
+module_init(b53_phy_driver_register);
+module_exit(b53_phy_driver_unregister);
+
+MODULE_DESCRIPTION("B53 MDIO access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_mmap.c
new file mode 100644 (file)
index 0000000..ab1895e
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * B53 register access through memory mapped registers
+ *
+ * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       *val = readb(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               *val = readw_be(regs + (page << 8) + reg);
+       else
+               *val = readw(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               *val = readl_be(regs + (page << 8) + reg);
+       else
+               *val = readl(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (reg % 4) {
+               u16 lo;
+               u32 hi;
+
+               b53_mmap_read16(dev, page, reg, &lo);
+               b53_mmap_read32(dev, page, reg + 2, &hi);
+
+               *val = ((u64)hi << 16) | lo;
+       } else {
+               u32 lo;
+               u16 hi;
+
+               b53_mmap_read32(dev, page, reg, &lo);
+               b53_mmap_read16(dev, page, reg + 4, &hi);
+
+               *val = ((u64)hi << 32) | lo;
+       }
+
+       return 0;
+}
+
+static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u32 hi, lo;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       b53_mmap_read32(dev, page, reg, &lo);
+       b53_mmap_read32(dev, page, reg + 4, &hi);
+
+       *val = ((u64)hi << 32) | lo;
+
+       return 0;
+}
+
+static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       writeb(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               writew_be(value, regs + (page << 8) + reg);
+       else
+               writew(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               writel_be(value, regs + (page << 8) + reg);
+       else
+               writel(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (reg % 4) {
+               u32 hi = (u32)(value >> 16);
+               u16 lo = (u16)value;
+
+               b53_mmap_write16(dev, page, reg, lo);
+               b53_mmap_write32(dev, page, reg + 2, hi);
+       } else {
+               u16 hi = (u16)(value >> 32);
+               u32 lo = (u32)value;
+
+               b53_mmap_write32(dev, page, reg, lo);
+               b53_mmap_write16(dev, page, reg + 4, hi);
+       }
+
+       return 0;
+}
+
+static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       u32 hi, lo;
+
+       hi = (u32)(value >> 32);
+       lo = (u32)value;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       b53_mmap_write32(dev, page, reg, lo);
+       b53_mmap_write32(dev, page, reg + 4, hi);
+
+       return 0;
+}
+
+static struct b53_io_ops b53_mmap_ops = {
+       .read8 = b53_mmap_read8,
+       .read16 = b53_mmap_read16,
+       .read32 = b53_mmap_read32,
+       .read48 = b53_mmap_read48,
+       .read64 = b53_mmap_read64,
+       .write8 = b53_mmap_write8,
+       .write16 = b53_mmap_write16,
+       .write32 = b53_mmap_write32,
+       .write48 = b53_mmap_write48,
+       .write64 = b53_mmap_write64,
+};
+
+static int b53_mmap_probe(struct platform_device *pdev)
+{
+       struct b53_platform_data *pdata = pdev->dev.platform_data;
+       struct b53_device *dev;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
+       if (!dev)
+               return -ENOMEM;
+
+       if (pdata)
+               dev->pdata = pdata;
+
+       platform_set_drvdata(pdev, dev);
+
+       return b53_switch_register(dev);
+}
+
+static int b53_mmap_remove(struct platform_device *pdev)
+{
+       struct b53_device *dev = platform_get_drvdata(pdev);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static struct platform_driver b53_mmap_driver = {
+       .probe = b53_mmap_probe,
+       .remove = b53_mmap_remove,
+       .driver = {
+               .name = "b53-switch",
+       },
+};
+
+module_platform_driver(b53_mmap_driver);
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 MMAP access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_phy_fixup.c b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_phy_fixup.c
new file mode 100644 (file)
index 0000000..e2f8a39
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * B53 PHY Fixup call
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/phy.h>
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+#define B53_BRCM_OUI_1 0x0143bc00
+#define B53_BRCM_OUI_2 0x03625c00
+#define B53_BRCM_OUI_3 0x00406000
+
+static int b53_phy_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u32 phy_id;
+
+       if (dev->mdio.addr != B53_PSEUDO_PHY)
+               return 0;
+
+       /* read the first port's id */
+       phy_id = mdiobus_read(bus, 0, 2) << 16;
+       phy_id |= mdiobus_read(bus, 0, 3);
+
+       if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
+           (phy_id & 0xfffffc00) == B53_BRCM_OUI_2 ||
+           (phy_id & 0xfffffc00) == B53_BRCM_OUI_3) {
+               dev->phy_id = phy_id;
+       }
+
+       return 0;
+}
+
+int __init b53_phy_fixup_register(void)
+{
+       return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
+}
+
+subsys_initcall(b53_phy_fixup_register);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_priv.h
new file mode 100644 (file)
index 0000000..a9296c9
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * B53 common definitions
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_PRIV_H
+#define __B53_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/switch.h>
+
+struct b53_device;
+
+struct b53_io_ops {
+       int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
+       int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
+       int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
+       int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+       int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+       int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
+       int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
+       int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
+       int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+       int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+       int (*phy_read16)(struct b53_device *dev, int addr, u8 reg, u16 *value);
+       int (*phy_write16)(struct b53_device *dev, int addr, u8 reg, u16 value);
+};
+
+enum {
+       BCM5325_DEVICE_ID = 0x25,
+       BCM5365_DEVICE_ID = 0x65,
+       BCM5395_DEVICE_ID = 0x95,
+       BCM5397_DEVICE_ID = 0x97,
+       BCM5398_DEVICE_ID = 0x98,
+       BCM53115_DEVICE_ID = 0x53115,
+       BCM53125_DEVICE_ID = 0x53125,
+       BCM53128_DEVICE_ID = 0x53128,
+       BCM63XX_DEVICE_ID = 0x6300,
+       BCM53010_DEVICE_ID = 0x53010,
+       BCM53011_DEVICE_ID = 0x53011,
+       BCM53012_DEVICE_ID = 0x53012,
+       BCM53018_DEVICE_ID = 0x53018,
+       BCM53019_DEVICE_ID = 0x53019,
+};
+
+#define B53_N_PORTS    9
+#define B53_N_PORTS_25 6
+
+struct b53_vlan {
+       unsigned int    members:B53_N_PORTS;
+       unsigned int    untag:B53_N_PORTS;
+};
+
+struct b53_port {
+       unsigned int    pvid:12;
+};
+
+struct b53_device {
+       struct switch_dev sw_dev;
+       struct b53_platform_data *pdata;
+
+       struct mutex reg_mutex;
+       const struct b53_io_ops *ops;
+
+       /* chip specific data */
+       u32 chip_id;
+       u8 core_rev;
+       u8 vta_regs[3];
+       u8 duplex_reg;
+       u8 jumbo_pm_reg;
+       u8 jumbo_size_reg;
+       int reset_gpio;
+
+       /* used ports mask */
+       u16 enabled_ports;
+
+       /* connect specific data */
+       u8 current_page;
+       struct device *dev;
+       void *priv;
+
+       /* run time configuration */
+       unsigned enable_vlan:1;
+       unsigned enable_jumbo:1;
+       unsigned allow_vid_4095:1;
+
+       struct b53_port *ports;
+       struct b53_vlan *vlans;
+
+       char *buf;
+};
+
+#define b53_for_each_port(dev, i) \
+       for (i = 0; i < B53_N_PORTS; i++) \
+               if (dev->enabled_ports & BIT(i))
+
+
+
+static inline int is5325(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5325_DEVICE_ID;
+}
+
+static inline int is5365(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+       return dev->chip_id == BCM5365_DEVICE_ID;
+#else
+       return 0;
+#endif
+}
+
+static inline int is5397_98(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5397_DEVICE_ID ||
+               dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is539x(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5395_DEVICE_ID ||
+               dev->chip_id == BCM5397_DEVICE_ID ||
+               dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is531x5(struct b53_device *dev)
+{
+       return dev->chip_id == BCM53115_DEVICE_ID ||
+               dev->chip_id == BCM53125_DEVICE_ID ||
+               dev->chip_id == BCM53128_DEVICE_ID;
+}
+
+static inline int is63xx(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM63XX
+       return dev->chip_id == BCM63XX_DEVICE_ID;
+#else
+       return 0;
+#endif
+}
+
+static inline int is5301x(struct b53_device *dev)
+{
+       return dev->chip_id == BCM53010_DEVICE_ID ||
+               dev->chip_id == BCM53011_DEVICE_ID ||
+               dev->chip_id == BCM53012_DEVICE_ID ||
+               dev->chip_id == BCM53018_DEVICE_ID ||
+               dev->chip_id == BCM53019_DEVICE_ID;
+}
+
+#define B53_CPU_PORT_25        5
+#define B53_CPU_PORT   8
+
+static inline int is_cpu_port(struct b53_device *dev, int port)
+{
+       return dev->sw_dev.cpu_port == port;
+}
+
+static inline int is_imp_port(struct b53_device *dev, int port)
+{
+       if (is5325(dev) || is5365(dev))
+               return port == B53_CPU_PORT_25;
+       else
+               return port == B53_CPU_PORT;
+}
+
+static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
+{
+       return container_of(sw, struct b53_device, sw_dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+                                   void *priv);
+
+int b53_switch_detect(struct b53_device *dev);
+
+int b53_switch_register(struct b53_device *dev);
+
+static inline void b53_switch_remove(struct b53_device *dev)
+{
+       unregister_switch(&dev->sw_dev);
+}
+
+static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read8(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read16(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read32(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read48(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read64(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write8(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
+                             u16 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write16(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
+                             u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write32(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
+                             u64 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write48(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
+                              u64 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write64(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+#ifdef CONFIG_BCM47XX
+#include <bcm47xx_board.h>
+#endif
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+#include <linux/bcm47xx_nvram.h>
+#endif
+static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+       enum bcm47xx_board board = bcm47xx_board_get();
+
+       switch (board) {
+       case BCM47XX_BOARD_LINKSYS_WRT300NV11:
+       case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+               return 8;
+       default:
+               break;
+       }
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+       return bcm47xx_nvram_gpio_pin("robo_reset");
+#else
+       return -ENOENT;
+#endif
+}
+
+#endif
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_regs.h
new file mode 100644 (file)
index 0000000..f0bf674
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * B53 register definitions
+ *
+ * Copyright (C) 2004 Broadcom Corporation
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_REGS_H
+#define __B53_REGS_H
+
+/* Management Port (SMP) Page offsets */
+#define B53_CTRL_PAGE                  0x00 /* Control */
+#define B53_STAT_PAGE                  0x01 /* Status */
+#define B53_MGMT_PAGE                  0x02 /* Management Mode */
+#define B53_MIB_AC_PAGE                        0x03 /* MIB Autocast */
+#define B53_ARLCTRL_PAGE               0x04 /* ARL Control */
+#define B53_ARLIO_PAGE                 0x05 /* ARL Access */
+#define B53_FRAMEBUF_PAGE              0x06 /* Management frame access */
+#define B53_MEM_ACCESS_PAGE            0x08 /* Memory access */
+
+/* PHY Registers */
+#define B53_PORT_MII_PAGE(i)           (0x10 + (i)) /* Port i MII Registers */
+#define B53_IM_PORT_PAGE               0x18 /* Inverse MII Port (to EMAC) */
+#define B53_ALL_PORT_PAGE              0x19 /* All ports MII (broadcast) */
+
+/* MIB registers */
+#define B53_MIB_PAGE(i)                        (0x20 + (i))
+
+/* Quality of Service (QoS) Registers */
+#define B53_QOS_PAGE                   0x30
+
+/* Port VLAN Page */
+#define B53_PVLAN_PAGE                 0x31
+
+/* VLAN Registers */
+#define B53_VLAN_PAGE                  0x34
+
+/* Jumbo Frame Registers */
+#define B53_JUMBO_PAGE                 0x40
+
+/* CFP Configuration Registers Page */
+#define B53_CFP_PAGE                   0xa1
+
+/*************************************************************************
+ * Control Page registers
+ *************************************************************************/
+
+/* Port Control Register (8 bit) */
+#define B53_PORT_CTRL(i)               (0x00 + (i))
+#define   PORT_CTRL_RX_DISABLE         BIT(0)
+#define   PORT_CTRL_TX_DISABLE         BIT(1)
+#define   PORT_CTRL_RX_BCST_EN         BIT(2) /* Broadcast RX (P8 only) */
+#define   PORT_CTRL_RX_MCST_EN         BIT(3) /* Multicast RX (P8 only) */
+#define   PORT_CTRL_RX_UCST_EN         BIT(4) /* Unicast RX (P8 only) */
+#define          PORT_CTRL_STP_STATE_S         5
+#define   PORT_CTRL_STP_STATE_MASK     (0x7 << PORT_CTRL_STP_STATE_S)
+
+/* SMP Control Register (8 bit) */
+#define B53_SMP_CTRL                   0x0a
+
+/* Switch Mode Control Register (8 bit) */
+#define B53_SWITCH_MODE                        0x0b
+#define   SM_SW_FWD_MODE               BIT(0)  /* 1 = Managed Mode */
+#define   SM_SW_FWD_EN                 BIT(1)  /* Forwarding Enable */
+
+/* IMP Port state override register (8 bit) */
+#define B53_PORT_OVERRIDE_CTRL         0x0e
+#define   PORT_OVERRIDE_LINK           BIT(0)
+#define   PORT_OVERRIDE_FULL_DUPLEX    BIT(1) /* 0 = Half Duplex */
+#define   PORT_OVERRIDE_SPEED_S                2
+#define   PORT_OVERRIDE_SPEED_10M      (0 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_SPEED_100M     (1 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_SPEED_1000M    (2 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_RV_MII_25      BIT(4) /* BCM5325 only */
+#define   PORT_OVERRIDE_RX_FLOW                BIT(4)
+#define   PORT_OVERRIDE_TX_FLOW                BIT(5)
+#define   PORT_OVERRIDE_SPEED_2000M    BIT(6) /* BCM5301X only, requires setting 1000M */
+#define   PORT_OVERRIDE_EN             BIT(7) /* Use the register contents */
+
+/* Power-down mode control */
+#define B53_PD_MODE_CTRL_25            0x0f
+
+/* IP Multicast control (8 bit) */
+#define B53_IP_MULTICAST_CTRL          0x21
+#define  B53_IPMC_FWD_EN               BIT(1)
+#define  B53_UC_FWD_EN                 BIT(6)
+#define  B53_MC_FWD_EN                 BIT(7)
+
+/* (16 bit) */
+#define B53_UC_FLOOD_MASK              0x32
+#define B53_MC_FLOOD_MASK              0x34
+#define B53_IPMC_FLOOD_MASK            0x36
+
+/*
+ * Override Ports 0-7 State on devices with xMII interfaces (8 bit)
+ *
+ * For port 8 still use B53_PORT_OVERRIDE_CTRL
+ * Please note that not all ports are available on every hardware, e.g. BCM5301X
+ * don't include overriding port 6, BCM63xx also have some limitations.
+ */
+#define B53_GMII_PORT_OVERRIDE_CTRL(i) (0x58 + (i))
+#define   GMII_PO_LINK                 BIT(0)
+#define   GMII_PO_FULL_DUPLEX          BIT(1) /* 0 = Half Duplex */
+#define   GMII_PO_SPEED_S              2
+#define   GMII_PO_SPEED_10M            (0 << GMII_PO_SPEED_S)
+#define   GMII_PO_SPEED_100M           (1 << GMII_PO_SPEED_S)
+#define   GMII_PO_SPEED_1000M          (2 << GMII_PO_SPEED_S)
+#define   GMII_PO_RX_FLOW              BIT(4)
+#define   GMII_PO_TX_FLOW              BIT(5)
+#define   GMII_PO_EN                   BIT(6) /* Use the register contents */
+#define   GMII_PO_SPEED_2000M          BIT(7) /* BCM5301X only, requires setting 1000M */
+
+/* Software reset register (8 bit) */
+#define B53_SOFTRESET                  0x79
+
+/* Fast Aging Control register (8 bit) */
+#define B53_FAST_AGE_CTRL              0x88
+#define   FAST_AGE_STATIC              BIT(0)
+#define   FAST_AGE_DYNAMIC             BIT(1)
+#define   FAST_AGE_PORT                        BIT(2)
+#define   FAST_AGE_VLAN                        BIT(3)
+#define   FAST_AGE_STP                 BIT(4)
+#define   FAST_AGE_MC                  BIT(5)
+#define   FAST_AGE_DONE                        BIT(7)
+
+/*************************************************************************
+ * Status Page registers
+ *************************************************************************/
+
+/* Link Status Summary Register (16bit) */
+#define B53_LINK_STAT                  0x00
+
+/* Link Status Change Register (16 bit) */
+#define B53_LINK_STAT_CHANGE           0x02
+
+/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
+#define B53_SPEED_STAT                 0x04
+#define  SPEED_PORT_FE(reg, port)      (((reg) >> (port)) & 1)
+#define  SPEED_PORT_GE(reg, port)      (((reg) >> 2 * (port)) & 3)
+#define  SPEED_STAT_10M                        0
+#define  SPEED_STAT_100M               1
+#define  SPEED_STAT_1000M              2
+
+/* Duplex Status Summary (16 bit) */
+#define B53_DUPLEX_STAT_FE             0x06
+#define B53_DUPLEX_STAT_GE             0x08
+#define B53_DUPLEX_STAT_63XX           0x0c
+
+/* Revision ID register for BCM5325 */
+#define B53_REV_ID_25                  0x50
+
+/* Strap Value (48 bit) */
+#define B53_STRAP_VALUE                        0x70
+#define   SV_GMII_CTRL_115             BIT(27)
+
+/*************************************************************************
+ * Management Mode Page Registers
+ *************************************************************************/
+
+/* Global Management Config Register (8 bit) */
+#define B53_GLOBAL_CONFIG              0x00
+#define   GC_RESET_MIB                 0x01
+#define   GC_RX_BPDU_EN                        0x02
+#define   GC_MIB_AC_HDR_EN             0x10
+#define   GC_MIB_AC_EN                 0x20
+#define   GC_FRM_MGMT_PORT_M           0xC0
+#define   GC_FRM_MGMT_PORT_04          0x00
+#define   GC_FRM_MGMT_PORT_MII         0x80
+
+/* Broadcom Header control register (8 bit) */
+#define B53_BRCM_HDR                   0x03
+#define   BRCM_HDR_P8_EN               BIT(0) /* Enable tagging on port 8 */
+#define   BRCM_HDR_P5_EN               BIT(1) /* Enable tagging on port 5 */
+
+/* Device ID register (8 or 32 bit) */
+#define B53_DEVICE_ID                  0x30
+
+/* Revision ID register (8 bit) */
+#define B53_REV_ID                     0x40
+
+/*************************************************************************
+ * ARL Access Page Registers
+ *************************************************************************/
+
+/* VLAN Table Access Register (8 bit) */
+#define B53_VT_ACCESS                  0x80
+#define B53_VT_ACCESS_9798             0x60 /* for BCM5397/BCM5398 */
+#define B53_VT_ACCESS_63XX             0x60 /* for BCM6328/62/68 */
+#define   VTA_CMD_WRITE                        0
+#define   VTA_CMD_READ                 1
+#define   VTA_CMD_CLEAR                        2
+#define   VTA_START_CMD                        BIT(7)
+
+/* VLAN Table Index Register (16 bit) */
+#define B53_VT_INDEX                   0x81
+#define B53_VT_INDEX_9798              0x61
+#define B53_VT_INDEX_63XX              0x62
+
+/* VLAN Table Entry Register (32 bit) */
+#define B53_VT_ENTRY                   0x83
+#define B53_VT_ENTRY_9798              0x63
+#define B53_VT_ENTRY_63XX              0x64
+#define   VTE_MEMBERS                  0x1ff
+#define   VTE_UNTAG_S                  9
+#define   VTE_UNTAG                    (0x1ff << 9)
+
+/*************************************************************************
+ * Port VLAN Registers
+ *************************************************************************/
+
+/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
+#define B53_PVLAN_PORT_MASK(i)         ((i) * 2)
+
+/*************************************************************************
+ * 802.1Q Page Registers
+ *************************************************************************/
+
+/* Global QoS Control (8 bit) */
+#define B53_QOS_GLOBAL_CTL             0x00
+
+/* Enable 802.1Q for individual Ports (16 bit) */
+#define B53_802_1P_EN                  0x04
+
+/*************************************************************************
+ * VLAN Page Registers
+ *************************************************************************/
+
+/* VLAN Control 0 (8 bit) */
+#define B53_VLAN_CTRL0                 0x00
+#define   VC0_8021PF_CTRL_MASK         0x3
+#define   VC0_8021PF_CTRL_NONE         0x0
+#define   VC0_8021PF_CTRL_CHANGE_PRI   0x1
+#define   VC0_8021PF_CTRL_CHANGE_VID   0x2
+#define   VC0_8021PF_CTRL_CHANGE_BOTH  0x3
+#define   VC0_8021QF_CTRL_MASK         0xc
+#define   VC0_8021QF_CTRL_CHANGE_PRI   0x1
+#define   VC0_8021QF_CTRL_CHANGE_VID   0x2
+#define   VC0_8021QF_CTRL_CHANGE_BOTH  0x3
+#define   VC0_RESERVED_1               BIT(1)
+#define   VC0_DROP_VID_MISS            BIT(4)
+#define   VC0_VID_HASH_VID             BIT(5)
+#define   VC0_VID_CHK_EN               BIT(6)  /* Use VID,DA or VID,SA */
+#define   VC0_VLAN_EN                  BIT(7)  /* 802.1Q VLAN Enabled */
+
+/* VLAN Control 1 (8 bit) */
+#define B53_VLAN_CTRL1                 0x01
+#define   VC1_RX_MCST_TAG_EN           BIT(1)
+#define   VC1_RX_MCST_FWD_EN           BIT(2)
+#define   VC1_RX_MCST_UNTAG_EN         BIT(3)
+
+/* VLAN Control 2 (8 bit) */
+#define B53_VLAN_CTRL2                 0x02
+
+/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
+#define B53_VLAN_CTRL3                 0x03
+#define B53_VLAN_CTRL3_63XX            0x04
+#define   VC3_MAXSIZE_1532             BIT(6) /* 5325 only */
+#define   VC3_HIGH_8BIT_EN             BIT(7) /* 5325 only */
+
+/* VLAN Control 4 (8 bit) */
+#define B53_VLAN_CTRL4                 0x05
+#define B53_VLAN_CTRL4_25              0x04
+#define B53_VLAN_CTRL4_63XX            0x06
+#define   VC4_ING_VID_CHECK_S          6
+#define   VC4_ING_VID_CHECK_MASK       (0x3 << VC4_ING_VID_CHECK_S)
+#define   VC4_ING_VID_VIO_FWD          0 /* forward, but do not learn */
+#define   VC4_ING_VID_VIO_DROP         1 /* drop VID violations */
+#define   VC4_NO_ING_VID_CHK           2 /* do not check */
+#define   VC4_ING_VID_VIO_TO_IMP       3 /* redirect to MII port */
+
+/* VLAN Control 5 (8 bit) */
+#define B53_VLAN_CTRL5                 0x06
+#define B53_VLAN_CTRL5_25              0x05
+#define B53_VLAN_CTRL5_63XX            0x07
+#define   VC5_VID_FFF_EN               BIT(2)
+#define   VC5_DROP_VTABLE_MISS         BIT(3)
+
+/* VLAN Control 6 (8 bit) */
+#define B53_VLAN_CTRL6                 0x07
+#define B53_VLAN_CTRL6_63XX            0x08
+
+/* VLAN Table Access Register (16 bit) */
+#define B53_VLAN_TABLE_ACCESS_25       0x06    /* BCM5325E/5350 */
+#define B53_VLAN_TABLE_ACCESS_65       0x08    /* BCM5365 */
+#define   VTA_VID_LOW_MASK_25          0xf
+#define   VTA_VID_LOW_MASK_65          0xff
+#define   VTA_VID_HIGH_S_25            4
+#define   VTA_VID_HIGH_S_65            8
+#define   VTA_VID_HIGH_MASK_25         (0xff << VTA_VID_HIGH_S_25E)
+#define   VTA_VID_HIGH_MASK_65         (0xf << VTA_VID_HIGH_S_65)
+#define   VTA_RW_STATE                 BIT(12)
+#define   VTA_RW_STATE_RD              0
+#define   VTA_RW_STATE_WR              BIT(12)
+#define   VTA_RW_OP_EN                 BIT(13)
+
+/* VLAN Read/Write Registers for (16/32 bit) */
+#define B53_VLAN_WRITE_25              0x08
+#define B53_VLAN_WRITE_65              0x0a
+#define B53_VLAN_READ                  0x0c
+#define   VA_MEMBER_MASK               0x3f
+#define   VA_UNTAG_S_25                        6
+#define   VA_UNTAG_MASK_25             0x3f
+#define   VA_UNTAG_S_65                        7
+#define   VA_UNTAG_MASK_65             0x1f
+#define   VA_VID_HIGH_S                        12
+#define   VA_VID_HIGH_MASK             (0xffff << VA_VID_HIGH_S)
+#define   VA_VALID_25                  BIT(20)
+#define   VA_VALID_25_R4               BIT(24)
+#define   VA_VALID_65                  BIT(14)
+
+/* VLAN Port Default Tag (16 bit) */
+#define B53_VLAN_PORT_DEF_TAG(i)       (0x10 + 2 * (i))
+
+/*************************************************************************
+ * Jumbo Frame Page Registers
+ *************************************************************************/
+
+/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
+#define B53_JUMBO_PORT_MASK            0x01
+#define B53_JUMBO_PORT_MASK_63XX       0x04
+#define   JPM_10_100_JUMBO_EN          BIT(24) /* GigE always enabled */
+
+/* Good Frame Max Size without 802.1Q TAG (16 bit) */
+#define B53_JUMBO_MAX_SIZE             0x05
+#define B53_JUMBO_MAX_SIZE_63XX                0x08
+#define   JMS_MIN_SIZE                 1518
+#define   JMS_MAX_SIZE                 9724
+
+/*************************************************************************
+ * CFP Configuration Page Registers
+ *************************************************************************/
+
+/* CFP Control Register with ports map (8 bit) */
+#define B53_CFP_CTRL                   0x00
+
+#endif /* !__B53_REGS_H */
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_spi.c
new file mode 100644 (file)
index 0000000..efc8f7e
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * B53 register access through SPI
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+#define B53_SPI_DATA           0xf0
+
+#define B53_SPI_STATUS         0xfe
+#define B53_SPI_CMD_SPIF       BIT(7)
+#define B53_SPI_CMD_RACK       BIT(5)
+
+#define B53_SPI_CMD_READ       0x00
+#define B53_SPI_CMD_WRITE      0x01
+#define B53_SPI_CMD_NORMAL     0x60
+#define B53_SPI_CMD_FAST       0x10
+
+#define B53_SPI_PAGE_SELECT    0xff
+
+static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
+                                    unsigned len)
+{
+       u8 txbuf[2];
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
+       txbuf[1] = reg;
+
+       return spi_write_then_read(spi, txbuf, 2, val, len);
+}
+
+static inline int b53_spi_clear_status(struct spi_device *spi)
+{
+       unsigned int i;
+       u8 rxbuf;
+       int ret;
+
+       for (i = 0; i < 10; i++) {
+               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+               if (ret)
+                       return ret;
+
+               if (!(rxbuf & B53_SPI_CMD_SPIF))
+                       break;
+
+               mdelay(1);
+       }
+
+       if (i == 10)
+               return -EIO;
+
+       return 0;
+}
+
+static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
+{
+       u8 txbuf[3];
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = B53_SPI_PAGE_SELECT;
+       txbuf[2] = page;
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
+{
+       int ret = b53_spi_clear_status(spi);
+
+       if (ret)
+               return ret;
+
+       return b53_spi_set_page(spi, page);
+}
+
+static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
+{
+       u8 rxbuf;
+       int retry_count;
+       int ret;
+
+       ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
+       if (ret)
+               return ret;
+
+       for (retry_count = 0; retry_count < 10; retry_count++) {
+               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+               if (ret)
+                       return ret;
+
+               if (rxbuf & B53_SPI_CMD_RACK)
+                       break;
+
+               mdelay(1);
+       }
+
+       if (retry_count == 10)
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
+                       unsigned len)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       ret = b53_spi_prepare_reg_read(spi, reg);
+       if (ret)
+               return ret;
+
+       return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
+}
+
+static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       return b53_spi_read(dev, page, reg, val, 1);
+}
+
+static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
+
+       if (!ret)
+               *val = le16_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
+
+       if (!ret)
+               *val = le32_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       *val = 0;
+       ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
+       if (!ret)
+               *val = le64_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
+
+       if (!ret)
+               *val = le64_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[3];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       txbuf[2] = value;
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[4];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le16(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[6];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le32(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[10];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le64(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf) - 2);
+}
+
+static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[10];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le64(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static struct b53_io_ops b53_spi_ops = {
+       .read8 = b53_spi_read8,
+       .read16 = b53_spi_read16,
+       .read32 = b53_spi_read32,
+       .read48 = b53_spi_read48,
+       .read64 = b53_spi_read64,
+       .write8 = b53_spi_write8,
+       .write16 = b53_spi_write16,
+       .write32 = b53_spi_write32,
+       .write48 = b53_spi_write48,
+       .write64 = b53_spi_write64,
+};
+
+static int b53_spi_probe(struct spi_device *spi)
+{
+       struct b53_device *dev;
+       int ret;
+
+       dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);
+       if (!dev)
+               return -ENOMEM;
+
+       if (spi->dev.platform_data)
+               dev->pdata = spi->dev.platform_data;
+
+       ret = b53_switch_register(dev);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, dev);
+
+       return 0;
+}
+
+static int b53_spi_remove(struct spi_device *spi)
+{
+       struct b53_device *dev = spi_get_drvdata(spi);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static const struct of_device_id b53_of_match[] = {
+       { .compatible = "brcm,bcm5325" },
+       { .compatible = "brcm,bcm53115" },
+       { .compatible = "brcm,bcm53125" },
+       { .compatible = "brcm,bcm53128" },
+       { .compatible = "brcm,bcm5365" },
+       { .compatible = "brcm,bcm5395" },
+       { .compatible = "brcm,bcm5397" },
+       { .compatible = "brcm,bcm5398" },
+       { /* sentinel */ },
+};
+
+static struct spi_driver b53_spi_driver = {
+       .driver = {
+               .name   = "b53-switch",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+               .of_match_table = b53_of_match,
+       },
+       .probe  = b53_spi_probe,
+       .remove = b53_spi_remove,
+};
+
+module_spi_driver(b53_spi_driver);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 SPI access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_srab.c b/target/linux/generic/files-4.14/drivers/net/phy/b53/b53_srab.c
new file mode 100644 (file)
index 0000000..012daa3
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * B53 register access through Switch Register Access Bridge Registers
+ *
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+/* command and status register of the SRAB */
+#define B53_SRAB_CMDSTAT               0x2c
+#define  B53_SRAB_CMDSTAT_RST          BIT(2)
+#define  B53_SRAB_CMDSTAT_WRITE                BIT(1)
+#define  B53_SRAB_CMDSTAT_GORDYN       BIT(0)
+#define  B53_SRAB_CMDSTAT_PAGE         24
+#define  B53_SRAB_CMDSTAT_REG          16
+
+/* high order word of write data to switch registe */
+#define B53_SRAB_WD_H                  0x30
+
+/* low order word of write data to switch registe */
+#define B53_SRAB_WD_L                  0x34
+
+/* high order word of read data from switch register */
+#define B53_SRAB_RD_H                  0x38
+
+/* low order word of read data from switch register */
+#define B53_SRAB_RD_L                  0x3c
+
+/* command and status register of the SRAB */
+#define B53_SRAB_CTRLS                 0x40
+#define  B53_SRAB_CTRLS_RCAREQ         BIT(3)
+#define  B53_SRAB_CTRLS_RCAGNT         BIT(4)
+#define  B53_SRAB_CTRLS_SW_INIT_DONE   BIT(6)
+
+/* the register captures interrupt pulses from the switch */
+#define B53_SRAB_INTR                  0x44
+
+static int b53_srab_request_grant(struct b53_device *dev)
+{
+       u8 __iomem *regs = dev->priv;
+       u32 ctrls;
+       int i;
+
+       ctrls = readl(regs + B53_SRAB_CTRLS);
+       ctrls |= B53_SRAB_CTRLS_RCAREQ;
+       writel(ctrls, regs + B53_SRAB_CTRLS);
+
+       for (i = 0; i < 20; i++) {
+               ctrls = readl(regs + B53_SRAB_CTRLS);
+               if (ctrls & B53_SRAB_CTRLS_RCAGNT)
+                       break;
+               usleep_range(10, 100);
+       }
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static void b53_srab_release_grant(struct b53_device *dev)
+{
+       u8 __iomem *regs = dev->priv;
+       u32 ctrls;
+
+       ctrls = readl(regs + B53_SRAB_CTRLS);
+       ctrls &= ~B53_SRAB_CTRLS_RCAREQ;
+       writel(ctrls, regs + B53_SRAB_CTRLS);
+}
+
+static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)
+{
+       int i;
+       u32 cmdstat;
+       u8 __iomem *regs = dev->priv;
+
+       /* set register address */
+       cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |
+                 (reg << B53_SRAB_CMDSTAT_REG) |
+                 B53_SRAB_CMDSTAT_GORDYN |
+                 op;
+       writel(cmdstat, regs + B53_SRAB_CMDSTAT);
+
+       /* check if operation completed */
+       for (i = 0; i < 5; ++i) {
+               cmdstat = readl(regs + B53_SRAB_CMDSTAT);
+               if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))
+                       break;
+               usleep_range(10, 100);
+       }
+
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L) & 0xff;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L) & 0xffff;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+       *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+       *val += (u64)readl(regs + B53_SRAB_RD_H) << 32;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+
+}
+
+static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel((u32)value, regs + B53_SRAB_WD_L);
+       writel((u16)(value >> 32), regs + B53_SRAB_WD_H);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+
+}
+
+static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel((u32)value, regs + B53_SRAB_WD_L);
+       writel((u32)(value >> 32), regs + B53_SRAB_WD_H);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static struct b53_io_ops b53_srab_ops = {
+       .read8 = b53_srab_read8,
+       .read16 = b53_srab_read16,
+       .read32 = b53_srab_read32,
+       .read48 = b53_srab_read48,
+       .read64 = b53_srab_read64,
+       .write8 = b53_srab_write8,
+       .write16 = b53_srab_write16,
+       .write32 = b53_srab_write32,
+       .write48 = b53_srab_write48,
+       .write64 = b53_srab_write64,
+};
+
+static int b53_srab_probe(struct platform_device *pdev)
+{
+       struct b53_platform_data *pdata = pdev->dev.platform_data;
+       struct b53_device *dev;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
+       if (!dev)
+               return -ENOMEM;
+
+       if (pdata)
+               dev->pdata = pdata;
+
+       platform_set_drvdata(pdev, dev);
+
+       return b53_switch_register(dev);
+}
+
+static int b53_srab_remove(struct platform_device *pdev)
+{
+       struct b53_device *dev = platform_get_drvdata(pdev);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static struct platform_driver b53_srab_driver = {
+       .probe = b53_srab_probe,
+       .remove = b53_srab_remove,
+       .driver = {
+               .name = "b53-srab-switch",
+       },
+};
+
+module_platform_driver(b53_srab_driver);
+MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
+MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/ip17xx.c b/target/linux/generic/files-4.14/drivers/net/phy/ip17xx.c
new file mode 100644 (file)
index 0000000..85a9617
--- /dev/null
@@ -0,0 +1,1377 @@
+/*
+ * ip17xx.c: Swconfig configuration for IC+ IP17xx switch family
+ *
+ * Copyright (C) 2008 Patrick Horn <patrick.horn@gmail.com>
+ * Copyright (C) 2008, 2010 Martin Mares <mj@ucw.cz>
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/device.h>
+
+#define MAX_VLANS 16
+#define MAX_PORTS 9
+#undef DUMP_MII_IO
+
+typedef struct ip17xx_reg {
+       u16 p;                  // phy
+       u16 m;                  // mii
+} reg;
+typedef char bitnum;
+
+#define NOTSUPPORTED {-1,-1}
+
+#define REG_SUPP(x) (((x).m != ((u16)-1)) && ((x).p != (u16)-1))
+
+struct ip17xx_state;
+
+/*********** CONSTANTS ***********/
+struct register_mappings {
+       char *NAME;
+       u16 MODEL_NO;                   // Compare to bits 4-9 of MII register 0,3.
+       bitnum NUM_PORTS;
+       bitnum CPU_PORT;
+
+/* The default VLAN for each port.
+        Default: 0x0001 for Ports 0,1,2,3
+                 0x0002 for Ports 4,5 */
+       reg VLAN_DEFAULT_TAG_REG[MAX_PORTS];
+
+/* These ports are tagged.
+        Default: 0x00 */
+       reg ADD_TAG_REG;
+       reg REMOVE_TAG_REG;
+       bitnum ADD_TAG_BIT[MAX_PORTS];
+/* These ports are untagged.
+        Default: 0x00 (i.e. do not alter any VLAN tags...)
+        Maybe set to 0 if user disables VLANs. */
+       bitnum REMOVE_TAG_BIT[MAX_PORTS];
+
+/* Port M and Port N are on the same VLAN.
+        Default: All ports on all VLANs. */
+// Use register {29, 19+N/2}
+       reg VLAN_LOOKUP_REG;
+// Port 5 uses register {30, 18} but same as odd bits.
+       reg VLAN_LOOKUP_REG_5;          // in a different register on IP175C.
+       bitnum VLAN_LOOKUP_EVEN_BIT[MAX_PORTS];
+       bitnum VLAN_LOOKUP_ODD_BIT[MAX_PORTS];
+
+/* This VLAN corresponds to which ports.
+        Default: 0x2f,0x30,0x3f,0x3f... */
+       reg TAG_VLAN_MASK_REG;
+       bitnum TAG_VLAN_MASK_EVEN_BIT[MAX_PORTS];
+       bitnum TAG_VLAN_MASK_ODD_BIT[MAX_PORTS];
+
+       int RESET_VAL;
+       reg RESET_REG;
+
+       reg MODE_REG;
+       int MODE_VAL;
+
+/* General flags */
+       reg ROUTER_CONTROL_REG;
+       reg VLAN_CONTROL_REG;
+       bitnum TAG_VLAN_BIT;
+       bitnum ROUTER_EN_BIT;
+       bitnum NUMLAN_GROUPS_MAX;
+       bitnum NUMLAN_GROUPS_BIT;
+
+       reg MII_REGISTER_EN;
+       bitnum MII_REGISTER_EN_BIT;
+
+       // set to 1 for 178C, 0 for 175C.
+       bitnum SIMPLE_VLAN_REGISTERS;   // 175C has two vlans per register but 178C has only one.
+
+       // Pointers to functions which manipulate hardware state
+       int (*update_state)(struct ip17xx_state *state);
+       int (*set_vlan_mode)(struct ip17xx_state *state);
+       int (*reset)(struct ip17xx_state *state);
+};
+
+static int ip175c_update_state(struct ip17xx_state *state);
+static int ip175c_set_vlan_mode(struct ip17xx_state *state);
+static int ip175c_reset(struct ip17xx_state *state);
+
+static const struct register_mappings IP178C = {
+       .NAME = "IP178C",
+       .MODEL_NO = 0x18,
+       .VLAN_DEFAULT_TAG_REG = {
+               {30,3},{30,4},{30,5},{30,6},{30,7},{30,8},
+               {30,9},{30,10},{30,11},
+       },
+
+       .ADD_TAG_REG = {30,12},
+       .ADD_TAG_BIT = {0,1,2,3,4,5,6,7,8},
+       .REMOVE_TAG_REG = {30,13},
+       .REMOVE_TAG_BIT = {4,5,6,7,8,9,10,11,12},
+
+       .SIMPLE_VLAN_REGISTERS = 1,
+
+       .VLAN_LOOKUP_REG = {31,0},// +N
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, // not used with SIMPLE_VLAN_REGISTERS
+       .VLAN_LOOKUP_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,5,6,7,8},
+
+       .TAG_VLAN_MASK_REG = {30,14}, // +N
+       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
+       .TAG_VLAN_MASK_ODD_BIT = {0,1,2,3,4,5,6,7,8},
+
+       .RESET_VAL = 0x55AA,
+       .RESET_REG = {30,0},
+       .MODE_VAL = 0,
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = {30,30},
+       .ROUTER_EN_BIT = 11,
+       .NUMLAN_GROUPS_MAX = 8,
+       .NUMLAN_GROUPS_BIT = 8, // {0-2}
+
+       .VLAN_CONTROL_REG = {30,13},
+       .TAG_VLAN_BIT = 3,
+
+       .CPU_PORT = 8,
+       .NUM_PORTS = 9,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+static const struct register_mappings IP175C = {
+       .NAME = "IP175C",
+       .MODEL_NO = 0x18,
+       .VLAN_DEFAULT_TAG_REG = {
+               {29,24},{29,25},{29,26},{29,27},{29,28},{29,30},
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
+       },
+
+       .ADD_TAG_REG = {29,23},
+       .REMOVE_TAG_REG = {29,23},
+       .ADD_TAG_BIT = {11,12,13,14,15,1,-1,-1,-1},
+       .REMOVE_TAG_BIT = {6,7,8,9,10,0,-1,-1,-1},
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       .VLAN_LOOKUP_REG = {29,19},// +N/2
+       .VLAN_LOOKUP_REG_5 = {30,18},
+       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,15,-1,-1,-1},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,7,-1,-1,-1},
+
+       .TAG_VLAN_MASK_REG = {30,1}, // +N/2
+       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,-1,-1,-1},
+       .TAG_VLAN_MASK_ODD_BIT = {8,9,10,11,12,13,-1,-1,-1},
+
+       .RESET_VAL = 0x175C,
+       .RESET_REG = {30,0},
+       .MODE_VAL = 0x175C,
+       .MODE_REG = {29,31},
+
+       .ROUTER_CONTROL_REG = {30,9},
+       .ROUTER_EN_BIT = 3,
+       .NUMLAN_GROUPS_MAX = 8,
+       .NUMLAN_GROUPS_BIT = 0, // {0-2}
+
+       .VLAN_CONTROL_REG = {30,9},
+       .TAG_VLAN_BIT = 7,
+
+       .NUM_PORTS = 6,
+       .CPU_PORT = 5,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+static const struct register_mappings IP175A = {
+       .NAME = "IP175A",
+       .MODEL_NO = 0x05,
+       .VLAN_DEFAULT_TAG_REG = {
+               {0,24},{0,25},{0,26},{0,27},{0,28},NOTSUPPORTED,
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
+       },
+
+       .ADD_TAG_REG = {0,23},
+       .REMOVE_TAG_REG = {0,23},
+       .ADD_TAG_BIT = {11,12,13,14,15,-1,-1,-1,-1},
+       .REMOVE_TAG_BIT = {6,7,8,9,10,-1,-1,-1,-1},
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       // Only programmable via EEPROM
+       .VLAN_LOOKUP_REG = NOTSUPPORTED,// +N/2
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
+       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,-1,-1,-1,-1},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,-1,-1,-1,-1},
+
+       .TAG_VLAN_MASK_REG = NOTSUPPORTED, // +N/2,
+       .TAG_VLAN_MASK_EVEN_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
+       .TAG_VLAN_MASK_ODD_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
+
+       .RESET_VAL = -1,
+       .RESET_REG = NOTSUPPORTED,
+       .MODE_VAL = 0,
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = NOTSUPPORTED,
+       .VLAN_CONTROL_REG = NOTSUPPORTED,
+       .TAG_VLAN_BIT = -1,
+       .ROUTER_EN_BIT = -1,
+       .NUMLAN_GROUPS_MAX = -1,
+       .NUMLAN_GROUPS_BIT = -1, // {0-2}
+
+       .NUM_PORTS = 5,
+       .CPU_PORT = 4,
+
+       .MII_REGISTER_EN = {0, 18},
+       .MII_REGISTER_EN_BIT = 7,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+
+static int ip175d_update_state(struct ip17xx_state *state);
+static int ip175d_set_vlan_mode(struct ip17xx_state *state);
+static int ip175d_reset(struct ip17xx_state *state);
+
+static const struct register_mappings IP175D = {
+       .NAME = "IP175D",
+       .MODEL_NO = 0x18,
+
+       // The IP175D has a completely different interface, so we leave most
+       // of the registers undefined and switch to different code paths.
+
+       .VLAN_DEFAULT_TAG_REG = {
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
+       },
+
+       .ADD_TAG_REG = NOTSUPPORTED,
+       .REMOVE_TAG_REG = NOTSUPPORTED,
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       .VLAN_LOOKUP_REG = NOTSUPPORTED,
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
+       .TAG_VLAN_MASK_REG = NOTSUPPORTED,
+
+       .RESET_VAL = 0x175D,
+       .RESET_REG = {20,2},
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = NOTSUPPORTED,
+       .ROUTER_EN_BIT = -1,
+       .NUMLAN_GROUPS_BIT = -1,
+
+       .VLAN_CONTROL_REG = NOTSUPPORTED,
+       .TAG_VLAN_BIT = -1,
+
+       .NUM_PORTS = 6,
+       .CPU_PORT = 5,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175d_update_state,
+       .set_vlan_mode = ip175d_set_vlan_mode,
+       .reset = ip175d_reset,
+};
+
+struct ip17xx_state {
+       struct switch_dev dev;
+       struct mii_bus *mii_bus;
+       bool registered;
+
+       int router_mode;                // ROUTER_EN
+       int vlan_enabled;               // TAG_VLAN_EN
+       struct port_state {
+               u16 pvid;
+               unsigned int shareports;
+       } ports[MAX_PORTS];
+       unsigned int add_tag;
+       unsigned int remove_tag;
+       int num_vlans;
+       struct vlan_state {
+               unsigned int ports;
+               unsigned int tag;       // VLAN tag (IP175D only)
+       } vlans[MAX_VLANS];
+       const struct register_mappings *regs;
+       reg proc_mii;   // phy/reg for the low level register access via swconfig
+
+       char buf[80];
+};
+
+#define get_state(_dev) container_of((_dev), struct ip17xx_state, dev)
+
+static int ip_phy_read(struct ip17xx_state *state, int port, int reg)
+{
+       int val = mdiobus_read(state->mii_bus, port, reg);
+       if (val < 0)
+               pr_warning("IP17xx: Unable to get MII register %d,%d: error %d\n", port, reg, -val);
+#ifdef DUMP_MII_IO
+       else
+               pr_debug("IP17xx: Read MII(%d,%d) -> %04x\n", port, reg, val);
+#endif
+       return val;
+}
+
+static int ip_phy_write(struct ip17xx_state *state, int port, int reg, u16 val)
+{
+       int err;
+
+#ifdef DUMP_MII_IO
+       pr_debug("IP17xx: Write MII(%d,%d) <- %04x\n", port, reg, val);
+#endif
+       err = mdiobus_write(state->mii_bus, port, reg, val);
+       if (err < 0)
+               pr_warning("IP17xx: Unable to write MII register %d,%d: error %d\n", port, reg, -err);
+       return err;
+}
+
+static int ip_phy_write_masked(struct ip17xx_state *state, int port, int reg, unsigned int mask, unsigned int data)
+{
+       int val = ip_phy_read(state, port, reg);
+       if (val < 0)
+               return 0;
+       return ip_phy_write(state, port, reg, (val & ~mask) | data);
+}
+
+static int getPhy(struct ip17xx_state *state, reg mii)
+{
+       if (!REG_SUPP(mii))
+               return -EFAULT;
+       return ip_phy_read(state, mii.p, mii.m);
+}
+
+static int setPhy(struct ip17xx_state *state, reg mii, u16 value)
+{
+       int err;
+
+       if (!REG_SUPP(mii))
+               return -EFAULT;
+       err = ip_phy_write(state, mii.p, mii.m, value);
+       if (err < 0)
+               return err;
+       mdelay(2);
+       getPhy(state, mii);
+       return 0;
+}
+
+
+/**
+ * These two macros are to simplify the mapping of logical bits to the bits in hardware.
+ * NOTE: these macros will return if there is an error!
+ */
+
+#define GET_PORT_BITS(state, bits, addr, bit_lookup)           \
+       do {                                                    \
+               int i, val = getPhy((state), (addr));           \
+               if (val < 0)                                    \
+                       return val;                             \
+               (bits) = 0;                                     \
+               for (i = 0; i < MAX_PORTS; i++) {               \
+                       if ((bit_lookup)[i] == -1) continue;    \
+                       if (val & (1<<(bit_lookup)[i]))         \
+                               (bits) |= (1<<i);               \
+               }                                               \
+       } while (0)
+
+#define SET_PORT_BITS(state, bits, addr, bit_lookup)           \
+       do {                                                    \
+               int i, val = getPhy((state), (addr));           \
+               if (val < 0)                                    \
+                       return val;                             \
+               for (i = 0; i < MAX_PORTS; i++) {               \
+                       unsigned int newmask = ((bits)&(1<<i)); \
+                       if ((bit_lookup)[i] == -1) continue;    \
+                       val &= ~(1<<(bit_lookup)[i]);           \
+                       val |= ((newmask>>i)<<(bit_lookup)[i]); \
+               }                                               \
+               val = setPhy((state), (addr), val);             \
+               if (val < 0)                                    \
+                       return val;                             \
+       } while (0)
+
+
+static int get_model(struct ip17xx_state *state)
+{
+       int id1, id2;
+       int oui_id, model_no, rev_no, chip_no;
+
+       id1 = ip_phy_read(state, 0, 2);
+       id2 = ip_phy_read(state, 0, 3);
+       oui_id = (id1 << 6) | ((id2 >> 10) & 0x3f);
+       model_no = (id2 >> 4) & 0x3f;
+       rev_no = id2 & 0xf;
+       pr_debug("IP17xx: Identified oui=%06x model=%02x rev=%X\n", oui_id, model_no, rev_no);
+
+       if (oui_id != 0x0090c3)  // No other oui_id should have reached us anyway
+               return -ENODEV;
+
+       if (model_no == IP175A.MODEL_NO) {
+               state->regs = &IP175A;
+       } else if (model_no == IP175C.MODEL_NO) {
+               /*
+                *  Several models share the same model_no:
+                *  178C has more PHYs, so we try whether the device responds to a read from PHY5
+                *  175D has a new chip ID register
+                *  175C has neither
+                */
+               if (ip_phy_read(state, 5, 2) == 0x0243) {
+                       state->regs = &IP178C;
+               } else {
+                       chip_no = ip_phy_read(state, 20, 0);
+                       pr_debug("IP17xx: Chip ID register reads %04x\n", chip_no);
+                       if (chip_no == 0x175d) {
+                               state->regs = &IP175D;
+                       } else {
+                               state->regs = &IP175C;
+                       }
+               }
+       } else {
+               pr_warning("IP17xx: Found an unknown IC+ switch with model number %02x, revision %X.\n", model_no, rev_no);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/*** Low-level functions for the older models ***/
+
+/** Only set vlan and router flags in the switch **/
+static int ip175c_set_flags(struct ip17xx_state *state)
+{
+       int val;
+
+       if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) {
+               return 0;
+       }
+
+       val = getPhy(state, state->regs->ROUTER_CONTROL_REG);
+       if (val < 0) {
+               return val;
+       }
+       if (state->regs->ROUTER_EN_BIT >= 0) {
+               if (state->router_mode) {
+                       val |= (1<<state->regs->ROUTER_EN_BIT);
+               } else {
+                       val &= (~(1<<state->regs->ROUTER_EN_BIT));
+               }
+       }
+       if (state->regs->TAG_VLAN_BIT >= 0) {
+               if (state->vlan_enabled) {
+                       val |= (1<<state->regs->TAG_VLAN_BIT);
+               } else {
+                       val &= (~(1<<state->regs->TAG_VLAN_BIT));
+               }
+       }
+       if (state->regs->NUMLAN_GROUPS_BIT >= 0) {
+               val &= (~((state->regs->NUMLAN_GROUPS_MAX-1)<<state->regs->NUMLAN_GROUPS_BIT));
+               if (state->num_vlans > state->regs->NUMLAN_GROUPS_MAX) {
+                       val |= state->regs->NUMLAN_GROUPS_MAX << state->regs->NUMLAN_GROUPS_BIT;
+               } else if (state->num_vlans >= 1) {
+                       val |= (state->num_vlans-1) << state->regs->NUMLAN_GROUPS_BIT;
+               }
+       }
+       return setPhy(state, state->regs->ROUTER_CONTROL_REG, val);
+}
+
+/** Set all VLAN and port state.  Usually you should call "correct_vlan_state" first. **/
+static int ip175c_set_state(struct ip17xx_state *state)
+{
+       int j;
+       int i;
+       SET_PORT_BITS(state, state->add_tag,
+                                 state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT);
+       SET_PORT_BITS(state, state->remove_tag,
+                                 state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT);
+
+       if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) {
+               for (j=0; j<state->regs->NUM_PORTS; j++) {
+                       reg addr;
+                       const bitnum *bit_lookup = (j%2==0)?
+                               state->regs->VLAN_LOOKUP_EVEN_BIT:
+                               state->regs->VLAN_LOOKUP_ODD_BIT;
+
+                       addr = state->regs->VLAN_LOOKUP_REG;
+                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
+                               addr.m += j;
+                       } else {
+                               switch (j) {
+                               case 0:
+                               case 1:
+                                       break;
+                               case 2:
+                               case 3:
+                                       addr.m+=1;
+                                       break;
+                               case 4:
+                                       addr.m+=2;
+                                       break;
+                               case 5:
+                                       addr = state->regs->VLAN_LOOKUP_REG_5;
+                                       break;
+                               default:
+                                       addr.m = -1; // shouldn't get here, but...
+                                       break;
+                               }
+                       }
+                       //printf("shareports for %d is %02X\n",j,state->ports[j].shareports);
+                       if (REG_SUPP(addr)) {
+                               SET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup);
+                       }
+               }
+       }
+       if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) {
+               for (j=0; j<MAX_VLANS; j++) {
+                       reg addr = state->regs->TAG_VLAN_MASK_REG;
+                       const bitnum *bit_lookup = (j%2==0)?
+                               state->regs->TAG_VLAN_MASK_EVEN_BIT:
+                               state->regs->TAG_VLAN_MASK_ODD_BIT;
+                       unsigned int vlan_mask;
+                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
+                               addr.m += j;
+                       } else {
+                               addr.m += j/2;
+                       }
+                       vlan_mask = state->vlans[j].ports;
+                       SET_PORT_BITS(state, vlan_mask, addr, bit_lookup);
+               }
+       }
+
+       for (i=0; i<MAX_PORTS; i++) {
+               if (REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[i])) {
+                       int err = setPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i],
+                                       state->ports[i].pvid);
+                       if (err < 0) {
+                               return err;
+                       }
+               }
+       }
+
+       return ip175c_set_flags(state);
+}
+
+/**
+ *  Uses only the VLAN port mask and the add tag mask to generate the other fields:
+ *  which ports are part of the same VLAN, removing vlan tags, and VLAN tag ids.
+ */
+static void ip175c_correct_vlan_state(struct ip17xx_state *state)
+{
+       int i, j;
+       state->num_vlans = 0;
+       for (i=0; i<MAX_VLANS; i++) {
+               if (state->vlans[i].ports != 0) {
+                       state->num_vlans = i+1; // Hack -- we need to store the "set" vlans somewhere...
+               }
+       }
+
+       for (i=0; i<state->regs->NUM_PORTS; i++) {
+               unsigned int portmask = (1<<i);
+               if (!state->vlan_enabled) {
+                       // Share with everybody!
+                       state->ports[i].shareports = (1<<state->regs->NUM_PORTS)-1;
+                       continue;
+               }
+               state->ports[i].shareports = portmask;
+               for (j=0; j<MAX_VLANS; j++) {
+                       if (state->vlans[j].ports & portmask)
+                               state->ports[i].shareports |= state->vlans[j].ports;
+               }
+       }
+}
+
+static int ip175c_update_state(struct ip17xx_state *state)
+{
+       ip175c_correct_vlan_state(state);
+       return ip175c_set_state(state);
+}
+
+static int ip175c_set_vlan_mode(struct ip17xx_state *state)
+{
+       return ip175c_update_state(state);
+}
+
+static int ip175c_reset(struct ip17xx_state *state)
+{
+       int err;
+
+       if (REG_SUPP(state->regs->MODE_REG)) {
+               err = setPhy(state, state->regs->MODE_REG, state->regs->MODE_VAL);
+               if (err < 0)
+                       return err;
+               err = getPhy(state, state->regs->MODE_REG);
+               if (err < 0)
+                       return err;
+       }
+
+       return ip175c_update_state(state);
+}
+
+/*** Low-level functions for IP175D ***/
+
+static int ip175d_update_state(struct ip17xx_state *state)
+{
+       unsigned int filter_mask = 0;
+       unsigned int ports[16], add[16], rem[16];
+       int i, j;
+       int err = 0;
+
+       for (i = 0; i < 16; i++) {
+               ports[i] = 0;
+               add[i] = 0;
+               rem[i] = 0;
+               if (!state->vlan_enabled) {
+                       err |= ip_phy_write(state, 22, 14+i, i+1);      // default tags
+                       ports[i] = 0x3f;
+                       continue;
+               }
+               if (!state->vlans[i].tag) {
+                       // Reset the filter
+                       err |= ip_phy_write(state, 22, 14+i, 0);        // tag
+                       continue;
+               }
+               filter_mask |= 1 << i;
+               err |= ip_phy_write(state, 22, 14+i, state->vlans[i].tag);
+               ports[i] = state->vlans[i].ports;
+               for (j = 0; j < 6; j++) {
+                       if (ports[i] & (1 << j)) {
+                               if (state->add_tag & (1 << j))
+                                       add[i] |= 1 << j;
+                               if (state->remove_tag & (1 << j))
+                                       rem[i] |= 1 << j;
+                       }
+               }
+       }
+
+       // Port masks, tag adds and removals
+       for (i = 0; i < 8; i++) {
+               err |= ip_phy_write(state, 23, i, ports[2*i] | (ports[2*i+1] << 8));
+               err |= ip_phy_write(state, 23, 8+i, add[2*i] | (add[2*i+1] << 8));
+               err |= ip_phy_write(state, 23, 16+i, rem[2*i] | (rem[2*i+1] << 8));
+       }
+       err |= ip_phy_write(state, 22, 10, filter_mask);
+
+       // Default VLAN tag for each port
+       for (i = 0; i < 6; i++)
+               err |= ip_phy_write(state, 22, 4+i, state->vlans[state->ports[i].pvid].tag);
+
+       return (err ? -EIO : 0);
+}
+
+static int ip175d_set_vlan_mode(struct ip17xx_state *state)
+{
+       int i;
+       int err = 0;
+
+       if (state->vlan_enabled) {
+               // VLAN classification rules: tag-based VLANs, use VID to classify,
+               // drop packets that cannot be classified.
+               err |= ip_phy_write_masked(state, 22, 0, 0x3fff, 0x003f);
+
+               // Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed,
+               // VID=0xfff discarded, admin both tagged and untagged, ingress
+               // filters enabled.
+               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
+
+               // Egress rules: IGMP processing off, keep VLAN header off
+               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
+       } else {
+               // VLAN classification rules: everything off & clear table
+               err |= ip_phy_write_masked(state, 22, 0, 0xbfff, 0x8000);
+
+               // Ingress and egress rules: set to defaults
+               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
+               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
+       }
+
+       // Reset default VLAN for each port to 0
+       for (i = 0; i < 6; i++)
+               state->ports[i].pvid = 0;
+
+       err |= ip175d_update_state(state);
+
+       return (err ? -EIO : 0);
+}
+
+static int ip175d_reset(struct ip17xx_state *state)
+{
+       int err = 0;
+
+       // Disable the special tagging mode
+       err |= ip_phy_write_masked(state, 21, 22, 0x0003, 0x0000);
+
+       // Set 802.1q protocol type
+       err |= ip_phy_write(state, 22, 3, 0x8100);
+
+       state->vlan_enabled = 0;
+       err |= ip175d_set_vlan_mode(state);
+
+       return (err ? -EIO : 0);
+}
+
+/*** High-level functions ***/
+
+static int ip17xx_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->vlan_enabled;
+       return 0;
+}
+
+static void ip17xx_reset_vlan_config(struct ip17xx_state *state)
+{
+       int i;
+
+       state->remove_tag = (state->vlan_enabled ? ((1<<state->regs->NUM_PORTS)-1) : 0x0000);
+       state->add_tag = 0x0000;
+       for (i = 0; i < MAX_VLANS; i++) {
+               state->vlans[i].ports = 0x0000;
+               state->vlans[i].tag = (i ? i : 16);
+       }
+       for (i = 0; i < MAX_PORTS; i++)
+               state->ports[i].pvid = 0;
+}
+
+static int ip17xx_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int enable;
+
+       enable = val->value.i;
+       if (state->vlan_enabled == enable) {
+               // Do not change any state.
+               return 0;
+       }
+       state->vlan_enabled = enable;
+
+       // Otherwise, if we are switching state, set fields to a known default.
+       ip17xx_reset_vlan_config(state);
+
+       return state->regs->set_vlan_mode(state);
+}
+
+static int ip17xx_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int b;
+       int ind;
+       unsigned int ports;
+
+       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
+               return -EINVAL;
+
+       ports = state->vlans[val->port_vlan].ports;
+       b = 0;
+       ind = 0;
+       while (b < MAX_PORTS) {
+               if (ports&1) {
+                       int istagged = ((state->add_tag >> b) & 1);
+                       val->value.ports[ind].id = b;
+                       val->value.ports[ind].flags = (istagged << SWITCH_PORT_FLAG_TAGGED);
+                       ind++;
+               }
+               b++;
+               ports >>= 1;
+       }
+       val->len = ind;
+
+       return 0;
+}
+
+static int ip17xx_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int i;
+
+       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
+               return -EINVAL;
+
+       state->vlans[val->port_vlan].ports = 0;
+       for (i = 0; i < val->len; i++) {
+               unsigned int bitmask = (1<<val->value.ports[i].id);
+               state->vlans[val->port_vlan].ports |= bitmask;
+               if (val->value.ports[i].flags & (1<<SWITCH_PORT_FLAG_TAGGED)) {
+                       state->add_tag |= bitmask;
+                       state->remove_tag &= (~bitmask);
+               } else {
+                       state->add_tag &= (~bitmask);
+                       state->remove_tag |= bitmask;
+               }
+       }
+
+       return state->regs->update_state(state);
+}
+
+static int ip17xx_apply(struct switch_dev *dev)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (REG_SUPP(state->regs->MII_REGISTER_EN)) {
+               int val = getPhy(state, state->regs->MII_REGISTER_EN);
+               if (val < 0) {
+                       return val;
+               }
+               val |= (1<<state->regs->MII_REGISTER_EN_BIT);
+               return setPhy(state, state->regs->MII_REGISTER_EN, val);
+       }
+       return 0;
+}
+
+static int ip17xx_reset(struct switch_dev *dev)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int i, err;
+
+       if (REG_SUPP(state->regs->RESET_REG)) {
+               err = setPhy(state, state->regs->RESET_REG, state->regs->RESET_VAL);
+               if (err < 0)
+                       return err;
+               err = getPhy(state, state->regs->RESET_REG);
+
+               /*
+                *  Data sheet specifies reset period to be 2 msec.
+                *  (I don't see any mention of the 2ms delay in the IP178C spec, only
+                *  in IP175C, but it can't hurt.)
+                */
+               mdelay(2);
+       }
+
+       /* reset switch ports */
+       for (i = 0; i < state->regs->NUM_PORTS-1; i++) {
+               err = ip_phy_write(state, i, MII_BMCR, BMCR_RESET);
+               if (err < 0)
+                       return err;
+       }
+
+       state->router_mode = 0;
+       state->vlan_enabled = 0;
+       ip17xx_reset_vlan_config(state);
+
+       return state->regs->reset(state);
+}
+
+static int ip17xx_get_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (state->add_tag & (1<<val->port_vlan)) {
+               if (state->remove_tag & (1<<val->port_vlan))
+                       val->value.i = 3; // shouldn't ever happen.
+               else
+                       val->value.i = 1;
+       } else {
+               if (state->remove_tag & (1<<val->port_vlan))
+                       val->value.i = 0;
+               else
+                       val->value.i = 2;
+       }
+       return 0;
+}
+
+static int ip17xx_set_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       state->add_tag &= ~(1<<val->port_vlan);
+       state->remove_tag &= ~(1<<val->port_vlan);
+
+       if (val->value.i == 0)
+               state->remove_tag |= (1<<val->port_vlan);
+       if (val->value.i == 1)
+               state->add_tag |= (1<<val->port_vlan);
+
+       return state->regs->update_state(state);
+}
+
+/** Get the current phy address */
+static int ip17xx_get_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->proc_mii.p;
+       return 0;
+}
+
+/** Set a new phy address for low level access to registers */
+static int ip17xx_set_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int new_reg = val->value.i;
+
+       if (new_reg < 0 || new_reg > 31)
+               state->proc_mii.p = (u16)-1;
+       else
+               state->proc_mii.p = (u16)new_reg;
+       return 0;
+}
+
+/** Get the current register number */
+static int ip17xx_get_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->proc_mii.m;
+       return 0;
+}
+
+/** Set a new register address for low level access to registers */
+static int ip17xx_set_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int new_reg = val->value.i;
+
+       if (new_reg < 0 || new_reg > 31)
+               state->proc_mii.m = (u16)-1;
+       else
+               state->proc_mii.m = (u16)new_reg;
+       return 0;
+}
+
+/** Get the register content of state->proc_mii */
+static int ip17xx_get_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int retval = -EINVAL;
+       if (REG_SUPP(state->proc_mii))
+               retval = getPhy(state, state->proc_mii);
+
+       if (retval < 0) {
+               return retval;
+       } else {
+               val->value.i = retval;
+               return 0;
+       }
+}
+
+/** Write a value to the register defined by phy/reg above */
+static int ip17xx_set_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int myval, err = -EINVAL;
+
+       myval = val->value.i;
+       if (myval <= 0xffff && myval >= 0 && REG_SUPP(state->proc_mii)) {
+               err = setPhy(state, state->proc_mii, (u16)myval);
+       }
+       return err;
+}
+
+static int ip17xx_read_name(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       val->value.s = state->regs->NAME; // Just a const pointer, won't be freed by swconfig.
+       return 0;
+}
+
+static int ip17xx_get_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int vlan = val->port_vlan;
+
+       if (vlan < 0 || vlan >= MAX_VLANS)
+               return -EINVAL;
+
+       val->value.i = state->vlans[vlan].tag;
+       return 0;
+}
+
+static int ip17xx_set_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int vlan = val->port_vlan;
+       int tag = val->value.i;
+
+       if (vlan < 0 || vlan >= MAX_VLANS)
+               return -EINVAL;
+
+       if (tag < 0 || tag > 4095)
+               return -EINVAL;
+
+       state->vlans[vlan].tag = tag;
+       return state->regs->update_state(state);
+}
+
+static int ip17xx_set_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int nr = val->port_vlan;
+       int ctrl;
+       int autoneg;
+       int speed;
+       if (val->value.i == 100) {
+               speed = 1;
+               autoneg = 0;
+       } else if (val->value.i == 10) {
+               speed = 0;
+               autoneg = 0;
+       } else {
+               autoneg = 1;
+               speed = 1;
+       }
+
+       /* Can't set speed for cpu port */
+       if (nr == state->regs->CPU_PORT)
+               return -EINVAL;
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       ctrl = ip_phy_read(state, nr, 0);
+       if (ctrl < 0)
+               return -EIO;
+
+       ctrl &= (~(1<<12));
+       ctrl &= (~(1<<13));
+       ctrl |= (autoneg<<12);
+       ctrl |= (speed<<13);
+
+       return ip_phy_write(state, nr, 0, ctrl);
+}
+
+static int ip17xx_get_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int nr = val->port_vlan;
+       int speed, status;
+
+       if (nr == state->regs->CPU_PORT) {
+               val->value.i = 100;
+               return 0;
+       }
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       status = ip_phy_read(state, nr, 1);
+       speed = ip_phy_read(state, nr, 18);
+       if (status < 0 || speed < 0)
+               return -EIO;
+
+       if (status & 4)
+               val->value.i = ((speed & (1<<11)) ? 100 : 10);
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int ip17xx_get_port_status(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int ctrl, speed, status;
+       int nr = val->port_vlan;
+       int len;
+       char *buf = state->buf; // fixed-length at 80.
+
+       if (nr == state->regs->CPU_PORT) {
+               sprintf(buf, "up, 100 Mbps, cpu port");
+               val->value.s = buf;
+               return 0;
+       }
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       ctrl = ip_phy_read(state, nr, 0);
+       status = ip_phy_read(state, nr, 1);
+       speed = ip_phy_read(state, nr, 18);
+       if (ctrl < 0 || status < 0 || speed < 0)
+               return -EIO;
+
+       if (status & 4)
+               len = sprintf(buf, "up, %d Mbps, %s duplex",
+                       ((speed & (1<<11)) ? 100 : 10),
+                       ((speed & (1<<10)) ? "full" : "half"));
+       else
+               len = sprintf(buf, "down");
+
+       if (ctrl & (1<<12)) {
+               len += sprintf(buf+len, ", auto-negotiate");
+               if (!(status & (1<<5)))
+                       len += sprintf(buf+len, " (in progress)");
+       } else {
+               len += sprintf(buf+len, ", fixed speed (%d)",
+                       ((ctrl & (1<<13)) ? 100 : 10));
+       }
+
+       buf[len] = '\0';
+       val->value.s = buf;
+       return 0;
+}
+
+static int ip17xx_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       *val = state->ports[port].pvid;
+       return 0;
+}
+
+static int ip17xx_set_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (val < 0 || val >= MAX_VLANS)
+               return -EINVAL;
+
+       state->ports[port].pvid = val;
+       return state->regs->update_state(state);
+}
+
+
+enum Ports {
+       IP17XX_PORT_STATUS,
+       IP17XX_PORT_LINK,
+       IP17XX_PORT_TAGGED,
+       IP17XX_PORT_PVID,
+};
+
+enum Globals {
+       IP17XX_ENABLE_VLAN,
+       IP17XX_GET_NAME,
+       IP17XX_REGISTER_PHY,
+       IP17XX_REGISTER_MII,
+       IP17XX_REGISTER_VALUE,
+       IP17XX_REGISTER_ERRNO,
+};
+
+enum Vlans {
+       IP17XX_VLAN_TAG,
+};
+
+static const struct switch_attr ip17xx_global[] = {
+       [IP17XX_ENABLE_VLAN] = {
+               .id = IP17XX_ENABLE_VLAN,
+               .type = SWITCH_TYPE_INT,
+               .name  = "enable_vlan",
+               .description = "Flag to enable or disable VLANs and tagging",
+               .get  = ip17xx_get_enable_vlan,
+               .set = ip17xx_set_enable_vlan,
+       },
+       [IP17XX_GET_NAME] = {
+               .id = IP17XX_GET_NAME,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Returns the type of IC+ chip.",
+               .name  = "name",
+               .get  = ip17xx_read_name,
+               .set = NULL,
+       },
+       /* jal: added for low level debugging etc. */
+       [IP17XX_REGISTER_PHY] = {
+               .id = IP17XX_REGISTER_PHY,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: set PHY (0-4, or 29,30,31)",
+               .name  = "phy",
+               .get  = ip17xx_get_phy,
+               .set = ip17xx_set_phy,
+       },
+       [IP17XX_REGISTER_MII] = {
+               .id = IP17XX_REGISTER_MII,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: set MII register number (0-31)",
+               .name  = "reg",
+               .get  = ip17xx_get_reg,
+               .set = ip17xx_set_reg,
+       },
+       [IP17XX_REGISTER_VALUE] = {
+               .id = IP17XX_REGISTER_VALUE,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: read/write to register (0-65535)",
+               .name  = "val",
+               .get  = ip17xx_get_val,
+               .set = ip17xx_set_val,
+       },
+};
+
+static const struct switch_attr ip17xx_vlan[] = {
+       [IP17XX_VLAN_TAG] = {
+               .id = IP17XX_VLAN_TAG,
+               .type = SWITCH_TYPE_INT,
+               .description = "VLAN ID (0-4095) [IP175D only]",
+               .name = "vid",
+               .get = ip17xx_get_tag,
+               .set = ip17xx_set_tag,
+       }
+};
+
+static const struct switch_attr ip17xx_port[] = {
+       [IP17XX_PORT_STATUS] = {
+               .id = IP17XX_PORT_STATUS,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Returns Detailed port status",
+               .name  = "status",
+               .get  = ip17xx_get_port_status,
+               .set = NULL,
+       },
+       [IP17XX_PORT_LINK] = {
+               .id = IP17XX_PORT_LINK,
+               .type = SWITCH_TYPE_INT,
+               .description = "Link speed. Can write 0 for auto-negotiate, or 10 or 100",
+               .name  = "link",
+               .get  = ip17xx_get_port_speed,
+               .set = ip17xx_set_port_speed,
+       },
+       [IP17XX_PORT_TAGGED] = {
+               .id = IP17XX_PORT_LINK,
+               .type = SWITCH_TYPE_INT,
+               .description = "0 = untag, 1 = add tags, 2 = do not alter (This value is reset if vlans are altered)",
+               .name  = "tagged",
+               .get  = ip17xx_get_tagged,
+               .set = ip17xx_set_tagged,
+       },
+};
+
+static const struct switch_dev_ops ip17xx_ops = {
+       .attr_global = {
+               .attr = ip17xx_global,
+               .n_attr = ARRAY_SIZE(ip17xx_global),
+       },
+       .attr_port = {
+               .attr = ip17xx_port,
+               .n_attr = ARRAY_SIZE(ip17xx_port),
+       },
+       .attr_vlan = {
+               .attr = ip17xx_vlan,
+               .n_attr = ARRAY_SIZE(ip17xx_vlan),
+       },
+
+       .get_port_pvid = ip17xx_get_pvid,
+       .set_port_pvid = ip17xx_set_pvid,
+       .get_vlan_ports = ip17xx_get_ports,
+       .set_vlan_ports = ip17xx_set_ports,
+       .apply_config = ip17xx_apply,
+       .reset_switch = ip17xx_reset,
+};
+
+static int ip17xx_probe(struct phy_device *pdev)
+{
+       struct ip17xx_state *state;
+       struct switch_dev *dev;
+       int err;
+
+       /* We only attach to PHY 0, but use all available PHYs */
+       if (pdev->mdio.addr != 0)
+               return -ENODEV;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       dev = &state->dev;
+
+       pdev->priv = state;
+       state->mii_bus = pdev->mdio.bus;
+
+       err = get_model(state);
+       if (err < 0)
+               goto error;
+
+       dev->vlans = MAX_VLANS;
+       dev->cpu_port = state->regs->CPU_PORT;
+       dev->ports = state->regs->NUM_PORTS;
+       dev->name = state->regs->NAME;
+       dev->ops = &ip17xx_ops;
+
+       pr_info("IP17xx: Found %s at %s\n", dev->name, dev_name(&pdev->mdio.dev));
+       return 0;
+
+error:
+       kfree(state);
+       return err;
+}
+
+static int ip17xx_config_init(struct phy_device *pdev)
+{
+       struct ip17xx_state *state = pdev->priv;
+       struct net_device *dev = pdev->attached_dev;
+       int err;
+
+       err = register_switch(&state->dev, dev);
+       if (err < 0)
+               return err;
+
+       state->registered = true;
+       ip17xx_reset(&state->dev);
+       return 0;
+}
+
+static void ip17xx_remove(struct phy_device *pdev)
+{
+       struct ip17xx_state *state = pdev->priv;
+
+       if (state->registered)
+               unregister_switch(&state->dev);
+       kfree(state);
+}
+
+static int ip17xx_config_aneg(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static int ip17xx_aneg_done(struct phy_device *pdev)
+{
+       return 1;       /* Return any positive value */
+}
+
+static int ip17xx_update_link(struct phy_device *pdev)
+{
+       pdev->link = 1;
+       return 0;
+}
+
+static int ip17xx_read_status(struct phy_device *pdev)
+{
+       pdev->speed = SPEED_100;
+       pdev->duplex = DUPLEX_FULL;
+       pdev->pause = pdev->asym_pause = 0;
+       pdev->link = 1;
+
+       return 0;
+}
+
+static struct phy_driver ip17xx_driver[] = {
+       {
+               .name           = "IC+ IP17xx",
+               .phy_id         = 0x02430c00,
+               .phy_id_mask    = 0x0ffffc00,
+               .features       = PHY_BASIC_FEATURES,
+               .probe          = ip17xx_probe,
+               .remove         = ip17xx_remove,
+               .config_init    = ip17xx_config_init,
+               .config_aneg    = ip17xx_config_aneg,
+               .aneg_done      = ip17xx_aneg_done,
+               .update_link    = ip17xx_update_link,
+               .read_status    = ip17xx_read_status,
+       }
+};
+
+module_phy_driver(ip17xx_driver);
+
+MODULE_AUTHOR("Patrick Horn <patrick.horn@gmail.com>");
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_AUTHOR("Martin Mares <mj@ucw.cz>");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/mvsw61xx.c b/target/linux/generic/files-4.14/drivers/net/phy/mvsw61xx.c
new file mode 100644 (file)
index 0000000..9a689e6
--- /dev/null
@@ -0,0 +1,947 @@
+/*
+ * Marvell 88E61xx switch driver
+ *
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
+ *
+ * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "mvsw61xx.h"
+
+MODULE_DESCRIPTION("Marvell 88E61xx Switch driver");
+MODULE_AUTHOR("Claudio Leite <leitec@staticky.com>");
+MODULE_AUTHOR("Nikita Nazarenko <nnazarenko@radiofid.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mvsw61xx");
+
+/*
+ * Register access is done through direct or indirect addressing,
+ * depending on how the switch is physically connected.
+ *
+ * Direct addressing: all port and global registers directly
+ *   accessible via an address/register pair
+ *
+ * Indirect addressing: switch is mapped at a single address,
+ *   port and global registers accessible via a single command/data
+ *   register pair
+ */
+
+static int
+mvsw61xx_wait_mask_raw(struct mii_bus *bus, int addr,
+               int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = bus->read(bus, addr, reg);
+               if ((r & mask) == val)
+                       return 0;
+       } while (--i > 0);
+
+       return -ETIMEDOUT;
+}
+
+static u16
+r16(struct mii_bus *bus, bool indirect, int base_addr, int addr, int reg)
+{
+       u16 ind_addr;
+
+       if (!indirect)
+               return bus->read(bus, addr, reg);
+
+       /* Indirect read: First, make sure switch is free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load address and request read */
+       ind_addr = MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg;
+       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       ind_addr);
+
+       /* Wait until it's ready */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Read the requested data */
+       return bus->read(bus, base_addr, MV_INDIRECT_REG_DATA);
+}
+
+static void
+w16(struct mii_bus *bus, bool indirect, int base_addr, int addr,
+               int reg, u16 val)
+{
+       u16 ind_addr;
+
+       if (!indirect) {
+               bus->write(bus, addr, reg, val);
+               return;
+       }
+
+       /* Indirect write: First, make sure switch is free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load the data to be written */
+       bus->write(bus, base_addr, MV_INDIRECT_REG_DATA, val);
+
+       /* Wait again for switch to be free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load address, and issue write command */
+       ind_addr = MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg;
+       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       ind_addr);
+}
+
+/* swconfig support */
+
+static inline u16
+sr16(struct switch_dev *dev, int addr, int reg)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       return r16(state->bus, state->is_indirect, state->base_addr, addr, reg);
+}
+
+static inline void
+sw16(struct switch_dev *dev, int addr, int reg, u16 val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       w16(state->bus, state->is_indirect, state->base_addr, addr, reg, val);
+}
+
+static int
+mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr,
+               int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = sr16(dev, addr, reg) & mask;
+               if (r == val)
+                       return 0;
+       } while (--i > 0);
+
+       return -ETIMEDOUT;
+}
+
+static int
+mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg)
+{
+       sw16(dev, MV_GLOBAL2REG(SMI_OP),
+            MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg);
+
+       if (mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
+                                MV_INDIRECT_INPROGRESS, 0) < 0)
+               return -ETIMEDOUT;
+
+       return sr16(dev, MV_GLOBAL2REG(SMI_DATA));
+}
+
+static int
+mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val)
+{
+       sw16(dev, MV_GLOBAL2REG(SMI_DATA), val);
+
+       sw16(dev, MV_GLOBAL2REG(SMI_OP),
+            MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg);
+
+       return mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
+                                   MV_INDIRECT_INPROGRESS, 0) < 0;
+}
+
+static int
+mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg)
+{
+       int ret;
+
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
+       ret = mvsw61xx_mdio_read(dev, port, reg);
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
+
+       return ret;
+}
+
+static void
+mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg,
+                        u16 val)
+{
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
+       mvsw61xx_mdio_write(dev, port, reg, val);
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
+}
+
+static int
+mvsw61xx_get_port_mask(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       char *buf = state->buf;
+       int port, len, i;
+       u16 reg;
+
+       port = val->port_vlan;
+       reg = sr16(dev, MV_PORTREG(VLANMAP, port)) & MV_PORTS_MASK;
+
+       len = sprintf(buf, "0x%04x: ", reg);
+
+       for (i = 0; i < MV_PORTS; i++) {
+               if (reg & (1 << i))
+                       len += sprintf(buf + len, "%d ", i);
+               else if (i == port)
+                       len += sprintf(buf + len, "(%d) ", i);
+       }
+
+       val->value.s = buf;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_qmode(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->ports[val->port_vlan].qmode;
+
+       return 0;
+}
+
+static int
+mvsw61xx_set_port_qmode(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->ports[val->port_vlan].qmode = val->value.i;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       *val = state->ports[port].pvid;
+
+       return 0;
+}
+
+static int
+mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       if (val < 0 || val >= MV_VLANS)
+               return -EINVAL;
+
+       state->ports[port].pvid = (u16)val;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_link(struct switch_dev *dev, int port,
+               struct switch_port_link *link)
+{
+       u16 status, speed;
+
+       status = sr16(dev, MV_PORTREG(STATUS, port));
+
+       link->link = status & MV_PORT_STATUS_LINK;
+       if (!link->link)
+               return 0;
+
+       link->duplex = status & MV_PORT_STATUS_FDX;
+
+       speed = (status & MV_PORT_STATUS_SPEED_MASK) >>
+                       MV_PORT_STATUS_SPEED_SHIFT;
+
+       switch (speed) {
+       case MV_PORT_STATUS_SPEED_10:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case MV_PORT_STATUS_SPEED_100:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case MV_PORT_STATUS_SPEED_1000:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       }
+
+       return 0;
+}
+
+static int mvsw61xx_get_vlan_ports(struct switch_dev *dev,
+               struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, j, mode, vno;
+
+       vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       for (i = 0, j = 0; i < dev->ports; i++) {
+               if (state->vlans[vno].mask & (1 << i)) {
+                       val->value.ports[j].id = i;
+
+                       mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
+                       if (mode == MV_VTUCTL_EGRESS_TAGGED)
+                               val->value.ports[j].flags =
+                                       (1 << SWITCH_PORT_FLAG_TAGGED);
+                       else
+                               val->value.ports[j].flags = 0;
+
+                       j++;
+               }
+       }
+
+       val->len = j;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vlan_ports(struct switch_dev *dev,
+               struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, mode, pno, vno;
+
+       vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       state->vlans[vno].mask = 0;
+       state->vlans[vno].port_mode = 0;
+       state->vlans[vno].port_sstate = 0;
+
+       if(state->vlans[vno].vid == 0)
+               state->vlans[vno].vid = vno;
+
+       for (i = 0; i < val->len; i++) {
+               pno = val->value.ports[i].id;
+
+               state->vlans[vno].mask |= (1 << pno);
+               if (val->value.ports[i].flags &
+                               (1 << SWITCH_PORT_FLAG_TAGGED))
+                       mode = MV_VTUCTL_EGRESS_TAGGED;
+               else
+                       mode = MV_VTUCTL_EGRESS_UNTAGGED;
+
+               state->vlans[vno].port_mode |= mode << (pno * 4);
+               state->vlans[vno].port_sstate |=
+                       MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2);
+       }
+
+       /*
+        * DISCARD is nonzero, so it must be explicitly
+        * set on ports not in the VLAN.
+        */
+       for (i = 0; i < dev->ports; i++)
+               if (!(state->vlans[vno].mask & (1 << i)))
+                       state->vlans[vno].port_mode |=
+                               MV_VTUCTL_DISCARD << (i * 4);
+
+       return 0;
+}
+
+static int mvsw61xx_get_vlan_port_based(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       if (state->vlans[vno].port_based)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vlan_port_based(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       if (val->value.i == 1)
+               state->vlans[vno].port_based = true;
+       else
+               state->vlans[vno].port_based = false;
+
+       return 0;
+}
+
+static int mvsw61xx_get_vid(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       val->value.i = state->vlans[vno].vid;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vid(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       state->vlans[vno].vid = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_get_enable_vlan(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->vlan_enabled;
+
+       return 0;
+}
+
+static int mvsw61xx_set_enable_vlan(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->vlan_enabled = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_vtu_program(struct switch_dev *dev)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       u16 v1, v2, s1, s2;
+       int i;
+
+       /* Flush */
+       mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                       MV_VTUOP_INPROGRESS, 0);
+       sw16(dev, MV_GLOBALREG(VTU_OP),
+                       MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE);
+
+       /* Write VLAN table */
+       for (i = 1; i < dev->vlans; i++) {
+               if (state->vlans[i].mask == 0 ||
+                               state->vlans[i].vid == 0 ||
+                               state->vlans[i].port_based == true)
+                       continue;
+
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+
+               /* Write per-VLAN port state into STU */
+               s1 = (u16) (state->vlans[i].port_sstate & 0xffff);
+               s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff);
+
+               sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID);
+               sw16(dev, MV_GLOBALREG(VTU_SID), i);
+               sw16(dev, MV_GLOBALREG(VTU_DATA1), s1);
+               sw16(dev, MV_GLOBALREG(VTU_DATA2), s2);
+               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
+
+               sw16(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD);
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+
+               /* Write VLAN information into VTU */
+               v1 = (u16) (state->vlans[i].port_mode & 0xffff);
+               v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff);
+
+               sw16(dev, MV_GLOBALREG(VTU_VID),
+                               MV_VTU_VID_VALID | state->vlans[i].vid);
+               sw16(dev, MV_GLOBALREG(VTU_SID), i);
+               sw16(dev, MV_GLOBALREG(VTU_FID), i);
+               sw16(dev, MV_GLOBALREG(VTU_DATA1), v1);
+               sw16(dev, MV_GLOBALREG(VTU_DATA2), v2);
+               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
+
+               sw16(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD);
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+       }
+
+       return 0;
+}
+
+static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, mode;
+
+       for (i = 0; i < dev->ports; i++) {
+               if (!(state->vlans[vno].mask & (1 << i)))
+                       continue;
+
+               mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
+
+               if(mode != MV_VTUCTL_EGRESS_TAGGED)
+                       state->ports[i].pvid = state->vlans[vno].vid;
+
+               if (state->vlans[vno].port_based) {
+                       state->ports[i].mask |= state->vlans[vno].mask;
+                       state->ports[i].fdb = vno;
+               }
+               else
+                       state->ports[i].qmode = MV_8021Q_MODE_SECURE;
+       }
+}
+
+static int mvsw61xx_update_state(struct switch_dev *dev)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i;
+       u16 reg;
+
+       if (!state->registered)
+               return -EINVAL;
+
+       /*
+        * Set 802.1q-only mode if vlan_enabled is true.
+        *
+        * Without this, even if 802.1q is enabled for
+        * a port/VLAN, it still depends on the port-based
+        * VLAN mask being set.
+        *
+        * With this setting, port-based VLANs are still
+        * functional, provided the VID is not in the VTU.
+        */
+       reg = sr16(dev, MV_GLOBAL2REG(SDET_POLARITY));
+
+       if (state->vlan_enabled)
+               reg |= MV_8021Q_VLAN_ONLY;
+       else
+               reg &= ~MV_8021Q_VLAN_ONLY;
+
+       sw16(dev, MV_GLOBAL2REG(SDET_POLARITY), reg);
+
+       /*
+        * Set port-based VLAN masks on each port
+        * based only on VLAN definitions known to
+        * the driver (i.e. in state).
+        *
+        * This means any pre-existing port mapping is
+        * wiped out once our driver is initialized.
+        */
+       for (i = 0; i < dev->ports; i++) {
+               state->ports[i].mask = 0;
+               state->ports[i].qmode = MV_8021Q_MODE_DISABLE;
+       }
+
+       for (i = 0; i < dev->vlans; i++)
+               mvsw61xx_vlan_port_config(dev, i);
+
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(VLANID, i)) & ~MV_PVID_MASK;
+               reg |= state->ports[i].pvid;
+               sw16(dev, MV_PORTREG(VLANID, i), reg);
+
+               state->ports[i].mask &= ~(1 << i);
+
+               /* set default forwarding DB number and port mask */
+               reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK;
+               reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) &
+                       MV_FDB_HI_MASK;
+               sw16(dev, MV_PORTREG(CONTROL1, i), reg);
+
+               reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) |
+                       state->ports[i].mask;
+               sw16(dev, MV_PORTREG(VLANMAP, i), reg);
+
+               reg = sr16(dev, MV_PORTREG(CONTROL2, i)) &
+                       ~MV_8021Q_MODE_MASK;
+               reg |= state->ports[i].qmode << MV_8021Q_MODE_SHIFT;
+               sw16(dev, MV_PORTREG(CONTROL2, i), reg);
+       }
+
+       mvsw61xx_vtu_program(dev);
+
+       return 0;
+}
+
+static int mvsw61xx_apply(struct switch_dev *dev)
+{
+       return mvsw61xx_update_state(dev);
+}
+
+static void mvsw61xx_enable_serdes(struct switch_dev *dev)
+{
+       int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES,
+                                          MV_PAGE_FIBER_SERDES, MII_BMCR);
+       if (bmcr < 0)
+               return;
+
+       if (bmcr & BMCR_PDOWN)
+               mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES,
+                                        MV_PAGE_FIBER_SERDES, MII_BMCR,
+                                        bmcr & ~BMCR_PDOWN);
+}
+
+static int _mvsw61xx_reset(struct switch_dev *dev, bool full)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i;
+       u16 reg;
+
+       /* Disable all ports before reset */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL, i)) &
+                       ~MV_PORTCTRL_FORWARDING;
+               sw16(dev, MV_PORTREG(CONTROL, i), reg);
+       }
+
+       reg = sr16(dev, MV_GLOBALREG(CONTROL)) | MV_CONTROL_RESET;
+
+       sw16(dev, MV_GLOBALREG(CONTROL), reg);
+       if (mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(CONTROL),
+                               MV_CONTROL_RESET, 0) < 0)
+               return -ETIMEDOUT;
+
+       for (i = 0; i < dev->ports; i++) {
+               state->ports[i].fdb = 0;
+               state->ports[i].qmode = 0;
+               state->ports[i].mask = 0;
+               state->ports[i].pvid = 0;
+
+               /* Force flow control off */
+               reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK;
+               reg |= MV_PHYCTL_FC_DISABLE;
+               sw16(dev, MV_PORTREG(PHYCTL, i), reg);
+
+               /* Set port association vector */
+               sw16(dev, MV_PORTREG(ASSOC, i), (1 << i));
+
+               /* power up phys */
+               if (full && i < 5) {
+                       mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL,
+                                           MV_SPEC_MDI_CROSS_AUTO |
+                                           MV_SPEC_ENERGY_DETECT |
+                                           MV_SPEC_DOWNSHIFT_COUNTER);
+                       mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET |
+                                           BMCR_ANENABLE | BMCR_FULLDPLX |
+                                           BMCR_SPEED1000);
+               }
+
+               /* enable SerDes if necessary */
+               if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) {
+                       u16 sts = sr16(dev, MV_PORTREG(STATUS, i));
+                       u16 mode = sts & MV_PORT_STATUS_CMODE_MASK;
+
+                       if (mode == MV_PORT_STATUS_CMODE_100BASE_X ||
+                           mode == MV_PORT_STATUS_CMODE_1000BASE_X ||
+                           mode == MV_PORT_STATUS_CMODE_SGMII) {
+                               mvsw61xx_enable_serdes(dev);
+                       }
+               }
+       }
+
+       for (i = 0; i < dev->vlans; i++) {
+               state->vlans[i].port_based = false;
+               state->vlans[i].mask = 0;
+               state->vlans[i].vid = 0;
+               state->vlans[i].port_mode = 0;
+               state->vlans[i].port_sstate = 0;
+       }
+
+       state->vlan_enabled = 0;
+
+       mvsw61xx_update_state(dev);
+
+       /* Re-enable ports */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL, i)) |
+                       MV_PORTCTRL_FORWARDING;
+               sw16(dev, MV_PORTREG(CONTROL, i), reg);
+       }
+
+       return 0;
+}
+
+static int mvsw61xx_reset(struct switch_dev *dev)
+{
+       return _mvsw61xx_reset(dev, false);
+}
+
+enum {
+       MVSW61XX_ENABLE_VLAN,
+};
+
+enum {
+       MVSW61XX_VLAN_PORT_BASED,
+       MVSW61XX_VLAN_ID,
+};
+
+enum {
+       MVSW61XX_PORT_MASK,
+       MVSW61XX_PORT_QMODE,
+};
+
+static const struct switch_attr mvsw61xx_global[] = {
+       [MVSW61XX_ENABLE_VLAN] = {
+               .id = MVSW61XX_ENABLE_VLAN,
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable 802.1q VLAN support",
+               .get = mvsw61xx_get_enable_vlan,
+               .set = mvsw61xx_set_enable_vlan,
+       },
+};
+
+static const struct switch_attr mvsw61xx_vlan[] = {
+       [MVSW61XX_VLAN_PORT_BASED] = {
+               .id = MVSW61XX_VLAN_PORT_BASED,
+               .type = SWITCH_TYPE_INT,
+               .name = "port_based",
+               .description = "Use port-based (non-802.1q) VLAN only",
+               .get = mvsw61xx_get_vlan_port_based,
+               .set = mvsw61xx_set_vlan_port_based,
+       },
+       [MVSW61XX_VLAN_ID] = {
+               .id = MVSW61XX_VLAN_ID,
+               .type = SWITCH_TYPE_INT,
+               .name = "vid",
+               .description = "Get/set VLAN ID",
+               .get = mvsw61xx_get_vid,
+               .set = mvsw61xx_set_vid,
+       },
+};
+
+static const struct switch_attr mvsw61xx_port[] = {
+       [MVSW61XX_PORT_MASK] = {
+               .id = MVSW61XX_PORT_MASK,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Port-based VLAN mask",
+               .name = "mask",
+               .get = mvsw61xx_get_port_mask,
+               .set = NULL,
+       },
+       [MVSW61XX_PORT_QMODE] = {
+               .id = MVSW61XX_PORT_QMODE,
+               .type = SWITCH_TYPE_INT,
+               .description = "802.1q mode: 0=off/1=fallback/2=check/3=secure",
+               .name = "qmode",
+               .get = mvsw61xx_get_port_qmode,
+               .set = mvsw61xx_set_port_qmode,
+       },
+};
+
+static const struct switch_dev_ops mvsw61xx_ops = {
+       .attr_global = {
+               .attr = mvsw61xx_global,
+               .n_attr = ARRAY_SIZE(mvsw61xx_global),
+       },
+       .attr_vlan = {
+               .attr = mvsw61xx_vlan,
+               .n_attr = ARRAY_SIZE(mvsw61xx_vlan),
+       },
+       .attr_port = {
+               .attr = mvsw61xx_port,
+               .n_attr = ARRAY_SIZE(mvsw61xx_port),
+       },
+       .get_port_link = mvsw61xx_get_port_link,
+       .get_port_pvid = mvsw61xx_get_port_pvid,
+       .set_port_pvid = mvsw61xx_set_port_pvid,
+       .get_vlan_ports = mvsw61xx_get_vlan_ports,
+       .set_vlan_ports = mvsw61xx_set_vlan_ports,
+       .apply_config = mvsw61xx_apply,
+       .reset_switch = mvsw61xx_reset,
+};
+
+/* end swconfig stuff */
+
+static int mvsw61xx_probe(struct platform_device *pdev)
+{
+       struct mvsw61xx_state *state;
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *mdio;
+       char *model_str;
+       u32 val;
+       int err;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       mdio = of_parse_phandle(np, "mii-bus", 0);
+       if (!mdio) {
+               dev_err(&pdev->dev, "Couldn't get MII bus handle\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->bus = of_mdio_find_bus(mdio);
+       if (!state->bus) {
+               dev_err(&pdev->dev, "Couldn't find MII bus from handle\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->is_indirect = of_property_read_bool(np, "is-indirect");
+
+       if (state->is_indirect) {
+               if (of_property_read_u32(np, "reg", &val)) {
+                       dev_err(&pdev->dev, "Switch address not specified\n");
+                       err = -ENODEV;
+                       goto out_err;
+               }
+
+               state->base_addr = val;
+       } else {
+               state->base_addr = MV_BASE;
+       }
+
+       state->model = r16(state->bus, state->is_indirect, state->base_addr,
+                               MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
+
+       switch(state->model) {
+       case MV_IDENT_VALUE_6171:
+               model_str = MV_IDENT_STR_6171;
+               break;
+       case MV_IDENT_VALUE_6172:
+               model_str = MV_IDENT_STR_6172;
+               break;
+       case MV_IDENT_VALUE_6176:
+               model_str = MV_IDENT_STR_6176;
+               break;
+       case MV_IDENT_VALUE_6352:
+               model_str = MV_IDENT_STR_6352;
+               break;
+       default:
+               dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n",
+                               state->base_addr);
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       platform_set_drvdata(pdev, state);
+       dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str,
+                       state->bus->id, state->base_addr);
+
+       dev_info(&pdev->dev, "Using %sdirect addressing\n",
+                       (state->is_indirect ? "in" : ""));
+
+       if (of_property_read_u32(np, "cpu-port-0", &val)) {
+               dev_err(&pdev->dev, "CPU port not set\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->cpu_port0 = val;
+
+       if (!of_property_read_u32(np, "cpu-port-1", &val))
+               state->cpu_port1 = val;
+       else
+               state->cpu_port1 = -1;
+
+       state->dev.vlans = MV_VLANS;
+       state->dev.cpu_port = state->cpu_port0;
+       state->dev.ports = MV_PORTS;
+       state->dev.name = model_str;
+       state->dev.ops = &mvsw61xx_ops;
+       state->dev.alias = dev_name(&pdev->dev);
+
+       _mvsw61xx_reset(&state->dev, true);
+
+       err = register_switch(&state->dev, NULL);
+       if (err < 0)
+               goto out_err;
+
+       state->registered = true;
+
+       return 0;
+out_err:
+       kfree(state);
+       return err;
+}
+
+static int
+mvsw61xx_remove(struct platform_device *pdev)
+{
+       struct mvsw61xx_state *state = platform_get_drvdata(pdev);
+
+       if (state->registered)
+               unregister_switch(&state->dev);
+
+       kfree(state);
+
+       return 0;
+}
+
+static const struct of_device_id mvsw61xx_match[] = {
+       { .compatible = "marvell,88e6171" },
+       { .compatible = "marvell,88e6172" },
+       { .compatible = "marvell,88e6176" },
+       { .compatible = "marvell,88e6352" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mvsw61xx_match);
+
+static struct platform_driver mvsw61xx_driver = {
+       .probe = mvsw61xx_probe,
+       .remove = mvsw61xx_remove,
+       .driver = {
+               .name = "mvsw61xx",
+               .of_match_table = of_match_ptr(mvsw61xx_match),
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init mvsw61xx_module_init(void)
+{
+       return platform_driver_register(&mvsw61xx_driver);
+}
+late_initcall(mvsw61xx_module_init);
+
+static void __exit mvsw61xx_module_exit(void)
+{
+       platform_driver_unregister(&mvsw61xx_driver);
+}
+module_exit(mvsw61xx_module_exit);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/mvsw61xx.h b/target/linux/generic/files-4.14/drivers/net/phy/mvsw61xx.h
new file mode 100644 (file)
index 0000000..a07b09c
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Marvell 88E61xx switch driver
+ *
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
+ *
+ * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#ifndef __MVSW61XX_H
+#define __MVSW61XX_H
+
+#define MV_PORTS                       7
+#define MV_PORTS_MASK                  ((1 << MV_PORTS) - 1)
+
+#define MV_BASE                                0x10
+
+#define MV_SWITCHPORT_BASE             0x10
+#define MV_SWITCHPORT(_n)              (MV_SWITCHPORT_BASE + (_n))
+#define MV_SWITCHREGS                  (MV_BASE + 0xb)
+
+#define MV_VLANS                       64
+
+enum {
+       MV_PORT_STATUS                  = 0x00,
+       MV_PORT_PHYCTL                  = 0x01,
+       MV_PORT_JAMCTL                  = 0x02,
+       MV_PORT_IDENT                   = 0x03,
+       MV_PORT_CONTROL                 = 0x04,
+       MV_PORT_CONTROL1                = 0x05,
+       MV_PORT_VLANMAP                 = 0x06,
+       MV_PORT_VLANID                  = 0x07,
+       MV_PORT_CONTROL2                = 0x08,
+       MV_PORT_ASSOC                   = 0x0b,
+       MV_PORT_RX_DISCARD_LOW          = 0x10,
+       MV_PORT_RX_DISCARD_HIGH         = 0x11,
+       MV_PORT_IN_FILTERED             = 0x12,
+       MV_PORT_OUT_ACCEPTED            = 0x13,
+};
+#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
+
+enum {
+       MV_PORT_STATUS_FDX              = (1 << 10),
+       MV_PORT_STATUS_LINK             = (1 << 11),
+};
+
+enum {
+       MV_PORT_STATUS_CMODE_100BASE_X  = 0x8,
+       MV_PORT_STATUS_CMODE_1000BASE_X = 0x9,
+       MV_PORT_STATUS_CMODE_SGMII      = 0xa,
+};
+
+#define MV_PORT_STATUS_CMODE_MASK      0xf
+
+enum {
+       MV_PORT_STATUS_SPEED_10         = 0x00,
+       MV_PORT_STATUS_SPEED_100        = 0x01,
+       MV_PORT_STATUS_SPEED_1000       = 0x02,
+};
+#define MV_PORT_STATUS_SPEED_SHIFT     8
+#define MV_PORT_STATUS_SPEED_MASK      (3 << 8)
+
+enum {
+       MV_PORTCTRL_DISABLED            = (0 << 0),
+       MV_PORTCTRL_BLOCKING            = (1 << 0),
+       MV_PORTCTRL_LEARNING            = (2 << 0),
+       MV_PORTCTRL_FORWARDING          = (3 << 0),
+       MV_PORTCTRL_VLANTUN             = (1 << 7),
+       MV_PORTCTRL_EGRESS              = (1 << 12),
+};
+
+#define MV_PHYCTL_FC_MASK              (3 << 6)
+
+enum {
+       MV_PHYCTL_FC_ENABLE             = (3 << 6),
+       MV_PHYCTL_FC_DISABLE            = (1 << 6),
+};
+
+enum {
+       MV_8021Q_EGRESS_UNMODIFIED      = 0x00,
+       MV_8021Q_EGRESS_UNTAGGED        = 0x01,
+       MV_8021Q_EGRESS_TAGGED          = 0x02,
+       MV_8021Q_EGRESS_ADDTAG          = 0x03,
+};
+
+#define MV_8021Q_MODE_SHIFT            10
+#define MV_8021Q_MODE_MASK             (0x3 << MV_8021Q_MODE_SHIFT)
+
+enum {
+       MV_8021Q_MODE_DISABLE           = 0x00,
+       MV_8021Q_MODE_FALLBACK          = 0x01,
+       MV_8021Q_MODE_CHECK             = 0x02,
+       MV_8021Q_MODE_SECURE            = 0x03,
+};
+
+enum {
+       MV_8021Q_VLAN_ONLY              = (1 << 15),
+};
+
+#define MV_PORTASSOC_MONITOR           (1 << 15)
+
+enum {
+       MV_SWITCH_ATU_FID0              = 0x01,
+       MV_SWITCH_ATU_FID1              = 0x02,
+       MV_SWITCH_ATU_SID               = 0x03,
+       MV_SWITCH_CTRL                  = 0x04,
+       MV_SWITCH_ATU_CTRL              = 0x0a,
+       MV_SWITCH_ATU_OP                = 0x0b,
+       MV_SWITCH_ATU_DATA              = 0x0c,
+       MV_SWITCH_ATU_MAC0              = 0x0d,
+       MV_SWITCH_ATU_MAC1              = 0x0e,
+       MV_SWITCH_ATU_MAC2              = 0x0f,
+       MV_SWITCH_GLOBAL                = 0x1b,
+       MV_SWITCH_GLOBAL2               = 0x1c,
+};
+#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
+
+enum {
+       MV_SWITCHCTL_EEIE               = (1 << 0),
+       MV_SWITCHCTL_PHYIE              = (1 << 1),
+       MV_SWITCHCTL_ATUDONE            = (1 << 2),
+       MV_SWITCHCTL_ATUIE              = (1 << 3),
+       MV_SWITCHCTL_CTRMODE            = (1 << 8),
+       MV_SWITCHCTL_RELOAD             = (1 << 9),
+       MV_SWITCHCTL_MSIZE              = (1 << 10),
+       MV_SWITCHCTL_DROP               = (1 << 13),
+};
+
+enum {
+#define MV_ATUCTL_AGETIME_MIN          16
+#define MV_ATUCTL_AGETIME_MAX          4080
+#define MV_ATUCTL_AGETIME(_n)          ((((_n) / 16) & 0xff) << 4)
+       MV_ATUCTL_ATU_256               = (0 << 12),
+       MV_ATUCTL_ATU_512               = (1 << 12),
+       MV_ATUCTL_ATU_1K                = (2 << 12),
+       MV_ATUCTL_ATUMASK               = (3 << 12),
+       MV_ATUCTL_NO_LEARN              = (1 << 14),
+       MV_ATUCTL_RESET                 = (1 << 15),
+};
+
+enum {
+#define MV_ATUOP_DBNUM(_n)             ((_n) & 0x0f)
+       MV_ATUOP_NOOP                   = (0 << 12),
+       MV_ATUOP_FLUSH_ALL              = (1 << 12),
+       MV_ATUOP_FLUSH_U                = (2 << 12),
+       MV_ATUOP_LOAD_DB                = (3 << 12),
+       MV_ATUOP_GET_NEXT               = (4 << 12),
+       MV_ATUOP_FLUSH_DB               = (5 << 12),
+       MV_ATUOP_FLUSH_DB_UU            = (6 << 12),
+       MV_ATUOP_INPROGRESS             = (1 << 15),
+};
+
+enum {
+       MV_GLOBAL_STATUS                = 0x00,
+       MV_GLOBAL_ATU_FID               = 0x01,
+       MV_GLOBAL_VTU_FID               = 0x02,
+       MV_GLOBAL_VTU_SID               = 0x03,
+       MV_GLOBAL_CONTROL               = 0x04,
+       MV_GLOBAL_VTU_OP                = 0x05,
+       MV_GLOBAL_VTU_VID               = 0x06,
+       MV_GLOBAL_VTU_DATA1             = 0x07,
+       MV_GLOBAL_VTU_DATA2             = 0x08,
+       MV_GLOBAL_VTU_DATA3             = 0x09,
+       MV_GLOBAL_CONTROL2              = 0x1c,
+};
+#define MV_GLOBALREG(_type) MV_SWITCH_GLOBAL, MV_GLOBAL_##_type
+
+enum {
+       MV_GLOBAL2_SMI_OP               = 0x18,
+       MV_GLOBAL2_SMI_DATA             = 0x19,
+       MV_GLOBAL2_SDET_POLARITY        = 0x1d,
+};
+#define MV_GLOBAL2REG(_type) MV_SWITCH_GLOBAL2, MV_GLOBAL2_##_type
+
+enum {
+       MV_VTU_VID_VALID                = (1 << 12),
+};
+
+enum {
+       MV_VTUOP_PURGE                  = (1 << 12),
+       MV_VTUOP_LOAD                   = (3 << 12),
+       MV_VTUOP_INPROGRESS             = (1 << 15),
+       MV_VTUOP_STULOAD                = (5 << 12),
+       MV_VTUOP_VTU_GET_NEXT           = (4 << 12),
+       MV_VTUOP_STU_GET_NEXT           = (6 << 12),
+       MV_VTUOP_GET_VIOLATION          = (7 << 12),
+};
+
+enum {
+       MV_CONTROL_RESET                = (1 << 15),
+       MV_CONTROL_PPU_ENABLE           = (1 << 14),
+};
+
+enum {
+       MV_VTUCTL_EGRESS_UNMODIFIED     = (0 << 0),
+       MV_VTUCTL_EGRESS_UNTAGGED       = (1 << 0),
+       MV_VTUCTL_EGRESS_TAGGED         = (2 << 0),
+       MV_VTUCTL_DISCARD               = (3 << 0),
+};
+
+enum {
+       MV_STUCTL_STATE_DISABLED        = (0 << 0),
+       MV_STUCTL_STATE_BLOCKING        = (1 << 0),
+       MV_STUCTL_STATE_LEARNING        = (2 << 0),
+       MV_STUCTL_STATE_FORWARDING      = (3 << 0),
+};
+
+enum {
+       MV_INDIRECT_REG_CMD             = 0,
+       MV_INDIRECT_REG_DATA            = 1,
+};
+
+enum {
+       MV_INDIRECT_INPROGRESS          = 0x8000,
+       MV_INDIRECT_WRITE               = 0x9400,
+       MV_INDIRECT_READ                = 0x9800,
+};
+#define MV_INDIRECT_ADDR_S             5
+
+#define MV_IDENT_MASK                  0xfff0
+
+#define MV_IDENT_VALUE_6171            0x1710
+#define MV_IDENT_STR_6171              "MV88E6171"
+
+#define MV_IDENT_VALUE_6172            0x1720
+#define MV_IDENT_STR_6172              "MV88E6172"
+
+#define MV_IDENT_VALUE_6176            0x1760
+#define MV_IDENT_STR_6176              "MV88E6176"
+
+#define MV_IDENT_VALUE_6352            0x3520
+#define MV_IDENT_STR_6352              "MV88E6352"
+
+#define MV_PVID_MASK                   0x0fff
+
+#define MV_FDB_HI_MASK                 0x00ff
+#define MV_FDB_LO_MASK                 0xf000
+#define MV_FDB_HI_SHIFT                        4
+#define MV_FDB_LO_SHIFT                        12
+
+/* Marvell Specific PHY register */
+#define MII_MV_SPEC_CTRL               16
+enum {
+       MV_SPEC_MDI_CROSS_AUTO          = (0x6 << 4),
+       MV_SPEC_ENERGY_DETECT           = (0x3 << 8),
+       MV_SPEC_DOWNSHIFT_COUNTER       = (0x3 << 12),
+};
+
+#define MII_MV_PAGE                    22
+
+#define MV_REG_FIBER_SERDES            0xf
+#define MV_PAGE_FIBER_SERDES           0x1
+
+struct mvsw61xx_state {
+       struct switch_dev dev;
+       struct mii_bus *bus;
+       int base_addr;
+       u16 model;
+
+       bool registered;
+       bool is_indirect;
+
+       int cpu_port0;
+       int cpu_port1;
+
+       int vlan_enabled;
+       struct port_state {
+               u16 fdb;
+               u16 pvid;
+               u16 mask;
+               u8 qmode;
+       } ports[MV_PORTS];
+
+       struct vlan_state {
+               bool port_based;
+
+               u16 mask;
+               u16 vid;
+               u32 port_mode;
+               u32 port_sstate;
+       } vlans[MV_VLANS];
+
+       char buf[128];
+};
+
+#define get_state(_dev) container_of((_dev), struct mvsw61xx_state, dev)
+
+#endif
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/mvswitch.c b/target/linux/generic/files-4.14/drivers/net/phy/mvswitch.c
new file mode 100644 (file)
index 0000000..043978f
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Marvell 88E6060 switch driver
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/if_vlan.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "mvswitch.h"
+
+/* Undefine this to use trailer mode instead.
+ * I don't know if header mode works with all chips */
+#define HEADER_MODE    1
+
+MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
+MODULE_AUTHOR("Felix Fietkau");
+MODULE_LICENSE("GPL");
+
+#define MVSWITCH_MAGIC 0x88E6060
+
+struct mvswitch_priv {
+       netdev_features_t orig_features;
+       u8 vlans[16];
+};
+
+#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
+
+static inline u16
+r16(struct phy_device *phydev, int addr, int reg)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, addr, reg);
+}
+
+static inline void
+w16(struct phy_device *phydev, int addr, int reg, u16 val)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, addr, reg, val);
+}
+
+
+static struct sk_buff *
+mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct mvswitch_priv *priv;
+       char *buf = NULL;
+       u16 vid;
+
+       priv = dev->phy_ptr;
+       if (unlikely(!priv))
+               goto error;
+
+       if (unlikely(skb->len < 16))
+               goto error;
+
+#ifdef HEADER_MODE
+       if (__vlan_hwaccel_get_tag(skb, &vid))
+               goto error;
+
+       if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
+               if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
+                       goto error_expand;
+               if (skb->len < 62)
+                       skb->len = 62;
+       }
+       buf = skb_push(skb, MV_HEADER_SIZE);
+#else
+       if (__vlan_get_tag(skb, &vid))
+               goto error;
+
+       if (unlikely((vid > 15 || !priv->vlans[vid])))
+               goto error;
+
+       if (skb->len <= 64) {
+               if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
+                       goto error_expand;
+
+               buf = skb->data + 64;
+               skb->len = 64 + MV_TRAILER_SIZE;
+       } else {
+               if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
+                       if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
+                               goto error_expand;
+               }
+               buf = skb_put(skb, 4);
+       }
+
+       /* move the ethernet header 4 bytes forward, overwriting the vlan tag */
+       memmove(skb->data + 4, skb->data, 12);
+       skb->data += 4;
+       skb->len -= 4;
+       skb->mac_header += 4;
+#endif
+
+       if (!buf)
+               goto error;
+
+
+#ifdef HEADER_MODE
+       /* prepend the tag */
+       *((__be16 *) buf) = cpu_to_be16(
+               ((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
+               ((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
+       );
+#else
+       /* append the tag */
+       *((__be32 *) buf) = cpu_to_be32((
+               (MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
+               ((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
+       ));
+#endif
+
+       return skb;
+
+error_expand:
+       if (net_ratelimit())
+               printk("%s: failed to expand/update skb for the switch\n", dev->name);
+
+error:
+       /* any errors? drop the packet! */
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static void
+mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct mvswitch_priv *priv;
+       unsigned char *buf;
+       int vlan = -1;
+       int i;
+
+       priv = dev->phy_ptr;
+       if (WARN_ON_ONCE(!priv))
+               return;
+
+#ifdef HEADER_MODE
+       buf = skb->data;
+       skb_pull(skb, MV_HEADER_SIZE);
+#else
+       buf = skb->data + skb->len - MV_TRAILER_SIZE;
+       if (buf[0] != 0x80)
+               return;
+#endif
+
+       /* look for the vlan matching the incoming port */
+       for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
+               if ((1 << buf[1]) & priv->vlans[i])
+                       vlan = i;
+       }
+
+       if (vlan == -1)
+               return;
+
+       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
+}
+
+
+static int
+mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = r16(pdev, addr, reg) & mask;
+               if (r == val)
+                       return 0;
+       } while(--i > 0);
+       return -ETIMEDOUT;
+}
+
+static int
+mvswitch_config_init(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+       struct net_device *dev = pdev->attached_dev;
+       u8 vlmap = 0;
+       int i;
+
+       if (!dev)
+               return -EINVAL;
+
+       printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
+       pdev->supported = ADVERTISED_100baseT_Full;
+       pdev->advertising = ADVERTISED_100baseT_Full;
+       dev->phy_ptr = priv;
+       pdev->irq = PHY_POLL;
+#ifdef HEADER_MODE
+       dev->flags |= IFF_PROMISC;
+#endif
+
+       /* initialize default vlans */
+       for (i = 0; i < MV_PORTS; i++)
+               priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i);
+
+       /* before entering reset, disable all ports */
+       for (i = 0; i < MV_PORTS; i++)
+               w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
+
+       msleep(2); /* wait for the status change to settle in */
+
+       /* put the ATU in reset */
+       w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
+
+       i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
+       if (i < 0) {
+               printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
+               return i;
+       }
+
+       /* set the ATU flags */
+       w16(pdev, MV_SWITCHREG(ATU_CTRL),
+               MV_ATUCTL_NO_LEARN |
+               MV_ATUCTL_ATU_1K |
+               MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
+       );
+
+       /* initialize the cpu port */
+       w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
+#ifdef HEADER_MODE
+               MV_PORTCTRL_HEADER |
+#else
+               MV_PORTCTRL_RXTR |
+               MV_PORTCTRL_TXTR |
+#endif
+               MV_PORTCTRL_ENABLED
+       );
+       /* wait for the phy change to settle in */
+       msleep(2);
+       for (i = 0; i < MV_PORTS; i++) {
+               u8 pvid = 0;
+               int j;
+
+               vlmap = 0;
+
+               /* look for the matching vlan */
+               for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
+                       if (priv->vlans[j] & (1 << i)) {
+                               vlmap = priv->vlans[j];
+                               pvid = j;
+                       }
+               }
+               /* leave port unconfigured if it's not part of a vlan */
+               if (!vlmap)
+                       continue;
+
+               /* add the cpu port to the allowed destinations list */
+               vlmap |= (1 << MV_CPUPORT);
+
+               /* take port out of its own vlan destination map */
+               vlmap &= ~(1 << i);
+
+               /* apply vlan settings */
+               w16(pdev, MV_PORTREG(VLANMAP, i),
+                       MV_PORTVLAN_PORTS(vlmap) |
+                       MV_PORTVLAN_ID(i)
+               );
+
+               /* re-enable port */
+               w16(pdev, MV_PORTREG(CONTROL, i),
+                       MV_PORTCTRL_ENABLED
+               );
+       }
+
+       w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
+               MV_PORTVLAN_ID(MV_CPUPORT)
+       );
+
+       /* set the port association vector */
+       for (i = 0; i <= MV_PORTS; i++) {
+               w16(pdev, MV_PORTREG(ASSOC, i),
+                       MV_PORTASSOC_PORTS(1 << i)
+               );
+       }
+
+       /* init switch control */
+       w16(pdev, MV_SWITCHREG(CTRL),
+               MV_SWITCHCTL_MSIZE |
+               MV_SWITCHCTL_DROP
+       );
+
+       dev->eth_mangle_rx = mvswitch_mangle_rx;
+       dev->eth_mangle_tx = mvswitch_mangle_tx;
+       priv->orig_features = dev->features;
+
+#ifdef HEADER_MODE
+       dev->priv_flags |= IFF_NO_IP_ALIGN;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
+#else
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+#endif
+
+       return 0;
+}
+
+static int
+mvswitch_read_status(struct phy_device *pdev)
+{
+       pdev->speed = SPEED_100;
+       pdev->duplex = DUPLEX_FULL;
+       pdev->link = 1;
+
+       /* XXX ugly workaround: we can't force the switch
+        * to gracefully handle hosts moving from one port to another,
+        * so we have to regularly clear the ATU database */
+
+       /* wait for the ATU to become available */
+       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
+
+       /* flush the ATU */
+       w16(pdev, MV_SWITCHREG(ATU_OP),
+               MV_ATUOP_INPROGRESS |
+               MV_ATUOP_FLUSH_ALL
+       );
+
+       /* wait for operation to complete */
+       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
+
+       return 0;
+}
+
+static int
+mvswitch_aneg_done(struct phy_device *phydev)
+{
+       return 1;       /* Return any positive value */
+}
+
+static int
+mvswitch_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static void
+mvswitch_detach(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+       struct net_device *dev = pdev->attached_dev;
+
+       if (!dev)
+               return;
+
+       dev->phy_ptr = NULL;
+       dev->eth_mangle_rx = NULL;
+       dev->eth_mangle_tx = NULL;
+       dev->features = priv->orig_features;
+       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+}
+
+static void
+mvswitch_remove(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+
+       kfree(priv);
+}
+
+static int
+mvswitch_probe(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv;
+
+       priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       pdev->priv = priv;
+
+       return 0;
+}
+
+static int
+mvswitch_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       if (dev->mdio.addr != 0x10)
+               return 0;
+
+       reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
+       if (reg != MV_IDENT_VALUE)
+               return 0;
+
+       dev->phy_id = MVSWITCH_MAGIC;
+       return 0;
+}
+
+
+static struct phy_driver mvswitch_driver = {
+       .name           = "Marvell 88E6060",
+       .phy_id         = MVSWITCH_MAGIC,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = &mvswitch_probe,
+       .remove         = &mvswitch_remove,
+       .detach         = &mvswitch_detach,
+       .config_init    = &mvswitch_config_init,
+       .config_aneg    = &mvswitch_config_aneg,
+       .aneg_done      = &mvswitch_aneg_done,
+       .read_status    = &mvswitch_read_status,
+};
+
+static int __init
+mvswitch_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
+       return phy_driver_register(&mvswitch_driver, THIS_MODULE);
+}
+
+static void __exit
+mvswitch_exit(void)
+{
+       phy_driver_unregister(&mvswitch_driver);
+}
+
+module_init(mvswitch_init);
+module_exit(mvswitch_exit);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/mvswitch.h b/target/linux/generic/files-4.14/drivers/net/phy/mvswitch.h
new file mode 100644 (file)
index 0000000..ab2a1a1
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Marvell 88E6060 switch driver
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#ifndef __MVSWITCH_H
+#define __MVSWITCH_H
+
+#define MV_HEADER_SIZE 2
+#define MV_HEADER_PORTS_M      0x001f
+#define MV_HEADER_PORTS_S      0
+#define MV_HEADER_VLAN_M       0xf000
+#define MV_HEADER_VLAN_S       12
+
+#define MV_TRAILER_SIZE        4
+#define MV_TRAILER_PORTS_M     0x1f
+#define MV_TRAILER_PORTS_S     16
+#define MV_TRAILER_FLAGS_S     24
+#define MV_TRAILER_OVERRIDE    0x80
+
+
+#define MV_PORTS       5
+#define MV_WANPORT     4
+#define MV_CPUPORT     5
+
+#define MV_BASE                0x10
+
+#define MV_PHYPORT_BASE                (MV_BASE + 0x0)
+#define MV_PHYPORT(_n)         (MV_PHYPORT_BASE + (_n))
+#define MV_SWITCHPORT_BASE     (MV_BASE + 0x8)
+#define MV_SWITCHPORT(_n)      (MV_SWITCHPORT_BASE + (_n))
+#define MV_SWITCHREGS          (MV_BASE + 0xf)
+
+enum {
+       MV_PHY_CONTROL      = 0x00,
+       MV_PHY_STATUS       = 0x01,
+       MV_PHY_IDENT0       = 0x02,
+       MV_PHY_IDENT1       = 0x03,
+       MV_PHY_ANEG         = 0x04,
+       MV_PHY_LINK_ABILITY = 0x05,
+       MV_PHY_ANEG_EXPAND  = 0x06,
+       MV_PHY_XMIT_NEXTP   = 0x07,
+       MV_PHY_LINK_NEXTP   = 0x08,
+       MV_PHY_CONTROL1     = 0x10,
+       MV_PHY_STATUS1      = 0x11,
+       MV_PHY_INTR_EN      = 0x12,
+       MV_PHY_INTR_STATUS  = 0x13,
+       MV_PHY_INTR_PORT    = 0x14,
+       MV_PHY_RECV_COUNTER = 0x16,
+       MV_PHY_LED_PARALLEL = 0x16,
+       MV_PHY_LED_STREAM   = 0x17,
+       MV_PHY_LED_CTRL     = 0x18,
+       MV_PHY_LED_OVERRIDE = 0x19,
+       MV_PHY_VCT_CTRL     = 0x1a,
+       MV_PHY_VCT_STATUS   = 0x1b,
+       MV_PHY_CONTROL2     = 0x1e
+};
+#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
+
+enum {
+       MV_PORT_STATUS      = 0x00,
+       MV_PORT_IDENT       = 0x03,
+       MV_PORT_CONTROL     = 0x04,
+       MV_PORT_VLANMAP     = 0x06,
+       MV_PORT_ASSOC       = 0x0b,
+       MV_PORT_RXCOUNT     = 0x10,
+       MV_PORT_TXCOUNT     = 0x11,
+};
+#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
+
+enum {
+       MV_PORTCTRL_BLOCK   =  (1 << 0),
+       MV_PORTCTRL_LEARN   =  (2 << 0),
+       MV_PORTCTRL_ENABLED =  (3 << 0),
+       MV_PORTCTRL_VLANTUN =  (1 << 7),        /* Enforce VLANs on packets */
+       MV_PORTCTRL_RXTR    =  (1 << 8),        /* Enable Marvell packet trailer for ingress */
+       MV_PORTCTRL_HEADER      = (1 << 11),    /* Enable Marvell packet header mode for port */
+       MV_PORTCTRL_TXTR    = (1 << 14),        /* Enable Marvell packet trailer for egress */
+       MV_PORTCTRL_FORCEFL = (1 << 15),        /* force flow control */
+};
+
+#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
+#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
+
+#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
+#define MV_PORTASSOC_MONITOR   (1 << 15)
+
+enum {
+       MV_SWITCH_MAC0      = 0x01,
+       MV_SWITCH_MAC1      = 0x02,
+       MV_SWITCH_MAC2      = 0x03,
+       MV_SWITCH_CTRL      = 0x04,
+       MV_SWITCH_ATU_CTRL  = 0x0a,
+       MV_SWITCH_ATU_OP    = 0x0b,
+       MV_SWITCH_ATU_DATA  = 0x0c,
+       MV_SWITCH_ATU_MAC0  = 0x0d,
+       MV_SWITCH_ATU_MAC1  = 0x0e,
+       MV_SWITCH_ATU_MAC2  = 0x0f,
+};
+#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
+
+enum {
+       MV_SWITCHCTL_EEIE   =  (1 << 0),        /* EEPROM interrupt enable */
+       MV_SWITCHCTL_PHYIE  =  (1 << 1),        /* PHY interrupt enable */
+       MV_SWITCHCTL_ATUDONE=  (1 << 2),        /* ATU done interrupt enable */
+       MV_SWITCHCTL_ATUIE  =  (1 << 3),        /* ATU interrupt enable */
+       MV_SWITCHCTL_CTRMODE=  (1 << 8),        /* statistics for rx and tx errors */
+       MV_SWITCHCTL_RELOAD =  (1 << 9),        /* reload registers from eeprom */
+       MV_SWITCHCTL_MSIZE  = (1 << 10),        /* increase maximum frame size */
+       MV_SWITCHCTL_DROP   = (1 << 13),        /* discard frames with excessive collisions */
+};
+
+enum {
+#define MV_ATUCTL_AGETIME_MIN  16
+#define MV_ATUCTL_AGETIME_MAX  4080
+#define MV_ATUCTL_AGETIME(_n)  ((((_n) / 16) & 0xff) << 4)
+       MV_ATUCTL_ATU_256   = (0 << 12),
+       MV_ATUCTL_ATU_512   = (1 << 12),
+       MV_ATUCTL_ATU_1K        = (2 << 12),
+       MV_ATUCTL_ATUMASK   = (3 << 12),
+       MV_ATUCTL_NO_LEARN  = (1 << 14),
+       MV_ATUCTL_RESET     = (1 << 15),
+};
+
+enum {
+#define MV_ATUOP_DBNUM(_n)     ((_n) & 0x0f)
+
+       MV_ATUOP_NOOP       = (0 << 12),
+       MV_ATUOP_FLUSH_ALL  = (1 << 12),
+       MV_ATUOP_FLUSH_U    = (2 << 12),
+       MV_ATUOP_LOAD_DB    = (3 << 12),
+       MV_ATUOP_GET_NEXT   = (4 << 12),
+       MV_ATUOP_FLUSH_DB   = (5 << 12),
+       MV_ATUOP_FLUSH_DB_UU= (6 << 12),
+
+       MV_ATUOP_INPROGRESS = (1 << 15),
+};
+
+#define MV_IDENT_MASK          0xfff0
+#define MV_IDENT_VALUE         0x0600
+
+#endif
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/psb6970.c b/target/linux/generic/files-4.14/drivers/net/phy/psb6970.c
new file mode 100644 (file)
index 0000000..c1a381c
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Lantiq PSB6970 (Tantos) Switch driver
+ *
+ * Copyright (c) 2009,2010 Team Embedded.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation.
+ *
+ * The switch programming done in this driver follows the 
+ * "Ethernet Traffic Separation using VLAN" Application Note as
+ * published by Lantiq.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/switch.h>
+#include <linux/phy.h>
+
+#define PSB6970_MAX_VLANS              16
+#define PSB6970_NUM_PORTS              7
+#define PSB6970_DEFAULT_PORT_CPU       6
+#define PSB6970_IS_CPU_PORT(x)         ((x) > 4)
+
+#define PHYADDR(_reg)          ((_reg >> 5) & 0xff), (_reg & 0x1f)
+
+/* --- Identification --- */
+#define PSB6970_CI0            0x0100
+#define PSB6970_CI0_MASK       0x000f
+#define PSB6970_CI1            0x0101
+#define PSB6970_CI1_VAL                0x2599
+#define PSB6970_CI1_MASK       0xffff
+
+/* --- VLAN filter table --- */
+#define PSB6970_VFxL(i)                ((i)*2+0x10)    /* VLAN Filter Low */
+#define PSB6970_VFxL_VV                (1 << 15)       /* VLAN_Valid */
+
+#define PSB6970_VFxH(i)                ((i)*2+0x11)    /* VLAN Filter High */
+#define PSB6970_VFxH_TM_SHIFT  7               /* Tagged Member */
+
+/* --- Port registers --- */
+#define PSB6970_EC(p)          ((p)*0x20+2)    /* Extended Control */
+#define PSB6970_EC_IFNTE       (1 << 1)        /* Input Force No Tag Enable */
+
+#define PSB6970_PBVM(p)                ((p)*0x20+3)    /* Port Base VLAN Map */
+#define PSB6970_PBVM_VMCE      (1 << 8)
+#define PSB6970_PBVM_AOVTP     (1 << 9)
+#define PSB6970_PBVM_VSD       (1 << 10)
+#define PSB6970_PBVM_VC                (1 << 11)       /* VID Check with VID table */
+#define PSB6970_PBVM_TBVE      (1 << 13)       /* Tag-Based VLAN enable */
+
+#define PSB6970_DVID(p)                ((p)*0x20+4)    /* Default VLAN ID & Priority */
+
+struct psb6970_priv {
+       struct switch_dev dev;
+       struct phy_device *phy;
+       u16 (*read) (struct phy_device* phydev, int reg);
+       void (*write) (struct phy_device* phydev, int reg, u16 val);
+       struct mutex reg_mutex;
+
+       /* all fields below are cleared on reset */
+       bool vlan;
+       u16 vlan_id[PSB6970_MAX_VLANS];
+       u8 vlan_table[PSB6970_MAX_VLANS];
+       u8 vlan_tagged;
+       u16 pvid[PSB6970_NUM_PORTS];
+};
+
+#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
+
+static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, PHYADDR(reg));
+}
+
+static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, PHYADDR(reg), val);
+}
+
+static int
+psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       priv->vlan = !!val->value.i;
+       return 0;
+}
+
+static int
+psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       val->value.i = priv->vlan;
+       return 0;
+}
+
+static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+
+       /* make sure no invalid PVIDs get set */
+       if (vlan >= dev->vlans)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+       return 0;
+}
+
+static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       *vlan = priv->pvid[port];
+       return 0;
+}
+
+static int
+psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       priv->vlan_id[val->port_vlan] = val->value.i;
+       return 0;
+}
+
+static int
+psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       val->value.i = priv->vlan_id[val->port_vlan];
+       return 0;
+}
+
+static struct switch_attr psb6970_globals[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "enable_vlan",
+        .description = "Enable VLAN mode",
+        .set = psb6970_set_vlan,
+        .get = psb6970_get_vlan,
+        .max = 1},
+};
+
+static struct switch_attr psb6970_port[] = {
+};
+
+static struct switch_attr psb6970_vlan[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "vid",
+        .description = "VLAN ID (0-4094)",
+        .set = psb6970_set_vid,
+        .get = psb6970_get_vid,
+        .max = 4094,
+        },
+};
+
+static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (priv->vlan_tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i, j;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
+                       priv->vlan_tagged |= (1 << p->id);
+               else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+
+                       /* make sure that an untagged port does not
+                        * appear in other vlans */
+                       for (j = 0; j < PSB6970_MAX_VLANS; j++) {
+                               if (j == val->port_vlan)
+                                       continue;
+                               priv->vlan_table[j] &= ~(1 << p->id);
+                       }
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static int psb6970_hw_apply(struct switch_dev *dev)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       int i, j;
+
+       mutex_lock(&priv->reg_mutex);
+
+       if (priv->vlan) {
+               /* into the vlan translation unit */
+               for (j = 0; j < PSB6970_MAX_VLANS; j++) {
+                       u8 vp = priv->vlan_table[j];
+
+                       if (vp) {
+                               priv->write(priv->phy, PSB6970_VFxL(j),
+                                           PSB6970_VFxL_VV | priv->vlan_id[j]);
+                               priv->write(priv->phy, PSB6970_VFxH(j),
+                                           ((vp & priv->
+                                             vlan_tagged) <<
+                                            PSB6970_VFxH_TM_SHIFT) | vp);
+                       } else  /* clear VLAN Valid flag for unused vlans */
+                               priv->write(priv->phy, PSB6970_VFxL(j), 0);
+
+               }
+       }
+
+       /* update the port destination mask registers and tag settings */
+       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
+               int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
+
+               if (priv->vlan) {
+                       ec = PSB6970_EC_IFNTE;
+                       dvid = priv->vlan_id[priv->pvid[i]];
+                       pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
+
+                       if ((i << 1) & priv->vlan_tagged)
+                               pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
+               }
+
+               priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
+
+               if (!PSB6970_IS_CPU_PORT(i)) {
+                       priv->write(priv->phy, PSB6970_EC(i), ec);
+                       priv->write(priv->phy, PSB6970_DVID(i), dvid);
+               }
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+static int psb6970_reset_switch(struct switch_dev *dev)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       memset(&priv->vlan, 0, sizeof(struct psb6970_priv) -
+              offsetof(struct psb6970_priv, vlan));
+
+       for (i = 0; i < PSB6970_MAX_VLANS; i++)
+               priv->vlan_id[i] = i;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return psb6970_hw_apply(dev);
+}
+
+static const struct switch_dev_ops psb6970_ops = {
+       .attr_global = {
+                       .attr = psb6970_globals,
+                       .n_attr = ARRAY_SIZE(psb6970_globals),
+                       },
+       .attr_port = {
+                     .attr = psb6970_port,
+                     .n_attr = ARRAY_SIZE(psb6970_port),
+                     },
+       .attr_vlan = {
+                     .attr = psb6970_vlan,
+                     .n_attr = ARRAY_SIZE(psb6970_vlan),
+                     },
+       .get_port_pvid = psb6970_get_pvid,
+       .set_port_pvid = psb6970_set_pvid,
+       .get_vlan_ports = psb6970_get_ports,
+       .set_vlan_ports = psb6970_set_ports,
+       .apply_config = psb6970_hw_apply,
+       .reset_switch = psb6970_reset_switch,
+};
+
+static int psb6970_config_init(struct phy_device *pdev)
+{
+       struct psb6970_priv *priv;
+       struct net_device *dev = pdev->attached_dev;
+       struct switch_dev *swdev;
+       int ret;
+
+       priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       priv->phy = pdev;
+
+       if (pdev->mdio.addr == 0)
+               printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
+                      pdev->attached_dev->name);
+
+       if (pdev->mdio.addr != 0) {
+               kfree(priv);
+               return 0;
+       }
+
+       pdev->supported = pdev->advertising = SUPPORTED_100baseT_Full;
+
+       mutex_init(&priv->reg_mutex);
+       priv->read = psb6970_mii_read;
+       priv->write = psb6970_mii_write;
+
+       pdev->priv = priv;
+
+       swdev = &priv->dev;
+       swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
+       swdev->ops = &psb6970_ops;
+
+       swdev->name = "Lantiq PSB6970";
+       swdev->vlans = PSB6970_MAX_VLANS;
+       swdev->ports = PSB6970_NUM_PORTS;
+
+       if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
+               kfree(priv);
+               goto done;
+       }
+
+       ret = psb6970_reset_switch(&priv->dev);
+       if (ret) {
+               kfree(priv);
+               goto done;
+       }
+
+       dev->phy_ptr = priv;
+
+done:
+       return ret;
+}
+
+static int psb6970_read_status(struct phy_device *phydev)
+{
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+static int psb6970_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int psb6970_probe(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static void psb6970_remove(struct phy_device *pdev)
+{
+       struct psb6970_priv *priv = pdev->priv;
+
+       if (!priv)
+               return;
+
+       if (pdev->mdio.addr == 0)
+               unregister_switch(&priv->dev);
+       kfree(priv);
+}
+
+static int psb6970_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       /* look for the switch on the bus */
+       reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
+       if (reg != PSB6970_CI1_VAL)
+               return 0;
+
+       dev->phy_id = (reg << 16);
+       dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
+
+       return 0;
+}
+
+static struct phy_driver psb6970_driver = {
+       .name = "Lantiq PSB6970",
+       .phy_id = PSB6970_CI1_VAL << 16,
+       .phy_id_mask = 0xffff0000,
+       .features = PHY_BASIC_FEATURES,
+       .probe = psb6970_probe,
+       .remove = psb6970_remove,
+       .config_init = &psb6970_config_init,
+       .config_aneg = &psb6970_config_aneg,
+       .read_status = &psb6970_read_status,
+};
+
+int __init psb6970_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
+       return phy_driver_register(&psb6970_driver, THIS_MODULE);
+}
+
+module_init(psb6970_init);
+
+void __exit psb6970_exit(void)
+{
+       phy_driver_unregister(&psb6970_driver);
+}
+
+module_exit(psb6970_exit);
+
+MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
+MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8306.c b/target/linux/generic/files-4.14/drivers/net/phy/rtl8306.c
new file mode 100644 (file)
index 0000000..6d09c10
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * rtl8306.c: RTL8306S switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/version.h>
+
+//#define DEBUG 1
+
+/* Global (PHY0) */
+#define RTL8306_REG_PAGE               16
+#define RTL8306_REG_PAGE_LO            (1 << 15)
+#define RTL8306_REG_PAGE_HI            (1 << 1) /* inverted */
+
+#define RTL8306_NUM_VLANS              16
+#define RTL8306_NUM_PORTS              6
+#define RTL8306_PORT_CPU               5
+#define RTL8306_NUM_PAGES              4
+#define RTL8306_NUM_REGS               32
+
+#define RTL_NAME_S          "RTL8306S"
+#define RTL_NAME_SD         "RTL8306SD"
+#define RTL_NAME_SDM        "RTL8306SDM"
+#define RTL_NAME_UNKNOWN    "RTL8306(unknown)"
+
+#define RTL8306_MAGIC  0x8306
+
+static LIST_HEAD(phydevs);
+
+struct rtl_priv {
+       struct list_head list;
+       struct switch_dev dev;
+       int page;
+       int type;
+       int do_cpu;
+       struct mii_bus *bus;
+       char hwname[sizeof(RTL_NAME_UNKNOWN)];
+       bool fixup;
+};
+
+struct rtl_phyregs {
+       int nway;
+       int speed;
+       int duplex;
+};
+
+#define to_rtl(_dev) container_of(_dev, struct rtl_priv, dev)
+
+enum {
+       RTL_TYPE_S,
+       RTL_TYPE_SD,
+       RTL_TYPE_SDM,
+};
+
+struct rtl_reg {
+       int page;
+       int phy;
+       int reg;
+       int bits;
+       int shift;
+       int inverted;
+};
+
+#define RTL_VLAN_REGOFS(name) \
+       (RTL_REG_VLAN1_##name - RTL_REG_VLAN0_##name)
+
+#define RTL_PORT_REGOFS(name) \
+       (RTL_REG_PORT1_##name - RTL_REG_PORT0_##name)
+
+#define RTL_PORT_REG(id, reg) \
+       (RTL_REG_PORT0_##reg + (id * RTL_PORT_REGOFS(reg)))
+
+#define RTL_VLAN_REG(id, reg) \
+       (RTL_REG_VLAN0_##reg + (id * RTL_VLAN_REGOFS(reg)))
+
+#define RTL_GLOBAL_REGATTR(reg) \
+       .id = RTL_REG_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = 0, \
+       .set = rtl_attr_set_int, \
+       .get = rtl_attr_get_int
+
+#define RTL_PORT_REGATTR(reg) \
+       .id = RTL_REG_PORT0_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = RTL_PORT_REGOFS(reg), \
+       .set = rtl_attr_set_port_int, \
+       .get = rtl_attr_get_port_int
+
+#define RTL_VLAN_REGATTR(reg) \
+       .id = RTL_REG_VLAN0_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = RTL_VLAN_REGOFS(reg), \
+       .set = rtl_attr_set_vlan_int, \
+       .get = rtl_attr_get_vlan_int
+
+enum rtl_regidx {
+       RTL_REG_CHIPID,
+       RTL_REG_CHIPVER,
+       RTL_REG_CHIPTYPE,
+       RTL_REG_CPUPORT,
+
+       RTL_REG_EN_CPUPORT,
+       RTL_REG_EN_TAG_OUT,
+       RTL_REG_EN_TAG_CLR,
+       RTL_REG_EN_TAG_IN,
+       RTL_REG_TRAP_CPU,
+       RTL_REG_CPU_LINKUP,
+       RTL_REG_TRUNK_PORTSEL,
+       RTL_REG_EN_TRUNK,
+       RTL_REG_RESET,
+
+       RTL_REG_VLAN_ENABLE,
+       RTL_REG_VLAN_FILTER,
+       RTL_REG_VLAN_TAG_ONLY,
+       RTL_REG_VLAN_TAG_AWARE,
+#define RTL_VLAN_ENUM(id) \
+       RTL_REG_VLAN##id##_VID, \
+       RTL_REG_VLAN##id##_PORTMASK
+       RTL_VLAN_ENUM(0),
+       RTL_VLAN_ENUM(1),
+       RTL_VLAN_ENUM(2),
+       RTL_VLAN_ENUM(3),
+       RTL_VLAN_ENUM(4),
+       RTL_VLAN_ENUM(5),
+       RTL_VLAN_ENUM(6),
+       RTL_VLAN_ENUM(7),
+       RTL_VLAN_ENUM(8),
+       RTL_VLAN_ENUM(9),
+       RTL_VLAN_ENUM(10),
+       RTL_VLAN_ENUM(11),
+       RTL_VLAN_ENUM(12),
+       RTL_VLAN_ENUM(13),
+       RTL_VLAN_ENUM(14),
+       RTL_VLAN_ENUM(15),
+#define RTL_PORT_ENUM(id) \
+       RTL_REG_PORT##id##_PVID, \
+       RTL_REG_PORT##id##_NULL_VID_REPLACE, \
+       RTL_REG_PORT##id##_NON_PVID_DISCARD, \
+       RTL_REG_PORT##id##_VID_INSERT, \
+       RTL_REG_PORT##id##_TAG_INSERT, \
+       RTL_REG_PORT##id##_LINK, \
+       RTL_REG_PORT##id##_SPEED, \
+       RTL_REG_PORT##id##_NWAY, \
+       RTL_REG_PORT##id##_NRESTART, \
+       RTL_REG_PORT##id##_DUPLEX, \
+       RTL_REG_PORT##id##_RXEN, \
+       RTL_REG_PORT##id##_TXEN
+       RTL_PORT_ENUM(0),
+       RTL_PORT_ENUM(1),
+       RTL_PORT_ENUM(2),
+       RTL_PORT_ENUM(3),
+       RTL_PORT_ENUM(4),
+       RTL_PORT_ENUM(5),
+};
+
+static const struct rtl_reg rtl_regs[] = {
+       [RTL_REG_CHIPID]         = { 0, 4, 30, 16,  0, 0 },
+       [RTL_REG_CHIPVER]        = { 0, 4, 31,  8,  0, 0 },
+       [RTL_REG_CHIPTYPE]       = { 0, 4, 31,  2,  8, 0 },
+
+       /* CPU port number */
+       [RTL_REG_CPUPORT]        = { 2, 4, 21,  3,  0, 0 },
+       /* Enable CPU port function */
+       [RTL_REG_EN_CPUPORT]     = { 3, 2, 21,  1, 15, 1 },
+       /* Enable CPU port tag insertion */
+       [RTL_REG_EN_TAG_OUT]     = { 3, 2, 21,  1, 12, 0 },
+       /* Enable CPU port tag removal */
+       [RTL_REG_EN_TAG_CLR]     = { 3, 2, 21,  1, 11, 0 },
+       /* Enable CPU port tag checking */
+       [RTL_REG_EN_TAG_IN]      = { 0, 4, 21,  1,  7, 0 },
+       [RTL_REG_EN_TRUNK]       = { 0, 0, 19,  1, 11, 1 },
+       [RTL_REG_TRUNK_PORTSEL]  = { 0, 0, 16,  1,  6, 1 },
+       [RTL_REG_RESET]          = { 0, 0, 16,  1, 12, 0 },
+
+       [RTL_REG_TRAP_CPU]       = { 3, 2, 22,  1,  6, 0 },
+       [RTL_REG_CPU_LINKUP]     = { 0, 6, 22,  1, 15, 0 },
+
+       [RTL_REG_VLAN_TAG_ONLY]  = { 0, 0, 16,  1,  8, 1 },
+       [RTL_REG_VLAN_FILTER]    = { 0, 0, 16,  1,  9, 1 },
+       [RTL_REG_VLAN_TAG_AWARE] = { 0, 0, 16,  1, 10, 1 },
+       [RTL_REG_VLAN_ENABLE]    = { 0, 0, 18,  1,  8, 1 },
+
+#define RTL_VLAN_REGS(id, phy, page, regofs) \
+       [RTL_REG_VLAN##id##_VID] = { page, phy, 25 + regofs, 12, 0, 0 }, \
+       [RTL_REG_VLAN##id##_PORTMASK] = { page, phy, 24 + regofs, 6, 0, 0 }
+       RTL_VLAN_REGS( 0, 0, 0, 0),
+       RTL_VLAN_REGS( 1, 1, 0, 0),
+       RTL_VLAN_REGS( 2, 2, 0, 0),
+       RTL_VLAN_REGS( 3, 3, 0, 0),
+       RTL_VLAN_REGS( 4, 4, 0, 0),
+       RTL_VLAN_REGS( 5, 0, 1, 2),
+       RTL_VLAN_REGS( 6, 1, 1, 2),
+       RTL_VLAN_REGS( 7, 2, 1, 2),
+       RTL_VLAN_REGS( 8, 3, 1, 2),
+       RTL_VLAN_REGS( 9, 4, 1, 2),
+       RTL_VLAN_REGS(10, 0, 1, 4),
+       RTL_VLAN_REGS(11, 1, 1, 4),
+       RTL_VLAN_REGS(12, 2, 1, 4),
+       RTL_VLAN_REGS(13, 3, 1, 4),
+       RTL_VLAN_REGS(14, 4, 1, 4),
+       RTL_VLAN_REGS(15, 0, 1, 6),
+
+#define REG_PORT_SETTING(port, phy) \
+       [RTL_REG_PORT##port##_SPEED] = { 0, phy, 0, 1, 13, 0 }, \
+       [RTL_REG_PORT##port##_NWAY] = { 0, phy, 0, 1, 12, 0 }, \
+       [RTL_REG_PORT##port##_NRESTART] = { 0, phy, 0, 1, 9, 0 }, \
+       [RTL_REG_PORT##port##_DUPLEX] = { 0, phy, 0, 1, 8, 0 }, \
+       [RTL_REG_PORT##port##_TXEN] = { 0, phy, 24, 1, 11, 0 }, \
+       [RTL_REG_PORT##port##_RXEN] = { 0, phy, 24, 1, 10, 0 }, \
+       [RTL_REG_PORT##port##_LINK] = { 0, phy, 1, 1, 2, 0 }, \
+       [RTL_REG_PORT##port##_NULL_VID_REPLACE] = { 0, phy, 22, 1, 12, 0 }, \
+       [RTL_REG_PORT##port##_NON_PVID_DISCARD] = { 0, phy, 22, 1, 11, 0 }, \
+       [RTL_REG_PORT##port##_VID_INSERT] = { 0, phy, 22, 2, 9, 0 }, \
+       [RTL_REG_PORT##port##_TAG_INSERT] = { 0, phy, 22, 2, 0, 0 }
+
+       REG_PORT_SETTING(0, 0),
+       REG_PORT_SETTING(1, 1),
+       REG_PORT_SETTING(2, 2),
+       REG_PORT_SETTING(3, 3),
+       REG_PORT_SETTING(4, 4),
+       REG_PORT_SETTING(5, 6),
+
+#define REG_PORT_PVID(phy, page, regofs) \
+       { page, phy, 24 + regofs, 4, 12, 0 }
+       [RTL_REG_PORT0_PVID] = REG_PORT_PVID(0, 0, 0),
+       [RTL_REG_PORT1_PVID] = REG_PORT_PVID(1, 0, 0),
+       [RTL_REG_PORT2_PVID] = REG_PORT_PVID(2, 0, 0),
+       [RTL_REG_PORT3_PVID] = REG_PORT_PVID(3, 0, 0),
+       [RTL_REG_PORT4_PVID] = REG_PORT_PVID(4, 0, 0),
+       [RTL_REG_PORT5_PVID] = REG_PORT_PVID(0, 1, 2),
+};
+
+
+static inline void
+rtl_set_page(struct rtl_priv *priv, unsigned int page)
+{
+       struct mii_bus *bus = priv->bus;
+       u16 pgsel;
+
+       if (priv->fixup)
+               return;
+
+       if (priv->page == page)
+               return;
+
+       BUG_ON(page > RTL8306_NUM_PAGES);
+       pgsel = bus->read(bus, 0, RTL8306_REG_PAGE);
+       pgsel &= ~(RTL8306_REG_PAGE_LO | RTL8306_REG_PAGE_HI);
+       if (page & (1 << 0))
+               pgsel |= RTL8306_REG_PAGE_LO;
+       if (!(page & (1 << 1))) /* bit is inverted */
+               pgsel |= RTL8306_REG_PAGE_HI;
+       bus->write(bus, 0, RTL8306_REG_PAGE, pgsel);
+}
+
+static inline int
+rtl_w16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+
+       rtl_set_page(priv, page);
+       bus->write(bus, phy, reg, val);
+       bus->read(bus, phy, reg); /* flush */
+       return 0;
+}
+
+static inline int
+rtl_r16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+
+       rtl_set_page(priv, page);
+       return bus->read(bus, phy, reg);
+}
+
+static inline u16
+rtl_rmw(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 mask, u16 val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+       u16 r;
+
+       rtl_set_page(priv, page);
+       r = bus->read(bus, phy, reg);
+       r &= ~mask;
+       r |= val;
+       bus->write(bus, phy, reg, r);
+       return bus->read(bus, phy, reg); /* flush */
+}
+
+
+static inline int
+rtl_get(struct switch_dev *dev, enum rtl_regidx s)
+{
+       const struct rtl_reg *r = &rtl_regs[s];
+       u16 val;
+
+       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
+       if (r->bits == 0) /* unimplemented */
+               return 0;
+
+       val = rtl_r16(dev, r->page, r->phy, r->reg);
+
+       if (r->shift > 0)
+               val >>= r->shift;
+
+       if (r->inverted)
+               val = ~val;
+
+       val &= (1 << r->bits) - 1;
+
+       return val;
+}
+
+static int
+rtl_set(struct switch_dev *dev, enum rtl_regidx s, unsigned int val)
+{
+       const struct rtl_reg *r = &rtl_regs[s];
+       u16 mask = 0xffff;
+
+       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
+
+       if (r->bits == 0) /* unimplemented */
+               return 0;
+
+       if (r->shift > 0)
+               val <<= r->shift;
+
+       if (r->inverted)
+               val = ~val;
+
+       if (r->bits != 16) {
+               mask = (1 << r->bits) - 1;
+               mask <<= r->shift;
+       }
+       val &= mask;
+       return rtl_rmw(dev, r->page, r->phy, r->reg, mask, val);
+}
+
+static void
+rtl_phy_save(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
+{
+       regs->nway = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+       regs->speed = rtl_get(dev, RTL_PORT_REG(port, SPEED));
+       regs->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+}
+
+static void
+rtl_phy_restore(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
+{
+       rtl_set(dev, RTL_PORT_REG(port, NWAY), regs->nway);
+       rtl_set(dev, RTL_PORT_REG(port, SPEED), regs->speed);
+       rtl_set(dev, RTL_PORT_REG(port, DUPLEX), regs->duplex);
+}
+
+static void
+rtl_port_set_enable(struct switch_dev *dev, int port, int enabled)
+{
+       rtl_set(dev, RTL_PORT_REG(port, RXEN), enabled);
+       rtl_set(dev, RTL_PORT_REG(port, TXEN), enabled);
+
+       if ((port >= 5) || !enabled)
+               return;
+
+       /* restart autonegotiation if enabled */
+       rtl_set(dev, RTL_PORT_REG(port, NRESTART), 1);
+}
+
+static int
+rtl_hw_apply(struct switch_dev *dev)
+{
+       int i;
+       int trunk_en, trunk_psel;
+       struct rtl_phyregs port5;
+
+       rtl_phy_save(dev, 5, &port5);
+
+       /* disable rx/tx from PHYs */
+       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
+               rtl_port_set_enable(dev, i, 0);
+       }
+
+       /* save trunking status */
+       trunk_en = rtl_get(dev, RTL_REG_EN_TRUNK);
+       trunk_psel = rtl_get(dev, RTL_REG_TRUNK_PORTSEL);
+
+       /* trunk port 3 and 4
+        * XXX: Big WTF, but RealTek seems to do it */
+       rtl_set(dev, RTL_REG_EN_TRUNK, 1);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 1);
+
+       /* execute the software reset */
+       rtl_set(dev, RTL_REG_RESET, 1);
+
+       /* wait for the reset to complete,
+        * but don't wait for too long */
+       for (i = 0; i < 10; i++) {
+               if (rtl_get(dev, RTL_REG_RESET) == 0)
+                       break;
+
+               msleep(1);
+       }
+
+       /* enable rx/tx from PHYs */
+       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
+               rtl_port_set_enable(dev, i, 1);
+       }
+
+       /* restore trunking settings */
+       rtl_set(dev, RTL_REG_EN_TRUNK, trunk_en);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, trunk_psel);
+       rtl_phy_restore(dev, 5, &port5);
+
+       rtl_set(dev, RTL_REG_CPU_LINKUP, 1);
+
+       return 0;
+}
+
+static void
+rtl_hw_init(struct switch_dev *dev)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       int cpu_mask = 1 << dev->cpu_port;
+       int i;
+
+       rtl_set(dev, RTL_REG_VLAN_ENABLE, 0);
+       rtl_set(dev, RTL_REG_VLAN_FILTER, 0);
+       rtl_set(dev, RTL_REG_EN_TRUNK, 0);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 0);
+
+       /* initialize cpu port settings */
+       if (priv->do_cpu) {
+               rtl_set(dev, RTL_REG_CPUPORT, dev->cpu_port);
+               rtl_set(dev, RTL_REG_EN_CPUPORT, 1);
+       } else {
+               rtl_set(dev, RTL_REG_CPUPORT, 7);
+               rtl_set(dev, RTL_REG_EN_CPUPORT, 0);
+       }
+       rtl_set(dev, RTL_REG_EN_TAG_OUT, 0);
+       rtl_set(dev, RTL_REG_EN_TAG_IN, 0);
+       rtl_set(dev, RTL_REG_EN_TAG_CLR, 0);
+
+       /* reset all vlans */
+       for (i = 0; i < RTL8306_NUM_VLANS; i++) {
+               rtl_set(dev, RTL_VLAN_REG(i, VID), i);
+               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), 0);
+       }
+
+       /* default to port isolation */
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               unsigned long mask;
+
+               if ((1 << i) == cpu_mask)
+                       mask = ((1 << RTL8306_NUM_PORTS) - 1) & ~cpu_mask; /* all bits set */
+               else
+                       mask = cpu_mask | (1 << i);
+
+               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), mask);
+               rtl_set(dev, RTL_PORT_REG(i, PVID), i);
+               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
+               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), 1);
+               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), 3);
+       }
+       rtl_hw_apply(dev);
+}
+
+#ifdef DEBUG
+static int
+rtl_set_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       priv->do_cpu = val->value.i;
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_get_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       val->value.i = priv->do_cpu;
+       return 0;
+}
+
+static int
+rtl_set_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       dev->cpu_port = val->value.i;
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_get_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       val->value.i = dev->cpu_port;
+       return 0;
+}
+#endif
+
+static int
+rtl_reset(struct switch_dev *dev)
+{
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_attr_set_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       int idx = attr->id + (val->port_vlan * attr->ofs);
+       struct rtl_phyregs port;
+
+       if (attr->id >= ARRAY_SIZE(rtl_regs))
+               return -EINVAL;
+
+       if ((attr->max > 0) && (val->value.i > attr->max))
+               return -EINVAL;
+
+       /* access to phy register 22 on port 4/5
+        * needs phy status save/restore */
+       if ((val->port_vlan > 3) &&
+               (rtl_regs[idx].reg == 22) &&
+               (rtl_regs[idx].page == 0)) {
+
+               rtl_phy_save(dev, val->port_vlan, &port);
+               rtl_set(dev, idx, val->value.i);
+               rtl_phy_restore(dev, val->port_vlan, &port);
+       } else {
+               rtl_set(dev, idx, val->value.i);
+       }
+
+       return 0;
+}
+
+static int
+rtl_attr_get_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       int idx = attr->id + (val->port_vlan * attr->ofs);
+
+       if (idx >= ARRAY_SIZE(rtl_regs))
+               return -EINVAL;
+
+       val->value.i = rtl_get(dev, idx);
+       return 0;
+}
+
+static int
+rtl_attr_set_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl_attr_set_int(dev, attr, val);
+}
+
+static int
+rtl_attr_get_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+       return rtl_attr_get_int(dev, attr, val);
+}
+
+static int 
+rtl_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link)
+{
+       if (port >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+
+       /* in case the link changes from down to up, the register is only updated on read */
+       link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
+       if (!link->link)
+               link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
+
+       if (!link->link)
+               return 0;
+
+       link->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+       link->aneg = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+
+       if (rtl_get(dev, RTL_PORT_REG(port, SPEED)))
+               link->speed = SWITCH_PORT_SPEED_100;
+       else
+               link->speed = SWITCH_PORT_SPEED_10;
+
+       return 0;
+}
+
+static int
+rtl_attr_set_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       return rtl_attr_set_int(dev, attr, val);
+}
+
+static int
+rtl_attr_get_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       return rtl_attr_get_int(dev, attr, val);
+}
+
+static int
+rtl_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       unsigned int i, mask;
+
+       mask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               struct switch_port *port;
+
+               if (!(mask & (1 << i)))
+                       continue;
+
+               port = &val->value.ports[val->len];
+               port->id = i;
+               if (rtl_get(dev, RTL_PORT_REG(i, TAG_INSERT)) == 2 || i == dev->cpu_port)
+                       port->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+       }
+
+       return 0;
+}
+
+static int
+rtl_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct rtl_phyregs port;
+       int en = val->value.i;
+       int i;
+
+       rtl_set(dev, RTL_REG_EN_TAG_OUT, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_EN_TAG_IN, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_EN_TAG_CLR, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_VLAN_TAG_AWARE, en);
+       if (en)
+               rtl_set(dev, RTL_REG_VLAN_FILTER, en);
+
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               if (i > 3)
+                       rtl_phy_save(dev, val->port_vlan, &port);
+               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
+               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), (en ? (i == dev->cpu_port ? 0 : 1) : 1));
+               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), (en ? (i == dev->cpu_port ? 2 : 1) : 3));
+               if (i > 3)
+                       rtl_phy_restore(dev, val->port_vlan, &port);
+       }
+       rtl_set(dev, RTL_REG_VLAN_ENABLE, en);
+
+       return 0;
+}
+
+static int
+rtl_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       val->value.i = rtl_get(dev, RTL_REG_VLAN_ENABLE);
+       return 0;
+}
+
+static int
+rtl_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       unsigned int mask = 0;
+       unsigned int oldmask;
+       int i;
+
+       for(i = 0; i < val->len; i++)
+       {
+               struct switch_port *port = &val->value.ports[i];
+               bool tagged = false;
+
+               mask |= (1 << port->id);
+
+               if (port->id == dev->cpu_port)
+                       continue;
+
+               if ((i == dev->cpu_port) ||
+                       (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+                       tagged = true;
+
+               /* fix up PVIDs for added ports */
+               if (!tagged)
+                       rtl_set(dev, RTL_PORT_REG(port->id, PVID), val->port_vlan);
+
+               rtl_set(dev, RTL_PORT_REG(port->id, NON_PVID_DISCARD), (tagged ? 0 : 1));
+               rtl_set(dev, RTL_PORT_REG(port->id, VID_INSERT), (tagged ? 0 : 1));
+               rtl_set(dev, RTL_PORT_REG(port->id, TAG_INSERT), (tagged ? 2 : 1));
+       }
+
+       oldmask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
+       rtl_set(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK), mask);
+
+       /* fix up PVIDs for removed ports, default to last vlan */
+       oldmask &= ~mask;
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               if (!(oldmask & (1 << i)))
+                       continue;
+
+               if (i == dev->cpu_port)
+                       continue;
+
+               if (rtl_get(dev, RTL_PORT_REG(i, PVID)) == val->port_vlan)
+                       rtl_set(dev, RTL_PORT_REG(i, PVID), dev->vlans - 1);
+       }
+
+       return 0;
+}
+
+static struct switch_attr rtl_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .max = 1,
+               .set = rtl_set_vlan,
+               .get = rtl_get_vlan,
+       },
+       {
+               RTL_GLOBAL_REGATTR(EN_TRUNK),
+               .name = "trunk",
+               .description = "Enable port trunking",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(TRUNK_PORTSEL),
+               .name = "trunk_sel",
+               .description = "Select ports for trunking (0: 0,1 - 1: 3,4)",
+               .max = 1,
+       },
+#ifdef DEBUG
+       {
+               RTL_GLOBAL_REGATTR(VLAN_FILTER),
+               .name = "vlan_filter",
+               .description = "Filter incoming packets for allowed VLANS",
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "cpuport",
+               .description = "CPU Port",
+               .set = rtl_set_cpuport,
+               .get = rtl_get_cpuport,
+               .max = RTL8306_NUM_PORTS,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "use_cpuport",
+               .description = "CPU Port handling flag",
+               .set = rtl_set_use_cpuport,
+               .get = rtl_get_use_cpuport,
+               .max = RTL8306_NUM_PORTS,
+       },
+       {
+               RTL_GLOBAL_REGATTR(TRAP_CPU),
+               .name = "trap_cpu",
+               .description = "VLAN trap to CPU",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(VLAN_TAG_AWARE),
+               .name = "vlan_tag_aware",
+               .description = "Enable VLAN tag awareness",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(VLAN_TAG_ONLY),
+               .name = "tag_only",
+               .description = "Only accept tagged packets",
+               .max = 1,
+       },
+#endif
+};
+static struct switch_attr rtl_port[] = {
+       {
+               RTL_PORT_REGATTR(PVID),
+               .name = "pvid",
+               .description = "Port VLAN ID",
+               .max = RTL8306_NUM_VLANS - 1,
+       },
+#ifdef DEBUG
+       {
+               RTL_PORT_REGATTR(NULL_VID_REPLACE),
+               .name = "null_vid",
+               .description = "NULL VID gets replaced by port default vid",
+               .max = 1,
+       },
+       {
+               RTL_PORT_REGATTR(NON_PVID_DISCARD),
+               .name = "non_pvid_discard",
+               .description = "discard packets with VID != PVID",
+               .max = 1,
+       },
+       {
+               RTL_PORT_REGATTR(VID_INSERT),
+               .name = "vid_insert_remove",
+               .description = "how should the switch insert and remove vids ?",
+               .max = 3,
+       },
+       {
+               RTL_PORT_REGATTR(TAG_INSERT),
+               .name = "tag_insert",
+               .description = "tag insertion handling",
+               .max = 3,
+       },
+#endif
+};
+
+static struct switch_attr rtl_vlan[] = {
+       {
+               RTL_VLAN_REGATTR(VID),
+               .name = "vid",
+               .description = "VLAN ID (1-4095)",
+               .max = 4095,
+       },
+};
+
+static const struct switch_dev_ops rtl8306_ops = {
+       .attr_global = {
+               .attr = rtl_globals,
+               .n_attr = ARRAY_SIZE(rtl_globals),
+       },
+       .attr_port = {
+               .attr = rtl_port,
+               .n_attr = ARRAY_SIZE(rtl_port),
+       },
+       .attr_vlan = {
+               .attr = rtl_vlan,
+               .n_attr = ARRAY_SIZE(rtl_vlan),
+       },
+
+       .get_vlan_ports = rtl_get_ports,
+       .set_vlan_ports = rtl_set_ports,
+       .apply_config = rtl_hw_apply,
+       .reset_switch = rtl_reset,
+       .get_port_link = rtl_get_port_link,
+};
+
+static int
+rtl8306_config_init(struct phy_device *pdev)
+{
+       struct net_device *netdev = pdev->attached_dev;
+       struct rtl_priv *priv = pdev->priv;
+       struct switch_dev *dev = &priv->dev;
+       struct switch_val val;
+       unsigned int chipid, chipver, chiptype;
+       int err;
+
+       /* Only init the switch for the primary PHY */
+       if (pdev->mdio.addr != 0)
+               return 0;
+
+       val.value.i = 1;
+       priv->dev.cpu_port = RTL8306_PORT_CPU;
+       priv->dev.ports = RTL8306_NUM_PORTS;
+       priv->dev.vlans = RTL8306_NUM_VLANS;
+       priv->dev.ops = &rtl8306_ops;
+       priv->do_cpu = 0;
+       priv->page = -1;
+       priv->bus = pdev->mdio.bus;
+
+       chipid = rtl_get(dev, RTL_REG_CHIPID);
+       chipver = rtl_get(dev, RTL_REG_CHIPVER);
+       chiptype = rtl_get(dev, RTL_REG_CHIPTYPE);
+       switch(chiptype) {
+       case 0:
+       case 2:
+               strncpy(priv->hwname, RTL_NAME_S, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_S;
+               break;
+       case 1:
+               strncpy(priv->hwname, RTL_NAME_SD, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_SD;
+               break;
+       case 3:
+               strncpy(priv->hwname, RTL_NAME_SDM, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_SDM;
+               break;
+       default:
+               strncpy(priv->hwname, RTL_NAME_UNKNOWN, sizeof(priv->hwname));
+               break;
+       }
+
+       dev->name = priv->hwname;
+       rtl_hw_init(dev);
+
+       printk(KERN_INFO "Registering %s switch with Chip ID: 0x%04x, version: 0x%04x\n", priv->hwname, chipid, chipver);
+
+       err = register_switch(dev, netdev);
+       if (err < 0) {
+               kfree(priv);
+               return err;
+       }
+
+       return 0;
+}
+
+
+static int
+rtl8306_fixup(struct phy_device *pdev)
+{
+       struct rtl_priv priv;
+       u16 chipid;
+
+       /* Attach to primary LAN port and WAN port */
+       if (pdev->mdio.addr != 0 && pdev->mdio.addr != 4)
+               return 0;
+
+       memset(&priv, 0, sizeof(priv));
+       priv.fixup = true;
+       priv.page = -1;
+       priv.bus = pdev->mdio.bus;
+       chipid = rtl_get(&priv.dev, RTL_REG_CHIPID);
+       if (chipid == 0x5988)
+               pdev->phy_id = RTL8306_MAGIC;
+
+       return 0;
+}
+
+static int
+rtl8306_probe(struct phy_device *pdev)
+{
+       struct rtl_priv *priv;
+
+       list_for_each_entry(priv, &phydevs, list) {
+               /*
+                * share one rtl_priv instance between virtual phy
+                * devices on the same bus
+                */
+               if (priv->bus == pdev->mdio.bus)
+                       goto found;
+       }
+       priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->bus = pdev->mdio.bus;
+
+found:
+       pdev->priv = priv;
+       return 0;
+}
+
+static void
+rtl8306_remove(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+       unregister_switch(&priv->dev);
+       kfree(priv);
+}
+
+static int
+rtl8306_config_aneg(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+
+       /* Only for WAN */
+       if (pdev->mdio.addr == 0)
+               return 0;
+
+       /* Restart autonegotiation */
+       rtl_set(&priv->dev, RTL_PORT_REG(4, NWAY), 1);
+       rtl_set(&priv->dev, RTL_PORT_REG(4, NRESTART), 1);
+
+       return 0;
+}
+
+static int
+rtl8306_read_status(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+       struct switch_dev *dev = &priv->dev;
+
+       if (pdev->mdio.addr == 4) {
+               /* WAN */
+               pdev->speed = rtl_get(dev, RTL_PORT_REG(4, SPEED)) ? SPEED_100 : SPEED_10;
+               pdev->duplex = rtl_get(dev, RTL_PORT_REG(4, DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF;
+               pdev->link = !!rtl_get(dev, RTL_PORT_REG(4, LINK));
+       } else {
+               /* LAN */
+               pdev->speed = SPEED_100;
+               pdev->duplex = DUPLEX_FULL;
+               pdev->link = 1;
+       }
+
+       /*
+        * Bypass generic PHY status read,
+        * it doesn't work with this switch
+        */
+       if (pdev->link) {
+               pdev->state = PHY_RUNNING;
+               netif_carrier_on(pdev->attached_dev);
+               pdev->adjust_link(pdev->attached_dev);
+       } else {
+               pdev->state = PHY_NOLINK;
+               netif_carrier_off(pdev->attached_dev);
+               pdev->adjust_link(pdev->attached_dev);
+       }
+
+       return 0;
+}
+
+
+static struct phy_driver rtl8306_driver = {
+       .name           = "Realtek RTL8306S",
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0))
+       .flags          = PHY_HAS_MAGICANEG,
+#endif
+       .phy_id         = RTL8306_MAGIC,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = &rtl8306_probe,
+       .remove         = &rtl8306_remove,
+       .config_init    = &rtl8306_config_init,
+       .config_aneg    = &rtl8306_config_aneg,
+       .read_status    = &rtl8306_read_status,
+};
+
+
+static int __init
+rtl_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, rtl8306_fixup);
+       return phy_driver_register(&rtl8306_driver, THIS_MODULE);
+}
+
+static void __exit
+rtl_exit(void)
+{
+       phy_driver_unregister(&rtl8306_driver);
+}
+
+module_init(rtl_init);
+module_exit(rtl_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files-4.14/drivers/net/phy/rtl8366_smi.c
new file mode 100644 (file)
index 0000000..c0cb680
--- /dev/null
@@ -0,0 +1,1628 @@
+/*
+ * Realtek RTL8366 SMI interface driver
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/rtl8366.h>
+#include <linux/version.h>
+#include <linux/of_mdio.h>
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#include "rtl8366_smi.h"
+
+#define RTL8366_SMI_ACK_RETRY_COUNT         5
+
+#define RTL8366_SMI_HW_STOP_DELAY              25      /* msecs */
+#define RTL8366_SMI_HW_START_DELAY             100     /* msecs */
+
+static inline void rtl8366_smi_clk_delay(struct rtl8366_smi *smi)
+{
+       ndelay(smi->clk_delay);
+}
+
+static void rtl8366_smi_start(struct rtl8366_smi *smi)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       /*
+        * Set GPIO pins to output mode, with initial state:
+        * SCK = 0, SDA = 1
+        */
+       gpio_direction_output(sck, 0);
+       gpio_direction_output(sda, 1);
+       rtl8366_smi_clk_delay(smi);
+
+       /* CLK 1: 0 -> 1, 1 -> 0 */
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+
+       /* CLK 2: */
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 1);
+}
+
+static void rtl8366_smi_stop(struct rtl8366_smi *smi)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 0);
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+
+       /* add a click */
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+
+       /* set GPIO pins to input mode */
+       gpio_direction_input(sda);
+       gpio_direction_input(sck);
+}
+
+static void rtl8366_smi_write_bits(struct rtl8366_smi *smi, u32 data, u32 len)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       for (; len > 0; len--) {
+               rtl8366_smi_clk_delay(smi);
+
+               /* prepare data */
+               gpio_set_value(sda, !!(data & ( 1 << (len - 1))));
+               rtl8366_smi_clk_delay(smi);
+
+               /* clocking */
+               gpio_set_value(sck, 1);
+               rtl8366_smi_clk_delay(smi);
+               gpio_set_value(sck, 0);
+       }
+}
+
+static void rtl8366_smi_read_bits(struct rtl8366_smi *smi, u32 len, u32 *data)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       gpio_direction_input(sda);
+
+       for (*data = 0; len > 0; len--) {
+               u32 u;
+
+               rtl8366_smi_clk_delay(smi);
+
+               /* clocking */
+               gpio_set_value(sck, 1);
+               rtl8366_smi_clk_delay(smi);
+               u = !!gpio_get_value(sda);
+               gpio_set_value(sck, 0);
+
+               *data |= (u << (len - 1));
+       }
+
+       gpio_direction_output(sda, 0);
+}
+
+static int rtl8366_smi_wait_for_ack(struct rtl8366_smi *smi)
+{
+       int retry_cnt;
+
+       retry_cnt = 0;
+       do {
+               u32 ack;
+
+               rtl8366_smi_read_bits(smi, 1, &ack);
+               if (ack == 0)
+                       break;
+
+               if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT) {
+                       dev_err(smi->parent, "ACK timeout\n");
+                       return -ETIMEDOUT;
+               }
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8366_smi_write_byte(struct rtl8366_smi *smi, u8 data)
+{
+       rtl8366_smi_write_bits(smi, data, 8);
+       return rtl8366_smi_wait_for_ack(smi);
+}
+
+static int rtl8366_smi_write_byte_noack(struct rtl8366_smi *smi, u8 data)
+{
+       rtl8366_smi_write_bits(smi, data, 8);
+       return 0;
+}
+
+static int rtl8366_smi_read_byte0(struct rtl8366_smi *smi, u8 *data)
+{
+       u32 t;
+
+       /* read data */
+       rtl8366_smi_read_bits(smi, 8, &t);
+       *data = (t & 0xff);
+
+       /* send an ACK */
+       rtl8366_smi_write_bits(smi, 0x00, 1);
+
+       return 0;
+}
+
+static int rtl8366_smi_read_byte1(struct rtl8366_smi *smi, u8 *data)
+{
+       u32 t;
+
+       /* read data */
+       rtl8366_smi_read_bits(smi, 8, &t);
+       *data = (t & 0xff);
+
+       /* send an ACK */
+       rtl8366_smi_write_bits(smi, 0x01, 1);
+
+       return 0;
+}
+
+static int __rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       unsigned long flags;
+       u8 lo = 0;
+       u8 hi = 0;
+       int ret;
+
+       spin_lock_irqsave(&smi->lock, flags);
+
+       rtl8366_smi_start(smi);
+
+       /* send READ command */
+       ret = rtl8366_smi_write_byte(smi, smi->cmd_read);
+       if (ret)
+               goto out;
+
+       /* set ADDR[7:0] */
+       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
+       if (ret)
+               goto out;
+
+       /* set ADDR[15:8] */
+       ret = rtl8366_smi_write_byte(smi, addr >> 8);
+       if (ret)
+               goto out;
+
+       /* read DATA[7:0] */
+       rtl8366_smi_read_byte0(smi, &lo);
+       /* read DATA[15:8] */
+       rtl8366_smi_read_byte1(smi, &hi);
+
+       *data = ((u32) lo) | (((u32) hi) << 8);
+
+       ret = 0;
+
+ out:
+       rtl8366_smi_stop(smi);
+       spin_unlock_irqrestore(&smi->lock, flags);
+
+       return ret;
+}
+/* Read/write via mdiobus */
+#define MDC_MDIO_CTRL0_REG             31
+#define MDC_MDIO_START_REG             29
+#define MDC_MDIO_CTRL1_REG             21
+#define MDC_MDIO_ADDRESS_REG           23
+#define MDC_MDIO_DATA_WRITE_REG                24
+#define MDC_MDIO_DATA_READ_REG         25
+
+#define MDC_MDIO_START_OP              0xFFFF
+#define MDC_MDIO_ADDR_OP               0x000E
+#define MDC_MDIO_READ_OP               0x0001
+#define MDC_MDIO_WRITE_OP              0x0003
+#define MDC_REALTEK_PHY_ADDR           0x0
+
+int __rtl8366_mdio_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       u32 phy_id = MDC_REALTEK_PHY_ADDR;
+       struct mii_bus *mbus = smi->ext_mbus;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock(&mbus->mdio_lock);
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address control code to register 31 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address to register 23 */
+       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write read control code to register 21 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_READ_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(smi->ext_mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Read data from register 25 */
+       *data = mbus->read(mbus, phy_id, MDC_MDIO_DATA_READ_REG);
+
+       mutex_unlock(&mbus->mdio_lock);
+
+       return 0;
+}
+
+static int __rtl8366_mdio_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       u32 phy_id = MDC_REALTEK_PHY_ADDR;
+       struct mii_bus *mbus = smi->ext_mbus;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock(&mbus->mdio_lock);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address control code to register 31 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address to register 23 */
+       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write data to register 24 */
+       mbus->write(mbus, phy_id, MDC_MDIO_DATA_WRITE_REG, data);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write data control code to register 21 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_WRITE_OP);
+
+       mutex_unlock(&mbus->mdio_lock);
+       return 0;
+}
+
+int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       if (smi->ext_mbus)
+               return __rtl8366_mdio_read_reg(smi, addr, data);
+       else
+               return __rtl8366_smi_read_reg(smi, addr, data);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_read_reg);
+
+static int __rtl8366_smi_write_reg(struct rtl8366_smi *smi,
+                                  u32 addr, u32 data, bool ack)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&smi->lock, flags);
+
+       rtl8366_smi_start(smi);
+
+       /* send WRITE command */
+       ret = rtl8366_smi_write_byte(smi, smi->cmd_write);
+       if (ret)
+               goto out;
+
+       /* set ADDR[7:0] */
+       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
+       if (ret)
+               goto out;
+
+       /* set ADDR[15:8] */
+       ret = rtl8366_smi_write_byte(smi, addr >> 8);
+       if (ret)
+               goto out;
+
+       /* write DATA[7:0] */
+       ret = rtl8366_smi_write_byte(smi, data & 0xff);
+       if (ret)
+               goto out;
+
+       /* write DATA[15:8] */
+       if (ack)
+               ret = rtl8366_smi_write_byte(smi, data >> 8);
+       else
+               ret = rtl8366_smi_write_byte_noack(smi, data >> 8);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+ out:
+       rtl8366_smi_stop(smi);
+       spin_unlock_irqrestore(&smi->lock, flags);
+
+       return ret;
+}
+
+int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       if (smi->ext_mbus)
+               return __rtl8366_mdio_write_reg(smi, addr, data);
+       else
+               return __rtl8366_smi_write_reg(smi, addr, data, true);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg);
+
+int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       return __rtl8366_smi_write_reg(smi, addr, data, false);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg_noack);
+
+int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data)
+{
+       u32 t;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, addr, &t);
+       if (err)
+               return err;
+
+       err = rtl8366_smi_write_reg(smi, addr, (t & ~mask) | data);
+       return err;
+
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_rmwr);
+
+static int rtl8366_reset(struct rtl8366_smi *smi)
+{
+       if (smi->hw_reset) {
+               smi->hw_reset(smi, true);
+               msleep(RTL8366_SMI_HW_STOP_DELAY);
+               smi->hw_reset(smi, false);
+               msleep(RTL8366_SMI_HW_START_DELAY);
+               return 0;
+       }
+
+       return smi->ops->reset_chip(smi);
+}
+
+static int rtl8366_mc_is_used(struct rtl8366_smi *smi, int mc_index, int *used)
+{
+       int err;
+       int i;
+
+       *used = 0;
+       for (i = 0; i < smi->num_ports; i++) {
+               int index = 0;
+
+               err = smi->ops->get_mc_index(smi, i, &index);
+               if (err)
+                       return err;
+
+               if (mc_index == index) {
+                       *used = 1;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member,
+                           u32 untag, u32 fid)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       int err;
+       int i;
+
+       /* Update the 4K table */
+       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+       if (err)
+               return err;
+
+       vlan4k.member = member;
+       vlan4k.untag = untag;
+       vlan4k.fid = fid;
+       err = smi->ops->set_vlan_4k(smi, &vlan4k);
+       if (err)
+               return err;
+
+       /* Try to find an existing MC entry for this VID */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               struct rtl8366_vlan_mc vlanmc;
+
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vid == vlanmc.vid) {
+                       /* update the MC entry */
+                       vlanmc.member = member;
+                       vlanmc.untag = untag;
+                       vlanmc.fid = fid;
+
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       int err;
+       int index;
+
+       err = smi->ops->get_mc_index(smi, port, &index);
+       if (err)
+               return err;
+
+       err = smi->ops->get_vlan_mc(smi, index, &vlanmc);
+       if (err)
+               return err;
+
+       *val = vlanmc.vid;
+       return 0;
+}
+
+static int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port,
+                           unsigned vid)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       struct rtl8366_vlan_4k vlan4k;
+       int err;
+       int i;
+
+       /* Try to find an existing MC entry for this VID */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vid == vlanmc.vid) {
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       /* We have no MC entry for this VID, try to find an empty one */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vlanmc.vid == 0 && vlanmc.member == 0) {
+                       /* Update the entry from the 4K table */
+                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+                       if (err)
+                               return err;
+
+                       vlanmc.vid = vid;
+                       vlanmc.member = vlan4k.member;
+                       vlanmc.untag = vlan4k.untag;
+                       vlanmc.fid = vlan4k.fid;
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       /* MC table is full, try to find an unused entry and replace it */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               int used;
+
+               err = rtl8366_mc_is_used(smi, i, &used);
+               if (err)
+                       return err;
+
+               if (!used) {
+                       /* Update the entry from the 4K table */
+                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+                       if (err)
+                               return err;
+
+                       vlanmc.vid = vid;
+                       vlanmc.member = vlan4k.member;
+                       vlanmc.untag = vlan4k.untag;
+                       vlanmc.fid = vlan4k.fid;
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       dev_err(smi->parent,
+               "all VLAN member configurations are in use\n");
+
+       return -ENOSPC;
+}
+
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+
+       err = smi->ops->enable_vlan(smi, enable);
+       if (err)
+               return err;
+
+       smi->vlan_enabled = enable;
+
+       if (!enable) {
+               smi->vlan4k_enabled = 0;
+               err = smi->ops->enable_vlan4k(smi, enable);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
+
+static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+
+       if (enable) {
+               err = smi->ops->enable_vlan(smi, enable);
+               if (err)
+                       return err;
+
+               smi->vlan_enabled = enable;
+       }
+
+       err = smi->ops->enable_vlan4k(smi, enable);
+       if (err)
+               return err;
+
+       smi->vlan4k_enabled = enable;
+       return 0;
+}
+
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable)
+{
+       int port;
+       int err;
+
+       for (port = 0; port < smi->num_ports; port++) {
+               err = smi->ops->enable_port(smi, port, enable);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports);
+
+int rtl8366_reset_vlan(struct rtl8366_smi *smi)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       int err;
+       int i;
+
+       rtl8366_enable_vlan(smi, 0);
+       rtl8366_enable_vlan4k(smi, 0);
+
+       /* clear VLAN member configurations */
+       vlanmc.vid = 0;
+       vlanmc.priority = 0;
+       vlanmc.member = 0;
+       vlanmc.untag = 0;
+       vlanmc.fid = 0;
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
+
+static int rtl8366_init_vlan(struct rtl8366_smi *smi)
+{
+       int port;
+       int err;
+
+       err = rtl8366_reset_vlan(smi);
+       if (err)
+               return err;
+
+       for (port = 0; port < smi->num_ports; port++) {
+               u32 mask;
+
+               if (port == smi->cpu_port)
+                       mask = (1 << smi->num_ports) - 1;
+               else
+                       mask = (1 << port) | (1 << smi->cpu_port);
+
+               err = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
+               if (err)
+                       return err;
+
+               err = rtl8366_set_pvid(smi, port, (port + 1));
+               if (err)
+                       return err;
+       }
+
+       return rtl8366_enable_vlan(smi, 1);
+}
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+int rtl8366_debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_debugfs_open);
+
+static ssize_t rtl8366_read_debugfs_vlan_mc(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       int i, len = 0;
+       char *buf = smi->buf;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "%2s %6s %4s %6s %6s %3s\n",
+                       "id", "vid","prio", "member", "untag", "fid");
+
+       for (i = 0; i < smi->num_vlan_mc; ++i) {
+               struct rtl8366_vlan_mc vlanmc;
+
+               smi->ops->get_vlan_mc(smi, i, &vlanmc);
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%2d %6d %4d 0x%04x 0x%04x %3d\n",
+                               i, vlanmc.vid, vlanmc.priority,
+                               vlanmc.member, vlanmc.untag, vlanmc.fid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+#define RTL8366_VLAN4K_PAGE_SIZE       64
+#define RTL8366_VLAN4K_NUM_PAGES       (4096 / RTL8366_VLAN4K_PAGE_SIZE)
+
+static ssize_t rtl8366_read_debugfs_vlan_4k(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       int i, len = 0;
+       int offset;
+       char *buf = smi->buf;
+
+       if (smi->dbg_vlan_4k_page >= RTL8366_VLAN4K_NUM_PAGES) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "invalid page: %u\n", smi->dbg_vlan_4k_page);
+               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       }
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "%4s %6s %6s %3s\n",
+                       "vid", "member", "untag", "fid");
+
+       offset = RTL8366_VLAN4K_PAGE_SIZE * smi->dbg_vlan_4k_page;
+       for (i = 0; i < RTL8366_VLAN4K_PAGE_SIZE; i++) {
+               struct rtl8366_vlan_4k vlan4k;
+
+               smi->ops->get_vlan_4k(smi, offset + i, &vlan4k);
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d 0x%04x 0x%04x %3d\n",
+                               vlan4k.vid, vlan4k.member,
+                               vlan4k.untag, vlan4k.fid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_pvid(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       char *buf = smi->buf;
+       int len = 0;
+       int i;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "%4s %4s\n",
+                       "port", "pvid");
+
+       for (i = 0; i < smi->num_ports; i++) {
+               int pvid;
+               int err;
+
+               err = rtl8366_get_pvid(smi, i, &pvid);
+               if (err)
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d error\n", i);
+               else
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d %4d\n", i, pvid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_reg(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       u32 t, reg = smi->dbg_reg;
+       int err, len = 0;
+       char *buf = smi->buf;
+
+       memset(buf, '\0', sizeof(smi->buf));
+
+       err = rtl8366_smi_read_reg(smi, reg, &t);
+       if (err) {
+               len += snprintf(buf, sizeof(smi->buf),
+                               "Read failed (reg: 0x%04x)\n", reg);
+               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       }
+
+       len += snprintf(buf, sizeof(smi->buf), "reg = 0x%04x, val = 0x%04x\n",
+                       reg, t);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_write_debugfs_reg(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       unsigned long data;
+       u32 reg = smi->dbg_reg;
+       int err;
+       size_t len;
+       char *buf = smi->buf;
+
+       len = min(count, sizeof(smi->buf) - 1);
+       if (copy_from_user(buf, user_buf, len)) {
+               dev_err(smi->parent, "copy from user failed\n");
+               return -EFAULT;
+       }
+
+       buf[len] = '\0';
+       if (len > 0 && buf[len - 1] == '\n')
+               buf[len - 1] = '\0';
+
+
+       if (kstrtoul(buf, 16, &data)) {
+               dev_err(smi->parent, "Invalid reg value %s\n", buf);
+       } else {
+               err = rtl8366_smi_write_reg(smi, reg, data);
+               if (err) {
+                       dev_err(smi->parent,
+                               "writing reg 0x%04x val 0x%04lx failed\n",
+                               reg, data);
+               }
+       }
+
+       return count;
+}
+
+static ssize_t rtl8366_read_debugfs_mibs(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = file->private_data;
+       int i, j, len = 0;
+       char *buf = smi->buf;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s",
+                       "Counter");
+
+       for (i = 0; i < smi->num_ports; i++) {
+               char port_buf[10];
+
+               snprintf(port_buf, sizeof(port_buf), "Port %d", i);
+               len += snprintf(buf + len, sizeof(smi->buf) - len, " %12s",
+                               port_buf);
+       }
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
+
+       for (i = 0; i < smi->num_mib_counters; i++) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s ",
+                               smi->mib_counters[i].name);
+               for (j = 0; j < smi->num_ports; j++) {
+                       unsigned long long counter = 0;
+
+                       if (!smi->ops->get_mib_counter(smi, i, j, &counter))
+                               len += snprintf(buf + len,
+                                               sizeof(smi->buf) - len,
+                                               "%12llu ", counter);
+                       else
+                               len += snprintf(buf + len,
+                                               sizeof(smi->buf) - len,
+                                               "%12s ", "error");
+               }
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_rtl8366_regs = {
+       .read   = rtl8366_read_debugfs_reg,
+       .write  = rtl8366_write_debugfs_reg,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_vlan_mc = {
+       .read   = rtl8366_read_debugfs_vlan_mc,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_vlan_4k = {
+       .read   = rtl8366_read_debugfs_vlan_4k,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_pvid = {
+       .read   = rtl8366_read_debugfs_pvid,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_mibs = {
+       .read = rtl8366_read_debugfs_mibs,
+       .open = rtl8366_debugfs_open,
+       .owner = THIS_MODULE
+};
+
+static void rtl8366_debugfs_init(struct rtl8366_smi *smi)
+{
+       struct dentry *node;
+       struct dentry *root;
+
+       if (!smi->debugfs_root)
+               smi->debugfs_root = debugfs_create_dir(dev_name(smi->parent),
+                                                      NULL);
+
+       if (!smi->debugfs_root) {
+               dev_err(smi->parent, "Unable to create debugfs dir\n");
+               return;
+       }
+       root = smi->debugfs_root;
+
+       node = debugfs_create_x16("reg", S_IRUGO | S_IWUSR, root,
+                                 &smi->dbg_reg);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "reg");
+               return;
+       }
+
+       node = debugfs_create_file("val", S_IRUGO | S_IWUSR, root, smi,
+                                  &fops_rtl8366_regs);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "val");
+               return;
+       }
+
+       node = debugfs_create_file("vlan_mc", S_IRUSR, root, smi,
+                                  &fops_rtl8366_vlan_mc);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_mc");
+               return;
+       }
+
+       node = debugfs_create_u8("vlan_4k_page", S_IRUGO | S_IWUSR, root,
+                                 &smi->dbg_vlan_4k_page);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_4k_page");
+               return;
+       }
+
+       node = debugfs_create_file("vlan_4k", S_IRUSR, root, smi,
+                                  &fops_rtl8366_vlan_4k);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_4k");
+               return;
+       }
+
+       node = debugfs_create_file("pvid", S_IRUSR, root, smi,
+                                  &fops_rtl8366_pvid);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "pvid");
+               return;
+       }
+
+       node = debugfs_create_file("mibs", S_IRUSR, smi->debugfs_root, smi,
+                                  &fops_rtl8366_mibs);
+       if (!node)
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "mibs");
+}
+
+static void rtl8366_debugfs_remove(struct rtl8366_smi *smi)
+{
+       if (smi->debugfs_root) {
+               debugfs_remove_recursive(smi->debugfs_root);
+               smi->debugfs_root = NULL;
+       }
+}
+#else
+static inline void rtl8366_debugfs_init(struct rtl8366_smi *smi) {}
+static inline void rtl8366_debugfs_remove(struct rtl8366_smi *smi) {}
+#endif /* CONFIG_RTL8366_SMI_DEBUG_FS */
+
+static int rtl8366_smi_mii_init(struct rtl8366_smi *smi)
+{
+       int ret;
+
+#ifdef CONFIG_OF
+       struct device_node *np = NULL;
+
+       np = of_get_child_by_name(smi->parent->of_node, "mdio-bus");
+#endif
+
+       smi->mii_bus = mdiobus_alloc();
+       if (smi->mii_bus == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       smi->mii_bus->priv = (void *) smi;
+       smi->mii_bus->name = dev_name(smi->parent);
+       smi->mii_bus->read = smi->ops->mii_read;
+       smi->mii_bus->write = smi->ops->mii_write;
+       snprintf(smi->mii_bus->id, MII_BUS_ID_SIZE, "%s",
+                dev_name(smi->parent));
+       smi->mii_bus->parent = smi->parent;
+       smi->mii_bus->phy_mask = ~(0x1f);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
+       {
+               int i;
+               smi->mii_bus->irq = smi->mii_irq;
+               for (i = 0; i < PHY_MAX_ADDR; i++)
+                       smi->mii_irq[i] = PHY_POLL;
+       }
+#endif
+
+#ifdef CONFIG_OF
+       if (np)
+               ret = of_mdiobus_register(smi->mii_bus, np);
+       else
+#endif
+               ret = mdiobus_register(smi->mii_bus);
+
+       if (ret)
+               goto err_free;
+
+       return 0;
+
+ err_free:
+       mdiobus_free(smi->mii_bus);
+ err:
+       return ret;
+}
+
+static void rtl8366_smi_mii_cleanup(struct rtl8366_smi *smi)
+{
+       mdiobus_unregister(smi->mii_bus);
+       mdiobus_free(smi->mii_bus);
+}
+
+int rtl8366_sw_reset_switch(struct switch_dev *dev)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       err = rtl8366_reset(smi);
+       if (err)
+               return err;
+
+       err = smi->ops->setup(smi);
+       if (err)
+               return err;
+
+       err = rtl8366_reset_vlan(smi);
+       if (err)
+               return err;
+
+       err = rtl8366_enable_vlan(smi, 1);
+       if (err)
+               return err;
+
+       return rtl8366_enable_all_ports(smi, 1);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_reset_switch);
+
+int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366_get_pvid(smi, port, val);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_pvid);
+
+int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366_set_pvid(smi, port, val);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid);
+
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int i, len = 0;
+       unsigned long long counter = 0;
+       char *buf = smi->buf;
+
+       if (val->port_vlan >= smi->num_ports)
+               return -EINVAL;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "Port %d MIB counters\n",
+                       val->port_vlan);
+
+       for (i = 0; i < smi->num_mib_counters; ++i) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%-36s: ", smi->mib_counters[i].name);
+               if (!smi->ops->get_mib_counter(smi, i, val->port_vlan,
+                                              &counter))
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                                       "%llu\n", counter);
+               else
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                                       "%s\n", "error");
+       }
+
+       val->value.s = buf;
+       val->len = len;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_mib);
+
+int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
+                               struct switch_port_stats *stats,
+                               int txb_id, int rxb_id)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       unsigned long long counter = 0;
+       int ret;
+
+       if (port >= smi->num_ports)
+               return -EINVAL;
+
+       ret = smi->ops->get_mib_counter(smi, txb_id, port, &counter);
+       if (ret)
+               return ret;
+
+       stats->tx_bytes = counter;
+
+       ret = smi->ops->get_mib_counter(smi, rxb_id, port, &counter);
+       if (ret)
+               return ret;
+
+       stats->rx_bytes = counter;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_stats);
+
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       int i;
+       u32 len = 0;
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       char *buf = smi->buf;
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       memset(buf, '\0', sizeof(smi->buf));
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "VLAN %d: Ports: '", vlan4k.vid);
+
+       for (i = 0; i < smi->num_ports; i++) {
+               if (!(vlan4k.member & (1 << i)))
+                       continue;
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "%d%s", i,
+                               (vlan4k.untag & (1 << i)) ? "" : "t");
+       }
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "', members=%04x, untag=%04x, fid=%u",
+                       vlan4k.member, vlan4k.untag, vlan4k.fid);
+
+       val->value.s = buf;
+       val->len = len;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_info);
+
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       struct switch_port *port;
+       struct rtl8366_vlan_4k vlan4k;
+       int i;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+
+       port = &val->value.ports[0];
+       val->len = 0;
+       for (i = 0; i < smi->num_ports; i++) {
+               if (!(vlan4k.member & BIT(i)))
+                       continue;
+
+               port->id = i;
+               port->flags = (vlan4k.untag & BIT(i)) ?
+                                       0 : BIT(SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+               port++;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_ports);
+
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       struct switch_port *port;
+       u32 member = 0;
+       u32 untag = 0;
+       int err;
+       int i;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       port = &val->value.ports[0];
+       for (i = 0; i < val->len; i++, port++) {
+               int pvid = 0;
+               member |= BIT(port->id);
+
+               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
+                       untag |= BIT(port->id);
+
+               /*
+                * To ensure that we have a valid MC entry for this VLAN,
+                * initialize the port VLAN ID here.
+                */
+               err = rtl8366_get_pvid(smi, port->id, &pvid);
+               if (err < 0)
+                       return err;
+               if (pvid == 0) {
+                       err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_ports);
+
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       val->value.i = vlan4k.fid;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_fid);
+
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       if (val->value.i < 0 || val->value.i > attr->max)
+               return -EINVAL;
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       return rtl8366_set_vlan(smi, val->port_vlan,
+                               vlan4k.member,
+                               vlan4k.untag,
+                               val->value.i);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_fid);
+
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (attr->ofs > 2)
+               return -EINVAL;
+
+       if (attr->ofs == 1)
+               val->value.i = smi->vlan_enabled;
+       else
+               val->value.i = smi->vlan4k_enabled;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_enable);
+
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (attr->ofs > 2)
+               return -EINVAL;
+
+       if (attr->ofs == 1)
+               err = rtl8366_enable_vlan(smi, val->value.i);
+       else
+               err = rtl8366_enable_vlan4k(smi, val->value.i);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_enable);
+
+struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent)
+{
+       struct rtl8366_smi *smi;
+
+       BUG_ON(!parent);
+
+       smi = kzalloc(sizeof(*smi), GFP_KERNEL);
+       if (!smi) {
+               dev_err(parent, "no memory for private data\n");
+               return NULL;
+       }
+
+       smi->parent = parent;
+       return smi;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_alloc);
+
+static int __rtl8366_smi_init(struct rtl8366_smi *smi, const char *name)
+{
+       int err;
+
+       if (!smi->ext_mbus) {
+               err = gpio_request(smi->gpio_sda, name);
+               if (err) {
+                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
+                               smi->gpio_sda, err);
+                       goto err_out;
+               }
+
+               err = gpio_request(smi->gpio_sck, name);
+               if (err) {
+                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
+                               smi->gpio_sck, err);
+                       goto err_free_sda;
+               }
+       }
+
+       spin_lock_init(&smi->lock);
+
+       /* start the switch */
+       if (smi->hw_reset) {
+               smi->hw_reset(smi, false);
+               msleep(RTL8366_SMI_HW_START_DELAY);
+       }
+
+       return 0;
+
+ err_free_sda:
+       gpio_free(smi->gpio_sda);
+ err_out:
+       return err;
+}
+
+static void __rtl8366_smi_cleanup(struct rtl8366_smi *smi)
+{
+       if (smi->hw_reset)
+               smi->hw_reset(smi, true);
+
+       if (!smi->ext_mbus) {
+               gpio_free(smi->gpio_sck);
+               gpio_free(smi->gpio_sda);
+       }
+}
+
+enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata)
+{
+       static struct rtl8366_smi smi;
+       enum rtl8366_type type = RTL8366_TYPE_UNKNOWN;
+       u32 reg = 0;
+
+       memset(&smi, 0, sizeof(smi));
+       smi.gpio_sda = pdata->gpio_sda;
+       smi.gpio_sck = pdata->gpio_sck;
+       smi.clk_delay = 10;
+       smi.cmd_read  = 0xa9;
+       smi.cmd_write = 0xa8;
+
+       if (__rtl8366_smi_init(&smi, "rtl8366"))
+               goto out;
+
+       if (rtl8366_smi_read_reg(&smi, 0x5c, &reg))
+               goto cleanup;
+
+       switch(reg) {
+       case 0x6027:
+               printk("Found an RTL8366S switch\n");
+               type = RTL8366_TYPE_S;
+               break;
+       case 0x5937:
+               printk("Found an RTL8366RB switch\n");
+               type = RTL8366_TYPE_RB;
+               break;
+       default:
+               printk("Found an Unknown RTL8366 switch (id=0x%04x)\n", reg);
+               break;
+       }
+
+cleanup:
+       __rtl8366_smi_cleanup(&smi);
+out:
+       return type;
+}
+
+int rtl8366_smi_init(struct rtl8366_smi *smi)
+{
+       int err;
+
+       if (!smi->ops)
+               return -EINVAL;
+
+       err = __rtl8366_smi_init(smi, dev_name(smi->parent));
+       if (err)
+               goto err_out;
+
+       if (!smi->ext_mbus)
+               dev_info(smi->parent, "using GPIO pins %u (SDA) and %u (SCK)\n",
+                        smi->gpio_sda, smi->gpio_sck);
+       else
+               dev_info(smi->parent, "using MDIO bus '%s'\n", smi->ext_mbus->name);
+
+       err = smi->ops->detect(smi);
+       if (err) {
+               dev_err(smi->parent, "chip detection failed, err=%d\n", err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_reset(smi);
+       if (err)
+               goto err_free_sck;
+
+       err = smi->ops->setup(smi);
+       if (err) {
+               dev_err(smi->parent, "chip setup failed, err=%d\n", err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_init_vlan(smi);
+       if (err) {
+               dev_err(smi->parent, "VLAN initialization failed, err=%d\n",
+                       err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_enable_all_ports(smi, 1);
+       if (err)
+               goto err_free_sck;
+
+       err = rtl8366_smi_mii_init(smi);
+       if (err)
+               goto err_free_sck;
+
+       rtl8366_debugfs_init(smi);
+
+       return 0;
+
+ err_free_sck:
+       __rtl8366_smi_cleanup(smi);
+ err_out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_init);
+
+void rtl8366_smi_cleanup(struct rtl8366_smi *smi)
+{
+       rtl8366_debugfs_remove(smi);
+       rtl8366_smi_mii_cleanup(smi);
+       __rtl8366_smi_cleanup(smi);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup);
+
+#ifdef CONFIG_OF
+static void rtl8366_smi_reset(struct rtl8366_smi *smi, bool active)
+{
+       if (active)
+               reset_control_assert(smi->reset);
+       else
+               reset_control_deassert(smi->reset);
+}
+
+int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0);
+       int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0);
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *mdio_node;
+
+       mdio_node = of_parse_phandle(np, "mii-bus", 0);
+       if (!mdio_node) {
+               dev_err(&pdev->dev, "cannot find mdio node phandle");
+               goto try_gpio;
+       }
+
+       smi->ext_mbus = of_mdio_find_bus(mdio_node);
+       if (!smi->ext_mbus) {
+               dev_err(&pdev->dev,
+                       "cannot find mdio bus from bus handle");
+               goto try_gpio;
+       }
+
+       return 0;
+
+try_gpio:
+       if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) {
+               dev_err(&pdev->dev, "gpios missing in devictree\n");
+               return -EINVAL;
+       }
+
+       smi->gpio_sda = sda;
+       smi->gpio_sck = sck;
+       smi->reset = devm_reset_control_get(&pdev->dev, "switch");
+       if (!IS_ERR(smi->reset))
+               smi->hw_reset = rtl8366_smi_reset;
+
+       return 0;
+}
+#else
+static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       return -ENODEV;
+}
+#endif
+
+int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata = pdev->dev.platform_data;
+
+       if (!pdev->dev.platform_data) {
+               dev_err(&pdev->dev, "no platform data specified\n");
+               return -EINVAL;
+       }
+
+       smi->gpio_sda = pdata->gpio_sda;
+       smi->gpio_sck = pdata->gpio_sck;
+       smi->hw_reset = pdata->hw_reset;
+
+       return 0;
+}
+
+
+struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_alloc(&pdev->dev);
+       if (!smi)
+               return NULL;
+
+       if (pdev->dev.of_node)
+               err = rtl8366_smi_probe_of(pdev, smi);
+       else
+               err = rtl8366_smi_probe_plat(pdev, smi);
+
+       if (err)
+               goto free_smi;
+
+       return smi;
+
+free_smi:
+       kfree(smi);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_probe);
+
+MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files-4.14/drivers/net/phy/rtl8366_smi.h
new file mode 100644 (file)
index 0000000..d1d988a
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Realtek RTL8366 SMI interface driver defines
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8366_SMI_H
+#define _RTL8366_SMI_H
+
+#include <linux/phy.h>
+#include <linux/switch.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct rtl8366_smi_ops;
+struct rtl8366_vlan_ops;
+struct mii_bus;
+struct dentry;
+struct inode;
+struct file;
+
+struct rtl8366_mib_counter {
+       unsigned        base;
+       unsigned        offset;
+       unsigned        length;
+       const char      *name;
+};
+
+struct rtl8366_smi {
+       struct device           *parent;
+       unsigned int            gpio_sda;
+       unsigned int            gpio_sck;
+       void                    (*hw_reset)(struct rtl8366_smi *smi, bool active);
+       unsigned int            clk_delay;      /* ns */
+       u8                      cmd_read;
+       u8                      cmd_write;
+       spinlock_t              lock;
+       struct mii_bus          *mii_bus;
+       int                     mii_irq[PHY_MAX_ADDR];
+       struct switch_dev       sw_dev;
+
+       unsigned int            cpu_port;
+       unsigned int            num_ports;
+       unsigned int            num_vlan_mc;
+       unsigned int            num_mib_counters;
+       struct rtl8366_mib_counter *mib_counters;
+
+       struct rtl8366_smi_ops  *ops;
+
+       int                     vlan_enabled;
+       int                     vlan4k_enabled;
+
+       char                    buf[4096];
+
+       struct reset_control    *reset;
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+       struct dentry           *debugfs_root;
+       u16                     dbg_reg;
+       u8                      dbg_vlan_4k_page;
+#endif
+       struct mii_bus          *ext_mbus;
+};
+
+struct rtl8366_vlan_mc {
+       u16     vid;
+       u16     untag;
+       u16     member;
+       u8      fid;
+       u8      priority;
+};
+
+struct rtl8366_vlan_4k {
+       u16     vid;
+       u16     untag;
+       u16     member;
+       u8      fid;
+};
+
+struct rtl8366_smi_ops {
+       int     (*detect)(struct rtl8366_smi *smi);
+       int     (*reset_chip)(struct rtl8366_smi *smi);
+       int     (*setup)(struct rtl8366_smi *smi);
+
+       int     (*mii_read)(struct mii_bus *bus, int addr, int reg);
+       int     (*mii_write)(struct mii_bus *bus, int addr, int reg, u16 val);
+
+       int     (*get_vlan_mc)(struct rtl8366_smi *smi, u32 index,
+                              struct rtl8366_vlan_mc *vlanmc);
+       int     (*set_vlan_mc)(struct rtl8366_smi *smi, u32 index,
+                              const struct rtl8366_vlan_mc *vlanmc);
+       int     (*get_vlan_4k)(struct rtl8366_smi *smi, u32 vid,
+                              struct rtl8366_vlan_4k *vlan4k);
+       int     (*set_vlan_4k)(struct rtl8366_smi *smi,
+                              const struct rtl8366_vlan_4k *vlan4k);
+       int     (*get_mc_index)(struct rtl8366_smi *smi, int port, int *val);
+       int     (*set_mc_index)(struct rtl8366_smi *smi, int port, int index);
+       int     (*get_mib_counter)(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val);
+       int     (*is_vlan_valid)(struct rtl8366_smi *smi, unsigned vlan);
+       int     (*enable_vlan)(struct rtl8366_smi *smi, int enable);
+       int     (*enable_vlan4k)(struct rtl8366_smi *smi, int enable);
+       int     (*enable_port)(struct rtl8366_smi *smi, int port, int enable);
+};
+
+struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent);
+int rtl8366_smi_init(struct rtl8366_smi *smi);
+void rtl8366_smi_cleanup(struct rtl8366_smi *smi);
+int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data);
+int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data);
+int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data);
+int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data);
+
+int rtl8366_reset_vlan(struct rtl8366_smi *smi);
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable);
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable);
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+int rtl8366_debugfs_open(struct inode *inode, struct file *file);
+#endif
+
+static inline struct rtl8366_smi *sw_to_rtl8366_smi(struct switch_dev *sw)
+{
+       return container_of(sw, struct rtl8366_smi, sw_dev);
+}
+
+int rtl8366_sw_reset_switch(struct switch_dev *dev);
+int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
+int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val);
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val);
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val);
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
+                               struct switch_port_stats *stats,
+                               int txb_id, int rxb_id);
+
+struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
+
+#endif /*  _RTL8366_SMI_H */
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files-4.14/drivers/net/phy/rtl8366rb.c
new file mode 100644 (file)
index 0000000..dc394c0
--- /dev/null
@@ -0,0 +1,1532 @@
+/*
+ * Platform driver for the Realtek RTL8366RB ethernet switch
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
+ * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8366.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8366RB_DRIVER_DESC  "Realtek RTL8366RB ethernet switch driver"
+#define RTL8366RB_DRIVER_VER   "0.2.4"
+
+#define RTL8366RB_PHY_NO_MAX   4
+#define RTL8366RB_PHY_PAGE_MAX 7
+#define RTL8366RB_PHY_ADDR_MAX 31
+
+/* Switch Global Configuration register */
+#define RTL8366RB_SGCR                         0x0000
+#define RTL8366RB_SGCR_EN_BC_STORM_CTRL                BIT(0)
+#define RTL8366RB_SGCR_MAX_LENGTH(_x)          (_x << 4)
+#define RTL8366RB_SGCR_MAX_LENGTH_MASK         RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_MAX_LENGTH_1522         RTL8366RB_SGCR_MAX_LENGTH(0x0)
+#define RTL8366RB_SGCR_MAX_LENGTH_1536         RTL8366RB_SGCR_MAX_LENGTH(0x1)
+#define RTL8366RB_SGCR_MAX_LENGTH_1552         RTL8366RB_SGCR_MAX_LENGTH(0x2)
+#define RTL8366RB_SGCR_MAX_LENGTH_9216         RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_EN_VLAN                 BIT(13)
+#define RTL8366RB_SGCR_EN_VLAN_4KTB            BIT(14)
+
+/* Port Enable Control register */
+#define RTL8366RB_PECR                         0x0001
+
+/* Port Mirror Control Register */
+#define RTL8366RB_PMCR                         0x0007
+#define RTL8366RB_PMCR_SOURCE_PORT(_x)         (_x)
+#define RTL8366RB_PMCR_SOURCE_PORT_MASK                0x000f
+#define RTL8366RB_PMCR_MONITOR_PORT(_x)                ((_x) << 4)
+#define RTL8366RB_PMCR_MONITOR_PORT_MASK       0x00f0
+#define RTL8366RB_PMCR_MIRROR_RX               BIT(8)
+#define RTL8366RB_PMCR_MIRROR_TX               BIT(9)
+#define RTL8366RB_PMCR_MIRROR_SPC              BIT(10)
+#define RTL8366RB_PMCR_MIRROR_ISO              BIT(11)
+
+/* Switch Security Control registers */
+#define RTL8366RB_SSCR0                                0x0002
+#define RTL8366RB_SSCR1                                0x0003
+#define RTL8366RB_SSCR2                                0x0004
+#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA                BIT(0)
+
+#define RTL8366RB_RESET_CTRL_REG               0x0100
+#define RTL8366RB_CHIP_CTRL_RESET_HW           1
+#define RTL8366RB_CHIP_CTRL_RESET_SW           (1 << 1)
+
+#define RTL8366RB_CHIP_VERSION_CTRL_REG                0x050A
+#define RTL8366RB_CHIP_VERSION_MASK            0xf
+#define RTL8366RB_CHIP_ID_REG                  0x0509
+#define RTL8366RB_CHIP_ID_8366                 0x5937
+
+/* PHY registers control */
+#define RTL8366RB_PHY_ACCESS_CTRL_REG          0x8000
+#define RTL8366RB_PHY_ACCESS_DATA_REG          0x8002
+
+#define RTL8366RB_PHY_CTRL_READ                        1
+#define RTL8366RB_PHY_CTRL_WRITE               0
+
+#define RTL8366RB_PHY_REG_MASK                 0x1f
+#define RTL8366RB_PHY_PAGE_OFFSET              5
+#define RTL8366RB_PHY_PAGE_MASK                        (0xf << 5)
+#define RTL8366RB_PHY_NO_OFFSET                        9
+#define RTL8366RB_PHY_NO_MASK                  (0x1f << 9)
+
+#define RTL8366RB_VLAN_INGRESS_CTRL2_REG       0x037f
+
+/* LED control registers */
+#define RTL8366RB_LED_BLINKRATE_REG            0x0430
+#define RTL8366RB_LED_BLINKRATE_BIT            0
+#define RTL8366RB_LED_BLINKRATE_MASK           0x0007
+
+#define RTL8366RB_LED_CTRL_REG                 0x0431
+#define RTL8366RB_LED_0_1_CTRL_REG             0x0432
+#define RTL8366RB_LED_2_3_CTRL_REG             0x0433
+
+#define RTL8366RB_MIB_COUNT                    33
+#define RTL8366RB_GLOBAL_MIB_COUNT             1
+#define RTL8366RB_MIB_COUNTER_PORT_OFFSET      0x0050
+#define RTL8366RB_MIB_COUNTER_BASE             0x1000
+#define RTL8366RB_MIB_CTRL_REG                 0x13F0
+#define RTL8366RB_MIB_CTRL_USER_MASK           0x0FFC
+#define RTL8366RB_MIB_CTRL_BUSY_MASK           BIT(0)
+#define RTL8366RB_MIB_CTRL_RESET_MASK          BIT(1)
+#define RTL8366RB_MIB_CTRL_PORT_RESET(_p)      BIT(2 + (_p))
+#define RTL8366RB_MIB_CTRL_GLOBAL_RESET                BIT(11)
+
+#define RTL8366RB_PORT_VLAN_CTRL_BASE          0x0063
+#define RTL8366RB_PORT_VLAN_CTRL_REG(_p)  \
+               (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366RB_PORT_VLAN_CTRL_MASK          0xf
+#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p)     (4 * ((_p) % 4))
+
+
+#define RTL8366RB_VLAN_TABLE_READ_BASE         0x018C
+#define RTL8366RB_VLAN_TABLE_WRITE_BASE                0x0185
+
+
+#define RTL8366RB_TABLE_ACCESS_CTRL_REG                0x0180
+#define RTL8366RB_TABLE_VLAN_READ_CTRL         0x0E01
+#define RTL8366RB_TABLE_VLAN_WRITE_CTRL                0x0F01
+
+#define RTL8366RB_VLAN_MC_BASE(_x)             (0x0020 + (_x) * 3)
+
+
+#define RTL8366RB_PORT_LINK_STATUS_BASE                0x0014
+#define RTL8366RB_PORT_STATUS_SPEED_MASK       0x0003
+#define RTL8366RB_PORT_STATUS_DUPLEX_MASK      0x0004
+#define RTL8366RB_PORT_STATUS_LINK_MASK                0x0010
+#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK     0x0020
+#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK     0x0040
+#define RTL8366RB_PORT_STATUS_AN_MASK          0x0080
+
+
+#define RTL8366RB_PORT_NUM_CPU         5
+#define RTL8366RB_NUM_PORTS            6
+#define RTL8366RB_NUM_VLANS            16
+#define RTL8366RB_NUM_LEDGROUPS                4
+#define RTL8366RB_NUM_VIDS             4096
+#define RTL8366RB_PRIORITYMAX          7
+#define RTL8366RB_FIDMAX               7
+
+
+#define RTL8366RB_PORT_1               (1 << 0) /* In userspace port 0 */
+#define RTL8366RB_PORT_2               (1 << 1) /* In userspace port 1 */
+#define RTL8366RB_PORT_3               (1 << 2) /* In userspace port 2 */
+#define RTL8366RB_PORT_4               (1 << 3) /* In userspace port 3 */
+#define RTL8366RB_PORT_5               (1 << 4) /* In userspace port 4 */
+
+#define RTL8366RB_PORT_CPU             (1 << 5) /* CPU port */
+
+#define RTL8366RB_PORT_ALL             (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4 |     \
+                                        RTL8366RB_PORT_5 |     \
+                                        RTL8366RB_PORT_CPU)
+
+#define RTL8366RB_PORT_ALL_BUT_CPU     (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4 |     \
+                                        RTL8366RB_PORT_5)
+
+#define RTL8366RB_PORT_ALL_EXTERNAL    (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4)
+
+#define RTL8366RB_PORT_ALL_INTERNAL     RTL8366RB_PORT_CPU
+
+#define RTL8366RB_VLAN_VID_MASK                0xfff
+#define RTL8366RB_VLAN_PRIORITY_SHIFT  12
+#define RTL8366RB_VLAN_PRIORITY_MASK   0x7
+#define RTL8366RB_VLAN_UNTAG_SHIFT     8
+#define RTL8366RB_VLAN_UNTAG_MASK      0xff
+#define RTL8366RB_VLAN_MEMBER_MASK     0xff
+#define RTL8366RB_VLAN_FID_MASK                0x7
+
+
+/* Port ingress bandwidth control */
+#define RTL8366RB_IB_BASE              0x0200
+#define RTL8366RB_IB_REG(pnum)         (RTL8366RB_IB_BASE + pnum)
+#define RTL8366RB_IB_BDTH_MASK         0x3fff
+#define RTL8366RB_IB_PREIFG_OFFSET     14
+#define RTL8366RB_IB_PREIFG_MASK       (1 << RTL8366RB_IB_PREIFG_OFFSET)
+
+/* Port egress bandwidth control */
+#define RTL8366RB_EB_BASE              0x02d1
+#define RTL8366RB_EB_REG(pnum)         (RTL8366RB_EB_BASE + pnum)
+#define RTL8366RB_EB_BDTH_MASK         0x3fff
+#define RTL8366RB_EB_PREIFG_REG        0x02f8
+#define RTL8366RB_EB_PREIFG_OFFSET     9
+#define RTL8366RB_EB_PREIFG_MASK       (1 << RTL8366RB_EB_PREIFG_OFFSET)
+
+#define RTL8366RB_BDTH_SW_MAX          1048512
+#define RTL8366RB_BDTH_UNIT            64
+#define RTL8366RB_BDTH_REG_DEFAULT     16383
+
+/* QOS */
+#define RTL8366RB_QOS_BIT              15
+#define RTL8366RB_QOS_MASK             (1 << RTL8366RB_QOS_BIT)
+/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */
+#define RTL8366RB_QOS_DEFAULT_PREIFG   1
+
+
+#define RTL8366RB_MIB_RXB_ID           0       /* IfInOctets */
+#define RTL8366RB_MIB_TXB_ID           20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 4, "EtherStatsOctets"                          },
+       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 10, 2, "EtherFragments"                            },
+       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 24, 2, "EtherOversizeStats"                        },
+       { 0, 26, 2, "EtherStatsJabbers"                         },
+       { 0, 28, 2, "IfInUcastPkts"                             },
+       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
+       { 0, 34, 2, "EtherStatsDropEvents"                      },
+       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
+       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
+       { 0, 40, 2, "Dot3InPauseFrames"                         },
+       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 44, 4, "IfOutOctets"                               },
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
+       { 0, 66, 2, "IfOutUcastPkts"                            },
+       { 0, 68, 2, "IfOutMulticastPkts"                        },
+       { 0, 70, 2, "IfOutBroadcastPkts"                        },
+};
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int rtl8366rb_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       u32 data;
+
+       rtl8366_smi_write_reg_noack(smi, RTL8366RB_RESET_CTRL_REG,
+                                   RTL8366RB_CHIP_CTRL_RESET_HW);
+       do {
+               msleep(1);
+               if (rtl8366_smi_read_reg(smi, RTL8366RB_RESET_CTRL_REG, &data))
+                       return -EIO;
+
+               if (!(data & RTL8366RB_CHIP_CTRL_RESET_HW))
+                       break;
+       } while (--timeout);
+
+       if (!timeout) {
+               printk("Timeout waiting for the switch to reset\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_setup(struct rtl8366_smi *smi)
+{
+       int err;
+#ifdef CONFIG_OF
+       unsigned i;
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK,
+               RTL8366RB_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366RB_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366RB_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366RB_VLAN_INGRESS_CTRL2_REG, RTL8366RB_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
+static int rtl8366rb_read_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_no, u32 page, u32 addr, u32 *data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366RB_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366RB_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                   RTL8366RB_PHY_CTRL_READ);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
+             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
+             (addr & RTL8366RB_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, 0);
+       if (ret)
+               return ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_PHY_ACCESS_DATA_REG, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366rb_write_phy_reg(struct rtl8366_smi *smi,
+                                 u32 phy_no, u32 page, u32 addr, u32 data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366RB_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366RB_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                   RTL8366RB_PHY_CTRL_WRITE);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
+             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
+             (addr & RTL8366RB_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366rb_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                    int port, unsigned long long *val)
+{
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8366RB_NUM_PORTS || counter >= RTL8366RB_MIB_COUNT)
+               return -EINVAL;
+
+       addr = RTL8366RB_MIB_COUNTER_BASE +
+              RTL8366RB_MIB_COUNTER_PORT_OFFSET * (port) +
+              rtl8366rb_mib_counters[counter].offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       data = 0; /* writing data will be discard by ASIC */
+       err = rtl8366_smi_write_reg(smi, addr, data);
+       if (err)
+               return err;
+
+       /* read MIB control register */
+       err =  rtl8366_smi_read_reg(smi, RTL8366RB_MIB_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       if (data & RTL8366RB_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8366RB_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       mibvalue = 0;
+       for (i = rtl8366rb_mib_counters[counter].length; i > 0; i--) {
+               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
+               if (err)
+                       return err;
+
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8366rb_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                                struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8366RB_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_VLAN_TABLE_WRITE_BASE,
+                                   vid & RTL8366RB_VLAN_VID_MASK);
+       if (err)
+               return err;
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366RB_TABLE_VLAN_READ_CTRL);
+       if (err)
+               return err;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366RB_VLAN_TABLE_READ_BASE + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlan4k->vid = vid;
+       vlan4k->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
+                       RTL8366RB_VLAN_UNTAG_MASK;
+       vlan4k->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
+       vlan4k->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_set_vlan_4k(struct rtl8366_smi *smi,
+                                const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8366RB_NUM_VIDS ||
+           vlan4k->member > RTL8366RB_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8366RB_VLAN_UNTAG_MASK ||
+           vlan4k->fid > RTL8366RB_FIDMAX)
+               return -EINVAL;
+
+       data[0] = vlan4k->vid & RTL8366RB_VLAN_VID_MASK;
+       data[1] = (vlan4k->member & RTL8366RB_VLAN_MEMBER_MASK) |
+                 ((vlan4k->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
+                       RTL8366RB_VLAN_UNTAG_SHIFT);
+       data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366RB_VLAN_TABLE_WRITE_BASE + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366RB_TABLE_VLAN_WRITE_CTRL);
+
+       return err;
+}
+
+static int rtl8366rb_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                                struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8366RB_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366RB_VLAN_MC_BASE(index) + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlanmc->vid = data[0] & RTL8366RB_VLAN_VID_MASK;
+       vlanmc->priority = (data[0] >> RTL8366RB_VLAN_PRIORITY_SHIFT) &
+                          RTL8366RB_VLAN_PRIORITY_MASK;
+       vlanmc->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
+                       RTL8366RB_VLAN_UNTAG_MASK;
+       vlanmc->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
+       vlanmc->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                                const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       if (index >= RTL8366RB_NUM_VLANS ||
+           vlanmc->vid >= RTL8366RB_NUM_VIDS ||
+           vlanmc->priority > RTL8366RB_PRIORITYMAX ||
+           vlanmc->member > RTL8366RB_VLAN_MEMBER_MASK ||
+           vlanmc->untag > RTL8366RB_VLAN_UNTAG_MASK ||
+           vlanmc->fid > RTL8366RB_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->vid & RTL8366RB_VLAN_VID_MASK) |
+                 ((vlanmc->priority & RTL8366RB_VLAN_PRIORITY_MASK) <<
+                       RTL8366RB_VLAN_PRIORITY_SHIFT);
+       data[1] = (vlanmc->member & RTL8366RB_VLAN_MEMBER_MASK) |
+                 ((vlanmc->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
+                       RTL8366RB_VLAN_UNTAG_SHIFT);
+       data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366RB_VLAN_MC_BASE(index) + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+                                  &data);
+       if (err)
+               return err;
+
+       *val = (data >> RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)) &
+              RTL8366RB_PORT_VLAN_CTRL_MASK;
+
+       return 0;
+
+}
+
+static int rtl8366rb_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8366RB_NUM_PORTS || index >= RTL8366RB_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+                               RTL8366RB_PORT_VLAN_CTRL_MASK <<
+                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port),
+                               (index & RTL8366RB_PORT_VLAN_CTRL_MASK) <<
+                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366rb_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8366RB_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8366RB_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8366rb_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN,
+                               (enable) ? RTL8366RB_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366rb_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR,
+                               RTL8366RB_SGCR_EN_VLAN_4KTB,
+                               (enable) ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0);
+}
+
+static int rtl8366rb_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, (1 << port),
+                               (enable) ? 0 : (1 << port));
+}
+
+static int rtl8366rb_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
+                               RTL8366RB_MIB_CTRL_GLOBAL_RESET);
+}
+
+static int rtl8366rb_sw_get_blinkrate(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_LED_BLINKRATE_REG, &data);
+
+       val->value.i = (data & (RTL8366RB_LED_BLINKRATE_MASK));
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_blinkrate(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->value.i >= 6)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_LED_BLINKRATE_REG,
+                               RTL8366RB_LED_BLINKRATE_MASK,
+                               val->value.i);
+}
+
+static int rtl8366rb_sw_get_learning_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_SSCR0, &data);
+       val->value.i = !data;
+
+       return 0;
+}
+
+
+static int rtl8366rb_sw_set_learning_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 portmask = 0;
+       int err = 0;
+
+       if (!val->value.i)
+               portmask = RTL8366RB_PORT_ALL;
+
+       /* set learning for all ports */
+       REG_WR(smi, RTL8366RB_SSCR0, portmask);
+
+       /* set auto ageing for all ports */
+       REG_WR(smi, RTL8366RB_SSCR1, portmask);
+
+       return 0;
+}
+
+static int rtl8366rb_sw_get_port_link(struct switch_dev *dev,
+                                    int port,
+                                    struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PORT_LINK_STATUS_BASE + (port / 2),
+                            &data);
+
+       if (port % 2)
+               data = data >> 8;
+
+       link->link = !!(data & RTL8366RB_PORT_STATUS_LINK_MASK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8366RB_PORT_STATUS_DUPLEX_MASK);
+       link->rx_flow = !!(data & RTL8366RB_PORT_STATUS_RXPAUSE_MASK);
+       link->tx_flow = !!(data & RTL8366RB_PORT_STATUS_TXPAUSE_MASK);
+       link->aneg = !!(data & RTL8366RB_PORT_STATUS_AN_MASK);
+
+       speed = (data & RTL8366RB_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       u32 mask;
+       u32 reg;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       if (val->port_vlan == RTL8366RB_PORT_NUM_CPU) {
+               reg = RTL8366RB_LED_BLINKRATE_REG;
+               mask = 0xF << 4;
+               data = val->value.i << 4;
+       } else {
+               reg = RTL8366RB_LED_CTRL_REG;
+               mask = 0xF << (val->port_vlan * 4),
+               data = val->value.i << (val->port_vlan * 4);
+       }
+
+       return rtl8366_smi_rmwr(smi, reg, mask, data);
+}
+
+static int rtl8366rb_sw_get_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+
+       if (val->port_vlan >= RTL8366RB_NUM_LEDGROUPS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_LED_CTRL_REG, &data);
+       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_disable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 mask, data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       mask = 1 << val->port_vlan ;
+       if (val->value.i)
+               data = mask;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, mask, data);
+}
+
+static int rtl8366rb_sw_get_port_disable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PECR, &data);
+       if (data & (1 << val->port_vlan))
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_rate_in(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
+               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
+       else
+               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_IB_REG(val->port_vlan),
+               RTL8366RB_IB_BDTH_MASK | RTL8366RB_IB_PREIFG_MASK,
+               val->value.i |
+               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_IB_PREIFG_OFFSET));
+
+}
+
+static int rtl8366rb_sw_get_port_rate_in(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_IB_REG(val->port_vlan), &data);
+       data &= RTL8366RB_IB_BDTH_MASK;
+       if (data < RTL8366RB_IB_BDTH_MASK)
+               data += 1;
+
+       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_rate_out(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_rmwr(smi, RTL8366RB_EB_PREIFG_REG,
+               RTL8366RB_EB_PREIFG_MASK,
+               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_EB_PREIFG_OFFSET));
+
+       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
+               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
+       else
+               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_EB_REG(val->port_vlan),
+                       RTL8366RB_EB_BDTH_MASK, val->value.i );
+
+}
+
+static int rtl8366rb_sw_get_port_rate_out(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_EB_REG(val->port_vlan), &data);
+       data &= RTL8366RB_EB_BDTH_MASK;
+       if (data < RTL8366RB_EB_BDTH_MASK)
+               data += 1;
+
+       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_qos_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_QOS_MASK;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_QOS_MASK, data);
+}
+
+static int rtl8366rb_sw_get_qos_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_SGCR, &data);
+       if (data & RTL8366RB_QOS_MASK)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_RX;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_RX, data);
+}
+
+static int rtl8366rb_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_RX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_TX;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_TX, data);
+}
+
+static int rtl8366rb_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_TX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_monitor_isolation_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_ISO;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_ISO, data);
+}
+
+static int rtl8366rb_sw_get_monitor_isolation_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_ISO)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_pause_frames_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_SPC;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_SPC, data);
+}
+
+static int rtl8366rb_sw_get_mirror_pause_frames_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_SPC)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       data = RTL8366RB_PMCR_MONITOR_PORT(val->value.i);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MONITOR_PORT_MASK, data);
+}
+
+static int rtl8366rb_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       val->value.i = (data & RTL8366RB_PMCR_MONITOR_PORT_MASK) >> 4;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_source_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       data = RTL8366RB_PMCR_SOURCE_PORT(val->value.i);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_SOURCE_PORT_MASK, data);
+}
+
+static int rtl8366rb_sw_get_mirror_source_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       val->value.i = data & RTL8366RB_PMCR_SOURCE_PORT_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
+                               RTL8366RB_MIB_CTRL_PORT_RESET(val->port_vlan));
+}
+
+static int rtl8366rb_sw_get_port_stats(struct switch_dev *dev, int port,
+                                       struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8366RB_MIB_TXB_ID, RTL8366RB_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8366rb_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_learning",
+               .description = "Enable learning, enable aging",
+               .set = rtl8366rb_sw_set_learning_enable,
+               .get = rtl8366rb_sw_get_learning_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8366rb_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "blinkrate",
+               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
+               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
+               .set = rtl8366rb_sw_set_blinkrate,
+               .get = rtl8366rb_sw_get_blinkrate,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_qos",
+               .description = "Enable QOS",
+               .set = rtl8366rb_sw_set_qos_enable,
+               .get = rtl8366rb_sw_get_qos_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = rtl8366rb_sw_set_mirror_rx_enable,
+               .get = rtl8366rb_sw_get_mirror_rx_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = rtl8366rb_sw_set_mirror_tx_enable,
+               .get = rtl8366rb_sw_get_mirror_tx_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_monitor_isolation",
+               .description = "Enable isolation of monitor port (TX packets will be dropped)",
+               .set = rtl8366rb_sw_set_monitor_isolation_enable,
+               .get = rtl8366rb_sw_get_monitor_isolation_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_pause_frames",
+               .description = "Enable mirroring of RX pause frames",
+               .set = rtl8366rb_sw_set_mirror_pause_frames_enable,
+               .get = rtl8366rb_sw_get_mirror_pause_frames_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = rtl8366rb_sw_set_mirror_monitor_port,
+               .get = rtl8366rb_sw_get_mirror_monitor_port,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = rtl8366rb_sw_set_mirror_source_port,
+               .get = rtl8366rb_sw_get_mirror_source_port,
+               .max = 5
+       },
+};
+
+static struct switch_attr rtl8366rb_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8366rb_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
+               .max = 15,
+               .set = rtl8366rb_sw_set_port_led,
+               .get = rtl8366rb_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "disable",
+               .description = "Get/Set port state (enabled or disabled)",
+               .max = 1,
+               .set = rtl8366rb_sw_set_port_disable,
+               .get = rtl8366rb_sw_get_port_disable,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "rate_in",
+               .description = "Get/Set port ingress (incoming) bandwidth limit in kbps",
+               .max = RTL8366RB_BDTH_SW_MAX,
+               .set = rtl8366rb_sw_set_port_rate_in,
+               .get = rtl8366rb_sw_get_port_rate_in,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "rate_out",
+               .description = "Get/Set port egress (outgoing) bandwidth limit in kbps",
+               .max = RTL8366RB_BDTH_SW_MAX,
+               .set = rtl8366rb_sw_set_port_rate_out,
+               .get = rtl8366rb_sw_get_port_rate_out,
+       },
+};
+
+static struct switch_attr rtl8366rb_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8366RB_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8366_ops = {
+       .attr_global = {
+               .attr = rtl8366rb_globals,
+               .n_attr = ARRAY_SIZE(rtl8366rb_globals),
+       },
+       .attr_port = {
+               .attr = rtl8366rb_port,
+               .n_attr = ARRAY_SIZE(rtl8366rb_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8366rb_vlan,
+               .n_attr = ARRAY_SIZE(rtl8366rb_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366rb_sw_get_port_link,
+       .get_port_stats = rtl8366rb_sw_get_port_stats,
+};
+
+static int rtl8366rb_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8366RB";
+       dev->cpu_port = RTL8366RB_PORT_NUM_CPU;
+       dev->ports = RTL8366RB_NUM_PORTS;
+       dev->vlans = RTL8366RB_NUM_VIDS;
+       dev->ops = &rtl8366_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8366rb_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8366rb_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8366rb_read_phy_reg(smi, addr, 0, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8366rb_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8366rb_write_phy_reg(smi, addr, 0, reg, val);
+       /* flush write */
+       (void) rtl8366rb_read_phy_reg(smi, addr, 0, reg, &t);
+
+       return err;
+}
+
+static int rtl8366rb_detect(struct rtl8366_smi *smi)
+{
+       u32 chip_id = 0;
+       u32 chip_ver = 0;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_ID_REG, &chip_id);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip id\n");
+               return ret;
+       }
+
+       switch (chip_id) {
+       case RTL8366RB_CHIP_ID_8366:
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_VERSION_CTRL_REG,
+                                  &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
+                chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8366rb_smi_ops = {
+       .detect         = rtl8366rb_detect,
+       .reset_chip     = rtl8366rb_reset_chip,
+       .setup          = rtl8366rb_setup,
+
+       .mii_read       = rtl8366rb_mii_read,
+       .mii_write      = rtl8366rb_mii_write,
+
+       .get_vlan_mc    = rtl8366rb_get_vlan_mc,
+       .set_vlan_mc    = rtl8366rb_set_vlan_mc,
+       .get_vlan_4k    = rtl8366rb_get_vlan_4k,
+       .set_vlan_4k    = rtl8366rb_set_vlan_4k,
+       .get_mc_index   = rtl8366rb_get_mc_index,
+       .set_mc_index   = rtl8366rb_set_mc_index,
+       .get_mib_counter = rtl8366rb_get_mib_counter,
+       .is_vlan_valid  = rtl8366rb_is_vlan_valid,
+       .enable_vlan    = rtl8366rb_enable_vlan,
+       .enable_vlan4k  = rtl8366rb_enable_vlan4k,
+       .enable_port    = rtl8366rb_enable_port,
+};
+
+static int rtl8366rb_probe(struct platform_device *pdev)
+{
+       static int rtl8366_smi_version_printed;
+       struct rtl8366_smi *smi;
+       int err;
+
+       if (!rtl8366_smi_version_printed++)
+               printk(KERN_NOTICE RTL8366RB_DRIVER_DESC
+                      " version " RTL8366RB_DRIVER_VER"\n");
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 10;
+       smi->cmd_read = 0xa9;
+       smi->cmd_write = 0xa8;
+       smi->ops = &rtl8366rb_smi_ops;
+       smi->cpu_port = RTL8366RB_PORT_NUM_CPU;
+       smi->num_ports = RTL8366RB_NUM_PORTS;
+       smi->num_vlan_mc = RTL8366RB_NUM_VLANS;
+       smi->mib_counters = rtl8366rb_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8366rb_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8366rb_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8366rb_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366rb_match[] = {
+       { .compatible = "realtek,rtl8366rb" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366rb_match);
+#endif
+
+static struct platform_driver rtl8366rb_driver = {
+       .driver = {
+               .name           = RTL8366RB_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(rtl8366rb_match),
+       },
+       .probe          = rtl8366rb_probe,
+       .remove         = rtl8366rb_remove,
+};
+
+static int __init rtl8366rb_module_init(void)
+{
+       return platform_driver_register(&rtl8366rb_driver);
+}
+module_init(rtl8366rb_module_init);
+
+static void __exit rtl8366rb_module_exit(void)
+{
+       platform_driver_unregister(&rtl8366rb_driver);
+}
+module_exit(rtl8366rb_module_exit);
+
+MODULE_DESCRIPTION(RTL8366RB_DRIVER_DESC);
+MODULE_VERSION(RTL8366RB_DRIVER_VER);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
+MODULE_AUTHOR("Roman Yeryomin <roman@advem.lv>");
+MODULE_AUTHOR("Colin Leitner <colin.leitner@googlemail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8366RB_DRIVER_NAME);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8366s.c b/target/linux/generic/files-4.14/drivers/net/phy/rtl8366s.c
new file mode 100644 (file)
index 0000000..3f458f9
--- /dev/null
@@ -0,0 +1,1320 @@
+/*
+ * Platform driver for the Realtek RTL8366S ethernet switch
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8366.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8366S_DRIVER_DESC   "Realtek RTL8366S ethernet switch driver"
+#define RTL8366S_DRIVER_VER    "0.2.2"
+
+#define RTL8366S_PHY_NO_MAX    4
+#define RTL8366S_PHY_PAGE_MAX  7
+#define RTL8366S_PHY_ADDR_MAX  31
+
+/* Switch Global Configuration register */
+#define RTL8366S_SGCR                          0x0000
+#define RTL8366S_SGCR_EN_BC_STORM_CTRL         BIT(0)
+#define RTL8366S_SGCR_MAX_LENGTH(_x)           (_x << 4)
+#define RTL8366S_SGCR_MAX_LENGTH_MASK          RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_MAX_LENGTH_1522          RTL8366S_SGCR_MAX_LENGTH(0x0)
+#define RTL8366S_SGCR_MAX_LENGTH_1536          RTL8366S_SGCR_MAX_LENGTH(0x1)
+#define RTL8366S_SGCR_MAX_LENGTH_1552          RTL8366S_SGCR_MAX_LENGTH(0x2)
+#define RTL8366S_SGCR_MAX_LENGTH_16000         RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_EN_VLAN                  BIT(13)
+
+/* Port Enable Control register */
+#define RTL8366S_PECR                          0x0001
+
+/* Green Ethernet Feature (based on GPL_BELKIN_F5D8235-4_v1000 v1.01.24) */
+#define RTL8366S_GREEN_ETHERNET_CTRL_REG       0x000a
+#define RTL8366S_GREEN_ETHERNET_CTRL_MASK      0x0018
+#define RTL8366S_GREEN_ETHERNET_TX_BIT         (1 << 3)
+#define RTL8366S_GREEN_ETHERNET_RX_BIT         (1 << 4)
+
+/* Switch Security Control registers */
+#define RTL8366S_SSCR0                         0x0002
+#define RTL8366S_SSCR1                         0x0003
+#define RTL8366S_SSCR2                         0x0004
+#define RTL8366S_SSCR2_DROP_UNKNOWN_DA         BIT(0)
+
+#define RTL8366S_RESET_CTRL_REG                        0x0100
+#define RTL8366S_CHIP_CTRL_RESET_HW            1
+#define RTL8366S_CHIP_CTRL_RESET_SW            (1 << 1)
+
+#define RTL8366S_CHIP_VERSION_CTRL_REG         0x0104
+#define RTL8366S_CHIP_VERSION_MASK             0xf
+#define RTL8366S_CHIP_ID_REG                   0x0105
+#define RTL8366S_CHIP_ID_8366                  0x8366
+
+/* PHY registers control */
+#define RTL8366S_PHY_ACCESS_CTRL_REG           0x8028
+#define RTL8366S_PHY_ACCESS_DATA_REG           0x8029
+
+#define RTL8366S_PHY_CTRL_READ                 1
+#define RTL8366S_PHY_CTRL_WRITE                        0
+
+#define RTL8366S_PHY_REG_MASK                  0x1f
+#define RTL8366S_PHY_PAGE_OFFSET               5
+#define RTL8366S_PHY_PAGE_MASK                 (0x7 << 5)
+#define RTL8366S_PHY_NO_OFFSET                 9
+#define RTL8366S_PHY_NO_MASK                   (0x1f << 9)
+
+/* Green Ethernet Feature for PHY ports */
+#define RTL8366S_PHY_POWER_SAVING_CTRL_REG     12
+#define RTL8366S_PHY_POWER_SAVING_MASK         0x1000
+
+/* LED control registers */
+#define RTL8366S_LED_BLINKRATE_REG             0x0420
+#define RTL8366S_LED_BLINKRATE_BIT             0
+#define RTL8366S_LED_BLINKRATE_MASK            0x0007
+
+#define RTL8366S_LED_CTRL_REG                  0x0421
+#define RTL8366S_LED_0_1_CTRL_REG              0x0422
+#define RTL8366S_LED_2_3_CTRL_REG              0x0423
+
+#define RTL8366S_MIB_COUNT                     33
+#define RTL8366S_GLOBAL_MIB_COUNT              1
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET       0x0040
+#define RTL8366S_MIB_COUNTER_BASE              0x1000
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET2      0x0008
+#define RTL8366S_MIB_COUNTER_BASE2             0x1180
+#define RTL8366S_MIB_CTRL_REG                  0x11F0
+#define RTL8366S_MIB_CTRL_USER_MASK            0x01FF
+#define RTL8366S_MIB_CTRL_BUSY_MASK            0x0001
+#define RTL8366S_MIB_CTRL_RESET_MASK           0x0002
+
+#define RTL8366S_MIB_CTRL_GLOBAL_RESET_MASK    0x0004
+#define RTL8366S_MIB_CTRL_PORT_RESET_BIT       0x0003
+#define RTL8366S_MIB_CTRL_PORT_RESET_MASK      0x01FC
+
+
+#define RTL8366S_PORT_VLAN_CTRL_BASE           0x0058
+#define RTL8366S_PORT_VLAN_CTRL_REG(_p)  \
+               (RTL8366S_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366S_PORT_VLAN_CTRL_MASK           0xf
+#define RTL8366S_PORT_VLAN_CTRL_SHIFT(_p)      (4 * ((_p) % 4))
+
+
+#define RTL8366S_VLAN_TABLE_READ_BASE          0x018B
+#define RTL8366S_VLAN_TABLE_WRITE_BASE         0x0185
+
+#define RTL8366S_VLAN_TB_CTRL_REG              0x010F
+
+#define RTL8366S_TABLE_ACCESS_CTRL_REG         0x0180
+#define RTL8366S_TABLE_VLAN_READ_CTRL          0x0E01
+#define RTL8366S_TABLE_VLAN_WRITE_CTRL         0x0F01
+
+#define RTL8366S_VLAN_MC_BASE(_x)              (0x0016 + (_x) * 2)
+
+#define RTL8366S_VLAN_MEMBERINGRESS_REG                0x0379
+
+#define RTL8366S_PORT_LINK_STATUS_BASE         0x0060
+#define RTL8366S_PORT_STATUS_SPEED_MASK                0x0003
+#define RTL8366S_PORT_STATUS_DUPLEX_MASK       0x0004
+#define RTL8366S_PORT_STATUS_LINK_MASK         0x0010
+#define RTL8366S_PORT_STATUS_TXPAUSE_MASK      0x0020
+#define RTL8366S_PORT_STATUS_RXPAUSE_MASK      0x0040
+#define RTL8366S_PORT_STATUS_AN_MASK           0x0080
+
+
+#define RTL8366S_PORT_NUM_CPU          5
+#define RTL8366S_NUM_PORTS             6
+#define RTL8366S_NUM_VLANS             16
+#define RTL8366S_NUM_LEDGROUPS         4
+#define RTL8366S_NUM_VIDS              4096
+#define RTL8366S_PRIORITYMAX           7
+#define RTL8366S_FIDMAX                        7
+
+
+#define RTL8366S_PORT_1                        (1 << 0) /* In userspace port 0 */
+#define RTL8366S_PORT_2                        (1 << 1) /* In userspace port 1 */
+#define RTL8366S_PORT_3                        (1 << 2) /* In userspace port 2 */
+#define RTL8366S_PORT_4                        (1 << 3) /* In userspace port 3 */
+
+#define RTL8366S_PORT_UNKNOWN          (1 << 4) /* No known connection */
+#define RTL8366S_PORT_CPU              (1 << 5) /* CPU port */
+
+#define RTL8366S_PORT_ALL              (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4 |      \
+                                        RTL8366S_PORT_UNKNOWN | \
+                                        RTL8366S_PORT_CPU)
+
+#define RTL8366S_PORT_ALL_BUT_CPU      (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4 |      \
+                                        RTL8366S_PORT_UNKNOWN)
+
+#define RTL8366S_PORT_ALL_EXTERNAL     (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4)
+
+#define RTL8366S_PORT_ALL_INTERNAL     (RTL8366S_PORT_UNKNOWN | \
+                                        RTL8366S_PORT_CPU)
+
+#define RTL8366S_VLAN_VID_MASK         0xfff
+#define RTL8366S_VLAN_PRIORITY_SHIFT   12
+#define RTL8366S_VLAN_PRIORITY_MASK    0x7
+#define RTL8366S_VLAN_MEMBER_MASK      0x3f
+#define RTL8366S_VLAN_UNTAG_SHIFT      6
+#define RTL8366S_VLAN_UNTAG_MASK       0x3f
+#define RTL8366S_VLAN_FID_SHIFT                12
+#define RTL8366S_VLAN_FID_MASK         0x7
+
+#define RTL8366S_MIB_RXB_ID            0       /* IfInOctets */
+#define RTL8366S_MIB_TXB_ID            20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366s_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 4, "EtherStatsOctets"                          },
+       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 10, 2, "EtherFragments"                            },
+       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 24, 2, "EtherOversizeStats"                        },
+       { 0, 26, 2, "EtherStatsJabbers"                         },
+       { 0, 28, 2, "IfInUcastPkts"                             },
+       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
+       { 0, 34, 2, "EtherStatsDropEvents"                      },
+       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
+       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
+       { 0, 40, 2, "Dot3InPauseFrames"                         },
+       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 44, 4, "IfOutOctets"                               },
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+
+       /*
+        * The following counters are accessible at a different
+        * base address.
+        */
+       { 1,  0, 2, "Dot1dTpPortInDiscards"                     },
+       { 1,  2, 2, "IfOutUcastPkts"                            },
+       { 1,  4, 2, "IfOutMulticastPkts"                        },
+       { 1,  6, 2, "IfOutBroadcastPkts"                        },
+};
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int rtl8366s_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       u32 data;
+
+       rtl8366_smi_write_reg_noack(smi, RTL8366S_RESET_CTRL_REG,
+                                   RTL8366S_CHIP_CTRL_RESET_HW);
+       do {
+               msleep(1);
+               if (rtl8366_smi_read_reg(smi, RTL8366S_RESET_CTRL_REG, &data))
+                       return -EIO;
+
+               if (!(data & RTL8366S_CHIP_CTRL_RESET_HW))
+                       break;
+       } while (--timeout);
+
+       if (!timeout) {
+               printk("Timeout waiting for the switch to reset\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_read_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_no, u32 page, u32 addr, u32 *data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366S_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366S_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366S_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
+                                   RTL8366S_PHY_CTRL_READ);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
+             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
+             (addr & RTL8366S_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, 0);
+       if (ret)
+               return ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_PHY_ACCESS_DATA_REG, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366s_write_phy_reg(struct rtl8366_smi *smi,
+                                 u32 phy_no, u32 page, u32 addr, u32 data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366S_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366S_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366S_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
+                                   RTL8366S_PHY_CTRL_WRITE);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
+             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
+             (addr & RTL8366S_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366s_set_green_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+       u32 phyData;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       if (enable)
+               phyData |= RTL8366S_PHY_POWER_SAVING_MASK;
+       else
+               phyData &= ~RTL8366S_PHY_POWER_SAVING_MASK;
+
+       err = rtl8366s_write_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, phyData);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8366s_set_green(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+       unsigned i;
+       u32 data = 0;
+
+       if (!enable) {
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       rtl8366s_set_green_port(smi, i, 0);
+               }
+       }
+
+       if (enable)
+               data = (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT);
+
+       REG_RMW(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, RTL8366S_GREEN_ETHERNET_CTRL_MASK, data);
+
+       return 0;
+}
+
+static int rtl8366s_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata;
+       int err;
+       unsigned i;
+#ifdef CONFIG_OF
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+#endif
+
+       pdata = smi->parent->platform_data;
+       if (pdata && pdata->num_initvals && pdata->initvals) {
+               dev_info(smi->parent, "applying initvals\n");
+               for (i = 0; i < pdata->num_initvals; i++)
+                       REG_WR(smi, pdata->initvals[i].reg,
+                              pdata->initvals[i].val);
+       }
+
+#ifdef CONFIG_OF
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+
+       if (of_property_read_bool(np, "realtek,green-ethernet-features")) {
+               dev_info(smi->parent, "activating Green Ethernet features\n");
+
+               err = rtl8366s_set_green(smi, 1);
+               if (err)
+                       return err;
+
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       err = rtl8366s_set_green_port(smi, i, 1);
+                       if (err)
+                               return err;
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366S_SGCR, RTL8366S_SGCR_MAX_LENGTH_MASK,
+               RTL8366S_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366S_VLAN_MEMBERINGRESS_REG, RTL8366S_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366S_SSCR2, RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
+static int rtl8366_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val)
+{
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8366S_NUM_PORTS || counter >= RTL8366S_MIB_COUNT)
+               return -EINVAL;
+
+       switch (rtl8366s_mib_counters[counter].base) {
+       case 0:
+               addr = RTL8366S_MIB_COUNTER_BASE +
+                      RTL8366S_MIB_COUNTER_PORT_OFFSET * port;
+               break;
+
+       case 1:
+               addr = RTL8366S_MIB_COUNTER_BASE2 +
+                       RTL8366S_MIB_COUNTER_PORT_OFFSET2 * port;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       addr += rtl8366s_mib_counters[counter].offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       data = 0; /* writing data will be discard by ASIC */
+       err = rtl8366_smi_write_reg(smi, addr, data);
+       if (err)
+               return err;
+
+       /* read MIB control register */
+       err =  rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       if (data & RTL8366S_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8366S_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       mibvalue = 0;
+       for (i = rtl8366s_mib_counters[counter].length; i > 0; i--) {
+               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
+               if (err)
+                       return err;
+
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8366s_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8366S_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE,
+                                   vid & RTL8366S_VLAN_VID_MASK);
+       if (err)
+               return err;
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366S_TABLE_VLAN_READ_CTRL);
+       if (err)
+               return err;
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366S_VLAN_TABLE_READ_BASE + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlan4k->vid = vid;
+       vlan4k->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+                       RTL8366S_VLAN_UNTAG_MASK;
+       vlan4k->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+       vlan4k->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+                       RTL8366S_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8366S_NUM_VIDS ||
+           vlan4k->member > RTL8366S_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8366S_VLAN_UNTAG_MASK ||
+           vlan4k->fid > RTL8366S_FIDMAX)
+               return -EINVAL;
+
+       data[0] = vlan4k->vid & RTL8366S_VLAN_VID_MASK;
+       data[1] = (vlan4k->member & RTL8366S_VLAN_MEMBER_MASK) |
+                 ((vlan4k->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+                       RTL8366S_VLAN_UNTAG_SHIFT) |
+                 ((vlan4k->fid & RTL8366S_VLAN_FID_MASK) <<
+                       RTL8366S_VLAN_FID_SHIFT);
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366S_VLAN_TABLE_WRITE_BASE + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366S_TABLE_VLAN_WRITE_CTRL);
+
+       return err;
+}
+
+static int rtl8366s_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8366S_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366S_VLAN_MC_BASE(index) + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlanmc->vid = data[0] & RTL8366S_VLAN_VID_MASK;
+       vlanmc->priority = (data[0] >> RTL8366S_VLAN_PRIORITY_SHIFT) &
+                          RTL8366S_VLAN_PRIORITY_MASK;
+       vlanmc->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+                       RTL8366S_VLAN_UNTAG_MASK;
+       vlanmc->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+                     RTL8366S_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       if (index >= RTL8366S_NUM_VLANS ||
+           vlanmc->vid >= RTL8366S_NUM_VIDS ||
+           vlanmc->priority > RTL8366S_PRIORITYMAX ||
+           vlanmc->member > RTL8366S_VLAN_MEMBER_MASK ||
+           vlanmc->untag > RTL8366S_VLAN_UNTAG_MASK ||
+           vlanmc->fid > RTL8366S_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->vid & RTL8366S_VLAN_VID_MASK) |
+                 ((vlanmc->priority & RTL8366S_VLAN_PRIORITY_MASK) <<
+                       RTL8366S_VLAN_PRIORITY_SHIFT);
+       data[1] = (vlanmc->member & RTL8366S_VLAN_MEMBER_MASK) |
+                 ((vlanmc->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+                       RTL8366S_VLAN_UNTAG_SHIFT) |
+                 ((vlanmc->fid & RTL8366S_VLAN_FID_MASK) <<
+                       RTL8366S_VLAN_FID_SHIFT);
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366S_VLAN_MC_BASE(index) + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
+                                  &data);
+       if (err)
+               return err;
+
+       *val = (data >> RTL8366S_PORT_VLAN_CTRL_SHIFT(port)) &
+              RTL8366S_PORT_VLAN_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8366S_NUM_PORTS || index >= RTL8366S_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
+                               RTL8366S_PORT_VLAN_CTRL_MASK <<
+                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port),
+                               (index & RTL8366S_PORT_VLAN_CTRL_MASK) <<
+                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366s_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR, RTL8366S_SGCR_EN_VLAN,
+                               (enable) ? RTL8366S_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366s_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_VLAN_TB_CTRL_REG,
+                               1, (enable) ? 1 : 0);
+}
+
+static int rtl8366s_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8366S_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8366S_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8366s_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_PECR, (1 << port),
+                               (enable) ? 0 : (1 << port));
+}
+
+static int rtl8366s_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG, 0, (1 << 2));
+}
+
+static int rtl8366s_sw_get_blinkrate(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_LED_BLINKRATE_REG, &data);
+
+       val->value.i = (data & (RTL8366S_LED_BLINKRATE_MASK));
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_blinkrate(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->value.i >= 6)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_LED_BLINKRATE_REG,
+                               RTL8366S_LED_BLINKRATE_MASK,
+                               val->value.i);
+}
+
+static int rtl8366s_sw_get_max_length(struct switch_dev *dev,
+                                       const struct switch_attr *attr,
+                                       struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_SGCR, &data);
+
+       val->value.i = ((data & (RTL8366S_SGCR_MAX_LENGTH_MASK)) >> 4);
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_max_length(struct switch_dev *dev,
+                                       const struct switch_attr *attr,
+                                       struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       char length_code;
+
+       switch (val->value.i) {
+               case 0:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1522;
+                       break;
+               case 1:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1536;
+                       break;
+               case 2:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1552;
+                       break;
+               case 3:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_16000;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR,
+                       RTL8366S_SGCR_MAX_LENGTH_MASK,
+                       length_code);
+}
+
+static int rtl8366s_sw_get_learning_enable(struct switch_dev *dev,
+                                          const struct switch_attr *attr,
+                                          struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi,RTL8366S_SSCR0, &data);
+       val->value.i = !data;
+
+       return 0;
+}
+
+
+static int rtl8366s_sw_set_learning_enable(struct switch_dev *dev,
+                                          const struct switch_attr *attr,
+                                          struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 portmask = 0;
+       int err = 0;
+
+       if (!val->value.i)
+               portmask = RTL8366S_PORT_ALL;
+
+       /* set learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, portmask);
+
+       /* set auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, portmask);
+
+       return 0;
+}
+
+static int rtl8366s_sw_get_green(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       val->value.i = ((data & (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT)) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366s_set_green(smi, val->value.i);
+}
+
+static int rtl8366s_sw_get_port_link(struct switch_dev *dev,
+                                    int port,
+                                    struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_PORT_LINK_STATUS_BASE + (port / 2),
+                            &data);
+
+       if (port % 2)
+               data = data >> 8;
+
+       link->link = !!(data & RTL8366S_PORT_STATUS_LINK_MASK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8366S_PORT_STATUS_DUPLEX_MASK);
+       link->rx_flow = !!(data & RTL8366S_PORT_STATUS_RXPAUSE_MASK);
+       link->tx_flow = !!(data & RTL8366S_PORT_STATUS_TXPAUSE_MASK);
+       link->aneg = !!(data & RTL8366S_PORT_STATUS_AN_MASK);
+
+       speed = (data & RTL8366S_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       u32 mask;
+       u32 reg;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS ||
+           (1 << val->port_vlan) == RTL8366S_PORT_UNKNOWN)
+               return -EINVAL;
+
+       if (val->port_vlan == RTL8366S_PORT_NUM_CPU) {
+               reg = RTL8366S_LED_BLINKRATE_REG;
+               mask = 0xF << 4;
+               data = val->value.i << 4;
+       } else {
+               reg = RTL8366S_LED_CTRL_REG;
+               mask = 0xF << (val->port_vlan * 4),
+               data = val->value.i << (val->port_vlan * 4);
+       }
+
+       return rtl8366_smi_rmwr(smi, reg, mask, data);
+}
+
+static int rtl8366s_sw_get_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+
+       if (val->port_vlan >= RTL8366S_NUM_LEDGROUPS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_LED_CTRL_REG, &data);
+       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
+
+       return 0;
+}
+
+static int rtl8366s_sw_get_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+       u32 phyData;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, val->port_vlan, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       val->value.i = ((phyData & RTL8366S_PHY_POWER_SAVING_MASK) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366s_set_green_port(smi, val->port_vlan, val->value.i);
+}
+
+static int rtl8366s_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG,
+                               0, (1 << (val->port_vlan + 3)));
+}
+
+static int rtl8366s_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8366S_MIB_TXB_ID, RTL8366S_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8366s_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_learning",
+               .description = "Enable learning, enable aging",
+               .set = rtl8366s_sw_set_learning_enable,
+               .get = rtl8366s_sw_get_learning_enable,
+               .max = 1,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8366s_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "blinkrate",
+               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
+               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
+               .set = rtl8366s_sw_set_blinkrate,
+               .get = rtl8366s_sw_get_blinkrate,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+               " (0 = 1522, 1 = 1536, 2 = 1552, 3 = 16000 (9216?))",
+               .set = rtl8366s_sw_set_max_length,
+               .get = rtl8366s_sw_get_max_length,
+               .max = 3,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_mode",
+               .description = "Get/Set the router green feature",
+               .set = rtl8366s_sw_set_green,
+               .get = rtl8366s_sw_get_green,
+               .max = 1,
+       },
+};
+
+static struct switch_attr rtl8366s_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8366s_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
+               .max = 15,
+               .set = rtl8366s_sw_set_port_led,
+               .get = rtl8366s_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_port",
+               .description = "Get/Set port green feature (0 - 1)",
+               .max = 1,
+               .set = rtl8366s_sw_set_green_port,
+               .get = rtl8366s_sw_get_green_port,
+       },
+};
+
+static struct switch_attr rtl8366s_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8366S_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8366_ops = {
+       .attr_global = {
+               .attr = rtl8366s_globals,
+               .n_attr = ARRAY_SIZE(rtl8366s_globals),
+       },
+       .attr_port = {
+               .attr = rtl8366s_port,
+               .n_attr = ARRAY_SIZE(rtl8366s_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8366s_vlan,
+               .n_attr = ARRAY_SIZE(rtl8366s_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366s_sw_get_port_link,
+       .get_port_stats = rtl8366s_sw_get_port_stats,
+};
+
+static int rtl8366s_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8366S";
+       dev->cpu_port = RTL8366S_PORT_NUM_CPU;
+       dev->ports = RTL8366S_NUM_PORTS;
+       dev->vlans = RTL8366S_NUM_VIDS;
+       dev->ops = &rtl8366_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8366s_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8366s_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8366s_read_phy_reg(smi, addr, 0, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8366s_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8366s_write_phy_reg(smi, addr, 0, reg, val);
+       /* flush write */
+       (void) rtl8366s_read_phy_reg(smi, addr, 0, reg, &t);
+
+       return err;
+}
+
+static int rtl8366s_detect(struct rtl8366_smi *smi)
+{
+       u32 chip_id = 0;
+       u32 chip_ver = 0;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_ID_REG, &chip_id);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip id\n");
+               return ret;
+       }
+
+       switch (chip_id) {
+       case RTL8366S_CHIP_ID_8366:
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_VERSION_CTRL_REG,
+                                  &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
+                chip_id, chip_ver & RTL8366S_CHIP_VERSION_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8366s_smi_ops = {
+       .detect         = rtl8366s_detect,
+       .reset_chip     = rtl8366s_reset_chip,
+       .setup          = rtl8366s_setup,
+
+       .mii_read       = rtl8366s_mii_read,
+       .mii_write      = rtl8366s_mii_write,
+
+       .get_vlan_mc    = rtl8366s_get_vlan_mc,
+       .set_vlan_mc    = rtl8366s_set_vlan_mc,
+       .get_vlan_4k    = rtl8366s_get_vlan_4k,
+       .set_vlan_4k    = rtl8366s_set_vlan_4k,
+       .get_mc_index   = rtl8366s_get_mc_index,
+       .set_mc_index   = rtl8366s_set_mc_index,
+       .get_mib_counter = rtl8366_get_mib_counter,
+       .is_vlan_valid  = rtl8366s_is_vlan_valid,
+       .enable_vlan    = rtl8366s_enable_vlan,
+       .enable_vlan4k  = rtl8366s_enable_vlan4k,
+       .enable_port    = rtl8366s_enable_port,
+};
+
+static int rtl8366s_probe(struct platform_device *pdev)
+{
+       static int rtl8366_smi_version_printed;
+       struct rtl8366_smi *smi;
+       int err;
+
+       if (!rtl8366_smi_version_printed++)
+               printk(KERN_NOTICE RTL8366S_DRIVER_DESC
+                      " version " RTL8366S_DRIVER_VER"\n");
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 10;
+       smi->cmd_read = 0xa9;
+       smi->cmd_write = 0xa8;
+       smi->ops = &rtl8366s_smi_ops;
+       smi->cpu_port = RTL8366S_PORT_NUM_CPU;
+       smi->num_ports = RTL8366S_NUM_PORTS;
+       smi->num_vlan_mc = RTL8366S_NUM_VLANS;
+       smi->mib_counters = rtl8366s_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8366s_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8366s_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8366s_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8366s_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366s_match[] = {
+       { .compatible = "realtek,rtl8366s" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366s_match);
+#endif
+
+static struct platform_driver rtl8366s_driver = {
+       .driver = {
+               .name           = RTL8366S_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8366s_match),
+#endif
+       },
+       .probe          = rtl8366s_probe,
+       .remove         = rtl8366s_remove,
+};
+
+static int __init rtl8366s_module_init(void)
+{
+       return platform_driver_register(&rtl8366s_driver);
+}
+module_init(rtl8366s_module_init);
+
+static void __exit rtl8366s_module_exit(void)
+{
+       platform_driver_unregister(&rtl8366s_driver);
+}
+module_exit(rtl8366s_module_exit);
+
+MODULE_DESCRIPTION(RTL8366S_DRIVER_DESC);
+MODULE_VERSION(RTL8366S_DRIVER_VER);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8366S_DRIVER_NAME);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8367.c b/target/linux/generic/files-4.14/drivers/net/phy/rtl8367.c
new file mode 100644 (file)
index 0000000..9549961
--- /dev/null
@@ -0,0 +1,1846 @@
+/*
+ * Platform driver for the Realtek RTL8367R/M ethernet switches
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367_RESET_DELAY    1000    /* msecs*/
+
+#define RTL8367_PHY_ADDR_MAX   8
+#define RTL8367_PHY_REG_MAX    31
+
+#define RTL8367_VID_MASK       0xffff
+#define RTL8367_FID_MASK       0xfff
+#define RTL8367_UNTAG_MASK     0xffff
+#define RTL8367_MEMBER_MASK    0xffff
+
+#define RTL8367_PORT_CFG_REG(_p)               (0x000e + 0x20 * (_p))
+#define   RTL8367_PORT_CFG_EGRESS_MODE_SHIFT   4
+#define   RTL8367_PORT_CFG_EGRESS_MODE_MASK    0x3
+#define   RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL        0
+#define   RTL8367_PORT_CFG_EGRESS_MODE_KEEP    1
+#define   RTL8367_PORT_CFG_EGRESS_MODE_PRI     2
+#define   RTL8367_PORT_CFG_EGRESS_MODE_REAL    3
+
+#define RTL8367_BYPASS_LINE_RATE_REG           0x03f7
+
+#define RTL8367_TA_CTRL_REG                    0x0500
+#define   RTL8367_TA_CTRL_STATUS               BIT(12)
+#define   RTL8367_TA_CTRL_METHOD               BIT(5)
+#define   RTL8367_TA_CTRL_CMD_SHIFT            4
+#define   RTL8367_TA_CTRL_CMD_READ             0
+#define   RTL8367_TA_CTRL_CMD_WRITE            1
+#define   RTL8367_TA_CTRL_TABLE_SHIFT          0
+#define   RTL8367_TA_CTRL_TABLE_ACLRULE                1
+#define   RTL8367_TA_CTRL_TABLE_ACLACT         2
+#define   RTL8367_TA_CTRL_TABLE_CVLAN          3
+#define   RTL8367_TA_CTRL_TABLE_L2             4
+#define   RTL8367_TA_CTRL_CVLAN_READ \
+               ((RTL8367_TA_CTRL_CMD_READ << RTL8367_TA_CTRL_CMD_SHIFT) | \
+                RTL8367_TA_CTRL_TABLE_CVLAN)
+#define   RTL8367_TA_CTRL_CVLAN_WRITE \
+               ((RTL8367_TA_CTRL_CMD_WRITE << RTL8367_TA_CTRL_CMD_SHIFT) | \
+                RTL8367_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367_TA_ADDR_REG                    0x0501
+#define   RTL8367_TA_ADDR_MASK                 0x3fff
+
+#define RTL8367_TA_DATA_REG(_x)                        (0x0503 + (_x))
+#define   RTL8367_TA_VLAN_DATA_SIZE            4
+#define   RTL8367_TA_VLAN_VID_MASK             RTL8367_VID_MASK
+#define   RTL8367_TA_VLAN_MEMBER_SHIFT         0
+#define   RTL8367_TA_VLAN_MEMBER_MASK          RTL8367_MEMBER_MASK
+#define   RTL8367_TA_VLAN_FID_SHIFT            0
+#define   RTL8367_TA_VLAN_FID_MASK             RTL8367_FID_MASK
+#define   RTL8367_TA_VLAN_UNTAG1_SHIFT         14
+#define   RTL8367_TA_VLAN_UNTAG1_MASK          0x3
+#define   RTL8367_TA_VLAN_UNTAG2_SHIFT         0
+#define   RTL8367_TA_VLAN_UNTAG2_MASK          0x3fff
+
+#define RTL8367_VLAN_PVID_CTRL_REG(_p)         (0x0700 + (_p) / 2)
+#define RTL8367_VLAN_PVID_CTRL_MASK            0x1f
+#define RTL8367_VLAN_PVID_CTRL_SHIFT(_p)       (8 * ((_p) % 2))
+
+#define RTL8367_VLAN_MC_BASE(_x)               (0x0728 + (_x) * 4)
+#define   RTL8367_VLAN_MC_DATA_SIZE            4
+#define   RTL8367_VLAN_MC_MEMBER_SHIFT         0
+#define   RTL8367_VLAN_MC_MEMBER_MASK          RTL8367_MEMBER_MASK
+#define   RTL8367_VLAN_MC_FID_SHIFT            0
+#define   RTL8367_VLAN_MC_FID_MASK             RTL8367_FID_MASK
+#define   RTL8367_VLAN_MC_EVID_SHIFT           0
+#define   RTL8367_VLAN_MC_EVID_MASK            RTL8367_VID_MASK
+
+#define RTL8367_VLAN_CTRL_REG                  0x07a8
+#define   RTL8367_VLAN_CTRL_ENABLE             BIT(0)
+
+#define RTL8367_VLAN_INGRESS_REG               0x07a9
+
+#define RTL8367_PORT_ISOLATION_REG(_p)         (0x08a2 + (_p))
+
+#define RTL8367_MIB_COUNTER_REG(_x)            (0x1000 + (_x))
+
+#define RTL8367_MIB_ADDRESS_REG                        0x1004
+
+#define RTL8367_MIB_CTRL_REG(_x)               (0x1005 + (_x))
+#define   RTL8367_MIB_CTRL_GLOBAL_RESET_MASK   BIT(11)
+#define   RTL8367_MIB_CTRL_QM_RESET_MASK       BIT(10)
+#define   RTL8367_MIB_CTRL_PORT_RESET_MASK(_p) BIT(2 + (_p))
+#define   RTL8367_MIB_CTRL_RESET_MASK          BIT(1)
+#define   RTL8367_MIB_CTRL_BUSY_MASK           BIT(0)
+
+#define RTL8367_MIB_COUNT                      36
+#define RTL8367_MIB_COUNTER_PORT_OFFSET                0x0050
+
+#define RTL8367_SWC0_REG                       0x1200
+#define   RTL8367_SWC0_MAX_LENGTH_SHIFT                13
+#define   RTL8367_SWC0_MAX_LENGTH(_x)          ((_x) << 13)
+#define   RTL8367_SWC0_MAX_LENGTH_MASK         RTL8367_SWC0_MAX_LENGTH(0x3)
+#define   RTL8367_SWC0_MAX_LENGTH_1522         RTL8367_SWC0_MAX_LENGTH(0)
+#define   RTL8367_SWC0_MAX_LENGTH_1536         RTL8367_SWC0_MAX_LENGTH(1)
+#define   RTL8367_SWC0_MAX_LENGTH_1552         RTL8367_SWC0_MAX_LENGTH(2)
+#define   RTL8367_SWC0_MAX_LENGTH_16000                RTL8367_SWC0_MAX_LENGTH(3)
+
+#define RTL8367_CHIP_NUMBER_REG                        0x1300
+
+#define RTL8367_CHIP_VER_REG                   0x1301
+#define   RTL8367_CHIP_VER_RLVID_SHIFT         12
+#define   RTL8367_CHIP_VER_RLVID_MASK          0xf
+#define   RTL8367_CHIP_VER_MCID_SHIFT          8
+#define   RTL8367_CHIP_VER_MCID_MASK           0xf
+#define   RTL8367_CHIP_VER_BOID_SHIFT          4
+#define   RTL8367_CHIP_VER_BOID_MASK           0xf
+
+#define RTL8367_CHIP_MODE_REG                  0x1302
+#define   RTL8367_CHIP_MODE_MASK               0x7
+
+#define RTL8367_CHIP_DEBUG0_REG                        0x1303
+#define   RTL8367_CHIP_DEBUG0_DUMMY0(_x)       BIT(8 + (_x))
+
+#define RTL8367_CHIP_DEBUG1_REG                        0x1304
+
+#define RTL8367_DIS_REG                                0x1305
+#define   RTL8367_DIS_SKIP_MII_RXER(_x)                BIT(12 + (_x))
+#define   RTL8367_DIS_RGMII_SHIFT(_x)          (4 * (_x))
+#define   RTL8367_DIS_RGMII_MASK               0x7
+
+#define RTL8367_EXT_RGMXF_REG(_x)              (0x1306 + (_x))
+#define   RTL8367_EXT_RGMXF_DUMMY0_SHIFT       5
+#define   RTL8367_EXT_RGMXF_DUMMY0_MASK        0x7ff
+#define   RTL8367_EXT_RGMXF_TXDELAY_SHIFT      3
+#define   RTL8367_EXT_RGMXF_TXDELAY_MASK       1
+#define   RTL8367_EXT_RGMXF_RXDELAY_MASK       0x7
+
+#define RTL8367_DI_FORCE_REG(_x)               (0x1310 + (_x))
+#define   RTL8367_DI_FORCE_MODE                        BIT(12)
+#define   RTL8367_DI_FORCE_NWAY                        BIT(7)
+#define   RTL8367_DI_FORCE_TXPAUSE             BIT(6)
+#define   RTL8367_DI_FORCE_RXPAUSE             BIT(5)
+#define   RTL8367_DI_FORCE_LINK                        BIT(4)
+#define   RTL8367_DI_FORCE_DUPLEX              BIT(2)
+#define   RTL8367_DI_FORCE_SPEED_MASK          3
+#define   RTL8367_DI_FORCE_SPEED_10            0
+#define   RTL8367_DI_FORCE_SPEED_100           1
+#define   RTL8367_DI_FORCE_SPEED_1000          2
+
+#define RTL8367_MAC_FORCE_REG(_x)              (0x1312 + (_x))
+
+#define RTL8367_CHIP_RESET_REG                 0x1322
+#define   RTL8367_CHIP_RESET_SW                        BIT(1)
+#define   RTL8367_CHIP_RESET_HW                        BIT(0)
+
+#define RTL8367_PORT_STATUS_REG(_p)            (0x1352 + (_p))
+#define   RTL8367_PORT_STATUS_NWAY             BIT(7)
+#define   RTL8367_PORT_STATUS_TXPAUSE          BIT(6)
+#define   RTL8367_PORT_STATUS_RXPAUSE          BIT(5)
+#define   RTL8367_PORT_STATUS_LINK             BIT(4)
+#define   RTL8367_PORT_STATUS_DUPLEX           BIT(2)
+#define   RTL8367_PORT_STATUS_SPEED_MASK       0x0003
+#define   RTL8367_PORT_STATUS_SPEED_10         0
+#define   RTL8367_PORT_STATUS_SPEED_100                1
+#define   RTL8367_PORT_STATUS_SPEED_1000       2
+
+#define RTL8367_RTL_NO_REG                     0x13c0
+#define   RTL8367_RTL_NO_8367R                 0x3670
+#define   RTL8367_RTL_NO_8367M                 0x3671
+
+#define RTL8367_RTL_VER_REG                    0x13c1
+#define   RTL8367_RTL_VER_MASK                 0xf
+
+#define RTL8367_RTL_MAGIC_ID_REG               0x13c2
+#define   RTL8367_RTL_MAGIC_ID_VAL             0x0249
+
+#define RTL8367_LED_SYS_CONFIG_REG             0x1b00
+#define RTL8367_LED_MODE_REG                   0x1b02
+#define   RTL8367_LED_MODE_RATE_M              0x7
+#define   RTL8367_LED_MODE_RATE_S              1
+
+#define RTL8367_LED_CONFIG_REG                 0x1b03
+#define   RTL8367_LED_CONFIG_DATA_S            12
+#define   RTL8367_LED_CONFIG_DATA_M            0x3
+#define   RTL8367_LED_CONFIG_SEL               BIT(14)
+#define   RTL8367_LED_CONFIG_LED_CFG_M         0xf
+
+#define RTL8367_PARA_LED_IO_EN1_REG            0x1b24
+#define RTL8367_PARA_LED_IO_EN2_REG            0x1b25
+#define   RTL8367_PARA_LED_IO_EN_PMASK         0xff
+
+#define RTL8367_IA_CTRL_REG                    0x1f00
+#define   RTL8367_IA_CTRL_RW(_x)               ((_x) << 1)
+#define   RTL8367_IA_CTRL_RW_READ              RTL8367_IA_CTRL_RW(0)
+#define   RTL8367_IA_CTRL_RW_WRITE             RTL8367_IA_CTRL_RW(1)
+#define   RTL8367_IA_CTRL_CMD_MASK             BIT(0)
+
+#define RTL8367_IA_STATUS_REG                  0x1f01
+#define   RTL8367_IA_STATUS_PHY_BUSY           BIT(2)
+#define   RTL8367_IA_STATUS_SDS_BUSY           BIT(1)
+#define   RTL8367_IA_STATUS_MDX_BUSY           BIT(0)
+
+#define RTL8367_IA_ADDRESS_REG                 0x1f02
+
+#define RTL8367_IA_WRITE_DATA_REG              0x1f03
+#define RTL8367_IA_READ_DATA_REG               0x1f04
+
+#define RTL8367_INTERNAL_PHY_REG(_a, _r)       (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367_CPU_PORT_NUM           9
+#define RTL8367_NUM_PORTS              10
+#define RTL8367_NUM_VLANS              32
+#define RTL8367_NUM_LEDGROUPS          4
+#define RTL8367_NUM_VIDS               4096
+#define RTL8367_PRIORITYMAX            7
+#define RTL8367_FIDMAX                 7
+
+#define RTL8367_PORT_0                 BIT(0)
+#define RTL8367_PORT_1                 BIT(1)
+#define RTL8367_PORT_2                 BIT(2)
+#define RTL8367_PORT_3                 BIT(3)
+#define RTL8367_PORT_4                 BIT(4)
+#define RTL8367_PORT_5                 BIT(5)
+#define RTL8367_PORT_6                 BIT(6)
+#define RTL8367_PORT_7                 BIT(7)
+#define RTL8367_PORT_E1                        BIT(8)  /* external port 1 */
+#define RTL8367_PORT_E0                        BIT(9)  /* external port 0 */
+
+#define RTL8367_PORTS_ALL                                      \
+       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
+        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
+        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1 |    \
+        RTL8367_PORT_E0)
+
+#define RTL8367_PORTS_ALL_BUT_CPU                              \
+       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
+        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
+        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1)
+
+struct rtl8367_initval {
+       u16 reg;
+       u16 val;
+};
+
+#define RTL8367_MIB_RXB_ID             0       /* IfInOctets */
+#define RTL8367_MIB_TXB_ID             20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8367_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 2, "Dot3StatsFCSErrors"                        },
+       { 0,  6, 2, "Dot3StatsSymbolErrors"                     },
+       { 0,  8, 2, "Dot3InPauseFrames"                         },
+       { 0, 10, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 12, 2, "EtherStatsFragments"                       },
+       { 0, 14, 2, "EtherStatsJabbers"                         },
+       { 0, 16, 2, "IfInUcastPkts"                             },
+       { 0, 18, 2, "EtherStatsDropEvents"                      },
+       { 0, 20, 4, "EtherStatsOctets"                          },
+
+       { 0, 24, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 26, 2, "EtherOversizeStats"                        },
+       { 0, 28, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 30, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 32, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 34, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 36, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 38, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 40, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 42, 2, "EtherStatsBroadcastPkts"                   },
+
+       { 0, 44, 4, "IfOutOctets"                               },
+
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
+       { 0, 66, 2, "IfOutUcastPkts"                            },
+       { 0, 68, 2, "IfOutMulticastPkts"                        },
+       { 0, 70, 2, "IfOutBroadcastPkts"                        },
+       { 0, 72, 2, "OutOampduPkts"                             },
+       { 0, 74, 2, "InOampduPkts"                              },
+       { 0, 76, 2, "PktgenPkts"                                },
+};
+
+#define REG_RD(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static const struct rtl8367_initval rtl8367_initvals_0_0[] = {
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
+       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
+       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
+       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
+       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
+       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
+       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
+       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
+       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
+       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
+       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
+       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
+       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
+       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
+       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
+       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
+       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
+       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
+       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
+       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
+       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
+       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1006}, {0x121e, 0x03e8},
+       {0x121f, 0x02b3}, {0x1220, 0x028f}, {0x1221, 0x029b}, {0x1222, 0x0277},
+       {0x1223, 0x02b3}, {0x1224, 0x028f}, {0x1225, 0x029b}, {0x1226, 0x0277},
+       {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0}, {0x1230, 0x00b4},
+       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
+       {0x0219, 0x0032}, {0x0200, 0x03e8}, {0x0201, 0x03e8}, {0x0202, 0x03e8},
+       {0x0203, 0x03e8}, {0x0204, 0x03e8}, {0x0205, 0x03e8}, {0x0206, 0x03e8},
+       {0x0207, 0x03e8}, {0x0218, 0x0032}, {0x0208, 0x029b}, {0x0209, 0x029b},
+       {0x020a, 0x029b}, {0x020b, 0x029b}, {0x020c, 0x029b}, {0x020d, 0x029b},
+       {0x020e, 0x029b}, {0x020f, 0x029b}, {0x0210, 0x029b}, {0x0211, 0x029b},
+       {0x0212, 0x029b}, {0x0213, 0x029b}, {0x0214, 0x029b}, {0x0215, 0x029b},
+       {0x0216, 0x029b}, {0x0217, 0x029b}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
+       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x1700, 0x014C}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
+       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
+       {0x133f, 0x0010}, {0x20A0, 0x1940}, {0x20C0, 0x1940}, {0x20E0, 0x1940},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_0_1[] = {
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
+       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
+       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
+       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
+       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
+       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
+       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
+       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
+       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
+       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
+       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
+       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
+       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
+       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
+       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
+       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
+       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
+       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
+       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
+       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
+       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
+       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1b06}, {0x121e, 0x07f0},
+       {0x121f, 0x0438}, {0x1220, 0x040f}, {0x1221, 0x040f}, {0x1222, 0x03eb},
+       {0x1223, 0x0438}, {0x1224, 0x040f}, {0x1225, 0x040f}, {0x1226, 0x03eb},
+       {0x1227, 0x0144}, {0x1228, 0x0138}, {0x122f, 0x0144}, {0x1230, 0x0138},
+       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
+       {0x0219, 0x0032}, {0x0200, 0x07d0}, {0x0201, 0x07d0}, {0x0202, 0x07d0},
+       {0x0203, 0x07d0}, {0x0204, 0x07d0}, {0x0205, 0x07d0}, {0x0206, 0x07d0},
+       {0x0207, 0x07d0}, {0x0218, 0x0032}, {0x0208, 0x0190}, {0x0209, 0x0190},
+       {0x020a, 0x0190}, {0x020b, 0x0190}, {0x020c, 0x0190}, {0x020d, 0x0190},
+       {0x020e, 0x0190}, {0x020f, 0x0190}, {0x0210, 0x0190}, {0x0211, 0x0190},
+       {0x0212, 0x0190}, {0x0213, 0x0190}, {0x0214, 0x0190}, {0x0215, 0x0190},
+       {0x0216, 0x0190}, {0x0217, 0x0190}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
+       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x1700, 0x0125}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
+       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
+       {0x133f, 0x0010},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_1_0[] = {
+       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
+       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
+       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
+       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
+       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
+       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
+       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
+       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
+       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
+       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
+       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
+       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
+       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
+       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
+       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
+       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
+       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
+       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
+       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
+       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
+       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
+       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
+       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
+       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
+       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
+       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
+       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
+       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
+       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
+       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
+       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
+       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
+       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
+       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
+       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
+       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
+       {0x121D, 0x7D16}, {0x121E, 0x03E8}, {0x121F, 0x024E}, {0x1220, 0x0230},
+       {0x1221, 0x0244}, {0x1222, 0x0226}, {0x1223, 0x024E}, {0x1224, 0x0230},
+       {0x1225, 0x0244}, {0x1226, 0x0226}, {0x1227, 0x00C0}, {0x1228, 0x00B4},
+       {0x122F, 0x00C0}, {0x1230, 0x00B4}, {0x0208, 0x03E8}, {0x0209, 0x03E8},
+       {0x020A, 0x03E8}, {0x020B, 0x03E8}, {0x020C, 0x03E8}, {0x020D, 0x03E8},
+       {0x020E, 0x03E8}, {0x020F, 0x03E8}, {0x0210, 0x03E8}, {0x0211, 0x03E8},
+       {0x0212, 0x03E8}, {0x0213, 0x03E8}, {0x0214, 0x03E8}, {0x0215, 0x03E8},
+       {0x0216, 0x03E8}, {0x0217, 0x03E8}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087B, 0x0000},
+       {0x087C, 0xFF00}, {0x087D, 0x0000}, {0x087E, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040},
+       {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
+       {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000}, {0x2200, 0x1340},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x20A0, 0x1940},
+       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_1_1[] = {
+       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
+       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
+       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
+       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
+       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
+       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
+       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
+       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
+       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
+       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
+       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
+       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
+       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
+       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
+       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
+       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
+       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
+       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
+       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
+       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
+       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
+       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
+       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
+       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
+       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
+       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
+       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
+       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
+       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
+       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
+       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
+       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
+       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
+       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
+       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
+       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
+       {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000},
+       {0x0865, 0x3210}, {0x087B, 0x0000}, {0x087C, 0xFF00}, {0x087D, 0x0000},
+       {0x087E, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040},
+       {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040},
+       {0x0A25, 0x2040}, {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040},
+       {0x0A29, 0x2040}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000},
+       {0x2200, 0x1340}, {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE},
+       {0x1B03, 0x0876},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_2_0[] = {
+       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
+       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
+       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
+       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
+       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
+       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
+       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
+       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
+       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
+       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
+       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
+       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
+       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
+       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
+       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
+       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
+       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
+       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
+       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
+       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
+       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
+       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
+       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
+       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
+       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
+       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
+       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
+       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
+       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
+       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
+       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
+       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
+       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
+       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
+       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
+       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x7D16},
+       {0x121e, 0x03e8}, {0x121f, 0x024e}, {0x1220, 0x0230}, {0x1221, 0x0244},
+       {0x1222, 0x0226}, {0x1223, 0x024e}, {0x1224, 0x0230}, {0x1225, 0x0244},
+       {0x1226, 0x0226}, {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0},
+       {0x1230, 0x00b4}, {0x0208, 0x03e8}, {0x0209, 0x03e8}, {0x020a, 0x03e8},
+       {0x020b, 0x03e8}, {0x020c, 0x03e8}, {0x020d, 0x03e8}, {0x020e, 0x03e8},
+       {0x020f, 0x03e8}, {0x0210, 0x03e8}, {0x0211, 0x03e8}, {0x0212, 0x03e8},
+       {0x0213, 0x03e8}, {0x0214, 0x03e8}, {0x0215, 0x03e8}, {0x0216, 0x03e8},
+       {0x0217, 0x03e8}, {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000},
+       {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000}, {0x087c, 0xff00},
+       {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100},
+       {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040},
+       {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040}, {0x20A0, 0x1940},
+       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_2_1[] = {
+       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
+       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
+       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
+       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
+       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
+       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
+       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
+       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
+       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
+       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
+       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
+       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
+       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
+       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
+       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
+       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
+       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
+       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
+       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
+       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
+       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
+       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
+       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
+       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
+       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
+       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
+       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
+       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
+       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
+       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
+       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
+       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
+       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
+       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
+       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
+       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x0900, 0x0000},
+       {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210},
+       {0x087b, 0x0000}, {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000},
+       {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040},
+       {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A25, 0x2040},
+       {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
+       {0x130c, 0x0050},
+};
+
+static int rtl8367_write_initvals(struct rtl8366_smi *smi,
+                                 const struct rtl8367_initval *initvals,
+                                 int count)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < count; i++)
+               REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+       return 0;
+}
+
+static int rtl8367_read_phy_reg(struct rtl8366_smi *smi,
+                               u32 phy_addr, u32 phy_reg, u32 *val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       if (phy_addr > RTL8367_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+       if (data & RTL8367_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* prepare address */
+       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
+              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send read command */
+       REG_WR(smi, RTL8367_IA_CTRL_REG,
+              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_READ);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy read timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       /* read data */
+       REG_RD(smi, RTL8367_IA_READ_DATA_REG, val);
+
+       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, *val);
+       return 0;
+}
+
+static int rtl8367_write_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_addr, u32 phy_reg, u32 val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, val);
+
+       if (phy_addr > RTL8367_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+       if (data & RTL8367_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* preapre data */
+       REG_WR(smi, RTL8367_IA_WRITE_DATA_REG, val);
+
+       /* prepare address */
+       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
+              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send write command */
+       REG_WR(smi, RTL8367_IA_CTRL_REG,
+              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_WRITE);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy write timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8367_init_regs0(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+       int err;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_0_0;
+               count = ARRAY_SIZE(rtl8367_initvals_0_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_0_1;
+               count = ARRAY_SIZE(rtl8367_initvals_0_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       err = rtl8367_write_initvals(smi, initvals, count);
+       if (err)
+               return err;
+
+       /* TODO: complete this */
+
+       return 0;
+}
+
+static int rtl8367_init_regs1(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_1_0;
+               count = ARRAY_SIZE(rtl8367_initvals_1_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_1_1;
+               count = ARRAY_SIZE(rtl8367_initvals_1_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       return rtl8367_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367_init_regs2(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_2_0;
+               count = ARRAY_SIZE(rtl8367_initvals_2_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_2_1;
+               count = ARRAY_SIZE(rtl8367_initvals_2_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       return rtl8367_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367_init_regs(struct rtl8366_smi *smi)
+{
+       u32 data;
+       u32 rlvid;
+       u32 mode;
+       int err;
+
+       REG_WR(smi, RTL8367_RTL_MAGIC_ID_REG, RTL8367_RTL_MAGIC_ID_VAL);
+
+       REG_RD(smi, RTL8367_CHIP_VER_REG, &data);
+       rlvid = (data >> RTL8367_CHIP_VER_RLVID_SHIFT) &
+               RTL8367_CHIP_VER_RLVID_MASK;
+
+       REG_RD(smi, RTL8367_CHIP_MODE_REG, &data);
+       mode = data & RTL8367_CHIP_MODE_MASK;
+
+       switch (rlvid) {
+       case 0:
+               err = rtl8367_init_regs0(smi, mode);
+               break;
+
+       case 1:
+               err = rtl8367_write_phy_reg(smi, 0, 31, 5);
+               if (err)
+                       break;
+
+               err = rtl8367_write_phy_reg(smi, 0, 5, 0x3ffe);
+               if (err)
+                       break;
+
+               err = rtl8367_read_phy_reg(smi, 0, 6, &data);
+               if (err)
+                       break;
+
+               if (data == 0x94eb) {
+                       err = rtl8367_init_regs1(smi, mode);
+               } else if (data == 0x2104) {
+                       err = rtl8367_init_regs2(smi, mode);
+               } else {
+                       dev_err(smi->parent, "unknow phy data %04x\n", data);
+                       return -ENODEV;
+               }
+
+               break;
+
+       default:
+               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+               err = -ENODEV;
+               break;
+       }
+
+       return err;
+}
+
+static int rtl8367_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       int err;
+       u32 data;
+
+       REG_WR(smi, RTL8367_CHIP_RESET_REG, RTL8367_CHIP_RESET_HW);
+       msleep(RTL8367_RESET_DELAY);
+
+       do {
+               REG_RD(smi, RTL8367_CHIP_RESET_REG, &data);
+               if (!(data & RTL8367_CHIP_RESET_HW))
+                       break;
+
+               msleep(1);
+       } while (--timeout);
+
+       if (!timeout) {
+               dev_err(smi->parent, "chip reset timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtl8367_extif_set_mode(struct rtl8366_smi *smi, int id,
+                                 enum rtl8367_extif_mode mode)
+{
+       int err;
+
+       /* set port mode */
+       switch (mode) {
+       case RTL8367_EXTIF_MODE_RGMII:
+       case RTL8367_EXTIF_MODE_RGMII_33V:
+               REG_WR(smi, RTL8367_CHIP_DEBUG0_REG, 0x0367);
+               REG_WR(smi, RTL8367_CHIP_DEBUG1_REG, 0x7777);
+               break;
+
+       case RTL8367_EXTIF_MODE_TMII_MAC:
+       case RTL8367_EXTIF_MODE_TMII_PHY:
+               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), BIT((id + 1) % 2));
+               break;
+
+       case RTL8367_EXTIF_MODE_GMII:
+               REG_RMW(smi, RTL8367_CHIP_DEBUG0_REG,
+                       RTL8367_CHIP_DEBUG0_DUMMY0(id),
+                       RTL8367_CHIP_DEBUG0_DUMMY0(id));
+               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+               break;
+
+       case RTL8367_EXTIF_MODE_MII_MAC:
+       case RTL8367_EXTIF_MODE_MII_PHY:
+       case RTL8367_EXTIF_MODE_DISABLED:
+               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), 0);
+               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), 0);
+               break;
+
+       default:
+               dev_err(smi->parent,
+                       "invalid mode for external interface %d\n", id);
+               return -EINVAL;
+       }
+
+       REG_RMW(smi, RTL8367_DIS_REG,
+               RTL8367_DIS_RGMII_MASK << RTL8367_DIS_RGMII_SHIFT(id),
+               mode << RTL8367_DIS_RGMII_SHIFT(id));
+
+       return 0;
+}
+
+static int rtl8367_extif_set_force(struct rtl8366_smi *smi, int id,
+                                  struct rtl8367_port_ability *pa)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367_DI_FORCE_MODE |
+               RTL8367_DI_FORCE_NWAY |
+               RTL8367_DI_FORCE_TXPAUSE |
+               RTL8367_DI_FORCE_RXPAUSE |
+               RTL8367_DI_FORCE_LINK |
+               RTL8367_DI_FORCE_DUPLEX |
+               RTL8367_DI_FORCE_SPEED_MASK);
+
+       val = pa->speed;
+       val |= pa->force_mode ? RTL8367_DI_FORCE_MODE : 0;
+       val |= pa->nway ? RTL8367_DI_FORCE_NWAY : 0;
+       val |= pa->txpause ? RTL8367_DI_FORCE_TXPAUSE : 0;
+       val |= pa->rxpause ? RTL8367_DI_FORCE_RXPAUSE : 0;
+       val |= pa->link ? RTL8367_DI_FORCE_LINK : 0;
+       val |= pa->duplex ? RTL8367_DI_FORCE_DUPLEX : 0;
+
+       REG_RMW(smi, RTL8367_DI_FORCE_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+                                        unsigned txdelay, unsigned rxdelay)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367_EXT_RGMXF_RXDELAY_MASK |
+               (RTL8367_EXT_RGMXF_TXDELAY_MASK <<
+                       RTL8367_EXT_RGMXF_TXDELAY_SHIFT));
+
+       val = rxdelay;
+       val |= txdelay << RTL8367_EXT_RGMXF_TXDELAY_SHIFT;
+
+       REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367_extif_init(struct rtl8366_smi *smi, int id,
+                             struct rtl8367_extif_config *cfg)
+{
+       enum rtl8367_extif_mode mode;
+       int err;
+
+       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+       err = rtl8367_extif_set_mode(smi, id, mode);
+       if (err)
+               return err;
+
+       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+               err = rtl8367_extif_set_force(smi, id, &cfg->ability);
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+                                                    cfg->rxdelay);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_ports(struct rtl8366_smi *smi,
+                                      unsigned int group, u16 port_mask)
+{
+       u32 reg;
+       u32 s;
+       int err;
+
+       port_mask &= RTL8367_PARA_LED_IO_EN_PMASK;
+       s = (group % 2) * 8;
+       reg = RTL8367_PARA_LED_IO_EN1_REG + (group / 2);
+
+       REG_RMW(smi, reg, (RTL8367_PARA_LED_IO_EN_PMASK << s), port_mask << s);
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_mode(struct rtl8366_smi *smi,
+                                     unsigned int mode)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mode &= RTL8367_LED_CONFIG_DATA_M;
+
+       mask = (RTL8367_LED_CONFIG_DATA_M << RTL8367_LED_CONFIG_DATA_S) |
+               RTL8367_LED_CONFIG_SEL;
+       set = (mode << RTL8367_LED_CONFIG_DATA_S) | RTL8367_LED_CONFIG_SEL;
+
+       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_config(struct rtl8366_smi *smi,
+                                       unsigned int led, unsigned int cfg)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mask = (RTL8367_LED_CONFIG_LED_CFG_M << (led * 4)) |
+               RTL8367_LED_CONFIG_SEL;
+       set = (cfg & RTL8367_LED_CONFIG_LED_CFG_M) << (led * 4);
+
+       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
+       return 0;
+}
+
+static int rtl8367_led_op_select_parallel(struct rtl8366_smi *smi)
+{
+       int err;
+
+       REG_WR(smi, RTL8367_LED_SYS_CONFIG_REG, 0x1472);
+       return 0;
+}
+
+static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mask = RTL8367_LED_MODE_RATE_M << RTL8367_LED_MODE_RATE_S;
+       set = (rate & RTL8367_LED_MODE_RATE_M) << RTL8367_LED_MODE_RATE_S;
+       REG_RMW(smi, RTL8367_LED_MODE_REG, mask, set);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+                                const char *name)
+{
+       struct rtl8367_extif_config *cfg;
+       const __be32 *prop;
+       int size;
+       int err;
+
+       prop = of_get_property(smi->parent->of_node, name, &size);
+       if (!prop)
+               return rtl8367_extif_init(smi, id, NULL);
+
+       if (size != (9 * sizeof(*prop))) {
+               dev_err(smi->parent, "%s property is invalid\n", name);
+               return -EINVAL;
+       }
+
+       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->txdelay = be32_to_cpup(prop++);
+       cfg->rxdelay = be32_to_cpup(prop++);
+       cfg->mode = be32_to_cpup(prop++);
+       cfg->ability.force_mode = be32_to_cpup(prop++);
+       cfg->ability.txpause = be32_to_cpup(prop++);
+       cfg->ability.rxpause = be32_to_cpup(prop++);
+       cfg->ability.link = be32_to_cpup(prop++);
+       cfg->ability.duplex = be32_to_cpup(prop++);
+       cfg->ability.speed = be32_to_cpup(prop++);
+
+       err = rtl8367_extif_init(smi, id, cfg);
+       kfree(cfg);
+
+       return err;
+}
+#else
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+                                const char *name)
+{
+       return -EINVAL;
+}
+#endif
+
+static int rtl8367_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8367_platform_data *pdata;
+       int err;
+       int i;
+
+       pdata = smi->parent->platform_data;
+
+       err = rtl8367_init_regs(smi);
+       if (err)
+               return err;
+
+       /* initialize external interfaces */
+       if (smi->parent->of_node) {
+               err = rtl8367_extif_init_of(smi, 0, "realtek,extif0");
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_init_of(smi, 1, "realtek,extif1");
+               if (err)
+                       return err;
+       } else {
+               err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg);
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg);
+               if (err)
+                       return err;
+       }
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8367_SWC0_REG, RTL8367_SWC0_MAX_LENGTH_MASK,
+               RTL8367_SWC0_MAX_LENGTH_1536);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8367_VLAN_INGRESS_REG, RTL8367_PORTS_ALL);
+
+       /*
+        * Setup egress tag mode for each port.
+        */
+       for (i = 0; i < RTL8367_NUM_PORTS; i++)
+               REG_RMW(smi,
+                       RTL8367_PORT_CFG_REG(i),
+                       RTL8367_PORT_CFG_EGRESS_MODE_MASK <<
+                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT,
+                       RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL <<
+                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT);
+
+       /* setup LEDs */
+       err = rtl8367_led_group_set_ports(smi, 0, RTL8367_PORTS_ALL);
+       if (err)
+               return err;
+
+       err = rtl8367_led_group_set_mode(smi, 0);
+       if (err)
+               return err;
+
+       err = rtl8367_led_op_select_parallel(smi);
+       if (err)
+               return err;
+
+       err = rtl8367_led_blinkrate_set(smi, 1);
+       if (err)
+               return err;
+
+       err = rtl8367_led_group_set_config(smi, 0, 2);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8367_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val)
+{
+       struct rtl8366_mib_counter *mib;
+       int offset;
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8367_NUM_PORTS || counter >= RTL8367_MIB_COUNT)
+               return -EINVAL;
+
+       mib = &rtl8367_mib_counters[counter];
+       addr = RTL8367_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       REG_WR(smi, RTL8367_MIB_ADDRESS_REG, addr >> 2);
+
+       /* read MIB control register */
+       REG_RD(smi, RTL8367_MIB_CTRL_REG(0), &data);
+
+       if (data & RTL8367_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8367_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       if (mib->length == 4)
+               offset = 3;
+       else
+               offset = (mib->offset + 1) % 4;
+
+       mibvalue = 0;
+       for (i = 0; i < mib->length; i++) {
+               REG_RD(smi, RTL8367_MIB_COUNTER_REG(offset - i), &data);
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8367_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8367_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       REG_WR(smi, RTL8367_TA_ADDR_REG, vid);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_READ);
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367_TA_DATA_REG(i), &data[i]);
+
+       vlan4k->vid = vid;
+       vlan4k->member = (data[0] >> RTL8367_TA_VLAN_MEMBER_SHIFT) &
+                        RTL8367_TA_VLAN_MEMBER_MASK;
+       vlan4k->fid = (data[1] >> RTL8367_TA_VLAN_FID_SHIFT) &
+                     RTL8367_TA_VLAN_FID_MASK;
+       vlan4k->untag = (data[2] >> RTL8367_TA_VLAN_UNTAG1_SHIFT) &
+                       RTL8367_TA_VLAN_UNTAG1_MASK;
+       vlan4k->untag |= ((data[3] >> RTL8367_TA_VLAN_UNTAG2_SHIFT) &
+                         RTL8367_TA_VLAN_UNTAG2_MASK) << 2;
+
+       return 0;
+}
+
+static int rtl8367_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8367_NUM_VIDS ||
+           vlan4k->member > RTL8367_TA_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8367_UNTAG_MASK ||
+           vlan4k->fid > RTL8367_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlan4k->member & RTL8367_TA_VLAN_MEMBER_MASK) <<
+                 RTL8367_TA_VLAN_MEMBER_SHIFT;
+       data[1] = (vlan4k->fid & RTL8367_TA_VLAN_FID_MASK) <<
+                 RTL8367_TA_VLAN_FID_SHIFT;
+       data[2] = (vlan4k->untag & RTL8367_TA_VLAN_UNTAG1_MASK) <<
+                 RTL8367_TA_VLAN_UNTAG1_SHIFT;
+       data[3] = ((vlan4k->untag >> 2) & RTL8367_TA_VLAN_UNTAG2_MASK) <<
+                 RTL8367_TA_VLAN_UNTAG2_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367_TA_DATA_REG(i), data[i]);
+
+       /* write VID */
+       REG_WR(smi, RTL8367_TA_ADDR_REG,
+              vlan4k->vid & RTL8367_TA_VLAN_VID_MASK);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_WRITE);
+
+       return 0;
+}
+
+static int rtl8367_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8367_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367_VLAN_MC_BASE(index) + i, &data[i]);
+
+       vlanmc->member = (data[0] >> RTL8367_VLAN_MC_MEMBER_SHIFT) &
+                        RTL8367_VLAN_MC_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8367_VLAN_MC_FID_SHIFT) &
+                     RTL8367_VLAN_MC_FID_MASK;
+       vlanmc->vid = (data[3] >> RTL8367_VLAN_MC_EVID_SHIFT) &
+                     RTL8367_VLAN_MC_EVID_MASK;
+
+       return 0;
+}
+
+static int rtl8367_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
+       int err;
+       int i;
+
+       if (index >= RTL8367_NUM_VLANS ||
+           vlanmc->vid >= RTL8367_NUM_VIDS ||
+           vlanmc->priority > RTL8367_PRIORITYMAX ||
+           vlanmc->member > RTL8367_VLAN_MC_MEMBER_MASK ||
+           vlanmc->untag > RTL8367_UNTAG_MASK ||
+           vlanmc->fid > RTL8367_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->member & RTL8367_VLAN_MC_MEMBER_MASK) <<
+                 RTL8367_VLAN_MC_MEMBER_SHIFT;
+       data[1] = (vlanmc->fid & RTL8367_VLAN_MC_FID_MASK) <<
+                 RTL8367_VLAN_MC_FID_SHIFT;
+       data[2] = 0;
+       data[3] = (vlanmc->vid & RTL8367_VLAN_MC_EVID_MASK) <<
+                  RTL8367_VLAN_MC_EVID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367_VLAN_MC_BASE(index) + i, data[i]);
+
+       return 0;
+}
+
+static int rtl8367_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_VLAN_PVID_CTRL_REG(port), &data);
+
+       *val = (data >> RTL8367_VLAN_PVID_CTRL_SHIFT(port)) &
+              RTL8367_VLAN_PVID_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8367_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8367_NUM_PORTS || index >= RTL8367_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_PVID_CTRL_REG(port),
+                               RTL8367_VLAN_PVID_CTRL_MASK <<
+                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port),
+                               (index & RTL8367_VLAN_PVID_CTRL_MASK) <<
+                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_CTRL_REG,
+                               RTL8367_VLAN_CTRL_ENABLE,
+                               (enable) ? RTL8367_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return 0;
+}
+
+static int rtl8367_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8367_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8367_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8367_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+
+       REG_WR(smi, RTL8367_PORT_ISOLATION_REG(port),
+              (enable) ? RTL8367_PORTS_ALL : 0);
+
+       return 0;
+}
+
+static int rtl8367_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(0), 0,
+                               RTL8367_MIB_CTRL_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367_sw_get_port_link(struct switch_dev *dev,
+                                   int port,
+                                   struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8367_PORT_STATUS_REG(port), &data);
+
+       link->link = !!(data & RTL8367_PORT_STATUS_LINK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8367_PORT_STATUS_DUPLEX);
+       link->rx_flow = !!(data & RTL8367_PORT_STATUS_RXPAUSE);
+       link->tx_flow = !!(data & RTL8367_PORT_STATUS_TXPAUSE);
+       link->aneg = !!(data & RTL8367_PORT_STATUS_NWAY);
+
+       speed = (data & RTL8367_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8367_sw_get_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8367_SWC0_REG, &data);
+       val->value.i = (data & RTL8367_SWC0_MAX_LENGTH_MASK) >>
+                       RTL8367_SWC0_MAX_LENGTH_SHIFT;
+
+       return 0;
+}
+
+static int rtl8367_sw_set_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 max_len;
+
+       switch (val->value.i) {
+       case 0:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1522;
+               break;
+       case 1:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1536;
+               break;
+       case 2:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1552;
+               break;
+       case 3:
+               max_len = RTL8367_SWC0_MAX_LENGTH_16000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8367_SWC0_REG,
+                               RTL8367_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int port;
+
+       port = val->port_vlan;
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(port / 8), 0,
+                               RTL8367_MIB_CTRL_PORT_RESET_MASK(port % 8));
+}
+
+static int rtl8367_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8367_MIB_TXB_ID, RTL8367_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8367_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8367_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+                              "(0:1522, 1:1536, 2:1552, 3:16000)",
+               .set = rtl8367_sw_set_max_length,
+               .get = rtl8367_sw_get_max_length,
+               .max = 3,
+       }
+};
+
+static struct switch_attr rtl8367_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8367_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr rtl8367_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8367_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8367_sw_ops = {
+       .attr_global = {
+               .attr = rtl8367_globals,
+               .n_attr = ARRAY_SIZE(rtl8367_globals),
+       },
+       .attr_port = {
+               .attr = rtl8367_port,
+               .n_attr = ARRAY_SIZE(rtl8367_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8367_vlan,
+               .n_attr = ARRAY_SIZE(rtl8367_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8367_sw_get_port_link,
+       .get_port_stats = rtl8367_sw_get_port_stats,
+};
+
+static int rtl8367_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8367";
+       dev->cpu_port = RTL8367_CPU_PORT_NUM;
+       dev->ports = RTL8367_NUM_PORTS;
+       dev->vlans = RTL8367_NUM_VIDS;
+       dev->ops = &rtl8367_sw_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8367_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8367_read_phy_reg(smi, addr, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8367_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8367_write_phy_reg(smi, addr, reg, val);
+       if (err)
+               return err;
+
+       /* flush write */
+       (void) rtl8367_read_phy_reg(smi, addr, reg, &t);
+
+       return err;
+}
+
+static int rtl8367_detect(struct rtl8366_smi *smi)
+{
+       u32 rtl_no = 0;
+       u32 rtl_ver = 0;
+       char *chip_name;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_NO_REG, &rtl_no);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip number\n");
+               return ret;
+       }
+
+       switch (rtl_no) {
+       case RTL8367_RTL_NO_8367R:
+               chip_name = "8367R";
+               break;
+       case RTL8367_RTL_NO_8367M:
+               chip_name = "8367M";
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip number (%04x)\n", rtl_no);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_VER_REG, &rtl_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%s ver. %u chip found\n",
+                chip_name, rtl_ver & RTL8367_RTL_VER_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367_smi_ops = {
+       .detect         = rtl8367_detect,
+       .reset_chip     = rtl8367_reset_chip,
+       .setup          = rtl8367_setup,
+
+       .mii_read       = rtl8367_mii_read,
+       .mii_write      = rtl8367_mii_write,
+
+       .get_vlan_mc    = rtl8367_get_vlan_mc,
+       .set_vlan_mc    = rtl8367_set_vlan_mc,
+       .get_vlan_4k    = rtl8367_get_vlan_4k,
+       .set_vlan_4k    = rtl8367_set_vlan_4k,
+       .get_mc_index   = rtl8367_get_mc_index,
+       .set_mc_index   = rtl8367_set_mc_index,
+       .get_mib_counter = rtl8367_get_mib_counter,
+       .is_vlan_valid  = rtl8367_is_vlan_valid,
+       .enable_vlan    = rtl8367_enable_vlan,
+       .enable_vlan4k  = rtl8367_enable_vlan4k,
+       .enable_port    = rtl8367_enable_port,
+};
+
+static int rtl8367_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 1500;
+       smi->cmd_read = 0xb9;
+       smi->cmd_write = 0xb8;
+       smi->ops = &rtl8367_smi_ops;
+       smi->cpu_port = RTL8367_CPU_PORT_NUM;
+       smi->num_ports = RTL8367_NUM_PORTS;
+       smi->num_vlan_mc = RTL8367_NUM_VLANS;
+       smi->mib_counters = rtl8367_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8367_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8367_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8367_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8367_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+static void rtl8367_shutdown(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi)
+               rtl8367_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367_match[] = {
+       { .compatible = "realtek,rtl8367" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367_match);
+#endif
+
+static struct platform_driver rtl8367_driver = {
+       .driver = {
+               .name           = RTL8367_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8367_match),
+#endif
+       },
+       .probe          = rtl8367_probe,
+       .remove         = rtl8367_remove,
+       .shutdown       = rtl8367_shutdown,
+};
+
+static int __init rtl8367_module_init(void)
+{
+       return platform_driver_register(&rtl8367_driver);
+}
+module_init(rtl8367_module_init);
+
+static void __exit rtl8367_module_exit(void)
+{
+       platform_driver_unregister(&rtl8367_driver);
+}
+module_exit(rtl8367_module_exit);
+
+MODULE_DESCRIPTION("Realtek RTL8367 ethernet switch driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367_DRIVER_NAME);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/rtl8367b.c b/target/linux/generic/files-4.14/drivers/net/phy/rtl8367b.c
new file mode 100644 (file)
index 0000000..e6ea650
--- /dev/null
@@ -0,0 +1,1612 @@
+/*
+ * Platform driver for the Realtek RTL8367R-VB ethernet switches
+ *
+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367B_RESET_DELAY   1000    /* msecs*/
+
+#define RTL8367B_PHY_ADDR_MAX  8
+#define RTL8367B_PHY_REG_MAX   31
+
+#define RTL8367B_VID_MASK      0x3fff
+#define RTL8367B_FID_MASK      0xf
+#define RTL8367B_UNTAG_MASK    0xff
+#define RTL8367B_MEMBER_MASK   0xff
+
+#define RTL8367B_PORT_MISC_CFG_REG(_p)         (0x000e + 0x20 * (_p))
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT     4
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK      0x3
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL  0
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_KEEP      1
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_PRI       2
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_REAL      3
+
+#define RTL8367B_BYPASS_LINE_RATE_REG          0x03f7
+
+#define RTL8367B_TA_CTRL_REG                   0x0500 /*GOOD*/
+#define   RTL8367B_TA_CTRL_SPA_SHIFT           8
+#define   RTL8367B_TA_CTRL_SPA_MASK            0x7
+#define   RTL8367B_TA_CTRL_METHOD              BIT(4)/*GOOD*/
+#define   RTL8367B_TA_CTRL_CMD_SHIFT           3
+#define   RTL8367B_TA_CTRL_CMD_READ            0
+#define   RTL8367B_TA_CTRL_CMD_WRITE           1
+#define   RTL8367B_TA_CTRL_TABLE_SHIFT         0 /*GOOD*/
+#define   RTL8367B_TA_CTRL_TABLE_ACLRULE       1
+#define   RTL8367B_TA_CTRL_TABLE_ACLACT                2
+#define   RTL8367B_TA_CTRL_TABLE_CVLAN         3
+#define   RTL8367B_TA_CTRL_TABLE_L2            4
+#define   RTL8367B_TA_CTRL_CVLAN_READ \
+               ((RTL8367B_TA_CTRL_CMD_READ << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+                RTL8367B_TA_CTRL_TABLE_CVLAN)
+#define   RTL8367B_TA_CTRL_CVLAN_WRITE \
+               ((RTL8367B_TA_CTRL_CMD_WRITE << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+                RTL8367B_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367B_TA_ADDR_REG                   0x0501/*GOOD*/
+#define   RTL8367B_TA_ADDR_MASK                        0x3fff/*GOOD*/
+
+#define RTL8367B_TA_LUT_REG                    0x0502/*GOOD*/
+
+#define RTL8367B_TA_WRDATA_REG(_x)             (0x0510 + (_x))/*GOOD*/
+#define   RTL8367B_TA_VLAN_NUM_WORDS           2
+#define   RTL8367B_TA_VLAN_VID_MASK            RTL8367B_VID_MASK
+#define   RTL8367B_TA_VLAN0_MEMBER_SHIFT       0
+#define   RTL8367B_TA_VLAN0_MEMBER_MASK                RTL8367B_MEMBER_MASK
+#define   RTL8367B_TA_VLAN0_UNTAG_SHIFT                8
+#define   RTL8367B_TA_VLAN0_UNTAG_MASK         RTL8367B_MEMBER_MASK
+#define   RTL8367B_TA_VLAN1_FID_SHIFT          0
+#define   RTL8367B_TA_VLAN1_FID_MASK           RTL8367B_FID_MASK
+
+#define RTL8367B_TA_RDDATA_REG(_x)             (0x0520 + (_x))/*GOOD*/
+
+#define RTL8367B_VLAN_PVID_CTRL_REG(_p)                (0x0700 + (_p) / 2) /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_MASK           0x1f /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_SHIFT(_p)      (8 * ((_p) % 2)) /*GOOD*/
+
+#define RTL8367B_VLAN_MC_BASE(_x)              (0x0728 + (_x) * 4) /*GOOD*/
+#define   RTL8367B_VLAN_MC_NUM_WORDS           4 /*GOOD*/
+#define   RTL8367B_VLAN_MC0_MEMBER_SHIFT       0/*GOOD*/
+#define   RTL8367B_VLAN_MC0_MEMBER_MASK                RTL8367B_MEMBER_MASK/*GOOD*/
+#define   RTL8367B_VLAN_MC1_FID_SHIFT          0/*GOOD*/
+#define   RTL8367B_VLAN_MC1_FID_MASK           RTL8367B_FID_MASK/*GOOD*/
+#define   RTL8367B_VLAN_MC3_EVID_SHIFT         0/*GOOD*/
+#define   RTL8367B_VLAN_MC3_EVID_MASK          RTL8367B_VID_MASK/*GOOD*/
+
+#define RTL8367B_VLAN_CTRL_REG                 0x07a8 /*GOOD*/
+#define   RTL8367B_VLAN_CTRL_ENABLE            BIT(0)
+
+#define RTL8367B_VLAN_INGRESS_REG              0x07a9 /*GOOD*/
+
+#define RTL8367B_PORT_ISOLATION_REG(_p)                (0x08a2 + (_p)) /*GOOD*/
+
+#define RTL8367B_MIB_COUNTER_REG(_x)           (0x1000 + (_x)) /*GOOD*/
+#define RTL8367B_MIB_COUNTER_PORT_OFFSET       0x007c /*GOOD*/
+
+#define RTL8367B_MIB_ADDRESS_REG               0x1004 /*GOOD*/
+
+#define RTL8367B_MIB_CTRL0_REG(_x)             (0x1005 + (_x)) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK BIT(11) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_QM_RESET_MASK     BIT(10) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_PORT_RESET_MASK(_p) BIT(2 + (_p)) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_RESET_MASK                BIT(1) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_BUSY_MASK         BIT(0) /*GOOD*/
+
+#define RTL8367B_SWC0_REG                      0x1200/*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH_SHIFT       13/*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH(_x)         ((_x) << 13) /*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH_MASK                RTL8367B_SWC0_MAX_LENGTH(0x3)
+#define   RTL8367B_SWC0_MAX_LENGTH_1522                RTL8367B_SWC0_MAX_LENGTH(0)
+#define   RTL8367B_SWC0_MAX_LENGTH_1536                RTL8367B_SWC0_MAX_LENGTH(1)
+#define   RTL8367B_SWC0_MAX_LENGTH_1552                RTL8367B_SWC0_MAX_LENGTH(2)
+#define   RTL8367B_SWC0_MAX_LENGTH_16000       RTL8367B_SWC0_MAX_LENGTH(3)
+
+#define RTL8367B_CHIP_NUMBER_REG               0x1300/*GOOD*/
+
+#define RTL8367B_CHIP_VER_REG                  0x1301/*GOOD*/
+#define   RTL8367B_CHIP_VER_RLVID_SHIFT                12/*GOOD*/
+#define   RTL8367B_CHIP_VER_RLVID_MASK         0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_MCID_SHIFT         8/*GOOD*/
+#define   RTL8367B_CHIP_VER_MCID_MASK          0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_BOID_SHIFT         4/*GOOD*/
+#define   RTL8367B_CHIP_VER_BOID_MASK          0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_AFE_SHIFT          0/*GOOD*/
+#define   RTL8367B_CHIP_VER_AFE_MASK           0x1/*GOOD*/
+
+#define RTL8367B_CHIP_MODE_REG                 0x1302
+#define   RTL8367B_CHIP_MODE_MASK              0x7
+
+#define RTL8367B_CHIP_DEBUG0_REG               0x1303
+#define   RTL8367B_CHIP_DEBUG0_DUMMY0(_x)      BIT(8 + (_x))
+
+#define RTL8367B_CHIP_DEBUG1_REG               0x1304
+
+#define RTL8367B_DIS_REG                       0x1305
+#define   RTL8367B_DIS_SKIP_MII_RXER(_x)       BIT(12 + (_x))
+#define   RTL8367B_DIS_RGMII_SHIFT(_x)         (4 * (_x))
+#define   RTL8367B_DIS_RGMII_MASK              0x7
+
+#define RTL8367B_EXT_RGMXF_REG(_x)             (0x1306 + (_x))
+#define   RTL8367B_EXT_RGMXF_DUMMY0_SHIFT      5
+#define   RTL8367B_EXT_RGMXF_DUMMY0_MASK       0x7ff
+#define   RTL8367B_EXT_RGMXF_TXDELAY_SHIFT     3
+#define   RTL8367B_EXT_RGMXF_TXDELAY_MASK      1
+#define   RTL8367B_EXT_RGMXF_RXDELAY_MASK      0x7
+
+#define RTL8367B_DI_FORCE_REG(_x)              (0x1310 + (_x))
+#define   RTL8367B_DI_FORCE_MODE               BIT(12)
+#define   RTL8367B_DI_FORCE_NWAY               BIT(7)
+#define   RTL8367B_DI_FORCE_TXPAUSE            BIT(6)
+#define   RTL8367B_DI_FORCE_RXPAUSE            BIT(5)
+#define   RTL8367B_DI_FORCE_LINK               BIT(4)
+#define   RTL8367B_DI_FORCE_DUPLEX             BIT(2)
+#define   RTL8367B_DI_FORCE_SPEED_MASK         3
+#define   RTL8367B_DI_FORCE_SPEED_10           0
+#define   RTL8367B_DI_FORCE_SPEED_100          1
+#define   RTL8367B_DI_FORCE_SPEED_1000         2
+
+#define RTL8367B_MAC_FORCE_REG(_x)             (0x1312 + (_x))
+
+#define RTL8367B_CHIP_RESET_REG                        0x1322 /*GOOD*/
+#define   RTL8367B_CHIP_RESET_SW               BIT(1) /*GOOD*/
+#define   RTL8367B_CHIP_RESET_HW               BIT(0) /*GOOD*/
+
+#define RTL8367B_PORT_STATUS_REG(_p)           (0x1352 + (_p)) /*GOOD*/
+#define   RTL8367B_PORT_STATUS_EN_1000_SPI     BIT(11) /*GOOD*/
+#define   RTL8367B_PORT_STATUS_EN_100_SPI      BIT(10)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_NWAY_FAULT      BIT(9)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_LINK_MASTER     BIT(8)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_NWAY            BIT(7)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_TXPAUSE         BIT(6)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_RXPAUSE         BIT(5)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_LINK            BIT(4)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_DUPLEX          BIT(2)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_MASK      0x0003/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_10                0/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_100       1/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_1000      2/*GOOD*/
+
+#define RTL8367B_RTL_MAGIC_ID_REG              0x13c2
+#define   RTL8367B_RTL_MAGIC_ID_VAL            0x0249
+
+#define RTL8367B_IA_CTRL_REG                   0x1f00
+#define   RTL8367B_IA_CTRL_RW(_x)              ((_x) << 1)
+#define   RTL8367B_IA_CTRL_RW_READ             RTL8367B_IA_CTRL_RW(0)
+#define   RTL8367B_IA_CTRL_RW_WRITE            RTL8367B_IA_CTRL_RW(1)
+#define   RTL8367B_IA_CTRL_CMD_MASK            BIT(0)
+
+#define RTL8367B_IA_STATUS_REG                 0x1f01
+#define   RTL8367B_IA_STATUS_PHY_BUSY          BIT(2)
+#define   RTL8367B_IA_STATUS_SDS_BUSY          BIT(1)
+#define   RTL8367B_IA_STATUS_MDX_BUSY          BIT(0)
+
+#define RTL8367B_IA_ADDRESS_REG                        0x1f02
+#define RTL8367B_IA_WRITE_DATA_REG             0x1f03
+#define RTL8367B_IA_READ_DATA_REG              0x1f04
+
+#define RTL8367B_INTERNAL_PHY_REG(_a, _r)      (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367B_NUM_MIB_COUNTERS      58
+
+#define RTL8367B_CPU_PORT_NUM          5
+#define RTL8367B_NUM_PORTS             8
+#define RTL8367B_NUM_VLANS             32
+#define RTL8367B_NUM_VIDS              4096
+#define RTL8367B_PRIORITYMAX           7
+#define RTL8367B_FIDMAX                        7
+
+#define RTL8367B_PORT_0                        BIT(0)
+#define RTL8367B_PORT_1                        BIT(1)
+#define RTL8367B_PORT_2                        BIT(2)
+#define RTL8367B_PORT_3                        BIT(3)
+#define RTL8367B_PORT_4                        BIT(4)
+#define RTL8367B_PORT_E0               BIT(5)  /* External port 0 */
+#define RTL8367B_PORT_E1               BIT(6)  /* External port 1 */
+#define RTL8367B_PORT_E2               BIT(7)  /* External port 2 */
+
+#define RTL8367B_PORTS_ALL                                     \
+       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
+        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E0 | \
+        RTL8367B_PORT_E1 | RTL8367B_PORT_E2)
+
+#define RTL8367B_PORTS_ALL_BUT_CPU                             \
+       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
+        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E1 | \
+        RTL8367B_PORT_E2)
+
+struct rtl8367b_initval {
+       u16 reg;
+       u16 val;
+};
+
+#define RTL8367B_MIB_RXB_ID            0       /* IfInOctets */
+#define RTL8367B_MIB_TXB_ID            28      /* IfOutOctets */
+
+static struct rtl8366_mib_counter
+rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
+       {0,   0, 4, "ifInOctets"                        },
+       {0,   4, 2, "dot3StatsFCSErrors"                },
+       {0,   6, 2, "dot3StatsSymbolErrors"             },
+       {0,   8, 2, "dot3InPauseFrames"                 },
+       {0,  10, 2, "dot3ControlInUnknownOpcodes"       },
+       {0,  12, 2, "etherStatsFragments"               },
+       {0,  14, 2, "etherStatsJabbers"                 },
+       {0,  16, 2, "ifInUcastPkts"                     },
+       {0,  18, 2, "etherStatsDropEvents"              },
+       {0,  20, 2, "ifInMulticastPkts"                 },
+       {0,  22, 2, "ifInBroadcastPkts"                 },
+       {0,  24, 2, "inMldChecksumError"                },
+       {0,  26, 2, "inIgmpChecksumError"               },
+       {0,  28, 2, "inMldSpecificQuery"                },
+       {0,  30, 2, "inMldGeneralQuery"                 },
+       {0,  32, 2, "inIgmpSpecificQuery"               },
+       {0,  34, 2, "inIgmpGeneralQuery"                },
+       {0,  36, 2, "inMldLeaves"                       },
+       {0,  38, 2, "inIgmpLeaves"                      },
+
+       {0,  40, 4, "etherStatsOctets"                  },
+       {0,  44, 2, "etherStatsUnderSizePkts"           },
+       {0,  46, 2, "etherOversizeStats"                },
+       {0,  48, 2, "etherStatsPkts64Octets"            },
+       {0,  50, 2, "etherStatsPkts65to127Octets"       },
+       {0,  52, 2, "etherStatsPkts128to255Octets"      },
+       {0,  54, 2, "etherStatsPkts256to511Octets"      },
+       {0,  56, 2, "etherStatsPkts512to1023Octets"     },
+       {0,  58, 2, "etherStatsPkts1024to1518Octets"    },
+
+       {0,  60, 4, "ifOutOctets"                       },
+       {0,  64, 2, "dot3StatsSingleCollisionFrames"    },
+       {0,  66, 2, "dot3StatMultipleCollisionFrames"   },
+       {0,  68, 2, "dot3sDeferredTransmissions"        },
+       {0,  70, 2, "dot3StatsLateCollisions"           },
+       {0,  72, 2, "etherStatsCollisions"              },
+       {0,  74, 2, "dot3StatsExcessiveCollisions"      },
+       {0,  76, 2, "dot3OutPauseFrames"                },
+       {0,  78, 2, "ifOutDiscards"                     },
+       {0,  80, 2, "dot1dTpPortInDiscards"             },
+       {0,  82, 2, "ifOutUcastPkts"                    },
+       {0,  84, 2, "ifOutMulticastPkts"                },
+       {0,  86, 2, "ifOutBroadcastPkts"                },
+       {0,  88, 2, "outOampduPkts"                     },
+       {0,  90, 2, "inOampduPkts"                      },
+       {0,  92, 2, "inIgmpJoinsSuccess"                },
+       {0,  94, 2, "inIgmpJoinsFail"                   },
+       {0,  96, 2, "inMldJoinsSuccess"                 },
+       {0,  98, 2, "inMldJoinsFail"                    },
+       {0, 100, 2, "inReportSuppressionDrop"           },
+       {0, 102, 2, "inLeaveSuppressionDrop"            },
+       {0, 104, 2, "outIgmpReports"                    },
+       {0, 106, 2, "outIgmpLeaves"                     },
+       {0, 108, 2, "outIgmpGeneralQuery"               },
+       {0, 110, 2, "outIgmpSpecificQuery"              },
+       {0, 112, 2, "outMldReports"                     },
+       {0, 114, 2, "outMldLeaves"                      },
+       {0, 116, 2, "outMldGeneralQuery"                },
+       {0, 118, 2, "outMldSpecificQuery"               },
+       {0, 120, 2, "inKnownMulticastPkts"              },
+};
+
+#define REG_RD(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_0[] = {
+       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x0301, 0x0026}, {0x1722, 0x0E14},
+       {0x205F, 0x0002}, {0x2059, 0x1A00}, {0x205F, 0x0000}, {0x207F, 0x0002},
+       {0x2077, 0x0000}, {0x2078, 0x0000}, {0x2079, 0x0000}, {0x207A, 0x0000},
+       {0x207B, 0x0000}, {0x207F, 0x0000}, {0x205F, 0x0002}, {0x2053, 0x0000},
+       {0x2054, 0x0000}, {0x2055, 0x0000}, {0x2056, 0x0000}, {0x2057, 0x0000},
+       {0x205F, 0x0000}, {0x12A4, 0x110A}, {0x12A6, 0x150A}, {0x13F1, 0x0013},
+       {0x13F4, 0x0010}, {0x13F5, 0x0000}, {0x0018, 0x0F00}, {0x0038, 0x0F00},
+       {0x0058, 0x0F00}, {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x12B6, 0x0C02},
+       {0x12B7, 0x030F}, {0x12B8, 0x11FF}, {0x12BC, 0x0004}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0007}, {0x221E, 0x002D}, {0x2218, 0xF030}, {0x221F, 0x0007},
+       {0x221E, 0x0023}, {0x2216, 0x0005}, {0x2215, 0x00B9}, {0x2219, 0x0044},
+       {0x2215, 0x00BA}, {0x2219, 0x0020}, {0x2215, 0x00BB}, {0x2219, 0x00C1},
+       {0x2215, 0x0148}, {0x2219, 0x0096}, {0x2215, 0x016E}, {0x2219, 0x0026},
+       {0x2216, 0x0000}, {0x2216, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221F, 0x0007}, {0x221E, 0x0020}, {0x2215, 0x0D00}, {0x221F, 0x0000},
+       {0x221F, 0x0000}, {0x2217, 0x2160}, {0x221F, 0x0001}, {0x2210, 0xF25E},
+       {0x221F, 0x0007}, {0x221E, 0x0042}, {0x2215, 0x0F00}, {0x2215, 0x0F00},
+       {0x2216, 0x7408}, {0x2215, 0x0E00}, {0x2215, 0x0F00}, {0x2215, 0x0F01},
+       {0x2216, 0x4000}, {0x2215, 0x0E01}, {0x2215, 0x0F01}, {0x2215, 0x0F02},
+       {0x2216, 0x9400}, {0x2215, 0x0E02}, {0x2215, 0x0F02}, {0x2215, 0x0F03},
+       {0x2216, 0x7408}, {0x2215, 0x0E03}, {0x2215, 0x0F03}, {0x2215, 0x0F04},
+       {0x2216, 0x4008}, {0x2215, 0x0E04}, {0x2215, 0x0F04}, {0x2215, 0x0F05},
+       {0x2216, 0x9400}, {0x2215, 0x0E05}, {0x2215, 0x0F05}, {0x2215, 0x0F06},
+       {0x2216, 0x0803}, {0x2215, 0x0E06}, {0x2215, 0x0F06}, {0x2215, 0x0D00},
+       {0x2215, 0x0100}, {0x221F, 0x0001}, {0x2210, 0xF05E}, {0x221F, 0x0000},
+       {0x2217, 0x2100}, {0x221F, 0x0000}, {0x220D, 0x0003}, {0x220E, 0x0015},
+       {0x220D, 0x4003}, {0x220E, 0x0006}, {0x221F, 0x0000}, {0x2200, 0x1340},
+       {0x133F, 0x0010}, {0x12A0, 0x0058}, {0x12A1, 0x0058}, {0x133E, 0x000E},
+       {0x133F, 0x0030}, {0x221F, 0x0000}, {0x2210, 0x0166}, {0x221F, 0x0000},
+       {0x133E, 0x000E}, {0x133F, 0x0010}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8B6E},
+       {0x2206, 0x0000}, {0x220F, 0x0100}, {0x2205, 0x8000}, {0x2206, 0x0280},
+       {0x2206, 0x28F7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+       {0x2206, 0x6602}, {0x2206, 0x80B9}, {0x2206, 0xE08B}, {0x2206, 0x8CE1},
+       {0x2206, 0x8B8D}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x8E1E},
+       {0x2206, 0x01A0}, {0x2206, 0x00E7}, {0x2206, 0xAEDB}, {0x2206, 0xEEE0},
+       {0x2206, 0x120E}, {0x2206, 0xEEE0}, {0x2206, 0x1300}, {0x2206, 0xEEE0},
+       {0x2206, 0x2001}, {0x2206, 0xEEE0}, {0x2206, 0x2166}, {0x2206, 0xEEE0},
+       {0x2206, 0xC463}, {0x2206, 0xEEE0}, {0x2206, 0xC5E8}, {0x2206, 0xEEE0},
+       {0x2206, 0xC699}, {0x2206, 0xEEE0}, {0x2206, 0xC7C2}, {0x2206, 0xEEE0},
+       {0x2206, 0xC801}, {0x2206, 0xEEE0}, {0x2206, 0xC913}, {0x2206, 0xEEE0},
+       {0x2206, 0xCA30}, {0x2206, 0xEEE0}, {0x2206, 0xCB3E}, {0x2206, 0xEEE0},
+       {0x2206, 0xDCE1}, {0x2206, 0xEEE0}, {0x2206, 0xDD00}, {0x2206, 0xEEE2},
+       {0x2206, 0x0001}, {0x2206, 0xEEE2}, {0x2206, 0x0100}, {0x2206, 0xEEE4},
+       {0x2206, 0x8860}, {0x2206, 0xEEE4}, {0x2206, 0x8902}, {0x2206, 0xEEE4},
+       {0x2206, 0x8C00}, {0x2206, 0xEEE4}, {0x2206, 0x8D30}, {0x2206, 0xEEEA},
+       {0x2206, 0x1480}, {0x2206, 0xEEEA}, {0x2206, 0x1503}, {0x2206, 0xEEEA},
+       {0x2206, 0xC600}, {0x2206, 0xEEEA}, {0x2206, 0xC706}, {0x2206, 0xEE85},
+       {0x2206, 0xEE00}, {0x2206, 0xEE85}, {0x2206, 0xEF00}, {0x2206, 0xEE8B},
+       {0x2206, 0x6750}, {0x2206, 0xEE8B}, {0x2206, 0x6632}, {0x2206, 0xEE8A},
+       {0x2206, 0xD448}, {0x2206, 0xEE8A}, {0x2206, 0xD548}, {0x2206, 0xEE8A},
+       {0x2206, 0xD649}, {0x2206, 0xEE8A}, {0x2206, 0xD7F8}, {0x2206, 0xEE8B},
+       {0x2206, 0x85E2}, {0x2206, 0xEE8B}, {0x2206, 0x8700}, {0x2206, 0xEEFF},
+       {0x2206, 0xF600}, {0x2206, 0xEEFF}, {0x2206, 0xF7FC}, {0x2206, 0x04F8},
+       {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2023}, {0x2206, 0xF620},
+       {0x2206, 0xE48B}, {0x2206, 0x8E02}, {0x2206, 0x2877}, {0x2206, 0x0225},
+       {0x2206, 0xC702}, {0x2206, 0x26A1}, {0x2206, 0x0281}, {0x2206, 0xB302},
+       {0x2206, 0x8496}, {0x2206, 0x0202}, {0x2206, 0xA102}, {0x2206, 0x27F1},
+       {0x2206, 0x0228}, {0x2206, 0xF902}, {0x2206, 0x2AA0}, {0x2206, 0x0282},
+       {0x2206, 0xB8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD21}, {0x2206, 0x08F6},
+       {0x2206, 0x21E4}, {0x2206, 0x8B8E}, {0x2206, 0x0202}, {0x2206, 0x80E0},
+       {0x2206, 0x8B8E}, {0x2206, 0xAD22}, {0x2206, 0x05F6}, {0x2206, 0x22E4},
+       {0x2206, 0x8B8E}, {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2305},
+       {0x2206, 0xF623}, {0x2206, 0xE48B}, {0x2206, 0x8EE0}, {0x2206, 0x8B8E},
+       {0x2206, 0xAD24}, {0x2206, 0x08F6}, {0x2206, 0x24E4}, {0x2206, 0x8B8E},
+       {0x2206, 0x0227}, {0x2206, 0x6AE0}, {0x2206, 0x8B8E}, {0x2206, 0xAD25},
+       {0x2206, 0x05F6}, {0x2206, 0x25E4}, {0x2206, 0x8B8E}, {0x2206, 0xE08B},
+       {0x2206, 0x8EAD}, {0x2206, 0x260B}, {0x2206, 0xF626}, {0x2206, 0xE48B},
+       {0x2206, 0x8E02}, {0x2206, 0x830D}, {0x2206, 0x021D}, {0x2206, 0x6BE0},
+       {0x2206, 0x8B8E}, {0x2206, 0xAD27}, {0x2206, 0x05F6}, {0x2206, 0x27E4},
+       {0x2206, 0x8B8E}, {0x2206, 0x0281}, {0x2206, 0x4402}, {0x2206, 0x045C},
+       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B83}, {0x2206, 0xAD23},
+       {0x2206, 0x30E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x2359},
+       {0x2206, 0x02E0}, {0x2206, 0x85EF}, {0x2206, 0xE585}, {0x2206, 0xEFAC},
+       {0x2206, 0x2907}, {0x2206, 0x1F01}, {0x2206, 0x9E51}, {0x2206, 0xAD29},
+       {0x2206, 0x20E0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x06E1},
+       {0x2206, 0x8B84}, {0x2206, 0xAD28}, {0x2206, 0x42E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD21}, {0x2206, 0x06E1}, {0x2206, 0x8B84}, {0x2206, 0xAD29},
+       {0x2206, 0x36BF}, {0x2206, 0x34BF}, {0x2206, 0x022C}, {0x2206, 0x31AE},
+       {0x2206, 0x2EE0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x10E0},
+       {0x2206, 0x8B84}, {0x2206, 0xF620}, {0x2206, 0xE48B}, {0x2206, 0x84EE},
+       {0x2206, 0x8ADA}, {0x2206, 0x00EE}, {0x2206, 0x8ADB}, {0x2206, 0x00E0},
+       {0x2206, 0x8B85}, {0x2206, 0xAD21}, {0x2206, 0x0CE0}, {0x2206, 0x8B84},
+       {0x2206, 0xF621}, {0x2206, 0xE48B}, {0x2206, 0x84EE}, {0x2206, 0x8B72},
+       {0x2206, 0xFFBF}, {0x2206, 0x34C2}, {0x2206, 0x022C}, {0x2206, 0x31FC},
+       {0x2206, 0x04F8}, {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD21}, {0x2206, 0x42E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0},
+       {0x2206, 0x2358}, {0x2206, 0xC059}, {0x2206, 0x021E}, {0x2206, 0x01E1},
+       {0x2206, 0x8B72}, {0x2206, 0x1F10}, {0x2206, 0x9E2F}, {0x2206, 0xE48B},
+       {0x2206, 0x72AD}, {0x2206, 0x2123}, {0x2206, 0xE18B}, {0x2206, 0x84F7},
+       {0x2206, 0x29E5}, {0x2206, 0x8B84}, {0x2206, 0xAC27}, {0x2206, 0x10AC},
+       {0x2206, 0x2605}, {0x2206, 0x0205}, {0x2206, 0x23AE}, {0x2206, 0x1602},
+       {0x2206, 0x0535}, {0x2206, 0x0282}, {0x2206, 0x30AE}, {0x2206, 0x0E02},
+       {0x2206, 0x056A}, {0x2206, 0x0282}, {0x2206, 0x75AE}, {0x2206, 0x0602},
+       {0x2206, 0x04DC}, {0x2206, 0x0282}, {0x2206, 0x04EF}, {0x2206, 0x96FE},
+       {0x2206, 0xFC04}, {0x2206, 0xF8F9}, {0x2206, 0xE08B}, {0x2206, 0x87AD},
+       {0x2206, 0x2321}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+       {0x2206, 0xAD26}, {0x2206, 0x18F6}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F6}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+       {0x2206, 0xE08B}, {0x2206, 0x87AD}, {0x2206, 0x233A}, {0x2206, 0xAD22},
+       {0x2206, 0x37E0}, {0x2206, 0xE020}, {0x2206, 0xE1E0}, {0x2206, 0x21AC},
+       {0x2206, 0x212E}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+       {0x2206, 0xF627}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xE2EA}, {0x2206, 0x12E3}, {0x2206, 0xEA13}, {0x2206, 0x5A8F},
+       {0x2206, 0x6A20}, {0x2206, 0xE6EA}, {0x2206, 0x12E7}, {0x2206, 0xEA13},
+       {0x2206, 0xF726}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xF727}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B87},
+       {0x2206, 0xAD23}, {0x2206, 0x38AD}, {0x2206, 0x2135}, {0x2206, 0xE0E0},
+       {0x2206, 0x20E1}, {0x2206, 0xE021}, {0x2206, 0xAC21}, {0x2206, 0x2CE0},
+       {0x2206, 0xEA14}, {0x2206, 0xE1EA}, {0x2206, 0x15F6}, {0x2206, 0x27E4},
+       {0x2206, 0xEA14}, {0x2206, 0xE5EA}, {0x2206, 0x15E2}, {0x2206, 0xEA12},
+       {0x2206, 0xE3EA}, {0x2206, 0x135A}, {0x2206, 0x8FE6}, {0x2206, 0xEA12},
+       {0x2206, 0xE7EA}, {0x2206, 0x13F7}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2146},
+       {0x2206, 0xE0E0}, {0x2206, 0x22E1}, {0x2206, 0xE023}, {0x2206, 0x58C0},
+       {0x2206, 0x5902}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x651F},
+       {0x2206, 0x109E}, {0x2206, 0x33E4}, {0x2206, 0x8B65}, {0x2206, 0xAD21},
+       {0x2206, 0x22AD}, {0x2206, 0x272A}, {0x2206, 0xD400}, {0x2206, 0x01BF},
+       {0x2206, 0x34F2}, {0x2206, 0x022C}, {0x2206, 0xA2BF}, {0x2206, 0x34F5},
+       {0x2206, 0x022C}, {0x2206, 0xE0E0}, {0x2206, 0x8B67}, {0x2206, 0x1B10},
+       {0x2206, 0xAA14}, {0x2206, 0xE18B}, {0x2206, 0x660D}, {0x2206, 0x1459},
+       {0x2206, 0x0FAE}, {0x2206, 0x05E1}, {0x2206, 0x8B66}, {0x2206, 0x590F},
+       {0x2206, 0xBF85}, {0x2206, 0x6102}, {0x2206, 0x2CA2}, {0x2206, 0xEF96},
+       {0x2206, 0xFEFC}, {0x2206, 0x04F8}, {0x2206, 0xF9FA}, {0x2206, 0xFBEF},
+       {0x2206, 0x79E2}, {0x2206, 0x8AD2}, {0x2206, 0xAC19}, {0x2206, 0x2DE0},
+       {0x2206, 0xE036}, {0x2206, 0xE1E0}, {0x2206, 0x37EF}, {0x2206, 0x311F},
+       {0x2206, 0x325B}, {0x2206, 0x019E}, {0x2206, 0x1F7A}, {0x2206, 0x0159},
+       {0x2206, 0x019F}, {0x2206, 0x0ABF}, {0x2206, 0x348E}, {0x2206, 0x022C},
+       {0x2206, 0x31F6}, {0x2206, 0x06AE}, {0x2206, 0x0FF6}, {0x2206, 0x0302},
+       {0x2206, 0x0470}, {0x2206, 0xF703}, {0x2206, 0xF706}, {0x2206, 0xBF34},
+       {0x2206, 0x9302}, {0x2206, 0x2C31}, {0x2206, 0xAC1A}, {0x2206, 0x25E0},
+       {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x23EF}, {0x2206, 0x300D},
+       {0x2206, 0x311F}, {0x2206, 0x325B}, {0x2206, 0x029E}, {0x2206, 0x157A},
+       {0x2206, 0x0258}, {0x2206, 0xC4A0}, {0x2206, 0x0408}, {0x2206, 0xBF34},
+       {0x2206, 0x9E02}, {0x2206, 0x2C31}, {0x2206, 0xAE06}, {0x2206, 0xBF34},
+       {0x2206, 0x9C02}, {0x2206, 0x2C31}, {0x2206, 0xAC1B}, {0x2206, 0x4AE0},
+       {0x2206, 0xE012}, {0x2206, 0xE1E0}, {0x2206, 0x13EF}, {0x2206, 0x300D},
+       {0x2206, 0x331F}, {0x2206, 0x325B}, {0x2206, 0x1C9E}, {0x2206, 0x3AEF},
+       {0x2206, 0x325B}, {0x2206, 0x1C9F}, {0x2206, 0x09BF}, {0x2206, 0x3498},
+       {0x2206, 0x022C}, {0x2206, 0x3102}, {0x2206, 0x83C5}, {0x2206, 0x5A03},
+       {0x2206, 0x0D03}, {0x2206, 0x581C}, {0x2206, 0x1E20}, {0x2206, 0x0207},
+       {0x2206, 0xA0A0}, {0x2206, 0x000E}, {0x2206, 0x0284}, {0x2206, 0x17AD},
+       {0x2206, 0x1817}, {0x2206, 0xBF34}, {0x2206, 0x9A02}, {0x2206, 0x2C31},
+       {0x2206, 0xAE0F}, {0x2206, 0xBF34}, {0x2206, 0xC802}, {0x2206, 0x2C31},
+       {0x2206, 0xBF34}, {0x2206, 0xC502}, {0x2206, 0x2C31}, {0x2206, 0x0284},
+       {0x2206, 0x52E6}, {0x2206, 0x8AD2}, {0x2206, 0xEF97}, {0x2206, 0xFFFE},
+       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xBF34}, {0x2206, 0xDA02},
+       {0x2206, 0x2CE0}, {0x2206, 0xE58A}, {0x2206, 0xD3BF}, {0x2206, 0x34D4},
+       {0x2206, 0x022C}, {0x2206, 0xE00C}, {0x2206, 0x1159}, {0x2206, 0x02E0},
+       {0x2206, 0x8AD3}, {0x2206, 0x1E01}, {0x2206, 0xE48A}, {0x2206, 0xD3D1},
+       {0x2206, 0x00BF}, {0x2206, 0x34DA}, {0x2206, 0x022C}, {0x2206, 0xA2D1},
+       {0x2206, 0x01BF}, {0x2206, 0x34D4}, {0x2206, 0x022C}, {0x2206, 0xA2BF},
+       {0x2206, 0x34CB}, {0x2206, 0x022C}, {0x2206, 0xE0E5}, {0x2206, 0x8ACE},
+       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CE0}, {0x2206, 0xE58A},
+       {0x2206, 0xCFBF}, {0x2206, 0x8564}, {0x2206, 0x022C}, {0x2206, 0xE0E5},
+       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6A02}, {0x2206, 0x2CE0},
+       {0x2206, 0xE58A}, {0x2206, 0xD1FC}, {0x2206, 0x04F8}, {0x2206, 0xE18A},
+       {0x2206, 0xD1BF}, {0x2206, 0x856A}, {0x2206, 0x022C}, {0x2206, 0xA2E1},
+       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+       {0x2206, 0xE18A}, {0x2206, 0xCFBF}, {0x2206, 0x8567}, {0x2206, 0x022C},
+       {0x2206, 0xA2E1}, {0x2206, 0x8ACE}, {0x2206, 0xBF34}, {0x2206, 0xCB02},
+       {0x2206, 0x2CA2}, {0x2206, 0xE18A}, {0x2206, 0xD3BF}, {0x2206, 0x34DA},
+       {0x2206, 0x022C}, {0x2206, 0xA2E1}, {0x2206, 0x8AD3}, {0x2206, 0x0D11},
+       {0x2206, 0xBF34}, {0x2206, 0xD402}, {0x2206, 0x2CA2}, {0x2206, 0xFC04},
+       {0x2206, 0xF9A0}, {0x2206, 0x0405}, {0x2206, 0xE38A}, {0x2206, 0xD4AE},
+       {0x2206, 0x13A0}, {0x2206, 0x0805}, {0x2206, 0xE38A}, {0x2206, 0xD5AE},
+       {0x2206, 0x0BA0}, {0x2206, 0x0C05}, {0x2206, 0xE38A}, {0x2206, 0xD6AE},
+       {0x2206, 0x03E3}, {0x2206, 0x8AD7}, {0x2206, 0xEF13}, {0x2206, 0xBF34},
+       {0x2206, 0xCB02}, {0x2206, 0x2CA2}, {0x2206, 0xEF13}, {0x2206, 0x0D11},
+       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CA2}, {0x2206, 0xEF13},
+       {0x2206, 0x0D14}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+       {0x2206, 0xEF13}, {0x2206, 0x0D17}, {0x2206, 0xBF85}, {0x2206, 0x6A02},
+       {0x2206, 0x2CA2}, {0x2206, 0xFD04}, {0x2206, 0xF8E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD27}, {0x2206, 0x2DE0}, {0x2206, 0xE036}, {0x2206, 0xE1E0},
+       {0x2206, 0x37E1}, {0x2206, 0x8B73}, {0x2206, 0x1F10}, {0x2206, 0x9E20},
+       {0x2206, 0xE48B}, {0x2206, 0x73AC}, {0x2206, 0x200B}, {0x2206, 0xAC21},
+       {0x2206, 0x0DAC}, {0x2206, 0x250F}, {0x2206, 0xAC27}, {0x2206, 0x0EAE},
+       {0x2206, 0x0F02}, {0x2206, 0x84CC}, {0x2206, 0xAE0A}, {0x2206, 0x0284},
+       {0x2206, 0xD1AE}, {0x2206, 0x05AE}, {0x2206, 0x0302}, {0x2206, 0x84D8},
+       {0x2206, 0xFC04}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0x0402},
+       {0x2206, 0x84E5}, {0x2206, 0x0285}, {0x2206, 0x2804}, {0x2206, 0x0285},
+       {0x2206, 0x4904}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0xEE8B},
+       {0x2206, 0x6902}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD26}, {0x2206, 0x38D0}, {0x2206, 0x0B02}, {0x2206, 0x2B4D},
+       {0x2206, 0x5882}, {0x2206, 0x7882}, {0x2206, 0x9F2D}, {0x2206, 0xE08B},
+       {0x2206, 0x68E1}, {0x2206, 0x8B69}, {0x2206, 0x1F10}, {0x2206, 0x9EC8},
+       {0x2206, 0x10E4}, {0x2206, 0x8B68}, {0x2206, 0xE0E0}, {0x2206, 0x00E1},
+       {0x2206, 0xE001}, {0x2206, 0xF727}, {0x2206, 0xE4E0}, {0x2206, 0x00E5},
+       {0x2206, 0xE001}, {0x2206, 0xE2E0}, {0x2206, 0x20E3}, {0x2206, 0xE021},
+       {0x2206, 0xAD30}, {0x2206, 0xF7F6}, {0x2206, 0x27E4}, {0x2206, 0xE000},
+       {0x2206, 0xE5E0}, {0x2206, 0x01FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2212},
+       {0x2206, 0xE0E0}, {0x2206, 0x14E1}, {0x2206, 0xE015}, {0x2206, 0xAD26},
+       {0x2206, 0x9CE1}, {0x2206, 0x85E0}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x04F8},
+       {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B86}, {0x2206, 0xAD22},
+       {0x2206, 0x09E1}, {0x2206, 0x85E1}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x0464},
+       {0x2206, 0xE48C}, {0x2206, 0xFDE4}, {0x2206, 0x80CA}, {0x2206, 0xE480},
+       {0x2206, 0x66E0}, {0x2206, 0x8E70}, {0x2206, 0xE076}, {0x2205, 0xE142},
+       {0x2206, 0x0701}, {0x2205, 0xE140}, {0x2206, 0x0405}, {0x220F, 0x0000},
+       {0x221F, 0x0000}, {0x2200, 0x1340}, {0x133E, 0x000E}, {0x133F, 0x0010},
+       {0x13EB, 0x11BB}
+};
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_1[] = {
+       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x1305, 0xC000}, {0x121E, 0x03CA},
+       {0x1233, 0x0352}, {0x1234, 0x0064}, {0x1237, 0x0096}, {0x1238, 0x0078},
+       {0x1239, 0x0084}, {0x123A, 0x0030}, {0x205F, 0x0002}, {0x2059, 0x1A00},
+       {0x205F, 0x0000}, {0x207F, 0x0002}, {0x2077, 0x0000}, {0x2078, 0x0000},
+       {0x2079, 0x0000}, {0x207A, 0x0000}, {0x207B, 0x0000}, {0x207F, 0x0000},
+       {0x205F, 0x0002}, {0x2053, 0x0000}, {0x2054, 0x0000}, {0x2055, 0x0000},
+       {0x2056, 0x0000}, {0x2057, 0x0000}, {0x205F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2205, 0x8B86}, {0x2206, 0x800E},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x12A3, 0x2200}, {0x6107, 0xE58B},
+       {0x6103, 0xA970}, {0x0018, 0x0F00}, {0x0038, 0x0F00}, {0x0058, 0x0F00},
+       {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0005}, {0x2205, 0x8B6E}, {0x2206, 0x0000}, {0x220F, 0x0100},
+       {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8000}, {0x2206, 0x0280},
+       {0x2206, 0x2BF7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+       {0x2206, 0x6602}, {0x2206, 0x8044}, {0x2206, 0x0201}, {0x2206, 0x7CE0},
+       {0x2206, 0x8B8C}, {0x2206, 0xE18B}, {0x2206, 0x8D1E}, {0x2206, 0x01E1},
+       {0x2206, 0x8B8E}, {0x2206, 0x1E01}, {0x2206, 0xA000}, {0x2206, 0xE4AE},
+       {0x2206, 0xD8EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE}, {0x2206, 0x85C1},
+       {0x2206, 0x00EE}, {0x2206, 0x8AFC}, {0x2206, 0x07EE}, {0x2206, 0x8AFD},
+       {0x2206, 0x73EE}, {0x2206, 0xFFF6}, {0x2206, 0x00EE}, {0x2206, 0xFFF7},
+       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD20},
+       {0x2206, 0x0302}, {0x2206, 0x8050}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+       {0x2206, 0xE08B}, {0x2206, 0x85AD}, {0x2206, 0x2548}, {0x2206, 0xE08A},
+       {0x2206, 0xE4E1}, {0x2206, 0x8AE5}, {0x2206, 0x7C00}, {0x2206, 0x009E},
+       {0x2206, 0x35EE}, {0x2206, 0x8AE4}, {0x2206, 0x00EE}, {0x2206, 0x8AE5},
+       {0x2206, 0x00E0}, {0x2206, 0x8AFC}, {0x2206, 0xE18A}, {0x2206, 0xFDE2},
+       {0x2206, 0x85C0}, {0x2206, 0xE385}, {0x2206, 0xC102}, {0x2206, 0x2DAC},
+       {0x2206, 0xAD20}, {0x2206, 0x12EE}, {0x2206, 0x8AE4}, {0x2206, 0x03EE},
+       {0x2206, 0x8AE5}, {0x2206, 0xB7EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE},
+       {0x2206, 0x85C1}, {0x2206, 0x00AE}, {0x2206, 0x1115}, {0x2206, 0xE685},
+       {0x2206, 0xC0E7}, {0x2206, 0x85C1}, {0x2206, 0xAE08}, {0x2206, 0xEE85},
+       {0x2206, 0xC000}, {0x2206, 0xEE85}, {0x2206, 0xC100}, {0x2206, 0xFDFC},
+       {0x2206, 0x0400}, {0x2205, 0xE142}, {0x2206, 0x0701}, {0x2205, 0xE140},
+       {0x2206, 0x0405}, {0x220F, 0x0000}, {0x221F, 0x0000}, {0x133E, 0x000E},
+       {0x133F, 0x0010}, {0x13EB, 0x11BB}, {0x207F, 0x0002}, {0x2073, 0x1D22},
+       {0x207F, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x2200, 0x1340},
+       {0x133E, 0x000E}, {0x133F, 0x0010},
+};
+
+static int rtl8367b_write_initvals(struct rtl8366_smi *smi,
+                                 const struct rtl8367b_initval *initvals,
+                                 int count)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < count; i++)
+               REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+       return 0;
+}
+
+static int rtl8367b_read_phy_reg(struct rtl8366_smi *smi,
+                               u32 phy_addr, u32 phy_reg, u32 *val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367B_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* prepare address */
+       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send read command */
+       REG_WR(smi, RTL8367B_IA_CTRL_REG,
+              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_READ);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy read timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       /* read data */
+       REG_RD(smi, RTL8367B_IA_READ_DATA_REG, val);
+
+       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, *val);
+       return 0;
+}
+
+static int rtl8367b_write_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_addr, u32 phy_reg, u32 val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, val);
+
+       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367B_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* preapre data */
+       REG_WR(smi, RTL8367B_IA_WRITE_DATA_REG, val);
+
+       /* prepare address */
+       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send write command */
+       REG_WR(smi, RTL8367B_IA_CTRL_REG,
+              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_WRITE);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy write timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8367b_init_regs(struct rtl8366_smi *smi)
+{
+       const struct rtl8367b_initval *initvals;
+       u32 chip_ver;
+       u32 rlvid;
+       int count;
+       int err;
+
+       REG_WR(smi, RTL8367B_RTL_MAGIC_ID_REG, RTL8367B_RTL_MAGIC_ID_VAL);
+       REG_RD(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+
+       rlvid = (chip_ver >> RTL8367B_CHIP_VER_RLVID_SHIFT) &
+               RTL8367B_CHIP_VER_RLVID_MASK;
+
+       switch (rlvid) {
+       case 0:
+               initvals = rtl8367r_vb_initvals_0;
+               count = ARRAY_SIZE(rtl8367r_vb_initvals_0);
+               break;
+
+       case 1:
+               initvals = rtl8367r_vb_initvals_1;
+               count = ARRAY_SIZE(rtl8367r_vb_initvals_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+               return -ENODEV;
+       }
+
+       /* TODO: disable RLTP */
+
+       return rtl8367b_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367b_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       int err;
+       u32 data;
+
+       REG_WR(smi, RTL8367B_CHIP_RESET_REG, RTL8367B_CHIP_RESET_HW);
+       msleep(RTL8367B_RESET_DELAY);
+
+       do {
+               REG_RD(smi, RTL8367B_CHIP_RESET_REG, &data);
+               if (!(data & RTL8367B_CHIP_RESET_HW))
+                       break;
+
+               msleep(1);
+       } while (--timeout);
+
+       if (!timeout) {
+               dev_err(smi->parent, "chip reset timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
+                                  enum rtl8367_extif_mode mode)
+{
+       int err;
+
+       /* set port mode */
+       switch (mode) {
+       case RTL8367_EXTIF_MODE_RGMII:
+       case RTL8367_EXTIF_MODE_RGMII_33V:
+               REG_WR(smi, RTL8367B_CHIP_DEBUG0_REG, 0x0367);
+               REG_WR(smi, RTL8367B_CHIP_DEBUG1_REG, 0x7777);
+               break;
+
+       case RTL8367_EXTIF_MODE_TMII_MAC:
+       case RTL8367_EXTIF_MODE_TMII_PHY:
+               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), BIT((id + 1) % 2));
+               break;
+
+       case RTL8367_EXTIF_MODE_GMII:
+               REG_RMW(smi, RTL8367B_CHIP_DEBUG0_REG,
+                       RTL8367B_CHIP_DEBUG0_DUMMY0(id),
+                       RTL8367B_CHIP_DEBUG0_DUMMY0(id));
+               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+               break;
+
+       case RTL8367_EXTIF_MODE_MII_MAC:
+       case RTL8367_EXTIF_MODE_MII_PHY:
+       case RTL8367_EXTIF_MODE_DISABLED:
+               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), 0);
+               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), 0);
+               break;
+
+       default:
+               dev_err(smi->parent,
+                       "invalid mode for external interface %d\n", id);
+               return -EINVAL;
+       }
+
+       REG_RMW(smi, RTL8367B_DIS_REG,
+               RTL8367B_DIS_RGMII_MASK << RTL8367B_DIS_RGMII_SHIFT(id),
+               mode << RTL8367B_DIS_RGMII_SHIFT(id));
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_force(struct rtl8366_smi *smi, int id,
+                                   struct rtl8367_port_ability *pa)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367B_DI_FORCE_MODE |
+               RTL8367B_DI_FORCE_NWAY |
+               RTL8367B_DI_FORCE_TXPAUSE |
+               RTL8367B_DI_FORCE_RXPAUSE |
+               RTL8367B_DI_FORCE_LINK |
+               RTL8367B_DI_FORCE_DUPLEX |
+               RTL8367B_DI_FORCE_SPEED_MASK);
+
+       val = pa->speed;
+       val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
+       val |= pa->nway ? RTL8367B_DI_FORCE_NWAY : 0;
+       val |= pa->txpause ? RTL8367B_DI_FORCE_TXPAUSE : 0;
+       val |= pa->rxpause ? RTL8367B_DI_FORCE_RXPAUSE : 0;
+       val |= pa->link ? RTL8367B_DI_FORCE_LINK : 0;
+       val |= pa->duplex ? RTL8367B_DI_FORCE_DUPLEX : 0;
+
+       REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+                                        unsigned txdelay, unsigned rxdelay)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367B_EXT_RGMXF_RXDELAY_MASK |
+               (RTL8367B_EXT_RGMXF_TXDELAY_MASK <<
+                       RTL8367B_EXT_RGMXF_TXDELAY_SHIFT));
+
+       val = rxdelay;
+       val |= txdelay << RTL8367B_EXT_RGMXF_TXDELAY_SHIFT;
+
+       REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id,
+                              struct rtl8367_extif_config *cfg)
+{
+       enum rtl8367_extif_mode mode;
+       int err;
+
+       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+       err = rtl8367b_extif_set_mode(smi, id, mode);
+       if (err)
+               return err;
+
+       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+               err = rtl8367b_extif_set_force(smi, id, &cfg->ability);
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+                                                    cfg->rxdelay);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+                                 const char *name)
+{
+       struct rtl8367_extif_config *cfg;
+       const __be32 *prop;
+       int size;
+       int err;
+
+       prop = of_get_property(smi->parent->of_node, name, &size);
+       if (!prop)
+               return rtl8367b_extif_init(smi, id, NULL);
+
+       if (size != (9 * sizeof(*prop))) {
+               dev_err(smi->parent, "%s property is invalid\n", name);
+               return -EINVAL;
+       }
+
+       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->txdelay = be32_to_cpup(prop++);
+       cfg->rxdelay = be32_to_cpup(prop++);
+       cfg->mode = be32_to_cpup(prop++);
+       cfg->ability.force_mode = be32_to_cpup(prop++);
+       cfg->ability.txpause = be32_to_cpup(prop++);
+       cfg->ability.rxpause = be32_to_cpup(prop++);
+       cfg->ability.link = be32_to_cpup(prop++);
+       cfg->ability.duplex = be32_to_cpup(prop++);
+       cfg->ability.speed = be32_to_cpup(prop++);
+
+       err = rtl8367b_extif_init(smi, id, cfg);
+       kfree(cfg);
+
+       return err;
+}
+#else
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+                                 const char *name)
+{
+       return -EINVAL;
+}
+#endif
+
+static int rtl8367b_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8367_platform_data *pdata;
+       int err;
+       int i;
+
+       pdata = smi->parent->platform_data;
+
+       err = rtl8367b_init_regs(smi);
+       if (err)
+               return err;
+
+       /* initialize external interfaces */
+       if (smi->parent->of_node) {
+               err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0");
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1");
+               if (err)
+                       return err;
+       } else {
+               err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg);
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg);
+               if (err)
+                       return err;
+       }
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8367B_SWC0_REG, RTL8367B_SWC0_MAX_LENGTH_MASK,
+               RTL8367B_SWC0_MAX_LENGTH_1536);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8367B_VLAN_INGRESS_REG, RTL8367B_PORTS_ALL);
+
+       /*
+        * Setup egress tag mode for each port.
+        */
+       for (i = 0; i < RTL8367B_NUM_PORTS; i++)
+               REG_RMW(smi,
+                       RTL8367B_PORT_MISC_CFG_REG(i),
+                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK <<
+                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT,
+                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL <<
+                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT);
+
+       return 0;
+}
+
+static int rtl8367b_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                   int port, unsigned long long *val)
+{
+       struct rtl8366_mib_counter *mib;
+       int offset;
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8367B_NUM_PORTS ||
+           counter >= RTL8367B_NUM_MIB_COUNTERS)
+               return -EINVAL;
+
+       mib = &rtl8367b_mib_counters[counter];
+       addr = RTL8367B_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       REG_WR(smi, RTL8367B_MIB_ADDRESS_REG, addr >> 2);
+
+       /* read MIB control register */
+       REG_RD(smi, RTL8367B_MIB_CTRL0_REG(0), &data);
+
+       if (data & RTL8367B_MIB_CTRL0_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8367B_MIB_CTRL0_RESET_MASK)
+               return -EIO;
+
+       if (mib->length == 4)
+               offset = 3;
+       else
+               offset = (mib->offset + 1) % 4;
+
+       mibvalue = 0;
+       for (i = 0; i < mib->length; i++) {
+               REG_RD(smi, RTL8367B_MIB_COUNTER_REG(offset - i), &data);
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8367b_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8367B_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       REG_WR(smi, RTL8367B_TA_ADDR_REG, vid);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_READ);
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367B_TA_RDDATA_REG(i), &data[i]);
+
+       vlan4k->vid = vid;
+       vlan4k->member = (data[0] >> RTL8367B_TA_VLAN0_MEMBER_SHIFT) &
+                        RTL8367B_TA_VLAN0_MEMBER_MASK;
+       vlan4k->untag = (data[0] >> RTL8367B_TA_VLAN0_UNTAG_SHIFT) &
+                       RTL8367B_TA_VLAN0_UNTAG_MASK;
+       vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
+                     RTL8367B_TA_VLAN1_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8367B_NUM_VIDS ||
+           vlan4k->member > RTL8367B_TA_VLAN0_MEMBER_MASK ||
+           vlan4k->untag > RTL8367B_UNTAG_MASK ||
+           vlan4k->fid > RTL8367B_FIDMAX)
+               return -EINVAL;
+
+       memset(data, 0, sizeof(data));
+
+       data[0] = (vlan4k->member & RTL8367B_TA_VLAN0_MEMBER_MASK) <<
+                 RTL8367B_TA_VLAN0_MEMBER_SHIFT;
+       data[0] |= (vlan4k->untag & RTL8367B_TA_VLAN0_UNTAG_MASK) <<
+                  RTL8367B_TA_VLAN0_UNTAG_SHIFT;
+       data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
+                 RTL8367B_TA_VLAN1_FID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367B_TA_WRDATA_REG(i), data[i]);
+
+       /* write VID */
+       REG_WR(smi, RTL8367B_TA_ADDR_REG,
+              vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_WRITE);
+
+       return 0;
+}
+
+static int rtl8367b_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8367B_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367B_VLAN_MC_BASE(index) + i, &data[i]);
+
+       vlanmc->member = (data[0] >> RTL8367B_VLAN_MC0_MEMBER_SHIFT) &
+                        RTL8367B_VLAN_MC0_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8367B_VLAN_MC1_FID_SHIFT) &
+                     RTL8367B_VLAN_MC1_FID_MASK;
+       vlanmc->vid = (data[3] >> RTL8367B_VLAN_MC3_EVID_SHIFT) &
+                     RTL8367B_VLAN_MC3_EVID_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+       int err;
+       int i;
+
+       if (index >= RTL8367B_NUM_VLANS ||
+           vlanmc->vid >= RTL8367B_NUM_VIDS ||
+           vlanmc->priority > RTL8367B_PRIORITYMAX ||
+           vlanmc->member > RTL8367B_VLAN_MC0_MEMBER_MASK ||
+           vlanmc->untag > RTL8367B_UNTAG_MASK ||
+           vlanmc->fid > RTL8367B_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->member & RTL8367B_VLAN_MC0_MEMBER_MASK) <<
+                 RTL8367B_VLAN_MC0_MEMBER_SHIFT;
+       data[1] = (vlanmc->fid & RTL8367B_VLAN_MC1_FID_MASK) <<
+                 RTL8367B_VLAN_MC1_FID_SHIFT;
+       data[2] = 0;
+       data[3] = (vlanmc->vid & RTL8367B_VLAN_MC3_EVID_MASK) <<
+                  RTL8367B_VLAN_MC3_EVID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367B_VLAN_MC_BASE(index) + i, data[i]);
+
+       return 0;
+}
+
+static int rtl8367b_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
+
+       *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
+              RTL8367B_VLAN_PVID_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8367B_NUM_PORTS || index >= RTL8367B_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
+                               RTL8367B_VLAN_PVID_CTRL_MASK <<
+                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port),
+                               (index & RTL8367B_VLAN_PVID_CTRL_MASK) <<
+                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367b_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_CTRL_REG,
+                               RTL8367B_VLAN_CTRL_ENABLE,
+                               (enable) ? RTL8367B_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367b_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return 0;
+}
+
+static int rtl8367b_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8367B_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8367B_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8367b_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+
+       REG_WR(smi, RTL8367B_PORT_ISOLATION_REG(port),
+              (enable) ? RTL8367B_PORTS_ALL : 0);
+
+       return 0;
+}
+
+static int rtl8367b_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(0), 0,
+                               RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
+                                   int port,
+                                   struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
+
+       link->link = !!(data & RTL8367B_PORT_STATUS_LINK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8367B_PORT_STATUS_DUPLEX);
+       link->rx_flow = !!(data & RTL8367B_PORT_STATUS_RXPAUSE);
+       link->tx_flow = !!(data & RTL8367B_PORT_STATUS_TXPAUSE);
+       link->aneg = !!(data & RTL8367B_PORT_STATUS_NWAY);
+
+       speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8367b_sw_get_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8367B_SWC0_REG, &data);
+       val->value.i = (data & RTL8367B_SWC0_MAX_LENGTH_MASK) >>
+                       RTL8367B_SWC0_MAX_LENGTH_SHIFT;
+
+       return 0;
+}
+
+static int rtl8367b_sw_set_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 max_len;
+
+       switch (val->value.i) {
+       case 0:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1522;
+               break;
+       case 1:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1536;
+               break;
+       case 2:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1552;
+               break;
+       case 3:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_16000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_SWC0_REG,
+                               RTL8367B_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367b_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int port;
+
+       port = val->port_vlan;
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(port / 8), 0,
+                               RTL8367B_MIB_CTRL0_PORT_RESET_MASK(port % 8));
+}
+
+static int rtl8367b_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8367B_MIB_TXB_ID, RTL8367B_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8367b_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8367b_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+                              "(0:1522, 1:1536, 2:1552, 3:16000)",
+               .set = rtl8367b_sw_set_max_length,
+               .get = rtl8367b_sw_get_max_length,
+               .max = 3,
+       }
+};
+
+static struct switch_attr rtl8367b_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8367b_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr rtl8367b_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       },
+};
+
+static const struct switch_dev_ops rtl8367b_sw_ops = {
+       .attr_global = {
+               .attr = rtl8367b_globals,
+               .n_attr = ARRAY_SIZE(rtl8367b_globals),
+       },
+       .attr_port = {
+               .attr = rtl8367b_port,
+               .n_attr = ARRAY_SIZE(rtl8367b_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8367b_vlan,
+               .n_attr = ARRAY_SIZE(rtl8367b_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8367b_sw_get_port_link,
+       .get_port_stats = rtl8367b_sw_get_port_stats,
+};
+
+static int rtl8367b_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8367B";
+       dev->cpu_port = RTL8367B_CPU_PORT_NUM;
+       dev->ports = RTL8367B_NUM_PORTS;
+       dev->vlans = RTL8367B_NUM_VIDS;
+       dev->ops = &rtl8367b_sw_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8367b_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367b_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8367b_read_phy_reg(smi, addr, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8367b_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8367b_write_phy_reg(smi, addr, reg, val);
+       if (err)
+               return err;
+
+       /* flush write */
+       (void) rtl8367b_read_phy_reg(smi, addr, reg, &t);
+
+       return err;
+}
+
+static int rtl8367b_detect(struct rtl8366_smi *smi)
+{
+       const char *chip_name;
+       u32 chip_num;
+       u32 chip_ver;
+       u32 chip_mode;
+       int ret;
+
+       /* TODO: improve chip detection */
+       rtl8366_smi_write_reg(smi, RTL8367B_RTL_MAGIC_ID_REG,
+                             RTL8367B_RTL_MAGIC_ID_VAL);
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_NUMBER_REG, &chip_num);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip number");
+               return ret;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip version");
+               return ret;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_MODE_REG, &chip_mode);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip mode");
+               return ret;
+       }
+
+       switch (chip_ver) {
+       case 0x1000:
+               chip_name = "8367RB";
+               break;
+       case 0x1010:
+               chip_name = "8367R-VB";
+               break;
+       default:
+               dev_err(smi->parent,
+                       "unknown chip num:%04x ver:%04x, mode:%04x\n",
+                       chip_num, chip_ver, chip_mode);
+               return -ENODEV;
+       }
+
+       dev_info(smi->parent, "RTL%s chip found\n", chip_name);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367b_smi_ops = {
+       .detect         = rtl8367b_detect,
+       .reset_chip     = rtl8367b_reset_chip,
+       .setup          = rtl8367b_setup,
+
+       .mii_read       = rtl8367b_mii_read,
+       .mii_write      = rtl8367b_mii_write,
+
+       .get_vlan_mc    = rtl8367b_get_vlan_mc,
+       .set_vlan_mc    = rtl8367b_set_vlan_mc,
+       .get_vlan_4k    = rtl8367b_get_vlan_4k,
+       .set_vlan_4k    = rtl8367b_set_vlan_4k,
+       .get_mc_index   = rtl8367b_get_mc_index,
+       .set_mc_index   = rtl8367b_set_mc_index,
+       .get_mib_counter = rtl8367b_get_mib_counter,
+       .is_vlan_valid  = rtl8367b_is_vlan_valid,
+       .enable_vlan    = rtl8367b_enable_vlan,
+       .enable_vlan4k  = rtl8367b_enable_vlan4k,
+       .enable_port    = rtl8367b_enable_port,
+};
+
+static int  rtl8367b_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 1500;
+       smi->cmd_read = 0xb9;
+       smi->cmd_write = 0xb8;
+       smi->ops = &rtl8367b_smi_ops;
+       smi->cpu_port = RTL8367B_CPU_PORT_NUM;
+       smi->num_ports = RTL8367B_NUM_PORTS;
+       smi->num_vlan_mc = RTL8367B_NUM_VLANS;
+       smi->mib_counters = rtl8367b_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8367b_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8367b_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8367b_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+static void rtl8367b_shutdown(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi)
+               rtl8367b_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367b_match[] = {
+       { .compatible = "realtek,rtl8367b" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367b_match);
+#endif
+
+static struct platform_driver rtl8367b_driver = {
+       .driver = {
+               .name           = RTL8367B_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8367b_match),
+#endif
+       },
+       .probe          = rtl8367b_probe,
+       .remove         = rtl8367b_remove,
+       .shutdown       = rtl8367b_shutdown,
+};
+
+module_platform_driver(rtl8367b_driver);
+
+MODULE_DESCRIPTION("Realtek RTL8367B ethernet switch driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367B_DRIVER_NAME);
+
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/swconfig.c b/target/linux/generic/files-4.14/drivers/net/phy/swconfig.c
new file mode 100644 (file)
index 0000000..e8a6847
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+ * swconfig.c: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/capability.h>
+#include <linux/skbuff.h>
+#include <linux/switch.h>
+#include <linux/of.h>
+#include <linux/version.h>
+#include <uapi/linux/mii.h>
+
+#define SWCONFIG_DEVNAME       "switch%d"
+
+#include "swconfig_leds.c"
+
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_LICENSE("GPL");
+
+static int swdev_id;
+static struct list_head swdevs;
+static DEFINE_MUTEX(swdevs_lock);
+struct swconfig_callback;
+
+struct swconfig_callback {
+       struct sk_buff *msg;
+       struct genlmsghdr *hdr;
+       struct genl_info *info;
+       int cmd;
+
+       /* callback for filling in the message data */
+       int (*fill)(struct swconfig_callback *cb, void *arg);
+
+       /* callback for closing the message before sending it */
+       int (*close)(struct swconfig_callback *cb, void *arg);
+
+       struct nlattr *nest[4];
+       int args[4];
+};
+
+/* defaults */
+
+static int
+swconfig_get_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       int ret;
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       if (!dev->ops->get_vlan_ports)
+               return -EOPNOTSUPP;
+
+       ret = dev->ops->get_vlan_ports(dev, val);
+       return ret;
+}
+
+static int
+swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct switch_port *ports = val->value.ports;
+       const struct switch_dev_ops *ops = dev->ops;
+       int i;
+
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       /* validate ports */
+       if (val->len > dev->ports)
+               return -EINVAL;
+
+       if (!ops->set_vlan_ports)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < val->len; i++) {
+               if (ports[i].id >= dev->ports)
+                       return -EINVAL;
+
+               if (ops->set_port_pvid &&
+                   !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+                       ops->set_port_pvid(dev, ports[i].id, val->port_vlan);
+       }
+
+       return ops->set_vlan_ports(dev, val);
+}
+
+static int
+swconfig_set_pvid(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->set_port_pvid)
+               return -EOPNOTSUPP;
+
+       return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i);
+}
+
+static int
+swconfig_get_pvid(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->get_port_pvid)
+               return -EOPNOTSUPP;
+
+       return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i);
+}
+
+static int
+swconfig_set_link(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (!dev->ops->set_port_link)
+               return -EOPNOTSUPP;
+
+       return dev->ops->set_port_link(dev, val->port_vlan, val->value.link);
+}
+
+static int
+swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct switch_port_link *link = val->value.link;
+
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->get_port_link)
+               return -EOPNOTSUPP;
+
+       memset(link, 0, sizeof(*link));
+       return dev->ops->get_port_link(dev, val->port_vlan, link);
+}
+
+static int
+swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->apply_config)
+               return 0;
+
+       return dev->ops->apply_config(dev);
+}
+
+static int
+swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->reset_switch)
+               return 0;
+
+       return dev->ops->reset_switch(dev);
+}
+
+enum global_defaults {
+       GLOBAL_APPLY,
+       GLOBAL_RESET,
+};
+
+enum vlan_defaults {
+       VLAN_PORTS,
+};
+
+enum port_defaults {
+       PORT_PVID,
+       PORT_LINK,
+};
+
+static struct switch_attr default_global[] = {
+       [GLOBAL_APPLY] = {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "apply",
+               .description = "Activate changes in the hardware",
+               .set = swconfig_apply_config,
+       },
+       [GLOBAL_RESET] = {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset",
+               .description = "Reset the switch",
+               .set = swconfig_reset_switch,
+       }
+};
+
+static struct switch_attr default_port[] = {
+       [PORT_PVID] = {
+               .type = SWITCH_TYPE_INT,
+               .name = "pvid",
+               .description = "Primary VLAN ID",
+               .set = swconfig_set_pvid,
+               .get = swconfig_get_pvid,
+       },
+       [PORT_LINK] = {
+               .type = SWITCH_TYPE_LINK,
+               .name = "link",
+               .description = "Get port link information",
+               .set = swconfig_set_link,
+               .get = swconfig_get_link,
+       }
+};
+
+static struct switch_attr default_vlan[] = {
+       [VLAN_PORTS] = {
+               .type = SWITCH_TYPE_PORTS,
+               .name = "ports",
+               .description = "VLAN port mapping",
+               .set = swconfig_set_vlan_ports,
+               .get = swconfig_get_vlan_ports,
+       },
+};
+
+static const struct switch_attr *
+swconfig_find_attr_by_name(const struct switch_attrlist *alist,
+                               const char *name)
+{
+       int i;
+
+       for (i = 0; i < alist->n_attr; i++)
+               if (strcmp(name, alist->attr[i].name) == 0)
+                       return &alist->attr[i];
+
+       return NULL;
+}
+
+static void swconfig_defaults_init(struct switch_dev *dev)
+{
+       const struct switch_dev_ops *ops = dev->ops;
+
+       dev->def_global = 0;
+       dev->def_vlan = 0;
+       dev->def_port = 0;
+
+       if (ops->get_vlan_ports || ops->set_vlan_ports)
+               set_bit(VLAN_PORTS, &dev->def_vlan);
+
+       if (ops->get_port_pvid || ops->set_port_pvid)
+               set_bit(PORT_PVID, &dev->def_port);
+
+       if (ops->get_port_link &&
+           !swconfig_find_attr_by_name(&ops->attr_port, "link"))
+               set_bit(PORT_LINK, &dev->def_port);
+
+       /* always present, can be no-op */
+       set_bit(GLOBAL_APPLY, &dev->def_global);
+       set_bit(GLOBAL_RESET, &dev->def_global);
+}
+
+
+static struct genl_family switch_fam;
+
+static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = {
+       [SWITCH_ATTR_ID] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING },
+       [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED },
+       [SWITCH_ATTR_TYPE] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = {
+       [SWITCH_PORT_ID] = { .type = NLA_U32 },
+       [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
+};
+
+static struct nla_policy link_policy[SWITCH_LINK_ATTR_MAX] = {
+       [SWITCH_LINK_FLAG_DUPLEX] = { .type = NLA_FLAG },
+       [SWITCH_LINK_FLAG_ANEG] = { .type = NLA_FLAG },
+       [SWITCH_LINK_SPEED] = { .type = NLA_U32 },
+};
+
+static inline void
+swconfig_lock(void)
+{
+       mutex_lock(&swdevs_lock);
+}
+
+static inline void
+swconfig_unlock(void)
+{
+       mutex_unlock(&swdevs_lock);
+}
+
+static struct switch_dev *
+swconfig_get_dev(struct genl_info *info)
+{
+       struct switch_dev *dev = NULL;
+       struct switch_dev *p;
+       int id;
+
+       if (!info->attrs[SWITCH_ATTR_ID])
+               goto done;
+
+       id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]);
+       swconfig_lock();
+       list_for_each_entry(p, &swdevs, dev_list) {
+               if (id != p->id)
+                       continue;
+
+               dev = p;
+               break;
+       }
+       if (dev)
+               mutex_lock(&dev->sw_mutex);
+       else
+               pr_debug("device %d not found\n", id);
+       swconfig_unlock();
+done:
+       return dev;
+}
+
+static inline void
+swconfig_put_dev(struct switch_dev *dev)
+{
+       mutex_unlock(&dev->sw_mutex);
+}
+
+static int
+swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
+{
+       struct switch_attr *op = arg;
+       struct genl_info *info = cb->info;
+       struct sk_buff *msg = cb->msg;
+       int id = cb->args[0];
+       void *hdr;
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
+                       NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
+       if (IS_ERR(hdr))
+               return -1;
+
+       if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name))
+               goto nla_put_failure;
+       if (op->description)
+               if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION,
+                       op->description))
+                       goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return msg->len;
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+/* spread multipart messages across multiple message buffers */
+static int
+swconfig_send_multipart(struct swconfig_callback *cb, void *arg)
+{
+       struct genl_info *info = cb->info;
+       int restart = 0;
+       int err;
+
+       do {
+               if (!cb->msg) {
+                       cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+                       if (cb->msg == NULL)
+                               goto error;
+               }
+
+               if (!(cb->fill(cb, arg) < 0))
+                       break;
+
+               /* fill failed, check if this was already the second attempt */
+               if (restart)
+                       goto error;
+
+               /* try again in a new message, send the current one */
+               restart = 1;
+               if (cb->close) {
+                       if (cb->close(cb, arg) < 0)
+                               goto error;
+               }
+               err = genlmsg_reply(cb->msg, info);
+               cb->msg = NULL;
+               if (err < 0)
+                       goto error;
+
+       } while (restart);
+
+       return 0;
+
+error:
+       if (cb->msg)
+               nlmsg_free(cb->msg);
+       return -1;
+}
+
+static int
+swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attrlist *alist;
+       struct switch_dev *dev;
+       struct swconfig_callback cb;
+       int err = -EINVAL;
+       int i;
+
+       /* defaults */
+       struct switch_attr *def_list;
+       unsigned long *def_active;
+       int n_def;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       switch (hdr->cmd) {
+       case SWITCH_CMD_LIST_GLOBAL:
+               alist = &dev->ops->attr_global;
+               def_list = default_global;
+               def_active = &dev->def_global;
+               n_def = ARRAY_SIZE(default_global);
+               break;
+       case SWITCH_CMD_LIST_VLAN:
+               alist = &dev->ops->attr_vlan;
+               def_list = default_vlan;
+               def_active = &dev->def_vlan;
+               n_def = ARRAY_SIZE(default_vlan);
+               break;
+       case SWITCH_CMD_LIST_PORT:
+               alist = &dev->ops->attr_port;
+               def_list = default_port;
+               def_active = &dev->def_port;
+               n_def = ARRAY_SIZE(default_port);
+               break;
+       default:
+               WARN_ON(1);
+               goto out;
+       }
+
+       memset(&cb, 0, sizeof(cb));
+       cb.info = info;
+       cb.fill = swconfig_dump_attr;
+       for (i = 0; i < alist->n_attr; i++) {
+               if (alist->attr[i].disabled)
+                       continue;
+               cb.args[0] = i;
+               err = swconfig_send_multipart(&cb, (void *) &alist->attr[i]);
+               if (err < 0)
+                       goto error;
+       }
+
+       /* defaults */
+       for (i = 0; i < n_def; i++) {
+               if (!test_bit(i, def_active))
+                       continue;
+               cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i;
+               err = swconfig_send_multipart(&cb, (void *) &def_list[i]);
+               if (err < 0)
+                       goto error;
+       }
+       swconfig_put_dev(dev);
+
+       if (!cb.msg)
+               return 0;
+
+       return genlmsg_reply(cb.msg, info);
+
+error:
+       if (cb.msg)
+               nlmsg_free(cb.msg);
+out:
+       swconfig_put_dev(dev);
+       return err;
+}
+
+static const struct switch_attr *
+swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info,
+               struct switch_val *val)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attrlist *alist;
+       const struct switch_attr *attr = NULL;
+       unsigned int attr_id;
+
+       /* defaults */
+       struct switch_attr *def_list;
+       unsigned long *def_active;
+       int n_def;
+
+       if (!info->attrs[SWITCH_ATTR_OP_ID])
+               goto done;
+
+       switch (hdr->cmd) {
+       case SWITCH_CMD_SET_GLOBAL:
+       case SWITCH_CMD_GET_GLOBAL:
+               alist = &dev->ops->attr_global;
+               def_list = default_global;
+               def_active = &dev->def_global;
+               n_def = ARRAY_SIZE(default_global);
+               break;
+       case SWITCH_CMD_SET_VLAN:
+       case SWITCH_CMD_GET_VLAN:
+               alist = &dev->ops->attr_vlan;
+               def_list = default_vlan;
+               def_active = &dev->def_vlan;
+               n_def = ARRAY_SIZE(default_vlan);
+               if (!info->attrs[SWITCH_ATTR_OP_VLAN])
+                       goto done;
+               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]);
+               if (val->port_vlan >= dev->vlans)
+                       goto done;
+               break;
+       case SWITCH_CMD_SET_PORT:
+       case SWITCH_CMD_GET_PORT:
+               alist = &dev->ops->attr_port;
+               def_list = default_port;
+               def_active = &dev->def_port;
+               n_def = ARRAY_SIZE(default_port);
+               if (!info->attrs[SWITCH_ATTR_OP_PORT])
+                       goto done;
+               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]);
+               if (val->port_vlan >= dev->ports)
+                       goto done;
+               break;
+       default:
+               WARN_ON(1);
+               goto done;
+       }
+
+       if (!alist)
+               goto done;
+
+       attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
+       if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) {
+               attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET;
+               if (attr_id >= n_def)
+                       goto done;
+               if (!test_bit(attr_id, def_active))
+                       goto done;
+               attr = &def_list[attr_id];
+       } else {
+               if (attr_id >= alist->n_attr)
+                       goto done;
+               attr = &alist->attr[attr_id];
+       }
+
+       if (attr->disabled)
+               attr = NULL;
+
+done:
+       if (!attr)
+               pr_debug("attribute lookup failed\n");
+       val->attr = attr;
+       return attr;
+}
+
+static int
+swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head,
+               struct switch_val *val, int max)
+{
+       struct nlattr *nla;
+       int rem;
+
+       val->len = 0;
+       nla_for_each_nested(nla, head, rem) {
+               struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
+               struct switch_port *port;
+
+               if (val->len >= max)
+                       return -EINVAL;
+
+               port = &val->value.ports[val->len];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
+               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
+                               port_policy))
+#else
+               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
+                               port_policy, NULL))
+#endif
+                       return -EINVAL;
+
+               if (!tb[SWITCH_PORT_ID])
+                       return -EINVAL;
+
+               port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
+               if (tb[SWITCH_PORT_FLAG_TAGGED])
+                       port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+       }
+
+       return 0;
+}
+
+static int
+swconfig_parse_link(struct sk_buff *msg, struct nlattr *nla,
+                   struct switch_port_link *link)
+{
+       struct nlattr *tb[SWITCH_LINK_ATTR_MAX + 1];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
+       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy))
+#else
+       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy, NULL))
+#endif
+               return -EINVAL;
+
+       link->duplex = !!tb[SWITCH_LINK_FLAG_DUPLEX];
+       link->aneg = !!tb[SWITCH_LINK_FLAG_ANEG];
+       link->speed = nla_get_u32(tb[SWITCH_LINK_SPEED]);
+
+       return 0;
+}
+
+static int
+swconfig_set_attr(struct sk_buff *skb, struct genl_info *info)
+{
+       const struct switch_attr *attr;
+       struct switch_dev *dev;
+       struct switch_val val;
+       int err = -EINVAL;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       memset(&val, 0, sizeof(val));
+       attr = swconfig_lookup_attr(dev, info, &val);
+       if (!attr || !attr->set)
+               goto error;
+
+       val.attr = attr;
+       switch (attr->type) {
+       case SWITCH_TYPE_NOVAL:
+               break;
+       case SWITCH_TYPE_INT:
+               if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT])
+                       goto error;
+               val.value.i =
+                       nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]);
+               break;
+       case SWITCH_TYPE_STRING:
+               if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR])
+                       goto error;
+               val.value.s =
+                       nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]);
+               break;
+       case SWITCH_TYPE_PORTS:
+               val.value.ports = dev->portbuf;
+               memset(dev->portbuf, 0,
+                       sizeof(struct switch_port) * dev->ports);
+
+               /* TODO: implement multipart? */
+               if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) {
+                       err = swconfig_parse_ports(skb,
+                               info->attrs[SWITCH_ATTR_OP_VALUE_PORTS],
+                               &val, dev->ports);
+                       if (err < 0)
+                               goto error;
+               } else {
+                       val.len = 0;
+                       err = 0;
+               }
+               break;
+       case SWITCH_TYPE_LINK:
+               val.value.link = &dev->linkbuf;
+               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
+
+               if (info->attrs[SWITCH_ATTR_OP_VALUE_LINK]) {
+                       err = swconfig_parse_link(skb,
+                                                 info->attrs[SWITCH_ATTR_OP_VALUE_LINK],
+                                                 val.value.link);
+                       if (err < 0)
+                               goto error;
+               } else {
+                       val.len = 0;
+                       err = 0;
+               }
+               break;
+       default:
+               goto error;
+       }
+
+       err = attr->set(dev, attr, &val);
+error:
+       swconfig_put_dev(dev);
+       return err;
+}
+
+static int
+swconfig_close_portlist(struct swconfig_callback *cb, void *arg)
+{
+       if (cb->nest[0])
+               nla_nest_end(cb->msg, cb->nest[0]);
+       return 0;
+}
+
+static int
+swconfig_send_port(struct swconfig_callback *cb, void *arg)
+{
+       const struct switch_port *port = arg;
+       struct nlattr *p = NULL;
+
+       if (!cb->nest[0]) {
+               cb->nest[0] = nla_nest_start(cb->msg, cb->cmd);
+               if (!cb->nest[0])
+                       return -1;
+       }
+
+       p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT);
+       if (!p)
+               goto error;
+
+       if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id))
+               goto nla_put_failure;
+       if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+               if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED))
+                       goto nla_put_failure;
+       }
+
+       nla_nest_end(cb->msg, p);
+       return 0;
+
+nla_put_failure:
+               nla_nest_cancel(cb->msg, p);
+error:
+       nla_nest_cancel(cb->msg, cb->nest[0]);
+       return -1;
+}
+
+static int
+swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr,
+               const struct switch_val *val)
+{
+       struct swconfig_callback cb;
+       int err = 0;
+       int i;
+
+       if (!val->value.ports)
+               return -EINVAL;
+
+       memset(&cb, 0, sizeof(cb));
+       cb.cmd = attr;
+       cb.msg = *msg;
+       cb.info = info;
+       cb.fill = swconfig_send_port;
+       cb.close = swconfig_close_portlist;
+
+       cb.nest[0] = nla_nest_start(cb.msg, cb.cmd);
+       for (i = 0; i < val->len; i++) {
+               err = swconfig_send_multipart(&cb, &val->value.ports[i]);
+               if (err)
+                       goto done;
+       }
+       err = val->len;
+       swconfig_close_portlist(&cb, NULL);
+       *msg = cb.msg;
+
+done:
+       return err;
+}
+
+static int
+swconfig_send_link(struct sk_buff *msg, struct genl_info *info, int attr,
+                  const struct switch_port_link *link)
+{
+       struct nlattr *p = NULL;
+       int err = 0;
+
+       p = nla_nest_start(msg, attr);
+       if (link->link) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_LINK))
+                       goto nla_put_failure;
+       }
+       if (link->duplex) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_DUPLEX))
+                       goto nla_put_failure;
+       }
+       if (link->aneg) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_ANEG))
+                       goto nla_put_failure;
+       }
+       if (link->tx_flow) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_TX_FLOW))
+                       goto nla_put_failure;
+       }
+       if (link->rx_flow) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_RX_FLOW))
+                       goto nla_put_failure;
+       }
+       if (nla_put_u32(msg, SWITCH_LINK_SPEED, link->speed))
+               goto nla_put_failure;
+       if (link->eee & ADVERTISED_100baseT_Full) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_100BASET))
+                       goto nla_put_failure;
+       }
+       if (link->eee & ADVERTISED_1000baseT_Full) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_1000BASET))
+                       goto nla_put_failure;
+       }
+       nla_nest_end(msg, p);
+
+       return err;
+
+nla_put_failure:
+       nla_nest_cancel(msg, p);
+       return -1;
+}
+
+static int
+swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attr *attr;
+       struct switch_dev *dev;
+       struct sk_buff *msg = NULL;
+       struct switch_val val;
+       int err = -EINVAL;
+       int cmd = hdr->cmd;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       memset(&val, 0, sizeof(val));
+       attr = swconfig_lookup_attr(dev, info, &val);
+       if (!attr || !attr->get)
+               goto error;
+
+       if (attr->type == SWITCH_TYPE_PORTS) {
+               val.value.ports = dev->portbuf;
+               memset(dev->portbuf, 0,
+                       sizeof(struct switch_port) * dev->ports);
+       } else if (attr->type == SWITCH_TYPE_LINK) {
+               val.value.link = &dev->linkbuf;
+               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
+       }
+
+       err = attr->get(dev, attr, &val);
+       if (err)
+               goto error;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               goto error;
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
+                       0, cmd);
+       if (IS_ERR(hdr))
+               goto nla_put_failure;
+
+       switch (attr->type) {
+       case SWITCH_TYPE_INT:
+               if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i))
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_STRING:
+               if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s))
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_PORTS:
+               err = swconfig_send_ports(&msg, info,
+                               SWITCH_ATTR_OP_VALUE_PORTS, &val);
+               if (err < 0)
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_LINK:
+               err = swconfig_send_link(msg, info,
+                                        SWITCH_ATTR_OP_VALUE_LINK, val.value.link);
+               if (err < 0)
+                       goto nla_put_failure;
+               break;
+       default:
+               pr_debug("invalid type in attribute\n");
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+       genlmsg_end(msg, hdr);
+       err = msg->len;
+       if (err < 0)
+               goto nla_put_failure;
+
+       swconfig_put_dev(dev);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       if (msg)
+               nlmsg_free(msg);
+error:
+       swconfig_put_dev(dev);
+       if (!err)
+               err = -ENOMEM;
+       return err;
+}
+
+static int
+swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+               const struct switch_dev *dev)
+{
+       struct nlattr *p = NULL, *m = NULL;
+       void *hdr;
+       int i;
+
+       hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
+                       SWITCH_CMD_NEW_ATTR);
+       if (IS_ERR(hdr))
+               return -1;
+
+       if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port))
+               goto nla_put_failure;
+
+       m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP);
+       if (!m)
+               goto nla_put_failure;
+       for (i = 0; i < dev->ports; i++) {
+               p = nla_nest_start(msg, SWITCH_ATTR_PORTS);
+               if (!p)
+                       continue;
+               if (dev->portmap[i].s) {
+                       if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT,
+                                               dev->portmap[i].s))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT,
+                                               dev->portmap[i].virt))
+                               goto nla_put_failure;
+               }
+               nla_nest_end(msg, p);
+       }
+       nla_nest_end(msg, m);
+       genlmsg_end(msg, hdr);
+       return msg->len;
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int swconfig_dump_switches(struct sk_buff *skb,
+               struct netlink_callback *cb)
+{
+       struct switch_dev *dev;
+       int start = cb->args[0];
+       int idx = 0;
+
+       swconfig_lock();
+       list_for_each_entry(dev, &swdevs, dev_list) {
+               if (++idx <= start)
+                       continue;
+               if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid,
+                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                               dev) < 0)
+                       break;
+       }
+       swconfig_unlock();
+       cb->args[0] = idx;
+
+       return skb->len;
+}
+
+static int
+swconfig_done(struct netlink_callback *cb)
+{
+       return 0;
+}
+
+static struct genl_ops swconfig_ops[] = {
+       {
+               .cmd = SWITCH_CMD_LIST_GLOBAL,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_LIST_VLAN,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_LIST_PORT,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_GLOBAL,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_VLAN,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_PORT,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_GLOBAL,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_VLAN,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_PORT,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_SWITCH,
+               .dumpit = swconfig_dump_switches,
+               .policy = switch_policy,
+               .done = swconfig_done,
+       }
+};
+
+static struct genl_family switch_fam = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+       .id = GENL_ID_GENERATE,
+#endif
+       .name = "switch",
+       .hdrsize = 0,
+       .version = 1,
+       .maxattr = SWITCH_ATTR_MAX,
+       .module = THIS_MODULE,
+       .ops = swconfig_ops,
+       .n_ops = ARRAY_SIZE(swconfig_ops),
+};
+
+#ifdef CONFIG_OF
+void
+of_switch_load_portmap(struct switch_dev *dev)
+{
+       struct device_node *port;
+
+       if (!dev->of_node)
+               return;
+
+       for_each_child_of_node(dev->of_node, port) {
+               const __be32 *prop;
+               const char *segment;
+               int size, phys;
+
+               if (!of_device_is_compatible(port, "swconfig,port"))
+                       continue;
+
+               if (of_property_read_string(port, "swconfig,segment", &segment))
+                       continue;
+
+               prop = of_get_property(port, "swconfig,portmap", &size);
+               if (!prop)
+                       continue;
+
+               if (size != (2 * sizeof(*prop))) {
+                       pr_err("%s: failed to parse port mapping\n",
+                                       port->name);
+                       continue;
+               }
+
+               phys = be32_to_cpup(prop++);
+               if ((phys < 0) | (phys >= dev->ports)) {
+                       pr_err("%s: physical port index out of range\n",
+                                       port->name);
+                       continue;
+               }
+
+               dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL);
+               dev->portmap[phys].virt = be32_to_cpup(prop);
+               pr_debug("Found port: %s, physical: %d, virtual: %d\n",
+                       segment, phys, dev->portmap[phys].virt);
+       }
+}
+#endif
+
+int
+register_switch(struct switch_dev *dev, struct net_device *netdev)
+{
+       struct switch_dev *sdev;
+       const int max_switches = 8 * sizeof(unsigned long);
+       unsigned long in_use = 0;
+       int err;
+       int i;
+
+       INIT_LIST_HEAD(&dev->dev_list);
+       if (netdev) {
+               dev->netdev = netdev;
+               if (!dev->alias)
+                       dev->alias = netdev->name;
+       }
+       BUG_ON(!dev->alias);
+
+       /* Make sure swdev_id doesn't overflow */
+       if (swdev_id == INT_MAX) {
+               return -ENOMEM;
+       }
+
+       if (dev->ports > 0) {
+               dev->portbuf = kzalloc(sizeof(struct switch_port) *
+                               dev->ports, GFP_KERNEL);
+               if (!dev->portbuf)
+                       return -ENOMEM;
+               dev->portmap = kzalloc(sizeof(struct switch_portmap) *
+                               dev->ports, GFP_KERNEL);
+               if (!dev->portmap) {
+                       kfree(dev->portbuf);
+                       return -ENOMEM;
+               }
+       }
+       swconfig_defaults_init(dev);
+       mutex_init(&dev->sw_mutex);
+       swconfig_lock();
+       dev->id = ++swdev_id;
+
+       list_for_each_entry(sdev, &swdevs, dev_list) {
+               if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i))
+                       continue;
+               if (i < 0 || i > max_switches)
+                       continue;
+
+               set_bit(i, &in_use);
+       }
+       i = find_first_zero_bit(&in_use, max_switches);
+
+       if (i == max_switches) {
+               swconfig_unlock();
+               return -ENFILE;
+       }
+
+#ifdef CONFIG_OF
+       if (dev->ports)
+               of_switch_load_portmap(dev);
+#endif
+
+       /* fill device name */
+       snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
+
+       list_add_tail(&dev->dev_list, &swdevs);
+       swconfig_unlock();
+
+       err = swconfig_create_led_trigger(dev);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(register_switch);
+
+void
+unregister_switch(struct switch_dev *dev)
+{
+       swconfig_destroy_led_trigger(dev);
+       kfree(dev->portbuf);
+       mutex_lock(&dev->sw_mutex);
+       swconfig_lock();
+       list_del(&dev->dev_list);
+       swconfig_unlock();
+       mutex_unlock(&dev->sw_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_switch);
+
+int
+switch_generic_set_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link)
+{
+       if (WARN_ON(!dev->ops->phy_write16))
+               return -ENOTSUPP;
+
+       /* Generic implementation */
+       if (link->aneg) {
+               dev->ops->phy_write16(dev, port, MII_BMCR, 0x0000);
+               dev->ops->phy_write16(dev, port, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+       } else {
+               u16 bmcr = 0;
+
+               if (link->duplex)
+                       bmcr |= BMCR_FULLDPLX;
+
+               switch (link->speed) {
+               case SWITCH_PORT_SPEED_10:
+                       break;
+               case SWITCH_PORT_SPEED_100:
+                       bmcr |= BMCR_SPEED100;
+                       break;
+               case SWITCH_PORT_SPEED_1000:
+                       bmcr |= BMCR_SPEED1000;
+                       break;
+               default:
+                       return -ENOTSUPP;
+               }
+
+               dev->ops->phy_write16(dev, port, MII_BMCR, bmcr);
+       }
+
+       return 0;
+}
+
+static int __init
+swconfig_init(void)
+{
+       INIT_LIST_HEAD(&swdevs);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+       return genl_register_family_with_ops(&switch_fam, swconfig_ops);
+#else
+       return genl_register_family(&switch_fam);
+#endif
+}
+
+static void __exit
+swconfig_exit(void)
+{
+       genl_unregister_family(&switch_fam);
+}
+
+module_init(swconfig_init);
+module_exit(swconfig_exit);
diff --git a/target/linux/generic/files-4.14/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files-4.14/drivers/net/phy/swconfig_leds.c
new file mode 100644 (file)
index 0000000..91824b7
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * swconfig_led.c: LED trigger support for the switch configuration API
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ */
+
+#ifdef CONFIG_SWCONFIG_LEDS
+
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+
+#define SWCONFIG_LED_TIMER_INTERVAL    (HZ / 10)
+#define SWCONFIG_LED_NUM_PORTS         32
+
+#define SWCONFIG_LED_PORT_SPEED_NA     0x01    /* unknown speed */
+#define SWCONFIG_LED_PORT_SPEED_10     0x02    /* 10 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_100    0x04    /* 100 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_1000   0x08    /* 1000 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_ALL    (SWCONFIG_LED_PORT_SPEED_NA | \
+                                        SWCONFIG_LED_PORT_SPEED_10 | \
+                                        SWCONFIG_LED_PORT_SPEED_100 | \
+                                        SWCONFIG_LED_PORT_SPEED_1000)
+
+#define SWCONFIG_LED_MODE_LINK         0x01
+#define SWCONFIG_LED_MODE_TX           0x02
+#define SWCONFIG_LED_MODE_RX           0x04
+#define SWCONFIG_LED_MODE_TXRX         (SWCONFIG_LED_MODE_TX   | \
+                                        SWCONFIG_LED_MODE_RX)
+#define SWCONFIG_LED_MODE_ALL          (SWCONFIG_LED_MODE_LINK | \
+                                        SWCONFIG_LED_MODE_TX   | \
+                                        SWCONFIG_LED_MODE_RX)
+
+struct switch_led_trigger {
+       struct led_trigger trig;
+       struct switch_dev *swdev;
+
+       struct delayed_work sw_led_work;
+       u32 port_mask;
+       u32 port_link;
+       unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
+       unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
+       u8 link_speed[SWCONFIG_LED_NUM_PORTS];
+};
+
+struct swconfig_trig_data {
+       struct led_classdev *led_cdev;
+       struct switch_dev *swdev;
+
+       rwlock_t lock;
+       u32 port_mask;
+
+       bool prev_link;
+       unsigned long prev_traffic;
+       enum led_brightness prev_brightness;
+       u8 mode;
+       u8 speed_mask;
+};
+
+static void
+swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
+                            enum led_brightness brightness)
+{
+       led_set_brightness(trig_data->led_cdev, brightness);
+       trig_data->prev_brightness = brightness;
+}
+
+static void
+swconfig_trig_update_port_mask(struct led_trigger *trigger)
+{
+       struct list_head *entry;
+       struct switch_led_trigger *sw_trig;
+       u32 port_mask;
+
+       if (!trigger)
+               return;
+
+       sw_trig = (void *) trigger;
+
+       port_mask = 0;
+       read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_cdevs) {
+               struct led_classdev *led_cdev;
+               struct swconfig_trig_data *trig_data;
+
+               led_cdev = list_entry(entry, struct led_classdev, trig_list);
+               trig_data = led_cdev->trigger_data;
+               if (trig_data) {
+                       read_lock(&trig_data->lock);
+                       port_mask |= trig_data->port_mask;
+                       read_unlock(&trig_data->lock);
+               }
+       }
+       read_unlock(&trigger->leddev_list_lock);
+
+       sw_trig->port_mask = port_mask;
+
+       if (port_mask)
+               schedule_delayed_work(&sw_trig->sw_led_work,
+                                     SWCONFIG_LED_TIMER_INTERVAL);
+       else
+               cancel_delayed_work_sync(&sw_trig->sw_led_work);
+}
+
+static ssize_t
+swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       unsigned long port_mask;
+       int ret;
+       bool changed;
+
+       ret = kstrtoul(buf, 0, &port_mask);
+       if (ret)
+               return ret;
+
+       write_lock(&trig_data->lock);
+       changed = (trig_data->port_mask != port_mask);
+       trig_data->port_mask = port_mask;
+       write_unlock(&trig_data->lock);
+
+       if (changed) {
+               if (port_mask == 0)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF);
+
+               swconfig_trig_update_port_mask(led_cdev->trigger);
+       }
+
+       return size;
+}
+
+static ssize_t
+swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u32 port_mask;
+
+       read_lock(&trig_data->lock);
+       port_mask = trig_data->port_mask;
+       read_unlock(&trig_data->lock);
+
+       sprintf(buf, "%#x\n", port_mask);
+
+       return strlen(buf) + 1;
+}
+
+static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
+                  swconfig_trig_port_mask_store);
+
+/* speed_mask file handler - display value */
+static ssize_t swconfig_trig_speed_mask_show(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 speed_mask;
+
+       read_lock(&trig_data->lock);
+       speed_mask = trig_data->speed_mask;
+       read_unlock(&trig_data->lock);
+
+       sprintf(buf, "%#x\n", speed_mask);
+
+       return strlen(buf) + 1;
+}
+
+/* speed_mask file handler - store value */
+static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
+                                             struct device_attribute *attr,
+                                             const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 speed_mask;
+       int ret;
+
+       ret = kstrtou8(buf, 0, &speed_mask);
+       if (ret)
+               return ret;
+
+       write_lock(&trig_data->lock);
+       trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
+       write_unlock(&trig_data->lock);
+
+       return size;
+}
+
+/* speed_mask special file */
+static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
+                  swconfig_trig_speed_mask_store);
+
+static ssize_t swconfig_trig_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 mode;
+
+       read_lock(&trig_data->lock);
+       mode = trig_data->mode;
+       read_unlock(&trig_data->lock);
+
+       if (mode == 0) {
+               strcpy(buf, "none\n");
+       } else {
+               if (mode & SWCONFIG_LED_MODE_LINK)
+                       strcat(buf, "link ");
+               if (mode & SWCONFIG_LED_MODE_TX)
+                       strcat(buf, "tx ");
+               if (mode & SWCONFIG_LED_MODE_RX)
+                       strcat(buf, "rx ");
+               strcat(buf, "\n");
+       }
+
+       return strlen(buf)+1;
+}
+
+static ssize_t swconfig_trig_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       char copybuf[128];
+       int new_mode = -1;
+       char *p, *token;
+
+       /* take a copy since we don't want to trash the inbound buffer when using strsep */
+       strncpy(copybuf, buf, sizeof(copybuf));
+       copybuf[sizeof(copybuf) - 1] = 0;
+       p = copybuf;
+
+       while ((token = strsep(&p, " \t\n")) != NULL) {
+               if (!*token)
+                       continue;
+
+               if (new_mode < 0)
+                       new_mode = 0;
+
+               if (!strcmp(token, "none"))
+                       new_mode = 0;
+               else if (!strcmp(token, "tx"))
+                       new_mode |= SWCONFIG_LED_MODE_TX;
+               else if (!strcmp(token, "rx"))
+                       new_mode |= SWCONFIG_LED_MODE_RX;
+               else if (!strcmp(token, "link"))
+                       new_mode |= SWCONFIG_LED_MODE_LINK;
+               else
+                       return -EINVAL;
+       }
+
+       if (new_mode < 0)
+               return -EINVAL;
+
+       write_lock(&trig_data->lock);
+       trig_data->mode = (u8)new_mode;
+       write_unlock(&trig_data->lock);
+
+       return size;
+}
+
+/* mode special file */
+static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
+                  swconfig_trig_mode_store);
+
+static void
+swconfig_trig_activate(struct led_classdev *led_cdev)
+{
+       struct switch_led_trigger *sw_trig;
+       struct swconfig_trig_data *trig_data;
+       int err;
+
+       if (led_cdev->trigger->activate != swconfig_trig_activate)
+               return;
+
+       trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
+       if (!trig_data)
+               return;
+
+       sw_trig = (void *) led_cdev->trigger;
+
+       rwlock_init(&trig_data->lock);
+       trig_data->led_cdev = led_cdev;
+       trig_data->swdev = sw_trig->swdev;
+       trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
+       trig_data->mode = SWCONFIG_LED_MODE_ALL;
+       led_cdev->trigger_data = trig_data;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
+       if (err)
+               goto err_free;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
+       if (err)
+               goto err_dev_free;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_mode);
+       if (err)
+               goto err_mode_free;
+
+       return;
+
+err_mode_free:
+       device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+
+err_dev_free:
+       device_remove_file(led_cdev->dev, &dev_attr_port_mask);
+
+err_free:
+       led_cdev->trigger_data = NULL;
+       kfree(trig_data);
+}
+
+static void
+swconfig_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct swconfig_trig_data *trig_data;
+
+       swconfig_trig_update_port_mask(led_cdev->trigger);
+
+       trig_data = (void *) led_cdev->trigger_data;
+       if (trig_data) {
+               device_remove_file(led_cdev->dev, &dev_attr_port_mask);
+               device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+               device_remove_file(led_cdev->dev, &dev_attr_mode);
+               kfree(trig_data);
+       }
+}
+
+/*
+ * link off -> led off (can't be any other reason to turn it on)
+ * link on:
+ *     mode link: led on by default only if speed matches, else off
+ *     mode txrx: blink only if speed matches, else off
+ */
+static void
+swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
+                       struct led_classdev *led_cdev)
+{
+       struct swconfig_trig_data *trig_data;
+       u32 port_mask;
+       bool link;
+       u8 speed_mask, mode;
+       enum led_brightness led_base, led_blink;
+
+       trig_data = led_cdev->trigger_data;
+       if (!trig_data)
+               return;
+
+       read_lock(&trig_data->lock);
+       port_mask = trig_data->port_mask;
+       speed_mask = trig_data->speed_mask;
+       mode = trig_data->mode;
+       read_unlock(&trig_data->lock);
+
+       link = !!(sw_trig->port_link & port_mask);
+       if (!link) {
+               if (trig_data->prev_brightness != LED_OFF)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
+       }
+       else {
+               unsigned long traffic;
+               int speedok;    /* link speed flag */
+               int i;
+
+               led_base = LED_FULL;
+               led_blink = LED_OFF;
+               traffic = 0;
+               speedok = 0;
+               for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+                       if (port_mask & (1 << i)) {
+                               if (sw_trig->link_speed[i] & speed_mask) {
+                                       traffic += ((mode & SWCONFIG_LED_MODE_TX) ?
+                                                   sw_trig->port_tx_traffic[i] : 0) +
+                                               ((mode & SWCONFIG_LED_MODE_RX) ?
+                                                sw_trig->port_rx_traffic[i] : 0);
+                                       speedok = 1;
+                               }
+                       }
+               }
+
+               if (speedok) {
+                       /* At least one port speed matches speed_mask */
+                       if (!(mode & SWCONFIG_LED_MODE_LINK)) {
+                               led_base = LED_OFF;
+                               led_blink = LED_FULL;
+                       }
+
+                       if (trig_data->prev_brightness != led_base)
+                               swconfig_trig_set_brightness(trig_data,
+                                                            led_base);
+                       else if (traffic != trig_data->prev_traffic)
+                               swconfig_trig_set_brightness(trig_data,
+                                                            led_blink);
+               } else if (trig_data->prev_brightness != LED_OFF)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF);
+
+               trig_data->prev_traffic = traffic;
+       }
+
+       trig_data->prev_link = link;
+}
+
+static void
+swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
+{
+       struct list_head *entry;
+       struct led_trigger *trigger;
+
+       trigger = &sw_trig->trig;
+       read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_cdevs) {
+               struct led_classdev *led_cdev;
+
+               led_cdev = list_entry(entry, struct led_classdev, trig_list);
+               swconfig_trig_led_event(sw_trig, led_cdev);
+       }
+       read_unlock(&trigger->leddev_list_lock);
+}
+
+static void
+swconfig_led_work_func(struct work_struct *work)
+{
+       struct switch_led_trigger *sw_trig;
+       struct switch_dev *swdev;
+       u32 port_mask;
+       u32 link;
+       int i;
+
+       sw_trig = container_of(work, struct switch_led_trigger,
+                              sw_led_work.work);
+
+       port_mask = sw_trig->port_mask;
+       swdev = sw_trig->swdev;
+
+       link = 0;
+       for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+               u32 port_bit;
+
+               sw_trig->link_speed[i] = 0;
+
+               port_bit = BIT(i);
+               if ((port_mask & port_bit) == 0)
+                       continue;
+
+               if (swdev->ops->get_port_link) {
+                       struct switch_port_link port_link;
+
+                       memset(&port_link, '\0', sizeof(port_link));
+                       swdev->ops->get_port_link(swdev, i, &port_link);
+
+                       if (port_link.link) {
+                               link |= port_bit;
+                               switch (port_link.speed) {
+                               case SWITCH_PORT_SPEED_UNKNOWN:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_NA;
+                                       break;
+                               case SWITCH_PORT_SPEED_10:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_10;
+                                       break;
+                               case SWITCH_PORT_SPEED_100:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_100;
+                                       break;
+                               case SWITCH_PORT_SPEED_1000:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_1000;
+                                       break;
+                               }
+                       }
+               }
+
+               if (swdev->ops->get_port_stats) {
+                       struct switch_port_stats port_stats;
+
+                       memset(&port_stats, '\0', sizeof(port_stats));
+                       swdev->ops->get_port_stats(swdev, i, &port_stats);
+                       sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
+                       sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
+               }
+       }
+
+       sw_trig->port_link = link;
+
+       swconfig_trig_update_leds(sw_trig);
+
+       schedule_delayed_work(&sw_trig->sw_led_work,
+                             SWCONFIG_LED_TIMER_INTERVAL);
+}
+
+static int
+swconfig_create_led_trigger(struct switch_dev *swdev)
+{
+       struct switch_led_trigger *sw_trig;
+       int err;
+
+       if (!swdev->ops->get_port_link)
+               return 0;
+
+       sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL);
+       if (!sw_trig)
+               return -ENOMEM;
+
+       sw_trig->swdev = swdev;
+       sw_trig->trig.name = swdev->devname;
+       sw_trig->trig.activate = swconfig_trig_activate;
+       sw_trig->trig.deactivate = swconfig_trig_deactivate;
+
+       INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func);
+
+       err = led_trigger_register(&sw_trig->trig);
+       if (err)
+               goto err_free;
+
+       swdev->led_trigger = sw_trig;
+
+       return 0;
+
+err_free:
+       kfree(sw_trig);
+       return err;
+}
+
+static void
+swconfig_destroy_led_trigger(struct switch_dev *swdev)
+{
+       struct switch_led_trigger *sw_trig;
+
+       sw_trig = swdev->led_trigger;
+       if (sw_trig) {
+               cancel_delayed_work_sync(&sw_trig->sw_led_work);
+               led_trigger_unregister(&sw_trig->trig);
+               kfree(sw_trig);
+       }
+}
+
+#else /* SWCONFIG_LEDS */
+static inline int
+swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; }
+
+static inline void
+swconfig_destroy_led_trigger(struct switch_dev *swdev) { }
+#endif /* CONFIG_SWCONFIG_LEDS */
diff --git a/target/linux/generic/files-4.14/include/linux/ar8216_platform.h b/target/linux/generic/files-4.14/include/linux/ar8216_platform.h
new file mode 100644 (file)
index 0000000..24bc442
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * AR8216 switch driver platform data
+ *
+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AR8216_PLATFORM_H
+#define AR8216_PLATFORM_H
+
+enum ar8327_pad_mode {
+       AR8327_PAD_NC = 0,
+       AR8327_PAD_MAC2MAC_MII,
+       AR8327_PAD_MAC2MAC_GMII,
+       AR8327_PAD_MAC_SGMII,
+       AR8327_PAD_MAC2PHY_MII,
+       AR8327_PAD_MAC2PHY_GMII,
+       AR8327_PAD_MAC_RGMII,
+       AR8327_PAD_PHY_GMII,
+       AR8327_PAD_PHY_RGMII,
+       AR8327_PAD_PHY_MII,
+};
+
+enum ar8327_clk_delay_sel {
+       AR8327_CLK_DELAY_SEL0 = 0,
+       AR8327_CLK_DELAY_SEL1,
+       AR8327_CLK_DELAY_SEL2,
+       AR8327_CLK_DELAY_SEL3,
+};
+
+struct ar8327_pad_cfg {
+       enum ar8327_pad_mode mode;
+       bool rxclk_sel;
+       bool txclk_sel;
+       bool pipe_rxclk_sel;
+       bool txclk_delay_en;
+       bool rxclk_delay_en;
+       bool sgmii_delay_en;
+       enum ar8327_clk_delay_sel txclk_delay_sel;
+       enum ar8327_clk_delay_sel rxclk_delay_sel;
+       bool mac06_exchange_dis;
+};
+
+enum ar8327_port_speed {
+       AR8327_PORT_SPEED_10 = 0,
+       AR8327_PORT_SPEED_100,
+       AR8327_PORT_SPEED_1000,
+};
+
+struct ar8327_port_cfg {
+       int force_link:1;
+       enum ar8327_port_speed speed;
+       int txpause:1;
+       int rxpause:1;
+       int duplex:1;
+};
+
+struct ar8327_sgmii_cfg {
+       u32 sgmii_ctrl;
+       bool serdes_aen;
+};
+
+struct ar8327_led_cfg {
+       u32 led_ctrl0;
+       u32 led_ctrl1;
+       u32 led_ctrl2;
+       u32 led_ctrl3;
+       bool open_drain;
+};
+
+enum ar8327_led_num {
+       AR8327_LED_PHY0_0 = 0,
+       AR8327_LED_PHY0_1,
+       AR8327_LED_PHY0_2,
+       AR8327_LED_PHY1_0,
+       AR8327_LED_PHY1_1,
+       AR8327_LED_PHY1_2,
+       AR8327_LED_PHY2_0,
+       AR8327_LED_PHY2_1,
+       AR8327_LED_PHY2_2,
+       AR8327_LED_PHY3_0,
+       AR8327_LED_PHY3_1,
+       AR8327_LED_PHY3_2,
+       AR8327_LED_PHY4_0,
+       AR8327_LED_PHY4_1,
+       AR8327_LED_PHY4_2,
+};
+
+enum ar8327_led_mode {
+       AR8327_LED_MODE_HW = 0,
+       AR8327_LED_MODE_SW,
+};
+
+struct ar8327_led_info {
+       const char *name;
+       const char *default_trigger;
+       bool active_low;
+       enum ar8327_led_num led_num;
+       enum ar8327_led_mode mode;
+};
+
+#define AR8327_LED_INFO(_led, _mode, _name) {  \
+       .name = (_name),                        \
+       .led_num = AR8327_LED_ ## _led,         \
+       .mode = AR8327_LED_MODE_ ## _mode       \
+}
+
+struct ar8327_platform_data {
+       struct ar8327_pad_cfg *pad0_cfg;
+       struct ar8327_pad_cfg *pad5_cfg;
+       struct ar8327_pad_cfg *pad6_cfg;
+       struct ar8327_sgmii_cfg *sgmii_cfg;
+       struct ar8327_port_cfg port0_cfg;
+       struct ar8327_port_cfg port6_cfg;
+       struct ar8327_led_cfg *led_cfg;
+
+       int (*get_port_link)(unsigned port);
+
+       unsigned num_leds;
+       const struct ar8327_led_info *leds;
+};
+
+#endif /* AR8216_PLATFORM_H */
+
diff --git a/target/linux/generic/files-4.14/include/linux/ath5k_platform.h b/target/linux/generic/files-4.14/include/linux/ath5k_platform.h
new file mode 100644 (file)
index 0000000..ec85224
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_ATH5K_PLATFORM_H
+#define _LINUX_ATH5K_PLATFORM_H
+
+#define ATH5K_PLAT_EEP_MAX_WORDS       2048
+
+struct ath5k_platform_data {
+       u16 *eeprom_data;
+       u8 *macaddr;
+};
+
+#endif /* _LINUX_ATH5K_PLATFORM_H */
diff --git a/target/linux/generic/files-4.14/include/linux/ath9k_platform.h b/target/linux/generic/files-4.14/include/linux/ath9k_platform.h
new file mode 100644 (file)
index 0000000..f1f2ad4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_ATH9K_PLATFORM_H
+#define _LINUX_ATH9K_PLATFORM_H
+
+#define ATH9K_PLAT_EEP_MAX_WORDS       2048
+
+struct ath9k_platform_data {
+       const char *eeprom_name;
+
+       u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
+       u8 *macaddr;
+
+       int led_pin;
+       u32 gpio_mask;
+       u32 gpio_val;
+
+       u32 bt_active_pin;
+       u32 bt_priority_pin;
+       u32 wlan_active_pin;
+
+       bool endian_check;
+       bool is_clk_25mhz;
+       bool tx_gain_buffalo;
+       bool disable_2ghz;
+       bool disable_5ghz;
+       bool led_active_high;
+
+       int (*get_mac_revision)(void);
+       int (*external_reset)(void);
+
+       bool use_eeprom;
+
+       int num_leds;
+       const struct gpio_led *leds;
+
+       unsigned num_btns;
+       const struct gpio_keys_button *btns;
+       unsigned btn_poll_interval;
+
+       bool ubnt_hsr;
+};
+
+#endif /* _LINUX_ATH9K_PLATFORM_H */
diff --git a/target/linux/generic/files-4.14/include/linux/myloader.h b/target/linux/generic/files-4.14/include/linux/myloader.h
new file mode 100644 (file)
index 0000000..d89e415
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  Compex's MyLoader specific definitions
+ *
+ *  Copyright (C) 2006-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MYLOADER_H_
+#define _MYLOADER_H_
+
+/* Myloader specific magic numbers */
+#define MYLO_MAGIC_SYS_PARAMS  0x20021107
+#define MYLO_MAGIC_PARTITIONS  0x20021103
+#define MYLO_MAGIC_BOARD_PARAMS        0x20021103
+
+/* Vendor ID's (seems to be same as the PCI vendor ID's) */
+#define VENID_COMPEX           0x11F6
+
+/* Devices based on the ADM5120 */
+#define DEVID_COMPEX_NP27G     0x0078
+#define DEVID_COMPEX_NP28G     0x044C
+#define DEVID_COMPEX_NP28GHS   0x044E
+#define DEVID_COMPEX_WP54Gv1C  0x0514
+#define DEVID_COMPEX_WP54G     0x0515
+#define DEVID_COMPEX_WP54AG    0x0546
+#define DEVID_COMPEX_WPP54AG   0x0550
+#define DEVID_COMPEX_WPP54G    0x0555
+
+/* Devices based on the Atheros AR2317 */
+#define DEVID_COMPEX_NP25G     0x05E6
+#define DEVID_COMPEX_WPE53G    0x05DC
+
+/* Devices based on the Atheros AR71xx */
+#define DEVID_COMPEX_WP543     0x0640
+#define DEVID_COMPEX_WPE72     0x0672
+
+/* Devices based on the IXP422 */
+#define DEVID_COMPEX_WP18      0x047E
+#define DEVID_COMPEX_NP18A     0x0489
+
+/* Other devices */
+#define DEVID_COMPEX_NP26G8M   0x03E8
+#define DEVID_COMPEX_NP26G16M  0x03E9
+
+struct mylo_partition {
+       uint16_t        flags;  /* partition flags */
+       uint16_t        type;   /* type of the partition */
+       uint32_t        addr;   /* relative address of the partition from the
+                                  flash start */
+       uint32_t        size;   /* size of the partition in bytes */
+       uint32_t        param;  /* if this is the active partition, the
+                                  MyLoader load code to this address */
+};
+
+#define PARTITION_FLAG_ACTIVE  0x8000 /* this is the active partition,
+                                       * MyLoader loads firmware from here */
+#define PARTITION_FLAG_ISRAM   0x2000 /* FIXME: this is a RAM partition? */
+#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */
+#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM
+                                       * before decompression */
+#define PARTITION_FLAG_LZMA    0x0100 /* partition data compressed by LZMA */
+#define PARTITION_FLAG_HAVEHDR  0x0002 /* the partition data have a header */
+
+#define PARTITION_TYPE_FREE    0
+#define PARTITION_TYPE_USED    1
+
+#define MYLO_MAX_PARTITIONS    8       /* maximum number of partitions in the
+                                          partition table */
+
+struct mylo_partition_table {
+       uint32_t        magic;          /* must be MYLO_MAGIC_PARTITIONS */
+       uint32_t        res0;           /* unknown/unused */
+       uint32_t        res1;           /* unknown/unused */
+       uint32_t        res2;           /* unknown/unused */
+       struct mylo_partition partitions[MYLO_MAX_PARTITIONS];
+};
+
+struct mylo_partition_header {
+       uint32_t        len;            /* length of the partition data */
+       uint32_t        crc;            /* CRC value of the partition data */
+};
+
+struct mylo_system_params {
+       uint32_t        magic;          /* must be MYLO_MAGIC_SYS_PARAMS */
+       uint32_t        res0;
+       uint32_t        res1;
+       uint32_t        mylo_ver;
+       uint16_t        vid;            /* Vendor ID */
+       uint16_t        did;            /* Device ID */
+       uint16_t        svid;           /* Sub Vendor ID */
+       uint16_t        sdid;           /* Sub Device ID */
+       uint32_t        rev;            /* device revision */
+       uint32_t        fwhi;
+       uint32_t        fwlo;
+       uint32_t        tftp_addr;
+       uint32_t        prog_start;
+       uint32_t        flash_size;     /* size of boot FLASH in bytes */
+       uint32_t        dram_size;      /* size of onboard RAM in bytes */
+};
+
+struct mylo_eth_addr {
+       uint8_t mac[6];
+       uint8_t csum[2];
+};
+
+#define MYLO_ETHADDR_COUNT     8       /* maximum number of ethernet address
+                                          in the board parameters */
+
+struct mylo_board_params {
+       uint32_t        magic;  /* must be MYLO_MAGIC_BOARD_PARAMS */
+       uint32_t        res0;
+       uint32_t        res1;
+       uint32_t        res2;
+       struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT];
+};
+
+#endif /* _MYLOADER_H_*/
diff --git a/target/linux/generic/files-4.14/include/linux/platform_data/adm6996-gpio.h b/target/linux/generic/files-4.14/include/linux/platform_data/adm6996-gpio.h
new file mode 100644 (file)
index 0000000..d5af9bb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * ADM6996 GPIO platform data
+ *
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#ifndef __PLATFORM_ADM6996_GPIO_H
+#define __PLATFORM_ADM6996_GPIO_H
+
+#include <linux/kernel.h>
+
+enum adm6996_model {
+       ADM6996FC = 1,
+       ADM6996M = 2,
+       ADM6996L = 3,
+};
+
+struct adm6996_gpio_platform_data {
+       u8 eecs;
+       u8 eesk;
+       u8 eedi;
+       enum adm6996_model model;
+};
+
+#endif
diff --git a/target/linux/generic/files-4.14/include/linux/platform_data/b53.h b/target/linux/generic/files-4.14/include/linux/platform_data/b53.h
new file mode 100644 (file)
index 0000000..7842741
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * B53 platform data
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_H
+#define __B53_H
+
+#include <linux/kernel.h>
+
+struct b53_platform_data {
+       u32 chip_id;
+       u16 enabled_ports;
+
+       /* allow to specify an ethX alias */
+       const char *alias;
+
+       /* only used by MMAP'd driver */
+       unsigned big_endian:1;
+       void __iomem *regs;
+};
+
+#endif
diff --git a/target/linux/generic/files-4.14/include/linux/routerboot.h b/target/linux/generic/files-4.14/include/linux/routerboot.h
new file mode 100644 (file)
index 0000000..3cda858
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Mikrotik's RouterBOOT definitions
+ *
+ *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef _ROUTERBOOT_H
+#define _ROUTERBOOT_H
+
+#define RB_MAC_SIZE            6
+
+/*
+ * Magic numbers
+ */
+#define RB_MAGIC_HARD  0x64726148 /* "Hard" */
+#define RB_MAGIC_SOFT  0x74666F53 /* "Soft" */
+#define RB_MAGIC_DAWN  0x6E776144 /* "Dawn" */
+
+#define RB_ID_TERMINATOR       0
+
+/*
+ * ID values for Hardware settings
+ */
+#define RB_ID_HARD_01          1
+#define RB_ID_HARD_02          2
+#define RB_ID_FLASH_INFO       3
+#define RB_ID_MAC_ADDRESS_PACK 4
+#define RB_ID_BOARD_NAME       5
+#define RB_ID_BIOS_VERSION     6
+#define RB_ID_HARD_07          7
+#define RB_ID_SDRAM_TIMINGS    8
+#define RB_ID_DEVICE_TIMINGS   9
+#define RB_ID_SOFTWARE_ID      10
+#define RB_ID_SERIAL_NUMBER    11
+#define RB_ID_HARD_12          12
+#define RB_ID_MEMORY_SIZE      13
+#define RB_ID_MAC_ADDRESS_COUNT        14
+#define RB_ID_HW_OPTIONS       21
+#define RB_ID_WLAN_DATA                22
+
+/*
+ * ID values for Software settings
+ */
+#define RB_ID_UART_SPEED       1
+#define RB_ID_BOOT_DELAY       2
+#define RB_ID_BOOT_DEVICE      3
+#define RB_ID_BOOT_KEY         4
+#define RB_ID_CPU_MODE         5
+#define RB_ID_FW_VERSION       6
+#define RB_ID_SOFT_07          7
+#define RB_ID_SOFT_08          8
+#define RB_ID_BOOT_PROTOCOL    9
+#define RB_ID_SOFT_10          10
+#define RB_ID_SOFT_11          11
+
+/*
+ * UART_SPEED values
+ */
+#define RB_UART_SPEED_115200   0
+#define RB_UART_SPEED_57600    1
+#define RB_UART_SPEED_38400    2
+#define RB_UART_SPEED_19200    3
+#define RB_UART_SPEED_9600     4
+#define RB_UART_SPEED_4800     5
+#define RB_UART_SPEED_2400     6
+#define RB_UART_SPEED_1200     7
+
+/*
+ * BOOT_DELAY values
+ */
+#define RB_BOOT_DELAY_0SEC     0
+#define RB_BOOT_DELAY_1SEC     1
+#define RB_BOOT_DELAY_2SEC     2
+
+/*
+ * BOOT_DEVICE values
+ */
+#define RB_BOOT_DEVICE_ETHER   0
+#define RB_BOOT_DEVICE_NANDETH 1
+#define RB_BOOT_DEVICE_ETHONCE 2
+#define RB_BOOT_DEVICE_NANDONLY        3
+
+/*
+ * BOOT_KEY values
+ */
+#define RB_BOOT_KEY_ANY                0
+#define RB_BOOT_KEY_DEL                1
+
+/*
+ * CPU_MODE values
+ */
+#define RB_CPU_MODE_POWERSAVE  0
+#define RB_CPU_MODE_REGULAR    1
+
+/*
+ * BOOT_PROTOCOL values
+ */
+#define RB_BOOT_PROTOCOL_BOOTP 0
+#define RB_BOOT_PROTOCOL_DHCP  1
+
+#endif /* _ROUTERBOOT_H */
diff --git a/target/linux/generic/files-4.14/include/linux/rt2x00_platform.h b/target/linux/generic/files-4.14/include/linux/rt2x00_platform.h
new file mode 100644 (file)
index 0000000..e10377e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Platform data definition for the rt2x00 driver
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef _RT2X00_PLATFORM_H
+#define _RT2X00_PLATFORM_H
+
+struct rt2x00_platform_data {
+       char *eeprom_file_name;
+       const u8 *mac_address;
+
+       int disable_2ghz;
+       int disable_5ghz;
+};
+
+#endif /* _RT2X00_PLATFORM_H */
diff --git a/target/linux/generic/files-4.14/include/linux/rtl8366.h b/target/linux/generic/files-4.14/include/linux/rtl8366.h
new file mode 100644 (file)
index 0000000..e3ce8f5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Platform data definition for the Realtek RTL8366RB/S ethernet switch driver
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8366_H
+#define _RTL8366_H
+
+#define RTL8366_DRIVER_NAME    "rtl8366"
+#define RTL8366S_DRIVER_NAME   "rtl8366s"
+#define RTL8366RB_DRIVER_NAME  "rtl8366rb"
+
+struct rtl8366_smi;
+
+enum rtl8366_type {
+       RTL8366_TYPE_UNKNOWN,
+       RTL8366_TYPE_S,
+       RTL8366_TYPE_RB,
+};
+
+struct rtl8366_initval {
+       unsigned        reg;
+       u16             val;
+};
+
+struct rtl8366_platform_data {
+       unsigned        gpio_sda;
+       unsigned        gpio_sck;
+       void            (*hw_reset)(struct rtl8366_smi *smi, bool active);
+
+       unsigned        num_initvals;
+       struct rtl8366_initval *initvals;
+};
+
+enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata);
+
+#endif /*  _RTL8366_H */
diff --git a/target/linux/generic/files-4.14/include/linux/rtl8367.h b/target/linux/generic/files-4.14/include/linux/rtl8367.h
new file mode 100644 (file)
index 0000000..855de6a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Platform data definition for the Realtek RTL8367 ethernet switch driver
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8367_H
+#define _RTL8367_H
+
+#define RTL8367_DRIVER_NAME    "rtl8367"
+#define RTL8367B_DRIVER_NAME   "rtl8367b"
+
+enum rtl8367_port_speed {
+       RTL8367_PORT_SPEED_10 = 0,
+       RTL8367_PORT_SPEED_100,
+       RTL8367_PORT_SPEED_1000,
+};
+
+struct rtl8367_port_ability {
+       int force_mode;
+       int nway;
+       int txpause;
+       int rxpause;
+       int link;
+       int duplex;
+       enum rtl8367_port_speed speed;
+};
+
+enum rtl8367_extif_mode {
+       RTL8367_EXTIF_MODE_DISABLED = 0,
+       RTL8367_EXTIF_MODE_RGMII,
+       RTL8367_EXTIF_MODE_MII_MAC,
+       RTL8367_EXTIF_MODE_MII_PHY,
+       RTL8367_EXTIF_MODE_TMII_MAC,
+       RTL8367_EXTIF_MODE_TMII_PHY,
+       RTL8367_EXTIF_MODE_GMII,
+       RTL8367_EXTIF_MODE_RGMII_33V,
+};
+
+struct rtl8367_extif_config {
+       unsigned int txdelay;
+       unsigned int rxdelay;
+       enum rtl8367_extif_mode mode;
+       struct rtl8367_port_ability ability;
+};
+
+struct rtl8367_platform_data {
+       unsigned gpio_sda;
+       unsigned gpio_sck;
+       void (*hw_reset)(bool active);
+
+       struct rtl8367_extif_config *extif0_cfg;
+       struct rtl8367_extif_config *extif1_cfg;
+};
+
+#endif /*  _RTL8367_H */
diff --git a/target/linux/generic/files-4.14/include/linux/switch.h b/target/linux/generic/files-4.14/include/linux/switch.h
new file mode 100644 (file)
index 0000000..4e62384
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * switch.h: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _LINUX_SWITCH_H
+#define _LINUX_SWITCH_H
+
+#include <net/genetlink.h>
+#include <uapi/linux/switch.h>
+
+struct switch_dev;
+struct switch_op;
+struct switch_val;
+struct switch_attr;
+struct switch_attrlist;
+struct switch_led_trigger;
+
+int register_switch(struct switch_dev *dev, struct net_device *netdev);
+void unregister_switch(struct switch_dev *dev);
+
+/**
+ * struct switch_attrlist - attribute list
+ *
+ * @n_attr: number of attributes
+ * @attr: pointer to the attributes array
+ */
+struct switch_attrlist {
+       int n_attr;
+       const struct switch_attr *attr;
+};
+
+enum switch_port_speed {
+       SWITCH_PORT_SPEED_UNKNOWN = 0,
+       SWITCH_PORT_SPEED_10 = 10,
+       SWITCH_PORT_SPEED_100 = 100,
+       SWITCH_PORT_SPEED_1000 = 1000,
+};
+
+struct switch_port_link {
+       bool link;
+       bool duplex;
+       bool aneg;
+       bool tx_flow;
+       bool rx_flow;
+       enum switch_port_speed speed;
+       /* in ethtool adv_t format */
+       u32 eee;
+};
+
+struct switch_port_stats {
+       unsigned long long tx_bytes;
+       unsigned long long rx_bytes;
+};
+
+/**
+ * struct switch_dev_ops - switch driver operations
+ *
+ * @attr_global: global switch attribute list
+ * @attr_port: port attribute list
+ * @attr_vlan: vlan attribute list
+ *
+ * Callbacks:
+ *
+ * @get_vlan_ports: read the port list of a VLAN
+ * @set_vlan_ports: set the port list of a VLAN
+ *
+ * @get_port_pvid: get the primary VLAN ID of a port
+ * @set_port_pvid: set the primary VLAN ID of a port
+ *
+ * @apply_config: apply all changed settings to the switch
+ * @reset_switch: resetting the switch
+ */
+struct switch_dev_ops {
+       struct switch_attrlist attr_global, attr_port, attr_vlan;
+
+       int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
+       int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
+
+       int (*get_port_pvid)(struct switch_dev *dev, int port, int *val);
+       int (*set_port_pvid)(struct switch_dev *dev, int port, int val);
+
+       int (*apply_config)(struct switch_dev *dev);
+       int (*reset_switch)(struct switch_dev *dev);
+
+       int (*get_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*set_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*get_port_stats)(struct switch_dev *dev, int port,
+                             struct switch_port_stats *stats);
+
+       int (*phy_read16)(struct switch_dev *dev, int addr, u8 reg, u16 *value);
+       int (*phy_write16)(struct switch_dev *dev, int addr, u8 reg, u16 value);
+};
+
+struct switch_dev {
+       struct device_node *of_node;
+       const struct switch_dev_ops *ops;
+       /* will be automatically filled */
+       char devname[IFNAMSIZ];
+
+       const char *name;
+       /* NB: either alias or netdev must be set */
+       const char *alias;
+       struct net_device *netdev;
+
+       unsigned int ports;
+       unsigned int vlans;
+       unsigned int cpu_port;
+
+       /* the following fields are internal for swconfig */
+       unsigned int id;
+       struct list_head dev_list;
+       unsigned long def_global, def_port, def_vlan;
+
+       struct mutex sw_mutex;
+       struct switch_port *portbuf;
+       struct switch_portmap *portmap;
+       struct switch_port_link linkbuf;
+
+       char buf[128];
+
+#ifdef CONFIG_SWCONFIG_LEDS
+       struct switch_led_trigger *led_trigger;
+#endif
+};
+
+struct switch_port {
+       u32 id;
+       u32 flags;
+};
+
+struct switch_portmap {
+       u32 virt;
+       const char *s;
+};
+
+struct switch_val {
+       const struct switch_attr *attr;
+       unsigned int port_vlan;
+       unsigned int len;
+       union {
+               const char *s;
+               u32 i;
+               struct switch_port *ports;
+               struct switch_port_link *link;
+       } value;
+};
+
+struct switch_attr {
+       int disabled;
+       int type;
+       const char *name;
+       const char *description;
+
+       int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
+       int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
+
+       /* for driver internal use */
+       int id;
+       int ofs;
+       int max;
+};
+
+int switch_generic_set_link(struct switch_dev *dev, int port,
+                           struct switch_port_link *link);
+
+#endif /* _LINUX_SWITCH_H */
diff --git a/target/linux/generic/files-4.14/include/uapi/linux/switch.h b/target/linux/generic/files-4.14/include/uapi/linux/switch.h
new file mode 100644 (file)
index 0000000..ea44965
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * switch.h: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_LINUX_SWITCH_H
+#define _UAPI_LINUX_SWITCH_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#ifndef __KERNEL__
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#endif
+
+/* main attributes */
+enum {
+       SWITCH_ATTR_UNSPEC,
+       /* global */
+       SWITCH_ATTR_TYPE,
+       /* device */
+       SWITCH_ATTR_ID,
+       SWITCH_ATTR_DEV_NAME,
+       SWITCH_ATTR_ALIAS,
+       SWITCH_ATTR_NAME,
+       SWITCH_ATTR_VLANS,
+       SWITCH_ATTR_PORTS,
+       SWITCH_ATTR_PORTMAP,
+       SWITCH_ATTR_CPU_PORT,
+       /* attributes */
+       SWITCH_ATTR_OP_ID,
+       SWITCH_ATTR_OP_TYPE,
+       SWITCH_ATTR_OP_NAME,
+       SWITCH_ATTR_OP_PORT,
+       SWITCH_ATTR_OP_VLAN,
+       SWITCH_ATTR_OP_VALUE_INT,
+       SWITCH_ATTR_OP_VALUE_STR,
+       SWITCH_ATTR_OP_VALUE_PORTS,
+       SWITCH_ATTR_OP_VALUE_LINK,
+       SWITCH_ATTR_OP_DESCRIPTION,
+       /* port lists */
+       SWITCH_ATTR_PORT,
+       SWITCH_ATTR_MAX
+};
+
+enum {
+       /* port map */
+       SWITCH_PORTMAP_PORTS,
+       SWITCH_PORTMAP_SEGMENT,
+       SWITCH_PORTMAP_VIRT,
+       SWITCH_PORTMAP_MAX
+};
+
+/* commands */
+enum {
+       SWITCH_CMD_UNSPEC,
+       SWITCH_CMD_GET_SWITCH,
+       SWITCH_CMD_NEW_ATTR,
+       SWITCH_CMD_LIST_GLOBAL,
+       SWITCH_CMD_GET_GLOBAL,
+       SWITCH_CMD_SET_GLOBAL,
+       SWITCH_CMD_LIST_PORT,
+       SWITCH_CMD_GET_PORT,
+       SWITCH_CMD_SET_PORT,
+       SWITCH_CMD_LIST_VLAN,
+       SWITCH_CMD_GET_VLAN,
+       SWITCH_CMD_SET_VLAN
+};
+
+/* data types */
+enum switch_val_type {
+       SWITCH_TYPE_UNSPEC,
+       SWITCH_TYPE_INT,
+       SWITCH_TYPE_STRING,
+       SWITCH_TYPE_PORTS,
+       SWITCH_TYPE_LINK,
+       SWITCH_TYPE_NOVAL,
+};
+
+/* port nested attributes */
+enum {
+       SWITCH_PORT_UNSPEC,
+       SWITCH_PORT_ID,
+       SWITCH_PORT_FLAG_TAGGED,
+       SWITCH_PORT_ATTR_MAX
+};
+
+/* link nested attributes */
+enum {
+       SWITCH_LINK_UNSPEC,
+       SWITCH_LINK_FLAG_LINK,
+       SWITCH_LINK_FLAG_DUPLEX,
+       SWITCH_LINK_FLAG_ANEG,
+       SWITCH_LINK_FLAG_TX_FLOW,
+       SWITCH_LINK_FLAG_RX_FLOW,
+       SWITCH_LINK_SPEED,
+       SWITCH_LINK_FLAG_EEE_100BASET,
+       SWITCH_LINK_FLAG_EEE_1000BASET,
+       SWITCH_LINK_ATTR_MAX,
+};
+
+#define SWITCH_ATTR_DEFAULTS_OFFSET    0x1000
+
+
+#endif /* _UAPI_LINUX_SWITCH_H */
diff --git a/target/linux/generic/files-4.9/Documentation/networking/adm6996.txt b/target/linux/generic/files-4.9/Documentation/networking/adm6996.txt
new file mode 100644 (file)
index 0000000..ab59f1d
--- /dev/null
@@ -0,0 +1,110 @@
+------- 
+
+ADM6996FC / ADM6996M switch chip driver
+
+
+1. General information
+
+  This driver supports the FC and M models only. The ADM6996F and L are
+  completely different chips.
+  
+  Support for the FC model is extremely limited at the moment. There is no VLAN
+  support as of yet. The driver will not offer an swconfig interface for the FC
+  chip.
+1.1 VLAN IDs
+
+  It is possible to define 16 different VLANs. Every VLAN has an identifier, its
+  VLAN ID. It is easiest if you use at most VLAN IDs 0-15. In that case, the
+  swconfig based configuration is very straightforward. To define two VLANs with
+  IDs 4 and 5, you can invoke, for example:
+  
+      # swconfig dev ethX vlan 4 set ports '0 1t 2 5t' 
+      # swconfig dev ethX vlan 5 set ports '0t 1t 5t'
+  
+  The swconfig framework will automatically invoke 'port Y set pvid Z' for every
+  port that is an untagged member of VLAN Y, setting its Primary VLAN ID. In
+  this example, ports 0 and 2 would get "pvid 4". The Primary VLAN ID of a port
+  is the VLAN ID associated with untagged packets coming in on that port.
+  
+  But if you wish to use VLAN IDs outside the range 0-15, this automatic
+  behaviour of the swconfig framework becomes a problem. The 16 VLANs that
+  swconfig can configure on the ADM6996 also have a "vid" setting. By default,
+  this is the same as the number of the VLAN entry, to make the simple behaviour
+  above possible. To still support a VLAN with a VLAN ID higher than 15
+  (presumably because you are in a network where such VLAN IDs are already in
+  use), you can change the "vid" setting of the VLAN to anything in the range
+  0-1023. But suppose you did the following:
+  
+      # swconfig dev ethX vlan 0 set vid 998 
+      # swconfig dev ethX vlan 0 set ports '0 2 5t'
+  Now the swconfig framework will issue 'port 0 set pvid 0' and 'port 2 set pvid
+  0'. But the "pvid" should be set to 998, so you are responsible for manually
+  fixing this!
+
+1.2 VLAN filtering
+
+  The switch is configured to apply source port filtering. This means that
+  packets are only accepted when the port the packets came in on is a member of
+  the VLAN the packet should go to.
+
+  Only membership of a VLAN is tested, it does not matter whether it is a tagged
+  or untagged membership.
+
+  For untagged packets, the destination VLAN is the Primary VLAN ID of the
+  incoming port. So if the PVID of a port is 0, but that port is not a member of
+  the VLAN with ID 0, this means that untagged packets on that port are dropped.
+  This can be used as a roundabout way of dropping untagged packets from a port,
+  a mode often referred to as "Admit only tagged packets".
+
+1.3 Reset
+
+  The two supported chip models do not have a sofware-initiated reset. When the
+  driver is initialised, as well as when the 'reset' swconfig option is invoked,
+  the driver will set those registers it knows about and supports to the correct
+  default value. But there are a lot of registers in the chip that the driver
+  does not support. If something changed those registers, invoking 'reset' or
+  performing a warm reboot might still leave the chip in a "broken" state. Only
+  a hardware reset will bring it back in the default state.
+
+2. Technical details on PHYs and the ADM6996
+
+  From the viewpoint of the Linux kernel, it is common that an Ethernet adapter
+  can be seen as a separate MAC entity and a separate PHY entity. The PHY entity
+  can be queried and set through registers accessible via an MDIO bus. A PHY
+  normally has a single address on that bus, in the range 0 through 31.
+
+  The ADM6996 has special-purpose registers in the range of PHYs 0 through 10.
+  Even though all these registers control a single ADM6996 chip, the Linux
+  kernel treats this as 11 separate PHYs.  The driver will bind to these
+  addresses to prevent a different PHY driver from binding and corrupting these
+  registers.
+
+  What Linux sees as the PHY on address 0 is meant for the Ethernet MAC
+  connected to the CPU port of the ADM6996 switch chip (port 5). This is the
+  Ethernet MAC you will use to send and receive data through the switch.
+
+  The PHYs at addresses 16 through 20 map to the PHYs on ports 0 through 4 of
+  the switch chip. These can be accessed with the Generic PHY driver, as the
+  registers have the common layout.
+
+  If a second Ethernet MAC on your board is wired to the port 4 PHY, that MAC
+  needs to bind to PHY address 20 for the port to work correctly.
+
+  The ADM6996 switch driver will reset the ports 0 through 3 on startup and when
+  'reset' is invoked. This could clash with a different PHY driver if the kernel
+  binds a PHY driver to address 16 through 19.
+
+  If Linux binds a PHY on addresses 1 through 10 to an Ethernet MAC, the ADM6996
+  driver will simply always report a connected 100 Mbit/s full-duplex link for
+  that PHY, and provide no other functionality. This is most likely not what you
+  want. So if you see a message in your log
+
+       ethX: PHY overlaps ADM6996, providing fixed PHY yy.
+
+  This is most likely an indication that ethX will not work properly, and your
+  kernel needs to be configured to attach a different PHY to that Ethernet MAC.
+
+  Controlling the mapping between MACs and PHYs is usually done in platform- or
+  board-specific fixup code. The ADM6996 driver has no influence over this.
diff --git a/target/linux/generic/files-4.9/arch/mips/fw/myloader/Makefile b/target/linux/generic/files-4.9/arch/mips/fw/myloader/Makefile
new file mode 100644 (file)
index 0000000..34acfd0
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for the Compex's MyLoader support on MIPS architecture
+#
+
+lib-y += myloader.o
diff --git a/target/linux/generic/files-4.9/arch/mips/fw/myloader/myloader.c b/target/linux/generic/files-4.9/arch/mips/fw/myloader/myloader.c
new file mode 100644 (file)
index 0000000..a26f9ad
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Compex's MyLoader specific prom routines
+ *
+ *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/addrspace.h>
+#include <asm/fw/myloader/myloader.h>
+
+#define SYS_PARAMS_ADDR                KSEG1ADDR(0x80000800)
+#define BOARD_PARAMS_ADDR      KSEG1ADDR(0x80000A00)
+#define PART_TABLE_ADDR                KSEG1ADDR(0x80000C00)
+#define BOOT_PARAMS_ADDR       KSEG1ADDR(0x80000E00)
+
+static struct myloader_info myloader_info __initdata;
+static int myloader_found __initdata;
+
+struct myloader_info * __init myloader_get_info(void)
+{
+       struct mylo_system_params *sysp;
+       struct mylo_board_params *boardp;
+       struct mylo_partition_table *parts;
+
+       if (myloader_found)
+               return &myloader_info;
+
+       sysp = (struct mylo_system_params *)(SYS_PARAMS_ADDR);
+       boardp = (struct mylo_board_params *)(BOARD_PARAMS_ADDR);
+       parts = (struct mylo_partition_table *)(PART_TABLE_ADDR);
+
+       printk(KERN_DEBUG "MyLoader: sysp=%08x, boardp=%08x, parts=%08x\n",
+               sysp->magic, boardp->magic, parts->magic);
+
+       /* Check for some magic numbers */
+       if (sysp->magic != MYLO_MAGIC_SYS_PARAMS ||
+           boardp->magic != MYLO_MAGIC_BOARD_PARAMS ||
+           le32_to_cpu(parts->magic) != MYLO_MAGIC_PARTITIONS)
+               return NULL;
+
+       printk(KERN_DEBUG "MyLoader: id=%04x:%04x, sub_id=%04x:%04x\n",
+               sysp->vid, sysp->did, sysp->svid, sysp->sdid);
+
+       myloader_info.vid = sysp->vid;
+       myloader_info.did = sysp->did;
+       myloader_info.svid = sysp->svid;
+       myloader_info.sdid = sysp->sdid;
+
+       memcpy(myloader_info.macs, boardp->addr, sizeof(myloader_info.macs));
+
+       myloader_found = 1;
+
+       return &myloader_info;
+}
diff --git a/target/linux/generic/files-4.9/drivers/leds/ledtrig-netdev.c b/target/linux/generic/files-4.9/drivers/leds/ledtrig-netdev.c
new file mode 100644 (file)
index 0000000..8d32490
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * LED Kernel Netdev Trigger
+ *
+ * Toggles the LED to reflect the link and traffic state of a named net device
+ *
+ * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
+ *
+ * Derived from ledtrig-timer.c which is:
+ *  Copyright 2005-2006 Openedhand Ltd.
+ *  Author: Richard Purdie <rpurdie@openedhand.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+
+#include "leds.h"
+
+/*
+ * Configurable sysfs attributes:
+ *
+ * device_name - network device name to monitor
+ *
+ * interval - duration of LED blink, in milliseconds
+ *
+ * mode - either "none" (LED is off) or a space separated list of one or more of:
+ *   link: LED's normal state reflects whether the link is up (has carrier) or not
+ *   tx:   LED blinks on transmitted data
+ *   rx:   LED blinks on receive data
+ *
+ * Some suggestions:
+ *
+ *  Simple link status LED:
+ *  $ echo netdev >someled/trigger
+ *  $ echo eth0 >someled/device_name
+ *  $ echo link >someled/mode
+ *
+ *  Ethernet-style link/activity LED:
+ *  $ echo netdev >someled/trigger
+ *  $ echo eth0 >someled/device_name
+ *  $ echo "link tx rx" >someled/mode
+ *
+ *  Modem-style tx/rx LEDs:
+ *  $ echo netdev >led1/trigger
+ *  $ echo ppp0 >led1/device_name
+ *  $ echo tx >led1/mode
+ *  $ echo netdev >led2/trigger
+ *  $ echo ppp0 >led2/device_name
+ *  $ echo rx >led2/mode
+ *
+ */
+
+#define MODE_LINK 1
+#define MODE_TX   2
+#define MODE_RX   4
+
+struct led_netdev_data {
+       spinlock_t lock;
+
+       struct delayed_work work;
+       struct notifier_block notifier;
+
+       struct led_classdev *led_cdev;
+       struct net_device *net_dev;
+
+       char device_name[IFNAMSIZ];
+       unsigned interval;
+       unsigned mode;
+       unsigned link_up;
+       unsigned last_activity;
+};
+
+static void set_baseline_state(struct led_netdev_data *trigger_data)
+{
+       if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
+               led_set_brightness(trigger_data->led_cdev, LED_FULL);
+       else
+               led_set_brightness(trigger_data->led_cdev, LED_OFF);
+
+       if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
+               schedule_delayed_work(&trigger_data->work, trigger_data->interval);
+}
+
+static ssize_t led_device_name_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+       sprintf(buf, "%s\n", trigger_data->device_name);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf) + 1;
+}
+
+static ssize_t led_device_name_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       if (size >= IFNAMSIZ)
+               return -EINVAL;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+
+       strcpy(trigger_data->device_name, buf);
+       if (size > 0 && trigger_data->device_name[size-1] == '\n')
+               trigger_data->device_name[size-1] = 0;
+       trigger_data->link_up = 0;
+       trigger_data->last_activity = 0;
+
+       if (trigger_data->device_name[0] != 0) {
+               /* check for existing device to update from */
+               trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
+               if (trigger_data->net_dev != NULL)
+                       trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
+       }
+
+       set_baseline_state(trigger_data);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
+
+static ssize_t led_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+
+       if (trigger_data->mode == 0) {
+               strcpy(buf, "none\n");
+       } else {
+               if (trigger_data->mode & MODE_LINK)
+                       strcat(buf, "link ");
+               if (trigger_data->mode & MODE_TX)
+                       strcat(buf, "tx ");
+               if (trigger_data->mode & MODE_RX)
+                       strcat(buf, "rx ");
+               strcat(buf, "\n");
+       }
+
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf)+1;
+}
+
+static ssize_t led_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+       char copybuf[128];
+       int new_mode = -1;
+       char *p, *token;
+
+       /* take a copy since we don't want to trash the inbound buffer when using strsep */
+       strncpy(copybuf, buf, sizeof(copybuf));
+       copybuf[sizeof(copybuf) - 1] = 0;
+       p = copybuf;
+
+       while ((token = strsep(&p, " \t\n")) != NULL) {
+               if (!*token)
+                       continue;
+
+               if (new_mode == -1)
+                       new_mode = 0;
+
+               if (!strcmp(token, "none"))
+                       new_mode = 0;
+               else if (!strcmp(token, "tx"))
+                       new_mode |= MODE_TX;
+               else if (!strcmp(token, "rx"))
+                       new_mode |= MODE_RX;
+               else if (!strcmp(token, "link"))
+                       new_mode |= MODE_LINK;
+               else
+                       return -EINVAL;
+       }
+
+       if (new_mode == -1)
+               return -EINVAL;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+       trigger_data->mode = new_mode;
+       set_baseline_state(trigger_data);
+       spin_unlock_bh(&trigger_data->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
+
+static ssize_t led_interval_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       spin_lock_bh(&trigger_data->lock);
+       sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
+       spin_unlock_bh(&trigger_data->lock);
+
+       return strlen(buf) + 1;
+}
+
+static ssize_t led_interval_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+       int ret = -EINVAL;
+       char *after;
+       unsigned long value = simple_strtoul(buf, &after, 10);
+       size_t count = after - buf;
+
+       if (isspace(*after))
+               count++;
+
+       /* impose some basic bounds on the timer interval */
+       if (count == size && value >= 5 && value <= 10000) {
+               cancel_delayed_work_sync(&trigger_data->work);
+
+               spin_lock_bh(&trigger_data->lock);
+               trigger_data->interval = msecs_to_jiffies(value);
+               set_baseline_state(trigger_data); /* resets timer */
+               spin_unlock_bh(&trigger_data->lock);
+
+               ret = count;
+       }
+
+       return ret;
+}
+
+static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
+
+static int netdev_trig_notify(struct notifier_block *nb,
+                             unsigned long evt,
+                             void *dv)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
+       struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
+
+       if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER && evt != NETDEV_CHANGENAME)
+               return NOTIFY_DONE;
+
+       if (strcmp(dev->name, trigger_data->device_name))
+               return NOTIFY_DONE;
+
+       cancel_delayed_work_sync(&trigger_data->work);
+
+       spin_lock_bh(&trigger_data->lock);
+
+       if (evt == NETDEV_REGISTER || evt == NETDEV_CHANGENAME) {
+               if (trigger_data->net_dev != NULL)
+                       dev_put(trigger_data->net_dev);
+
+               dev_hold(dev);
+               trigger_data->net_dev = dev;
+               trigger_data->link_up = 0;
+               goto done;
+       }
+
+       if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
+               dev_put(trigger_data->net_dev);
+               trigger_data->net_dev = NULL;
+               goto done;
+       }
+
+       /* UP / DOWN / CHANGE */
+
+       trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
+       set_baseline_state(trigger_data);
+
+done:
+       spin_unlock_bh(&trigger_data->lock);
+       return NOTIFY_DONE;
+}
+
+/* here's the real work! */
+static void netdev_trig_work(struct work_struct *work)
+{
+       struct led_netdev_data *trigger_data = container_of(work, struct led_netdev_data, work.work);
+       struct rtnl_link_stats64 *dev_stats;
+       unsigned new_activity;
+       struct rtnl_link_stats64 temp;
+
+       if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
+               /* we don't need to do timer work, just reflect link state. */
+               led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
+               return;
+       }
+
+       dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
+       new_activity =
+               ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
+               ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
+
+       if (trigger_data->mode & MODE_LINK) {
+               /* base state is ON (link present) */
+               /* if there's no link, we don't get this far and the LED is off */
+
+               /* OFF -> ON always */
+               /* ON -> OFF on activity */
+               if (trigger_data->led_cdev->brightness == LED_OFF) {
+                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
+               } else if (trigger_data->last_activity != new_activity) {
+                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
+               }
+       } else {
+               /* base state is OFF */
+               /* ON -> OFF always */
+               /* OFF -> ON on activity */
+               if (trigger_data->led_cdev->brightness == LED_FULL) {
+                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
+               } else if (trigger_data->last_activity != new_activity) {
+                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
+               }
+       }
+
+       trigger_data->last_activity = new_activity;
+       schedule_delayed_work(&trigger_data->work, trigger_data->interval);
+}
+
+static void netdev_trig_activate(struct led_classdev *led_cdev)
+{
+       struct led_netdev_data *trigger_data;
+       int rc;
+
+       trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
+       if (!trigger_data)
+               return;
+
+       spin_lock_init(&trigger_data->lock);
+
+       trigger_data->notifier.notifier_call = netdev_trig_notify;
+       trigger_data->notifier.priority = 10;
+
+       INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
+
+       trigger_data->led_cdev = led_cdev;
+       trigger_data->net_dev = NULL;
+       trigger_data->device_name[0] = 0;
+
+       trigger_data->mode = 0;
+       trigger_data->interval = msecs_to_jiffies(50);
+       trigger_data->link_up = 0;
+       trigger_data->last_activity = 0;
+
+       led_cdev->trigger_data = trigger_data;
+
+       rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
+       if (rc)
+               goto err_out;
+       rc = device_create_file(led_cdev->dev, &dev_attr_mode);
+       if (rc)
+               goto err_out_device_name;
+       rc = device_create_file(led_cdev->dev, &dev_attr_interval);
+       if (rc)
+               goto err_out_mode;
+
+       register_netdevice_notifier(&trigger_data->notifier);
+       return;
+
+err_out_mode:
+       device_remove_file(led_cdev->dev, &dev_attr_mode);
+err_out_device_name:
+       device_remove_file(led_cdev->dev, &dev_attr_device_name);
+err_out:
+       led_cdev->trigger_data = NULL;
+       kfree(trigger_data);
+}
+
+static void netdev_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
+
+       if (trigger_data) {
+               unregister_netdevice_notifier(&trigger_data->notifier);
+
+               device_remove_file(led_cdev->dev, &dev_attr_device_name);
+               device_remove_file(led_cdev->dev, &dev_attr_mode);
+               device_remove_file(led_cdev->dev, &dev_attr_interval);
+
+               cancel_delayed_work_sync(&trigger_data->work);
+
+               spin_lock_bh(&trigger_data->lock);
+
+               if (trigger_data->net_dev) {
+                       dev_put(trigger_data->net_dev);
+                       trigger_data->net_dev = NULL;
+               }
+
+               spin_unlock_bh(&trigger_data->lock);
+
+               kfree(trigger_data);
+       }
+}
+
+static struct led_trigger netdev_led_trigger = {
+       .name     = "netdev",
+       .activate = netdev_trig_activate,
+       .deactivate = netdev_trig_deactivate,
+};
+
+static int __init netdev_trig_init(void)
+{
+       return led_trigger_register(&netdev_led_trigger);
+}
+
+static void __exit netdev_trig_exit(void)
+{
+       led_trigger_unregister(&netdev_led_trigger);
+}
+
+module_init(netdev_trig_init);
+module_exit(netdev_trig_exit);
+
+MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
+MODULE_DESCRIPTION("Netdev LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.9/drivers/misc/owl-loader.c b/target/linux/generic/files-4.9/drivers/misc/owl-loader.c
new file mode 100644 (file)
index 0000000..f11cb2b
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Initialize Owl Emulation Devices
+ *
+ * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
+ * need to be able to initialize the PCIe wifi device. Normally, this is done
+ * during the early stages of booting linux, because the necessary init code
+ * is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
+ * However,this isn't possible for devices which have the init code for the
+ * Atheros chip stored on NAND. Hence, this module can be used to initialze
+ * the chip when the user-space is ready to extract the init code.
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/completion.h>
+#include <linux/etherdevice.h>
+#include <linux/firmware.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+
+struct owl_ctx {
+       struct completion eeprom_load;
+};
+
+#define EEPROM_FILENAME_LEN 100
+
+#define AR5416_EEPROM_MAGIC 0xa55a
+
+static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
+                          size_t cal_len)
+{
+       void __iomem *mem;
+       const void *cal_end = (void *)cal_data + cal_len;
+       const struct {
+               __be16 reg;
+               __be16 low_val;
+               __be16 high_val;
+       } __packed *data;
+       u16 cmd;
+       u32 bar0;
+       bool swap_needed = false;
+
+       if (*cal_data != AR5416_EEPROM_MAGIC) {
+               if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
+                       dev_err(&pdev->dev, "invalid calibration data\n");
+                       return -EINVAL;
+               }
+
+               dev_dbg(&pdev->dev, "calibration data needs swapping\n");
+               swap_needed = true;
+       }
+
+       dev_info(&pdev->dev, "fixup device configuration\n");
+
+       mem = pcim_iomap(pdev, 0, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "ioremap error\n");
+               return -EINVAL;
+       }
+
+       pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
+       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
+                              pci_resource_start(pdev, 0));
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+       /* set pointer to first reg address */
+       for (data = (const void *) (cal_data + 3);
+            (const void *) data <= cal_end && data->reg != cpu_to_be16(~0);
+            data++) {
+               u32 val;
+               u16 reg;
+
+               reg = data->reg;
+               val = data->low_val;
+               val |= data->high_val << 16;
+
+               if (swap_needed) {
+                       reg = swab16(reg);
+                       val = swahb32(val);
+               }
+
+#ifdef CONFIG_LANTIQ
+               val = swab32(val);
+#endif
+
+               __raw_writel(val, mem + reg);
+               udelay(100);
+       }
+
+       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+       cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+       pci_write_config_word(pdev, PCI_COMMAND, cmd);
+
+       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
+       pcim_iounmap(pdev, mem);
+
+       pci_disable_device(pdev);
+
+       return 0;
+}
+
+static void owl_fw_cb(const struct firmware *fw, void *context)
+{
+       struct pci_dev *pdev = (struct pci_dev *) context;
+       struct owl_ctx *ctx = (struct owl_ctx *) pci_get_drvdata(pdev);
+       struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct pci_bus *bus;
+
+       complete(&ctx->eeprom_load);
+
+       if (!fw) {
+               dev_err(&pdev->dev, "no eeprom data received.\n");
+               goto release;
+       }
+
+       /* also note that we are doing *u16 operations on the file */
+       if (fw->size > sizeof(pdata->eeprom_data) || fw->size < 0x200 ||
+           (fw->size & 1) == 1) {
+               dev_err(&pdev->dev, "eeprom file has an invalid size.\n");
+               goto release;
+       }
+
+       if (pdata) {
+               memcpy(pdata->eeprom_data, fw->data, fw->size);
+
+               /*
+                * eeprom has been successfully loaded - pass the data to ath9k
+                * but remove the eeprom_name, so it doesn't try to load it too.
+                */
+               pdata->eeprom_name = NULL;
+       }
+
+       if (ath9k_pci_fixup(pdev, (const u16 *) fw->data, fw->size))
+               goto release;
+
+       pci_lock_rescan_remove();
+       bus = pdev->bus;
+       pci_stop_and_remove_bus_device(pdev);
+       /*
+        * the device should come back with the proper
+        * ProductId. But we have to initiate a rescan.
+        */
+       pci_rescan_bus(bus);
+       pci_unlock_rescan_remove();
+
+release:
+       release_firmware(fw);
+}
+
+static const char *owl_get_eeprom_name(struct pci_dev *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct ath9k_platform_data *pdata;
+       char *eeprom_name;
+
+       /* try the existing platform data first */
+       pdata = dev_get_platdata(dev);
+       if (pdata && pdata->eeprom_name)
+               return pdata->eeprom_name;
+
+       dev_dbg(dev, "using auto-generated eeprom filename\n");
+
+       eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
+       if (!eeprom_name)
+               return NULL;
+
+       /* this should match the pattern used in ath9k/init.c */
+       scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
+                 dev_name(dev));
+
+       return eeprom_name;
+}
+
+static int owl_probe(struct pci_dev *pdev,
+                   const struct pci_device_id *id)
+{
+       struct owl_ctx *ctx;
+       const char *eeprom_name;
+       int err = 0;
+
+       if (pcim_enable_device(pdev))
+               return -EIO;
+
+       pcim_pin_device(pdev);
+
+       eeprom_name = owl_get_eeprom_name(pdev);
+       if (!eeprom_name) {
+               dev_err(&pdev->dev, "no eeprom filename found.\n");
+               return -ENODEV;
+       }
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx) {
+               dev_err(&pdev->dev, "failed to alloc device context.\n");
+               return -ENOMEM;
+       }
+       init_completion(&ctx->eeprom_load);
+
+       pci_set_drvdata(pdev, ctx);
+       err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
+                                     &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
+       if (err) {
+               dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
+               kfree(ctx);
+       }
+       return err;
+}
+
+static void owl_remove(struct pci_dev *pdev)
+{
+       struct owl_ctx *ctx = pci_get_drvdata(pdev);
+
+       if (ctx) {
+               wait_for_completion(&ctx->eeprom_load);
+               pci_set_drvdata(pdev, NULL);
+               kfree(ctx);
+       }
+}
+
+static const struct pci_device_id owl_pci_table[] = {
+       { PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
+       { PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
+       { },
+};
+MODULE_DEVICE_TABLE(pci, owl_pci_table);
+
+static struct pci_driver owl_driver = {
+       .name           = "owl-loader",
+       .id_table       = owl_pci_table,
+       .probe          = owl_probe,
+       .remove         = owl_remove,
+};
+module_pci_driver(owl_driver);
+MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
+MODULE_DESCRIPTION("Initializes Atheros' Owl Emulation devices");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/Kconfig
new file mode 100644 (file)
index 0000000..81ece43
--- /dev/null
@@ -0,0 +1,76 @@
+config MTD_SPLIT
+       def_bool n
+       help
+         Generic MTD split support.
+
+config MTD_SPLIT_SUPPORT
+       def_bool MTD = y
+
+comment "Rootfs partition parsers"
+
+config MTD_SPLIT_SQUASHFS_ROOT
+       bool "Squashfs based root partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+       default n
+       help
+         This provides a parsing function which allows to detect the
+         offset and size of the unused portion of a rootfs partition
+         containing a squashfs.
+
+comment "Firmware partition parsers"
+
+config MTD_SPLIT_SEAMA_FW
+       bool "Seama firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_WRGG_FW
+       bool "WRGG firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_UIMAGE_FW
+       bool "uImage based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_FIT_FW
+       bool "FIT based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_LZMA_FW
+       bool "LZMA compressed kernel based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_TPLINK_FW
+       bool "TP-Link firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_TRX_FW
+       bool "TRX image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_BRNIMAGE_FW
+       bool "brnImage (brnboot image) firmware parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_EVA_FW
+       bool "EVA image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_MINOR_FW
+       bool "Mikrotik NOR image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
+
+config MTD_SPLIT_JIMAGE_FW
+       bool "JBOOT Image based firmware partition parser"
+       depends on MTD_SPLIT_SUPPORT
+       select MTD_SPLIT
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/Makefile
new file mode 100644 (file)
index 0000000..206e754
--- /dev/null
@@ -0,0 +1,13 @@
+obj-$(CONFIG_MTD_SPLIT)                += mtdsplit.o
+obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
+obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o
+obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o
+obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o
+obj-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o
+obj-$(CONFIG_MTD_SPLIT_TPLINK_FW) += mtdsplit_tplink.o
+obj-$(CONFIG_MTD_SPLIT_TRX_FW) += mtdsplit_trx.o
+obj-$(CONFIG_MTD_SPLIT_BRNIMAGE_FW) += mtdsplit_brnimage.o
+obj-$(CONFIG_MTD_SPLIT_EVA_FW) += mtdsplit_eva.o
+obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o
+obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
+obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit.c
new file mode 100644 (file)
index 0000000..b2e51dc
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    "mtdsplit: " fmt
+
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/magic.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define UBI_EC_MAGIC                   0x55424923      /* UBI# */
+
+struct squashfs_super_block {
+       __le32 s_magic;
+       __le32 pad0[9];
+       __le64 bytes_used;
+};
+
+int mtd_get_squashfs_len(struct mtd_info *master,
+                        size_t offset,
+                        size_t *squashfs_len)
+{
+       struct squashfs_super_block sb;
+       size_t retlen;
+       int err;
+
+       err = mtd_read(master, offset, sizeof(sb), &retlen, (void *)&sb);
+       if (err || (retlen != sizeof(sb))) {
+               pr_alert("error occured while reading from \"%s\"\n",
+                        master->name);
+               return -EIO;
+       }
+
+       if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC) {
+               pr_alert("no squashfs found in \"%s\"\n", master->name);
+               return -EINVAL;
+       }
+
+       retlen = le64_to_cpu(sb.bytes_used);
+       if (retlen <= 0) {
+               pr_alert("squashfs is empty in \"%s\"\n", master->name);
+               return -ENODEV;
+       }
+
+       if (offset + retlen > master->size) {
+               pr_alert("squashfs has invalid size in \"%s\"\n",
+                        master->name);
+               return -EINVAL;
+       }
+
+       *squashfs_len = retlen;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(mtd_get_squashfs_len);
+
+static ssize_t mtd_next_eb(struct mtd_info *mtd, size_t offset)
+{
+       return mtd_rounddown_to_eb(offset, mtd) + mtd->erasesize;
+}
+
+int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                          enum mtdsplit_part_type *type)
+{
+       u32 magic;
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
+                      (unsigned char *) &magic);
+       if (ret)
+               return ret;
+
+       if (retlen != sizeof(magic))
+               return -EIO;
+
+       if (le32_to_cpu(magic) == SQUASHFS_MAGIC) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_SQUASHFS;
+               return 0;
+       } else if (magic == 0x19852003) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_JFFS2;
+               return 0;
+       } else if (be32_to_cpu(magic) == UBI_EC_MAGIC) {
+               if (type)
+                       *type = MTDSPLIT_PART_TYPE_UBI;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(mtd_check_rootfs_magic);
+
+int mtd_find_rootfs_from(struct mtd_info *mtd,
+                        size_t from,
+                        size_t limit,
+                        size_t *ret_offset,
+                        enum mtdsplit_part_type *type)
+{
+       size_t offset;
+       int err;
+
+       for (offset = from; offset < limit;
+            offset = mtd_next_eb(mtd, offset)) {
+               err = mtd_check_rootfs_magic(mtd, offset, type);
+               if (err)
+                       continue;
+
+               *ret_offset = offset;
+               return 0;
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(mtd_find_rootfs_from);
+
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit.h b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit.h
new file mode 100644 (file)
index 0000000..71d62a8
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MTDSPLIT_H
+#define _MTDSPLIT_H
+
+#define KERNEL_PART_NAME       "kernel"
+#define ROOTFS_PART_NAME       "rootfs"
+#define UBI_PART_NAME          "ubi"
+
+#define ROOTFS_SPLIT_NAME      "rootfs_data"
+
+enum mtdsplit_part_type {
+       MTDSPLIT_PART_TYPE_UNK = 0,
+       MTDSPLIT_PART_TYPE_SQUASHFS,
+       MTDSPLIT_PART_TYPE_JFFS2,
+       MTDSPLIT_PART_TYPE_UBI,
+};
+
+#ifdef CONFIG_MTD_SPLIT
+int mtd_get_squashfs_len(struct mtd_info *master,
+                        size_t offset,
+                        size_t *squashfs_len);
+
+int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                          enum mtdsplit_part_type *type);
+
+int mtd_find_rootfs_from(struct mtd_info *mtd,
+                        size_t from,
+                        size_t limit,
+                        size_t *ret_offset,
+                        enum mtdsplit_part_type *type);
+
+#else
+static inline int mtd_get_squashfs_len(struct mtd_info *master,
+                                      size_t offset,
+                                      size_t *squashfs_len)
+{
+       return -ENODEV;
+}
+
+static inline int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
+                                        enum mtdsplit_part_type *type)
+{
+       return -EINVAL;
+}
+
+static inline int mtd_find_rootfs_from(struct mtd_info *mtd,
+                                      size_t from,
+                                      size_t limit,
+                                      size_t *ret_offset,
+                                      enum mtdsplit_part_type *type)
+{
+       return -ENODEV;
+}
+#endif /* CONFIG_MTD_SPLIT */
+
+#endif /* _MTDSPLIT_H */
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_brnimage.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_brnimage.c
new file mode 100644 (file)
index 0000000..3f2d796
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define BRNIMAGE_NR_PARTS      2
+
+#define BRNIMAGE_ALIGN_BYTES   0x400
+#define BRNIMAGE_FOOTER_SIZE   12
+
+#define BRNIMAGE_MIN_OVERHEAD  (BRNIMAGE_FOOTER_SIZE)
+#define BRNIMAGE_MAX_OVERHEAD  (BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
+
+static int mtdsplit_parse_brnimage(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       uint32_t buf;
+       unsigned long rootfs_offset, rootfs_size, kernel_size;
+       size_t len;
+       int ret = 0;
+
+       for (rootfs_offset = 0; rootfs_offset < master->size;
+            rootfs_offset += BRNIMAGE_ALIGN_BYTES) {
+               ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+               if (!ret)
+                       break;
+       }
+
+       if (ret)
+               return ret;
+
+       if (rootfs_offset >= master->size)
+               return -EINVAL;
+
+       ret = mtd_read(master, rootfs_offset - BRNIMAGE_FOOTER_SIZE, 4, &len,
+                       (void *)&buf);
+       if (ret)
+               return ret;
+
+       if (len != 4)
+               return -EIO;
+
+       kernel_size = le32_to_cpu(buf);
+
+       if (kernel_size > (rootfs_offset - BRNIMAGE_MIN_OVERHEAD))
+               return -EINVAL;
+
+       if (kernel_size < (rootfs_offset - BRNIMAGE_MAX_OVERHEAD))
+               return -EINVAL;
+
+       /*
+        * The footer must be untouched as it contains the checksum of the
+        * original brnImage (kernel + squashfs)!
+        */
+       rootfs_size = master->size - rootfs_offset - BRNIMAGE_FOOTER_SIZE;
+
+       parts = kzalloc(BRNIMAGE_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return BRNIMAGE_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_brnimage_parser = {
+       .owner = THIS_MODULE,
+       .name = "brnimage-fw",
+       .parse_fn = mtdsplit_parse_brnimage,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_brnimage_init(void)
+{
+       register_mtd_parser(&mtdsplit_brnimage_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_brnimage_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_eva.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_eva.c
new file mode 100644 (file)
index 0000000..746944e
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
+ *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define EVA_NR_PARTS           2
+#define EVA_MAGIC              0xfeed1281
+#define EVA_FOOTER_SIZE                0x18
+#define EVA_DUMMY_SQUASHFS_SIZE        0x100
+
+struct eva_image_header {
+       uint32_t        magic;
+       uint32_t        size;
+};
+
+static int mtdsplit_parse_eva(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       struct eva_image_header hdr;
+       size_t retlen;
+       unsigned long kernel_size, rootfs_offset;
+       int err;
+
+       err = mtd_read(master, 0, sizeof(hdr), &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != sizeof(hdr))
+               return -EIO;
+
+       if (le32_to_cpu(hdr.magic) != EVA_MAGIC)
+               return -EINVAL;
+
+       kernel_size = le32_to_cpu(hdr.size) + EVA_FOOTER_SIZE;
+
+       /* rootfs starts at the next 0x10000 boundary: */
+       rootfs_offset = round_up(kernel_size, 0x10000);
+
+       /* skip the dummy EVA squashfs partition (with wrong endianness): */
+       rootfs_offset += EVA_DUMMY_SQUASHFS_SIZE;
+
+       if (rootfs_offset >= master->size)
+               return -EINVAL;
+
+       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(EVA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return EVA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_eva_parser = {
+       .owner = THIS_MODULE,
+       .name = "eva-fw",
+       .parse_fn = mtdsplit_parse_eva,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_eva_init(void)
+{
+       register_mtd_parser(&mtdsplit_eva_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_eva_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_fit.c
new file mode 100644 (file)
index 0000000..f356adc
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 The Linux Foundation
+ * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/types.h>
+#include <linux/byteorder/generic.h>
+#include <linux/slab.h>
+#include <linux/of_fdt.h>
+
+#include "mtdsplit.h"
+
+struct fdt_header {
+       uint32_t magic;                  /* magic word FDT_MAGIC */
+       uint32_t totalsize;              /* total size of DT block */
+       uint32_t off_dt_struct;          /* offset to structure */
+       uint32_t off_dt_strings;         /* offset to strings */
+       uint32_t off_mem_rsvmap;         /* offset to memory reserve map */
+       uint32_t version;                /* format version */
+       uint32_t last_comp_version;      /* last compatible version */
+
+       /* version 2 fields below */
+       uint32_t boot_cpuid_phys;        /* Which physical CPU id we're
+                                           booting on */
+       /* version 3 fields below */
+       uint32_t size_dt_strings;        /* size of the strings block */
+
+       /* version 17 fields below */
+       uint32_t size_dt_struct;         /* size of the structure block */
+};
+
+static int
+mtdsplit_fit_parse(struct mtd_info *mtd,
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data)
+{
+       struct fdt_header hdr;
+       size_t hdr_len, retlen;
+       size_t offset;
+       size_t fit_offset, fit_size;
+       size_t rootfs_offset, rootfs_size;
+       struct mtd_partition *parts;
+       int ret;
+
+       hdr_len = sizeof(struct fdt_header);
+
+       /* Parse the MTD device & search for the FIT image location */
+       for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
+               ret = mtd_read(mtd, 0, hdr_len, &retlen, (void*) &hdr);
+               if (ret) {
+                       pr_err("read error in \"%s\" at offset 0x%llx\n",
+                              mtd->name, (unsigned long long) offset);
+                       return ret;
+               }
+
+               if (retlen != hdr_len) {
+                       pr_err("short read in \"%s\"\n", mtd->name);
+                       return -EIO;
+               }
+
+               /* Check the magic - see if this is a FIT image */
+               if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
+                       pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
+                                mtd->name, (unsigned long long) offset);
+                       continue;
+               }
+
+               /* We found a FIT image. Let's keep going */
+               break;
+       }
+
+       fit_offset = offset;
+       fit_size = be32_to_cpu(hdr.totalsize);
+
+       if (fit_size == 0) {
+               pr_err("FIT image in \"%s\" at offset %llx has null size\n",
+                      mtd->name, (unsigned long long) fit_offset);
+               return -ENODEV;
+       }
+
+       /* Search for the rootfs partition after the FIT image */
+       ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
+                                  &rootfs_offset, NULL);
+       if (ret) {
+               pr_info("no rootfs found after FIT image in \"%s\"\n",
+                       mtd->name);
+               return ret;
+       }
+
+       rootfs_size = mtd->size - rootfs_offset;
+
+       parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = fit_offset;
+       parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return 2;
+}
+
+static struct mtd_part_parser uimage_parser = {
+       .owner = THIS_MODULE,
+       .name = "fit-fw",
+       .parse_fn = mtdsplit_fit_parse,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_fit_init(void)
+{
+       register_mtd_parser(&uimage_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_fit_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_jimage.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_jimage.c
new file mode 100644 (file)
index 0000000..51544a7
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ *  Copyright (C) 2018 PaweÅ‚ Dembicki <paweldembicki@gmail.com> 
+ *
+ *  Based on: mtdsplit_uimage.c
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE )
+
+#define STAG_SIZE 16
+#define STAG_ID 0x04
+#define STAG_MAGIC 0x2B24
+
+#define SCH2_SIZE 40
+#define SCH2_MAGIC 0x2124
+#define SCH2_VER 0x02
+
+/*
+ * Jboot image header,
+ * all data in little endian.
+ */
+
+struct jimage_header           //stag + sch2 jboot joined headers
+{
+       uint8_t stag_cmark;             // in factory 0xFF , in sysupgrade must be the same as stag_id
+       uint8_t stag_id;                // 0x04
+       uint16_t stag_magic;            //magic 0x2B24
+       uint32_t stag_time_stamp;       // timestamp calculated in jboot way
+       uint32_t stag_image_length;     // lentgh of kernel + sch2 header
+       uint16_t stag_image_checksum;   // negated jboot_checksum of sch2 + kernel
+       uint16_t stag_tag_checksum;     // negated jboot_checksum of stag header data
+       uint16_t sch2_magic;            // magic 0x2124
+       uint8_t sch2_cp_type;   // 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma
+       uint8_t sch2_version;   // 0x02 for sch2
+       uint32_t sch2_ram_addr; // ram entry address
+       uint32_t sch2_image_len;        // kernel image length
+       uint32_t sch2_image_crc32;      // kernel image crc
+       uint32_t sch2_start_addr;       // ram start address
+       uint32_t sch2_rootfs_addr;      // rootfs flash address
+       uint32_t sch2_rootfs_len;       // rootfls length
+       uint32_t sch2_rootfs_crc32;     // rootfs crc32
+       uint32_t sch2_header_crc32;     // sch2 header crc32, durring calculation this area is replaced by zero
+       uint16_t sch2_header_length;    // sch2 header length: 0x28
+       uint16_t sch2_cmd_line_length;  // cmd line length, known zeros
+};
+
+static int
+read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
+                  size_t header_len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts
+ *
+ * @find_header: function to call for a block of data that will return offset
+ *      of a valid jImage header if found
+ */
+static int __mtdsplit_parse_jimage(struct mtd_info *master,
+                                  const struct mtd_partition **pparts,
+                                  struct mtd_part_parser_data *data,
+                                  ssize_t (*find_header)(u_char *buf, size_t len))
+{
+       struct mtd_partition *parts;
+       u_char *buf;
+       int nr_parts;
+       size_t offset;
+       size_t jimage_offset;
+       size_t jimage_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int jimage_part, rf_part;
+       int ret;
+       enum mtdsplit_part_type type;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       buf = vmalloc(MAX_HEADER_LEN);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_free_parts;
+       }
+
+       /* find jImage on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               struct jimage_header *header;
+
+               jimage_size = 0;
+
+               ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN);
+               if (ret)
+                       continue;
+
+               ret = find_header(buf, MAX_HEADER_LEN);
+               if (ret < 0) {
+                       pr_debug("no valid jImage found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+               header = (struct jimage_header *)(buf + ret);
+
+               jimage_size = sizeof(*header) + header->sch2_image_len + ret;
+               if ((offset + jimage_size) > master->size) {
+                       pr_debug("jImage exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (jimage_size == 0) {
+               pr_debug("no jImage found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       jimage_offset = offset;
+
+       if (jimage_offset == 0) {
+               jimage_part = 0;
+               rf_part = 1;
+
+               /* find the roots after the jImage */
+               ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size,
+                                          master->size, &rootfs_offset, &type);
+               if (ret) {
+                       pr_debug("no rootfs after jImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_size = master->size - rootfs_offset;
+               jimage_size = rootfs_offset - jimage_offset;
+       } else {
+               rf_part = 0;
+               jimage_part = 1;
+
+               /* check rootfs presence at offset 0 */
+               ret = mtd_check_rootfs_magic(master, 0, &type);
+               if (ret) {
+                       pr_debug("no rootfs before jImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_offset = 0;
+               rootfs_size = jimage_offset;
+       }
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       parts[jimage_part].name = KERNEL_PART_NAME;
+       parts[jimage_part].offset = jimage_offset;
+       parts[jimage_part].size = jimage_size;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[rf_part].name = UBI_PART_NAME;
+       else
+               parts[rf_part].name = ROOTFS_PART_NAME;
+       parts[rf_part].offset = rootfs_offset;
+       parts[rf_part].size = rootfs_size;
+
+       vfree(buf);
+
+       *pparts = parts;
+       return nr_parts;
+
+err_free_buf:
+       vfree(buf);
+
+err_free_parts:
+       kfree(parts);
+       return ret;
+}
+
+static ssize_t jimage_verify_default(u_char *buf, size_t len)
+{
+       struct jimage_header *header = (struct jimage_header *)buf;
+
+       /* default sanity checks */
+       if (header->stag_magic != STAG_MAGIC) {
+               pr_debug("invalid jImage stag header magic: %04x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->sch2_magic != SCH2_MAGIC) {
+               pr_debug("invalid jImage sch2 header magic: %04x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->stag_cmark != header->stag_id) {
+               pr_debug("invalid jImage stag header cmark: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->stag_id != STAG_ID) {
+               pr_debug("invalid jImage stag header id: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+       if (header->sch2_version != SCH2_VER) {
+               pr_debug("invalid jImage sch2 header version: %02x\n",
+                        header->stag_magic);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_jimage_parse_generic(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_jimage(master, pparts, data,
+                                     jimage_verify_default);
+}
+
+static struct mtd_part_parser jimage_generic_parser = {
+       .owner = THIS_MODULE,
+       .name = "jimage-fw",
+       .parse_fn = mtdsplit_jimage_parse_generic,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_jimage_init(void)
+{
+       register_mtd_parser(&jimage_generic_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_jimage_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_lzma.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_lzma.c
new file mode 100644 (file)
index 0000000..b7f044a
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/unaligned.h>
+
+#include "mtdsplit.h"
+
+#define LZMA_NR_PARTS          2
+#define LZMA_PROPERTIES_SIZE   5
+
+struct lzma_header {
+       u8 props[LZMA_PROPERTIES_SIZE];
+       u8 size_low[4];
+       u8 size_high[4];
+};
+
+static int mtdsplit_parse_lzma(struct mtd_info *master,
+                              const struct mtd_partition **pparts,
+                              struct mtd_part_parser_data *data)
+{
+       struct lzma_header hdr;
+       size_t hdr_len, retlen;
+       size_t rootfs_offset;
+       u32 t;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* verify LZMA properties */
+       if (hdr.props[0] >= (9 * 5 * 5))
+               return -EINVAL;
+
+       t = get_unaligned_le32(&hdr.props[1]);
+       if (!is_power_of_2(t))
+               return -EINVAL;
+
+       t = get_unaligned_le32(&hdr.size_high);
+       if (t)
+               return -EINVAL;
+
+       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
+                                  &rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(LZMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return LZMA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_lzma_parser = {
+       .owner = THIS_MODULE,
+       .name = "lzma-fw",
+       .parse_fn = mtdsplit_parse_lzma,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_lzma_init(void)
+{
+       register_mtd_parser(&mtdsplit_lzma_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_lzma_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_minor.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_minor.c
new file mode 100644 (file)
index 0000000..f971f0a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  MTD splitter for MikroTik NOR devices
+ *
+ *  Copyright (C) 2017 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  The rootfs is expected at erase-block boundary due to the use of
+ *  mtd_find_rootfs_from(). We use a trimmed down version of the yaffs header
+ *  for two main reasons:
+ *  - the original header uses weakly defined types (int, enum...) which can
+ *    vary in length depending on build host (and the struct is not packed),
+ *    and the name field can have a different total length depending on
+ *    whether or not the yaffs code was _built_ with unicode support.
+ *  - the only field that could be of real use here (file_size_low) contains
+ *    invalid data in the header generated by kernel2minor, so we cannot use
+ *    it to infer the exact position of the rootfs and do away with
+ *    mtd_find_rootfs_from() (and thus have non-EB-aligned rootfs).
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/string.h>
+
+#include "mtdsplit.h"
+
+#define YAFFS_OBJECT_TYPE_FILE 0x1
+#define YAFFS_OBJECTID_ROOT    0x1
+#define YAFFS_SUM_UNUSED       0xFFFF
+#define YAFFS_NAME             "kernel"
+
+#define MINOR_NR_PARTS         2
+
+/*
+ * This structure is based on yaffs_obj_hdr from yaffs_guts.h
+ * The weak types match upstream. The fields have cpu-endianness
+ */
+struct minor_header {
+       int yaffs_type;
+       int yaffs_obj_id;
+       u16 yaffs_sum_unused;
+       char yaffs_name[sizeof(YAFFS_NAME)];
+};
+
+static int mtdsplit_parse_minor(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct minor_header hdr;
+       size_t hdr_len, retlen;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* match header */
+       if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE)
+               return -EINVAL;
+
+       if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT)
+               return -EINVAL;
+
+       if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED)
+               return -EINVAL;
+
+       if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME)))
+               return -EINVAL;
+
+       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
+                                  &rootfs_offset, NULL);
+       if (err)
+               return err;
+
+       parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return MINOR_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_minor_parser = {
+       .owner = THIS_MODULE,
+       .name = "minor-fw",
+       .parse_fn = mtdsplit_parse_minor,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_minor_init(void)
+{
+       register_mtd_parser(&mtdsplit_minor_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_minor_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_seama.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_seama.c
new file mode 100644 (file)
index 0000000..f8556e0
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define SEAMA_MAGIC            0x5EA3A417
+#define SEAMA_NR_PARTS         2
+#define SEAMA_MIN_ROOTFS_OFFS  0x80000 /* 512KiB */
+
+struct seama_header {
+       __be32  magic;          /* should always be SEAMA_MAGIC. */
+       __be16  reserved;       /* reserved for  */
+       __be16  metasize;       /* size of the META data */
+       __be32  size;           /* size of the image */
+       u8      md5[16];        /* digest */
+};
+
+static int mtdsplit_parse_seama(struct mtd_info *master,
+                               const struct mtd_partition **pparts,
+                               struct mtd_part_parser_data *data)
+{
+       struct seama_header hdr;
+       size_t hdr_len, retlen, kernel_ent_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       enum mtdsplit_part_type type;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* sanity checks */
+       if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC)
+               return -EINVAL;
+
+       kernel_ent_size = hdr_len + be32_to_cpu(hdr.size) +
+                         be16_to_cpu(hdr.metasize);
+       if (kernel_ent_size > master->size)
+               return -EINVAL;
+
+       /* Check for the rootfs right after Seama entity with a kernel. */
+       err = mtd_check_rootfs_magic(master, kernel_ent_size, &type);
+       if (!err) {
+               rootfs_offset = kernel_ent_size;
+       } else {
+               /*
+                * On some devices firmware entity might contain both: kernel
+                * and rootfs. We can't determine kernel size so we just have to
+                * look for rootfs magic.
+                * Start the search from an arbitrary offset.
+                */
+               err = mtd_find_rootfs_from(master, SEAMA_MIN_ROOTFS_OFFS,
+                                          master->size, &rootfs_offset, &type);
+               if (err)
+                       return err;
+       }
+
+       parts = kzalloc(SEAMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = sizeof hdr + be16_to_cpu(hdr.metasize);
+       parts[0].size = rootfs_offset - parts[0].offset;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[1].name = UBI_PART_NAME;
+       else
+               parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return SEAMA_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_seama_parser = {
+       .owner = THIS_MODULE,
+       .name = "seama-fw",
+       .parse_fn = mtdsplit_parse_seama,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_seama_init(void)
+{
+       register_mtd_parser(&mtdsplit_seama_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_seama_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_squashfs.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_squashfs.c
new file mode 100644 (file)
index 0000000..79e1f73
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (C) 2013 Felix Fietkau <nbd@nbd.name>
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/magic.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+static int
+mtdsplit_parse_squashfs(struct mtd_info *master,
+                       const struct mtd_partition **pparts,
+                       struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *part;
+       struct mtd_info *parent_mtd;
+       size_t part_offset;
+       size_t squashfs_len;
+       int err;
+
+       err = mtd_get_squashfs_len(master, 0, &squashfs_len);
+       if (err)
+               return err;
+
+       parent_mtd = mtdpart_get_master(master);
+       part_offset = mtdpart_get_offset(master);
+
+       part = kzalloc(sizeof(*part), GFP_KERNEL);
+       if (!part) {
+               pr_alert("unable to allocate memory for \"%s\" partition\n",
+                        ROOTFS_SPLIT_NAME);
+               return -ENOMEM;
+       }
+
+       part->name = ROOTFS_SPLIT_NAME;
+       part->offset = mtd_roundup_to_eb(part_offset + squashfs_len,
+                                        parent_mtd) - part_offset;
+       part->size = mtd_rounddown_to_eb(master->size - part->offset, master);
+
+       *pparts = part;
+       return 1;
+}
+
+static struct mtd_part_parser mtdsplit_squashfs_parser = {
+       .owner = THIS_MODULE,
+       .name = "squashfs-split",
+       .parse_fn = mtdsplit_parse_squashfs,
+       .type = MTD_PARSER_TYPE_ROOTFS,
+};
+
+static int __init mtdsplit_squashfs_init(void)
+{
+       register_mtd_parser(&mtdsplit_squashfs_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_squashfs_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_tplink.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_tplink.c
new file mode 100644 (file)
index 0000000..c346aa8
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define TPLINK_NR_PARTS                2
+#define TPLINK_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
+
+#define MD5SUM_LEN  16
+
+struct fw_v1 {
+       char            vendor_name[24];
+       char            fw_version[36];
+       uint32_t        hw_id;          /* hardware id */
+       uint32_t        hw_rev;         /* hardware revision */
+       uint32_t        unk1;
+       uint8_t         md5sum1[MD5SUM_LEN];
+       uint32_t        unk2;
+       uint8_t         md5sum2[MD5SUM_LEN];
+       uint32_t        unk3;
+       uint32_t        kernel_la;      /* kernel load address */
+       uint32_t        kernel_ep;      /* kernel entry point */
+       uint32_t        fw_length;      /* total length of the firmware */
+       uint32_t        kernel_ofs;     /* kernel data offset */
+       uint32_t        kernel_len;     /* kernel data length */
+       uint32_t        rootfs_ofs;     /* rootfs data offset */
+       uint32_t        rootfs_len;     /* rootfs data length */
+       uint32_t        boot_ofs;       /* bootloader data offset */
+       uint32_t        boot_len;       /* bootloader data length */
+       uint8_t         pad[360];
+} __attribute__ ((packed));
+
+struct fw_v2 {
+       char            fw_version[48]; /* 0x04: fw version string */
+       uint32_t        hw_id;          /* 0x34: hardware id */
+       uint32_t        hw_rev;         /* 0x38: FIXME: hardware revision? */
+       uint32_t        unk1;           /* 0x3c: 0x00000000 */
+       uint8_t         md5sum1[MD5SUM_LEN]; /* 0x40 */
+       uint32_t        unk2;           /* 0x50: 0x00000000 */
+       uint8_t         md5sum2[MD5SUM_LEN]; /* 0x54 */
+       uint32_t        unk3;           /* 0x64: 0xffffffff */
+
+       uint32_t        kernel_la;      /* 0x68: kernel load address */
+       uint32_t        kernel_ep;      /* 0x6c: kernel entry point */
+       uint32_t        fw_length;      /* 0x70: total length of the image */
+       uint32_t        kernel_ofs;     /* 0x74: kernel data offset */
+       uint32_t        kernel_len;     /* 0x78: kernel data length */
+       uint32_t        rootfs_ofs;     /* 0x7c: rootfs data offset */
+       uint32_t        rootfs_len;     /* 0x80: rootfs data length */
+       uint32_t        boot_ofs;       /* 0x84: FIXME: seems to be unused */
+       uint32_t        boot_len;       /* 0x88: FIXME: seems to be unused */
+       uint16_t        unk4;           /* 0x8c: 0x55aa */
+       uint8_t         sver_hi;        /* 0x8e */
+       uint8_t         sver_lo;        /* 0x8f */
+       uint8_t         unk5;           /* 0x90: magic: 0xa5 */
+       uint8_t         ver_hi;         /* 0x91 */
+       uint8_t         ver_mid;        /* 0x92 */
+       uint8_t         ver_lo;         /* 0x93 */
+       uint8_t         pad[364];
+} __attribute__ ((packed));
+
+struct tplink_fw_header {
+       uint32_t version;
+       union {
+               struct fw_v1 v1;
+               struct fw_v2 v2;
+       };
+};
+
+static int mtdsplit_parse_tplink(struct mtd_info *master,
+                                const struct mtd_partition **pparts,
+                                struct mtd_part_parser_data *data)
+{
+       struct tplink_fw_header hdr;
+       size_t hdr_len, retlen, kernel_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       switch (le32_to_cpu(hdr.version)) {
+       case 1:
+               if (be32_to_cpu(hdr.v1.kernel_ofs) != sizeof(hdr))
+                       return -EINVAL;
+
+               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v1.kernel_len);
+               rootfs_offset = be32_to_cpu(hdr.v1.rootfs_ofs);
+               break;
+       case 2:
+       case 3:
+               if (be32_to_cpu(hdr.v2.kernel_ofs) != sizeof(hdr))
+                       return -EINVAL;
+
+               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v2.kernel_len);
+               rootfs_offset = be32_to_cpu(hdr.v2.rootfs_ofs);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (kernel_size > master->size)
+               return -EINVAL;
+
+       /* Find the rootfs */
+       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
+       if (err) {
+               /*
+                * The size in the header might cover the rootfs as well.
+                * Start the search from an arbitrary offset.
+                */
+               err = mtd_find_rootfs_from(master, TPLINK_MIN_ROOTFS_OFFS,
+                                          master->size, &rootfs_offset, NULL);
+               if (err)
+                       return err;
+       }
+
+       parts = kzalloc(TPLINK_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = kernel_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return TPLINK_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_tplink_parser = {
+       .owner = THIS_MODULE,
+       .name = "tplink-fw",
+       .parse_fn = mtdsplit_parse_tplink,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_tplink_init(void)
+{
+       register_mtd_parser(&mtdsplit_tplink_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_tplink_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_trx.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_trx.c
new file mode 100644 (file)
index 0000000..53aebc5
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define TRX_MAGIC   0x30524448  /* "HDR0" */
+
+struct trx_header {
+       __le32 magic;
+       __le32 len;
+       __le32 crc32;
+       __le32 flag_version;
+       __le32 offset[4];
+};
+
+static int
+read_trx_header(struct mtd_info *mtd, size_t offset,
+                  struct trx_header *header)
+{
+       size_t header_len;
+       size_t retlen;
+       int ret;
+
+       header_len = sizeof(*header);
+       ret = mtd_read(mtd, offset, header_len, &retlen,
+                      (unsigned char *) header);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_parse_trx(struct mtd_info *master,
+                  const struct mtd_partition **pparts,
+                  struct mtd_part_parser_data *data)
+{
+       struct mtd_partition *parts;
+       struct trx_header hdr;
+       int nr_parts;
+       size_t offset;
+       size_t trx_offset;
+       size_t trx_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int ret;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       /* find trx image on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               trx_size = 0;
+
+               ret = read_trx_header(master, offset, &hdr);
+               if (ret)
+                       continue;
+
+               if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
+                       pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+
+               trx_size = le32_to_cpu(hdr.len);
+               if ((offset + trx_size) > master->size) {
+                       pr_debug("trx image exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (trx_size == 0) {
+               pr_debug("no trx header found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       trx_offset = offset + hdr.offset[0];
+       rootfs_offset = offset + hdr.offset[1];
+       rootfs_size = master->size - rootfs_offset;
+       trx_size = rootfs_offset - trx_offset;
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err;
+       }
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = trx_offset;
+       parts[0].size = trx_size;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = rootfs_size;
+
+       *pparts = parts;
+       return nr_parts;
+
+err:
+       kfree(parts);
+       return ret;
+}
+
+static struct mtd_part_parser trx_parser = {
+       .owner = THIS_MODULE,
+       .name = "trx-fw",
+       .parse_fn = mtdsplit_parse_trx,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_trx_init(void)
+{
+       register_mtd_parser(&trx_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_trx_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_uimage.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_uimage.c
new file mode 100644 (file)
index 0000000..bd1c723
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+/*
+ * uimage_header itself is only 64B, but it may be prepended with another data.
+ * Currently the biggest size is for Edimax devices: 20B + 64B
+ */
+#define MAX_HEADER_LEN         84
+
+#define IH_MAGIC       0x27051956      /* Image Magic Number           */
+#define IH_NMLEN               32      /* Image Name Length            */
+
+#define IH_OS_LINUX            5       /* Linux        */
+
+#define IH_TYPE_KERNEL         2       /* OS Kernel Image              */
+#define IH_TYPE_FILESYSTEM     7       /* Filesystem Image             */
+
+/*
+ * Legacy format image header,
+ * all data in network byte order (aka natural aka bigendian).
+ */
+struct uimage_header {
+       uint32_t        ih_magic;       /* Image Header Magic Number    */
+       uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
+       uint32_t        ih_time;        /* Image Creation Timestamp     */
+       uint32_t        ih_size;        /* Image Data Size              */
+       uint32_t        ih_load;        /* Data  Load  Address          */
+       uint32_t        ih_ep;          /* Entry Point Address          */
+       uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
+       uint8_t         ih_os;          /* Operating System             */
+       uint8_t         ih_arch;        /* CPU architecture             */
+       uint8_t         ih_type;        /* Image Type                   */
+       uint8_t         ih_comp;        /* Compression Type             */
+       uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
+};
+
+static int
+read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
+                  size_t header_len)
+{
+       size_t retlen;
+       int ret;
+
+       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
+       if (ret) {
+               pr_debug("read error in \"%s\"\n", mtd->name);
+               return ret;
+       }
+
+       if (retlen != header_len) {
+               pr_debug("short read in \"%s\"\n", mtd->name);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
+ *
+ * @find_header: function to call for a block of data that will return offset
+ *      of a valid uImage header if found
+ */
+static int __mtdsplit_parse_uimage(struct mtd_info *master,
+                                  const struct mtd_partition **pparts,
+                                  struct mtd_part_parser_data *data,
+                                  ssize_t (*find_header)(u_char *buf, size_t len))
+{
+       struct mtd_partition *parts;
+       u_char *buf;
+       int nr_parts;
+       size_t offset;
+       size_t uimage_offset;
+       size_t uimage_size = 0;
+       size_t rootfs_offset;
+       size_t rootfs_size = 0;
+       int uimage_part, rf_part;
+       int ret;
+       enum mtdsplit_part_type type;
+
+       nr_parts = 2;
+       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       buf = vmalloc(MAX_HEADER_LEN);
+       if (!buf) {
+               ret = -ENOMEM;
+               goto err_free_parts;
+       }
+
+       /* find uImage on erase block boundaries */
+       for (offset = 0; offset < master->size; offset += master->erasesize) {
+               struct uimage_header *header;
+
+               uimage_size = 0;
+
+               ret = read_uimage_header(master, offset, buf, MAX_HEADER_LEN);
+               if (ret)
+                       continue;
+
+               ret = find_header(buf, MAX_HEADER_LEN);
+               if (ret < 0) {
+                       pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
+                                master->name, (unsigned long long) offset);
+                       continue;
+               }
+               header = (struct uimage_header *)(buf + ret);
+
+               uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
+               if ((offset + uimage_size) > master->size) {
+                       pr_debug("uImage exceeds MTD device \"%s\"\n",
+                                master->name);
+                       continue;
+               }
+               break;
+       }
+
+       if (uimage_size == 0) {
+               pr_debug("no uImage found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       uimage_offset = offset;
+
+       if (uimage_offset == 0) {
+               uimage_part = 0;
+               rf_part = 1;
+
+               /* find the roots after the uImage */
+               ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
+                                          master->size, &rootfs_offset, &type);
+               if (ret) {
+                       pr_debug("no rootfs after uImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_size = master->size - rootfs_offset;
+               uimage_size = rootfs_offset - uimage_offset;
+       } else {
+               rf_part = 0;
+               uimage_part = 1;
+
+               /* check rootfs presence at offset 0 */
+               ret = mtd_check_rootfs_magic(master, 0, &type);
+               if (ret) {
+                       pr_debug("no rootfs before uImage in \"%s\"\n",
+                                master->name);
+                       goto err_free_buf;
+               }
+
+               rootfs_offset = 0;
+               rootfs_size = uimage_offset;
+       }
+
+       if (rootfs_size == 0) {
+               pr_debug("no rootfs found in \"%s\"\n", master->name);
+               ret = -ENODEV;
+               goto err_free_buf;
+       }
+
+       parts[uimage_part].name = KERNEL_PART_NAME;
+       parts[uimage_part].offset = uimage_offset;
+       parts[uimage_part].size = uimage_size;
+
+       if (type == MTDSPLIT_PART_TYPE_UBI)
+               parts[rf_part].name = UBI_PART_NAME;
+       else
+               parts[rf_part].name = ROOTFS_PART_NAME;
+       parts[rf_part].offset = rootfs_offset;
+       parts[rf_part].size = rootfs_size;
+
+       vfree(buf);
+
+       *pparts = parts;
+       return nr_parts;
+
+err_free_buf:
+       vfree(buf);
+
+err_free_parts:
+       kfree(parts);
+       return ret;
+}
+
+static ssize_t uimage_verify_default(u_char *buf, size_t len)
+{
+       struct uimage_header *header = (struct uimage_header *)buf;
+
+       /* default sanity checks */
+       if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
+               pr_debug("invalid uImage magic: %08x\n",
+                        be32_to_cpu(header->ih_magic));
+               return -EINVAL;
+       }
+
+       if (header->ih_os != IH_OS_LINUX) {
+               pr_debug("invalid uImage OS: %08x\n",
+                        be32_to_cpu(header->ih_os));
+               return -EINVAL;
+       }
+
+       if (header->ih_type != IH_TYPE_KERNEL) {
+               pr_debug("invalid uImage type: %08x\n",
+                        be32_to_cpu(header->ih_type));
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_generic(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                     uimage_verify_default);
+}
+
+static struct mtd_part_parser uimage_generic_parser = {
+       .owner = THIS_MODULE,
+       .name = "uimage-fw",
+       .parse_fn = mtdsplit_uimage_parse_generic,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+#define FW_MAGIC_WNR2000V1     0x32303031
+#define FW_MAGIC_WNR2000V3     0x32303033
+#define FW_MAGIC_WNR2000V4     0x32303034
+#define FW_MAGIC_WNR2200       0x32323030
+#define FW_MAGIC_WNR612V2      0x32303631
+#define FW_MAGIC_WNR1000V2     0x31303031
+#define FW_MAGIC_WNR1000V2_VC  0x31303030
+#define FW_MAGIC_WNDR3700      0x33373030
+#define FW_MAGIC_WNDR3700V2    0x33373031
+#define FW_MAGIC_WPN824N       0x31313030
+
+static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
+{
+       struct uimage_header *header = (struct uimage_header *)buf;
+       uint8_t expected_type = IH_TYPE_FILESYSTEM;
+
+       switch (be32_to_cpu(header->ih_magic)) {
+       case FW_MAGIC_WNR612V2:
+       case FW_MAGIC_WNR1000V2:
+       case FW_MAGIC_WNR1000V2_VC:
+       case FW_MAGIC_WNR2000V1:
+       case FW_MAGIC_WNR2000V3:
+       case FW_MAGIC_WNR2200:
+       case FW_MAGIC_WNDR3700:
+       case FW_MAGIC_WNDR3700V2:
+       case FW_MAGIC_WPN824N:
+               break;
+       case FW_MAGIC_WNR2000V4:
+               expected_type = IH_TYPE_KERNEL;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (header->ih_os != IH_OS_LINUX ||
+           header->ih_type != expected_type)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int
+mtdsplit_uimage_parse_netgear(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                     uimage_verify_wndr3700);
+}
+
+static struct mtd_part_parser uimage_netgear_parser = {
+       .owner = THIS_MODULE,
+       .name = "netgear-fw",
+       .parse_fn = mtdsplit_uimage_parse_netgear,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Edimax
+ **************************************************/
+
+#define FW_EDIMAX_OFFSET       20
+#define FW_MAGIC_EDIMAX                0x43535953
+
+static ssize_t uimage_find_edimax(u_char *buf, size_t len)
+{
+       u32 *magic;
+
+       if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
+               pr_err("Buffer too small for checking Edimax header\n");
+               return -ENOSPC;
+       }
+
+       magic = (u32 *)buf;
+       if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
+               return -EINVAL;
+
+       if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
+               return FW_EDIMAX_OFFSET;
+
+       return -EINVAL;
+}
+
+static int
+mtdsplit_uimage_parse_edimax(struct mtd_info *master,
+                             const struct mtd_partition **pparts,
+                             struct mtd_part_parser_data *data)
+{
+       return __mtdsplit_parse_uimage(master, pparts, data,
+                                      uimage_find_edimax);
+}
+
+static struct mtd_part_parser uimage_edimax_parser = {
+       .owner = THIS_MODULE,
+       .name = "edimax-fw",
+       .parse_fn = mtdsplit_uimage_parse_edimax,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+/**************************************************
+ * Init
+ **************************************************/
+
+static int __init mtdsplit_uimage_init(void)
+{
+       register_mtd_parser(&uimage_generic_parser);
+       register_mtd_parser(&uimage_netgear_parser);
+       register_mtd_parser(&uimage_edimax_parser);
+
+       return 0;
+}
+
+module_init(mtdsplit_uimage_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_wrgg.c b/target/linux/generic/files-4.9/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
new file mode 100644 (file)
index 0000000..16ebd51
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
+ *  Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+
+#include "mtdsplit.h"
+
+#define WRGG_NR_PARTS          2
+#define WRGG_MIN_ROOTFS_OFFS   0x80000 /* 512KiB */
+#define WRGG03_MAGIC           0x20080321
+#define WRG_MAGIC              0x20040220
+
+struct wrgg03_header {
+       char            signature[32];
+       uint32_t        magic1;
+       uint32_t        magic2;
+       char            version[16];
+       char            model[16];
+       uint32_t        flag[2];
+       uint32_t        reserve[2];
+       char            buildno[16];
+       uint32_t        size;
+       uint32_t        offset;
+       char            devname[32];
+       char            digest[16];
+} __attribute__ ((packed));
+
+struct wrg_header {
+       char            signature[32];
+       uint32_t        magic1;
+       uint32_t        magic2;
+       uint32_t        size;
+       uint32_t        offset;
+       char            devname[32];
+       char            digest[16];
+} __attribute__ ((packed));
+
+
+static int mtdsplit_parse_wrgg(struct mtd_info *master,
+                              const struct mtd_partition **pparts,
+                              struct mtd_part_parser_data *data)
+{
+       struct wrgg03_header hdr;
+       size_t hdr_len, retlen, kernel_ent_size;
+       size_t rootfs_offset;
+       struct mtd_partition *parts;
+       enum mtdsplit_part_type type;
+       int err;
+
+       hdr_len = sizeof(hdr);
+       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
+       if (err)
+               return err;
+
+       if (retlen != hdr_len)
+               return -EIO;
+
+       /* sanity checks */
+       if (le32_to_cpu(hdr.magic1) == WRGG03_MAGIC) {
+               kernel_ent_size = hdr_len + be32_to_cpu(hdr.size);
+       } else if (le32_to_cpu(hdr.magic1) == WRG_MAGIC) {
+               kernel_ent_size = sizeof(struct wrg_header) + le32_to_cpu(
+                                 ((struct wrg_header*)&hdr)->size);
+       } else {
+               return -EINVAL;
+       }
+
+       if (kernel_ent_size > master->size)
+               return -EINVAL;
+
+       /*
+        * The size in the header covers the rootfs as well.
+        * Start the search from an arbitrary offset.
+        */
+       err = mtd_find_rootfs_from(master, WRGG_MIN_ROOTFS_OFFS,
+                                  master->size, &rootfs_offset, &type);
+       if (err)
+               return err;
+
+       parts = kzalloc(WRGG_NR_PARTS * sizeof(*parts), GFP_KERNEL);
+       if (!parts)
+               return -ENOMEM;
+
+       parts[0].name = KERNEL_PART_NAME;
+       parts[0].offset = 0;
+       parts[0].size = rootfs_offset;
+
+       parts[1].name = ROOTFS_PART_NAME;
+       parts[1].offset = rootfs_offset;
+       parts[1].size = master->size - rootfs_offset;
+
+       *pparts = parts;
+       return WRGG_NR_PARTS;
+}
+
+static struct mtd_part_parser mtdsplit_wrgg_parser = {
+       .owner = THIS_MODULE,
+       .name = "wrgg-fw",
+       .parse_fn = mtdsplit_parse_wrgg,
+       .type = MTD_PARSER_TYPE_FIRMWARE,
+};
+
+static int __init mtdsplit_wrgg_init(void)
+{
+       register_mtd_parser(&mtdsplit_wrgg_parser);
+
+       return 0;
+}
+
+subsys_initcall(mtdsplit_wrgg_init);
diff --git a/target/linux/generic/files-4.9/drivers/mtd/myloader.c b/target/linux/generic/files-4.9/drivers/mtd/myloader.c
new file mode 100644 (file)
index 0000000..7532d45
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *  Parse MyLoader-style flash partition tables and produce a Linux partition
+ *  array to match.
+ *
+ *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This file was based on drivers/mtd/redboot.c
+ *  Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/byteorder/generic.h>
+#include <linux/myloader.h>
+
+#define BLOCK_LEN_MIN          0x10000
+#define PART_NAME_LEN          32
+
+struct part_data {
+       struct mylo_partition_table     tab;
+       char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
+};
+
+static int myloader_parse_partitions(struct mtd_info *master,
+                                    const struct mtd_partition **pparts,
+                                    struct mtd_part_parser_data *data)
+{
+       struct part_data *buf;
+       struct mylo_partition_table *tab;
+       struct mylo_partition *part;
+       struct mtd_partition *mtd_parts;
+       struct mtd_partition *mtd_part;
+       int num_parts;
+       int ret, i;
+       size_t retlen;
+       char *names;
+       unsigned long offset;
+       unsigned long blocklen;
+
+       buf = vmalloc(sizeof(*buf));
+       if (!buf) {
+               return -ENOMEM;
+               goto out;
+       }
+       tab = &buf->tab;
+
+       blocklen = master->erasesize;
+       if (blocklen < BLOCK_LEN_MIN)
+               blocklen = BLOCK_LEN_MIN;
+
+       offset = blocklen;
+
+       /* Find the partition table */
+       for (i = 0; i < 4; i++, offset += blocklen) {
+               printk(KERN_DEBUG "%s: searching for MyLoader partition table"
+                               " at offset 0x%lx\n", master->name, offset);
+
+               ret = mtd_read(master, offset, sizeof(*buf), &retlen,
+                              (void *)buf);
+               if (ret)
+                       goto out_free_buf;
+
+               if (retlen != sizeof(*buf)) {
+                       ret = -EIO;
+                       goto out_free_buf;
+               }
+
+               /* Check for Partition Table magic number */
+               if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
+                       break;
+
+       }
+
+       if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
+               printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
+                       master->name);
+               ret = 0;
+               goto out_free_buf;
+       }
+
+       /* The MyLoader and the Partition Table is always present */
+       num_parts = 2;
+
+       /* Detect number of used partitions */
+       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+               part = &tab->partitions[i];
+
+               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+                       continue;
+
+               num_parts++;
+       }
+
+       mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
+                               num_parts * PART_NAME_LEN), GFP_KERNEL);
+
+       if (!mtd_parts) {
+               ret = -ENOMEM;
+               goto out_free_buf;
+       }
+
+       mtd_part = mtd_parts;
+       names = (char *)&mtd_parts[num_parts];
+
+       strncpy(names, "myloader", PART_NAME_LEN);
+       mtd_part->name = names;
+       mtd_part->offset = 0;
+       mtd_part->size = offset;
+       mtd_part->mask_flags = MTD_WRITEABLE;
+       mtd_part++;
+       names += PART_NAME_LEN;
+
+       strncpy(names, "partition_table", PART_NAME_LEN);
+       mtd_part->name = names;
+       mtd_part->offset = offset;
+       mtd_part->size = blocklen;
+       mtd_part->mask_flags = MTD_WRITEABLE;
+       mtd_part++;
+       names += PART_NAME_LEN;
+
+       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
+               part = &tab->partitions[i];
+
+               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
+                       continue;
+
+               if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
+                       strncpy(names, buf->names[i], PART_NAME_LEN);
+               else
+                       snprintf(names, PART_NAME_LEN, "partition%d", i);
+
+               mtd_part->offset = le32_to_cpu(part->addr);
+               mtd_part->size = le32_to_cpu(part->size);
+               mtd_part->name = names;
+               mtd_part++;
+               names += PART_NAME_LEN;
+       }
+
+       *pparts = mtd_parts;
+       ret = num_parts;
+
+ out_free_buf:
+       vfree(buf);
+ out:
+       return ret;
+}
+
+static struct mtd_part_parser myloader_mtd_parser = {
+       .owner          = THIS_MODULE,
+       .parse_fn       = myloader_parse_partitions,
+       .name           = "MyLoader",
+};
+
+static int __init myloader_mtd_parser_init(void)
+{
+       register_mtd_parser(&myloader_mtd_parser);
+
+       return 0;
+}
+
+static void __exit myloader_mtd_parser_exit(void)
+{
+       deregister_mtd_parser(&myloader_mtd_parser);
+}
+
+module_init(myloader_mtd_parser_init);
+module_exit(myloader_mtd_parser_exit);
+
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/adm6996.c b/target/linux/generic/files-4.9/drivers/net/phy/adm6996.c
new file mode 100644 (file)
index 0000000..42928ba
--- /dev/null
@@ -0,0 +1,1241 @@
+/*
+ * ADM6996 switch driver
+ *
+ * swconfig interface based on ar8216.c
+ *
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ * VLAN support Copyright (c) 2010, 2011 Peter Lebbing <peter@digitalbrains.com>
+ * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright (c) 2014 Matti Laakso <malaakso@elisanet.fi>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+/*#define DEBUG 1*/
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/adm6996-gpio.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/switch.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "adm6996.h"
+
+MODULE_DESCRIPTION("Infineon ADM6996 Switch");
+MODULE_AUTHOR("Felix Fietkau, Peter Lebbing <peter@digitalbrains.com>");
+MODULE_LICENSE("GPL");
+
+static const char * const adm6996_model_name[] =
+{
+       NULL,
+       "ADM6996FC",
+       "ADM6996M",
+       "ADM6996L"
+};
+
+struct adm6996_mib_desc {
+       unsigned int offset;
+       const char *name;
+};
+
+struct adm6996_priv {
+       struct switch_dev dev;
+       void *priv;
+
+       u8 eecs;
+       u8 eesk;
+       u8 eedi;
+
+       enum adm6996_model model;
+
+       bool enable_vlan;
+       bool vlan_enabled;      /* Current hardware state */
+
+#ifdef DEBUG
+       u16 addr;               /* Debugging: register address to operate on */
+#endif
+
+       u16 pvid[ADM_NUM_PORTS];        /* Primary VLAN ID */
+       u8 tagged_ports;
+
+       u16 vlan_id[ADM_NUM_VLANS];
+       u8 vlan_table[ADM_NUM_VLANS];   /* bitmap, 1 = port is member */
+       u8 vlan_tagged[ADM_NUM_VLANS];  /* bitmap, 1 = tagged member */
+       
+       struct mutex mib_lock;
+       char buf[2048];
+
+       struct mutex reg_mutex;
+
+       /* use abstraction for regops, we want to add gpio support in the future */
+       u16 (*read)(struct adm6996_priv *priv, enum admreg reg);
+       void (*write)(struct adm6996_priv *priv, enum admreg reg, u16 val);
+};
+
+#define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev)
+#define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
+
+#define MIB_DESC(_o, _n)       \
+       {                       \
+               .offset = (_o), \
+               .name = (_n),   \
+       }
+
+static const struct adm6996_mib_desc adm6996_mibs[] = {
+       MIB_DESC(ADM_CL0, "RxPacket"),
+       MIB_DESC(ADM_CL6, "RxByte"),
+       MIB_DESC(ADM_CL12, "TxPacket"),
+       MIB_DESC(ADM_CL18, "TxByte"),
+       MIB_DESC(ADM_CL24, "Collision"),
+       MIB_DESC(ADM_CL30, "Error"),
+};
+
+#define ADM6996_MIB_RXB_ID     1
+#define ADM6996_MIB_TXB_ID     3
+
+static inline u16
+r16(struct adm6996_priv *priv, enum admreg reg)
+{
+       return priv->read(priv, reg);
+}
+
+static inline void
+w16(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       priv->write(priv, reg, val);
+}
+
+/* Minimum timing constants */
+#define EECK_EDGE_TIME  3   /* 3us - max(adm 2.5us, 93c 1us) */
+#define EEDI_SETUP_TIME 1   /* 1us - max(adm 10ns, 93c 400ns) */
+#define EECS_SETUP_TIME 1   /* 1us - max(adm no, 93c 200ns) */
+
+static void adm6996_gpio_write(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
+{
+       int i, len = (bits + 7) / 8;
+       u8 mask;
+
+       gpio_set_value(priv->eecs, cs);
+       udelay(EECK_EDGE_TIME);
+
+       /* Byte assemble from MSB to LSB */
+       for (i = 0; i < len; i++) {
+               /* Bit bang from MSB to LSB */
+               for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
+                       /* Clock low */
+                       gpio_set_value(priv->eesk, 0);
+                       udelay(EECK_EDGE_TIME);
+
+                       /* Output on rising edge */
+                       gpio_set_value(priv->eedi, (mask & buf[i]));
+                       udelay(EEDI_SETUP_TIME);
+
+                       /* Clock high */
+                       gpio_set_value(priv->eesk, 1);
+                       udelay(EECK_EDGE_TIME);
+               }
+       }
+
+       /* Clock low */
+       gpio_set_value(priv->eesk, 0);
+       udelay(EECK_EDGE_TIME);
+
+       if (cs)
+               gpio_set_value(priv->eecs, 0);
+}
+
+static void adm6996_gpio_read(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
+{
+       int i, len = (bits + 7) / 8;
+       u8 mask;
+
+       gpio_set_value(priv->eecs, cs);
+       udelay(EECK_EDGE_TIME);
+
+       /* Byte assemble from MSB to LSB */
+       for (i = 0; i < len; i++) {
+               u8 byte;
+
+               /* Bit bang from MSB to LSB */
+               for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) {
+                       u8 gp;
+
+                       /* Clock low */
+                       gpio_set_value(priv->eesk, 0);
+                       udelay(EECK_EDGE_TIME);
+
+                       /* Input on rising edge */
+                       gp = gpio_get_value(priv->eedi);
+                       if (gp)
+                               byte |= mask;
+
+                       /* Clock high */
+                       gpio_set_value(priv->eesk, 1);
+                       udelay(EECK_EDGE_TIME);
+               }
+
+               *buf++ = byte;
+       }
+
+       /* Clock low */
+       gpio_set_value(priv->eesk, 0);
+       udelay(EECK_EDGE_TIME);
+
+       if (cs)
+               gpio_set_value(priv->eecs, 0);
+}
+
+/* Advance clock(s) */
+static void adm6996_gpio_adclk(struct adm6996_priv *priv, int clocks)
+{
+       int i;
+       for (i = 0; i < clocks; i++) {
+               /* Clock high */
+               gpio_set_value(priv->eesk, 1);
+               udelay(EECK_EDGE_TIME);
+
+               /* Clock low */
+               gpio_set_value(priv->eesk, 0);
+               udelay(EECK_EDGE_TIME);
+       }
+}
+
+static u16
+adm6996_read_gpio_reg(struct adm6996_priv *priv, enum admreg reg)
+{
+       /* cmd: 01 10 T DD R RRRRRR */
+       u8 bits[6] = {
+               0xFF, 0xFF, 0xFF, 0xFF,
+               (0x06 << 4) | ((0 & 0x01) << 3 | (reg&64)>>6),
+               ((reg&63)<<2)
+       };
+
+       u8 rbits[4];
+
+       /* Enable GPIO outputs with all pins to 0 */
+       gpio_direction_output(priv->eecs, 0);
+       gpio_direction_output(priv->eesk, 0);
+       gpio_direction_output(priv->eedi, 0);
+
+       adm6996_gpio_write(priv, 0, bits, 46);
+       gpio_direction_input(priv->eedi);
+       adm6996_gpio_adclk(priv, 2);
+       adm6996_gpio_read(priv, 0, rbits, 32);
+
+       /* Extra clock(s) required per datasheet */
+       adm6996_gpio_adclk(priv, 2);
+
+       /* Disable GPIO outputs */
+       gpio_direction_input(priv->eecs);
+       gpio_direction_input(priv->eesk);
+
+        /* EEPROM has 16-bit registers, but pumps out two registers in one request */
+       return (reg & 0x01 ?  (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3]));
+}
+
+/* Write chip configuration register */
+/* Follow 93c66 timing and chip's min EEPROM timing requirement */
+static void
+adm6996_write_gpio_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
+       u8 bits[4] = {
+               (0x05 << 5) | (reg >> 3),
+               (reg << 5) | (u8)(val >> 11),
+               (u8)(val >> 3),
+               (u8)(val << 5)
+       };
+
+       /* Enable GPIO outputs with all pins to 0 */
+       gpio_direction_output(priv->eecs, 0);
+       gpio_direction_output(priv->eesk, 0);
+       gpio_direction_output(priv->eedi, 0);
+
+       /* Write cmd. Total 27 bits */
+       adm6996_gpio_write(priv, 1, bits, 27);
+
+       /* Extra clock(s) required per datasheet */
+       adm6996_gpio_adclk(priv, 2);
+
+       /* Disable GPIO outputs */
+       gpio_direction_input(priv->eecs);
+       gpio_direction_input(priv->eesk);
+       gpio_direction_input(priv->eedi);
+}
+
+static u16
+adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg)
+{
+       struct phy_device *phydev = priv->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, PHYADDR(reg));
+}
+
+static void
+adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
+{
+       struct phy_device *phydev = priv->priv;
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, PHYADDR(reg), val);
+}
+
+static int
+adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 1)
+               return -EINVAL;
+
+       priv->enable_vlan = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = priv->enable_vlan;
+
+       return 0;
+};
+
+#ifdef DEBUG
+
+static int
+adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 1023)
+               return -EINVAL;
+
+       priv->addr = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = priv->addr;
+
+       return 0;
+};
+
+static int
+adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       if (val->value.i > 65535)
+               return -EINVAL;
+
+       w16(priv, priv->addr, val->value.i);
+
+       return 0;
+};
+
+static int
+adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       val->value.i = r16(priv, priv->addr);
+
+       return 0;
+};
+
+#endif /* def DEBUG */
+
+static int
+adm6996_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("set_pvid port %d vlan %d\n", port, vlan);
+
+       if (vlan > ADM_VLAN_MAX_ID)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+
+       return 0;
+}
+
+static int
+adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("get_pvid port %d\n", port);
+       *vlan = priv->pvid[port];
+
+       return 0;
+}
+
+static int
+adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("set_vid port %d vid %d\n", val->port_vlan, val->value.i);
+
+       if (val->value.i > ADM_VLAN_MAX_ID)
+               return -EINVAL;
+
+       priv->vlan_id[val->port_vlan] = val->value.i;
+
+       return 0;
+};
+
+static int
+adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("get_vid port %d\n", val->port_vlan);
+
+       val->value.i = priv->vlan_id[val->port_vlan];
+
+       return 0;
+};
+
+static int
+adm6996_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       u8 tagged = priv->vlan_tagged[val->port_vlan];
+       int i;
+
+       pr_devel("get_ports port_vlan %d\n", val->port_vlan);
+
+       val->len = 0;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+
+       return 0;
+};
+
+static int
+adm6996_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       u8 *ports = &priv->vlan_table[val->port_vlan];
+       u8 *tagged = &priv->vlan_tagged[val->port_vlan];
+       int i;
+
+       pr_devel("set_ports port_vlan %d ports", val->port_vlan);
+
+       *ports = 0;
+       *tagged = 0;
+
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+#ifdef DEBUG
+               pr_cont(" %d%s", p->id,
+                      ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" :
+                       ""));
+#endif
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       *tagged |= (1 << p->id);
+                       priv->tagged_ports |= (1 << p->id);
+               }
+
+               *ports |= (1 << p->id);
+       }
+
+#ifdef DEBUG
+       pr_cont("\n");
+#endif
+
+       return 0;
+};
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_enable_vlan(struct adm6996_priv *priv)
+{
+       u16 reg;
+
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg &= ~(ADM_OTBE_MASK);
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = r16(priv, ADM_IFNTE);
+       reg &= ~(ADM_IFNTE_MASK);
+       w16(priv, ADM_IFNTE, reg);
+       reg = r16(priv, ADM_VID_CHECK);
+       reg |= ADM_VID_CHECK_MASK;
+       w16(priv, ADM_VID_CHECK, reg);
+       reg = r16(priv, ADM_SYSC0);
+       reg |= ADM_NTTE;
+       reg &= ~(ADM_RVID1);
+       w16(priv, ADM_SYSC0, reg);
+       reg = r16(priv, ADM_SYSC3);
+       reg |= ADM_TBV;
+       w16(priv, ADM_SYSC3, reg);
+}
+
+static void
+adm6996_enable_vlan_6996l(struct adm6996_priv *priv)
+{
+       u16 reg;
+
+       reg = r16(priv, ADM_SYSC3);
+       reg |= ADM_TBV;
+       reg |= ADM_MAC_CLONE;
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Disable VLANs
+ *
+ * Sets VLAN mapping for port-based VLAN with all ports connected to
+ * eachother (this is also the power-on default).
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_disable_vlan(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               reg = ADM_VLAN_FILT_MEMBER_MASK;
+               w16(priv, ADM_VLAN_FILT_L(i), reg);
+               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1);
+               w16(priv, ADM_VLAN_FILT_H(i), reg);
+       }
+
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg |= ADM_OTBE_MASK;
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = r16(priv, ADM_IFNTE);
+       reg |= ADM_IFNTE_MASK;
+       w16(priv, ADM_IFNTE, reg);
+       reg = r16(priv, ADM_VID_CHECK);
+       reg &= ~(ADM_VID_CHECK_MASK);
+       w16(priv, ADM_VID_CHECK, reg);
+       reg = r16(priv, ADM_SYSC0);
+       reg &= ~(ADM_NTTE);
+       reg |= ADM_RVID1;
+       w16(priv, ADM_SYSC0, reg);
+       reg = r16(priv, ADM_SYSC3);
+       reg &= ~(ADM_TBV);
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Disable VLANs
+ *
+ * Sets VLAN mapping for port-based VLAN with all ports connected to
+ * eachother (this is also the power-on default).
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_disable_vlan_6996l(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               w16(priv, ADM_VLAN_MAP(i), 0);
+       }
+
+       reg = r16(priv, ADM_SYSC3);
+       reg &= ~(ADM_TBV);
+       reg &= ~(ADM_MAC_CLONE);
+       w16(priv, ADM_SYSC3, reg);
+}
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_apply_port_pvids(struct adm6996_priv *priv)
+{
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               reg = r16(priv, adm_portcfg[i]);
+               reg &= ~(ADM_PORTCFG_PVID_MASK);
+               reg |= ADM_PORTCFG_PVID(priv->pvid[i]);
+               if (priv->model == ADM6996L) {
+                       if (priv->tagged_ports & (1 << i))
+                               reg |= (1 << 4);
+                       else
+                               reg &= ~(1 << 4);
+               }
+               w16(priv, adm_portcfg[i], reg);
+       }
+
+       w16(priv, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0]));
+       w16(priv, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1]));
+       reg = r16(priv, ADM_OTBE_P2_PVID);
+       reg &= ~(ADM_P2_PVID_MASK);
+       reg |= ADM_P2_PVID_VAL(priv->pvid[2]);
+       w16(priv, ADM_OTBE_P2_PVID, reg);
+       reg = ADM_P3_PVID_VAL(priv->pvid[3]);
+       reg |= ADM_P4_PVID_VAL(priv->pvid[4]);
+       w16(priv, ADM_P3_P4_PVID, reg);
+       reg = r16(priv, ADM_P5_PVID);
+       reg &= ~(ADM_P2_PVID_MASK);
+       reg |= ADM_P5_PVID_VAL(priv->pvid[5]);
+       w16(priv, ADM_P5_PVID, reg);
+}
+
+/*
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_apply_vlan_filters(struct adm6996_priv *priv)
+{
+       u8 ports, tagged;
+       u16 vid, reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               vid = priv->vlan_id[i];
+               ports = priv->vlan_table[i];
+               tagged = priv->vlan_tagged[i];
+
+               if (ports == 0) {
+                       /* Disable VLAN entry */
+                       w16(priv, ADM_VLAN_FILT_H(i), 0);
+                       w16(priv, ADM_VLAN_FILT_L(i), 0);
+                       continue;
+               }
+
+               reg = ADM_VLAN_FILT_MEMBER(ports);
+               reg |= ADM_VLAN_FILT_TAGGED(tagged);
+               w16(priv, ADM_VLAN_FILT_L(i), reg);
+               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid);
+               w16(priv, ADM_VLAN_FILT_H(i), reg);
+       }
+}
+
+static void
+adm6996_apply_vlan_filters_6996l(struct adm6996_priv *priv)
+{
+       u8 ports;
+       u16 reg;
+       int i;
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               ports = priv->vlan_table[i];
+
+               if (ports == 0) {
+                       /* Disable VLAN entry */
+                       w16(priv, ADM_VLAN_MAP(i), 0);
+                       continue;
+               } else {
+                       reg = ADM_VLAN_FILT(ports);
+                       w16(priv, ADM_VLAN_MAP(i), reg);
+               }
+       }
+}
+
+static int
+adm6996_hw_apply(struct switch_dev *dev)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("hw_apply\n");
+
+       mutex_lock(&priv->reg_mutex);
+
+       if (!priv->enable_vlan) {
+               if (priv->vlan_enabled) {
+                       if (priv->model == ADM6996L)
+                               adm6996_disable_vlan_6996l(priv);
+                       else
+                               adm6996_disable_vlan(priv);
+                       priv->vlan_enabled = 0;
+               }
+               goto out;
+       }
+
+       if (!priv->vlan_enabled) {
+               if (priv->model == ADM6996L)
+                       adm6996_enable_vlan_6996l(priv);
+               else
+                       adm6996_enable_vlan(priv);
+               priv->vlan_enabled = 1;
+       }
+
+       adm6996_apply_port_pvids(priv);
+       if (priv->model == ADM6996L)
+               adm6996_apply_vlan_filters_6996l(priv);
+       else
+               adm6996_apply_vlan_filters(priv);
+
+out:
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+/*
+ * Reset the switch
+ *
+ * The ADM6996 can't do a software-initiated reset, so we just initialise the
+ * registers we support in this driver.
+ *
+ * Precondition: reg_mutex must be held
+ */
+static void
+adm6996_perform_reset (struct adm6996_priv *priv)
+{
+       int i;
+
+       /* initialize port and vlan settings */
+       for (i = 0; i < ADM_NUM_PORTS - 1; i++) {
+               w16(priv, adm_portcfg[i], ADM_PORTCFG_INIT |
+                       ADM_PORTCFG_PVID(0));
+       }
+       w16(priv, adm_portcfg[5], ADM_PORTCFG_CPU);
+
+       if (priv->model == ADM6996M || priv->model == ADM6996FC) {
+               /* reset all PHY ports */
+               for (i = 0; i < ADM_PHY_PORTS; i++) {
+                       w16(priv, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
+               }
+       }
+
+       priv->enable_vlan = 0;
+       priv->vlan_enabled = 0;
+
+       for (i = 0; i < ADM_NUM_PORTS; i++) {
+               priv->pvid[i] = 0;
+       }
+
+       for (i = 0; i < ADM_NUM_VLANS; i++) {
+               priv->vlan_id[i] = i;
+               priv->vlan_table[i] = 0;
+               priv->vlan_tagged[i] = 0;
+       }
+
+       if (priv->model == ADM6996M) {
+               /* Clear VLAN priority map so prio's are unused */
+               w16 (priv, ADM_VLAN_PRIOMAP, 0);
+
+               adm6996_disable_vlan(priv);
+               adm6996_apply_port_pvids(priv);
+       } else if (priv->model == ADM6996L) {
+               /* Clear VLAN priority map so prio's are unused */
+               w16 (priv, ADM_VLAN_PRIOMAP, 0);
+
+               adm6996_disable_vlan_6996l(priv);
+               adm6996_apply_port_pvids(priv);
+       }
+}
+
+static int
+adm6996_reset_switch(struct switch_dev *dev)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+
+       pr_devel("reset\n");
+
+       mutex_lock(&priv->reg_mutex);
+       adm6996_perform_reset (priv);
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+static int
+adm6996_get_port_link(struct switch_dev *dev, int port,
+               struct switch_port_link *link)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       
+       u16 reg = 0;
+       
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+       
+       switch (port) {
+       case 0:
+               reg = r16(priv, ADM_PS0);
+               break;
+       case 1:
+               reg = r16(priv, ADM_PS0);
+               reg = reg >> 8;
+               break;
+       case 2:
+               reg = r16(priv, ADM_PS1);
+               break;
+       case 3:
+               reg = r16(priv, ADM_PS1);
+               reg = reg >> 8;
+               break;
+       case 4:
+               reg = r16(priv, ADM_PS1);
+               reg = reg >> 12;
+               break;
+       case 5:
+               reg = r16(priv, ADM_PS2);
+               /* Bits 0, 1, 3 and 4. */
+               reg = (reg & 3) | ((reg & 24) >> 1);
+               break;
+       default:
+               return -EINVAL;
+       }
+       
+       link->link = reg & ADM_PS_LS;
+       if (!link->link)
+               return 0;
+       link->aneg = true;
+       link->duplex = reg & ADM_PS_DS;
+       link->tx_flow = reg & ADM_PS_FCS;
+       link->rx_flow = reg & ADM_PS_FCS;
+       if (reg & ADM_PS_SS)
+               link->speed = SWITCH_PORT_SPEED_100;
+       else
+               link->speed = SWITCH_PORT_SPEED_10;
+
+       return 0;
+}
+
+static int
+adm6996_sw_get_port_mib(struct switch_dev *dev,
+                      const struct switch_attr *attr,
+                      struct switch_val *val)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       int port;
+       char *buf = priv->buf;
+       int i, len = 0;
+       u32 reg = 0;
+
+       port = val->port_vlan;
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+
+       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                       "Port %d MIB counters\n",
+                       port);
+
+       for (i = 0; i < ARRAY_SIZE(adm6996_mibs); i++) {
+               reg = r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port));
+               reg += r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+               len += snprintf(buf + len, sizeof(priv->buf) - len,
+                               "%-12s: %u\n",
+                               adm6996_mibs[i].name,
+                               reg);
+       }
+
+       mutex_unlock(&priv->mib_lock);
+
+       val->value.s = buf;
+       val->len = len;
+
+       return 0;
+}
+
+static int
+adm6996_get_port_stats(struct switch_dev *dev, int port,
+                       struct switch_port_stats *stats)
+{
+       struct adm6996_priv *priv = to_adm(dev);
+       int id;
+       u32 reg = 0;
+
+       if (port >= ADM_NUM_PORTS)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+
+       id = ADM6996_MIB_TXB_ID;
+       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
+       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+       stats->tx_bytes = reg;
+
+       id = ADM6996_MIB_RXB_ID;
+       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
+       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
+       stats->rx_bytes = reg;
+
+       mutex_unlock(&priv->mib_lock);
+
+       return 0;
+}
+
+static struct switch_attr adm6996_globals[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "enable_vlan",
+        .description = "Enable VLANs",
+        .set = adm6996_set_enable_vlan,
+        .get = adm6996_get_enable_vlan,
+       },
+#ifdef DEBUG
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "addr",
+        .description =
+        "Direct register access: set register address (0 - 1023)",
+        .set = adm6996_set_addr,
+        .get = adm6996_get_addr,
+        },
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "data",
+        .description =
+        "Direct register access: read/write to register (0 - 65535)",
+        .set = adm6996_set_data,
+        .get = adm6996_get_data,
+        },
+#endif /* def DEBUG */
+};
+
+static struct switch_attr adm6996_port[] = {
+       {
+        .type = SWITCH_TYPE_STRING,
+        .name = "mib",
+        .description = "Get port's MIB counters",
+        .set = NULL,
+        .get = adm6996_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr adm6996_vlan[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "vid",
+        .description = "VLAN ID",
+        .set = adm6996_set_vid,
+        .get = adm6996_get_vid,
+        },
+};
+
+static struct switch_dev_ops adm6996_ops = {
+       .attr_global = {
+                       .attr = adm6996_globals,
+                       .n_attr = ARRAY_SIZE(adm6996_globals),
+                       },
+       .attr_port = {
+                     .attr = adm6996_port,
+                     .n_attr = ARRAY_SIZE(adm6996_port),
+                     },
+       .attr_vlan = {
+                     .attr = adm6996_vlan,
+                     .n_attr = ARRAY_SIZE(adm6996_vlan),
+                     },
+       .get_port_pvid = adm6996_get_pvid,
+       .set_port_pvid = adm6996_set_pvid,
+       .get_vlan_ports = adm6996_get_ports,
+       .set_vlan_ports = adm6996_set_ports,
+       .apply_config = adm6996_hw_apply,
+       .reset_switch = adm6996_reset_switch,
+       .get_port_link = adm6996_get_port_link,
+       .get_port_stats = adm6996_get_port_stats,
+};
+
+static int adm6996_switch_init(struct adm6996_priv *priv, const char *alias, struct net_device *netdev)
+{
+       struct switch_dev *swdev;
+       u16 test, old;
+
+       if (!priv->model) {
+               /* Detect type of chip */
+               old = r16(priv, ADM_VID_CHECK);
+               test = old ^ (1 << 12);
+               w16(priv, ADM_VID_CHECK, test);
+               test ^= r16(priv, ADM_VID_CHECK);
+               if (test & (1 << 12)) {
+                       /* 
+                        * Bit 12 of this register is read-only. 
+                        * This is the FC model. 
+                        */
+                       priv->model = ADM6996FC;
+               } else {
+                       /* Bit 12 is read-write. This is the M model. */
+                       priv->model = ADM6996M;
+                       w16(priv, ADM_VID_CHECK, old);
+               }
+       }
+
+       swdev = &priv->dev;
+       swdev->name = (adm6996_model_name[priv->model]);
+       swdev->cpu_port = ADM_CPU_PORT;
+       swdev->ports = ADM_NUM_PORTS;
+       swdev->vlans = ADM_NUM_VLANS;
+       swdev->ops = &adm6996_ops;
+       swdev->alias = alias;
+
+       /* The ADM6996L connected through GPIOs does not support any switch
+          status calls */
+       if (priv->model == ADM6996L) {
+               adm6996_ops.attr_port.n_attr = 0;
+               adm6996_ops.get_port_link = NULL;
+       }
+
+       pr_info ("%s: %s model PHY found.\n", alias, swdev->name);
+
+       mutex_lock(&priv->reg_mutex);
+       adm6996_perform_reset (priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       if (priv->model == ADM6996M || priv->model == ADM6996L) {
+               return register_switch(swdev, netdev);
+       }
+
+       return -ENODEV;
+}
+
+static int adm6996_config_init(struct phy_device *pdev)
+{
+       struct adm6996_priv *priv;
+       int ret;
+
+       pdev->supported = ADVERTISED_100baseT_Full;
+       pdev->advertising = ADVERTISED_100baseT_Full;
+
+       if (pdev->mdio.addr != 0) {
+               pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n"
+                               , pdev->attached_dev->name, pdev->mdio.addr);
+               return 0;
+       }
+
+       priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+       priv->priv = pdev;
+       priv->read = adm6996_read_mii_reg;
+       priv->write = adm6996_write_mii_reg;
+
+       ret = adm6996_switch_init(priv, pdev->attached_dev->name, pdev->attached_dev);
+       if (ret < 0)
+               return ret;
+
+       pdev->priv = priv;
+
+       return 0;
+}
+
+/*
+ * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
+ */
+static int adm6996_read_status(struct phy_device *phydev)
+{
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+/*
+ * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
+ */
+static int adm6996_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int adm6996_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       /* Our custom registers are at PHY addresses 0-10. Claim those. */
+       if (dev->mdio.addr > 10)
+               return 0;
+
+       /* look for the switch on the bus */
+       reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
+       if (reg != ADM_SIG0_VAL)
+               return 0;
+
+       reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
+       if (reg != ADM_SIG1_VAL)
+               return 0;
+
+       dev->phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL;
+
+       return 0;
+}
+
+static int adm6996_probe(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static void adm6996_remove(struct phy_device *pdev)
+{
+       struct adm6996_priv *priv = phy_to_adm(pdev);
+
+       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
+               unregister_switch(&priv->dev);
+}
+
+static int adm6996_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+
+static struct phy_driver adm6996_phy_driver = {
+       .name           = "Infineon ADM6996",
+       .phy_id         = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = adm6996_probe,
+       .remove         = adm6996_remove,
+       .config_init    = &adm6996_config_init,
+       .config_aneg    = &adm6996_config_aneg,
+       .read_status    = &adm6996_read_status,
+       .soft_reset     = adm6996_soft_reset,
+};
+
+static int adm6996_gpio_probe(struct platform_device *pdev)
+{
+       struct adm6996_gpio_platform_data *pdata = pdev->dev.platform_data;
+       struct adm6996_priv *priv;
+       int ret;
+
+       if (!pdata)
+               return -EINVAL;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+
+       priv->eecs = pdata->eecs;
+       priv->eedi = pdata->eedi;
+       priv->eesk = pdata->eesk;
+
+       priv->model = pdata->model;
+       priv->read = adm6996_read_gpio_reg;
+       priv->write = adm6996_write_gpio_reg;
+
+       ret = devm_gpio_request(&pdev->dev, priv->eecs, "adm_eecs");
+       if (ret)
+               return ret;
+       ret = devm_gpio_request(&pdev->dev, priv->eedi, "adm_eedi");
+       if (ret)
+               return ret;
+       ret = devm_gpio_request(&pdev->dev, priv->eesk, "adm_eesk");
+       if (ret)
+               return ret;
+
+       ret = adm6996_switch_init(priv, dev_name(&pdev->dev), NULL);
+       if (ret < 0)
+               return ret;
+
+       platform_set_drvdata(pdev, priv);
+
+       return 0;
+}
+
+static int adm6996_gpio_remove(struct platform_device *pdev)
+{
+       struct adm6996_priv *priv = platform_get_drvdata(pdev);
+
+       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
+               unregister_switch(&priv->dev);
+
+       return 0;
+}
+
+static struct platform_driver adm6996_gpio_driver = {
+       .probe = adm6996_gpio_probe,
+       .remove = adm6996_gpio_remove,
+       .driver = {
+               .name = "adm6996_gpio",
+       },
+};
+
+static int __init adm6996_init(void)
+{
+       int err;
+
+       phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup);
+       err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE);
+       if (err)
+               return err;
+
+       err = platform_driver_register(&adm6996_gpio_driver);
+       if (err)
+               phy_driver_unregister(&adm6996_phy_driver);
+
+       return err;
+}
+
+static void __exit adm6996_exit(void)
+{
+       platform_driver_unregister(&adm6996_gpio_driver);
+       phy_driver_unregister(&adm6996_phy_driver);
+}
+
+module_init(adm6996_init);
+module_exit(adm6996_exit);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/adm6996.h b/target/linux/generic/files-4.9/drivers/net/phy/adm6996.h
new file mode 100644 (file)
index 0000000..6fd460a
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * ADM6996 switch driver
+ *
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ * Copyright (c) 2010,2011 Peter Lebbing <peter@digitalbrains.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#ifndef __ADM6996_H
+#define __ADM6996_H
+
+/*
+ * ADM_PHY_PORTS: Number of ports with a PHY.
+ * We only control ports 0 to 3, because if 4 is connected, it is most likely
+ * not connected to the switch but to a separate MII and MAC for the WAN port.
+ */
+#define ADM_PHY_PORTS  4
+#define ADM_NUM_PORTS  6
+#define ADM_CPU_PORT   5
+
+#define ADM_NUM_VLANS 16
+#define ADM_VLAN_MAX_ID 4094
+
+enum admreg {
+       ADM_EEPROM_BASE         = 0x0,
+               ADM_P0_CFG              = ADM_EEPROM_BASE + 1,
+               ADM_P1_CFG              = ADM_EEPROM_BASE + 3,
+               ADM_P2_CFG              = ADM_EEPROM_BASE + 5,
+               ADM_P3_CFG              = ADM_EEPROM_BASE + 7,
+               ADM_P4_CFG              = ADM_EEPROM_BASE + 8,
+               ADM_P5_CFG              = ADM_EEPROM_BASE + 9,
+               ADM_SYSC0               = ADM_EEPROM_BASE + 0xa,
+               ADM_VLAN_PRIOMAP        = ADM_EEPROM_BASE + 0xe,
+               ADM_SYSC3               = ADM_EEPROM_BASE + 0x11,
+               /* Input Force No Tag Enable */
+               ADM_IFNTE               = ADM_EEPROM_BASE + 0x20,
+               ADM_VID_CHECK           = ADM_EEPROM_BASE + 0x26,
+               ADM_P0_PVID             = ADM_EEPROM_BASE + 0x28,
+               ADM_P1_PVID             = ADM_EEPROM_BASE + 0x29,
+               /* Output Tag Bypass Enable and P2 PVID */
+               ADM_OTBE_P2_PVID        = ADM_EEPROM_BASE + 0x2a,
+               ADM_P3_P4_PVID          = ADM_EEPROM_BASE + 0x2b,
+               ADM_P5_PVID             = ADM_EEPROM_BASE + 0x2c,
+       ADM_EEPROM_EXT_BASE     = 0x40,
+#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n))
+#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n))
+#define ADM_VLAN_MAP(n) (ADM_EEPROM_BASE + 0x13 + n)
+       ADM_COUNTER_BASE        = 0xa0,
+               ADM_SIG0                = ADM_COUNTER_BASE + 0,
+               ADM_SIG1                = ADM_COUNTER_BASE + 1,
+               ADM_PS0         = ADM_COUNTER_BASE + 2,
+               ADM_PS1         = ADM_COUNTER_BASE + 3,
+               ADM_PS2         = ADM_COUNTER_BASE + 4,
+               ADM_CL0         = ADM_COUNTER_BASE + 8, /* RxPacket */
+               ADM_CL6         = ADM_COUNTER_BASE + 0x1a, /* RxByte */
+               ADM_CL12                = ADM_COUNTER_BASE + 0x2c, /* TxPacket */
+               ADM_CL18                = ADM_COUNTER_BASE + 0x3e, /* TxByte */
+               ADM_CL24                = ADM_COUNTER_BASE + 0x50, /* Coll */
+               ADM_CL30                = ADM_COUNTER_BASE + 0x62, /* Err */
+#define ADM_OFFSET_PORT(n) ((n * 4) - (n / 4) * 2 - (n / 5) * 2)
+       ADM_PHY_BASE            = 0x200,
+#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
+};
+
+/* Chip identification patterns */
+#define        ADM_SIG0_MASK   0xffff
+#define ADM_SIG0_VAL   0x1023
+#define ADM_SIG1_MASK  0xffff
+#define ADM_SIG1_VAL   0x0007
+
+enum {
+       ADM_PHYCFG_COLTST     = (1 << 7),       /* Enable collision test */
+       ADM_PHYCFG_DPLX       = (1 << 8),       /* Enable full duplex */
+       ADM_PHYCFG_ANEN_RST   = (1 << 9),       /* Restart auto negotiation (self clear) */
+       ADM_PHYCFG_ISO        = (1 << 10),      /* Isolate PHY */
+       ADM_PHYCFG_PDN        = (1 << 11),      /* Power down PHY */
+       ADM_PHYCFG_ANEN       = (1 << 12),      /* Enable auto negotiation */
+       ADM_PHYCFG_SPEED_100  = (1 << 13),      /* Enable 100 Mbit/s */
+       ADM_PHYCFG_LPBK       = (1 << 14),      /* Enable loopback operation */
+       ADM_PHYCFG_RST        = (1 << 15),      /* Reset the port (self clear) */
+       ADM_PHYCFG_INIT = (
+               ADM_PHYCFG_RST |
+               ADM_PHYCFG_SPEED_100 |
+               ADM_PHYCFG_ANEN |
+               ADM_PHYCFG_ANEN_RST
+       )
+};
+
+enum {
+       ADM_PORTCFG_FC        = (1 << 0),       /* Enable 802.x flow control */
+       ADM_PORTCFG_AN        = (1 << 1),       /* Enable auto-negotiation */
+       ADM_PORTCFG_SPEED_100 = (1 << 2),       /* Enable 100 Mbit/s */
+       ADM_PORTCFG_DPLX      = (1 << 3),       /* Enable full duplex */
+       ADM_PORTCFG_OT        = (1 << 4),       /* Output tagged packets */
+       ADM_PORTCFG_PD        = (1 << 5),       /* Port disable */
+       ADM_PORTCFG_TV_PRIO   = (1 << 6),       /* 0 = VLAN based priority
+                                                * 1 = TOS based priority */
+       ADM_PORTCFG_PPE       = (1 << 7),       /* Port based priority enable */
+       ADM_PORTCFG_PP_S      = (1 << 8),       /* Port based priority, 2 bits */
+       ADM_PORTCFG_PVID_BASE = (1 << 10),      /* Primary VLAN id, 4 bits */
+       ADM_PORTCFG_FSE       = (1 << 14),      /* Fx select enable */
+       ADM_PORTCFG_CAM       = (1 << 15),      /* Crossover Auto MDIX */
+
+       ADM_PORTCFG_INIT = (
+               ADM_PORTCFG_FC |
+               ADM_PORTCFG_AN |
+               ADM_PORTCFG_SPEED_100 |
+               ADM_PORTCFG_DPLX |
+               ADM_PORTCFG_CAM
+       ),
+       ADM_PORTCFG_CPU = (
+               ADM_PORTCFG_FC |
+               ADM_PORTCFG_SPEED_100 |
+               ADM_PORTCFG_OT |
+               ADM_PORTCFG_DPLX
+       ),
+};
+
+#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8)
+#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
+#define ADM_PORTCFG_PVID_MASK (0xf << 10)
+
+#define ADM_IFNTE_MASK (0x3f << 9)
+#define ADM_VID_CHECK_MASK (0x3f << 6)
+
+#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8)
+#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
+#define ADM_P2_PVID_MASK 0xff
+
+#define ADM_OTBE(n) (((n) & 0x3f) << 8)
+#define ADM_OTBE_MASK (0x3f << 8)
+
+/* ADM_SYSC0 */
+enum {
+       ADM_NTTE        = (1 << 2),     /* New Tag Transmit Enable */
+       ADM_RVID1       = (1 << 8)      /* Replace VLAN ID 1 */
+};
+
+/* Tag Based VLAN in ADM_SYSC3 */
+#define ADM_MAC_CLONE  BIT(4)
+#define ADM_TBV                BIT(5)
+
+static const u8 adm_portcfg[] = {
+       [0] = ADM_P0_CFG,
+       [1] = ADM_P1_CFG,
+       [2] = ADM_P2_CFG,
+       [3] = ADM_P3_CFG,
+       [4] = ADM_P4_CFG,
+       [5] = ADM_P5_CFG,
+};
+
+/* Fields in ADM_VLAN_FILT_L(x) */
+#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12)
+#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6)
+#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0)
+#define ADM_VLAN_FILT_MEMBER_MASK 0x3f
+/* Fields in ADM_VLAN_FILT_H(x) */
+#define ADM_VLAN_FILT_VALID (1 << 15)
+#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0)
+
+/* Convert ports to a form for ADM6996L VLAN map */
+#define ADM_VLAN_FILT(ports) ((ports & 0x01) | ((ports & 0x02) << 1) | \
+                       ((ports & 0x04) << 2) | ((ports & 0x08) << 3) | \
+                       ((ports & 0x10) << 3) | ((ports & 0x20) << 3))
+
+/* Port status register */
+enum {
+       ADM_PS_LS = (1 << 0),   /* Link status */
+       ADM_PS_SS = (1 << 1),   /* Speed status */
+       ADM_PS_DS = (1 << 2),   /* Duplex status */
+       ADM_PS_FCS = (1 << 3)   /* Flow control status */
+};
+
+/*
+ * Split the register address in phy id and register
+ * it will get combined again by the mdio bus op
+ */
+#define PHYADDR(_reg)  ((_reg >> 5) & 0xff), (_reg & 0x1f)
+
+#endif
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/ar8216.c b/target/linux/generic/files-4.9/drivers/net/phy/ar8216.c
new file mode 100644 (file)
index 0000000..7512ee1
--- /dev/null
@@ -0,0 +1,2313 @@
+/*
+ * ar8216.c: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/bitops.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/lockdep.h>
+#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/version.h>
+
+#include "ar8216.h"
+
+extern const struct ar8xxx_chip ar8327_chip;
+extern const struct ar8xxx_chip ar8337_chip;
+
+#define AR8XXX_MIB_WORK_DELAY  2000 /* msecs */
+
+#define MIB_DESC(_s , _o, _n)  \
+       {                       \
+               .size = (_s),   \
+               .offset = (_o), \
+               .name = (_n),   \
+       }
+
+static const struct ar8xxx_mib_desc ar8216_mibs[] = {
+       MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"),
+       MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"),
+       MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"),
+       MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"),
+       MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"),
+       MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"),
+       MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"),
+       MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"),
+       MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"),
+       MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"),
+       MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"),
+       MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"),
+       MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"),
+       MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"),
+       MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"),
+       MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"),
+       MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"),
+       MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"),
+       MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"),
+       MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"),
+       MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"),
+       MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"),
+       MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"),
+       MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"),
+       MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"),
+       MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"),
+       MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"),
+       MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"),
+       MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"),
+       MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"),
+       MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"),
+       MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"),
+       MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"),
+       MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"),
+       MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"),
+       MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"),
+       MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"),
+};
+
+const struct ar8xxx_mib_desc ar8236_mibs[39] = {
+       MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"),
+       MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"),
+       MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"),
+       MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"),
+       MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"),
+       MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"),
+       MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"),
+       MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"),
+       MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"),
+       MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"),
+       MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"),
+       MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"),
+       MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"),
+       MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"),
+       MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"),
+       MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"),
+       MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"),
+       MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"),
+       MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"),
+       MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"),
+       MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"),
+       MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"),
+       MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"),
+       MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"),
+       MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"),
+       MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"),
+       MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"),
+       MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"),
+       MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"),
+       MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"),
+       MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"),
+       MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"),
+       MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"),
+       MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"),
+       MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"),
+       MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"),
+       MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"),
+       MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"),
+       MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"),
+};
+
+static DEFINE_MUTEX(ar8xxx_dev_list_lock);
+static LIST_HEAD(ar8xxx_dev_list);
+
+/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
+static int
+ar8xxx_phy_poll_reset(struct mii_bus *bus)
+{
+        unsigned int sleep_msecs = 20;
+        int ret, elapsed, i;
+
+        for (elapsed = sleep_msecs; elapsed <= 600;
+            elapsed += sleep_msecs) {
+                msleep(sleep_msecs);
+                for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+                        ret = mdiobus_read(bus, i, MII_BMCR);
+                        if (ret < 0)
+                               return ret;
+                        if (ret & BMCR_RESET)
+                               break;
+                        if (i == AR8XXX_NUM_PHYS - 1) {
+                                usleep_range(1000, 2000);
+                                return 0;
+                        }
+                }
+        }
+        return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_phy_check_aneg(struct phy_device *phydev)
+{
+       int ret;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return 0;
+       /*
+        * BMCR_ANENABLE might have been cleared
+        * by phy_init_hw in certain kernel versions
+        * therefore check for it
+        */
+       ret = phy_read(phydev, MII_BMCR);
+       if (ret < 0)
+               return ret;
+       if (ret & BMCR_ANENABLE)
+               return 0;
+
+       dev_info(&phydev->mdio.dev, "ANEG disabled, re-enabling ...\n");
+       ret |= BMCR_ANENABLE | BMCR_ANRESTART;
+       return phy_write(phydev, MII_BMCR, ret);
+}
+
+void
+ar8xxx_phy_init(struct ar8xxx_priv *priv)
+{
+       int i;
+       struct mii_bus *bus;
+
+       bus = priv->mii_bus;
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
+               if (priv->chip->phy_fixup)
+                       priv->chip->phy_fixup(priv, i);
+
+               /* initialize the port itself */
+               mdiobus_write(bus, i, MII_ADVERTISE,
+                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
+               if (ar8xxx_has_gige(priv))
+                       mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
+               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
+       }
+
+       ar8xxx_phy_poll_reset(bus);
+}
+
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 lo, hi;
+
+       lo = bus->read(bus, phy_id, regnum);
+       hi = bus->read(bus, phy_id, regnum + 1);
+
+       return (hi << 16) | lo;
+}
+
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 lo, hi;
+
+       lo = val & 0xffff;
+       hi = (u16) (val >> 16);
+
+       if (priv->chip->mii_lo_first)
+       {
+               bus->write(bus, phy_id, regnum, lo);
+               bus->write(bus, phy_id, regnum + 1, hi);
+       } else {
+               bus->write(bus, phy_id, regnum + 1, hi);
+               bus->write(bus, phy_id, regnum, lo);
+       }
+}
+
+u32
+ar8xxx_read(struct ar8xxx_priv *priv, int reg)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+       u32 val;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+       val = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
+
+       mutex_unlock(&bus->mdio_lock);
+
+       return val;
+}
+
+void
+ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, val);
+
+       mutex_unlock(&bus->mdio_lock);
+}
+
+u32
+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r1, r2, page;
+       u32 ret;
+
+       split_addr((u32) reg, &r1, &r2, &page);
+
+       mutex_lock(&bus->mdio_lock);
+
+       bus->write(bus, 0x18, 0, page);
+       wait_for_page_switch();
+
+       ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
+       ret &= ~mask;
+       ret |= val;
+       ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret);
+
+       mutex_unlock(&bus->mdio_lock);
+
+       return ret;
+}
+
+void
+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
+                    u16 dbg_addr, u16 dbg_data)
+{
+       struct mii_bus *bus = priv->mii_bus;
+
+       mutex_lock(&bus->mdio_lock);
+       bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
+       bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
+       mutex_unlock(&bus->mdio_lock);
+}
+
+static inline void
+ar8xxx_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg)
+{
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
+       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, reg);
+       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
+}
+
+void
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data)
+{
+       struct mii_bus *bus = priv->mii_bus;
+
+       mutex_lock(&bus->mdio_lock);
+       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
+       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data);
+       mutex_unlock(&bus->mdio_lock);
+}
+
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 data;
+
+       mutex_lock(&bus->mdio_lock);
+       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
+       data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
+       mutex_unlock(&bus->mdio_lock);
+
+       return data;
+}
+
+static int
+ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
+               unsigned timeout)
+{
+       int i;
+
+       for (i = 0; i < timeout; i++) {
+               u32 t;
+
+               t = ar8xxx_read(priv, reg);
+               if ((t & mask) == val)
+                       return 0;
+
+               usleep_range(1000, 2000);
+               cond_resched();
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int
+ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
+{
+       unsigned mib_func = priv->chip->mib_func;
+       int ret;
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       /* Capture the hardware statistics for all ports */
+       ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S));
+
+       /* Wait for the capturing to complete. */
+       ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+out:
+       return ret;
+}
+
+static int
+ar8xxx_mib_capture(struct ar8xxx_priv *priv)
+{
+       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE);
+}
+
+static int
+ar8xxx_mib_flush(struct ar8xxx_priv *priv)
+{
+       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH);
+}
+
+static void
+ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush)
+{
+       unsigned int base;
+       u64 *mib_stats;
+       int i;
+
+       WARN_ON(port >= priv->dev.ports);
+
+       lockdep_assert_held(&priv->mib_lock);
+
+       base = priv->chip->reg_port_stats_start +
+              priv->chip->reg_port_stats_length * port;
+
+       mib_stats = &priv->mib_stats[port * priv->chip->num_mibs];
+       for (i = 0; i < priv->chip->num_mibs; i++) {
+               const struct ar8xxx_mib_desc *mib;
+               u64 t;
+
+               mib = &priv->chip->mib_decs[i];
+               t = ar8xxx_read(priv, base + mib->offset);
+               if (mib->size == 2) {
+                       u64 hi;
+
+                       hi = ar8xxx_read(priv, base + mib->offset + 4);
+                       t |= hi << 32;
+               }
+
+               if (flush)
+                       mib_stats[i] = 0;
+               else
+                       mib_stats[i] += t;
+               cond_resched();
+       }
+}
+
+static void
+ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
+                     struct switch_port_link *link)
+{
+       u32 status;
+       u32 speed;
+
+       memset(link, '\0', sizeof(*link));
+
+       status = priv->chip->read_port_status(priv, port);
+
+       link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO);
+       if (link->aneg) {
+               link->link = !!(status & AR8216_PORT_STATUS_LINK_UP);
+       } else {
+               link->link = true;
+
+               if (priv->get_port_link) {
+                       int err;
+
+                       err = priv->get_port_link(port);
+                       if (err >= 0)
+                               link->link = !!err;
+               }
+       }
+
+       if (!link->link)
+               return;
+
+       link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX);
+       link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
+       link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
+
+       if (link->aneg && link->duplex && priv->chip->read_port_eee_status)
+               link->eee = priv->chip->read_port_eee_status(priv, port);
+
+       speed = (status & AR8216_PORT_STATUS_SPEED) >>
+                AR8216_PORT_STATUS_SPEED_S;
+
+       switch (speed) {
+       case AR8216_PORT_SPEED_10M:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case AR8216_PORT_SPEED_100M:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case AR8216_PORT_SPEED_1000M:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+}
+
+static struct sk_buff *
+ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ar8xxx_priv *priv = dev->phy_ptr;
+       unsigned char *buf;
+
+       if (unlikely(!priv))
+               goto error;
+
+       if (!priv->vlan)
+               goto send;
+
+       if (unlikely(skb_headroom(skb) < 2)) {
+               if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0)
+                       goto error;
+       }
+
+       buf = skb_push(skb, 2);
+       buf[0] = 0x10;
+       buf[1] = 0x80;
+
+send:
+       return skb;
+
+error:
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static void
+ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct ar8xxx_priv *priv;
+       unsigned char *buf;
+       int port, vlan;
+
+       priv = dev->phy_ptr;
+       if (!priv)
+               return;
+
+       /* don't strip the header if vlan mode is disabled */
+       if (!priv->vlan)
+               return;
+
+       /* strip header, get vlan id */
+       buf = skb->data;
+       skb_pull(skb, 2);
+
+       /* check for vlan header presence */
+       if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00))
+               return;
+
+       port = buf[0] & 0x7;
+
+       /* no need to fix up packets coming from a tagged source */
+       if (priv->vlan_tagged & (1 << port))
+               return;
+
+       /* lookup port vid from local table, the switch passes an invalid vlan id */
+       vlan = priv->vlan_id[priv->pvid[port]];
+
+       buf[14 + 2] &= 0xf0;
+       buf[14 + 2] |= vlan >> 8;
+       buf[15 + 2] = vlan & 0xff;
+}
+
+int
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
+{
+       int timeout = 20;
+       u32 t = 0;
+
+       while (1) {
+               t = ar8xxx_read(priv, reg);
+               if ((t & mask) == val)
+                       return 0;
+
+               if (timeout-- <= 0)
+                       break;
+
+               udelay(10);
+               cond_resched();
+       }
+
+       pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n",
+              (unsigned int) reg, t, mask, val);
+       return -ETIMEDOUT;
+}
+
+static void
+ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
+{
+       if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0))
+               return;
+       if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) {
+               val &= AR8216_VTUDATA_MEMBER;
+               val |= AR8216_VTUDATA_VALID;
+               ar8xxx_write(priv, AR8216_REG_VTU_DATA, val);
+       }
+       op |= AR8216_VTU_ACTIVE;
+       ar8xxx_write(priv, AR8216_REG_VTU, op);
+}
+
+static void
+ar8216_vtu_flush(struct ar8xxx_priv *priv)
+{
+       ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
+}
+
+static void
+ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
+{
+       u32 op;
+
+       op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S);
+       ar8216_vtu_op(priv, op, port_mask);
+}
+
+static int
+ar8216_atu_flush(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret)
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH |
+                                                        AR8216_ATU_ACTIVE);
+
+       return ret;
+}
+
+static int
+ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
+       if (!ret) {
+               t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT;
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t);
+       }
+
+       return ret;
+}
+
+static u32
+ar8216_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+       return ar8xxx_read(priv, AR8216_REG_PORT_STATUS(port));
+}
+
+static void
+ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 header;
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU)
+               header = AR8216_PORT_CTRL_HEADER;
+       else
+               header = 0;
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
+                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
+                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
+                  AR8216_PORT_CTRL_LEARN | header |
+                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
+                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port),
+                  AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE |
+                  AR8216_PORT_VLAN_DEFAULT_ID,
+                  (members << AR8216_PORT_VLAN_DEST_PORTS_S) |
+                  (ingress << AR8216_PORT_VLAN_MODE_S) |
+                  (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S));
+}
+
+static int
+ar8216_hw_init(struct ar8xxx_priv *priv)
+{
+       if (priv->initialized)
+               return 0;
+
+       ar8xxx_phy_init(priv);
+
+       priv->initialized = true;
+       return 0;
+}
+
+static void
+ar8216_init_globals(struct ar8xxx_priv *priv)
+{
+       /* standard atheros magic */
+       ar8xxx_write(priv, 0x38, 0xc000050e);
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8216_GCTRL_MTU, 1518 + 8 + 2);
+}
+
+static void
+ar8216_init_port(struct ar8xxx_priv *priv, int port)
+{
+       /* Enable port learning and tx */
+       ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port),
+               AR8216_PORT_CTRL_LEARN |
+               (4 << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_write(priv, AR8216_REG_PORT_VLAN(port), 0);
+
+       if (port == AR8216_PORT_CPU) {
+               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
+                       AR8216_PORT_STATUS_LINK_UP |
+                       (ar8xxx_has_gige(priv) ?
+                                AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) |
+                       AR8216_PORT_STATUS_TXMAC |
+                       AR8216_PORT_STATUS_RXMAC |
+                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) |
+                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) |
+                       AR8216_PORT_STATUS_DUPLEX);
+       } else {
+               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
+                       AR8216_PORT_STATUS_LINK_AUTO);
+       }
+}
+
+static void
+ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) {
+               udelay(10);
+               cond_resched();
+       }
+
+       if (!timeout)
+               pr_err("ar8216: timeout waiting for atu to become ready\n");
+}
+
+static void ar8216_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_func0, r1_func1, r1_func2;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e;
+       r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT);
+               ar8xxx_mii_write32(priv, r2, r1_func1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_func2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               t = ar8xxx_mii_read32(priv, r2, r1_func0);
+               t |= AR8216_ATU_ACTIVE;
+               ar8xxx_mii_write32(priv, r2, r1_func0, t);
+               ar8216_wait_atu_ready(priv, r2, r1_func0);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_func0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_func1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_func2);
+
+               *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8216_ATU_PORT0;
+               while (!(val2 & t) && ++i < priv->dev.ports)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S;
+               a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S;
+               a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S;
+               a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S;
+               a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S;
+               a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S;
+               break;
+       }
+}
+
+static void
+ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 egress, ingress;
+       u32 pvid;
+
+       if (priv->vlan) {
+               pvid = priv->vlan_id[priv->pvid[port]];
+               if (priv->vlan_tagged & (1 << port))
+                       egress = AR8216_OUT_ADD_VLAN;
+               else
+                       egress = AR8216_OUT_STRIP_VLAN;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               pvid = port;
+               egress = AR8216_OUT_KEEP;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
+                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
+                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
+                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
+                  AR8216_PORT_CTRL_LEARN |
+                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
+                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
+
+       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port),
+                  AR8236_PORT_VLAN_DEFAULT_ID,
+                  (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S));
+
+       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port),
+                  AR8236_PORT_VLAN2_VLAN_MODE |
+                  AR8236_PORT_VLAN2_MEMBER,
+                  (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) |
+                  (members << AR8236_PORT_VLAN2_MEMBER_S));
+}
+
+static void
+ar8236_init_globals(struct ar8xxx_priv *priv)
+{
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
+
+       /* enable cpu port to receive arp frames */
+       ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL,
+                  AR8236_ATU_CTRL_RES);
+
+       /* enable cpu port to receive multicast and broadcast frames */
+       ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK,
+                  AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN);
+
+       /* Enable MIB counters */
+       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
+                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
+                  AR8236_MIB_EN);
+}
+
+static int
+ar8316_hw_init(struct ar8xxx_priv *priv)
+{
+       u32 val, newval;
+
+       val = ar8xxx_read(priv, AR8316_REG_POSTRIP);
+
+       if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
+               if (priv->port4_phy) {
+                       /* value taken from Ubiquiti RouterStation Pro */
+                       newval = 0x81461bea;
+                       pr_info("ar8316: Using port 4 as PHY\n");
+               } else {
+                       newval = 0x01261be2;
+                       pr_info("ar8316: Using port 4 as switch port\n");
+               }
+       } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) {
+               /* value taken from AVM Fritz!Box 7390 sources */
+               newval = 0x010e5b71;
+       } else {
+               /* no known value for phy interface */
+               pr_err("ar8316: unsupported mii mode: %d.\n",
+                      priv->phy->interface);
+               return -EINVAL;
+       }
+
+       if (val == newval)
+               goto out;
+
+       ar8xxx_write(priv, AR8316_REG_POSTRIP, newval);
+
+       if (priv->port4_phy &&
+           priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
+               /* work around for phy4 rgmii mode */
+               ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c);
+               /* rx delay */
+               ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e);
+               /* tx delay */
+               ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47);
+               msleep(1000);
+       }
+
+       ar8xxx_phy_init(priv);
+
+out:
+       priv->initialized = true;
+       return 0;
+}
+
+static void
+ar8316_init_globals(struct ar8xxx_priv *priv)
+{
+       /* standard atheros magic */
+       ar8xxx_write(priv, 0x38, 0xc000050e);
+
+       /* enable cpu port to receive multicast and broadcast frames */
+       ar8xxx_write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f);
+
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
+                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
+
+       /* Enable MIB counters */
+       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
+                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
+                  AR8236_MIB_EN);
+}
+
+int
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       priv->vlan = !!val->value.i;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->vlan;
+       return 0;
+}
+
+
+int
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       /* make sure no invalid PVIDs get set */
+
+       if (vlan < 0 || vlan >= dev->vlans ||
+           port < 0 || port >= AR8X16_MAX_PORTS)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       if (port < 0 || port >= AR8X16_MAX_PORTS)
+               return -EINVAL;
+
+       *vlan = priv->pvid[port];
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       if (val->port_vlan >= AR8X16_MAX_VLANS)
+               return -EINVAL;
+
+       priv->vlan_id[val->port_vlan] = val->value.i;
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->vlan_id[val->port_vlan];
+       return 0;
+}
+
+int
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       ar8216_read_port_link(priv, port, link);
+       return 0;
+}
+
+static int
+ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports;
+       int i;
+
+       if (val->port_vlan >= AR8X16_MAX_VLANS)
+               return -EINVAL;
+
+       ports = priv->vlan_table[val->port_vlan];
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (priv->vlan_tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
+ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i, j;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       priv->vlan_tagged |= (1 << p->id);
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+
+                       /* make sure that an untagged port does not
+                        * appear in other vlans */
+                       for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+                               if (j == val->port_vlan)
+                                       continue;
+                               priv->vlan_table[j] &= ~(1 << p->id);
+                       }
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static void
+ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+       for (port = 0; port < AR8216_NUM_PORTS; port++) {
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8216_NUM_PORTS ||
+           priv->monitor_port >= AR8216_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
+                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
+                  (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_RX);
+
+       if (priv->mirror_tx)
+               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
+                          AR8216_PORT_CTRL_MIRROR_TX);
+}
+
+static inline u32
+ar8xxx_age_time_val(int age_time)
+{
+       return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) /
+              AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS;
+}
+
+static inline void
+ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg)
+{
+       u32 age_time = ar8xxx_age_time_val(priv->arl_age_time);
+       ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S);
+}
+
+int
+ar8xxx_sw_hw_apply(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       u8 portmask[AR8X16_MAX_PORTS];
+       int i, j;
+
+       mutex_lock(&priv->reg_mutex);
+       /* flush all vlan translation unit entries */
+       priv->chip->vtu_flush(priv);
+
+       memset(portmask, 0, sizeof(portmask));
+       if (!priv->init) {
+               /* calculate the port destination masks and load vlans
+                * into the vlan translation unit */
+               for (j = 0; j < AR8X16_MAX_VLANS; j++) {
+                       u8 vp = priv->vlan_table[j];
+
+                       if (!vp)
+                               continue;
+
+                       for (i = 0; i < dev->ports; i++) {
+                               u8 mask = (1 << i);
+                               if (vp & mask)
+                                       portmask[i] |= vp & ~mask;
+                       }
+
+                       chip->vtu_load_vlan(priv, priv->vlan_id[j],
+                                           priv->vlan_table[j]);
+               }
+       } else {
+               /* vlan disabled:
+                * isolate all ports, but connect them to the cpu port */
+               for (i = 0; i < dev->ports; i++) {
+                       if (i == AR8216_PORT_CPU)
+                               continue;
+
+                       portmask[i] = 1 << AR8216_PORT_CPU;
+                       portmask[AR8216_PORT_CPU] |= (1 << i);
+               }
+       }
+
+       /* update the port destination mask registers and tag settings */
+       for (i = 0; i < dev->ports; i++) {
+               chip->setup_port(priv, i, portmask[i]);
+       }
+
+       chip->set_mirror_regs(priv);
+
+       /* set age time */
+       if (chip->reg_arl_ctrl)
+               ar8xxx_set_age_time(priv, chip->reg_arl_ctrl);
+
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+int
+ar8xxx_sw_reset_switch(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+       memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) -
+               offsetof(struct ar8xxx_priv, vlan));
+
+       for (i = 0; i < AR8X16_MAX_VLANS; i++)
+               priv->vlan_id[i] = i;
+
+       /* Configure all ports */
+       for (i = 0; i < dev->ports; i++)
+               chip->init_port(priv, i);
+
+       priv->mirror_rx = false;
+       priv->mirror_tx = false;
+       priv->source_port = 0;
+       priv->monitor_port = 0;
+       priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME;
+
+       chip->init_globals(priv);
+       chip->atu_flush(priv);
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return chip->sw_hw_apply(dev);
+}
+
+int
+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       unsigned int len;
+       int ret;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->mib_lock);
+
+       len = priv->dev.ports * priv->chip->num_mibs *
+             sizeof(*priv->mib_stats);
+       memset(priv->mib_stats, '\0', len);
+       ret = ar8xxx_mib_flush(priv);
+       if (ret)
+               goto unlock;
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_rx = !!val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_rx;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->mirror_tx = !!val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->mirror_tx;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->monitor_port = val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->monitor_port;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       priv->source_port = val->value.i;
+       priv->chip->set_mirror_regs(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->source_port;
+       return 0;
+}
+
+int
+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port;
+       int ret;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+       ret = ar8xxx_mib_capture(priv);
+       if (ret)
+               goto unlock;
+
+       ar8xxx_mib_fetch_port_stat(priv, port, true);
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+static void
+ar8xxx_byte_to_str(char *buf, int len, u64 byte)
+{
+       unsigned long b;
+       const char *unit;
+
+       if (byte >= 0x40000000) { /* 1 GiB */
+               b = byte * 10 / 0x40000000;
+               unit = "GiB";
+       } else if (byte >= 0x100000) { /* 1 MiB */
+               b = byte * 10 / 0x100000;
+               unit = "MiB";
+       } else if (byte >= 0x400) { /* 1 KiB */
+               b = byte * 10 / 0x400;
+               unit = "KiB";
+       } else {
+               b = byte;
+               unit = "Byte";
+       }
+       if (strcmp(unit, "Byte"))
+               snprintf(buf, len, "%lu.%lu %s", b / 10, b % 10, unit);
+       else
+               snprintf(buf, len, "%lu %s", b, unit);
+}
+
+int
+ar8xxx_sw_get_port_mib(struct switch_dev *dev,
+                      const struct switch_attr *attr,
+                      struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8xxx_chip *chip = priv->chip;
+       u64 *mib_stats, mib_data;
+       unsigned int port;
+       int ret;
+       char *buf = priv->buf;
+       char buf1[64];
+       const char *mib_name;
+       int i, len = 0;
+       bool mib_stats_empty = true;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return -EOPNOTSUPP;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->mib_lock);
+       ret = ar8xxx_mib_capture(priv);
+       if (ret)
+               goto unlock;
+
+       ar8xxx_mib_fetch_port_stat(priv, port, false);
+
+       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                       "MIB counters\n");
+
+       mib_stats = &priv->mib_stats[port * chip->num_mibs];
+       for (i = 0; i < chip->num_mibs; i++) {
+               mib_name = chip->mib_decs[i].name;
+               mib_data = mib_stats[i];
+               len += snprintf(buf + len, sizeof(priv->buf) - len,
+                               "%-12s: %llu\n", mib_name, mib_data);
+               if ((!strcmp(mib_name, "TxByte") ||
+                   !strcmp(mib_name, "RxGoodByte")) &&
+                   mib_data >= 1024) {
+                       ar8xxx_byte_to_str(buf1, sizeof(buf1), mib_data);
+                       --len; /* discard newline at the end of buf */
+                       len += snprintf(buf + len, sizeof(priv->buf) - len,
+                                       " (%s)\n", buf1);
+               }
+               if (mib_stats_empty && mib_data)
+                       mib_stats_empty = false;
+       }
+
+       if (mib_stats_empty)
+               len = snprintf(buf, sizeof(priv->buf), "No MIB data");
+
+       val->value.s = buf;
+       val->len = len;
+
+       ret = 0;
+
+unlock:
+       mutex_unlock(&priv->mib_lock);
+       return ret;
+}
+
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+                          struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int age_time = val->value.i;
+       u32 age_time_val;
+
+       if (age_time < 0)
+               return -EINVAL;
+
+       age_time_val = ar8xxx_age_time_val(age_time);
+       if (age_time_val == 0 || age_time_val > 0xffff)
+               return -EINVAL;
+
+       priv->arl_age_time = age_time;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
+                   struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       val->value.i = priv->arl_age_time;
+       return 0;
+}
+
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct mii_bus *bus = priv->mii_bus;
+       const struct ar8xxx_chip *chip = priv->chip;
+       char *buf = priv->arl_buf;
+       int i, j, k, len = 0;
+       struct arl_entry *a, *a1;
+       u32 status;
+
+       if (!chip->get_arl_entry)
+               return -EOPNOTSUPP;
+
+       mutex_lock(&priv->reg_mutex);
+       mutex_lock(&bus->mdio_lock);
+
+       chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE);
+
+       for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) {
+               a = &priv->arl_table[i];
+               duplicate:
+               chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT);
+
+               if (!status)
+                       break;
+
+               /* avoid duplicates
+                * ARL table can include multiple valid entries
+                * per MAC, just with differing status codes
+                */
+               for (j = 0; j < i; ++j) {
+                       a1 = &priv->arl_table[j];
+                       if (a->port == a1->port && !memcmp(a->mac, a1->mac, sizeof(a->mac)))
+                               goto duplicate;
+               }
+       }
+
+       mutex_unlock(&bus->mdio_lock);
+
+       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                        "address resolution table\n");
+
+       if (i == AR8XXX_NUM_ARL_RECORDS)
+               len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                               "Too many entries found, displaying the first %d only!\n",
+                               AR8XXX_NUM_ARL_RECORDS);
+
+       for (j = 0; j < priv->dev.ports; ++j) {
+               for (k = 0; k < i; ++k) {
+                       a = &priv->arl_table[k];
+                       if (a->port != j)
+                               continue;
+                       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
+                                       "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+                                       j,
+                                       a->mac[5], a->mac[4], a->mac[3],
+                                       a->mac[2], a->mac[1], a->mac[0]);
+               }
+       }
+
+       val->value.s = buf;
+       val->len = len;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int ret;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush(priv);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port, ret;
+
+       port = val->port_vlan;
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ret = priv->chip->atu_flush_port(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return ret;
+}
+
+static const struct switch_attr ar8xxx_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8216_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
+};
+
+const struct switch_attr ar8xxx_sw_attr_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = ar8xxx_sw_set_port_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .set = NULL,
+               .get = ar8xxx_sw_get_port_mib,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
+};
+
+const struct switch_attr ar8xxx_sw_attr_vlan[1] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "vid",
+               .description = "VLAN ID (0-4094)",
+               .set = ar8xxx_sw_set_vid,
+               .get = ar8xxx_sw_get_vid,
+               .max = 4094,
+       },
+};
+
+static const struct switch_dev_ops ar8xxx_sw_ops = {
+       .attr_global = {
+               .attr = ar8xxx_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8xxx_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8xxx_sw_get_ports,
+       .set_vlan_ports = ar8xxx_sw_set_ports,
+       .apply_config = ar8xxx_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+/* The following op is disabled as it hogs the CPU and degrades performance.
+   An implementation has been attempted in 4d8a66d but reading MIB data is slow
+   on ar8xxx switches.
+
+   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
+   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
+   the request to update the MIB counter is cleared. */
+#if 0
+       .get_port_stats = ar8xxx_sw_get_port_stats,
+#endif
+};
+
+static const struct ar8xxx_chip ar8216_chip = {
+       .caps = AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x19000,
+       .reg_port_stats_length = 0xa0,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8216",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8216_NUM_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8216_hw_init,
+       .init_globals = ar8216_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8216_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8216_mibs),
+       .mib_decs = ar8216_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static const struct ar8xxx_chip ar8236_chip = {
+       .caps = AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x20000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8236",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8216_NUM_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8216_hw_init,
+       .init_globals = ar8236_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8236_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static const struct ar8xxx_chip ar8316_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+
+       .reg_port_stats_start = 0x20000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
+
+       .name = "Atheros AR8316",
+       .ports = AR8216_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8xxx_sw_ops,
+
+       .hw_init = ar8316_hw_init,
+       .init_globals = ar8316_init_globals,
+       .init_port = ar8216_init_port,
+       .setup_port = ar8216_setup_port,
+       .read_port_status = ar8216_read_port_status,
+       .atu_flush = ar8216_atu_flush,
+       .atu_flush_port = ar8216_atu_flush_port,
+       .vtu_flush = ar8216_vtu_flush,
+       .vtu_load_vlan = ar8216_vtu_load_vlan,
+       .set_mirror_regs = ar8216_set_mirror_regs,
+       .get_arl_entry = ar8216_get_arl_entry,
+       .sw_hw_apply = ar8xxx_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8216_REG_MIB_FUNC
+};
+
+static int
+ar8xxx_id_chip(struct ar8xxx_priv *priv)
+{
+       u32 val;
+       u16 id;
+       int i;
+
+       val = ar8xxx_read(priv, AR8216_REG_CTRL);
+       if (val == ~0)
+               return -ENODEV;
+
+       id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
+       for (i = 0; i < AR8X16_PROBE_RETRIES; i++) {
+               u16 t;
+
+               val = ar8xxx_read(priv, AR8216_REG_CTRL);
+               if (val == ~0)
+                       return -ENODEV;
+
+               t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
+               if (t != id)
+                       return -ENODEV;
+       }
+
+       priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S;
+       priv->chip_rev = (id & AR8216_CTRL_REVISION);
+
+       switch (priv->chip_ver) {
+       case AR8XXX_VER_AR8216:
+               priv->chip = &ar8216_chip;
+               break;
+       case AR8XXX_VER_AR8236:
+               priv->chip = &ar8236_chip;
+               break;
+       case AR8XXX_VER_AR8316:
+               priv->chip = &ar8316_chip;
+               break;
+       case AR8XXX_VER_AR8327:
+               priv->chip = &ar8327_chip;
+               break;
+       case AR8XXX_VER_AR8337:
+               priv->chip = &ar8337_chip;
+               break;
+       default:
+               pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n",
+                      priv->chip_ver, priv->chip_rev);
+
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void
+ar8xxx_mib_work_func(struct work_struct *work)
+{
+       struct ar8xxx_priv *priv;
+       int err;
+
+       priv = container_of(work, struct ar8xxx_priv, mib_work.work);
+
+       mutex_lock(&priv->mib_lock);
+
+       err = ar8xxx_mib_capture(priv);
+       if (err)
+               goto next_port;
+
+       ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false);
+
+next_port:
+       priv->mib_next_port++;
+       if (priv->mib_next_port >= priv->dev.ports)
+               priv->mib_next_port = 0;
+
+       mutex_unlock(&priv->mib_lock);
+       schedule_delayed_work(&priv->mib_work,
+                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
+}
+
+static int
+ar8xxx_mib_init(struct ar8xxx_priv *priv)
+{
+       unsigned int len;
+
+       if (!ar8xxx_has_mib_counters(priv))
+               return 0;
+
+       BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs);
+
+       len = priv->dev.ports * priv->chip->num_mibs *
+             sizeof(*priv->mib_stats);
+       priv->mib_stats = kzalloc(len, GFP_KERNEL);
+
+       if (!priv->mib_stats)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void
+ar8xxx_mib_start(struct ar8xxx_priv *priv)
+{
+       if (!ar8xxx_has_mib_counters(priv))
+               return;
+
+       schedule_delayed_work(&priv->mib_work,
+                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
+}
+
+static void
+ar8xxx_mib_stop(struct ar8xxx_priv *priv)
+{
+       if (!ar8xxx_has_mib_counters(priv))
+               return;
+
+       cancel_delayed_work_sync(&priv->mib_work);
+}
+
+static struct ar8xxx_priv *
+ar8xxx_create(void)
+{
+       struct ar8xxx_priv *priv;
+
+       priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return NULL;
+
+       mutex_init(&priv->reg_mutex);
+       mutex_init(&priv->mib_lock);
+       INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func);
+
+       return priv;
+}
+
+static void
+ar8xxx_free(struct ar8xxx_priv *priv)
+{
+       if (priv->chip && priv->chip->cleanup)
+               priv->chip->cleanup(priv);
+
+       kfree(priv->chip_data);
+       kfree(priv->mib_stats);
+       kfree(priv);
+}
+
+static int
+ar8xxx_probe_switch(struct ar8xxx_priv *priv)
+{
+       const struct ar8xxx_chip *chip;
+       struct switch_dev *swdev;
+       int ret;
+
+       ret = ar8xxx_id_chip(priv);
+       if (ret)
+               return ret;
+
+       chip = priv->chip;
+
+       swdev = &priv->dev;
+       swdev->cpu_port = AR8216_PORT_CPU;
+       swdev->name = chip->name;
+       swdev->vlans = chip->vlans;
+       swdev->ports = chip->ports;
+       swdev->ops = chip->swops;
+
+       ret = ar8xxx_mib_init(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int
+ar8xxx_start(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->init = true;
+
+       ret = priv->chip->hw_init(priv);
+       if (ret)
+               return ret;
+
+       ret = ar8xxx_sw_reset_switch(&priv->dev);
+       if (ret)
+               return ret;
+
+       priv->init = false;
+
+       ar8xxx_mib_start(priv);
+
+       return 0;
+}
+
+static int
+ar8xxx_phy_config_init(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+       struct net_device *dev = phydev->attached_dev;
+       int ret;
+
+       if (WARN_ON(!priv))
+               return -ENODEV;
+
+       if (priv->chip->config_at_probe)
+               return ar8xxx_phy_check_aneg(phydev);
+
+       priv->phy = phydev;
+
+       if (phydev->mdio.addr != 0) {
+               if (chip_is_ar8316(priv)) {
+                       /* switch device has been initialized, reinit */
+                       priv->dev.ports = (AR8216_NUM_PORTS - 1);
+                       priv->initialized = false;
+                       priv->port4_phy = true;
+                       ar8316_hw_init(priv);
+                       return 0;
+               }
+
+               return 0;
+       }
+
+       ret = ar8xxx_start(priv);
+       if (ret)
+               return ret;
+
+       /* VID fixup only needed on ar8216 */
+       if (chip_is_ar8216(priv)) {
+               dev->phy_ptr = priv;
+               dev->priv_flags |= IFF_NO_IP_ALIGN;
+               dev->eth_mangle_rx = ar8216_mangle_rx;
+               dev->eth_mangle_tx = ar8216_mangle_tx;
+       }
+
+       return 0;
+}
+
+static bool
+ar8xxx_check_link_states(struct ar8xxx_priv *priv)
+{
+       bool link_new, changed = false;
+       u32 status;
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       for (i = 0; i < priv->dev.ports; i++) {
+               status = priv->chip->read_port_status(priv, i);
+               link_new = !!(status & AR8216_PORT_STATUS_LINK_UP);
+               if (link_new == priv->link_up[i])
+                       continue;
+
+               priv->link_up[i] = link_new;
+               changed = true;
+               /* flush ARL entries for this port if it went down*/
+               if (!link_new)
+                       priv->chip->atu_flush_port(priv, i);
+               dev_info(&priv->phy->mdio.dev, "Port %d is %s\n",
+                        i, link_new ? "up" : "down");
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return changed;
+}
+
+static int
+ar8xxx_phy_read_status(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+       struct switch_port_link link;
+
+       /* check for switch port link changes */
+       if (phydev->state == PHY_CHANGELINK)
+               ar8xxx_check_link_states(priv);
+
+       if (phydev->mdio.addr != 0)
+               return genphy_read_status(phydev);
+
+       ar8216_read_port_link(priv, phydev->mdio.addr, &link);
+       phydev->link = !!link.link;
+       if (!phydev->link)
+               return 0;
+
+       switch (link.speed) {
+       case SWITCH_PORT_SPEED_10:
+               phydev->speed = SPEED_10;
+               break;
+       case SWITCH_PORT_SPEED_100:
+               phydev->speed = SPEED_100;
+               break;
+       case SWITCH_PORT_SPEED_1000:
+               phydev->speed = SPEED_1000;
+               break;
+       default:
+               phydev->speed = 0;
+       }
+       phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+static int
+ar8xxx_phy_config_aneg(struct phy_device *phydev)
+{
+       if (phydev->mdio.addr == 0)
+               return 0;
+
+       return genphy_config_aneg(phydev);
+}
+
+static const u32 ar8xxx_phy_ids[] = {
+       0x004dd033,
+       0x004dd034, /* AR8327 */
+       0x004dd036, /* AR8337 */
+       0x004dd041,
+       0x004dd042,
+       0x004dd043, /* AR8236 */
+};
+
+static bool
+ar8xxx_phy_match(u32 phy_id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++)
+               if (phy_id == ar8xxx_phy_ids[i])
+                       return true;
+
+       return false;
+}
+
+static bool
+ar8xxx_is_possible(struct mii_bus *bus)
+{
+       unsigned int i, found_phys = 0;
+
+       for (i = 0; i < 5; i++) {
+               u32 phy_id;
+
+               phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16;
+               phy_id |= mdiobus_read(bus, i, MII_PHYSID2);
+               if (ar8xxx_phy_match(phy_id)) {
+                       found_phys++;
+               } else if (phy_id) {
+                       pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n",
+                                dev_name(&bus->dev), i, phy_id);
+               }
+       }
+       return !!found_phys;
+}
+
+static int
+ar8xxx_phy_probe(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv;
+       struct switch_dev *swdev;
+       int ret;
+
+       /* skip PHYs at unused adresses */
+       if (phydev->mdio.addr != 0 && phydev->mdio.addr != 4)
+               return -ENODEV;
+
+       if (!ar8xxx_is_possible(phydev->mdio.bus))
+               return -ENODEV;
+
+       mutex_lock(&ar8xxx_dev_list_lock);
+       list_for_each_entry(priv, &ar8xxx_dev_list, list)
+               if (priv->mii_bus == phydev->mdio.bus)
+                       goto found;
+
+       priv = ar8xxx_create();
+       if (priv == NULL) {
+               ret = -ENOMEM;
+               goto unlock;
+       }
+
+       priv->mii_bus = phydev->mdio.bus;
+
+       ret = ar8xxx_probe_switch(priv);
+       if (ret)
+               goto free_priv;
+
+       swdev = &priv->dev;
+       swdev->alias = dev_name(&priv->mii_bus->dev);
+       ret = register_switch(swdev, NULL);
+       if (ret)
+               goto free_priv;
+
+       pr_info("%s: %s rev. %u switch registered on %s\n",
+               swdev->devname, swdev->name, priv->chip_rev,
+               dev_name(&priv->mii_bus->dev));
+
+       list_add(&priv->list, &ar8xxx_dev_list);
+
+found:
+       priv->use_count++;
+
+       if (phydev->mdio.addr == 0) {
+               if (ar8xxx_has_gige(priv)) {
+                       phydev->supported = SUPPORTED_1000baseT_Full;
+                       phydev->advertising = ADVERTISED_1000baseT_Full;
+               } else {
+                       phydev->supported = SUPPORTED_100baseT_Full;
+                       phydev->advertising = ADVERTISED_100baseT_Full;
+               }
+
+               if (priv->chip->config_at_probe) {
+                       priv->phy = phydev;
+
+                       ret = ar8xxx_start(priv);
+                       if (ret)
+                               goto err_unregister_switch;
+               }
+       } else {
+               if (ar8xxx_has_gige(priv)) {
+                       phydev->supported |= SUPPORTED_1000baseT_Full;
+                       phydev->advertising |= ADVERTISED_1000baseT_Full;
+               }
+       }
+
+       phydev->priv = priv;
+
+       mutex_unlock(&ar8xxx_dev_list_lock);
+
+       return 0;
+
+err_unregister_switch:
+       if (--priv->use_count)
+               goto unlock;
+
+       unregister_switch(&priv->dev);
+
+free_priv:
+       ar8xxx_free(priv);
+unlock:
+       mutex_unlock(&ar8xxx_dev_list_lock);
+       return ret;
+}
+
+static void
+ar8xxx_phy_detach(struct phy_device *phydev)
+{
+       struct net_device *dev = phydev->attached_dev;
+
+       if (!dev)
+               return;
+
+       dev->phy_ptr = NULL;
+       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+       dev->eth_mangle_rx = NULL;
+       dev->eth_mangle_tx = NULL;
+}
+
+static void
+ar8xxx_phy_remove(struct phy_device *phydev)
+{
+       struct ar8xxx_priv *priv = phydev->priv;
+
+       if (WARN_ON(!priv))
+               return;
+
+       phydev->priv = NULL;
+
+       mutex_lock(&ar8xxx_dev_list_lock);
+
+       if (--priv->use_count > 0) {
+               mutex_unlock(&ar8xxx_dev_list_lock);
+               return;
+       }
+
+       list_del(&priv->list);
+       mutex_unlock(&ar8xxx_dev_list_lock);
+
+       unregister_switch(&priv->dev);
+       ar8xxx_mib_stop(priv);
+       ar8xxx_free(priv);
+}
+
+static int
+ar8xxx_phy_soft_reset(struct phy_device *phydev)
+{
+       /* we don't need an extra reset */
+       return 0;
+}
+
+static struct phy_driver ar8xxx_phy_driver[] = {
+       {
+               .phy_id         = 0x004d0000,
+               .name           = "Atheros AR8216/AR8236/AR8316",
+               .phy_id_mask    = 0xffff0000,
+               .features       = PHY_BASIC_FEATURES,
+               .probe          = ar8xxx_phy_probe,
+               .remove         = ar8xxx_phy_remove,
+               .detach         = ar8xxx_phy_detach,
+               .config_init    = ar8xxx_phy_config_init,
+               .config_aneg    = ar8xxx_phy_config_aneg,
+               .read_status    = ar8xxx_phy_read_status,
+               .soft_reset     = ar8xxx_phy_soft_reset,
+       }
+};
+
+module_phy_driver(ar8xxx_phy_driver);
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/ar8216.h b/target/linux/generic/files-4.9/drivers/net/phy/ar8216.h
new file mode 100644 (file)
index 0000000..ba0e0dd
--- /dev/null
@@ -0,0 +1,644 @@
+/*
+ * ar8216.h: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AR8216_H
+#define __AR8216_H
+
+#define BITS(_s, _n)   (((1UL << (_n)) - 1) << _s)
+
+#define AR8XXX_CAP_GIGE                        BIT(0)
+#define AR8XXX_CAP_MIB_COUNTERS                BIT(1)
+
+#define AR8XXX_NUM_PHYS        5
+#define AR8216_PORT_CPU        0
+#define AR8216_NUM_PORTS       6
+#define AR8216_NUM_VLANS       16
+#define AR8316_NUM_VLANS       4096
+
+/* size of the vlan table */
+#define AR8X16_MAX_VLANS       128
+#define AR8X16_PROBE_RETRIES   10
+#define AR8X16_MAX_PORTS       8
+
+#define AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS      7
+#define AR8XXX_DEFAULT_ARL_AGE_TIME            300
+
+/* Atheros specific MII registers */
+#define MII_ATH_MMD_ADDR               0x0d
+#define MII_ATH_MMD_DATA               0x0e
+#define MII_ATH_DBG_ADDR               0x1d
+#define MII_ATH_DBG_DATA               0x1e
+
+#define AR8216_REG_CTRL                        0x0000
+#define   AR8216_CTRL_REVISION         BITS(0, 8)
+#define   AR8216_CTRL_REVISION_S       0
+#define   AR8216_CTRL_VERSION          BITS(8, 8)
+#define   AR8216_CTRL_VERSION_S                8
+#define   AR8216_CTRL_RESET            BIT(31)
+
+#define AR8216_REG_FLOOD_MASK          0x002C
+#define   AR8216_FM_UNI_DEST_PORTS     BITS(0, 6)
+#define   AR8216_FM_MULTI_DEST_PORTS   BITS(16, 6)
+#define   AR8236_FM_CPU_BROADCAST_EN   BIT(26)
+#define   AR8236_FM_CPU_BCAST_FWD_EN   BIT(25)
+
+#define AR8216_REG_GLOBAL_CTRL         0x0030
+#define   AR8216_GCTRL_MTU             BITS(0, 11)
+#define   AR8236_GCTRL_MTU             BITS(0, 14)
+#define   AR8316_GCTRL_MTU             BITS(0, 14)
+
+#define AR8216_REG_VTU                 0x0040
+#define   AR8216_VTU_OP                        BITS(0, 3)
+#define   AR8216_VTU_OP_NOOP           0x0
+#define   AR8216_VTU_OP_FLUSH          0x1
+#define   AR8216_VTU_OP_LOAD           0x2
+#define   AR8216_VTU_OP_PURGE          0x3
+#define   AR8216_VTU_OP_REMOVE_PORT    0x4
+#define   AR8216_VTU_ACTIVE            BIT(3)
+#define   AR8216_VTU_FULL              BIT(4)
+#define   AR8216_VTU_PORT              BITS(8, 4)
+#define   AR8216_VTU_PORT_S            8
+#define   AR8216_VTU_VID               BITS(16, 12)
+#define   AR8216_VTU_VID_S             16
+#define   AR8216_VTU_PRIO              BITS(28, 3)
+#define   AR8216_VTU_PRIO_S            28
+#define   AR8216_VTU_PRIO_EN           BIT(31)
+
+#define AR8216_REG_VTU_DATA            0x0044
+#define   AR8216_VTUDATA_MEMBER                BITS(0, 10)
+#define   AR8236_VTUDATA_MEMBER                BITS(0, 7)
+#define   AR8216_VTUDATA_VALID         BIT(11)
+
+#define AR8216_REG_ATU_FUNC0           0x0050
+#define   AR8216_ATU_OP                        BITS(0, 3)
+#define   AR8216_ATU_OP_NOOP           0x0
+#define   AR8216_ATU_OP_FLUSH          0x1
+#define   AR8216_ATU_OP_LOAD           0x2
+#define   AR8216_ATU_OP_PURGE          0x3
+#define   AR8216_ATU_OP_FLUSH_UNLOCKED 0x4
+#define   AR8216_ATU_OP_FLUSH_PORT     0x5
+#define   AR8216_ATU_OP_GET_NEXT       0x6
+#define   AR8216_ATU_ACTIVE            BIT(3)
+#define   AR8216_ATU_PORT_NUM          BITS(8, 4)
+#define   AR8216_ATU_PORT_NUM_S                8
+#define   AR8216_ATU_FULL_VIO          BIT(12)
+#define   AR8216_ATU_ADDR5             BITS(16, 8)
+#define   AR8216_ATU_ADDR5_S           16
+#define   AR8216_ATU_ADDR4             BITS(24, 8)
+#define   AR8216_ATU_ADDR4_S           24
+
+#define AR8216_REG_ATU_FUNC1           0x0054
+#define   AR8216_ATU_ADDR3             BITS(0, 8)
+#define   AR8216_ATU_ADDR3_S           0
+#define   AR8216_ATU_ADDR2             BITS(8, 8)
+#define   AR8216_ATU_ADDR2_S           8
+#define   AR8216_ATU_ADDR1             BITS(16, 8)
+#define   AR8216_ATU_ADDR1_S           16
+#define   AR8216_ATU_ADDR0             BITS(24, 8)
+#define   AR8216_ATU_ADDR0_S           24
+
+#define AR8216_REG_ATU_FUNC2           0x0058
+#define   AR8216_ATU_PORTS             BITS(0, 6)
+#define   AR8216_ATU_PORT0             BIT(0)
+#define   AR8216_ATU_PORT1             BIT(1)
+#define   AR8216_ATU_PORT2             BIT(2)
+#define   AR8216_ATU_PORT3             BIT(3)
+#define   AR8216_ATU_PORT4             BIT(4)
+#define   AR8216_ATU_PORT5             BIT(5)
+#define   AR8216_ATU_STATUS            BITS(16, 4)
+#define   AR8216_ATU_STATUS_S          16
+
+#define AR8216_REG_ATU_CTRL            0x005C
+#define   AR8216_ATU_CTRL_AGE_EN       BIT(17)
+#define   AR8216_ATU_CTRL_AGE_TIME     BITS(0, 16)
+#define   AR8216_ATU_CTRL_AGE_TIME_S   0
+#define   AR8236_ATU_CTRL_RES          BIT(20)
+
+#define AR8216_REG_MIB_FUNC            0x0080
+#define   AR8216_MIB_TIMER             BITS(0, 16)
+#define   AR8216_MIB_AT_HALF_EN                BIT(16)
+#define   AR8216_MIB_BUSY              BIT(17)
+#define   AR8216_MIB_FUNC              BITS(24, 3)
+#define   AR8216_MIB_FUNC_S            24
+#define   AR8216_MIB_FUNC_NO_OP                0x0
+#define   AR8216_MIB_FUNC_FLUSH                0x1
+#define   AR8216_MIB_FUNC_CAPTURE      0x3
+#define   AR8236_MIB_EN                        BIT(30)
+
+#define AR8216_REG_GLOBAL_CPUPORT              0x0078
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT    BITS(4, 4)
+#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S  4
+
+#define AR8216_PORT_OFFSET(_i)         (0x0100 * (_i + 1))
+#define AR8216_REG_PORT_STATUS(_i)     (AR8216_PORT_OFFSET(_i) + 0x0000)
+#define   AR8216_PORT_STATUS_SPEED     BITS(0,2)
+#define   AR8216_PORT_STATUS_SPEED_S   0
+#define   AR8216_PORT_STATUS_TXMAC     BIT(2)
+#define   AR8216_PORT_STATUS_RXMAC     BIT(3)
+#define   AR8216_PORT_STATUS_TXFLOW    BIT(4)
+#define   AR8216_PORT_STATUS_RXFLOW    BIT(5)
+#define   AR8216_PORT_STATUS_DUPLEX    BIT(6)
+#define   AR8216_PORT_STATUS_LINK_UP   BIT(8)
+#define   AR8216_PORT_STATUS_LINK_AUTO BIT(9)
+#define   AR8216_PORT_STATUS_LINK_PAUSE        BIT(10)
+#define   AR8216_PORT_STATUS_FLOW_CONTROL  BIT(12)
+
+#define AR8216_REG_PORT_CTRL(_i)       (AR8216_PORT_OFFSET(_i) + 0x0004)
+
+/* port forwarding state */
+#define   AR8216_PORT_CTRL_STATE       BITS(0, 3)
+#define   AR8216_PORT_CTRL_STATE_S     0
+
+#define   AR8216_PORT_CTRL_LEARN_LOCK  BIT(7)
+
+/* egress 802.1q mode */
+#define   AR8216_PORT_CTRL_VLAN_MODE   BITS(8, 2)
+#define   AR8216_PORT_CTRL_VLAN_MODE_S 8
+
+#define   AR8216_PORT_CTRL_IGMP_SNOOP  BIT(10)
+#define   AR8216_PORT_CTRL_HEADER      BIT(11)
+#define   AR8216_PORT_CTRL_MAC_LOOP    BIT(12)
+#define   AR8216_PORT_CTRL_SINGLE_VLAN BIT(13)
+#define   AR8216_PORT_CTRL_LEARN       BIT(14)
+#define   AR8216_PORT_CTRL_MIRROR_TX   BIT(16)
+#define   AR8216_PORT_CTRL_MIRROR_RX   BIT(17)
+
+#define AR8216_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET(_i) + 0x0008)
+
+#define   AR8216_PORT_VLAN_DEFAULT_ID  BITS(0, 12)
+#define   AR8216_PORT_VLAN_DEFAULT_ID_S        0
+
+#define   AR8216_PORT_VLAN_DEST_PORTS  BITS(16, 9)
+#define   AR8216_PORT_VLAN_DEST_PORTS_S        16
+
+/* bit0 added to the priority field of egress frames */
+#define   AR8216_PORT_VLAN_TX_PRIO     BIT(27)
+
+/* port default priority */
+#define   AR8216_PORT_VLAN_PRIORITY    BITS(28, 2)
+#define   AR8216_PORT_VLAN_PRIORITY_S  28
+
+/* ingress 802.1q mode */
+#define   AR8216_PORT_VLAN_MODE                BITS(30, 2)
+#define   AR8216_PORT_VLAN_MODE_S      30
+
+#define AR8216_REG_PORT_RATE(_i)       (AR8216_PORT_OFFSET(_i) + 0x000c)
+#define AR8216_REG_PORT_PRIO(_i)       (AR8216_PORT_OFFSET(_i) + 0x0010)
+
+#define AR8216_STATS_RXBROAD           0x00
+#define AR8216_STATS_RXPAUSE           0x04
+#define AR8216_STATS_RXMULTI           0x08
+#define AR8216_STATS_RXFCSERR          0x0c
+#define AR8216_STATS_RXALIGNERR                0x10
+#define AR8216_STATS_RXRUNT            0x14
+#define AR8216_STATS_RXFRAGMENT                0x18
+#define AR8216_STATS_RX64BYTE          0x1c
+#define AR8216_STATS_RX128BYTE         0x20
+#define AR8216_STATS_RX256BYTE         0x24
+#define AR8216_STATS_RX512BYTE         0x28
+#define AR8216_STATS_RX1024BYTE                0x2c
+#define AR8216_STATS_RXMAXBYTE         0x30
+#define AR8216_STATS_RXTOOLONG         0x34
+#define AR8216_STATS_RXGOODBYTE                0x38
+#define AR8216_STATS_RXBADBYTE         0x40
+#define AR8216_STATS_RXOVERFLOW                0x48
+#define AR8216_STATS_FILTERED          0x4c
+#define AR8216_STATS_TXBROAD           0x50
+#define AR8216_STATS_TXPAUSE           0x54
+#define AR8216_STATS_TXMULTI           0x58
+#define AR8216_STATS_TXUNDERRUN                0x5c
+#define AR8216_STATS_TX64BYTE          0x60
+#define AR8216_STATS_TX128BYTE         0x64
+#define AR8216_STATS_TX256BYTE         0x68
+#define AR8216_STATS_TX512BYTE         0x6c
+#define AR8216_STATS_TX1024BYTE                0x70
+#define AR8216_STATS_TXMAXBYTE         0x74
+#define AR8216_STATS_TXOVERSIZE                0x78
+#define AR8216_STATS_TXBYTE            0x7c
+#define AR8216_STATS_TXCOLLISION       0x84
+#define AR8216_STATS_TXABORTCOL                0x88
+#define AR8216_STATS_TXMULTICOL                0x8c
+#define AR8216_STATS_TXSINGLECOL       0x90
+#define AR8216_STATS_TXEXCDEFER                0x94
+#define AR8216_STATS_TXDEFER           0x98
+#define AR8216_STATS_TXLATECOL         0x9c
+
+#define AR8236_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET((_i)) + 0x0008)
+#define   AR8236_PORT_VLAN_DEFAULT_ID  BITS(16, 12)
+#define   AR8236_PORT_VLAN_DEFAULT_ID_S        16
+#define   AR8236_PORT_VLAN_PRIORITY    BITS(29, 3)
+#define   AR8236_PORT_VLAN_PRIORITY_S  28
+
+#define AR8236_REG_PORT_VLAN2(_i)      (AR8216_PORT_OFFSET((_i)) + 0x000c)
+#define   AR8236_PORT_VLAN2_MEMBER     BITS(16, 7)
+#define   AR8236_PORT_VLAN2_MEMBER_S   16
+#define   AR8236_PORT_VLAN2_TX_PRIO    BIT(23)
+#define   AR8236_PORT_VLAN2_VLAN_MODE  BITS(30, 2)
+#define   AR8236_PORT_VLAN2_VLAN_MODE_S        30
+
+#define AR8236_STATS_RXBROAD           0x00
+#define AR8236_STATS_RXPAUSE           0x04
+#define AR8236_STATS_RXMULTI           0x08
+#define AR8236_STATS_RXFCSERR          0x0c
+#define AR8236_STATS_RXALIGNERR                0x10
+#define AR8236_STATS_RXRUNT            0x14
+#define AR8236_STATS_RXFRAGMENT                0x18
+#define AR8236_STATS_RX64BYTE          0x1c
+#define AR8236_STATS_RX128BYTE         0x20
+#define AR8236_STATS_RX256BYTE         0x24
+#define AR8236_STATS_RX512BYTE         0x28
+#define AR8236_STATS_RX1024BYTE                0x2c
+#define AR8236_STATS_RX1518BYTE                0x30
+#define AR8236_STATS_RXMAXBYTE         0x34
+#define AR8236_STATS_RXTOOLONG         0x38
+#define AR8236_STATS_RXGOODBYTE                0x3c
+#define AR8236_STATS_RXBADBYTE         0x44
+#define AR8236_STATS_RXOVERFLOW                0x4c
+#define AR8236_STATS_FILTERED          0x50
+#define AR8236_STATS_TXBROAD           0x54
+#define AR8236_STATS_TXPAUSE           0x58
+#define AR8236_STATS_TXMULTI           0x5c
+#define AR8236_STATS_TXUNDERRUN                0x60
+#define AR8236_STATS_TX64BYTE          0x64
+#define AR8236_STATS_TX128BYTE         0x68
+#define AR8236_STATS_TX256BYTE         0x6c
+#define AR8236_STATS_TX512BYTE         0x70
+#define AR8236_STATS_TX1024BYTE                0x74
+#define AR8236_STATS_TX1518BYTE                0x78
+#define AR8236_STATS_TXMAXBYTE         0x7c
+#define AR8236_STATS_TXOVERSIZE                0x80
+#define AR8236_STATS_TXBYTE            0x84
+#define AR8236_STATS_TXCOLLISION       0x8c
+#define AR8236_STATS_TXABORTCOL                0x90
+#define AR8236_STATS_TXMULTICOL                0x94
+#define AR8236_STATS_TXSINGLECOL       0x98
+#define AR8236_STATS_TXEXCDEFER                0x9c
+#define AR8236_STATS_TXDEFER           0xa0
+#define AR8236_STATS_TXLATECOL         0xa4
+
+#define AR8316_REG_POSTRIP                     0x0008
+#define   AR8316_POSTRIP_MAC0_GMII_EN          BIT(0)
+#define   AR8316_POSTRIP_MAC0_RGMII_EN         BIT(1)
+#define   AR8316_POSTRIP_PHY4_GMII_EN          BIT(2)
+#define   AR8316_POSTRIP_PHY4_RGMII_EN         BIT(3)
+#define   AR8316_POSTRIP_MAC0_MAC_MODE         BIT(4)
+#define   AR8316_POSTRIP_RTL_MODE              BIT(5)
+#define   AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN  BIT(6)
+#define   AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN  BIT(7)
+#define   AR8316_POSTRIP_SERDES_EN             BIT(8)
+#define   AR8316_POSTRIP_SEL_ANA_RST           BIT(9)
+#define   AR8316_POSTRIP_GATE_25M_EN           BIT(10)
+#define   AR8316_POSTRIP_SEL_CLK25M            BIT(11)
+#define   AR8316_POSTRIP_HIB_PULSE_HW          BIT(12)
+#define   AR8316_POSTRIP_DBG_MODE_I            BIT(13)
+#define   AR8316_POSTRIP_MAC5_MAC_MODE         BIT(14)
+#define   AR8316_POSTRIP_MAC5_PHY_MODE         BIT(15)
+#define   AR8316_POSTRIP_POWER_DOWN_HW         BIT(16)
+#define   AR8316_POSTRIP_LPW_STATE_EN          BIT(17)
+#define   AR8316_POSTRIP_MAN_EN                        BIT(18)
+#define   AR8316_POSTRIP_PHY_PLL_ON            BIT(19)
+#define   AR8316_POSTRIP_LPW_EXIT              BIT(20)
+#define   AR8316_POSTRIP_TXDELAY_S0            BIT(21)
+#define   AR8316_POSTRIP_TXDELAY_S1            BIT(22)
+#define   AR8316_POSTRIP_RXDELAY_S0            BIT(23)
+#define   AR8316_POSTRIP_LED_OPEN_EN           BIT(24)
+#define   AR8316_POSTRIP_SPI_EN                        BIT(25)
+#define   AR8316_POSTRIP_RXDELAY_S1            BIT(26)
+#define   AR8316_POSTRIP_POWER_ON_SEL          BIT(31)
+
+/* port speed */
+enum {
+        AR8216_PORT_SPEED_10M = 0,
+        AR8216_PORT_SPEED_100M = 1,
+        AR8216_PORT_SPEED_1000M = 2,
+        AR8216_PORT_SPEED_ERR = 3,
+};
+
+/* ingress 802.1q mode */
+enum {
+       AR8216_IN_PORT_ONLY = 0,
+       AR8216_IN_PORT_FALLBACK = 1,
+       AR8216_IN_VLAN_ONLY = 2,
+       AR8216_IN_SECURE = 3
+};
+
+/* egress 802.1q mode */
+enum {
+       AR8216_OUT_KEEP = 0,
+       AR8216_OUT_STRIP_VLAN = 1,
+       AR8216_OUT_ADD_VLAN = 2
+};
+
+/* port forwarding state */
+enum {
+       AR8216_PORT_STATE_DISABLED = 0,
+       AR8216_PORT_STATE_BLOCK = 1,
+       AR8216_PORT_STATE_LISTEN = 2,
+       AR8216_PORT_STATE_LEARN = 3,
+       AR8216_PORT_STATE_FORWARD = 4
+};
+
+enum {
+       AR8XXX_VER_AR8216 = 0x01,
+       AR8XXX_VER_AR8236 = 0x03,
+       AR8XXX_VER_AR8316 = 0x10,
+       AR8XXX_VER_AR8327 = 0x12,
+       AR8XXX_VER_AR8337 = 0x13,
+};
+
+#define AR8XXX_NUM_ARL_RECORDS 100
+
+enum arl_op {
+       AR8XXX_ARL_INITIALIZE,
+       AR8XXX_ARL_GET_NEXT
+};
+
+struct arl_entry {
+       u8 port;
+       u8 mac[6];
+};
+
+struct ar8xxx_priv;
+
+struct ar8xxx_mib_desc {
+       unsigned int size;
+       unsigned int offset;
+       const char *name;
+};
+
+struct ar8xxx_chip {
+       unsigned long caps;
+       bool config_at_probe;
+       bool mii_lo_first;
+
+       /* parameters to calculate REG_PORT_STATS_BASE */
+       unsigned reg_port_stats_start;
+       unsigned reg_port_stats_length;
+
+       unsigned reg_arl_ctrl;
+
+       int (*hw_init)(struct ar8xxx_priv *priv);
+       void (*cleanup)(struct ar8xxx_priv *priv);
+
+       const char *name;
+       int vlans;
+       int ports;
+       const struct switch_dev_ops *swops;
+
+       void (*init_globals)(struct ar8xxx_priv *priv);
+       void (*init_port)(struct ar8xxx_priv *priv, int port);
+       void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
+       u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
+       u32 (*read_port_eee_status)(struct ar8xxx_priv *priv, int port);
+       int (*atu_flush)(struct ar8xxx_priv *priv);
+       int (*atu_flush_port)(struct ar8xxx_priv *priv, int port);
+       void (*vtu_flush)(struct ar8xxx_priv *priv);
+       void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
+       void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
+       void (*set_mirror_regs)(struct ar8xxx_priv *priv);
+       void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
+                             u32 *status, enum arl_op op);
+       int (*sw_hw_apply)(struct switch_dev *dev);
+
+       const struct ar8xxx_mib_desc *mib_decs;
+       unsigned num_mibs;
+       unsigned mib_func;
+};
+
+struct ar8xxx_priv {
+       struct switch_dev dev;
+       struct mii_bus *mii_bus;
+       struct phy_device *phy;
+
+       int (*get_port_link)(unsigned port);
+
+       const struct net_device_ops *ndo_old;
+       struct net_device_ops ndo;
+       struct mutex reg_mutex;
+       u8 chip_ver;
+       u8 chip_rev;
+       const struct ar8xxx_chip *chip;
+       void *chip_data;
+       bool initialized;
+       bool port4_phy;
+       char buf[2048];
+       struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
+       char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
+       bool link_up[AR8X16_MAX_PORTS];
+
+       bool init;
+
+       struct mutex mib_lock;
+       struct delayed_work mib_work;
+       int mib_next_port;
+       u64 *mib_stats;
+
+       struct list_head list;
+       unsigned int use_count;
+
+       /* all fields below are cleared on reset */
+       bool vlan;
+       u16 vlan_id[AR8X16_MAX_VLANS];
+       u8 vlan_table[AR8X16_MAX_VLANS];
+       u8 vlan_tagged;
+       u16 pvid[AR8X16_MAX_PORTS];
+       int arl_age_time;
+
+       /* mirroring */
+       bool mirror_rx;
+       bool mirror_tx;
+       int source_port;
+       int monitor_port;
+       u8 port_vlan_prio[AR8X16_MAX_PORTS];
+};
+
+u32
+ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
+void
+ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
+u32
+ar8xxx_read(struct ar8xxx_priv *priv, int reg);
+void
+ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val);
+u32
+ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
+
+void
+ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
+                    u16 dbg_addr, u16 dbg_data);
+void
+ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data);
+u16
+ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg);
+void
+ar8xxx_phy_init(struct ar8xxx_priv *priv);
+int
+ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val);
+int
+ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val);
+int
+ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
+                        const struct switch_attr *attr,
+                        struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val);
+int
+ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val);
+int
+ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val);
+int
+ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan);
+int
+ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan);
+int
+ar8xxx_sw_hw_apply(struct switch_dev *dev);
+int
+ar8xxx_sw_reset_switch(struct switch_dev *dev);
+int
+ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link);
+int
+ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val);
+int
+ar8xxx_sw_get_port_mib(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val);
+int
+ar8xxx_sw_get_arl_age_time(struct switch_dev *dev,
+                          const struct switch_attr *attr,
+                          struct switch_val *val);
+int
+ar8xxx_sw_set_arl_age_time(struct switch_dev *dev,
+                          const struct switch_attr *attr,
+                          struct switch_val *val);
+int
+ar8xxx_sw_get_arl_table(struct switch_dev *dev,
+                       const struct switch_attr *attr,
+                       struct switch_val *val);
+int
+ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val);
+int
+ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
+                                  const struct switch_attr *attr,
+                                  struct switch_val *val);
+int
+ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
+
+static inline struct ar8xxx_priv *
+swdev_to_ar8xxx(struct switch_dev *swdev)
+{
+       return container_of(swdev, struct ar8xxx_priv, dev);
+}
+
+static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv)
+{
+       return priv->chip->caps & AR8XXX_CAP_GIGE;
+}
+
+static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv)
+{
+       return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS;
+}
+
+static inline bool chip_is_ar8216(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8216;
+}
+
+static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8236;
+}
+
+static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8316;
+}
+
+static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8327;
+}
+
+static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
+{
+       return priv->chip_ver == AR8XXX_VER_AR8337;
+}
+
+static inline void
+ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       ar8xxx_rmw(priv, reg, 0, val);
+}
+
+static inline void
+ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
+{
+       ar8xxx_rmw(priv, reg, val, 0);
+}
+
+static inline void
+split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
+{
+       regaddr >>= 1;
+       *r1 = regaddr & 0x1e;
+
+       regaddr >>= 5;
+       *r2 = regaddr & 0x7;
+
+       regaddr >>= 3;
+       *page = regaddr & 0x1ff;
+}
+
+static inline void
+wait_for_page_switch(void)
+{
+       udelay(5);
+}
+
+#endif
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/ar8327.c b/target/linux/generic/files-4.9/drivers/net/phy/ar8327.c
new file mode 100644 (file)
index 0000000..74f0a08
--- /dev/null
@@ -0,0 +1,1503 @@
+/*
+ * ar8327.c: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/lockdep.h>
+#include <linux/ar8216_platform.h>
+#include <linux/workqueue.h>
+#include <linux/of_device.h>
+#include <linux/leds.h>
+#include <linux/mdio.h>
+
+#include "ar8216.h"
+#include "ar8327.h"
+
+extern const struct ar8xxx_mib_desc ar8236_mibs[39];
+extern const struct switch_attr ar8xxx_sw_attr_vlan[1];
+
+static u32
+ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
+{
+       u32 t;
+
+       if (!cfg)
+               return 0;
+
+       t = 0;
+       switch (cfg->mode) {
+       case AR8327_PAD_NC:
+               break;
+
+       case AR8327_PAD_MAC2MAC_MII:
+               t = AR8327_PAD_MAC_MII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC2MAC_GMII:
+               t = AR8327_PAD_MAC_GMII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC_SGMII:
+               t = AR8327_PAD_SGMII_EN;
+
+               /*
+                * WAR for the QUalcomm Atheros AP136 board.
+                * It seems that RGMII TX/RX delay settings needs to be
+                * applied for SGMII mode as well, The ethernet is not
+                * reliable without this.
+                */
+               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+               if (cfg->rxclk_delay_en)
+                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+               if (cfg->txclk_delay_en)
+                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+
+               if (cfg->sgmii_delay_en)
+                       t |= AR8327_PAD_SGMII_DELAY_EN;
+
+               break;
+
+       case AR8327_PAD_MAC2PHY_MII:
+               t = AR8327_PAD_PHY_MII_EN;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC2PHY_GMII:
+               t = AR8327_PAD_PHY_GMII_EN;
+               if (cfg->pipe_rxclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
+               if (cfg->rxclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
+               if (cfg->txclk_sel)
+                       t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
+               break;
+
+       case AR8327_PAD_MAC_RGMII:
+               t = AR8327_PAD_RGMII_EN;
+               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
+               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
+               if (cfg->rxclk_delay_en)
+                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
+               if (cfg->txclk_delay_en)
+                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
+               break;
+
+       case AR8327_PAD_PHY_GMII:
+               t = AR8327_PAD_PHYX_GMII_EN;
+               break;
+
+       case AR8327_PAD_PHY_RGMII:
+               t = AR8327_PAD_PHYX_RGMII_EN;
+               break;
+
+       case AR8327_PAD_PHY_MII:
+               t = AR8327_PAD_PHYX_MII_EN;
+               break;
+       }
+
+       return t;
+}
+
+static void
+ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy)
+{
+       switch (priv->chip_rev) {
+       case 1:
+               /* For 100M waveform */
+               ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea);
+               /* Turn on Gigabit clock */
+               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
+               break;
+
+       case 2:
+               ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0);
+               /* fallthrough */
+       case 4:
+               ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f);
+               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860);
+               ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46);
+               ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000);
+               break;
+       }
+}
+
+static u32
+ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
+{
+       u32 t;
+
+       if (!cfg->force_link)
+               return AR8216_PORT_STATUS_LINK_AUTO;
+
+       t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC;
+       t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0;
+       t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0;
+       t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0;
+
+       switch (cfg->speed) {
+       case AR8327_PORT_SPEED_10:
+               t |= AR8216_PORT_SPEED_10M;
+               break;
+       case AR8327_PORT_SPEED_100:
+               t |= AR8216_PORT_SPEED_100M;
+               break;
+       case AR8327_PORT_SPEED_1000:
+               t |= AR8216_PORT_SPEED_1000M;
+               break;
+       }
+
+       return t;
+}
+
+#define AR8327_LED_ENTRY(_num, _reg, _shift) \
+       [_num] = { .reg = (_reg), .shift = (_shift) }
+
+static const struct ar8327_led_entry
+ar8327_led_map[AR8327_NUM_LEDS] = {
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_0, 0, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_1, 1, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY0_2, 2, 14),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_0, 3, 8),
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_1, 3, 10),
+       AR8327_LED_ENTRY(AR8327_LED_PHY1_2, 3, 12),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_0, 3, 14),
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_1, 3, 16),
+       AR8327_LED_ENTRY(AR8327_LED_PHY2_2, 3, 18),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_0, 3, 20),
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_1, 3, 22),
+       AR8327_LED_ENTRY(AR8327_LED_PHY3_2, 3, 24),
+
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_0, 0, 30),
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_1, 1, 30),
+       AR8327_LED_ENTRY(AR8327_LED_PHY4_2, 2, 30),
+};
+
+static void
+ar8327_set_led_pattern(struct ar8xxx_priv *priv, unsigned int led_num,
+                      enum ar8327_led_pattern pattern)
+{
+       const struct ar8327_led_entry *entry;
+
+       entry = &ar8327_led_map[led_num];
+       ar8xxx_rmw(priv, AR8327_REG_LED_CTRL(entry->reg),
+                  (3 << entry->shift), pattern << entry->shift);
+}
+
+static void
+ar8327_led_work_func(struct work_struct *work)
+{
+       struct ar8327_led *aled;
+       u8 pattern;
+
+       aled = container_of(work, struct ar8327_led, led_work);
+
+       pattern = aled->pattern;
+
+       ar8327_set_led_pattern(aled->sw_priv, aled->led_num,
+                              pattern);
+}
+
+static void
+ar8327_led_schedule_change(struct ar8327_led *aled, u8 pattern)
+{
+       if (aled->pattern == pattern)
+               return;
+
+       aled->pattern = pattern;
+       schedule_work(&aled->led_work);
+}
+
+static inline struct ar8327_led *
+led_cdev_to_ar8327_led(struct led_classdev *led_cdev)
+{
+       return container_of(led_cdev, struct ar8327_led, cdev);
+}
+
+static int
+ar8327_led_blink_set(struct led_classdev *led_cdev,
+                    unsigned long *delay_on,
+                    unsigned long *delay_off)
+{
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+
+       if (*delay_on == 0 && *delay_off == 0) {
+               *delay_on = 125;
+               *delay_off = 125;
+       }
+
+       if (*delay_on != 125 || *delay_off != 125) {
+               /*
+                * The hardware only supports blinking at 4Hz. Fall back
+                * to software implementation in other cases.
+                */
+               return -EINVAL;
+       }
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = false;
+       ar8327_led_schedule_change(aled, AR8327_LED_PATTERN_BLINK);
+
+       spin_unlock(&aled->lock);
+
+       return 0;
+}
+
+static void
+ar8327_led_set_brightness(struct led_classdev *led_cdev,
+                         enum led_brightness brightness)
+{
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       u8 pattern;
+       bool active;
+
+       active = (brightness != LED_OFF);
+       active ^= aled->active_low;
+
+       pattern = (active) ? AR8327_LED_PATTERN_ON :
+                            AR8327_LED_PATTERN_OFF;
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = false;
+       ar8327_led_schedule_change(aled, pattern);
+
+       spin_unlock(&aled->lock);
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_show(struct device *dev,
+                              struct device_attribute *attr,
+                              char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       ssize_t ret = 0;
+
+       ret += scnprintf(buf, PAGE_SIZE, "%d\n", aled->enable_hw_mode);
+
+       return ret;
+}
+
+static ssize_t
+ar8327_led_enable_hw_mode_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf,
+                               size_t size)
+{
+        struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
+       u8 pattern;
+       u8 value;
+       int ret;
+
+       ret = kstrtou8(buf, 10, &value);
+       if (ret < 0)
+               return -EINVAL;
+
+       spin_lock(&aled->lock);
+
+       aled->enable_hw_mode = !!value;
+       if (aled->enable_hw_mode)
+               pattern = AR8327_LED_PATTERN_RULE;
+       else
+               pattern = AR8327_LED_PATTERN_OFF;
+
+       ar8327_led_schedule_change(aled, pattern);
+
+       spin_unlock(&aled->lock);
+
+       return size;
+}
+
+static DEVICE_ATTR(enable_hw_mode,  S_IRUGO | S_IWUSR,
+                  ar8327_led_enable_hw_mode_show,
+                  ar8327_led_enable_hw_mode_store);
+
+static int
+ar8327_led_register(struct ar8327_led *aled)
+{
+       int ret;
+
+       ret = led_classdev_register(NULL, &aled->cdev);
+       if (ret < 0)
+               return ret;
+
+       if (aled->mode == AR8327_LED_MODE_HW) {
+               ret = device_create_file(aled->cdev.dev,
+                                        &dev_attr_enable_hw_mode);
+               if (ret)
+                       goto err_unregister;
+       }
+
+       return 0;
+
+err_unregister:
+       led_classdev_unregister(&aled->cdev);
+       return ret;
+}
+
+static void
+ar8327_led_unregister(struct ar8327_led *aled)
+{
+       if (aled->mode == AR8327_LED_MODE_HW)
+               device_remove_file(aled->cdev.dev, &dev_attr_enable_hw_mode);
+
+       led_classdev_unregister(&aled->cdev);
+       cancel_work_sync(&aled->led_work);
+}
+
+static int
+ar8327_led_create(struct ar8xxx_priv *priv,
+                 const struct ar8327_led_info *led_info)
+{
+       struct ar8327_data *data = priv->chip_data;
+       struct ar8327_led *aled;
+       int ret;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return 0;
+
+       if (!led_info->name)
+               return -EINVAL;
+
+       if (led_info->led_num >= AR8327_NUM_LEDS)
+               return -EINVAL;
+
+       aled = kzalloc(sizeof(*aled) + strlen(led_info->name) + 1,
+                      GFP_KERNEL);
+       if (!aled)
+               return -ENOMEM;
+
+       aled->sw_priv = priv;
+       aled->led_num = led_info->led_num;
+       aled->active_low = led_info->active_low;
+       aled->mode = led_info->mode;
+
+       if (aled->mode == AR8327_LED_MODE_HW)
+               aled->enable_hw_mode = true;
+
+       aled->name = (char *)(aled + 1);
+       strcpy(aled->name, led_info->name);
+
+       aled->cdev.name = aled->name;
+       aled->cdev.brightness_set = ar8327_led_set_brightness;
+       aled->cdev.blink_set = ar8327_led_blink_set;
+       aled->cdev.default_trigger = led_info->default_trigger;
+
+       spin_lock_init(&aled->lock);
+       mutex_init(&aled->mutex);
+       INIT_WORK(&aled->led_work, ar8327_led_work_func);
+
+       ret = ar8327_led_register(aled);
+       if (ret)
+               goto err_free;
+
+       data->leds[data->num_leds++] = aled;
+
+       return 0;
+
+err_free:
+       kfree(aled);
+       return ret;
+}
+
+static void
+ar8327_led_destroy(struct ar8327_led *aled)
+{
+       ar8327_led_unregister(aled);
+       kfree(aled);
+}
+
+static void
+ar8327_leds_init(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       unsigned i;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return;
+
+       for (i = 0; i < data->num_leds; i++) {
+               struct ar8327_led *aled;
+
+               aled = data->leds[i];
+
+               if (aled->enable_hw_mode)
+                       aled->pattern = AR8327_LED_PATTERN_RULE;
+               else
+                       aled->pattern = AR8327_LED_PATTERN_OFF;
+
+               ar8327_set_led_pattern(priv, aled->led_num, aled->pattern);
+       }
+}
+
+static void
+ar8327_leds_cleanup(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       unsigned i;
+
+       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
+               return;
+
+       for (i = 0; i < data->num_leds; i++) {
+               struct ar8327_led *aled;
+
+               aled = data->leds[i];
+               ar8327_led_destroy(aled);
+       }
+
+       kfree(data->leds);
+}
+
+static int
+ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
+                      struct ar8327_platform_data *pdata)
+{
+       struct ar8327_led_cfg *led_cfg;
+       struct ar8327_data *data = priv->chip_data;
+       u32 pos, new_pos;
+       u32 t;
+
+       if (!pdata)
+               return -EINVAL;
+
+       priv->get_port_link = pdata->get_port_link;
+
+       data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg);
+       data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
+
+       t = ar8327_get_pad_cfg(pdata->pad0_cfg);
+       if (chip_is_ar8337(priv) && !pdata->pad0_cfg->mac06_exchange_dis)
+           t |= AR8337_PAD_MAC06_EXCHANGE_EN;
+       ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t);
+
+       t = ar8327_get_pad_cfg(pdata->pad5_cfg);
+       ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t);
+       t = ar8327_get_pad_cfg(pdata->pad6_cfg);
+       ar8xxx_write(priv, AR8327_REG_PAD6_MODE, t);
+
+       pos = ar8xxx_read(priv, AR8327_REG_POWER_ON_STRIP);
+       new_pos = pos;
+
+       led_cfg = pdata->led_cfg;
+       if (led_cfg) {
+               if (led_cfg->open_drain)
+                       new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+               else
+                       new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
+
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2);
+               ar8xxx_write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3);
+
+               if (new_pos != pos)
+                       new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
+       }
+
+       if (pdata->sgmii_cfg) {
+               t = pdata->sgmii_cfg->sgmii_ctrl;
+               if (priv->chip_rev == 1)
+                       t |= AR8327_SGMII_CTRL_EN_PLL |
+                            AR8327_SGMII_CTRL_EN_RX |
+                            AR8327_SGMII_CTRL_EN_TX;
+               else
+                       t &= ~(AR8327_SGMII_CTRL_EN_PLL |
+                              AR8327_SGMII_CTRL_EN_RX |
+                              AR8327_SGMII_CTRL_EN_TX);
+
+               ar8xxx_write(priv, AR8327_REG_SGMII_CTRL, t);
+
+               if (pdata->sgmii_cfg->serdes_aen)
+                       new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
+               else
+                       new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
+       }
+
+       ar8xxx_write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
+
+       if (pdata->leds && pdata->num_leds) {
+               int i;
+
+               data->leds = kzalloc(pdata->num_leds * sizeof(void *),
+                                    GFP_KERNEL);
+               if (!data->leds)
+                       return -ENOMEM;
+
+               for (i = 0; i < pdata->num_leds; i++)
+                       ar8327_led_create(priv, &pdata->leds[i]);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       struct ar8327_data *data = priv->chip_data;
+       const __be32 *paddr;
+       int len;
+       int i;
+
+       paddr = of_get_property(np, "qca,ar8327-initvals", &len);
+       if (!paddr || len < (2 * sizeof(*paddr)))
+               return -EINVAL;
+
+       len /= sizeof(*paddr);
+
+       for (i = 0; i < len - 1; i += 2) {
+               u32 reg;
+               u32 val;
+
+               reg = be32_to_cpup(paddr + i);
+               val = be32_to_cpup(paddr + i + 1);
+
+               switch (reg) {
+               case AR8327_REG_PORT_STATUS(0):
+                       data->port0_status = val;
+                       break;
+               case AR8327_REG_PORT_STATUS(6):
+                       data->port6_status = val;
+                       break;
+               default:
+                       ar8xxx_write(priv, reg, val);
+                       break;
+               }
+       }
+
+       return 0;
+}
+#else
+static inline int
+ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
+{
+       return -EINVAL;
+}
+#endif
+
+static int
+ar8327_hw_init(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       priv->chip_data = kzalloc(sizeof(struct ar8327_data), GFP_KERNEL);
+       if (!priv->chip_data)
+               return -ENOMEM;
+
+       if (priv->phy->mdio.dev.of_node)
+               ret = ar8327_hw_config_of(priv, priv->phy->mdio.dev.of_node);
+       else
+               ret = ar8327_hw_config_pdata(priv,
+                                            priv->phy->mdio.dev.platform_data);
+
+       if (ret)
+               return ret;
+
+       ar8327_leds_init(priv);
+
+       ar8xxx_phy_init(priv);
+
+       return 0;
+}
+
+static void
+ar8327_cleanup(struct ar8xxx_priv *priv)
+{
+       ar8327_leds_cleanup(priv);
+}
+
+static void
+ar8327_init_globals(struct ar8xxx_priv *priv)
+{
+       struct ar8327_data *data = priv->chip_data;
+       u32 t;
+       int i;
+
+       /* enable CPU port and disable mirror port */
+       t = AR8327_FWD_CTRL0_CPU_PORT_EN |
+           AR8327_FWD_CTRL0_MIRROR_PORT;
+       ar8xxx_write(priv, AR8327_REG_FWD_CTRL0, t);
+
+       /* forward multicast and broadcast frames to CPU */
+       t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
+           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
+           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
+       ar8xxx_write(priv, AR8327_REG_FWD_CTRL1, t);
+
+       /* enable jumbo frames */
+       ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
+                  AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
+
+       /* Enable MIB counters */
+       ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN,
+                      AR8327_MODULE_EN_MIB);
+
+       /* Disable EEE on all phy's due to stability issues */
+       for (i = 0; i < AR8XXX_NUM_PHYS; i++)
+               data->eee[i] = false;
+}
+
+static void
+ar8327_init_port(struct ar8xxx_priv *priv, int port)
+{
+       struct ar8327_data *data = priv->chip_data;
+       u32 t;
+
+       if (port == AR8216_PORT_CPU)
+               t = data->port0_status;
+       else if (port == 6)
+               t = data->port6_status;
+       else
+               t = AR8216_PORT_STATUS_LINK_AUTO;
+
+       if (port != AR8216_PORT_CPU && port != 6) {
+               /*hw limitation:if configure mac when there is traffic,
+               port MAC may work abnormal. Need disable lan&wan mac at fisrt*/
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), 0);
+               msleep(100);
+               t |= AR8216_PORT_STATUS_FLOW_CONTROL;
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
+       } else {
+               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
+       }
+
+       ar8xxx_write(priv, AR8327_REG_PORT_HEADER(port), 0);
+
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), 0);
+
+       t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+       t = AR8327_PORT_LOOKUP_LEARN;
+       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static u32
+ar8327_read_port_status(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+
+       t = ar8xxx_read(priv, AR8327_REG_PORT_STATUS(port));
+       /* map the flow control autoneg result bits to the flow control bits
+        * used in forced mode to allow ar8216_read_port_link detect
+        * flow control properly if autoneg is used
+        */
+       if (t & AR8216_PORT_STATUS_LINK_UP &&
+           t & AR8216_PORT_STATUS_LINK_AUTO) {
+               t &= ~(AR8216_PORT_STATUS_TXFLOW | AR8216_PORT_STATUS_RXFLOW);
+               if (t & AR8327_PORT_STATUS_TXFLOW_AUTO)
+                       t |= AR8216_PORT_STATUS_TXFLOW;
+               if (t & AR8327_PORT_STATUS_RXFLOW_AUTO)
+                       t |= AR8216_PORT_STATUS_RXFLOW;
+       }
+
+       return t;
+}
+
+static u32
+ar8327_read_port_eee_status(struct ar8xxx_priv *priv, int port)
+{
+       int phy;
+       u16 t;
+
+       if (port >= priv->dev.ports)
+               return 0;
+
+       if (port == 0 || port == 6)
+               return 0;
+
+       phy = port - 1;
+
+       /* EEE Ability Auto-negotiation Result */
+       t = ar8xxx_phy_mmd_read(priv, phy, 0x7, 0x8000);
+
+       return mmd_eee_adv_to_ethtool_adv_t(t);
+}
+
+static int
+ar8327_atu_flush(struct ar8xxx_priv *priv)
+{
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+                             AR8327_ATU_FUNC_BUSY, 0);
+       if (!ret)
+               ar8xxx_write(priv, AR8327_REG_ATU_FUNC,
+                            AR8327_ATU_FUNC_OP_FLUSH |
+                            AR8327_ATU_FUNC_BUSY);
+
+       return ret;
+}
+
+static int
+ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port)
+{
+       u32 t;
+       int ret;
+
+       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
+                             AR8327_ATU_FUNC_BUSY, 0);
+       if (!ret) {
+               t = (port << AR8327_ATU_PORT_NUM_S);
+               t |= AR8327_ATU_FUNC_OP_FLUSH_PORT;
+               t |= AR8327_ATU_FUNC_BUSY;
+               ar8xxx_write(priv, AR8327_REG_ATU_FUNC, t);
+       }
+
+       return ret;
+}
+
+static int
+ar8327_get_port_igmp(struct ar8xxx_priv *priv, int port)
+{
+       u32 fwd_ctrl, frame_ack;
+
+       fwd_ctrl = (BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
+       frame_ack = ((AR8327_FRAME_ACK_CTRL_IGMP_MLD |
+                     AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
+                     AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
+                    AR8327_FRAME_ACK_CTRL_S(port));
+
+       return (ar8xxx_read(priv, AR8327_REG_FWD_CTRL1) &
+                       fwd_ctrl) == fwd_ctrl &&
+               (ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL(port)) &
+                       frame_ack) == frame_ack;
+}
+
+static void
+ar8327_set_port_igmp(struct ar8xxx_priv *priv, int port, int enable)
+{
+       int reg_frame_ack = AR8327_REG_FRAME_ACK_CTRL(port);
+       u32 val_frame_ack = (AR8327_FRAME_ACK_CTRL_IGMP_MLD |
+                         AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
+                         AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
+                        AR8327_FRAME_ACK_CTRL_S(port);
+
+       if (enable) {
+               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
+                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S,
+                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
+               ar8xxx_reg_set(priv, reg_frame_ack, val_frame_ack);
+       } else {
+               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
+                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S,
+                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S);
+               ar8xxx_reg_clear(priv, reg_frame_ack, val_frame_ack);
+       }
+}
+
+static void
+ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
+{
+       if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1,
+                           AR8327_VTU_FUNC1_BUSY, 0))
+               return;
+
+       if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD)
+               ar8xxx_write(priv, AR8327_REG_VTU_FUNC0, val);
+
+       op |= AR8327_VTU_FUNC1_BUSY;
+       ar8xxx_write(priv, AR8327_REG_VTU_FUNC1, op);
+}
+
+static void
+ar8327_vtu_flush(struct ar8xxx_priv *priv)
+{
+       ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0);
+}
+
+static void
+ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
+{
+       u32 op;
+       u32 val;
+       int i;
+
+       op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S);
+       val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL;
+       for (i = 0; i < AR8327_NUM_PORTS; i++) {
+               u32 mode;
+
+               if ((port_mask & BIT(i)) == 0)
+                       mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
+               else if (priv->vlan == 0)
+                       mode = AR8327_VTU_FUNC0_EG_MODE_KEEP;
+               else if ((priv->vlan_tagged & BIT(i)) || (priv->vlan_id[priv->pvid[i]] != vid))
+                       mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
+               else
+                       mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
+
+               val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i);
+       }
+       ar8327_vtu_op(priv, op, val);
+}
+
+static void
+ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
+{
+       u32 t;
+       u32 egress, ingress;
+       u32 pvid = priv->vlan_id[priv->pvid[port]];
+
+       if (priv->vlan) {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
+               ingress = AR8216_IN_SECURE;
+       } else {
+               egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
+               ingress = AR8216_IN_PORT_ONLY;
+       }
+
+       t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
+       t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
+       if (priv->vlan && priv->port_vlan_prio[port]) {
+               u32 prio = priv->port_vlan_prio[port];
+
+               t |= prio << AR8327_PORT_VLAN0_DEF_SPRI_S;
+               t |= prio << AR8327_PORT_VLAN0_DEF_CPRI_S;
+       }
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t);
+
+       t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
+       t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
+       if (priv->vlan && priv->port_vlan_prio[port])
+               t |= AR8327_PORT_VLAN1_VLAN_PRI_PROP;
+
+       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
+
+       t = members;
+       t |= AR8327_PORT_LOOKUP_LEARN;
+       t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S;
+       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
+       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
+}
+
+static int
+ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < dev->ports; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if ((priv->vlan_tagged & (1 << i)) || (priv->pvid[i] != val->port_vlan))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int
+ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+                       if (val->port_vlan == priv->pvid[p->id]) {
+                               priv->vlan_tagged |= (1 << p->id);
+                       }
+               } else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static void
+ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
+{
+       int port;
+
+       /* reset all mirror registers */
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+       for (port = 0; port < AR8327_NUM_PORTS; port++) {
+               ar8xxx_reg_clear(priv, AR8327_REG_PORT_LOOKUP(port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+               ar8xxx_reg_clear(priv, AR8327_REG_PORT_HOL_CTRL1(port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+       }
+
+       /* now enable mirroring if necessary */
+       if (priv->source_port >= AR8327_NUM_PORTS ||
+           priv->monitor_port >= AR8327_NUM_PORTS ||
+           priv->source_port == priv->monitor_port) {
+               return;
+       }
+
+       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
+                  AR8327_FWD_CTRL0_MIRROR_PORT,
+                  (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
+
+       if (priv->mirror_rx)
+               ar8xxx_reg_set(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
+                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
+
+       if (priv->mirror_tx)
+               ar8xxx_reg_set(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
+                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
+}
+
+static int
+ar8327_sw_set_eee(struct switch_dev *dev,
+                 const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       struct ar8327_data *data = priv->chip_data;
+       int port = val->port_vlan;
+       int phy;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+
+       phy = port - 1;
+
+       data->eee[phy] = !!(val->value.i);
+
+       return 0;
+}
+
+static int
+ar8327_sw_get_eee(struct switch_dev *dev,
+                 const struct switch_attr *attr,
+                 struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8327_data *data = priv->chip_data;
+       int port = val->port_vlan;
+       int phy;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+
+       phy = port - 1;
+
+       val->value.i = data->eee[phy];
+
+       return 0;
+}
+
+static void
+ar8327_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
+{
+       int timeout = 20;
+
+       while (ar8xxx_mii_read32(priv, r2, r1) & AR8327_ATU_FUNC_BUSY && --timeout) {
+               udelay(10);
+               cond_resched();
+       }
+
+       if (!timeout)
+               pr_err("ar8327: timeout waiting for atu to become ready\n");
+}
+
+static void ar8327_get_arl_entry(struct ar8xxx_priv *priv,
+                                struct arl_entry *a, u32 *status, enum arl_op op)
+{
+       struct mii_bus *bus = priv->mii_bus;
+       u16 r2, page;
+       u16 r1_data0, r1_data1, r1_data2, r1_func;
+       u32 t, val0, val1, val2;
+       int i;
+
+       split_addr(AR8327_REG_ATU_DATA0, &r1_data0, &r2, &page);
+       r2 |= 0x10;
+
+       r1_data1 = (AR8327_REG_ATU_DATA1 >> 1) & 0x1e;
+       r1_data2 = (AR8327_REG_ATU_DATA2 >> 1) & 0x1e;
+       r1_func  = (AR8327_REG_ATU_FUNC >> 1) & 0x1e;
+
+       switch (op) {
+       case AR8XXX_ARL_INITIALIZE:
+               /* all ATU registers are on the same page
+               * therefore set page only once
+               */
+               bus->write(bus, 0x18, 0, page);
+               wait_for_page_switch();
+
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               ar8xxx_mii_write32(priv, r2, r1_data0, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data1, 0);
+               ar8xxx_mii_write32(priv, r2, r1_data2, 0);
+               break;
+       case AR8XXX_ARL_GET_NEXT:
+               ar8xxx_mii_write32(priv, r2, r1_func,
+                                  AR8327_ATU_FUNC_OP_GET_NEXT |
+                                  AR8327_ATU_FUNC_BUSY);
+               ar8327_wait_atu_ready(priv, r2, r1_func);
+
+               val0 = ar8xxx_mii_read32(priv, r2, r1_data0);
+               val1 = ar8xxx_mii_read32(priv, r2, r1_data1);
+               val2 = ar8xxx_mii_read32(priv, r2, r1_data2);
+
+               *status = val2 & AR8327_ATU_STATUS;
+               if (!*status)
+                       break;
+
+               i = 0;
+               t = AR8327_ATU_PORT0;
+               while (!(val1 & t) && ++i < AR8327_NUM_PORTS)
+                       t <<= 1;
+
+               a->port = i;
+               a->mac[0] = (val0 & AR8327_ATU_ADDR0) >> AR8327_ATU_ADDR0_S;
+               a->mac[1] = (val0 & AR8327_ATU_ADDR1) >> AR8327_ATU_ADDR1_S;
+               a->mac[2] = (val0 & AR8327_ATU_ADDR2) >> AR8327_ATU_ADDR2_S;
+               a->mac[3] = (val0 & AR8327_ATU_ADDR3) >> AR8327_ATU_ADDR3_S;
+               a->mac[4] = (val1 & AR8327_ATU_ADDR4) >> AR8327_ATU_ADDR4_S;
+               a->mac[5] = (val1 & AR8327_ATU_ADDR5) >> AR8327_ATU_ADDR5_S;
+               break;
+       }
+}
+
+static int
+ar8327_sw_hw_apply(struct switch_dev *dev)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       const struct ar8327_data *data = priv->chip_data;
+       int ret, i;
+
+       ret = ar8xxx_sw_hw_apply(dev);
+       if (ret)
+               return ret;
+
+       for (i=0; i < AR8XXX_NUM_PHYS; i++) {
+               if (data->eee[i])
+                       ar8xxx_reg_clear(priv, AR8327_REG_EEE_CTRL,
+                              AR8327_EEE_CTRL_DISABLE_PHY(i));
+               else
+                       ar8xxx_reg_set(priv, AR8327_REG_EEE_CTRL,
+                              AR8327_EEE_CTRL_DISABLE_PHY(i));
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_get_port_igmp_snooping(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       val->value.i = ar8327_get_port_igmp(priv, port);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_set_port_igmp_snooping(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+
+       mutex_lock(&priv->reg_mutex);
+       ar8327_set_port_igmp(priv, port, val->value.i);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_get_igmp_snooping(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       int port;
+
+       for (port = 0; port < dev->ports; port++) {
+               val->port_vlan = port;
+               if (ar8327_sw_get_port_igmp_snooping(dev, attr, val) ||
+                   !val->value.i)
+                       break;
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_set_igmp_snooping(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       int port;
+
+       for (port = 0; port < dev->ports; port++) {
+               val->port_vlan = port;
+               if (ar8327_sw_set_port_igmp_snooping(dev, attr, val))
+                       break;
+       }
+
+       return 0;
+}
+
+int
+ar8327_sw_get_igmp_v3(struct switch_dev *dev,
+                     const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       u32 val_reg;
+
+       mutex_lock(&priv->reg_mutex);
+       val_reg = ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL1);
+       val->value.i = ((val_reg & AR8327_FRAME_ACK_CTRL_IGMP_V3_EN) != 0);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+int
+ar8327_sw_set_igmp_v3(struct switch_dev *dev,
+                     const struct switch_attr *attr,
+                     struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+
+       mutex_lock(&priv->reg_mutex);
+       if (val->value.i)
+               ar8xxx_reg_set(priv, AR8327_REG_FRAME_ACK_CTRL1,
+                              AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
+       else
+               ar8xxx_reg_clear(priv, AR8327_REG_FRAME_ACK_CTRL1,
+                                AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
+       mutex_unlock(&priv->reg_mutex);
+
+       return 0;
+}
+
+static int
+ar8327_sw_set_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       if (port >= dev->ports)
+               return -EINVAL;
+       if (port == 0 || port == 6)
+               return -EOPNOTSUPP;
+       if (val->value.i < 0 || val->value.i > 7)
+               return -EINVAL;
+
+       priv->port_vlan_prio[port] = val->value.i;
+
+       return 0;
+}
+
+static int
+ar8327_sw_get_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
+                  struct switch_val *val)
+{
+       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
+       int port = val->port_vlan;
+
+       val->value.i = priv->port_vlan_prio[port];
+
+       return 0;
+}
+
+static const struct switch_attr ar8327_sw_attr_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = ar8xxx_sw_set_vlan,
+               .get = ar8xxx_sw_get_vlan,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = ar8xxx_sw_set_reset_mibs,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = ar8xxx_sw_set_mirror_rx_enable,
+               .get = ar8xxx_sw_get_mirror_rx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = ar8xxx_sw_set_mirror_tx_enable,
+               .get = ar8xxx_sw_get_mirror_tx_enable,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = ar8xxx_sw_set_mirror_monitor_port,
+               .get = ar8xxx_sw_get_mirror_monitor_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = ar8xxx_sw_set_mirror_source_port,
+               .get = ar8xxx_sw_get_mirror_source_port,
+               .max = AR8327_NUM_PORTS - 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "arl_age_time",
+               .description = "ARL age time (secs)",
+               .set = ar8xxx_sw_set_arl_age_time,
+               .get = ar8xxx_sw_get_arl_age_time,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "arl_table",
+               .description = "Get ARL table",
+               .set = NULL,
+               .get = ar8xxx_sw_get_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush ARL table",
+               .set = ar8xxx_sw_set_flush_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_snooping",
+               .description = "Enable IGMP Snooping",
+               .set = ar8327_sw_set_igmp_snooping,
+               .get = ar8327_sw_get_igmp_snooping,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_v3",
+               .description = "Enable IGMPv3 support",
+               .set = ar8327_sw_set_igmp_v3,
+               .get = ar8327_sw_get_igmp_v3,
+               .max = 1
+       },
+};
+
+static const struct switch_attr ar8327_sw_attr_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = ar8xxx_sw_set_port_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .set = NULL,
+               .get = ar8xxx_sw_get_port_mib,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_eee",
+               .description = "Enable EEE PHY sleep mode",
+               .set = ar8327_sw_set_eee,
+               .get = ar8327_sw_get_eee,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "flush_arl_table",
+               .description = "Flush port's ARL table entries",
+               .set = ar8xxx_sw_set_flush_port_arl_table,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "igmp_snooping",
+               .description = "Enable port's IGMP Snooping",
+               .set = ar8327_sw_set_port_igmp_snooping,
+               .get = ar8327_sw_get_port_igmp_snooping,
+               .max = 1
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "vlan_prio",
+               .description = "Port VLAN default priority (VLAN PCP) (0-7)",
+               .set = ar8327_sw_set_port_vlan_prio,
+               .get = ar8327_sw_get_port_vlan_prio,
+               .max = 7,
+       },
+};
+
+static const struct switch_dev_ops ar8327_sw_ops = {
+       .attr_global = {
+               .attr = ar8327_sw_attr_globals,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
+       },
+       .attr_port = {
+               .attr = ar8327_sw_attr_port,
+               .n_attr = ARRAY_SIZE(ar8327_sw_attr_port),
+       },
+       .attr_vlan = {
+               .attr = ar8xxx_sw_attr_vlan,
+               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
+       },
+       .get_port_pvid = ar8xxx_sw_get_pvid,
+       .set_port_pvid = ar8xxx_sw_set_pvid,
+       .get_vlan_ports = ar8327_sw_get_ports,
+       .set_vlan_ports = ar8327_sw_set_ports,
+       .apply_config = ar8327_sw_hw_apply,
+       .reset_switch = ar8xxx_sw_reset_switch,
+       .get_port_link = ar8xxx_sw_get_port_link,
+/* The following op is disabled as it hogs the CPU and degrades performance.
+   An implementation has been attempted in 4d8a66d but reading MIB data is slow
+   on ar8xxx switches.
+
+   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
+   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
+   the request to update the MIB counter is cleared. */
+#if 0
+       .get_port_stats = ar8xxx_sw_get_port_stats,
+#endif
+};
+
+const struct ar8xxx_chip ar8327_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
+       .mii_lo_first = true,
+
+       .name = "Atheros AR8327",
+       .ports = AR8327_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8327_sw_ops,
+
+       .reg_port_stats_start = 0x1000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
+
+       .hw_init = ar8327_hw_init,
+       .cleanup = ar8327_cleanup,
+       .init_globals = ar8327_init_globals,
+       .init_port = ar8327_init_port,
+       .setup_port = ar8327_setup_port,
+       .read_port_status = ar8327_read_port_status,
+       .read_port_eee_status = ar8327_read_port_eee_status,
+       .atu_flush = ar8327_atu_flush,
+       .atu_flush_port = ar8327_atu_flush_port,
+       .vtu_flush = ar8327_vtu_flush,
+       .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
+       .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
+       .sw_hw_apply = ar8327_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
+};
+
+const struct ar8xxx_chip ar8337_chip = {
+       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
+       .config_at_probe = true,
+       .mii_lo_first = true,
+
+       .name = "Atheros AR8337",
+       .ports = AR8327_NUM_PORTS,
+       .vlans = AR8X16_MAX_VLANS,
+       .swops = &ar8327_sw_ops,
+
+       .reg_port_stats_start = 0x1000,
+       .reg_port_stats_length = 0x100,
+       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
+
+       .hw_init = ar8327_hw_init,
+       .cleanup = ar8327_cleanup,
+       .init_globals = ar8327_init_globals,
+       .init_port = ar8327_init_port,
+       .setup_port = ar8327_setup_port,
+       .read_port_status = ar8327_read_port_status,
+       .read_port_eee_status = ar8327_read_port_eee_status,
+       .atu_flush = ar8327_atu_flush,
+       .atu_flush_port = ar8327_atu_flush_port,
+       .vtu_flush = ar8327_vtu_flush,
+       .vtu_load_vlan = ar8327_vtu_load_vlan,
+       .phy_fixup = ar8327_phy_fixup,
+       .set_mirror_regs = ar8327_set_mirror_regs,
+       .get_arl_entry = ar8327_get_arl_entry,
+       .sw_hw_apply = ar8327_sw_hw_apply,
+
+       .num_mibs = ARRAY_SIZE(ar8236_mibs),
+       .mib_decs = ar8236_mibs,
+       .mib_func = AR8327_REG_MIB_FUNC
+};
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/ar8327.h b/target/linux/generic/files-4.9/drivers/net/phy/ar8327.h
new file mode 100644 (file)
index 0000000..d53ef88
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * ar8327.h: AR8216 switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AR8327_H
+#define __AR8327_H
+
+#define AR8327_NUM_PORTS       7
+#define AR8327_NUM_LEDS                15
+#define AR8327_PORTS_ALL       0x7f
+#define AR8327_NUM_LED_CTRL_REGS       4
+
+#define AR8327_REG_MASK                                0x000
+
+#define AR8327_REG_PAD0_MODE                   0x004
+#define AR8327_REG_PAD5_MODE                   0x008
+#define AR8327_REG_PAD6_MODE                   0x00c
+#define   AR8327_PAD_MAC_MII_RXCLK_SEL         BIT(0)
+#define   AR8327_PAD_MAC_MII_TXCLK_SEL         BIT(1)
+#define   AR8327_PAD_MAC_MII_EN                        BIT(2)
+#define   AR8327_PAD_MAC_GMII_RXCLK_SEL                BIT(4)
+#define   AR8327_PAD_MAC_GMII_TXCLK_SEL                BIT(5)
+#define   AR8327_PAD_MAC_GMII_EN               BIT(6)
+#define   AR8327_PAD_SGMII_EN                  BIT(7)
+#define   AR8327_PAD_PHY_MII_RXCLK_SEL         BIT(8)
+#define   AR8327_PAD_PHY_MII_TXCLK_SEL         BIT(9)
+#define   AR8327_PAD_PHY_MII_EN                        BIT(10)
+#define   AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL   BIT(11)
+#define   AR8327_PAD_PHY_GMII_RXCLK_SEL                BIT(12)
+#define   AR8327_PAD_PHY_GMII_TXCLK_SEL                BIT(13)
+#define   AR8327_PAD_PHY_GMII_EN               BIT(14)
+#define   AR8327_PAD_PHYX_GMII_EN              BIT(16)
+#define   AR8327_PAD_PHYX_RGMII_EN             BIT(17)
+#define   AR8327_PAD_PHYX_MII_EN               BIT(18)
+#define   AR8327_PAD_SGMII_DELAY_EN            BIT(19)
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL     BITS(20, 2)
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S   20
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL     BITS(22, 2)
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S   22
+#define   AR8327_PAD_RGMII_RXCLK_DELAY_EN      BIT(24)
+#define   AR8327_PAD_RGMII_TXCLK_DELAY_EN      BIT(25)
+#define   AR8327_PAD_RGMII_EN                  BIT(26)
+
+#define AR8327_REG_POWER_ON_STRIP              0x010
+#define   AR8327_POWER_ON_STRIP_POWER_ON_SEL   BIT(31)
+#define   AR8327_POWER_ON_STRIP_LED_OPEN_EN    BIT(24)
+#define   AR8327_POWER_ON_STRIP_SERDES_AEN     BIT(7)
+
+#define AR8327_REG_INT_STATUS0                 0x020
+#define   AR8327_INT0_VT_DONE                  BIT(20)
+
+#define AR8327_REG_INT_STATUS1                 0x024
+#define AR8327_REG_INT_MASK0                   0x028
+#define AR8327_REG_INT_MASK1                   0x02c
+
+#define AR8327_REG_MODULE_EN                   0x030
+#define   AR8327_MODULE_EN_MIB                 BIT(0)
+
+#define AR8327_REG_MIB_FUNC                    0x034
+#define   AR8327_MIB_CPU_KEEP                  BIT(20)
+
+#define AR8327_REG_SERVICE_TAG                 0x048
+#define AR8327_REG_LED_CTRL(_i)                        (0x050 + (_i) * 4)
+#define AR8327_REG_LED_CTRL0                   0x050
+#define AR8327_REG_LED_CTRL1                   0x054
+#define AR8327_REG_LED_CTRL2                   0x058
+#define AR8327_REG_LED_CTRL3                   0x05c
+#define AR8327_REG_MAC_ADDR0                   0x060
+#define AR8327_REG_MAC_ADDR1                   0x064
+
+#define AR8327_REG_MAX_FRAME_SIZE              0x078
+#define   AR8327_MAX_FRAME_SIZE_MTU            BITS(0, 14)
+
+#define AR8327_REG_PORT_STATUS(_i)             (0x07c + (_i) * 4)
+#define   AR8327_PORT_STATUS_TXFLOW_AUTO       BIT(10)
+#define   AR8327_PORT_STATUS_RXFLOW_AUTO       BIT(11)
+
+#define AR8327_REG_HEADER_CTRL                 0x098
+#define AR8327_REG_PORT_HEADER(_i)             (0x09c + (_i) * 4)
+
+#define AR8327_REG_SGMII_CTRL                  0x0e0
+#define   AR8327_SGMII_CTRL_EN_PLL             BIT(1)
+#define   AR8327_SGMII_CTRL_EN_RX              BIT(2)
+#define   AR8327_SGMII_CTRL_EN_TX              BIT(3)
+
+#define AR8327_REG_EEE_CTRL                    0x100
+#define   AR8327_EEE_CTRL_DISABLE_PHY(_i)      BIT(4 + (_i) * 2)
+
+#define AR8327_REG_FRAME_ACK_CTRL0             0x210
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0   BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0  BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0 BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN0      BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN0       BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0    BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0    BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1   BIT(8)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1  BIT(9)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1 BIT(10)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN1      BIT(11)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN1       BIT(12)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1    BIT(13)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1    BIT(14)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2   BIT(16)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2  BIT(17)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2 BIT(18)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN2      BIT(19)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN2       BIT(20)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2    BIT(21)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2    BIT(22)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3   BIT(24)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3  BIT(25)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3 BIT(26)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN3      BIT(27)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN3       BIT(28)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3    BIT(29)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3    BIT(30)
+
+#define AR8327_REG_FRAME_ACK_CTRL1             0x214
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4   BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4  BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4 BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN4      BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN4       BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4    BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4    BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5   BIT(8)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5  BIT(9)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5 BIT(10)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN5      BIT(11)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN5       BIT(12)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5    BIT(13)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5    BIT(14)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6   BIT(16)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6  BIT(17)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6 BIT(18)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN6      BIT(19)
+#define   AR8327_FRAME_ACK_CTRL_DHCP_EN6       BIT(20)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6    BIT(21)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6    BIT(22)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_V3_EN     BIT(24)
+#define   AR8327_FRAME_ACK_CTRL_PPPOE_EN       BIT(25)
+
+#define AR8327_REG_FRAME_ACK_CTRL(_i)          (0x210 + ((_i) / 4) * 0x4)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD       BIT(0)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN      BIT(1)
+#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE     BIT(2)
+#define   AR8327_FRAME_ACK_CTRL_EAPOL          BIT(3)
+#define   AR8327_FRAME_ACK_CTRL_DHCP           BIT(4)
+#define   AR8327_FRAME_ACK_CTRL_ARP_ACK                BIT(5)
+#define   AR8327_FRAME_ACK_CTRL_ARP_REQ                BIT(6)
+#define   AR8327_FRAME_ACK_CTRL_S(_i)          (((_i) % 4) * 8)
+
+#define AR8327_REG_PORT_VLAN0(_i)              (0x420 + (_i) * 0x8)
+#define   AR8327_PORT_VLAN0_DEF_PRI_MASK       BITS(0, 3)
+#define   AR8327_PORT_VLAN0_DEF_SVID           BITS(0, 12)
+#define   AR8327_PORT_VLAN0_DEF_SVID_S         0
+#define   AR8327_PORT_VLAN0_DEF_SPRI           BITS(13, 3)
+#define   AR8327_PORT_VLAN0_DEF_SPRI_S         13
+#define   AR8327_PORT_VLAN0_DEF_CVID           BITS(16, 12)
+#define   AR8327_PORT_VLAN0_DEF_CVID_S         16
+#define   AR8327_PORT_VLAN0_DEF_CPRI           BITS(29, 3)
+#define   AR8327_PORT_VLAN0_DEF_CPRI_S         29
+
+#define AR8327_REG_PORT_VLAN1(_i)              (0x424 + (_i) * 0x8)
+#define   AR8327_PORT_VLAN1_VLAN_PRI_PROP      BIT(4)
+#define   AR8327_PORT_VLAN1_PORT_VLAN_PROP     BIT(6)
+#define   AR8327_PORT_VLAN1_OUT_MODE           BITS(12, 2)
+#define   AR8327_PORT_VLAN1_OUT_MODE_S         12
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNMOD     0
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNTAG     1
+#define   AR8327_PORT_VLAN1_OUT_MODE_TAG       2
+#define   AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH   3
+
+#define AR8327_REG_ATU_DATA0                   0x600
+#define   AR8327_ATU_ADDR0                     BITS(0, 8)
+#define   AR8327_ATU_ADDR0_S                   0
+#define   AR8327_ATU_ADDR1                     BITS(8, 8)
+#define   AR8327_ATU_ADDR1_S                   8
+#define   AR8327_ATU_ADDR2                     BITS(16, 8)
+#define   AR8327_ATU_ADDR2_S                   16
+#define   AR8327_ATU_ADDR3                     BITS(24, 8)
+#define   AR8327_ATU_ADDR3_S                   24
+#define AR8327_REG_ATU_DATA1                   0x604
+#define   AR8327_ATU_ADDR4                     BITS(0, 8)
+#define   AR8327_ATU_ADDR4_S                   0
+#define   AR8327_ATU_ADDR5                     BITS(8, 8)
+#define   AR8327_ATU_ADDR5_S                   8
+#define   AR8327_ATU_PORTS                     BITS(16, 7)
+#define   AR8327_ATU_PORT0                     BIT(16)
+#define   AR8327_ATU_PORT1                     BIT(17)
+#define   AR8327_ATU_PORT2                     BIT(18)
+#define   AR8327_ATU_PORT3                     BIT(19)
+#define   AR8327_ATU_PORT4                     BIT(20)
+#define   AR8327_ATU_PORT5                     BIT(21)
+#define   AR8327_ATU_PORT6                     BIT(22)
+#define AR8327_REG_ATU_DATA2                   0x608
+#define   AR8327_ATU_STATUS                    BITS(0, 4)
+
+#define AR8327_REG_ATU_FUNC                    0x60c
+#define   AR8327_ATU_FUNC_OP                   BITS(0, 4)
+#define   AR8327_ATU_FUNC_OP_NOOP              0x0
+#define   AR8327_ATU_FUNC_OP_FLUSH             0x1
+#define   AR8327_ATU_FUNC_OP_LOAD              0x2
+#define   AR8327_ATU_FUNC_OP_PURGE             0x3
+#define   AR8327_ATU_FUNC_OP_FLUSH_UNLOCKED    0x4
+#define   AR8327_ATU_FUNC_OP_FLUSH_PORT                0x5
+#define   AR8327_ATU_FUNC_OP_GET_NEXT          0x6
+#define   AR8327_ATU_FUNC_OP_SEARCH_MAC                0x7
+#define   AR8327_ATU_FUNC_OP_CHANGE_TRUNK      0x8
+#define   AR8327_ATU_PORT_NUM                  BITS(8, 4)
+#define   AR8327_ATU_PORT_NUM_S                        8
+#define   AR8327_ATU_FUNC_BUSY                 BIT(31)
+
+#define AR8327_REG_VTU_FUNC0                   0x0610
+#define   AR8327_VTU_FUNC0_EG_MODE             BITS(4, 14)
+#define   AR8327_VTU_FUNC0_EG_MODE_S(_i)       (4 + (_i) * 2)
+#define   AR8327_VTU_FUNC0_EG_MODE_KEEP                0
+#define   AR8327_VTU_FUNC0_EG_MODE_UNTAG       1
+#define   AR8327_VTU_FUNC0_EG_MODE_TAG         2
+#define   AR8327_VTU_FUNC0_EG_MODE_NOT         3
+#define   AR8327_VTU_FUNC0_IVL                 BIT(19)
+#define   AR8327_VTU_FUNC0_VALID               BIT(20)
+
+#define AR8327_REG_VTU_FUNC1                   0x0614
+#define   AR8327_VTU_FUNC1_OP                  BITS(0, 3)
+#define   AR8327_VTU_FUNC1_OP_NOOP             0
+#define   AR8327_VTU_FUNC1_OP_FLUSH            1
+#define   AR8327_VTU_FUNC1_OP_LOAD             2
+#define   AR8327_VTU_FUNC1_OP_PURGE            3
+#define   AR8327_VTU_FUNC1_OP_REMOVE_PORT      4
+#define   AR8327_VTU_FUNC1_OP_GET_NEXT         5
+#define   AR8327_VTU_FUNC1_OP_GET_ONE          6
+#define   AR8327_VTU_FUNC1_FULL                        BIT(4)
+#define   AR8327_VTU_FUNC1_PORT                        BIT(8, 4)
+#define   AR8327_VTU_FUNC1_PORT_S              8
+#define   AR8327_VTU_FUNC1_VID                 BIT(16, 12)
+#define   AR8327_VTU_FUNC1_VID_S               16
+#define   AR8327_VTU_FUNC1_BUSY                        BIT(31)
+
+#define AR8327_REG_ARL_CTRL                    0x0618
+
+#define AR8327_REG_FWD_CTRL0                   0x620
+#define   AR8327_FWD_CTRL0_CPU_PORT_EN         BIT(10)
+#define   AR8327_FWD_CTRL0_MIRROR_PORT         BITS(4, 4)
+#define   AR8327_FWD_CTRL0_MIRROR_PORT_S       4
+
+#define AR8327_REG_FWD_CTRL1                   0x624
+#define   AR8327_FWD_CTRL1_UC_FLOOD            BITS(0, 7)
+#define   AR8327_FWD_CTRL1_UC_FLOOD_S          0
+#define   AR8327_FWD_CTRL1_MC_FLOOD            BITS(8, 7)
+#define   AR8327_FWD_CTRL1_MC_FLOOD_S          8
+#define   AR8327_FWD_CTRL1_BC_FLOOD            BITS(16, 7)
+#define   AR8327_FWD_CTRL1_BC_FLOOD_S          16
+#define   AR8327_FWD_CTRL1_IGMP                        BITS(24, 7)
+#define   AR8327_FWD_CTRL1_IGMP_S              24
+
+#define AR8327_REG_PORT_LOOKUP(_i)             (0x660 + (_i) * 0xc)
+#define   AR8327_PORT_LOOKUP_MEMBER            BITS(0, 7)
+#define   AR8327_PORT_LOOKUP_IN_MODE           BITS(8, 2)
+#define   AR8327_PORT_LOOKUP_IN_MODE_S         8
+#define   AR8327_PORT_LOOKUP_STATE             BITS(16, 3)
+#define   AR8327_PORT_LOOKUP_STATE_S           16
+#define   AR8327_PORT_LOOKUP_LEARN             BIT(20)
+#define   AR8327_PORT_LOOKUP_ING_MIRROR_EN     BIT(25)
+
+#define AR8327_REG_PORT_PRIO(_i)               (0x664 + (_i) * 0xc)
+
+#define AR8327_REG_PORT_HOL_CTRL1(_i)          (0x974 + (_i) * 0x8)
+#define   AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN   BIT(16)
+
+#define AR8337_PAD_MAC06_EXCHANGE_EN           BIT(31)
+
+enum ar8327_led_pattern {
+       AR8327_LED_PATTERN_OFF = 0,
+       AR8327_LED_PATTERN_BLINK,
+       AR8327_LED_PATTERN_ON,
+       AR8327_LED_PATTERN_RULE,
+};
+
+struct ar8327_led_entry {
+       unsigned reg;
+       unsigned shift;
+};
+
+struct ar8327_led {
+       struct led_classdev cdev;
+       struct ar8xxx_priv *sw_priv;
+
+       char *name;
+       bool active_low;
+       u8 led_num;
+       enum ar8327_led_mode mode;
+
+       struct mutex mutex;
+       spinlock_t lock;
+       struct work_struct led_work;
+       bool enable_hw_mode;
+       enum ar8327_led_pattern pattern;
+};
+
+struct ar8327_data {
+       u32 port0_status;
+       u32 port6_status;
+
+       struct ar8327_led **leds;
+       unsigned int num_leds;
+
+       /* all fields below are cleared on reset */
+       bool eee[AR8XXX_NUM_PHYS];
+};
+
+#endif
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/Kconfig b/target/linux/generic/files-4.9/drivers/net/phy/b53/Kconfig
new file mode 100644 (file)
index 0000000..08287e7
--- /dev/null
@@ -0,0 +1,37 @@
+menuconfig SWCONFIG_B53
+       tristate "Broadcom bcm53xx managed switch support"
+       depends on SWCONFIG
+       help
+         This driver adds support for Broadcom managed switch chips. It supports
+         BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
+         integrated switches.
+
+config SWCONFIG_B53_SPI_DRIVER
+       tristate "B53 SPI connected switch driver"
+       depends on SWCONFIG_B53 && SPI
+       help
+         Select to enable support for registering switches configured through SPI.
+
+config SWCONFIG_B53_PHY_DRIVER
+       tristate "B53 MDIO connected switch driver"
+       depends on SWCONFIG_B53
+       select SWCONFIG_B53_PHY_FIXUP
+       help
+         Select to enable support for registering switches configured through MDIO.
+
+config SWCONFIG_B53_MMAP_DRIVER
+       tristate "B53 MMAP connected switch driver"
+       depends on SWCONFIG_B53
+       help
+         Select to enable support for memory-mapped switches like the BCM63XX
+         integrated switches.
+
+config SWCONFIG_B53_SRAB_DRIVER
+       tristate "B53 SRAB connected switch driver"
+       depends on SWCONFIG_B53
+       help
+         Select to enable support for memory-mapped Switch Register Access
+         Bridge Registers (SRAB) like it is found on the BCM53010
+
+config SWCONFIG_B53_PHY_FIXUP
+       bool
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/Makefile b/target/linux/generic/files-4.9/drivers/net/phy/b53/Makefile
new file mode 100644 (file)
index 0000000..13ff366
--- /dev/null
@@ -0,0 +1,10 @@
+obj-$(CONFIG_SWCONFIG_B53)             += b53_common.o
+
+obj-$(CONFIG_SWCONFIG_B53_PHY_FIXUP)   += b53_phy_fixup.o
+
+obj-$(CONFIG_SWCONFIG_B53_MMAP_DRIVER) += b53_mmap.o
+obj-$(CONFIG_SWCONFIG_B53_SRAB_DRIVER) += b53_srab.o
+obj-$(CONFIG_SWCONFIG_B53_PHY_DRIVER)  += b53_mdio.o
+obj-$(CONFIG_SWCONFIG_B53_SPI_DRIVER)  += b53_spi.o
+
+ccflags-y                              += -Werror
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_common.c
new file mode 100644 (file)
index 0000000..670588c
--- /dev/null
@@ -0,0 +1,1722 @@
+/*
+ * B53 switch driver main logic
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_net.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_regs.h"
+#include "b53_priv.h"
+
+/* buffer size needed for displaying all MIBs with max'd values */
+#define B53_BUF_SIZE   1188
+
+struct b53_mib_desc {
+       u8 size;
+       u8 offset;
+       const char *name;
+};
+
+/* BCM5365 MIB counters */
+static const struct b53_mib_desc b53_mibs_65[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x44, "RxOctets" },
+       { 4, 0x4c, "RxUndersizePkts" },
+       { 4, 0x50, "RxPausePkts" },
+       { 4, 0x54, "Pkts64Octets" },
+       { 4, 0x58, "Pkts65to127Octets" },
+       { 4, 0x5c, "Pkts128to255Octets" },
+       { 4, 0x60, "Pkts256to511Octets" },
+       { 4, 0x64, "Pkts512to1023Octets" },
+       { 4, 0x68, "Pkts1024to1522Octets" },
+       { 4, 0x6c, "RxOversizePkts" },
+       { 4, 0x70, "RxJabbers" },
+       { 4, 0x74, "RxAlignmentErrors" },
+       { 4, 0x78, "RxFCSErrors" },
+       { 8, 0x7c, "RxGoodOctets" },
+       { 4, 0x84, "RxDropPkts" },
+       { 4, 0x88, "RxUnicastPkts" },
+       { 4, 0x8c, "RxMulticastPkts" },
+       { 4, 0x90, "RxBroadcastPkts" },
+       { 4, 0x94, "RxSAChanges" },
+       { 4, 0x98, "RxFragments" },
+       { },
+};
+
+#define B63XX_MIB_TXB_ID       0       /* TxOctets */
+#define B63XX_MIB_RXB_ID       14      /* RxOctets */
+
+/* BCM63xx MIB counters */
+static const struct b53_mib_desc b53_mibs_63xx[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x0c, "TxQoSPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x3c, "TxQoSOctets" },
+       { 8, 0x44, "RxOctets" },
+       { 4, 0x4c, "RxUndersizePkts" },
+       { 4, 0x50, "RxPausePkts" },
+       { 4, 0x54, "Pkts64Octets" },
+       { 4, 0x58, "Pkts65to127Octets" },
+       { 4, 0x5c, "Pkts128to255Octets" },
+       { 4, 0x60, "Pkts256to511Octets" },
+       { 4, 0x64, "Pkts512to1023Octets" },
+       { 4, 0x68, "Pkts1024to1522Octets" },
+       { 4, 0x6c, "RxOversizePkts" },
+       { 4, 0x70, "RxJabbers" },
+       { 4, 0x74, "RxAlignmentErrors" },
+       { 4, 0x78, "RxFCSErrors" },
+       { 8, 0x7c, "RxGoodOctets" },
+       { 4, 0x84, "RxDropPkts" },
+       { 4, 0x88, "RxUnicastPkts" },
+       { 4, 0x8c, "RxMulticastPkts" },
+       { 4, 0x90, "RxBroadcastPkts" },
+       { 4, 0x94, "RxSAChanges" },
+       { 4, 0x98, "RxFragments" },
+       { 4, 0xa0, "RxSymbolErrors" },
+       { 4, 0xa4, "RxQoSPkts" },
+       { 8, 0xa8, "RxQoSOctets" },
+       { 4, 0xb0, "Pkts1523to2047Octets" },
+       { 4, 0xb4, "Pkts2048to4095Octets" },
+       { 4, 0xb8, "Pkts4096to8191Octets" },
+       { 4, 0xbc, "Pkts8192to9728Octets" },
+       { 4, 0xc0, "RxDiscarded" },
+       { }
+};
+
+#define B53XX_MIB_TXB_ID       0       /* TxOctets */
+#define B53XX_MIB_RXB_ID       12      /* RxOctets */
+
+/* MIB counters */
+static const struct b53_mib_desc b53_mibs[] = {
+       { 8, 0x00, "TxOctets" },
+       { 4, 0x08, "TxDropPkts" },
+       { 4, 0x10, "TxBroadcastPkts" },
+       { 4, 0x14, "TxMulticastPkts" },
+       { 4, 0x18, "TxUnicastPkts" },
+       { 4, 0x1c, "TxCollisions" },
+       { 4, 0x20, "TxSingleCollision" },
+       { 4, 0x24, "TxMultipleCollision" },
+       { 4, 0x28, "TxDeferredTransmit" },
+       { 4, 0x2c, "TxLateCollision" },
+       { 4, 0x30, "TxExcessiveCollision" },
+       { 4, 0x38, "TxPausePkts" },
+       { 8, 0x50, "RxOctets" },
+       { 4, 0x58, "RxUndersizePkts" },
+       { 4, 0x5c, "RxPausePkts" },
+       { 4, 0x60, "Pkts64Octets" },
+       { 4, 0x64, "Pkts65to127Octets" },
+       { 4, 0x68, "Pkts128to255Octets" },
+       { 4, 0x6c, "Pkts256to511Octets" },
+       { 4, 0x70, "Pkts512to1023Octets" },
+       { 4, 0x74, "Pkts1024to1522Octets" },
+       { 4, 0x78, "RxOversizePkts" },
+       { 4, 0x7c, "RxJabbers" },
+       { 4, 0x80, "RxAlignmentErrors" },
+       { 4, 0x84, "RxFCSErrors" },
+       { 8, 0x88, "RxGoodOctets" },
+       { 4, 0x90, "RxDropPkts" },
+       { 4, 0x94, "RxUnicastPkts" },
+       { 4, 0x98, "RxMulticastPkts" },
+       { 4, 0x9c, "RxBroadcastPkts" },
+       { 4, 0xa0, "RxSAChanges" },
+       { 4, 0xa4, "RxFragments" },
+       { 4, 0xa8, "RxJumboPkts" },
+       { 4, 0xac, "RxSymbolErrors" },
+       { 4, 0xc0, "RxDiscarded" },
+       { }
+};
+
+static int b53_do_vlan_op(struct b53_device *dev, u8 op)
+{
+       unsigned int i;
+
+       b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
+
+       for (i = 0; i < 10; i++) {
+               u8 vta;
+
+               b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
+               if (!(vta & VTA_START_CMD))
+                       return 0;
+
+               usleep_range(100, 200);
+       }
+
+       return -EIO;
+}
+
+static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
+                              u16 untag)
+{
+       if (is5325(dev)) {
+               u32 entry = 0;
+
+               if (members) {
+                       entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) |
+                               members;
+                       if (dev->core_rev >= 3)
+                               entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S;
+                       else
+                               entry |= VA_VALID_25;
+               }
+
+               b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
+                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
+       } else if (is5365(dev)) {
+               u16 entry = 0;
+
+               if (members)
+                       entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) |
+                               members | VA_VALID_65;
+
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
+                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
+       } else {
+               b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
+               b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
+                           (untag << VTE_UNTAG_S) | members);
+
+               b53_do_vlan_op(dev, VTA_CMD_WRITE);
+       }
+}
+
+void b53_set_forwarding(struct b53_device *dev, int enable)
+{
+       u8 mgmt;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (enable)
+               mgmt |= SM_SW_FWD_EN;
+       else
+               mgmt &= ~SM_SW_FWD_EN;
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static void b53_enable_vlan(struct b53_device *dev, int enable)
+{
+       u8 mgmt, vc0, vc1, vc4 = 0, vc5;
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
+       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
+
+       if (is5325(dev) || is5365(dev)) {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
+       } else if (is63xx(dev)) {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
+       } else {
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
+       }
+
+       mgmt &= ~SM_SW_FWD_MODE;
+
+       if (enable) {
+               vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
+               vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
+               vc4 &= ~VC4_ING_VID_CHECK_MASK;
+               vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
+               vc5 |= VC5_DROP_VTABLE_MISS;
+
+               if (is5325(dev))
+                       vc0 &= ~VC0_RESERVED_1;
+
+               if (is5325(dev) || is5365(dev))
+                       vc1 |= VC1_RX_MCST_TAG_EN;
+
+               if (!is5325(dev) && !is5365(dev)) {
+                       if (dev->allow_vid_4095)
+                               vc5 |= VC5_VID_FFF_EN;
+                       else
+                               vc5 &= ~VC5_VID_FFF_EN;
+               }
+       } else {
+               vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
+               vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
+               vc4 &= ~VC4_ING_VID_CHECK_MASK;
+               vc5 &= ~VC5_DROP_VTABLE_MISS;
+
+               if (is5325(dev) || is5365(dev))
+                       vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
+               else
+                       vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
+
+               if (is5325(dev) || is5365(dev))
+                       vc1 &= ~VC1_RX_MCST_TAG_EN;
+
+               if (!is5325(dev) && !is5365(dev))
+                       vc5 &= ~VC5_VID_FFF_EN;
+       }
+
+       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
+       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
+
+       if (is5325(dev) || is5365(dev)) {
+               /* enable the high 8 bit vid check on 5325 */
+               if (is5325(dev) && enable)
+                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
+                                  VC3_HIGH_8BIT_EN);
+               else
+                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
+       } else if (is63xx(dev)) {
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
+       } else {
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
+               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
+       }
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+}
+
+static int b53_set_jumbo(struct b53_device *dev, int enable, int allow_10_100)
+{
+       u32 port_mask = 0;
+       u16 max_size = JMS_MIN_SIZE;
+
+       if (is5325(dev) || is5365(dev))
+               return -EINVAL;
+
+       if (enable) {
+               port_mask = dev->enabled_ports;
+               max_size = JMS_MAX_SIZE;
+               if (allow_10_100)
+                       port_mask |= JPM_10_100_JUMBO_EN;
+       }
+
+       b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
+       return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
+}
+
+static int b53_flush_arl(struct b53_device *dev)
+{
+       unsigned int i;
+
+       b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+                  FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
+
+       for (i = 0; i < 10; i++) {
+               u8 fast_age_ctrl;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
+                         &fast_age_ctrl);
+
+               if (!(fast_age_ctrl & FAST_AGE_DONE))
+                       return 0;
+
+               mdelay(1);
+       }
+
+       pr_warn("time out while flushing ARL\n");
+
+       return -EINVAL;
+}
+
+static void b53_enable_ports(struct b53_device *dev)
+{
+       unsigned i;
+
+       b53_for_each_port(dev, i) {
+               u8 port_ctrl;
+               u16 pvlan_mask;
+
+               /*
+                * prevent leaking packets between wan and lan in unmanaged
+                * mode through port vlans.
+                */
+               if (dev->enable_vlan || is_cpu_port(dev, i))
+                       pvlan_mask = 0x1ff;
+               else if (is531x5(dev) || is5301x(dev))
+                       /* BCM53115 may use a different port as cpu port */
+                       pvlan_mask = BIT(dev->sw_dev.cpu_port);
+               else
+                       pvlan_mask = BIT(B53_CPU_PORT);
+
+               /* BCM5325 CPU port is at 8 */
+               if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25)
+                       i = B53_CPU_PORT;
+
+               if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7))
+                       /* disable unused ports 6 & 7 */
+                       port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
+               else if (i == B53_CPU_PORT)
+                       port_ctrl = PORT_CTRL_RX_BCST_EN |
+                                   PORT_CTRL_RX_MCST_EN |
+                                   PORT_CTRL_RX_UCST_EN;
+               else
+                       port_ctrl = 0;
+
+               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i),
+                           pvlan_mask);
+
+               /* port state is handled by bcm63xx_enet driver */
+               if (!is63xx(dev) && !(is5301x(dev) && i == 6))
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i),
+                                  port_ctrl);
+       }
+}
+
+static void b53_enable_mib(struct b53_device *dev)
+{
+       u8 gc;
+
+       b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+       gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
+
+       b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
+}
+
+static int b53_apply(struct b53_device *dev)
+{
+       int i;
+
+       /* clear all vlan entries */
+       if (is5325(dev) || is5365(dev)) {
+               for (i = 1; i < dev->sw_dev.vlans; i++)
+                       b53_set_vlan_entry(dev, i, 0, 0);
+       } else {
+               b53_do_vlan_op(dev, VTA_CMD_CLEAR);
+       }
+
+       b53_enable_vlan(dev, dev->enable_vlan);
+
+       /* fill VLAN table */
+       if (dev->enable_vlan) {
+               for (i = 0; i < dev->sw_dev.vlans; i++) {
+                       struct b53_vlan *vlan = &dev->vlans[i];
+
+                       if (!vlan->members)
+                               continue;
+
+                       b53_set_vlan_entry(dev, i, vlan->members, vlan->untag);
+               }
+
+               b53_for_each_port(dev, i)
+                       b53_write16(dev, B53_VLAN_PAGE,
+                                   B53_VLAN_PORT_DEF_TAG(i),
+                                   dev->ports[i].pvid);
+       } else {
+               b53_for_each_port(dev, i)
+                       b53_write16(dev, B53_VLAN_PAGE,
+                                   B53_VLAN_PORT_DEF_TAG(i), 1);
+
+       }
+
+       b53_enable_ports(dev);
+
+       if (!is5325(dev) && !is5365(dev))
+               b53_set_jumbo(dev, dev->enable_jumbo, 1);
+
+       return 0;
+}
+
+static void b53_switch_reset_gpio(struct b53_device *dev)
+{
+       int gpio = dev->reset_gpio;
+
+       if (gpio < 0)
+               return;
+
+       /*
+        * Reset sequence: RESET low(50ms)->high(20ms)
+        */
+       gpio_set_value(gpio, 0);
+       mdelay(50);
+
+       gpio_set_value(gpio, 1);
+       mdelay(20);
+
+       dev->current_page = 0xff;
+}
+
+static int b53_configure_ports_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       u32 port_num;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+
+       for_each_available_child_of_node(dn, pn) {
+               struct device_node *fixed_link;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               fixed_link = of_get_child_by_name(pn, "fixed-link");
+               if (fixed_link) {
+                       u32 spd;
+                       u8 po = GMII_PO_LINK;
+                       int mode = of_get_phy_mode(pn);
+
+                       if (!of_property_read_u32(fixed_link, "speed", &spd)) {
+                               switch (spd) {
+                               case 10:
+                                       po |= GMII_PO_SPEED_10M;
+                                       break;
+                               case 100:
+                                       po |= GMII_PO_SPEED_100M;
+                                       break;
+                               case 2000:
+                                       if (is_imp_port(dev, port_num))
+                                               po |= PORT_OVERRIDE_SPEED_2000M;
+                                       else
+                                               po |= GMII_PO_SPEED_2000M;
+                                       /* fall through */
+                               case 1000:
+                                       po |= GMII_PO_SPEED_1000M;
+                                       break;
+                               }
+                       }
+
+                       if (of_property_read_bool(fixed_link, "full-duplex"))
+                               po |= PORT_OVERRIDE_FULL_DUPLEX;
+                       if (of_property_read_bool(fixed_link, "pause"))
+                               po |= GMII_PO_RX_FLOW;
+                       if (of_property_read_bool(fixed_link, "asym-pause"))
+                               po |= GMII_PO_TX_FLOW;
+
+                       if (is_imp_port(dev, port_num)) {
+                               po |= PORT_OVERRIDE_EN;
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII)
+                                       po |= PORT_OVERRIDE_RV_MII_25;
+
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_PORT_OVERRIDE_CTRL, po);
+
+                               if (is5325(dev) &&
+                                   mode == PHY_INTERFACE_MODE_REVMII) {
+                                       b53_read8(dev, B53_CTRL_PAGE,
+                                                 B53_PORT_OVERRIDE_CTRL, &po);
+                                       if (!(po & PORT_OVERRIDE_RV_MII_25))
+                                       pr_err("Failed to enable reverse MII mode\n");
+                                       return -EINVAL;
+                               }
+                       } else {
+                               po |= GMII_PO_EN;
+                               b53_write8(dev, B53_CTRL_PAGE,
+                                          B53_GMII_PORT_OVERRIDE_CTRL(port_num),
+                                          po);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static int b53_configure_ports(struct b53_device *dev)
+{
+       u8 cpu_port = dev->sw_dev.cpu_port;
+
+       /* configure MII port if necessary */
+       if (is5325(dev)) {
+               u8 mii_port_override;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                         &mii_port_override);
+               /* reverse mii needs to be enabled */
+               if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                  mii_port_override | PORT_OVERRIDE_RV_MII_25);
+                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                 &mii_port_override);
+
+                       if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
+                               pr_err("Failed to enable reverse MII mode\n");
+                               return -EINVAL;
+                       }
+               }
+       } else if (is531x5(dev) && cpu_port == B53_CPU_PORT) {
+               u8 mii_port_override;
+
+               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                         &mii_port_override);
+               b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                          mii_port_override | PORT_OVERRIDE_EN |
+                          PORT_OVERRIDE_LINK);
+
+               /* BCM47189 has another interface connected to the port 5 */
+               if (dev->enabled_ports & BIT(5)) {
+                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(5);
+                       u8 gmii_po;
+
+                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
+                       gmii_po |= GMII_PO_LINK |
+                                  GMII_PO_RX_FLOW |
+                                  GMII_PO_TX_FLOW |
+                                  GMII_PO_EN;
+                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
+               }
+       } else if (is5301x(dev)) {
+               if (cpu_port == 8) {
+                       u8 mii_port_override;
+
+                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                 &mii_port_override);
+                       mii_port_override |= PORT_OVERRIDE_LINK |
+                                            PORT_OVERRIDE_RX_FLOW |
+                                            PORT_OVERRIDE_TX_FLOW |
+                                            PORT_OVERRIDE_SPEED_2000M |
+                                            PORT_OVERRIDE_EN;
+                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
+                                  mii_port_override);
+
+                       /* TODO: Ports 5 & 7 require some extra handling */
+               } else {
+                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(cpu_port);
+                       u8 gmii_po;
+
+                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
+                       gmii_po |= GMII_PO_LINK |
+                                  GMII_PO_RX_FLOW |
+                                  GMII_PO_TX_FLOW |
+                                  GMII_PO_EN |
+                                  GMII_PO_SPEED_2000M;
+                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
+               }
+       }
+
+       return 0;
+}
+
+static int b53_switch_reset(struct b53_device *dev)
+{
+       int ret = 0;
+       u8 mgmt;
+
+       b53_switch_reset_gpio(dev);
+
+       if (is539x(dev)) {
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
+               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
+       }
+
+       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+       if (!(mgmt & SM_SW_FWD_EN)) {
+               mgmt &= ~SM_SW_FWD_MODE;
+               mgmt |= SM_SW_FWD_EN;
+
+               b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+               b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
+
+               if (!(mgmt & SM_SW_FWD_EN)) {
+                       pr_err("Failed to enable switch!\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* enable all ports */
+       b53_enable_ports(dev);
+
+       if (dev->dev->of_node)
+               ret = b53_configure_ports_of(dev);
+       else
+               ret = b53_configure_ports(dev);
+
+       if (ret)
+               return ret;
+
+       b53_enable_mib(dev);
+
+       return b53_flush_arl(dev);
+}
+
+/*
+ * Swconfig glue functions
+ */
+
+static int b53_global_get_vlan_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->enable_vlan;
+
+       return 0;
+}
+
+static int b53_global_set_vlan_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->enable_vlan = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_jumbo_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->enable_jumbo;
+
+       return 0;
+}
+
+static int b53_global_set_jumbo_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->enable_jumbo = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_4095_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->value.i = priv->allow_vid_4095;
+
+       return 0;
+}
+
+static int b53_global_set_4095_enable(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       priv->allow_vid_4095 = val->value.i;
+
+       return 0;
+}
+
+static int b53_global_get_ports(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x",
+                           priv->enabled_ports);
+       val->value.s = priv->buf;
+
+       return 0;
+}
+
+static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       *val = priv->ports[port].pvid;
+
+       return 0;
+}
+
+static int b53_port_set_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (val > 15 && is5325(priv))
+               return -EINVAL;
+       if (val == 4095 && !priv->allow_vid_4095)
+               return -EINVAL;
+
+       priv->ports[port].pvid = val;
+
+       return 0;
+}
+
+static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       struct switch_port *port = &val->value.ports[0];
+       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+       int i;
+
+       val->len = 0;
+
+       if (!vlan->members)
+               return 0;
+
+       for (i = 0; i < dev->ports; i++) {
+               if (!(vlan->members & BIT(i)))
+                       continue;
+
+
+               if (!(vlan->untag & BIT(i)))
+                       port->flags = BIT(SWITCH_PORT_FLAG_TAGGED);
+               else
+                       port->flags = 0;
+
+               port->id = i;
+               val->len++;
+               port++;
+       }
+
+       return 0;
+}
+
+static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       struct switch_port *port;
+       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
+       int i;
+
+       /* only BCM5325 and BCM5365 supports VID 0 */
+       if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv))
+               return -EINVAL;
+
+       /* VLAN 4095 needs special handling */
+       if (val->port_vlan == 4095 && !priv->allow_vid_4095)
+               return -EINVAL;
+
+       port = &val->value.ports[0];
+       vlan->members = 0;
+       vlan->untag = 0;
+       for (i = 0; i < val->len; i++, port++) {
+               vlan->members |= BIT(port->id);
+
+               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) {
+                       vlan->untag |= BIT(port->id);
+                       priv->ports[port->id].pvid = val->port_vlan;
+               };
+       }
+
+       /* ignore disabled ports */
+       vlan->members &= priv->enabled_ports;
+       vlan->untag &= priv->enabled_ports;
+
+       return 0;
+}
+
+static int b53_port_get_link(struct switch_dev *dev, int port,
+                            struct switch_port_link *link)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (is_cpu_port(priv, port)) {
+               link->link = 1;
+               link->duplex = 1;
+               link->speed = is5325(priv) || is5365(priv) ?
+                               SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000;
+               link->aneg = 0;
+       } else if (priv->enabled_ports & BIT(port)) {
+               u32 speed;
+               u16 lnk, duplex;
+
+               b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk);
+               b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex);
+
+               lnk = (lnk >> port) & 1;
+               duplex = (duplex >> port) & 1;
+
+               if (is5325(priv) || is5365(priv)) {
+                       u16 tmp;
+
+                       b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp);
+                       speed = SPEED_PORT_FE(tmp, port);
+               } else {
+                       b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed);
+                       speed = SPEED_PORT_GE(speed, port);
+               }
+
+               link->link = lnk;
+               if (lnk) {
+                       link->duplex = duplex;
+                       switch (speed) {
+                       case SPEED_STAT_10M:
+                               link->speed = SWITCH_PORT_SPEED_10;
+                               break;
+                       case SPEED_STAT_100M:
+                               link->speed = SWITCH_PORT_SPEED_100;
+                               break;
+                       case SPEED_STAT_1000M:
+                               link->speed = SWITCH_PORT_SPEED_1000;
+                               break;
+                       }
+               }
+
+               link->aneg = 1;
+       } else {
+               link->link = 0;
+       }
+
+       return 0;
+
+}
+
+static int b53_port_set_link(struct switch_dev *sw_dev, int port,
+                            struct switch_port_link *link)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+
+       /*
+        * TODO: BCM63XX requires special handling as it can have external phys
+        * and ports might be GE or only FE
+        */
+       if (is63xx(dev))
+               return -ENOTSUPP;
+
+       if (port == sw_dev->cpu_port)
+               return -EINVAL;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -EINVAL;
+
+       if (link->speed == SWITCH_PORT_SPEED_1000 &&
+           (is5325(dev) || is5365(dev)))
+               return -EINVAL;
+
+       if (link->speed == SWITCH_PORT_SPEED_1000 && !link->duplex)
+               return -EINVAL;
+
+       return switch_generic_set_link(sw_dev, port, link);
+}
+
+static int b53_phy_read16(struct switch_dev *dev, int addr, u8 reg, u16 *value)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (priv->ops->phy_read16)
+               return priv->ops->phy_read16(priv, addr, reg, value);
+
+       return b53_read16(priv, B53_PORT_MII_PAGE(addr), reg, value);
+}
+
+static int b53_phy_write16(struct switch_dev *dev, int addr, u8 reg, u16 value)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       if (priv->ops->phy_write16)
+               return priv->ops->phy_write16(priv, addr, reg, value);
+
+       return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg, value);
+}
+
+static int b53_global_reset_switch(struct switch_dev *dev)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       /* reset vlans */
+       priv->enable_vlan = 0;
+       priv->enable_jumbo = 0;
+       priv->allow_vid_4095 = 0;
+
+       memset(priv->vlans, 0, sizeof(*priv->vlans) * dev->vlans);
+       memset(priv->ports, 0, sizeof(*priv->ports) * dev->ports);
+
+       return b53_switch_reset(priv);
+}
+
+static int b53_global_apply_config(struct switch_dev *dev)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+
+       /* disable switching */
+       b53_set_forwarding(priv, 0);
+
+       b53_apply(priv);
+
+       /* enable switching */
+       b53_set_forwarding(priv, 1);
+
+       return 0;
+}
+
+
+static int b53_global_reset_mib(struct switch_dev *dev,
+                               const struct switch_attr *attr,
+                               struct switch_val *val)
+{
+       struct b53_device *priv = sw_to_b53(dev);
+       u8 gc;
+
+       b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
+
+       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
+       mdelay(1);
+       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
+       mdelay(1);
+
+       return 0;
+}
+
+static int b53_port_get_mib(struct switch_dev *sw_dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+       const struct b53_mib_desc *mibs;
+       int port = val->port_vlan;
+       int len = 0;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -1;
+
+       if (is5365(dev)) {
+               if (port == 5)
+                       port = 8;
+
+               mibs = b53_mibs_65;
+       } else if (is63xx(dev)) {
+               mibs = b53_mibs_63xx;
+       } else {
+               mibs = b53_mibs;
+       }
+
+       dev->buf[0] = 0;
+
+       for (; mibs->size > 0; mibs++) {
+               u64 val;
+
+               if (mibs->size == 8) {
+                       b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val);
+               } else {
+                       u32 val32;
+
+                       b53_read32(dev, B53_MIB_PAGE(port), mibs->offset,
+                                  &val32);
+                       val = val32;
+               }
+
+               len += snprintf(dev->buf + len, B53_BUF_SIZE - len,
+                               "%-20s: %llu\n", mibs->name, val);
+       }
+
+       val->len = len;
+       val->value.s = dev->buf;
+
+       return 0;
+}
+
+static int b53_port_get_stats(struct switch_dev *sw_dev, int port,
+                               struct switch_port_stats *stats)
+{
+       struct b53_device *dev = sw_to_b53(sw_dev);
+       const struct b53_mib_desc *mibs;
+       int txb_id, rxb_id;
+       u64 rxb, txb;
+
+       if (!(BIT(port) & dev->enabled_ports))
+               return -EINVAL;
+
+       txb_id = B53XX_MIB_TXB_ID;
+       rxb_id = B53XX_MIB_RXB_ID;
+
+       if (is5365(dev)) {
+               if (port == 5)
+                       port = 8;
+
+               mibs = b53_mibs_65;
+       } else if (is63xx(dev)) {
+               mibs = b53_mibs_63xx;
+               txb_id = B63XX_MIB_TXB_ID;
+               rxb_id = B63XX_MIB_RXB_ID;
+       } else {
+               mibs = b53_mibs;
+       }
+
+       dev->buf[0] = 0;
+
+       if (mibs->size == 8) {
+               b53_read64(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &txb);
+               b53_read64(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &rxb);
+       } else {
+               u32 val32;
+
+               b53_read32(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &val32);
+               txb = val32;
+
+               b53_read32(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &val32);
+               rxb = val32;
+       }
+
+       stats->tx_bytes = txb;
+       stats->rx_bytes = rxb;
+
+       return 0;
+}
+
+static struct switch_attr b53_global_ops_25[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+};
+
+static struct switch_attr b53_global_ops_65[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "reset_mib",
+               .description = "Reset MIB counters",
+               .set = b53_global_reset_mib,
+       },
+};
+
+static struct switch_attr b53_global_ops[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = b53_global_set_vlan_enable,
+               .get = b53_global_get_vlan_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "ports",
+               .description = "Available Ports (as bitmask)",
+               .get = b53_global_get_ports,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "reset_mib",
+               .description = "Reset MIB counters",
+               .set = b53_global_reset_mib,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_jumbo",
+               .description = "Enable Jumbo Frames",
+               .set = b53_global_set_jumbo_enable,
+               .get = b53_global_get_jumbo_enable,
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "allow_vid_4095",
+               .description = "Allow VID 4095",
+               .set = b53_global_set_4095_enable,
+               .get = b53_global_get_4095_enable,
+               .max = 1,
+       },
+};
+
+static struct switch_attr b53_port_ops[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get port's MIB counters",
+               .get = b53_port_get_mib,
+       },
+};
+
+static struct switch_attr b53_no_ops[] = {
+};
+
+static const struct switch_dev_ops b53_switch_ops_25 = {
+       .attr_global = {
+               .attr = b53_global_ops_25,
+               .n_attr = ARRAY_SIZE(b53_global_ops_25),
+       },
+       .attr_port = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+static const struct switch_dev_ops b53_switch_ops_65 = {
+       .attr_global = {
+               .attr = b53_global_ops_65,
+               .n_attr = ARRAY_SIZE(b53_global_ops_65),
+       },
+       .attr_port = {
+               .attr = b53_port_ops,
+               .n_attr = ARRAY_SIZE(b53_port_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+static const struct switch_dev_ops b53_switch_ops = {
+       .attr_global = {
+               .attr = b53_global_ops,
+               .n_attr = ARRAY_SIZE(b53_global_ops),
+       },
+       .attr_port = {
+               .attr = b53_port_ops,
+               .n_attr = ARRAY_SIZE(b53_port_ops),
+       },
+       .attr_vlan = {
+               .attr = b53_no_ops,
+               .n_attr = ARRAY_SIZE(b53_no_ops),
+       },
+
+       .get_vlan_ports = b53_vlan_get_ports,
+       .set_vlan_ports = b53_vlan_set_ports,
+       .get_port_pvid = b53_port_get_pvid,
+       .set_port_pvid = b53_port_set_pvid,
+       .apply_config = b53_global_apply_config,
+       .reset_switch = b53_global_reset_switch,
+       .get_port_link = b53_port_get_link,
+       .set_port_link = b53_port_set_link,
+       .get_port_stats = b53_port_get_stats,
+       .phy_read16 = b53_phy_read16,
+       .phy_write16 = b53_phy_write16,
+};
+
+struct b53_chip_data {
+       u32 chip_id;
+       const char *dev_name;
+       const char *alias;
+       u16 vlans;
+       u16 enabled_ports;
+       u8 cpu_port;
+       u8 vta_regs[3];
+       u8 duplex_reg;
+       u8 jumbo_pm_reg;
+       u8 jumbo_size_reg;
+       const struct switch_dev_ops *sw_ops;
+};
+
+#define B53_VTA_REGS   \
+       { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
+#define B53_VTA_REGS_9798 \
+       { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
+#define B53_VTA_REGS_63XX \
+       { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
+
+static const struct b53_chip_data b53_switch_chips[] = {
+       {
+               .chip_id = BCM5325_DEVICE_ID,
+               .dev_name = "BCM5325",
+               .alias = "bcm5325",
+               .vlans = 16,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25,
+               .duplex_reg = B53_DUPLEX_STAT_FE,
+               .sw_ops = &b53_switch_ops_25,
+       },
+       {
+               .chip_id = BCM5365_DEVICE_ID,
+               .dev_name = "BCM5365",
+               .alias = "bcm5365",
+               .vlans = 256,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25,
+               .duplex_reg = B53_DUPLEX_STAT_FE,
+               .sw_ops = &b53_switch_ops_65,
+       },
+       {
+               .chip_id = BCM5395_DEVICE_ID,
+               .dev_name = "BCM5395",
+               .alias = "bcm5395",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM5397_DEVICE_ID,
+               .dev_name = "BCM5397",
+               .alias = "bcm5397",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_9798,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM5398_DEVICE_ID,
+               .dev_name = "BCM5398",
+               .alias = "bcm5398",
+               .vlans = 4096,
+               .enabled_ports = 0x7f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_9798,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53115_DEVICE_ID,
+               .dev_name = "BCM53115",
+               .alias = "bcm53115",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .vta_regs = B53_VTA_REGS,
+               .cpu_port = B53_CPU_PORT,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53125_DEVICE_ID,
+               .dev_name = "BCM53125",
+               .alias = "bcm53125",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53128_DEVICE_ID,
+               .dev_name = "BCM53128",
+               .alias = "bcm53128",
+               .vlans = 4096,
+               .enabled_ports = 0x1ff,
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM63XX_DEVICE_ID,
+               .dev_name = "BCM63xx",
+               .alias = "bcm63xx",
+               .vlans = 4096,
+               .enabled_ports = 0, /* pdata must provide them */
+               .cpu_port = B53_CPU_PORT,
+               .vta_regs = B53_VTA_REGS_63XX,
+               .duplex_reg = B53_DUPLEX_STAT_63XX,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53010_DEVICE_ID,
+               .dev_name = "BCM53010",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53011_DEVICE_ID,
+               .dev_name = "BCM53011",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53012_DEVICE_ID,
+               .dev_name = "BCM53012",
+               .alias = "bcm53011",
+               .vlans = 4096,
+               .enabled_ports = 0x1bf,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53018_DEVICE_ID,
+               .dev_name = "BCM53018",
+               .alias = "bcm53018",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+       {
+               .chip_id = BCM53019_DEVICE_ID,
+               .dev_name = "BCM53019",
+               .alias = "bcm53019",
+               .vlans = 4096,
+               .enabled_ports = 0x1f,
+               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
+               .vta_regs = B53_VTA_REGS,
+               .duplex_reg = B53_DUPLEX_STAT_GE,
+               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
+               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
+               .sw_ops = &b53_switch_ops,
+       },
+};
+
+static int b53_switch_init_of(struct b53_device *dev)
+{
+       struct device_node *dn, *pn;
+       const char *alias;
+       u32 port_num;
+       u16 ports = 0;
+
+       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
+       if (!dn)
+               return -EINVAL;
+
+       for_each_available_child_of_node(dn, pn) {
+               const char *label;
+               int len;
+
+               if (of_property_read_u32(pn, "reg", &port_num))
+                       continue;
+
+               if (port_num > B53_CPU_PORT)
+                       continue;
+
+               ports |= BIT(port_num);
+
+               label = of_get_property(pn, "label", &len);
+               if (label && !strcmp(label, "cpu"))
+                       dev->sw_dev.cpu_port = port_num;
+       }
+
+       dev->enabled_ports = ports;
+
+       if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias",
+                                                &alias))
+               dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL);
+
+       return 0;
+}
+
+static int b53_switch_init(struct b53_device *dev)
+{
+       struct switch_dev *sw_dev = &dev->sw_dev;
+       unsigned i;
+       int ret;
+
+       for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
+               const struct b53_chip_data *chip = &b53_switch_chips[i];
+
+               if (chip->chip_id == dev->chip_id) {
+                       sw_dev->name = chip->dev_name;
+                       if (!sw_dev->alias)
+                               sw_dev->alias = chip->alias;
+                       if (!dev->enabled_ports)
+                               dev->enabled_ports = chip->enabled_ports;
+                       dev->duplex_reg = chip->duplex_reg;
+                       dev->vta_regs[0] = chip->vta_regs[0];
+                       dev->vta_regs[1] = chip->vta_regs[1];
+                       dev->vta_regs[2] = chip->vta_regs[2];
+                       dev->jumbo_pm_reg = chip->jumbo_pm_reg;
+                       sw_dev->ops = chip->sw_ops;
+                       sw_dev->cpu_port = chip->cpu_port;
+                       sw_dev->vlans = chip->vlans;
+                       break;
+               }
+       }
+
+       if (!sw_dev->name)
+               return -EINVAL;
+
+       /* check which BCM5325x version we have */
+       if (is5325(dev)) {
+               u8 vc4;
+
+               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
+
+               /* check reserved bits */
+               switch (vc4 & 3) {
+               case 1:
+                       /* BCM5325E */
+                       break;
+               case 3:
+                       /* BCM5325F - do not use port 4 */
+                       dev->enabled_ports &= ~BIT(4);
+                       break;
+               default:
+/* On the BCM47XX SoCs this is the supported internal switch.*/
+#ifndef CONFIG_BCM47XX
+                       /* BCM5325M */
+                       return -EINVAL;
+#else
+                       break;
+#endif
+               }
+       } else if (dev->chip_id == BCM53115_DEVICE_ID) {
+               u64 strap_value;
+
+               b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
+               /* use second IMP port if GMII is enabled */
+               if (strap_value & SV_GMII_CTRL_115)
+                       sw_dev->cpu_port = 5;
+       }
+
+       if (dev_of_node(dev->dev)) {
+               ret = b53_switch_init_of(dev);
+               if (ret)
+                       return ret;
+       }
+
+       dev->enabled_ports |= BIT(sw_dev->cpu_port);
+       sw_dev->ports = fls(dev->enabled_ports);
+
+       dev->ports = devm_kzalloc(dev->dev,
+                                 sizeof(struct b53_port) * sw_dev->ports,
+                                 GFP_KERNEL);
+       if (!dev->ports)
+               return -ENOMEM;
+
+       dev->vlans = devm_kzalloc(dev->dev,
+                                 sizeof(struct b53_vlan) * sw_dev->vlans,
+                                 GFP_KERNEL);
+       if (!dev->vlans)
+               return -ENOMEM;
+
+       dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL);
+       if (!dev->buf)
+               return -ENOMEM;
+
+       dev->reset_gpio = b53_switch_get_reset_gpio(dev);
+       if (dev->reset_gpio >= 0) {
+               ret = devm_gpio_request_one(dev->dev, dev->reset_gpio,
+                                           GPIOF_OUT_INIT_HIGH, "robo_reset");
+               if (ret)
+                       return ret;
+       }
+
+       return b53_switch_reset(dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+                                   void *priv)
+{
+       struct b53_device *dev;
+
+       dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+
+       dev->dev = base;
+       dev->ops = ops;
+       dev->priv = priv;
+       mutex_init(&dev->reg_mutex);
+
+       return dev;
+}
+EXPORT_SYMBOL(b53_switch_alloc);
+
+int b53_switch_detect(struct b53_device *dev)
+{
+       u32 id32;
+       u16 tmp;
+       u8 id8;
+       int ret;
+
+       ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
+       if (ret)
+               return ret;
+
+       switch (id8) {
+       case 0:
+               /*
+                * BCM5325 and BCM5365 do not have this register so reads
+                * return 0. But the read operation did succeed, so assume
+                * this is one of them.
+                *
+                * Next check if we can write to the 5325's VTA register; for
+                * 5365 it is read only.
+                */
+
+               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
+               b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
+
+               if (tmp == 0xf)
+                       dev->chip_id = BCM5325_DEVICE_ID;
+               else
+                       dev->chip_id = BCM5365_DEVICE_ID;
+               break;
+       case BCM5395_DEVICE_ID:
+       case BCM5397_DEVICE_ID:
+       case BCM5398_DEVICE_ID:
+               dev->chip_id = id8;
+               break;
+       default:
+               ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
+               if (ret)
+                       return ret;
+
+               switch (id32) {
+               case BCM53115_DEVICE_ID:
+               case BCM53125_DEVICE_ID:
+               case BCM53128_DEVICE_ID:
+               case BCM53010_DEVICE_ID:
+               case BCM53011_DEVICE_ID:
+               case BCM53012_DEVICE_ID:
+               case BCM53018_DEVICE_ID:
+               case BCM53019_DEVICE_ID:
+                       dev->chip_id = id32;
+                       break;
+               default:
+                       pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
+                              id8, id32);
+                       return -ENODEV;
+               }
+       }
+
+       if (dev->chip_id == BCM5325_DEVICE_ID)
+               return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25,
+                                &dev->core_rev);
+       else
+               return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
+                                &dev->core_rev);
+}
+EXPORT_SYMBOL(b53_switch_detect);
+
+int b53_switch_register(struct b53_device *dev)
+{
+       int ret;
+
+       if (dev->pdata) {
+               dev->chip_id = dev->pdata->chip_id;
+               dev->enabled_ports = dev->pdata->enabled_ports;
+               dev->sw_dev.alias = dev->pdata->alias;
+       }
+
+       if (!dev->chip_id && b53_switch_detect(dev))
+               return -EINVAL;
+
+       ret = b53_switch_init(dev);
+       if (ret)
+               return ret;
+
+       pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev);
+
+       return register_switch(&dev->sw_dev, NULL);
+}
+EXPORT_SYMBOL(b53_switch_register);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 switch library");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_mdio.c
new file mode 100644 (file)
index 0000000..75bb4d9
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * B53 register access through MII registers
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/phy.h>
+#include <linux/module.h>
+
+#include "b53_priv.h"
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+/* MII registers */
+#define REG_MII_PAGE    0x10    /* MII Page register */
+#define REG_MII_ADDR    0x11    /* MII Address register */
+#define REG_MII_DATA0   0x18    /* MII Data register 0 */
+#define REG_MII_DATA1   0x19    /* MII Data register 1 */
+#define REG_MII_DATA2   0x1a    /* MII Data register 2 */
+#define REG_MII_DATA3   0x1b    /* MII Data register 3 */
+
+#define REG_MII_PAGE_ENABLE     BIT(0)
+#define REG_MII_ADDR_WRITE      BIT(0)
+#define REG_MII_ADDR_READ       BIT(1)
+
+static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
+{
+       int i;
+       u16 v;
+       int ret;
+       struct mii_bus *bus = dev->priv;
+
+       if (dev->current_page != page) {
+               /* set page number */
+               v = (page << 8) | REG_MII_PAGE_ENABLE;
+               ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
+               if (ret)
+                       return ret;
+               dev->current_page = page;
+       }
+
+       /* set register address */
+       v = (reg << 8) | op;
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
+       if (ret)
+               return ret;
+
+       /* check if operation completed */
+       for (i = 0; i < 5; ++i) {
+               v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
+               if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
+                       break;
+               usleep_range(10, 100);
+       }
+
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
+
+       return 0;
+}
+
+static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+
+       return 0;
+}
+
+static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
+       *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
+
+       return 0;
+}
+
+static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       u64 temp = 0;
+       int i;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       for (i = 2; i >= 0; i--) {
+               temp <<= 16;
+               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+       }
+
+       *val = temp;
+
+       return 0;
+}
+
+static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       struct mii_bus *bus = dev->priv;
+       u64 temp = 0;
+       int i;
+       int ret;
+
+       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
+       if (ret)
+               return ret;
+
+       for (i = 3; i >= 0; i--) {
+               temp <<= 16;
+               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
+       }
+
+       *val = temp;
+
+       return 0;
+}
+
+static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+       if (ret)
+               return ret;
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       struct mii_bus *bus = dev->priv;
+       int ret;
+
+       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
+       if (ret)
+               return ret;
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned int i;
+       u32 temp = value;
+
+       for (i = 0; i < 2; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned i;
+       u64 temp = value;
+
+       for (i = 0; i < 3; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+
+}
+
+static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       struct mii_bus *bus = dev->priv;
+       unsigned i;
+       u64 temp = value;
+
+       for (i = 0; i < 4; i++) {
+               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
+                                   temp & 0xffff);
+               if (ret)
+                       return ret;
+               temp >>= 16;
+       }
+
+       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
+}
+
+static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg,
+                              u16 *value)
+{
+       struct mii_bus *bus = dev->priv;
+
+       *value = mdiobus_read(bus, addr, reg);
+
+       return 0;
+}
+
+static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg,
+                               u16 value)
+{
+       struct mii_bus *bus = dev->priv;
+
+       return mdiobus_write(bus, addr, reg, value);
+}
+
+static struct b53_io_ops b53_mdio_ops = {
+       .read8 = b53_mdio_read8,
+       .read16 = b53_mdio_read16,
+       .read32 = b53_mdio_read32,
+       .read48 = b53_mdio_read48,
+       .read64 = b53_mdio_read64,
+       .write8 = b53_mdio_write8,
+       .write16 = b53_mdio_write16,
+       .write32 = b53_mdio_write32,
+       .write48 = b53_mdio_write48,
+       .write64 = b53_mdio_write64,
+       .phy_read16 = b53_mdio_phy_read16,
+       .phy_write16 = b53_mdio_phy_write16,
+};
+
+static int b53_phy_probe(struct phy_device *phydev)
+{
+       struct b53_device dev;
+       int ret;
+
+       /* allow the generic phy driver to take over */
+       if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
+               return -ENODEV;
+
+       dev.current_page = 0xff;
+       dev.priv = phydev->mdio.bus;
+       dev.ops = &b53_mdio_ops;
+       dev.pdata = NULL;
+       mutex_init(&dev.reg_mutex);
+
+       ret = b53_switch_detect(&dev);
+       if (ret)
+               return ret;
+
+       if (is5325(&dev) || is5365(&dev))
+               phydev->supported = SUPPORTED_100baseT_Full;
+       else
+               phydev->supported = SUPPORTED_1000baseT_Full;
+
+       phydev->advertising = phydev->supported;
+
+       return 0;
+}
+
+static int b53_phy_config_init(struct phy_device *phydev)
+{
+       struct b53_device *dev;
+       int ret;
+
+       dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
+       if (!dev)
+               return -ENOMEM;
+
+       /* we don't use page 0xff, so force a page set */
+       dev->current_page = 0xff;
+       /* force the ethX as alias */
+       dev->sw_dev.alias = phydev->attached_dev->name;
+
+       ret = b53_switch_register(dev);
+       if (ret) {
+               dev_err(dev->dev, "failed to register switch: %i\n", ret);
+               return ret;
+       }
+
+       phydev->priv = dev;
+
+       return 0;
+}
+
+static void b53_phy_remove(struct phy_device *phydev)
+{
+       struct b53_device *priv = phydev->priv;
+
+       if (!priv)
+               return;
+
+       b53_switch_remove(priv);
+
+       phydev->priv = NULL;
+}
+
+static int b53_phy_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int b53_phy_read_status(struct phy_device *phydev)
+{
+       struct b53_device *priv = phydev->priv;
+
+       if (is5325(priv) || is5365(priv))
+               phydev->speed = 100;
+       else
+               phydev->speed = 1000;
+
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+       phydev->state = PHY_RUNNING;
+
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+/* BCM5325, BCM539x */
+static struct phy_driver b53_phy_driver_id1 = {
+       .phy_id         = 0x0143bc00,
+       .name           = "Broadcom B53 (1)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+/* BCM53125, BCM53128 */
+static struct phy_driver b53_phy_driver_id2 = {
+       .phy_id         = 0x03625c00,
+       .name           = "Broadcom B53 (2)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+/* BCM5365 */
+static struct phy_driver b53_phy_driver_id3 = {
+       .phy_id         = 0x00406000,
+       .name           = "Broadcom B53 (3)",
+       .phy_id_mask    = 0x1ffffc00,
+       .features       = 0,
+       .probe          = b53_phy_probe,
+       .remove         = b53_phy_remove,
+       .config_aneg    = b53_phy_config_aneg,
+       .config_init    = b53_phy_config_init,
+       .read_status    = b53_phy_read_status,
+};
+
+int __init b53_phy_driver_register(void)
+{
+       int ret;
+
+       ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE);
+       if (ret)
+               return ret;
+
+       ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE);
+       if (ret)
+               goto err1;
+
+       ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE);
+       if (!ret)
+               return 0;
+
+       phy_driver_unregister(&b53_phy_driver_id2);
+err1:
+       phy_driver_unregister(&b53_phy_driver_id1);
+       return ret;
+}
+
+void __exit b53_phy_driver_unregister(void)
+{
+       phy_driver_unregister(&b53_phy_driver_id3);
+       phy_driver_unregister(&b53_phy_driver_id2);
+       phy_driver_unregister(&b53_phy_driver_id1);
+}
+
+module_init(b53_phy_driver_register);
+module_exit(b53_phy_driver_unregister);
+
+MODULE_DESCRIPTION("B53 MDIO access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_mmap.c
new file mode 100644 (file)
index 0000000..ab1895e
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * B53 register access through memory mapped registers
+ *
+ * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       *val = readb(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               *val = readw_be(regs + (page << 8) + reg);
+       else
+               *val = readw(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               *val = readl_be(regs + (page << 8) + reg);
+       else
+               *val = readl(regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (reg % 4) {
+               u16 lo;
+               u32 hi;
+
+               b53_mmap_read16(dev, page, reg, &lo);
+               b53_mmap_read32(dev, page, reg + 2, &hi);
+
+               *val = ((u64)hi << 16) | lo;
+       } else {
+               u32 lo;
+               u16 hi;
+
+               b53_mmap_read32(dev, page, reg, &lo);
+               b53_mmap_read16(dev, page, reg + 4, &hi);
+
+               *val = ((u64)hi << 32) | lo;
+       }
+
+       return 0;
+}
+
+static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u32 hi, lo;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       b53_mmap_read32(dev, page, reg, &lo);
+       b53_mmap_read32(dev, page, reg + 4, &hi);
+
+       *val = ((u64)hi << 32) | lo;
+
+       return 0;
+}
+
+static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       writeb(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               writew_be(value, regs + (page << 8) + reg);
+       else
+               writew(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       u8 __iomem *regs = dev->priv;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       if (dev->pdata && dev->pdata->big_endian)
+               writel_be(value, regs + (page << 8) + reg);
+       else
+               writel(value, regs + (page << 8) + reg);
+
+       return 0;
+}
+
+static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       if (WARN_ON(reg % 2))
+               return -EINVAL;
+
+       if (reg % 4) {
+               u32 hi = (u32)(value >> 16);
+               u16 lo = (u16)value;
+
+               b53_mmap_write16(dev, page, reg, lo);
+               b53_mmap_write32(dev, page, reg + 2, hi);
+       } else {
+               u16 hi = (u16)(value >> 32);
+               u32 lo = (u32)value;
+
+               b53_mmap_write32(dev, page, reg, lo);
+               b53_mmap_write16(dev, page, reg + 4, hi);
+       }
+
+       return 0;
+}
+
+static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       u32 hi, lo;
+
+       hi = (u32)(value >> 32);
+       lo = (u32)value;
+
+       if (WARN_ON(reg % 4))
+               return -EINVAL;
+
+       b53_mmap_write32(dev, page, reg, lo);
+       b53_mmap_write32(dev, page, reg + 4, hi);
+
+       return 0;
+}
+
+static struct b53_io_ops b53_mmap_ops = {
+       .read8 = b53_mmap_read8,
+       .read16 = b53_mmap_read16,
+       .read32 = b53_mmap_read32,
+       .read48 = b53_mmap_read48,
+       .read64 = b53_mmap_read64,
+       .write8 = b53_mmap_write8,
+       .write16 = b53_mmap_write16,
+       .write32 = b53_mmap_write32,
+       .write48 = b53_mmap_write48,
+       .write64 = b53_mmap_write64,
+};
+
+static int b53_mmap_probe(struct platform_device *pdev)
+{
+       struct b53_platform_data *pdata = pdev->dev.platform_data;
+       struct b53_device *dev;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
+       if (!dev)
+               return -ENOMEM;
+
+       if (pdata)
+               dev->pdata = pdata;
+
+       platform_set_drvdata(pdev, dev);
+
+       return b53_switch_register(dev);
+}
+
+static int b53_mmap_remove(struct platform_device *pdev)
+{
+       struct b53_device *dev = platform_get_drvdata(pdev);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static struct platform_driver b53_mmap_driver = {
+       .probe = b53_mmap_probe,
+       .remove = b53_mmap_remove,
+       .driver = {
+               .name = "b53-switch",
+       },
+};
+
+module_platform_driver(b53_mmap_driver);
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 MMAP access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_phy_fixup.c b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_phy_fixup.c
new file mode 100644 (file)
index 0000000..e2f8a39
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * B53 PHY Fixup call
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/phy.h>
+
+#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
+
+#define B53_BRCM_OUI_1 0x0143bc00
+#define B53_BRCM_OUI_2 0x03625c00
+#define B53_BRCM_OUI_3 0x00406000
+
+static int b53_phy_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u32 phy_id;
+
+       if (dev->mdio.addr != B53_PSEUDO_PHY)
+               return 0;
+
+       /* read the first port's id */
+       phy_id = mdiobus_read(bus, 0, 2) << 16;
+       phy_id |= mdiobus_read(bus, 0, 3);
+
+       if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
+           (phy_id & 0xfffffc00) == B53_BRCM_OUI_2 ||
+           (phy_id & 0xfffffc00) == B53_BRCM_OUI_3) {
+               dev->phy_id = phy_id;
+       }
+
+       return 0;
+}
+
+int __init b53_phy_fixup_register(void)
+{
+       return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
+}
+
+subsys_initcall(b53_phy_fixup_register);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_priv.h
new file mode 100644 (file)
index 0000000..a9296c9
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * B53 common definitions
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_PRIV_H
+#define __B53_PRIV_H
+
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/switch.h>
+
+struct b53_device;
+
+struct b53_io_ops {
+       int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
+       int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
+       int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
+       int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+       int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
+       int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
+       int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
+       int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
+       int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+       int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
+       int (*phy_read16)(struct b53_device *dev, int addr, u8 reg, u16 *value);
+       int (*phy_write16)(struct b53_device *dev, int addr, u8 reg, u16 value);
+};
+
+enum {
+       BCM5325_DEVICE_ID = 0x25,
+       BCM5365_DEVICE_ID = 0x65,
+       BCM5395_DEVICE_ID = 0x95,
+       BCM5397_DEVICE_ID = 0x97,
+       BCM5398_DEVICE_ID = 0x98,
+       BCM53115_DEVICE_ID = 0x53115,
+       BCM53125_DEVICE_ID = 0x53125,
+       BCM53128_DEVICE_ID = 0x53128,
+       BCM63XX_DEVICE_ID = 0x6300,
+       BCM53010_DEVICE_ID = 0x53010,
+       BCM53011_DEVICE_ID = 0x53011,
+       BCM53012_DEVICE_ID = 0x53012,
+       BCM53018_DEVICE_ID = 0x53018,
+       BCM53019_DEVICE_ID = 0x53019,
+};
+
+#define B53_N_PORTS    9
+#define B53_N_PORTS_25 6
+
+struct b53_vlan {
+       unsigned int    members:B53_N_PORTS;
+       unsigned int    untag:B53_N_PORTS;
+};
+
+struct b53_port {
+       unsigned int    pvid:12;
+};
+
+struct b53_device {
+       struct switch_dev sw_dev;
+       struct b53_platform_data *pdata;
+
+       struct mutex reg_mutex;
+       const struct b53_io_ops *ops;
+
+       /* chip specific data */
+       u32 chip_id;
+       u8 core_rev;
+       u8 vta_regs[3];
+       u8 duplex_reg;
+       u8 jumbo_pm_reg;
+       u8 jumbo_size_reg;
+       int reset_gpio;
+
+       /* used ports mask */
+       u16 enabled_ports;
+
+       /* connect specific data */
+       u8 current_page;
+       struct device *dev;
+       void *priv;
+
+       /* run time configuration */
+       unsigned enable_vlan:1;
+       unsigned enable_jumbo:1;
+       unsigned allow_vid_4095:1;
+
+       struct b53_port *ports;
+       struct b53_vlan *vlans;
+
+       char *buf;
+};
+
+#define b53_for_each_port(dev, i) \
+       for (i = 0; i < B53_N_PORTS; i++) \
+               if (dev->enabled_ports & BIT(i))
+
+
+
+static inline int is5325(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5325_DEVICE_ID;
+}
+
+static inline int is5365(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+       return dev->chip_id == BCM5365_DEVICE_ID;
+#else
+       return 0;
+#endif
+}
+
+static inline int is5397_98(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5397_DEVICE_ID ||
+               dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is539x(struct b53_device *dev)
+{
+       return dev->chip_id == BCM5395_DEVICE_ID ||
+               dev->chip_id == BCM5397_DEVICE_ID ||
+               dev->chip_id == BCM5398_DEVICE_ID;
+}
+
+static inline int is531x5(struct b53_device *dev)
+{
+       return dev->chip_id == BCM53115_DEVICE_ID ||
+               dev->chip_id == BCM53125_DEVICE_ID ||
+               dev->chip_id == BCM53128_DEVICE_ID;
+}
+
+static inline int is63xx(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM63XX
+       return dev->chip_id == BCM63XX_DEVICE_ID;
+#else
+       return 0;
+#endif
+}
+
+static inline int is5301x(struct b53_device *dev)
+{
+       return dev->chip_id == BCM53010_DEVICE_ID ||
+               dev->chip_id == BCM53011_DEVICE_ID ||
+               dev->chip_id == BCM53012_DEVICE_ID ||
+               dev->chip_id == BCM53018_DEVICE_ID ||
+               dev->chip_id == BCM53019_DEVICE_ID;
+}
+
+#define B53_CPU_PORT_25        5
+#define B53_CPU_PORT   8
+
+static inline int is_cpu_port(struct b53_device *dev, int port)
+{
+       return dev->sw_dev.cpu_port == port;
+}
+
+static inline int is_imp_port(struct b53_device *dev, int port)
+{
+       if (is5325(dev) || is5365(dev))
+               return port == B53_CPU_PORT_25;
+       else
+               return port == B53_CPU_PORT;
+}
+
+static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
+{
+       return container_of(sw, struct b53_device, sw_dev);
+}
+
+struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
+                                   void *priv);
+
+int b53_switch_detect(struct b53_device *dev);
+
+int b53_switch_register(struct b53_device *dev);
+
+static inline void b53_switch_remove(struct b53_device *dev)
+{
+       unregister_switch(&dev->sw_dev);
+}
+
+static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read8(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read16(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read32(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read48(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->read64(dev, page, reg, val);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write8(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
+                             u16 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write16(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
+                             u32 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write32(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
+                             u64 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write48(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
+                              u64 value)
+{
+       int ret;
+
+       mutex_lock(&dev->reg_mutex);
+       ret = dev->ops->write64(dev, page, reg, value);
+       mutex_unlock(&dev->reg_mutex);
+
+       return ret;
+}
+
+#ifdef CONFIG_BCM47XX
+#include <bcm47xx_board.h>
+#endif
+
+#include <linux/version.h>
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+#include <linux/bcm47xx_nvram.h>
+#endif
+static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
+{
+#ifdef CONFIG_BCM47XX
+       enum bcm47xx_board board = bcm47xx_board_get();
+
+       switch (board) {
+       case BCM47XX_BOARD_LINKSYS_WRT300NV11:
+       case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+               return 8;
+       default:
+               break;
+       }
+#endif
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
+       return bcm47xx_nvram_gpio_pin("robo_reset");
+#else
+       return -ENOENT;
+#endif
+}
+
+#endif
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_regs.h
new file mode 100644 (file)
index 0000000..f0bf674
--- /dev/null
@@ -0,0 +1,348 @@
+/*
+ * B53 register definitions
+ *
+ * Copyright (C) 2004 Broadcom Corporation
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_REGS_H
+#define __B53_REGS_H
+
+/* Management Port (SMP) Page offsets */
+#define B53_CTRL_PAGE                  0x00 /* Control */
+#define B53_STAT_PAGE                  0x01 /* Status */
+#define B53_MGMT_PAGE                  0x02 /* Management Mode */
+#define B53_MIB_AC_PAGE                        0x03 /* MIB Autocast */
+#define B53_ARLCTRL_PAGE               0x04 /* ARL Control */
+#define B53_ARLIO_PAGE                 0x05 /* ARL Access */
+#define B53_FRAMEBUF_PAGE              0x06 /* Management frame access */
+#define B53_MEM_ACCESS_PAGE            0x08 /* Memory access */
+
+/* PHY Registers */
+#define B53_PORT_MII_PAGE(i)           (0x10 + (i)) /* Port i MII Registers */
+#define B53_IM_PORT_PAGE               0x18 /* Inverse MII Port (to EMAC) */
+#define B53_ALL_PORT_PAGE              0x19 /* All ports MII (broadcast) */
+
+/* MIB registers */
+#define B53_MIB_PAGE(i)                        (0x20 + (i))
+
+/* Quality of Service (QoS) Registers */
+#define B53_QOS_PAGE                   0x30
+
+/* Port VLAN Page */
+#define B53_PVLAN_PAGE                 0x31
+
+/* VLAN Registers */
+#define B53_VLAN_PAGE                  0x34
+
+/* Jumbo Frame Registers */
+#define B53_JUMBO_PAGE                 0x40
+
+/* CFP Configuration Registers Page */
+#define B53_CFP_PAGE                   0xa1
+
+/*************************************************************************
+ * Control Page registers
+ *************************************************************************/
+
+/* Port Control Register (8 bit) */
+#define B53_PORT_CTRL(i)               (0x00 + (i))
+#define   PORT_CTRL_RX_DISABLE         BIT(0)
+#define   PORT_CTRL_TX_DISABLE         BIT(1)
+#define   PORT_CTRL_RX_BCST_EN         BIT(2) /* Broadcast RX (P8 only) */
+#define   PORT_CTRL_RX_MCST_EN         BIT(3) /* Multicast RX (P8 only) */
+#define   PORT_CTRL_RX_UCST_EN         BIT(4) /* Unicast RX (P8 only) */
+#define          PORT_CTRL_STP_STATE_S         5
+#define   PORT_CTRL_STP_STATE_MASK     (0x7 << PORT_CTRL_STP_STATE_S)
+
+/* SMP Control Register (8 bit) */
+#define B53_SMP_CTRL                   0x0a
+
+/* Switch Mode Control Register (8 bit) */
+#define B53_SWITCH_MODE                        0x0b
+#define   SM_SW_FWD_MODE               BIT(0)  /* 1 = Managed Mode */
+#define   SM_SW_FWD_EN                 BIT(1)  /* Forwarding Enable */
+
+/* IMP Port state override register (8 bit) */
+#define B53_PORT_OVERRIDE_CTRL         0x0e
+#define   PORT_OVERRIDE_LINK           BIT(0)
+#define   PORT_OVERRIDE_FULL_DUPLEX    BIT(1) /* 0 = Half Duplex */
+#define   PORT_OVERRIDE_SPEED_S                2
+#define   PORT_OVERRIDE_SPEED_10M      (0 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_SPEED_100M     (1 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_SPEED_1000M    (2 << PORT_OVERRIDE_SPEED_S)
+#define   PORT_OVERRIDE_RV_MII_25      BIT(4) /* BCM5325 only */
+#define   PORT_OVERRIDE_RX_FLOW                BIT(4)
+#define   PORT_OVERRIDE_TX_FLOW                BIT(5)
+#define   PORT_OVERRIDE_SPEED_2000M    BIT(6) /* BCM5301X only, requires setting 1000M */
+#define   PORT_OVERRIDE_EN             BIT(7) /* Use the register contents */
+
+/* Power-down mode control */
+#define B53_PD_MODE_CTRL_25            0x0f
+
+/* IP Multicast control (8 bit) */
+#define B53_IP_MULTICAST_CTRL          0x21
+#define  B53_IPMC_FWD_EN               BIT(1)
+#define  B53_UC_FWD_EN                 BIT(6)
+#define  B53_MC_FWD_EN                 BIT(7)
+
+/* (16 bit) */
+#define B53_UC_FLOOD_MASK              0x32
+#define B53_MC_FLOOD_MASK              0x34
+#define B53_IPMC_FLOOD_MASK            0x36
+
+/*
+ * Override Ports 0-7 State on devices with xMII interfaces (8 bit)
+ *
+ * For port 8 still use B53_PORT_OVERRIDE_CTRL
+ * Please note that not all ports are available on every hardware, e.g. BCM5301X
+ * don't include overriding port 6, BCM63xx also have some limitations.
+ */
+#define B53_GMII_PORT_OVERRIDE_CTRL(i) (0x58 + (i))
+#define   GMII_PO_LINK                 BIT(0)
+#define   GMII_PO_FULL_DUPLEX          BIT(1) /* 0 = Half Duplex */
+#define   GMII_PO_SPEED_S              2
+#define   GMII_PO_SPEED_10M            (0 << GMII_PO_SPEED_S)
+#define   GMII_PO_SPEED_100M           (1 << GMII_PO_SPEED_S)
+#define   GMII_PO_SPEED_1000M          (2 << GMII_PO_SPEED_S)
+#define   GMII_PO_RX_FLOW              BIT(4)
+#define   GMII_PO_TX_FLOW              BIT(5)
+#define   GMII_PO_EN                   BIT(6) /* Use the register contents */
+#define   GMII_PO_SPEED_2000M          BIT(7) /* BCM5301X only, requires setting 1000M */
+
+/* Software reset register (8 bit) */
+#define B53_SOFTRESET                  0x79
+
+/* Fast Aging Control register (8 bit) */
+#define B53_FAST_AGE_CTRL              0x88
+#define   FAST_AGE_STATIC              BIT(0)
+#define   FAST_AGE_DYNAMIC             BIT(1)
+#define   FAST_AGE_PORT                        BIT(2)
+#define   FAST_AGE_VLAN                        BIT(3)
+#define   FAST_AGE_STP                 BIT(4)
+#define   FAST_AGE_MC                  BIT(5)
+#define   FAST_AGE_DONE                        BIT(7)
+
+/*************************************************************************
+ * Status Page registers
+ *************************************************************************/
+
+/* Link Status Summary Register (16bit) */
+#define B53_LINK_STAT                  0x00
+
+/* Link Status Change Register (16 bit) */
+#define B53_LINK_STAT_CHANGE           0x02
+
+/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
+#define B53_SPEED_STAT                 0x04
+#define  SPEED_PORT_FE(reg, port)      (((reg) >> (port)) & 1)
+#define  SPEED_PORT_GE(reg, port)      (((reg) >> 2 * (port)) & 3)
+#define  SPEED_STAT_10M                        0
+#define  SPEED_STAT_100M               1
+#define  SPEED_STAT_1000M              2
+
+/* Duplex Status Summary (16 bit) */
+#define B53_DUPLEX_STAT_FE             0x06
+#define B53_DUPLEX_STAT_GE             0x08
+#define B53_DUPLEX_STAT_63XX           0x0c
+
+/* Revision ID register for BCM5325 */
+#define B53_REV_ID_25                  0x50
+
+/* Strap Value (48 bit) */
+#define B53_STRAP_VALUE                        0x70
+#define   SV_GMII_CTRL_115             BIT(27)
+
+/*************************************************************************
+ * Management Mode Page Registers
+ *************************************************************************/
+
+/* Global Management Config Register (8 bit) */
+#define B53_GLOBAL_CONFIG              0x00
+#define   GC_RESET_MIB                 0x01
+#define   GC_RX_BPDU_EN                        0x02
+#define   GC_MIB_AC_HDR_EN             0x10
+#define   GC_MIB_AC_EN                 0x20
+#define   GC_FRM_MGMT_PORT_M           0xC0
+#define   GC_FRM_MGMT_PORT_04          0x00
+#define   GC_FRM_MGMT_PORT_MII         0x80
+
+/* Broadcom Header control register (8 bit) */
+#define B53_BRCM_HDR                   0x03
+#define   BRCM_HDR_P8_EN               BIT(0) /* Enable tagging on port 8 */
+#define   BRCM_HDR_P5_EN               BIT(1) /* Enable tagging on port 5 */
+
+/* Device ID register (8 or 32 bit) */
+#define B53_DEVICE_ID                  0x30
+
+/* Revision ID register (8 bit) */
+#define B53_REV_ID                     0x40
+
+/*************************************************************************
+ * ARL Access Page Registers
+ *************************************************************************/
+
+/* VLAN Table Access Register (8 bit) */
+#define B53_VT_ACCESS                  0x80
+#define B53_VT_ACCESS_9798             0x60 /* for BCM5397/BCM5398 */
+#define B53_VT_ACCESS_63XX             0x60 /* for BCM6328/62/68 */
+#define   VTA_CMD_WRITE                        0
+#define   VTA_CMD_READ                 1
+#define   VTA_CMD_CLEAR                        2
+#define   VTA_START_CMD                        BIT(7)
+
+/* VLAN Table Index Register (16 bit) */
+#define B53_VT_INDEX                   0x81
+#define B53_VT_INDEX_9798              0x61
+#define B53_VT_INDEX_63XX              0x62
+
+/* VLAN Table Entry Register (32 bit) */
+#define B53_VT_ENTRY                   0x83
+#define B53_VT_ENTRY_9798              0x63
+#define B53_VT_ENTRY_63XX              0x64
+#define   VTE_MEMBERS                  0x1ff
+#define   VTE_UNTAG_S                  9
+#define   VTE_UNTAG                    (0x1ff << 9)
+
+/*************************************************************************
+ * Port VLAN Registers
+ *************************************************************************/
+
+/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
+#define B53_PVLAN_PORT_MASK(i)         ((i) * 2)
+
+/*************************************************************************
+ * 802.1Q Page Registers
+ *************************************************************************/
+
+/* Global QoS Control (8 bit) */
+#define B53_QOS_GLOBAL_CTL             0x00
+
+/* Enable 802.1Q for individual Ports (16 bit) */
+#define B53_802_1P_EN                  0x04
+
+/*************************************************************************
+ * VLAN Page Registers
+ *************************************************************************/
+
+/* VLAN Control 0 (8 bit) */
+#define B53_VLAN_CTRL0                 0x00
+#define   VC0_8021PF_CTRL_MASK         0x3
+#define   VC0_8021PF_CTRL_NONE         0x0
+#define   VC0_8021PF_CTRL_CHANGE_PRI   0x1
+#define   VC0_8021PF_CTRL_CHANGE_VID   0x2
+#define   VC0_8021PF_CTRL_CHANGE_BOTH  0x3
+#define   VC0_8021QF_CTRL_MASK         0xc
+#define   VC0_8021QF_CTRL_CHANGE_PRI   0x1
+#define   VC0_8021QF_CTRL_CHANGE_VID   0x2
+#define   VC0_8021QF_CTRL_CHANGE_BOTH  0x3
+#define   VC0_RESERVED_1               BIT(1)
+#define   VC0_DROP_VID_MISS            BIT(4)
+#define   VC0_VID_HASH_VID             BIT(5)
+#define   VC0_VID_CHK_EN               BIT(6)  /* Use VID,DA or VID,SA */
+#define   VC0_VLAN_EN                  BIT(7)  /* 802.1Q VLAN Enabled */
+
+/* VLAN Control 1 (8 bit) */
+#define B53_VLAN_CTRL1                 0x01
+#define   VC1_RX_MCST_TAG_EN           BIT(1)
+#define   VC1_RX_MCST_FWD_EN           BIT(2)
+#define   VC1_RX_MCST_UNTAG_EN         BIT(3)
+
+/* VLAN Control 2 (8 bit) */
+#define B53_VLAN_CTRL2                 0x02
+
+/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
+#define B53_VLAN_CTRL3                 0x03
+#define B53_VLAN_CTRL3_63XX            0x04
+#define   VC3_MAXSIZE_1532             BIT(6) /* 5325 only */
+#define   VC3_HIGH_8BIT_EN             BIT(7) /* 5325 only */
+
+/* VLAN Control 4 (8 bit) */
+#define B53_VLAN_CTRL4                 0x05
+#define B53_VLAN_CTRL4_25              0x04
+#define B53_VLAN_CTRL4_63XX            0x06
+#define   VC4_ING_VID_CHECK_S          6
+#define   VC4_ING_VID_CHECK_MASK       (0x3 << VC4_ING_VID_CHECK_S)
+#define   VC4_ING_VID_VIO_FWD          0 /* forward, but do not learn */
+#define   VC4_ING_VID_VIO_DROP         1 /* drop VID violations */
+#define   VC4_NO_ING_VID_CHK           2 /* do not check */
+#define   VC4_ING_VID_VIO_TO_IMP       3 /* redirect to MII port */
+
+/* VLAN Control 5 (8 bit) */
+#define B53_VLAN_CTRL5                 0x06
+#define B53_VLAN_CTRL5_25              0x05
+#define B53_VLAN_CTRL5_63XX            0x07
+#define   VC5_VID_FFF_EN               BIT(2)
+#define   VC5_DROP_VTABLE_MISS         BIT(3)
+
+/* VLAN Control 6 (8 bit) */
+#define B53_VLAN_CTRL6                 0x07
+#define B53_VLAN_CTRL6_63XX            0x08
+
+/* VLAN Table Access Register (16 bit) */
+#define B53_VLAN_TABLE_ACCESS_25       0x06    /* BCM5325E/5350 */
+#define B53_VLAN_TABLE_ACCESS_65       0x08    /* BCM5365 */
+#define   VTA_VID_LOW_MASK_25          0xf
+#define   VTA_VID_LOW_MASK_65          0xff
+#define   VTA_VID_HIGH_S_25            4
+#define   VTA_VID_HIGH_S_65            8
+#define   VTA_VID_HIGH_MASK_25         (0xff << VTA_VID_HIGH_S_25E)
+#define   VTA_VID_HIGH_MASK_65         (0xf << VTA_VID_HIGH_S_65)
+#define   VTA_RW_STATE                 BIT(12)
+#define   VTA_RW_STATE_RD              0
+#define   VTA_RW_STATE_WR              BIT(12)
+#define   VTA_RW_OP_EN                 BIT(13)
+
+/* VLAN Read/Write Registers for (16/32 bit) */
+#define B53_VLAN_WRITE_25              0x08
+#define B53_VLAN_WRITE_65              0x0a
+#define B53_VLAN_READ                  0x0c
+#define   VA_MEMBER_MASK               0x3f
+#define   VA_UNTAG_S_25                        6
+#define   VA_UNTAG_MASK_25             0x3f
+#define   VA_UNTAG_S_65                        7
+#define   VA_UNTAG_MASK_65             0x1f
+#define   VA_VID_HIGH_S                        12
+#define   VA_VID_HIGH_MASK             (0xffff << VA_VID_HIGH_S)
+#define   VA_VALID_25                  BIT(20)
+#define   VA_VALID_25_R4               BIT(24)
+#define   VA_VALID_65                  BIT(14)
+
+/* VLAN Port Default Tag (16 bit) */
+#define B53_VLAN_PORT_DEF_TAG(i)       (0x10 + 2 * (i))
+
+/*************************************************************************
+ * Jumbo Frame Page Registers
+ *************************************************************************/
+
+/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
+#define B53_JUMBO_PORT_MASK            0x01
+#define B53_JUMBO_PORT_MASK_63XX       0x04
+#define   JPM_10_100_JUMBO_EN          BIT(24) /* GigE always enabled */
+
+/* Good Frame Max Size without 802.1Q TAG (16 bit) */
+#define B53_JUMBO_MAX_SIZE             0x05
+#define B53_JUMBO_MAX_SIZE_63XX                0x08
+#define   JMS_MIN_SIZE                 1518
+#define   JMS_MAX_SIZE                 9724
+
+/*************************************************************************
+ * CFP Configuration Page Registers
+ *************************************************************************/
+
+/* CFP Control Register with ports map (8 bit) */
+#define B53_CFP_CTRL                   0x00
+
+#endif /* !__B53_REGS_H */
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_spi.c
new file mode 100644 (file)
index 0000000..efc8f7e
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * B53 register access through SPI
+ *
+ * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <asm/unaligned.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/of.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+#define B53_SPI_DATA           0xf0
+
+#define B53_SPI_STATUS         0xfe
+#define B53_SPI_CMD_SPIF       BIT(7)
+#define B53_SPI_CMD_RACK       BIT(5)
+
+#define B53_SPI_CMD_READ       0x00
+#define B53_SPI_CMD_WRITE      0x01
+#define B53_SPI_CMD_NORMAL     0x60
+#define B53_SPI_CMD_FAST       0x10
+
+#define B53_SPI_PAGE_SELECT    0xff
+
+static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
+                                    unsigned len)
+{
+       u8 txbuf[2];
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
+       txbuf[1] = reg;
+
+       return spi_write_then_read(spi, txbuf, 2, val, len);
+}
+
+static inline int b53_spi_clear_status(struct spi_device *spi)
+{
+       unsigned int i;
+       u8 rxbuf;
+       int ret;
+
+       for (i = 0; i < 10; i++) {
+               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+               if (ret)
+                       return ret;
+
+               if (!(rxbuf & B53_SPI_CMD_SPIF))
+                       break;
+
+               mdelay(1);
+       }
+
+       if (i == 10)
+               return -EIO;
+
+       return 0;
+}
+
+static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
+{
+       u8 txbuf[3];
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = B53_SPI_PAGE_SELECT;
+       txbuf[2] = page;
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
+{
+       int ret = b53_spi_clear_status(spi);
+
+       if (ret)
+               return ret;
+
+       return b53_spi_set_page(spi, page);
+}
+
+static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
+{
+       u8 rxbuf;
+       int retry_count;
+       int ret;
+
+       ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
+       if (ret)
+               return ret;
+
+       for (retry_count = 0; retry_count < 10; retry_count++) {
+               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
+               if (ret)
+                       return ret;
+
+               if (rxbuf & B53_SPI_CMD_RACK)
+                       break;
+
+               mdelay(1);
+       }
+
+       if (retry_count == 10)
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
+                       unsigned len)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       ret = b53_spi_prepare_reg_read(spi, reg);
+       if (ret)
+               return ret;
+
+       return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
+}
+
+static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       return b53_spi_read(dev, page, reg, val, 1);
+}
+
+static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
+
+       if (!ret)
+               *val = le16_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
+
+       if (!ret)
+               *val = le32_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret;
+
+       *val = 0;
+       ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
+       if (!ret)
+               *val = le64_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
+
+       if (!ret)
+               *val = le64_to_cpu(*val);
+
+       return ret;
+}
+
+static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[3];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       txbuf[2] = value;
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[4];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le16(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[6];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le32(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[10];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le64(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf) - 2);
+}
+
+static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
+{
+       struct spi_device *spi = dev->priv;
+       int ret;
+       u8 txbuf[10];
+
+       ret = b53_prepare_reg_access(spi, page);
+       if (ret)
+               return ret;
+
+       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
+       txbuf[1] = reg;
+       put_unaligned_le64(value, &txbuf[2]);
+
+       return spi_write(spi, txbuf, sizeof(txbuf));
+}
+
+static struct b53_io_ops b53_spi_ops = {
+       .read8 = b53_spi_read8,
+       .read16 = b53_spi_read16,
+       .read32 = b53_spi_read32,
+       .read48 = b53_spi_read48,
+       .read64 = b53_spi_read64,
+       .write8 = b53_spi_write8,
+       .write16 = b53_spi_write16,
+       .write32 = b53_spi_write32,
+       .write48 = b53_spi_write48,
+       .write64 = b53_spi_write64,
+};
+
+static int b53_spi_probe(struct spi_device *spi)
+{
+       struct b53_device *dev;
+       int ret;
+
+       dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);
+       if (!dev)
+               return -ENOMEM;
+
+       if (spi->dev.platform_data)
+               dev->pdata = spi->dev.platform_data;
+
+       ret = b53_switch_register(dev);
+       if (ret)
+               return ret;
+
+       spi_set_drvdata(spi, dev);
+
+       return 0;
+}
+
+static int b53_spi_remove(struct spi_device *spi)
+{
+       struct b53_device *dev = spi_get_drvdata(spi);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static const struct of_device_id b53_of_match[] = {
+       { .compatible = "brcm,bcm5325" },
+       { .compatible = "brcm,bcm53115" },
+       { .compatible = "brcm,bcm53125" },
+       { .compatible = "brcm,bcm53128" },
+       { .compatible = "brcm,bcm5365" },
+       { .compatible = "brcm,bcm5395" },
+       { .compatible = "brcm,bcm5397" },
+       { .compatible = "brcm,bcm5398" },
+       { /* sentinel */ },
+};
+
+static struct spi_driver b53_spi_driver = {
+       .driver = {
+               .name   = "b53-switch",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+               .of_match_table = b53_of_match,
+       },
+       .probe  = b53_spi_probe,
+       .remove = b53_spi_remove,
+};
+
+module_spi_driver(b53_spi_driver);
+
+MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
+MODULE_DESCRIPTION("B53 SPI access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_srab.c b/target/linux/generic/files-4.9/drivers/net/phy/b53/b53_srab.c
new file mode 100644 (file)
index 0000000..012daa3
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * B53 register access through Switch Register Access Bridge Registers
+ *
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/b53.h>
+
+#include "b53_priv.h"
+
+/* command and status register of the SRAB */
+#define B53_SRAB_CMDSTAT               0x2c
+#define  B53_SRAB_CMDSTAT_RST          BIT(2)
+#define  B53_SRAB_CMDSTAT_WRITE                BIT(1)
+#define  B53_SRAB_CMDSTAT_GORDYN       BIT(0)
+#define  B53_SRAB_CMDSTAT_PAGE         24
+#define  B53_SRAB_CMDSTAT_REG          16
+
+/* high order word of write data to switch registe */
+#define B53_SRAB_WD_H                  0x30
+
+/* low order word of write data to switch registe */
+#define B53_SRAB_WD_L                  0x34
+
+/* high order word of read data from switch register */
+#define B53_SRAB_RD_H                  0x38
+
+/* low order word of read data from switch register */
+#define B53_SRAB_RD_L                  0x3c
+
+/* command and status register of the SRAB */
+#define B53_SRAB_CTRLS                 0x40
+#define  B53_SRAB_CTRLS_RCAREQ         BIT(3)
+#define  B53_SRAB_CTRLS_RCAGNT         BIT(4)
+#define  B53_SRAB_CTRLS_SW_INIT_DONE   BIT(6)
+
+/* the register captures interrupt pulses from the switch */
+#define B53_SRAB_INTR                  0x44
+
+static int b53_srab_request_grant(struct b53_device *dev)
+{
+       u8 __iomem *regs = dev->priv;
+       u32 ctrls;
+       int i;
+
+       ctrls = readl(regs + B53_SRAB_CTRLS);
+       ctrls |= B53_SRAB_CTRLS_RCAREQ;
+       writel(ctrls, regs + B53_SRAB_CTRLS);
+
+       for (i = 0; i < 20; i++) {
+               ctrls = readl(regs + B53_SRAB_CTRLS);
+               if (ctrls & B53_SRAB_CTRLS_RCAGNT)
+                       break;
+               usleep_range(10, 100);
+       }
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static void b53_srab_release_grant(struct b53_device *dev)
+{
+       u8 __iomem *regs = dev->priv;
+       u32 ctrls;
+
+       ctrls = readl(regs + B53_SRAB_CTRLS);
+       ctrls &= ~B53_SRAB_CTRLS_RCAREQ;
+       writel(ctrls, regs + B53_SRAB_CTRLS);
+}
+
+static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)
+{
+       int i;
+       u32 cmdstat;
+       u8 __iomem *regs = dev->priv;
+
+       /* set register address */
+       cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |
+                 (reg << B53_SRAB_CMDSTAT_REG) |
+                 B53_SRAB_CMDSTAT_GORDYN |
+                 op;
+       writel(cmdstat, regs + B53_SRAB_CMDSTAT);
+
+       /* check if operation completed */
+       for (i = 0; i < 5; ++i) {
+               cmdstat = readl(regs + B53_SRAB_CMDSTAT);
+               if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))
+                       break;
+               usleep_range(10, 100);
+       }
+
+       if (WARN_ON(i == 5))
+               return -EIO;
+
+       return 0;
+}
+
+static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L) & 0xff;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L) & 0xffff;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+       *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       ret = b53_srab_op(dev, page, reg, 0);
+       if (ret)
+               goto err;
+
+       *val = readl(regs + B53_SRAB_RD_L);
+       *val += (u64)readl(regs + B53_SRAB_RD_H) << 32;
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,
+                            u16 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,
+                                   u32 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel(value, regs + B53_SRAB_WD_L);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+
+}
+
+static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,
+                                   u64 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel((u32)value, regs + B53_SRAB_WD_L);
+       writel((u16)(value >> 32), regs + B53_SRAB_WD_H);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+
+}
+
+static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,
+                            u64 value)
+{
+       u8 __iomem *regs = dev->priv;
+       int ret = 0;
+
+       ret = b53_srab_request_grant(dev);
+       if (ret)
+               goto err;
+
+       writel((u32)value, regs + B53_SRAB_WD_L);
+       writel((u32)(value >> 32), regs + B53_SRAB_WD_H);
+
+       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
+
+err:
+       b53_srab_release_grant(dev);
+
+       return ret;
+}
+
+static struct b53_io_ops b53_srab_ops = {
+       .read8 = b53_srab_read8,
+       .read16 = b53_srab_read16,
+       .read32 = b53_srab_read32,
+       .read48 = b53_srab_read48,
+       .read64 = b53_srab_read64,
+       .write8 = b53_srab_write8,
+       .write16 = b53_srab_write16,
+       .write32 = b53_srab_write32,
+       .write48 = b53_srab_write48,
+       .write64 = b53_srab_write64,
+};
+
+static int b53_srab_probe(struct platform_device *pdev)
+{
+       struct b53_platform_data *pdata = pdev->dev.platform_data;
+       struct b53_device *dev;
+
+       if (!pdata)
+               return -EINVAL;
+
+       dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
+       if (!dev)
+               return -ENOMEM;
+
+       if (pdata)
+               dev->pdata = pdata;
+
+       platform_set_drvdata(pdev, dev);
+
+       return b53_switch_register(dev);
+}
+
+static int b53_srab_remove(struct platform_device *pdev)
+{
+       struct b53_device *dev = platform_get_drvdata(pdev);
+
+       if (dev)
+               b53_switch_remove(dev);
+
+       return 0;
+}
+
+static struct platform_driver b53_srab_driver = {
+       .probe = b53_srab_probe,
+       .remove = b53_srab_remove,
+       .driver = {
+               .name = "b53-srab-switch",
+       },
+};
+
+module_platform_driver(b53_srab_driver);
+MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
+MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/ip17xx.c b/target/linux/generic/files-4.9/drivers/net/phy/ip17xx.c
new file mode 100644 (file)
index 0000000..85a9617
--- /dev/null
@@ -0,0 +1,1377 @@
+/*
+ * ip17xx.c: Swconfig configuration for IC+ IP17xx switch family
+ *
+ * Copyright (C) 2008 Patrick Horn <patrick.horn@gmail.com>
+ * Copyright (C) 2008, 2010 Martin Mares <mj@ucw.cz>
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/device.h>
+
+#define MAX_VLANS 16
+#define MAX_PORTS 9
+#undef DUMP_MII_IO
+
+typedef struct ip17xx_reg {
+       u16 p;                  // phy
+       u16 m;                  // mii
+} reg;
+typedef char bitnum;
+
+#define NOTSUPPORTED {-1,-1}
+
+#define REG_SUPP(x) (((x).m != ((u16)-1)) && ((x).p != (u16)-1))
+
+struct ip17xx_state;
+
+/*********** CONSTANTS ***********/
+struct register_mappings {
+       char *NAME;
+       u16 MODEL_NO;                   // Compare to bits 4-9 of MII register 0,3.
+       bitnum NUM_PORTS;
+       bitnum CPU_PORT;
+
+/* The default VLAN for each port.
+        Default: 0x0001 for Ports 0,1,2,3
+                 0x0002 for Ports 4,5 */
+       reg VLAN_DEFAULT_TAG_REG[MAX_PORTS];
+
+/* These ports are tagged.
+        Default: 0x00 */
+       reg ADD_TAG_REG;
+       reg REMOVE_TAG_REG;
+       bitnum ADD_TAG_BIT[MAX_PORTS];
+/* These ports are untagged.
+        Default: 0x00 (i.e. do not alter any VLAN tags...)
+        Maybe set to 0 if user disables VLANs. */
+       bitnum REMOVE_TAG_BIT[MAX_PORTS];
+
+/* Port M and Port N are on the same VLAN.
+        Default: All ports on all VLANs. */
+// Use register {29, 19+N/2}
+       reg VLAN_LOOKUP_REG;
+// Port 5 uses register {30, 18} but same as odd bits.
+       reg VLAN_LOOKUP_REG_5;          // in a different register on IP175C.
+       bitnum VLAN_LOOKUP_EVEN_BIT[MAX_PORTS];
+       bitnum VLAN_LOOKUP_ODD_BIT[MAX_PORTS];
+
+/* This VLAN corresponds to which ports.
+        Default: 0x2f,0x30,0x3f,0x3f... */
+       reg TAG_VLAN_MASK_REG;
+       bitnum TAG_VLAN_MASK_EVEN_BIT[MAX_PORTS];
+       bitnum TAG_VLAN_MASK_ODD_BIT[MAX_PORTS];
+
+       int RESET_VAL;
+       reg RESET_REG;
+
+       reg MODE_REG;
+       int MODE_VAL;
+
+/* General flags */
+       reg ROUTER_CONTROL_REG;
+       reg VLAN_CONTROL_REG;
+       bitnum TAG_VLAN_BIT;
+       bitnum ROUTER_EN_BIT;
+       bitnum NUMLAN_GROUPS_MAX;
+       bitnum NUMLAN_GROUPS_BIT;
+
+       reg MII_REGISTER_EN;
+       bitnum MII_REGISTER_EN_BIT;
+
+       // set to 1 for 178C, 0 for 175C.
+       bitnum SIMPLE_VLAN_REGISTERS;   // 175C has two vlans per register but 178C has only one.
+
+       // Pointers to functions which manipulate hardware state
+       int (*update_state)(struct ip17xx_state *state);
+       int (*set_vlan_mode)(struct ip17xx_state *state);
+       int (*reset)(struct ip17xx_state *state);
+};
+
+static int ip175c_update_state(struct ip17xx_state *state);
+static int ip175c_set_vlan_mode(struct ip17xx_state *state);
+static int ip175c_reset(struct ip17xx_state *state);
+
+static const struct register_mappings IP178C = {
+       .NAME = "IP178C",
+       .MODEL_NO = 0x18,
+       .VLAN_DEFAULT_TAG_REG = {
+               {30,3},{30,4},{30,5},{30,6},{30,7},{30,8},
+               {30,9},{30,10},{30,11},
+       },
+
+       .ADD_TAG_REG = {30,12},
+       .ADD_TAG_BIT = {0,1,2,3,4,5,6,7,8},
+       .REMOVE_TAG_REG = {30,13},
+       .REMOVE_TAG_BIT = {4,5,6,7,8,9,10,11,12},
+
+       .SIMPLE_VLAN_REGISTERS = 1,
+
+       .VLAN_LOOKUP_REG = {31,0},// +N
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, // not used with SIMPLE_VLAN_REGISTERS
+       .VLAN_LOOKUP_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,5,6,7,8},
+
+       .TAG_VLAN_MASK_REG = {30,14}, // +N
+       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
+       .TAG_VLAN_MASK_ODD_BIT = {0,1,2,3,4,5,6,7,8},
+
+       .RESET_VAL = 0x55AA,
+       .RESET_REG = {30,0},
+       .MODE_VAL = 0,
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = {30,30},
+       .ROUTER_EN_BIT = 11,
+       .NUMLAN_GROUPS_MAX = 8,
+       .NUMLAN_GROUPS_BIT = 8, // {0-2}
+
+       .VLAN_CONTROL_REG = {30,13},
+       .TAG_VLAN_BIT = 3,
+
+       .CPU_PORT = 8,
+       .NUM_PORTS = 9,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+static const struct register_mappings IP175C = {
+       .NAME = "IP175C",
+       .MODEL_NO = 0x18,
+       .VLAN_DEFAULT_TAG_REG = {
+               {29,24},{29,25},{29,26},{29,27},{29,28},{29,30},
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
+       },
+
+       .ADD_TAG_REG = {29,23},
+       .REMOVE_TAG_REG = {29,23},
+       .ADD_TAG_BIT = {11,12,13,14,15,1,-1,-1,-1},
+       .REMOVE_TAG_BIT = {6,7,8,9,10,0,-1,-1,-1},
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       .VLAN_LOOKUP_REG = {29,19},// +N/2
+       .VLAN_LOOKUP_REG_5 = {30,18},
+       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,15,-1,-1,-1},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,7,-1,-1,-1},
+
+       .TAG_VLAN_MASK_REG = {30,1}, // +N/2
+       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,-1,-1,-1},
+       .TAG_VLAN_MASK_ODD_BIT = {8,9,10,11,12,13,-1,-1,-1},
+
+       .RESET_VAL = 0x175C,
+       .RESET_REG = {30,0},
+       .MODE_VAL = 0x175C,
+       .MODE_REG = {29,31},
+
+       .ROUTER_CONTROL_REG = {30,9},
+       .ROUTER_EN_BIT = 3,
+       .NUMLAN_GROUPS_MAX = 8,
+       .NUMLAN_GROUPS_BIT = 0, // {0-2}
+
+       .VLAN_CONTROL_REG = {30,9},
+       .TAG_VLAN_BIT = 7,
+
+       .NUM_PORTS = 6,
+       .CPU_PORT = 5,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+static const struct register_mappings IP175A = {
+       .NAME = "IP175A",
+       .MODEL_NO = 0x05,
+       .VLAN_DEFAULT_TAG_REG = {
+               {0,24},{0,25},{0,26},{0,27},{0,28},NOTSUPPORTED,
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
+       },
+
+       .ADD_TAG_REG = {0,23},
+       .REMOVE_TAG_REG = {0,23},
+       .ADD_TAG_BIT = {11,12,13,14,15,-1,-1,-1,-1},
+       .REMOVE_TAG_BIT = {6,7,8,9,10,-1,-1,-1,-1},
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       // Only programmable via EEPROM
+       .VLAN_LOOKUP_REG = NOTSUPPORTED,// +N/2
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
+       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,-1,-1,-1,-1},
+       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,-1,-1,-1,-1},
+
+       .TAG_VLAN_MASK_REG = NOTSUPPORTED, // +N/2,
+       .TAG_VLAN_MASK_EVEN_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
+       .TAG_VLAN_MASK_ODD_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
+
+       .RESET_VAL = -1,
+       .RESET_REG = NOTSUPPORTED,
+       .MODE_VAL = 0,
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = NOTSUPPORTED,
+       .VLAN_CONTROL_REG = NOTSUPPORTED,
+       .TAG_VLAN_BIT = -1,
+       .ROUTER_EN_BIT = -1,
+       .NUMLAN_GROUPS_MAX = -1,
+       .NUMLAN_GROUPS_BIT = -1, // {0-2}
+
+       .NUM_PORTS = 5,
+       .CPU_PORT = 4,
+
+       .MII_REGISTER_EN = {0, 18},
+       .MII_REGISTER_EN_BIT = 7,
+
+       .update_state = ip175c_update_state,
+       .set_vlan_mode = ip175c_set_vlan_mode,
+       .reset = ip175c_reset,
+};
+
+
+static int ip175d_update_state(struct ip17xx_state *state);
+static int ip175d_set_vlan_mode(struct ip17xx_state *state);
+static int ip175d_reset(struct ip17xx_state *state);
+
+static const struct register_mappings IP175D = {
+       .NAME = "IP175D",
+       .MODEL_NO = 0x18,
+
+       // The IP175D has a completely different interface, so we leave most
+       // of the registers undefined and switch to different code paths.
+
+       .VLAN_DEFAULT_TAG_REG = {
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
+               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
+       },
+
+       .ADD_TAG_REG = NOTSUPPORTED,
+       .REMOVE_TAG_REG = NOTSUPPORTED,
+
+       .SIMPLE_VLAN_REGISTERS = 0,
+
+       .VLAN_LOOKUP_REG = NOTSUPPORTED,
+       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
+       .TAG_VLAN_MASK_REG = NOTSUPPORTED,
+
+       .RESET_VAL = 0x175D,
+       .RESET_REG = {20,2},
+       .MODE_REG = NOTSUPPORTED,
+
+       .ROUTER_CONTROL_REG = NOTSUPPORTED,
+       .ROUTER_EN_BIT = -1,
+       .NUMLAN_GROUPS_BIT = -1,
+
+       .VLAN_CONTROL_REG = NOTSUPPORTED,
+       .TAG_VLAN_BIT = -1,
+
+       .NUM_PORTS = 6,
+       .CPU_PORT = 5,
+
+       .MII_REGISTER_EN = NOTSUPPORTED,
+
+       .update_state = ip175d_update_state,
+       .set_vlan_mode = ip175d_set_vlan_mode,
+       .reset = ip175d_reset,
+};
+
+struct ip17xx_state {
+       struct switch_dev dev;
+       struct mii_bus *mii_bus;
+       bool registered;
+
+       int router_mode;                // ROUTER_EN
+       int vlan_enabled;               // TAG_VLAN_EN
+       struct port_state {
+               u16 pvid;
+               unsigned int shareports;
+       } ports[MAX_PORTS];
+       unsigned int add_tag;
+       unsigned int remove_tag;
+       int num_vlans;
+       struct vlan_state {
+               unsigned int ports;
+               unsigned int tag;       // VLAN tag (IP175D only)
+       } vlans[MAX_VLANS];
+       const struct register_mappings *regs;
+       reg proc_mii;   // phy/reg for the low level register access via swconfig
+
+       char buf[80];
+};
+
+#define get_state(_dev) container_of((_dev), struct ip17xx_state, dev)
+
+static int ip_phy_read(struct ip17xx_state *state, int port, int reg)
+{
+       int val = mdiobus_read(state->mii_bus, port, reg);
+       if (val < 0)
+               pr_warning("IP17xx: Unable to get MII register %d,%d: error %d\n", port, reg, -val);
+#ifdef DUMP_MII_IO
+       else
+               pr_debug("IP17xx: Read MII(%d,%d) -> %04x\n", port, reg, val);
+#endif
+       return val;
+}
+
+static int ip_phy_write(struct ip17xx_state *state, int port, int reg, u16 val)
+{
+       int err;
+
+#ifdef DUMP_MII_IO
+       pr_debug("IP17xx: Write MII(%d,%d) <- %04x\n", port, reg, val);
+#endif
+       err = mdiobus_write(state->mii_bus, port, reg, val);
+       if (err < 0)
+               pr_warning("IP17xx: Unable to write MII register %d,%d: error %d\n", port, reg, -err);
+       return err;
+}
+
+static int ip_phy_write_masked(struct ip17xx_state *state, int port, int reg, unsigned int mask, unsigned int data)
+{
+       int val = ip_phy_read(state, port, reg);
+       if (val < 0)
+               return 0;
+       return ip_phy_write(state, port, reg, (val & ~mask) | data);
+}
+
+static int getPhy(struct ip17xx_state *state, reg mii)
+{
+       if (!REG_SUPP(mii))
+               return -EFAULT;
+       return ip_phy_read(state, mii.p, mii.m);
+}
+
+static int setPhy(struct ip17xx_state *state, reg mii, u16 value)
+{
+       int err;
+
+       if (!REG_SUPP(mii))
+               return -EFAULT;
+       err = ip_phy_write(state, mii.p, mii.m, value);
+       if (err < 0)
+               return err;
+       mdelay(2);
+       getPhy(state, mii);
+       return 0;
+}
+
+
+/**
+ * These two macros are to simplify the mapping of logical bits to the bits in hardware.
+ * NOTE: these macros will return if there is an error!
+ */
+
+#define GET_PORT_BITS(state, bits, addr, bit_lookup)           \
+       do {                                                    \
+               int i, val = getPhy((state), (addr));           \
+               if (val < 0)                                    \
+                       return val;                             \
+               (bits) = 0;                                     \
+               for (i = 0; i < MAX_PORTS; i++) {               \
+                       if ((bit_lookup)[i] == -1) continue;    \
+                       if (val & (1<<(bit_lookup)[i]))         \
+                               (bits) |= (1<<i);               \
+               }                                               \
+       } while (0)
+
+#define SET_PORT_BITS(state, bits, addr, bit_lookup)           \
+       do {                                                    \
+               int i, val = getPhy((state), (addr));           \
+               if (val < 0)                                    \
+                       return val;                             \
+               for (i = 0; i < MAX_PORTS; i++) {               \
+                       unsigned int newmask = ((bits)&(1<<i)); \
+                       if ((bit_lookup)[i] == -1) continue;    \
+                       val &= ~(1<<(bit_lookup)[i]);           \
+                       val |= ((newmask>>i)<<(bit_lookup)[i]); \
+               }                                               \
+               val = setPhy((state), (addr), val);             \
+               if (val < 0)                                    \
+                       return val;                             \
+       } while (0)
+
+
+static int get_model(struct ip17xx_state *state)
+{
+       int id1, id2;
+       int oui_id, model_no, rev_no, chip_no;
+
+       id1 = ip_phy_read(state, 0, 2);
+       id2 = ip_phy_read(state, 0, 3);
+       oui_id = (id1 << 6) | ((id2 >> 10) & 0x3f);
+       model_no = (id2 >> 4) & 0x3f;
+       rev_no = id2 & 0xf;
+       pr_debug("IP17xx: Identified oui=%06x model=%02x rev=%X\n", oui_id, model_no, rev_no);
+
+       if (oui_id != 0x0090c3)  // No other oui_id should have reached us anyway
+               return -ENODEV;
+
+       if (model_no == IP175A.MODEL_NO) {
+               state->regs = &IP175A;
+       } else if (model_no == IP175C.MODEL_NO) {
+               /*
+                *  Several models share the same model_no:
+                *  178C has more PHYs, so we try whether the device responds to a read from PHY5
+                *  175D has a new chip ID register
+                *  175C has neither
+                */
+               if (ip_phy_read(state, 5, 2) == 0x0243) {
+                       state->regs = &IP178C;
+               } else {
+                       chip_no = ip_phy_read(state, 20, 0);
+                       pr_debug("IP17xx: Chip ID register reads %04x\n", chip_no);
+                       if (chip_no == 0x175d) {
+                               state->regs = &IP175D;
+                       } else {
+                               state->regs = &IP175C;
+                       }
+               }
+       } else {
+               pr_warning("IP17xx: Found an unknown IC+ switch with model number %02x, revision %X.\n", model_no, rev_no);
+               return -EPERM;
+       }
+       return 0;
+}
+
+/*** Low-level functions for the older models ***/
+
+/** Only set vlan and router flags in the switch **/
+static int ip175c_set_flags(struct ip17xx_state *state)
+{
+       int val;
+
+       if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) {
+               return 0;
+       }
+
+       val = getPhy(state, state->regs->ROUTER_CONTROL_REG);
+       if (val < 0) {
+               return val;
+       }
+       if (state->regs->ROUTER_EN_BIT >= 0) {
+               if (state->router_mode) {
+                       val |= (1<<state->regs->ROUTER_EN_BIT);
+               } else {
+                       val &= (~(1<<state->regs->ROUTER_EN_BIT));
+               }
+       }
+       if (state->regs->TAG_VLAN_BIT >= 0) {
+               if (state->vlan_enabled) {
+                       val |= (1<<state->regs->TAG_VLAN_BIT);
+               } else {
+                       val &= (~(1<<state->regs->TAG_VLAN_BIT));
+               }
+       }
+       if (state->regs->NUMLAN_GROUPS_BIT >= 0) {
+               val &= (~((state->regs->NUMLAN_GROUPS_MAX-1)<<state->regs->NUMLAN_GROUPS_BIT));
+               if (state->num_vlans > state->regs->NUMLAN_GROUPS_MAX) {
+                       val |= state->regs->NUMLAN_GROUPS_MAX << state->regs->NUMLAN_GROUPS_BIT;
+               } else if (state->num_vlans >= 1) {
+                       val |= (state->num_vlans-1) << state->regs->NUMLAN_GROUPS_BIT;
+               }
+       }
+       return setPhy(state, state->regs->ROUTER_CONTROL_REG, val);
+}
+
+/** Set all VLAN and port state.  Usually you should call "correct_vlan_state" first. **/
+static int ip175c_set_state(struct ip17xx_state *state)
+{
+       int j;
+       int i;
+       SET_PORT_BITS(state, state->add_tag,
+                                 state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT);
+       SET_PORT_BITS(state, state->remove_tag,
+                                 state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT);
+
+       if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) {
+               for (j=0; j<state->regs->NUM_PORTS; j++) {
+                       reg addr;
+                       const bitnum *bit_lookup = (j%2==0)?
+                               state->regs->VLAN_LOOKUP_EVEN_BIT:
+                               state->regs->VLAN_LOOKUP_ODD_BIT;
+
+                       addr = state->regs->VLAN_LOOKUP_REG;
+                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
+                               addr.m += j;
+                       } else {
+                               switch (j) {
+                               case 0:
+                               case 1:
+                                       break;
+                               case 2:
+                               case 3:
+                                       addr.m+=1;
+                                       break;
+                               case 4:
+                                       addr.m+=2;
+                                       break;
+                               case 5:
+                                       addr = state->regs->VLAN_LOOKUP_REG_5;
+                                       break;
+                               default:
+                                       addr.m = -1; // shouldn't get here, but...
+                                       break;
+                               }
+                       }
+                       //printf("shareports for %d is %02X\n",j,state->ports[j].shareports);
+                       if (REG_SUPP(addr)) {
+                               SET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup);
+                       }
+               }
+       }
+       if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) {
+               for (j=0; j<MAX_VLANS; j++) {
+                       reg addr = state->regs->TAG_VLAN_MASK_REG;
+                       const bitnum *bit_lookup = (j%2==0)?
+                               state->regs->TAG_VLAN_MASK_EVEN_BIT:
+                               state->regs->TAG_VLAN_MASK_ODD_BIT;
+                       unsigned int vlan_mask;
+                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
+                               addr.m += j;
+                       } else {
+                               addr.m += j/2;
+                       }
+                       vlan_mask = state->vlans[j].ports;
+                       SET_PORT_BITS(state, vlan_mask, addr, bit_lookup);
+               }
+       }
+
+       for (i=0; i<MAX_PORTS; i++) {
+               if (REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[i])) {
+                       int err = setPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i],
+                                       state->ports[i].pvid);
+                       if (err < 0) {
+                               return err;
+                       }
+               }
+       }
+
+       return ip175c_set_flags(state);
+}
+
+/**
+ *  Uses only the VLAN port mask and the add tag mask to generate the other fields:
+ *  which ports are part of the same VLAN, removing vlan tags, and VLAN tag ids.
+ */
+static void ip175c_correct_vlan_state(struct ip17xx_state *state)
+{
+       int i, j;
+       state->num_vlans = 0;
+       for (i=0; i<MAX_VLANS; i++) {
+               if (state->vlans[i].ports != 0) {
+                       state->num_vlans = i+1; // Hack -- we need to store the "set" vlans somewhere...
+               }
+       }
+
+       for (i=0; i<state->regs->NUM_PORTS; i++) {
+               unsigned int portmask = (1<<i);
+               if (!state->vlan_enabled) {
+                       // Share with everybody!
+                       state->ports[i].shareports = (1<<state->regs->NUM_PORTS)-1;
+                       continue;
+               }
+               state->ports[i].shareports = portmask;
+               for (j=0; j<MAX_VLANS; j++) {
+                       if (state->vlans[j].ports & portmask)
+                               state->ports[i].shareports |= state->vlans[j].ports;
+               }
+       }
+}
+
+static int ip175c_update_state(struct ip17xx_state *state)
+{
+       ip175c_correct_vlan_state(state);
+       return ip175c_set_state(state);
+}
+
+static int ip175c_set_vlan_mode(struct ip17xx_state *state)
+{
+       return ip175c_update_state(state);
+}
+
+static int ip175c_reset(struct ip17xx_state *state)
+{
+       int err;
+
+       if (REG_SUPP(state->regs->MODE_REG)) {
+               err = setPhy(state, state->regs->MODE_REG, state->regs->MODE_VAL);
+               if (err < 0)
+                       return err;
+               err = getPhy(state, state->regs->MODE_REG);
+               if (err < 0)
+                       return err;
+       }
+
+       return ip175c_update_state(state);
+}
+
+/*** Low-level functions for IP175D ***/
+
+static int ip175d_update_state(struct ip17xx_state *state)
+{
+       unsigned int filter_mask = 0;
+       unsigned int ports[16], add[16], rem[16];
+       int i, j;
+       int err = 0;
+
+       for (i = 0; i < 16; i++) {
+               ports[i] = 0;
+               add[i] = 0;
+               rem[i] = 0;
+               if (!state->vlan_enabled) {
+                       err |= ip_phy_write(state, 22, 14+i, i+1);      // default tags
+                       ports[i] = 0x3f;
+                       continue;
+               }
+               if (!state->vlans[i].tag) {
+                       // Reset the filter
+                       err |= ip_phy_write(state, 22, 14+i, 0);        // tag
+                       continue;
+               }
+               filter_mask |= 1 << i;
+               err |= ip_phy_write(state, 22, 14+i, state->vlans[i].tag);
+               ports[i] = state->vlans[i].ports;
+               for (j = 0; j < 6; j++) {
+                       if (ports[i] & (1 << j)) {
+                               if (state->add_tag & (1 << j))
+                                       add[i] |= 1 << j;
+                               if (state->remove_tag & (1 << j))
+                                       rem[i] |= 1 << j;
+                       }
+               }
+       }
+
+       // Port masks, tag adds and removals
+       for (i = 0; i < 8; i++) {
+               err |= ip_phy_write(state, 23, i, ports[2*i] | (ports[2*i+1] << 8));
+               err |= ip_phy_write(state, 23, 8+i, add[2*i] | (add[2*i+1] << 8));
+               err |= ip_phy_write(state, 23, 16+i, rem[2*i] | (rem[2*i+1] << 8));
+       }
+       err |= ip_phy_write(state, 22, 10, filter_mask);
+
+       // Default VLAN tag for each port
+       for (i = 0; i < 6; i++)
+               err |= ip_phy_write(state, 22, 4+i, state->vlans[state->ports[i].pvid].tag);
+
+       return (err ? -EIO : 0);
+}
+
+static int ip175d_set_vlan_mode(struct ip17xx_state *state)
+{
+       int i;
+       int err = 0;
+
+       if (state->vlan_enabled) {
+               // VLAN classification rules: tag-based VLANs, use VID to classify,
+               // drop packets that cannot be classified.
+               err |= ip_phy_write_masked(state, 22, 0, 0x3fff, 0x003f);
+
+               // Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed,
+               // VID=0xfff discarded, admin both tagged and untagged, ingress
+               // filters enabled.
+               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
+
+               // Egress rules: IGMP processing off, keep VLAN header off
+               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
+       } else {
+               // VLAN classification rules: everything off & clear table
+               err |= ip_phy_write_masked(state, 22, 0, 0xbfff, 0x8000);
+
+               // Ingress and egress rules: set to defaults
+               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
+               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
+       }
+
+       // Reset default VLAN for each port to 0
+       for (i = 0; i < 6; i++)
+               state->ports[i].pvid = 0;
+
+       err |= ip175d_update_state(state);
+
+       return (err ? -EIO : 0);
+}
+
+static int ip175d_reset(struct ip17xx_state *state)
+{
+       int err = 0;
+
+       // Disable the special tagging mode
+       err |= ip_phy_write_masked(state, 21, 22, 0x0003, 0x0000);
+
+       // Set 802.1q protocol type
+       err |= ip_phy_write(state, 22, 3, 0x8100);
+
+       state->vlan_enabled = 0;
+       err |= ip175d_set_vlan_mode(state);
+
+       return (err ? -EIO : 0);
+}
+
+/*** High-level functions ***/
+
+static int ip17xx_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->vlan_enabled;
+       return 0;
+}
+
+static void ip17xx_reset_vlan_config(struct ip17xx_state *state)
+{
+       int i;
+
+       state->remove_tag = (state->vlan_enabled ? ((1<<state->regs->NUM_PORTS)-1) : 0x0000);
+       state->add_tag = 0x0000;
+       for (i = 0; i < MAX_VLANS; i++) {
+               state->vlans[i].ports = 0x0000;
+               state->vlans[i].tag = (i ? i : 16);
+       }
+       for (i = 0; i < MAX_PORTS; i++)
+               state->ports[i].pvid = 0;
+}
+
+static int ip17xx_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int enable;
+
+       enable = val->value.i;
+       if (state->vlan_enabled == enable) {
+               // Do not change any state.
+               return 0;
+       }
+       state->vlan_enabled = enable;
+
+       // Otherwise, if we are switching state, set fields to a known default.
+       ip17xx_reset_vlan_config(state);
+
+       return state->regs->set_vlan_mode(state);
+}
+
+static int ip17xx_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int b;
+       int ind;
+       unsigned int ports;
+
+       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
+               return -EINVAL;
+
+       ports = state->vlans[val->port_vlan].ports;
+       b = 0;
+       ind = 0;
+       while (b < MAX_PORTS) {
+               if (ports&1) {
+                       int istagged = ((state->add_tag >> b) & 1);
+                       val->value.ports[ind].id = b;
+                       val->value.ports[ind].flags = (istagged << SWITCH_PORT_FLAG_TAGGED);
+                       ind++;
+               }
+               b++;
+               ports >>= 1;
+       }
+       val->len = ind;
+
+       return 0;
+}
+
+static int ip17xx_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int i;
+
+       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
+               return -EINVAL;
+
+       state->vlans[val->port_vlan].ports = 0;
+       for (i = 0; i < val->len; i++) {
+               unsigned int bitmask = (1<<val->value.ports[i].id);
+               state->vlans[val->port_vlan].ports |= bitmask;
+               if (val->value.ports[i].flags & (1<<SWITCH_PORT_FLAG_TAGGED)) {
+                       state->add_tag |= bitmask;
+                       state->remove_tag &= (~bitmask);
+               } else {
+                       state->add_tag &= (~bitmask);
+                       state->remove_tag |= bitmask;
+               }
+       }
+
+       return state->regs->update_state(state);
+}
+
+static int ip17xx_apply(struct switch_dev *dev)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (REG_SUPP(state->regs->MII_REGISTER_EN)) {
+               int val = getPhy(state, state->regs->MII_REGISTER_EN);
+               if (val < 0) {
+                       return val;
+               }
+               val |= (1<<state->regs->MII_REGISTER_EN_BIT);
+               return setPhy(state, state->regs->MII_REGISTER_EN, val);
+       }
+       return 0;
+}
+
+static int ip17xx_reset(struct switch_dev *dev)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int i, err;
+
+       if (REG_SUPP(state->regs->RESET_REG)) {
+               err = setPhy(state, state->regs->RESET_REG, state->regs->RESET_VAL);
+               if (err < 0)
+                       return err;
+               err = getPhy(state, state->regs->RESET_REG);
+
+               /*
+                *  Data sheet specifies reset period to be 2 msec.
+                *  (I don't see any mention of the 2ms delay in the IP178C spec, only
+                *  in IP175C, but it can't hurt.)
+                */
+               mdelay(2);
+       }
+
+       /* reset switch ports */
+       for (i = 0; i < state->regs->NUM_PORTS-1; i++) {
+               err = ip_phy_write(state, i, MII_BMCR, BMCR_RESET);
+               if (err < 0)
+                       return err;
+       }
+
+       state->router_mode = 0;
+       state->vlan_enabled = 0;
+       ip17xx_reset_vlan_config(state);
+
+       return state->regs->reset(state);
+}
+
+static int ip17xx_get_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (state->add_tag & (1<<val->port_vlan)) {
+               if (state->remove_tag & (1<<val->port_vlan))
+                       val->value.i = 3; // shouldn't ever happen.
+               else
+                       val->value.i = 1;
+       } else {
+               if (state->remove_tag & (1<<val->port_vlan))
+                       val->value.i = 0;
+               else
+                       val->value.i = 2;
+       }
+       return 0;
+}
+
+static int ip17xx_set_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       state->add_tag &= ~(1<<val->port_vlan);
+       state->remove_tag &= ~(1<<val->port_vlan);
+
+       if (val->value.i == 0)
+               state->remove_tag |= (1<<val->port_vlan);
+       if (val->value.i == 1)
+               state->add_tag |= (1<<val->port_vlan);
+
+       return state->regs->update_state(state);
+}
+
+/** Get the current phy address */
+static int ip17xx_get_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->proc_mii.p;
+       return 0;
+}
+
+/** Set a new phy address for low level access to registers */
+static int ip17xx_set_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int new_reg = val->value.i;
+
+       if (new_reg < 0 || new_reg > 31)
+               state->proc_mii.p = (u16)-1;
+       else
+               state->proc_mii.p = (u16)new_reg;
+       return 0;
+}
+
+/** Get the current register number */
+static int ip17xx_get_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       val->value.i = state->proc_mii.m;
+       return 0;
+}
+
+/** Set a new register address for low level access to registers */
+static int ip17xx_set_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int new_reg = val->value.i;
+
+       if (new_reg < 0 || new_reg > 31)
+               state->proc_mii.m = (u16)-1;
+       else
+               state->proc_mii.m = (u16)new_reg;
+       return 0;
+}
+
+/** Get the register content of state->proc_mii */
+static int ip17xx_get_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int retval = -EINVAL;
+       if (REG_SUPP(state->proc_mii))
+               retval = getPhy(state, state->proc_mii);
+
+       if (retval < 0) {
+               return retval;
+       } else {
+               val->value.i = retval;
+               return 0;
+       }
+}
+
+/** Write a value to the register defined by phy/reg above */
+static int ip17xx_set_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int myval, err = -EINVAL;
+
+       myval = val->value.i;
+       if (myval <= 0xffff && myval >= 0 && REG_SUPP(state->proc_mii)) {
+               err = setPhy(state, state->proc_mii, (u16)myval);
+       }
+       return err;
+}
+
+static int ip17xx_read_name(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       val->value.s = state->regs->NAME; // Just a const pointer, won't be freed by swconfig.
+       return 0;
+}
+
+static int ip17xx_get_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int vlan = val->port_vlan;
+
+       if (vlan < 0 || vlan >= MAX_VLANS)
+               return -EINVAL;
+
+       val->value.i = state->vlans[vlan].tag;
+       return 0;
+}
+
+static int ip17xx_set_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int vlan = val->port_vlan;
+       int tag = val->value.i;
+
+       if (vlan < 0 || vlan >= MAX_VLANS)
+               return -EINVAL;
+
+       if (tag < 0 || tag > 4095)
+               return -EINVAL;
+
+       state->vlans[vlan].tag = tag;
+       return state->regs->update_state(state);
+}
+
+static int ip17xx_set_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int nr = val->port_vlan;
+       int ctrl;
+       int autoneg;
+       int speed;
+       if (val->value.i == 100) {
+               speed = 1;
+               autoneg = 0;
+       } else if (val->value.i == 10) {
+               speed = 0;
+               autoneg = 0;
+       } else {
+               autoneg = 1;
+               speed = 1;
+       }
+
+       /* Can't set speed for cpu port */
+       if (nr == state->regs->CPU_PORT)
+               return -EINVAL;
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       ctrl = ip_phy_read(state, nr, 0);
+       if (ctrl < 0)
+               return -EIO;
+
+       ctrl &= (~(1<<12));
+       ctrl &= (~(1<<13));
+       ctrl |= (autoneg<<12);
+       ctrl |= (speed<<13);
+
+       return ip_phy_write(state, nr, 0, ctrl);
+}
+
+static int ip17xx_get_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int nr = val->port_vlan;
+       int speed, status;
+
+       if (nr == state->regs->CPU_PORT) {
+               val->value.i = 100;
+               return 0;
+       }
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       status = ip_phy_read(state, nr, 1);
+       speed = ip_phy_read(state, nr, 18);
+       if (status < 0 || speed < 0)
+               return -EIO;
+
+       if (status & 4)
+               val->value.i = ((speed & (1<<11)) ? 100 : 10);
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int ip17xx_get_port_status(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+       int ctrl, speed, status;
+       int nr = val->port_vlan;
+       int len;
+       char *buf = state->buf; // fixed-length at 80.
+
+       if (nr == state->regs->CPU_PORT) {
+               sprintf(buf, "up, 100 Mbps, cpu port");
+               val->value.s = buf;
+               return 0;
+       }
+
+       if (nr >= dev->ports || nr < 0)
+               return -EINVAL;
+
+       ctrl = ip_phy_read(state, nr, 0);
+       status = ip_phy_read(state, nr, 1);
+       speed = ip_phy_read(state, nr, 18);
+       if (ctrl < 0 || status < 0 || speed < 0)
+               return -EIO;
+
+       if (status & 4)
+               len = sprintf(buf, "up, %d Mbps, %s duplex",
+                       ((speed & (1<<11)) ? 100 : 10),
+                       ((speed & (1<<10)) ? "full" : "half"));
+       else
+               len = sprintf(buf, "down");
+
+       if (ctrl & (1<<12)) {
+               len += sprintf(buf+len, ", auto-negotiate");
+               if (!(status & (1<<5)))
+                       len += sprintf(buf+len, " (in progress)");
+       } else {
+               len += sprintf(buf+len, ", fixed speed (%d)",
+                       ((ctrl & (1<<13)) ? 100 : 10));
+       }
+
+       buf[len] = '\0';
+       val->value.s = buf;
+       return 0;
+}
+
+static int ip17xx_get_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       *val = state->ports[port].pvid;
+       return 0;
+}
+
+static int ip17xx_set_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct ip17xx_state *state = get_state(dev);
+
+       if (val < 0 || val >= MAX_VLANS)
+               return -EINVAL;
+
+       state->ports[port].pvid = val;
+       return state->regs->update_state(state);
+}
+
+
+enum Ports {
+       IP17XX_PORT_STATUS,
+       IP17XX_PORT_LINK,
+       IP17XX_PORT_TAGGED,
+       IP17XX_PORT_PVID,
+};
+
+enum Globals {
+       IP17XX_ENABLE_VLAN,
+       IP17XX_GET_NAME,
+       IP17XX_REGISTER_PHY,
+       IP17XX_REGISTER_MII,
+       IP17XX_REGISTER_VALUE,
+       IP17XX_REGISTER_ERRNO,
+};
+
+enum Vlans {
+       IP17XX_VLAN_TAG,
+};
+
+static const struct switch_attr ip17xx_global[] = {
+       [IP17XX_ENABLE_VLAN] = {
+               .id = IP17XX_ENABLE_VLAN,
+               .type = SWITCH_TYPE_INT,
+               .name  = "enable_vlan",
+               .description = "Flag to enable or disable VLANs and tagging",
+               .get  = ip17xx_get_enable_vlan,
+               .set = ip17xx_set_enable_vlan,
+       },
+       [IP17XX_GET_NAME] = {
+               .id = IP17XX_GET_NAME,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Returns the type of IC+ chip.",
+               .name  = "name",
+               .get  = ip17xx_read_name,
+               .set = NULL,
+       },
+       /* jal: added for low level debugging etc. */
+       [IP17XX_REGISTER_PHY] = {
+               .id = IP17XX_REGISTER_PHY,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: set PHY (0-4, or 29,30,31)",
+               .name  = "phy",
+               .get  = ip17xx_get_phy,
+               .set = ip17xx_set_phy,
+       },
+       [IP17XX_REGISTER_MII] = {
+               .id = IP17XX_REGISTER_MII,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: set MII register number (0-31)",
+               .name  = "reg",
+               .get  = ip17xx_get_reg,
+               .set = ip17xx_set_reg,
+       },
+       [IP17XX_REGISTER_VALUE] = {
+               .id = IP17XX_REGISTER_VALUE,
+               .type = SWITCH_TYPE_INT,
+               .description = "Direct register access: read/write to register (0-65535)",
+               .name  = "val",
+               .get  = ip17xx_get_val,
+               .set = ip17xx_set_val,
+       },
+};
+
+static const struct switch_attr ip17xx_vlan[] = {
+       [IP17XX_VLAN_TAG] = {
+               .id = IP17XX_VLAN_TAG,
+               .type = SWITCH_TYPE_INT,
+               .description = "VLAN ID (0-4095) [IP175D only]",
+               .name = "vid",
+               .get = ip17xx_get_tag,
+               .set = ip17xx_set_tag,
+       }
+};
+
+static const struct switch_attr ip17xx_port[] = {
+       [IP17XX_PORT_STATUS] = {
+               .id = IP17XX_PORT_STATUS,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Returns Detailed port status",
+               .name  = "status",
+               .get  = ip17xx_get_port_status,
+               .set = NULL,
+       },
+       [IP17XX_PORT_LINK] = {
+               .id = IP17XX_PORT_LINK,
+               .type = SWITCH_TYPE_INT,
+               .description = "Link speed. Can write 0 for auto-negotiate, or 10 or 100",
+               .name  = "link",
+               .get  = ip17xx_get_port_speed,
+               .set = ip17xx_set_port_speed,
+       },
+       [IP17XX_PORT_TAGGED] = {
+               .id = IP17XX_PORT_LINK,
+               .type = SWITCH_TYPE_INT,
+               .description = "0 = untag, 1 = add tags, 2 = do not alter (This value is reset if vlans are altered)",
+               .name  = "tagged",
+               .get  = ip17xx_get_tagged,
+               .set = ip17xx_set_tagged,
+       },
+};
+
+static const struct switch_dev_ops ip17xx_ops = {
+       .attr_global = {
+               .attr = ip17xx_global,
+               .n_attr = ARRAY_SIZE(ip17xx_global),
+       },
+       .attr_port = {
+               .attr = ip17xx_port,
+               .n_attr = ARRAY_SIZE(ip17xx_port),
+       },
+       .attr_vlan = {
+               .attr = ip17xx_vlan,
+               .n_attr = ARRAY_SIZE(ip17xx_vlan),
+       },
+
+       .get_port_pvid = ip17xx_get_pvid,
+       .set_port_pvid = ip17xx_set_pvid,
+       .get_vlan_ports = ip17xx_get_ports,
+       .set_vlan_ports = ip17xx_set_ports,
+       .apply_config = ip17xx_apply,
+       .reset_switch = ip17xx_reset,
+};
+
+static int ip17xx_probe(struct phy_device *pdev)
+{
+       struct ip17xx_state *state;
+       struct switch_dev *dev;
+       int err;
+
+       /* We only attach to PHY 0, but use all available PHYs */
+       if (pdev->mdio.addr != 0)
+               return -ENODEV;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       dev = &state->dev;
+
+       pdev->priv = state;
+       state->mii_bus = pdev->mdio.bus;
+
+       err = get_model(state);
+       if (err < 0)
+               goto error;
+
+       dev->vlans = MAX_VLANS;
+       dev->cpu_port = state->regs->CPU_PORT;
+       dev->ports = state->regs->NUM_PORTS;
+       dev->name = state->regs->NAME;
+       dev->ops = &ip17xx_ops;
+
+       pr_info("IP17xx: Found %s at %s\n", dev->name, dev_name(&pdev->mdio.dev));
+       return 0;
+
+error:
+       kfree(state);
+       return err;
+}
+
+static int ip17xx_config_init(struct phy_device *pdev)
+{
+       struct ip17xx_state *state = pdev->priv;
+       struct net_device *dev = pdev->attached_dev;
+       int err;
+
+       err = register_switch(&state->dev, dev);
+       if (err < 0)
+               return err;
+
+       state->registered = true;
+       ip17xx_reset(&state->dev);
+       return 0;
+}
+
+static void ip17xx_remove(struct phy_device *pdev)
+{
+       struct ip17xx_state *state = pdev->priv;
+
+       if (state->registered)
+               unregister_switch(&state->dev);
+       kfree(state);
+}
+
+static int ip17xx_config_aneg(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static int ip17xx_aneg_done(struct phy_device *pdev)
+{
+       return 1;       /* Return any positive value */
+}
+
+static int ip17xx_update_link(struct phy_device *pdev)
+{
+       pdev->link = 1;
+       return 0;
+}
+
+static int ip17xx_read_status(struct phy_device *pdev)
+{
+       pdev->speed = SPEED_100;
+       pdev->duplex = DUPLEX_FULL;
+       pdev->pause = pdev->asym_pause = 0;
+       pdev->link = 1;
+
+       return 0;
+}
+
+static struct phy_driver ip17xx_driver[] = {
+       {
+               .name           = "IC+ IP17xx",
+               .phy_id         = 0x02430c00,
+               .phy_id_mask    = 0x0ffffc00,
+               .features       = PHY_BASIC_FEATURES,
+               .probe          = ip17xx_probe,
+               .remove         = ip17xx_remove,
+               .config_init    = ip17xx_config_init,
+               .config_aneg    = ip17xx_config_aneg,
+               .aneg_done      = ip17xx_aneg_done,
+               .update_link    = ip17xx_update_link,
+               .read_status    = ip17xx_read_status,
+       }
+};
+
+module_phy_driver(ip17xx_driver);
+
+MODULE_AUTHOR("Patrick Horn <patrick.horn@gmail.com>");
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_AUTHOR("Martin Mares <mj@ucw.cz>");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/mvsw61xx.c b/target/linux/generic/files-4.9/drivers/net/phy/mvsw61xx.c
new file mode 100644 (file)
index 0000000..9a689e6
--- /dev/null
@@ -0,0 +1,947 @@
+/*
+ * Marvell 88E61xx switch driver
+ *
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
+ *
+ * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "mvsw61xx.h"
+
+MODULE_DESCRIPTION("Marvell 88E61xx Switch driver");
+MODULE_AUTHOR("Claudio Leite <leitec@staticky.com>");
+MODULE_AUTHOR("Nikita Nazarenko <nnazarenko@radiofid.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mvsw61xx");
+
+/*
+ * Register access is done through direct or indirect addressing,
+ * depending on how the switch is physically connected.
+ *
+ * Direct addressing: all port and global registers directly
+ *   accessible via an address/register pair
+ *
+ * Indirect addressing: switch is mapped at a single address,
+ *   port and global registers accessible via a single command/data
+ *   register pair
+ */
+
+static int
+mvsw61xx_wait_mask_raw(struct mii_bus *bus, int addr,
+               int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = bus->read(bus, addr, reg);
+               if ((r & mask) == val)
+                       return 0;
+       } while (--i > 0);
+
+       return -ETIMEDOUT;
+}
+
+static u16
+r16(struct mii_bus *bus, bool indirect, int base_addr, int addr, int reg)
+{
+       u16 ind_addr;
+
+       if (!indirect)
+               return bus->read(bus, addr, reg);
+
+       /* Indirect read: First, make sure switch is free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load address and request read */
+       ind_addr = MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg;
+       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       ind_addr);
+
+       /* Wait until it's ready */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Read the requested data */
+       return bus->read(bus, base_addr, MV_INDIRECT_REG_DATA);
+}
+
+static void
+w16(struct mii_bus *bus, bool indirect, int base_addr, int addr,
+               int reg, u16 val)
+{
+       u16 ind_addr;
+
+       if (!indirect) {
+               bus->write(bus, addr, reg, val);
+               return;
+       }
+
+       /* Indirect write: First, make sure switch is free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load the data to be written */
+       bus->write(bus, base_addr, MV_INDIRECT_REG_DATA, val);
+
+       /* Wait again for switch to be free */
+       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       MV_INDIRECT_INPROGRESS, 0);
+
+       /* Load address, and issue write command */
+       ind_addr = MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg;
+       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
+                       ind_addr);
+}
+
+/* swconfig support */
+
+static inline u16
+sr16(struct switch_dev *dev, int addr, int reg)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       return r16(state->bus, state->is_indirect, state->base_addr, addr, reg);
+}
+
+static inline void
+sw16(struct switch_dev *dev, int addr, int reg, u16 val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       w16(state->bus, state->is_indirect, state->base_addr, addr, reg, val);
+}
+
+static int
+mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr,
+               int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = sr16(dev, addr, reg) & mask;
+               if (r == val)
+                       return 0;
+       } while (--i > 0);
+
+       return -ETIMEDOUT;
+}
+
+static int
+mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg)
+{
+       sw16(dev, MV_GLOBAL2REG(SMI_OP),
+            MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg);
+
+       if (mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
+                                MV_INDIRECT_INPROGRESS, 0) < 0)
+               return -ETIMEDOUT;
+
+       return sr16(dev, MV_GLOBAL2REG(SMI_DATA));
+}
+
+static int
+mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val)
+{
+       sw16(dev, MV_GLOBAL2REG(SMI_DATA), val);
+
+       sw16(dev, MV_GLOBAL2REG(SMI_OP),
+            MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg);
+
+       return mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
+                                   MV_INDIRECT_INPROGRESS, 0) < 0;
+}
+
+static int
+mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg)
+{
+       int ret;
+
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
+       ret = mvsw61xx_mdio_read(dev, port, reg);
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
+
+       return ret;
+}
+
+static void
+mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg,
+                        u16 val)
+{
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
+       mvsw61xx_mdio_write(dev, port, reg, val);
+       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
+}
+
+static int
+mvsw61xx_get_port_mask(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       char *buf = state->buf;
+       int port, len, i;
+       u16 reg;
+
+       port = val->port_vlan;
+       reg = sr16(dev, MV_PORTREG(VLANMAP, port)) & MV_PORTS_MASK;
+
+       len = sprintf(buf, "0x%04x: ", reg);
+
+       for (i = 0; i < MV_PORTS; i++) {
+               if (reg & (1 << i))
+                       len += sprintf(buf + len, "%d ", i);
+               else if (i == port)
+                       len += sprintf(buf + len, "(%d) ", i);
+       }
+
+       val->value.s = buf;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_qmode(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->ports[val->port_vlan].qmode;
+
+       return 0;
+}
+
+static int
+mvsw61xx_set_port_qmode(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->ports[val->port_vlan].qmode = val->value.i;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       *val = state->ports[port].pvid;
+
+       return 0;
+}
+
+static int
+mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       if (val < 0 || val >= MV_VLANS)
+               return -EINVAL;
+
+       state->ports[port].pvid = (u16)val;
+
+       return 0;
+}
+
+static int
+mvsw61xx_get_port_link(struct switch_dev *dev, int port,
+               struct switch_port_link *link)
+{
+       u16 status, speed;
+
+       status = sr16(dev, MV_PORTREG(STATUS, port));
+
+       link->link = status & MV_PORT_STATUS_LINK;
+       if (!link->link)
+               return 0;
+
+       link->duplex = status & MV_PORT_STATUS_FDX;
+
+       speed = (status & MV_PORT_STATUS_SPEED_MASK) >>
+                       MV_PORT_STATUS_SPEED_SHIFT;
+
+       switch (speed) {
+       case MV_PORT_STATUS_SPEED_10:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case MV_PORT_STATUS_SPEED_100:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case MV_PORT_STATUS_SPEED_1000:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       }
+
+       return 0;
+}
+
+static int mvsw61xx_get_vlan_ports(struct switch_dev *dev,
+               struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, j, mode, vno;
+
+       vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       for (i = 0, j = 0; i < dev->ports; i++) {
+               if (state->vlans[vno].mask & (1 << i)) {
+                       val->value.ports[j].id = i;
+
+                       mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
+                       if (mode == MV_VTUCTL_EGRESS_TAGGED)
+                               val->value.ports[j].flags =
+                                       (1 << SWITCH_PORT_FLAG_TAGGED);
+                       else
+                               val->value.ports[j].flags = 0;
+
+                       j++;
+               }
+       }
+
+       val->len = j;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vlan_ports(struct switch_dev *dev,
+               struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, mode, pno, vno;
+
+       vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       state->vlans[vno].mask = 0;
+       state->vlans[vno].port_mode = 0;
+       state->vlans[vno].port_sstate = 0;
+
+       if(state->vlans[vno].vid == 0)
+               state->vlans[vno].vid = vno;
+
+       for (i = 0; i < val->len; i++) {
+               pno = val->value.ports[i].id;
+
+               state->vlans[vno].mask |= (1 << pno);
+               if (val->value.ports[i].flags &
+                               (1 << SWITCH_PORT_FLAG_TAGGED))
+                       mode = MV_VTUCTL_EGRESS_TAGGED;
+               else
+                       mode = MV_VTUCTL_EGRESS_UNTAGGED;
+
+               state->vlans[vno].port_mode |= mode << (pno * 4);
+               state->vlans[vno].port_sstate |=
+                       MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2);
+       }
+
+       /*
+        * DISCARD is nonzero, so it must be explicitly
+        * set on ports not in the VLAN.
+        */
+       for (i = 0; i < dev->ports; i++)
+               if (!(state->vlans[vno].mask & (1 << i)))
+                       state->vlans[vno].port_mode |=
+                               MV_VTUCTL_DISCARD << (i * 4);
+
+       return 0;
+}
+
+static int mvsw61xx_get_vlan_port_based(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       if (state->vlans[vno].port_based)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vlan_port_based(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       if (val->value.i == 1)
+               state->vlans[vno].port_based = true;
+       else
+               state->vlans[vno].port_based = false;
+
+       return 0;
+}
+
+static int mvsw61xx_get_vid(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       val->value.i = state->vlans[vno].vid;
+
+       return 0;
+}
+
+static int mvsw61xx_set_vid(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int vno = val->port_vlan;
+
+       if (vno <= 0 || vno >= dev->vlans)
+               return -EINVAL;
+
+       state->vlans[vno].vid = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_get_enable_vlan(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       val->value.i = state->vlan_enabled;
+
+       return 0;
+}
+
+static int mvsw61xx_set_enable_vlan(struct switch_dev *dev,
+               const struct switch_attr *attr, struct switch_val *val)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+
+       state->vlan_enabled = val->value.i;
+
+       return 0;
+}
+
+static int mvsw61xx_vtu_program(struct switch_dev *dev)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       u16 v1, v2, s1, s2;
+       int i;
+
+       /* Flush */
+       mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                       MV_VTUOP_INPROGRESS, 0);
+       sw16(dev, MV_GLOBALREG(VTU_OP),
+                       MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE);
+
+       /* Write VLAN table */
+       for (i = 1; i < dev->vlans; i++) {
+               if (state->vlans[i].mask == 0 ||
+                               state->vlans[i].vid == 0 ||
+                               state->vlans[i].port_based == true)
+                       continue;
+
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+
+               /* Write per-VLAN port state into STU */
+               s1 = (u16) (state->vlans[i].port_sstate & 0xffff);
+               s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff);
+
+               sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID);
+               sw16(dev, MV_GLOBALREG(VTU_SID), i);
+               sw16(dev, MV_GLOBALREG(VTU_DATA1), s1);
+               sw16(dev, MV_GLOBALREG(VTU_DATA2), s2);
+               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
+
+               sw16(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD);
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+
+               /* Write VLAN information into VTU */
+               v1 = (u16) (state->vlans[i].port_mode & 0xffff);
+               v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff);
+
+               sw16(dev, MV_GLOBALREG(VTU_VID),
+                               MV_VTU_VID_VALID | state->vlans[i].vid);
+               sw16(dev, MV_GLOBALREG(VTU_SID), i);
+               sw16(dev, MV_GLOBALREG(VTU_FID), i);
+               sw16(dev, MV_GLOBALREG(VTU_DATA1), v1);
+               sw16(dev, MV_GLOBALREG(VTU_DATA2), v2);
+               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
+
+               sw16(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD);
+               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
+                               MV_VTUOP_INPROGRESS, 0);
+       }
+
+       return 0;
+}
+
+static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i, mode;
+
+       for (i = 0; i < dev->ports; i++) {
+               if (!(state->vlans[vno].mask & (1 << i)))
+                       continue;
+
+               mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
+
+               if(mode != MV_VTUCTL_EGRESS_TAGGED)
+                       state->ports[i].pvid = state->vlans[vno].vid;
+
+               if (state->vlans[vno].port_based) {
+                       state->ports[i].mask |= state->vlans[vno].mask;
+                       state->ports[i].fdb = vno;
+               }
+               else
+                       state->ports[i].qmode = MV_8021Q_MODE_SECURE;
+       }
+}
+
+static int mvsw61xx_update_state(struct switch_dev *dev)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i;
+       u16 reg;
+
+       if (!state->registered)
+               return -EINVAL;
+
+       /*
+        * Set 802.1q-only mode if vlan_enabled is true.
+        *
+        * Without this, even if 802.1q is enabled for
+        * a port/VLAN, it still depends on the port-based
+        * VLAN mask being set.
+        *
+        * With this setting, port-based VLANs are still
+        * functional, provided the VID is not in the VTU.
+        */
+       reg = sr16(dev, MV_GLOBAL2REG(SDET_POLARITY));
+
+       if (state->vlan_enabled)
+               reg |= MV_8021Q_VLAN_ONLY;
+       else
+               reg &= ~MV_8021Q_VLAN_ONLY;
+
+       sw16(dev, MV_GLOBAL2REG(SDET_POLARITY), reg);
+
+       /*
+        * Set port-based VLAN masks on each port
+        * based only on VLAN definitions known to
+        * the driver (i.e. in state).
+        *
+        * This means any pre-existing port mapping is
+        * wiped out once our driver is initialized.
+        */
+       for (i = 0; i < dev->ports; i++) {
+               state->ports[i].mask = 0;
+               state->ports[i].qmode = MV_8021Q_MODE_DISABLE;
+       }
+
+       for (i = 0; i < dev->vlans; i++)
+               mvsw61xx_vlan_port_config(dev, i);
+
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(VLANID, i)) & ~MV_PVID_MASK;
+               reg |= state->ports[i].pvid;
+               sw16(dev, MV_PORTREG(VLANID, i), reg);
+
+               state->ports[i].mask &= ~(1 << i);
+
+               /* set default forwarding DB number and port mask */
+               reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK;
+               reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) &
+                       MV_FDB_HI_MASK;
+               sw16(dev, MV_PORTREG(CONTROL1, i), reg);
+
+               reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) |
+                       state->ports[i].mask;
+               sw16(dev, MV_PORTREG(VLANMAP, i), reg);
+
+               reg = sr16(dev, MV_PORTREG(CONTROL2, i)) &
+                       ~MV_8021Q_MODE_MASK;
+               reg |= state->ports[i].qmode << MV_8021Q_MODE_SHIFT;
+               sw16(dev, MV_PORTREG(CONTROL2, i), reg);
+       }
+
+       mvsw61xx_vtu_program(dev);
+
+       return 0;
+}
+
+static int mvsw61xx_apply(struct switch_dev *dev)
+{
+       return mvsw61xx_update_state(dev);
+}
+
+static void mvsw61xx_enable_serdes(struct switch_dev *dev)
+{
+       int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES,
+                                          MV_PAGE_FIBER_SERDES, MII_BMCR);
+       if (bmcr < 0)
+               return;
+
+       if (bmcr & BMCR_PDOWN)
+               mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES,
+                                        MV_PAGE_FIBER_SERDES, MII_BMCR,
+                                        bmcr & ~BMCR_PDOWN);
+}
+
+static int _mvsw61xx_reset(struct switch_dev *dev, bool full)
+{
+       struct mvsw61xx_state *state = get_state(dev);
+       int i;
+       u16 reg;
+
+       /* Disable all ports before reset */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL, i)) &
+                       ~MV_PORTCTRL_FORWARDING;
+               sw16(dev, MV_PORTREG(CONTROL, i), reg);
+       }
+
+       reg = sr16(dev, MV_GLOBALREG(CONTROL)) | MV_CONTROL_RESET;
+
+       sw16(dev, MV_GLOBALREG(CONTROL), reg);
+       if (mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(CONTROL),
+                               MV_CONTROL_RESET, 0) < 0)
+               return -ETIMEDOUT;
+
+       for (i = 0; i < dev->ports; i++) {
+               state->ports[i].fdb = 0;
+               state->ports[i].qmode = 0;
+               state->ports[i].mask = 0;
+               state->ports[i].pvid = 0;
+
+               /* Force flow control off */
+               reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK;
+               reg |= MV_PHYCTL_FC_DISABLE;
+               sw16(dev, MV_PORTREG(PHYCTL, i), reg);
+
+               /* Set port association vector */
+               sw16(dev, MV_PORTREG(ASSOC, i), (1 << i));
+
+               /* power up phys */
+               if (full && i < 5) {
+                       mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL,
+                                           MV_SPEC_MDI_CROSS_AUTO |
+                                           MV_SPEC_ENERGY_DETECT |
+                                           MV_SPEC_DOWNSHIFT_COUNTER);
+                       mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET |
+                                           BMCR_ANENABLE | BMCR_FULLDPLX |
+                                           BMCR_SPEED1000);
+               }
+
+               /* enable SerDes if necessary */
+               if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) {
+                       u16 sts = sr16(dev, MV_PORTREG(STATUS, i));
+                       u16 mode = sts & MV_PORT_STATUS_CMODE_MASK;
+
+                       if (mode == MV_PORT_STATUS_CMODE_100BASE_X ||
+                           mode == MV_PORT_STATUS_CMODE_1000BASE_X ||
+                           mode == MV_PORT_STATUS_CMODE_SGMII) {
+                               mvsw61xx_enable_serdes(dev);
+                       }
+               }
+       }
+
+       for (i = 0; i < dev->vlans; i++) {
+               state->vlans[i].port_based = false;
+               state->vlans[i].mask = 0;
+               state->vlans[i].vid = 0;
+               state->vlans[i].port_mode = 0;
+               state->vlans[i].port_sstate = 0;
+       }
+
+       state->vlan_enabled = 0;
+
+       mvsw61xx_update_state(dev);
+
+       /* Re-enable ports */
+       for (i = 0; i < dev->ports; i++) {
+               reg = sr16(dev, MV_PORTREG(CONTROL, i)) |
+                       MV_PORTCTRL_FORWARDING;
+               sw16(dev, MV_PORTREG(CONTROL, i), reg);
+       }
+
+       return 0;
+}
+
+static int mvsw61xx_reset(struct switch_dev *dev)
+{
+       return _mvsw61xx_reset(dev, false);
+}
+
+enum {
+       MVSW61XX_ENABLE_VLAN,
+};
+
+enum {
+       MVSW61XX_VLAN_PORT_BASED,
+       MVSW61XX_VLAN_ID,
+};
+
+enum {
+       MVSW61XX_PORT_MASK,
+       MVSW61XX_PORT_QMODE,
+};
+
+static const struct switch_attr mvsw61xx_global[] = {
+       [MVSW61XX_ENABLE_VLAN] = {
+               .id = MVSW61XX_ENABLE_VLAN,
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable 802.1q VLAN support",
+               .get = mvsw61xx_get_enable_vlan,
+               .set = mvsw61xx_set_enable_vlan,
+       },
+};
+
+static const struct switch_attr mvsw61xx_vlan[] = {
+       [MVSW61XX_VLAN_PORT_BASED] = {
+               .id = MVSW61XX_VLAN_PORT_BASED,
+               .type = SWITCH_TYPE_INT,
+               .name = "port_based",
+               .description = "Use port-based (non-802.1q) VLAN only",
+               .get = mvsw61xx_get_vlan_port_based,
+               .set = mvsw61xx_set_vlan_port_based,
+       },
+       [MVSW61XX_VLAN_ID] = {
+               .id = MVSW61XX_VLAN_ID,
+               .type = SWITCH_TYPE_INT,
+               .name = "vid",
+               .description = "Get/set VLAN ID",
+               .get = mvsw61xx_get_vid,
+               .set = mvsw61xx_set_vid,
+       },
+};
+
+static const struct switch_attr mvsw61xx_port[] = {
+       [MVSW61XX_PORT_MASK] = {
+               .id = MVSW61XX_PORT_MASK,
+               .type = SWITCH_TYPE_STRING,
+               .description = "Port-based VLAN mask",
+               .name = "mask",
+               .get = mvsw61xx_get_port_mask,
+               .set = NULL,
+       },
+       [MVSW61XX_PORT_QMODE] = {
+               .id = MVSW61XX_PORT_QMODE,
+               .type = SWITCH_TYPE_INT,
+               .description = "802.1q mode: 0=off/1=fallback/2=check/3=secure",
+               .name = "qmode",
+               .get = mvsw61xx_get_port_qmode,
+               .set = mvsw61xx_set_port_qmode,
+       },
+};
+
+static const struct switch_dev_ops mvsw61xx_ops = {
+       .attr_global = {
+               .attr = mvsw61xx_global,
+               .n_attr = ARRAY_SIZE(mvsw61xx_global),
+       },
+       .attr_vlan = {
+               .attr = mvsw61xx_vlan,
+               .n_attr = ARRAY_SIZE(mvsw61xx_vlan),
+       },
+       .attr_port = {
+               .attr = mvsw61xx_port,
+               .n_attr = ARRAY_SIZE(mvsw61xx_port),
+       },
+       .get_port_link = mvsw61xx_get_port_link,
+       .get_port_pvid = mvsw61xx_get_port_pvid,
+       .set_port_pvid = mvsw61xx_set_port_pvid,
+       .get_vlan_ports = mvsw61xx_get_vlan_ports,
+       .set_vlan_ports = mvsw61xx_set_vlan_ports,
+       .apply_config = mvsw61xx_apply,
+       .reset_switch = mvsw61xx_reset,
+};
+
+/* end swconfig stuff */
+
+static int mvsw61xx_probe(struct platform_device *pdev)
+{
+       struct mvsw61xx_state *state;
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *mdio;
+       char *model_str;
+       u32 val;
+       int err;
+
+       state = kzalloc(sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       mdio = of_parse_phandle(np, "mii-bus", 0);
+       if (!mdio) {
+               dev_err(&pdev->dev, "Couldn't get MII bus handle\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->bus = of_mdio_find_bus(mdio);
+       if (!state->bus) {
+               dev_err(&pdev->dev, "Couldn't find MII bus from handle\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->is_indirect = of_property_read_bool(np, "is-indirect");
+
+       if (state->is_indirect) {
+               if (of_property_read_u32(np, "reg", &val)) {
+                       dev_err(&pdev->dev, "Switch address not specified\n");
+                       err = -ENODEV;
+                       goto out_err;
+               }
+
+               state->base_addr = val;
+       } else {
+               state->base_addr = MV_BASE;
+       }
+
+       state->model = r16(state->bus, state->is_indirect, state->base_addr,
+                               MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
+
+       switch(state->model) {
+       case MV_IDENT_VALUE_6171:
+               model_str = MV_IDENT_STR_6171;
+               break;
+       case MV_IDENT_VALUE_6172:
+               model_str = MV_IDENT_STR_6172;
+               break;
+       case MV_IDENT_VALUE_6176:
+               model_str = MV_IDENT_STR_6176;
+               break;
+       case MV_IDENT_VALUE_6352:
+               model_str = MV_IDENT_STR_6352;
+               break;
+       default:
+               dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n",
+                               state->base_addr);
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       platform_set_drvdata(pdev, state);
+       dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str,
+                       state->bus->id, state->base_addr);
+
+       dev_info(&pdev->dev, "Using %sdirect addressing\n",
+                       (state->is_indirect ? "in" : ""));
+
+       if (of_property_read_u32(np, "cpu-port-0", &val)) {
+               dev_err(&pdev->dev, "CPU port not set\n");
+               err = -ENODEV;
+               goto out_err;
+       }
+
+       state->cpu_port0 = val;
+
+       if (!of_property_read_u32(np, "cpu-port-1", &val))
+               state->cpu_port1 = val;
+       else
+               state->cpu_port1 = -1;
+
+       state->dev.vlans = MV_VLANS;
+       state->dev.cpu_port = state->cpu_port0;
+       state->dev.ports = MV_PORTS;
+       state->dev.name = model_str;
+       state->dev.ops = &mvsw61xx_ops;
+       state->dev.alias = dev_name(&pdev->dev);
+
+       _mvsw61xx_reset(&state->dev, true);
+
+       err = register_switch(&state->dev, NULL);
+       if (err < 0)
+               goto out_err;
+
+       state->registered = true;
+
+       return 0;
+out_err:
+       kfree(state);
+       return err;
+}
+
+static int
+mvsw61xx_remove(struct platform_device *pdev)
+{
+       struct mvsw61xx_state *state = platform_get_drvdata(pdev);
+
+       if (state->registered)
+               unregister_switch(&state->dev);
+
+       kfree(state);
+
+       return 0;
+}
+
+static const struct of_device_id mvsw61xx_match[] = {
+       { .compatible = "marvell,88e6171" },
+       { .compatible = "marvell,88e6172" },
+       { .compatible = "marvell,88e6176" },
+       { .compatible = "marvell,88e6352" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mvsw61xx_match);
+
+static struct platform_driver mvsw61xx_driver = {
+       .probe = mvsw61xx_probe,
+       .remove = mvsw61xx_remove,
+       .driver = {
+               .name = "mvsw61xx",
+               .of_match_table = of_match_ptr(mvsw61xx_match),
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init mvsw61xx_module_init(void)
+{
+       return platform_driver_register(&mvsw61xx_driver);
+}
+late_initcall(mvsw61xx_module_init);
+
+static void __exit mvsw61xx_module_exit(void)
+{
+       platform_driver_unregister(&mvsw61xx_driver);
+}
+module_exit(mvsw61xx_module_exit);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/mvsw61xx.h b/target/linux/generic/files-4.9/drivers/net/phy/mvsw61xx.h
new file mode 100644 (file)
index 0000000..a07b09c
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Marvell 88E61xx switch driver
+ *
+ * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
+ * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
+ *
+ * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#ifndef __MVSW61XX_H
+#define __MVSW61XX_H
+
+#define MV_PORTS                       7
+#define MV_PORTS_MASK                  ((1 << MV_PORTS) - 1)
+
+#define MV_BASE                                0x10
+
+#define MV_SWITCHPORT_BASE             0x10
+#define MV_SWITCHPORT(_n)              (MV_SWITCHPORT_BASE + (_n))
+#define MV_SWITCHREGS                  (MV_BASE + 0xb)
+
+#define MV_VLANS                       64
+
+enum {
+       MV_PORT_STATUS                  = 0x00,
+       MV_PORT_PHYCTL                  = 0x01,
+       MV_PORT_JAMCTL                  = 0x02,
+       MV_PORT_IDENT                   = 0x03,
+       MV_PORT_CONTROL                 = 0x04,
+       MV_PORT_CONTROL1                = 0x05,
+       MV_PORT_VLANMAP                 = 0x06,
+       MV_PORT_VLANID                  = 0x07,
+       MV_PORT_CONTROL2                = 0x08,
+       MV_PORT_ASSOC                   = 0x0b,
+       MV_PORT_RX_DISCARD_LOW          = 0x10,
+       MV_PORT_RX_DISCARD_HIGH         = 0x11,
+       MV_PORT_IN_FILTERED             = 0x12,
+       MV_PORT_OUT_ACCEPTED            = 0x13,
+};
+#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
+
+enum {
+       MV_PORT_STATUS_FDX              = (1 << 10),
+       MV_PORT_STATUS_LINK             = (1 << 11),
+};
+
+enum {
+       MV_PORT_STATUS_CMODE_100BASE_X  = 0x8,
+       MV_PORT_STATUS_CMODE_1000BASE_X = 0x9,
+       MV_PORT_STATUS_CMODE_SGMII      = 0xa,
+};
+
+#define MV_PORT_STATUS_CMODE_MASK      0xf
+
+enum {
+       MV_PORT_STATUS_SPEED_10         = 0x00,
+       MV_PORT_STATUS_SPEED_100        = 0x01,
+       MV_PORT_STATUS_SPEED_1000       = 0x02,
+};
+#define MV_PORT_STATUS_SPEED_SHIFT     8
+#define MV_PORT_STATUS_SPEED_MASK      (3 << 8)
+
+enum {
+       MV_PORTCTRL_DISABLED            = (0 << 0),
+       MV_PORTCTRL_BLOCKING            = (1 << 0),
+       MV_PORTCTRL_LEARNING            = (2 << 0),
+       MV_PORTCTRL_FORWARDING          = (3 << 0),
+       MV_PORTCTRL_VLANTUN             = (1 << 7),
+       MV_PORTCTRL_EGRESS              = (1 << 12),
+};
+
+#define MV_PHYCTL_FC_MASK              (3 << 6)
+
+enum {
+       MV_PHYCTL_FC_ENABLE             = (3 << 6),
+       MV_PHYCTL_FC_DISABLE            = (1 << 6),
+};
+
+enum {
+       MV_8021Q_EGRESS_UNMODIFIED      = 0x00,
+       MV_8021Q_EGRESS_UNTAGGED        = 0x01,
+       MV_8021Q_EGRESS_TAGGED          = 0x02,
+       MV_8021Q_EGRESS_ADDTAG          = 0x03,
+};
+
+#define MV_8021Q_MODE_SHIFT            10
+#define MV_8021Q_MODE_MASK             (0x3 << MV_8021Q_MODE_SHIFT)
+
+enum {
+       MV_8021Q_MODE_DISABLE           = 0x00,
+       MV_8021Q_MODE_FALLBACK          = 0x01,
+       MV_8021Q_MODE_CHECK             = 0x02,
+       MV_8021Q_MODE_SECURE            = 0x03,
+};
+
+enum {
+       MV_8021Q_VLAN_ONLY              = (1 << 15),
+};
+
+#define MV_PORTASSOC_MONITOR           (1 << 15)
+
+enum {
+       MV_SWITCH_ATU_FID0              = 0x01,
+       MV_SWITCH_ATU_FID1              = 0x02,
+       MV_SWITCH_ATU_SID               = 0x03,
+       MV_SWITCH_CTRL                  = 0x04,
+       MV_SWITCH_ATU_CTRL              = 0x0a,
+       MV_SWITCH_ATU_OP                = 0x0b,
+       MV_SWITCH_ATU_DATA              = 0x0c,
+       MV_SWITCH_ATU_MAC0              = 0x0d,
+       MV_SWITCH_ATU_MAC1              = 0x0e,
+       MV_SWITCH_ATU_MAC2              = 0x0f,
+       MV_SWITCH_GLOBAL                = 0x1b,
+       MV_SWITCH_GLOBAL2               = 0x1c,
+};
+#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
+
+enum {
+       MV_SWITCHCTL_EEIE               = (1 << 0),
+       MV_SWITCHCTL_PHYIE              = (1 << 1),
+       MV_SWITCHCTL_ATUDONE            = (1 << 2),
+       MV_SWITCHCTL_ATUIE              = (1 << 3),
+       MV_SWITCHCTL_CTRMODE            = (1 << 8),
+       MV_SWITCHCTL_RELOAD             = (1 << 9),
+       MV_SWITCHCTL_MSIZE              = (1 << 10),
+       MV_SWITCHCTL_DROP               = (1 << 13),
+};
+
+enum {
+#define MV_ATUCTL_AGETIME_MIN          16
+#define MV_ATUCTL_AGETIME_MAX          4080
+#define MV_ATUCTL_AGETIME(_n)          ((((_n) / 16) & 0xff) << 4)
+       MV_ATUCTL_ATU_256               = (0 << 12),
+       MV_ATUCTL_ATU_512               = (1 << 12),
+       MV_ATUCTL_ATU_1K                = (2 << 12),
+       MV_ATUCTL_ATUMASK               = (3 << 12),
+       MV_ATUCTL_NO_LEARN              = (1 << 14),
+       MV_ATUCTL_RESET                 = (1 << 15),
+};
+
+enum {
+#define MV_ATUOP_DBNUM(_n)             ((_n) & 0x0f)
+       MV_ATUOP_NOOP                   = (0 << 12),
+       MV_ATUOP_FLUSH_ALL              = (1 << 12),
+       MV_ATUOP_FLUSH_U                = (2 << 12),
+       MV_ATUOP_LOAD_DB                = (3 << 12),
+       MV_ATUOP_GET_NEXT               = (4 << 12),
+       MV_ATUOP_FLUSH_DB               = (5 << 12),
+       MV_ATUOP_FLUSH_DB_UU            = (6 << 12),
+       MV_ATUOP_INPROGRESS             = (1 << 15),
+};
+
+enum {
+       MV_GLOBAL_STATUS                = 0x00,
+       MV_GLOBAL_ATU_FID               = 0x01,
+       MV_GLOBAL_VTU_FID               = 0x02,
+       MV_GLOBAL_VTU_SID               = 0x03,
+       MV_GLOBAL_CONTROL               = 0x04,
+       MV_GLOBAL_VTU_OP                = 0x05,
+       MV_GLOBAL_VTU_VID               = 0x06,
+       MV_GLOBAL_VTU_DATA1             = 0x07,
+       MV_GLOBAL_VTU_DATA2             = 0x08,
+       MV_GLOBAL_VTU_DATA3             = 0x09,
+       MV_GLOBAL_CONTROL2              = 0x1c,
+};
+#define MV_GLOBALREG(_type) MV_SWITCH_GLOBAL, MV_GLOBAL_##_type
+
+enum {
+       MV_GLOBAL2_SMI_OP               = 0x18,
+       MV_GLOBAL2_SMI_DATA             = 0x19,
+       MV_GLOBAL2_SDET_POLARITY        = 0x1d,
+};
+#define MV_GLOBAL2REG(_type) MV_SWITCH_GLOBAL2, MV_GLOBAL2_##_type
+
+enum {
+       MV_VTU_VID_VALID                = (1 << 12),
+};
+
+enum {
+       MV_VTUOP_PURGE                  = (1 << 12),
+       MV_VTUOP_LOAD                   = (3 << 12),
+       MV_VTUOP_INPROGRESS             = (1 << 15),
+       MV_VTUOP_STULOAD                = (5 << 12),
+       MV_VTUOP_VTU_GET_NEXT           = (4 << 12),
+       MV_VTUOP_STU_GET_NEXT           = (6 << 12),
+       MV_VTUOP_GET_VIOLATION          = (7 << 12),
+};
+
+enum {
+       MV_CONTROL_RESET                = (1 << 15),
+       MV_CONTROL_PPU_ENABLE           = (1 << 14),
+};
+
+enum {
+       MV_VTUCTL_EGRESS_UNMODIFIED     = (0 << 0),
+       MV_VTUCTL_EGRESS_UNTAGGED       = (1 << 0),
+       MV_VTUCTL_EGRESS_TAGGED         = (2 << 0),
+       MV_VTUCTL_DISCARD               = (3 << 0),
+};
+
+enum {
+       MV_STUCTL_STATE_DISABLED        = (0 << 0),
+       MV_STUCTL_STATE_BLOCKING        = (1 << 0),
+       MV_STUCTL_STATE_LEARNING        = (2 << 0),
+       MV_STUCTL_STATE_FORWARDING      = (3 << 0),
+};
+
+enum {
+       MV_INDIRECT_REG_CMD             = 0,
+       MV_INDIRECT_REG_DATA            = 1,
+};
+
+enum {
+       MV_INDIRECT_INPROGRESS          = 0x8000,
+       MV_INDIRECT_WRITE               = 0x9400,
+       MV_INDIRECT_READ                = 0x9800,
+};
+#define MV_INDIRECT_ADDR_S             5
+
+#define MV_IDENT_MASK                  0xfff0
+
+#define MV_IDENT_VALUE_6171            0x1710
+#define MV_IDENT_STR_6171              "MV88E6171"
+
+#define MV_IDENT_VALUE_6172            0x1720
+#define MV_IDENT_STR_6172              "MV88E6172"
+
+#define MV_IDENT_VALUE_6176            0x1760
+#define MV_IDENT_STR_6176              "MV88E6176"
+
+#define MV_IDENT_VALUE_6352            0x3520
+#define MV_IDENT_STR_6352              "MV88E6352"
+
+#define MV_PVID_MASK                   0x0fff
+
+#define MV_FDB_HI_MASK                 0x00ff
+#define MV_FDB_LO_MASK                 0xf000
+#define MV_FDB_HI_SHIFT                        4
+#define MV_FDB_LO_SHIFT                        12
+
+/* Marvell Specific PHY register */
+#define MII_MV_SPEC_CTRL               16
+enum {
+       MV_SPEC_MDI_CROSS_AUTO          = (0x6 << 4),
+       MV_SPEC_ENERGY_DETECT           = (0x3 << 8),
+       MV_SPEC_DOWNSHIFT_COUNTER       = (0x3 << 12),
+};
+
+#define MII_MV_PAGE                    22
+
+#define MV_REG_FIBER_SERDES            0xf
+#define MV_PAGE_FIBER_SERDES           0x1
+
+struct mvsw61xx_state {
+       struct switch_dev dev;
+       struct mii_bus *bus;
+       int base_addr;
+       u16 model;
+
+       bool registered;
+       bool is_indirect;
+
+       int cpu_port0;
+       int cpu_port1;
+
+       int vlan_enabled;
+       struct port_state {
+               u16 fdb;
+               u16 pvid;
+               u16 mask;
+               u8 qmode;
+       } ports[MV_PORTS];
+
+       struct vlan_state {
+               bool port_based;
+
+               u16 mask;
+               u16 vid;
+               u32 port_mode;
+               u32 port_sstate;
+       } vlans[MV_VLANS];
+
+       char buf[128];
+};
+
+#define get_state(_dev) container_of((_dev), struct mvsw61xx_state, dev)
+
+#endif
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/mvswitch.c b/target/linux/generic/files-4.9/drivers/net/phy/mvswitch.c
new file mode 100644 (file)
index 0000000..043978f
--- /dev/null
@@ -0,0 +1,444 @@
+/*
+ * Marvell 88E6060 switch driver
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/if_vlan.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include "mvswitch.h"
+
+/* Undefine this to use trailer mode instead.
+ * I don't know if header mode works with all chips */
+#define HEADER_MODE    1
+
+MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
+MODULE_AUTHOR("Felix Fietkau");
+MODULE_LICENSE("GPL");
+
+#define MVSWITCH_MAGIC 0x88E6060
+
+struct mvswitch_priv {
+       netdev_features_t orig_features;
+       u8 vlans[16];
+};
+
+#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
+
+static inline u16
+r16(struct phy_device *phydev, int addr, int reg)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, addr, reg);
+}
+
+static inline void
+w16(struct phy_device *phydev, int addr, int reg, u16 val)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, addr, reg, val);
+}
+
+
+static struct sk_buff *
+mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct mvswitch_priv *priv;
+       char *buf = NULL;
+       u16 vid;
+
+       priv = dev->phy_ptr;
+       if (unlikely(!priv))
+               goto error;
+
+       if (unlikely(skb->len < 16))
+               goto error;
+
+#ifdef HEADER_MODE
+       if (__vlan_hwaccel_get_tag(skb, &vid))
+               goto error;
+
+       if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
+               if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
+                       goto error_expand;
+               if (skb->len < 62)
+                       skb->len = 62;
+       }
+       buf = skb_push(skb, MV_HEADER_SIZE);
+#else
+       if (__vlan_get_tag(skb, &vid))
+               goto error;
+
+       if (unlikely((vid > 15 || !priv->vlans[vid])))
+               goto error;
+
+       if (skb->len <= 64) {
+               if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
+                       goto error_expand;
+
+               buf = skb->data + 64;
+               skb->len = 64 + MV_TRAILER_SIZE;
+       } else {
+               if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
+                       if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
+                               goto error_expand;
+               }
+               buf = skb_put(skb, 4);
+       }
+
+       /* move the ethernet header 4 bytes forward, overwriting the vlan tag */
+       memmove(skb->data + 4, skb->data, 12);
+       skb->data += 4;
+       skb->len -= 4;
+       skb->mac_header += 4;
+#endif
+
+       if (!buf)
+               goto error;
+
+
+#ifdef HEADER_MODE
+       /* prepend the tag */
+       *((__be16 *) buf) = cpu_to_be16(
+               ((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
+               ((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
+       );
+#else
+       /* append the tag */
+       *((__be32 *) buf) = cpu_to_be32((
+               (MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
+               ((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
+       ));
+#endif
+
+       return skb;
+
+error_expand:
+       if (net_ratelimit())
+               printk("%s: failed to expand/update skb for the switch\n", dev->name);
+
+error:
+       /* any errors? drop the packet! */
+       dev_kfree_skb_any(skb);
+       return NULL;
+}
+
+static void
+mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
+{
+       struct mvswitch_priv *priv;
+       unsigned char *buf;
+       int vlan = -1;
+       int i;
+
+       priv = dev->phy_ptr;
+       if (WARN_ON_ONCE(!priv))
+               return;
+
+#ifdef HEADER_MODE
+       buf = skb->data;
+       skb_pull(skb, MV_HEADER_SIZE);
+#else
+       buf = skb->data + skb->len - MV_TRAILER_SIZE;
+       if (buf[0] != 0x80)
+               return;
+#endif
+
+       /* look for the vlan matching the incoming port */
+       for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
+               if ((1 << buf[1]) & priv->vlans[i])
+                       vlan = i;
+       }
+
+       if (vlan == -1)
+               return;
+
+       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
+}
+
+
+static int
+mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
+{
+       int i = 100;
+       u16 r;
+
+       do {
+               r = r16(pdev, addr, reg) & mask;
+               if (r == val)
+                       return 0;
+       } while(--i > 0);
+       return -ETIMEDOUT;
+}
+
+static int
+mvswitch_config_init(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+       struct net_device *dev = pdev->attached_dev;
+       u8 vlmap = 0;
+       int i;
+
+       if (!dev)
+               return -EINVAL;
+
+       printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
+       pdev->supported = ADVERTISED_100baseT_Full;
+       pdev->advertising = ADVERTISED_100baseT_Full;
+       dev->phy_ptr = priv;
+       pdev->irq = PHY_POLL;
+#ifdef HEADER_MODE
+       dev->flags |= IFF_PROMISC;
+#endif
+
+       /* initialize default vlans */
+       for (i = 0; i < MV_PORTS; i++)
+               priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i);
+
+       /* before entering reset, disable all ports */
+       for (i = 0; i < MV_PORTS; i++)
+               w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
+
+       msleep(2); /* wait for the status change to settle in */
+
+       /* put the ATU in reset */
+       w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
+
+       i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
+       if (i < 0) {
+               printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
+               return i;
+       }
+
+       /* set the ATU flags */
+       w16(pdev, MV_SWITCHREG(ATU_CTRL),
+               MV_ATUCTL_NO_LEARN |
+               MV_ATUCTL_ATU_1K |
+               MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
+       );
+
+       /* initialize the cpu port */
+       w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
+#ifdef HEADER_MODE
+               MV_PORTCTRL_HEADER |
+#else
+               MV_PORTCTRL_RXTR |
+               MV_PORTCTRL_TXTR |
+#endif
+               MV_PORTCTRL_ENABLED
+       );
+       /* wait for the phy change to settle in */
+       msleep(2);
+       for (i = 0; i < MV_PORTS; i++) {
+               u8 pvid = 0;
+               int j;
+
+               vlmap = 0;
+
+               /* look for the matching vlan */
+               for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
+                       if (priv->vlans[j] & (1 << i)) {
+                               vlmap = priv->vlans[j];
+                               pvid = j;
+                       }
+               }
+               /* leave port unconfigured if it's not part of a vlan */
+               if (!vlmap)
+                       continue;
+
+               /* add the cpu port to the allowed destinations list */
+               vlmap |= (1 << MV_CPUPORT);
+
+               /* take port out of its own vlan destination map */
+               vlmap &= ~(1 << i);
+
+               /* apply vlan settings */
+               w16(pdev, MV_PORTREG(VLANMAP, i),
+                       MV_PORTVLAN_PORTS(vlmap) |
+                       MV_PORTVLAN_ID(i)
+               );
+
+               /* re-enable port */
+               w16(pdev, MV_PORTREG(CONTROL, i),
+                       MV_PORTCTRL_ENABLED
+               );
+       }
+
+       w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
+               MV_PORTVLAN_ID(MV_CPUPORT)
+       );
+
+       /* set the port association vector */
+       for (i = 0; i <= MV_PORTS; i++) {
+               w16(pdev, MV_PORTREG(ASSOC, i),
+                       MV_PORTASSOC_PORTS(1 << i)
+               );
+       }
+
+       /* init switch control */
+       w16(pdev, MV_SWITCHREG(CTRL),
+               MV_SWITCHCTL_MSIZE |
+               MV_SWITCHCTL_DROP
+       );
+
+       dev->eth_mangle_rx = mvswitch_mangle_rx;
+       dev->eth_mangle_tx = mvswitch_mangle_tx;
+       priv->orig_features = dev->features;
+
+#ifdef HEADER_MODE
+       dev->priv_flags |= IFF_NO_IP_ALIGN;
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
+#else
+       dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
+#endif
+
+       return 0;
+}
+
+static int
+mvswitch_read_status(struct phy_device *pdev)
+{
+       pdev->speed = SPEED_100;
+       pdev->duplex = DUPLEX_FULL;
+       pdev->link = 1;
+
+       /* XXX ugly workaround: we can't force the switch
+        * to gracefully handle hosts moving from one port to another,
+        * so we have to regularly clear the ATU database */
+
+       /* wait for the ATU to become available */
+       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
+
+       /* flush the ATU */
+       w16(pdev, MV_SWITCHREG(ATU_OP),
+               MV_ATUOP_INPROGRESS |
+               MV_ATUOP_FLUSH_ALL
+       );
+
+       /* wait for operation to complete */
+       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
+
+       return 0;
+}
+
+static int
+mvswitch_aneg_done(struct phy_device *phydev)
+{
+       return 1;       /* Return any positive value */
+}
+
+static int
+mvswitch_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static void
+mvswitch_detach(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+       struct net_device *dev = pdev->attached_dev;
+
+       if (!dev)
+               return;
+
+       dev->phy_ptr = NULL;
+       dev->eth_mangle_rx = NULL;
+       dev->eth_mangle_tx = NULL;
+       dev->features = priv->orig_features;
+       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
+}
+
+static void
+mvswitch_remove(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv = to_mvsw(pdev);
+
+       kfree(priv);
+}
+
+static int
+mvswitch_probe(struct phy_device *pdev)
+{
+       struct mvswitch_priv *priv;
+
+       priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       pdev->priv = priv;
+
+       return 0;
+}
+
+static int
+mvswitch_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       if (dev->mdio.addr != 0x10)
+               return 0;
+
+       reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
+       if (reg != MV_IDENT_VALUE)
+               return 0;
+
+       dev->phy_id = MVSWITCH_MAGIC;
+       return 0;
+}
+
+
+static struct phy_driver mvswitch_driver = {
+       .name           = "Marvell 88E6060",
+       .phy_id         = MVSWITCH_MAGIC,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = &mvswitch_probe,
+       .remove         = &mvswitch_remove,
+       .detach         = &mvswitch_detach,
+       .config_init    = &mvswitch_config_init,
+       .config_aneg    = &mvswitch_config_aneg,
+       .aneg_done      = &mvswitch_aneg_done,
+       .read_status    = &mvswitch_read_status,
+};
+
+static int __init
+mvswitch_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
+       return phy_driver_register(&mvswitch_driver, THIS_MODULE);
+}
+
+static void __exit
+mvswitch_exit(void)
+{
+       phy_driver_unregister(&mvswitch_driver);
+}
+
+module_init(mvswitch_init);
+module_exit(mvswitch_exit);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/mvswitch.h b/target/linux/generic/files-4.9/drivers/net/phy/mvswitch.h
new file mode 100644 (file)
index 0000000..ab2a1a1
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Marvell 88E6060 switch driver
+ * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+#ifndef __MVSWITCH_H
+#define __MVSWITCH_H
+
+#define MV_HEADER_SIZE 2
+#define MV_HEADER_PORTS_M      0x001f
+#define MV_HEADER_PORTS_S      0
+#define MV_HEADER_VLAN_M       0xf000
+#define MV_HEADER_VLAN_S       12
+
+#define MV_TRAILER_SIZE        4
+#define MV_TRAILER_PORTS_M     0x1f
+#define MV_TRAILER_PORTS_S     16
+#define MV_TRAILER_FLAGS_S     24
+#define MV_TRAILER_OVERRIDE    0x80
+
+
+#define MV_PORTS       5
+#define MV_WANPORT     4
+#define MV_CPUPORT     5
+
+#define MV_BASE                0x10
+
+#define MV_PHYPORT_BASE                (MV_BASE + 0x0)
+#define MV_PHYPORT(_n)         (MV_PHYPORT_BASE + (_n))
+#define MV_SWITCHPORT_BASE     (MV_BASE + 0x8)
+#define MV_SWITCHPORT(_n)      (MV_SWITCHPORT_BASE + (_n))
+#define MV_SWITCHREGS          (MV_BASE + 0xf)
+
+enum {
+       MV_PHY_CONTROL      = 0x00,
+       MV_PHY_STATUS       = 0x01,
+       MV_PHY_IDENT0       = 0x02,
+       MV_PHY_IDENT1       = 0x03,
+       MV_PHY_ANEG         = 0x04,
+       MV_PHY_LINK_ABILITY = 0x05,
+       MV_PHY_ANEG_EXPAND  = 0x06,
+       MV_PHY_XMIT_NEXTP   = 0x07,
+       MV_PHY_LINK_NEXTP   = 0x08,
+       MV_PHY_CONTROL1     = 0x10,
+       MV_PHY_STATUS1      = 0x11,
+       MV_PHY_INTR_EN      = 0x12,
+       MV_PHY_INTR_STATUS  = 0x13,
+       MV_PHY_INTR_PORT    = 0x14,
+       MV_PHY_RECV_COUNTER = 0x16,
+       MV_PHY_LED_PARALLEL = 0x16,
+       MV_PHY_LED_STREAM   = 0x17,
+       MV_PHY_LED_CTRL     = 0x18,
+       MV_PHY_LED_OVERRIDE = 0x19,
+       MV_PHY_VCT_CTRL     = 0x1a,
+       MV_PHY_VCT_STATUS   = 0x1b,
+       MV_PHY_CONTROL2     = 0x1e
+};
+#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
+
+enum {
+       MV_PORT_STATUS      = 0x00,
+       MV_PORT_IDENT       = 0x03,
+       MV_PORT_CONTROL     = 0x04,
+       MV_PORT_VLANMAP     = 0x06,
+       MV_PORT_ASSOC       = 0x0b,
+       MV_PORT_RXCOUNT     = 0x10,
+       MV_PORT_TXCOUNT     = 0x11,
+};
+#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
+
+enum {
+       MV_PORTCTRL_BLOCK   =  (1 << 0),
+       MV_PORTCTRL_LEARN   =  (2 << 0),
+       MV_PORTCTRL_ENABLED =  (3 << 0),
+       MV_PORTCTRL_VLANTUN =  (1 << 7),        /* Enforce VLANs on packets */
+       MV_PORTCTRL_RXTR    =  (1 << 8),        /* Enable Marvell packet trailer for ingress */
+       MV_PORTCTRL_HEADER      = (1 << 11),    /* Enable Marvell packet header mode for port */
+       MV_PORTCTRL_TXTR    = (1 << 14),        /* Enable Marvell packet trailer for egress */
+       MV_PORTCTRL_FORCEFL = (1 << 15),        /* force flow control */
+};
+
+#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
+#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
+
+#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
+#define MV_PORTASSOC_MONITOR   (1 << 15)
+
+enum {
+       MV_SWITCH_MAC0      = 0x01,
+       MV_SWITCH_MAC1      = 0x02,
+       MV_SWITCH_MAC2      = 0x03,
+       MV_SWITCH_CTRL      = 0x04,
+       MV_SWITCH_ATU_CTRL  = 0x0a,
+       MV_SWITCH_ATU_OP    = 0x0b,
+       MV_SWITCH_ATU_DATA  = 0x0c,
+       MV_SWITCH_ATU_MAC0  = 0x0d,
+       MV_SWITCH_ATU_MAC1  = 0x0e,
+       MV_SWITCH_ATU_MAC2  = 0x0f,
+};
+#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
+
+enum {
+       MV_SWITCHCTL_EEIE   =  (1 << 0),        /* EEPROM interrupt enable */
+       MV_SWITCHCTL_PHYIE  =  (1 << 1),        /* PHY interrupt enable */
+       MV_SWITCHCTL_ATUDONE=  (1 << 2),        /* ATU done interrupt enable */
+       MV_SWITCHCTL_ATUIE  =  (1 << 3),        /* ATU interrupt enable */
+       MV_SWITCHCTL_CTRMODE=  (1 << 8),        /* statistics for rx and tx errors */
+       MV_SWITCHCTL_RELOAD =  (1 << 9),        /* reload registers from eeprom */
+       MV_SWITCHCTL_MSIZE  = (1 << 10),        /* increase maximum frame size */
+       MV_SWITCHCTL_DROP   = (1 << 13),        /* discard frames with excessive collisions */
+};
+
+enum {
+#define MV_ATUCTL_AGETIME_MIN  16
+#define MV_ATUCTL_AGETIME_MAX  4080
+#define MV_ATUCTL_AGETIME(_n)  ((((_n) / 16) & 0xff) << 4)
+       MV_ATUCTL_ATU_256   = (0 << 12),
+       MV_ATUCTL_ATU_512   = (1 << 12),
+       MV_ATUCTL_ATU_1K        = (2 << 12),
+       MV_ATUCTL_ATUMASK   = (3 << 12),
+       MV_ATUCTL_NO_LEARN  = (1 << 14),
+       MV_ATUCTL_RESET     = (1 << 15),
+};
+
+enum {
+#define MV_ATUOP_DBNUM(_n)     ((_n) & 0x0f)
+
+       MV_ATUOP_NOOP       = (0 << 12),
+       MV_ATUOP_FLUSH_ALL  = (1 << 12),
+       MV_ATUOP_FLUSH_U    = (2 << 12),
+       MV_ATUOP_LOAD_DB    = (3 << 12),
+       MV_ATUOP_GET_NEXT   = (4 << 12),
+       MV_ATUOP_FLUSH_DB   = (5 << 12),
+       MV_ATUOP_FLUSH_DB_UU= (6 << 12),
+
+       MV_ATUOP_INPROGRESS = (1 << 15),
+};
+
+#define MV_IDENT_MASK          0xfff0
+#define MV_IDENT_VALUE         0x0600
+
+#endif
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/psb6970.c b/target/linux/generic/files-4.9/drivers/net/phy/psb6970.c
new file mode 100644 (file)
index 0000000..c1a381c
--- /dev/null
@@ -0,0 +1,441 @@
+/*
+ * Lantiq PSB6970 (Tantos) Switch driver
+ *
+ * Copyright (c) 2009,2010 Team Embedded.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation.
+ *
+ * The switch programming done in this driver follows the 
+ * "Ethernet Traffic Separation using VLAN" Application Note as
+ * published by Lantiq.
+ */
+
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/switch.h>
+#include <linux/phy.h>
+
+#define PSB6970_MAX_VLANS              16
+#define PSB6970_NUM_PORTS              7
+#define PSB6970_DEFAULT_PORT_CPU       6
+#define PSB6970_IS_CPU_PORT(x)         ((x) > 4)
+
+#define PHYADDR(_reg)          ((_reg >> 5) & 0xff), (_reg & 0x1f)
+
+/* --- Identification --- */
+#define PSB6970_CI0            0x0100
+#define PSB6970_CI0_MASK       0x000f
+#define PSB6970_CI1            0x0101
+#define PSB6970_CI1_VAL                0x2599
+#define PSB6970_CI1_MASK       0xffff
+
+/* --- VLAN filter table --- */
+#define PSB6970_VFxL(i)                ((i)*2+0x10)    /* VLAN Filter Low */
+#define PSB6970_VFxL_VV                (1 << 15)       /* VLAN_Valid */
+
+#define PSB6970_VFxH(i)                ((i)*2+0x11)    /* VLAN Filter High */
+#define PSB6970_VFxH_TM_SHIFT  7               /* Tagged Member */
+
+/* --- Port registers --- */
+#define PSB6970_EC(p)          ((p)*0x20+2)    /* Extended Control */
+#define PSB6970_EC_IFNTE       (1 << 1)        /* Input Force No Tag Enable */
+
+#define PSB6970_PBVM(p)                ((p)*0x20+3)    /* Port Base VLAN Map */
+#define PSB6970_PBVM_VMCE      (1 << 8)
+#define PSB6970_PBVM_AOVTP     (1 << 9)
+#define PSB6970_PBVM_VSD       (1 << 10)
+#define PSB6970_PBVM_VC                (1 << 11)       /* VID Check with VID table */
+#define PSB6970_PBVM_TBVE      (1 << 13)       /* Tag-Based VLAN enable */
+
+#define PSB6970_DVID(p)                ((p)*0x20+4)    /* Default VLAN ID & Priority */
+
+struct psb6970_priv {
+       struct switch_dev dev;
+       struct phy_device *phy;
+       u16 (*read) (struct phy_device* phydev, int reg);
+       void (*write) (struct phy_device* phydev, int reg, u16 val);
+       struct mutex reg_mutex;
+
+       /* all fields below are cleared on reset */
+       bool vlan;
+       u16 vlan_id[PSB6970_MAX_VLANS];
+       u8 vlan_table[PSB6970_MAX_VLANS];
+       u8 vlan_tagged;
+       u16 pvid[PSB6970_NUM_PORTS];
+};
+
+#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
+
+static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       return bus->read(bus, PHYADDR(reg));
+}
+
+static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
+{
+       struct mii_bus *bus = phydev->mdio.bus;
+
+       bus->write(bus, PHYADDR(reg), val);
+}
+
+static int
+psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       priv->vlan = !!val->value.i;
+       return 0;
+}
+
+static int
+psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
+                struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       val->value.i = priv->vlan;
+       return 0;
+}
+
+static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+
+       /* make sure no invalid PVIDs get set */
+       if (vlan >= dev->vlans)
+               return -EINVAL;
+
+       priv->pvid[port] = vlan;
+       return 0;
+}
+
+static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       *vlan = priv->pvid[port];
+       return 0;
+}
+
+static int
+psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       priv->vlan_id[val->port_vlan] = val->value.i;
+       return 0;
+}
+
+static int
+psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
+               struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       val->value.i = priv->vlan_id[val->port_vlan];
+       return 0;
+}
+
+static struct switch_attr psb6970_globals[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "enable_vlan",
+        .description = "Enable VLAN mode",
+        .set = psb6970_set_vlan,
+        .get = psb6970_get_vlan,
+        .max = 1},
+};
+
+static struct switch_attr psb6970_port[] = {
+};
+
+static struct switch_attr psb6970_vlan[] = {
+       {
+        .type = SWITCH_TYPE_INT,
+        .name = "vid",
+        .description = "VLAN ID (0-4094)",
+        .set = psb6970_set_vid,
+        .get = psb6970_get_vid,
+        .max = 4094,
+        },
+};
+
+static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       u8 ports = priv->vlan_table[val->port_vlan];
+       int i;
+
+       val->len = 0;
+       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
+               struct switch_port *p;
+
+               if (!(ports & (1 << i)))
+                       continue;
+
+               p = &val->value.ports[val->len++];
+               p->id = i;
+               if (priv->vlan_tagged & (1 << i))
+                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               else
+                       p->flags = 0;
+       }
+       return 0;
+}
+
+static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       u8 *vt = &priv->vlan_table[val->port_vlan];
+       int i, j;
+
+       *vt = 0;
+       for (i = 0; i < val->len; i++) {
+               struct switch_port *p = &val->value.ports[i];
+
+               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
+                       priv->vlan_tagged |= (1 << p->id);
+               else {
+                       priv->vlan_tagged &= ~(1 << p->id);
+                       priv->pvid[p->id] = val->port_vlan;
+
+                       /* make sure that an untagged port does not
+                        * appear in other vlans */
+                       for (j = 0; j < PSB6970_MAX_VLANS; j++) {
+                               if (j == val->port_vlan)
+                                       continue;
+                               priv->vlan_table[j] &= ~(1 << p->id);
+                       }
+               }
+
+               *vt |= 1 << p->id;
+       }
+       return 0;
+}
+
+static int psb6970_hw_apply(struct switch_dev *dev)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       int i, j;
+
+       mutex_lock(&priv->reg_mutex);
+
+       if (priv->vlan) {
+               /* into the vlan translation unit */
+               for (j = 0; j < PSB6970_MAX_VLANS; j++) {
+                       u8 vp = priv->vlan_table[j];
+
+                       if (vp) {
+                               priv->write(priv->phy, PSB6970_VFxL(j),
+                                           PSB6970_VFxL_VV | priv->vlan_id[j]);
+                               priv->write(priv->phy, PSB6970_VFxH(j),
+                                           ((vp & priv->
+                                             vlan_tagged) <<
+                                            PSB6970_VFxH_TM_SHIFT) | vp);
+                       } else  /* clear VLAN Valid flag for unused vlans */
+                               priv->write(priv->phy, PSB6970_VFxL(j), 0);
+
+               }
+       }
+
+       /* update the port destination mask registers and tag settings */
+       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
+               int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
+
+               if (priv->vlan) {
+                       ec = PSB6970_EC_IFNTE;
+                       dvid = priv->vlan_id[priv->pvid[i]];
+                       pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
+
+                       if ((i << 1) & priv->vlan_tagged)
+                               pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
+               }
+
+               priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
+
+               if (!PSB6970_IS_CPU_PORT(i)) {
+                       priv->write(priv->phy, PSB6970_EC(i), ec);
+                       priv->write(priv->phy, PSB6970_DVID(i), dvid);
+               }
+       }
+
+       mutex_unlock(&priv->reg_mutex);
+       return 0;
+}
+
+static int psb6970_reset_switch(struct switch_dev *dev)
+{
+       struct psb6970_priv *priv = to_psb6970(dev);
+       int i;
+
+       mutex_lock(&priv->reg_mutex);
+
+       memset(&priv->vlan, 0, sizeof(struct psb6970_priv) -
+              offsetof(struct psb6970_priv, vlan));
+
+       for (i = 0; i < PSB6970_MAX_VLANS; i++)
+               priv->vlan_id[i] = i;
+
+       mutex_unlock(&priv->reg_mutex);
+
+       return psb6970_hw_apply(dev);
+}
+
+static const struct switch_dev_ops psb6970_ops = {
+       .attr_global = {
+                       .attr = psb6970_globals,
+                       .n_attr = ARRAY_SIZE(psb6970_globals),
+                       },
+       .attr_port = {
+                     .attr = psb6970_port,
+                     .n_attr = ARRAY_SIZE(psb6970_port),
+                     },
+       .attr_vlan = {
+                     .attr = psb6970_vlan,
+                     .n_attr = ARRAY_SIZE(psb6970_vlan),
+                     },
+       .get_port_pvid = psb6970_get_pvid,
+       .set_port_pvid = psb6970_set_pvid,
+       .get_vlan_ports = psb6970_get_ports,
+       .set_vlan_ports = psb6970_set_ports,
+       .apply_config = psb6970_hw_apply,
+       .reset_switch = psb6970_reset_switch,
+};
+
+static int psb6970_config_init(struct phy_device *pdev)
+{
+       struct psb6970_priv *priv;
+       struct net_device *dev = pdev->attached_dev;
+       struct switch_dev *swdev;
+       int ret;
+
+       priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
+       priv->phy = pdev;
+
+       if (pdev->mdio.addr == 0)
+               printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
+                      pdev->attached_dev->name);
+
+       if (pdev->mdio.addr != 0) {
+               kfree(priv);
+               return 0;
+       }
+
+       pdev->supported = pdev->advertising = SUPPORTED_100baseT_Full;
+
+       mutex_init(&priv->reg_mutex);
+       priv->read = psb6970_mii_read;
+       priv->write = psb6970_mii_write;
+
+       pdev->priv = priv;
+
+       swdev = &priv->dev;
+       swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
+       swdev->ops = &psb6970_ops;
+
+       swdev->name = "Lantiq PSB6970";
+       swdev->vlans = PSB6970_MAX_VLANS;
+       swdev->ports = PSB6970_NUM_PORTS;
+
+       if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
+               kfree(priv);
+               goto done;
+       }
+
+       ret = psb6970_reset_switch(&priv->dev);
+       if (ret) {
+               kfree(priv);
+               goto done;
+       }
+
+       dev->phy_ptr = priv;
+
+done:
+       return ret;
+}
+
+static int psb6970_read_status(struct phy_device *phydev)
+{
+       phydev->speed = SPEED_100;
+       phydev->duplex = DUPLEX_FULL;
+       phydev->link = 1;
+
+       phydev->state = PHY_RUNNING;
+       netif_carrier_on(phydev->attached_dev);
+       phydev->adjust_link(phydev->attached_dev);
+
+       return 0;
+}
+
+static int psb6970_config_aneg(struct phy_device *phydev)
+{
+       return 0;
+}
+
+static int psb6970_probe(struct phy_device *pdev)
+{
+       return 0;
+}
+
+static void psb6970_remove(struct phy_device *pdev)
+{
+       struct psb6970_priv *priv = pdev->priv;
+
+       if (!priv)
+               return;
+
+       if (pdev->mdio.addr == 0)
+               unregister_switch(&priv->dev);
+       kfree(priv);
+}
+
+static int psb6970_fixup(struct phy_device *dev)
+{
+       struct mii_bus *bus = dev->mdio.bus;
+       u16 reg;
+
+       /* look for the switch on the bus */
+       reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
+       if (reg != PSB6970_CI1_VAL)
+               return 0;
+
+       dev->phy_id = (reg << 16);
+       dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
+
+       return 0;
+}
+
+static struct phy_driver psb6970_driver = {
+       .name = "Lantiq PSB6970",
+       .phy_id = PSB6970_CI1_VAL << 16,
+       .phy_id_mask = 0xffff0000,
+       .features = PHY_BASIC_FEATURES,
+       .probe = psb6970_probe,
+       .remove = psb6970_remove,
+       .config_init = &psb6970_config_init,
+       .config_aneg = &psb6970_config_aneg,
+       .read_status = &psb6970_read_status,
+};
+
+int __init psb6970_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
+       return phy_driver_register(&psb6970_driver, THIS_MODULE);
+}
+
+module_init(psb6970_init);
+
+void __exit psb6970_exit(void)
+{
+       phy_driver_unregister(&psb6970_driver);
+}
+
+module_exit(psb6970_exit);
+
+MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
+MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
+MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8306.c b/target/linux/generic/files-4.9/drivers/net/phy/rtl8306.c
new file mode 100644 (file)
index 0000000..6d09c10
--- /dev/null
@@ -0,0 +1,1066 @@
+/*
+ * rtl8306.c: RTL8306S switch driver
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <net/genetlink.h>
+#include <linux/switch.h>
+#include <linux/delay.h>
+#include <linux/phy.h>
+#include <linux/version.h>
+
+//#define DEBUG 1
+
+/* Global (PHY0) */
+#define RTL8306_REG_PAGE               16
+#define RTL8306_REG_PAGE_LO            (1 << 15)
+#define RTL8306_REG_PAGE_HI            (1 << 1) /* inverted */
+
+#define RTL8306_NUM_VLANS              16
+#define RTL8306_NUM_PORTS              6
+#define RTL8306_PORT_CPU               5
+#define RTL8306_NUM_PAGES              4
+#define RTL8306_NUM_REGS               32
+
+#define RTL_NAME_S          "RTL8306S"
+#define RTL_NAME_SD         "RTL8306SD"
+#define RTL_NAME_SDM        "RTL8306SDM"
+#define RTL_NAME_UNKNOWN    "RTL8306(unknown)"
+
+#define RTL8306_MAGIC  0x8306
+
+static LIST_HEAD(phydevs);
+
+struct rtl_priv {
+       struct list_head list;
+       struct switch_dev dev;
+       int page;
+       int type;
+       int do_cpu;
+       struct mii_bus *bus;
+       char hwname[sizeof(RTL_NAME_UNKNOWN)];
+       bool fixup;
+};
+
+struct rtl_phyregs {
+       int nway;
+       int speed;
+       int duplex;
+};
+
+#define to_rtl(_dev) container_of(_dev, struct rtl_priv, dev)
+
+enum {
+       RTL_TYPE_S,
+       RTL_TYPE_SD,
+       RTL_TYPE_SDM,
+};
+
+struct rtl_reg {
+       int page;
+       int phy;
+       int reg;
+       int bits;
+       int shift;
+       int inverted;
+};
+
+#define RTL_VLAN_REGOFS(name) \
+       (RTL_REG_VLAN1_##name - RTL_REG_VLAN0_##name)
+
+#define RTL_PORT_REGOFS(name) \
+       (RTL_REG_PORT1_##name - RTL_REG_PORT0_##name)
+
+#define RTL_PORT_REG(id, reg) \
+       (RTL_REG_PORT0_##reg + (id * RTL_PORT_REGOFS(reg)))
+
+#define RTL_VLAN_REG(id, reg) \
+       (RTL_REG_VLAN0_##reg + (id * RTL_VLAN_REGOFS(reg)))
+
+#define RTL_GLOBAL_REGATTR(reg) \
+       .id = RTL_REG_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = 0, \
+       .set = rtl_attr_set_int, \
+       .get = rtl_attr_get_int
+
+#define RTL_PORT_REGATTR(reg) \
+       .id = RTL_REG_PORT0_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = RTL_PORT_REGOFS(reg), \
+       .set = rtl_attr_set_port_int, \
+       .get = rtl_attr_get_port_int
+
+#define RTL_VLAN_REGATTR(reg) \
+       .id = RTL_REG_VLAN0_##reg, \
+       .type = SWITCH_TYPE_INT, \
+       .ofs = RTL_VLAN_REGOFS(reg), \
+       .set = rtl_attr_set_vlan_int, \
+       .get = rtl_attr_get_vlan_int
+
+enum rtl_regidx {
+       RTL_REG_CHIPID,
+       RTL_REG_CHIPVER,
+       RTL_REG_CHIPTYPE,
+       RTL_REG_CPUPORT,
+
+       RTL_REG_EN_CPUPORT,
+       RTL_REG_EN_TAG_OUT,
+       RTL_REG_EN_TAG_CLR,
+       RTL_REG_EN_TAG_IN,
+       RTL_REG_TRAP_CPU,
+       RTL_REG_CPU_LINKUP,
+       RTL_REG_TRUNK_PORTSEL,
+       RTL_REG_EN_TRUNK,
+       RTL_REG_RESET,
+
+       RTL_REG_VLAN_ENABLE,
+       RTL_REG_VLAN_FILTER,
+       RTL_REG_VLAN_TAG_ONLY,
+       RTL_REG_VLAN_TAG_AWARE,
+#define RTL_VLAN_ENUM(id) \
+       RTL_REG_VLAN##id##_VID, \
+       RTL_REG_VLAN##id##_PORTMASK
+       RTL_VLAN_ENUM(0),
+       RTL_VLAN_ENUM(1),
+       RTL_VLAN_ENUM(2),
+       RTL_VLAN_ENUM(3),
+       RTL_VLAN_ENUM(4),
+       RTL_VLAN_ENUM(5),
+       RTL_VLAN_ENUM(6),
+       RTL_VLAN_ENUM(7),
+       RTL_VLAN_ENUM(8),
+       RTL_VLAN_ENUM(9),
+       RTL_VLAN_ENUM(10),
+       RTL_VLAN_ENUM(11),
+       RTL_VLAN_ENUM(12),
+       RTL_VLAN_ENUM(13),
+       RTL_VLAN_ENUM(14),
+       RTL_VLAN_ENUM(15),
+#define RTL_PORT_ENUM(id) \
+       RTL_REG_PORT##id##_PVID, \
+       RTL_REG_PORT##id##_NULL_VID_REPLACE, \
+       RTL_REG_PORT##id##_NON_PVID_DISCARD, \
+       RTL_REG_PORT##id##_VID_INSERT, \
+       RTL_REG_PORT##id##_TAG_INSERT, \
+       RTL_REG_PORT##id##_LINK, \
+       RTL_REG_PORT##id##_SPEED, \
+       RTL_REG_PORT##id##_NWAY, \
+       RTL_REG_PORT##id##_NRESTART, \
+       RTL_REG_PORT##id##_DUPLEX, \
+       RTL_REG_PORT##id##_RXEN, \
+       RTL_REG_PORT##id##_TXEN
+       RTL_PORT_ENUM(0),
+       RTL_PORT_ENUM(1),
+       RTL_PORT_ENUM(2),
+       RTL_PORT_ENUM(3),
+       RTL_PORT_ENUM(4),
+       RTL_PORT_ENUM(5),
+};
+
+static const struct rtl_reg rtl_regs[] = {
+       [RTL_REG_CHIPID]         = { 0, 4, 30, 16,  0, 0 },
+       [RTL_REG_CHIPVER]        = { 0, 4, 31,  8,  0, 0 },
+       [RTL_REG_CHIPTYPE]       = { 0, 4, 31,  2,  8, 0 },
+
+       /* CPU port number */
+       [RTL_REG_CPUPORT]        = { 2, 4, 21,  3,  0, 0 },
+       /* Enable CPU port function */
+       [RTL_REG_EN_CPUPORT]     = { 3, 2, 21,  1, 15, 1 },
+       /* Enable CPU port tag insertion */
+       [RTL_REG_EN_TAG_OUT]     = { 3, 2, 21,  1, 12, 0 },
+       /* Enable CPU port tag removal */
+       [RTL_REG_EN_TAG_CLR]     = { 3, 2, 21,  1, 11, 0 },
+       /* Enable CPU port tag checking */
+       [RTL_REG_EN_TAG_IN]      = { 0, 4, 21,  1,  7, 0 },
+       [RTL_REG_EN_TRUNK]       = { 0, 0, 19,  1, 11, 1 },
+       [RTL_REG_TRUNK_PORTSEL]  = { 0, 0, 16,  1,  6, 1 },
+       [RTL_REG_RESET]          = { 0, 0, 16,  1, 12, 0 },
+
+       [RTL_REG_TRAP_CPU]       = { 3, 2, 22,  1,  6, 0 },
+       [RTL_REG_CPU_LINKUP]     = { 0, 6, 22,  1, 15, 0 },
+
+       [RTL_REG_VLAN_TAG_ONLY]  = { 0, 0, 16,  1,  8, 1 },
+       [RTL_REG_VLAN_FILTER]    = { 0, 0, 16,  1,  9, 1 },
+       [RTL_REG_VLAN_TAG_AWARE] = { 0, 0, 16,  1, 10, 1 },
+       [RTL_REG_VLAN_ENABLE]    = { 0, 0, 18,  1,  8, 1 },
+
+#define RTL_VLAN_REGS(id, phy, page, regofs) \
+       [RTL_REG_VLAN##id##_VID] = { page, phy, 25 + regofs, 12, 0, 0 }, \
+       [RTL_REG_VLAN##id##_PORTMASK] = { page, phy, 24 + regofs, 6, 0, 0 }
+       RTL_VLAN_REGS( 0, 0, 0, 0),
+       RTL_VLAN_REGS( 1, 1, 0, 0),
+       RTL_VLAN_REGS( 2, 2, 0, 0),
+       RTL_VLAN_REGS( 3, 3, 0, 0),
+       RTL_VLAN_REGS( 4, 4, 0, 0),
+       RTL_VLAN_REGS( 5, 0, 1, 2),
+       RTL_VLAN_REGS( 6, 1, 1, 2),
+       RTL_VLAN_REGS( 7, 2, 1, 2),
+       RTL_VLAN_REGS( 8, 3, 1, 2),
+       RTL_VLAN_REGS( 9, 4, 1, 2),
+       RTL_VLAN_REGS(10, 0, 1, 4),
+       RTL_VLAN_REGS(11, 1, 1, 4),
+       RTL_VLAN_REGS(12, 2, 1, 4),
+       RTL_VLAN_REGS(13, 3, 1, 4),
+       RTL_VLAN_REGS(14, 4, 1, 4),
+       RTL_VLAN_REGS(15, 0, 1, 6),
+
+#define REG_PORT_SETTING(port, phy) \
+       [RTL_REG_PORT##port##_SPEED] = { 0, phy, 0, 1, 13, 0 }, \
+       [RTL_REG_PORT##port##_NWAY] = { 0, phy, 0, 1, 12, 0 }, \
+       [RTL_REG_PORT##port##_NRESTART] = { 0, phy, 0, 1, 9, 0 }, \
+       [RTL_REG_PORT##port##_DUPLEX] = { 0, phy, 0, 1, 8, 0 }, \
+       [RTL_REG_PORT##port##_TXEN] = { 0, phy, 24, 1, 11, 0 }, \
+       [RTL_REG_PORT##port##_RXEN] = { 0, phy, 24, 1, 10, 0 }, \
+       [RTL_REG_PORT##port##_LINK] = { 0, phy, 1, 1, 2, 0 }, \
+       [RTL_REG_PORT##port##_NULL_VID_REPLACE] = { 0, phy, 22, 1, 12, 0 }, \
+       [RTL_REG_PORT##port##_NON_PVID_DISCARD] = { 0, phy, 22, 1, 11, 0 }, \
+       [RTL_REG_PORT##port##_VID_INSERT] = { 0, phy, 22, 2, 9, 0 }, \
+       [RTL_REG_PORT##port##_TAG_INSERT] = { 0, phy, 22, 2, 0, 0 }
+
+       REG_PORT_SETTING(0, 0),
+       REG_PORT_SETTING(1, 1),
+       REG_PORT_SETTING(2, 2),
+       REG_PORT_SETTING(3, 3),
+       REG_PORT_SETTING(4, 4),
+       REG_PORT_SETTING(5, 6),
+
+#define REG_PORT_PVID(phy, page, regofs) \
+       { page, phy, 24 + regofs, 4, 12, 0 }
+       [RTL_REG_PORT0_PVID] = REG_PORT_PVID(0, 0, 0),
+       [RTL_REG_PORT1_PVID] = REG_PORT_PVID(1, 0, 0),
+       [RTL_REG_PORT2_PVID] = REG_PORT_PVID(2, 0, 0),
+       [RTL_REG_PORT3_PVID] = REG_PORT_PVID(3, 0, 0),
+       [RTL_REG_PORT4_PVID] = REG_PORT_PVID(4, 0, 0),
+       [RTL_REG_PORT5_PVID] = REG_PORT_PVID(0, 1, 2),
+};
+
+
+static inline void
+rtl_set_page(struct rtl_priv *priv, unsigned int page)
+{
+       struct mii_bus *bus = priv->bus;
+       u16 pgsel;
+
+       if (priv->fixup)
+               return;
+
+       if (priv->page == page)
+               return;
+
+       BUG_ON(page > RTL8306_NUM_PAGES);
+       pgsel = bus->read(bus, 0, RTL8306_REG_PAGE);
+       pgsel &= ~(RTL8306_REG_PAGE_LO | RTL8306_REG_PAGE_HI);
+       if (page & (1 << 0))
+               pgsel |= RTL8306_REG_PAGE_LO;
+       if (!(page & (1 << 1))) /* bit is inverted */
+               pgsel |= RTL8306_REG_PAGE_HI;
+       bus->write(bus, 0, RTL8306_REG_PAGE, pgsel);
+}
+
+static inline int
+rtl_w16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+
+       rtl_set_page(priv, page);
+       bus->write(bus, phy, reg, val);
+       bus->read(bus, phy, reg); /* flush */
+       return 0;
+}
+
+static inline int
+rtl_r16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+
+       rtl_set_page(priv, page);
+       return bus->read(bus, phy, reg);
+}
+
+static inline u16
+rtl_rmw(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 mask, u16 val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct mii_bus *bus = priv->bus;
+       u16 r;
+
+       rtl_set_page(priv, page);
+       r = bus->read(bus, phy, reg);
+       r &= ~mask;
+       r |= val;
+       bus->write(bus, phy, reg, r);
+       return bus->read(bus, phy, reg); /* flush */
+}
+
+
+static inline int
+rtl_get(struct switch_dev *dev, enum rtl_regidx s)
+{
+       const struct rtl_reg *r = &rtl_regs[s];
+       u16 val;
+
+       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
+       if (r->bits == 0) /* unimplemented */
+               return 0;
+
+       val = rtl_r16(dev, r->page, r->phy, r->reg);
+
+       if (r->shift > 0)
+               val >>= r->shift;
+
+       if (r->inverted)
+               val = ~val;
+
+       val &= (1 << r->bits) - 1;
+
+       return val;
+}
+
+static int
+rtl_set(struct switch_dev *dev, enum rtl_regidx s, unsigned int val)
+{
+       const struct rtl_reg *r = &rtl_regs[s];
+       u16 mask = 0xffff;
+
+       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
+
+       if (r->bits == 0) /* unimplemented */
+               return 0;
+
+       if (r->shift > 0)
+               val <<= r->shift;
+
+       if (r->inverted)
+               val = ~val;
+
+       if (r->bits != 16) {
+               mask = (1 << r->bits) - 1;
+               mask <<= r->shift;
+       }
+       val &= mask;
+       return rtl_rmw(dev, r->page, r->phy, r->reg, mask, val);
+}
+
+static void
+rtl_phy_save(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
+{
+       regs->nway = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+       regs->speed = rtl_get(dev, RTL_PORT_REG(port, SPEED));
+       regs->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+}
+
+static void
+rtl_phy_restore(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
+{
+       rtl_set(dev, RTL_PORT_REG(port, NWAY), regs->nway);
+       rtl_set(dev, RTL_PORT_REG(port, SPEED), regs->speed);
+       rtl_set(dev, RTL_PORT_REG(port, DUPLEX), regs->duplex);
+}
+
+static void
+rtl_port_set_enable(struct switch_dev *dev, int port, int enabled)
+{
+       rtl_set(dev, RTL_PORT_REG(port, RXEN), enabled);
+       rtl_set(dev, RTL_PORT_REG(port, TXEN), enabled);
+
+       if ((port >= 5) || !enabled)
+               return;
+
+       /* restart autonegotiation if enabled */
+       rtl_set(dev, RTL_PORT_REG(port, NRESTART), 1);
+}
+
+static int
+rtl_hw_apply(struct switch_dev *dev)
+{
+       int i;
+       int trunk_en, trunk_psel;
+       struct rtl_phyregs port5;
+
+       rtl_phy_save(dev, 5, &port5);
+
+       /* disable rx/tx from PHYs */
+       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
+               rtl_port_set_enable(dev, i, 0);
+       }
+
+       /* save trunking status */
+       trunk_en = rtl_get(dev, RTL_REG_EN_TRUNK);
+       trunk_psel = rtl_get(dev, RTL_REG_TRUNK_PORTSEL);
+
+       /* trunk port 3 and 4
+        * XXX: Big WTF, but RealTek seems to do it */
+       rtl_set(dev, RTL_REG_EN_TRUNK, 1);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 1);
+
+       /* execute the software reset */
+       rtl_set(dev, RTL_REG_RESET, 1);
+
+       /* wait for the reset to complete,
+        * but don't wait for too long */
+       for (i = 0; i < 10; i++) {
+               if (rtl_get(dev, RTL_REG_RESET) == 0)
+                       break;
+
+               msleep(1);
+       }
+
+       /* enable rx/tx from PHYs */
+       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
+               rtl_port_set_enable(dev, i, 1);
+       }
+
+       /* restore trunking settings */
+       rtl_set(dev, RTL_REG_EN_TRUNK, trunk_en);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, trunk_psel);
+       rtl_phy_restore(dev, 5, &port5);
+
+       rtl_set(dev, RTL_REG_CPU_LINKUP, 1);
+
+       return 0;
+}
+
+static void
+rtl_hw_init(struct switch_dev *dev)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       int cpu_mask = 1 << dev->cpu_port;
+       int i;
+
+       rtl_set(dev, RTL_REG_VLAN_ENABLE, 0);
+       rtl_set(dev, RTL_REG_VLAN_FILTER, 0);
+       rtl_set(dev, RTL_REG_EN_TRUNK, 0);
+       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 0);
+
+       /* initialize cpu port settings */
+       if (priv->do_cpu) {
+               rtl_set(dev, RTL_REG_CPUPORT, dev->cpu_port);
+               rtl_set(dev, RTL_REG_EN_CPUPORT, 1);
+       } else {
+               rtl_set(dev, RTL_REG_CPUPORT, 7);
+               rtl_set(dev, RTL_REG_EN_CPUPORT, 0);
+       }
+       rtl_set(dev, RTL_REG_EN_TAG_OUT, 0);
+       rtl_set(dev, RTL_REG_EN_TAG_IN, 0);
+       rtl_set(dev, RTL_REG_EN_TAG_CLR, 0);
+
+       /* reset all vlans */
+       for (i = 0; i < RTL8306_NUM_VLANS; i++) {
+               rtl_set(dev, RTL_VLAN_REG(i, VID), i);
+               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), 0);
+       }
+
+       /* default to port isolation */
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               unsigned long mask;
+
+               if ((1 << i) == cpu_mask)
+                       mask = ((1 << RTL8306_NUM_PORTS) - 1) & ~cpu_mask; /* all bits set */
+               else
+                       mask = cpu_mask | (1 << i);
+
+               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), mask);
+               rtl_set(dev, RTL_PORT_REG(i, PVID), i);
+               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
+               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), 1);
+               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), 3);
+       }
+       rtl_hw_apply(dev);
+}
+
+#ifdef DEBUG
+static int
+rtl_set_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       priv->do_cpu = val->value.i;
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_get_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       val->value.i = priv->do_cpu;
+       return 0;
+}
+
+static int
+rtl_set_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       dev->cpu_port = val->value.i;
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_get_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       val->value.i = dev->cpu_port;
+       return 0;
+}
+#endif
+
+static int
+rtl_reset(struct switch_dev *dev)
+{
+       rtl_hw_init(dev);
+       return 0;
+}
+
+static int
+rtl_attr_set_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       int idx = attr->id + (val->port_vlan * attr->ofs);
+       struct rtl_phyregs port;
+
+       if (attr->id >= ARRAY_SIZE(rtl_regs))
+               return -EINVAL;
+
+       if ((attr->max > 0) && (val->value.i > attr->max))
+               return -EINVAL;
+
+       /* access to phy register 22 on port 4/5
+        * needs phy status save/restore */
+       if ((val->port_vlan > 3) &&
+               (rtl_regs[idx].reg == 22) &&
+               (rtl_regs[idx].page == 0)) {
+
+               rtl_phy_save(dev, val->port_vlan, &port);
+               rtl_set(dev, idx, val->value.i);
+               rtl_phy_restore(dev, val->port_vlan, &port);
+       } else {
+               rtl_set(dev, idx, val->value.i);
+       }
+
+       return 0;
+}
+
+static int
+rtl_attr_get_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       int idx = attr->id + (val->port_vlan * attr->ofs);
+
+       if (idx >= ARRAY_SIZE(rtl_regs))
+               return -EINVAL;
+
+       val->value.i = rtl_get(dev, idx);
+       return 0;
+}
+
+static int
+rtl_attr_set_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl_attr_set_int(dev, attr, val);
+}
+
+static int
+rtl_attr_get_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+       return rtl_attr_get_int(dev, attr, val);
+}
+
+static int 
+rtl_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link)
+{
+       if (port >= RTL8306_NUM_PORTS)
+               return -EINVAL;
+
+       /* in case the link changes from down to up, the register is only updated on read */
+       link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
+       if (!link->link)
+               link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
+
+       if (!link->link)
+               return 0;
+
+       link->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
+       link->aneg = rtl_get(dev, RTL_PORT_REG(port, NWAY));
+
+       if (rtl_get(dev, RTL_PORT_REG(port, SPEED)))
+               link->speed = SWITCH_PORT_SPEED_100;
+       else
+               link->speed = SWITCH_PORT_SPEED_10;
+
+       return 0;
+}
+
+static int
+rtl_attr_set_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       return rtl_attr_set_int(dev, attr, val);
+}
+
+static int
+rtl_attr_get_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       return rtl_attr_get_int(dev, attr, val);
+}
+
+static int
+rtl_get_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       unsigned int i, mask;
+
+       mask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               struct switch_port *port;
+
+               if (!(mask & (1 << i)))
+                       continue;
+
+               port = &val->value.ports[val->len];
+               port->id = i;
+               if (rtl_get(dev, RTL_PORT_REG(i, TAG_INSERT)) == 2 || i == dev->cpu_port)
+                       port->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+       }
+
+       return 0;
+}
+
+static int
+rtl_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       struct rtl_priv *priv = to_rtl(dev);
+       struct rtl_phyregs port;
+       int en = val->value.i;
+       int i;
+
+       rtl_set(dev, RTL_REG_EN_TAG_OUT, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_EN_TAG_IN, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_EN_TAG_CLR, en && priv->do_cpu);
+       rtl_set(dev, RTL_REG_VLAN_TAG_AWARE, en);
+       if (en)
+               rtl_set(dev, RTL_REG_VLAN_FILTER, en);
+
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               if (i > 3)
+                       rtl_phy_save(dev, val->port_vlan, &port);
+               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
+               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), (en ? (i == dev->cpu_port ? 0 : 1) : 1));
+               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), (en ? (i == dev->cpu_port ? 2 : 1) : 3));
+               if (i > 3)
+                       rtl_phy_restore(dev, val->port_vlan, &port);
+       }
+       rtl_set(dev, RTL_REG_VLAN_ENABLE, en);
+
+       return 0;
+}
+
+static int
+rtl_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
+{
+       val->value.i = rtl_get(dev, RTL_REG_VLAN_ENABLE);
+       return 0;
+}
+
+static int
+rtl_set_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       unsigned int mask = 0;
+       unsigned int oldmask;
+       int i;
+
+       for(i = 0; i < val->len; i++)
+       {
+               struct switch_port *port = &val->value.ports[i];
+               bool tagged = false;
+
+               mask |= (1 << port->id);
+
+               if (port->id == dev->cpu_port)
+                       continue;
+
+               if ((i == dev->cpu_port) ||
+                       (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+                       tagged = true;
+
+               /* fix up PVIDs for added ports */
+               if (!tagged)
+                       rtl_set(dev, RTL_PORT_REG(port->id, PVID), val->port_vlan);
+
+               rtl_set(dev, RTL_PORT_REG(port->id, NON_PVID_DISCARD), (tagged ? 0 : 1));
+               rtl_set(dev, RTL_PORT_REG(port->id, VID_INSERT), (tagged ? 0 : 1));
+               rtl_set(dev, RTL_PORT_REG(port->id, TAG_INSERT), (tagged ? 2 : 1));
+       }
+
+       oldmask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
+       rtl_set(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK), mask);
+
+       /* fix up PVIDs for removed ports, default to last vlan */
+       oldmask &= ~mask;
+       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
+               if (!(oldmask & (1 << i)))
+                       continue;
+
+               if (i == dev->cpu_port)
+                       continue;
+
+               if (rtl_get(dev, RTL_PORT_REG(i, PVID)) == val->port_vlan)
+                       rtl_set(dev, RTL_PORT_REG(i, PVID), dev->vlans - 1);
+       }
+
+       return 0;
+}
+
+static struct switch_attr rtl_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .max = 1,
+               .set = rtl_set_vlan,
+               .get = rtl_get_vlan,
+       },
+       {
+               RTL_GLOBAL_REGATTR(EN_TRUNK),
+               .name = "trunk",
+               .description = "Enable port trunking",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(TRUNK_PORTSEL),
+               .name = "trunk_sel",
+               .description = "Select ports for trunking (0: 0,1 - 1: 3,4)",
+               .max = 1,
+       },
+#ifdef DEBUG
+       {
+               RTL_GLOBAL_REGATTR(VLAN_FILTER),
+               .name = "vlan_filter",
+               .description = "Filter incoming packets for allowed VLANS",
+               .max = 1,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "cpuport",
+               .description = "CPU Port",
+               .set = rtl_set_cpuport,
+               .get = rtl_get_cpuport,
+               .max = RTL8306_NUM_PORTS,
+       },
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "use_cpuport",
+               .description = "CPU Port handling flag",
+               .set = rtl_set_use_cpuport,
+               .get = rtl_get_use_cpuport,
+               .max = RTL8306_NUM_PORTS,
+       },
+       {
+               RTL_GLOBAL_REGATTR(TRAP_CPU),
+               .name = "trap_cpu",
+               .description = "VLAN trap to CPU",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(VLAN_TAG_AWARE),
+               .name = "vlan_tag_aware",
+               .description = "Enable VLAN tag awareness",
+               .max = 1,
+       },
+       {
+               RTL_GLOBAL_REGATTR(VLAN_TAG_ONLY),
+               .name = "tag_only",
+               .description = "Only accept tagged packets",
+               .max = 1,
+       },
+#endif
+};
+static struct switch_attr rtl_port[] = {
+       {
+               RTL_PORT_REGATTR(PVID),
+               .name = "pvid",
+               .description = "Port VLAN ID",
+               .max = RTL8306_NUM_VLANS - 1,
+       },
+#ifdef DEBUG
+       {
+               RTL_PORT_REGATTR(NULL_VID_REPLACE),
+               .name = "null_vid",
+               .description = "NULL VID gets replaced by port default vid",
+               .max = 1,
+       },
+       {
+               RTL_PORT_REGATTR(NON_PVID_DISCARD),
+               .name = "non_pvid_discard",
+               .description = "discard packets with VID != PVID",
+               .max = 1,
+       },
+       {
+               RTL_PORT_REGATTR(VID_INSERT),
+               .name = "vid_insert_remove",
+               .description = "how should the switch insert and remove vids ?",
+               .max = 3,
+       },
+       {
+               RTL_PORT_REGATTR(TAG_INSERT),
+               .name = "tag_insert",
+               .description = "tag insertion handling",
+               .max = 3,
+       },
+#endif
+};
+
+static struct switch_attr rtl_vlan[] = {
+       {
+               RTL_VLAN_REGATTR(VID),
+               .name = "vid",
+               .description = "VLAN ID (1-4095)",
+               .max = 4095,
+       },
+};
+
+static const struct switch_dev_ops rtl8306_ops = {
+       .attr_global = {
+               .attr = rtl_globals,
+               .n_attr = ARRAY_SIZE(rtl_globals),
+       },
+       .attr_port = {
+               .attr = rtl_port,
+               .n_attr = ARRAY_SIZE(rtl_port),
+       },
+       .attr_vlan = {
+               .attr = rtl_vlan,
+               .n_attr = ARRAY_SIZE(rtl_vlan),
+       },
+
+       .get_vlan_ports = rtl_get_ports,
+       .set_vlan_ports = rtl_set_ports,
+       .apply_config = rtl_hw_apply,
+       .reset_switch = rtl_reset,
+       .get_port_link = rtl_get_port_link,
+};
+
+static int
+rtl8306_config_init(struct phy_device *pdev)
+{
+       struct net_device *netdev = pdev->attached_dev;
+       struct rtl_priv *priv = pdev->priv;
+       struct switch_dev *dev = &priv->dev;
+       struct switch_val val;
+       unsigned int chipid, chipver, chiptype;
+       int err;
+
+       /* Only init the switch for the primary PHY */
+       if (pdev->mdio.addr != 0)
+               return 0;
+
+       val.value.i = 1;
+       priv->dev.cpu_port = RTL8306_PORT_CPU;
+       priv->dev.ports = RTL8306_NUM_PORTS;
+       priv->dev.vlans = RTL8306_NUM_VLANS;
+       priv->dev.ops = &rtl8306_ops;
+       priv->do_cpu = 0;
+       priv->page = -1;
+       priv->bus = pdev->mdio.bus;
+
+       chipid = rtl_get(dev, RTL_REG_CHIPID);
+       chipver = rtl_get(dev, RTL_REG_CHIPVER);
+       chiptype = rtl_get(dev, RTL_REG_CHIPTYPE);
+       switch(chiptype) {
+       case 0:
+       case 2:
+               strncpy(priv->hwname, RTL_NAME_S, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_S;
+               break;
+       case 1:
+               strncpy(priv->hwname, RTL_NAME_SD, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_SD;
+               break;
+       case 3:
+               strncpy(priv->hwname, RTL_NAME_SDM, sizeof(priv->hwname));
+               priv->type = RTL_TYPE_SDM;
+               break;
+       default:
+               strncpy(priv->hwname, RTL_NAME_UNKNOWN, sizeof(priv->hwname));
+               break;
+       }
+
+       dev->name = priv->hwname;
+       rtl_hw_init(dev);
+
+       printk(KERN_INFO "Registering %s switch with Chip ID: 0x%04x, version: 0x%04x\n", priv->hwname, chipid, chipver);
+
+       err = register_switch(dev, netdev);
+       if (err < 0) {
+               kfree(priv);
+               return err;
+       }
+
+       return 0;
+}
+
+
+static int
+rtl8306_fixup(struct phy_device *pdev)
+{
+       struct rtl_priv priv;
+       u16 chipid;
+
+       /* Attach to primary LAN port and WAN port */
+       if (pdev->mdio.addr != 0 && pdev->mdio.addr != 4)
+               return 0;
+
+       memset(&priv, 0, sizeof(priv));
+       priv.fixup = true;
+       priv.page = -1;
+       priv.bus = pdev->mdio.bus;
+       chipid = rtl_get(&priv.dev, RTL_REG_CHIPID);
+       if (chipid == 0x5988)
+               pdev->phy_id = RTL8306_MAGIC;
+
+       return 0;
+}
+
+static int
+rtl8306_probe(struct phy_device *pdev)
+{
+       struct rtl_priv *priv;
+
+       list_for_each_entry(priv, &phydevs, list) {
+               /*
+                * share one rtl_priv instance between virtual phy
+                * devices on the same bus
+                */
+               if (priv->bus == pdev->mdio.bus)
+                       goto found;
+       }
+       priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->bus = pdev->mdio.bus;
+
+found:
+       pdev->priv = priv;
+       return 0;
+}
+
+static void
+rtl8306_remove(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+       unregister_switch(&priv->dev);
+       kfree(priv);
+}
+
+static int
+rtl8306_config_aneg(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+
+       /* Only for WAN */
+       if (pdev->mdio.addr == 0)
+               return 0;
+
+       /* Restart autonegotiation */
+       rtl_set(&priv->dev, RTL_PORT_REG(4, NWAY), 1);
+       rtl_set(&priv->dev, RTL_PORT_REG(4, NRESTART), 1);
+
+       return 0;
+}
+
+static int
+rtl8306_read_status(struct phy_device *pdev)
+{
+       struct rtl_priv *priv = pdev->priv;
+       struct switch_dev *dev = &priv->dev;
+
+       if (pdev->mdio.addr == 4) {
+               /* WAN */
+               pdev->speed = rtl_get(dev, RTL_PORT_REG(4, SPEED)) ? SPEED_100 : SPEED_10;
+               pdev->duplex = rtl_get(dev, RTL_PORT_REG(4, DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF;
+               pdev->link = !!rtl_get(dev, RTL_PORT_REG(4, LINK));
+       } else {
+               /* LAN */
+               pdev->speed = SPEED_100;
+               pdev->duplex = DUPLEX_FULL;
+               pdev->link = 1;
+       }
+
+       /*
+        * Bypass generic PHY status read,
+        * it doesn't work with this switch
+        */
+       if (pdev->link) {
+               pdev->state = PHY_RUNNING;
+               netif_carrier_on(pdev->attached_dev);
+               pdev->adjust_link(pdev->attached_dev);
+       } else {
+               pdev->state = PHY_NOLINK;
+               netif_carrier_off(pdev->attached_dev);
+               pdev->adjust_link(pdev->attached_dev);
+       }
+
+       return 0;
+}
+
+
+static struct phy_driver rtl8306_driver = {
+       .name           = "Realtek RTL8306S",
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0))
+       .flags          = PHY_HAS_MAGICANEG,
+#endif
+       .phy_id         = RTL8306_MAGIC,
+       .phy_id_mask    = 0xffffffff,
+       .features       = PHY_BASIC_FEATURES,
+       .probe          = &rtl8306_probe,
+       .remove         = &rtl8306_remove,
+       .config_init    = &rtl8306_config_init,
+       .config_aneg    = &rtl8306_config_aneg,
+       .read_status    = &rtl8306_read_status,
+};
+
+
+static int __init
+rtl_init(void)
+{
+       phy_register_fixup_for_id(PHY_ANY_ID, rtl8306_fixup);
+       return phy_driver_register(&rtl8306_driver, THIS_MODULE);
+}
+
+static void __exit
+rtl_exit(void)
+{
+       phy_driver_unregister(&rtl8306_driver);
+}
+
+module_init(rtl_init);
+module_exit(rtl_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files-4.9/drivers/net/phy/rtl8366_smi.c
new file mode 100644 (file)
index 0000000..c0cb680
--- /dev/null
@@ -0,0 +1,1628 @@
+/*
+ * Realtek RTL8366 SMI interface driver
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/skbuff.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/rtl8366.h>
+#include <linux/version.h>
+#include <linux/of_mdio.h>
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
+
+#include "rtl8366_smi.h"
+
+#define RTL8366_SMI_ACK_RETRY_COUNT         5
+
+#define RTL8366_SMI_HW_STOP_DELAY              25      /* msecs */
+#define RTL8366_SMI_HW_START_DELAY             100     /* msecs */
+
+static inline void rtl8366_smi_clk_delay(struct rtl8366_smi *smi)
+{
+       ndelay(smi->clk_delay);
+}
+
+static void rtl8366_smi_start(struct rtl8366_smi *smi)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       /*
+        * Set GPIO pins to output mode, with initial state:
+        * SCK = 0, SDA = 1
+        */
+       gpio_direction_output(sck, 0);
+       gpio_direction_output(sda, 1);
+       rtl8366_smi_clk_delay(smi);
+
+       /* CLK 1: 0 -> 1, 1 -> 0 */
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+
+       /* CLK 2: */
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 1);
+}
+
+static void rtl8366_smi_stop(struct rtl8366_smi *smi)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 0);
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sda, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+
+       /* add a click */
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 0);
+       rtl8366_smi_clk_delay(smi);
+       gpio_set_value(sck, 1);
+
+       /* set GPIO pins to input mode */
+       gpio_direction_input(sda);
+       gpio_direction_input(sck);
+}
+
+static void rtl8366_smi_write_bits(struct rtl8366_smi *smi, u32 data, u32 len)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       for (; len > 0; len--) {
+               rtl8366_smi_clk_delay(smi);
+
+               /* prepare data */
+               gpio_set_value(sda, !!(data & ( 1 << (len - 1))));
+               rtl8366_smi_clk_delay(smi);
+
+               /* clocking */
+               gpio_set_value(sck, 1);
+               rtl8366_smi_clk_delay(smi);
+               gpio_set_value(sck, 0);
+       }
+}
+
+static void rtl8366_smi_read_bits(struct rtl8366_smi *smi, u32 len, u32 *data)
+{
+       unsigned int sda = smi->gpio_sda;
+       unsigned int sck = smi->gpio_sck;
+
+       gpio_direction_input(sda);
+
+       for (*data = 0; len > 0; len--) {
+               u32 u;
+
+               rtl8366_smi_clk_delay(smi);
+
+               /* clocking */
+               gpio_set_value(sck, 1);
+               rtl8366_smi_clk_delay(smi);
+               u = !!gpio_get_value(sda);
+               gpio_set_value(sck, 0);
+
+               *data |= (u << (len - 1));
+       }
+
+       gpio_direction_output(sda, 0);
+}
+
+static int rtl8366_smi_wait_for_ack(struct rtl8366_smi *smi)
+{
+       int retry_cnt;
+
+       retry_cnt = 0;
+       do {
+               u32 ack;
+
+               rtl8366_smi_read_bits(smi, 1, &ack);
+               if (ack == 0)
+                       break;
+
+               if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT) {
+                       dev_err(smi->parent, "ACK timeout\n");
+                       return -ETIMEDOUT;
+               }
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8366_smi_write_byte(struct rtl8366_smi *smi, u8 data)
+{
+       rtl8366_smi_write_bits(smi, data, 8);
+       return rtl8366_smi_wait_for_ack(smi);
+}
+
+static int rtl8366_smi_write_byte_noack(struct rtl8366_smi *smi, u8 data)
+{
+       rtl8366_smi_write_bits(smi, data, 8);
+       return 0;
+}
+
+static int rtl8366_smi_read_byte0(struct rtl8366_smi *smi, u8 *data)
+{
+       u32 t;
+
+       /* read data */
+       rtl8366_smi_read_bits(smi, 8, &t);
+       *data = (t & 0xff);
+
+       /* send an ACK */
+       rtl8366_smi_write_bits(smi, 0x00, 1);
+
+       return 0;
+}
+
+static int rtl8366_smi_read_byte1(struct rtl8366_smi *smi, u8 *data)
+{
+       u32 t;
+
+       /* read data */
+       rtl8366_smi_read_bits(smi, 8, &t);
+       *data = (t & 0xff);
+
+       /* send an ACK */
+       rtl8366_smi_write_bits(smi, 0x01, 1);
+
+       return 0;
+}
+
+static int __rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       unsigned long flags;
+       u8 lo = 0;
+       u8 hi = 0;
+       int ret;
+
+       spin_lock_irqsave(&smi->lock, flags);
+
+       rtl8366_smi_start(smi);
+
+       /* send READ command */
+       ret = rtl8366_smi_write_byte(smi, smi->cmd_read);
+       if (ret)
+               goto out;
+
+       /* set ADDR[7:0] */
+       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
+       if (ret)
+               goto out;
+
+       /* set ADDR[15:8] */
+       ret = rtl8366_smi_write_byte(smi, addr >> 8);
+       if (ret)
+               goto out;
+
+       /* read DATA[7:0] */
+       rtl8366_smi_read_byte0(smi, &lo);
+       /* read DATA[15:8] */
+       rtl8366_smi_read_byte1(smi, &hi);
+
+       *data = ((u32) lo) | (((u32) hi) << 8);
+
+       ret = 0;
+
+ out:
+       rtl8366_smi_stop(smi);
+       spin_unlock_irqrestore(&smi->lock, flags);
+
+       return ret;
+}
+/* Read/write via mdiobus */
+#define MDC_MDIO_CTRL0_REG             31
+#define MDC_MDIO_START_REG             29
+#define MDC_MDIO_CTRL1_REG             21
+#define MDC_MDIO_ADDRESS_REG           23
+#define MDC_MDIO_DATA_WRITE_REG                24
+#define MDC_MDIO_DATA_READ_REG         25
+
+#define MDC_MDIO_START_OP              0xFFFF
+#define MDC_MDIO_ADDR_OP               0x000E
+#define MDC_MDIO_READ_OP               0x0001
+#define MDC_MDIO_WRITE_OP              0x0003
+#define MDC_REALTEK_PHY_ADDR           0x0
+
+int __rtl8366_mdio_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       u32 phy_id = MDC_REALTEK_PHY_ADDR;
+       struct mii_bus *mbus = smi->ext_mbus;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock(&mbus->mdio_lock);
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address control code to register 31 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address to register 23 */
+       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write read control code to register 21 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_READ_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(smi->ext_mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Read data from register 25 */
+       *data = mbus->read(mbus, phy_id, MDC_MDIO_DATA_READ_REG);
+
+       mutex_unlock(&mbus->mdio_lock);
+
+       return 0;
+}
+
+static int __rtl8366_mdio_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       u32 phy_id = MDC_REALTEK_PHY_ADDR;
+       struct mii_bus *mbus = smi->ext_mbus;
+
+       BUG_ON(in_interrupt());
+
+       mutex_lock(&mbus->mdio_lock);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address control code to register 31 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write address to register 23 */
+       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write data to register 24 */
+       mbus->write(mbus, phy_id, MDC_MDIO_DATA_WRITE_REG, data);
+
+       /* Write Start command to register 29 */
+       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
+
+       /* Write data control code to register 21 */
+       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_WRITE_OP);
+
+       mutex_unlock(&mbus->mdio_lock);
+       return 0;
+}
+
+int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
+{
+       if (smi->ext_mbus)
+               return __rtl8366_mdio_read_reg(smi, addr, data);
+       else
+               return __rtl8366_smi_read_reg(smi, addr, data);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_read_reg);
+
+static int __rtl8366_smi_write_reg(struct rtl8366_smi *smi,
+                                  u32 addr, u32 data, bool ack)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&smi->lock, flags);
+
+       rtl8366_smi_start(smi);
+
+       /* send WRITE command */
+       ret = rtl8366_smi_write_byte(smi, smi->cmd_write);
+       if (ret)
+               goto out;
+
+       /* set ADDR[7:0] */
+       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
+       if (ret)
+               goto out;
+
+       /* set ADDR[15:8] */
+       ret = rtl8366_smi_write_byte(smi, addr >> 8);
+       if (ret)
+               goto out;
+
+       /* write DATA[7:0] */
+       ret = rtl8366_smi_write_byte(smi, data & 0xff);
+       if (ret)
+               goto out;
+
+       /* write DATA[15:8] */
+       if (ack)
+               ret = rtl8366_smi_write_byte(smi, data >> 8);
+       else
+               ret = rtl8366_smi_write_byte_noack(smi, data >> 8);
+       if (ret)
+               goto out;
+
+       ret = 0;
+
+ out:
+       rtl8366_smi_stop(smi);
+       spin_unlock_irqrestore(&smi->lock, flags);
+
+       return ret;
+}
+
+int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       if (smi->ext_mbus)
+               return __rtl8366_mdio_write_reg(smi, addr, data);
+       else
+               return __rtl8366_smi_write_reg(smi, addr, data, true);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg);
+
+int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data)
+{
+       return __rtl8366_smi_write_reg(smi, addr, data, false);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg_noack);
+
+int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data)
+{
+       u32 t;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, addr, &t);
+       if (err)
+               return err;
+
+       err = rtl8366_smi_write_reg(smi, addr, (t & ~mask) | data);
+       return err;
+
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_rmwr);
+
+static int rtl8366_reset(struct rtl8366_smi *smi)
+{
+       if (smi->hw_reset) {
+               smi->hw_reset(smi, true);
+               msleep(RTL8366_SMI_HW_STOP_DELAY);
+               smi->hw_reset(smi, false);
+               msleep(RTL8366_SMI_HW_START_DELAY);
+               return 0;
+       }
+
+       return smi->ops->reset_chip(smi);
+}
+
+static int rtl8366_mc_is_used(struct rtl8366_smi *smi, int mc_index, int *used)
+{
+       int err;
+       int i;
+
+       *used = 0;
+       for (i = 0; i < smi->num_ports; i++) {
+               int index = 0;
+
+               err = smi->ops->get_mc_index(smi, i, &index);
+               if (err)
+                       return err;
+
+               if (mc_index == index) {
+                       *used = 1;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member,
+                           u32 untag, u32 fid)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       int err;
+       int i;
+
+       /* Update the 4K table */
+       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+       if (err)
+               return err;
+
+       vlan4k.member = member;
+       vlan4k.untag = untag;
+       vlan4k.fid = fid;
+       err = smi->ops->set_vlan_4k(smi, &vlan4k);
+       if (err)
+               return err;
+
+       /* Try to find an existing MC entry for this VID */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               struct rtl8366_vlan_mc vlanmc;
+
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vid == vlanmc.vid) {
+                       /* update the MC entry */
+                       vlanmc.member = member;
+                       vlanmc.untag = untag;
+                       vlanmc.fid = fid;
+
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       int err;
+       int index;
+
+       err = smi->ops->get_mc_index(smi, port, &index);
+       if (err)
+               return err;
+
+       err = smi->ops->get_vlan_mc(smi, index, &vlanmc);
+       if (err)
+               return err;
+
+       *val = vlanmc.vid;
+       return 0;
+}
+
+static int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port,
+                           unsigned vid)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       struct rtl8366_vlan_4k vlan4k;
+       int err;
+       int i;
+
+       /* Try to find an existing MC entry for this VID */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vid == vlanmc.vid) {
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       /* We have no MC entry for this VID, try to find an empty one */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+
+               if (vlanmc.vid == 0 && vlanmc.member == 0) {
+                       /* Update the entry from the 4K table */
+                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+                       if (err)
+                               return err;
+
+                       vlanmc.vid = vid;
+                       vlanmc.member = vlan4k.member;
+                       vlanmc.untag = vlan4k.untag;
+                       vlanmc.fid = vlan4k.fid;
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       /* MC table is full, try to find an unused entry and replace it */
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               int used;
+
+               err = rtl8366_mc_is_used(smi, i, &used);
+               if (err)
+                       return err;
+
+               if (!used) {
+                       /* Update the entry from the 4K table */
+                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
+                       if (err)
+                               return err;
+
+                       vlanmc.vid = vid;
+                       vlanmc.member = vlan4k.member;
+                       vlanmc.untag = vlan4k.untag;
+                       vlanmc.fid = vlan4k.fid;
+                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+                       if (err)
+                               return err;
+
+                       err = smi->ops->set_mc_index(smi, port, i);
+                       return err;
+               }
+       }
+
+       dev_err(smi->parent,
+               "all VLAN member configurations are in use\n");
+
+       return -ENOSPC;
+}
+
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+
+       err = smi->ops->enable_vlan(smi, enable);
+       if (err)
+               return err;
+
+       smi->vlan_enabled = enable;
+
+       if (!enable) {
+               smi->vlan4k_enabled = 0;
+               err = smi->ops->enable_vlan4k(smi, enable);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
+
+static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+
+       if (enable) {
+               err = smi->ops->enable_vlan(smi, enable);
+               if (err)
+                       return err;
+
+               smi->vlan_enabled = enable;
+       }
+
+       err = smi->ops->enable_vlan4k(smi, enable);
+       if (err)
+               return err;
+
+       smi->vlan4k_enabled = enable;
+       return 0;
+}
+
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable)
+{
+       int port;
+       int err;
+
+       for (port = 0; port < smi->num_ports; port++) {
+               err = smi->ops->enable_port(smi, port, enable);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports);
+
+int rtl8366_reset_vlan(struct rtl8366_smi *smi)
+{
+       struct rtl8366_vlan_mc vlanmc;
+       int err;
+       int i;
+
+       rtl8366_enable_vlan(smi, 0);
+       rtl8366_enable_vlan4k(smi, 0);
+
+       /* clear VLAN member configurations */
+       vlanmc.vid = 0;
+       vlanmc.priority = 0;
+       vlanmc.member = 0;
+       vlanmc.untag = 0;
+       vlanmc.fid = 0;
+       for (i = 0; i < smi->num_vlan_mc; i++) {
+               err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
+
+static int rtl8366_init_vlan(struct rtl8366_smi *smi)
+{
+       int port;
+       int err;
+
+       err = rtl8366_reset_vlan(smi);
+       if (err)
+               return err;
+
+       for (port = 0; port < smi->num_ports; port++) {
+               u32 mask;
+
+               if (port == smi->cpu_port)
+                       mask = (1 << smi->num_ports) - 1;
+               else
+                       mask = (1 << port) | (1 << smi->cpu_port);
+
+               err = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
+               if (err)
+                       return err;
+
+               err = rtl8366_set_pvid(smi, port, (port + 1));
+               if (err)
+                       return err;
+       }
+
+       return rtl8366_enable_vlan(smi, 1);
+}
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+int rtl8366_debugfs_open(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_debugfs_open);
+
+static ssize_t rtl8366_read_debugfs_vlan_mc(struct file *file,
+                                             char __user *user_buf,
+                                             size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       int i, len = 0;
+       char *buf = smi->buf;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "%2s %6s %4s %6s %6s %3s\n",
+                       "id", "vid","prio", "member", "untag", "fid");
+
+       for (i = 0; i < smi->num_vlan_mc; ++i) {
+               struct rtl8366_vlan_mc vlanmc;
+
+               smi->ops->get_vlan_mc(smi, i, &vlanmc);
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%2d %6d %4d 0x%04x 0x%04x %3d\n",
+                               i, vlanmc.vid, vlanmc.priority,
+                               vlanmc.member, vlanmc.untag, vlanmc.fid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+#define RTL8366_VLAN4K_PAGE_SIZE       64
+#define RTL8366_VLAN4K_NUM_PAGES       (4096 / RTL8366_VLAN4K_PAGE_SIZE)
+
+static ssize_t rtl8366_read_debugfs_vlan_4k(struct file *file,
+                                           char __user *user_buf,
+                                           size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       int i, len = 0;
+       int offset;
+       char *buf = smi->buf;
+
+       if (smi->dbg_vlan_4k_page >= RTL8366_VLAN4K_NUM_PAGES) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "invalid page: %u\n", smi->dbg_vlan_4k_page);
+               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       }
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "%4s %6s %6s %3s\n",
+                       "vid", "member", "untag", "fid");
+
+       offset = RTL8366_VLAN4K_PAGE_SIZE * smi->dbg_vlan_4k_page;
+       for (i = 0; i < RTL8366_VLAN4K_PAGE_SIZE; i++) {
+               struct rtl8366_vlan_4k vlan4k;
+
+               smi->ops->get_vlan_4k(smi, offset + i, &vlan4k);
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d 0x%04x 0x%04x %3d\n",
+                               vlan4k.vid, vlan4k.member,
+                               vlan4k.untag, vlan4k.fid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_pvid(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       char *buf = smi->buf;
+       int len = 0;
+       int i;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "%4s %4s\n",
+                       "port", "pvid");
+
+       for (i = 0; i < smi->num_ports; i++) {
+               int pvid;
+               int err;
+
+               err = rtl8366_get_pvid(smi, i, &pvid);
+               if (err)
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d error\n", i);
+               else
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%4d %4d\n", i, pvid);
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_read_debugfs_reg(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       u32 t, reg = smi->dbg_reg;
+       int err, len = 0;
+       char *buf = smi->buf;
+
+       memset(buf, '\0', sizeof(smi->buf));
+
+       err = rtl8366_smi_read_reg(smi, reg, &t);
+       if (err) {
+               len += snprintf(buf, sizeof(smi->buf),
+                               "Read failed (reg: 0x%04x)\n", reg);
+               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+       }
+
+       len += snprintf(buf, sizeof(smi->buf), "reg = 0x%04x, val = 0x%04x\n",
+                       reg, t);
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static ssize_t rtl8366_write_debugfs_reg(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
+       unsigned long data;
+       u32 reg = smi->dbg_reg;
+       int err;
+       size_t len;
+       char *buf = smi->buf;
+
+       len = min(count, sizeof(smi->buf) - 1);
+       if (copy_from_user(buf, user_buf, len)) {
+               dev_err(smi->parent, "copy from user failed\n");
+               return -EFAULT;
+       }
+
+       buf[len] = '\0';
+       if (len > 0 && buf[len - 1] == '\n')
+               buf[len - 1] = '\0';
+
+
+       if (kstrtoul(buf, 16, &data)) {
+               dev_err(smi->parent, "Invalid reg value %s\n", buf);
+       } else {
+               err = rtl8366_smi_write_reg(smi, reg, data);
+               if (err) {
+                       dev_err(smi->parent,
+                               "writing reg 0x%04x val 0x%04lx failed\n",
+                               reg, data);
+               }
+       }
+
+       return count;
+}
+
+static ssize_t rtl8366_read_debugfs_mibs(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct rtl8366_smi *smi = file->private_data;
+       int i, j, len = 0;
+       char *buf = smi->buf;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s",
+                       "Counter");
+
+       for (i = 0; i < smi->num_ports; i++) {
+               char port_buf[10];
+
+               snprintf(port_buf, sizeof(port_buf), "Port %d", i);
+               len += snprintf(buf + len, sizeof(smi->buf) - len, " %12s",
+                               port_buf);
+       }
+       len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
+
+       for (i = 0; i < smi->num_mib_counters; i++) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s ",
+                               smi->mib_counters[i].name);
+               for (j = 0; j < smi->num_ports; j++) {
+                       unsigned long long counter = 0;
+
+                       if (!smi->ops->get_mib_counter(smi, i, j, &counter))
+                               len += snprintf(buf + len,
+                                               sizeof(smi->buf) - len,
+                                               "%12llu ", counter);
+                       else
+                               len += snprintf(buf + len,
+                                               sizeof(smi->buf) - len,
+                                               "%12s ", "error");
+               }
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
+       }
+
+       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
+}
+
+static const struct file_operations fops_rtl8366_regs = {
+       .read   = rtl8366_read_debugfs_reg,
+       .write  = rtl8366_write_debugfs_reg,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_vlan_mc = {
+       .read   = rtl8366_read_debugfs_vlan_mc,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_vlan_4k = {
+       .read   = rtl8366_read_debugfs_vlan_4k,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_pvid = {
+       .read   = rtl8366_read_debugfs_pvid,
+       .open   = rtl8366_debugfs_open,
+       .owner  = THIS_MODULE
+};
+
+static const struct file_operations fops_rtl8366_mibs = {
+       .read = rtl8366_read_debugfs_mibs,
+       .open = rtl8366_debugfs_open,
+       .owner = THIS_MODULE
+};
+
+static void rtl8366_debugfs_init(struct rtl8366_smi *smi)
+{
+       struct dentry *node;
+       struct dentry *root;
+
+       if (!smi->debugfs_root)
+               smi->debugfs_root = debugfs_create_dir(dev_name(smi->parent),
+                                                      NULL);
+
+       if (!smi->debugfs_root) {
+               dev_err(smi->parent, "Unable to create debugfs dir\n");
+               return;
+       }
+       root = smi->debugfs_root;
+
+       node = debugfs_create_x16("reg", S_IRUGO | S_IWUSR, root,
+                                 &smi->dbg_reg);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "reg");
+               return;
+       }
+
+       node = debugfs_create_file("val", S_IRUGO | S_IWUSR, root, smi,
+                                  &fops_rtl8366_regs);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "val");
+               return;
+       }
+
+       node = debugfs_create_file("vlan_mc", S_IRUSR, root, smi,
+                                  &fops_rtl8366_vlan_mc);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_mc");
+               return;
+       }
+
+       node = debugfs_create_u8("vlan_4k_page", S_IRUGO | S_IWUSR, root,
+                                 &smi->dbg_vlan_4k_page);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_4k_page");
+               return;
+       }
+
+       node = debugfs_create_file("vlan_4k", S_IRUSR, root, smi,
+                                  &fops_rtl8366_vlan_4k);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "vlan_4k");
+               return;
+       }
+
+       node = debugfs_create_file("pvid", S_IRUSR, root, smi,
+                                  &fops_rtl8366_pvid);
+       if (!node) {
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "pvid");
+               return;
+       }
+
+       node = debugfs_create_file("mibs", S_IRUSR, smi->debugfs_root, smi,
+                                  &fops_rtl8366_mibs);
+       if (!node)
+               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
+                       "mibs");
+}
+
+static void rtl8366_debugfs_remove(struct rtl8366_smi *smi)
+{
+       if (smi->debugfs_root) {
+               debugfs_remove_recursive(smi->debugfs_root);
+               smi->debugfs_root = NULL;
+       }
+}
+#else
+static inline void rtl8366_debugfs_init(struct rtl8366_smi *smi) {}
+static inline void rtl8366_debugfs_remove(struct rtl8366_smi *smi) {}
+#endif /* CONFIG_RTL8366_SMI_DEBUG_FS */
+
+static int rtl8366_smi_mii_init(struct rtl8366_smi *smi)
+{
+       int ret;
+
+#ifdef CONFIG_OF
+       struct device_node *np = NULL;
+
+       np = of_get_child_by_name(smi->parent->of_node, "mdio-bus");
+#endif
+
+       smi->mii_bus = mdiobus_alloc();
+       if (smi->mii_bus == NULL) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       smi->mii_bus->priv = (void *) smi;
+       smi->mii_bus->name = dev_name(smi->parent);
+       smi->mii_bus->read = smi->ops->mii_read;
+       smi->mii_bus->write = smi->ops->mii_write;
+       snprintf(smi->mii_bus->id, MII_BUS_ID_SIZE, "%s",
+                dev_name(smi->parent));
+       smi->mii_bus->parent = smi->parent;
+       smi->mii_bus->phy_mask = ~(0x1f);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
+       {
+               int i;
+               smi->mii_bus->irq = smi->mii_irq;
+               for (i = 0; i < PHY_MAX_ADDR; i++)
+                       smi->mii_irq[i] = PHY_POLL;
+       }
+#endif
+
+#ifdef CONFIG_OF
+       if (np)
+               ret = of_mdiobus_register(smi->mii_bus, np);
+       else
+#endif
+               ret = mdiobus_register(smi->mii_bus);
+
+       if (ret)
+               goto err_free;
+
+       return 0;
+
+ err_free:
+       mdiobus_free(smi->mii_bus);
+ err:
+       return ret;
+}
+
+static void rtl8366_smi_mii_cleanup(struct rtl8366_smi *smi)
+{
+       mdiobus_unregister(smi->mii_bus);
+       mdiobus_free(smi->mii_bus);
+}
+
+int rtl8366_sw_reset_switch(struct switch_dev *dev)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       err = rtl8366_reset(smi);
+       if (err)
+               return err;
+
+       err = smi->ops->setup(smi);
+       if (err)
+               return err;
+
+       err = rtl8366_reset_vlan(smi);
+       if (err)
+               return err;
+
+       err = rtl8366_enable_vlan(smi, 1);
+       if (err)
+               return err;
+
+       return rtl8366_enable_all_ports(smi, 1);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_reset_switch);
+
+int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366_get_pvid(smi, port, val);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_pvid);
+
+int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366_set_pvid(smi, port, val);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid);
+
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int i, len = 0;
+       unsigned long long counter = 0;
+       char *buf = smi->buf;
+
+       if (val->port_vlan >= smi->num_ports)
+               return -EINVAL;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "Port %d MIB counters\n",
+                       val->port_vlan);
+
+       for (i = 0; i < smi->num_mib_counters; ++i) {
+               len += snprintf(buf + len, sizeof(smi->buf) - len,
+                               "%-36s: ", smi->mib_counters[i].name);
+               if (!smi->ops->get_mib_counter(smi, i, val->port_vlan,
+                                              &counter))
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                                       "%llu\n", counter);
+               else
+                       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                                       "%s\n", "error");
+       }
+
+       val->value.s = buf;
+       val->len = len;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_mib);
+
+int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
+                               struct switch_port_stats *stats,
+                               int txb_id, int rxb_id)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       unsigned long long counter = 0;
+       int ret;
+
+       if (port >= smi->num_ports)
+               return -EINVAL;
+
+       ret = smi->ops->get_mib_counter(smi, txb_id, port, &counter);
+       if (ret)
+               return ret;
+
+       stats->tx_bytes = counter;
+
+       ret = smi->ops->get_mib_counter(smi, rxb_id, port, &counter);
+       if (ret)
+               return ret;
+
+       stats->rx_bytes = counter;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_stats);
+
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val)
+{
+       int i;
+       u32 len = 0;
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       char *buf = smi->buf;
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       memset(buf, '\0', sizeof(smi->buf));
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "VLAN %d: Ports: '", vlan4k.vid);
+
+       for (i = 0; i < smi->num_ports; i++) {
+               if (!(vlan4k.member & (1 << i)))
+                       continue;
+
+               len += snprintf(buf + len, sizeof(smi->buf) - len, "%d%s", i,
+                               (vlan4k.untag & (1 << i)) ? "" : "t");
+       }
+
+       len += snprintf(buf + len, sizeof(smi->buf) - len,
+                       "', members=%04x, untag=%04x, fid=%u",
+                       vlan4k.member, vlan4k.untag, vlan4k.fid);
+
+       val->value.s = buf;
+       val->len = len;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_info);
+
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       struct switch_port *port;
+       struct rtl8366_vlan_4k vlan4k;
+       int i;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+
+       port = &val->value.ports[0];
+       val->len = 0;
+       for (i = 0; i < smi->num_ports; i++) {
+               if (!(vlan4k.member & BIT(i)))
+                       continue;
+
+               port->id = i;
+               port->flags = (vlan4k.untag & BIT(i)) ?
+                                       0 : BIT(SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+               port++;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_ports);
+
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       struct switch_port *port;
+       u32 member = 0;
+       u32 untag = 0;
+       int err;
+       int i;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       port = &val->value.ports[0];
+       for (i = 0; i < val->len; i++, port++) {
+               int pvid = 0;
+               member |= BIT(port->id);
+
+               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
+                       untag |= BIT(port->id);
+
+               /*
+                * To ensure that we have a valid MC entry for this VLAN,
+                * initialize the port VLAN ID here.
+                */
+               err = rtl8366_get_pvid(smi, port->id, &pvid);
+               if (err < 0)
+                       return err;
+               if (pvid == 0) {
+                       err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
+                       if (err < 0)
+                               return err;
+               }
+       }
+
+       return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_ports);
+
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       val->value.i = vlan4k.fid;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_fid);
+
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val)
+{
+       struct rtl8366_vlan_4k vlan4k;
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
+               return -EINVAL;
+
+       if (val->value.i < 0 || val->value.i > attr->max)
+               return -EINVAL;
+
+       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
+       if (err)
+               return err;
+
+       return rtl8366_set_vlan(smi, val->port_vlan,
+                               vlan4k.member,
+                               vlan4k.untag,
+                               val->value.i);
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_fid);
+
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (attr->ofs > 2)
+               return -EINVAL;
+
+       if (attr->ofs == 1)
+               val->value.i = smi->vlan_enabled;
+       else
+               val->value.i = smi->vlan4k_enabled;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_enable);
+
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+
+       if (attr->ofs > 2)
+               return -EINVAL;
+
+       if (attr->ofs == 1)
+               err = rtl8366_enable_vlan(smi, val->value.i);
+       else
+               err = rtl8366_enable_vlan4k(smi, val->value.i);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_enable);
+
+struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent)
+{
+       struct rtl8366_smi *smi;
+
+       BUG_ON(!parent);
+
+       smi = kzalloc(sizeof(*smi), GFP_KERNEL);
+       if (!smi) {
+               dev_err(parent, "no memory for private data\n");
+               return NULL;
+       }
+
+       smi->parent = parent;
+       return smi;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_alloc);
+
+static int __rtl8366_smi_init(struct rtl8366_smi *smi, const char *name)
+{
+       int err;
+
+       if (!smi->ext_mbus) {
+               err = gpio_request(smi->gpio_sda, name);
+               if (err) {
+                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
+                               smi->gpio_sda, err);
+                       goto err_out;
+               }
+
+               err = gpio_request(smi->gpio_sck, name);
+               if (err) {
+                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
+                               smi->gpio_sck, err);
+                       goto err_free_sda;
+               }
+       }
+
+       spin_lock_init(&smi->lock);
+
+       /* start the switch */
+       if (smi->hw_reset) {
+               smi->hw_reset(smi, false);
+               msleep(RTL8366_SMI_HW_START_DELAY);
+       }
+
+       return 0;
+
+ err_free_sda:
+       gpio_free(smi->gpio_sda);
+ err_out:
+       return err;
+}
+
+static void __rtl8366_smi_cleanup(struct rtl8366_smi *smi)
+{
+       if (smi->hw_reset)
+               smi->hw_reset(smi, true);
+
+       if (!smi->ext_mbus) {
+               gpio_free(smi->gpio_sck);
+               gpio_free(smi->gpio_sda);
+       }
+}
+
+enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata)
+{
+       static struct rtl8366_smi smi;
+       enum rtl8366_type type = RTL8366_TYPE_UNKNOWN;
+       u32 reg = 0;
+
+       memset(&smi, 0, sizeof(smi));
+       smi.gpio_sda = pdata->gpio_sda;
+       smi.gpio_sck = pdata->gpio_sck;
+       smi.clk_delay = 10;
+       smi.cmd_read  = 0xa9;
+       smi.cmd_write = 0xa8;
+
+       if (__rtl8366_smi_init(&smi, "rtl8366"))
+               goto out;
+
+       if (rtl8366_smi_read_reg(&smi, 0x5c, &reg))
+               goto cleanup;
+
+       switch(reg) {
+       case 0x6027:
+               printk("Found an RTL8366S switch\n");
+               type = RTL8366_TYPE_S;
+               break;
+       case 0x5937:
+               printk("Found an RTL8366RB switch\n");
+               type = RTL8366_TYPE_RB;
+               break;
+       default:
+               printk("Found an Unknown RTL8366 switch (id=0x%04x)\n", reg);
+               break;
+       }
+
+cleanup:
+       __rtl8366_smi_cleanup(&smi);
+out:
+       return type;
+}
+
+int rtl8366_smi_init(struct rtl8366_smi *smi)
+{
+       int err;
+
+       if (!smi->ops)
+               return -EINVAL;
+
+       err = __rtl8366_smi_init(smi, dev_name(smi->parent));
+       if (err)
+               goto err_out;
+
+       if (!smi->ext_mbus)
+               dev_info(smi->parent, "using GPIO pins %u (SDA) and %u (SCK)\n",
+                        smi->gpio_sda, smi->gpio_sck);
+       else
+               dev_info(smi->parent, "using MDIO bus '%s'\n", smi->ext_mbus->name);
+
+       err = smi->ops->detect(smi);
+       if (err) {
+               dev_err(smi->parent, "chip detection failed, err=%d\n", err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_reset(smi);
+       if (err)
+               goto err_free_sck;
+
+       err = smi->ops->setup(smi);
+       if (err) {
+               dev_err(smi->parent, "chip setup failed, err=%d\n", err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_init_vlan(smi);
+       if (err) {
+               dev_err(smi->parent, "VLAN initialization failed, err=%d\n",
+                       err);
+               goto err_free_sck;
+       }
+
+       err = rtl8366_enable_all_ports(smi, 1);
+       if (err)
+               goto err_free_sck;
+
+       err = rtl8366_smi_mii_init(smi);
+       if (err)
+               goto err_free_sck;
+
+       rtl8366_debugfs_init(smi);
+
+       return 0;
+
+ err_free_sck:
+       __rtl8366_smi_cleanup(smi);
+ err_out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_init);
+
+void rtl8366_smi_cleanup(struct rtl8366_smi *smi)
+{
+       rtl8366_debugfs_remove(smi);
+       rtl8366_smi_mii_cleanup(smi);
+       __rtl8366_smi_cleanup(smi);
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup);
+
+#ifdef CONFIG_OF
+static void rtl8366_smi_reset(struct rtl8366_smi *smi, bool active)
+{
+       if (active)
+               reset_control_assert(smi->reset);
+       else
+               reset_control_deassert(smi->reset);
+}
+
+int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0);
+       int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0);
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *mdio_node;
+
+       mdio_node = of_parse_phandle(np, "mii-bus", 0);
+       if (!mdio_node) {
+               dev_err(&pdev->dev, "cannot find mdio node phandle");
+               goto try_gpio;
+       }
+
+       smi->ext_mbus = of_mdio_find_bus(mdio_node);
+       if (!smi->ext_mbus) {
+               dev_err(&pdev->dev,
+                       "cannot find mdio bus from bus handle");
+               goto try_gpio;
+       }
+
+       return 0;
+
+try_gpio:
+       if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) {
+               dev_err(&pdev->dev, "gpios missing in devictree\n");
+               return -EINVAL;
+       }
+
+       smi->gpio_sda = sda;
+       smi->gpio_sck = sck;
+       smi->reset = devm_reset_control_get(&pdev->dev, "switch");
+       if (!IS_ERR(smi->reset))
+               smi->hw_reset = rtl8366_smi_reset;
+
+       return 0;
+}
+#else
+static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       return -ENODEV;
+}
+#endif
+
+int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata = pdev->dev.platform_data;
+
+       if (!pdev->dev.platform_data) {
+               dev_err(&pdev->dev, "no platform data specified\n");
+               return -EINVAL;
+       }
+
+       smi->gpio_sda = pdata->gpio_sda;
+       smi->gpio_sck = pdata->gpio_sck;
+       smi->hw_reset = pdata->hw_reset;
+
+       return 0;
+}
+
+
+struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_alloc(&pdev->dev);
+       if (!smi)
+               return NULL;
+
+       if (pdev->dev.of_node)
+               err = rtl8366_smi_probe_of(pdev, smi);
+       else
+               err = rtl8366_smi_probe_plat(pdev, smi);
+
+       if (err)
+               goto free_smi;
+
+       return smi;
+
+free_smi:
+       kfree(smi);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(rtl8366_smi_probe);
+
+MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files-4.9/drivers/net/phy/rtl8366_smi.h
new file mode 100644 (file)
index 0000000..d1d988a
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Realtek RTL8366 SMI interface driver defines
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8366_SMI_H
+#define _RTL8366_SMI_H
+
+#include <linux/phy.h>
+#include <linux/switch.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct rtl8366_smi_ops;
+struct rtl8366_vlan_ops;
+struct mii_bus;
+struct dentry;
+struct inode;
+struct file;
+
+struct rtl8366_mib_counter {
+       unsigned        base;
+       unsigned        offset;
+       unsigned        length;
+       const char      *name;
+};
+
+struct rtl8366_smi {
+       struct device           *parent;
+       unsigned int            gpio_sda;
+       unsigned int            gpio_sck;
+       void                    (*hw_reset)(struct rtl8366_smi *smi, bool active);
+       unsigned int            clk_delay;      /* ns */
+       u8                      cmd_read;
+       u8                      cmd_write;
+       spinlock_t              lock;
+       struct mii_bus          *mii_bus;
+       int                     mii_irq[PHY_MAX_ADDR];
+       struct switch_dev       sw_dev;
+
+       unsigned int            cpu_port;
+       unsigned int            num_ports;
+       unsigned int            num_vlan_mc;
+       unsigned int            num_mib_counters;
+       struct rtl8366_mib_counter *mib_counters;
+
+       struct rtl8366_smi_ops  *ops;
+
+       int                     vlan_enabled;
+       int                     vlan4k_enabled;
+
+       char                    buf[4096];
+
+       struct reset_control    *reset;
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+       struct dentry           *debugfs_root;
+       u16                     dbg_reg;
+       u8                      dbg_vlan_4k_page;
+#endif
+       struct mii_bus          *ext_mbus;
+};
+
+struct rtl8366_vlan_mc {
+       u16     vid;
+       u16     untag;
+       u16     member;
+       u8      fid;
+       u8      priority;
+};
+
+struct rtl8366_vlan_4k {
+       u16     vid;
+       u16     untag;
+       u16     member;
+       u8      fid;
+};
+
+struct rtl8366_smi_ops {
+       int     (*detect)(struct rtl8366_smi *smi);
+       int     (*reset_chip)(struct rtl8366_smi *smi);
+       int     (*setup)(struct rtl8366_smi *smi);
+
+       int     (*mii_read)(struct mii_bus *bus, int addr, int reg);
+       int     (*mii_write)(struct mii_bus *bus, int addr, int reg, u16 val);
+
+       int     (*get_vlan_mc)(struct rtl8366_smi *smi, u32 index,
+                              struct rtl8366_vlan_mc *vlanmc);
+       int     (*set_vlan_mc)(struct rtl8366_smi *smi, u32 index,
+                              const struct rtl8366_vlan_mc *vlanmc);
+       int     (*get_vlan_4k)(struct rtl8366_smi *smi, u32 vid,
+                              struct rtl8366_vlan_4k *vlan4k);
+       int     (*set_vlan_4k)(struct rtl8366_smi *smi,
+                              const struct rtl8366_vlan_4k *vlan4k);
+       int     (*get_mc_index)(struct rtl8366_smi *smi, int port, int *val);
+       int     (*set_mc_index)(struct rtl8366_smi *smi, int port, int index);
+       int     (*get_mib_counter)(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val);
+       int     (*is_vlan_valid)(struct rtl8366_smi *smi, unsigned vlan);
+       int     (*enable_vlan)(struct rtl8366_smi *smi, int enable);
+       int     (*enable_vlan4k)(struct rtl8366_smi *smi, int enable);
+       int     (*enable_port)(struct rtl8366_smi *smi, int port, int enable);
+};
+
+struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent);
+int rtl8366_smi_init(struct rtl8366_smi *smi);
+void rtl8366_smi_cleanup(struct rtl8366_smi *smi);
+int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data);
+int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data);
+int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data);
+int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data);
+
+int rtl8366_reset_vlan(struct rtl8366_smi *smi);
+int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable);
+int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable);
+
+#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
+int rtl8366_debugfs_open(struct inode *inode, struct file *file);
+#endif
+
+static inline struct rtl8366_smi *sw_to_rtl8366_smi(struct switch_dev *sw)
+{
+       return container_of(sw, struct rtl8366_smi, sw_dev);
+}
+
+int rtl8366_sw_reset_switch(struct switch_dev *dev);
+int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
+int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
+int rtl8366_sw_get_port_mib(struct switch_dev *dev,
+                           const struct switch_attr *attr,
+                           struct switch_val *val);
+int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
+                            const struct switch_attr *attr,
+                            struct switch_val *val);
+int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val);
+int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val);
+int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
+                              const struct switch_attr *attr,
+                              struct switch_val *val);
+int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
+                               struct switch_port_stats *stats,
+                               int txb_id, int rxb_id);
+
+struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
+
+#endif /*  _RTL8366_SMI_H */
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files-4.9/drivers/net/phy/rtl8366rb.c
new file mode 100644 (file)
index 0000000..dc394c0
--- /dev/null
@@ -0,0 +1,1532 @@
+/*
+ * Platform driver for the Realtek RTL8366RB ethernet switch
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
+ * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8366.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8366RB_DRIVER_DESC  "Realtek RTL8366RB ethernet switch driver"
+#define RTL8366RB_DRIVER_VER   "0.2.4"
+
+#define RTL8366RB_PHY_NO_MAX   4
+#define RTL8366RB_PHY_PAGE_MAX 7
+#define RTL8366RB_PHY_ADDR_MAX 31
+
+/* Switch Global Configuration register */
+#define RTL8366RB_SGCR                         0x0000
+#define RTL8366RB_SGCR_EN_BC_STORM_CTRL                BIT(0)
+#define RTL8366RB_SGCR_MAX_LENGTH(_x)          (_x << 4)
+#define RTL8366RB_SGCR_MAX_LENGTH_MASK         RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_MAX_LENGTH_1522         RTL8366RB_SGCR_MAX_LENGTH(0x0)
+#define RTL8366RB_SGCR_MAX_LENGTH_1536         RTL8366RB_SGCR_MAX_LENGTH(0x1)
+#define RTL8366RB_SGCR_MAX_LENGTH_1552         RTL8366RB_SGCR_MAX_LENGTH(0x2)
+#define RTL8366RB_SGCR_MAX_LENGTH_9216         RTL8366RB_SGCR_MAX_LENGTH(0x3)
+#define RTL8366RB_SGCR_EN_VLAN                 BIT(13)
+#define RTL8366RB_SGCR_EN_VLAN_4KTB            BIT(14)
+
+/* Port Enable Control register */
+#define RTL8366RB_PECR                         0x0001
+
+/* Port Mirror Control Register */
+#define RTL8366RB_PMCR                         0x0007
+#define RTL8366RB_PMCR_SOURCE_PORT(_x)         (_x)
+#define RTL8366RB_PMCR_SOURCE_PORT_MASK                0x000f
+#define RTL8366RB_PMCR_MONITOR_PORT(_x)                ((_x) << 4)
+#define RTL8366RB_PMCR_MONITOR_PORT_MASK       0x00f0
+#define RTL8366RB_PMCR_MIRROR_RX               BIT(8)
+#define RTL8366RB_PMCR_MIRROR_TX               BIT(9)
+#define RTL8366RB_PMCR_MIRROR_SPC              BIT(10)
+#define RTL8366RB_PMCR_MIRROR_ISO              BIT(11)
+
+/* Switch Security Control registers */
+#define RTL8366RB_SSCR0                                0x0002
+#define RTL8366RB_SSCR1                                0x0003
+#define RTL8366RB_SSCR2                                0x0004
+#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA                BIT(0)
+
+#define RTL8366RB_RESET_CTRL_REG               0x0100
+#define RTL8366RB_CHIP_CTRL_RESET_HW           1
+#define RTL8366RB_CHIP_CTRL_RESET_SW           (1 << 1)
+
+#define RTL8366RB_CHIP_VERSION_CTRL_REG                0x050A
+#define RTL8366RB_CHIP_VERSION_MASK            0xf
+#define RTL8366RB_CHIP_ID_REG                  0x0509
+#define RTL8366RB_CHIP_ID_8366                 0x5937
+
+/* PHY registers control */
+#define RTL8366RB_PHY_ACCESS_CTRL_REG          0x8000
+#define RTL8366RB_PHY_ACCESS_DATA_REG          0x8002
+
+#define RTL8366RB_PHY_CTRL_READ                        1
+#define RTL8366RB_PHY_CTRL_WRITE               0
+
+#define RTL8366RB_PHY_REG_MASK                 0x1f
+#define RTL8366RB_PHY_PAGE_OFFSET              5
+#define RTL8366RB_PHY_PAGE_MASK                        (0xf << 5)
+#define RTL8366RB_PHY_NO_OFFSET                        9
+#define RTL8366RB_PHY_NO_MASK                  (0x1f << 9)
+
+#define RTL8366RB_VLAN_INGRESS_CTRL2_REG       0x037f
+
+/* LED control registers */
+#define RTL8366RB_LED_BLINKRATE_REG            0x0430
+#define RTL8366RB_LED_BLINKRATE_BIT            0
+#define RTL8366RB_LED_BLINKRATE_MASK           0x0007
+
+#define RTL8366RB_LED_CTRL_REG                 0x0431
+#define RTL8366RB_LED_0_1_CTRL_REG             0x0432
+#define RTL8366RB_LED_2_3_CTRL_REG             0x0433
+
+#define RTL8366RB_MIB_COUNT                    33
+#define RTL8366RB_GLOBAL_MIB_COUNT             1
+#define RTL8366RB_MIB_COUNTER_PORT_OFFSET      0x0050
+#define RTL8366RB_MIB_COUNTER_BASE             0x1000
+#define RTL8366RB_MIB_CTRL_REG                 0x13F0
+#define RTL8366RB_MIB_CTRL_USER_MASK           0x0FFC
+#define RTL8366RB_MIB_CTRL_BUSY_MASK           BIT(0)
+#define RTL8366RB_MIB_CTRL_RESET_MASK          BIT(1)
+#define RTL8366RB_MIB_CTRL_PORT_RESET(_p)      BIT(2 + (_p))
+#define RTL8366RB_MIB_CTRL_GLOBAL_RESET                BIT(11)
+
+#define RTL8366RB_PORT_VLAN_CTRL_BASE          0x0063
+#define RTL8366RB_PORT_VLAN_CTRL_REG(_p)  \
+               (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366RB_PORT_VLAN_CTRL_MASK          0xf
+#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p)     (4 * ((_p) % 4))
+
+
+#define RTL8366RB_VLAN_TABLE_READ_BASE         0x018C
+#define RTL8366RB_VLAN_TABLE_WRITE_BASE                0x0185
+
+
+#define RTL8366RB_TABLE_ACCESS_CTRL_REG                0x0180
+#define RTL8366RB_TABLE_VLAN_READ_CTRL         0x0E01
+#define RTL8366RB_TABLE_VLAN_WRITE_CTRL                0x0F01
+
+#define RTL8366RB_VLAN_MC_BASE(_x)             (0x0020 + (_x) * 3)
+
+
+#define RTL8366RB_PORT_LINK_STATUS_BASE                0x0014
+#define RTL8366RB_PORT_STATUS_SPEED_MASK       0x0003
+#define RTL8366RB_PORT_STATUS_DUPLEX_MASK      0x0004
+#define RTL8366RB_PORT_STATUS_LINK_MASK                0x0010
+#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK     0x0020
+#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK     0x0040
+#define RTL8366RB_PORT_STATUS_AN_MASK          0x0080
+
+
+#define RTL8366RB_PORT_NUM_CPU         5
+#define RTL8366RB_NUM_PORTS            6
+#define RTL8366RB_NUM_VLANS            16
+#define RTL8366RB_NUM_LEDGROUPS                4
+#define RTL8366RB_NUM_VIDS             4096
+#define RTL8366RB_PRIORITYMAX          7
+#define RTL8366RB_FIDMAX               7
+
+
+#define RTL8366RB_PORT_1               (1 << 0) /* In userspace port 0 */
+#define RTL8366RB_PORT_2               (1 << 1) /* In userspace port 1 */
+#define RTL8366RB_PORT_3               (1 << 2) /* In userspace port 2 */
+#define RTL8366RB_PORT_4               (1 << 3) /* In userspace port 3 */
+#define RTL8366RB_PORT_5               (1 << 4) /* In userspace port 4 */
+
+#define RTL8366RB_PORT_CPU             (1 << 5) /* CPU port */
+
+#define RTL8366RB_PORT_ALL             (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4 |     \
+                                        RTL8366RB_PORT_5 |     \
+                                        RTL8366RB_PORT_CPU)
+
+#define RTL8366RB_PORT_ALL_BUT_CPU     (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4 |     \
+                                        RTL8366RB_PORT_5)
+
+#define RTL8366RB_PORT_ALL_EXTERNAL    (RTL8366RB_PORT_1 |     \
+                                        RTL8366RB_PORT_2 |     \
+                                        RTL8366RB_PORT_3 |     \
+                                        RTL8366RB_PORT_4)
+
+#define RTL8366RB_PORT_ALL_INTERNAL     RTL8366RB_PORT_CPU
+
+#define RTL8366RB_VLAN_VID_MASK                0xfff
+#define RTL8366RB_VLAN_PRIORITY_SHIFT  12
+#define RTL8366RB_VLAN_PRIORITY_MASK   0x7
+#define RTL8366RB_VLAN_UNTAG_SHIFT     8
+#define RTL8366RB_VLAN_UNTAG_MASK      0xff
+#define RTL8366RB_VLAN_MEMBER_MASK     0xff
+#define RTL8366RB_VLAN_FID_MASK                0x7
+
+
+/* Port ingress bandwidth control */
+#define RTL8366RB_IB_BASE              0x0200
+#define RTL8366RB_IB_REG(pnum)         (RTL8366RB_IB_BASE + pnum)
+#define RTL8366RB_IB_BDTH_MASK         0x3fff
+#define RTL8366RB_IB_PREIFG_OFFSET     14
+#define RTL8366RB_IB_PREIFG_MASK       (1 << RTL8366RB_IB_PREIFG_OFFSET)
+
+/* Port egress bandwidth control */
+#define RTL8366RB_EB_BASE              0x02d1
+#define RTL8366RB_EB_REG(pnum)         (RTL8366RB_EB_BASE + pnum)
+#define RTL8366RB_EB_BDTH_MASK         0x3fff
+#define RTL8366RB_EB_PREIFG_REG        0x02f8
+#define RTL8366RB_EB_PREIFG_OFFSET     9
+#define RTL8366RB_EB_PREIFG_MASK       (1 << RTL8366RB_EB_PREIFG_OFFSET)
+
+#define RTL8366RB_BDTH_SW_MAX          1048512
+#define RTL8366RB_BDTH_UNIT            64
+#define RTL8366RB_BDTH_REG_DEFAULT     16383
+
+/* QOS */
+#define RTL8366RB_QOS_BIT              15
+#define RTL8366RB_QOS_MASK             (1 << RTL8366RB_QOS_BIT)
+/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */
+#define RTL8366RB_QOS_DEFAULT_PREIFG   1
+
+
+#define RTL8366RB_MIB_RXB_ID           0       /* IfInOctets */
+#define RTL8366RB_MIB_TXB_ID           20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 4, "EtherStatsOctets"                          },
+       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 10, 2, "EtherFragments"                            },
+       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 24, 2, "EtherOversizeStats"                        },
+       { 0, 26, 2, "EtherStatsJabbers"                         },
+       { 0, 28, 2, "IfInUcastPkts"                             },
+       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
+       { 0, 34, 2, "EtherStatsDropEvents"                      },
+       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
+       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
+       { 0, 40, 2, "Dot3InPauseFrames"                         },
+       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 44, 4, "IfOutOctets"                               },
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
+       { 0, 66, 2, "IfOutUcastPkts"                            },
+       { 0, 68, 2, "IfOutMulticastPkts"                        },
+       { 0, 70, 2, "IfOutBroadcastPkts"                        },
+};
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int rtl8366rb_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       u32 data;
+
+       rtl8366_smi_write_reg_noack(smi, RTL8366RB_RESET_CTRL_REG,
+                                   RTL8366RB_CHIP_CTRL_RESET_HW);
+       do {
+               msleep(1);
+               if (rtl8366_smi_read_reg(smi, RTL8366RB_RESET_CTRL_REG, &data))
+                       return -EIO;
+
+               if (!(data & RTL8366RB_CHIP_CTRL_RESET_HW))
+                       break;
+       } while (--timeout);
+
+       if (!timeout) {
+               printk("Timeout waiting for the switch to reset\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_setup(struct rtl8366_smi *smi)
+{
+       int err;
+#ifdef CONFIG_OF
+       unsigned i;
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK,
+               RTL8366RB_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366RB_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366RB_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366RB_VLAN_INGRESS_CTRL2_REG, RTL8366RB_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
+static int rtl8366rb_read_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_no, u32 page, u32 addr, u32 *data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366RB_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366RB_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                   RTL8366RB_PHY_CTRL_READ);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
+             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
+             (addr & RTL8366RB_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, 0);
+       if (ret)
+               return ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_PHY_ACCESS_DATA_REG, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366rb_write_phy_reg(struct rtl8366_smi *smi,
+                                 u32 phy_no, u32 page, u32 addr, u32 data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366RB_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366RB_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366RB_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
+                                   RTL8366RB_PHY_CTRL_WRITE);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
+             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
+             (addr & RTL8366RB_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366rb_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                    int port, unsigned long long *val)
+{
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8366RB_NUM_PORTS || counter >= RTL8366RB_MIB_COUNT)
+               return -EINVAL;
+
+       addr = RTL8366RB_MIB_COUNTER_BASE +
+              RTL8366RB_MIB_COUNTER_PORT_OFFSET * (port) +
+              rtl8366rb_mib_counters[counter].offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       data = 0; /* writing data will be discard by ASIC */
+       err = rtl8366_smi_write_reg(smi, addr, data);
+       if (err)
+               return err;
+
+       /* read MIB control register */
+       err =  rtl8366_smi_read_reg(smi, RTL8366RB_MIB_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       if (data & RTL8366RB_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8366RB_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       mibvalue = 0;
+       for (i = rtl8366rb_mib_counters[counter].length; i > 0; i--) {
+               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
+               if (err)
+                       return err;
+
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8366rb_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                                struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8366RB_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_VLAN_TABLE_WRITE_BASE,
+                                   vid & RTL8366RB_VLAN_VID_MASK);
+       if (err)
+               return err;
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366RB_TABLE_VLAN_READ_CTRL);
+       if (err)
+               return err;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366RB_VLAN_TABLE_READ_BASE + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlan4k->vid = vid;
+       vlan4k->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
+                       RTL8366RB_VLAN_UNTAG_MASK;
+       vlan4k->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
+       vlan4k->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_set_vlan_4k(struct rtl8366_smi *smi,
+                                const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8366RB_NUM_VIDS ||
+           vlan4k->member > RTL8366RB_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8366RB_VLAN_UNTAG_MASK ||
+           vlan4k->fid > RTL8366RB_FIDMAX)
+               return -EINVAL;
+
+       data[0] = vlan4k->vid & RTL8366RB_VLAN_VID_MASK;
+       data[1] = (vlan4k->member & RTL8366RB_VLAN_MEMBER_MASK) |
+                 ((vlan4k->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
+                       RTL8366RB_VLAN_UNTAG_SHIFT);
+       data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366RB_VLAN_TABLE_WRITE_BASE + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366RB_TABLE_VLAN_WRITE_CTRL);
+
+       return err;
+}
+
+static int rtl8366rb_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                                struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8366RB_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366RB_VLAN_MC_BASE(index) + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlanmc->vid = data[0] & RTL8366RB_VLAN_VID_MASK;
+       vlanmc->priority = (data[0] >> RTL8366RB_VLAN_PRIORITY_SHIFT) &
+                          RTL8366RB_VLAN_PRIORITY_MASK;
+       vlanmc->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
+                       RTL8366RB_VLAN_UNTAG_MASK;
+       vlanmc->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
+       vlanmc->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                                const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[3];
+       int err;
+       int i;
+
+       if (index >= RTL8366RB_NUM_VLANS ||
+           vlanmc->vid >= RTL8366RB_NUM_VIDS ||
+           vlanmc->priority > RTL8366RB_PRIORITYMAX ||
+           vlanmc->member > RTL8366RB_VLAN_MEMBER_MASK ||
+           vlanmc->untag > RTL8366RB_VLAN_UNTAG_MASK ||
+           vlanmc->fid > RTL8366RB_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->vid & RTL8366RB_VLAN_VID_MASK) |
+                 ((vlanmc->priority & RTL8366RB_VLAN_PRIORITY_MASK) <<
+                       RTL8366RB_VLAN_PRIORITY_SHIFT);
+       data[1] = (vlanmc->member & RTL8366RB_VLAN_MEMBER_MASK) |
+                 ((vlanmc->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
+                       RTL8366RB_VLAN_UNTAG_SHIFT);
+       data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK;
+
+       for (i = 0; i < 3; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366RB_VLAN_MC_BASE(index) + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+                                  &data);
+       if (err)
+               return err;
+
+       *val = (data >> RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)) &
+              RTL8366RB_PORT_VLAN_CTRL_MASK;
+
+       return 0;
+
+}
+
+static int rtl8366rb_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8366RB_NUM_PORTS || index >= RTL8366RB_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
+                               RTL8366RB_PORT_VLAN_CTRL_MASK <<
+                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port),
+                               (index & RTL8366RB_PORT_VLAN_CTRL_MASK) <<
+                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366rb_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8366RB_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8366RB_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8366rb_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN,
+                               (enable) ? RTL8366RB_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366rb_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR,
+                               RTL8366RB_SGCR_EN_VLAN_4KTB,
+                               (enable) ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0);
+}
+
+static int rtl8366rb_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, (1 << port),
+                               (enable) ? 0 : (1 << port));
+}
+
+static int rtl8366rb_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
+                               RTL8366RB_MIB_CTRL_GLOBAL_RESET);
+}
+
+static int rtl8366rb_sw_get_blinkrate(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_LED_BLINKRATE_REG, &data);
+
+       val->value.i = (data & (RTL8366RB_LED_BLINKRATE_MASK));
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_blinkrate(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->value.i >= 6)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_LED_BLINKRATE_REG,
+                               RTL8366RB_LED_BLINKRATE_MASK,
+                               val->value.i);
+}
+
+static int rtl8366rb_sw_get_learning_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_SSCR0, &data);
+       val->value.i = !data;
+
+       return 0;
+}
+
+
+static int rtl8366rb_sw_set_learning_enable(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 portmask = 0;
+       int err = 0;
+
+       if (!val->value.i)
+               portmask = RTL8366RB_PORT_ALL;
+
+       /* set learning for all ports */
+       REG_WR(smi, RTL8366RB_SSCR0, portmask);
+
+       /* set auto ageing for all ports */
+       REG_WR(smi, RTL8366RB_SSCR1, portmask);
+
+       return 0;
+}
+
+static int rtl8366rb_sw_get_port_link(struct switch_dev *dev,
+                                    int port,
+                                    struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PORT_LINK_STATUS_BASE + (port / 2),
+                            &data);
+
+       if (port % 2)
+               data = data >> 8;
+
+       link->link = !!(data & RTL8366RB_PORT_STATUS_LINK_MASK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8366RB_PORT_STATUS_DUPLEX_MASK);
+       link->rx_flow = !!(data & RTL8366RB_PORT_STATUS_RXPAUSE_MASK);
+       link->tx_flow = !!(data & RTL8366RB_PORT_STATUS_TXPAUSE_MASK);
+       link->aneg = !!(data & RTL8366RB_PORT_STATUS_AN_MASK);
+
+       speed = (data & RTL8366RB_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       u32 mask;
+       u32 reg;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       if (val->port_vlan == RTL8366RB_PORT_NUM_CPU) {
+               reg = RTL8366RB_LED_BLINKRATE_REG;
+               mask = 0xF << 4;
+               data = val->value.i << 4;
+       } else {
+               reg = RTL8366RB_LED_CTRL_REG;
+               mask = 0xF << (val->port_vlan * 4),
+               data = val->value.i << (val->port_vlan * 4);
+       }
+
+       return rtl8366_smi_rmwr(smi, reg, mask, data);
+}
+
+static int rtl8366rb_sw_get_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+
+       if (val->port_vlan >= RTL8366RB_NUM_LEDGROUPS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_LED_CTRL_REG, &data);
+       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_disable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 mask, data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       mask = 1 << val->port_vlan ;
+       if (val->value.i)
+               data = mask;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, mask, data);
+}
+
+static int rtl8366rb_sw_get_port_disable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PECR, &data);
+       if (data & (1 << val->port_vlan))
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_rate_in(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
+               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
+       else
+               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_IB_REG(val->port_vlan),
+               RTL8366RB_IB_BDTH_MASK | RTL8366RB_IB_PREIFG_MASK,
+               val->value.i |
+               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_IB_PREIFG_OFFSET));
+
+}
+
+static int rtl8366rb_sw_get_port_rate_in(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_IB_REG(val->port_vlan), &data);
+       data &= RTL8366RB_IB_BDTH_MASK;
+       if (data < RTL8366RB_IB_BDTH_MASK)
+               data += 1;
+
+       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_port_rate_out(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_rmwr(smi, RTL8366RB_EB_PREIFG_REG,
+               RTL8366RB_EB_PREIFG_MASK,
+               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_EB_PREIFG_OFFSET));
+
+       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
+               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
+       else
+               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_EB_REG(val->port_vlan),
+                       RTL8366RB_EB_BDTH_MASK, val->value.i );
+
+}
+
+static int rtl8366rb_sw_get_port_rate_out(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_EB_REG(val->port_vlan), &data);
+       data &= RTL8366RB_EB_BDTH_MASK;
+       if (data < RTL8366RB_EB_BDTH_MASK)
+               data += 1;
+
+       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_qos_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_QOS_MASK;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_QOS_MASK, data);
+}
+
+static int rtl8366rb_sw_get_qos_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_SGCR, &data);
+       if (data & RTL8366RB_QOS_MASK)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_rx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_RX;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_RX, data);
+}
+
+static int rtl8366rb_sw_get_mirror_rx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_RX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_tx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_TX;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_TX, data);
+}
+
+static int rtl8366rb_sw_get_mirror_tx_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_TX)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_monitor_isolation_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_ISO;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_ISO, data);
+}
+
+static int rtl8366rb_sw_get_monitor_isolation_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_ISO)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_pause_frames_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       if (val->value.i)
+               data = RTL8366RB_PMCR_MIRROR_SPC;
+       else
+               data = 0;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_SPC, data);
+}
+
+static int rtl8366rb_sw_get_mirror_pause_frames_enable(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       if (data & RTL8366RB_PMCR_MIRROR_SPC)
+               val->value.i = 1;
+       else
+               val->value.i = 0;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_monitor_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       data = RTL8366RB_PMCR_MONITOR_PORT(val->value.i);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MONITOR_PORT_MASK, data);
+}
+
+static int rtl8366rb_sw_get_mirror_monitor_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       val->value.i = (data & RTL8366RB_PMCR_MONITOR_PORT_MASK) >> 4;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_set_mirror_source_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       data = RTL8366RB_PMCR_SOURCE_PORT(val->value.i);
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_SOURCE_PORT_MASK, data);
+}
+
+static int rtl8366rb_sw_get_mirror_source_port(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
+       val->value.i = data & RTL8366RB_PMCR_SOURCE_PORT_MASK;
+
+       return 0;
+}
+
+static int rtl8366rb_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
+                               RTL8366RB_MIB_CTRL_PORT_RESET(val->port_vlan));
+}
+
+static int rtl8366rb_sw_get_port_stats(struct switch_dev *dev, int port,
+                                       struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8366RB_MIB_TXB_ID, RTL8366RB_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8366rb_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_learning",
+               .description = "Enable learning, enable aging",
+               .set = rtl8366rb_sw_set_learning_enable,
+               .get = rtl8366rb_sw_get_learning_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8366rb_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "blinkrate",
+               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
+               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
+               .set = rtl8366rb_sw_set_blinkrate,
+               .get = rtl8366rb_sw_get_blinkrate,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_qos",
+               .description = "Enable QOS",
+               .set = rtl8366rb_sw_set_qos_enable,
+               .get = rtl8366rb_sw_get_qos_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_rx",
+               .description = "Enable mirroring of RX packets",
+               .set = rtl8366rb_sw_set_mirror_rx_enable,
+               .get = rtl8366rb_sw_get_mirror_rx_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_tx",
+               .description = "Enable mirroring of TX packets",
+               .set = rtl8366rb_sw_set_mirror_tx_enable,
+               .get = rtl8366rb_sw_get_mirror_tx_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_monitor_isolation",
+               .description = "Enable isolation of monitor port (TX packets will be dropped)",
+               .set = rtl8366rb_sw_set_monitor_isolation_enable,
+               .get = rtl8366rb_sw_get_monitor_isolation_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_mirror_pause_frames",
+               .description = "Enable mirroring of RX pause frames",
+               .set = rtl8366rb_sw_set_mirror_pause_frames_enable,
+               .get = rtl8366rb_sw_get_mirror_pause_frames_enable,
+               .max = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_monitor_port",
+               .description = "Mirror monitor port",
+               .set = rtl8366rb_sw_set_mirror_monitor_port,
+               .get = rtl8366rb_sw_get_mirror_monitor_port,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "mirror_source_port",
+               .description = "Mirror source port",
+               .set = rtl8366rb_sw_set_mirror_source_port,
+               .get = rtl8366rb_sw_get_mirror_source_port,
+               .max = 5
+       },
+};
+
+static struct switch_attr rtl8366rb_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8366rb_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
+               .max = 15,
+               .set = rtl8366rb_sw_set_port_led,
+               .get = rtl8366rb_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "disable",
+               .description = "Get/Set port state (enabled or disabled)",
+               .max = 1,
+               .set = rtl8366rb_sw_set_port_disable,
+               .get = rtl8366rb_sw_get_port_disable,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "rate_in",
+               .description = "Get/Set port ingress (incoming) bandwidth limit in kbps",
+               .max = RTL8366RB_BDTH_SW_MAX,
+               .set = rtl8366rb_sw_set_port_rate_in,
+               .get = rtl8366rb_sw_get_port_rate_in,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "rate_out",
+               .description = "Get/Set port egress (outgoing) bandwidth limit in kbps",
+               .max = RTL8366RB_BDTH_SW_MAX,
+               .set = rtl8366rb_sw_set_port_rate_out,
+               .get = rtl8366rb_sw_get_port_rate_out,
+       },
+};
+
+static struct switch_attr rtl8366rb_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8366RB_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8366_ops = {
+       .attr_global = {
+               .attr = rtl8366rb_globals,
+               .n_attr = ARRAY_SIZE(rtl8366rb_globals),
+       },
+       .attr_port = {
+               .attr = rtl8366rb_port,
+               .n_attr = ARRAY_SIZE(rtl8366rb_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8366rb_vlan,
+               .n_attr = ARRAY_SIZE(rtl8366rb_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366rb_sw_get_port_link,
+       .get_port_stats = rtl8366rb_sw_get_port_stats,
+};
+
+static int rtl8366rb_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8366RB";
+       dev->cpu_port = RTL8366RB_PORT_NUM_CPU;
+       dev->ports = RTL8366RB_NUM_PORTS;
+       dev->vlans = RTL8366RB_NUM_VIDS;
+       dev->ops = &rtl8366_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8366rb_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8366rb_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8366rb_read_phy_reg(smi, addr, 0, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8366rb_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8366rb_write_phy_reg(smi, addr, 0, reg, val);
+       /* flush write */
+       (void) rtl8366rb_read_phy_reg(smi, addr, 0, reg, &t);
+
+       return err;
+}
+
+static int rtl8366rb_detect(struct rtl8366_smi *smi)
+{
+       u32 chip_id = 0;
+       u32 chip_ver = 0;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_ID_REG, &chip_id);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip id\n");
+               return ret;
+       }
+
+       switch (chip_id) {
+       case RTL8366RB_CHIP_ID_8366:
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_VERSION_CTRL_REG,
+                                  &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
+                chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8366rb_smi_ops = {
+       .detect         = rtl8366rb_detect,
+       .reset_chip     = rtl8366rb_reset_chip,
+       .setup          = rtl8366rb_setup,
+
+       .mii_read       = rtl8366rb_mii_read,
+       .mii_write      = rtl8366rb_mii_write,
+
+       .get_vlan_mc    = rtl8366rb_get_vlan_mc,
+       .set_vlan_mc    = rtl8366rb_set_vlan_mc,
+       .get_vlan_4k    = rtl8366rb_get_vlan_4k,
+       .set_vlan_4k    = rtl8366rb_set_vlan_4k,
+       .get_mc_index   = rtl8366rb_get_mc_index,
+       .set_mc_index   = rtl8366rb_set_mc_index,
+       .get_mib_counter = rtl8366rb_get_mib_counter,
+       .is_vlan_valid  = rtl8366rb_is_vlan_valid,
+       .enable_vlan    = rtl8366rb_enable_vlan,
+       .enable_vlan4k  = rtl8366rb_enable_vlan4k,
+       .enable_port    = rtl8366rb_enable_port,
+};
+
+static int rtl8366rb_probe(struct platform_device *pdev)
+{
+       static int rtl8366_smi_version_printed;
+       struct rtl8366_smi *smi;
+       int err;
+
+       if (!rtl8366_smi_version_printed++)
+               printk(KERN_NOTICE RTL8366RB_DRIVER_DESC
+                      " version " RTL8366RB_DRIVER_VER"\n");
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 10;
+       smi->cmd_read = 0xa9;
+       smi->cmd_write = 0xa8;
+       smi->ops = &rtl8366rb_smi_ops;
+       smi->cpu_port = RTL8366RB_PORT_NUM_CPU;
+       smi->num_ports = RTL8366RB_NUM_PORTS;
+       smi->num_vlan_mc = RTL8366RB_NUM_VLANS;
+       smi->mib_counters = rtl8366rb_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8366rb_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8366rb_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8366rb_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366rb_match[] = {
+       { .compatible = "realtek,rtl8366rb" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366rb_match);
+#endif
+
+static struct platform_driver rtl8366rb_driver = {
+       .driver = {
+               .name           = RTL8366RB_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(rtl8366rb_match),
+       },
+       .probe          = rtl8366rb_probe,
+       .remove         = rtl8366rb_remove,
+};
+
+static int __init rtl8366rb_module_init(void)
+{
+       return platform_driver_register(&rtl8366rb_driver);
+}
+module_init(rtl8366rb_module_init);
+
+static void __exit rtl8366rb_module_exit(void)
+{
+       platform_driver_unregister(&rtl8366rb_driver);
+}
+module_exit(rtl8366rb_module_exit);
+
+MODULE_DESCRIPTION(RTL8366RB_DRIVER_DESC);
+MODULE_VERSION(RTL8366RB_DRIVER_VER);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
+MODULE_AUTHOR("Roman Yeryomin <roman@advem.lv>");
+MODULE_AUTHOR("Colin Leitner <colin.leitner@googlemail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8366RB_DRIVER_NAME);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8366s.c b/target/linux/generic/files-4.9/drivers/net/phy/rtl8366s.c
new file mode 100644 (file)
index 0000000..3f458f9
--- /dev/null
@@ -0,0 +1,1320 @@
+/*
+ * Platform driver for the Realtek RTL8366S ethernet switch
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8366.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8366S_DRIVER_DESC   "Realtek RTL8366S ethernet switch driver"
+#define RTL8366S_DRIVER_VER    "0.2.2"
+
+#define RTL8366S_PHY_NO_MAX    4
+#define RTL8366S_PHY_PAGE_MAX  7
+#define RTL8366S_PHY_ADDR_MAX  31
+
+/* Switch Global Configuration register */
+#define RTL8366S_SGCR                          0x0000
+#define RTL8366S_SGCR_EN_BC_STORM_CTRL         BIT(0)
+#define RTL8366S_SGCR_MAX_LENGTH(_x)           (_x << 4)
+#define RTL8366S_SGCR_MAX_LENGTH_MASK          RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_MAX_LENGTH_1522          RTL8366S_SGCR_MAX_LENGTH(0x0)
+#define RTL8366S_SGCR_MAX_LENGTH_1536          RTL8366S_SGCR_MAX_LENGTH(0x1)
+#define RTL8366S_SGCR_MAX_LENGTH_1552          RTL8366S_SGCR_MAX_LENGTH(0x2)
+#define RTL8366S_SGCR_MAX_LENGTH_16000         RTL8366S_SGCR_MAX_LENGTH(0x3)
+#define RTL8366S_SGCR_EN_VLAN                  BIT(13)
+
+/* Port Enable Control register */
+#define RTL8366S_PECR                          0x0001
+
+/* Green Ethernet Feature (based on GPL_BELKIN_F5D8235-4_v1000 v1.01.24) */
+#define RTL8366S_GREEN_ETHERNET_CTRL_REG       0x000a
+#define RTL8366S_GREEN_ETHERNET_CTRL_MASK      0x0018
+#define RTL8366S_GREEN_ETHERNET_TX_BIT         (1 << 3)
+#define RTL8366S_GREEN_ETHERNET_RX_BIT         (1 << 4)
+
+/* Switch Security Control registers */
+#define RTL8366S_SSCR0                         0x0002
+#define RTL8366S_SSCR1                         0x0003
+#define RTL8366S_SSCR2                         0x0004
+#define RTL8366S_SSCR2_DROP_UNKNOWN_DA         BIT(0)
+
+#define RTL8366S_RESET_CTRL_REG                        0x0100
+#define RTL8366S_CHIP_CTRL_RESET_HW            1
+#define RTL8366S_CHIP_CTRL_RESET_SW            (1 << 1)
+
+#define RTL8366S_CHIP_VERSION_CTRL_REG         0x0104
+#define RTL8366S_CHIP_VERSION_MASK             0xf
+#define RTL8366S_CHIP_ID_REG                   0x0105
+#define RTL8366S_CHIP_ID_8366                  0x8366
+
+/* PHY registers control */
+#define RTL8366S_PHY_ACCESS_CTRL_REG           0x8028
+#define RTL8366S_PHY_ACCESS_DATA_REG           0x8029
+
+#define RTL8366S_PHY_CTRL_READ                 1
+#define RTL8366S_PHY_CTRL_WRITE                        0
+
+#define RTL8366S_PHY_REG_MASK                  0x1f
+#define RTL8366S_PHY_PAGE_OFFSET               5
+#define RTL8366S_PHY_PAGE_MASK                 (0x7 << 5)
+#define RTL8366S_PHY_NO_OFFSET                 9
+#define RTL8366S_PHY_NO_MASK                   (0x1f << 9)
+
+/* Green Ethernet Feature for PHY ports */
+#define RTL8366S_PHY_POWER_SAVING_CTRL_REG     12
+#define RTL8366S_PHY_POWER_SAVING_MASK         0x1000
+
+/* LED control registers */
+#define RTL8366S_LED_BLINKRATE_REG             0x0420
+#define RTL8366S_LED_BLINKRATE_BIT             0
+#define RTL8366S_LED_BLINKRATE_MASK            0x0007
+
+#define RTL8366S_LED_CTRL_REG                  0x0421
+#define RTL8366S_LED_0_1_CTRL_REG              0x0422
+#define RTL8366S_LED_2_3_CTRL_REG              0x0423
+
+#define RTL8366S_MIB_COUNT                     33
+#define RTL8366S_GLOBAL_MIB_COUNT              1
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET       0x0040
+#define RTL8366S_MIB_COUNTER_BASE              0x1000
+#define RTL8366S_MIB_COUNTER_PORT_OFFSET2      0x0008
+#define RTL8366S_MIB_COUNTER_BASE2             0x1180
+#define RTL8366S_MIB_CTRL_REG                  0x11F0
+#define RTL8366S_MIB_CTRL_USER_MASK            0x01FF
+#define RTL8366S_MIB_CTRL_BUSY_MASK            0x0001
+#define RTL8366S_MIB_CTRL_RESET_MASK           0x0002
+
+#define RTL8366S_MIB_CTRL_GLOBAL_RESET_MASK    0x0004
+#define RTL8366S_MIB_CTRL_PORT_RESET_BIT       0x0003
+#define RTL8366S_MIB_CTRL_PORT_RESET_MASK      0x01FC
+
+
+#define RTL8366S_PORT_VLAN_CTRL_BASE           0x0058
+#define RTL8366S_PORT_VLAN_CTRL_REG(_p)  \
+               (RTL8366S_PORT_VLAN_CTRL_BASE + (_p) / 4)
+#define RTL8366S_PORT_VLAN_CTRL_MASK           0xf
+#define RTL8366S_PORT_VLAN_CTRL_SHIFT(_p)      (4 * ((_p) % 4))
+
+
+#define RTL8366S_VLAN_TABLE_READ_BASE          0x018B
+#define RTL8366S_VLAN_TABLE_WRITE_BASE         0x0185
+
+#define RTL8366S_VLAN_TB_CTRL_REG              0x010F
+
+#define RTL8366S_TABLE_ACCESS_CTRL_REG         0x0180
+#define RTL8366S_TABLE_VLAN_READ_CTRL          0x0E01
+#define RTL8366S_TABLE_VLAN_WRITE_CTRL         0x0F01
+
+#define RTL8366S_VLAN_MC_BASE(_x)              (0x0016 + (_x) * 2)
+
+#define RTL8366S_VLAN_MEMBERINGRESS_REG                0x0379
+
+#define RTL8366S_PORT_LINK_STATUS_BASE         0x0060
+#define RTL8366S_PORT_STATUS_SPEED_MASK                0x0003
+#define RTL8366S_PORT_STATUS_DUPLEX_MASK       0x0004
+#define RTL8366S_PORT_STATUS_LINK_MASK         0x0010
+#define RTL8366S_PORT_STATUS_TXPAUSE_MASK      0x0020
+#define RTL8366S_PORT_STATUS_RXPAUSE_MASK      0x0040
+#define RTL8366S_PORT_STATUS_AN_MASK           0x0080
+
+
+#define RTL8366S_PORT_NUM_CPU          5
+#define RTL8366S_NUM_PORTS             6
+#define RTL8366S_NUM_VLANS             16
+#define RTL8366S_NUM_LEDGROUPS         4
+#define RTL8366S_NUM_VIDS              4096
+#define RTL8366S_PRIORITYMAX           7
+#define RTL8366S_FIDMAX                        7
+
+
+#define RTL8366S_PORT_1                        (1 << 0) /* In userspace port 0 */
+#define RTL8366S_PORT_2                        (1 << 1) /* In userspace port 1 */
+#define RTL8366S_PORT_3                        (1 << 2) /* In userspace port 2 */
+#define RTL8366S_PORT_4                        (1 << 3) /* In userspace port 3 */
+
+#define RTL8366S_PORT_UNKNOWN          (1 << 4) /* No known connection */
+#define RTL8366S_PORT_CPU              (1 << 5) /* CPU port */
+
+#define RTL8366S_PORT_ALL              (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4 |      \
+                                        RTL8366S_PORT_UNKNOWN | \
+                                        RTL8366S_PORT_CPU)
+
+#define RTL8366S_PORT_ALL_BUT_CPU      (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4 |      \
+                                        RTL8366S_PORT_UNKNOWN)
+
+#define RTL8366S_PORT_ALL_EXTERNAL     (RTL8366S_PORT_1 |      \
+                                        RTL8366S_PORT_2 |      \
+                                        RTL8366S_PORT_3 |      \
+                                        RTL8366S_PORT_4)
+
+#define RTL8366S_PORT_ALL_INTERNAL     (RTL8366S_PORT_UNKNOWN | \
+                                        RTL8366S_PORT_CPU)
+
+#define RTL8366S_VLAN_VID_MASK         0xfff
+#define RTL8366S_VLAN_PRIORITY_SHIFT   12
+#define RTL8366S_VLAN_PRIORITY_MASK    0x7
+#define RTL8366S_VLAN_MEMBER_MASK      0x3f
+#define RTL8366S_VLAN_UNTAG_SHIFT      6
+#define RTL8366S_VLAN_UNTAG_MASK       0x3f
+#define RTL8366S_VLAN_FID_SHIFT                12
+#define RTL8366S_VLAN_FID_MASK         0x7
+
+#define RTL8366S_MIB_RXB_ID            0       /* IfInOctets */
+#define RTL8366S_MIB_TXB_ID            20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8366s_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 4, "EtherStatsOctets"                          },
+       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 10, 2, "EtherFragments"                            },
+       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 24, 2, "EtherOversizeStats"                        },
+       { 0, 26, 2, "EtherStatsJabbers"                         },
+       { 0, 28, 2, "IfInUcastPkts"                             },
+       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
+       { 0, 34, 2, "EtherStatsDropEvents"                      },
+       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
+       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
+       { 0, 40, 2, "Dot3InPauseFrames"                         },
+       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 44, 4, "IfOutOctets"                               },
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+
+       /*
+        * The following counters are accessible at a different
+        * base address.
+        */
+       { 1,  0, 2, "Dot1dTpPortInDiscards"                     },
+       { 1,  2, 2, "IfOutUcastPkts"                            },
+       { 1,  4, 2, "IfOutMulticastPkts"                        },
+       { 1,  6, 2, "IfOutBroadcastPkts"                        },
+};
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static int rtl8366s_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       u32 data;
+
+       rtl8366_smi_write_reg_noack(smi, RTL8366S_RESET_CTRL_REG,
+                                   RTL8366S_CHIP_CTRL_RESET_HW);
+       do {
+               msleep(1);
+               if (rtl8366_smi_read_reg(smi, RTL8366S_RESET_CTRL_REG, &data))
+                       return -EIO;
+
+               if (!(data & RTL8366S_CHIP_CTRL_RESET_HW))
+                       break;
+       } while (--timeout);
+
+       if (!timeout) {
+               printk("Timeout waiting for the switch to reset\n");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_read_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_no, u32 page, u32 addr, u32 *data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366S_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366S_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366S_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
+                                   RTL8366S_PHY_CTRL_READ);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
+             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
+             (addr & RTL8366S_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, 0);
+       if (ret)
+               return ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_PHY_ACCESS_DATA_REG, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366s_write_phy_reg(struct rtl8366_smi *smi,
+                                 u32 phy_no, u32 page, u32 addr, u32 data)
+{
+       u32 reg;
+       int ret;
+
+       if (phy_no > RTL8366S_PHY_NO_MAX)
+               return -EINVAL;
+
+       if (page > RTL8366S_PHY_PAGE_MAX)
+               return -EINVAL;
+
+       if (addr > RTL8366S_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
+                                   RTL8366S_PHY_CTRL_WRITE);
+       if (ret)
+               return ret;
+
+       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
+             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
+             (addr & RTL8366S_PHY_REG_MASK);
+
+       ret = rtl8366_smi_write_reg(smi, reg, data);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtl8366s_set_green_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+       u32 phyData;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       if (enable)
+               phyData |= RTL8366S_PHY_POWER_SAVING_MASK;
+       else
+               phyData &= ~RTL8366S_PHY_POWER_SAVING_MASK;
+
+       err = rtl8366s_write_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, phyData);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8366s_set_green(struct rtl8366_smi *smi, int enable)
+{
+       int err;
+       unsigned i;
+       u32 data = 0;
+
+       if (!enable) {
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       rtl8366s_set_green_port(smi, i, 0);
+               }
+       }
+
+       if (enable)
+               data = (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT);
+
+       REG_RMW(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, RTL8366S_GREEN_ETHERNET_CTRL_MASK, data);
+
+       return 0;
+}
+
+static int rtl8366s_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8366_platform_data *pdata;
+       int err;
+       unsigned i;
+#ifdef CONFIG_OF
+       struct device_node *np;
+       unsigned num_initvals;
+       const __be32 *paddr;
+#endif
+
+       pdata = smi->parent->platform_data;
+       if (pdata && pdata->num_initvals && pdata->initvals) {
+               dev_info(smi->parent, "applying initvals\n");
+               for (i = 0; i < pdata->num_initvals; i++)
+                       REG_WR(smi, pdata->initvals[i].reg,
+                              pdata->initvals[i].val);
+       }
+
+#ifdef CONFIG_OF
+       np = smi->parent->of_node;
+
+       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
+       if (paddr) {
+               dev_info(smi->parent, "applying initvals from DTS\n");
+
+               if (num_initvals < (2 * sizeof(*paddr)))
+                       return -EINVAL;
+
+               num_initvals /= sizeof(*paddr);
+
+               for (i = 0; i < num_initvals - 1; i += 2) {
+                       u32 reg = be32_to_cpup(paddr + i);
+                       u32 val = be32_to_cpup(paddr + i + 1);
+
+                       REG_WR(smi, reg, val);
+               }
+       }
+
+       if (of_property_read_bool(np, "realtek,green-ethernet-features")) {
+               dev_info(smi->parent, "activating Green Ethernet features\n");
+
+               err = rtl8366s_set_green(smi, 1);
+               if (err)
+                       return err;
+
+               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
+                       err = rtl8366s_set_green_port(smi, i, 1);
+                       if (err)
+                               return err;
+               }
+       }
+#endif
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8366S_SGCR, RTL8366S_SGCR_MAX_LENGTH_MASK,
+               RTL8366S_SGCR_MAX_LENGTH_1536);
+
+       /* enable learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, 0);
+
+       /* enable auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, 0);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8366S_VLAN_MEMBERINGRESS_REG, RTL8366S_PORT_ALL);
+
+       /* don't drop packets whose DA has not been learned */
+       REG_RMW(smi, RTL8366S_SSCR2, RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
+
+       return 0;
+}
+
+static int rtl8366_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val)
+{
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8366S_NUM_PORTS || counter >= RTL8366S_MIB_COUNT)
+               return -EINVAL;
+
+       switch (rtl8366s_mib_counters[counter].base) {
+       case 0:
+               addr = RTL8366S_MIB_COUNTER_BASE +
+                      RTL8366S_MIB_COUNTER_PORT_OFFSET * port;
+               break;
+
+       case 1:
+               addr = RTL8366S_MIB_COUNTER_BASE2 +
+                       RTL8366S_MIB_COUNTER_PORT_OFFSET2 * port;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       addr += rtl8366s_mib_counters[counter].offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       data = 0; /* writing data will be discard by ASIC */
+       err = rtl8366_smi_write_reg(smi, addr, data);
+       if (err)
+               return err;
+
+       /* read MIB control register */
+       err =  rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       if (data & RTL8366S_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8366S_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       mibvalue = 0;
+       for (i = rtl8366s_mib_counters[counter].length; i > 0; i--) {
+               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
+               if (err)
+                       return err;
+
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8366s_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8366S_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE,
+                                   vid & RTL8366S_VLAN_VID_MASK);
+       if (err)
+               return err;
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366S_TABLE_VLAN_READ_CTRL);
+       if (err)
+               return err;
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366S_VLAN_TABLE_READ_BASE + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlan4k->vid = vid;
+       vlan4k->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+                       RTL8366S_VLAN_UNTAG_MASK;
+       vlan4k->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+       vlan4k->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+                       RTL8366S_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8366S_NUM_VIDS ||
+           vlan4k->member > RTL8366S_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8366S_VLAN_UNTAG_MASK ||
+           vlan4k->fid > RTL8366S_FIDMAX)
+               return -EINVAL;
+
+       data[0] = vlan4k->vid & RTL8366S_VLAN_VID_MASK;
+       data[1] = (vlan4k->member & RTL8366S_VLAN_MEMBER_MASK) |
+                 ((vlan4k->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+                       RTL8366S_VLAN_UNTAG_SHIFT) |
+                 ((vlan4k->fid & RTL8366S_VLAN_FID_MASK) <<
+                       RTL8366S_VLAN_FID_SHIFT);
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366S_VLAN_TABLE_WRITE_BASE + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       /* write table access control word */
+       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
+                                   RTL8366S_TABLE_VLAN_WRITE_CTRL);
+
+       return err;
+}
+
+static int rtl8366s_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8366S_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_read_reg(smi,
+                                          RTL8366S_VLAN_MC_BASE(index) + i,
+                                          &data[i]);
+               if (err)
+                       return err;
+       }
+
+       vlanmc->vid = data[0] & RTL8366S_VLAN_VID_MASK;
+       vlanmc->priority = (data[0] >> RTL8366S_VLAN_PRIORITY_SHIFT) &
+                          RTL8366S_VLAN_PRIORITY_MASK;
+       vlanmc->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
+                       RTL8366S_VLAN_UNTAG_MASK;
+       vlanmc->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
+                     RTL8366S_VLAN_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[2];
+       int err;
+       int i;
+
+       if (index >= RTL8366S_NUM_VLANS ||
+           vlanmc->vid >= RTL8366S_NUM_VIDS ||
+           vlanmc->priority > RTL8366S_PRIORITYMAX ||
+           vlanmc->member > RTL8366S_VLAN_MEMBER_MASK ||
+           vlanmc->untag > RTL8366S_VLAN_UNTAG_MASK ||
+           vlanmc->fid > RTL8366S_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->vid & RTL8366S_VLAN_VID_MASK) |
+                 ((vlanmc->priority & RTL8366S_VLAN_PRIORITY_MASK) <<
+                       RTL8366S_VLAN_PRIORITY_SHIFT);
+       data[1] = (vlanmc->member & RTL8366S_VLAN_MEMBER_MASK) |
+                 ((vlanmc->untag & RTL8366S_VLAN_UNTAG_MASK) <<
+                       RTL8366S_VLAN_UNTAG_SHIFT) |
+                 ((vlanmc->fid & RTL8366S_VLAN_FID_MASK) <<
+                       RTL8366S_VLAN_FID_SHIFT);
+
+       for (i = 0; i < 2; i++) {
+               err = rtl8366_smi_write_reg(smi,
+                                           RTL8366S_VLAN_MC_BASE(index) + i,
+                                           data[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
+                                  &data);
+       if (err)
+               return err;
+
+       *val = (data >> RTL8366S_PORT_VLAN_CTRL_SHIFT(port)) &
+              RTL8366S_PORT_VLAN_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8366s_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8366S_NUM_PORTS || index >= RTL8366S_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
+                               RTL8366S_PORT_VLAN_CTRL_MASK <<
+                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port),
+                               (index & RTL8366S_PORT_VLAN_CTRL_MASK) <<
+                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port));
+}
+
+static int rtl8366s_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR, RTL8366S_SGCR_EN_VLAN,
+                               (enable) ? RTL8366S_SGCR_EN_VLAN : 0);
+}
+
+static int rtl8366s_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_VLAN_TB_CTRL_REG,
+                               1, (enable) ? 1 : 0);
+}
+
+static int rtl8366s_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8366S_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8366S_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8366s_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8366S_PECR, (1 << port),
+                               (enable) ? 0 : (1 << port));
+}
+
+static int rtl8366s_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG, 0, (1 << 2));
+}
+
+static int rtl8366s_sw_get_blinkrate(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_LED_BLINKRATE_REG, &data);
+
+       val->value.i = (data & (RTL8366S_LED_BLINKRATE_MASK));
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_blinkrate(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->value.i >= 6)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_LED_BLINKRATE_REG,
+                               RTL8366S_LED_BLINKRATE_MASK,
+                               val->value.i);
+}
+
+static int rtl8366s_sw_get_max_length(struct switch_dev *dev,
+                                       const struct switch_attr *attr,
+                                       struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_SGCR, &data);
+
+       val->value.i = ((data & (RTL8366S_SGCR_MAX_LENGTH_MASK)) >> 4);
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_max_length(struct switch_dev *dev,
+                                       const struct switch_attr *attr,
+                                       struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       char length_code;
+
+       switch (val->value.i) {
+               case 0:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1522;
+                       break;
+               case 1:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1536;
+                       break;
+               case 2:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_1552;
+                       break;
+               case 3:
+                       length_code = RTL8366S_SGCR_MAX_LENGTH_16000;
+                       break;
+               default:
+                       return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR,
+                       RTL8366S_SGCR_MAX_LENGTH_MASK,
+                       length_code);
+}
+
+static int rtl8366s_sw_get_learning_enable(struct switch_dev *dev,
+                                          const struct switch_attr *attr,
+                                          struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi,RTL8366S_SSCR0, &data);
+       val->value.i = !data;
+
+       return 0;
+}
+
+
+static int rtl8366s_sw_set_learning_enable(struct switch_dev *dev,
+                                          const struct switch_attr *attr,
+                                          struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 portmask = 0;
+       int err = 0;
+
+       if (!val->value.i)
+               portmask = RTL8366S_PORT_ALL;
+
+       /* set learning for all ports */
+       REG_WR(smi, RTL8366S_SSCR0, portmask);
+
+       /* set auto ageing for all ports */
+       REG_WR(smi, RTL8366S_SSCR1, portmask);
+
+       return 0;
+}
+
+static int rtl8366s_sw_get_green(struct switch_dev *dev,
+                             const struct switch_attr *attr,
+                             struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       int err;
+
+       err = rtl8366_smi_read_reg(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, &data);
+       if (err)
+               return err;
+
+       val->value.i = ((data & (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT)) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green(struct switch_dev *dev,
+                                const struct switch_attr *attr,
+                                struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366s_set_green(smi, val->value.i);
+}
+
+static int rtl8366s_sw_get_port_link(struct switch_dev *dev,
+                                    int port,
+                                    struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_PORT_LINK_STATUS_BASE + (port / 2),
+                            &data);
+
+       if (port % 2)
+               data = data >> 8;
+
+       link->link = !!(data & RTL8366S_PORT_STATUS_LINK_MASK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8366S_PORT_STATUS_DUPLEX_MASK);
+       link->rx_flow = !!(data & RTL8366S_PORT_STATUS_RXPAUSE_MASK);
+       link->tx_flow = !!(data & RTL8366S_PORT_STATUS_TXPAUSE_MASK);
+       link->aneg = !!(data & RTL8366S_PORT_STATUS_AN_MASK);
+
+       speed = (data & RTL8366S_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+       u32 mask;
+       u32 reg;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS ||
+           (1 << val->port_vlan) == RTL8366S_PORT_UNKNOWN)
+               return -EINVAL;
+
+       if (val->port_vlan == RTL8366S_PORT_NUM_CPU) {
+               reg = RTL8366S_LED_BLINKRATE_REG;
+               mask = 0xF << 4;
+               data = val->value.i << 4;
+       } else {
+               reg = RTL8366S_LED_CTRL_REG;
+               mask = 0xF << (val->port_vlan * 4),
+               data = val->value.i << (val->port_vlan * 4);
+       }
+
+       return rtl8366_smi_rmwr(smi, reg, mask, data);
+}
+
+static int rtl8366s_sw_get_port_led(struct switch_dev *dev,
+                                   const struct switch_attr *attr,
+                                   struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+
+       if (val->port_vlan >= RTL8366S_NUM_LEDGROUPS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8366S_LED_CTRL_REG, &data);
+       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
+
+       return 0;
+}
+
+static int rtl8366s_sw_get_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int err;
+       u32 phyData;
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+       err = rtl8366s_read_phy_reg(smi, val->port_vlan, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
+       if (err)
+               return err;
+
+       val->value.i = ((phyData & RTL8366S_PHY_POWER_SAVING_MASK) != 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int rtl8366s_sw_set_green_port(struct switch_dev *dev,
+                                     const struct switch_attr *attr,
+                                     struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       return rtl8366s_set_green_port(smi, val->port_vlan, val->value.i);
+}
+
+static int rtl8366s_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       if (val->port_vlan >= RTL8366S_NUM_PORTS)
+               return -EINVAL;
+
+
+       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG,
+                               0, (1 << (val->port_vlan + 3)));
+}
+
+static int rtl8366s_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8366S_MIB_TXB_ID, RTL8366S_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8366s_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_learning",
+               .description = "Enable learning, enable aging",
+               .set = rtl8366s_sw_set_learning_enable,
+               .get = rtl8366s_sw_get_learning_enable,
+               .max = 1,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8366s_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "blinkrate",
+               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
+               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
+               .set = rtl8366s_sw_set_blinkrate,
+               .get = rtl8366s_sw_get_blinkrate,
+               .max = 5
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+               " (0 = 1522, 1 = 1536, 2 = 1552, 3 = 16000 (9216?))",
+               .set = rtl8366s_sw_set_max_length,
+               .get = rtl8366s_sw_get_max_length,
+               .max = 3,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_mode",
+               .description = "Get/Set the router green feature",
+               .set = rtl8366s_sw_set_green,
+               .get = rtl8366s_sw_get_green,
+               .max = 1,
+       },
+};
+
+static struct switch_attr rtl8366s_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8366s_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "led",
+               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
+               .max = 15,
+               .set = rtl8366s_sw_set_port_led,
+               .get = rtl8366s_sw_get_port_led,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "green_port",
+               .description = "Get/Set port green feature (0 - 1)",
+               .max = 1,
+               .set = rtl8366s_sw_set_green_port,
+               .get = rtl8366s_sw_get_green_port,
+       },
+};
+
+static struct switch_attr rtl8366s_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8366S_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8366_ops = {
+       .attr_global = {
+               .attr = rtl8366s_globals,
+               .n_attr = ARRAY_SIZE(rtl8366s_globals),
+       },
+       .attr_port = {
+               .attr = rtl8366s_port,
+               .n_attr = ARRAY_SIZE(rtl8366s_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8366s_vlan,
+               .n_attr = ARRAY_SIZE(rtl8366s_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8366s_sw_get_port_link,
+       .get_port_stats = rtl8366s_sw_get_port_stats,
+};
+
+static int rtl8366s_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8366S";
+       dev->cpu_port = RTL8366S_PORT_NUM_CPU;
+       dev->ports = RTL8366S_NUM_PORTS;
+       dev->vlans = RTL8366S_NUM_VIDS;
+       dev->ops = &rtl8366_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8366s_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8366s_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8366s_read_phy_reg(smi, addr, 0, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8366s_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8366s_write_phy_reg(smi, addr, 0, reg, val);
+       /* flush write */
+       (void) rtl8366s_read_phy_reg(smi, addr, 0, reg, &t);
+
+       return err;
+}
+
+static int rtl8366s_detect(struct rtl8366_smi *smi)
+{
+       u32 chip_id = 0;
+       u32 chip_ver = 0;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_ID_REG, &chip_id);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip id\n");
+               return ret;
+       }
+
+       switch (chip_id) {
+       case RTL8366S_CHIP_ID_8366:
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_VERSION_CTRL_REG,
+                                  &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
+                chip_id, chip_ver & RTL8366S_CHIP_VERSION_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8366s_smi_ops = {
+       .detect         = rtl8366s_detect,
+       .reset_chip     = rtl8366s_reset_chip,
+       .setup          = rtl8366s_setup,
+
+       .mii_read       = rtl8366s_mii_read,
+       .mii_write      = rtl8366s_mii_write,
+
+       .get_vlan_mc    = rtl8366s_get_vlan_mc,
+       .set_vlan_mc    = rtl8366s_set_vlan_mc,
+       .get_vlan_4k    = rtl8366s_get_vlan_4k,
+       .set_vlan_4k    = rtl8366s_set_vlan_4k,
+       .get_mc_index   = rtl8366s_get_mc_index,
+       .set_mc_index   = rtl8366s_set_mc_index,
+       .get_mib_counter = rtl8366_get_mib_counter,
+       .is_vlan_valid  = rtl8366s_is_vlan_valid,
+       .enable_vlan    = rtl8366s_enable_vlan,
+       .enable_vlan4k  = rtl8366s_enable_vlan4k,
+       .enable_port    = rtl8366s_enable_port,
+};
+
+static int rtl8366s_probe(struct platform_device *pdev)
+{
+       static int rtl8366_smi_version_printed;
+       struct rtl8366_smi *smi;
+       int err;
+
+       if (!rtl8366_smi_version_printed++)
+               printk(KERN_NOTICE RTL8366S_DRIVER_DESC
+                      " version " RTL8366S_DRIVER_VER"\n");
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 10;
+       smi->cmd_read = 0xa9;
+       smi->cmd_write = 0xa8;
+       smi->ops = &rtl8366s_smi_ops;
+       smi->cpu_port = RTL8366S_PORT_NUM_CPU;
+       smi->num_ports = RTL8366S_NUM_PORTS;
+       smi->num_vlan_mc = RTL8366S_NUM_VLANS;
+       smi->mib_counters = rtl8366s_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8366s_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8366s_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8366s_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8366s_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8366s_match[] = {
+       { .compatible = "realtek,rtl8366s" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8366s_match);
+#endif
+
+static struct platform_driver rtl8366s_driver = {
+       .driver = {
+               .name           = RTL8366S_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8366s_match),
+#endif
+       },
+       .probe          = rtl8366s_probe,
+       .remove         = rtl8366s_remove,
+};
+
+static int __init rtl8366s_module_init(void)
+{
+       return platform_driver_register(&rtl8366s_driver);
+}
+module_init(rtl8366s_module_init);
+
+static void __exit rtl8366s_module_exit(void)
+{
+       platform_driver_unregister(&rtl8366s_driver);
+}
+module_exit(rtl8366s_module_exit);
+
+MODULE_DESCRIPTION(RTL8366S_DRIVER_DESC);
+MODULE_VERSION(RTL8366S_DRIVER_VER);
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8366S_DRIVER_NAME);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8367.c b/target/linux/generic/files-4.9/drivers/net/phy/rtl8367.c
new file mode 100644 (file)
index 0000000..9549961
--- /dev/null
@@ -0,0 +1,1846 @@
+/*
+ * Platform driver for the Realtek RTL8367R/M ethernet switches
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367_RESET_DELAY    1000    /* msecs*/
+
+#define RTL8367_PHY_ADDR_MAX   8
+#define RTL8367_PHY_REG_MAX    31
+
+#define RTL8367_VID_MASK       0xffff
+#define RTL8367_FID_MASK       0xfff
+#define RTL8367_UNTAG_MASK     0xffff
+#define RTL8367_MEMBER_MASK    0xffff
+
+#define RTL8367_PORT_CFG_REG(_p)               (0x000e + 0x20 * (_p))
+#define   RTL8367_PORT_CFG_EGRESS_MODE_SHIFT   4
+#define   RTL8367_PORT_CFG_EGRESS_MODE_MASK    0x3
+#define   RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL        0
+#define   RTL8367_PORT_CFG_EGRESS_MODE_KEEP    1
+#define   RTL8367_PORT_CFG_EGRESS_MODE_PRI     2
+#define   RTL8367_PORT_CFG_EGRESS_MODE_REAL    3
+
+#define RTL8367_BYPASS_LINE_RATE_REG           0x03f7
+
+#define RTL8367_TA_CTRL_REG                    0x0500
+#define   RTL8367_TA_CTRL_STATUS               BIT(12)
+#define   RTL8367_TA_CTRL_METHOD               BIT(5)
+#define   RTL8367_TA_CTRL_CMD_SHIFT            4
+#define   RTL8367_TA_CTRL_CMD_READ             0
+#define   RTL8367_TA_CTRL_CMD_WRITE            1
+#define   RTL8367_TA_CTRL_TABLE_SHIFT          0
+#define   RTL8367_TA_CTRL_TABLE_ACLRULE                1
+#define   RTL8367_TA_CTRL_TABLE_ACLACT         2
+#define   RTL8367_TA_CTRL_TABLE_CVLAN          3
+#define   RTL8367_TA_CTRL_TABLE_L2             4
+#define   RTL8367_TA_CTRL_CVLAN_READ \
+               ((RTL8367_TA_CTRL_CMD_READ << RTL8367_TA_CTRL_CMD_SHIFT) | \
+                RTL8367_TA_CTRL_TABLE_CVLAN)
+#define   RTL8367_TA_CTRL_CVLAN_WRITE \
+               ((RTL8367_TA_CTRL_CMD_WRITE << RTL8367_TA_CTRL_CMD_SHIFT) | \
+                RTL8367_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367_TA_ADDR_REG                    0x0501
+#define   RTL8367_TA_ADDR_MASK                 0x3fff
+
+#define RTL8367_TA_DATA_REG(_x)                        (0x0503 + (_x))
+#define   RTL8367_TA_VLAN_DATA_SIZE            4
+#define   RTL8367_TA_VLAN_VID_MASK             RTL8367_VID_MASK
+#define   RTL8367_TA_VLAN_MEMBER_SHIFT         0
+#define   RTL8367_TA_VLAN_MEMBER_MASK          RTL8367_MEMBER_MASK
+#define   RTL8367_TA_VLAN_FID_SHIFT            0
+#define   RTL8367_TA_VLAN_FID_MASK             RTL8367_FID_MASK
+#define   RTL8367_TA_VLAN_UNTAG1_SHIFT         14
+#define   RTL8367_TA_VLAN_UNTAG1_MASK          0x3
+#define   RTL8367_TA_VLAN_UNTAG2_SHIFT         0
+#define   RTL8367_TA_VLAN_UNTAG2_MASK          0x3fff
+
+#define RTL8367_VLAN_PVID_CTRL_REG(_p)         (0x0700 + (_p) / 2)
+#define RTL8367_VLAN_PVID_CTRL_MASK            0x1f
+#define RTL8367_VLAN_PVID_CTRL_SHIFT(_p)       (8 * ((_p) % 2))
+
+#define RTL8367_VLAN_MC_BASE(_x)               (0x0728 + (_x) * 4)
+#define   RTL8367_VLAN_MC_DATA_SIZE            4
+#define   RTL8367_VLAN_MC_MEMBER_SHIFT         0
+#define   RTL8367_VLAN_MC_MEMBER_MASK          RTL8367_MEMBER_MASK
+#define   RTL8367_VLAN_MC_FID_SHIFT            0
+#define   RTL8367_VLAN_MC_FID_MASK             RTL8367_FID_MASK
+#define   RTL8367_VLAN_MC_EVID_SHIFT           0
+#define   RTL8367_VLAN_MC_EVID_MASK            RTL8367_VID_MASK
+
+#define RTL8367_VLAN_CTRL_REG                  0x07a8
+#define   RTL8367_VLAN_CTRL_ENABLE             BIT(0)
+
+#define RTL8367_VLAN_INGRESS_REG               0x07a9
+
+#define RTL8367_PORT_ISOLATION_REG(_p)         (0x08a2 + (_p))
+
+#define RTL8367_MIB_COUNTER_REG(_x)            (0x1000 + (_x))
+
+#define RTL8367_MIB_ADDRESS_REG                        0x1004
+
+#define RTL8367_MIB_CTRL_REG(_x)               (0x1005 + (_x))
+#define   RTL8367_MIB_CTRL_GLOBAL_RESET_MASK   BIT(11)
+#define   RTL8367_MIB_CTRL_QM_RESET_MASK       BIT(10)
+#define   RTL8367_MIB_CTRL_PORT_RESET_MASK(_p) BIT(2 + (_p))
+#define   RTL8367_MIB_CTRL_RESET_MASK          BIT(1)
+#define   RTL8367_MIB_CTRL_BUSY_MASK           BIT(0)
+
+#define RTL8367_MIB_COUNT                      36
+#define RTL8367_MIB_COUNTER_PORT_OFFSET                0x0050
+
+#define RTL8367_SWC0_REG                       0x1200
+#define   RTL8367_SWC0_MAX_LENGTH_SHIFT                13
+#define   RTL8367_SWC0_MAX_LENGTH(_x)          ((_x) << 13)
+#define   RTL8367_SWC0_MAX_LENGTH_MASK         RTL8367_SWC0_MAX_LENGTH(0x3)
+#define   RTL8367_SWC0_MAX_LENGTH_1522         RTL8367_SWC0_MAX_LENGTH(0)
+#define   RTL8367_SWC0_MAX_LENGTH_1536         RTL8367_SWC0_MAX_LENGTH(1)
+#define   RTL8367_SWC0_MAX_LENGTH_1552         RTL8367_SWC0_MAX_LENGTH(2)
+#define   RTL8367_SWC0_MAX_LENGTH_16000                RTL8367_SWC0_MAX_LENGTH(3)
+
+#define RTL8367_CHIP_NUMBER_REG                        0x1300
+
+#define RTL8367_CHIP_VER_REG                   0x1301
+#define   RTL8367_CHIP_VER_RLVID_SHIFT         12
+#define   RTL8367_CHIP_VER_RLVID_MASK          0xf
+#define   RTL8367_CHIP_VER_MCID_SHIFT          8
+#define   RTL8367_CHIP_VER_MCID_MASK           0xf
+#define   RTL8367_CHIP_VER_BOID_SHIFT          4
+#define   RTL8367_CHIP_VER_BOID_MASK           0xf
+
+#define RTL8367_CHIP_MODE_REG                  0x1302
+#define   RTL8367_CHIP_MODE_MASK               0x7
+
+#define RTL8367_CHIP_DEBUG0_REG                        0x1303
+#define   RTL8367_CHIP_DEBUG0_DUMMY0(_x)       BIT(8 + (_x))
+
+#define RTL8367_CHIP_DEBUG1_REG                        0x1304
+
+#define RTL8367_DIS_REG                                0x1305
+#define   RTL8367_DIS_SKIP_MII_RXER(_x)                BIT(12 + (_x))
+#define   RTL8367_DIS_RGMII_SHIFT(_x)          (4 * (_x))
+#define   RTL8367_DIS_RGMII_MASK               0x7
+
+#define RTL8367_EXT_RGMXF_REG(_x)              (0x1306 + (_x))
+#define   RTL8367_EXT_RGMXF_DUMMY0_SHIFT       5
+#define   RTL8367_EXT_RGMXF_DUMMY0_MASK        0x7ff
+#define   RTL8367_EXT_RGMXF_TXDELAY_SHIFT      3
+#define   RTL8367_EXT_RGMXF_TXDELAY_MASK       1
+#define   RTL8367_EXT_RGMXF_RXDELAY_MASK       0x7
+
+#define RTL8367_DI_FORCE_REG(_x)               (0x1310 + (_x))
+#define   RTL8367_DI_FORCE_MODE                        BIT(12)
+#define   RTL8367_DI_FORCE_NWAY                        BIT(7)
+#define   RTL8367_DI_FORCE_TXPAUSE             BIT(6)
+#define   RTL8367_DI_FORCE_RXPAUSE             BIT(5)
+#define   RTL8367_DI_FORCE_LINK                        BIT(4)
+#define   RTL8367_DI_FORCE_DUPLEX              BIT(2)
+#define   RTL8367_DI_FORCE_SPEED_MASK          3
+#define   RTL8367_DI_FORCE_SPEED_10            0
+#define   RTL8367_DI_FORCE_SPEED_100           1
+#define   RTL8367_DI_FORCE_SPEED_1000          2
+
+#define RTL8367_MAC_FORCE_REG(_x)              (0x1312 + (_x))
+
+#define RTL8367_CHIP_RESET_REG                 0x1322
+#define   RTL8367_CHIP_RESET_SW                        BIT(1)
+#define   RTL8367_CHIP_RESET_HW                        BIT(0)
+
+#define RTL8367_PORT_STATUS_REG(_p)            (0x1352 + (_p))
+#define   RTL8367_PORT_STATUS_NWAY             BIT(7)
+#define   RTL8367_PORT_STATUS_TXPAUSE          BIT(6)
+#define   RTL8367_PORT_STATUS_RXPAUSE          BIT(5)
+#define   RTL8367_PORT_STATUS_LINK             BIT(4)
+#define   RTL8367_PORT_STATUS_DUPLEX           BIT(2)
+#define   RTL8367_PORT_STATUS_SPEED_MASK       0x0003
+#define   RTL8367_PORT_STATUS_SPEED_10         0
+#define   RTL8367_PORT_STATUS_SPEED_100                1
+#define   RTL8367_PORT_STATUS_SPEED_1000       2
+
+#define RTL8367_RTL_NO_REG                     0x13c0
+#define   RTL8367_RTL_NO_8367R                 0x3670
+#define   RTL8367_RTL_NO_8367M                 0x3671
+
+#define RTL8367_RTL_VER_REG                    0x13c1
+#define   RTL8367_RTL_VER_MASK                 0xf
+
+#define RTL8367_RTL_MAGIC_ID_REG               0x13c2
+#define   RTL8367_RTL_MAGIC_ID_VAL             0x0249
+
+#define RTL8367_LED_SYS_CONFIG_REG             0x1b00
+#define RTL8367_LED_MODE_REG                   0x1b02
+#define   RTL8367_LED_MODE_RATE_M              0x7
+#define   RTL8367_LED_MODE_RATE_S              1
+
+#define RTL8367_LED_CONFIG_REG                 0x1b03
+#define   RTL8367_LED_CONFIG_DATA_S            12
+#define   RTL8367_LED_CONFIG_DATA_M            0x3
+#define   RTL8367_LED_CONFIG_SEL               BIT(14)
+#define   RTL8367_LED_CONFIG_LED_CFG_M         0xf
+
+#define RTL8367_PARA_LED_IO_EN1_REG            0x1b24
+#define RTL8367_PARA_LED_IO_EN2_REG            0x1b25
+#define   RTL8367_PARA_LED_IO_EN_PMASK         0xff
+
+#define RTL8367_IA_CTRL_REG                    0x1f00
+#define   RTL8367_IA_CTRL_RW(_x)               ((_x) << 1)
+#define   RTL8367_IA_CTRL_RW_READ              RTL8367_IA_CTRL_RW(0)
+#define   RTL8367_IA_CTRL_RW_WRITE             RTL8367_IA_CTRL_RW(1)
+#define   RTL8367_IA_CTRL_CMD_MASK             BIT(0)
+
+#define RTL8367_IA_STATUS_REG                  0x1f01
+#define   RTL8367_IA_STATUS_PHY_BUSY           BIT(2)
+#define   RTL8367_IA_STATUS_SDS_BUSY           BIT(1)
+#define   RTL8367_IA_STATUS_MDX_BUSY           BIT(0)
+
+#define RTL8367_IA_ADDRESS_REG                 0x1f02
+
+#define RTL8367_IA_WRITE_DATA_REG              0x1f03
+#define RTL8367_IA_READ_DATA_REG               0x1f04
+
+#define RTL8367_INTERNAL_PHY_REG(_a, _r)       (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367_CPU_PORT_NUM           9
+#define RTL8367_NUM_PORTS              10
+#define RTL8367_NUM_VLANS              32
+#define RTL8367_NUM_LEDGROUPS          4
+#define RTL8367_NUM_VIDS               4096
+#define RTL8367_PRIORITYMAX            7
+#define RTL8367_FIDMAX                 7
+
+#define RTL8367_PORT_0                 BIT(0)
+#define RTL8367_PORT_1                 BIT(1)
+#define RTL8367_PORT_2                 BIT(2)
+#define RTL8367_PORT_3                 BIT(3)
+#define RTL8367_PORT_4                 BIT(4)
+#define RTL8367_PORT_5                 BIT(5)
+#define RTL8367_PORT_6                 BIT(6)
+#define RTL8367_PORT_7                 BIT(7)
+#define RTL8367_PORT_E1                        BIT(8)  /* external port 1 */
+#define RTL8367_PORT_E0                        BIT(9)  /* external port 0 */
+
+#define RTL8367_PORTS_ALL                                      \
+       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
+        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
+        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1 |    \
+        RTL8367_PORT_E0)
+
+#define RTL8367_PORTS_ALL_BUT_CPU                              \
+       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
+        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
+        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1)
+
+struct rtl8367_initval {
+       u16 reg;
+       u16 val;
+};
+
+#define RTL8367_MIB_RXB_ID             0       /* IfInOctets */
+#define RTL8367_MIB_TXB_ID             20      /* IfOutOctets */
+
+static struct rtl8366_mib_counter rtl8367_mib_counters[] = {
+       { 0,  0, 4, "IfInOctets"                                },
+       { 0,  4, 2, "Dot3StatsFCSErrors"                        },
+       { 0,  6, 2, "Dot3StatsSymbolErrors"                     },
+       { 0,  8, 2, "Dot3InPauseFrames"                         },
+       { 0, 10, 2, "Dot3ControlInUnknownOpcodes"               },
+       { 0, 12, 2, "EtherStatsFragments"                       },
+       { 0, 14, 2, "EtherStatsJabbers"                         },
+       { 0, 16, 2, "IfInUcastPkts"                             },
+       { 0, 18, 2, "EtherStatsDropEvents"                      },
+       { 0, 20, 4, "EtherStatsOctets"                          },
+
+       { 0, 24, 2, "EtherStatsUnderSizePkts"                   },
+       { 0, 26, 2, "EtherOversizeStats"                        },
+       { 0, 28, 2, "EtherStatsPkts64Octets"                    },
+       { 0, 30, 2, "EtherStatsPkts65to127Octets"               },
+       { 0, 32, 2, "EtherStatsPkts128to255Octets"              },
+       { 0, 34, 2, "EtherStatsPkts256to511Octets"              },
+       { 0, 36, 2, "EtherStatsPkts512to1023Octets"             },
+       { 0, 38, 2, "EtherStatsPkts1024to1518Octets"            },
+       { 0, 40, 2, "EtherStatsMulticastPkts"                   },
+       { 0, 42, 2, "EtherStatsBroadcastPkts"                   },
+
+       { 0, 44, 4, "IfOutOctets"                               },
+
+       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
+       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
+       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
+       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
+       { 0, 56, 2, "EtherStatsCollisions"                      },
+       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
+       { 0, 60, 2, "Dot3OutPauseFrames"                        },
+       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
+       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
+       { 0, 66, 2, "IfOutUcastPkts"                            },
+       { 0, 68, 2, "IfOutMulticastPkts"                        },
+       { 0, 70, 2, "IfOutBroadcastPkts"                        },
+       { 0, 72, 2, "OutOampduPkts"                             },
+       { 0, 74, 2, "InOampduPkts"                              },
+       { 0, 76, 2, "PktgenPkts"                                },
+};
+
+#define REG_RD(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static const struct rtl8367_initval rtl8367_initvals_0_0[] = {
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
+       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
+       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
+       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
+       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
+       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
+       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
+       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
+       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
+       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
+       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
+       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
+       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
+       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
+       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
+       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
+       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
+       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
+       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
+       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
+       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
+       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1006}, {0x121e, 0x03e8},
+       {0x121f, 0x02b3}, {0x1220, 0x028f}, {0x1221, 0x029b}, {0x1222, 0x0277},
+       {0x1223, 0x02b3}, {0x1224, 0x028f}, {0x1225, 0x029b}, {0x1226, 0x0277},
+       {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0}, {0x1230, 0x00b4},
+       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
+       {0x0219, 0x0032}, {0x0200, 0x03e8}, {0x0201, 0x03e8}, {0x0202, 0x03e8},
+       {0x0203, 0x03e8}, {0x0204, 0x03e8}, {0x0205, 0x03e8}, {0x0206, 0x03e8},
+       {0x0207, 0x03e8}, {0x0218, 0x0032}, {0x0208, 0x029b}, {0x0209, 0x029b},
+       {0x020a, 0x029b}, {0x020b, 0x029b}, {0x020c, 0x029b}, {0x020d, 0x029b},
+       {0x020e, 0x029b}, {0x020f, 0x029b}, {0x0210, 0x029b}, {0x0211, 0x029b},
+       {0x0212, 0x029b}, {0x0213, 0x029b}, {0x0214, 0x029b}, {0x0215, 0x029b},
+       {0x0216, 0x029b}, {0x0217, 0x029b}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
+       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x1700, 0x014C}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
+       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
+       {0x133f, 0x0010}, {0x20A0, 0x1940}, {0x20C0, 0x1940}, {0x20E0, 0x1940},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_0_1[] = {
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
+       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
+       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
+       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
+       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
+       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
+       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
+       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
+       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
+       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
+       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
+       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
+       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
+       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
+       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
+       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
+       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
+       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
+       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
+       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
+       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
+       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1b06}, {0x121e, 0x07f0},
+       {0x121f, 0x0438}, {0x1220, 0x040f}, {0x1221, 0x040f}, {0x1222, 0x03eb},
+       {0x1223, 0x0438}, {0x1224, 0x040f}, {0x1225, 0x040f}, {0x1226, 0x03eb},
+       {0x1227, 0x0144}, {0x1228, 0x0138}, {0x122f, 0x0144}, {0x1230, 0x0138},
+       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
+       {0x0219, 0x0032}, {0x0200, 0x07d0}, {0x0201, 0x07d0}, {0x0202, 0x07d0},
+       {0x0203, 0x07d0}, {0x0204, 0x07d0}, {0x0205, 0x07d0}, {0x0206, 0x07d0},
+       {0x0207, 0x07d0}, {0x0218, 0x0032}, {0x0208, 0x0190}, {0x0209, 0x0190},
+       {0x020a, 0x0190}, {0x020b, 0x0190}, {0x020c, 0x0190}, {0x020d, 0x0190},
+       {0x020e, 0x0190}, {0x020f, 0x0190}, {0x0210, 0x0190}, {0x0211, 0x0190},
+       {0x0212, 0x0190}, {0x0213, 0x0190}, {0x0214, 0x0190}, {0x0215, 0x0190},
+       {0x0216, 0x0190}, {0x0217, 0x0190}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
+       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x1700, 0x0125}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
+       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
+       {0x133f, 0x0010},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_1_0[] = {
+       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
+       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
+       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
+       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
+       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
+       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
+       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
+       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
+       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
+       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
+       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
+       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
+       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
+       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
+       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
+       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
+       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
+       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
+       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
+       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
+       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
+       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
+       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
+       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
+       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
+       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
+       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
+       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
+       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
+       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
+       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
+       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
+       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
+       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
+       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
+       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
+       {0x121D, 0x7D16}, {0x121E, 0x03E8}, {0x121F, 0x024E}, {0x1220, 0x0230},
+       {0x1221, 0x0244}, {0x1222, 0x0226}, {0x1223, 0x024E}, {0x1224, 0x0230},
+       {0x1225, 0x0244}, {0x1226, 0x0226}, {0x1227, 0x00C0}, {0x1228, 0x00B4},
+       {0x122F, 0x00C0}, {0x1230, 0x00B4}, {0x0208, 0x03E8}, {0x0209, 0x03E8},
+       {0x020A, 0x03E8}, {0x020B, 0x03E8}, {0x020C, 0x03E8}, {0x020D, 0x03E8},
+       {0x020E, 0x03E8}, {0x020F, 0x03E8}, {0x0210, 0x03E8}, {0x0211, 0x03E8},
+       {0x0212, 0x03E8}, {0x0213, 0x03E8}, {0x0214, 0x03E8}, {0x0215, 0x03E8},
+       {0x0216, 0x03E8}, {0x0217, 0x03E8}, {0x0900, 0x0000}, {0x0901, 0x0000},
+       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087B, 0x0000},
+       {0x087C, 0xFF00}, {0x087D, 0x0000}, {0x087E, 0x0000}, {0x0801, 0x0100},
+       {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040},
+       {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
+       {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000}, {0x2200, 0x1340},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x20A0, 0x1940},
+       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_1_1[] = {
+       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
+       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
+       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
+       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
+       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
+       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
+       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
+       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
+       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
+       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
+       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
+       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
+       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
+       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
+       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
+       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
+       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
+       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
+       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
+       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
+       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
+       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
+       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
+       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
+       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
+       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
+       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
+       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
+       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
+       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
+       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
+       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
+       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
+       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
+       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
+       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
+       {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000},
+       {0x0865, 0x3210}, {0x087B, 0x0000}, {0x087C, 0xFF00}, {0x087D, 0x0000},
+       {0x087E, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040},
+       {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040},
+       {0x0A25, 0x2040}, {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040},
+       {0x0A29, 0x2040}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000},
+       {0x2200, 0x1340}, {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE},
+       {0x1B03, 0x0876},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_2_0[] = {
+       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
+       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
+       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
+       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
+       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
+       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
+       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
+       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
+       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
+       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
+       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
+       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
+       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
+       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
+       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
+       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
+       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
+       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
+       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
+       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
+       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
+       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
+       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
+       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
+       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
+       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
+       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
+       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
+       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
+       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
+       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
+       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
+       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
+       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
+       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
+       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x7D16},
+       {0x121e, 0x03e8}, {0x121f, 0x024e}, {0x1220, 0x0230}, {0x1221, 0x0244},
+       {0x1222, 0x0226}, {0x1223, 0x024e}, {0x1224, 0x0230}, {0x1225, 0x0244},
+       {0x1226, 0x0226}, {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0},
+       {0x1230, 0x00b4}, {0x0208, 0x03e8}, {0x0209, 0x03e8}, {0x020a, 0x03e8},
+       {0x020b, 0x03e8}, {0x020c, 0x03e8}, {0x020d, 0x03e8}, {0x020e, 0x03e8},
+       {0x020f, 0x03e8}, {0x0210, 0x03e8}, {0x0211, 0x03e8}, {0x0212, 0x03e8},
+       {0x0213, 0x03e8}, {0x0214, 0x03e8}, {0x0215, 0x03e8}, {0x0216, 0x03e8},
+       {0x0217, 0x03e8}, {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000},
+       {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000}, {0x087c, 0xff00},
+       {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100},
+       {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040},
+       {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040}, {0x20A0, 0x1940},
+       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
+};
+
+static const struct rtl8367_initval rtl8367_initvals_2_1[] = {
+       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
+       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
+       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
+       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
+       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
+       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
+       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
+       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
+       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
+       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
+       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
+       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
+       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
+       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
+       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
+       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
+       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
+       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
+       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
+       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
+       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
+       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
+       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
+       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
+       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
+       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
+       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
+       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
+       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
+       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
+       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
+       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
+       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
+       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
+       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
+       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
+       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
+       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x0900, 0x0000},
+       {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210},
+       {0x087b, 0x0000}, {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000},
+       {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040},
+       {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A25, 0x2040},
+       {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
+       {0x130c, 0x0050},
+};
+
+static int rtl8367_write_initvals(struct rtl8366_smi *smi,
+                                 const struct rtl8367_initval *initvals,
+                                 int count)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < count; i++)
+               REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+       return 0;
+}
+
+static int rtl8367_read_phy_reg(struct rtl8366_smi *smi,
+                               u32 phy_addr, u32 phy_reg, u32 *val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       if (phy_addr > RTL8367_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+       if (data & RTL8367_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* prepare address */
+       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
+              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send read command */
+       REG_WR(smi, RTL8367_IA_CTRL_REG,
+              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_READ);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy read timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       /* read data */
+       REG_RD(smi, RTL8367_IA_READ_DATA_REG, val);
+
+       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, *val);
+       return 0;
+}
+
+static int rtl8367_write_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_addr, u32 phy_reg, u32 val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, val);
+
+       if (phy_addr > RTL8367_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+       if (data & RTL8367_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* preapre data */
+       REG_WR(smi, RTL8367_IA_WRITE_DATA_REG, val);
+
+       /* prepare address */
+       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
+              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send write command */
+       REG_WR(smi, RTL8367_IA_CTRL_REG,
+              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_WRITE);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
+               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy write timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8367_init_regs0(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+       int err;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_0_0;
+               count = ARRAY_SIZE(rtl8367_initvals_0_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_0_1;
+               count = ARRAY_SIZE(rtl8367_initvals_0_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       err = rtl8367_write_initvals(smi, initvals, count);
+       if (err)
+               return err;
+
+       /* TODO: complete this */
+
+       return 0;
+}
+
+static int rtl8367_init_regs1(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_1_0;
+               count = ARRAY_SIZE(rtl8367_initvals_1_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_1_1;
+               count = ARRAY_SIZE(rtl8367_initvals_1_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       return rtl8367_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367_init_regs2(struct rtl8366_smi *smi, unsigned mode)
+{
+       const struct rtl8367_initval *initvals;
+       int count;
+
+       switch (mode) {
+       case 0:
+               initvals = rtl8367_initvals_2_0;
+               count = ARRAY_SIZE(rtl8367_initvals_2_0);
+               break;
+
+       case 1:
+       case 2:
+               initvals = rtl8367_initvals_2_1;
+               count = ARRAY_SIZE(rtl8367_initvals_2_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
+               return -ENODEV;
+       }
+
+       return rtl8367_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367_init_regs(struct rtl8366_smi *smi)
+{
+       u32 data;
+       u32 rlvid;
+       u32 mode;
+       int err;
+
+       REG_WR(smi, RTL8367_RTL_MAGIC_ID_REG, RTL8367_RTL_MAGIC_ID_VAL);
+
+       REG_RD(smi, RTL8367_CHIP_VER_REG, &data);
+       rlvid = (data >> RTL8367_CHIP_VER_RLVID_SHIFT) &
+               RTL8367_CHIP_VER_RLVID_MASK;
+
+       REG_RD(smi, RTL8367_CHIP_MODE_REG, &data);
+       mode = data & RTL8367_CHIP_MODE_MASK;
+
+       switch (rlvid) {
+       case 0:
+               err = rtl8367_init_regs0(smi, mode);
+               break;
+
+       case 1:
+               err = rtl8367_write_phy_reg(smi, 0, 31, 5);
+               if (err)
+                       break;
+
+               err = rtl8367_write_phy_reg(smi, 0, 5, 0x3ffe);
+               if (err)
+                       break;
+
+               err = rtl8367_read_phy_reg(smi, 0, 6, &data);
+               if (err)
+                       break;
+
+               if (data == 0x94eb) {
+                       err = rtl8367_init_regs1(smi, mode);
+               } else if (data == 0x2104) {
+                       err = rtl8367_init_regs2(smi, mode);
+               } else {
+                       dev_err(smi->parent, "unknow phy data %04x\n", data);
+                       return -ENODEV;
+               }
+
+               break;
+
+       default:
+               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+               err = -ENODEV;
+               break;
+       }
+
+       return err;
+}
+
+static int rtl8367_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       int err;
+       u32 data;
+
+       REG_WR(smi, RTL8367_CHIP_RESET_REG, RTL8367_CHIP_RESET_HW);
+       msleep(RTL8367_RESET_DELAY);
+
+       do {
+               REG_RD(smi, RTL8367_CHIP_RESET_REG, &data);
+               if (!(data & RTL8367_CHIP_RESET_HW))
+                       break;
+
+               msleep(1);
+       } while (--timeout);
+
+       if (!timeout) {
+               dev_err(smi->parent, "chip reset timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtl8367_extif_set_mode(struct rtl8366_smi *smi, int id,
+                                 enum rtl8367_extif_mode mode)
+{
+       int err;
+
+       /* set port mode */
+       switch (mode) {
+       case RTL8367_EXTIF_MODE_RGMII:
+       case RTL8367_EXTIF_MODE_RGMII_33V:
+               REG_WR(smi, RTL8367_CHIP_DEBUG0_REG, 0x0367);
+               REG_WR(smi, RTL8367_CHIP_DEBUG1_REG, 0x7777);
+               break;
+
+       case RTL8367_EXTIF_MODE_TMII_MAC:
+       case RTL8367_EXTIF_MODE_TMII_PHY:
+               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), BIT((id + 1) % 2));
+               break;
+
+       case RTL8367_EXTIF_MODE_GMII:
+               REG_RMW(smi, RTL8367_CHIP_DEBUG0_REG,
+                       RTL8367_CHIP_DEBUG0_DUMMY0(id),
+                       RTL8367_CHIP_DEBUG0_DUMMY0(id));
+               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+               break;
+
+       case RTL8367_EXTIF_MODE_MII_MAC:
+       case RTL8367_EXTIF_MODE_MII_PHY:
+       case RTL8367_EXTIF_MODE_DISABLED:
+               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), 0);
+               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), 0);
+               break;
+
+       default:
+               dev_err(smi->parent,
+                       "invalid mode for external interface %d\n", id);
+               return -EINVAL;
+       }
+
+       REG_RMW(smi, RTL8367_DIS_REG,
+               RTL8367_DIS_RGMII_MASK << RTL8367_DIS_RGMII_SHIFT(id),
+               mode << RTL8367_DIS_RGMII_SHIFT(id));
+
+       return 0;
+}
+
+static int rtl8367_extif_set_force(struct rtl8366_smi *smi, int id,
+                                  struct rtl8367_port_ability *pa)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367_DI_FORCE_MODE |
+               RTL8367_DI_FORCE_NWAY |
+               RTL8367_DI_FORCE_TXPAUSE |
+               RTL8367_DI_FORCE_RXPAUSE |
+               RTL8367_DI_FORCE_LINK |
+               RTL8367_DI_FORCE_DUPLEX |
+               RTL8367_DI_FORCE_SPEED_MASK);
+
+       val = pa->speed;
+       val |= pa->force_mode ? RTL8367_DI_FORCE_MODE : 0;
+       val |= pa->nway ? RTL8367_DI_FORCE_NWAY : 0;
+       val |= pa->txpause ? RTL8367_DI_FORCE_TXPAUSE : 0;
+       val |= pa->rxpause ? RTL8367_DI_FORCE_RXPAUSE : 0;
+       val |= pa->link ? RTL8367_DI_FORCE_LINK : 0;
+       val |= pa->duplex ? RTL8367_DI_FORCE_DUPLEX : 0;
+
+       REG_RMW(smi, RTL8367_DI_FORCE_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+                                        unsigned txdelay, unsigned rxdelay)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367_EXT_RGMXF_RXDELAY_MASK |
+               (RTL8367_EXT_RGMXF_TXDELAY_MASK <<
+                       RTL8367_EXT_RGMXF_TXDELAY_SHIFT));
+
+       val = rxdelay;
+       val |= txdelay << RTL8367_EXT_RGMXF_TXDELAY_SHIFT;
+
+       REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367_extif_init(struct rtl8366_smi *smi, int id,
+                             struct rtl8367_extif_config *cfg)
+{
+       enum rtl8367_extif_mode mode;
+       int err;
+
+       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+       err = rtl8367_extif_set_mode(smi, id, mode);
+       if (err)
+               return err;
+
+       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+               err = rtl8367_extif_set_force(smi, id, &cfg->ability);
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+                                                    cfg->rxdelay);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_ports(struct rtl8366_smi *smi,
+                                      unsigned int group, u16 port_mask)
+{
+       u32 reg;
+       u32 s;
+       int err;
+
+       port_mask &= RTL8367_PARA_LED_IO_EN_PMASK;
+       s = (group % 2) * 8;
+       reg = RTL8367_PARA_LED_IO_EN1_REG + (group / 2);
+
+       REG_RMW(smi, reg, (RTL8367_PARA_LED_IO_EN_PMASK << s), port_mask << s);
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_mode(struct rtl8366_smi *smi,
+                                     unsigned int mode)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mode &= RTL8367_LED_CONFIG_DATA_M;
+
+       mask = (RTL8367_LED_CONFIG_DATA_M << RTL8367_LED_CONFIG_DATA_S) |
+               RTL8367_LED_CONFIG_SEL;
+       set = (mode << RTL8367_LED_CONFIG_DATA_S) | RTL8367_LED_CONFIG_SEL;
+
+       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
+
+       return 0;
+}
+
+static int rtl8367_led_group_set_config(struct rtl8366_smi *smi,
+                                       unsigned int led, unsigned int cfg)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mask = (RTL8367_LED_CONFIG_LED_CFG_M << (led * 4)) |
+               RTL8367_LED_CONFIG_SEL;
+       set = (cfg & RTL8367_LED_CONFIG_LED_CFG_M) << (led * 4);
+
+       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
+       return 0;
+}
+
+static int rtl8367_led_op_select_parallel(struct rtl8366_smi *smi)
+{
+       int err;
+
+       REG_WR(smi, RTL8367_LED_SYS_CONFIG_REG, 0x1472);
+       return 0;
+}
+
+static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate)
+{
+       u16 mask;
+       u16 set;
+       int err;
+
+       mask = RTL8367_LED_MODE_RATE_M << RTL8367_LED_MODE_RATE_S;
+       set = (rate & RTL8367_LED_MODE_RATE_M) << RTL8367_LED_MODE_RATE_S;
+       REG_RMW(smi, RTL8367_LED_MODE_REG, mask, set);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+                                const char *name)
+{
+       struct rtl8367_extif_config *cfg;
+       const __be32 *prop;
+       int size;
+       int err;
+
+       prop = of_get_property(smi->parent->of_node, name, &size);
+       if (!prop)
+               return rtl8367_extif_init(smi, id, NULL);
+
+       if (size != (9 * sizeof(*prop))) {
+               dev_err(smi->parent, "%s property is invalid\n", name);
+               return -EINVAL;
+       }
+
+       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->txdelay = be32_to_cpup(prop++);
+       cfg->rxdelay = be32_to_cpup(prop++);
+       cfg->mode = be32_to_cpup(prop++);
+       cfg->ability.force_mode = be32_to_cpup(prop++);
+       cfg->ability.txpause = be32_to_cpup(prop++);
+       cfg->ability.rxpause = be32_to_cpup(prop++);
+       cfg->ability.link = be32_to_cpup(prop++);
+       cfg->ability.duplex = be32_to_cpup(prop++);
+       cfg->ability.speed = be32_to_cpup(prop++);
+
+       err = rtl8367_extif_init(smi, id, cfg);
+       kfree(cfg);
+
+       return err;
+}
+#else
+static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
+                                const char *name)
+{
+       return -EINVAL;
+}
+#endif
+
+static int rtl8367_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8367_platform_data *pdata;
+       int err;
+       int i;
+
+       pdata = smi->parent->platform_data;
+
+       err = rtl8367_init_regs(smi);
+       if (err)
+               return err;
+
+       /* initialize external interfaces */
+       if (smi->parent->of_node) {
+               err = rtl8367_extif_init_of(smi, 0, "realtek,extif0");
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_init_of(smi, 1, "realtek,extif1");
+               if (err)
+                       return err;
+       } else {
+               err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg);
+               if (err)
+                       return err;
+
+               err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg);
+               if (err)
+                       return err;
+       }
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8367_SWC0_REG, RTL8367_SWC0_MAX_LENGTH_MASK,
+               RTL8367_SWC0_MAX_LENGTH_1536);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8367_VLAN_INGRESS_REG, RTL8367_PORTS_ALL);
+
+       /*
+        * Setup egress tag mode for each port.
+        */
+       for (i = 0; i < RTL8367_NUM_PORTS; i++)
+               REG_RMW(smi,
+                       RTL8367_PORT_CFG_REG(i),
+                       RTL8367_PORT_CFG_EGRESS_MODE_MASK <<
+                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT,
+                       RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL <<
+                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT);
+
+       /* setup LEDs */
+       err = rtl8367_led_group_set_ports(smi, 0, RTL8367_PORTS_ALL);
+       if (err)
+               return err;
+
+       err = rtl8367_led_group_set_mode(smi, 0);
+       if (err)
+               return err;
+
+       err = rtl8367_led_op_select_parallel(smi);
+       if (err)
+               return err;
+
+       err = rtl8367_led_blinkrate_set(smi, 1);
+       if (err)
+               return err;
+
+       err = rtl8367_led_group_set_config(smi, 0, 2);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+static int rtl8367_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                  int port, unsigned long long *val)
+{
+       struct rtl8366_mib_counter *mib;
+       int offset;
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8367_NUM_PORTS || counter >= RTL8367_MIB_COUNT)
+               return -EINVAL;
+
+       mib = &rtl8367_mib_counters[counter];
+       addr = RTL8367_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       REG_WR(smi, RTL8367_MIB_ADDRESS_REG, addr >> 2);
+
+       /* read MIB control register */
+       REG_RD(smi, RTL8367_MIB_CTRL_REG(0), &data);
+
+       if (data & RTL8367_MIB_CTRL_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8367_MIB_CTRL_RESET_MASK)
+               return -EIO;
+
+       if (mib->length == 4)
+               offset = 3;
+       else
+               offset = (mib->offset + 1) % 4;
+
+       mibvalue = 0;
+       for (i = 0; i < mib->length; i++) {
+               REG_RD(smi, RTL8367_MIB_COUNTER_REG(offset - i), &data);
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8367_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8367_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       REG_WR(smi, RTL8367_TA_ADDR_REG, vid);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_READ);
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367_TA_DATA_REG(i), &data[i]);
+
+       vlan4k->vid = vid;
+       vlan4k->member = (data[0] >> RTL8367_TA_VLAN_MEMBER_SHIFT) &
+                        RTL8367_TA_VLAN_MEMBER_MASK;
+       vlan4k->fid = (data[1] >> RTL8367_TA_VLAN_FID_SHIFT) &
+                     RTL8367_TA_VLAN_FID_MASK;
+       vlan4k->untag = (data[2] >> RTL8367_TA_VLAN_UNTAG1_SHIFT) &
+                       RTL8367_TA_VLAN_UNTAG1_MASK;
+       vlan4k->untag |= ((data[3] >> RTL8367_TA_VLAN_UNTAG2_SHIFT) &
+                         RTL8367_TA_VLAN_UNTAG2_MASK) << 2;
+
+       return 0;
+}
+
+static int rtl8367_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8367_NUM_VIDS ||
+           vlan4k->member > RTL8367_TA_VLAN_MEMBER_MASK ||
+           vlan4k->untag > RTL8367_UNTAG_MASK ||
+           vlan4k->fid > RTL8367_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlan4k->member & RTL8367_TA_VLAN_MEMBER_MASK) <<
+                 RTL8367_TA_VLAN_MEMBER_SHIFT;
+       data[1] = (vlan4k->fid & RTL8367_TA_VLAN_FID_MASK) <<
+                 RTL8367_TA_VLAN_FID_SHIFT;
+       data[2] = (vlan4k->untag & RTL8367_TA_VLAN_UNTAG1_MASK) <<
+                 RTL8367_TA_VLAN_UNTAG1_SHIFT;
+       data[3] = ((vlan4k->untag >> 2) & RTL8367_TA_VLAN_UNTAG2_MASK) <<
+                 RTL8367_TA_VLAN_UNTAG2_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367_TA_DATA_REG(i), data[i]);
+
+       /* write VID */
+       REG_WR(smi, RTL8367_TA_ADDR_REG,
+              vlan4k->vid & RTL8367_TA_VLAN_VID_MASK);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_WRITE);
+
+       return 0;
+}
+
+static int rtl8367_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8367_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367_VLAN_MC_BASE(index) + i, &data[i]);
+
+       vlanmc->member = (data[0] >> RTL8367_VLAN_MC_MEMBER_SHIFT) &
+                        RTL8367_VLAN_MC_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8367_VLAN_MC_FID_SHIFT) &
+                     RTL8367_VLAN_MC_FID_MASK;
+       vlanmc->vid = (data[3] >> RTL8367_VLAN_MC_EVID_SHIFT) &
+                     RTL8367_VLAN_MC_EVID_MASK;
+
+       return 0;
+}
+
+static int rtl8367_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
+       int err;
+       int i;
+
+       if (index >= RTL8367_NUM_VLANS ||
+           vlanmc->vid >= RTL8367_NUM_VIDS ||
+           vlanmc->priority > RTL8367_PRIORITYMAX ||
+           vlanmc->member > RTL8367_VLAN_MC_MEMBER_MASK ||
+           vlanmc->untag > RTL8367_UNTAG_MASK ||
+           vlanmc->fid > RTL8367_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->member & RTL8367_VLAN_MC_MEMBER_MASK) <<
+                 RTL8367_VLAN_MC_MEMBER_SHIFT;
+       data[1] = (vlanmc->fid & RTL8367_VLAN_MC_FID_MASK) <<
+                 RTL8367_VLAN_MC_FID_SHIFT;
+       data[2] = 0;
+       data[3] = (vlanmc->vid & RTL8367_VLAN_MC_EVID_MASK) <<
+                  RTL8367_VLAN_MC_EVID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367_VLAN_MC_BASE(index) + i, data[i]);
+
+       return 0;
+}
+
+static int rtl8367_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367_VLAN_PVID_CTRL_REG(port), &data);
+
+       *val = (data >> RTL8367_VLAN_PVID_CTRL_SHIFT(port)) &
+              RTL8367_VLAN_PVID_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8367_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8367_NUM_PORTS || index >= RTL8367_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_PVID_CTRL_REG(port),
+                               RTL8367_VLAN_PVID_CTRL_MASK <<
+                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port),
+                               (index & RTL8367_VLAN_PVID_CTRL_MASK) <<
+                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_CTRL_REG,
+                               RTL8367_VLAN_CTRL_ENABLE,
+                               (enable) ? RTL8367_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return 0;
+}
+
+static int rtl8367_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8367_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8367_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8367_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+
+       REG_WR(smi, RTL8367_PORT_ISOLATION_REG(port),
+              (enable) ? RTL8367_PORTS_ALL : 0);
+
+       return 0;
+}
+
+static int rtl8367_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(0), 0,
+                               RTL8367_MIB_CTRL_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367_sw_get_port_link(struct switch_dev *dev,
+                                   int port,
+                                   struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8367_PORT_STATUS_REG(port), &data);
+
+       link->link = !!(data & RTL8367_PORT_STATUS_LINK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8367_PORT_STATUS_DUPLEX);
+       link->rx_flow = !!(data & RTL8367_PORT_STATUS_RXPAUSE);
+       link->tx_flow = !!(data & RTL8367_PORT_STATUS_TXPAUSE);
+       link->aneg = !!(data & RTL8367_PORT_STATUS_NWAY);
+
+       speed = (data & RTL8367_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8367_sw_get_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8367_SWC0_REG, &data);
+       val->value.i = (data & RTL8367_SWC0_MAX_LENGTH_MASK) >>
+                       RTL8367_SWC0_MAX_LENGTH_SHIFT;
+
+       return 0;
+}
+
+static int rtl8367_sw_set_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 max_len;
+
+       switch (val->value.i) {
+       case 0:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1522;
+               break;
+       case 1:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1536;
+               break;
+       case 2:
+               max_len = RTL8367_SWC0_MAX_LENGTH_1552;
+               break;
+       case 3:
+               max_len = RTL8367_SWC0_MAX_LENGTH_16000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8367_SWC0_REG,
+                               RTL8367_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int port;
+
+       port = val->port_vlan;
+       if (port >= RTL8367_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(port / 8), 0,
+                               RTL8367_MIB_CTRL_PORT_RESET_MASK(port % 8));
+}
+
+static int rtl8367_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8367_MIB_TXB_ID, RTL8367_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8367_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8367_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+                              "(0:1522, 1:1536, 2:1552, 3:16000)",
+               .set = rtl8367_sw_set_max_length,
+               .get = rtl8367_sw_get_max_length,
+               .max = 3,
+       }
+};
+
+static struct switch_attr rtl8367_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8367_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr rtl8367_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "fid",
+               .description = "Get/Set vlan FID",
+               .max = RTL8367_FIDMAX,
+               .set = rtl8366_sw_set_vlan_fid,
+               .get = rtl8366_sw_get_vlan_fid,
+       },
+};
+
+static const struct switch_dev_ops rtl8367_sw_ops = {
+       .attr_global = {
+               .attr = rtl8367_globals,
+               .n_attr = ARRAY_SIZE(rtl8367_globals),
+       },
+       .attr_port = {
+               .attr = rtl8367_port,
+               .n_attr = ARRAY_SIZE(rtl8367_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8367_vlan,
+               .n_attr = ARRAY_SIZE(rtl8367_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8367_sw_get_port_link,
+       .get_port_stats = rtl8367_sw_get_port_stats,
+};
+
+static int rtl8367_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8367";
+       dev->cpu_port = RTL8367_CPU_PORT_NUM;
+       dev->ports = RTL8367_NUM_PORTS;
+       dev->vlans = RTL8367_NUM_VIDS;
+       dev->ops = &rtl8367_sw_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8367_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8367_read_phy_reg(smi, addr, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8367_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8367_write_phy_reg(smi, addr, reg, val);
+       if (err)
+               return err;
+
+       /* flush write */
+       (void) rtl8367_read_phy_reg(smi, addr, reg, &t);
+
+       return err;
+}
+
+static int rtl8367_detect(struct rtl8366_smi *smi)
+{
+       u32 rtl_no = 0;
+       u32 rtl_ver = 0;
+       char *chip_name;
+       int ret;
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_NO_REG, &rtl_no);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip number\n");
+               return ret;
+       }
+
+       switch (rtl_no) {
+       case RTL8367_RTL_NO_8367R:
+               chip_name = "8367R";
+               break;
+       case RTL8367_RTL_NO_8367M:
+               chip_name = "8367M";
+               break;
+       default:
+               dev_err(smi->parent, "unknown chip number (%04x)\n", rtl_no);
+               return -ENODEV;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_VER_REG, &rtl_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read chip version\n");
+               return ret;
+       }
+
+       dev_info(smi->parent, "RTL%s ver. %u chip found\n",
+                chip_name, rtl_ver & RTL8367_RTL_VER_MASK);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367_smi_ops = {
+       .detect         = rtl8367_detect,
+       .reset_chip     = rtl8367_reset_chip,
+       .setup          = rtl8367_setup,
+
+       .mii_read       = rtl8367_mii_read,
+       .mii_write      = rtl8367_mii_write,
+
+       .get_vlan_mc    = rtl8367_get_vlan_mc,
+       .set_vlan_mc    = rtl8367_set_vlan_mc,
+       .get_vlan_4k    = rtl8367_get_vlan_4k,
+       .set_vlan_4k    = rtl8367_set_vlan_4k,
+       .get_mc_index   = rtl8367_get_mc_index,
+       .set_mc_index   = rtl8367_set_mc_index,
+       .get_mib_counter = rtl8367_get_mib_counter,
+       .is_vlan_valid  = rtl8367_is_vlan_valid,
+       .enable_vlan    = rtl8367_enable_vlan,
+       .enable_vlan4k  = rtl8367_enable_vlan4k,
+       .enable_port    = rtl8367_enable_port,
+};
+
+static int rtl8367_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 1500;
+       smi->cmd_read = 0xb9;
+       smi->cmd_write = 0xb8;
+       smi->ops = &rtl8367_smi_ops;
+       smi->cpu_port = RTL8367_CPU_PORT_NUM;
+       smi->num_ports = RTL8367_NUM_PORTS;
+       smi->num_vlan_mc = RTL8367_NUM_VLANS;
+       smi->mib_counters = rtl8367_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8367_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8367_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8367_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8367_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+static void rtl8367_shutdown(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi)
+               rtl8367_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367_match[] = {
+       { .compatible = "realtek,rtl8367" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367_match);
+#endif
+
+static struct platform_driver rtl8367_driver = {
+       .driver = {
+               .name           = RTL8367_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8367_match),
+#endif
+       },
+       .probe          = rtl8367_probe,
+       .remove         = rtl8367_remove,
+       .shutdown       = rtl8367_shutdown,
+};
+
+static int __init rtl8367_module_init(void)
+{
+       return platform_driver_register(&rtl8367_driver);
+}
+module_init(rtl8367_module_init);
+
+static void __exit rtl8367_module_exit(void)
+{
+       platform_driver_unregister(&rtl8367_driver);
+}
+module_exit(rtl8367_module_exit);
+
+MODULE_DESCRIPTION("Realtek RTL8367 ethernet switch driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367_DRIVER_NAME);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/rtl8367b.c b/target/linux/generic/files-4.9/drivers/net/phy/rtl8367b.c
new file mode 100644 (file)
index 0000000..e6ea650
--- /dev/null
@@ -0,0 +1,1612 @@
+/*
+ * Platform driver for the Realtek RTL8367R-VB ethernet switches
+ *
+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/rtl8367.h>
+
+#include "rtl8366_smi.h"
+
+#define RTL8367B_RESET_DELAY   1000    /* msecs*/
+
+#define RTL8367B_PHY_ADDR_MAX  8
+#define RTL8367B_PHY_REG_MAX   31
+
+#define RTL8367B_VID_MASK      0x3fff
+#define RTL8367B_FID_MASK      0xf
+#define RTL8367B_UNTAG_MASK    0xff
+#define RTL8367B_MEMBER_MASK   0xff
+
+#define RTL8367B_PORT_MISC_CFG_REG(_p)         (0x000e + 0x20 * (_p))
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT     4
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK      0x3
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL  0
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_KEEP      1
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_PRI       2
+#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_REAL      3
+
+#define RTL8367B_BYPASS_LINE_RATE_REG          0x03f7
+
+#define RTL8367B_TA_CTRL_REG                   0x0500 /*GOOD*/
+#define   RTL8367B_TA_CTRL_SPA_SHIFT           8
+#define   RTL8367B_TA_CTRL_SPA_MASK            0x7
+#define   RTL8367B_TA_CTRL_METHOD              BIT(4)/*GOOD*/
+#define   RTL8367B_TA_CTRL_CMD_SHIFT           3
+#define   RTL8367B_TA_CTRL_CMD_READ            0
+#define   RTL8367B_TA_CTRL_CMD_WRITE           1
+#define   RTL8367B_TA_CTRL_TABLE_SHIFT         0 /*GOOD*/
+#define   RTL8367B_TA_CTRL_TABLE_ACLRULE       1
+#define   RTL8367B_TA_CTRL_TABLE_ACLACT                2
+#define   RTL8367B_TA_CTRL_TABLE_CVLAN         3
+#define   RTL8367B_TA_CTRL_TABLE_L2            4
+#define   RTL8367B_TA_CTRL_CVLAN_READ \
+               ((RTL8367B_TA_CTRL_CMD_READ << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+                RTL8367B_TA_CTRL_TABLE_CVLAN)
+#define   RTL8367B_TA_CTRL_CVLAN_WRITE \
+               ((RTL8367B_TA_CTRL_CMD_WRITE << RTL8367B_TA_CTRL_CMD_SHIFT) | \
+                RTL8367B_TA_CTRL_TABLE_CVLAN)
+
+#define RTL8367B_TA_ADDR_REG                   0x0501/*GOOD*/
+#define   RTL8367B_TA_ADDR_MASK                        0x3fff/*GOOD*/
+
+#define RTL8367B_TA_LUT_REG                    0x0502/*GOOD*/
+
+#define RTL8367B_TA_WRDATA_REG(_x)             (0x0510 + (_x))/*GOOD*/
+#define   RTL8367B_TA_VLAN_NUM_WORDS           2
+#define   RTL8367B_TA_VLAN_VID_MASK            RTL8367B_VID_MASK
+#define   RTL8367B_TA_VLAN0_MEMBER_SHIFT       0
+#define   RTL8367B_TA_VLAN0_MEMBER_MASK                RTL8367B_MEMBER_MASK
+#define   RTL8367B_TA_VLAN0_UNTAG_SHIFT                8
+#define   RTL8367B_TA_VLAN0_UNTAG_MASK         RTL8367B_MEMBER_MASK
+#define   RTL8367B_TA_VLAN1_FID_SHIFT          0
+#define   RTL8367B_TA_VLAN1_FID_MASK           RTL8367B_FID_MASK
+
+#define RTL8367B_TA_RDDATA_REG(_x)             (0x0520 + (_x))/*GOOD*/
+
+#define RTL8367B_VLAN_PVID_CTRL_REG(_p)                (0x0700 + (_p) / 2) /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_MASK           0x1f /*GOOD*/
+#define RTL8367B_VLAN_PVID_CTRL_SHIFT(_p)      (8 * ((_p) % 2)) /*GOOD*/
+
+#define RTL8367B_VLAN_MC_BASE(_x)              (0x0728 + (_x) * 4) /*GOOD*/
+#define   RTL8367B_VLAN_MC_NUM_WORDS           4 /*GOOD*/
+#define   RTL8367B_VLAN_MC0_MEMBER_SHIFT       0/*GOOD*/
+#define   RTL8367B_VLAN_MC0_MEMBER_MASK                RTL8367B_MEMBER_MASK/*GOOD*/
+#define   RTL8367B_VLAN_MC1_FID_SHIFT          0/*GOOD*/
+#define   RTL8367B_VLAN_MC1_FID_MASK           RTL8367B_FID_MASK/*GOOD*/
+#define   RTL8367B_VLAN_MC3_EVID_SHIFT         0/*GOOD*/
+#define   RTL8367B_VLAN_MC3_EVID_MASK          RTL8367B_VID_MASK/*GOOD*/
+
+#define RTL8367B_VLAN_CTRL_REG                 0x07a8 /*GOOD*/
+#define   RTL8367B_VLAN_CTRL_ENABLE            BIT(0)
+
+#define RTL8367B_VLAN_INGRESS_REG              0x07a9 /*GOOD*/
+
+#define RTL8367B_PORT_ISOLATION_REG(_p)                (0x08a2 + (_p)) /*GOOD*/
+
+#define RTL8367B_MIB_COUNTER_REG(_x)           (0x1000 + (_x)) /*GOOD*/
+#define RTL8367B_MIB_COUNTER_PORT_OFFSET       0x007c /*GOOD*/
+
+#define RTL8367B_MIB_ADDRESS_REG               0x1004 /*GOOD*/
+
+#define RTL8367B_MIB_CTRL0_REG(_x)             (0x1005 + (_x)) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK BIT(11) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_QM_RESET_MASK     BIT(10) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_PORT_RESET_MASK(_p) BIT(2 + (_p)) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_RESET_MASK                BIT(1) /*GOOD*/
+#define   RTL8367B_MIB_CTRL0_BUSY_MASK         BIT(0) /*GOOD*/
+
+#define RTL8367B_SWC0_REG                      0x1200/*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH_SHIFT       13/*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH(_x)         ((_x) << 13) /*GOOD*/
+#define   RTL8367B_SWC0_MAX_LENGTH_MASK                RTL8367B_SWC0_MAX_LENGTH(0x3)
+#define   RTL8367B_SWC0_MAX_LENGTH_1522                RTL8367B_SWC0_MAX_LENGTH(0)
+#define   RTL8367B_SWC0_MAX_LENGTH_1536                RTL8367B_SWC0_MAX_LENGTH(1)
+#define   RTL8367B_SWC0_MAX_LENGTH_1552                RTL8367B_SWC0_MAX_LENGTH(2)
+#define   RTL8367B_SWC0_MAX_LENGTH_16000       RTL8367B_SWC0_MAX_LENGTH(3)
+
+#define RTL8367B_CHIP_NUMBER_REG               0x1300/*GOOD*/
+
+#define RTL8367B_CHIP_VER_REG                  0x1301/*GOOD*/
+#define   RTL8367B_CHIP_VER_RLVID_SHIFT                12/*GOOD*/
+#define   RTL8367B_CHIP_VER_RLVID_MASK         0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_MCID_SHIFT         8/*GOOD*/
+#define   RTL8367B_CHIP_VER_MCID_MASK          0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_BOID_SHIFT         4/*GOOD*/
+#define   RTL8367B_CHIP_VER_BOID_MASK          0xf/*GOOD*/
+#define   RTL8367B_CHIP_VER_AFE_SHIFT          0/*GOOD*/
+#define   RTL8367B_CHIP_VER_AFE_MASK           0x1/*GOOD*/
+
+#define RTL8367B_CHIP_MODE_REG                 0x1302
+#define   RTL8367B_CHIP_MODE_MASK              0x7
+
+#define RTL8367B_CHIP_DEBUG0_REG               0x1303
+#define   RTL8367B_CHIP_DEBUG0_DUMMY0(_x)      BIT(8 + (_x))
+
+#define RTL8367B_CHIP_DEBUG1_REG               0x1304
+
+#define RTL8367B_DIS_REG                       0x1305
+#define   RTL8367B_DIS_SKIP_MII_RXER(_x)       BIT(12 + (_x))
+#define   RTL8367B_DIS_RGMII_SHIFT(_x)         (4 * (_x))
+#define   RTL8367B_DIS_RGMII_MASK              0x7
+
+#define RTL8367B_EXT_RGMXF_REG(_x)             (0x1306 + (_x))
+#define   RTL8367B_EXT_RGMXF_DUMMY0_SHIFT      5
+#define   RTL8367B_EXT_RGMXF_DUMMY0_MASK       0x7ff
+#define   RTL8367B_EXT_RGMXF_TXDELAY_SHIFT     3
+#define   RTL8367B_EXT_RGMXF_TXDELAY_MASK      1
+#define   RTL8367B_EXT_RGMXF_RXDELAY_MASK      0x7
+
+#define RTL8367B_DI_FORCE_REG(_x)              (0x1310 + (_x))
+#define   RTL8367B_DI_FORCE_MODE               BIT(12)
+#define   RTL8367B_DI_FORCE_NWAY               BIT(7)
+#define   RTL8367B_DI_FORCE_TXPAUSE            BIT(6)
+#define   RTL8367B_DI_FORCE_RXPAUSE            BIT(5)
+#define   RTL8367B_DI_FORCE_LINK               BIT(4)
+#define   RTL8367B_DI_FORCE_DUPLEX             BIT(2)
+#define   RTL8367B_DI_FORCE_SPEED_MASK         3
+#define   RTL8367B_DI_FORCE_SPEED_10           0
+#define   RTL8367B_DI_FORCE_SPEED_100          1
+#define   RTL8367B_DI_FORCE_SPEED_1000         2
+
+#define RTL8367B_MAC_FORCE_REG(_x)             (0x1312 + (_x))
+
+#define RTL8367B_CHIP_RESET_REG                        0x1322 /*GOOD*/
+#define   RTL8367B_CHIP_RESET_SW               BIT(1) /*GOOD*/
+#define   RTL8367B_CHIP_RESET_HW               BIT(0) /*GOOD*/
+
+#define RTL8367B_PORT_STATUS_REG(_p)           (0x1352 + (_p)) /*GOOD*/
+#define   RTL8367B_PORT_STATUS_EN_1000_SPI     BIT(11) /*GOOD*/
+#define   RTL8367B_PORT_STATUS_EN_100_SPI      BIT(10)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_NWAY_FAULT      BIT(9)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_LINK_MASTER     BIT(8)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_NWAY            BIT(7)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_TXPAUSE         BIT(6)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_RXPAUSE         BIT(5)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_LINK            BIT(4)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_DUPLEX          BIT(2)/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_MASK      0x0003/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_10                0/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_100       1/*GOOD*/
+#define   RTL8367B_PORT_STATUS_SPEED_1000      2/*GOOD*/
+
+#define RTL8367B_RTL_MAGIC_ID_REG              0x13c2
+#define   RTL8367B_RTL_MAGIC_ID_VAL            0x0249
+
+#define RTL8367B_IA_CTRL_REG                   0x1f00
+#define   RTL8367B_IA_CTRL_RW(_x)              ((_x) << 1)
+#define   RTL8367B_IA_CTRL_RW_READ             RTL8367B_IA_CTRL_RW(0)
+#define   RTL8367B_IA_CTRL_RW_WRITE            RTL8367B_IA_CTRL_RW(1)
+#define   RTL8367B_IA_CTRL_CMD_MASK            BIT(0)
+
+#define RTL8367B_IA_STATUS_REG                 0x1f01
+#define   RTL8367B_IA_STATUS_PHY_BUSY          BIT(2)
+#define   RTL8367B_IA_STATUS_SDS_BUSY          BIT(1)
+#define   RTL8367B_IA_STATUS_MDX_BUSY          BIT(0)
+
+#define RTL8367B_IA_ADDRESS_REG                        0x1f02
+#define RTL8367B_IA_WRITE_DATA_REG             0x1f03
+#define RTL8367B_IA_READ_DATA_REG              0x1f04
+
+#define RTL8367B_INTERNAL_PHY_REG(_a, _r)      (0x2000 + 32 * (_a) + (_r))
+
+#define RTL8367B_NUM_MIB_COUNTERS      58
+
+#define RTL8367B_CPU_PORT_NUM          5
+#define RTL8367B_NUM_PORTS             8
+#define RTL8367B_NUM_VLANS             32
+#define RTL8367B_NUM_VIDS              4096
+#define RTL8367B_PRIORITYMAX           7
+#define RTL8367B_FIDMAX                        7
+
+#define RTL8367B_PORT_0                        BIT(0)
+#define RTL8367B_PORT_1                        BIT(1)
+#define RTL8367B_PORT_2                        BIT(2)
+#define RTL8367B_PORT_3                        BIT(3)
+#define RTL8367B_PORT_4                        BIT(4)
+#define RTL8367B_PORT_E0               BIT(5)  /* External port 0 */
+#define RTL8367B_PORT_E1               BIT(6)  /* External port 1 */
+#define RTL8367B_PORT_E2               BIT(7)  /* External port 2 */
+
+#define RTL8367B_PORTS_ALL                                     \
+       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
+        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E0 | \
+        RTL8367B_PORT_E1 | RTL8367B_PORT_E2)
+
+#define RTL8367B_PORTS_ALL_BUT_CPU                             \
+       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
+        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E1 | \
+        RTL8367B_PORT_E2)
+
+struct rtl8367b_initval {
+       u16 reg;
+       u16 val;
+};
+
+#define RTL8367B_MIB_RXB_ID            0       /* IfInOctets */
+#define RTL8367B_MIB_TXB_ID            28      /* IfOutOctets */
+
+static struct rtl8366_mib_counter
+rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
+       {0,   0, 4, "ifInOctets"                        },
+       {0,   4, 2, "dot3StatsFCSErrors"                },
+       {0,   6, 2, "dot3StatsSymbolErrors"             },
+       {0,   8, 2, "dot3InPauseFrames"                 },
+       {0,  10, 2, "dot3ControlInUnknownOpcodes"       },
+       {0,  12, 2, "etherStatsFragments"               },
+       {0,  14, 2, "etherStatsJabbers"                 },
+       {0,  16, 2, "ifInUcastPkts"                     },
+       {0,  18, 2, "etherStatsDropEvents"              },
+       {0,  20, 2, "ifInMulticastPkts"                 },
+       {0,  22, 2, "ifInBroadcastPkts"                 },
+       {0,  24, 2, "inMldChecksumError"                },
+       {0,  26, 2, "inIgmpChecksumError"               },
+       {0,  28, 2, "inMldSpecificQuery"                },
+       {0,  30, 2, "inMldGeneralQuery"                 },
+       {0,  32, 2, "inIgmpSpecificQuery"               },
+       {0,  34, 2, "inIgmpGeneralQuery"                },
+       {0,  36, 2, "inMldLeaves"                       },
+       {0,  38, 2, "inIgmpLeaves"                      },
+
+       {0,  40, 4, "etherStatsOctets"                  },
+       {0,  44, 2, "etherStatsUnderSizePkts"           },
+       {0,  46, 2, "etherOversizeStats"                },
+       {0,  48, 2, "etherStatsPkts64Octets"            },
+       {0,  50, 2, "etherStatsPkts65to127Octets"       },
+       {0,  52, 2, "etherStatsPkts128to255Octets"      },
+       {0,  54, 2, "etherStatsPkts256to511Octets"      },
+       {0,  56, 2, "etherStatsPkts512to1023Octets"     },
+       {0,  58, 2, "etherStatsPkts1024to1518Octets"    },
+
+       {0,  60, 4, "ifOutOctets"                       },
+       {0,  64, 2, "dot3StatsSingleCollisionFrames"    },
+       {0,  66, 2, "dot3StatMultipleCollisionFrames"   },
+       {0,  68, 2, "dot3sDeferredTransmissions"        },
+       {0,  70, 2, "dot3StatsLateCollisions"           },
+       {0,  72, 2, "etherStatsCollisions"              },
+       {0,  74, 2, "dot3StatsExcessiveCollisions"      },
+       {0,  76, 2, "dot3OutPauseFrames"                },
+       {0,  78, 2, "ifOutDiscards"                     },
+       {0,  80, 2, "dot1dTpPortInDiscards"             },
+       {0,  82, 2, "ifOutUcastPkts"                    },
+       {0,  84, 2, "ifOutMulticastPkts"                },
+       {0,  86, 2, "ifOutBroadcastPkts"                },
+       {0,  88, 2, "outOampduPkts"                     },
+       {0,  90, 2, "inOampduPkts"                      },
+       {0,  92, 2, "inIgmpJoinsSuccess"                },
+       {0,  94, 2, "inIgmpJoinsFail"                   },
+       {0,  96, 2, "inMldJoinsSuccess"                 },
+       {0,  98, 2, "inMldJoinsFail"                    },
+       {0, 100, 2, "inReportSuppressionDrop"           },
+       {0, 102, 2, "inLeaveSuppressionDrop"            },
+       {0, 104, 2, "outIgmpReports"                    },
+       {0, 106, 2, "outIgmpLeaves"                     },
+       {0, 108, 2, "outIgmpGeneralQuery"               },
+       {0, 110, 2, "outIgmpSpecificQuery"              },
+       {0, 112, 2, "outMldReports"                     },
+       {0, 114, 2, "outMldLeaves"                      },
+       {0, 116, 2, "outMldGeneralQuery"                },
+       {0, 118, 2, "outMldSpecificQuery"               },
+       {0, 120, 2, "inKnownMulticastPkts"              },
+};
+
+#define REG_RD(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_WR(_smi, _reg, _val)                                       \
+       do {                                                            \
+               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+#define REG_RMW(_smi, _reg, _mask, _val)                               \
+       do {                                                            \
+               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
+               if (err)                                                \
+                       return err;                                     \
+       } while (0)
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_0[] = {
+       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x0301, 0x0026}, {0x1722, 0x0E14},
+       {0x205F, 0x0002}, {0x2059, 0x1A00}, {0x205F, 0x0000}, {0x207F, 0x0002},
+       {0x2077, 0x0000}, {0x2078, 0x0000}, {0x2079, 0x0000}, {0x207A, 0x0000},
+       {0x207B, 0x0000}, {0x207F, 0x0000}, {0x205F, 0x0002}, {0x2053, 0x0000},
+       {0x2054, 0x0000}, {0x2055, 0x0000}, {0x2056, 0x0000}, {0x2057, 0x0000},
+       {0x205F, 0x0000}, {0x12A4, 0x110A}, {0x12A6, 0x150A}, {0x13F1, 0x0013},
+       {0x13F4, 0x0010}, {0x13F5, 0x0000}, {0x0018, 0x0F00}, {0x0038, 0x0F00},
+       {0x0058, 0x0F00}, {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x12B6, 0x0C02},
+       {0x12B7, 0x030F}, {0x12B8, 0x11FF}, {0x12BC, 0x0004}, {0x1362, 0x0115},
+       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0007}, {0x221E, 0x002D}, {0x2218, 0xF030}, {0x221F, 0x0007},
+       {0x221E, 0x0023}, {0x2216, 0x0005}, {0x2215, 0x00B9}, {0x2219, 0x0044},
+       {0x2215, 0x00BA}, {0x2219, 0x0020}, {0x2215, 0x00BB}, {0x2219, 0x00C1},
+       {0x2215, 0x0148}, {0x2219, 0x0096}, {0x2215, 0x016E}, {0x2219, 0x0026},
+       {0x2216, 0x0000}, {0x2216, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
+       {0x221F, 0x0007}, {0x221E, 0x0020}, {0x2215, 0x0D00}, {0x221F, 0x0000},
+       {0x221F, 0x0000}, {0x2217, 0x2160}, {0x221F, 0x0001}, {0x2210, 0xF25E},
+       {0x221F, 0x0007}, {0x221E, 0x0042}, {0x2215, 0x0F00}, {0x2215, 0x0F00},
+       {0x2216, 0x7408}, {0x2215, 0x0E00}, {0x2215, 0x0F00}, {0x2215, 0x0F01},
+       {0x2216, 0x4000}, {0x2215, 0x0E01}, {0x2215, 0x0F01}, {0x2215, 0x0F02},
+       {0x2216, 0x9400}, {0x2215, 0x0E02}, {0x2215, 0x0F02}, {0x2215, 0x0F03},
+       {0x2216, 0x7408}, {0x2215, 0x0E03}, {0x2215, 0x0F03}, {0x2215, 0x0F04},
+       {0x2216, 0x4008}, {0x2215, 0x0E04}, {0x2215, 0x0F04}, {0x2215, 0x0F05},
+       {0x2216, 0x9400}, {0x2215, 0x0E05}, {0x2215, 0x0F05}, {0x2215, 0x0F06},
+       {0x2216, 0x0803}, {0x2215, 0x0E06}, {0x2215, 0x0F06}, {0x2215, 0x0D00},
+       {0x2215, 0x0100}, {0x221F, 0x0001}, {0x2210, 0xF05E}, {0x221F, 0x0000},
+       {0x2217, 0x2100}, {0x221F, 0x0000}, {0x220D, 0x0003}, {0x220E, 0x0015},
+       {0x220D, 0x4003}, {0x220E, 0x0006}, {0x221F, 0x0000}, {0x2200, 0x1340},
+       {0x133F, 0x0010}, {0x12A0, 0x0058}, {0x12A1, 0x0058}, {0x133E, 0x000E},
+       {0x133F, 0x0030}, {0x221F, 0x0000}, {0x2210, 0x0166}, {0x221F, 0x0000},
+       {0x133E, 0x000E}, {0x133F, 0x0010}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8B6E},
+       {0x2206, 0x0000}, {0x220F, 0x0100}, {0x2205, 0x8000}, {0x2206, 0x0280},
+       {0x2206, 0x28F7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+       {0x2206, 0x6602}, {0x2206, 0x80B9}, {0x2206, 0xE08B}, {0x2206, 0x8CE1},
+       {0x2206, 0x8B8D}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x8E1E},
+       {0x2206, 0x01A0}, {0x2206, 0x00E7}, {0x2206, 0xAEDB}, {0x2206, 0xEEE0},
+       {0x2206, 0x120E}, {0x2206, 0xEEE0}, {0x2206, 0x1300}, {0x2206, 0xEEE0},
+       {0x2206, 0x2001}, {0x2206, 0xEEE0}, {0x2206, 0x2166}, {0x2206, 0xEEE0},
+       {0x2206, 0xC463}, {0x2206, 0xEEE0}, {0x2206, 0xC5E8}, {0x2206, 0xEEE0},
+       {0x2206, 0xC699}, {0x2206, 0xEEE0}, {0x2206, 0xC7C2}, {0x2206, 0xEEE0},
+       {0x2206, 0xC801}, {0x2206, 0xEEE0}, {0x2206, 0xC913}, {0x2206, 0xEEE0},
+       {0x2206, 0xCA30}, {0x2206, 0xEEE0}, {0x2206, 0xCB3E}, {0x2206, 0xEEE0},
+       {0x2206, 0xDCE1}, {0x2206, 0xEEE0}, {0x2206, 0xDD00}, {0x2206, 0xEEE2},
+       {0x2206, 0x0001}, {0x2206, 0xEEE2}, {0x2206, 0x0100}, {0x2206, 0xEEE4},
+       {0x2206, 0x8860}, {0x2206, 0xEEE4}, {0x2206, 0x8902}, {0x2206, 0xEEE4},
+       {0x2206, 0x8C00}, {0x2206, 0xEEE4}, {0x2206, 0x8D30}, {0x2206, 0xEEEA},
+       {0x2206, 0x1480}, {0x2206, 0xEEEA}, {0x2206, 0x1503}, {0x2206, 0xEEEA},
+       {0x2206, 0xC600}, {0x2206, 0xEEEA}, {0x2206, 0xC706}, {0x2206, 0xEE85},
+       {0x2206, 0xEE00}, {0x2206, 0xEE85}, {0x2206, 0xEF00}, {0x2206, 0xEE8B},
+       {0x2206, 0x6750}, {0x2206, 0xEE8B}, {0x2206, 0x6632}, {0x2206, 0xEE8A},
+       {0x2206, 0xD448}, {0x2206, 0xEE8A}, {0x2206, 0xD548}, {0x2206, 0xEE8A},
+       {0x2206, 0xD649}, {0x2206, 0xEE8A}, {0x2206, 0xD7F8}, {0x2206, 0xEE8B},
+       {0x2206, 0x85E2}, {0x2206, 0xEE8B}, {0x2206, 0x8700}, {0x2206, 0xEEFF},
+       {0x2206, 0xF600}, {0x2206, 0xEEFF}, {0x2206, 0xF7FC}, {0x2206, 0x04F8},
+       {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2023}, {0x2206, 0xF620},
+       {0x2206, 0xE48B}, {0x2206, 0x8E02}, {0x2206, 0x2877}, {0x2206, 0x0225},
+       {0x2206, 0xC702}, {0x2206, 0x26A1}, {0x2206, 0x0281}, {0x2206, 0xB302},
+       {0x2206, 0x8496}, {0x2206, 0x0202}, {0x2206, 0xA102}, {0x2206, 0x27F1},
+       {0x2206, 0x0228}, {0x2206, 0xF902}, {0x2206, 0x2AA0}, {0x2206, 0x0282},
+       {0x2206, 0xB8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD21}, {0x2206, 0x08F6},
+       {0x2206, 0x21E4}, {0x2206, 0x8B8E}, {0x2206, 0x0202}, {0x2206, 0x80E0},
+       {0x2206, 0x8B8E}, {0x2206, 0xAD22}, {0x2206, 0x05F6}, {0x2206, 0x22E4},
+       {0x2206, 0x8B8E}, {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2305},
+       {0x2206, 0xF623}, {0x2206, 0xE48B}, {0x2206, 0x8EE0}, {0x2206, 0x8B8E},
+       {0x2206, 0xAD24}, {0x2206, 0x08F6}, {0x2206, 0x24E4}, {0x2206, 0x8B8E},
+       {0x2206, 0x0227}, {0x2206, 0x6AE0}, {0x2206, 0x8B8E}, {0x2206, 0xAD25},
+       {0x2206, 0x05F6}, {0x2206, 0x25E4}, {0x2206, 0x8B8E}, {0x2206, 0xE08B},
+       {0x2206, 0x8EAD}, {0x2206, 0x260B}, {0x2206, 0xF626}, {0x2206, 0xE48B},
+       {0x2206, 0x8E02}, {0x2206, 0x830D}, {0x2206, 0x021D}, {0x2206, 0x6BE0},
+       {0x2206, 0x8B8E}, {0x2206, 0xAD27}, {0x2206, 0x05F6}, {0x2206, 0x27E4},
+       {0x2206, 0x8B8E}, {0x2206, 0x0281}, {0x2206, 0x4402}, {0x2206, 0x045C},
+       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B83}, {0x2206, 0xAD23},
+       {0x2206, 0x30E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x2359},
+       {0x2206, 0x02E0}, {0x2206, 0x85EF}, {0x2206, 0xE585}, {0x2206, 0xEFAC},
+       {0x2206, 0x2907}, {0x2206, 0x1F01}, {0x2206, 0x9E51}, {0x2206, 0xAD29},
+       {0x2206, 0x20E0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x06E1},
+       {0x2206, 0x8B84}, {0x2206, 0xAD28}, {0x2206, 0x42E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD21}, {0x2206, 0x06E1}, {0x2206, 0x8B84}, {0x2206, 0xAD29},
+       {0x2206, 0x36BF}, {0x2206, 0x34BF}, {0x2206, 0x022C}, {0x2206, 0x31AE},
+       {0x2206, 0x2EE0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x10E0},
+       {0x2206, 0x8B84}, {0x2206, 0xF620}, {0x2206, 0xE48B}, {0x2206, 0x84EE},
+       {0x2206, 0x8ADA}, {0x2206, 0x00EE}, {0x2206, 0x8ADB}, {0x2206, 0x00E0},
+       {0x2206, 0x8B85}, {0x2206, 0xAD21}, {0x2206, 0x0CE0}, {0x2206, 0x8B84},
+       {0x2206, 0xF621}, {0x2206, 0xE48B}, {0x2206, 0x84EE}, {0x2206, 0x8B72},
+       {0x2206, 0xFFBF}, {0x2206, 0x34C2}, {0x2206, 0x022C}, {0x2206, 0x31FC},
+       {0x2206, 0x04F8}, {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD21}, {0x2206, 0x42E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0},
+       {0x2206, 0x2358}, {0x2206, 0xC059}, {0x2206, 0x021E}, {0x2206, 0x01E1},
+       {0x2206, 0x8B72}, {0x2206, 0x1F10}, {0x2206, 0x9E2F}, {0x2206, 0xE48B},
+       {0x2206, 0x72AD}, {0x2206, 0x2123}, {0x2206, 0xE18B}, {0x2206, 0x84F7},
+       {0x2206, 0x29E5}, {0x2206, 0x8B84}, {0x2206, 0xAC27}, {0x2206, 0x10AC},
+       {0x2206, 0x2605}, {0x2206, 0x0205}, {0x2206, 0x23AE}, {0x2206, 0x1602},
+       {0x2206, 0x0535}, {0x2206, 0x0282}, {0x2206, 0x30AE}, {0x2206, 0x0E02},
+       {0x2206, 0x056A}, {0x2206, 0x0282}, {0x2206, 0x75AE}, {0x2206, 0x0602},
+       {0x2206, 0x04DC}, {0x2206, 0x0282}, {0x2206, 0x04EF}, {0x2206, 0x96FE},
+       {0x2206, 0xFC04}, {0x2206, 0xF8F9}, {0x2206, 0xE08B}, {0x2206, 0x87AD},
+       {0x2206, 0x2321}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+       {0x2206, 0xAD26}, {0x2206, 0x18F6}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F6}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+       {0x2206, 0xE08B}, {0x2206, 0x87AD}, {0x2206, 0x233A}, {0x2206, 0xAD22},
+       {0x2206, 0x37E0}, {0x2206, 0xE020}, {0x2206, 0xE1E0}, {0x2206, 0x21AC},
+       {0x2206, 0x212E}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
+       {0x2206, 0xF627}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xE2EA}, {0x2206, 0x12E3}, {0x2206, 0xEA13}, {0x2206, 0x5A8F},
+       {0x2206, 0x6A20}, {0x2206, 0xE6EA}, {0x2206, 0x12E7}, {0x2206, 0xEA13},
+       {0x2206, 0xF726}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xF727}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
+       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B87},
+       {0x2206, 0xAD23}, {0x2206, 0x38AD}, {0x2206, 0x2135}, {0x2206, 0xE0E0},
+       {0x2206, 0x20E1}, {0x2206, 0xE021}, {0x2206, 0xAC21}, {0x2206, 0x2CE0},
+       {0x2206, 0xEA14}, {0x2206, 0xE1EA}, {0x2206, 0x15F6}, {0x2206, 0x27E4},
+       {0x2206, 0xEA14}, {0x2206, 0xE5EA}, {0x2206, 0x15E2}, {0x2206, 0xEA12},
+       {0x2206, 0xE3EA}, {0x2206, 0x135A}, {0x2206, 0x8FE6}, {0x2206, 0xEA12},
+       {0x2206, 0xE7EA}, {0x2206, 0x13F7}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
+       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2146},
+       {0x2206, 0xE0E0}, {0x2206, 0x22E1}, {0x2206, 0xE023}, {0x2206, 0x58C0},
+       {0x2206, 0x5902}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x651F},
+       {0x2206, 0x109E}, {0x2206, 0x33E4}, {0x2206, 0x8B65}, {0x2206, 0xAD21},
+       {0x2206, 0x22AD}, {0x2206, 0x272A}, {0x2206, 0xD400}, {0x2206, 0x01BF},
+       {0x2206, 0x34F2}, {0x2206, 0x022C}, {0x2206, 0xA2BF}, {0x2206, 0x34F5},
+       {0x2206, 0x022C}, {0x2206, 0xE0E0}, {0x2206, 0x8B67}, {0x2206, 0x1B10},
+       {0x2206, 0xAA14}, {0x2206, 0xE18B}, {0x2206, 0x660D}, {0x2206, 0x1459},
+       {0x2206, 0x0FAE}, {0x2206, 0x05E1}, {0x2206, 0x8B66}, {0x2206, 0x590F},
+       {0x2206, 0xBF85}, {0x2206, 0x6102}, {0x2206, 0x2CA2}, {0x2206, 0xEF96},
+       {0x2206, 0xFEFC}, {0x2206, 0x04F8}, {0x2206, 0xF9FA}, {0x2206, 0xFBEF},
+       {0x2206, 0x79E2}, {0x2206, 0x8AD2}, {0x2206, 0xAC19}, {0x2206, 0x2DE0},
+       {0x2206, 0xE036}, {0x2206, 0xE1E0}, {0x2206, 0x37EF}, {0x2206, 0x311F},
+       {0x2206, 0x325B}, {0x2206, 0x019E}, {0x2206, 0x1F7A}, {0x2206, 0x0159},
+       {0x2206, 0x019F}, {0x2206, 0x0ABF}, {0x2206, 0x348E}, {0x2206, 0x022C},
+       {0x2206, 0x31F6}, {0x2206, 0x06AE}, {0x2206, 0x0FF6}, {0x2206, 0x0302},
+       {0x2206, 0x0470}, {0x2206, 0xF703}, {0x2206, 0xF706}, {0x2206, 0xBF34},
+       {0x2206, 0x9302}, {0x2206, 0x2C31}, {0x2206, 0xAC1A}, {0x2206, 0x25E0},
+       {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x23EF}, {0x2206, 0x300D},
+       {0x2206, 0x311F}, {0x2206, 0x325B}, {0x2206, 0x029E}, {0x2206, 0x157A},
+       {0x2206, 0x0258}, {0x2206, 0xC4A0}, {0x2206, 0x0408}, {0x2206, 0xBF34},
+       {0x2206, 0x9E02}, {0x2206, 0x2C31}, {0x2206, 0xAE06}, {0x2206, 0xBF34},
+       {0x2206, 0x9C02}, {0x2206, 0x2C31}, {0x2206, 0xAC1B}, {0x2206, 0x4AE0},
+       {0x2206, 0xE012}, {0x2206, 0xE1E0}, {0x2206, 0x13EF}, {0x2206, 0x300D},
+       {0x2206, 0x331F}, {0x2206, 0x325B}, {0x2206, 0x1C9E}, {0x2206, 0x3AEF},
+       {0x2206, 0x325B}, {0x2206, 0x1C9F}, {0x2206, 0x09BF}, {0x2206, 0x3498},
+       {0x2206, 0x022C}, {0x2206, 0x3102}, {0x2206, 0x83C5}, {0x2206, 0x5A03},
+       {0x2206, 0x0D03}, {0x2206, 0x581C}, {0x2206, 0x1E20}, {0x2206, 0x0207},
+       {0x2206, 0xA0A0}, {0x2206, 0x000E}, {0x2206, 0x0284}, {0x2206, 0x17AD},
+       {0x2206, 0x1817}, {0x2206, 0xBF34}, {0x2206, 0x9A02}, {0x2206, 0x2C31},
+       {0x2206, 0xAE0F}, {0x2206, 0xBF34}, {0x2206, 0xC802}, {0x2206, 0x2C31},
+       {0x2206, 0xBF34}, {0x2206, 0xC502}, {0x2206, 0x2C31}, {0x2206, 0x0284},
+       {0x2206, 0x52E6}, {0x2206, 0x8AD2}, {0x2206, 0xEF97}, {0x2206, 0xFFFE},
+       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xBF34}, {0x2206, 0xDA02},
+       {0x2206, 0x2CE0}, {0x2206, 0xE58A}, {0x2206, 0xD3BF}, {0x2206, 0x34D4},
+       {0x2206, 0x022C}, {0x2206, 0xE00C}, {0x2206, 0x1159}, {0x2206, 0x02E0},
+       {0x2206, 0x8AD3}, {0x2206, 0x1E01}, {0x2206, 0xE48A}, {0x2206, 0xD3D1},
+       {0x2206, 0x00BF}, {0x2206, 0x34DA}, {0x2206, 0x022C}, {0x2206, 0xA2D1},
+       {0x2206, 0x01BF}, {0x2206, 0x34D4}, {0x2206, 0x022C}, {0x2206, 0xA2BF},
+       {0x2206, 0x34CB}, {0x2206, 0x022C}, {0x2206, 0xE0E5}, {0x2206, 0x8ACE},
+       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CE0}, {0x2206, 0xE58A},
+       {0x2206, 0xCFBF}, {0x2206, 0x8564}, {0x2206, 0x022C}, {0x2206, 0xE0E5},
+       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6A02}, {0x2206, 0x2CE0},
+       {0x2206, 0xE58A}, {0x2206, 0xD1FC}, {0x2206, 0x04F8}, {0x2206, 0xE18A},
+       {0x2206, 0xD1BF}, {0x2206, 0x856A}, {0x2206, 0x022C}, {0x2206, 0xA2E1},
+       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+       {0x2206, 0xE18A}, {0x2206, 0xCFBF}, {0x2206, 0x8567}, {0x2206, 0x022C},
+       {0x2206, 0xA2E1}, {0x2206, 0x8ACE}, {0x2206, 0xBF34}, {0x2206, 0xCB02},
+       {0x2206, 0x2CA2}, {0x2206, 0xE18A}, {0x2206, 0xD3BF}, {0x2206, 0x34DA},
+       {0x2206, 0x022C}, {0x2206, 0xA2E1}, {0x2206, 0x8AD3}, {0x2206, 0x0D11},
+       {0x2206, 0xBF34}, {0x2206, 0xD402}, {0x2206, 0x2CA2}, {0x2206, 0xFC04},
+       {0x2206, 0xF9A0}, {0x2206, 0x0405}, {0x2206, 0xE38A}, {0x2206, 0xD4AE},
+       {0x2206, 0x13A0}, {0x2206, 0x0805}, {0x2206, 0xE38A}, {0x2206, 0xD5AE},
+       {0x2206, 0x0BA0}, {0x2206, 0x0C05}, {0x2206, 0xE38A}, {0x2206, 0xD6AE},
+       {0x2206, 0x03E3}, {0x2206, 0x8AD7}, {0x2206, 0xEF13}, {0x2206, 0xBF34},
+       {0x2206, 0xCB02}, {0x2206, 0x2CA2}, {0x2206, 0xEF13}, {0x2206, 0x0D11},
+       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CA2}, {0x2206, 0xEF13},
+       {0x2206, 0x0D14}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
+       {0x2206, 0xEF13}, {0x2206, 0x0D17}, {0x2206, 0xBF85}, {0x2206, 0x6A02},
+       {0x2206, 0x2CA2}, {0x2206, 0xFD04}, {0x2206, 0xF8E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD27}, {0x2206, 0x2DE0}, {0x2206, 0xE036}, {0x2206, 0xE1E0},
+       {0x2206, 0x37E1}, {0x2206, 0x8B73}, {0x2206, 0x1F10}, {0x2206, 0x9E20},
+       {0x2206, 0xE48B}, {0x2206, 0x73AC}, {0x2206, 0x200B}, {0x2206, 0xAC21},
+       {0x2206, 0x0DAC}, {0x2206, 0x250F}, {0x2206, 0xAC27}, {0x2206, 0x0EAE},
+       {0x2206, 0x0F02}, {0x2206, 0x84CC}, {0x2206, 0xAE0A}, {0x2206, 0x0284},
+       {0x2206, 0xD1AE}, {0x2206, 0x05AE}, {0x2206, 0x0302}, {0x2206, 0x84D8},
+       {0x2206, 0xFC04}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0x0402},
+       {0x2206, 0x84E5}, {0x2206, 0x0285}, {0x2206, 0x2804}, {0x2206, 0x0285},
+       {0x2206, 0x4904}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0xEE8B},
+       {0x2206, 0x6902}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B85},
+       {0x2206, 0xAD26}, {0x2206, 0x38D0}, {0x2206, 0x0B02}, {0x2206, 0x2B4D},
+       {0x2206, 0x5882}, {0x2206, 0x7882}, {0x2206, 0x9F2D}, {0x2206, 0xE08B},
+       {0x2206, 0x68E1}, {0x2206, 0x8B69}, {0x2206, 0x1F10}, {0x2206, 0x9EC8},
+       {0x2206, 0x10E4}, {0x2206, 0x8B68}, {0x2206, 0xE0E0}, {0x2206, 0x00E1},
+       {0x2206, 0xE001}, {0x2206, 0xF727}, {0x2206, 0xE4E0}, {0x2206, 0x00E5},
+       {0x2206, 0xE001}, {0x2206, 0xE2E0}, {0x2206, 0x20E3}, {0x2206, 0xE021},
+       {0x2206, 0xAD30}, {0x2206, 0xF7F6}, {0x2206, 0x27E4}, {0x2206, 0xE000},
+       {0x2206, 0xE5E0}, {0x2206, 0x01FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
+       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2212},
+       {0x2206, 0xE0E0}, {0x2206, 0x14E1}, {0x2206, 0xE015}, {0x2206, 0xAD26},
+       {0x2206, 0x9CE1}, {0x2206, 0x85E0}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x04F8},
+       {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B86}, {0x2206, 0xAD22},
+       {0x2206, 0x09E1}, {0x2206, 0x85E1}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
+       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x0464},
+       {0x2206, 0xE48C}, {0x2206, 0xFDE4}, {0x2206, 0x80CA}, {0x2206, 0xE480},
+       {0x2206, 0x66E0}, {0x2206, 0x8E70}, {0x2206, 0xE076}, {0x2205, 0xE142},
+       {0x2206, 0x0701}, {0x2205, 0xE140}, {0x2206, 0x0405}, {0x220F, 0x0000},
+       {0x221F, 0x0000}, {0x2200, 0x1340}, {0x133E, 0x000E}, {0x133F, 0x0010},
+       {0x13EB, 0x11BB}
+};
+
+static const struct rtl8367b_initval rtl8367r_vb_initvals_1[] = {
+       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x1305, 0xC000}, {0x121E, 0x03CA},
+       {0x1233, 0x0352}, {0x1234, 0x0064}, {0x1237, 0x0096}, {0x1238, 0x0078},
+       {0x1239, 0x0084}, {0x123A, 0x0030}, {0x205F, 0x0002}, {0x2059, 0x1A00},
+       {0x205F, 0x0000}, {0x207F, 0x0002}, {0x2077, 0x0000}, {0x2078, 0x0000},
+       {0x2079, 0x0000}, {0x207A, 0x0000}, {0x207B, 0x0000}, {0x207F, 0x0000},
+       {0x205F, 0x0002}, {0x2053, 0x0000}, {0x2054, 0x0000}, {0x2055, 0x0000},
+       {0x2056, 0x0000}, {0x2057, 0x0000}, {0x205F, 0x0000}, {0x133F, 0x0030},
+       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2205, 0x8B86}, {0x2206, 0x800E},
+       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x12A3, 0x2200}, {0x6107, 0xE58B},
+       {0x6103, 0xA970}, {0x0018, 0x0F00}, {0x0038, 0x0F00}, {0x0058, 0x0F00},
+       {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x133F, 0x0030}, {0x133E, 0x000E},
+       {0x221F, 0x0005}, {0x2205, 0x8B6E}, {0x2206, 0x0000}, {0x220F, 0x0100},
+       {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8000}, {0x2206, 0x0280},
+       {0x2206, 0x2BF7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
+       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
+       {0x2206, 0x6602}, {0x2206, 0x8044}, {0x2206, 0x0201}, {0x2206, 0x7CE0},
+       {0x2206, 0x8B8C}, {0x2206, 0xE18B}, {0x2206, 0x8D1E}, {0x2206, 0x01E1},
+       {0x2206, 0x8B8E}, {0x2206, 0x1E01}, {0x2206, 0xA000}, {0x2206, 0xE4AE},
+       {0x2206, 0xD8EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE}, {0x2206, 0x85C1},
+       {0x2206, 0x00EE}, {0x2206, 0x8AFC}, {0x2206, 0x07EE}, {0x2206, 0x8AFD},
+       {0x2206, 0x73EE}, {0x2206, 0xFFF6}, {0x2206, 0x00EE}, {0x2206, 0xFFF7},
+       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD20},
+       {0x2206, 0x0302}, {0x2206, 0x8050}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
+       {0x2206, 0xE08B}, {0x2206, 0x85AD}, {0x2206, 0x2548}, {0x2206, 0xE08A},
+       {0x2206, 0xE4E1}, {0x2206, 0x8AE5}, {0x2206, 0x7C00}, {0x2206, 0x009E},
+       {0x2206, 0x35EE}, {0x2206, 0x8AE4}, {0x2206, 0x00EE}, {0x2206, 0x8AE5},
+       {0x2206, 0x00E0}, {0x2206, 0x8AFC}, {0x2206, 0xE18A}, {0x2206, 0xFDE2},
+       {0x2206, 0x85C0}, {0x2206, 0xE385}, {0x2206, 0xC102}, {0x2206, 0x2DAC},
+       {0x2206, 0xAD20}, {0x2206, 0x12EE}, {0x2206, 0x8AE4}, {0x2206, 0x03EE},
+       {0x2206, 0x8AE5}, {0x2206, 0xB7EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE},
+       {0x2206, 0x85C1}, {0x2206, 0x00AE}, {0x2206, 0x1115}, {0x2206, 0xE685},
+       {0x2206, 0xC0E7}, {0x2206, 0x85C1}, {0x2206, 0xAE08}, {0x2206, 0xEE85},
+       {0x2206, 0xC000}, {0x2206, 0xEE85}, {0x2206, 0xC100}, {0x2206, 0xFDFC},
+       {0x2206, 0x0400}, {0x2205, 0xE142}, {0x2206, 0x0701}, {0x2205, 0xE140},
+       {0x2206, 0x0405}, {0x220F, 0x0000}, {0x221F, 0x0000}, {0x133E, 0x000E},
+       {0x133F, 0x0010}, {0x13EB, 0x11BB}, {0x207F, 0x0002}, {0x2073, 0x1D22},
+       {0x207F, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x2200, 0x1340},
+       {0x133E, 0x000E}, {0x133F, 0x0010},
+};
+
+static int rtl8367b_write_initvals(struct rtl8366_smi *smi,
+                                 const struct rtl8367b_initval *initvals,
+                                 int count)
+{
+       int err;
+       int i;
+
+       for (i = 0; i < count; i++)
+               REG_WR(smi, initvals[i].reg, initvals[i].val);
+
+       return 0;
+}
+
+static int rtl8367b_read_phy_reg(struct rtl8366_smi *smi,
+                               u32 phy_addr, u32 phy_reg, u32 *val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367B_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* prepare address */
+       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send read command */
+       REG_WR(smi, RTL8367B_IA_CTRL_REG,
+              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_READ);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy read timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       /* read data */
+       REG_RD(smi, RTL8367B_IA_READ_DATA_REG, val);
+
+       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, *val);
+       return 0;
+}
+
+static int rtl8367b_write_phy_reg(struct rtl8366_smi *smi,
+                                u32 phy_addr, u32 phy_reg, u32 val)
+{
+       int timeout;
+       u32 data;
+       int err;
+
+       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
+               phy_addr, phy_reg, val);
+
+       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
+               return -EINVAL;
+
+       if (phy_reg > RTL8367B_PHY_REG_MAX)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
+               return -ETIMEDOUT;
+
+       /* preapre data */
+       REG_WR(smi, RTL8367B_IA_WRITE_DATA_REG, val);
+
+       /* prepare address */
+       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
+              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
+
+       /* send write command */
+       REG_WR(smi, RTL8367B_IA_CTRL_REG,
+              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_WRITE);
+
+       timeout = 5;
+       do {
+               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
+               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
+                       break;
+
+               if (timeout--) {
+                       dev_err(smi->parent, "phy write timed out\n");
+                       return -ETIMEDOUT;
+               }
+
+               udelay(1);
+       } while (1);
+
+       return 0;
+}
+
+static int rtl8367b_init_regs(struct rtl8366_smi *smi)
+{
+       const struct rtl8367b_initval *initvals;
+       u32 chip_ver;
+       u32 rlvid;
+       int count;
+       int err;
+
+       REG_WR(smi, RTL8367B_RTL_MAGIC_ID_REG, RTL8367B_RTL_MAGIC_ID_VAL);
+       REG_RD(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+
+       rlvid = (chip_ver >> RTL8367B_CHIP_VER_RLVID_SHIFT) &
+               RTL8367B_CHIP_VER_RLVID_MASK;
+
+       switch (rlvid) {
+       case 0:
+               initvals = rtl8367r_vb_initvals_0;
+               count = ARRAY_SIZE(rtl8367r_vb_initvals_0);
+               break;
+
+       case 1:
+               initvals = rtl8367r_vb_initvals_1;
+               count = ARRAY_SIZE(rtl8367r_vb_initvals_1);
+               break;
+
+       default:
+               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
+               return -ENODEV;
+       }
+
+       /* TODO: disable RLTP */
+
+       return rtl8367b_write_initvals(smi, initvals, count);
+}
+
+static int rtl8367b_reset_chip(struct rtl8366_smi *smi)
+{
+       int timeout = 10;
+       int err;
+       u32 data;
+
+       REG_WR(smi, RTL8367B_CHIP_RESET_REG, RTL8367B_CHIP_RESET_HW);
+       msleep(RTL8367B_RESET_DELAY);
+
+       do {
+               REG_RD(smi, RTL8367B_CHIP_RESET_REG, &data);
+               if (!(data & RTL8367B_CHIP_RESET_HW))
+                       break;
+
+               msleep(1);
+       } while (--timeout);
+
+       if (!timeout) {
+               dev_err(smi->parent, "chip reset timed out\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
+                                  enum rtl8367_extif_mode mode)
+{
+       int err;
+
+       /* set port mode */
+       switch (mode) {
+       case RTL8367_EXTIF_MODE_RGMII:
+       case RTL8367_EXTIF_MODE_RGMII_33V:
+               REG_WR(smi, RTL8367B_CHIP_DEBUG0_REG, 0x0367);
+               REG_WR(smi, RTL8367B_CHIP_DEBUG1_REG, 0x7777);
+               break;
+
+       case RTL8367_EXTIF_MODE_TMII_MAC:
+       case RTL8367_EXTIF_MODE_TMII_PHY:
+               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), BIT((id + 1) % 2));
+               break;
+
+       case RTL8367_EXTIF_MODE_GMII:
+               REG_RMW(smi, RTL8367B_CHIP_DEBUG0_REG,
+                       RTL8367B_CHIP_DEBUG0_DUMMY0(id),
+                       RTL8367B_CHIP_DEBUG0_DUMMY0(id));
+               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), BIT(6));
+               break;
+
+       case RTL8367_EXTIF_MODE_MII_MAC:
+       case RTL8367_EXTIF_MODE_MII_PHY:
+       case RTL8367_EXTIF_MODE_DISABLED:
+               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
+                       BIT((id + 1) % 2), 0);
+               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), 0);
+               break;
+
+       default:
+               dev_err(smi->parent,
+                       "invalid mode for external interface %d\n", id);
+               return -EINVAL;
+       }
+
+       REG_RMW(smi, RTL8367B_DIS_REG,
+               RTL8367B_DIS_RGMII_MASK << RTL8367B_DIS_RGMII_SHIFT(id),
+               mode << RTL8367B_DIS_RGMII_SHIFT(id));
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_force(struct rtl8366_smi *smi, int id,
+                                   struct rtl8367_port_ability *pa)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367B_DI_FORCE_MODE |
+               RTL8367B_DI_FORCE_NWAY |
+               RTL8367B_DI_FORCE_TXPAUSE |
+               RTL8367B_DI_FORCE_RXPAUSE |
+               RTL8367B_DI_FORCE_LINK |
+               RTL8367B_DI_FORCE_DUPLEX |
+               RTL8367B_DI_FORCE_SPEED_MASK);
+
+       val = pa->speed;
+       val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
+       val |= pa->nway ? RTL8367B_DI_FORCE_NWAY : 0;
+       val |= pa->txpause ? RTL8367B_DI_FORCE_TXPAUSE : 0;
+       val |= pa->rxpause ? RTL8367B_DI_FORCE_RXPAUSE : 0;
+       val |= pa->link ? RTL8367B_DI_FORCE_LINK : 0;
+       val |= pa->duplex ? RTL8367B_DI_FORCE_DUPLEX : 0;
+
+       REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367b_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
+                                        unsigned txdelay, unsigned rxdelay)
+{
+       u32 mask;
+       u32 val;
+       int err;
+
+       mask = (RTL8367B_EXT_RGMXF_RXDELAY_MASK |
+               (RTL8367B_EXT_RGMXF_TXDELAY_MASK <<
+                       RTL8367B_EXT_RGMXF_TXDELAY_SHIFT));
+
+       val = rxdelay;
+       val |= txdelay << RTL8367B_EXT_RGMXF_TXDELAY_SHIFT;
+
+       REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), mask, val);
+
+       return 0;
+}
+
+static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id,
+                              struct rtl8367_extif_config *cfg)
+{
+       enum rtl8367_extif_mode mode;
+       int err;
+
+       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
+
+       err = rtl8367b_extif_set_mode(smi, id, mode);
+       if (err)
+               return err;
+
+       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
+               err = rtl8367b_extif_set_force(smi, id, &cfg->ability);
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_set_rgmii_delay(smi, id, cfg->txdelay,
+                                                    cfg->rxdelay);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+                                 const char *name)
+{
+       struct rtl8367_extif_config *cfg;
+       const __be32 *prop;
+       int size;
+       int err;
+
+       prop = of_get_property(smi->parent->of_node, name, &size);
+       if (!prop)
+               return rtl8367b_extif_init(smi, id, NULL);
+
+       if (size != (9 * sizeof(*prop))) {
+               dev_err(smi->parent, "%s property is invalid\n", name);
+               return -EINVAL;
+       }
+
+       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
+       if (!cfg)
+               return -ENOMEM;
+
+       cfg->txdelay = be32_to_cpup(prop++);
+       cfg->rxdelay = be32_to_cpup(prop++);
+       cfg->mode = be32_to_cpup(prop++);
+       cfg->ability.force_mode = be32_to_cpup(prop++);
+       cfg->ability.txpause = be32_to_cpup(prop++);
+       cfg->ability.rxpause = be32_to_cpup(prop++);
+       cfg->ability.link = be32_to_cpup(prop++);
+       cfg->ability.duplex = be32_to_cpup(prop++);
+       cfg->ability.speed = be32_to_cpup(prop++);
+
+       err = rtl8367b_extif_init(smi, id, cfg);
+       kfree(cfg);
+
+       return err;
+}
+#else
+static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
+                                 const char *name)
+{
+       return -EINVAL;
+}
+#endif
+
+static int rtl8367b_setup(struct rtl8366_smi *smi)
+{
+       struct rtl8367_platform_data *pdata;
+       int err;
+       int i;
+
+       pdata = smi->parent->platform_data;
+
+       err = rtl8367b_init_regs(smi);
+       if (err)
+               return err;
+
+       /* initialize external interfaces */
+       if (smi->parent->of_node) {
+               err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0");
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1");
+               if (err)
+                       return err;
+       } else {
+               err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg);
+               if (err)
+                       return err;
+
+               err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg);
+               if (err)
+                       return err;
+       }
+
+       /* set maximum packet length to 1536 bytes */
+       REG_RMW(smi, RTL8367B_SWC0_REG, RTL8367B_SWC0_MAX_LENGTH_MASK,
+               RTL8367B_SWC0_MAX_LENGTH_1536);
+
+       /*
+        * discard VLAN tagged packets if the port is not a member of
+        * the VLAN with which the packets is associated.
+        */
+       REG_WR(smi, RTL8367B_VLAN_INGRESS_REG, RTL8367B_PORTS_ALL);
+
+       /*
+        * Setup egress tag mode for each port.
+        */
+       for (i = 0; i < RTL8367B_NUM_PORTS; i++)
+               REG_RMW(smi,
+                       RTL8367B_PORT_MISC_CFG_REG(i),
+                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK <<
+                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT,
+                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL <<
+                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT);
+
+       return 0;
+}
+
+static int rtl8367b_get_mib_counter(struct rtl8366_smi *smi, int counter,
+                                   int port, unsigned long long *val)
+{
+       struct rtl8366_mib_counter *mib;
+       int offset;
+       int i;
+       int err;
+       u32 addr, data;
+       u64 mibvalue;
+
+       if (port > RTL8367B_NUM_PORTS ||
+           counter >= RTL8367B_NUM_MIB_COUNTERS)
+               return -EINVAL;
+
+       mib = &rtl8367b_mib_counters[counter];
+       addr = RTL8367B_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
+
+       /*
+        * Writing access counter address first
+        * then ASIC will prepare 64bits counter wait for being retrived
+        */
+       REG_WR(smi, RTL8367B_MIB_ADDRESS_REG, addr >> 2);
+
+       /* read MIB control register */
+       REG_RD(smi, RTL8367B_MIB_CTRL0_REG(0), &data);
+
+       if (data & RTL8367B_MIB_CTRL0_BUSY_MASK)
+               return -EBUSY;
+
+       if (data & RTL8367B_MIB_CTRL0_RESET_MASK)
+               return -EIO;
+
+       if (mib->length == 4)
+               offset = 3;
+       else
+               offset = (mib->offset + 1) % 4;
+
+       mibvalue = 0;
+       for (i = 0; i < mib->length; i++) {
+               REG_RD(smi, RTL8367B_MIB_COUNTER_REG(offset - i), &data);
+               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
+       }
+
+       *val = mibvalue;
+       return 0;
+}
+
+static int rtl8367b_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
+                               struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+       int err;
+       int i;
+
+       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
+
+       if (vid >= RTL8367B_NUM_VIDS)
+               return -EINVAL;
+
+       /* write VID */
+       REG_WR(smi, RTL8367B_TA_ADDR_REG, vid);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_READ);
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367B_TA_RDDATA_REG(i), &data[i]);
+
+       vlan4k->vid = vid;
+       vlan4k->member = (data[0] >> RTL8367B_TA_VLAN0_MEMBER_SHIFT) &
+                        RTL8367B_TA_VLAN0_MEMBER_MASK;
+       vlan4k->untag = (data[0] >> RTL8367B_TA_VLAN0_UNTAG_SHIFT) &
+                       RTL8367B_TA_VLAN0_UNTAG_MASK;
+       vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
+                     RTL8367B_TA_VLAN1_FID_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
+                               const struct rtl8366_vlan_4k *vlan4k)
+{
+       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
+       int err;
+       int i;
+
+       if (vlan4k->vid >= RTL8367B_NUM_VIDS ||
+           vlan4k->member > RTL8367B_TA_VLAN0_MEMBER_MASK ||
+           vlan4k->untag > RTL8367B_UNTAG_MASK ||
+           vlan4k->fid > RTL8367B_FIDMAX)
+               return -EINVAL;
+
+       memset(data, 0, sizeof(data));
+
+       data[0] = (vlan4k->member & RTL8367B_TA_VLAN0_MEMBER_MASK) <<
+                 RTL8367B_TA_VLAN0_MEMBER_SHIFT;
+       data[0] |= (vlan4k->untag & RTL8367B_TA_VLAN0_UNTAG_MASK) <<
+                  RTL8367B_TA_VLAN0_UNTAG_SHIFT;
+       data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
+                 RTL8367B_TA_VLAN1_FID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367B_TA_WRDATA_REG(i), data[i]);
+
+       /* write VID */
+       REG_WR(smi, RTL8367B_TA_ADDR_REG,
+              vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
+
+       /* write table access control word */
+       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_WRITE);
+
+       return 0;
+}
+
+static int rtl8367b_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+       int err;
+       int i;
+
+       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
+
+       if (index >= RTL8367B_NUM_VLANS)
+               return -EINVAL;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_RD(smi, RTL8367B_VLAN_MC_BASE(index) + i, &data[i]);
+
+       vlanmc->member = (data[0] >> RTL8367B_VLAN_MC0_MEMBER_SHIFT) &
+                        RTL8367B_VLAN_MC0_MEMBER_MASK;
+       vlanmc->fid = (data[1] >> RTL8367B_VLAN_MC1_FID_SHIFT) &
+                     RTL8367B_VLAN_MC1_FID_MASK;
+       vlanmc->vid = (data[3] >> RTL8367B_VLAN_MC3_EVID_SHIFT) &
+                     RTL8367B_VLAN_MC3_EVID_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
+                               const struct rtl8366_vlan_mc *vlanmc)
+{
+       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
+       int err;
+       int i;
+
+       if (index >= RTL8367B_NUM_VLANS ||
+           vlanmc->vid >= RTL8367B_NUM_VIDS ||
+           vlanmc->priority > RTL8367B_PRIORITYMAX ||
+           vlanmc->member > RTL8367B_VLAN_MC0_MEMBER_MASK ||
+           vlanmc->untag > RTL8367B_UNTAG_MASK ||
+           vlanmc->fid > RTL8367B_FIDMAX)
+               return -EINVAL;
+
+       data[0] = (vlanmc->member & RTL8367B_VLAN_MC0_MEMBER_MASK) <<
+                 RTL8367B_VLAN_MC0_MEMBER_SHIFT;
+       data[1] = (vlanmc->fid & RTL8367B_VLAN_MC1_FID_MASK) <<
+                 RTL8367B_VLAN_MC1_FID_SHIFT;
+       data[2] = 0;
+       data[3] = (vlanmc->vid & RTL8367B_VLAN_MC3_EVID_MASK) <<
+                  RTL8367B_VLAN_MC3_EVID_SHIFT;
+
+       for (i = 0; i < ARRAY_SIZE(data); i++)
+               REG_WR(smi, RTL8367B_VLAN_MC_BASE(index) + i, data[i]);
+
+       return 0;
+}
+
+static int rtl8367b_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
+{
+       u32 data;
+       int err;
+
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
+
+       *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
+              RTL8367B_VLAN_PVID_CTRL_MASK;
+
+       return 0;
+}
+
+static int rtl8367b_set_mc_index(struct rtl8366_smi *smi, int port, int index)
+{
+       if (port >= RTL8367B_NUM_PORTS || index >= RTL8367B_NUM_VLANS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
+                               RTL8367B_VLAN_PVID_CTRL_MASK <<
+                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port),
+                               (index & RTL8367B_VLAN_PVID_CTRL_MASK) <<
+                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port));
+}
+
+static int rtl8367b_enable_vlan(struct rtl8366_smi *smi, int enable)
+{
+       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_CTRL_REG,
+                               RTL8367B_VLAN_CTRL_ENABLE,
+                               (enable) ? RTL8367B_VLAN_CTRL_ENABLE : 0);
+}
+
+static int rtl8367b_enable_vlan4k(struct rtl8366_smi *smi, int enable)
+{
+       return 0;
+}
+
+static int rtl8367b_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
+{
+       unsigned max = RTL8367B_NUM_VLANS;
+
+       if (smi->vlan4k_enabled)
+               max = RTL8367B_NUM_VIDS - 1;
+
+       if (vlan == 0 || vlan >= max)
+               return 0;
+
+       return 1;
+}
+
+static int rtl8367b_enable_port(struct rtl8366_smi *smi, int port, int enable)
+{
+       int err;
+
+       REG_WR(smi, RTL8367B_PORT_ISOLATION_REG(port),
+              (enable) ? RTL8367B_PORTS_ALL : 0);
+
+       return 0;
+}
+
+static int rtl8367b_sw_reset_mibs(struct switch_dev *dev,
+                                 const struct switch_attr *attr,
+                                 struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(0), 0,
+                               RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK);
+}
+
+static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
+                                   int port,
+                                   struct switch_port_link *link)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data = 0;
+       u32 speed;
+
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
+
+       link->link = !!(data & RTL8367B_PORT_STATUS_LINK);
+       if (!link->link)
+               return 0;
+
+       link->duplex = !!(data & RTL8367B_PORT_STATUS_DUPLEX);
+       link->rx_flow = !!(data & RTL8367B_PORT_STATUS_RXPAUSE);
+       link->tx_flow = !!(data & RTL8367B_PORT_STATUS_TXPAUSE);
+       link->aneg = !!(data & RTL8367B_PORT_STATUS_NWAY);
+
+       speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
+       switch (speed) {
+       case 0:
+               link->speed = SWITCH_PORT_SPEED_10;
+               break;
+       case 1:
+               link->speed = SWITCH_PORT_SPEED_100;
+               break;
+       case 2:
+               link->speed = SWITCH_PORT_SPEED_1000;
+               break;
+       default:
+               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
+               break;
+       }
+
+       return 0;
+}
+
+static int rtl8367b_sw_get_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 data;
+
+       rtl8366_smi_read_reg(smi, RTL8367B_SWC0_REG, &data);
+       val->value.i = (data & RTL8367B_SWC0_MAX_LENGTH_MASK) >>
+                       RTL8367B_SWC0_MAX_LENGTH_SHIFT;
+
+       return 0;
+}
+
+static int rtl8367b_sw_set_max_length(struct switch_dev *dev,
+                                    const struct switch_attr *attr,
+                                    struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       u32 max_len;
+
+       switch (val->value.i) {
+       case 0:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1522;
+               break;
+       case 1:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1536;
+               break;
+       case 2:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_1552;
+               break;
+       case 3:
+               max_len = RTL8367B_SWC0_MAX_LENGTH_16000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_SWC0_REG,
+                               RTL8367B_SWC0_MAX_LENGTH_MASK, max_len);
+}
+
+
+static int rtl8367b_sw_reset_port_mibs(struct switch_dev *dev,
+                                      const struct switch_attr *attr,
+                                      struct switch_val *val)
+{
+       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
+       int port;
+
+       port = val->port_vlan;
+       if (port >= RTL8367B_NUM_PORTS)
+               return -EINVAL;
+
+       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(port / 8), 0,
+                               RTL8367B_MIB_CTRL0_PORT_RESET_MASK(port % 8));
+}
+
+static int rtl8367b_sw_get_port_stats(struct switch_dev *dev, int port,
+                                        struct switch_port_stats *stats)
+{
+       return (rtl8366_sw_get_port_stats(dev, port, stats,
+                               RTL8367B_MIB_TXB_ID, RTL8367B_MIB_RXB_ID));
+}
+
+static struct switch_attr rtl8367b_globals[] = {
+       {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan",
+               .description = "Enable VLAN mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 1
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "enable_vlan4k",
+               .description = "Enable VLAN 4K mode",
+               .set = rtl8366_sw_set_vlan_enable,
+               .get = rtl8366_sw_get_vlan_enable,
+               .max = 1,
+               .ofs = 2
+       }, {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mibs",
+               .description = "Reset all MIB counters",
+               .set = rtl8367b_sw_reset_mibs,
+       }, {
+               .type = SWITCH_TYPE_INT,
+               .name = "max_length",
+               .description = "Get/Set the maximum length of valid packets"
+                              "(0:1522, 1:1536, 2:1552, 3:16000)",
+               .set = rtl8367b_sw_set_max_length,
+               .get = rtl8367b_sw_get_max_length,
+               .max = 3,
+       }
+};
+
+static struct switch_attr rtl8367b_port[] = {
+       {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset_mib",
+               .description = "Reset single port MIB counters",
+               .set = rtl8367b_sw_reset_port_mibs,
+       }, {
+               .type = SWITCH_TYPE_STRING,
+               .name = "mib",
+               .description = "Get MIB counters for port",
+               .max = 33,
+               .set = NULL,
+               .get = rtl8366_sw_get_port_mib,
+       },
+};
+
+static struct switch_attr rtl8367b_vlan[] = {
+       {
+               .type = SWITCH_TYPE_STRING,
+               .name = "info",
+               .description = "Get vlan information",
+               .max = 1,
+               .set = NULL,
+               .get = rtl8366_sw_get_vlan_info,
+       },
+};
+
+static const struct switch_dev_ops rtl8367b_sw_ops = {
+       .attr_global = {
+               .attr = rtl8367b_globals,
+               .n_attr = ARRAY_SIZE(rtl8367b_globals),
+       },
+       .attr_port = {
+               .attr = rtl8367b_port,
+               .n_attr = ARRAY_SIZE(rtl8367b_port),
+       },
+       .attr_vlan = {
+               .attr = rtl8367b_vlan,
+               .n_attr = ARRAY_SIZE(rtl8367b_vlan),
+       },
+
+       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
+       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
+       .get_port_pvid = rtl8366_sw_get_port_pvid,
+       .set_port_pvid = rtl8366_sw_set_port_pvid,
+       .reset_switch = rtl8366_sw_reset_switch,
+       .get_port_link = rtl8367b_sw_get_port_link,
+       .get_port_stats = rtl8367b_sw_get_port_stats,
+};
+
+static int rtl8367b_switch_init(struct rtl8366_smi *smi)
+{
+       struct switch_dev *dev = &smi->sw_dev;
+       int err;
+
+       dev->name = "RTL8367B";
+       dev->cpu_port = RTL8367B_CPU_PORT_NUM;
+       dev->ports = RTL8367B_NUM_PORTS;
+       dev->vlans = RTL8367B_NUM_VIDS;
+       dev->ops = &rtl8367b_sw_ops;
+       dev->alias = dev_name(smi->parent);
+
+       err = register_switch(dev, NULL);
+       if (err)
+               dev_err(smi->parent, "switch registration failed\n");
+
+       return err;
+}
+
+static void rtl8367b_switch_cleanup(struct rtl8366_smi *smi)
+{
+       unregister_switch(&smi->sw_dev);
+}
+
+static int rtl8367b_mii_read(struct mii_bus *bus, int addr, int reg)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 val = 0;
+       int err;
+
+       err = rtl8367b_read_phy_reg(smi, addr, reg, &val);
+       if (err)
+               return 0xffff;
+
+       return val;
+}
+
+static int rtl8367b_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
+{
+       struct rtl8366_smi *smi = bus->priv;
+       u32 t;
+       int err;
+
+       err = rtl8367b_write_phy_reg(smi, addr, reg, val);
+       if (err)
+               return err;
+
+       /* flush write */
+       (void) rtl8367b_read_phy_reg(smi, addr, reg, &t);
+
+       return err;
+}
+
+static int rtl8367b_detect(struct rtl8366_smi *smi)
+{
+       const char *chip_name;
+       u32 chip_num;
+       u32 chip_ver;
+       u32 chip_mode;
+       int ret;
+
+       /* TODO: improve chip detection */
+       rtl8366_smi_write_reg(smi, RTL8367B_RTL_MAGIC_ID_REG,
+                             RTL8367B_RTL_MAGIC_ID_VAL);
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_NUMBER_REG, &chip_num);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip number");
+               return ret;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip version");
+               return ret;
+       }
+
+       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_MODE_REG, &chip_mode);
+       if (ret) {
+               dev_err(smi->parent, "unable to read %s register\n",
+                       "chip mode");
+               return ret;
+       }
+
+       switch (chip_ver) {
+       case 0x1000:
+               chip_name = "8367RB";
+               break;
+       case 0x1010:
+               chip_name = "8367R-VB";
+               break;
+       default:
+               dev_err(smi->parent,
+                       "unknown chip num:%04x ver:%04x, mode:%04x\n",
+                       chip_num, chip_ver, chip_mode);
+               return -ENODEV;
+       }
+
+       dev_info(smi->parent, "RTL%s chip found\n", chip_name);
+
+       return 0;
+}
+
+static struct rtl8366_smi_ops rtl8367b_smi_ops = {
+       .detect         = rtl8367b_detect,
+       .reset_chip     = rtl8367b_reset_chip,
+       .setup          = rtl8367b_setup,
+
+       .mii_read       = rtl8367b_mii_read,
+       .mii_write      = rtl8367b_mii_write,
+
+       .get_vlan_mc    = rtl8367b_get_vlan_mc,
+       .set_vlan_mc    = rtl8367b_set_vlan_mc,
+       .get_vlan_4k    = rtl8367b_get_vlan_4k,
+       .set_vlan_4k    = rtl8367b_set_vlan_4k,
+       .get_mc_index   = rtl8367b_get_mc_index,
+       .set_mc_index   = rtl8367b_set_mc_index,
+       .get_mib_counter = rtl8367b_get_mib_counter,
+       .is_vlan_valid  = rtl8367b_is_vlan_valid,
+       .enable_vlan    = rtl8367b_enable_vlan,
+       .enable_vlan4k  = rtl8367b_enable_vlan4k,
+       .enable_port    = rtl8367b_enable_port,
+};
+
+static int  rtl8367b_probe(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi;
+       int err;
+
+       smi = rtl8366_smi_probe(pdev);
+       if (!smi)
+               return -ENODEV;
+
+       smi->clk_delay = 1500;
+       smi->cmd_read = 0xb9;
+       smi->cmd_write = 0xb8;
+       smi->ops = &rtl8367b_smi_ops;
+       smi->cpu_port = RTL8367B_CPU_PORT_NUM;
+       smi->num_ports = RTL8367B_NUM_PORTS;
+       smi->num_vlan_mc = RTL8367B_NUM_VLANS;
+       smi->mib_counters = rtl8367b_mib_counters;
+       smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters);
+
+       err = rtl8366_smi_init(smi);
+       if (err)
+               goto err_free_smi;
+
+       platform_set_drvdata(pdev, smi);
+
+       err = rtl8367b_switch_init(smi);
+       if (err)
+               goto err_clear_drvdata;
+
+       return 0;
+
+ err_clear_drvdata:
+       platform_set_drvdata(pdev, NULL);
+       rtl8366_smi_cleanup(smi);
+ err_free_smi:
+       kfree(smi);
+       return err;
+}
+
+static int rtl8367b_remove(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi) {
+               rtl8367b_switch_cleanup(smi);
+               platform_set_drvdata(pdev, NULL);
+               rtl8366_smi_cleanup(smi);
+               kfree(smi);
+       }
+
+       return 0;
+}
+
+static void rtl8367b_shutdown(struct platform_device *pdev)
+{
+       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
+
+       if (smi)
+               rtl8367b_reset_chip(smi);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtl8367b_match[] = {
+       { .compatible = "realtek,rtl8367b" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rtl8367b_match);
+#endif
+
+static struct platform_driver rtl8367b_driver = {
+       .driver = {
+               .name           = RTL8367B_DRIVER_NAME,
+               .owner          = THIS_MODULE,
+#ifdef CONFIG_OF
+               .of_match_table = of_match_ptr(rtl8367b_match),
+#endif
+       },
+       .probe          = rtl8367b_probe,
+       .remove         = rtl8367b_remove,
+       .shutdown       = rtl8367b_shutdown,
+};
+
+module_platform_driver(rtl8367b_driver);
+
+MODULE_DESCRIPTION("Realtek RTL8367B ethernet switch driver");
+MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" RTL8367B_DRIVER_NAME);
+
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/swconfig.c b/target/linux/generic/files-4.9/drivers/net/phy/swconfig.c
new file mode 100644 (file)
index 0000000..e8a6847
--- /dev/null
@@ -0,0 +1,1256 @@
+/*
+ * swconfig.c: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/capability.h>
+#include <linux/skbuff.h>
+#include <linux/switch.h>
+#include <linux/of.h>
+#include <linux/version.h>
+#include <uapi/linux/mii.h>
+
+#define SWCONFIG_DEVNAME       "switch%d"
+
+#include "swconfig_leds.c"
+
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_LICENSE("GPL");
+
+static int swdev_id;
+static struct list_head swdevs;
+static DEFINE_MUTEX(swdevs_lock);
+struct swconfig_callback;
+
+struct swconfig_callback {
+       struct sk_buff *msg;
+       struct genlmsghdr *hdr;
+       struct genl_info *info;
+       int cmd;
+
+       /* callback for filling in the message data */
+       int (*fill)(struct swconfig_callback *cb, void *arg);
+
+       /* callback for closing the message before sending it */
+       int (*close)(struct swconfig_callback *cb, void *arg);
+
+       struct nlattr *nest[4];
+       int args[4];
+};
+
+/* defaults */
+
+static int
+swconfig_get_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       int ret;
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       if (!dev->ops->get_vlan_ports)
+               return -EOPNOTSUPP;
+
+       ret = dev->ops->get_vlan_ports(dev, val);
+       return ret;
+}
+
+static int
+swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct switch_port *ports = val->value.ports;
+       const struct switch_dev_ops *ops = dev->ops;
+       int i;
+
+       if (val->port_vlan >= dev->vlans)
+               return -EINVAL;
+
+       /* validate ports */
+       if (val->len > dev->ports)
+               return -EINVAL;
+
+       if (!ops->set_vlan_ports)
+               return -EOPNOTSUPP;
+
+       for (i = 0; i < val->len; i++) {
+               if (ports[i].id >= dev->ports)
+                       return -EINVAL;
+
+               if (ops->set_port_pvid &&
+                   !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
+                       ops->set_port_pvid(dev, ports[i].id, val->port_vlan);
+       }
+
+       return ops->set_vlan_ports(dev, val);
+}
+
+static int
+swconfig_set_pvid(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->set_port_pvid)
+               return -EOPNOTSUPP;
+
+       return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i);
+}
+
+static int
+swconfig_get_pvid(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->get_port_pvid)
+               return -EOPNOTSUPP;
+
+       return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i);
+}
+
+static int
+swconfig_set_link(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       if (!dev->ops->set_port_link)
+               return -EOPNOTSUPP;
+
+       return dev->ops->set_port_link(dev, val->port_vlan, val->value.link);
+}
+
+static int
+swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       struct switch_port_link *link = val->value.link;
+
+       if (val->port_vlan >= dev->ports)
+               return -EINVAL;
+
+       if (!dev->ops->get_port_link)
+               return -EOPNOTSUPP;
+
+       memset(link, 0, sizeof(*link));
+       return dev->ops->get_port_link(dev, val->port_vlan, link);
+}
+
+static int
+swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->apply_config)
+               return 0;
+
+       return dev->ops->apply_config(dev);
+}
+
+static int
+swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr,
+                       struct switch_val *val)
+{
+       /* don't complain if not supported by the switch driver */
+       if (!dev->ops->reset_switch)
+               return 0;
+
+       return dev->ops->reset_switch(dev);
+}
+
+enum global_defaults {
+       GLOBAL_APPLY,
+       GLOBAL_RESET,
+};
+
+enum vlan_defaults {
+       VLAN_PORTS,
+};
+
+enum port_defaults {
+       PORT_PVID,
+       PORT_LINK,
+};
+
+static struct switch_attr default_global[] = {
+       [GLOBAL_APPLY] = {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "apply",
+               .description = "Activate changes in the hardware",
+               .set = swconfig_apply_config,
+       },
+       [GLOBAL_RESET] = {
+               .type = SWITCH_TYPE_NOVAL,
+               .name = "reset",
+               .description = "Reset the switch",
+               .set = swconfig_reset_switch,
+       }
+};
+
+static struct switch_attr default_port[] = {
+       [PORT_PVID] = {
+               .type = SWITCH_TYPE_INT,
+               .name = "pvid",
+               .description = "Primary VLAN ID",
+               .set = swconfig_set_pvid,
+               .get = swconfig_get_pvid,
+       },
+       [PORT_LINK] = {
+               .type = SWITCH_TYPE_LINK,
+               .name = "link",
+               .description = "Get port link information",
+               .set = swconfig_set_link,
+               .get = swconfig_get_link,
+       }
+};
+
+static struct switch_attr default_vlan[] = {
+       [VLAN_PORTS] = {
+               .type = SWITCH_TYPE_PORTS,
+               .name = "ports",
+               .description = "VLAN port mapping",
+               .set = swconfig_set_vlan_ports,
+               .get = swconfig_get_vlan_ports,
+       },
+};
+
+static const struct switch_attr *
+swconfig_find_attr_by_name(const struct switch_attrlist *alist,
+                               const char *name)
+{
+       int i;
+
+       for (i = 0; i < alist->n_attr; i++)
+               if (strcmp(name, alist->attr[i].name) == 0)
+                       return &alist->attr[i];
+
+       return NULL;
+}
+
+static void swconfig_defaults_init(struct switch_dev *dev)
+{
+       const struct switch_dev_ops *ops = dev->ops;
+
+       dev->def_global = 0;
+       dev->def_vlan = 0;
+       dev->def_port = 0;
+
+       if (ops->get_vlan_ports || ops->set_vlan_ports)
+               set_bit(VLAN_PORTS, &dev->def_vlan);
+
+       if (ops->get_port_pvid || ops->set_port_pvid)
+               set_bit(PORT_PVID, &dev->def_port);
+
+       if (ops->get_port_link &&
+           !swconfig_find_attr_by_name(&ops->attr_port, "link"))
+               set_bit(PORT_LINK, &dev->def_port);
+
+       /* always present, can be no-op */
+       set_bit(GLOBAL_APPLY, &dev->def_global);
+       set_bit(GLOBAL_RESET, &dev->def_global);
+}
+
+
+static struct genl_family switch_fam;
+
+static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = {
+       [SWITCH_ATTR_ID] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 },
+       [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING },
+       [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED },
+       [SWITCH_ATTR_TYPE] = { .type = NLA_U32 },
+};
+
+static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = {
+       [SWITCH_PORT_ID] = { .type = NLA_U32 },
+       [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
+};
+
+static struct nla_policy link_policy[SWITCH_LINK_ATTR_MAX] = {
+       [SWITCH_LINK_FLAG_DUPLEX] = { .type = NLA_FLAG },
+       [SWITCH_LINK_FLAG_ANEG] = { .type = NLA_FLAG },
+       [SWITCH_LINK_SPEED] = { .type = NLA_U32 },
+};
+
+static inline void
+swconfig_lock(void)
+{
+       mutex_lock(&swdevs_lock);
+}
+
+static inline void
+swconfig_unlock(void)
+{
+       mutex_unlock(&swdevs_lock);
+}
+
+static struct switch_dev *
+swconfig_get_dev(struct genl_info *info)
+{
+       struct switch_dev *dev = NULL;
+       struct switch_dev *p;
+       int id;
+
+       if (!info->attrs[SWITCH_ATTR_ID])
+               goto done;
+
+       id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]);
+       swconfig_lock();
+       list_for_each_entry(p, &swdevs, dev_list) {
+               if (id != p->id)
+                       continue;
+
+               dev = p;
+               break;
+       }
+       if (dev)
+               mutex_lock(&dev->sw_mutex);
+       else
+               pr_debug("device %d not found\n", id);
+       swconfig_unlock();
+done:
+       return dev;
+}
+
+static inline void
+swconfig_put_dev(struct switch_dev *dev)
+{
+       mutex_unlock(&dev->sw_mutex);
+}
+
+static int
+swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
+{
+       struct switch_attr *op = arg;
+       struct genl_info *info = cb->info;
+       struct sk_buff *msg = cb->msg;
+       int id = cb->args[0];
+       void *hdr;
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
+                       NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
+       if (IS_ERR(hdr))
+               return -1;
+
+       if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name))
+               goto nla_put_failure;
+       if (op->description)
+               if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION,
+                       op->description))
+                       goto nla_put_failure;
+
+       genlmsg_end(msg, hdr);
+       return msg->len;
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+/* spread multipart messages across multiple message buffers */
+static int
+swconfig_send_multipart(struct swconfig_callback *cb, void *arg)
+{
+       struct genl_info *info = cb->info;
+       int restart = 0;
+       int err;
+
+       do {
+               if (!cb->msg) {
+                       cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+                       if (cb->msg == NULL)
+                               goto error;
+               }
+
+               if (!(cb->fill(cb, arg) < 0))
+                       break;
+
+               /* fill failed, check if this was already the second attempt */
+               if (restart)
+                       goto error;
+
+               /* try again in a new message, send the current one */
+               restart = 1;
+               if (cb->close) {
+                       if (cb->close(cb, arg) < 0)
+                               goto error;
+               }
+               err = genlmsg_reply(cb->msg, info);
+               cb->msg = NULL;
+               if (err < 0)
+                       goto error;
+
+       } while (restart);
+
+       return 0;
+
+error:
+       if (cb->msg)
+               nlmsg_free(cb->msg);
+       return -1;
+}
+
+static int
+swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attrlist *alist;
+       struct switch_dev *dev;
+       struct swconfig_callback cb;
+       int err = -EINVAL;
+       int i;
+
+       /* defaults */
+       struct switch_attr *def_list;
+       unsigned long *def_active;
+       int n_def;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       switch (hdr->cmd) {
+       case SWITCH_CMD_LIST_GLOBAL:
+               alist = &dev->ops->attr_global;
+               def_list = default_global;
+               def_active = &dev->def_global;
+               n_def = ARRAY_SIZE(default_global);
+               break;
+       case SWITCH_CMD_LIST_VLAN:
+               alist = &dev->ops->attr_vlan;
+               def_list = default_vlan;
+               def_active = &dev->def_vlan;
+               n_def = ARRAY_SIZE(default_vlan);
+               break;
+       case SWITCH_CMD_LIST_PORT:
+               alist = &dev->ops->attr_port;
+               def_list = default_port;
+               def_active = &dev->def_port;
+               n_def = ARRAY_SIZE(default_port);
+               break;
+       default:
+               WARN_ON(1);
+               goto out;
+       }
+
+       memset(&cb, 0, sizeof(cb));
+       cb.info = info;
+       cb.fill = swconfig_dump_attr;
+       for (i = 0; i < alist->n_attr; i++) {
+               if (alist->attr[i].disabled)
+                       continue;
+               cb.args[0] = i;
+               err = swconfig_send_multipart(&cb, (void *) &alist->attr[i]);
+               if (err < 0)
+                       goto error;
+       }
+
+       /* defaults */
+       for (i = 0; i < n_def; i++) {
+               if (!test_bit(i, def_active))
+                       continue;
+               cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i;
+               err = swconfig_send_multipart(&cb, (void *) &def_list[i]);
+               if (err < 0)
+                       goto error;
+       }
+       swconfig_put_dev(dev);
+
+       if (!cb.msg)
+               return 0;
+
+       return genlmsg_reply(cb.msg, info);
+
+error:
+       if (cb.msg)
+               nlmsg_free(cb.msg);
+out:
+       swconfig_put_dev(dev);
+       return err;
+}
+
+static const struct switch_attr *
+swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info,
+               struct switch_val *val)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attrlist *alist;
+       const struct switch_attr *attr = NULL;
+       unsigned int attr_id;
+
+       /* defaults */
+       struct switch_attr *def_list;
+       unsigned long *def_active;
+       int n_def;
+
+       if (!info->attrs[SWITCH_ATTR_OP_ID])
+               goto done;
+
+       switch (hdr->cmd) {
+       case SWITCH_CMD_SET_GLOBAL:
+       case SWITCH_CMD_GET_GLOBAL:
+               alist = &dev->ops->attr_global;
+               def_list = default_global;
+               def_active = &dev->def_global;
+               n_def = ARRAY_SIZE(default_global);
+               break;
+       case SWITCH_CMD_SET_VLAN:
+       case SWITCH_CMD_GET_VLAN:
+               alist = &dev->ops->attr_vlan;
+               def_list = default_vlan;
+               def_active = &dev->def_vlan;
+               n_def = ARRAY_SIZE(default_vlan);
+               if (!info->attrs[SWITCH_ATTR_OP_VLAN])
+                       goto done;
+               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]);
+               if (val->port_vlan >= dev->vlans)
+                       goto done;
+               break;
+       case SWITCH_CMD_SET_PORT:
+       case SWITCH_CMD_GET_PORT:
+               alist = &dev->ops->attr_port;
+               def_list = default_port;
+               def_active = &dev->def_port;
+               n_def = ARRAY_SIZE(default_port);
+               if (!info->attrs[SWITCH_ATTR_OP_PORT])
+                       goto done;
+               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]);
+               if (val->port_vlan >= dev->ports)
+                       goto done;
+               break;
+       default:
+               WARN_ON(1);
+               goto done;
+       }
+
+       if (!alist)
+               goto done;
+
+       attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
+       if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) {
+               attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET;
+               if (attr_id >= n_def)
+                       goto done;
+               if (!test_bit(attr_id, def_active))
+                       goto done;
+               attr = &def_list[attr_id];
+       } else {
+               if (attr_id >= alist->n_attr)
+                       goto done;
+               attr = &alist->attr[attr_id];
+       }
+
+       if (attr->disabled)
+               attr = NULL;
+
+done:
+       if (!attr)
+               pr_debug("attribute lookup failed\n");
+       val->attr = attr;
+       return attr;
+}
+
+static int
+swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head,
+               struct switch_val *val, int max)
+{
+       struct nlattr *nla;
+       int rem;
+
+       val->len = 0;
+       nla_for_each_nested(nla, head, rem) {
+               struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
+               struct switch_port *port;
+
+               if (val->len >= max)
+                       return -EINVAL;
+
+               port = &val->value.ports[val->len];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
+               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
+                               port_policy))
+#else
+               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
+                               port_policy, NULL))
+#endif
+                       return -EINVAL;
+
+               if (!tb[SWITCH_PORT_ID])
+                       return -EINVAL;
+
+               port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
+               if (tb[SWITCH_PORT_FLAG_TAGGED])
+                       port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED);
+               val->len++;
+       }
+
+       return 0;
+}
+
+static int
+swconfig_parse_link(struct sk_buff *msg, struct nlattr *nla,
+                   struct switch_port_link *link)
+{
+       struct nlattr *tb[SWITCH_LINK_ATTR_MAX + 1];
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
+       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy))
+#else
+       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy, NULL))
+#endif
+               return -EINVAL;
+
+       link->duplex = !!tb[SWITCH_LINK_FLAG_DUPLEX];
+       link->aneg = !!tb[SWITCH_LINK_FLAG_ANEG];
+       link->speed = nla_get_u32(tb[SWITCH_LINK_SPEED]);
+
+       return 0;
+}
+
+static int
+swconfig_set_attr(struct sk_buff *skb, struct genl_info *info)
+{
+       const struct switch_attr *attr;
+       struct switch_dev *dev;
+       struct switch_val val;
+       int err = -EINVAL;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       memset(&val, 0, sizeof(val));
+       attr = swconfig_lookup_attr(dev, info, &val);
+       if (!attr || !attr->set)
+               goto error;
+
+       val.attr = attr;
+       switch (attr->type) {
+       case SWITCH_TYPE_NOVAL:
+               break;
+       case SWITCH_TYPE_INT:
+               if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT])
+                       goto error;
+               val.value.i =
+                       nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]);
+               break;
+       case SWITCH_TYPE_STRING:
+               if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR])
+                       goto error;
+               val.value.s =
+                       nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]);
+               break;
+       case SWITCH_TYPE_PORTS:
+               val.value.ports = dev->portbuf;
+               memset(dev->portbuf, 0,
+                       sizeof(struct switch_port) * dev->ports);
+
+               /* TODO: implement multipart? */
+               if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) {
+                       err = swconfig_parse_ports(skb,
+                               info->attrs[SWITCH_ATTR_OP_VALUE_PORTS],
+                               &val, dev->ports);
+                       if (err < 0)
+                               goto error;
+               } else {
+                       val.len = 0;
+                       err = 0;
+               }
+               break;
+       case SWITCH_TYPE_LINK:
+               val.value.link = &dev->linkbuf;
+               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
+
+               if (info->attrs[SWITCH_ATTR_OP_VALUE_LINK]) {
+                       err = swconfig_parse_link(skb,
+                                                 info->attrs[SWITCH_ATTR_OP_VALUE_LINK],
+                                                 val.value.link);
+                       if (err < 0)
+                               goto error;
+               } else {
+                       val.len = 0;
+                       err = 0;
+               }
+               break;
+       default:
+               goto error;
+       }
+
+       err = attr->set(dev, attr, &val);
+error:
+       swconfig_put_dev(dev);
+       return err;
+}
+
+static int
+swconfig_close_portlist(struct swconfig_callback *cb, void *arg)
+{
+       if (cb->nest[0])
+               nla_nest_end(cb->msg, cb->nest[0]);
+       return 0;
+}
+
+static int
+swconfig_send_port(struct swconfig_callback *cb, void *arg)
+{
+       const struct switch_port *port = arg;
+       struct nlattr *p = NULL;
+
+       if (!cb->nest[0]) {
+               cb->nest[0] = nla_nest_start(cb->msg, cb->cmd);
+               if (!cb->nest[0])
+                       return -1;
+       }
+
+       p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT);
+       if (!p)
+               goto error;
+
+       if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id))
+               goto nla_put_failure;
+       if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
+               if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED))
+                       goto nla_put_failure;
+       }
+
+       nla_nest_end(cb->msg, p);
+       return 0;
+
+nla_put_failure:
+               nla_nest_cancel(cb->msg, p);
+error:
+       nla_nest_cancel(cb->msg, cb->nest[0]);
+       return -1;
+}
+
+static int
+swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr,
+               const struct switch_val *val)
+{
+       struct swconfig_callback cb;
+       int err = 0;
+       int i;
+
+       if (!val->value.ports)
+               return -EINVAL;
+
+       memset(&cb, 0, sizeof(cb));
+       cb.cmd = attr;
+       cb.msg = *msg;
+       cb.info = info;
+       cb.fill = swconfig_send_port;
+       cb.close = swconfig_close_portlist;
+
+       cb.nest[0] = nla_nest_start(cb.msg, cb.cmd);
+       for (i = 0; i < val->len; i++) {
+               err = swconfig_send_multipart(&cb, &val->value.ports[i]);
+               if (err)
+                       goto done;
+       }
+       err = val->len;
+       swconfig_close_portlist(&cb, NULL);
+       *msg = cb.msg;
+
+done:
+       return err;
+}
+
+static int
+swconfig_send_link(struct sk_buff *msg, struct genl_info *info, int attr,
+                  const struct switch_port_link *link)
+{
+       struct nlattr *p = NULL;
+       int err = 0;
+
+       p = nla_nest_start(msg, attr);
+       if (link->link) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_LINK))
+                       goto nla_put_failure;
+       }
+       if (link->duplex) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_DUPLEX))
+                       goto nla_put_failure;
+       }
+       if (link->aneg) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_ANEG))
+                       goto nla_put_failure;
+       }
+       if (link->tx_flow) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_TX_FLOW))
+                       goto nla_put_failure;
+       }
+       if (link->rx_flow) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_RX_FLOW))
+                       goto nla_put_failure;
+       }
+       if (nla_put_u32(msg, SWITCH_LINK_SPEED, link->speed))
+               goto nla_put_failure;
+       if (link->eee & ADVERTISED_100baseT_Full) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_100BASET))
+                       goto nla_put_failure;
+       }
+       if (link->eee & ADVERTISED_1000baseT_Full) {
+               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_1000BASET))
+                       goto nla_put_failure;
+       }
+       nla_nest_end(msg, p);
+
+       return err;
+
+nla_put_failure:
+       nla_nest_cancel(msg, p);
+       return -1;
+}
+
+static int
+swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
+{
+       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
+       const struct switch_attr *attr;
+       struct switch_dev *dev;
+       struct sk_buff *msg = NULL;
+       struct switch_val val;
+       int err = -EINVAL;
+       int cmd = hdr->cmd;
+
+       dev = swconfig_get_dev(info);
+       if (!dev)
+               return -EINVAL;
+
+       memset(&val, 0, sizeof(val));
+       attr = swconfig_lookup_attr(dev, info, &val);
+       if (!attr || !attr->get)
+               goto error;
+
+       if (attr->type == SWITCH_TYPE_PORTS) {
+               val.value.ports = dev->portbuf;
+               memset(dev->portbuf, 0,
+                       sizeof(struct switch_port) * dev->ports);
+       } else if (attr->type == SWITCH_TYPE_LINK) {
+               val.value.link = &dev->linkbuf;
+               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
+       }
+
+       err = attr->get(dev, attr, &val);
+       if (err)
+               goto error;
+
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg)
+               goto error;
+
+       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
+                       0, cmd);
+       if (IS_ERR(hdr))
+               goto nla_put_failure;
+
+       switch (attr->type) {
+       case SWITCH_TYPE_INT:
+               if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i))
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_STRING:
+               if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s))
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_PORTS:
+               err = swconfig_send_ports(&msg, info,
+                               SWITCH_ATTR_OP_VALUE_PORTS, &val);
+               if (err < 0)
+                       goto nla_put_failure;
+               break;
+       case SWITCH_TYPE_LINK:
+               err = swconfig_send_link(msg, info,
+                                        SWITCH_ATTR_OP_VALUE_LINK, val.value.link);
+               if (err < 0)
+                       goto nla_put_failure;
+               break;
+       default:
+               pr_debug("invalid type in attribute\n");
+               err = -EINVAL;
+               goto nla_put_failure;
+       }
+       genlmsg_end(msg, hdr);
+       err = msg->len;
+       if (err < 0)
+               goto nla_put_failure;
+
+       swconfig_put_dev(dev);
+       return genlmsg_reply(msg, info);
+
+nla_put_failure:
+       if (msg)
+               nlmsg_free(msg);
+error:
+       swconfig_put_dev(dev);
+       if (!err)
+               err = -ENOMEM;
+       return err;
+}
+
+static int
+swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
+               const struct switch_dev *dev)
+{
+       struct nlattr *p = NULL, *m = NULL;
+       void *hdr;
+       int i;
+
+       hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
+                       SWITCH_CMD_NEW_ATTR);
+       if (IS_ERR(hdr))
+               return -1;
+
+       if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias))
+               goto nla_put_failure;
+       if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports))
+               goto nla_put_failure;
+       if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port))
+               goto nla_put_failure;
+
+       m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP);
+       if (!m)
+               goto nla_put_failure;
+       for (i = 0; i < dev->ports; i++) {
+               p = nla_nest_start(msg, SWITCH_ATTR_PORTS);
+               if (!p)
+                       continue;
+               if (dev->portmap[i].s) {
+                       if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT,
+                                               dev->portmap[i].s))
+                               goto nla_put_failure;
+                       if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT,
+                                               dev->portmap[i].virt))
+                               goto nla_put_failure;
+               }
+               nla_nest_end(msg, p);
+       }
+       nla_nest_end(msg, m);
+       genlmsg_end(msg, hdr);
+       return msg->len;
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return -EMSGSIZE;
+}
+
+static int swconfig_dump_switches(struct sk_buff *skb,
+               struct netlink_callback *cb)
+{
+       struct switch_dev *dev;
+       int start = cb->args[0];
+       int idx = 0;
+
+       swconfig_lock();
+       list_for_each_entry(dev, &swdevs, dev_list) {
+               if (++idx <= start)
+                       continue;
+               if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid,
+                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
+                               dev) < 0)
+                       break;
+       }
+       swconfig_unlock();
+       cb->args[0] = idx;
+
+       return skb->len;
+}
+
+static int
+swconfig_done(struct netlink_callback *cb)
+{
+       return 0;
+}
+
+static struct genl_ops swconfig_ops[] = {
+       {
+               .cmd = SWITCH_CMD_LIST_GLOBAL,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_LIST_VLAN,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_LIST_PORT,
+               .doit = swconfig_list_attrs,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_GLOBAL,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_VLAN,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_PORT,
+               .doit = swconfig_get_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_GLOBAL,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_VLAN,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_SET_PORT,
+               .flags = GENL_ADMIN_PERM,
+               .doit = swconfig_set_attr,
+               .policy = switch_policy,
+       },
+       {
+               .cmd = SWITCH_CMD_GET_SWITCH,
+               .dumpit = swconfig_dump_switches,
+               .policy = switch_policy,
+               .done = swconfig_done,
+       }
+};
+
+static struct genl_family switch_fam = {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+       .id = GENL_ID_GENERATE,
+#endif
+       .name = "switch",
+       .hdrsize = 0,
+       .version = 1,
+       .maxattr = SWITCH_ATTR_MAX,
+       .module = THIS_MODULE,
+       .ops = swconfig_ops,
+       .n_ops = ARRAY_SIZE(swconfig_ops),
+};
+
+#ifdef CONFIG_OF
+void
+of_switch_load_portmap(struct switch_dev *dev)
+{
+       struct device_node *port;
+
+       if (!dev->of_node)
+               return;
+
+       for_each_child_of_node(dev->of_node, port) {
+               const __be32 *prop;
+               const char *segment;
+               int size, phys;
+
+               if (!of_device_is_compatible(port, "swconfig,port"))
+                       continue;
+
+               if (of_property_read_string(port, "swconfig,segment", &segment))
+                       continue;
+
+               prop = of_get_property(port, "swconfig,portmap", &size);
+               if (!prop)
+                       continue;
+
+               if (size != (2 * sizeof(*prop))) {
+                       pr_err("%s: failed to parse port mapping\n",
+                                       port->name);
+                       continue;
+               }
+
+               phys = be32_to_cpup(prop++);
+               if ((phys < 0) | (phys >= dev->ports)) {
+                       pr_err("%s: physical port index out of range\n",
+                                       port->name);
+                       continue;
+               }
+
+               dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL);
+               dev->portmap[phys].virt = be32_to_cpup(prop);
+               pr_debug("Found port: %s, physical: %d, virtual: %d\n",
+                       segment, phys, dev->portmap[phys].virt);
+       }
+}
+#endif
+
+int
+register_switch(struct switch_dev *dev, struct net_device *netdev)
+{
+       struct switch_dev *sdev;
+       const int max_switches = 8 * sizeof(unsigned long);
+       unsigned long in_use = 0;
+       int err;
+       int i;
+
+       INIT_LIST_HEAD(&dev->dev_list);
+       if (netdev) {
+               dev->netdev = netdev;
+               if (!dev->alias)
+                       dev->alias = netdev->name;
+       }
+       BUG_ON(!dev->alias);
+
+       /* Make sure swdev_id doesn't overflow */
+       if (swdev_id == INT_MAX) {
+               return -ENOMEM;
+       }
+
+       if (dev->ports > 0) {
+               dev->portbuf = kzalloc(sizeof(struct switch_port) *
+                               dev->ports, GFP_KERNEL);
+               if (!dev->portbuf)
+                       return -ENOMEM;
+               dev->portmap = kzalloc(sizeof(struct switch_portmap) *
+                               dev->ports, GFP_KERNEL);
+               if (!dev->portmap) {
+                       kfree(dev->portbuf);
+                       return -ENOMEM;
+               }
+       }
+       swconfig_defaults_init(dev);
+       mutex_init(&dev->sw_mutex);
+       swconfig_lock();
+       dev->id = ++swdev_id;
+
+       list_for_each_entry(sdev, &swdevs, dev_list) {
+               if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i))
+                       continue;
+               if (i < 0 || i > max_switches)
+                       continue;
+
+               set_bit(i, &in_use);
+       }
+       i = find_first_zero_bit(&in_use, max_switches);
+
+       if (i == max_switches) {
+               swconfig_unlock();
+               return -ENFILE;
+       }
+
+#ifdef CONFIG_OF
+       if (dev->ports)
+               of_switch_load_portmap(dev);
+#endif
+
+       /* fill device name */
+       snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
+
+       list_add_tail(&dev->dev_list, &swdevs);
+       swconfig_unlock();
+
+       err = swconfig_create_led_trigger(dev);
+       if (err)
+               return err;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(register_switch);
+
+void
+unregister_switch(struct switch_dev *dev)
+{
+       swconfig_destroy_led_trigger(dev);
+       kfree(dev->portbuf);
+       mutex_lock(&dev->sw_mutex);
+       swconfig_lock();
+       list_del(&dev->dev_list);
+       swconfig_unlock();
+       mutex_unlock(&dev->sw_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_switch);
+
+int
+switch_generic_set_link(struct switch_dev *dev, int port,
+                       struct switch_port_link *link)
+{
+       if (WARN_ON(!dev->ops->phy_write16))
+               return -ENOTSUPP;
+
+       /* Generic implementation */
+       if (link->aneg) {
+               dev->ops->phy_write16(dev, port, MII_BMCR, 0x0000);
+               dev->ops->phy_write16(dev, port, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
+       } else {
+               u16 bmcr = 0;
+
+               if (link->duplex)
+                       bmcr |= BMCR_FULLDPLX;
+
+               switch (link->speed) {
+               case SWITCH_PORT_SPEED_10:
+                       break;
+               case SWITCH_PORT_SPEED_100:
+                       bmcr |= BMCR_SPEED100;
+                       break;
+               case SWITCH_PORT_SPEED_1000:
+                       bmcr |= BMCR_SPEED1000;
+                       break;
+               default:
+                       return -ENOTSUPP;
+               }
+
+               dev->ops->phy_write16(dev, port, MII_BMCR, bmcr);
+       }
+
+       return 0;
+}
+
+static int __init
+swconfig_init(void)
+{
+       INIT_LIST_HEAD(&swdevs);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
+       return genl_register_family_with_ops(&switch_fam, swconfig_ops);
+#else
+       return genl_register_family(&switch_fam);
+#endif
+}
+
+static void __exit
+swconfig_exit(void)
+{
+       genl_unregister_family(&switch_fam);
+}
+
+module_init(swconfig_init);
+module_exit(swconfig_exit);
diff --git a/target/linux/generic/files-4.9/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files-4.9/drivers/net/phy/swconfig_leds.c
new file mode 100644 (file)
index 0000000..91824b7
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * swconfig_led.c: LED trigger support for the switch configuration API
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ */
+
+#ifdef CONFIG_SWCONFIG_LEDS
+
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/device.h>
+#include <linux/workqueue.h>
+
+#define SWCONFIG_LED_TIMER_INTERVAL    (HZ / 10)
+#define SWCONFIG_LED_NUM_PORTS         32
+
+#define SWCONFIG_LED_PORT_SPEED_NA     0x01    /* unknown speed */
+#define SWCONFIG_LED_PORT_SPEED_10     0x02    /* 10 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_100    0x04    /* 100 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_1000   0x08    /* 1000 Mbps */
+#define SWCONFIG_LED_PORT_SPEED_ALL    (SWCONFIG_LED_PORT_SPEED_NA | \
+                                        SWCONFIG_LED_PORT_SPEED_10 | \
+                                        SWCONFIG_LED_PORT_SPEED_100 | \
+                                        SWCONFIG_LED_PORT_SPEED_1000)
+
+#define SWCONFIG_LED_MODE_LINK         0x01
+#define SWCONFIG_LED_MODE_TX           0x02
+#define SWCONFIG_LED_MODE_RX           0x04
+#define SWCONFIG_LED_MODE_TXRX         (SWCONFIG_LED_MODE_TX   | \
+                                        SWCONFIG_LED_MODE_RX)
+#define SWCONFIG_LED_MODE_ALL          (SWCONFIG_LED_MODE_LINK | \
+                                        SWCONFIG_LED_MODE_TX   | \
+                                        SWCONFIG_LED_MODE_RX)
+
+struct switch_led_trigger {
+       struct led_trigger trig;
+       struct switch_dev *swdev;
+
+       struct delayed_work sw_led_work;
+       u32 port_mask;
+       u32 port_link;
+       unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
+       unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
+       u8 link_speed[SWCONFIG_LED_NUM_PORTS];
+};
+
+struct swconfig_trig_data {
+       struct led_classdev *led_cdev;
+       struct switch_dev *swdev;
+
+       rwlock_t lock;
+       u32 port_mask;
+
+       bool prev_link;
+       unsigned long prev_traffic;
+       enum led_brightness prev_brightness;
+       u8 mode;
+       u8 speed_mask;
+};
+
+static void
+swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
+                            enum led_brightness brightness)
+{
+       led_set_brightness(trig_data->led_cdev, brightness);
+       trig_data->prev_brightness = brightness;
+}
+
+static void
+swconfig_trig_update_port_mask(struct led_trigger *trigger)
+{
+       struct list_head *entry;
+       struct switch_led_trigger *sw_trig;
+       u32 port_mask;
+
+       if (!trigger)
+               return;
+
+       sw_trig = (void *) trigger;
+
+       port_mask = 0;
+       read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_cdevs) {
+               struct led_classdev *led_cdev;
+               struct swconfig_trig_data *trig_data;
+
+               led_cdev = list_entry(entry, struct led_classdev, trig_list);
+               trig_data = led_cdev->trigger_data;
+               if (trig_data) {
+                       read_lock(&trig_data->lock);
+                       port_mask |= trig_data->port_mask;
+                       read_unlock(&trig_data->lock);
+               }
+       }
+       read_unlock(&trigger->leddev_list_lock);
+
+       sw_trig->port_mask = port_mask;
+
+       if (port_mask)
+               schedule_delayed_work(&sw_trig->sw_led_work,
+                                     SWCONFIG_LED_TIMER_INTERVAL);
+       else
+               cancel_delayed_work_sync(&sw_trig->sw_led_work);
+}
+
+static ssize_t
+swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr,
+                             const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       unsigned long port_mask;
+       int ret;
+       bool changed;
+
+       ret = kstrtoul(buf, 0, &port_mask);
+       if (ret)
+               return ret;
+
+       write_lock(&trig_data->lock);
+       changed = (trig_data->port_mask != port_mask);
+       trig_data->port_mask = port_mask;
+       write_unlock(&trig_data->lock);
+
+       if (changed) {
+               if (port_mask == 0)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF);
+
+               swconfig_trig_update_port_mask(led_cdev->trigger);
+       }
+
+       return size;
+}
+
+static ssize_t
+swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u32 port_mask;
+
+       read_lock(&trig_data->lock);
+       port_mask = trig_data->port_mask;
+       read_unlock(&trig_data->lock);
+
+       sprintf(buf, "%#x\n", port_mask);
+
+       return strlen(buf) + 1;
+}
+
+static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
+                  swconfig_trig_port_mask_store);
+
+/* speed_mask file handler - display value */
+static ssize_t swconfig_trig_speed_mask_show(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 speed_mask;
+
+       read_lock(&trig_data->lock);
+       speed_mask = trig_data->speed_mask;
+       read_unlock(&trig_data->lock);
+
+       sprintf(buf, "%#x\n", speed_mask);
+
+       return strlen(buf) + 1;
+}
+
+/* speed_mask file handler - store value */
+static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
+                                             struct device_attribute *attr,
+                                             const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 speed_mask;
+       int ret;
+
+       ret = kstrtou8(buf, 0, &speed_mask);
+       if (ret)
+               return ret;
+
+       write_lock(&trig_data->lock);
+       trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
+       write_unlock(&trig_data->lock);
+
+       return size;
+}
+
+/* speed_mask special file */
+static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
+                  swconfig_trig_speed_mask_store);
+
+static ssize_t swconfig_trig_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       u8 mode;
+
+       read_lock(&trig_data->lock);
+       mode = trig_data->mode;
+       read_unlock(&trig_data->lock);
+
+       if (mode == 0) {
+               strcpy(buf, "none\n");
+       } else {
+               if (mode & SWCONFIG_LED_MODE_LINK)
+                       strcat(buf, "link ");
+               if (mode & SWCONFIG_LED_MODE_TX)
+                       strcat(buf, "tx ");
+               if (mode & SWCONFIG_LED_MODE_RX)
+                       strcat(buf, "rx ");
+               strcat(buf, "\n");
+       }
+
+       return strlen(buf)+1;
+}
+
+static ssize_t swconfig_trig_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
+       char copybuf[128];
+       int new_mode = -1;
+       char *p, *token;
+
+       /* take a copy since we don't want to trash the inbound buffer when using strsep */
+       strncpy(copybuf, buf, sizeof(copybuf));
+       copybuf[sizeof(copybuf) - 1] = 0;
+       p = copybuf;
+
+       while ((token = strsep(&p, " \t\n")) != NULL) {
+               if (!*token)
+                       continue;
+
+               if (new_mode < 0)
+                       new_mode = 0;
+
+               if (!strcmp(token, "none"))
+                       new_mode = 0;
+               else if (!strcmp(token, "tx"))
+                       new_mode |= SWCONFIG_LED_MODE_TX;
+               else if (!strcmp(token, "rx"))
+                       new_mode |= SWCONFIG_LED_MODE_RX;
+               else if (!strcmp(token, "link"))
+                       new_mode |= SWCONFIG_LED_MODE_LINK;
+               else
+                       return -EINVAL;
+       }
+
+       if (new_mode < 0)
+               return -EINVAL;
+
+       write_lock(&trig_data->lock);
+       trig_data->mode = (u8)new_mode;
+       write_unlock(&trig_data->lock);
+
+       return size;
+}
+
+/* mode special file */
+static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
+                  swconfig_trig_mode_store);
+
+static void
+swconfig_trig_activate(struct led_classdev *led_cdev)
+{
+       struct switch_led_trigger *sw_trig;
+       struct swconfig_trig_data *trig_data;
+       int err;
+
+       if (led_cdev->trigger->activate != swconfig_trig_activate)
+               return;
+
+       trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
+       if (!trig_data)
+               return;
+
+       sw_trig = (void *) led_cdev->trigger;
+
+       rwlock_init(&trig_data->lock);
+       trig_data->led_cdev = led_cdev;
+       trig_data->swdev = sw_trig->swdev;
+       trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
+       trig_data->mode = SWCONFIG_LED_MODE_ALL;
+       led_cdev->trigger_data = trig_data;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
+       if (err)
+               goto err_free;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
+       if (err)
+               goto err_dev_free;
+
+       err = device_create_file(led_cdev->dev, &dev_attr_mode);
+       if (err)
+               goto err_mode_free;
+
+       return;
+
+err_mode_free:
+       device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+
+err_dev_free:
+       device_remove_file(led_cdev->dev, &dev_attr_port_mask);
+
+err_free:
+       led_cdev->trigger_data = NULL;
+       kfree(trig_data);
+}
+
+static void
+swconfig_trig_deactivate(struct led_classdev *led_cdev)
+{
+       struct swconfig_trig_data *trig_data;
+
+       swconfig_trig_update_port_mask(led_cdev->trigger);
+
+       trig_data = (void *) led_cdev->trigger_data;
+       if (trig_data) {
+               device_remove_file(led_cdev->dev, &dev_attr_port_mask);
+               device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
+               device_remove_file(led_cdev->dev, &dev_attr_mode);
+               kfree(trig_data);
+       }
+}
+
+/*
+ * link off -> led off (can't be any other reason to turn it on)
+ * link on:
+ *     mode link: led on by default only if speed matches, else off
+ *     mode txrx: blink only if speed matches, else off
+ */
+static void
+swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
+                       struct led_classdev *led_cdev)
+{
+       struct swconfig_trig_data *trig_data;
+       u32 port_mask;
+       bool link;
+       u8 speed_mask, mode;
+       enum led_brightness led_base, led_blink;
+
+       trig_data = led_cdev->trigger_data;
+       if (!trig_data)
+               return;
+
+       read_lock(&trig_data->lock);
+       port_mask = trig_data->port_mask;
+       speed_mask = trig_data->speed_mask;
+       mode = trig_data->mode;
+       read_unlock(&trig_data->lock);
+
+       link = !!(sw_trig->port_link & port_mask);
+       if (!link) {
+               if (trig_data->prev_brightness != LED_OFF)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
+       }
+       else {
+               unsigned long traffic;
+               int speedok;    /* link speed flag */
+               int i;
+
+               led_base = LED_FULL;
+               led_blink = LED_OFF;
+               traffic = 0;
+               speedok = 0;
+               for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+                       if (port_mask & (1 << i)) {
+                               if (sw_trig->link_speed[i] & speed_mask) {
+                                       traffic += ((mode & SWCONFIG_LED_MODE_TX) ?
+                                                   sw_trig->port_tx_traffic[i] : 0) +
+                                               ((mode & SWCONFIG_LED_MODE_RX) ?
+                                                sw_trig->port_rx_traffic[i] : 0);
+                                       speedok = 1;
+                               }
+                       }
+               }
+
+               if (speedok) {
+                       /* At least one port speed matches speed_mask */
+                       if (!(mode & SWCONFIG_LED_MODE_LINK)) {
+                               led_base = LED_OFF;
+                               led_blink = LED_FULL;
+                       }
+
+                       if (trig_data->prev_brightness != led_base)
+                               swconfig_trig_set_brightness(trig_data,
+                                                            led_base);
+                       else if (traffic != trig_data->prev_traffic)
+                               swconfig_trig_set_brightness(trig_data,
+                                                            led_blink);
+               } else if (trig_data->prev_brightness != LED_OFF)
+                       swconfig_trig_set_brightness(trig_data, LED_OFF);
+
+               trig_data->prev_traffic = traffic;
+       }
+
+       trig_data->prev_link = link;
+}
+
+static void
+swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
+{
+       struct list_head *entry;
+       struct led_trigger *trigger;
+
+       trigger = &sw_trig->trig;
+       read_lock(&trigger->leddev_list_lock);
+       list_for_each(entry, &trigger->led_cdevs) {
+               struct led_classdev *led_cdev;
+
+               led_cdev = list_entry(entry, struct led_classdev, trig_list);
+               swconfig_trig_led_event(sw_trig, led_cdev);
+       }
+       read_unlock(&trigger->leddev_list_lock);
+}
+
+static void
+swconfig_led_work_func(struct work_struct *work)
+{
+       struct switch_led_trigger *sw_trig;
+       struct switch_dev *swdev;
+       u32 port_mask;
+       u32 link;
+       int i;
+
+       sw_trig = container_of(work, struct switch_led_trigger,
+                              sw_led_work.work);
+
+       port_mask = sw_trig->port_mask;
+       swdev = sw_trig->swdev;
+
+       link = 0;
+       for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
+               u32 port_bit;
+
+               sw_trig->link_speed[i] = 0;
+
+               port_bit = BIT(i);
+               if ((port_mask & port_bit) == 0)
+                       continue;
+
+               if (swdev->ops->get_port_link) {
+                       struct switch_port_link port_link;
+
+                       memset(&port_link, '\0', sizeof(port_link));
+                       swdev->ops->get_port_link(swdev, i, &port_link);
+
+                       if (port_link.link) {
+                               link |= port_bit;
+                               switch (port_link.speed) {
+                               case SWITCH_PORT_SPEED_UNKNOWN:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_NA;
+                                       break;
+                               case SWITCH_PORT_SPEED_10:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_10;
+                                       break;
+                               case SWITCH_PORT_SPEED_100:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_100;
+                                       break;
+                               case SWITCH_PORT_SPEED_1000:
+                                       sw_trig->link_speed[i] =
+                                               SWCONFIG_LED_PORT_SPEED_1000;
+                                       break;
+                               }
+                       }
+               }
+
+               if (swdev->ops->get_port_stats) {
+                       struct switch_port_stats port_stats;
+
+                       memset(&port_stats, '\0', sizeof(port_stats));
+                       swdev->ops->get_port_stats(swdev, i, &port_stats);
+                       sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
+                       sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
+               }
+       }
+
+       sw_trig->port_link = link;
+
+       swconfig_trig_update_leds(sw_trig);
+
+       schedule_delayed_work(&sw_trig->sw_led_work,
+                             SWCONFIG_LED_TIMER_INTERVAL);
+}
+
+static int
+swconfig_create_led_trigger(struct switch_dev *swdev)
+{
+       struct switch_led_trigger *sw_trig;
+       int err;
+
+       if (!swdev->ops->get_port_link)
+               return 0;
+
+       sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL);
+       if (!sw_trig)
+               return -ENOMEM;
+
+       sw_trig->swdev = swdev;
+       sw_trig->trig.name = swdev->devname;
+       sw_trig->trig.activate = swconfig_trig_activate;
+       sw_trig->trig.deactivate = swconfig_trig_deactivate;
+
+       INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func);
+
+       err = led_trigger_register(&sw_trig->trig);
+       if (err)
+               goto err_free;
+
+       swdev->led_trigger = sw_trig;
+
+       return 0;
+
+err_free:
+       kfree(sw_trig);
+       return err;
+}
+
+static void
+swconfig_destroy_led_trigger(struct switch_dev *swdev)
+{
+       struct switch_led_trigger *sw_trig;
+
+       sw_trig = swdev->led_trigger;
+       if (sw_trig) {
+               cancel_delayed_work_sync(&sw_trig->sw_led_work);
+               led_trigger_unregister(&sw_trig->trig);
+               kfree(sw_trig);
+       }
+}
+
+#else /* SWCONFIG_LEDS */
+static inline int
+swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; }
+
+static inline void
+swconfig_destroy_led_trigger(struct switch_dev *swdev) { }
+#endif /* CONFIG_SWCONFIG_LEDS */
diff --git a/target/linux/generic/files-4.9/include/linux/ar8216_platform.h b/target/linux/generic/files-4.9/include/linux/ar8216_platform.h
new file mode 100644 (file)
index 0000000..24bc442
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * AR8216 switch driver platform data
+ *
+ * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef AR8216_PLATFORM_H
+#define AR8216_PLATFORM_H
+
+enum ar8327_pad_mode {
+       AR8327_PAD_NC = 0,
+       AR8327_PAD_MAC2MAC_MII,
+       AR8327_PAD_MAC2MAC_GMII,
+       AR8327_PAD_MAC_SGMII,
+       AR8327_PAD_MAC2PHY_MII,
+       AR8327_PAD_MAC2PHY_GMII,
+       AR8327_PAD_MAC_RGMII,
+       AR8327_PAD_PHY_GMII,
+       AR8327_PAD_PHY_RGMII,
+       AR8327_PAD_PHY_MII,
+};
+
+enum ar8327_clk_delay_sel {
+       AR8327_CLK_DELAY_SEL0 = 0,
+       AR8327_CLK_DELAY_SEL1,
+       AR8327_CLK_DELAY_SEL2,
+       AR8327_CLK_DELAY_SEL3,
+};
+
+struct ar8327_pad_cfg {
+       enum ar8327_pad_mode mode;
+       bool rxclk_sel;
+       bool txclk_sel;
+       bool pipe_rxclk_sel;
+       bool txclk_delay_en;
+       bool rxclk_delay_en;
+       bool sgmii_delay_en;
+       enum ar8327_clk_delay_sel txclk_delay_sel;
+       enum ar8327_clk_delay_sel rxclk_delay_sel;
+       bool mac06_exchange_dis;
+};
+
+enum ar8327_port_speed {
+       AR8327_PORT_SPEED_10 = 0,
+       AR8327_PORT_SPEED_100,
+       AR8327_PORT_SPEED_1000,
+};
+
+struct ar8327_port_cfg {
+       int force_link:1;
+       enum ar8327_port_speed speed;
+       int txpause:1;
+       int rxpause:1;
+       int duplex:1;
+};
+
+struct ar8327_sgmii_cfg {
+       u32 sgmii_ctrl;
+       bool serdes_aen;
+};
+
+struct ar8327_led_cfg {
+       u32 led_ctrl0;
+       u32 led_ctrl1;
+       u32 led_ctrl2;
+       u32 led_ctrl3;
+       bool open_drain;
+};
+
+enum ar8327_led_num {
+       AR8327_LED_PHY0_0 = 0,
+       AR8327_LED_PHY0_1,
+       AR8327_LED_PHY0_2,
+       AR8327_LED_PHY1_0,
+       AR8327_LED_PHY1_1,
+       AR8327_LED_PHY1_2,
+       AR8327_LED_PHY2_0,
+       AR8327_LED_PHY2_1,
+       AR8327_LED_PHY2_2,
+       AR8327_LED_PHY3_0,
+       AR8327_LED_PHY3_1,
+       AR8327_LED_PHY3_2,
+       AR8327_LED_PHY4_0,
+       AR8327_LED_PHY4_1,
+       AR8327_LED_PHY4_2,
+};
+
+enum ar8327_led_mode {
+       AR8327_LED_MODE_HW = 0,
+       AR8327_LED_MODE_SW,
+};
+
+struct ar8327_led_info {
+       const char *name;
+       const char *default_trigger;
+       bool active_low;
+       enum ar8327_led_num led_num;
+       enum ar8327_led_mode mode;
+};
+
+#define AR8327_LED_INFO(_led, _mode, _name) {  \
+       .name = (_name),                        \
+       .led_num = AR8327_LED_ ## _led,         \
+       .mode = AR8327_LED_MODE_ ## _mode       \
+}
+
+struct ar8327_platform_data {
+       struct ar8327_pad_cfg *pad0_cfg;
+       struct ar8327_pad_cfg *pad5_cfg;
+       struct ar8327_pad_cfg *pad6_cfg;
+       struct ar8327_sgmii_cfg *sgmii_cfg;
+       struct ar8327_port_cfg port0_cfg;
+       struct ar8327_port_cfg port6_cfg;
+       struct ar8327_led_cfg *led_cfg;
+
+       int (*get_port_link)(unsigned port);
+
+       unsigned num_leds;
+       const struct ar8327_led_info *leds;
+};
+
+#endif /* AR8216_PLATFORM_H */
+
diff --git a/target/linux/generic/files-4.9/include/linux/ath5k_platform.h b/target/linux/generic/files-4.9/include/linux/ath5k_platform.h
new file mode 100644 (file)
index 0000000..ec85224
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_ATH5K_PLATFORM_H
+#define _LINUX_ATH5K_PLATFORM_H
+
+#define ATH5K_PLAT_EEP_MAX_WORDS       2048
+
+struct ath5k_platform_data {
+       u16 *eeprom_data;
+       u8 *macaddr;
+};
+
+#endif /* _LINUX_ATH5K_PLATFORM_H */
diff --git a/target/linux/generic/files-4.9/include/linux/ath9k_platform.h b/target/linux/generic/files-4.9/include/linux/ath9k_platform.h
new file mode 100644 (file)
index 0000000..f1f2ad4
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2008 Atheros Communications Inc.
+ * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _LINUX_ATH9K_PLATFORM_H
+#define _LINUX_ATH9K_PLATFORM_H
+
+#define ATH9K_PLAT_EEP_MAX_WORDS       2048
+
+struct ath9k_platform_data {
+       const char *eeprom_name;
+
+       u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
+       u8 *macaddr;
+
+       int led_pin;
+       u32 gpio_mask;
+       u32 gpio_val;
+
+       u32 bt_active_pin;
+       u32 bt_priority_pin;
+       u32 wlan_active_pin;
+
+       bool endian_check;
+       bool is_clk_25mhz;
+       bool tx_gain_buffalo;
+       bool disable_2ghz;
+       bool disable_5ghz;
+       bool led_active_high;
+
+       int (*get_mac_revision)(void);
+       int (*external_reset)(void);
+
+       bool use_eeprom;
+
+       int num_leds;
+       const struct gpio_led *leds;
+
+       unsigned num_btns;
+       const struct gpio_keys_button *btns;
+       unsigned btn_poll_interval;
+
+       bool ubnt_hsr;
+};
+
+#endif /* _LINUX_ATH9K_PLATFORM_H */
diff --git a/target/linux/generic/files-4.9/include/linux/myloader.h b/target/linux/generic/files-4.9/include/linux/myloader.h
new file mode 100644 (file)
index 0000000..d89e415
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  Compex's MyLoader specific definitions
+ *
+ *  Copyright (C) 2006-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef _MYLOADER_H_
+#define _MYLOADER_H_
+
+/* Myloader specific magic numbers */
+#define MYLO_MAGIC_SYS_PARAMS  0x20021107
+#define MYLO_MAGIC_PARTITIONS  0x20021103
+#define MYLO_MAGIC_BOARD_PARAMS        0x20021103
+
+/* Vendor ID's (seems to be same as the PCI vendor ID's) */
+#define VENID_COMPEX           0x11F6
+
+/* Devices based on the ADM5120 */
+#define DEVID_COMPEX_NP27G     0x0078
+#define DEVID_COMPEX_NP28G     0x044C
+#define DEVID_COMPEX_NP28GHS   0x044E
+#define DEVID_COMPEX_WP54Gv1C  0x0514
+#define DEVID_COMPEX_WP54G     0x0515
+#define DEVID_COMPEX_WP54AG    0x0546
+#define DEVID_COMPEX_WPP54AG   0x0550
+#define DEVID_COMPEX_WPP54G    0x0555
+
+/* Devices based on the Atheros AR2317 */
+#define DEVID_COMPEX_NP25G     0x05E6
+#define DEVID_COMPEX_WPE53G    0x05DC
+
+/* Devices based on the Atheros AR71xx */
+#define DEVID_COMPEX_WP543     0x0640
+#define DEVID_COMPEX_WPE72     0x0672
+
+/* Devices based on the IXP422 */
+#define DEVID_COMPEX_WP18      0x047E
+#define DEVID_COMPEX_NP18A     0x0489
+
+/* Other devices */
+#define DEVID_COMPEX_NP26G8M   0x03E8
+#define DEVID_COMPEX_NP26G16M  0x03E9
+
+struct mylo_partition {
+       uint16_t        flags;  /* partition flags */
+       uint16_t        type;   /* type of the partition */
+       uint32_t        addr;   /* relative address of the partition from the
+                                  flash start */
+       uint32_t        size;   /* size of the partition in bytes */
+       uint32_t        param;  /* if this is the active partition, the
+                                  MyLoader load code to this address */
+};
+
+#define PARTITION_FLAG_ACTIVE  0x8000 /* this is the active partition,
+                                       * MyLoader loads firmware from here */
+#define PARTITION_FLAG_ISRAM   0x2000 /* FIXME: this is a RAM partition? */
+#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */
+#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM
+                                       * before decompression */
+#define PARTITION_FLAG_LZMA    0x0100 /* partition data compressed by LZMA */
+#define PARTITION_FLAG_HAVEHDR  0x0002 /* the partition data have a header */
+
+#define PARTITION_TYPE_FREE    0
+#define PARTITION_TYPE_USED    1
+
+#define MYLO_MAX_PARTITIONS    8       /* maximum number of partitions in the
+                                          partition table */
+
+struct mylo_partition_table {
+       uint32_t        magic;          /* must be MYLO_MAGIC_PARTITIONS */
+       uint32_t        res0;           /* unknown/unused */
+       uint32_t        res1;           /* unknown/unused */
+       uint32_t        res2;           /* unknown/unused */
+       struct mylo_partition partitions[MYLO_MAX_PARTITIONS];
+};
+
+struct mylo_partition_header {
+       uint32_t        len;            /* length of the partition data */
+       uint32_t        crc;            /* CRC value of the partition data */
+};
+
+struct mylo_system_params {
+       uint32_t        magic;          /* must be MYLO_MAGIC_SYS_PARAMS */
+       uint32_t        res0;
+       uint32_t        res1;
+       uint32_t        mylo_ver;
+       uint16_t        vid;            /* Vendor ID */
+       uint16_t        did;            /* Device ID */
+       uint16_t        svid;           /* Sub Vendor ID */
+       uint16_t        sdid;           /* Sub Device ID */
+       uint32_t        rev;            /* device revision */
+       uint32_t        fwhi;
+       uint32_t        fwlo;
+       uint32_t        tftp_addr;
+       uint32_t        prog_start;
+       uint32_t        flash_size;     /* size of boot FLASH in bytes */
+       uint32_t        dram_size;      /* size of onboard RAM in bytes */
+};
+
+struct mylo_eth_addr {
+       uint8_t mac[6];
+       uint8_t csum[2];
+};
+
+#define MYLO_ETHADDR_COUNT     8       /* maximum number of ethernet address
+                                          in the board parameters */
+
+struct mylo_board_params {
+       uint32_t        magic;  /* must be MYLO_MAGIC_BOARD_PARAMS */
+       uint32_t        res0;
+       uint32_t        res1;
+       uint32_t        res2;
+       struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT];
+};
+
+#endif /* _MYLOADER_H_*/
diff --git a/target/linux/generic/files-4.9/include/linux/platform_data/adm6996-gpio.h b/target/linux/generic/files-4.9/include/linux/platform_data/adm6996-gpio.h
new file mode 100644 (file)
index 0000000..d5af9bb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * ADM6996 GPIO platform data
+ *
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of the GNU General Public License v2 as published by the
+ * Free Software Foundation
+ */
+
+#ifndef __PLATFORM_ADM6996_GPIO_H
+#define __PLATFORM_ADM6996_GPIO_H
+
+#include <linux/kernel.h>
+
+enum adm6996_model {
+       ADM6996FC = 1,
+       ADM6996M = 2,
+       ADM6996L = 3,
+};
+
+struct adm6996_gpio_platform_data {
+       u8 eecs;
+       u8 eesk;
+       u8 eedi;
+       enum adm6996_model model;
+};
+
+#endif
diff --git a/target/linux/generic/files-4.9/include/linux/platform_data/b53.h b/target/linux/generic/files-4.9/include/linux/platform_data/b53.h
new file mode 100644 (file)
index 0000000..7842741
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * B53 platform data
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __B53_H
+#define __B53_H
+
+#include <linux/kernel.h>
+
+struct b53_platform_data {
+       u32 chip_id;
+       u16 enabled_ports;
+
+       /* allow to specify an ethX alias */
+       const char *alias;
+
+       /* only used by MMAP'd driver */
+       unsigned big_endian:1;
+       void __iomem *regs;
+};
+
+#endif
diff --git a/target/linux/generic/files-4.9/include/linux/routerboot.h b/target/linux/generic/files-4.9/include/linux/routerboot.h
new file mode 100644 (file)
index 0000000..3cda858
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  Mikrotik's RouterBOOT definitions
+ *
+ *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ *
+ */
+
+#ifndef _ROUTERBOOT_H
+#define _ROUTERBOOT_H
+
+#define RB_MAC_SIZE            6
+
+/*
+ * Magic numbers
+ */
+#define RB_MAGIC_HARD  0x64726148 /* "Hard" */
+#define RB_MAGIC_SOFT  0x74666F53 /* "Soft" */
+#define RB_MAGIC_DAWN  0x6E776144 /* "Dawn" */
+
+#define RB_ID_TERMINATOR       0
+
+/*
+ * ID values for Hardware settings
+ */
+#define RB_ID_HARD_01          1
+#define RB_ID_HARD_02          2
+#define RB_ID_FLASH_INFO       3
+#define RB_ID_MAC_ADDRESS_PACK 4
+#define RB_ID_BOARD_NAME       5
+#define RB_ID_BIOS_VERSION     6
+#define RB_ID_HARD_07          7
+#define RB_ID_SDRAM_TIMINGS    8
+#define RB_ID_DEVICE_TIMINGS   9
+#define RB_ID_SOFTWARE_ID      10
+#define RB_ID_SERIAL_NUMBER    11
+#define RB_ID_HARD_12          12
+#define RB_ID_MEMORY_SIZE      13
+#define RB_ID_MAC_ADDRESS_COUNT        14
+#define RB_ID_HW_OPTIONS       21
+#define RB_ID_WLAN_DATA                22
+
+/*
+ * ID values for Software settings
+ */
+#define RB_ID_UART_SPEED       1
+#define RB_ID_BOOT_DELAY       2
+#define RB_ID_BOOT_DEVICE      3
+#define RB_ID_BOOT_KEY         4
+#define RB_ID_CPU_MODE         5
+#define RB_ID_FW_VERSION       6
+#define RB_ID_SOFT_07          7
+#define RB_ID_SOFT_08          8
+#define RB_ID_BOOT_PROTOCOL    9
+#define RB_ID_SOFT_10          10
+#define RB_ID_SOFT_11          11
+
+/*
+ * UART_SPEED values
+ */
+#define RB_UART_SPEED_115200   0
+#define RB_UART_SPEED_57600    1
+#define RB_UART_SPEED_38400    2
+#define RB_UART_SPEED_19200    3
+#define RB_UART_SPEED_9600     4
+#define RB_UART_SPEED_4800     5
+#define RB_UART_SPEED_2400     6
+#define RB_UART_SPEED_1200     7
+
+/*
+ * BOOT_DELAY values
+ */
+#define RB_BOOT_DELAY_0SEC     0
+#define RB_BOOT_DELAY_1SEC     1
+#define RB_BOOT_DELAY_2SEC     2
+
+/*
+ * BOOT_DEVICE values
+ */
+#define RB_BOOT_DEVICE_ETHER   0
+#define RB_BOOT_DEVICE_NANDETH 1
+#define RB_BOOT_DEVICE_ETHONCE 2
+#define RB_BOOT_DEVICE_NANDONLY        3
+
+/*
+ * BOOT_KEY values
+ */
+#define RB_BOOT_KEY_ANY                0
+#define RB_BOOT_KEY_DEL                1
+
+/*
+ * CPU_MODE values
+ */
+#define RB_CPU_MODE_POWERSAVE  0
+#define RB_CPU_MODE_REGULAR    1
+
+/*
+ * BOOT_PROTOCOL values
+ */
+#define RB_BOOT_PROTOCOL_BOOTP 0
+#define RB_BOOT_PROTOCOL_DHCP  1
+
+#endif /* _ROUTERBOOT_H */
diff --git a/target/linux/generic/files-4.9/include/linux/rt2x00_platform.h b/target/linux/generic/files-4.9/include/linux/rt2x00_platform.h
new file mode 100644 (file)
index 0000000..e10377e
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Platform data definition for the rt2x00 driver
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ */
+
+#ifndef _RT2X00_PLATFORM_H
+#define _RT2X00_PLATFORM_H
+
+struct rt2x00_platform_data {
+       char *eeprom_file_name;
+       const u8 *mac_address;
+
+       int disable_2ghz;
+       int disable_5ghz;
+};
+
+#endif /* _RT2X00_PLATFORM_H */
diff --git a/target/linux/generic/files-4.9/include/linux/rtl8366.h b/target/linux/generic/files-4.9/include/linux/rtl8366.h
new file mode 100644 (file)
index 0000000..e3ce8f5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Platform data definition for the Realtek RTL8366RB/S ethernet switch driver
+ *
+ * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8366_H
+#define _RTL8366_H
+
+#define RTL8366_DRIVER_NAME    "rtl8366"
+#define RTL8366S_DRIVER_NAME   "rtl8366s"
+#define RTL8366RB_DRIVER_NAME  "rtl8366rb"
+
+struct rtl8366_smi;
+
+enum rtl8366_type {
+       RTL8366_TYPE_UNKNOWN,
+       RTL8366_TYPE_S,
+       RTL8366_TYPE_RB,
+};
+
+struct rtl8366_initval {
+       unsigned        reg;
+       u16             val;
+};
+
+struct rtl8366_platform_data {
+       unsigned        gpio_sda;
+       unsigned        gpio_sck;
+       void            (*hw_reset)(struct rtl8366_smi *smi, bool active);
+
+       unsigned        num_initvals;
+       struct rtl8366_initval *initvals;
+};
+
+enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata);
+
+#endif /*  _RTL8366_H */
diff --git a/target/linux/generic/files-4.9/include/linux/rtl8367.h b/target/linux/generic/files-4.9/include/linux/rtl8367.h
new file mode 100644 (file)
index 0000000..855de6a
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Platform data definition for the Realtek RTL8367 ethernet switch driver
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#ifndef _RTL8367_H
+#define _RTL8367_H
+
+#define RTL8367_DRIVER_NAME    "rtl8367"
+#define RTL8367B_DRIVER_NAME   "rtl8367b"
+
+enum rtl8367_port_speed {
+       RTL8367_PORT_SPEED_10 = 0,
+       RTL8367_PORT_SPEED_100,
+       RTL8367_PORT_SPEED_1000,
+};
+
+struct rtl8367_port_ability {
+       int force_mode;
+       int nway;
+       int txpause;
+       int rxpause;
+       int link;
+       int duplex;
+       enum rtl8367_port_speed speed;
+};
+
+enum rtl8367_extif_mode {
+       RTL8367_EXTIF_MODE_DISABLED = 0,
+       RTL8367_EXTIF_MODE_RGMII,
+       RTL8367_EXTIF_MODE_MII_MAC,
+       RTL8367_EXTIF_MODE_MII_PHY,
+       RTL8367_EXTIF_MODE_TMII_MAC,
+       RTL8367_EXTIF_MODE_TMII_PHY,
+       RTL8367_EXTIF_MODE_GMII,
+       RTL8367_EXTIF_MODE_RGMII_33V,
+};
+
+struct rtl8367_extif_config {
+       unsigned int txdelay;
+       unsigned int rxdelay;
+       enum rtl8367_extif_mode mode;
+       struct rtl8367_port_ability ability;
+};
+
+struct rtl8367_platform_data {
+       unsigned gpio_sda;
+       unsigned gpio_sck;
+       void (*hw_reset)(bool active);
+
+       struct rtl8367_extif_config *extif0_cfg;
+       struct rtl8367_extif_config *extif1_cfg;
+};
+
+#endif /*  _RTL8367_H */
diff --git a/target/linux/generic/files-4.9/include/linux/switch.h b/target/linux/generic/files-4.9/include/linux/switch.h
new file mode 100644 (file)
index 0000000..4e62384
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * switch.h: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _LINUX_SWITCH_H
+#define _LINUX_SWITCH_H
+
+#include <net/genetlink.h>
+#include <uapi/linux/switch.h>
+
+struct switch_dev;
+struct switch_op;
+struct switch_val;
+struct switch_attr;
+struct switch_attrlist;
+struct switch_led_trigger;
+
+int register_switch(struct switch_dev *dev, struct net_device *netdev);
+void unregister_switch(struct switch_dev *dev);
+
+/**
+ * struct switch_attrlist - attribute list
+ *
+ * @n_attr: number of attributes
+ * @attr: pointer to the attributes array
+ */
+struct switch_attrlist {
+       int n_attr;
+       const struct switch_attr *attr;
+};
+
+enum switch_port_speed {
+       SWITCH_PORT_SPEED_UNKNOWN = 0,
+       SWITCH_PORT_SPEED_10 = 10,
+       SWITCH_PORT_SPEED_100 = 100,
+       SWITCH_PORT_SPEED_1000 = 1000,
+};
+
+struct switch_port_link {
+       bool link;
+       bool duplex;
+       bool aneg;
+       bool tx_flow;
+       bool rx_flow;
+       enum switch_port_speed speed;
+       /* in ethtool adv_t format */
+       u32 eee;
+};
+
+struct switch_port_stats {
+       unsigned long long tx_bytes;
+       unsigned long long rx_bytes;
+};
+
+/**
+ * struct switch_dev_ops - switch driver operations
+ *
+ * @attr_global: global switch attribute list
+ * @attr_port: port attribute list
+ * @attr_vlan: vlan attribute list
+ *
+ * Callbacks:
+ *
+ * @get_vlan_ports: read the port list of a VLAN
+ * @set_vlan_ports: set the port list of a VLAN
+ *
+ * @get_port_pvid: get the primary VLAN ID of a port
+ * @set_port_pvid: set the primary VLAN ID of a port
+ *
+ * @apply_config: apply all changed settings to the switch
+ * @reset_switch: resetting the switch
+ */
+struct switch_dev_ops {
+       struct switch_attrlist attr_global, attr_port, attr_vlan;
+
+       int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
+       int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
+
+       int (*get_port_pvid)(struct switch_dev *dev, int port, int *val);
+       int (*set_port_pvid)(struct switch_dev *dev, int port, int val);
+
+       int (*apply_config)(struct switch_dev *dev);
+       int (*reset_switch)(struct switch_dev *dev);
+
+       int (*get_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*set_port_link)(struct switch_dev *dev, int port,
+                            struct switch_port_link *link);
+       int (*get_port_stats)(struct switch_dev *dev, int port,
+                             struct switch_port_stats *stats);
+
+       int (*phy_read16)(struct switch_dev *dev, int addr, u8 reg, u16 *value);
+       int (*phy_write16)(struct switch_dev *dev, int addr, u8 reg, u16 value);
+};
+
+struct switch_dev {
+       struct device_node *of_node;
+       const struct switch_dev_ops *ops;
+       /* will be automatically filled */
+       char devname[IFNAMSIZ];
+
+       const char *name;
+       /* NB: either alias or netdev must be set */
+       const char *alias;
+       struct net_device *netdev;
+
+       unsigned int ports;
+       unsigned int vlans;
+       unsigned int cpu_port;
+
+       /* the following fields are internal for swconfig */
+       unsigned int id;
+       struct list_head dev_list;
+       unsigned long def_global, def_port, def_vlan;
+
+       struct mutex sw_mutex;
+       struct switch_port *portbuf;
+       struct switch_portmap *portmap;
+       struct switch_port_link linkbuf;
+
+       char buf[128];
+
+#ifdef CONFIG_SWCONFIG_LEDS
+       struct switch_led_trigger *led_trigger;
+#endif
+};
+
+struct switch_port {
+       u32 id;
+       u32 flags;
+};
+
+struct switch_portmap {
+       u32 virt;
+       const char *s;
+};
+
+struct switch_val {
+       const struct switch_attr *attr;
+       unsigned int port_vlan;
+       unsigned int len;
+       union {
+               const char *s;
+               u32 i;
+               struct switch_port *ports;
+               struct switch_port_link *link;
+       } value;
+};
+
+struct switch_attr {
+       int disabled;
+       int type;
+       const char *name;
+       const char *description;
+
+       int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
+       int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
+
+       /* for driver internal use */
+       int id;
+       int ofs;
+       int max;
+};
+
+int switch_generic_set_link(struct switch_dev *dev, int port,
+                           struct switch_port_link *link);
+
+#endif /* _LINUX_SWITCH_H */
diff --git a/target/linux/generic/files-4.9/include/uapi/linux/switch.h b/target/linux/generic/files-4.9/include/uapi/linux/switch.h
new file mode 100644 (file)
index 0000000..ea44965
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * switch.h: Switch configuration API
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_LINUX_SWITCH_H
+#define _UAPI_LINUX_SWITCH_H
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#ifndef __KERNEL__
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#endif
+
+/* main attributes */
+enum {
+       SWITCH_ATTR_UNSPEC,
+       /* global */
+       SWITCH_ATTR_TYPE,
+       /* device */
+       SWITCH_ATTR_ID,
+       SWITCH_ATTR_DEV_NAME,
+       SWITCH_ATTR_ALIAS,
+       SWITCH_ATTR_NAME,
+       SWITCH_ATTR_VLANS,
+       SWITCH_ATTR_PORTS,
+       SWITCH_ATTR_PORTMAP,
+       SWITCH_ATTR_CPU_PORT,
+       /* attributes */
+       SWITCH_ATTR_OP_ID,
+       SWITCH_ATTR_OP_TYPE,
+       SWITCH_ATTR_OP_NAME,
+       SWITCH_ATTR_OP_PORT,
+       SWITCH_ATTR_OP_VLAN,
+       SWITCH_ATTR_OP_VALUE_INT,
+       SWITCH_ATTR_OP_VALUE_STR,
+       SWITCH_ATTR_OP_VALUE_PORTS,
+       SWITCH_ATTR_OP_VALUE_LINK,
+       SWITCH_ATTR_OP_DESCRIPTION,
+       /* port lists */
+       SWITCH_ATTR_PORT,
+       SWITCH_ATTR_MAX
+};
+
+enum {
+       /* port map */
+       SWITCH_PORTMAP_PORTS,
+       SWITCH_PORTMAP_SEGMENT,
+       SWITCH_PORTMAP_VIRT,
+       SWITCH_PORTMAP_MAX
+};
+
+/* commands */
+enum {
+       SWITCH_CMD_UNSPEC,
+       SWITCH_CMD_GET_SWITCH,
+       SWITCH_CMD_NEW_ATTR,
+       SWITCH_CMD_LIST_GLOBAL,
+       SWITCH_CMD_GET_GLOBAL,
+       SWITCH_CMD_SET_GLOBAL,
+       SWITCH_CMD_LIST_PORT,
+       SWITCH_CMD_GET_PORT,
+       SWITCH_CMD_SET_PORT,
+       SWITCH_CMD_LIST_VLAN,
+       SWITCH_CMD_GET_VLAN,
+       SWITCH_CMD_SET_VLAN
+};
+
+/* data types */
+enum switch_val_type {
+       SWITCH_TYPE_UNSPEC,
+       SWITCH_TYPE_INT,
+       SWITCH_TYPE_STRING,
+       SWITCH_TYPE_PORTS,
+       SWITCH_TYPE_LINK,
+       SWITCH_TYPE_NOVAL,
+};
+
+/* port nested attributes */
+enum {
+       SWITCH_PORT_UNSPEC,
+       SWITCH_PORT_ID,
+       SWITCH_PORT_FLAG_TAGGED,
+       SWITCH_PORT_ATTR_MAX
+};
+
+/* link nested attributes */
+enum {
+       SWITCH_LINK_UNSPEC,
+       SWITCH_LINK_FLAG_LINK,
+       SWITCH_LINK_FLAG_DUPLEX,
+       SWITCH_LINK_FLAG_ANEG,
+       SWITCH_LINK_FLAG_TX_FLOW,
+       SWITCH_LINK_FLAG_RX_FLOW,
+       SWITCH_LINK_SPEED,
+       SWITCH_LINK_FLAG_EEE_100BASET,
+       SWITCH_LINK_FLAG_EEE_1000BASET,
+       SWITCH_LINK_ATTR_MAX,
+};
+
+#define SWITCH_ATTR_DEFAULTS_OFFSET    0x1000
+
+
+#endif /* _UAPI_LINUX_SWITCH_H */
diff --git a/target/linux/generic/files/Documentation/networking/adm6996.txt b/target/linux/generic/files/Documentation/networking/adm6996.txt
deleted file mode 100644 (file)
index ab59f1d..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-------- 
-
-ADM6996FC / ADM6996M switch chip driver
-
-
-1. General information
-
-  This driver supports the FC and M models only. The ADM6996F and L are
-  completely different chips.
-  
-  Support for the FC model is extremely limited at the moment. There is no VLAN
-  support as of yet. The driver will not offer an swconfig interface for the FC
-  chip.
-1.1 VLAN IDs
-
-  It is possible to define 16 different VLANs. Every VLAN has an identifier, its
-  VLAN ID. It is easiest if you use at most VLAN IDs 0-15. In that case, the
-  swconfig based configuration is very straightforward. To define two VLANs with
-  IDs 4 and 5, you can invoke, for example:
-  
-      # swconfig dev ethX vlan 4 set ports '0 1t 2 5t' 
-      # swconfig dev ethX vlan 5 set ports '0t 1t 5t'
-  
-  The swconfig framework will automatically invoke 'port Y set pvid Z' for every
-  port that is an untagged member of VLAN Y, setting its Primary VLAN ID. In
-  this example, ports 0 and 2 would get "pvid 4". The Primary VLAN ID of a port
-  is the VLAN ID associated with untagged packets coming in on that port.
-  
-  But if you wish to use VLAN IDs outside the range 0-15, this automatic
-  behaviour of the swconfig framework becomes a problem. The 16 VLANs that
-  swconfig can configure on the ADM6996 also have a "vid" setting. By default,
-  this is the same as the number of the VLAN entry, to make the simple behaviour
-  above possible. To still support a VLAN with a VLAN ID higher than 15
-  (presumably because you are in a network where such VLAN IDs are already in
-  use), you can change the "vid" setting of the VLAN to anything in the range
-  0-1023. But suppose you did the following:
-  
-      # swconfig dev ethX vlan 0 set vid 998 
-      # swconfig dev ethX vlan 0 set ports '0 2 5t'
-  Now the swconfig framework will issue 'port 0 set pvid 0' and 'port 2 set pvid
-  0'. But the "pvid" should be set to 998, so you are responsible for manually
-  fixing this!
-
-1.2 VLAN filtering
-
-  The switch is configured to apply source port filtering. This means that
-  packets are only accepted when the port the packets came in on is a member of
-  the VLAN the packet should go to.
-
-  Only membership of a VLAN is tested, it does not matter whether it is a tagged
-  or untagged membership.
-
-  For untagged packets, the destination VLAN is the Primary VLAN ID of the
-  incoming port. So if the PVID of a port is 0, but that port is not a member of
-  the VLAN with ID 0, this means that untagged packets on that port are dropped.
-  This can be used as a roundabout way of dropping untagged packets from a port,
-  a mode often referred to as "Admit only tagged packets".
-
-1.3 Reset
-
-  The two supported chip models do not have a sofware-initiated reset. When the
-  driver is initialised, as well as when the 'reset' swconfig option is invoked,
-  the driver will set those registers it knows about and supports to the correct
-  default value. But there are a lot of registers in the chip that the driver
-  does not support. If something changed those registers, invoking 'reset' or
-  performing a warm reboot might still leave the chip in a "broken" state. Only
-  a hardware reset will bring it back in the default state.
-
-2. Technical details on PHYs and the ADM6996
-
-  From the viewpoint of the Linux kernel, it is common that an Ethernet adapter
-  can be seen as a separate MAC entity and a separate PHY entity. The PHY entity
-  can be queried and set through registers accessible via an MDIO bus. A PHY
-  normally has a single address on that bus, in the range 0 through 31.
-
-  The ADM6996 has special-purpose registers in the range of PHYs 0 through 10.
-  Even though all these registers control a single ADM6996 chip, the Linux
-  kernel treats this as 11 separate PHYs.  The driver will bind to these
-  addresses to prevent a different PHY driver from binding and corrupting these
-  registers.
-
-  What Linux sees as the PHY on address 0 is meant for the Ethernet MAC
-  connected to the CPU port of the ADM6996 switch chip (port 5). This is the
-  Ethernet MAC you will use to send and receive data through the switch.
-
-  The PHYs at addresses 16 through 20 map to the PHYs on ports 0 through 4 of
-  the switch chip. These can be accessed with the Generic PHY driver, as the
-  registers have the common layout.
-
-  If a second Ethernet MAC on your board is wired to the port 4 PHY, that MAC
-  needs to bind to PHY address 20 for the port to work correctly.
-
-  The ADM6996 switch driver will reset the ports 0 through 3 on startup and when
-  'reset' is invoked. This could clash with a different PHY driver if the kernel
-  binds a PHY driver to address 16 through 19.
-
-  If Linux binds a PHY on addresses 1 through 10 to an Ethernet MAC, the ADM6996
-  driver will simply always report a connected 100 Mbit/s full-duplex link for
-  that PHY, and provide no other functionality. This is most likely not what you
-  want. So if you see a message in your log
-
-       ethX: PHY overlaps ADM6996, providing fixed PHY yy.
-
-  This is most likely an indication that ethX will not work properly, and your
-  kernel needs to be configured to attach a different PHY to that Ethernet MAC.
-
-  Controlling the mapping between MACs and PHYs is usually done in platform- or
-  board-specific fixup code. The ADM6996 driver has no influence over this.
diff --git a/target/linux/generic/files/arch/mips/fw/myloader/Makefile b/target/linux/generic/files/arch/mips/fw/myloader/Makefile
deleted file mode 100644 (file)
index 34acfd0..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-#
-# Makefile for the Compex's MyLoader support on MIPS architecture
-#
-
-lib-y += myloader.o
diff --git a/target/linux/generic/files/arch/mips/fw/myloader/myloader.c b/target/linux/generic/files/arch/mips/fw/myloader/myloader.c
deleted file mode 100644 (file)
index a26f9ad..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- *  Compex's MyLoader specific prom routines
- *
- *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#include <asm/addrspace.h>
-#include <asm/fw/myloader/myloader.h>
-
-#define SYS_PARAMS_ADDR                KSEG1ADDR(0x80000800)
-#define BOARD_PARAMS_ADDR      KSEG1ADDR(0x80000A00)
-#define PART_TABLE_ADDR                KSEG1ADDR(0x80000C00)
-#define BOOT_PARAMS_ADDR       KSEG1ADDR(0x80000E00)
-
-static struct myloader_info myloader_info __initdata;
-static int myloader_found __initdata;
-
-struct myloader_info * __init myloader_get_info(void)
-{
-       struct mylo_system_params *sysp;
-       struct mylo_board_params *boardp;
-       struct mylo_partition_table *parts;
-
-       if (myloader_found)
-               return &myloader_info;
-
-       sysp = (struct mylo_system_params *)(SYS_PARAMS_ADDR);
-       boardp = (struct mylo_board_params *)(BOARD_PARAMS_ADDR);
-       parts = (struct mylo_partition_table *)(PART_TABLE_ADDR);
-
-       printk(KERN_DEBUG "MyLoader: sysp=%08x, boardp=%08x, parts=%08x\n",
-               sysp->magic, boardp->magic, parts->magic);
-
-       /* Check for some magic numbers */
-       if (sysp->magic != MYLO_MAGIC_SYS_PARAMS ||
-           boardp->magic != MYLO_MAGIC_BOARD_PARAMS ||
-           le32_to_cpu(parts->magic) != MYLO_MAGIC_PARTITIONS)
-               return NULL;
-
-       printk(KERN_DEBUG "MyLoader: id=%04x:%04x, sub_id=%04x:%04x\n",
-               sysp->vid, sysp->did, sysp->svid, sysp->sdid);
-
-       myloader_info.vid = sysp->vid;
-       myloader_info.did = sysp->did;
-       myloader_info.svid = sysp->svid;
-       myloader_info.sdid = sysp->sdid;
-
-       memcpy(myloader_info.macs, boardp->addr, sizeof(myloader_info.macs));
-
-       myloader_found = 1;
-
-       return &myloader_info;
-}
diff --git a/target/linux/generic/files/drivers/leds/ledtrig-netdev.c b/target/linux/generic/files/drivers/leds/ledtrig-netdev.c
deleted file mode 100644 (file)
index 8d32490..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * LED Kernel Netdev Trigger
- *
- * Toggles the LED to reflect the link and traffic state of a named net device
- *
- * Copyright 2007 Oliver Jowett <oliver@opencloud.com>
- *
- * Derived from ledtrig-timer.c which is:
- *  Copyright 2005-2006 Openedhand Ltd.
- *  Author: Richard Purdie <rpurdie@openedhand.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/device.h>
-#include <linux/netdevice.h>
-#include <linux/timer.h>
-#include <linux/ctype.h>
-#include <linux/leds.h>
-
-#include "leds.h"
-
-/*
- * Configurable sysfs attributes:
- *
- * device_name - network device name to monitor
- *
- * interval - duration of LED blink, in milliseconds
- *
- * mode - either "none" (LED is off) or a space separated list of one or more of:
- *   link: LED's normal state reflects whether the link is up (has carrier) or not
- *   tx:   LED blinks on transmitted data
- *   rx:   LED blinks on receive data
- *
- * Some suggestions:
- *
- *  Simple link status LED:
- *  $ echo netdev >someled/trigger
- *  $ echo eth0 >someled/device_name
- *  $ echo link >someled/mode
- *
- *  Ethernet-style link/activity LED:
- *  $ echo netdev >someled/trigger
- *  $ echo eth0 >someled/device_name
- *  $ echo "link tx rx" >someled/mode
- *
- *  Modem-style tx/rx LEDs:
- *  $ echo netdev >led1/trigger
- *  $ echo ppp0 >led1/device_name
- *  $ echo tx >led1/mode
- *  $ echo netdev >led2/trigger
- *  $ echo ppp0 >led2/device_name
- *  $ echo rx >led2/mode
- *
- */
-
-#define MODE_LINK 1
-#define MODE_TX   2
-#define MODE_RX   4
-
-struct led_netdev_data {
-       spinlock_t lock;
-
-       struct delayed_work work;
-       struct notifier_block notifier;
-
-       struct led_classdev *led_cdev;
-       struct net_device *net_dev;
-
-       char device_name[IFNAMSIZ];
-       unsigned interval;
-       unsigned mode;
-       unsigned link_up;
-       unsigned last_activity;
-};
-
-static void set_baseline_state(struct led_netdev_data *trigger_data)
-{
-       if ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up)
-               led_set_brightness(trigger_data->led_cdev, LED_FULL);
-       else
-               led_set_brightness(trigger_data->led_cdev, LED_OFF);
-
-       if ((trigger_data->mode & (MODE_TX | MODE_RX)) != 0 && trigger_data->link_up)
-               schedule_delayed_work(&trigger_data->work, trigger_data->interval);
-}
-
-static ssize_t led_device_name_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-
-       spin_lock_bh(&trigger_data->lock);
-       sprintf(buf, "%s\n", trigger_data->device_name);
-       spin_unlock_bh(&trigger_data->lock);
-
-       return strlen(buf) + 1;
-}
-
-static ssize_t led_device_name_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-
-       if (size >= IFNAMSIZ)
-               return -EINVAL;
-
-       cancel_delayed_work_sync(&trigger_data->work);
-
-       spin_lock_bh(&trigger_data->lock);
-
-       strcpy(trigger_data->device_name, buf);
-       if (size > 0 && trigger_data->device_name[size-1] == '\n')
-               trigger_data->device_name[size-1] = 0;
-       trigger_data->link_up = 0;
-       trigger_data->last_activity = 0;
-
-       if (trigger_data->device_name[0] != 0) {
-               /* check for existing device to update from */
-               trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name);
-               if (trigger_data->net_dev != NULL)
-                       trigger_data->link_up = (dev_get_flags(trigger_data->net_dev) & IFF_LOWER_UP) != 0;
-       }
-
-       set_baseline_state(trigger_data);
-       spin_unlock_bh(&trigger_data->lock);
-
-       return size;
-}
-
-static DEVICE_ATTR(device_name, 0644, led_device_name_show, led_device_name_store);
-
-static ssize_t led_mode_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-
-       spin_lock_bh(&trigger_data->lock);
-
-       if (trigger_data->mode == 0) {
-               strcpy(buf, "none\n");
-       } else {
-               if (trigger_data->mode & MODE_LINK)
-                       strcat(buf, "link ");
-               if (trigger_data->mode & MODE_TX)
-                       strcat(buf, "tx ");
-               if (trigger_data->mode & MODE_RX)
-                       strcat(buf, "rx ");
-               strcat(buf, "\n");
-       }
-
-       spin_unlock_bh(&trigger_data->lock);
-
-       return strlen(buf)+1;
-}
-
-static ssize_t led_mode_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-       char copybuf[128];
-       int new_mode = -1;
-       char *p, *token;
-
-       /* take a copy since we don't want to trash the inbound buffer when using strsep */
-       strncpy(copybuf, buf, sizeof(copybuf));
-       copybuf[sizeof(copybuf) - 1] = 0;
-       p = copybuf;
-
-       while ((token = strsep(&p, " \t\n")) != NULL) {
-               if (!*token)
-                       continue;
-
-               if (new_mode == -1)
-                       new_mode = 0;
-
-               if (!strcmp(token, "none"))
-                       new_mode = 0;
-               else if (!strcmp(token, "tx"))
-                       new_mode |= MODE_TX;
-               else if (!strcmp(token, "rx"))
-                       new_mode |= MODE_RX;
-               else if (!strcmp(token, "link"))
-                       new_mode |= MODE_LINK;
-               else
-                       return -EINVAL;
-       }
-
-       if (new_mode == -1)
-               return -EINVAL;
-
-       cancel_delayed_work_sync(&trigger_data->work);
-
-       spin_lock_bh(&trigger_data->lock);
-       trigger_data->mode = new_mode;
-       set_baseline_state(trigger_data);
-       spin_unlock_bh(&trigger_data->lock);
-
-       return size;
-}
-
-static DEVICE_ATTR(mode, 0644, led_mode_show, led_mode_store);
-
-static ssize_t led_interval_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-
-       spin_lock_bh(&trigger_data->lock);
-       sprintf(buf, "%u\n", jiffies_to_msecs(trigger_data->interval));
-       spin_unlock_bh(&trigger_data->lock);
-
-       return strlen(buf) + 1;
-}
-
-static ssize_t led_interval_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-       int ret = -EINVAL;
-       char *after;
-       unsigned long value = simple_strtoul(buf, &after, 10);
-       size_t count = after - buf;
-
-       if (isspace(*after))
-               count++;
-
-       /* impose some basic bounds on the timer interval */
-       if (count == size && value >= 5 && value <= 10000) {
-               cancel_delayed_work_sync(&trigger_data->work);
-
-               spin_lock_bh(&trigger_data->lock);
-               trigger_data->interval = msecs_to_jiffies(value);
-               set_baseline_state(trigger_data); /* resets timer */
-               spin_unlock_bh(&trigger_data->lock);
-
-               ret = count;
-       }
-
-       return ret;
-}
-
-static DEVICE_ATTR(interval, 0644, led_interval_show, led_interval_store);
-
-static int netdev_trig_notify(struct notifier_block *nb,
-                             unsigned long evt,
-                             void *dv)
-{
-       struct net_device *dev = netdev_notifier_info_to_dev((struct netdev_notifier_info *) dv);
-       struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier);
-
-       if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER && evt != NETDEV_CHANGENAME)
-               return NOTIFY_DONE;
-
-       if (strcmp(dev->name, trigger_data->device_name))
-               return NOTIFY_DONE;
-
-       cancel_delayed_work_sync(&trigger_data->work);
-
-       spin_lock_bh(&trigger_data->lock);
-
-       if (evt == NETDEV_REGISTER || evt == NETDEV_CHANGENAME) {
-               if (trigger_data->net_dev != NULL)
-                       dev_put(trigger_data->net_dev);
-
-               dev_hold(dev);
-               trigger_data->net_dev = dev;
-               trigger_data->link_up = 0;
-               goto done;
-       }
-
-       if (evt == NETDEV_UNREGISTER && trigger_data->net_dev != NULL) {
-               dev_put(trigger_data->net_dev);
-               trigger_data->net_dev = NULL;
-               goto done;
-       }
-
-       /* UP / DOWN / CHANGE */
-
-       trigger_data->link_up = (evt != NETDEV_DOWN && netif_carrier_ok(dev));
-       set_baseline_state(trigger_data);
-
-done:
-       spin_unlock_bh(&trigger_data->lock);
-       return NOTIFY_DONE;
-}
-
-/* here's the real work! */
-static void netdev_trig_work(struct work_struct *work)
-{
-       struct led_netdev_data *trigger_data = container_of(work, struct led_netdev_data, work.work);
-       struct rtnl_link_stats64 *dev_stats;
-       unsigned new_activity;
-       struct rtnl_link_stats64 temp;
-
-       if (!trigger_data->link_up || !trigger_data->net_dev || (trigger_data->mode & (MODE_TX | MODE_RX)) == 0) {
-               /* we don't need to do timer work, just reflect link state. */
-               led_set_brightness(trigger_data->led_cdev, ((trigger_data->mode & MODE_LINK) != 0 && trigger_data->link_up) ? LED_FULL : LED_OFF);
-               return;
-       }
-
-       dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
-       new_activity =
-               ((trigger_data->mode & MODE_TX) ? dev_stats->tx_packets : 0) +
-               ((trigger_data->mode & MODE_RX) ? dev_stats->rx_packets : 0);
-
-       if (trigger_data->mode & MODE_LINK) {
-               /* base state is ON (link present) */
-               /* if there's no link, we don't get this far and the LED is off */
-
-               /* OFF -> ON always */
-               /* ON -> OFF on activity */
-               if (trigger_data->led_cdev->brightness == LED_OFF) {
-                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
-               } else if (trigger_data->last_activity != new_activity) {
-                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
-               }
-       } else {
-               /* base state is OFF */
-               /* ON -> OFF always */
-               /* OFF -> ON on activity */
-               if (trigger_data->led_cdev->brightness == LED_FULL) {
-                       led_set_brightness(trigger_data->led_cdev, LED_OFF);
-               } else if (trigger_data->last_activity != new_activity) {
-                       led_set_brightness(trigger_data->led_cdev, LED_FULL);
-               }
-       }
-
-       trigger_data->last_activity = new_activity;
-       schedule_delayed_work(&trigger_data->work, trigger_data->interval);
-}
-
-static void netdev_trig_activate(struct led_classdev *led_cdev)
-{
-       struct led_netdev_data *trigger_data;
-       int rc;
-
-       trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL);
-       if (!trigger_data)
-               return;
-
-       spin_lock_init(&trigger_data->lock);
-
-       trigger_data->notifier.notifier_call = netdev_trig_notify;
-       trigger_data->notifier.priority = 10;
-
-       INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work);
-
-       trigger_data->led_cdev = led_cdev;
-       trigger_data->net_dev = NULL;
-       trigger_data->device_name[0] = 0;
-
-       trigger_data->mode = 0;
-       trigger_data->interval = msecs_to_jiffies(50);
-       trigger_data->link_up = 0;
-       trigger_data->last_activity = 0;
-
-       led_cdev->trigger_data = trigger_data;
-
-       rc = device_create_file(led_cdev->dev, &dev_attr_device_name);
-       if (rc)
-               goto err_out;
-       rc = device_create_file(led_cdev->dev, &dev_attr_mode);
-       if (rc)
-               goto err_out_device_name;
-       rc = device_create_file(led_cdev->dev, &dev_attr_interval);
-       if (rc)
-               goto err_out_mode;
-
-       register_netdevice_notifier(&trigger_data->notifier);
-       return;
-
-err_out_mode:
-       device_remove_file(led_cdev->dev, &dev_attr_mode);
-err_out_device_name:
-       device_remove_file(led_cdev->dev, &dev_attr_device_name);
-err_out:
-       led_cdev->trigger_data = NULL;
-       kfree(trigger_data);
-}
-
-static void netdev_trig_deactivate(struct led_classdev *led_cdev)
-{
-       struct led_netdev_data *trigger_data = led_cdev->trigger_data;
-
-       if (trigger_data) {
-               unregister_netdevice_notifier(&trigger_data->notifier);
-
-               device_remove_file(led_cdev->dev, &dev_attr_device_name);
-               device_remove_file(led_cdev->dev, &dev_attr_mode);
-               device_remove_file(led_cdev->dev, &dev_attr_interval);
-
-               cancel_delayed_work_sync(&trigger_data->work);
-
-               spin_lock_bh(&trigger_data->lock);
-
-               if (trigger_data->net_dev) {
-                       dev_put(trigger_data->net_dev);
-                       trigger_data->net_dev = NULL;
-               }
-
-               spin_unlock_bh(&trigger_data->lock);
-
-               kfree(trigger_data);
-       }
-}
-
-static struct led_trigger netdev_led_trigger = {
-       .name     = "netdev",
-       .activate = netdev_trig_activate,
-       .deactivate = netdev_trig_deactivate,
-};
-
-static int __init netdev_trig_init(void)
-{
-       return led_trigger_register(&netdev_led_trigger);
-}
-
-static void __exit netdev_trig_exit(void)
-{
-       led_trigger_unregister(&netdev_led_trigger);
-}
-
-module_init(netdev_trig_init);
-module_exit(netdev_trig_exit);
-
-MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>");
-MODULE_DESCRIPTION("Netdev LED trigger");
-MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/drivers/misc/owl-loader.c b/target/linux/generic/files/drivers/misc/owl-loader.c
deleted file mode 100644 (file)
index f11cb2b..0000000
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Initialize Owl Emulation Devices
- *
- * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com>
- * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- * Some devices (like the Cisco Meraki Z1 Cloud Managed Teleworker Gateway)
- * need to be able to initialize the PCIe wifi device. Normally, this is done
- * during the early stages of booting linux, because the necessary init code
- * is read from the memory mapped SPI and passed to pci_enable_ath9k_fixup.
- * However,this isn't possible for devices which have the init code for the
- * Atheros chip stored on NAND. Hence, this module can be used to initialze
- * the chip when the user-space is ready to extract the init code.
- */
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/completion.h>
-#include <linux/etherdevice.h>
-#include <linux/firmware.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/ath9k_platform.h>
-
-struct owl_ctx {
-       struct completion eeprom_load;
-};
-
-#define EEPROM_FILENAME_LEN 100
-
-#define AR5416_EEPROM_MAGIC 0xa55a
-
-static int ath9k_pci_fixup(struct pci_dev *pdev, const u16 *cal_data,
-                          size_t cal_len)
-{
-       void __iomem *mem;
-       const void *cal_end = (void *)cal_data + cal_len;
-       const struct {
-               __be16 reg;
-               __be16 low_val;
-               __be16 high_val;
-       } __packed *data;
-       u16 cmd;
-       u32 bar0;
-       bool swap_needed = false;
-
-       if (*cal_data != AR5416_EEPROM_MAGIC) {
-               if (*cal_data != swab16(AR5416_EEPROM_MAGIC)) {
-                       dev_err(&pdev->dev, "invalid calibration data\n");
-                       return -EINVAL;
-               }
-
-               dev_dbg(&pdev->dev, "calibration data needs swapping\n");
-               swap_needed = true;
-       }
-
-       dev_info(&pdev->dev, "fixup device configuration\n");
-
-       mem = pcim_iomap(pdev, 0, 0);
-       if (!mem) {
-               dev_err(&pdev->dev, "ioremap error\n");
-               return -EINVAL;
-       }
-
-       pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &bar0);
-       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0,
-                              pci_resource_start(pdev, 0));
-       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-       cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
-       pci_write_config_word(pdev, PCI_COMMAND, cmd);
-
-       /* set pointer to first reg address */
-       for (data = (const void *) (cal_data + 3);
-            (const void *) data <= cal_end && data->reg != cpu_to_be16(~0);
-            data++) {
-               u32 val;
-               u16 reg;
-
-               reg = data->reg;
-               val = data->low_val;
-               val |= data->high_val << 16;
-
-               if (swap_needed) {
-                       reg = swab16(reg);
-                       val = swahb32(val);
-               }
-
-#ifdef CONFIG_LANTIQ
-               val = swab32(val);
-#endif
-
-               __raw_writel(val, mem + reg);
-               udelay(100);
-       }
-
-       pci_read_config_word(pdev, PCI_COMMAND, &cmd);
-       cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
-       pci_write_config_word(pdev, PCI_COMMAND, cmd);
-
-       pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, bar0);
-       pcim_iounmap(pdev, mem);
-
-       pci_disable_device(pdev);
-
-       return 0;
-}
-
-static void owl_fw_cb(const struct firmware *fw, void *context)
-{
-       struct pci_dev *pdev = (struct pci_dev *) context;
-       struct owl_ctx *ctx = (struct owl_ctx *) pci_get_drvdata(pdev);
-       struct ath9k_platform_data *pdata = dev_get_platdata(&pdev->dev);
-       struct pci_bus *bus;
-
-       complete(&ctx->eeprom_load);
-
-       if (!fw) {
-               dev_err(&pdev->dev, "no eeprom data received.\n");
-               goto release;
-       }
-
-       /* also note that we are doing *u16 operations on the file */
-       if (fw->size > sizeof(pdata->eeprom_data) || fw->size < 0x200 ||
-           (fw->size & 1) == 1) {
-               dev_err(&pdev->dev, "eeprom file has an invalid size.\n");
-               goto release;
-       }
-
-       if (pdata) {
-               memcpy(pdata->eeprom_data, fw->data, fw->size);
-
-               /*
-                * eeprom has been successfully loaded - pass the data to ath9k
-                * but remove the eeprom_name, so it doesn't try to load it too.
-                */
-               pdata->eeprom_name = NULL;
-       }
-
-       if (ath9k_pci_fixup(pdev, (const u16 *) fw->data, fw->size))
-               goto release;
-
-       pci_lock_rescan_remove();
-       bus = pdev->bus;
-       pci_stop_and_remove_bus_device(pdev);
-       /*
-        * the device should come back with the proper
-        * ProductId. But we have to initiate a rescan.
-        */
-       pci_rescan_bus(bus);
-       pci_unlock_rescan_remove();
-
-release:
-       release_firmware(fw);
-}
-
-static const char *owl_get_eeprom_name(struct pci_dev *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct ath9k_platform_data *pdata;
-       char *eeprom_name;
-
-       /* try the existing platform data first */
-       pdata = dev_get_platdata(dev);
-       if (pdata && pdata->eeprom_name)
-               return pdata->eeprom_name;
-
-       dev_dbg(dev, "using auto-generated eeprom filename\n");
-
-       eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
-       if (!eeprom_name)
-               return NULL;
-
-       /* this should match the pattern used in ath9k/init.c */
-       scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
-                 dev_name(dev));
-
-       return eeprom_name;
-}
-
-static int owl_probe(struct pci_dev *pdev,
-                   const struct pci_device_id *id)
-{
-       struct owl_ctx *ctx;
-       const char *eeprom_name;
-       int err = 0;
-
-       if (pcim_enable_device(pdev))
-               return -EIO;
-
-       pcim_pin_device(pdev);
-
-       eeprom_name = owl_get_eeprom_name(pdev);
-       if (!eeprom_name) {
-               dev_err(&pdev->dev, "no eeprom filename found.\n");
-               return -ENODEV;
-       }
-
-       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
-       if (!ctx) {
-               dev_err(&pdev->dev, "failed to alloc device context.\n");
-               return -ENOMEM;
-       }
-       init_completion(&ctx->eeprom_load);
-
-       pci_set_drvdata(pdev, ctx);
-       err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
-                                     &pdev->dev, GFP_KERNEL, pdev, owl_fw_cb);
-       if (err) {
-               dev_err(&pdev->dev, "failed to request caldata (%d).\n", err);
-               kfree(ctx);
-       }
-       return err;
-}
-
-static void owl_remove(struct pci_dev *pdev)
-{
-       struct owl_ctx *ctx = pci_get_drvdata(pdev);
-
-       if (ctx) {
-               wait_for_completion(&ctx->eeprom_load);
-               pci_set_drvdata(pdev, NULL);
-               kfree(ctx);
-       }
-}
-
-static const struct pci_device_id owl_pci_table[] = {
-       { PCI_VDEVICE(ATHEROS, 0xff1c) }, /* PCIe */
-       { PCI_VDEVICE(ATHEROS, 0xff1d) }, /* PCI */
-       { },
-};
-MODULE_DEVICE_TABLE(pci, owl_pci_table);
-
-static struct pci_driver owl_driver = {
-       .name           = "owl-loader",
-       .id_table       = owl_pci_table,
-       .probe          = owl_probe,
-       .remove         = owl_remove,
-};
-module_pci_driver(owl_driver);
-MODULE_AUTHOR("Christian Lamparter <chunkeey@googlemail.com>");
-MODULE_DESCRIPTION("Initializes Atheros' Owl Emulation devices");
-MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig b/target/linux/generic/files/drivers/mtd/mtdsplit/Kconfig
deleted file mode 100644 (file)
index 81ece43..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-config MTD_SPLIT
-       def_bool n
-       help
-         Generic MTD split support.
-
-config MTD_SPLIT_SUPPORT
-       def_bool MTD = y
-
-comment "Rootfs partition parsers"
-
-config MTD_SPLIT_SQUASHFS_ROOT
-       bool "Squashfs based root partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-       default n
-       help
-         This provides a parsing function which allows to detect the
-         offset and size of the unused portion of a rootfs partition
-         containing a squashfs.
-
-comment "Firmware partition parsers"
-
-config MTD_SPLIT_SEAMA_FW
-       bool "Seama firmware parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_WRGG_FW
-       bool "WRGG firmware parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_UIMAGE_FW
-       bool "uImage based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_FIT_FW
-       bool "FIT based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_LZMA_FW
-       bool "LZMA compressed kernel based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_TPLINK_FW
-       bool "TP-Link firmware parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_TRX_FW
-       bool "TRX image based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_BRNIMAGE_FW
-       bool "brnImage (brnboot image) firmware parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_EVA_FW
-       bool "EVA image based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_MINOR_FW
-       bool "Mikrotik NOR image based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
-
-config MTD_SPLIT_JIMAGE_FW
-       bool "JBOOT Image based firmware partition parser"
-       depends on MTD_SPLIT_SUPPORT
-       select MTD_SPLIT
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile b/target/linux/generic/files/drivers/mtd/mtdsplit/Makefile
deleted file mode 100644 (file)
index 206e754..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-obj-$(CONFIG_MTD_SPLIT)                += mtdsplit.o
-obj-$(CONFIG_MTD_SPLIT_SEAMA_FW) += mtdsplit_seama.o
-obj-$(CONFIG_MTD_SPLIT_SQUASHFS_ROOT) += mtdsplit_squashfs.o
-obj-$(CONFIG_MTD_SPLIT_UIMAGE_FW) += mtdsplit_uimage.o
-obj-$(CONFIG_MTD_SPLIT_FIT_FW) += mtdsplit_fit.o
-obj-$(CONFIG_MTD_SPLIT_LZMA_FW) += mtdsplit_lzma.o
-obj-$(CONFIG_MTD_SPLIT_TPLINK_FW) += mtdsplit_tplink.o
-obj-$(CONFIG_MTD_SPLIT_TRX_FW) += mtdsplit_trx.o
-obj-$(CONFIG_MTD_SPLIT_BRNIMAGE_FW) += mtdsplit_brnimage.o
-obj-$(CONFIG_MTD_SPLIT_EVA_FW) += mtdsplit_eva.o
-obj-$(CONFIG_MTD_SPLIT_WRGG_FW) += mtdsplit_wrgg.o
-obj-$(CONFIG_MTD_SPLIT_MINOR_FW) += mtdsplit_minor.o
-obj-$(CONFIG_MTD_SPLIT_JIMAGE_FW) += mtdsplit_jimage.o
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.c
deleted file mode 100644 (file)
index b2e51dc..0000000
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
- * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
- * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
- * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- */
-
-#define pr_fmt(fmt)    "mtdsplit: " fmt
-
-#include <linux/export.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/magic.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define UBI_EC_MAGIC                   0x55424923      /* UBI# */
-
-struct squashfs_super_block {
-       __le32 s_magic;
-       __le32 pad0[9];
-       __le64 bytes_used;
-};
-
-int mtd_get_squashfs_len(struct mtd_info *master,
-                        size_t offset,
-                        size_t *squashfs_len)
-{
-       struct squashfs_super_block sb;
-       size_t retlen;
-       int err;
-
-       err = mtd_read(master, offset, sizeof(sb), &retlen, (void *)&sb);
-       if (err || (retlen != sizeof(sb))) {
-               pr_alert("error occured while reading from \"%s\"\n",
-                        master->name);
-               return -EIO;
-       }
-
-       if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC) {
-               pr_alert("no squashfs found in \"%s\"\n", master->name);
-               return -EINVAL;
-       }
-
-       retlen = le64_to_cpu(sb.bytes_used);
-       if (retlen <= 0) {
-               pr_alert("squashfs is empty in \"%s\"\n", master->name);
-               return -ENODEV;
-       }
-
-       if (offset + retlen > master->size) {
-               pr_alert("squashfs has invalid size in \"%s\"\n",
-                        master->name);
-               return -EINVAL;
-       }
-
-       *squashfs_len = retlen;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(mtd_get_squashfs_len);
-
-static ssize_t mtd_next_eb(struct mtd_info *mtd, size_t offset)
-{
-       return mtd_rounddown_to_eb(offset, mtd) + mtd->erasesize;
-}
-
-int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
-                          enum mtdsplit_part_type *type)
-{
-       u32 magic;
-       size_t retlen;
-       int ret;
-
-       ret = mtd_read(mtd, offset, sizeof(magic), &retlen,
-                      (unsigned char *) &magic);
-       if (ret)
-               return ret;
-
-       if (retlen != sizeof(magic))
-               return -EIO;
-
-       if (le32_to_cpu(magic) == SQUASHFS_MAGIC) {
-               if (type)
-                       *type = MTDSPLIT_PART_TYPE_SQUASHFS;
-               return 0;
-       } else if (magic == 0x19852003) {
-               if (type)
-                       *type = MTDSPLIT_PART_TYPE_JFFS2;
-               return 0;
-       } else if (be32_to_cpu(magic) == UBI_EC_MAGIC) {
-               if (type)
-                       *type = MTDSPLIT_PART_TYPE_UBI;
-               return 0;
-       }
-
-       return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(mtd_check_rootfs_magic);
-
-int mtd_find_rootfs_from(struct mtd_info *mtd,
-                        size_t from,
-                        size_t limit,
-                        size_t *ret_offset,
-                        enum mtdsplit_part_type *type)
-{
-       size_t offset;
-       int err;
-
-       for (offset = from; offset < limit;
-            offset = mtd_next_eb(mtd, offset)) {
-               err = mtd_check_rootfs_magic(mtd, offset, type);
-               if (err)
-                       continue;
-
-               *ret_offset = offset;
-               return 0;
-       }
-
-       return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(mtd_find_rootfs_from);
-
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.h b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit.h
deleted file mode 100644 (file)
index 71d62a8..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2009-2013 Felix Fietkau <nbd@nbd.name>
- * Copyright (C) 2009-2013 Gabor Juhos <juhosg@openwrt.org>
- * Copyright (C) 2012 Jonas Gorski <jogo@openwrt.org>
- * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- */
-
-#ifndef _MTDSPLIT_H
-#define _MTDSPLIT_H
-
-#define KERNEL_PART_NAME       "kernel"
-#define ROOTFS_PART_NAME       "rootfs"
-#define UBI_PART_NAME          "ubi"
-
-#define ROOTFS_SPLIT_NAME      "rootfs_data"
-
-enum mtdsplit_part_type {
-       MTDSPLIT_PART_TYPE_UNK = 0,
-       MTDSPLIT_PART_TYPE_SQUASHFS,
-       MTDSPLIT_PART_TYPE_JFFS2,
-       MTDSPLIT_PART_TYPE_UBI,
-};
-
-#ifdef CONFIG_MTD_SPLIT
-int mtd_get_squashfs_len(struct mtd_info *master,
-                        size_t offset,
-                        size_t *squashfs_len);
-
-int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
-                          enum mtdsplit_part_type *type);
-
-int mtd_find_rootfs_from(struct mtd_info *mtd,
-                        size_t from,
-                        size_t limit,
-                        size_t *ret_offset,
-                        enum mtdsplit_part_type *type);
-
-#else
-static inline int mtd_get_squashfs_len(struct mtd_info *master,
-                                      size_t offset,
-                                      size_t *squashfs_len)
-{
-       return -ENODEV;
-}
-
-static inline int mtd_check_rootfs_magic(struct mtd_info *mtd, size_t offset,
-                                        enum mtdsplit_part_type *type)
-{
-       return -EINVAL;
-}
-
-static inline int mtd_find_rootfs_from(struct mtd_info *mtd,
-                                      size_t from,
-                                      size_t limit,
-                                      size_t *ret_offset,
-                                      enum mtdsplit_part_type *type)
-{
-       return -ENODEV;
-}
-#endif /* CONFIG_MTD_SPLIT */
-
-#endif /* _MTDSPLIT_H */
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_brnimage.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_brnimage.c
deleted file mode 100644 (file)
index 3f2d796..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
- *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define BRNIMAGE_NR_PARTS      2
-
-#define BRNIMAGE_ALIGN_BYTES   0x400
-#define BRNIMAGE_FOOTER_SIZE   12
-
-#define BRNIMAGE_MIN_OVERHEAD  (BRNIMAGE_FOOTER_SIZE)
-#define BRNIMAGE_MAX_OVERHEAD  (BRNIMAGE_ALIGN_BYTES + BRNIMAGE_FOOTER_SIZE)
-
-static int mtdsplit_parse_brnimage(struct mtd_info *master,
-                               const struct mtd_partition **pparts,
-                               struct mtd_part_parser_data *data)
-{
-       struct mtd_partition *parts;
-       uint32_t buf;
-       unsigned long rootfs_offset, rootfs_size, kernel_size;
-       size_t len;
-       int ret = 0;
-
-       for (rootfs_offset = 0; rootfs_offset < master->size;
-            rootfs_offset += BRNIMAGE_ALIGN_BYTES) {
-               ret = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
-               if (!ret)
-                       break;
-       }
-
-       if (ret)
-               return ret;
-
-       if (rootfs_offset >= master->size)
-               return -EINVAL;
-
-       ret = mtd_read(master, rootfs_offset - BRNIMAGE_FOOTER_SIZE, 4, &len,
-                       (void *)&buf);
-       if (ret)
-               return ret;
-
-       if (len != 4)
-               return -EIO;
-
-       kernel_size = le32_to_cpu(buf);
-
-       if (kernel_size > (rootfs_offset - BRNIMAGE_MIN_OVERHEAD))
-               return -EINVAL;
-
-       if (kernel_size < (rootfs_offset - BRNIMAGE_MAX_OVERHEAD))
-               return -EINVAL;
-
-       /*
-        * The footer must be untouched as it contains the checksum of the
-        * original brnImage (kernel + squashfs)!
-        */
-       rootfs_size = master->size - rootfs_offset - BRNIMAGE_FOOTER_SIZE;
-
-       parts = kzalloc(BRNIMAGE_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = 0;
-       parts[0].size = kernel_size;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = rootfs_size;
-
-       *pparts = parts;
-       return BRNIMAGE_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_brnimage_parser = {
-       .owner = THIS_MODULE,
-       .name = "brnimage-fw",
-       .parse_fn = mtdsplit_parse_brnimage,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_brnimage_init(void)
-{
-       register_mtd_parser(&mtdsplit_brnimage_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_brnimage_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_eva.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_eva.c
deleted file mode 100644 (file)
index 746944e..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- *  Copyright (C) 2012 John Crispin <blogic@openwrt.org>
- *  Copyright (C) 2015 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define EVA_NR_PARTS           2
-#define EVA_MAGIC              0xfeed1281
-#define EVA_FOOTER_SIZE                0x18
-#define EVA_DUMMY_SQUASHFS_SIZE        0x100
-
-struct eva_image_header {
-       uint32_t        magic;
-       uint32_t        size;
-};
-
-static int mtdsplit_parse_eva(struct mtd_info *master,
-                               const struct mtd_partition **pparts,
-                               struct mtd_part_parser_data *data)
-{
-       struct mtd_partition *parts;
-       struct eva_image_header hdr;
-       size_t retlen;
-       unsigned long kernel_size, rootfs_offset;
-       int err;
-
-       err = mtd_read(master, 0, sizeof(hdr), &retlen, (void *) &hdr);
-       if (err)
-               return err;
-
-       if (retlen != sizeof(hdr))
-               return -EIO;
-
-       if (le32_to_cpu(hdr.magic) != EVA_MAGIC)
-               return -EINVAL;
-
-       kernel_size = le32_to_cpu(hdr.size) + EVA_FOOTER_SIZE;
-
-       /* rootfs starts at the next 0x10000 boundary: */
-       rootfs_offset = round_up(kernel_size, 0x10000);
-
-       /* skip the dummy EVA squashfs partition (with wrong endianness): */
-       rootfs_offset += EVA_DUMMY_SQUASHFS_SIZE;
-
-       if (rootfs_offset >= master->size)
-               return -EINVAL;
-
-       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
-       if (err)
-               return err;
-
-       parts = kzalloc(EVA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = 0;
-       parts[0].size = kernel_size;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = master->size - rootfs_offset;
-
-       *pparts = parts;
-       return EVA_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_eva_parser = {
-       .owner = THIS_MODULE,
-       .name = "eva-fw",
-       .parse_fn = mtdsplit_parse_eva,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_eva_init(void)
-{
-       register_mtd_parser(&mtdsplit_eva_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_eva_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_fit.c
deleted file mode 100644 (file)
index f356adc..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (c) 2015 The Linux Foundation
- * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/module.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/types.h>
-#include <linux/byteorder/generic.h>
-#include <linux/slab.h>
-#include <linux/of_fdt.h>
-
-#include "mtdsplit.h"
-
-struct fdt_header {
-       uint32_t magic;                  /* magic word FDT_MAGIC */
-       uint32_t totalsize;              /* total size of DT block */
-       uint32_t off_dt_struct;          /* offset to structure */
-       uint32_t off_dt_strings;         /* offset to strings */
-       uint32_t off_mem_rsvmap;         /* offset to memory reserve map */
-       uint32_t version;                /* format version */
-       uint32_t last_comp_version;      /* last compatible version */
-
-       /* version 2 fields below */
-       uint32_t boot_cpuid_phys;        /* Which physical CPU id we're
-                                           booting on */
-       /* version 3 fields below */
-       uint32_t size_dt_strings;        /* size of the strings block */
-
-       /* version 17 fields below */
-       uint32_t size_dt_struct;         /* size of the structure block */
-};
-
-static int
-mtdsplit_fit_parse(struct mtd_info *mtd,
-                  const struct mtd_partition **pparts,
-                  struct mtd_part_parser_data *data)
-{
-       struct fdt_header hdr;
-       size_t hdr_len, retlen;
-       size_t offset;
-       size_t fit_offset, fit_size;
-       size_t rootfs_offset, rootfs_size;
-       struct mtd_partition *parts;
-       int ret;
-
-       hdr_len = sizeof(struct fdt_header);
-
-       /* Parse the MTD device & search for the FIT image location */
-       for(offset = 0; offset < mtd->size; offset += mtd->erasesize) {
-               ret = mtd_read(mtd, 0, hdr_len, &retlen, (void*) &hdr);
-               if (ret) {
-                       pr_err("read error in \"%s\" at offset 0x%llx\n",
-                              mtd->name, (unsigned long long) offset);
-                       return ret;
-               }
-
-               if (retlen != hdr_len) {
-                       pr_err("short read in \"%s\"\n", mtd->name);
-                       return -EIO;
-               }
-
-               /* Check the magic - see if this is a FIT image */
-               if (be32_to_cpu(hdr.magic) != OF_DT_HEADER) {
-                       pr_debug("no valid FIT image found in \"%s\" at offset %llx\n",
-                                mtd->name, (unsigned long long) offset);
-                       continue;
-               }
-
-               /* We found a FIT image. Let's keep going */
-               break;
-       }
-
-       fit_offset = offset;
-       fit_size = be32_to_cpu(hdr.totalsize);
-
-       if (fit_size == 0) {
-               pr_err("FIT image in \"%s\" at offset %llx has null size\n",
-                      mtd->name, (unsigned long long) fit_offset);
-               return -ENODEV;
-       }
-
-       /* Search for the rootfs partition after the FIT image */
-       ret = mtd_find_rootfs_from(mtd, fit_offset + fit_size, mtd->size,
-                                  &rootfs_offset, NULL);
-       if (ret) {
-               pr_info("no rootfs found after FIT image in \"%s\"\n",
-                       mtd->name);
-               return ret;
-       }
-
-       rootfs_size = mtd->size - rootfs_offset;
-
-       parts = kzalloc(2 * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = fit_offset;
-       parts[0].size = mtd_rounddown_to_eb(fit_size, mtd) + mtd->erasesize;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = rootfs_size;
-
-       *pparts = parts;
-       return 2;
-}
-
-static struct mtd_part_parser uimage_parser = {
-       .owner = THIS_MODULE,
-       .name = "fit-fw",
-       .parse_fn = mtdsplit_fit_parse,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-/**************************************************
- * Init
- **************************************************/
-
-static int __init mtdsplit_fit_init(void)
-{
-       register_mtd_parser(&uimage_parser);
-
-       return 0;
-}
-
-module_init(mtdsplit_fit_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_jimage.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_jimage.c
deleted file mode 100644 (file)
index 51544a7..0000000
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- *  Copyright (C) 2018 PaweÅ‚ Dembicki <paweldembicki@gmail.com> 
- *
- *  Based on: mtdsplit_uimage.c
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE )
-
-#define STAG_SIZE 16
-#define STAG_ID 0x04
-#define STAG_MAGIC 0x2B24
-
-#define SCH2_SIZE 40
-#define SCH2_MAGIC 0x2124
-#define SCH2_VER 0x02
-
-/*
- * Jboot image header,
- * all data in little endian.
- */
-
-struct jimage_header           //stag + sch2 jboot joined headers
-{
-       uint8_t stag_cmark;             // in factory 0xFF , in sysupgrade must be the same as stag_id
-       uint8_t stag_id;                // 0x04
-       uint16_t stag_magic;            //magic 0x2B24
-       uint32_t stag_time_stamp;       // timestamp calculated in jboot way
-       uint32_t stag_image_length;     // lentgh of kernel + sch2 header
-       uint16_t stag_image_checksum;   // negated jboot_checksum of sch2 + kernel
-       uint16_t stag_tag_checksum;     // negated jboot_checksum of stag header data
-       uint16_t sch2_magic;            // magic 0x2124
-       uint8_t sch2_cp_type;   // 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma
-       uint8_t sch2_version;   // 0x02 for sch2
-       uint32_t sch2_ram_addr; // ram entry address
-       uint32_t sch2_image_len;        // kernel image length
-       uint32_t sch2_image_crc32;      // kernel image crc
-       uint32_t sch2_start_addr;       // ram start address
-       uint32_t sch2_rootfs_addr;      // rootfs flash address
-       uint32_t sch2_rootfs_len;       // rootfls length
-       uint32_t sch2_rootfs_crc32;     // rootfs crc32
-       uint32_t sch2_header_crc32;     // sch2 header crc32, durring calculation this area is replaced by zero
-       uint16_t sch2_header_length;    // sch2 header length: 0x28
-       uint16_t sch2_cmd_line_length;  // cmd line length, known zeros
-};
-
-static int
-read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
-                  size_t header_len)
-{
-       size_t retlen;
-       int ret;
-
-       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
-       if (ret) {
-               pr_debug("read error in \"%s\"\n", mtd->name);
-               return ret;
-       }
-
-       if (retlen != header_len) {
-               pr_debug("short read in \"%s\"\n", mtd->name);
-               return -EIO;
-       }
-
-       return 0;
-}
-
-/**
- * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts
- *
- * @find_header: function to call for a block of data that will return offset
- *      of a valid jImage header if found
- */
-static int __mtdsplit_parse_jimage(struct mtd_info *master,
-                                  const struct mtd_partition **pparts,
-                                  struct mtd_part_parser_data *data,
-                                  ssize_t (*find_header)(u_char *buf, size_t len))
-{
-       struct mtd_partition *parts;
-       u_char *buf;
-       int nr_parts;
-       size_t offset;
-       size_t jimage_offset;
-       size_t jimage_size = 0;
-       size_t rootfs_offset;
-       size_t rootfs_size = 0;
-       int jimage_part, rf_part;
-       int ret;
-       enum mtdsplit_part_type type;
-
-       nr_parts = 2;
-       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       buf = vmalloc(MAX_HEADER_LEN);
-       if (!buf) {
-               ret = -ENOMEM;
-               goto err_free_parts;
-       }
-
-       /* find jImage on erase block boundaries */
-       for (offset = 0; offset < master->size; offset += master->erasesize) {
-               struct jimage_header *header;
-
-               jimage_size = 0;
-
-               ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN);
-               if (ret)
-                       continue;
-
-               ret = find_header(buf, MAX_HEADER_LEN);
-               if (ret < 0) {
-                       pr_debug("no valid jImage found in \"%s\" at offset %llx\n",
-                                master->name, (unsigned long long) offset);
-                       continue;
-               }
-               header = (struct jimage_header *)(buf + ret);
-
-               jimage_size = sizeof(*header) + header->sch2_image_len + ret;
-               if ((offset + jimage_size) > master->size) {
-                       pr_debug("jImage exceeds MTD device \"%s\"\n",
-                                master->name);
-                       continue;
-               }
-               break;
-       }
-
-       if (jimage_size == 0) {
-               pr_debug("no jImage found in \"%s\"\n", master->name);
-               ret = -ENODEV;
-               goto err_free_buf;
-       }
-
-       jimage_offset = offset;
-
-       if (jimage_offset == 0) {
-               jimage_part = 0;
-               rf_part = 1;
-
-               /* find the roots after the jImage */
-               ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size,
-                                          master->size, &rootfs_offset, &type);
-               if (ret) {
-                       pr_debug("no rootfs after jImage in \"%s\"\n",
-                                master->name);
-                       goto err_free_buf;
-               }
-
-               rootfs_size = master->size - rootfs_offset;
-               jimage_size = rootfs_offset - jimage_offset;
-       } else {
-               rf_part = 0;
-               jimage_part = 1;
-
-               /* check rootfs presence at offset 0 */
-               ret = mtd_check_rootfs_magic(master, 0, &type);
-               if (ret) {
-                       pr_debug("no rootfs before jImage in \"%s\"\n",
-                                master->name);
-                       goto err_free_buf;
-               }
-
-               rootfs_offset = 0;
-               rootfs_size = jimage_offset;
-       }
-
-       if (rootfs_size == 0) {
-               pr_debug("no rootfs found in \"%s\"\n", master->name);
-               ret = -ENODEV;
-               goto err_free_buf;
-       }
-
-       parts[jimage_part].name = KERNEL_PART_NAME;
-       parts[jimage_part].offset = jimage_offset;
-       parts[jimage_part].size = jimage_size;
-
-       if (type == MTDSPLIT_PART_TYPE_UBI)
-               parts[rf_part].name = UBI_PART_NAME;
-       else
-               parts[rf_part].name = ROOTFS_PART_NAME;
-       parts[rf_part].offset = rootfs_offset;
-       parts[rf_part].size = rootfs_size;
-
-       vfree(buf);
-
-       *pparts = parts;
-       return nr_parts;
-
-err_free_buf:
-       vfree(buf);
-
-err_free_parts:
-       kfree(parts);
-       return ret;
-}
-
-static ssize_t jimage_verify_default(u_char *buf, size_t len)
-{
-       struct jimage_header *header = (struct jimage_header *)buf;
-
-       /* default sanity checks */
-       if (header->stag_magic != STAG_MAGIC) {
-               pr_debug("invalid jImage stag header magic: %04x\n",
-                        header->stag_magic);
-               return -EINVAL;
-       }
-       if (header->sch2_magic != SCH2_MAGIC) {
-               pr_debug("invalid jImage sch2 header magic: %04x\n",
-                        header->stag_magic);
-               return -EINVAL;
-       }
-       if (header->stag_cmark != header->stag_id) {
-               pr_debug("invalid jImage stag header cmark: %02x\n",
-                        header->stag_magic);
-               return -EINVAL;
-       }
-       if (header->stag_id != STAG_ID) {
-               pr_debug("invalid jImage stag header id: %02x\n",
-                        header->stag_magic);
-               return -EINVAL;
-       }
-       if (header->sch2_version != SCH2_VER) {
-               pr_debug("invalid jImage sch2 header version: %02x\n",
-                        header->stag_magic);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int
-mtdsplit_jimage_parse_generic(struct mtd_info *master,
-                             const struct mtd_partition **pparts,
-                             struct mtd_part_parser_data *data)
-{
-       return __mtdsplit_parse_jimage(master, pparts, data,
-                                     jimage_verify_default);
-}
-
-static struct mtd_part_parser jimage_generic_parser = {
-       .owner = THIS_MODULE,
-       .name = "jimage-fw",
-       .parse_fn = mtdsplit_jimage_parse_generic,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-/**************************************************
- * Init
- **************************************************/
-
-static int __init mtdsplit_jimage_init(void)
-{
-       register_mtd_parser(&jimage_generic_parser);
-
-       return 0;
-}
-
-module_init(mtdsplit_jimage_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_lzma.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_lzma.c
deleted file mode 100644 (file)
index b7f044a..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- *  Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-
-#include <asm/unaligned.h>
-
-#include "mtdsplit.h"
-
-#define LZMA_NR_PARTS          2
-#define LZMA_PROPERTIES_SIZE   5
-
-struct lzma_header {
-       u8 props[LZMA_PROPERTIES_SIZE];
-       u8 size_low[4];
-       u8 size_high[4];
-};
-
-static int mtdsplit_parse_lzma(struct mtd_info *master,
-                              const struct mtd_partition **pparts,
-                              struct mtd_part_parser_data *data)
-{
-       struct lzma_header hdr;
-       size_t hdr_len, retlen;
-       size_t rootfs_offset;
-       u32 t;
-       struct mtd_partition *parts;
-       int err;
-
-       hdr_len = sizeof(hdr);
-       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
-       if (err)
-               return err;
-
-       if (retlen != hdr_len)
-               return -EIO;
-
-       /* verify LZMA properties */
-       if (hdr.props[0] >= (9 * 5 * 5))
-               return -EINVAL;
-
-       t = get_unaligned_le32(&hdr.props[1]);
-       if (!is_power_of_2(t))
-               return -EINVAL;
-
-       t = get_unaligned_le32(&hdr.size_high);
-       if (t)
-               return -EINVAL;
-
-       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
-                                  &rootfs_offset, NULL);
-       if (err)
-               return err;
-
-       parts = kzalloc(LZMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = 0;
-       parts[0].size = rootfs_offset;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = master->size - rootfs_offset;
-
-       *pparts = parts;
-       return LZMA_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_lzma_parser = {
-       .owner = THIS_MODULE,
-       .name = "lzma-fw",
-       .parse_fn = mtdsplit_parse_lzma,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_lzma_init(void)
-{
-       register_mtd_parser(&mtdsplit_lzma_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_lzma_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_minor.c
deleted file mode 100644 (file)
index f971f0a..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- *  MTD splitter for MikroTik NOR devices
- *
- *  Copyright (C) 2017 Thibaut VARENE <varenet@parisc-linux.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- *  The rootfs is expected at erase-block boundary due to the use of
- *  mtd_find_rootfs_from(). We use a trimmed down version of the yaffs header
- *  for two main reasons:
- *  - the original header uses weakly defined types (int, enum...) which can
- *    vary in length depending on build host (and the struct is not packed),
- *    and the name field can have a different total length depending on
- *    whether or not the yaffs code was _built_ with unicode support.
- *  - the only field that could be of real use here (file_size_low) contains
- *    invalid data in the header generated by kernel2minor, so we cannot use
- *    it to infer the exact position of the rootfs and do away with
- *    mtd_find_rootfs_from() (and thus have non-EB-aligned rootfs).
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/string.h>
-
-#include "mtdsplit.h"
-
-#define YAFFS_OBJECT_TYPE_FILE 0x1
-#define YAFFS_OBJECTID_ROOT    0x1
-#define YAFFS_SUM_UNUSED       0xFFFF
-#define YAFFS_NAME             "kernel"
-
-#define MINOR_NR_PARTS         2
-
-/*
- * This structure is based on yaffs_obj_hdr from yaffs_guts.h
- * The weak types match upstream. The fields have cpu-endianness
- */
-struct minor_header {
-       int yaffs_type;
-       int yaffs_obj_id;
-       u16 yaffs_sum_unused;
-       char yaffs_name[sizeof(YAFFS_NAME)];
-};
-
-static int mtdsplit_parse_minor(struct mtd_info *master,
-                               const struct mtd_partition **pparts,
-                               struct mtd_part_parser_data *data)
-{
-       struct minor_header hdr;
-       size_t hdr_len, retlen;
-       size_t rootfs_offset;
-       struct mtd_partition *parts;
-       int err;
-
-       hdr_len = sizeof(hdr);
-       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
-       if (err)
-               return err;
-
-       if (retlen != hdr_len)
-               return -EIO;
-
-       /* match header */
-       if (hdr.yaffs_type != YAFFS_OBJECT_TYPE_FILE)
-               return -EINVAL;
-
-       if (hdr.yaffs_obj_id != YAFFS_OBJECTID_ROOT)
-               return -EINVAL;
-
-       if (hdr.yaffs_sum_unused != YAFFS_SUM_UNUSED)
-               return -EINVAL;
-
-       if (memcmp(hdr.yaffs_name, YAFFS_NAME, sizeof(YAFFS_NAME)))
-               return -EINVAL;
-
-       err = mtd_find_rootfs_from(master, master->erasesize, master->size,
-                                  &rootfs_offset, NULL);
-       if (err)
-               return err;
-
-       parts = kzalloc(MINOR_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = 0;
-       parts[0].size = rootfs_offset;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = master->size - rootfs_offset;
-
-       *pparts = parts;
-       return MINOR_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_minor_parser = {
-       .owner = THIS_MODULE,
-       .name = "minor-fw",
-       .parse_fn = mtdsplit_parse_minor,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_minor_init(void)
-{
-       register_mtd_parser(&mtdsplit_minor_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_minor_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seama.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_seama.c
deleted file mode 100644 (file)
index f8556e0..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define SEAMA_MAGIC            0x5EA3A417
-#define SEAMA_NR_PARTS         2
-#define SEAMA_MIN_ROOTFS_OFFS  0x80000 /* 512KiB */
-
-struct seama_header {
-       __be32  magic;          /* should always be SEAMA_MAGIC. */
-       __be16  reserved;       /* reserved for  */
-       __be16  metasize;       /* size of the META data */
-       __be32  size;           /* size of the image */
-       u8      md5[16];        /* digest */
-};
-
-static int mtdsplit_parse_seama(struct mtd_info *master,
-                               const struct mtd_partition **pparts,
-                               struct mtd_part_parser_data *data)
-{
-       struct seama_header hdr;
-       size_t hdr_len, retlen, kernel_ent_size;
-       size_t rootfs_offset;
-       struct mtd_partition *parts;
-       enum mtdsplit_part_type type;
-       int err;
-
-       hdr_len = sizeof(hdr);
-       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
-       if (err)
-               return err;
-
-       if (retlen != hdr_len)
-               return -EIO;
-
-       /* sanity checks */
-       if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC)
-               return -EINVAL;
-
-       kernel_ent_size = hdr_len + be32_to_cpu(hdr.size) +
-                         be16_to_cpu(hdr.metasize);
-       if (kernel_ent_size > master->size)
-               return -EINVAL;
-
-       /* Check for the rootfs right after Seama entity with a kernel. */
-       err = mtd_check_rootfs_magic(master, kernel_ent_size, &type);
-       if (!err) {
-               rootfs_offset = kernel_ent_size;
-       } else {
-               /*
-                * On some devices firmware entity might contain both: kernel
-                * and rootfs. We can't determine kernel size so we just have to
-                * look for rootfs magic.
-                * Start the search from an arbitrary offset.
-                */
-               err = mtd_find_rootfs_from(master, SEAMA_MIN_ROOTFS_OFFS,
-                                          master->size, &rootfs_offset, &type);
-               if (err)
-                       return err;
-       }
-
-       parts = kzalloc(SEAMA_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = sizeof hdr + be16_to_cpu(hdr.metasize);
-       parts[0].size = rootfs_offset - parts[0].offset;
-
-       if (type == MTDSPLIT_PART_TYPE_UBI)
-               parts[1].name = UBI_PART_NAME;
-       else
-               parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = master->size - rootfs_offset;
-
-       *pparts = parts;
-       return SEAMA_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_seama_parser = {
-       .owner = THIS_MODULE,
-       .name = "seama-fw",
-       .parse_fn = mtdsplit_parse_seama,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_seama_init(void)
-{
-       register_mtd_parser(&mtdsplit_seama_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_seama_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_squashfs.c
deleted file mode 100644 (file)
index 79e1f73..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- *  Copyright (C) 2013 Felix Fietkau <nbd@nbd.name>
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/magic.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-static int
-mtdsplit_parse_squashfs(struct mtd_info *master,
-                       const struct mtd_partition **pparts,
-                       struct mtd_part_parser_data *data)
-{
-       struct mtd_partition *part;
-       struct mtd_info *parent_mtd;
-       size_t part_offset;
-       size_t squashfs_len;
-       int err;
-
-       err = mtd_get_squashfs_len(master, 0, &squashfs_len);
-       if (err)
-               return err;
-
-       parent_mtd = mtdpart_get_master(master);
-       part_offset = mtdpart_get_offset(master);
-
-       part = kzalloc(sizeof(*part), GFP_KERNEL);
-       if (!part) {
-               pr_alert("unable to allocate memory for \"%s\" partition\n",
-                        ROOTFS_SPLIT_NAME);
-               return -ENOMEM;
-       }
-
-       part->name = ROOTFS_SPLIT_NAME;
-       part->offset = mtd_roundup_to_eb(part_offset + squashfs_len,
-                                        parent_mtd) - part_offset;
-       part->size = mtd_rounddown_to_eb(master->size - part->offset, master);
-
-       *pparts = part;
-       return 1;
-}
-
-static struct mtd_part_parser mtdsplit_squashfs_parser = {
-       .owner = THIS_MODULE,
-       .name = "squashfs-split",
-       .parse_fn = mtdsplit_parse_squashfs,
-       .type = MTD_PARSER_TYPE_ROOTFS,
-};
-
-static int __init mtdsplit_squashfs_init(void)
-{
-       register_mtd_parser(&mtdsplit_squashfs_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_squashfs_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_tplink.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_tplink.c
deleted file mode 100644 (file)
index c346aa8..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define TPLINK_NR_PARTS                2
-#define TPLINK_MIN_ROOTFS_OFFS 0x80000 /* 512KiB */
-
-#define MD5SUM_LEN  16
-
-struct fw_v1 {
-       char            vendor_name[24];
-       char            fw_version[36];
-       uint32_t        hw_id;          /* hardware id */
-       uint32_t        hw_rev;         /* hardware revision */
-       uint32_t        unk1;
-       uint8_t         md5sum1[MD5SUM_LEN];
-       uint32_t        unk2;
-       uint8_t         md5sum2[MD5SUM_LEN];
-       uint32_t        unk3;
-       uint32_t        kernel_la;      /* kernel load address */
-       uint32_t        kernel_ep;      /* kernel entry point */
-       uint32_t        fw_length;      /* total length of the firmware */
-       uint32_t        kernel_ofs;     /* kernel data offset */
-       uint32_t        kernel_len;     /* kernel data length */
-       uint32_t        rootfs_ofs;     /* rootfs data offset */
-       uint32_t        rootfs_len;     /* rootfs data length */
-       uint32_t        boot_ofs;       /* bootloader data offset */
-       uint32_t        boot_len;       /* bootloader data length */
-       uint8_t         pad[360];
-} __attribute__ ((packed));
-
-struct fw_v2 {
-       char            fw_version[48]; /* 0x04: fw version string */
-       uint32_t        hw_id;          /* 0x34: hardware id */
-       uint32_t        hw_rev;         /* 0x38: FIXME: hardware revision? */
-       uint32_t        unk1;           /* 0x3c: 0x00000000 */
-       uint8_t         md5sum1[MD5SUM_LEN]; /* 0x40 */
-       uint32_t        unk2;           /* 0x50: 0x00000000 */
-       uint8_t         md5sum2[MD5SUM_LEN]; /* 0x54 */
-       uint32_t        unk3;           /* 0x64: 0xffffffff */
-
-       uint32_t        kernel_la;      /* 0x68: kernel load address */
-       uint32_t        kernel_ep;      /* 0x6c: kernel entry point */
-       uint32_t        fw_length;      /* 0x70: total length of the image */
-       uint32_t        kernel_ofs;     /* 0x74: kernel data offset */
-       uint32_t        kernel_len;     /* 0x78: kernel data length */
-       uint32_t        rootfs_ofs;     /* 0x7c: rootfs data offset */
-       uint32_t        rootfs_len;     /* 0x80: rootfs data length */
-       uint32_t        boot_ofs;       /* 0x84: FIXME: seems to be unused */
-       uint32_t        boot_len;       /* 0x88: FIXME: seems to be unused */
-       uint16_t        unk4;           /* 0x8c: 0x55aa */
-       uint8_t         sver_hi;        /* 0x8e */
-       uint8_t         sver_lo;        /* 0x8f */
-       uint8_t         unk5;           /* 0x90: magic: 0xa5 */
-       uint8_t         ver_hi;         /* 0x91 */
-       uint8_t         ver_mid;        /* 0x92 */
-       uint8_t         ver_lo;         /* 0x93 */
-       uint8_t         pad[364];
-} __attribute__ ((packed));
-
-struct tplink_fw_header {
-       uint32_t version;
-       union {
-               struct fw_v1 v1;
-               struct fw_v2 v2;
-       };
-};
-
-static int mtdsplit_parse_tplink(struct mtd_info *master,
-                                const struct mtd_partition **pparts,
-                                struct mtd_part_parser_data *data)
-{
-       struct tplink_fw_header hdr;
-       size_t hdr_len, retlen, kernel_size;
-       size_t rootfs_offset;
-       struct mtd_partition *parts;
-       int err;
-
-       hdr_len = sizeof(hdr);
-       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
-       if (err)
-               return err;
-
-       if (retlen != hdr_len)
-               return -EIO;
-
-       switch (le32_to_cpu(hdr.version)) {
-       case 1:
-               if (be32_to_cpu(hdr.v1.kernel_ofs) != sizeof(hdr))
-                       return -EINVAL;
-
-               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v1.kernel_len);
-               rootfs_offset = be32_to_cpu(hdr.v1.rootfs_ofs);
-               break;
-       case 2:
-       case 3:
-               if (be32_to_cpu(hdr.v2.kernel_ofs) != sizeof(hdr))
-                       return -EINVAL;
-
-               kernel_size = sizeof(hdr) + be32_to_cpu(hdr.v2.kernel_len);
-               rootfs_offset = be32_to_cpu(hdr.v2.rootfs_ofs);
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       if (kernel_size > master->size)
-               return -EINVAL;
-
-       /* Find the rootfs */
-       err = mtd_check_rootfs_magic(master, rootfs_offset, NULL);
-       if (err) {
-               /*
-                * The size in the header might cover the rootfs as well.
-                * Start the search from an arbitrary offset.
-                */
-               err = mtd_find_rootfs_from(master, TPLINK_MIN_ROOTFS_OFFS,
-                                          master->size, &rootfs_offset, NULL);
-               if (err)
-                       return err;
-       }
-
-       parts = kzalloc(TPLINK_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = 0;
-       parts[0].size = kernel_size;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = master->size - rootfs_offset;
-
-       *pparts = parts;
-       return TPLINK_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_tplink_parser = {
-       .owner = THIS_MODULE,
-       .name = "tplink-fw",
-       .parse_fn = mtdsplit_parse_tplink,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_tplink_init(void)
-{
-       register_mtd_parser(&mtdsplit_tplink_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_tplink_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_trx.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_trx.c
deleted file mode 100644 (file)
index 53aebc5..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define TRX_MAGIC   0x30524448  /* "HDR0" */
-
-struct trx_header {
-       __le32 magic;
-       __le32 len;
-       __le32 crc32;
-       __le32 flag_version;
-       __le32 offset[4];
-};
-
-static int
-read_trx_header(struct mtd_info *mtd, size_t offset,
-                  struct trx_header *header)
-{
-       size_t header_len;
-       size_t retlen;
-       int ret;
-
-       header_len = sizeof(*header);
-       ret = mtd_read(mtd, offset, header_len, &retlen,
-                      (unsigned char *) header);
-       if (ret) {
-               pr_debug("read error in \"%s\"\n", mtd->name);
-               return ret;
-       }
-
-       if (retlen != header_len) {
-               pr_debug("short read in \"%s\"\n", mtd->name);
-               return -EIO;
-       }
-
-       return 0;
-}
-
-static int
-mtdsplit_parse_trx(struct mtd_info *master,
-                  const struct mtd_partition **pparts,
-                  struct mtd_part_parser_data *data)
-{
-       struct mtd_partition *parts;
-       struct trx_header hdr;
-       int nr_parts;
-       size_t offset;
-       size_t trx_offset;
-       size_t trx_size = 0;
-       size_t rootfs_offset;
-       size_t rootfs_size = 0;
-       int ret;
-
-       nr_parts = 2;
-       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       /* find trx image on erase block boundaries */
-       for (offset = 0; offset < master->size; offset += master->erasesize) {
-               trx_size = 0;
-
-               ret = read_trx_header(master, offset, &hdr);
-               if (ret)
-                       continue;
-
-               if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
-                       pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
-                                master->name, (unsigned long long) offset);
-                       continue;
-               }
-
-               trx_size = le32_to_cpu(hdr.len);
-               if ((offset + trx_size) > master->size) {
-                       pr_debug("trx image exceeds MTD device \"%s\"\n",
-                                master->name);
-                       continue;
-               }
-               break;
-       }
-
-       if (trx_size == 0) {
-               pr_debug("no trx header found in \"%s\"\n", master->name);
-               ret = -ENODEV;
-               goto err;
-       }
-
-       trx_offset = offset + hdr.offset[0];
-       rootfs_offset = offset + hdr.offset[1];
-       rootfs_size = master->size - rootfs_offset;
-       trx_size = rootfs_offset - trx_offset;
-
-       if (rootfs_size == 0) {
-               pr_debug("no rootfs found in \"%s\"\n", master->name);
-               ret = -ENODEV;
-               goto err;
-       }
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = trx_offset;
-       parts[0].size = trx_size;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = rootfs_size;
-
-       *pparts = parts;
-       return nr_parts;
-
-err:
-       kfree(parts);
-       return ret;
-}
-
-static struct mtd_part_parser trx_parser = {
-       .owner = THIS_MODULE,
-       .name = "trx-fw",
-       .parse_fn = mtdsplit_parse_trx,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_trx_init(void)
-{
-       register_mtd_parser(&trx_parser);
-
-       return 0;
-}
-
-module_init(mtdsplit_trx_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_uimage.c
deleted file mode 100644 (file)
index bd1c723..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#define pr_fmt(fmt)    KBUILD_MODNAME ": " fmt
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/vmalloc.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-/*
- * uimage_header itself is only 64B, but it may be prepended with another data.
- * Currently the biggest size is for Edimax devices: 20B + 64B
- */
-#define MAX_HEADER_LEN         84
-
-#define IH_MAGIC       0x27051956      /* Image Magic Number           */
-#define IH_NMLEN               32      /* Image Name Length            */
-
-#define IH_OS_LINUX            5       /* Linux        */
-
-#define IH_TYPE_KERNEL         2       /* OS Kernel Image              */
-#define IH_TYPE_FILESYSTEM     7       /* Filesystem Image             */
-
-/*
- * Legacy format image header,
- * all data in network byte order (aka natural aka bigendian).
- */
-struct uimage_header {
-       uint32_t        ih_magic;       /* Image Header Magic Number    */
-       uint32_t        ih_hcrc;        /* Image Header CRC Checksum    */
-       uint32_t        ih_time;        /* Image Creation Timestamp     */
-       uint32_t        ih_size;        /* Image Data Size              */
-       uint32_t        ih_load;        /* Data  Load  Address          */
-       uint32_t        ih_ep;          /* Entry Point Address          */
-       uint32_t        ih_dcrc;        /* Image Data CRC Checksum      */
-       uint8_t         ih_os;          /* Operating System             */
-       uint8_t         ih_arch;        /* CPU architecture             */
-       uint8_t         ih_type;        /* Image Type                   */
-       uint8_t         ih_comp;        /* Compression Type             */
-       uint8_t         ih_name[IH_NMLEN];      /* Image Name           */
-};
-
-static int
-read_uimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
-                  size_t header_len)
-{
-       size_t retlen;
-       int ret;
-
-       ret = mtd_read(mtd, offset, header_len, &retlen, buf);
-       if (ret) {
-               pr_debug("read error in \"%s\"\n", mtd->name);
-               return ret;
-       }
-
-       if (retlen != header_len) {
-               pr_debug("short read in \"%s\"\n", mtd->name);
-               return -EIO;
-       }
-
-       return 0;
-}
-
-/**
- * __mtdsplit_parse_uimage - scan partition and create kernel + rootfs parts
- *
- * @find_header: function to call for a block of data that will return offset
- *      of a valid uImage header if found
- */
-static int __mtdsplit_parse_uimage(struct mtd_info *master,
-                                  const struct mtd_partition **pparts,
-                                  struct mtd_part_parser_data *data,
-                                  ssize_t (*find_header)(u_char *buf, size_t len))
-{
-       struct mtd_partition *parts;
-       u_char *buf;
-       int nr_parts;
-       size_t offset;
-       size_t uimage_offset;
-       size_t uimage_size = 0;
-       size_t rootfs_offset;
-       size_t rootfs_size = 0;
-       int uimage_part, rf_part;
-       int ret;
-       enum mtdsplit_part_type type;
-
-       nr_parts = 2;
-       parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       buf = vmalloc(MAX_HEADER_LEN);
-       if (!buf) {
-               ret = -ENOMEM;
-               goto err_free_parts;
-       }
-
-       /* find uImage on erase block boundaries */
-       for (offset = 0; offset < master->size; offset += master->erasesize) {
-               struct uimage_header *header;
-
-               uimage_size = 0;
-
-               ret = read_uimage_header(master, offset, buf, MAX_HEADER_LEN);
-               if (ret)
-                       continue;
-
-               ret = find_header(buf, MAX_HEADER_LEN);
-               if (ret < 0) {
-                       pr_debug("no valid uImage found in \"%s\" at offset %llx\n",
-                                master->name, (unsigned long long) offset);
-                       continue;
-               }
-               header = (struct uimage_header *)(buf + ret);
-
-               uimage_size = sizeof(*header) + be32_to_cpu(header->ih_size) + ret;
-               if ((offset + uimage_size) > master->size) {
-                       pr_debug("uImage exceeds MTD device \"%s\"\n",
-                                master->name);
-                       continue;
-               }
-               break;
-       }
-
-       if (uimage_size == 0) {
-               pr_debug("no uImage found in \"%s\"\n", master->name);
-               ret = -ENODEV;
-               goto err_free_buf;
-       }
-
-       uimage_offset = offset;
-
-       if (uimage_offset == 0) {
-               uimage_part = 0;
-               rf_part = 1;
-
-               /* find the roots after the uImage */
-               ret = mtd_find_rootfs_from(master, uimage_offset + uimage_size,
-                                          master->size, &rootfs_offset, &type);
-               if (ret) {
-                       pr_debug("no rootfs after uImage in \"%s\"\n",
-                                master->name);
-                       goto err_free_buf;
-               }
-
-               rootfs_size = master->size - rootfs_offset;
-               uimage_size = rootfs_offset - uimage_offset;
-       } else {
-               rf_part = 0;
-               uimage_part = 1;
-
-               /* check rootfs presence at offset 0 */
-               ret = mtd_check_rootfs_magic(master, 0, &type);
-               if (ret) {
-                       pr_debug("no rootfs before uImage in \"%s\"\n",
-                                master->name);
-                       goto err_free_buf;
-               }
-
-               rootfs_offset = 0;
-               rootfs_size = uimage_offset;
-       }
-
-       if (rootfs_size == 0) {
-               pr_debug("no rootfs found in \"%s\"\n", master->name);
-               ret = -ENODEV;
-               goto err_free_buf;
-       }
-
-       parts[uimage_part].name = KERNEL_PART_NAME;
-       parts[uimage_part].offset = uimage_offset;
-       parts[uimage_part].size = uimage_size;
-
-       if (type == MTDSPLIT_PART_TYPE_UBI)
-               parts[rf_part].name = UBI_PART_NAME;
-       else
-               parts[rf_part].name = ROOTFS_PART_NAME;
-       parts[rf_part].offset = rootfs_offset;
-       parts[rf_part].size = rootfs_size;
-
-       vfree(buf);
-
-       *pparts = parts;
-       return nr_parts;
-
-err_free_buf:
-       vfree(buf);
-
-err_free_parts:
-       kfree(parts);
-       return ret;
-}
-
-static ssize_t uimage_verify_default(u_char *buf, size_t len)
-{
-       struct uimage_header *header = (struct uimage_header *)buf;
-
-       /* default sanity checks */
-       if (be32_to_cpu(header->ih_magic) != IH_MAGIC) {
-               pr_debug("invalid uImage magic: %08x\n",
-                        be32_to_cpu(header->ih_magic));
-               return -EINVAL;
-       }
-
-       if (header->ih_os != IH_OS_LINUX) {
-               pr_debug("invalid uImage OS: %08x\n",
-                        be32_to_cpu(header->ih_os));
-               return -EINVAL;
-       }
-
-       if (header->ih_type != IH_TYPE_KERNEL) {
-               pr_debug("invalid uImage type: %08x\n",
-                        be32_to_cpu(header->ih_type));
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int
-mtdsplit_uimage_parse_generic(struct mtd_info *master,
-                             const struct mtd_partition **pparts,
-                             struct mtd_part_parser_data *data)
-{
-       return __mtdsplit_parse_uimage(master, pparts, data,
-                                     uimage_verify_default);
-}
-
-static struct mtd_part_parser uimage_generic_parser = {
-       .owner = THIS_MODULE,
-       .name = "uimage-fw",
-       .parse_fn = mtdsplit_uimage_parse_generic,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-#define FW_MAGIC_WNR2000V1     0x32303031
-#define FW_MAGIC_WNR2000V3     0x32303033
-#define FW_MAGIC_WNR2000V4     0x32303034
-#define FW_MAGIC_WNR2200       0x32323030
-#define FW_MAGIC_WNR612V2      0x32303631
-#define FW_MAGIC_WNR1000V2     0x31303031
-#define FW_MAGIC_WNR1000V2_VC  0x31303030
-#define FW_MAGIC_WNDR3700      0x33373030
-#define FW_MAGIC_WNDR3700V2    0x33373031
-#define FW_MAGIC_WPN824N       0x31313030
-
-static ssize_t uimage_verify_wndr3700(u_char *buf, size_t len)
-{
-       struct uimage_header *header = (struct uimage_header *)buf;
-       uint8_t expected_type = IH_TYPE_FILESYSTEM;
-
-       switch (be32_to_cpu(header->ih_magic)) {
-       case FW_MAGIC_WNR612V2:
-       case FW_MAGIC_WNR1000V2:
-       case FW_MAGIC_WNR1000V2_VC:
-       case FW_MAGIC_WNR2000V1:
-       case FW_MAGIC_WNR2000V3:
-       case FW_MAGIC_WNR2200:
-       case FW_MAGIC_WNDR3700:
-       case FW_MAGIC_WNDR3700V2:
-       case FW_MAGIC_WPN824N:
-               break;
-       case FW_MAGIC_WNR2000V4:
-               expected_type = IH_TYPE_KERNEL;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       if (header->ih_os != IH_OS_LINUX ||
-           header->ih_type != expected_type)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int
-mtdsplit_uimage_parse_netgear(struct mtd_info *master,
-                             const struct mtd_partition **pparts,
-                             struct mtd_part_parser_data *data)
-{
-       return __mtdsplit_parse_uimage(master, pparts, data,
-                                     uimage_verify_wndr3700);
-}
-
-static struct mtd_part_parser uimage_netgear_parser = {
-       .owner = THIS_MODULE,
-       .name = "netgear-fw",
-       .parse_fn = mtdsplit_uimage_parse_netgear,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-/**************************************************
- * Edimax
- **************************************************/
-
-#define FW_EDIMAX_OFFSET       20
-#define FW_MAGIC_EDIMAX                0x43535953
-
-static ssize_t uimage_find_edimax(u_char *buf, size_t len)
-{
-       u32 *magic;
-
-       if (len < FW_EDIMAX_OFFSET + sizeof(struct uimage_header)) {
-               pr_err("Buffer too small for checking Edimax header\n");
-               return -ENOSPC;
-       }
-
-       magic = (u32 *)buf;
-       if (be32_to_cpu(*magic) != FW_MAGIC_EDIMAX)
-               return -EINVAL;
-
-       if (!uimage_verify_default(buf + FW_EDIMAX_OFFSET, len))
-               return FW_EDIMAX_OFFSET;
-
-       return -EINVAL;
-}
-
-static int
-mtdsplit_uimage_parse_edimax(struct mtd_info *master,
-                             const struct mtd_partition **pparts,
-                             struct mtd_part_parser_data *data)
-{
-       return __mtdsplit_parse_uimage(master, pparts, data,
-                                      uimage_find_edimax);
-}
-
-static struct mtd_part_parser uimage_edimax_parser = {
-       .owner = THIS_MODULE,
-       .name = "edimax-fw",
-       .parse_fn = mtdsplit_uimage_parse_edimax,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-/**************************************************
- * Init
- **************************************************/
-
-static int __init mtdsplit_uimage_init(void)
-{
-       register_mtd_parser(&uimage_generic_parser);
-       register_mtd_parser(&uimage_netgear_parser);
-       register_mtd_parser(&uimage_edimax_parser);
-
-       return 0;
-}
-
-module_init(mtdsplit_uimage_init);
diff --git a/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_wrgg.c b/target/linux/generic/files/drivers/mtd/mtdsplit/mtdsplit_wrgg.c
deleted file mode 100644 (file)
index 16ebd51..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- *  Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
- *  Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
- *  Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-
-#include "mtdsplit.h"
-
-#define WRGG_NR_PARTS          2
-#define WRGG_MIN_ROOTFS_OFFS   0x80000 /* 512KiB */
-#define WRGG03_MAGIC           0x20080321
-#define WRG_MAGIC              0x20040220
-
-struct wrgg03_header {
-       char            signature[32];
-       uint32_t        magic1;
-       uint32_t        magic2;
-       char            version[16];
-       char            model[16];
-       uint32_t        flag[2];
-       uint32_t        reserve[2];
-       char            buildno[16];
-       uint32_t        size;
-       uint32_t        offset;
-       char            devname[32];
-       char            digest[16];
-} __attribute__ ((packed));
-
-struct wrg_header {
-       char            signature[32];
-       uint32_t        magic1;
-       uint32_t        magic2;
-       uint32_t        size;
-       uint32_t        offset;
-       char            devname[32];
-       char            digest[16];
-} __attribute__ ((packed));
-
-
-static int mtdsplit_parse_wrgg(struct mtd_info *master,
-                              const struct mtd_partition **pparts,
-                              struct mtd_part_parser_data *data)
-{
-       struct wrgg03_header hdr;
-       size_t hdr_len, retlen, kernel_ent_size;
-       size_t rootfs_offset;
-       struct mtd_partition *parts;
-       enum mtdsplit_part_type type;
-       int err;
-
-       hdr_len = sizeof(hdr);
-       err = mtd_read(master, 0, hdr_len, &retlen, (void *) &hdr);
-       if (err)
-               return err;
-
-       if (retlen != hdr_len)
-               return -EIO;
-
-       /* sanity checks */
-       if (le32_to_cpu(hdr.magic1) == WRGG03_MAGIC) {
-               kernel_ent_size = hdr_len + be32_to_cpu(hdr.size);
-       } else if (le32_to_cpu(hdr.magic1) == WRG_MAGIC) {
-               kernel_ent_size = sizeof(struct wrg_header) + le32_to_cpu(
-                                 ((struct wrg_header*)&hdr)->size);
-       } else {
-               return -EINVAL;
-       }
-
-       if (kernel_ent_size > master->size)
-               return -EINVAL;
-
-       /*
-        * The size in the header covers the rootfs as well.
-        * Start the search from an arbitrary offset.
-        */
-       err = mtd_find_rootfs_from(master, WRGG_MIN_ROOTFS_OFFS,
-                                  master->size, &rootfs_offset, &type);
-       if (err)
-               return err;
-
-       parts = kzalloc(WRGG_NR_PARTS * sizeof(*parts), GFP_KERNEL);
-       if (!parts)
-               return -ENOMEM;
-
-       parts[0].name = KERNEL_PART_NAME;
-       parts[0].offset = 0;
-       parts[0].size = rootfs_offset;
-
-       parts[1].name = ROOTFS_PART_NAME;
-       parts[1].offset = rootfs_offset;
-       parts[1].size = master->size - rootfs_offset;
-
-       *pparts = parts;
-       return WRGG_NR_PARTS;
-}
-
-static struct mtd_part_parser mtdsplit_wrgg_parser = {
-       .owner = THIS_MODULE,
-       .name = "wrgg-fw",
-       .parse_fn = mtdsplit_parse_wrgg,
-       .type = MTD_PARSER_TYPE_FIRMWARE,
-};
-
-static int __init mtdsplit_wrgg_init(void)
-{
-       register_mtd_parser(&mtdsplit_wrgg_parser);
-
-       return 0;
-}
-
-subsys_initcall(mtdsplit_wrgg_init);
diff --git a/target/linux/generic/files/drivers/mtd/myloader.c b/target/linux/generic/files/drivers/mtd/myloader.c
deleted file mode 100644 (file)
index 7532d45..0000000
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *  Parse MyLoader-style flash partition tables and produce a Linux partition
- *  array to match.
- *
- *  Copyright (C) 2007-2009 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This file was based on drivers/mtd/redboot.c
- *  Author: Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/version.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-#include <linux/vmalloc.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/partitions.h>
-#include <linux/byteorder/generic.h>
-#include <linux/myloader.h>
-
-#define BLOCK_LEN_MIN          0x10000
-#define PART_NAME_LEN          32
-
-struct part_data {
-       struct mylo_partition_table     tab;
-       char names[MYLO_MAX_PARTITIONS][PART_NAME_LEN];
-};
-
-static int myloader_parse_partitions(struct mtd_info *master,
-                                    const struct mtd_partition **pparts,
-                                    struct mtd_part_parser_data *data)
-{
-       struct part_data *buf;
-       struct mylo_partition_table *tab;
-       struct mylo_partition *part;
-       struct mtd_partition *mtd_parts;
-       struct mtd_partition *mtd_part;
-       int num_parts;
-       int ret, i;
-       size_t retlen;
-       char *names;
-       unsigned long offset;
-       unsigned long blocklen;
-
-       buf = vmalloc(sizeof(*buf));
-       if (!buf) {
-               return -ENOMEM;
-               goto out;
-       }
-       tab = &buf->tab;
-
-       blocklen = master->erasesize;
-       if (blocklen < BLOCK_LEN_MIN)
-               blocklen = BLOCK_LEN_MIN;
-
-       offset = blocklen;
-
-       /* Find the partition table */
-       for (i = 0; i < 4; i++, offset += blocklen) {
-               printk(KERN_DEBUG "%s: searching for MyLoader partition table"
-                               " at offset 0x%lx\n", master->name, offset);
-
-               ret = mtd_read(master, offset, sizeof(*buf), &retlen,
-                              (void *)buf);
-               if (ret)
-                       goto out_free_buf;
-
-               if (retlen != sizeof(*buf)) {
-                       ret = -EIO;
-                       goto out_free_buf;
-               }
-
-               /* Check for Partition Table magic number */
-               if (tab->magic == le32_to_cpu(MYLO_MAGIC_PARTITIONS))
-                       break;
-
-       }
-
-       if (tab->magic != le32_to_cpu(MYLO_MAGIC_PARTITIONS)) {
-               printk(KERN_DEBUG "%s: no MyLoader partition table found\n",
-                       master->name);
-               ret = 0;
-               goto out_free_buf;
-       }
-
-       /* The MyLoader and the Partition Table is always present */
-       num_parts = 2;
-
-       /* Detect number of used partitions */
-       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
-               part = &tab->partitions[i];
-
-               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
-                       continue;
-
-               num_parts++;
-       }
-
-       mtd_parts = kzalloc((num_parts * sizeof(*mtd_part) +
-                               num_parts * PART_NAME_LEN), GFP_KERNEL);
-
-       if (!mtd_parts) {
-               ret = -ENOMEM;
-               goto out_free_buf;
-       }
-
-       mtd_part = mtd_parts;
-       names = (char *)&mtd_parts[num_parts];
-
-       strncpy(names, "myloader", PART_NAME_LEN);
-       mtd_part->name = names;
-       mtd_part->offset = 0;
-       mtd_part->size = offset;
-       mtd_part->mask_flags = MTD_WRITEABLE;
-       mtd_part++;
-       names += PART_NAME_LEN;
-
-       strncpy(names, "partition_table", PART_NAME_LEN);
-       mtd_part->name = names;
-       mtd_part->offset = offset;
-       mtd_part->size = blocklen;
-       mtd_part->mask_flags = MTD_WRITEABLE;
-       mtd_part++;
-       names += PART_NAME_LEN;
-
-       for (i = 0; i < MYLO_MAX_PARTITIONS; i++) {
-               part = &tab->partitions[i];
-
-               if (le16_to_cpu(part->type) == PARTITION_TYPE_FREE)
-                       continue;
-
-               if ((buf->names[i][0]) && (buf->names[i][0] != '\xff'))
-                       strncpy(names, buf->names[i], PART_NAME_LEN);
-               else
-                       snprintf(names, PART_NAME_LEN, "partition%d", i);
-
-               mtd_part->offset = le32_to_cpu(part->addr);
-               mtd_part->size = le32_to_cpu(part->size);
-               mtd_part->name = names;
-               mtd_part++;
-               names += PART_NAME_LEN;
-       }
-
-       *pparts = mtd_parts;
-       ret = num_parts;
-
- out_free_buf:
-       vfree(buf);
- out:
-       return ret;
-}
-
-static struct mtd_part_parser myloader_mtd_parser = {
-       .owner          = THIS_MODULE,
-       .parse_fn       = myloader_parse_partitions,
-       .name           = "MyLoader",
-};
-
-static int __init myloader_mtd_parser_init(void)
-{
-       register_mtd_parser(&myloader_mtd_parser);
-
-       return 0;
-}
-
-static void __exit myloader_mtd_parser_exit(void)
-{
-       deregister_mtd_parser(&myloader_mtd_parser);
-}
-
-module_init(myloader_mtd_parser_init);
-module_exit(myloader_mtd_parser_exit);
-
-MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
-MODULE_DESCRIPTION("Parsing code for MyLoader partition tables");
-MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files/drivers/net/phy/adm6996.c b/target/linux/generic/files/drivers/net/phy/adm6996.c
deleted file mode 100644 (file)
index 42928ba..0000000
+++ /dev/null
@@ -1,1241 +0,0 @@
-/*
- * ADM6996 switch driver
- *
- * swconfig interface based on ar8216.c
- *
- * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
- * VLAN support Copyright (c) 2010, 2011 Peter Lebbing <peter@digitalbrains.com>
- * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
- * Copyright (c) 2014 Matti Laakso <malaakso@elisanet.fi>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-/*#define DEBUG 1*/
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/mii.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/adm6996-gpio.h>
-#include <linux/ethtool.h>
-#include <linux/phy.h>
-#include <linux/switch.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include "adm6996.h"
-
-MODULE_DESCRIPTION("Infineon ADM6996 Switch");
-MODULE_AUTHOR("Felix Fietkau, Peter Lebbing <peter@digitalbrains.com>");
-MODULE_LICENSE("GPL");
-
-static const char * const adm6996_model_name[] =
-{
-       NULL,
-       "ADM6996FC",
-       "ADM6996M",
-       "ADM6996L"
-};
-
-struct adm6996_mib_desc {
-       unsigned int offset;
-       const char *name;
-};
-
-struct adm6996_priv {
-       struct switch_dev dev;
-       void *priv;
-
-       u8 eecs;
-       u8 eesk;
-       u8 eedi;
-
-       enum adm6996_model model;
-
-       bool enable_vlan;
-       bool vlan_enabled;      /* Current hardware state */
-
-#ifdef DEBUG
-       u16 addr;               /* Debugging: register address to operate on */
-#endif
-
-       u16 pvid[ADM_NUM_PORTS];        /* Primary VLAN ID */
-       u8 tagged_ports;
-
-       u16 vlan_id[ADM_NUM_VLANS];
-       u8 vlan_table[ADM_NUM_VLANS];   /* bitmap, 1 = port is member */
-       u8 vlan_tagged[ADM_NUM_VLANS];  /* bitmap, 1 = tagged member */
-       
-       struct mutex mib_lock;
-       char buf[2048];
-
-       struct mutex reg_mutex;
-
-       /* use abstraction for regops, we want to add gpio support in the future */
-       u16 (*read)(struct adm6996_priv *priv, enum admreg reg);
-       void (*write)(struct adm6996_priv *priv, enum admreg reg, u16 val);
-};
-
-#define to_adm(_dev) container_of(_dev, struct adm6996_priv, dev)
-#define phy_to_adm(_phy) ((struct adm6996_priv *) (_phy)->priv)
-
-#define MIB_DESC(_o, _n)       \
-       {                       \
-               .offset = (_o), \
-               .name = (_n),   \
-       }
-
-static const struct adm6996_mib_desc adm6996_mibs[] = {
-       MIB_DESC(ADM_CL0, "RxPacket"),
-       MIB_DESC(ADM_CL6, "RxByte"),
-       MIB_DESC(ADM_CL12, "TxPacket"),
-       MIB_DESC(ADM_CL18, "TxByte"),
-       MIB_DESC(ADM_CL24, "Collision"),
-       MIB_DESC(ADM_CL30, "Error"),
-};
-
-#define ADM6996_MIB_RXB_ID     1
-#define ADM6996_MIB_TXB_ID     3
-
-static inline u16
-r16(struct adm6996_priv *priv, enum admreg reg)
-{
-       return priv->read(priv, reg);
-}
-
-static inline void
-w16(struct adm6996_priv *priv, enum admreg reg, u16 val)
-{
-       priv->write(priv, reg, val);
-}
-
-/* Minimum timing constants */
-#define EECK_EDGE_TIME  3   /* 3us - max(adm 2.5us, 93c 1us) */
-#define EEDI_SETUP_TIME 1   /* 1us - max(adm 10ns, 93c 400ns) */
-#define EECS_SETUP_TIME 1   /* 1us - max(adm no, 93c 200ns) */
-
-static void adm6996_gpio_write(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
-{
-       int i, len = (bits + 7) / 8;
-       u8 mask;
-
-       gpio_set_value(priv->eecs, cs);
-       udelay(EECK_EDGE_TIME);
-
-       /* Byte assemble from MSB to LSB */
-       for (i = 0; i < len; i++) {
-               /* Bit bang from MSB to LSB */
-               for (mask = 0x80; mask && bits > 0; mask >>= 1, bits --) {
-                       /* Clock low */
-                       gpio_set_value(priv->eesk, 0);
-                       udelay(EECK_EDGE_TIME);
-
-                       /* Output on rising edge */
-                       gpio_set_value(priv->eedi, (mask & buf[i]));
-                       udelay(EEDI_SETUP_TIME);
-
-                       /* Clock high */
-                       gpio_set_value(priv->eesk, 1);
-                       udelay(EECK_EDGE_TIME);
-               }
-       }
-
-       /* Clock low */
-       gpio_set_value(priv->eesk, 0);
-       udelay(EECK_EDGE_TIME);
-
-       if (cs)
-               gpio_set_value(priv->eecs, 0);
-}
-
-static void adm6996_gpio_read(struct adm6996_priv *priv, int cs, char *buf, unsigned int bits)
-{
-       int i, len = (bits + 7) / 8;
-       u8 mask;
-
-       gpio_set_value(priv->eecs, cs);
-       udelay(EECK_EDGE_TIME);
-
-       /* Byte assemble from MSB to LSB */
-       for (i = 0; i < len; i++) {
-               u8 byte;
-
-               /* Bit bang from MSB to LSB */
-               for (mask = 0x80, byte = 0; mask && bits > 0; mask >>= 1, bits --) {
-                       u8 gp;
-
-                       /* Clock low */
-                       gpio_set_value(priv->eesk, 0);
-                       udelay(EECK_EDGE_TIME);
-
-                       /* Input on rising edge */
-                       gp = gpio_get_value(priv->eedi);
-                       if (gp)
-                               byte |= mask;
-
-                       /* Clock high */
-                       gpio_set_value(priv->eesk, 1);
-                       udelay(EECK_EDGE_TIME);
-               }
-
-               *buf++ = byte;
-       }
-
-       /* Clock low */
-       gpio_set_value(priv->eesk, 0);
-       udelay(EECK_EDGE_TIME);
-
-       if (cs)
-               gpio_set_value(priv->eecs, 0);
-}
-
-/* Advance clock(s) */
-static void adm6996_gpio_adclk(struct adm6996_priv *priv, int clocks)
-{
-       int i;
-       for (i = 0; i < clocks; i++) {
-               /* Clock high */
-               gpio_set_value(priv->eesk, 1);
-               udelay(EECK_EDGE_TIME);
-
-               /* Clock low */
-               gpio_set_value(priv->eesk, 0);
-               udelay(EECK_EDGE_TIME);
-       }
-}
-
-static u16
-adm6996_read_gpio_reg(struct adm6996_priv *priv, enum admreg reg)
-{
-       /* cmd: 01 10 T DD R RRRRRR */
-       u8 bits[6] = {
-               0xFF, 0xFF, 0xFF, 0xFF,
-               (0x06 << 4) | ((0 & 0x01) << 3 | (reg&64)>>6),
-               ((reg&63)<<2)
-       };
-
-       u8 rbits[4];
-
-       /* Enable GPIO outputs with all pins to 0 */
-       gpio_direction_output(priv->eecs, 0);
-       gpio_direction_output(priv->eesk, 0);
-       gpio_direction_output(priv->eedi, 0);
-
-       adm6996_gpio_write(priv, 0, bits, 46);
-       gpio_direction_input(priv->eedi);
-       adm6996_gpio_adclk(priv, 2);
-       adm6996_gpio_read(priv, 0, rbits, 32);
-
-       /* Extra clock(s) required per datasheet */
-       adm6996_gpio_adclk(priv, 2);
-
-       /* Disable GPIO outputs */
-       gpio_direction_input(priv->eecs);
-       gpio_direction_input(priv->eesk);
-
-        /* EEPROM has 16-bit registers, but pumps out two registers in one request */
-       return (reg & 0x01 ?  (rbits[0]<<8) | rbits[1] : (rbits[2]<<8) | (rbits[3]));
-}
-
-/* Write chip configuration register */
-/* Follow 93c66 timing and chip's min EEPROM timing requirement */
-static void
-adm6996_write_gpio_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
-{
-       /* cmd(27bits): sb(1) + opc(01) + addr(bbbbbbbb) + data(bbbbbbbbbbbbbbbb) */
-       u8 bits[4] = {
-               (0x05 << 5) | (reg >> 3),
-               (reg << 5) | (u8)(val >> 11),
-               (u8)(val >> 3),
-               (u8)(val << 5)
-       };
-
-       /* Enable GPIO outputs with all pins to 0 */
-       gpio_direction_output(priv->eecs, 0);
-       gpio_direction_output(priv->eesk, 0);
-       gpio_direction_output(priv->eedi, 0);
-
-       /* Write cmd. Total 27 bits */
-       adm6996_gpio_write(priv, 1, bits, 27);
-
-       /* Extra clock(s) required per datasheet */
-       adm6996_gpio_adclk(priv, 2);
-
-       /* Disable GPIO outputs */
-       gpio_direction_input(priv->eecs);
-       gpio_direction_input(priv->eesk);
-       gpio_direction_input(priv->eedi);
-}
-
-static u16
-adm6996_read_mii_reg(struct adm6996_priv *priv, enum admreg reg)
-{
-       struct phy_device *phydev = priv->priv;
-       struct mii_bus *bus = phydev->mdio.bus;
-
-       return bus->read(bus, PHYADDR(reg));
-}
-
-static void
-adm6996_write_mii_reg(struct adm6996_priv *priv, enum admreg reg, u16 val)
-{
-       struct phy_device *phydev = priv->priv;
-       struct mii_bus *bus = phydev->mdio.bus;
-
-       bus->write(bus, PHYADDR(reg), val);
-}
-
-static int
-adm6996_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       if (val->value.i > 1)
-               return -EINVAL;
-
-       priv->enable_vlan = val->value.i;
-
-       return 0;
-};
-
-static int
-adm6996_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       val->value.i = priv->enable_vlan;
-
-       return 0;
-};
-
-#ifdef DEBUG
-
-static int
-adm6996_set_addr(struct switch_dev *dev, const struct switch_attr *attr,
-                struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       if (val->value.i > 1023)
-               return -EINVAL;
-
-       priv->addr = val->value.i;
-
-       return 0;
-};
-
-static int
-adm6996_get_addr(struct switch_dev *dev, const struct switch_attr *attr,
-                struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       val->value.i = priv->addr;
-
-       return 0;
-};
-
-static int
-adm6996_set_data(struct switch_dev *dev, const struct switch_attr *attr,
-                struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       if (val->value.i > 65535)
-               return -EINVAL;
-
-       w16(priv, priv->addr, val->value.i);
-
-       return 0;
-};
-
-static int
-adm6996_get_data(struct switch_dev *dev, const struct switch_attr *attr,
-                struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       val->value.i = r16(priv, priv->addr);
-
-       return 0;
-};
-
-#endif /* def DEBUG */
-
-static int
-adm6996_set_pvid(struct switch_dev *dev, int port, int vlan)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       pr_devel("set_pvid port %d vlan %d\n", port, vlan);
-
-       if (vlan > ADM_VLAN_MAX_ID)
-               return -EINVAL;
-
-       priv->pvid[port] = vlan;
-
-       return 0;
-}
-
-static int
-adm6996_get_pvid(struct switch_dev *dev, int port, int *vlan)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       pr_devel("get_pvid port %d\n", port);
-       *vlan = priv->pvid[port];
-
-       return 0;
-}
-
-static int
-adm6996_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
-               struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       pr_devel("set_vid port %d vid %d\n", val->port_vlan, val->value.i);
-
-       if (val->value.i > ADM_VLAN_MAX_ID)
-               return -EINVAL;
-
-       priv->vlan_id[val->port_vlan] = val->value.i;
-
-       return 0;
-};
-
-static int
-adm6996_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
-               struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       pr_devel("get_vid port %d\n", val->port_vlan);
-
-       val->value.i = priv->vlan_id[val->port_vlan];
-
-       return 0;
-};
-
-static int
-adm6996_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-       u8 ports = priv->vlan_table[val->port_vlan];
-       u8 tagged = priv->vlan_tagged[val->port_vlan];
-       int i;
-
-       pr_devel("get_ports port_vlan %d\n", val->port_vlan);
-
-       val->len = 0;
-
-       for (i = 0; i < ADM_NUM_PORTS; i++) {
-               struct switch_port *p;
-
-               if (!(ports & (1 << i)))
-                       continue;
-
-               p = &val->value.ports[val->len++];
-               p->id = i;
-               if (tagged & (1 << i))
-                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
-               else
-                       p->flags = 0;
-       }
-
-       return 0;
-};
-
-static int
-adm6996_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-       u8 *ports = &priv->vlan_table[val->port_vlan];
-       u8 *tagged = &priv->vlan_tagged[val->port_vlan];
-       int i;
-
-       pr_devel("set_ports port_vlan %d ports", val->port_vlan);
-
-       *ports = 0;
-       *tagged = 0;
-
-       for (i = 0; i < val->len; i++) {
-               struct switch_port *p = &val->value.ports[i];
-
-#ifdef DEBUG
-               pr_cont(" %d%s", p->id,
-                      ((p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) ? "T" :
-                       ""));
-#endif
-
-               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
-                       *tagged |= (1 << p->id);
-                       priv->tagged_ports |= (1 << p->id);
-               }
-
-               *ports |= (1 << p->id);
-       }
-
-#ifdef DEBUG
-       pr_cont("\n");
-#endif
-
-       return 0;
-};
-
-/*
- * Precondition: reg_mutex must be held
- */
-static void
-adm6996_enable_vlan(struct adm6996_priv *priv)
-{
-       u16 reg;
-
-       reg = r16(priv, ADM_OTBE_P2_PVID);
-       reg &= ~(ADM_OTBE_MASK);
-       w16(priv, ADM_OTBE_P2_PVID, reg);
-       reg = r16(priv, ADM_IFNTE);
-       reg &= ~(ADM_IFNTE_MASK);
-       w16(priv, ADM_IFNTE, reg);
-       reg = r16(priv, ADM_VID_CHECK);
-       reg |= ADM_VID_CHECK_MASK;
-       w16(priv, ADM_VID_CHECK, reg);
-       reg = r16(priv, ADM_SYSC0);
-       reg |= ADM_NTTE;
-       reg &= ~(ADM_RVID1);
-       w16(priv, ADM_SYSC0, reg);
-       reg = r16(priv, ADM_SYSC3);
-       reg |= ADM_TBV;
-       w16(priv, ADM_SYSC3, reg);
-}
-
-static void
-adm6996_enable_vlan_6996l(struct adm6996_priv *priv)
-{
-       u16 reg;
-
-       reg = r16(priv, ADM_SYSC3);
-       reg |= ADM_TBV;
-       reg |= ADM_MAC_CLONE;
-       w16(priv, ADM_SYSC3, reg);
-}
-
-/*
- * Disable VLANs
- *
- * Sets VLAN mapping for port-based VLAN with all ports connected to
- * eachother (this is also the power-on default).
- *
- * Precondition: reg_mutex must be held
- */
-static void
-adm6996_disable_vlan(struct adm6996_priv *priv)
-{
-       u16 reg;
-       int i;
-
-       for (i = 0; i < ADM_NUM_VLANS; i++) {
-               reg = ADM_VLAN_FILT_MEMBER_MASK;
-               w16(priv, ADM_VLAN_FILT_L(i), reg);
-               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(1);
-               w16(priv, ADM_VLAN_FILT_H(i), reg);
-       }
-
-       reg = r16(priv, ADM_OTBE_P2_PVID);
-       reg |= ADM_OTBE_MASK;
-       w16(priv, ADM_OTBE_P2_PVID, reg);
-       reg = r16(priv, ADM_IFNTE);
-       reg |= ADM_IFNTE_MASK;
-       w16(priv, ADM_IFNTE, reg);
-       reg = r16(priv, ADM_VID_CHECK);
-       reg &= ~(ADM_VID_CHECK_MASK);
-       w16(priv, ADM_VID_CHECK, reg);
-       reg = r16(priv, ADM_SYSC0);
-       reg &= ~(ADM_NTTE);
-       reg |= ADM_RVID1;
-       w16(priv, ADM_SYSC0, reg);
-       reg = r16(priv, ADM_SYSC3);
-       reg &= ~(ADM_TBV);
-       w16(priv, ADM_SYSC3, reg);
-}
-
-/*
- * Disable VLANs
- *
- * Sets VLAN mapping for port-based VLAN with all ports connected to
- * eachother (this is also the power-on default).
- *
- * Precondition: reg_mutex must be held
- */
-static void
-adm6996_disable_vlan_6996l(struct adm6996_priv *priv)
-{
-       u16 reg;
-       int i;
-
-       for (i = 0; i < ADM_NUM_VLANS; i++) {
-               w16(priv, ADM_VLAN_MAP(i), 0);
-       }
-
-       reg = r16(priv, ADM_SYSC3);
-       reg &= ~(ADM_TBV);
-       reg &= ~(ADM_MAC_CLONE);
-       w16(priv, ADM_SYSC3, reg);
-}
-
-/*
- * Precondition: reg_mutex must be held
- */
-static void
-adm6996_apply_port_pvids(struct adm6996_priv *priv)
-{
-       u16 reg;
-       int i;
-
-       for (i = 0; i < ADM_NUM_PORTS; i++) {
-               reg = r16(priv, adm_portcfg[i]);
-               reg &= ~(ADM_PORTCFG_PVID_MASK);
-               reg |= ADM_PORTCFG_PVID(priv->pvid[i]);
-               if (priv->model == ADM6996L) {
-                       if (priv->tagged_ports & (1 << i))
-                               reg |= (1 << 4);
-                       else
-                               reg &= ~(1 << 4);
-               }
-               w16(priv, adm_portcfg[i], reg);
-       }
-
-       w16(priv, ADM_P0_PVID, ADM_P0_PVID_VAL(priv->pvid[0]));
-       w16(priv, ADM_P1_PVID, ADM_P1_PVID_VAL(priv->pvid[1]));
-       reg = r16(priv, ADM_OTBE_P2_PVID);
-       reg &= ~(ADM_P2_PVID_MASK);
-       reg |= ADM_P2_PVID_VAL(priv->pvid[2]);
-       w16(priv, ADM_OTBE_P2_PVID, reg);
-       reg = ADM_P3_PVID_VAL(priv->pvid[3]);
-       reg |= ADM_P4_PVID_VAL(priv->pvid[4]);
-       w16(priv, ADM_P3_P4_PVID, reg);
-       reg = r16(priv, ADM_P5_PVID);
-       reg &= ~(ADM_P2_PVID_MASK);
-       reg |= ADM_P5_PVID_VAL(priv->pvid[5]);
-       w16(priv, ADM_P5_PVID, reg);
-}
-
-/*
- * Precondition: reg_mutex must be held
- */
-static void
-adm6996_apply_vlan_filters(struct adm6996_priv *priv)
-{
-       u8 ports, tagged;
-       u16 vid, reg;
-       int i;
-
-       for (i = 0; i < ADM_NUM_VLANS; i++) {
-               vid = priv->vlan_id[i];
-               ports = priv->vlan_table[i];
-               tagged = priv->vlan_tagged[i];
-
-               if (ports == 0) {
-                       /* Disable VLAN entry */
-                       w16(priv, ADM_VLAN_FILT_H(i), 0);
-                       w16(priv, ADM_VLAN_FILT_L(i), 0);
-                       continue;
-               }
-
-               reg = ADM_VLAN_FILT_MEMBER(ports);
-               reg |= ADM_VLAN_FILT_TAGGED(tagged);
-               w16(priv, ADM_VLAN_FILT_L(i), reg);
-               reg = ADM_VLAN_FILT_VALID | ADM_VLAN_FILT_VID(vid);
-               w16(priv, ADM_VLAN_FILT_H(i), reg);
-       }
-}
-
-static void
-adm6996_apply_vlan_filters_6996l(struct adm6996_priv *priv)
-{
-       u8 ports;
-       u16 reg;
-       int i;
-
-       for (i = 0; i < ADM_NUM_VLANS; i++) {
-               ports = priv->vlan_table[i];
-
-               if (ports == 0) {
-                       /* Disable VLAN entry */
-                       w16(priv, ADM_VLAN_MAP(i), 0);
-                       continue;
-               } else {
-                       reg = ADM_VLAN_FILT(ports);
-                       w16(priv, ADM_VLAN_MAP(i), reg);
-               }
-       }
-}
-
-static int
-adm6996_hw_apply(struct switch_dev *dev)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       pr_devel("hw_apply\n");
-
-       mutex_lock(&priv->reg_mutex);
-
-       if (!priv->enable_vlan) {
-               if (priv->vlan_enabled) {
-                       if (priv->model == ADM6996L)
-                               adm6996_disable_vlan_6996l(priv);
-                       else
-                               adm6996_disable_vlan(priv);
-                       priv->vlan_enabled = 0;
-               }
-               goto out;
-       }
-
-       if (!priv->vlan_enabled) {
-               if (priv->model == ADM6996L)
-                       adm6996_enable_vlan_6996l(priv);
-               else
-                       adm6996_enable_vlan(priv);
-               priv->vlan_enabled = 1;
-       }
-
-       adm6996_apply_port_pvids(priv);
-       if (priv->model == ADM6996L)
-               adm6996_apply_vlan_filters_6996l(priv);
-       else
-               adm6996_apply_vlan_filters(priv);
-
-out:
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-/*
- * Reset the switch
- *
- * The ADM6996 can't do a software-initiated reset, so we just initialise the
- * registers we support in this driver.
- *
- * Precondition: reg_mutex must be held
- */
-static void
-adm6996_perform_reset (struct adm6996_priv *priv)
-{
-       int i;
-
-       /* initialize port and vlan settings */
-       for (i = 0; i < ADM_NUM_PORTS - 1; i++) {
-               w16(priv, adm_portcfg[i], ADM_PORTCFG_INIT |
-                       ADM_PORTCFG_PVID(0));
-       }
-       w16(priv, adm_portcfg[5], ADM_PORTCFG_CPU);
-
-       if (priv->model == ADM6996M || priv->model == ADM6996FC) {
-               /* reset all PHY ports */
-               for (i = 0; i < ADM_PHY_PORTS; i++) {
-                       w16(priv, ADM_PHY_PORT(i), ADM_PHYCFG_INIT);
-               }
-       }
-
-       priv->enable_vlan = 0;
-       priv->vlan_enabled = 0;
-
-       for (i = 0; i < ADM_NUM_PORTS; i++) {
-               priv->pvid[i] = 0;
-       }
-
-       for (i = 0; i < ADM_NUM_VLANS; i++) {
-               priv->vlan_id[i] = i;
-               priv->vlan_table[i] = 0;
-               priv->vlan_tagged[i] = 0;
-       }
-
-       if (priv->model == ADM6996M) {
-               /* Clear VLAN priority map so prio's are unused */
-               w16 (priv, ADM_VLAN_PRIOMAP, 0);
-
-               adm6996_disable_vlan(priv);
-               adm6996_apply_port_pvids(priv);
-       } else if (priv->model == ADM6996L) {
-               /* Clear VLAN priority map so prio's are unused */
-               w16 (priv, ADM_VLAN_PRIOMAP, 0);
-
-               adm6996_disable_vlan_6996l(priv);
-               adm6996_apply_port_pvids(priv);
-       }
-}
-
-static int
-adm6996_reset_switch(struct switch_dev *dev)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-
-       pr_devel("reset\n");
-
-       mutex_lock(&priv->reg_mutex);
-       adm6996_perform_reset (priv);
-       mutex_unlock(&priv->reg_mutex);
-       return 0;
-}
-
-static int
-adm6996_get_port_link(struct switch_dev *dev, int port,
-               struct switch_port_link *link)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-       
-       u16 reg = 0;
-       
-       if (port >= ADM_NUM_PORTS)
-               return -EINVAL;
-       
-       switch (port) {
-       case 0:
-               reg = r16(priv, ADM_PS0);
-               break;
-       case 1:
-               reg = r16(priv, ADM_PS0);
-               reg = reg >> 8;
-               break;
-       case 2:
-               reg = r16(priv, ADM_PS1);
-               break;
-       case 3:
-               reg = r16(priv, ADM_PS1);
-               reg = reg >> 8;
-               break;
-       case 4:
-               reg = r16(priv, ADM_PS1);
-               reg = reg >> 12;
-               break;
-       case 5:
-               reg = r16(priv, ADM_PS2);
-               /* Bits 0, 1, 3 and 4. */
-               reg = (reg & 3) | ((reg & 24) >> 1);
-               break;
-       default:
-               return -EINVAL;
-       }
-       
-       link->link = reg & ADM_PS_LS;
-       if (!link->link)
-               return 0;
-       link->aneg = true;
-       link->duplex = reg & ADM_PS_DS;
-       link->tx_flow = reg & ADM_PS_FCS;
-       link->rx_flow = reg & ADM_PS_FCS;
-       if (reg & ADM_PS_SS)
-               link->speed = SWITCH_PORT_SPEED_100;
-       else
-               link->speed = SWITCH_PORT_SPEED_10;
-
-       return 0;
-}
-
-static int
-adm6996_sw_get_port_mib(struct switch_dev *dev,
-                      const struct switch_attr *attr,
-                      struct switch_val *val)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-       int port;
-       char *buf = priv->buf;
-       int i, len = 0;
-       u32 reg = 0;
-
-       port = val->port_vlan;
-       if (port >= ADM_NUM_PORTS)
-               return -EINVAL;
-
-       mutex_lock(&priv->mib_lock);
-
-       len += snprintf(buf + len, sizeof(priv->buf) - len,
-                       "Port %d MIB counters\n",
-                       port);
-
-       for (i = 0; i < ARRAY_SIZE(adm6996_mibs); i++) {
-               reg = r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port));
-               reg += r16(priv, adm6996_mibs[i].offset + ADM_OFFSET_PORT(port) + 1) << 16;
-               len += snprintf(buf + len, sizeof(priv->buf) - len,
-                               "%-12s: %u\n",
-                               adm6996_mibs[i].name,
-                               reg);
-       }
-
-       mutex_unlock(&priv->mib_lock);
-
-       val->value.s = buf;
-       val->len = len;
-
-       return 0;
-}
-
-static int
-adm6996_get_port_stats(struct switch_dev *dev, int port,
-                       struct switch_port_stats *stats)
-{
-       struct adm6996_priv *priv = to_adm(dev);
-       int id;
-       u32 reg = 0;
-
-       if (port >= ADM_NUM_PORTS)
-               return -EINVAL;
-
-       mutex_lock(&priv->mib_lock);
-
-       id = ADM6996_MIB_TXB_ID;
-       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
-       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
-       stats->tx_bytes = reg;
-
-       id = ADM6996_MIB_RXB_ID;
-       reg = r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port));
-       reg += r16(priv, adm6996_mibs[id].offset + ADM_OFFSET_PORT(port) + 1) << 16;
-       stats->rx_bytes = reg;
-
-       mutex_unlock(&priv->mib_lock);
-
-       return 0;
-}
-
-static struct switch_attr adm6996_globals[] = {
-       {
-        .type = SWITCH_TYPE_INT,
-        .name = "enable_vlan",
-        .description = "Enable VLANs",
-        .set = adm6996_set_enable_vlan,
-        .get = adm6996_get_enable_vlan,
-       },
-#ifdef DEBUG
-       {
-        .type = SWITCH_TYPE_INT,
-        .name = "addr",
-        .description =
-        "Direct register access: set register address (0 - 1023)",
-        .set = adm6996_set_addr,
-        .get = adm6996_get_addr,
-        },
-       {
-        .type = SWITCH_TYPE_INT,
-        .name = "data",
-        .description =
-        "Direct register access: read/write to register (0 - 65535)",
-        .set = adm6996_set_data,
-        .get = adm6996_get_data,
-        },
-#endif /* def DEBUG */
-};
-
-static struct switch_attr adm6996_port[] = {
-       {
-        .type = SWITCH_TYPE_STRING,
-        .name = "mib",
-        .description = "Get port's MIB counters",
-        .set = NULL,
-        .get = adm6996_sw_get_port_mib,
-       },
-};
-
-static struct switch_attr adm6996_vlan[] = {
-       {
-        .type = SWITCH_TYPE_INT,
-        .name = "vid",
-        .description = "VLAN ID",
-        .set = adm6996_set_vid,
-        .get = adm6996_get_vid,
-        },
-};
-
-static struct switch_dev_ops adm6996_ops = {
-       .attr_global = {
-                       .attr = adm6996_globals,
-                       .n_attr = ARRAY_SIZE(adm6996_globals),
-                       },
-       .attr_port = {
-                     .attr = adm6996_port,
-                     .n_attr = ARRAY_SIZE(adm6996_port),
-                     },
-       .attr_vlan = {
-                     .attr = adm6996_vlan,
-                     .n_attr = ARRAY_SIZE(adm6996_vlan),
-                     },
-       .get_port_pvid = adm6996_get_pvid,
-       .set_port_pvid = adm6996_set_pvid,
-       .get_vlan_ports = adm6996_get_ports,
-       .set_vlan_ports = adm6996_set_ports,
-       .apply_config = adm6996_hw_apply,
-       .reset_switch = adm6996_reset_switch,
-       .get_port_link = adm6996_get_port_link,
-       .get_port_stats = adm6996_get_port_stats,
-};
-
-static int adm6996_switch_init(struct adm6996_priv *priv, const char *alias, struct net_device *netdev)
-{
-       struct switch_dev *swdev;
-       u16 test, old;
-
-       if (!priv->model) {
-               /* Detect type of chip */
-               old = r16(priv, ADM_VID_CHECK);
-               test = old ^ (1 << 12);
-               w16(priv, ADM_VID_CHECK, test);
-               test ^= r16(priv, ADM_VID_CHECK);
-               if (test & (1 << 12)) {
-                       /* 
-                        * Bit 12 of this register is read-only. 
-                        * This is the FC model. 
-                        */
-                       priv->model = ADM6996FC;
-               } else {
-                       /* Bit 12 is read-write. This is the M model. */
-                       priv->model = ADM6996M;
-                       w16(priv, ADM_VID_CHECK, old);
-               }
-       }
-
-       swdev = &priv->dev;
-       swdev->name = (adm6996_model_name[priv->model]);
-       swdev->cpu_port = ADM_CPU_PORT;
-       swdev->ports = ADM_NUM_PORTS;
-       swdev->vlans = ADM_NUM_VLANS;
-       swdev->ops = &adm6996_ops;
-       swdev->alias = alias;
-
-       /* The ADM6996L connected through GPIOs does not support any switch
-          status calls */
-       if (priv->model == ADM6996L) {
-               adm6996_ops.attr_port.n_attr = 0;
-               adm6996_ops.get_port_link = NULL;
-       }
-
-       pr_info ("%s: %s model PHY found.\n", alias, swdev->name);
-
-       mutex_lock(&priv->reg_mutex);
-       adm6996_perform_reset (priv);
-       mutex_unlock(&priv->reg_mutex);
-
-       if (priv->model == ADM6996M || priv->model == ADM6996L) {
-               return register_switch(swdev, netdev);
-       }
-
-       return -ENODEV;
-}
-
-static int adm6996_config_init(struct phy_device *pdev)
-{
-       struct adm6996_priv *priv;
-       int ret;
-
-       pdev->supported = ADVERTISED_100baseT_Full;
-       pdev->advertising = ADVERTISED_100baseT_Full;
-
-       if (pdev->mdio.addr != 0) {
-               pr_info ("%s: PHY overlaps ADM6996, providing fixed PHY 0x%x.\n"
-                               , pdev->attached_dev->name, pdev->mdio.addr);
-               return 0;
-       }
-
-       priv = devm_kzalloc(&pdev->mdio.dev, sizeof(struct adm6996_priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       mutex_init(&priv->reg_mutex);
-       mutex_init(&priv->mib_lock);
-       priv->priv = pdev;
-       priv->read = adm6996_read_mii_reg;
-       priv->write = adm6996_write_mii_reg;
-
-       ret = adm6996_switch_init(priv, pdev->attached_dev->name, pdev->attached_dev);
-       if (ret < 0)
-               return ret;
-
-       pdev->priv = priv;
-
-       return 0;
-}
-
-/*
- * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
- */
-static int adm6996_read_status(struct phy_device *phydev)
-{
-       phydev->speed = SPEED_100;
-       phydev->duplex = DUPLEX_FULL;
-       phydev->link = 1;
-
-       phydev->state = PHY_RUNNING;
-       netif_carrier_on(phydev->attached_dev);
-       phydev->adjust_link(phydev->attached_dev);
-
-       return 0;
-}
-
-/*
- * Warning: phydev->priv is NULL if phydev->mdio.addr != 0
- */
-static int adm6996_config_aneg(struct phy_device *phydev)
-{
-       return 0;
-}
-
-static int adm6996_fixup(struct phy_device *dev)
-{
-       struct mii_bus *bus = dev->mdio.bus;
-       u16 reg;
-
-       /* Our custom registers are at PHY addresses 0-10. Claim those. */
-       if (dev->mdio.addr > 10)
-               return 0;
-
-       /* look for the switch on the bus */
-       reg = bus->read(bus, PHYADDR(ADM_SIG0)) & ADM_SIG0_MASK;
-       if (reg != ADM_SIG0_VAL)
-               return 0;
-
-       reg = bus->read(bus, PHYADDR(ADM_SIG1)) & ADM_SIG1_MASK;
-       if (reg != ADM_SIG1_VAL)
-               return 0;
-
-       dev->phy_id = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL;
-
-       return 0;
-}
-
-static int adm6996_probe(struct phy_device *pdev)
-{
-       return 0;
-}
-
-static void adm6996_remove(struct phy_device *pdev)
-{
-       struct adm6996_priv *priv = phy_to_adm(pdev);
-
-       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
-               unregister_switch(&priv->dev);
-}
-
-static int adm6996_soft_reset(struct phy_device *phydev)
-{
-       /* we don't need an extra reset */
-       return 0;
-}
-
-static struct phy_driver adm6996_phy_driver = {
-       .name           = "Infineon ADM6996",
-       .phy_id         = (ADM_SIG0_VAL << 16) | ADM_SIG1_VAL,
-       .phy_id_mask    = 0xffffffff,
-       .features       = PHY_BASIC_FEATURES,
-       .probe          = adm6996_probe,
-       .remove         = adm6996_remove,
-       .config_init    = &adm6996_config_init,
-       .config_aneg    = &adm6996_config_aneg,
-       .read_status    = &adm6996_read_status,
-       .soft_reset     = adm6996_soft_reset,
-};
-
-static int adm6996_gpio_probe(struct platform_device *pdev)
-{
-       struct adm6996_gpio_platform_data *pdata = pdev->dev.platform_data;
-       struct adm6996_priv *priv;
-       int ret;
-
-       if (!pdata)
-               return -EINVAL;
-
-       priv = devm_kzalloc(&pdev->dev, sizeof(struct adm6996_priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       mutex_init(&priv->reg_mutex);
-       mutex_init(&priv->mib_lock);
-
-       priv->eecs = pdata->eecs;
-       priv->eedi = pdata->eedi;
-       priv->eesk = pdata->eesk;
-
-       priv->model = pdata->model;
-       priv->read = adm6996_read_gpio_reg;
-       priv->write = adm6996_write_gpio_reg;
-
-       ret = devm_gpio_request(&pdev->dev, priv->eecs, "adm_eecs");
-       if (ret)
-               return ret;
-       ret = devm_gpio_request(&pdev->dev, priv->eedi, "adm_eedi");
-       if (ret)
-               return ret;
-       ret = devm_gpio_request(&pdev->dev, priv->eesk, "adm_eesk");
-       if (ret)
-               return ret;
-
-       ret = adm6996_switch_init(priv, dev_name(&pdev->dev), NULL);
-       if (ret < 0)
-               return ret;
-
-       platform_set_drvdata(pdev, priv);
-
-       return 0;
-}
-
-static int adm6996_gpio_remove(struct platform_device *pdev)
-{
-       struct adm6996_priv *priv = platform_get_drvdata(pdev);
-
-       if (priv && (priv->model == ADM6996M || priv->model == ADM6996L))
-               unregister_switch(&priv->dev);
-
-       return 0;
-}
-
-static struct platform_driver adm6996_gpio_driver = {
-       .probe = adm6996_gpio_probe,
-       .remove = adm6996_gpio_remove,
-       .driver = {
-               .name = "adm6996_gpio",
-       },
-};
-
-static int __init adm6996_init(void)
-{
-       int err;
-
-       phy_register_fixup_for_id(PHY_ANY_ID, adm6996_fixup);
-       err = phy_driver_register(&adm6996_phy_driver, THIS_MODULE);
-       if (err)
-               return err;
-
-       err = platform_driver_register(&adm6996_gpio_driver);
-       if (err)
-               phy_driver_unregister(&adm6996_phy_driver);
-
-       return err;
-}
-
-static void __exit adm6996_exit(void)
-{
-       platform_driver_unregister(&adm6996_gpio_driver);
-       phy_driver_unregister(&adm6996_phy_driver);
-}
-
-module_init(adm6996_init);
-module_exit(adm6996_exit);
diff --git a/target/linux/generic/files/drivers/net/phy/adm6996.h b/target/linux/generic/files/drivers/net/phy/adm6996.h
deleted file mode 100644 (file)
index 6fd460a..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * ADM6996 switch driver
- *
- * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
- * Copyright (c) 2010,2011 Peter Lebbing <peter@digitalbrains.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-#ifndef __ADM6996_H
-#define __ADM6996_H
-
-/*
- * ADM_PHY_PORTS: Number of ports with a PHY.
- * We only control ports 0 to 3, because if 4 is connected, it is most likely
- * not connected to the switch but to a separate MII and MAC for the WAN port.
- */
-#define ADM_PHY_PORTS  4
-#define ADM_NUM_PORTS  6
-#define ADM_CPU_PORT   5
-
-#define ADM_NUM_VLANS 16
-#define ADM_VLAN_MAX_ID 4094
-
-enum admreg {
-       ADM_EEPROM_BASE         = 0x0,
-               ADM_P0_CFG              = ADM_EEPROM_BASE + 1,
-               ADM_P1_CFG              = ADM_EEPROM_BASE + 3,
-               ADM_P2_CFG              = ADM_EEPROM_BASE + 5,
-               ADM_P3_CFG              = ADM_EEPROM_BASE + 7,
-               ADM_P4_CFG              = ADM_EEPROM_BASE + 8,
-               ADM_P5_CFG              = ADM_EEPROM_BASE + 9,
-               ADM_SYSC0               = ADM_EEPROM_BASE + 0xa,
-               ADM_VLAN_PRIOMAP        = ADM_EEPROM_BASE + 0xe,
-               ADM_SYSC3               = ADM_EEPROM_BASE + 0x11,
-               /* Input Force No Tag Enable */
-               ADM_IFNTE               = ADM_EEPROM_BASE + 0x20,
-               ADM_VID_CHECK           = ADM_EEPROM_BASE + 0x26,
-               ADM_P0_PVID             = ADM_EEPROM_BASE + 0x28,
-               ADM_P1_PVID             = ADM_EEPROM_BASE + 0x29,
-               /* Output Tag Bypass Enable and P2 PVID */
-               ADM_OTBE_P2_PVID        = ADM_EEPROM_BASE + 0x2a,
-               ADM_P3_P4_PVID          = ADM_EEPROM_BASE + 0x2b,
-               ADM_P5_PVID             = ADM_EEPROM_BASE + 0x2c,
-       ADM_EEPROM_EXT_BASE     = 0x40,
-#define ADM_VLAN_FILT_L(n) (ADM_EEPROM_EXT_BASE + 2 * (n))
-#define ADM_VLAN_FILT_H(n) (ADM_EEPROM_EXT_BASE + 1 + 2 * (n))
-#define ADM_VLAN_MAP(n) (ADM_EEPROM_BASE + 0x13 + n)
-       ADM_COUNTER_BASE        = 0xa0,
-               ADM_SIG0                = ADM_COUNTER_BASE + 0,
-               ADM_SIG1                = ADM_COUNTER_BASE + 1,
-               ADM_PS0         = ADM_COUNTER_BASE + 2,
-               ADM_PS1         = ADM_COUNTER_BASE + 3,
-               ADM_PS2         = ADM_COUNTER_BASE + 4,
-               ADM_CL0         = ADM_COUNTER_BASE + 8, /* RxPacket */
-               ADM_CL6         = ADM_COUNTER_BASE + 0x1a, /* RxByte */
-               ADM_CL12                = ADM_COUNTER_BASE + 0x2c, /* TxPacket */
-               ADM_CL18                = ADM_COUNTER_BASE + 0x3e, /* TxByte */
-               ADM_CL24                = ADM_COUNTER_BASE + 0x50, /* Coll */
-               ADM_CL30                = ADM_COUNTER_BASE + 0x62, /* Err */
-#define ADM_OFFSET_PORT(n) ((n * 4) - (n / 4) * 2 - (n / 5) * 2)
-       ADM_PHY_BASE            = 0x200,
-#define ADM_PHY_PORT(n) (ADM_PHY_BASE + (0x20 * n))
-};
-
-/* Chip identification patterns */
-#define        ADM_SIG0_MASK   0xffff
-#define ADM_SIG0_VAL   0x1023
-#define ADM_SIG1_MASK  0xffff
-#define ADM_SIG1_VAL   0x0007
-
-enum {
-       ADM_PHYCFG_COLTST     = (1 << 7),       /* Enable collision test */
-       ADM_PHYCFG_DPLX       = (1 << 8),       /* Enable full duplex */
-       ADM_PHYCFG_ANEN_RST   = (1 << 9),       /* Restart auto negotiation (self clear) */
-       ADM_PHYCFG_ISO        = (1 << 10),      /* Isolate PHY */
-       ADM_PHYCFG_PDN        = (1 << 11),      /* Power down PHY */
-       ADM_PHYCFG_ANEN       = (1 << 12),      /* Enable auto negotiation */
-       ADM_PHYCFG_SPEED_100  = (1 << 13),      /* Enable 100 Mbit/s */
-       ADM_PHYCFG_LPBK       = (1 << 14),      /* Enable loopback operation */
-       ADM_PHYCFG_RST        = (1 << 15),      /* Reset the port (self clear) */
-       ADM_PHYCFG_INIT = (
-               ADM_PHYCFG_RST |
-               ADM_PHYCFG_SPEED_100 |
-               ADM_PHYCFG_ANEN |
-               ADM_PHYCFG_ANEN_RST
-       )
-};
-
-enum {
-       ADM_PORTCFG_FC        = (1 << 0),       /* Enable 802.x flow control */
-       ADM_PORTCFG_AN        = (1 << 1),       /* Enable auto-negotiation */
-       ADM_PORTCFG_SPEED_100 = (1 << 2),       /* Enable 100 Mbit/s */
-       ADM_PORTCFG_DPLX      = (1 << 3),       /* Enable full duplex */
-       ADM_PORTCFG_OT        = (1 << 4),       /* Output tagged packets */
-       ADM_PORTCFG_PD        = (1 << 5),       /* Port disable */
-       ADM_PORTCFG_TV_PRIO   = (1 << 6),       /* 0 = VLAN based priority
-                                                * 1 = TOS based priority */
-       ADM_PORTCFG_PPE       = (1 << 7),       /* Port based priority enable */
-       ADM_PORTCFG_PP_S      = (1 << 8),       /* Port based priority, 2 bits */
-       ADM_PORTCFG_PVID_BASE = (1 << 10),      /* Primary VLAN id, 4 bits */
-       ADM_PORTCFG_FSE       = (1 << 14),      /* Fx select enable */
-       ADM_PORTCFG_CAM       = (1 << 15),      /* Crossover Auto MDIX */
-
-       ADM_PORTCFG_INIT = (
-               ADM_PORTCFG_FC |
-               ADM_PORTCFG_AN |
-               ADM_PORTCFG_SPEED_100 |
-               ADM_PORTCFG_DPLX |
-               ADM_PORTCFG_CAM
-       ),
-       ADM_PORTCFG_CPU = (
-               ADM_PORTCFG_FC |
-               ADM_PORTCFG_SPEED_100 |
-               ADM_PORTCFG_OT |
-               ADM_PORTCFG_DPLX
-       ),
-};
-
-#define ADM_PORTCFG_PPID(n) ((n & 0x3) << 8)
-#define ADM_PORTCFG_PVID(n) ((n & 0xf) << 10)
-#define ADM_PORTCFG_PVID_MASK (0xf << 10)
-
-#define ADM_IFNTE_MASK (0x3f << 9)
-#define ADM_VID_CHECK_MASK (0x3f << 6)
-
-#define ADM_P0_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
-#define ADM_P1_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
-#define ADM_P2_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
-#define ADM_P3_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
-#define ADM_P4_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 8)
-#define ADM_P5_PVID_VAL(n) ((((n) & 0xff0) >> 4) << 0)
-#define ADM_P2_PVID_MASK 0xff
-
-#define ADM_OTBE(n) (((n) & 0x3f) << 8)
-#define ADM_OTBE_MASK (0x3f << 8)
-
-/* ADM_SYSC0 */
-enum {
-       ADM_NTTE        = (1 << 2),     /* New Tag Transmit Enable */
-       ADM_RVID1       = (1 << 8)      /* Replace VLAN ID 1 */
-};
-
-/* Tag Based VLAN in ADM_SYSC3 */
-#define ADM_MAC_CLONE  BIT(4)
-#define ADM_TBV                BIT(5)
-
-static const u8 adm_portcfg[] = {
-       [0] = ADM_P0_CFG,
-       [1] = ADM_P1_CFG,
-       [2] = ADM_P2_CFG,
-       [3] = ADM_P3_CFG,
-       [4] = ADM_P4_CFG,
-       [5] = ADM_P5_CFG,
-};
-
-/* Fields in ADM_VLAN_FILT_L(x) */
-#define ADM_VLAN_FILT_FID(n) (((n) & 0xf) << 12)
-#define ADM_VLAN_FILT_TAGGED(n) (((n) & 0x3f) << 6)
-#define ADM_VLAN_FILT_MEMBER(n) (((n) & 0x3f) << 0)
-#define ADM_VLAN_FILT_MEMBER_MASK 0x3f
-/* Fields in ADM_VLAN_FILT_H(x) */
-#define ADM_VLAN_FILT_VALID (1 << 15)
-#define ADM_VLAN_FILT_VID(n) (((n) & 0xfff) << 0)
-
-/* Convert ports to a form for ADM6996L VLAN map */
-#define ADM_VLAN_FILT(ports) ((ports & 0x01) | ((ports & 0x02) << 1) | \
-                       ((ports & 0x04) << 2) | ((ports & 0x08) << 3) | \
-                       ((ports & 0x10) << 3) | ((ports & 0x20) << 3))
-
-/* Port status register */
-enum {
-       ADM_PS_LS = (1 << 0),   /* Link status */
-       ADM_PS_SS = (1 << 1),   /* Speed status */
-       ADM_PS_DS = (1 << 2),   /* Duplex status */
-       ADM_PS_FCS = (1 << 3)   /* Flow control status */
-};
-
-/*
- * Split the register address in phy id and register
- * it will get combined again by the mdio bus op
- */
-#define PHYADDR(_reg)  ((_reg >> 5) & 0xff), (_reg & 0x1f)
-
-#endif
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.c b/target/linux/generic/files/drivers/net/phy/ar8216.c
deleted file mode 100644 (file)
index 7512ee1..0000000
+++ /dev/null
@@ -1,2313 +0,0 @@
-/*
- * ar8216.c: AR8216 switch driver
- *
- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/if.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/if_ether.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/netlink.h>
-#include <linux/bitops.h>
-#include <net/genetlink.h>
-#include <linux/switch.h>
-#include <linux/delay.h>
-#include <linux/phy.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/lockdep.h>
-#include <linux/ar8216_platform.h>
-#include <linux/workqueue.h>
-#include <linux/version.h>
-
-#include "ar8216.h"
-
-extern const struct ar8xxx_chip ar8327_chip;
-extern const struct ar8xxx_chip ar8337_chip;
-
-#define AR8XXX_MIB_WORK_DELAY  2000 /* msecs */
-
-#define MIB_DESC(_s , _o, _n)  \
-       {                       \
-               .size = (_s),   \
-               .offset = (_o), \
-               .name = (_n),   \
-       }
-
-static const struct ar8xxx_mib_desc ar8216_mibs[] = {
-       MIB_DESC(1, AR8216_STATS_RXBROAD, "RxBroad"),
-       MIB_DESC(1, AR8216_STATS_RXPAUSE, "RxPause"),
-       MIB_DESC(1, AR8216_STATS_RXMULTI, "RxMulti"),
-       MIB_DESC(1, AR8216_STATS_RXFCSERR, "RxFcsErr"),
-       MIB_DESC(1, AR8216_STATS_RXALIGNERR, "RxAlignErr"),
-       MIB_DESC(1, AR8216_STATS_RXRUNT, "RxRunt"),
-       MIB_DESC(1, AR8216_STATS_RXFRAGMENT, "RxFragment"),
-       MIB_DESC(1, AR8216_STATS_RX64BYTE, "Rx64Byte"),
-       MIB_DESC(1, AR8216_STATS_RX128BYTE, "Rx128Byte"),
-       MIB_DESC(1, AR8216_STATS_RX256BYTE, "Rx256Byte"),
-       MIB_DESC(1, AR8216_STATS_RX512BYTE, "Rx512Byte"),
-       MIB_DESC(1, AR8216_STATS_RX1024BYTE, "Rx1024Byte"),
-       MIB_DESC(1, AR8216_STATS_RXMAXBYTE, "RxMaxByte"),
-       MIB_DESC(1, AR8216_STATS_RXTOOLONG, "RxTooLong"),
-       MIB_DESC(2, AR8216_STATS_RXGOODBYTE, "RxGoodByte"),
-       MIB_DESC(2, AR8216_STATS_RXBADBYTE, "RxBadByte"),
-       MIB_DESC(1, AR8216_STATS_RXOVERFLOW, "RxOverFlow"),
-       MIB_DESC(1, AR8216_STATS_FILTERED, "Filtered"),
-       MIB_DESC(1, AR8216_STATS_TXBROAD, "TxBroad"),
-       MIB_DESC(1, AR8216_STATS_TXPAUSE, "TxPause"),
-       MIB_DESC(1, AR8216_STATS_TXMULTI, "TxMulti"),
-       MIB_DESC(1, AR8216_STATS_TXUNDERRUN, "TxUnderRun"),
-       MIB_DESC(1, AR8216_STATS_TX64BYTE, "Tx64Byte"),
-       MIB_DESC(1, AR8216_STATS_TX128BYTE, "Tx128Byte"),
-       MIB_DESC(1, AR8216_STATS_TX256BYTE, "Tx256Byte"),
-       MIB_DESC(1, AR8216_STATS_TX512BYTE, "Tx512Byte"),
-       MIB_DESC(1, AR8216_STATS_TX1024BYTE, "Tx1024Byte"),
-       MIB_DESC(1, AR8216_STATS_TXMAXBYTE, "TxMaxByte"),
-       MIB_DESC(1, AR8216_STATS_TXOVERSIZE, "TxOverSize"),
-       MIB_DESC(2, AR8216_STATS_TXBYTE, "TxByte"),
-       MIB_DESC(1, AR8216_STATS_TXCOLLISION, "TxCollision"),
-       MIB_DESC(1, AR8216_STATS_TXABORTCOL, "TxAbortCol"),
-       MIB_DESC(1, AR8216_STATS_TXMULTICOL, "TxMultiCol"),
-       MIB_DESC(1, AR8216_STATS_TXSINGLECOL, "TxSingleCol"),
-       MIB_DESC(1, AR8216_STATS_TXEXCDEFER, "TxExcDefer"),
-       MIB_DESC(1, AR8216_STATS_TXDEFER, "TxDefer"),
-       MIB_DESC(1, AR8216_STATS_TXLATECOL, "TxLateCol"),
-};
-
-const struct ar8xxx_mib_desc ar8236_mibs[39] = {
-       MIB_DESC(1, AR8236_STATS_RXBROAD, "RxBroad"),
-       MIB_DESC(1, AR8236_STATS_RXPAUSE, "RxPause"),
-       MIB_DESC(1, AR8236_STATS_RXMULTI, "RxMulti"),
-       MIB_DESC(1, AR8236_STATS_RXFCSERR, "RxFcsErr"),
-       MIB_DESC(1, AR8236_STATS_RXALIGNERR, "RxAlignErr"),
-       MIB_DESC(1, AR8236_STATS_RXRUNT, "RxRunt"),
-       MIB_DESC(1, AR8236_STATS_RXFRAGMENT, "RxFragment"),
-       MIB_DESC(1, AR8236_STATS_RX64BYTE, "Rx64Byte"),
-       MIB_DESC(1, AR8236_STATS_RX128BYTE, "Rx128Byte"),
-       MIB_DESC(1, AR8236_STATS_RX256BYTE, "Rx256Byte"),
-       MIB_DESC(1, AR8236_STATS_RX512BYTE, "Rx512Byte"),
-       MIB_DESC(1, AR8236_STATS_RX1024BYTE, "Rx1024Byte"),
-       MIB_DESC(1, AR8236_STATS_RX1518BYTE, "Rx1518Byte"),
-       MIB_DESC(1, AR8236_STATS_RXMAXBYTE, "RxMaxByte"),
-       MIB_DESC(1, AR8236_STATS_RXTOOLONG, "RxTooLong"),
-       MIB_DESC(2, AR8236_STATS_RXGOODBYTE, "RxGoodByte"),
-       MIB_DESC(2, AR8236_STATS_RXBADBYTE, "RxBadByte"),
-       MIB_DESC(1, AR8236_STATS_RXOVERFLOW, "RxOverFlow"),
-       MIB_DESC(1, AR8236_STATS_FILTERED, "Filtered"),
-       MIB_DESC(1, AR8236_STATS_TXBROAD, "TxBroad"),
-       MIB_DESC(1, AR8236_STATS_TXPAUSE, "TxPause"),
-       MIB_DESC(1, AR8236_STATS_TXMULTI, "TxMulti"),
-       MIB_DESC(1, AR8236_STATS_TXUNDERRUN, "TxUnderRun"),
-       MIB_DESC(1, AR8236_STATS_TX64BYTE, "Tx64Byte"),
-       MIB_DESC(1, AR8236_STATS_TX128BYTE, "Tx128Byte"),
-       MIB_DESC(1, AR8236_STATS_TX256BYTE, "Tx256Byte"),
-       MIB_DESC(1, AR8236_STATS_TX512BYTE, "Tx512Byte"),
-       MIB_DESC(1, AR8236_STATS_TX1024BYTE, "Tx1024Byte"),
-       MIB_DESC(1, AR8236_STATS_TX1518BYTE, "Tx1518Byte"),
-       MIB_DESC(1, AR8236_STATS_TXMAXBYTE, "TxMaxByte"),
-       MIB_DESC(1, AR8236_STATS_TXOVERSIZE, "TxOverSize"),
-       MIB_DESC(2, AR8236_STATS_TXBYTE, "TxByte"),
-       MIB_DESC(1, AR8236_STATS_TXCOLLISION, "TxCollision"),
-       MIB_DESC(1, AR8236_STATS_TXABORTCOL, "TxAbortCol"),
-       MIB_DESC(1, AR8236_STATS_TXMULTICOL, "TxMultiCol"),
-       MIB_DESC(1, AR8236_STATS_TXSINGLECOL, "TxSingleCol"),
-       MIB_DESC(1, AR8236_STATS_TXEXCDEFER, "TxExcDefer"),
-       MIB_DESC(1, AR8236_STATS_TXDEFER, "TxDefer"),
-       MIB_DESC(1, AR8236_STATS_TXLATECOL, "TxLateCol"),
-};
-
-static DEFINE_MUTEX(ar8xxx_dev_list_lock);
-static LIST_HEAD(ar8xxx_dev_list);
-
-/* inspired by phy_poll_reset in drivers/net/phy/phy_device.c */
-static int
-ar8xxx_phy_poll_reset(struct mii_bus *bus)
-{
-        unsigned int sleep_msecs = 20;
-        int ret, elapsed, i;
-
-        for (elapsed = sleep_msecs; elapsed <= 600;
-            elapsed += sleep_msecs) {
-                msleep(sleep_msecs);
-                for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
-                        ret = mdiobus_read(bus, i, MII_BMCR);
-                        if (ret < 0)
-                               return ret;
-                        if (ret & BMCR_RESET)
-                               break;
-                        if (i == AR8XXX_NUM_PHYS - 1) {
-                                usleep_range(1000, 2000);
-                                return 0;
-                        }
-                }
-        }
-        return -ETIMEDOUT;
-}
-
-static int
-ar8xxx_phy_check_aneg(struct phy_device *phydev)
-{
-       int ret;
-
-       if (phydev->autoneg != AUTONEG_ENABLE)
-               return 0;
-       /*
-        * BMCR_ANENABLE might have been cleared
-        * by phy_init_hw in certain kernel versions
-        * therefore check for it
-        */
-       ret = phy_read(phydev, MII_BMCR);
-       if (ret < 0)
-               return ret;
-       if (ret & BMCR_ANENABLE)
-               return 0;
-
-       dev_info(&phydev->mdio.dev, "ANEG disabled, re-enabling ...\n");
-       ret |= BMCR_ANENABLE | BMCR_ANRESTART;
-       return phy_write(phydev, MII_BMCR, ret);
-}
-
-void
-ar8xxx_phy_init(struct ar8xxx_priv *priv)
-{
-       int i;
-       struct mii_bus *bus;
-
-       bus = priv->mii_bus;
-       for (i = 0; i < AR8XXX_NUM_PHYS; i++) {
-               if (priv->chip->phy_fixup)
-                       priv->chip->phy_fixup(priv, i);
-
-               /* initialize the port itself */
-               mdiobus_write(bus, i, MII_ADVERTISE,
-                       ADVERTISE_ALL | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM);
-               if (ar8xxx_has_gige(priv))
-                       mdiobus_write(bus, i, MII_CTRL1000, ADVERTISE_1000FULL);
-               mdiobus_write(bus, i, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
-       }
-
-       ar8xxx_phy_poll_reset(bus);
-}
-
-u32
-ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 lo, hi;
-
-       lo = bus->read(bus, phy_id, regnum);
-       hi = bus->read(bus, phy_id, regnum + 1);
-
-       return (hi << 16) | lo;
-}
-
-void
-ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 lo, hi;
-
-       lo = val & 0xffff;
-       hi = (u16) (val >> 16);
-
-       if (priv->chip->mii_lo_first)
-       {
-               bus->write(bus, phy_id, regnum, lo);
-               bus->write(bus, phy_id, regnum + 1, hi);
-       } else {
-               bus->write(bus, phy_id, regnum + 1, hi);
-               bus->write(bus, phy_id, regnum, lo);
-       }
-}
-
-u32
-ar8xxx_read(struct ar8xxx_priv *priv, int reg)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 r1, r2, page;
-       u32 val;
-
-       split_addr((u32) reg, &r1, &r2, &page);
-
-       mutex_lock(&bus->mdio_lock);
-
-       bus->write(bus, 0x18, 0, page);
-       wait_for_page_switch();
-       val = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
-
-       mutex_unlock(&bus->mdio_lock);
-
-       return val;
-}
-
-void
-ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 r1, r2, page;
-
-       split_addr((u32) reg, &r1, &r2, &page);
-
-       mutex_lock(&bus->mdio_lock);
-
-       bus->write(bus, 0x18, 0, page);
-       wait_for_page_switch();
-       ar8xxx_mii_write32(priv, 0x10 | r2, r1, val);
-
-       mutex_unlock(&bus->mdio_lock);
-}
-
-u32
-ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 r1, r2, page;
-       u32 ret;
-
-       split_addr((u32) reg, &r1, &r2, &page);
-
-       mutex_lock(&bus->mdio_lock);
-
-       bus->write(bus, 0x18, 0, page);
-       wait_for_page_switch();
-
-       ret = ar8xxx_mii_read32(priv, 0x10 | r2, r1);
-       ret &= ~mask;
-       ret |= val;
-       ar8xxx_mii_write32(priv, 0x10 | r2, r1, ret);
-
-       mutex_unlock(&bus->mdio_lock);
-
-       return ret;
-}
-
-void
-ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
-                    u16 dbg_addr, u16 dbg_data)
-{
-       struct mii_bus *bus = priv->mii_bus;
-
-       mutex_lock(&bus->mdio_lock);
-       bus->write(bus, phy_addr, MII_ATH_DBG_ADDR, dbg_addr);
-       bus->write(bus, phy_addr, MII_ATH_DBG_DATA, dbg_data);
-       mutex_unlock(&bus->mdio_lock);
-}
-
-static inline void
-ar8xxx_phy_mmd_prep(struct mii_bus *bus, int phy_addr, u16 addr, u16 reg)
-{
-       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr);
-       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, reg);
-       bus->write(bus, phy_addr, MII_ATH_MMD_ADDR, addr | 0x4000);
-}
-
-void
-ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data)
-{
-       struct mii_bus *bus = priv->mii_bus;
-
-       mutex_lock(&bus->mdio_lock);
-       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
-       bus->write(bus, phy_addr, MII_ATH_MMD_DATA, data);
-       mutex_unlock(&bus->mdio_lock);
-}
-
-u16
-ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 data;
-
-       mutex_lock(&bus->mdio_lock);
-       ar8xxx_phy_mmd_prep(bus, phy_addr, addr, reg);
-       data = bus->read(bus, phy_addr, MII_ATH_MMD_DATA);
-       mutex_unlock(&bus->mdio_lock);
-
-       return data;
-}
-
-static int
-ar8xxx_reg_wait(struct ar8xxx_priv *priv, u32 reg, u32 mask, u32 val,
-               unsigned timeout)
-{
-       int i;
-
-       for (i = 0; i < timeout; i++) {
-               u32 t;
-
-               t = ar8xxx_read(priv, reg);
-               if ((t & mask) == val)
-                       return 0;
-
-               usleep_range(1000, 2000);
-               cond_resched();
-       }
-
-       return -ETIMEDOUT;
-}
-
-static int
-ar8xxx_mib_op(struct ar8xxx_priv *priv, u32 op)
-{
-       unsigned mib_func = priv->chip->mib_func;
-       int ret;
-
-       lockdep_assert_held(&priv->mib_lock);
-
-       /* Capture the hardware statistics for all ports */
-       ar8xxx_rmw(priv, mib_func, AR8216_MIB_FUNC, (op << AR8216_MIB_FUNC_S));
-
-       /* Wait for the capturing to complete. */
-       ret = ar8xxx_reg_wait(priv, mib_func, AR8216_MIB_BUSY, 0, 10);
-       if (ret)
-               goto out;
-
-       ret = 0;
-
-out:
-       return ret;
-}
-
-static int
-ar8xxx_mib_capture(struct ar8xxx_priv *priv)
-{
-       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_CAPTURE);
-}
-
-static int
-ar8xxx_mib_flush(struct ar8xxx_priv *priv)
-{
-       return ar8xxx_mib_op(priv, AR8216_MIB_FUNC_FLUSH);
-}
-
-static void
-ar8xxx_mib_fetch_port_stat(struct ar8xxx_priv *priv, int port, bool flush)
-{
-       unsigned int base;
-       u64 *mib_stats;
-       int i;
-
-       WARN_ON(port >= priv->dev.ports);
-
-       lockdep_assert_held(&priv->mib_lock);
-
-       base = priv->chip->reg_port_stats_start +
-              priv->chip->reg_port_stats_length * port;
-
-       mib_stats = &priv->mib_stats[port * priv->chip->num_mibs];
-       for (i = 0; i < priv->chip->num_mibs; i++) {
-               const struct ar8xxx_mib_desc *mib;
-               u64 t;
-
-               mib = &priv->chip->mib_decs[i];
-               t = ar8xxx_read(priv, base + mib->offset);
-               if (mib->size == 2) {
-                       u64 hi;
-
-                       hi = ar8xxx_read(priv, base + mib->offset + 4);
-                       t |= hi << 32;
-               }
-
-               if (flush)
-                       mib_stats[i] = 0;
-               else
-                       mib_stats[i] += t;
-               cond_resched();
-       }
-}
-
-static void
-ar8216_read_port_link(struct ar8xxx_priv *priv, int port,
-                     struct switch_port_link *link)
-{
-       u32 status;
-       u32 speed;
-
-       memset(link, '\0', sizeof(*link));
-
-       status = priv->chip->read_port_status(priv, port);
-
-       link->aneg = !!(status & AR8216_PORT_STATUS_LINK_AUTO);
-       if (link->aneg) {
-               link->link = !!(status & AR8216_PORT_STATUS_LINK_UP);
-       } else {
-               link->link = true;
-
-               if (priv->get_port_link) {
-                       int err;
-
-                       err = priv->get_port_link(port);
-                       if (err >= 0)
-                               link->link = !!err;
-               }
-       }
-
-       if (!link->link)
-               return;
-
-       link->duplex = !!(status & AR8216_PORT_STATUS_DUPLEX);
-       link->tx_flow = !!(status & AR8216_PORT_STATUS_TXFLOW);
-       link->rx_flow = !!(status & AR8216_PORT_STATUS_RXFLOW);
-
-       if (link->aneg && link->duplex && priv->chip->read_port_eee_status)
-               link->eee = priv->chip->read_port_eee_status(priv, port);
-
-       speed = (status & AR8216_PORT_STATUS_SPEED) >>
-                AR8216_PORT_STATUS_SPEED_S;
-
-       switch (speed) {
-       case AR8216_PORT_SPEED_10M:
-               link->speed = SWITCH_PORT_SPEED_10;
-               break;
-       case AR8216_PORT_SPEED_100M:
-               link->speed = SWITCH_PORT_SPEED_100;
-               break;
-       case AR8216_PORT_SPEED_1000M:
-               link->speed = SWITCH_PORT_SPEED_1000;
-               break;
-       default:
-               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
-               break;
-       }
-}
-
-static struct sk_buff *
-ar8216_mangle_tx(struct net_device *dev, struct sk_buff *skb)
-{
-       struct ar8xxx_priv *priv = dev->phy_ptr;
-       unsigned char *buf;
-
-       if (unlikely(!priv))
-               goto error;
-
-       if (!priv->vlan)
-               goto send;
-
-       if (unlikely(skb_headroom(skb) < 2)) {
-               if (pskb_expand_head(skb, 2, 0, GFP_ATOMIC) < 0)
-                       goto error;
-       }
-
-       buf = skb_push(skb, 2);
-       buf[0] = 0x10;
-       buf[1] = 0x80;
-
-send:
-       return skb;
-
-error:
-       dev_kfree_skb_any(skb);
-       return NULL;
-}
-
-static void
-ar8216_mangle_rx(struct net_device *dev, struct sk_buff *skb)
-{
-       struct ar8xxx_priv *priv;
-       unsigned char *buf;
-       int port, vlan;
-
-       priv = dev->phy_ptr;
-       if (!priv)
-               return;
-
-       /* don't strip the header if vlan mode is disabled */
-       if (!priv->vlan)
-               return;
-
-       /* strip header, get vlan id */
-       buf = skb->data;
-       skb_pull(skb, 2);
-
-       /* check for vlan header presence */
-       if ((buf[12 + 2] != 0x81) || (buf[13 + 2] != 0x00))
-               return;
-
-       port = buf[0] & 0x7;
-
-       /* no need to fix up packets coming from a tagged source */
-       if (priv->vlan_tagged & (1 << port))
-               return;
-
-       /* lookup port vid from local table, the switch passes an invalid vlan id */
-       vlan = priv->vlan_id[priv->pvid[port]];
-
-       buf[14 + 2] &= 0xf0;
-       buf[14 + 2] |= vlan >> 8;
-       buf[15 + 2] = vlan & 0xff;
-}
-
-int
-ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val)
-{
-       int timeout = 20;
-       u32 t = 0;
-
-       while (1) {
-               t = ar8xxx_read(priv, reg);
-               if ((t & mask) == val)
-                       return 0;
-
-               if (timeout-- <= 0)
-                       break;
-
-               udelay(10);
-               cond_resched();
-       }
-
-       pr_err("ar8216: timeout on reg %08x: %08x & %08x != %08x\n",
-              (unsigned int) reg, t, mask, val);
-       return -ETIMEDOUT;
-}
-
-static void
-ar8216_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
-{
-       if (ar8216_wait_bit(priv, AR8216_REG_VTU, AR8216_VTU_ACTIVE, 0))
-               return;
-       if ((op & AR8216_VTU_OP) == AR8216_VTU_OP_LOAD) {
-               val &= AR8216_VTUDATA_MEMBER;
-               val |= AR8216_VTUDATA_VALID;
-               ar8xxx_write(priv, AR8216_REG_VTU_DATA, val);
-       }
-       op |= AR8216_VTU_ACTIVE;
-       ar8xxx_write(priv, AR8216_REG_VTU, op);
-}
-
-static void
-ar8216_vtu_flush(struct ar8xxx_priv *priv)
-{
-       ar8216_vtu_op(priv, AR8216_VTU_OP_FLUSH, 0);
-}
-
-static void
-ar8216_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
-{
-       u32 op;
-
-       op = AR8216_VTU_OP_LOAD | (vid << AR8216_VTU_VID_S);
-       ar8216_vtu_op(priv, op, port_mask);
-}
-
-static int
-ar8216_atu_flush(struct ar8xxx_priv *priv)
-{
-       int ret;
-
-       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
-       if (!ret)
-               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_OP_FLUSH |
-                                                        AR8216_ATU_ACTIVE);
-
-       return ret;
-}
-
-static int
-ar8216_atu_flush_port(struct ar8xxx_priv *priv, int port)
-{
-       u32 t;
-       int ret;
-
-       ret = ar8216_wait_bit(priv, AR8216_REG_ATU_FUNC0, AR8216_ATU_ACTIVE, 0);
-       if (!ret) {
-               t = (port << AR8216_ATU_PORT_NUM_S) | AR8216_ATU_OP_FLUSH_PORT;
-               t |= AR8216_ATU_ACTIVE;
-               ar8xxx_write(priv, AR8216_REG_ATU_FUNC0, t);
-       }
-
-       return ret;
-}
-
-static u32
-ar8216_read_port_status(struct ar8xxx_priv *priv, int port)
-{
-       return ar8xxx_read(priv, AR8216_REG_PORT_STATUS(port));
-}
-
-static void
-ar8216_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
-{
-       u32 header;
-       u32 egress, ingress;
-       u32 pvid;
-
-       if (priv->vlan) {
-               pvid = priv->vlan_id[priv->pvid[port]];
-               if (priv->vlan_tagged & (1 << port))
-                       egress = AR8216_OUT_ADD_VLAN;
-               else
-                       egress = AR8216_OUT_STRIP_VLAN;
-               ingress = AR8216_IN_SECURE;
-       } else {
-               pvid = port;
-               egress = AR8216_OUT_KEEP;
-               ingress = AR8216_IN_PORT_ONLY;
-       }
-
-       if (chip_is_ar8216(priv) && priv->vlan && port == AR8216_PORT_CPU)
-               header = AR8216_PORT_CTRL_HEADER;
-       else
-               header = 0;
-
-       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
-                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
-                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
-                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
-                  AR8216_PORT_CTRL_LEARN | header |
-                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
-                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
-
-       ar8xxx_rmw(priv, AR8216_REG_PORT_VLAN(port),
-                  AR8216_PORT_VLAN_DEST_PORTS | AR8216_PORT_VLAN_MODE |
-                  AR8216_PORT_VLAN_DEFAULT_ID,
-                  (members << AR8216_PORT_VLAN_DEST_PORTS_S) |
-                  (ingress << AR8216_PORT_VLAN_MODE_S) |
-                  (pvid << AR8216_PORT_VLAN_DEFAULT_ID_S));
-}
-
-static int
-ar8216_hw_init(struct ar8xxx_priv *priv)
-{
-       if (priv->initialized)
-               return 0;
-
-       ar8xxx_phy_init(priv);
-
-       priv->initialized = true;
-       return 0;
-}
-
-static void
-ar8216_init_globals(struct ar8xxx_priv *priv)
-{
-       /* standard atheros magic */
-       ar8xxx_write(priv, 0x38, 0xc000050e);
-
-       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
-                  AR8216_GCTRL_MTU, 1518 + 8 + 2);
-}
-
-static void
-ar8216_init_port(struct ar8xxx_priv *priv, int port)
-{
-       /* Enable port learning and tx */
-       ar8xxx_write(priv, AR8216_REG_PORT_CTRL(port),
-               AR8216_PORT_CTRL_LEARN |
-               (4 << AR8216_PORT_CTRL_STATE_S));
-
-       ar8xxx_write(priv, AR8216_REG_PORT_VLAN(port), 0);
-
-       if (port == AR8216_PORT_CPU) {
-               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
-                       AR8216_PORT_STATUS_LINK_UP |
-                       (ar8xxx_has_gige(priv) ?
-                                AR8216_PORT_SPEED_1000M : AR8216_PORT_SPEED_100M) |
-                       AR8216_PORT_STATUS_TXMAC |
-                       AR8216_PORT_STATUS_RXMAC |
-                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_RXFLOW : 0) |
-                       (chip_is_ar8316(priv) ? AR8216_PORT_STATUS_TXFLOW : 0) |
-                       AR8216_PORT_STATUS_DUPLEX);
-       } else {
-               ar8xxx_write(priv, AR8216_REG_PORT_STATUS(port),
-                       AR8216_PORT_STATUS_LINK_AUTO);
-       }
-}
-
-static void
-ar8216_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
-{
-       int timeout = 20;
-
-       while (ar8xxx_mii_read32(priv, r2, r1) & AR8216_ATU_ACTIVE && --timeout) {
-               udelay(10);
-               cond_resched();
-       }
-
-       if (!timeout)
-               pr_err("ar8216: timeout waiting for atu to become ready\n");
-}
-
-static void ar8216_get_arl_entry(struct ar8xxx_priv *priv,
-                                struct arl_entry *a, u32 *status, enum arl_op op)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 r2, page;
-       u16 r1_func0, r1_func1, r1_func2;
-       u32 t, val0, val1, val2;
-       int i;
-
-       split_addr(AR8216_REG_ATU_FUNC0, &r1_func0, &r2, &page);
-       r2 |= 0x10;
-
-       r1_func1 = (AR8216_REG_ATU_FUNC1 >> 1) & 0x1e;
-       r1_func2 = (AR8216_REG_ATU_FUNC2 >> 1) & 0x1e;
-
-       switch (op) {
-       case AR8XXX_ARL_INITIALIZE:
-               /* all ATU registers are on the same page
-               * therefore set page only once
-               */
-               bus->write(bus, 0x18, 0, page);
-               wait_for_page_switch();
-
-               ar8216_wait_atu_ready(priv, r2, r1_func0);
-
-               ar8xxx_mii_write32(priv, r2, r1_func0, AR8216_ATU_OP_GET_NEXT);
-               ar8xxx_mii_write32(priv, r2, r1_func1, 0);
-               ar8xxx_mii_write32(priv, r2, r1_func2, 0);
-               break;
-       case AR8XXX_ARL_GET_NEXT:
-               t = ar8xxx_mii_read32(priv, r2, r1_func0);
-               t |= AR8216_ATU_ACTIVE;
-               ar8xxx_mii_write32(priv, r2, r1_func0, t);
-               ar8216_wait_atu_ready(priv, r2, r1_func0);
-
-               val0 = ar8xxx_mii_read32(priv, r2, r1_func0);
-               val1 = ar8xxx_mii_read32(priv, r2, r1_func1);
-               val2 = ar8xxx_mii_read32(priv, r2, r1_func2);
-
-               *status = (val2 & AR8216_ATU_STATUS) >> AR8216_ATU_STATUS_S;
-               if (!*status)
-                       break;
-
-               i = 0;
-               t = AR8216_ATU_PORT0;
-               while (!(val2 & t) && ++i < priv->dev.ports)
-                       t <<= 1;
-
-               a->port = i;
-               a->mac[0] = (val0 & AR8216_ATU_ADDR5) >> AR8216_ATU_ADDR5_S;
-               a->mac[1] = (val0 & AR8216_ATU_ADDR4) >> AR8216_ATU_ADDR4_S;
-               a->mac[2] = (val1 & AR8216_ATU_ADDR3) >> AR8216_ATU_ADDR3_S;
-               a->mac[3] = (val1 & AR8216_ATU_ADDR2) >> AR8216_ATU_ADDR2_S;
-               a->mac[4] = (val1 & AR8216_ATU_ADDR1) >> AR8216_ATU_ADDR1_S;
-               a->mac[5] = (val1 & AR8216_ATU_ADDR0) >> AR8216_ATU_ADDR0_S;
-               break;
-       }
-}
-
-static void
-ar8236_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
-{
-       u32 egress, ingress;
-       u32 pvid;
-
-       if (priv->vlan) {
-               pvid = priv->vlan_id[priv->pvid[port]];
-               if (priv->vlan_tagged & (1 << port))
-                       egress = AR8216_OUT_ADD_VLAN;
-               else
-                       egress = AR8216_OUT_STRIP_VLAN;
-               ingress = AR8216_IN_SECURE;
-       } else {
-               pvid = port;
-               egress = AR8216_OUT_KEEP;
-               ingress = AR8216_IN_PORT_ONLY;
-       }
-
-       ar8xxx_rmw(priv, AR8216_REG_PORT_CTRL(port),
-                  AR8216_PORT_CTRL_LEARN | AR8216_PORT_CTRL_VLAN_MODE |
-                  AR8216_PORT_CTRL_SINGLE_VLAN | AR8216_PORT_CTRL_STATE |
-                  AR8216_PORT_CTRL_HEADER | AR8216_PORT_CTRL_LEARN_LOCK,
-                  AR8216_PORT_CTRL_LEARN |
-                  (egress << AR8216_PORT_CTRL_VLAN_MODE_S) |
-                  (AR8216_PORT_STATE_FORWARD << AR8216_PORT_CTRL_STATE_S));
-
-       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN(port),
-                  AR8236_PORT_VLAN_DEFAULT_ID,
-                  (pvid << AR8236_PORT_VLAN_DEFAULT_ID_S));
-
-       ar8xxx_rmw(priv, AR8236_REG_PORT_VLAN2(port),
-                  AR8236_PORT_VLAN2_VLAN_MODE |
-                  AR8236_PORT_VLAN2_MEMBER,
-                  (ingress << AR8236_PORT_VLAN2_VLAN_MODE_S) |
-                  (members << AR8236_PORT_VLAN2_MEMBER_S));
-}
-
-static void
-ar8236_init_globals(struct ar8xxx_priv *priv)
-{
-       /* enable jumbo frames */
-       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
-                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
-
-       /* enable cpu port to receive arp frames */
-       ar8xxx_reg_set(priv, AR8216_REG_ATU_CTRL,
-                  AR8236_ATU_CTRL_RES);
-
-       /* enable cpu port to receive multicast and broadcast frames */
-       ar8xxx_reg_set(priv, AR8216_REG_FLOOD_MASK,
-                  AR8236_FM_CPU_BROADCAST_EN | AR8236_FM_CPU_BCAST_FWD_EN);
-
-       /* Enable MIB counters */
-       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
-                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
-                  AR8236_MIB_EN);
-}
-
-static int
-ar8316_hw_init(struct ar8xxx_priv *priv)
-{
-       u32 val, newval;
-
-       val = ar8xxx_read(priv, AR8316_REG_POSTRIP);
-
-       if (priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
-               if (priv->port4_phy) {
-                       /* value taken from Ubiquiti RouterStation Pro */
-                       newval = 0x81461bea;
-                       pr_info("ar8316: Using port 4 as PHY\n");
-               } else {
-                       newval = 0x01261be2;
-                       pr_info("ar8316: Using port 4 as switch port\n");
-               }
-       } else if (priv->phy->interface == PHY_INTERFACE_MODE_GMII) {
-               /* value taken from AVM Fritz!Box 7390 sources */
-               newval = 0x010e5b71;
-       } else {
-               /* no known value for phy interface */
-               pr_err("ar8316: unsupported mii mode: %d.\n",
-                      priv->phy->interface);
-               return -EINVAL;
-       }
-
-       if (val == newval)
-               goto out;
-
-       ar8xxx_write(priv, AR8316_REG_POSTRIP, newval);
-
-       if (priv->port4_phy &&
-           priv->phy->interface == PHY_INTERFACE_MODE_RGMII) {
-               /* work around for phy4 rgmii mode */
-               ar8xxx_phy_dbg_write(priv, 4, 0x12, 0x480c);
-               /* rx delay */
-               ar8xxx_phy_dbg_write(priv, 4, 0x0, 0x824e);
-               /* tx delay */
-               ar8xxx_phy_dbg_write(priv, 4, 0x5, 0x3d47);
-               msleep(1000);
-       }
-
-       ar8xxx_phy_init(priv);
-
-out:
-       priv->initialized = true;
-       return 0;
-}
-
-static void
-ar8316_init_globals(struct ar8xxx_priv *priv)
-{
-       /* standard atheros magic */
-       ar8xxx_write(priv, 0x38, 0xc000050e);
-
-       /* enable cpu port to receive multicast and broadcast frames */
-       ar8xxx_write(priv, AR8216_REG_FLOOD_MASK, 0x003f003f);
-
-       /* enable jumbo frames */
-       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CTRL,
-                  AR8316_GCTRL_MTU, 9018 + 8 + 2);
-
-       /* Enable MIB counters */
-       ar8xxx_rmw(priv, AR8216_REG_MIB_FUNC, AR8216_MIB_FUNC | AR8236_MIB_EN,
-                  (AR8216_MIB_FUNC_NO_OP << AR8216_MIB_FUNC_S) |
-                  AR8236_MIB_EN);
-}
-
-int
-ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                  struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       priv->vlan = !!val->value.i;
-       return 0;
-}
-
-int
-ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                  struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->vlan;
-       return 0;
-}
-
-
-int
-ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       /* make sure no invalid PVIDs get set */
-
-       if (vlan < 0 || vlan >= dev->vlans ||
-           port < 0 || port >= AR8X16_MAX_PORTS)
-               return -EINVAL;
-
-       priv->pvid[port] = vlan;
-       return 0;
-}
-
-int
-ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       if (port < 0 || port >= AR8X16_MAX_PORTS)
-               return -EINVAL;
-
-       *vlan = priv->pvid[port];
-       return 0;
-}
-
-static int
-ar8xxx_sw_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
-                 struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       if (val->port_vlan >= AR8X16_MAX_VLANS)
-               return -EINVAL;
-
-       priv->vlan_id[val->port_vlan] = val->value.i;
-       return 0;
-}
-
-static int
-ar8xxx_sw_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
-                 struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->vlan_id[val->port_vlan];
-       return 0;
-}
-
-int
-ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
-                       struct switch_port_link *link)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       ar8216_read_port_link(priv, port, link);
-       return 0;
-}
-
-static int
-ar8xxx_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       u8 ports;
-       int i;
-
-       if (val->port_vlan >= AR8X16_MAX_VLANS)
-               return -EINVAL;
-
-       ports = priv->vlan_table[val->port_vlan];
-       val->len = 0;
-       for (i = 0; i < dev->ports; i++) {
-               struct switch_port *p;
-
-               if (!(ports & (1 << i)))
-                       continue;
-
-               p = &val->value.ports[val->len++];
-               p->id = i;
-               if (priv->vlan_tagged & (1 << i))
-                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
-               else
-                       p->flags = 0;
-       }
-       return 0;
-}
-
-static int
-ar8xxx_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       u8 *vt = &priv->vlan_table[val->port_vlan];
-       int i, j;
-
-       *vt = 0;
-       for (i = 0; i < val->len; i++) {
-               struct switch_port *p = &val->value.ports[i];
-
-               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
-                       priv->vlan_tagged |= (1 << p->id);
-               } else {
-                       priv->vlan_tagged &= ~(1 << p->id);
-                       priv->pvid[p->id] = val->port_vlan;
-
-                       /* make sure that an untagged port does not
-                        * appear in other vlans */
-                       for (j = 0; j < AR8X16_MAX_VLANS; j++) {
-                               if (j == val->port_vlan)
-                                       continue;
-                               priv->vlan_table[j] &= ~(1 << p->id);
-                       }
-               }
-
-               *vt |= 1 << p->id;
-       }
-       return 0;
-}
-
-static void
-ar8216_set_mirror_regs(struct ar8xxx_priv *priv)
-{
-       int port;
-
-       /* reset all mirror registers */
-       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
-                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
-                  (0xF << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
-       for (port = 0; port < AR8216_NUM_PORTS; port++) {
-               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
-                          AR8216_PORT_CTRL_MIRROR_RX);
-
-               ar8xxx_reg_clear(priv, AR8216_REG_PORT_CTRL(port),
-                          AR8216_PORT_CTRL_MIRROR_TX);
-       }
-
-       /* now enable mirroring if necessary */
-       if (priv->source_port >= AR8216_NUM_PORTS ||
-           priv->monitor_port >= AR8216_NUM_PORTS ||
-           priv->source_port == priv->monitor_port) {
-               return;
-       }
-
-       ar8xxx_rmw(priv, AR8216_REG_GLOBAL_CPUPORT,
-                  AR8216_GLOBAL_CPUPORT_MIRROR_PORT,
-                  (priv->monitor_port << AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S));
-
-       if (priv->mirror_rx)
-               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
-                          AR8216_PORT_CTRL_MIRROR_RX);
-
-       if (priv->mirror_tx)
-               ar8xxx_reg_set(priv, AR8216_REG_PORT_CTRL(priv->source_port),
-                          AR8216_PORT_CTRL_MIRROR_TX);
-}
-
-static inline u32
-ar8xxx_age_time_val(int age_time)
-{
-       return (age_time + AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS / 2) /
-              AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS;
-}
-
-static inline void
-ar8xxx_set_age_time(struct ar8xxx_priv *priv, int reg)
-{
-       u32 age_time = ar8xxx_age_time_val(priv->arl_age_time);
-       ar8xxx_rmw(priv, reg, AR8216_ATU_CTRL_AGE_TIME, age_time << AR8216_ATU_CTRL_AGE_TIME_S);
-}
-
-int
-ar8xxx_sw_hw_apply(struct switch_dev *dev)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       const struct ar8xxx_chip *chip = priv->chip;
-       u8 portmask[AR8X16_MAX_PORTS];
-       int i, j;
-
-       mutex_lock(&priv->reg_mutex);
-       /* flush all vlan translation unit entries */
-       priv->chip->vtu_flush(priv);
-
-       memset(portmask, 0, sizeof(portmask));
-       if (!priv->init) {
-               /* calculate the port destination masks and load vlans
-                * into the vlan translation unit */
-               for (j = 0; j < AR8X16_MAX_VLANS; j++) {
-                       u8 vp = priv->vlan_table[j];
-
-                       if (!vp)
-                               continue;
-
-                       for (i = 0; i < dev->ports; i++) {
-                               u8 mask = (1 << i);
-                               if (vp & mask)
-                                       portmask[i] |= vp & ~mask;
-                       }
-
-                       chip->vtu_load_vlan(priv, priv->vlan_id[j],
-                                           priv->vlan_table[j]);
-               }
-       } else {
-               /* vlan disabled:
-                * isolate all ports, but connect them to the cpu port */
-               for (i = 0; i < dev->ports; i++) {
-                       if (i == AR8216_PORT_CPU)
-                               continue;
-
-                       portmask[i] = 1 << AR8216_PORT_CPU;
-                       portmask[AR8216_PORT_CPU] |= (1 << i);
-               }
-       }
-
-       /* update the port destination mask registers and tag settings */
-       for (i = 0; i < dev->ports; i++) {
-               chip->setup_port(priv, i, portmask[i]);
-       }
-
-       chip->set_mirror_regs(priv);
-
-       /* set age time */
-       if (chip->reg_arl_ctrl)
-               ar8xxx_set_age_time(priv, chip->reg_arl_ctrl);
-
-       mutex_unlock(&priv->reg_mutex);
-       return 0;
-}
-
-int
-ar8xxx_sw_reset_switch(struct switch_dev *dev)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       const struct ar8xxx_chip *chip = priv->chip;
-       int i;
-
-       mutex_lock(&priv->reg_mutex);
-       memset(&priv->vlan, 0, sizeof(struct ar8xxx_priv) -
-               offsetof(struct ar8xxx_priv, vlan));
-
-       for (i = 0; i < AR8X16_MAX_VLANS; i++)
-               priv->vlan_id[i] = i;
-
-       /* Configure all ports */
-       for (i = 0; i < dev->ports; i++)
-               chip->init_port(priv, i);
-
-       priv->mirror_rx = false;
-       priv->mirror_tx = false;
-       priv->source_port = 0;
-       priv->monitor_port = 0;
-       priv->arl_age_time = AR8XXX_DEFAULT_ARL_AGE_TIME;
-
-       chip->init_globals(priv);
-       chip->atu_flush(priv);
-
-       mutex_unlock(&priv->reg_mutex);
-
-       return chip->sw_hw_apply(dev);
-}
-
-int
-ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
-                        const struct switch_attr *attr,
-                        struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       unsigned int len;
-       int ret;
-
-       if (!ar8xxx_has_mib_counters(priv))
-               return -EOPNOTSUPP;
-
-       mutex_lock(&priv->mib_lock);
-
-       len = priv->dev.ports * priv->chip->num_mibs *
-             sizeof(*priv->mib_stats);
-       memset(priv->mib_stats, '\0', len);
-       ret = ar8xxx_mib_flush(priv);
-       if (ret)
-               goto unlock;
-
-       ret = 0;
-
-unlock:
-       mutex_unlock(&priv->mib_lock);
-       return ret;
-}
-
-int
-ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       mutex_lock(&priv->reg_mutex);
-       priv->mirror_rx = !!val->value.i;
-       priv->chip->set_mirror_regs(priv);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->mirror_rx;
-       return 0;
-}
-
-int
-ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       mutex_lock(&priv->reg_mutex);
-       priv->mirror_tx = !!val->value.i;
-       priv->chip->set_mirror_regs(priv);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->mirror_tx;
-       return 0;
-}
-
-int
-ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       mutex_lock(&priv->reg_mutex);
-       priv->monitor_port = val->value.i;
-       priv->chip->set_mirror_regs(priv);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->monitor_port;
-       return 0;
-}
-
-int
-ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       mutex_lock(&priv->reg_mutex);
-       priv->source_port = val->value.i;
-       priv->chip->set_mirror_regs(priv);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->source_port;
-       return 0;
-}
-
-int
-ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
-                            const struct switch_attr *attr,
-                            struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int port;
-       int ret;
-
-       if (!ar8xxx_has_mib_counters(priv))
-               return -EOPNOTSUPP;
-
-       port = val->port_vlan;
-       if (port >= dev->ports)
-               return -EINVAL;
-
-       mutex_lock(&priv->mib_lock);
-       ret = ar8xxx_mib_capture(priv);
-       if (ret)
-               goto unlock;
-
-       ar8xxx_mib_fetch_port_stat(priv, port, true);
-
-       ret = 0;
-
-unlock:
-       mutex_unlock(&priv->mib_lock);
-       return ret;
-}
-
-static void
-ar8xxx_byte_to_str(char *buf, int len, u64 byte)
-{
-       unsigned long b;
-       const char *unit;
-
-       if (byte >= 0x40000000) { /* 1 GiB */
-               b = byte * 10 / 0x40000000;
-               unit = "GiB";
-       } else if (byte >= 0x100000) { /* 1 MiB */
-               b = byte * 10 / 0x100000;
-               unit = "MiB";
-       } else if (byte >= 0x400) { /* 1 KiB */
-               b = byte * 10 / 0x400;
-               unit = "KiB";
-       } else {
-               b = byte;
-               unit = "Byte";
-       }
-       if (strcmp(unit, "Byte"))
-               snprintf(buf, len, "%lu.%lu %s", b / 10, b % 10, unit);
-       else
-               snprintf(buf, len, "%lu %s", b, unit);
-}
-
-int
-ar8xxx_sw_get_port_mib(struct switch_dev *dev,
-                      const struct switch_attr *attr,
-                      struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       const struct ar8xxx_chip *chip = priv->chip;
-       u64 *mib_stats, mib_data;
-       unsigned int port;
-       int ret;
-       char *buf = priv->buf;
-       char buf1[64];
-       const char *mib_name;
-       int i, len = 0;
-       bool mib_stats_empty = true;
-
-       if (!ar8xxx_has_mib_counters(priv))
-               return -EOPNOTSUPP;
-
-       port = val->port_vlan;
-       if (port >= dev->ports)
-               return -EINVAL;
-
-       mutex_lock(&priv->mib_lock);
-       ret = ar8xxx_mib_capture(priv);
-       if (ret)
-               goto unlock;
-
-       ar8xxx_mib_fetch_port_stat(priv, port, false);
-
-       len += snprintf(buf + len, sizeof(priv->buf) - len,
-                       "MIB counters\n");
-
-       mib_stats = &priv->mib_stats[port * chip->num_mibs];
-       for (i = 0; i < chip->num_mibs; i++) {
-               mib_name = chip->mib_decs[i].name;
-               mib_data = mib_stats[i];
-               len += snprintf(buf + len, sizeof(priv->buf) - len,
-                               "%-12s: %llu\n", mib_name, mib_data);
-               if ((!strcmp(mib_name, "TxByte") ||
-                   !strcmp(mib_name, "RxGoodByte")) &&
-                   mib_data >= 1024) {
-                       ar8xxx_byte_to_str(buf1, sizeof(buf1), mib_data);
-                       --len; /* discard newline at the end of buf */
-                       len += snprintf(buf + len, sizeof(priv->buf) - len,
-                                       " (%s)\n", buf1);
-               }
-               if (mib_stats_empty && mib_data)
-                       mib_stats_empty = false;
-       }
-
-       if (mib_stats_empty)
-               len = snprintf(buf, sizeof(priv->buf), "No MIB data");
-
-       val->value.s = buf;
-       val->len = len;
-
-       ret = 0;
-
-unlock:
-       mutex_unlock(&priv->mib_lock);
-       return ret;
-}
-
-int
-ar8xxx_sw_set_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
-                          struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int age_time = val->value.i;
-       u32 age_time_val;
-
-       if (age_time < 0)
-               return -EINVAL;
-
-       age_time_val = ar8xxx_age_time_val(age_time);
-       if (age_time_val == 0 || age_time_val > 0xffff)
-               return -EINVAL;
-
-       priv->arl_age_time = age_time;
-       return 0;
-}
-
-int
-ar8xxx_sw_get_arl_age_time(struct switch_dev *dev, const struct switch_attr *attr,
-                   struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       val->value.i = priv->arl_age_time;
-       return 0;
-}
-
-int
-ar8xxx_sw_get_arl_table(struct switch_dev *dev,
-                       const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       struct mii_bus *bus = priv->mii_bus;
-       const struct ar8xxx_chip *chip = priv->chip;
-       char *buf = priv->arl_buf;
-       int i, j, k, len = 0;
-       struct arl_entry *a, *a1;
-       u32 status;
-
-       if (!chip->get_arl_entry)
-               return -EOPNOTSUPP;
-
-       mutex_lock(&priv->reg_mutex);
-       mutex_lock(&bus->mdio_lock);
-
-       chip->get_arl_entry(priv, NULL, NULL, AR8XXX_ARL_INITIALIZE);
-
-       for(i = 0; i < AR8XXX_NUM_ARL_RECORDS; ++i) {
-               a = &priv->arl_table[i];
-               duplicate:
-               chip->get_arl_entry(priv, a, &status, AR8XXX_ARL_GET_NEXT);
-
-               if (!status)
-                       break;
-
-               /* avoid duplicates
-                * ARL table can include multiple valid entries
-                * per MAC, just with differing status codes
-                */
-               for (j = 0; j < i; ++j) {
-                       a1 = &priv->arl_table[j];
-                       if (a->port == a1->port && !memcmp(a->mac, a1->mac, sizeof(a->mac)))
-                               goto duplicate;
-               }
-       }
-
-       mutex_unlock(&bus->mdio_lock);
-
-       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
-                        "address resolution table\n");
-
-       if (i == AR8XXX_NUM_ARL_RECORDS)
-               len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
-                               "Too many entries found, displaying the first %d only!\n",
-                               AR8XXX_NUM_ARL_RECORDS);
-
-       for (j = 0; j < priv->dev.ports; ++j) {
-               for (k = 0; k < i; ++k) {
-                       a = &priv->arl_table[k];
-                       if (a->port != j)
-                               continue;
-                       len += snprintf(buf + len, sizeof(priv->arl_buf) - len,
-                                       "Port %d: MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
-                                       j,
-                                       a->mac[5], a->mac[4], a->mac[3],
-                                       a->mac[2], a->mac[1], a->mac[0]);
-               }
-       }
-
-       val->value.s = buf;
-       val->len = len;
-
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
-                             const struct switch_attr *attr,
-                             struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int ret;
-
-       mutex_lock(&priv->reg_mutex);
-       ret = priv->chip->atu_flush(priv);
-       mutex_unlock(&priv->reg_mutex);
-
-       return ret;
-}
-
-int
-ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
-                                  const struct switch_attr *attr,
-                                  struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int port, ret;
-
-       port = val->port_vlan;
-       if (port >= dev->ports)
-               return -EINVAL;
-
-       mutex_lock(&priv->reg_mutex);
-       ret = priv->chip->atu_flush_port(priv, port);
-       mutex_unlock(&priv->reg_mutex);
-
-       return ret;
-}
-
-static const struct switch_attr ar8xxx_sw_attr_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = ar8xxx_sw_set_vlan,
-               .get = ar8xxx_sw_get_vlan,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mibs",
-               .description = "Reset all MIB counters",
-               .set = ar8xxx_sw_set_reset_mibs,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_rx",
-               .description = "Enable mirroring of RX packets",
-               .set = ar8xxx_sw_set_mirror_rx_enable,
-               .get = ar8xxx_sw_get_mirror_rx_enable,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_tx",
-               .description = "Enable mirroring of TX packets",
-               .set = ar8xxx_sw_set_mirror_tx_enable,
-               .get = ar8xxx_sw_get_mirror_tx_enable,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "mirror_monitor_port",
-               .description = "Mirror monitor port",
-               .set = ar8xxx_sw_set_mirror_monitor_port,
-               .get = ar8xxx_sw_get_mirror_monitor_port,
-               .max = AR8216_NUM_PORTS - 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "mirror_source_port",
-               .description = "Mirror source port",
-               .set = ar8xxx_sw_set_mirror_source_port,
-               .get = ar8xxx_sw_get_mirror_source_port,
-               .max = AR8216_NUM_PORTS - 1
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "arl_table",
-               .description = "Get ARL table",
-               .set = NULL,
-               .get = ar8xxx_sw_get_arl_table,
-       },
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "flush_arl_table",
-               .description = "Flush ARL table",
-               .set = ar8xxx_sw_set_flush_arl_table,
-       },
-};
-
-const struct switch_attr ar8xxx_sw_attr_port[] = {
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mib",
-               .description = "Reset single port MIB counters",
-               .set = ar8xxx_sw_set_port_reset_mib,
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get port's MIB counters",
-               .set = NULL,
-               .get = ar8xxx_sw_get_port_mib,
-       },
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "flush_arl_table",
-               .description = "Flush port's ARL table entries",
-               .set = ar8xxx_sw_set_flush_port_arl_table,
-       },
-};
-
-const struct switch_attr ar8xxx_sw_attr_vlan[1] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "vid",
-               .description = "VLAN ID (0-4094)",
-               .set = ar8xxx_sw_set_vid,
-               .get = ar8xxx_sw_get_vid,
-               .max = 4094,
-       },
-};
-
-static const struct switch_dev_ops ar8xxx_sw_ops = {
-       .attr_global = {
-               .attr = ar8xxx_sw_attr_globals,
-               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_globals),
-       },
-       .attr_port = {
-               .attr = ar8xxx_sw_attr_port,
-               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_port),
-       },
-       .attr_vlan = {
-               .attr = ar8xxx_sw_attr_vlan,
-               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
-       },
-       .get_port_pvid = ar8xxx_sw_get_pvid,
-       .set_port_pvid = ar8xxx_sw_set_pvid,
-       .get_vlan_ports = ar8xxx_sw_get_ports,
-       .set_vlan_ports = ar8xxx_sw_set_ports,
-       .apply_config = ar8xxx_sw_hw_apply,
-       .reset_switch = ar8xxx_sw_reset_switch,
-       .get_port_link = ar8xxx_sw_get_port_link,
-/* The following op is disabled as it hogs the CPU and degrades performance.
-   An implementation has been attempted in 4d8a66d but reading MIB data is slow
-   on ar8xxx switches.
-
-   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
-   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
-   the request to update the MIB counter is cleared. */
-#if 0
-       .get_port_stats = ar8xxx_sw_get_port_stats,
-#endif
-};
-
-static const struct ar8xxx_chip ar8216_chip = {
-       .caps = AR8XXX_CAP_MIB_COUNTERS,
-
-       .reg_port_stats_start = 0x19000,
-       .reg_port_stats_length = 0xa0,
-       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
-
-       .name = "Atheros AR8216",
-       .ports = AR8216_NUM_PORTS,
-       .vlans = AR8216_NUM_VLANS,
-       .swops = &ar8xxx_sw_ops,
-
-       .hw_init = ar8216_hw_init,
-       .init_globals = ar8216_init_globals,
-       .init_port = ar8216_init_port,
-       .setup_port = ar8216_setup_port,
-       .read_port_status = ar8216_read_port_status,
-       .atu_flush = ar8216_atu_flush,
-       .atu_flush_port = ar8216_atu_flush_port,
-       .vtu_flush = ar8216_vtu_flush,
-       .vtu_load_vlan = ar8216_vtu_load_vlan,
-       .set_mirror_regs = ar8216_set_mirror_regs,
-       .get_arl_entry = ar8216_get_arl_entry,
-       .sw_hw_apply = ar8xxx_sw_hw_apply,
-
-       .num_mibs = ARRAY_SIZE(ar8216_mibs),
-       .mib_decs = ar8216_mibs,
-       .mib_func = AR8216_REG_MIB_FUNC
-};
-
-static const struct ar8xxx_chip ar8236_chip = {
-       .caps = AR8XXX_CAP_MIB_COUNTERS,
-
-       .reg_port_stats_start = 0x20000,
-       .reg_port_stats_length = 0x100,
-       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
-
-       .name = "Atheros AR8236",
-       .ports = AR8216_NUM_PORTS,
-       .vlans = AR8216_NUM_VLANS,
-       .swops = &ar8xxx_sw_ops,
-
-       .hw_init = ar8216_hw_init,
-       .init_globals = ar8236_init_globals,
-       .init_port = ar8216_init_port,
-       .setup_port = ar8236_setup_port,
-       .read_port_status = ar8216_read_port_status,
-       .atu_flush = ar8216_atu_flush,
-       .atu_flush_port = ar8216_atu_flush_port,
-       .vtu_flush = ar8216_vtu_flush,
-       .vtu_load_vlan = ar8216_vtu_load_vlan,
-       .set_mirror_regs = ar8216_set_mirror_regs,
-       .get_arl_entry = ar8216_get_arl_entry,
-       .sw_hw_apply = ar8xxx_sw_hw_apply,
-
-       .num_mibs = ARRAY_SIZE(ar8236_mibs),
-       .mib_decs = ar8236_mibs,
-       .mib_func = AR8216_REG_MIB_FUNC
-};
-
-static const struct ar8xxx_chip ar8316_chip = {
-       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
-
-       .reg_port_stats_start = 0x20000,
-       .reg_port_stats_length = 0x100,
-       .reg_arl_ctrl = AR8216_REG_ATU_CTRL,
-
-       .name = "Atheros AR8316",
-       .ports = AR8216_NUM_PORTS,
-       .vlans = AR8X16_MAX_VLANS,
-       .swops = &ar8xxx_sw_ops,
-
-       .hw_init = ar8316_hw_init,
-       .init_globals = ar8316_init_globals,
-       .init_port = ar8216_init_port,
-       .setup_port = ar8216_setup_port,
-       .read_port_status = ar8216_read_port_status,
-       .atu_flush = ar8216_atu_flush,
-       .atu_flush_port = ar8216_atu_flush_port,
-       .vtu_flush = ar8216_vtu_flush,
-       .vtu_load_vlan = ar8216_vtu_load_vlan,
-       .set_mirror_regs = ar8216_set_mirror_regs,
-       .get_arl_entry = ar8216_get_arl_entry,
-       .sw_hw_apply = ar8xxx_sw_hw_apply,
-
-       .num_mibs = ARRAY_SIZE(ar8236_mibs),
-       .mib_decs = ar8236_mibs,
-       .mib_func = AR8216_REG_MIB_FUNC
-};
-
-static int
-ar8xxx_id_chip(struct ar8xxx_priv *priv)
-{
-       u32 val;
-       u16 id;
-       int i;
-
-       val = ar8xxx_read(priv, AR8216_REG_CTRL);
-       if (val == ~0)
-               return -ENODEV;
-
-       id = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
-       for (i = 0; i < AR8X16_PROBE_RETRIES; i++) {
-               u16 t;
-
-               val = ar8xxx_read(priv, AR8216_REG_CTRL);
-               if (val == ~0)
-                       return -ENODEV;
-
-               t = val & (AR8216_CTRL_REVISION | AR8216_CTRL_VERSION);
-               if (t != id)
-                       return -ENODEV;
-       }
-
-       priv->chip_ver = (id & AR8216_CTRL_VERSION) >> AR8216_CTRL_VERSION_S;
-       priv->chip_rev = (id & AR8216_CTRL_REVISION);
-
-       switch (priv->chip_ver) {
-       case AR8XXX_VER_AR8216:
-               priv->chip = &ar8216_chip;
-               break;
-       case AR8XXX_VER_AR8236:
-               priv->chip = &ar8236_chip;
-               break;
-       case AR8XXX_VER_AR8316:
-               priv->chip = &ar8316_chip;
-               break;
-       case AR8XXX_VER_AR8327:
-               priv->chip = &ar8327_chip;
-               break;
-       case AR8XXX_VER_AR8337:
-               priv->chip = &ar8337_chip;
-               break;
-       default:
-               pr_err("ar8216: Unknown Atheros device [ver=%d, rev=%d]\n",
-                      priv->chip_ver, priv->chip_rev);
-
-               return -ENODEV;
-       }
-
-       return 0;
-}
-
-static void
-ar8xxx_mib_work_func(struct work_struct *work)
-{
-       struct ar8xxx_priv *priv;
-       int err;
-
-       priv = container_of(work, struct ar8xxx_priv, mib_work.work);
-
-       mutex_lock(&priv->mib_lock);
-
-       err = ar8xxx_mib_capture(priv);
-       if (err)
-               goto next_port;
-
-       ar8xxx_mib_fetch_port_stat(priv, priv->mib_next_port, false);
-
-next_port:
-       priv->mib_next_port++;
-       if (priv->mib_next_port >= priv->dev.ports)
-               priv->mib_next_port = 0;
-
-       mutex_unlock(&priv->mib_lock);
-       schedule_delayed_work(&priv->mib_work,
-                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
-}
-
-static int
-ar8xxx_mib_init(struct ar8xxx_priv *priv)
-{
-       unsigned int len;
-
-       if (!ar8xxx_has_mib_counters(priv))
-               return 0;
-
-       BUG_ON(!priv->chip->mib_decs || !priv->chip->num_mibs);
-
-       len = priv->dev.ports * priv->chip->num_mibs *
-             sizeof(*priv->mib_stats);
-       priv->mib_stats = kzalloc(len, GFP_KERNEL);
-
-       if (!priv->mib_stats)
-               return -ENOMEM;
-
-       return 0;
-}
-
-static void
-ar8xxx_mib_start(struct ar8xxx_priv *priv)
-{
-       if (!ar8xxx_has_mib_counters(priv))
-               return;
-
-       schedule_delayed_work(&priv->mib_work,
-                             msecs_to_jiffies(AR8XXX_MIB_WORK_DELAY));
-}
-
-static void
-ar8xxx_mib_stop(struct ar8xxx_priv *priv)
-{
-       if (!ar8xxx_has_mib_counters(priv))
-               return;
-
-       cancel_delayed_work_sync(&priv->mib_work);
-}
-
-static struct ar8xxx_priv *
-ar8xxx_create(void)
-{
-       struct ar8xxx_priv *priv;
-
-       priv = kzalloc(sizeof(struct ar8xxx_priv), GFP_KERNEL);
-       if (priv == NULL)
-               return NULL;
-
-       mutex_init(&priv->reg_mutex);
-       mutex_init(&priv->mib_lock);
-       INIT_DELAYED_WORK(&priv->mib_work, ar8xxx_mib_work_func);
-
-       return priv;
-}
-
-static void
-ar8xxx_free(struct ar8xxx_priv *priv)
-{
-       if (priv->chip && priv->chip->cleanup)
-               priv->chip->cleanup(priv);
-
-       kfree(priv->chip_data);
-       kfree(priv->mib_stats);
-       kfree(priv);
-}
-
-static int
-ar8xxx_probe_switch(struct ar8xxx_priv *priv)
-{
-       const struct ar8xxx_chip *chip;
-       struct switch_dev *swdev;
-       int ret;
-
-       ret = ar8xxx_id_chip(priv);
-       if (ret)
-               return ret;
-
-       chip = priv->chip;
-
-       swdev = &priv->dev;
-       swdev->cpu_port = AR8216_PORT_CPU;
-       swdev->name = chip->name;
-       swdev->vlans = chip->vlans;
-       swdev->ports = chip->ports;
-       swdev->ops = chip->swops;
-
-       ret = ar8xxx_mib_init(priv);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int
-ar8xxx_start(struct ar8xxx_priv *priv)
-{
-       int ret;
-
-       priv->init = true;
-
-       ret = priv->chip->hw_init(priv);
-       if (ret)
-               return ret;
-
-       ret = ar8xxx_sw_reset_switch(&priv->dev);
-       if (ret)
-               return ret;
-
-       priv->init = false;
-
-       ar8xxx_mib_start(priv);
-
-       return 0;
-}
-
-static int
-ar8xxx_phy_config_init(struct phy_device *phydev)
-{
-       struct ar8xxx_priv *priv = phydev->priv;
-       struct net_device *dev = phydev->attached_dev;
-       int ret;
-
-       if (WARN_ON(!priv))
-               return -ENODEV;
-
-       if (priv->chip->config_at_probe)
-               return ar8xxx_phy_check_aneg(phydev);
-
-       priv->phy = phydev;
-
-       if (phydev->mdio.addr != 0) {
-               if (chip_is_ar8316(priv)) {
-                       /* switch device has been initialized, reinit */
-                       priv->dev.ports = (AR8216_NUM_PORTS - 1);
-                       priv->initialized = false;
-                       priv->port4_phy = true;
-                       ar8316_hw_init(priv);
-                       return 0;
-               }
-
-               return 0;
-       }
-
-       ret = ar8xxx_start(priv);
-       if (ret)
-               return ret;
-
-       /* VID fixup only needed on ar8216 */
-       if (chip_is_ar8216(priv)) {
-               dev->phy_ptr = priv;
-               dev->priv_flags |= IFF_NO_IP_ALIGN;
-               dev->eth_mangle_rx = ar8216_mangle_rx;
-               dev->eth_mangle_tx = ar8216_mangle_tx;
-       }
-
-       return 0;
-}
-
-static bool
-ar8xxx_check_link_states(struct ar8xxx_priv *priv)
-{
-       bool link_new, changed = false;
-       u32 status;
-       int i;
-
-       mutex_lock(&priv->reg_mutex);
-
-       for (i = 0; i < priv->dev.ports; i++) {
-               status = priv->chip->read_port_status(priv, i);
-               link_new = !!(status & AR8216_PORT_STATUS_LINK_UP);
-               if (link_new == priv->link_up[i])
-                       continue;
-
-               priv->link_up[i] = link_new;
-               changed = true;
-               /* flush ARL entries for this port if it went down*/
-               if (!link_new)
-                       priv->chip->atu_flush_port(priv, i);
-               dev_info(&priv->phy->mdio.dev, "Port %d is %s\n",
-                        i, link_new ? "up" : "down");
-       }
-
-       mutex_unlock(&priv->reg_mutex);
-
-       return changed;
-}
-
-static int
-ar8xxx_phy_read_status(struct phy_device *phydev)
-{
-       struct ar8xxx_priv *priv = phydev->priv;
-       struct switch_port_link link;
-
-       /* check for switch port link changes */
-       if (phydev->state == PHY_CHANGELINK)
-               ar8xxx_check_link_states(priv);
-
-       if (phydev->mdio.addr != 0)
-               return genphy_read_status(phydev);
-
-       ar8216_read_port_link(priv, phydev->mdio.addr, &link);
-       phydev->link = !!link.link;
-       if (!phydev->link)
-               return 0;
-
-       switch (link.speed) {
-       case SWITCH_PORT_SPEED_10:
-               phydev->speed = SPEED_10;
-               break;
-       case SWITCH_PORT_SPEED_100:
-               phydev->speed = SPEED_100;
-               break;
-       case SWITCH_PORT_SPEED_1000:
-               phydev->speed = SPEED_1000;
-               break;
-       default:
-               phydev->speed = 0;
-       }
-       phydev->duplex = link.duplex ? DUPLEX_FULL : DUPLEX_HALF;
-
-       phydev->state = PHY_RUNNING;
-       netif_carrier_on(phydev->attached_dev);
-       phydev->adjust_link(phydev->attached_dev);
-
-       return 0;
-}
-
-static int
-ar8xxx_phy_config_aneg(struct phy_device *phydev)
-{
-       if (phydev->mdio.addr == 0)
-               return 0;
-
-       return genphy_config_aneg(phydev);
-}
-
-static const u32 ar8xxx_phy_ids[] = {
-       0x004dd033,
-       0x004dd034, /* AR8327 */
-       0x004dd036, /* AR8337 */
-       0x004dd041,
-       0x004dd042,
-       0x004dd043, /* AR8236 */
-};
-
-static bool
-ar8xxx_phy_match(u32 phy_id)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(ar8xxx_phy_ids); i++)
-               if (phy_id == ar8xxx_phy_ids[i])
-                       return true;
-
-       return false;
-}
-
-static bool
-ar8xxx_is_possible(struct mii_bus *bus)
-{
-       unsigned int i, found_phys = 0;
-
-       for (i = 0; i < 5; i++) {
-               u32 phy_id;
-
-               phy_id = mdiobus_read(bus, i, MII_PHYSID1) << 16;
-               phy_id |= mdiobus_read(bus, i, MII_PHYSID2);
-               if (ar8xxx_phy_match(phy_id)) {
-                       found_phys++;
-               } else if (phy_id) {
-                       pr_debug("ar8xxx: unknown PHY at %s:%02x id:%08x\n",
-                                dev_name(&bus->dev), i, phy_id);
-               }
-       }
-       return !!found_phys;
-}
-
-static int
-ar8xxx_phy_probe(struct phy_device *phydev)
-{
-       struct ar8xxx_priv *priv;
-       struct switch_dev *swdev;
-       int ret;
-
-       /* skip PHYs at unused adresses */
-       if (phydev->mdio.addr != 0 && phydev->mdio.addr != 4)
-               return -ENODEV;
-
-       if (!ar8xxx_is_possible(phydev->mdio.bus))
-               return -ENODEV;
-
-       mutex_lock(&ar8xxx_dev_list_lock);
-       list_for_each_entry(priv, &ar8xxx_dev_list, list)
-               if (priv->mii_bus == phydev->mdio.bus)
-                       goto found;
-
-       priv = ar8xxx_create();
-       if (priv == NULL) {
-               ret = -ENOMEM;
-               goto unlock;
-       }
-
-       priv->mii_bus = phydev->mdio.bus;
-
-       ret = ar8xxx_probe_switch(priv);
-       if (ret)
-               goto free_priv;
-
-       swdev = &priv->dev;
-       swdev->alias = dev_name(&priv->mii_bus->dev);
-       ret = register_switch(swdev, NULL);
-       if (ret)
-               goto free_priv;
-
-       pr_info("%s: %s rev. %u switch registered on %s\n",
-               swdev->devname, swdev->name, priv->chip_rev,
-               dev_name(&priv->mii_bus->dev));
-
-       list_add(&priv->list, &ar8xxx_dev_list);
-
-found:
-       priv->use_count++;
-
-       if (phydev->mdio.addr == 0) {
-               if (ar8xxx_has_gige(priv)) {
-                       phydev->supported = SUPPORTED_1000baseT_Full;
-                       phydev->advertising = ADVERTISED_1000baseT_Full;
-               } else {
-                       phydev->supported = SUPPORTED_100baseT_Full;
-                       phydev->advertising = ADVERTISED_100baseT_Full;
-               }
-
-               if (priv->chip->config_at_probe) {
-                       priv->phy = phydev;
-
-                       ret = ar8xxx_start(priv);
-                       if (ret)
-                               goto err_unregister_switch;
-               }
-       } else {
-               if (ar8xxx_has_gige(priv)) {
-                       phydev->supported |= SUPPORTED_1000baseT_Full;
-                       phydev->advertising |= ADVERTISED_1000baseT_Full;
-               }
-       }
-
-       phydev->priv = priv;
-
-       mutex_unlock(&ar8xxx_dev_list_lock);
-
-       return 0;
-
-err_unregister_switch:
-       if (--priv->use_count)
-               goto unlock;
-
-       unregister_switch(&priv->dev);
-
-free_priv:
-       ar8xxx_free(priv);
-unlock:
-       mutex_unlock(&ar8xxx_dev_list_lock);
-       return ret;
-}
-
-static void
-ar8xxx_phy_detach(struct phy_device *phydev)
-{
-       struct net_device *dev = phydev->attached_dev;
-
-       if (!dev)
-               return;
-
-       dev->phy_ptr = NULL;
-       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
-       dev->eth_mangle_rx = NULL;
-       dev->eth_mangle_tx = NULL;
-}
-
-static void
-ar8xxx_phy_remove(struct phy_device *phydev)
-{
-       struct ar8xxx_priv *priv = phydev->priv;
-
-       if (WARN_ON(!priv))
-               return;
-
-       phydev->priv = NULL;
-
-       mutex_lock(&ar8xxx_dev_list_lock);
-
-       if (--priv->use_count > 0) {
-               mutex_unlock(&ar8xxx_dev_list_lock);
-               return;
-       }
-
-       list_del(&priv->list);
-       mutex_unlock(&ar8xxx_dev_list_lock);
-
-       unregister_switch(&priv->dev);
-       ar8xxx_mib_stop(priv);
-       ar8xxx_free(priv);
-}
-
-static int
-ar8xxx_phy_soft_reset(struct phy_device *phydev)
-{
-       /* we don't need an extra reset */
-       return 0;
-}
-
-static struct phy_driver ar8xxx_phy_driver[] = {
-       {
-               .phy_id         = 0x004d0000,
-               .name           = "Atheros AR8216/AR8236/AR8316",
-               .phy_id_mask    = 0xffff0000,
-               .features       = PHY_BASIC_FEATURES,
-               .probe          = ar8xxx_phy_probe,
-               .remove         = ar8xxx_phy_remove,
-               .detach         = ar8xxx_phy_detach,
-               .config_init    = ar8xxx_phy_config_init,
-               .config_aneg    = ar8xxx_phy_config_aneg,
-               .read_status    = ar8xxx_phy_read_status,
-               .soft_reset     = ar8xxx_phy_soft_reset,
-       }
-};
-
-module_phy_driver(ar8xxx_phy_driver);
-MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/ar8216.h b/target/linux/generic/files/drivers/net/phy/ar8216.h
deleted file mode 100644 (file)
index ba0e0dd..0000000
+++ /dev/null
@@ -1,644 +0,0 @@
-/*
- * ar8216.h: AR8216 switch driver
- *
- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef __AR8216_H
-#define __AR8216_H
-
-#define BITS(_s, _n)   (((1UL << (_n)) - 1) << _s)
-
-#define AR8XXX_CAP_GIGE                        BIT(0)
-#define AR8XXX_CAP_MIB_COUNTERS                BIT(1)
-
-#define AR8XXX_NUM_PHYS        5
-#define AR8216_PORT_CPU        0
-#define AR8216_NUM_PORTS       6
-#define AR8216_NUM_VLANS       16
-#define AR8316_NUM_VLANS       4096
-
-/* size of the vlan table */
-#define AR8X16_MAX_VLANS       128
-#define AR8X16_PROBE_RETRIES   10
-#define AR8X16_MAX_PORTS       8
-
-#define AR8XXX_REG_ARL_CTRL_AGE_TIME_SECS      7
-#define AR8XXX_DEFAULT_ARL_AGE_TIME            300
-
-/* Atheros specific MII registers */
-#define MII_ATH_MMD_ADDR               0x0d
-#define MII_ATH_MMD_DATA               0x0e
-#define MII_ATH_DBG_ADDR               0x1d
-#define MII_ATH_DBG_DATA               0x1e
-
-#define AR8216_REG_CTRL                        0x0000
-#define   AR8216_CTRL_REVISION         BITS(0, 8)
-#define   AR8216_CTRL_REVISION_S       0
-#define   AR8216_CTRL_VERSION          BITS(8, 8)
-#define   AR8216_CTRL_VERSION_S                8
-#define   AR8216_CTRL_RESET            BIT(31)
-
-#define AR8216_REG_FLOOD_MASK          0x002C
-#define   AR8216_FM_UNI_DEST_PORTS     BITS(0, 6)
-#define   AR8216_FM_MULTI_DEST_PORTS   BITS(16, 6)
-#define   AR8236_FM_CPU_BROADCAST_EN   BIT(26)
-#define   AR8236_FM_CPU_BCAST_FWD_EN   BIT(25)
-
-#define AR8216_REG_GLOBAL_CTRL         0x0030
-#define   AR8216_GCTRL_MTU             BITS(0, 11)
-#define   AR8236_GCTRL_MTU             BITS(0, 14)
-#define   AR8316_GCTRL_MTU             BITS(0, 14)
-
-#define AR8216_REG_VTU                 0x0040
-#define   AR8216_VTU_OP                        BITS(0, 3)
-#define   AR8216_VTU_OP_NOOP           0x0
-#define   AR8216_VTU_OP_FLUSH          0x1
-#define   AR8216_VTU_OP_LOAD           0x2
-#define   AR8216_VTU_OP_PURGE          0x3
-#define   AR8216_VTU_OP_REMOVE_PORT    0x4
-#define   AR8216_VTU_ACTIVE            BIT(3)
-#define   AR8216_VTU_FULL              BIT(4)
-#define   AR8216_VTU_PORT              BITS(8, 4)
-#define   AR8216_VTU_PORT_S            8
-#define   AR8216_VTU_VID               BITS(16, 12)
-#define   AR8216_VTU_VID_S             16
-#define   AR8216_VTU_PRIO              BITS(28, 3)
-#define   AR8216_VTU_PRIO_S            28
-#define   AR8216_VTU_PRIO_EN           BIT(31)
-
-#define AR8216_REG_VTU_DATA            0x0044
-#define   AR8216_VTUDATA_MEMBER                BITS(0, 10)
-#define   AR8236_VTUDATA_MEMBER                BITS(0, 7)
-#define   AR8216_VTUDATA_VALID         BIT(11)
-
-#define AR8216_REG_ATU_FUNC0           0x0050
-#define   AR8216_ATU_OP                        BITS(0, 3)
-#define   AR8216_ATU_OP_NOOP           0x0
-#define   AR8216_ATU_OP_FLUSH          0x1
-#define   AR8216_ATU_OP_LOAD           0x2
-#define   AR8216_ATU_OP_PURGE          0x3
-#define   AR8216_ATU_OP_FLUSH_UNLOCKED 0x4
-#define   AR8216_ATU_OP_FLUSH_PORT     0x5
-#define   AR8216_ATU_OP_GET_NEXT       0x6
-#define   AR8216_ATU_ACTIVE            BIT(3)
-#define   AR8216_ATU_PORT_NUM          BITS(8, 4)
-#define   AR8216_ATU_PORT_NUM_S                8
-#define   AR8216_ATU_FULL_VIO          BIT(12)
-#define   AR8216_ATU_ADDR5             BITS(16, 8)
-#define   AR8216_ATU_ADDR5_S           16
-#define   AR8216_ATU_ADDR4             BITS(24, 8)
-#define   AR8216_ATU_ADDR4_S           24
-
-#define AR8216_REG_ATU_FUNC1           0x0054
-#define   AR8216_ATU_ADDR3             BITS(0, 8)
-#define   AR8216_ATU_ADDR3_S           0
-#define   AR8216_ATU_ADDR2             BITS(8, 8)
-#define   AR8216_ATU_ADDR2_S           8
-#define   AR8216_ATU_ADDR1             BITS(16, 8)
-#define   AR8216_ATU_ADDR1_S           16
-#define   AR8216_ATU_ADDR0             BITS(24, 8)
-#define   AR8216_ATU_ADDR0_S           24
-
-#define AR8216_REG_ATU_FUNC2           0x0058
-#define   AR8216_ATU_PORTS             BITS(0, 6)
-#define   AR8216_ATU_PORT0             BIT(0)
-#define   AR8216_ATU_PORT1             BIT(1)
-#define   AR8216_ATU_PORT2             BIT(2)
-#define   AR8216_ATU_PORT3             BIT(3)
-#define   AR8216_ATU_PORT4             BIT(4)
-#define   AR8216_ATU_PORT5             BIT(5)
-#define   AR8216_ATU_STATUS            BITS(16, 4)
-#define   AR8216_ATU_STATUS_S          16
-
-#define AR8216_REG_ATU_CTRL            0x005C
-#define   AR8216_ATU_CTRL_AGE_EN       BIT(17)
-#define   AR8216_ATU_CTRL_AGE_TIME     BITS(0, 16)
-#define   AR8216_ATU_CTRL_AGE_TIME_S   0
-#define   AR8236_ATU_CTRL_RES          BIT(20)
-
-#define AR8216_REG_MIB_FUNC            0x0080
-#define   AR8216_MIB_TIMER             BITS(0, 16)
-#define   AR8216_MIB_AT_HALF_EN                BIT(16)
-#define   AR8216_MIB_BUSY              BIT(17)
-#define   AR8216_MIB_FUNC              BITS(24, 3)
-#define   AR8216_MIB_FUNC_S            24
-#define   AR8216_MIB_FUNC_NO_OP                0x0
-#define   AR8216_MIB_FUNC_FLUSH                0x1
-#define   AR8216_MIB_FUNC_CAPTURE      0x3
-#define   AR8236_MIB_EN                        BIT(30)
-
-#define AR8216_REG_GLOBAL_CPUPORT              0x0078
-#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT    BITS(4, 4)
-#define   AR8216_GLOBAL_CPUPORT_MIRROR_PORT_S  4
-
-#define AR8216_PORT_OFFSET(_i)         (0x0100 * (_i + 1))
-#define AR8216_REG_PORT_STATUS(_i)     (AR8216_PORT_OFFSET(_i) + 0x0000)
-#define   AR8216_PORT_STATUS_SPEED     BITS(0,2)
-#define   AR8216_PORT_STATUS_SPEED_S   0
-#define   AR8216_PORT_STATUS_TXMAC     BIT(2)
-#define   AR8216_PORT_STATUS_RXMAC     BIT(3)
-#define   AR8216_PORT_STATUS_TXFLOW    BIT(4)
-#define   AR8216_PORT_STATUS_RXFLOW    BIT(5)
-#define   AR8216_PORT_STATUS_DUPLEX    BIT(6)
-#define   AR8216_PORT_STATUS_LINK_UP   BIT(8)
-#define   AR8216_PORT_STATUS_LINK_AUTO BIT(9)
-#define   AR8216_PORT_STATUS_LINK_PAUSE        BIT(10)
-#define   AR8216_PORT_STATUS_FLOW_CONTROL  BIT(12)
-
-#define AR8216_REG_PORT_CTRL(_i)       (AR8216_PORT_OFFSET(_i) + 0x0004)
-
-/* port forwarding state */
-#define   AR8216_PORT_CTRL_STATE       BITS(0, 3)
-#define   AR8216_PORT_CTRL_STATE_S     0
-
-#define   AR8216_PORT_CTRL_LEARN_LOCK  BIT(7)
-
-/* egress 802.1q mode */
-#define   AR8216_PORT_CTRL_VLAN_MODE   BITS(8, 2)
-#define   AR8216_PORT_CTRL_VLAN_MODE_S 8
-
-#define   AR8216_PORT_CTRL_IGMP_SNOOP  BIT(10)
-#define   AR8216_PORT_CTRL_HEADER      BIT(11)
-#define   AR8216_PORT_CTRL_MAC_LOOP    BIT(12)
-#define   AR8216_PORT_CTRL_SINGLE_VLAN BIT(13)
-#define   AR8216_PORT_CTRL_LEARN       BIT(14)
-#define   AR8216_PORT_CTRL_MIRROR_TX   BIT(16)
-#define   AR8216_PORT_CTRL_MIRROR_RX   BIT(17)
-
-#define AR8216_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET(_i) + 0x0008)
-
-#define   AR8216_PORT_VLAN_DEFAULT_ID  BITS(0, 12)
-#define   AR8216_PORT_VLAN_DEFAULT_ID_S        0
-
-#define   AR8216_PORT_VLAN_DEST_PORTS  BITS(16, 9)
-#define   AR8216_PORT_VLAN_DEST_PORTS_S        16
-
-/* bit0 added to the priority field of egress frames */
-#define   AR8216_PORT_VLAN_TX_PRIO     BIT(27)
-
-/* port default priority */
-#define   AR8216_PORT_VLAN_PRIORITY    BITS(28, 2)
-#define   AR8216_PORT_VLAN_PRIORITY_S  28
-
-/* ingress 802.1q mode */
-#define   AR8216_PORT_VLAN_MODE                BITS(30, 2)
-#define   AR8216_PORT_VLAN_MODE_S      30
-
-#define AR8216_REG_PORT_RATE(_i)       (AR8216_PORT_OFFSET(_i) + 0x000c)
-#define AR8216_REG_PORT_PRIO(_i)       (AR8216_PORT_OFFSET(_i) + 0x0010)
-
-#define AR8216_STATS_RXBROAD           0x00
-#define AR8216_STATS_RXPAUSE           0x04
-#define AR8216_STATS_RXMULTI           0x08
-#define AR8216_STATS_RXFCSERR          0x0c
-#define AR8216_STATS_RXALIGNERR                0x10
-#define AR8216_STATS_RXRUNT            0x14
-#define AR8216_STATS_RXFRAGMENT                0x18
-#define AR8216_STATS_RX64BYTE          0x1c
-#define AR8216_STATS_RX128BYTE         0x20
-#define AR8216_STATS_RX256BYTE         0x24
-#define AR8216_STATS_RX512BYTE         0x28
-#define AR8216_STATS_RX1024BYTE                0x2c
-#define AR8216_STATS_RXMAXBYTE         0x30
-#define AR8216_STATS_RXTOOLONG         0x34
-#define AR8216_STATS_RXGOODBYTE                0x38
-#define AR8216_STATS_RXBADBYTE         0x40
-#define AR8216_STATS_RXOVERFLOW                0x48
-#define AR8216_STATS_FILTERED          0x4c
-#define AR8216_STATS_TXBROAD           0x50
-#define AR8216_STATS_TXPAUSE           0x54
-#define AR8216_STATS_TXMULTI           0x58
-#define AR8216_STATS_TXUNDERRUN                0x5c
-#define AR8216_STATS_TX64BYTE          0x60
-#define AR8216_STATS_TX128BYTE         0x64
-#define AR8216_STATS_TX256BYTE         0x68
-#define AR8216_STATS_TX512BYTE         0x6c
-#define AR8216_STATS_TX1024BYTE                0x70
-#define AR8216_STATS_TXMAXBYTE         0x74
-#define AR8216_STATS_TXOVERSIZE                0x78
-#define AR8216_STATS_TXBYTE            0x7c
-#define AR8216_STATS_TXCOLLISION       0x84
-#define AR8216_STATS_TXABORTCOL                0x88
-#define AR8216_STATS_TXMULTICOL                0x8c
-#define AR8216_STATS_TXSINGLECOL       0x90
-#define AR8216_STATS_TXEXCDEFER                0x94
-#define AR8216_STATS_TXDEFER           0x98
-#define AR8216_STATS_TXLATECOL         0x9c
-
-#define AR8236_REG_PORT_VLAN(_i)       (AR8216_PORT_OFFSET((_i)) + 0x0008)
-#define   AR8236_PORT_VLAN_DEFAULT_ID  BITS(16, 12)
-#define   AR8236_PORT_VLAN_DEFAULT_ID_S        16
-#define   AR8236_PORT_VLAN_PRIORITY    BITS(29, 3)
-#define   AR8236_PORT_VLAN_PRIORITY_S  28
-
-#define AR8236_REG_PORT_VLAN2(_i)      (AR8216_PORT_OFFSET((_i)) + 0x000c)
-#define   AR8236_PORT_VLAN2_MEMBER     BITS(16, 7)
-#define   AR8236_PORT_VLAN2_MEMBER_S   16
-#define   AR8236_PORT_VLAN2_TX_PRIO    BIT(23)
-#define   AR8236_PORT_VLAN2_VLAN_MODE  BITS(30, 2)
-#define   AR8236_PORT_VLAN2_VLAN_MODE_S        30
-
-#define AR8236_STATS_RXBROAD           0x00
-#define AR8236_STATS_RXPAUSE           0x04
-#define AR8236_STATS_RXMULTI           0x08
-#define AR8236_STATS_RXFCSERR          0x0c
-#define AR8236_STATS_RXALIGNERR                0x10
-#define AR8236_STATS_RXRUNT            0x14
-#define AR8236_STATS_RXFRAGMENT                0x18
-#define AR8236_STATS_RX64BYTE          0x1c
-#define AR8236_STATS_RX128BYTE         0x20
-#define AR8236_STATS_RX256BYTE         0x24
-#define AR8236_STATS_RX512BYTE         0x28
-#define AR8236_STATS_RX1024BYTE                0x2c
-#define AR8236_STATS_RX1518BYTE                0x30
-#define AR8236_STATS_RXMAXBYTE         0x34
-#define AR8236_STATS_RXTOOLONG         0x38
-#define AR8236_STATS_RXGOODBYTE                0x3c
-#define AR8236_STATS_RXBADBYTE         0x44
-#define AR8236_STATS_RXOVERFLOW                0x4c
-#define AR8236_STATS_FILTERED          0x50
-#define AR8236_STATS_TXBROAD           0x54
-#define AR8236_STATS_TXPAUSE           0x58
-#define AR8236_STATS_TXMULTI           0x5c
-#define AR8236_STATS_TXUNDERRUN                0x60
-#define AR8236_STATS_TX64BYTE          0x64
-#define AR8236_STATS_TX128BYTE         0x68
-#define AR8236_STATS_TX256BYTE         0x6c
-#define AR8236_STATS_TX512BYTE         0x70
-#define AR8236_STATS_TX1024BYTE                0x74
-#define AR8236_STATS_TX1518BYTE                0x78
-#define AR8236_STATS_TXMAXBYTE         0x7c
-#define AR8236_STATS_TXOVERSIZE                0x80
-#define AR8236_STATS_TXBYTE            0x84
-#define AR8236_STATS_TXCOLLISION       0x8c
-#define AR8236_STATS_TXABORTCOL                0x90
-#define AR8236_STATS_TXMULTICOL                0x94
-#define AR8236_STATS_TXSINGLECOL       0x98
-#define AR8236_STATS_TXEXCDEFER                0x9c
-#define AR8236_STATS_TXDEFER           0xa0
-#define AR8236_STATS_TXLATECOL         0xa4
-
-#define AR8316_REG_POSTRIP                     0x0008
-#define   AR8316_POSTRIP_MAC0_GMII_EN          BIT(0)
-#define   AR8316_POSTRIP_MAC0_RGMII_EN         BIT(1)
-#define   AR8316_POSTRIP_PHY4_GMII_EN          BIT(2)
-#define   AR8316_POSTRIP_PHY4_RGMII_EN         BIT(3)
-#define   AR8316_POSTRIP_MAC0_MAC_MODE         BIT(4)
-#define   AR8316_POSTRIP_RTL_MODE              BIT(5)
-#define   AR8316_POSTRIP_RGMII_RXCLK_DELAY_EN  BIT(6)
-#define   AR8316_POSTRIP_RGMII_TXCLK_DELAY_EN  BIT(7)
-#define   AR8316_POSTRIP_SERDES_EN             BIT(8)
-#define   AR8316_POSTRIP_SEL_ANA_RST           BIT(9)
-#define   AR8316_POSTRIP_GATE_25M_EN           BIT(10)
-#define   AR8316_POSTRIP_SEL_CLK25M            BIT(11)
-#define   AR8316_POSTRIP_HIB_PULSE_HW          BIT(12)
-#define   AR8316_POSTRIP_DBG_MODE_I            BIT(13)
-#define   AR8316_POSTRIP_MAC5_MAC_MODE         BIT(14)
-#define   AR8316_POSTRIP_MAC5_PHY_MODE         BIT(15)
-#define   AR8316_POSTRIP_POWER_DOWN_HW         BIT(16)
-#define   AR8316_POSTRIP_LPW_STATE_EN          BIT(17)
-#define   AR8316_POSTRIP_MAN_EN                        BIT(18)
-#define   AR8316_POSTRIP_PHY_PLL_ON            BIT(19)
-#define   AR8316_POSTRIP_LPW_EXIT              BIT(20)
-#define   AR8316_POSTRIP_TXDELAY_S0            BIT(21)
-#define   AR8316_POSTRIP_TXDELAY_S1            BIT(22)
-#define   AR8316_POSTRIP_RXDELAY_S0            BIT(23)
-#define   AR8316_POSTRIP_LED_OPEN_EN           BIT(24)
-#define   AR8316_POSTRIP_SPI_EN                        BIT(25)
-#define   AR8316_POSTRIP_RXDELAY_S1            BIT(26)
-#define   AR8316_POSTRIP_POWER_ON_SEL          BIT(31)
-
-/* port speed */
-enum {
-        AR8216_PORT_SPEED_10M = 0,
-        AR8216_PORT_SPEED_100M = 1,
-        AR8216_PORT_SPEED_1000M = 2,
-        AR8216_PORT_SPEED_ERR = 3,
-};
-
-/* ingress 802.1q mode */
-enum {
-       AR8216_IN_PORT_ONLY = 0,
-       AR8216_IN_PORT_FALLBACK = 1,
-       AR8216_IN_VLAN_ONLY = 2,
-       AR8216_IN_SECURE = 3
-};
-
-/* egress 802.1q mode */
-enum {
-       AR8216_OUT_KEEP = 0,
-       AR8216_OUT_STRIP_VLAN = 1,
-       AR8216_OUT_ADD_VLAN = 2
-};
-
-/* port forwarding state */
-enum {
-       AR8216_PORT_STATE_DISABLED = 0,
-       AR8216_PORT_STATE_BLOCK = 1,
-       AR8216_PORT_STATE_LISTEN = 2,
-       AR8216_PORT_STATE_LEARN = 3,
-       AR8216_PORT_STATE_FORWARD = 4
-};
-
-enum {
-       AR8XXX_VER_AR8216 = 0x01,
-       AR8XXX_VER_AR8236 = 0x03,
-       AR8XXX_VER_AR8316 = 0x10,
-       AR8XXX_VER_AR8327 = 0x12,
-       AR8XXX_VER_AR8337 = 0x13,
-};
-
-#define AR8XXX_NUM_ARL_RECORDS 100
-
-enum arl_op {
-       AR8XXX_ARL_INITIALIZE,
-       AR8XXX_ARL_GET_NEXT
-};
-
-struct arl_entry {
-       u8 port;
-       u8 mac[6];
-};
-
-struct ar8xxx_priv;
-
-struct ar8xxx_mib_desc {
-       unsigned int size;
-       unsigned int offset;
-       const char *name;
-};
-
-struct ar8xxx_chip {
-       unsigned long caps;
-       bool config_at_probe;
-       bool mii_lo_first;
-
-       /* parameters to calculate REG_PORT_STATS_BASE */
-       unsigned reg_port_stats_start;
-       unsigned reg_port_stats_length;
-
-       unsigned reg_arl_ctrl;
-
-       int (*hw_init)(struct ar8xxx_priv *priv);
-       void (*cleanup)(struct ar8xxx_priv *priv);
-
-       const char *name;
-       int vlans;
-       int ports;
-       const struct switch_dev_ops *swops;
-
-       void (*init_globals)(struct ar8xxx_priv *priv);
-       void (*init_port)(struct ar8xxx_priv *priv, int port);
-       void (*setup_port)(struct ar8xxx_priv *priv, int port, u32 members);
-       u32 (*read_port_status)(struct ar8xxx_priv *priv, int port);
-       u32 (*read_port_eee_status)(struct ar8xxx_priv *priv, int port);
-       int (*atu_flush)(struct ar8xxx_priv *priv);
-       int (*atu_flush_port)(struct ar8xxx_priv *priv, int port);
-       void (*vtu_flush)(struct ar8xxx_priv *priv);
-       void (*vtu_load_vlan)(struct ar8xxx_priv *priv, u32 vid, u32 port_mask);
-       void (*phy_fixup)(struct ar8xxx_priv *priv, int phy);
-       void (*set_mirror_regs)(struct ar8xxx_priv *priv);
-       void (*get_arl_entry)(struct ar8xxx_priv *priv, struct arl_entry *a,
-                             u32 *status, enum arl_op op);
-       int (*sw_hw_apply)(struct switch_dev *dev);
-
-       const struct ar8xxx_mib_desc *mib_decs;
-       unsigned num_mibs;
-       unsigned mib_func;
-};
-
-struct ar8xxx_priv {
-       struct switch_dev dev;
-       struct mii_bus *mii_bus;
-       struct phy_device *phy;
-
-       int (*get_port_link)(unsigned port);
-
-       const struct net_device_ops *ndo_old;
-       struct net_device_ops ndo;
-       struct mutex reg_mutex;
-       u8 chip_ver;
-       u8 chip_rev;
-       const struct ar8xxx_chip *chip;
-       void *chip_data;
-       bool initialized;
-       bool port4_phy;
-       char buf[2048];
-       struct arl_entry arl_table[AR8XXX_NUM_ARL_RECORDS];
-       char arl_buf[AR8XXX_NUM_ARL_RECORDS * 32 + 256];
-       bool link_up[AR8X16_MAX_PORTS];
-
-       bool init;
-
-       struct mutex mib_lock;
-       struct delayed_work mib_work;
-       int mib_next_port;
-       u64 *mib_stats;
-
-       struct list_head list;
-       unsigned int use_count;
-
-       /* all fields below are cleared on reset */
-       bool vlan;
-       u16 vlan_id[AR8X16_MAX_VLANS];
-       u8 vlan_table[AR8X16_MAX_VLANS];
-       u8 vlan_tagged;
-       u16 pvid[AR8X16_MAX_PORTS];
-       int arl_age_time;
-
-       /* mirroring */
-       bool mirror_rx;
-       bool mirror_tx;
-       int source_port;
-       int monitor_port;
-       u8 port_vlan_prio[AR8X16_MAX_PORTS];
-};
-
-u32
-ar8xxx_mii_read32(struct ar8xxx_priv *priv, int phy_id, int regnum);
-void
-ar8xxx_mii_write32(struct ar8xxx_priv *priv, int phy_id, int regnum, u32 val);
-u32
-ar8xxx_read(struct ar8xxx_priv *priv, int reg);
-void
-ar8xxx_write(struct ar8xxx_priv *priv, int reg, u32 val);
-u32
-ar8xxx_rmw(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
-
-void
-ar8xxx_phy_dbg_write(struct ar8xxx_priv *priv, int phy_addr,
-                    u16 dbg_addr, u16 dbg_data);
-void
-ar8xxx_phy_mmd_write(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg, u16 data);
-u16
-ar8xxx_phy_mmd_read(struct ar8xxx_priv *priv, int phy_addr, u16 addr, u16 reg);
-void
-ar8xxx_phy_init(struct ar8xxx_priv *priv);
-int
-ar8xxx_sw_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                  struct switch_val *val);
-int
-ar8xxx_sw_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                  struct switch_val *val);
-int
-ar8xxx_sw_set_reset_mibs(struct switch_dev *dev,
-                        const struct switch_attr *attr,
-                        struct switch_val *val);
-int
-ar8xxx_sw_set_mirror_rx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val);
-int
-ar8xxx_sw_get_mirror_rx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val);
-int
-ar8xxx_sw_set_mirror_tx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val);
-int
-ar8xxx_sw_get_mirror_tx_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val);
-int
-ar8xxx_sw_set_mirror_monitor_port(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val);
-int
-ar8xxx_sw_get_mirror_monitor_port(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val);
-int
-ar8xxx_sw_set_mirror_source_port(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val);
-int
-ar8xxx_sw_get_mirror_source_port(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val);
-int
-ar8xxx_sw_set_pvid(struct switch_dev *dev, int port, int vlan);
-int
-ar8xxx_sw_get_pvid(struct switch_dev *dev, int port, int *vlan);
-int
-ar8xxx_sw_hw_apply(struct switch_dev *dev);
-int
-ar8xxx_sw_reset_switch(struct switch_dev *dev);
-int
-ar8xxx_sw_get_port_link(struct switch_dev *dev, int port,
-                       struct switch_port_link *link);
-int
-ar8xxx_sw_set_port_reset_mib(struct switch_dev *dev,
-                             const struct switch_attr *attr,
-                             struct switch_val *val);
-int
-ar8xxx_sw_get_port_mib(struct switch_dev *dev,
-                       const struct switch_attr *attr,
-                       struct switch_val *val);
-int
-ar8xxx_sw_get_arl_age_time(struct switch_dev *dev,
-                          const struct switch_attr *attr,
-                          struct switch_val *val);
-int
-ar8xxx_sw_set_arl_age_time(struct switch_dev *dev,
-                          const struct switch_attr *attr,
-                          struct switch_val *val);
-int
-ar8xxx_sw_get_arl_table(struct switch_dev *dev,
-                       const struct switch_attr *attr,
-                       struct switch_val *val);
-int
-ar8xxx_sw_set_flush_arl_table(struct switch_dev *dev,
-                             const struct switch_attr *attr,
-                             struct switch_val *val);
-int
-ar8xxx_sw_set_flush_port_arl_table(struct switch_dev *dev,
-                                  const struct switch_attr *attr,
-                                  struct switch_val *val);
-int
-ar8216_wait_bit(struct ar8xxx_priv *priv, int reg, u32 mask, u32 val);
-
-static inline struct ar8xxx_priv *
-swdev_to_ar8xxx(struct switch_dev *swdev)
-{
-       return container_of(swdev, struct ar8xxx_priv, dev);
-}
-
-static inline bool ar8xxx_has_gige(struct ar8xxx_priv *priv)
-{
-       return priv->chip->caps & AR8XXX_CAP_GIGE;
-}
-
-static inline bool ar8xxx_has_mib_counters(struct ar8xxx_priv *priv)
-{
-       return priv->chip->caps & AR8XXX_CAP_MIB_COUNTERS;
-}
-
-static inline bool chip_is_ar8216(struct ar8xxx_priv *priv)
-{
-       return priv->chip_ver == AR8XXX_VER_AR8216;
-}
-
-static inline bool chip_is_ar8236(struct ar8xxx_priv *priv)
-{
-       return priv->chip_ver == AR8XXX_VER_AR8236;
-}
-
-static inline bool chip_is_ar8316(struct ar8xxx_priv *priv)
-{
-       return priv->chip_ver == AR8XXX_VER_AR8316;
-}
-
-static inline bool chip_is_ar8327(struct ar8xxx_priv *priv)
-{
-       return priv->chip_ver == AR8XXX_VER_AR8327;
-}
-
-static inline bool chip_is_ar8337(struct ar8xxx_priv *priv)
-{
-       return priv->chip_ver == AR8XXX_VER_AR8337;
-}
-
-static inline void
-ar8xxx_reg_set(struct ar8xxx_priv *priv, int reg, u32 val)
-{
-       ar8xxx_rmw(priv, reg, 0, val);
-}
-
-static inline void
-ar8xxx_reg_clear(struct ar8xxx_priv *priv, int reg, u32 val)
-{
-       ar8xxx_rmw(priv, reg, val, 0);
-}
-
-static inline void
-split_addr(u32 regaddr, u16 *r1, u16 *r2, u16 *page)
-{
-       regaddr >>= 1;
-       *r1 = regaddr & 0x1e;
-
-       regaddr >>= 5;
-       *r2 = regaddr & 0x7;
-
-       regaddr >>= 3;
-       *page = regaddr & 0x1ff;
-}
-
-static inline void
-wait_for_page_switch(void)
-{
-       udelay(5);
-}
-
-#endif
diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.c b/target/linux/generic/files/drivers/net/phy/ar8327.c
deleted file mode 100644 (file)
index 74f0a08..0000000
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * ar8327.c: AR8216 switch driver
- *
- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/list.h>
-#include <linux/bitops.h>
-#include <linux/switch.h>
-#include <linux/delay.h>
-#include <linux/phy.h>
-#include <linux/lockdep.h>
-#include <linux/ar8216_platform.h>
-#include <linux/workqueue.h>
-#include <linux/of_device.h>
-#include <linux/leds.h>
-#include <linux/mdio.h>
-
-#include "ar8216.h"
-#include "ar8327.h"
-
-extern const struct ar8xxx_mib_desc ar8236_mibs[39];
-extern const struct switch_attr ar8xxx_sw_attr_vlan[1];
-
-static u32
-ar8327_get_pad_cfg(struct ar8327_pad_cfg *cfg)
-{
-       u32 t;
-
-       if (!cfg)
-               return 0;
-
-       t = 0;
-       switch (cfg->mode) {
-       case AR8327_PAD_NC:
-               break;
-
-       case AR8327_PAD_MAC2MAC_MII:
-               t = AR8327_PAD_MAC_MII_EN;
-               if (cfg->rxclk_sel)
-                       t |= AR8327_PAD_MAC_MII_RXCLK_SEL;
-               if (cfg->txclk_sel)
-                       t |= AR8327_PAD_MAC_MII_TXCLK_SEL;
-               break;
-
-       case AR8327_PAD_MAC2MAC_GMII:
-               t = AR8327_PAD_MAC_GMII_EN;
-               if (cfg->rxclk_sel)
-                       t |= AR8327_PAD_MAC_GMII_RXCLK_SEL;
-               if (cfg->txclk_sel)
-                       t |= AR8327_PAD_MAC_GMII_TXCLK_SEL;
-               break;
-
-       case AR8327_PAD_MAC_SGMII:
-               t = AR8327_PAD_SGMII_EN;
-
-               /*
-                * WAR for the QUalcomm Atheros AP136 board.
-                * It seems that RGMII TX/RX delay settings needs to be
-                * applied for SGMII mode as well, The ethernet is not
-                * reliable without this.
-                */
-               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
-               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
-               if (cfg->rxclk_delay_en)
-                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
-               if (cfg->txclk_delay_en)
-                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
-
-               if (cfg->sgmii_delay_en)
-                       t |= AR8327_PAD_SGMII_DELAY_EN;
-
-               break;
-
-       case AR8327_PAD_MAC2PHY_MII:
-               t = AR8327_PAD_PHY_MII_EN;
-               if (cfg->rxclk_sel)
-                       t |= AR8327_PAD_PHY_MII_RXCLK_SEL;
-               if (cfg->txclk_sel)
-                       t |= AR8327_PAD_PHY_MII_TXCLK_SEL;
-               break;
-
-       case AR8327_PAD_MAC2PHY_GMII:
-               t = AR8327_PAD_PHY_GMII_EN;
-               if (cfg->pipe_rxclk_sel)
-                       t |= AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL;
-               if (cfg->rxclk_sel)
-                       t |= AR8327_PAD_PHY_GMII_RXCLK_SEL;
-               if (cfg->txclk_sel)
-                       t |= AR8327_PAD_PHY_GMII_TXCLK_SEL;
-               break;
-
-       case AR8327_PAD_MAC_RGMII:
-               t = AR8327_PAD_RGMII_EN;
-               t |= cfg->txclk_delay_sel << AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S;
-               t |= cfg->rxclk_delay_sel << AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S;
-               if (cfg->rxclk_delay_en)
-                       t |= AR8327_PAD_RGMII_RXCLK_DELAY_EN;
-               if (cfg->txclk_delay_en)
-                       t |= AR8327_PAD_RGMII_TXCLK_DELAY_EN;
-               break;
-
-       case AR8327_PAD_PHY_GMII:
-               t = AR8327_PAD_PHYX_GMII_EN;
-               break;
-
-       case AR8327_PAD_PHY_RGMII:
-               t = AR8327_PAD_PHYX_RGMII_EN;
-               break;
-
-       case AR8327_PAD_PHY_MII:
-               t = AR8327_PAD_PHYX_MII_EN;
-               break;
-       }
-
-       return t;
-}
-
-static void
-ar8327_phy_fixup(struct ar8xxx_priv *priv, int phy)
-{
-       switch (priv->chip_rev) {
-       case 1:
-               /* For 100M waveform */
-               ar8xxx_phy_dbg_write(priv, phy, 0, 0x02ea);
-               /* Turn on Gigabit clock */
-               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x68a0);
-               break;
-
-       case 2:
-               ar8xxx_phy_mmd_write(priv, phy, 0x7, 0x3c, 0x0);
-               /* fallthrough */
-       case 4:
-               ar8xxx_phy_mmd_write(priv, phy, 0x3, 0x800d, 0x803f);
-               ar8xxx_phy_dbg_write(priv, phy, 0x3d, 0x6860);
-               ar8xxx_phy_dbg_write(priv, phy, 0x5, 0x2c46);
-               ar8xxx_phy_dbg_write(priv, phy, 0x3c, 0x6000);
-               break;
-       }
-}
-
-static u32
-ar8327_get_port_init_status(struct ar8327_port_cfg *cfg)
-{
-       u32 t;
-
-       if (!cfg->force_link)
-               return AR8216_PORT_STATUS_LINK_AUTO;
-
-       t = AR8216_PORT_STATUS_TXMAC | AR8216_PORT_STATUS_RXMAC;
-       t |= cfg->duplex ? AR8216_PORT_STATUS_DUPLEX : 0;
-       t |= cfg->rxpause ? AR8216_PORT_STATUS_RXFLOW : 0;
-       t |= cfg->txpause ? AR8216_PORT_STATUS_TXFLOW : 0;
-
-       switch (cfg->speed) {
-       case AR8327_PORT_SPEED_10:
-               t |= AR8216_PORT_SPEED_10M;
-               break;
-       case AR8327_PORT_SPEED_100:
-               t |= AR8216_PORT_SPEED_100M;
-               break;
-       case AR8327_PORT_SPEED_1000:
-               t |= AR8216_PORT_SPEED_1000M;
-               break;
-       }
-
-       return t;
-}
-
-#define AR8327_LED_ENTRY(_num, _reg, _shift) \
-       [_num] = { .reg = (_reg), .shift = (_shift) }
-
-static const struct ar8327_led_entry
-ar8327_led_map[AR8327_NUM_LEDS] = {
-       AR8327_LED_ENTRY(AR8327_LED_PHY0_0, 0, 14),
-       AR8327_LED_ENTRY(AR8327_LED_PHY0_1, 1, 14),
-       AR8327_LED_ENTRY(AR8327_LED_PHY0_2, 2, 14),
-
-       AR8327_LED_ENTRY(AR8327_LED_PHY1_0, 3, 8),
-       AR8327_LED_ENTRY(AR8327_LED_PHY1_1, 3, 10),
-       AR8327_LED_ENTRY(AR8327_LED_PHY1_2, 3, 12),
-
-       AR8327_LED_ENTRY(AR8327_LED_PHY2_0, 3, 14),
-       AR8327_LED_ENTRY(AR8327_LED_PHY2_1, 3, 16),
-       AR8327_LED_ENTRY(AR8327_LED_PHY2_2, 3, 18),
-
-       AR8327_LED_ENTRY(AR8327_LED_PHY3_0, 3, 20),
-       AR8327_LED_ENTRY(AR8327_LED_PHY3_1, 3, 22),
-       AR8327_LED_ENTRY(AR8327_LED_PHY3_2, 3, 24),
-
-       AR8327_LED_ENTRY(AR8327_LED_PHY4_0, 0, 30),
-       AR8327_LED_ENTRY(AR8327_LED_PHY4_1, 1, 30),
-       AR8327_LED_ENTRY(AR8327_LED_PHY4_2, 2, 30),
-};
-
-static void
-ar8327_set_led_pattern(struct ar8xxx_priv *priv, unsigned int led_num,
-                      enum ar8327_led_pattern pattern)
-{
-       const struct ar8327_led_entry *entry;
-
-       entry = &ar8327_led_map[led_num];
-       ar8xxx_rmw(priv, AR8327_REG_LED_CTRL(entry->reg),
-                  (3 << entry->shift), pattern << entry->shift);
-}
-
-static void
-ar8327_led_work_func(struct work_struct *work)
-{
-       struct ar8327_led *aled;
-       u8 pattern;
-
-       aled = container_of(work, struct ar8327_led, led_work);
-
-       pattern = aled->pattern;
-
-       ar8327_set_led_pattern(aled->sw_priv, aled->led_num,
-                              pattern);
-}
-
-static void
-ar8327_led_schedule_change(struct ar8327_led *aled, u8 pattern)
-{
-       if (aled->pattern == pattern)
-               return;
-
-       aled->pattern = pattern;
-       schedule_work(&aled->led_work);
-}
-
-static inline struct ar8327_led *
-led_cdev_to_ar8327_led(struct led_classdev *led_cdev)
-{
-       return container_of(led_cdev, struct ar8327_led, cdev);
-}
-
-static int
-ar8327_led_blink_set(struct led_classdev *led_cdev,
-                    unsigned long *delay_on,
-                    unsigned long *delay_off)
-{
-       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
-
-       if (*delay_on == 0 && *delay_off == 0) {
-               *delay_on = 125;
-               *delay_off = 125;
-       }
-
-       if (*delay_on != 125 || *delay_off != 125) {
-               /*
-                * The hardware only supports blinking at 4Hz. Fall back
-                * to software implementation in other cases.
-                */
-               return -EINVAL;
-       }
-
-       spin_lock(&aled->lock);
-
-       aled->enable_hw_mode = false;
-       ar8327_led_schedule_change(aled, AR8327_LED_PATTERN_BLINK);
-
-       spin_unlock(&aled->lock);
-
-       return 0;
-}
-
-static void
-ar8327_led_set_brightness(struct led_classdev *led_cdev,
-                         enum led_brightness brightness)
-{
-       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
-       u8 pattern;
-       bool active;
-
-       active = (brightness != LED_OFF);
-       active ^= aled->active_low;
-
-       pattern = (active) ? AR8327_LED_PATTERN_ON :
-                            AR8327_LED_PATTERN_OFF;
-
-       spin_lock(&aled->lock);
-
-       aled->enable_hw_mode = false;
-       ar8327_led_schedule_change(aled, pattern);
-
-       spin_unlock(&aled->lock);
-}
-
-static ssize_t
-ar8327_led_enable_hw_mode_show(struct device *dev,
-                              struct device_attribute *attr,
-                              char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
-       ssize_t ret = 0;
-
-       ret += scnprintf(buf, PAGE_SIZE, "%d\n", aled->enable_hw_mode);
-
-       return ret;
-}
-
-static ssize_t
-ar8327_led_enable_hw_mode_store(struct device *dev,
-                               struct device_attribute *attr,
-                               const char *buf,
-                               size_t size)
-{
-        struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct ar8327_led *aled = led_cdev_to_ar8327_led(led_cdev);
-       u8 pattern;
-       u8 value;
-       int ret;
-
-       ret = kstrtou8(buf, 10, &value);
-       if (ret < 0)
-               return -EINVAL;
-
-       spin_lock(&aled->lock);
-
-       aled->enable_hw_mode = !!value;
-       if (aled->enable_hw_mode)
-               pattern = AR8327_LED_PATTERN_RULE;
-       else
-               pattern = AR8327_LED_PATTERN_OFF;
-
-       ar8327_led_schedule_change(aled, pattern);
-
-       spin_unlock(&aled->lock);
-
-       return size;
-}
-
-static DEVICE_ATTR(enable_hw_mode,  S_IRUGO | S_IWUSR,
-                  ar8327_led_enable_hw_mode_show,
-                  ar8327_led_enable_hw_mode_store);
-
-static int
-ar8327_led_register(struct ar8327_led *aled)
-{
-       int ret;
-
-       ret = led_classdev_register(NULL, &aled->cdev);
-       if (ret < 0)
-               return ret;
-
-       if (aled->mode == AR8327_LED_MODE_HW) {
-               ret = device_create_file(aled->cdev.dev,
-                                        &dev_attr_enable_hw_mode);
-               if (ret)
-                       goto err_unregister;
-       }
-
-       return 0;
-
-err_unregister:
-       led_classdev_unregister(&aled->cdev);
-       return ret;
-}
-
-static void
-ar8327_led_unregister(struct ar8327_led *aled)
-{
-       if (aled->mode == AR8327_LED_MODE_HW)
-               device_remove_file(aled->cdev.dev, &dev_attr_enable_hw_mode);
-
-       led_classdev_unregister(&aled->cdev);
-       cancel_work_sync(&aled->led_work);
-}
-
-static int
-ar8327_led_create(struct ar8xxx_priv *priv,
-                 const struct ar8327_led_info *led_info)
-{
-       struct ar8327_data *data = priv->chip_data;
-       struct ar8327_led *aled;
-       int ret;
-
-       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
-               return 0;
-
-       if (!led_info->name)
-               return -EINVAL;
-
-       if (led_info->led_num >= AR8327_NUM_LEDS)
-               return -EINVAL;
-
-       aled = kzalloc(sizeof(*aled) + strlen(led_info->name) + 1,
-                      GFP_KERNEL);
-       if (!aled)
-               return -ENOMEM;
-
-       aled->sw_priv = priv;
-       aled->led_num = led_info->led_num;
-       aled->active_low = led_info->active_low;
-       aled->mode = led_info->mode;
-
-       if (aled->mode == AR8327_LED_MODE_HW)
-               aled->enable_hw_mode = true;
-
-       aled->name = (char *)(aled + 1);
-       strcpy(aled->name, led_info->name);
-
-       aled->cdev.name = aled->name;
-       aled->cdev.brightness_set = ar8327_led_set_brightness;
-       aled->cdev.blink_set = ar8327_led_blink_set;
-       aled->cdev.default_trigger = led_info->default_trigger;
-
-       spin_lock_init(&aled->lock);
-       mutex_init(&aled->mutex);
-       INIT_WORK(&aled->led_work, ar8327_led_work_func);
-
-       ret = ar8327_led_register(aled);
-       if (ret)
-               goto err_free;
-
-       data->leds[data->num_leds++] = aled;
-
-       return 0;
-
-err_free:
-       kfree(aled);
-       return ret;
-}
-
-static void
-ar8327_led_destroy(struct ar8327_led *aled)
-{
-       ar8327_led_unregister(aled);
-       kfree(aled);
-}
-
-static void
-ar8327_leds_init(struct ar8xxx_priv *priv)
-{
-       struct ar8327_data *data = priv->chip_data;
-       unsigned i;
-
-       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
-               return;
-
-       for (i = 0; i < data->num_leds; i++) {
-               struct ar8327_led *aled;
-
-               aled = data->leds[i];
-
-               if (aled->enable_hw_mode)
-                       aled->pattern = AR8327_LED_PATTERN_RULE;
-               else
-                       aled->pattern = AR8327_LED_PATTERN_OFF;
-
-               ar8327_set_led_pattern(priv, aled->led_num, aled->pattern);
-       }
-}
-
-static void
-ar8327_leds_cleanup(struct ar8xxx_priv *priv)
-{
-       struct ar8327_data *data = priv->chip_data;
-       unsigned i;
-
-       if (!IS_ENABLED(CONFIG_AR8216_PHY_LEDS))
-               return;
-
-       for (i = 0; i < data->num_leds; i++) {
-               struct ar8327_led *aled;
-
-               aled = data->leds[i];
-               ar8327_led_destroy(aled);
-       }
-
-       kfree(data->leds);
-}
-
-static int
-ar8327_hw_config_pdata(struct ar8xxx_priv *priv,
-                      struct ar8327_platform_data *pdata)
-{
-       struct ar8327_led_cfg *led_cfg;
-       struct ar8327_data *data = priv->chip_data;
-       u32 pos, new_pos;
-       u32 t;
-
-       if (!pdata)
-               return -EINVAL;
-
-       priv->get_port_link = pdata->get_port_link;
-
-       data->port0_status = ar8327_get_port_init_status(&pdata->port0_cfg);
-       data->port6_status = ar8327_get_port_init_status(&pdata->port6_cfg);
-
-       t = ar8327_get_pad_cfg(pdata->pad0_cfg);
-       if (chip_is_ar8337(priv) && !pdata->pad0_cfg->mac06_exchange_dis)
-           t |= AR8337_PAD_MAC06_EXCHANGE_EN;
-       ar8xxx_write(priv, AR8327_REG_PAD0_MODE, t);
-
-       t = ar8327_get_pad_cfg(pdata->pad5_cfg);
-       ar8xxx_write(priv, AR8327_REG_PAD5_MODE, t);
-       t = ar8327_get_pad_cfg(pdata->pad6_cfg);
-       ar8xxx_write(priv, AR8327_REG_PAD6_MODE, t);
-
-       pos = ar8xxx_read(priv, AR8327_REG_POWER_ON_STRIP);
-       new_pos = pos;
-
-       led_cfg = pdata->led_cfg;
-       if (led_cfg) {
-               if (led_cfg->open_drain)
-                       new_pos |= AR8327_POWER_ON_STRIP_LED_OPEN_EN;
-               else
-                       new_pos &= ~AR8327_POWER_ON_STRIP_LED_OPEN_EN;
-
-               ar8xxx_write(priv, AR8327_REG_LED_CTRL0, led_cfg->led_ctrl0);
-               ar8xxx_write(priv, AR8327_REG_LED_CTRL1, led_cfg->led_ctrl1);
-               ar8xxx_write(priv, AR8327_REG_LED_CTRL2, led_cfg->led_ctrl2);
-               ar8xxx_write(priv, AR8327_REG_LED_CTRL3, led_cfg->led_ctrl3);
-
-               if (new_pos != pos)
-                       new_pos |= AR8327_POWER_ON_STRIP_POWER_ON_SEL;
-       }
-
-       if (pdata->sgmii_cfg) {
-               t = pdata->sgmii_cfg->sgmii_ctrl;
-               if (priv->chip_rev == 1)
-                       t |= AR8327_SGMII_CTRL_EN_PLL |
-                            AR8327_SGMII_CTRL_EN_RX |
-                            AR8327_SGMII_CTRL_EN_TX;
-               else
-                       t &= ~(AR8327_SGMII_CTRL_EN_PLL |
-                              AR8327_SGMII_CTRL_EN_RX |
-                              AR8327_SGMII_CTRL_EN_TX);
-
-               ar8xxx_write(priv, AR8327_REG_SGMII_CTRL, t);
-
-               if (pdata->sgmii_cfg->serdes_aen)
-                       new_pos &= ~AR8327_POWER_ON_STRIP_SERDES_AEN;
-               else
-                       new_pos |= AR8327_POWER_ON_STRIP_SERDES_AEN;
-       }
-
-       ar8xxx_write(priv, AR8327_REG_POWER_ON_STRIP, new_pos);
-
-       if (pdata->leds && pdata->num_leds) {
-               int i;
-
-               data->leds = kzalloc(pdata->num_leds * sizeof(void *),
-                                    GFP_KERNEL);
-               if (!data->leds)
-                       return -ENOMEM;
-
-               for (i = 0; i < pdata->num_leds; i++)
-                       ar8327_led_create(priv, &pdata->leds[i]);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static int
-ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
-{
-       struct ar8327_data *data = priv->chip_data;
-       const __be32 *paddr;
-       int len;
-       int i;
-
-       paddr = of_get_property(np, "qca,ar8327-initvals", &len);
-       if (!paddr || len < (2 * sizeof(*paddr)))
-               return -EINVAL;
-
-       len /= sizeof(*paddr);
-
-       for (i = 0; i < len - 1; i += 2) {
-               u32 reg;
-               u32 val;
-
-               reg = be32_to_cpup(paddr + i);
-               val = be32_to_cpup(paddr + i + 1);
-
-               switch (reg) {
-               case AR8327_REG_PORT_STATUS(0):
-                       data->port0_status = val;
-                       break;
-               case AR8327_REG_PORT_STATUS(6):
-                       data->port6_status = val;
-                       break;
-               default:
-                       ar8xxx_write(priv, reg, val);
-                       break;
-               }
-       }
-
-       return 0;
-}
-#else
-static inline int
-ar8327_hw_config_of(struct ar8xxx_priv *priv, struct device_node *np)
-{
-       return -EINVAL;
-}
-#endif
-
-static int
-ar8327_hw_init(struct ar8xxx_priv *priv)
-{
-       int ret;
-
-       priv->chip_data = kzalloc(sizeof(struct ar8327_data), GFP_KERNEL);
-       if (!priv->chip_data)
-               return -ENOMEM;
-
-       if (priv->phy->mdio.dev.of_node)
-               ret = ar8327_hw_config_of(priv, priv->phy->mdio.dev.of_node);
-       else
-               ret = ar8327_hw_config_pdata(priv,
-                                            priv->phy->mdio.dev.platform_data);
-
-       if (ret)
-               return ret;
-
-       ar8327_leds_init(priv);
-
-       ar8xxx_phy_init(priv);
-
-       return 0;
-}
-
-static void
-ar8327_cleanup(struct ar8xxx_priv *priv)
-{
-       ar8327_leds_cleanup(priv);
-}
-
-static void
-ar8327_init_globals(struct ar8xxx_priv *priv)
-{
-       struct ar8327_data *data = priv->chip_data;
-       u32 t;
-       int i;
-
-       /* enable CPU port and disable mirror port */
-       t = AR8327_FWD_CTRL0_CPU_PORT_EN |
-           AR8327_FWD_CTRL0_MIRROR_PORT;
-       ar8xxx_write(priv, AR8327_REG_FWD_CTRL0, t);
-
-       /* forward multicast and broadcast frames to CPU */
-       t = (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_UC_FLOOD_S) |
-           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_MC_FLOOD_S) |
-           (AR8327_PORTS_ALL << AR8327_FWD_CTRL1_BC_FLOOD_S);
-       ar8xxx_write(priv, AR8327_REG_FWD_CTRL1, t);
-
-       /* enable jumbo frames */
-       ar8xxx_rmw(priv, AR8327_REG_MAX_FRAME_SIZE,
-                  AR8327_MAX_FRAME_SIZE_MTU, 9018 + 8 + 2);
-
-       /* Enable MIB counters */
-       ar8xxx_reg_set(priv, AR8327_REG_MODULE_EN,
-                      AR8327_MODULE_EN_MIB);
-
-       /* Disable EEE on all phy's due to stability issues */
-       for (i = 0; i < AR8XXX_NUM_PHYS; i++)
-               data->eee[i] = false;
-}
-
-static void
-ar8327_init_port(struct ar8xxx_priv *priv, int port)
-{
-       struct ar8327_data *data = priv->chip_data;
-       u32 t;
-
-       if (port == AR8216_PORT_CPU)
-               t = data->port0_status;
-       else if (port == 6)
-               t = data->port6_status;
-       else
-               t = AR8216_PORT_STATUS_LINK_AUTO;
-
-       if (port != AR8216_PORT_CPU && port != 6) {
-               /*hw limitation:if configure mac when there is traffic,
-               port MAC may work abnormal. Need disable lan&wan mac at fisrt*/
-               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), 0);
-               msleep(100);
-               t |= AR8216_PORT_STATUS_FLOW_CONTROL;
-               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
-       } else {
-               ar8xxx_write(priv, AR8327_REG_PORT_STATUS(port), t);
-       }
-
-       ar8xxx_write(priv, AR8327_REG_PORT_HEADER(port), 0);
-
-       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), 0);
-
-       t = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH << AR8327_PORT_VLAN1_OUT_MODE_S;
-       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
-
-       t = AR8327_PORT_LOOKUP_LEARN;
-       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
-       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
-}
-
-static u32
-ar8327_read_port_status(struct ar8xxx_priv *priv, int port)
-{
-       u32 t;
-
-       t = ar8xxx_read(priv, AR8327_REG_PORT_STATUS(port));
-       /* map the flow control autoneg result bits to the flow control bits
-        * used in forced mode to allow ar8216_read_port_link detect
-        * flow control properly if autoneg is used
-        */
-       if (t & AR8216_PORT_STATUS_LINK_UP &&
-           t & AR8216_PORT_STATUS_LINK_AUTO) {
-               t &= ~(AR8216_PORT_STATUS_TXFLOW | AR8216_PORT_STATUS_RXFLOW);
-               if (t & AR8327_PORT_STATUS_TXFLOW_AUTO)
-                       t |= AR8216_PORT_STATUS_TXFLOW;
-               if (t & AR8327_PORT_STATUS_RXFLOW_AUTO)
-                       t |= AR8216_PORT_STATUS_RXFLOW;
-       }
-
-       return t;
-}
-
-static u32
-ar8327_read_port_eee_status(struct ar8xxx_priv *priv, int port)
-{
-       int phy;
-       u16 t;
-
-       if (port >= priv->dev.ports)
-               return 0;
-
-       if (port == 0 || port == 6)
-               return 0;
-
-       phy = port - 1;
-
-       /* EEE Ability Auto-negotiation Result */
-       t = ar8xxx_phy_mmd_read(priv, phy, 0x7, 0x8000);
-
-       return mmd_eee_adv_to_ethtool_adv_t(t);
-}
-
-static int
-ar8327_atu_flush(struct ar8xxx_priv *priv)
-{
-       int ret;
-
-       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
-                             AR8327_ATU_FUNC_BUSY, 0);
-       if (!ret)
-               ar8xxx_write(priv, AR8327_REG_ATU_FUNC,
-                            AR8327_ATU_FUNC_OP_FLUSH |
-                            AR8327_ATU_FUNC_BUSY);
-
-       return ret;
-}
-
-static int
-ar8327_atu_flush_port(struct ar8xxx_priv *priv, int port)
-{
-       u32 t;
-       int ret;
-
-       ret = ar8216_wait_bit(priv, AR8327_REG_ATU_FUNC,
-                             AR8327_ATU_FUNC_BUSY, 0);
-       if (!ret) {
-               t = (port << AR8327_ATU_PORT_NUM_S);
-               t |= AR8327_ATU_FUNC_OP_FLUSH_PORT;
-               t |= AR8327_ATU_FUNC_BUSY;
-               ar8xxx_write(priv, AR8327_REG_ATU_FUNC, t);
-       }
-
-       return ret;
-}
-
-static int
-ar8327_get_port_igmp(struct ar8xxx_priv *priv, int port)
-{
-       u32 fwd_ctrl, frame_ack;
-
-       fwd_ctrl = (BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
-       frame_ack = ((AR8327_FRAME_ACK_CTRL_IGMP_MLD |
-                     AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
-                     AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
-                    AR8327_FRAME_ACK_CTRL_S(port));
-
-       return (ar8xxx_read(priv, AR8327_REG_FWD_CTRL1) &
-                       fwd_ctrl) == fwd_ctrl &&
-               (ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL(port)) &
-                       frame_ack) == frame_ack;
-}
-
-static void
-ar8327_set_port_igmp(struct ar8xxx_priv *priv, int port, int enable)
-{
-       int reg_frame_ack = AR8327_REG_FRAME_ACK_CTRL(port);
-       u32 val_frame_ack = (AR8327_FRAME_ACK_CTRL_IGMP_MLD |
-                         AR8327_FRAME_ACK_CTRL_IGMP_JOIN |
-                         AR8327_FRAME_ACK_CTRL_IGMP_LEAVE) <<
-                        AR8327_FRAME_ACK_CTRL_S(port);
-
-       if (enable) {
-               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
-                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S,
-                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S);
-               ar8xxx_reg_set(priv, reg_frame_ack, val_frame_ack);
-       } else {
-               ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL1,
-                          BIT(port) << AR8327_FWD_CTRL1_IGMP_S,
-                          BIT(port) << AR8327_FWD_CTRL1_MC_FLOOD_S);
-               ar8xxx_reg_clear(priv, reg_frame_ack, val_frame_ack);
-       }
-}
-
-static void
-ar8327_vtu_op(struct ar8xxx_priv *priv, u32 op, u32 val)
-{
-       if (ar8216_wait_bit(priv, AR8327_REG_VTU_FUNC1,
-                           AR8327_VTU_FUNC1_BUSY, 0))
-               return;
-
-       if ((op & AR8327_VTU_FUNC1_OP) == AR8327_VTU_FUNC1_OP_LOAD)
-               ar8xxx_write(priv, AR8327_REG_VTU_FUNC0, val);
-
-       op |= AR8327_VTU_FUNC1_BUSY;
-       ar8xxx_write(priv, AR8327_REG_VTU_FUNC1, op);
-}
-
-static void
-ar8327_vtu_flush(struct ar8xxx_priv *priv)
-{
-       ar8327_vtu_op(priv, AR8327_VTU_FUNC1_OP_FLUSH, 0);
-}
-
-static void
-ar8327_vtu_load_vlan(struct ar8xxx_priv *priv, u32 vid, u32 port_mask)
-{
-       u32 op;
-       u32 val;
-       int i;
-
-       op = AR8327_VTU_FUNC1_OP_LOAD | (vid << AR8327_VTU_FUNC1_VID_S);
-       val = AR8327_VTU_FUNC0_VALID | AR8327_VTU_FUNC0_IVL;
-       for (i = 0; i < AR8327_NUM_PORTS; i++) {
-               u32 mode;
-
-               if ((port_mask & BIT(i)) == 0)
-                       mode = AR8327_VTU_FUNC0_EG_MODE_NOT;
-               else if (priv->vlan == 0)
-                       mode = AR8327_VTU_FUNC0_EG_MODE_KEEP;
-               else if ((priv->vlan_tagged & BIT(i)) || (priv->vlan_id[priv->pvid[i]] != vid))
-                       mode = AR8327_VTU_FUNC0_EG_MODE_TAG;
-               else
-                       mode = AR8327_VTU_FUNC0_EG_MODE_UNTAG;
-
-               val |= mode << AR8327_VTU_FUNC0_EG_MODE_S(i);
-       }
-       ar8327_vtu_op(priv, op, val);
-}
-
-static void
-ar8327_setup_port(struct ar8xxx_priv *priv, int port, u32 members)
-{
-       u32 t;
-       u32 egress, ingress;
-       u32 pvid = priv->vlan_id[priv->pvid[port]];
-
-       if (priv->vlan) {
-               egress = AR8327_PORT_VLAN1_OUT_MODE_UNMOD;
-               ingress = AR8216_IN_SECURE;
-       } else {
-               egress = AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH;
-               ingress = AR8216_IN_PORT_ONLY;
-       }
-
-       t = pvid << AR8327_PORT_VLAN0_DEF_SVID_S;
-       t |= pvid << AR8327_PORT_VLAN0_DEF_CVID_S;
-       if (priv->vlan && priv->port_vlan_prio[port]) {
-               u32 prio = priv->port_vlan_prio[port];
-
-               t |= prio << AR8327_PORT_VLAN0_DEF_SPRI_S;
-               t |= prio << AR8327_PORT_VLAN0_DEF_CPRI_S;
-       }
-       ar8xxx_write(priv, AR8327_REG_PORT_VLAN0(port), t);
-
-       t = AR8327_PORT_VLAN1_PORT_VLAN_PROP;
-       t |= egress << AR8327_PORT_VLAN1_OUT_MODE_S;
-       if (priv->vlan && priv->port_vlan_prio[port])
-               t |= AR8327_PORT_VLAN1_VLAN_PRI_PROP;
-
-       ar8xxx_write(priv, AR8327_REG_PORT_VLAN1(port), t);
-
-       t = members;
-       t |= AR8327_PORT_LOOKUP_LEARN;
-       t |= ingress << AR8327_PORT_LOOKUP_IN_MODE_S;
-       t |= AR8216_PORT_STATE_FORWARD << AR8327_PORT_LOOKUP_STATE_S;
-       ar8xxx_write(priv, AR8327_REG_PORT_LOOKUP(port), t);
-}
-
-static int
-ar8327_sw_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       u8 ports = priv->vlan_table[val->port_vlan];
-       int i;
-
-       val->len = 0;
-       for (i = 0; i < dev->ports; i++) {
-               struct switch_port *p;
-
-               if (!(ports & (1 << i)))
-                       continue;
-
-               p = &val->value.ports[val->len++];
-               p->id = i;
-               if ((priv->vlan_tagged & (1 << i)) || (priv->pvid[i] != val->port_vlan))
-                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
-               else
-                       p->flags = 0;
-       }
-       return 0;
-}
-
-static int
-ar8327_sw_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       u8 *vt = &priv->vlan_table[val->port_vlan];
-       int i;
-
-       *vt = 0;
-       for (i = 0; i < val->len; i++) {
-               struct switch_port *p = &val->value.ports[i];
-
-               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
-                       if (val->port_vlan == priv->pvid[p->id]) {
-                               priv->vlan_tagged |= (1 << p->id);
-                       }
-               } else {
-                       priv->vlan_tagged &= ~(1 << p->id);
-                       priv->pvid[p->id] = val->port_vlan;
-               }
-
-               *vt |= 1 << p->id;
-       }
-       return 0;
-}
-
-static void
-ar8327_set_mirror_regs(struct ar8xxx_priv *priv)
-{
-       int port;
-
-       /* reset all mirror registers */
-       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
-                  AR8327_FWD_CTRL0_MIRROR_PORT,
-                  (0xF << AR8327_FWD_CTRL0_MIRROR_PORT_S));
-       for (port = 0; port < AR8327_NUM_PORTS; port++) {
-               ar8xxx_reg_clear(priv, AR8327_REG_PORT_LOOKUP(port),
-                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
-
-               ar8xxx_reg_clear(priv, AR8327_REG_PORT_HOL_CTRL1(port),
-                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
-       }
-
-       /* now enable mirroring if necessary */
-       if (priv->source_port >= AR8327_NUM_PORTS ||
-           priv->monitor_port >= AR8327_NUM_PORTS ||
-           priv->source_port == priv->monitor_port) {
-               return;
-       }
-
-       ar8xxx_rmw(priv, AR8327_REG_FWD_CTRL0,
-                  AR8327_FWD_CTRL0_MIRROR_PORT,
-                  (priv->monitor_port << AR8327_FWD_CTRL0_MIRROR_PORT_S));
-
-       if (priv->mirror_rx)
-               ar8xxx_reg_set(priv, AR8327_REG_PORT_LOOKUP(priv->source_port),
-                          AR8327_PORT_LOOKUP_ING_MIRROR_EN);
-
-       if (priv->mirror_tx)
-               ar8xxx_reg_set(priv, AR8327_REG_PORT_HOL_CTRL1(priv->source_port),
-                          AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN);
-}
-
-static int
-ar8327_sw_set_eee(struct switch_dev *dev,
-                 const struct switch_attr *attr,
-                 struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       struct ar8327_data *data = priv->chip_data;
-       int port = val->port_vlan;
-       int phy;
-
-       if (port >= dev->ports)
-               return -EINVAL;
-       if (port == 0 || port == 6)
-               return -EOPNOTSUPP;
-
-       phy = port - 1;
-
-       data->eee[phy] = !!(val->value.i);
-
-       return 0;
-}
-
-static int
-ar8327_sw_get_eee(struct switch_dev *dev,
-                 const struct switch_attr *attr,
-                 struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       const struct ar8327_data *data = priv->chip_data;
-       int port = val->port_vlan;
-       int phy;
-
-       if (port >= dev->ports)
-               return -EINVAL;
-       if (port == 0 || port == 6)
-               return -EOPNOTSUPP;
-
-       phy = port - 1;
-
-       val->value.i = data->eee[phy];
-
-       return 0;
-}
-
-static void
-ar8327_wait_atu_ready(struct ar8xxx_priv *priv, u16 r2, u16 r1)
-{
-       int timeout = 20;
-
-       while (ar8xxx_mii_read32(priv, r2, r1) & AR8327_ATU_FUNC_BUSY && --timeout) {
-               udelay(10);
-               cond_resched();
-       }
-
-       if (!timeout)
-               pr_err("ar8327: timeout waiting for atu to become ready\n");
-}
-
-static void ar8327_get_arl_entry(struct ar8xxx_priv *priv,
-                                struct arl_entry *a, u32 *status, enum arl_op op)
-{
-       struct mii_bus *bus = priv->mii_bus;
-       u16 r2, page;
-       u16 r1_data0, r1_data1, r1_data2, r1_func;
-       u32 t, val0, val1, val2;
-       int i;
-
-       split_addr(AR8327_REG_ATU_DATA0, &r1_data0, &r2, &page);
-       r2 |= 0x10;
-
-       r1_data1 = (AR8327_REG_ATU_DATA1 >> 1) & 0x1e;
-       r1_data2 = (AR8327_REG_ATU_DATA2 >> 1) & 0x1e;
-       r1_func  = (AR8327_REG_ATU_FUNC >> 1) & 0x1e;
-
-       switch (op) {
-       case AR8XXX_ARL_INITIALIZE:
-               /* all ATU registers are on the same page
-               * therefore set page only once
-               */
-               bus->write(bus, 0x18, 0, page);
-               wait_for_page_switch();
-
-               ar8327_wait_atu_ready(priv, r2, r1_func);
-
-               ar8xxx_mii_write32(priv, r2, r1_data0, 0);
-               ar8xxx_mii_write32(priv, r2, r1_data1, 0);
-               ar8xxx_mii_write32(priv, r2, r1_data2, 0);
-               break;
-       case AR8XXX_ARL_GET_NEXT:
-               ar8xxx_mii_write32(priv, r2, r1_func,
-                                  AR8327_ATU_FUNC_OP_GET_NEXT |
-                                  AR8327_ATU_FUNC_BUSY);
-               ar8327_wait_atu_ready(priv, r2, r1_func);
-
-               val0 = ar8xxx_mii_read32(priv, r2, r1_data0);
-               val1 = ar8xxx_mii_read32(priv, r2, r1_data1);
-               val2 = ar8xxx_mii_read32(priv, r2, r1_data2);
-
-               *status = val2 & AR8327_ATU_STATUS;
-               if (!*status)
-                       break;
-
-               i = 0;
-               t = AR8327_ATU_PORT0;
-               while (!(val1 & t) && ++i < AR8327_NUM_PORTS)
-                       t <<= 1;
-
-               a->port = i;
-               a->mac[0] = (val0 & AR8327_ATU_ADDR0) >> AR8327_ATU_ADDR0_S;
-               a->mac[1] = (val0 & AR8327_ATU_ADDR1) >> AR8327_ATU_ADDR1_S;
-               a->mac[2] = (val0 & AR8327_ATU_ADDR2) >> AR8327_ATU_ADDR2_S;
-               a->mac[3] = (val0 & AR8327_ATU_ADDR3) >> AR8327_ATU_ADDR3_S;
-               a->mac[4] = (val1 & AR8327_ATU_ADDR4) >> AR8327_ATU_ADDR4_S;
-               a->mac[5] = (val1 & AR8327_ATU_ADDR5) >> AR8327_ATU_ADDR5_S;
-               break;
-       }
-}
-
-static int
-ar8327_sw_hw_apply(struct switch_dev *dev)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       const struct ar8327_data *data = priv->chip_data;
-       int ret, i;
-
-       ret = ar8xxx_sw_hw_apply(dev);
-       if (ret)
-               return ret;
-
-       for (i=0; i < AR8XXX_NUM_PHYS; i++) {
-               if (data->eee[i])
-                       ar8xxx_reg_clear(priv, AR8327_REG_EEE_CTRL,
-                              AR8327_EEE_CTRL_DISABLE_PHY(i));
-               else
-                       ar8xxx_reg_set(priv, AR8327_REG_EEE_CTRL,
-                              AR8327_EEE_CTRL_DISABLE_PHY(i));
-       }
-
-       return 0;
-}
-
-int
-ar8327_sw_get_port_igmp_snooping(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int port = val->port_vlan;
-
-       if (port >= dev->ports)
-               return -EINVAL;
-
-       mutex_lock(&priv->reg_mutex);
-       val->value.i = ar8327_get_port_igmp(priv, port);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8327_sw_set_port_igmp_snooping(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int port = val->port_vlan;
-
-       if (port >= dev->ports)
-               return -EINVAL;
-
-       mutex_lock(&priv->reg_mutex);
-       ar8327_set_port_igmp(priv, port, val->value.i);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8327_sw_get_igmp_snooping(struct switch_dev *dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val)
-{
-       int port;
-
-       for (port = 0; port < dev->ports; port++) {
-               val->port_vlan = port;
-               if (ar8327_sw_get_port_igmp_snooping(dev, attr, val) ||
-                   !val->value.i)
-                       break;
-       }
-
-       return 0;
-}
-
-int
-ar8327_sw_set_igmp_snooping(struct switch_dev *dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val)
-{
-       int port;
-
-       for (port = 0; port < dev->ports; port++) {
-               val->port_vlan = port;
-               if (ar8327_sw_set_port_igmp_snooping(dev, attr, val))
-                       break;
-       }
-
-       return 0;
-}
-
-int
-ar8327_sw_get_igmp_v3(struct switch_dev *dev,
-                     const struct switch_attr *attr,
-                     struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       u32 val_reg;
-
-       mutex_lock(&priv->reg_mutex);
-       val_reg = ar8xxx_read(priv, AR8327_REG_FRAME_ACK_CTRL1);
-       val->value.i = ((val_reg & AR8327_FRAME_ACK_CTRL_IGMP_V3_EN) != 0);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-int
-ar8327_sw_set_igmp_v3(struct switch_dev *dev,
-                     const struct switch_attr *attr,
-                     struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-
-       mutex_lock(&priv->reg_mutex);
-       if (val->value.i)
-               ar8xxx_reg_set(priv, AR8327_REG_FRAME_ACK_CTRL1,
-                              AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
-       else
-               ar8xxx_reg_clear(priv, AR8327_REG_FRAME_ACK_CTRL1,
-                                AR8327_FRAME_ACK_CTRL_IGMP_V3_EN);
-       mutex_unlock(&priv->reg_mutex);
-
-       return 0;
-}
-
-static int
-ar8327_sw_set_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
-                            struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int port = val->port_vlan;
-
-       if (port >= dev->ports)
-               return -EINVAL;
-       if (port == 0 || port == 6)
-               return -EOPNOTSUPP;
-       if (val->value.i < 0 || val->value.i > 7)
-               return -EINVAL;
-
-       priv->port_vlan_prio[port] = val->value.i;
-
-       return 0;
-}
-
-static int
-ar8327_sw_get_port_vlan_prio(struct switch_dev *dev, const struct switch_attr *attr,
-                  struct switch_val *val)
-{
-       struct ar8xxx_priv *priv = swdev_to_ar8xxx(dev);
-       int port = val->port_vlan;
-
-       val->value.i = priv->port_vlan_prio[port];
-
-       return 0;
-}
-
-static const struct switch_attr ar8327_sw_attr_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = ar8xxx_sw_set_vlan,
-               .get = ar8xxx_sw_get_vlan,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mibs",
-               .description = "Reset all MIB counters",
-               .set = ar8xxx_sw_set_reset_mibs,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_rx",
-               .description = "Enable mirroring of RX packets",
-               .set = ar8xxx_sw_set_mirror_rx_enable,
-               .get = ar8xxx_sw_get_mirror_rx_enable,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_tx",
-               .description = "Enable mirroring of TX packets",
-               .set = ar8xxx_sw_set_mirror_tx_enable,
-               .get = ar8xxx_sw_get_mirror_tx_enable,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "mirror_monitor_port",
-               .description = "Mirror monitor port",
-               .set = ar8xxx_sw_set_mirror_monitor_port,
-               .get = ar8xxx_sw_get_mirror_monitor_port,
-               .max = AR8327_NUM_PORTS - 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "mirror_source_port",
-               .description = "Mirror source port",
-               .set = ar8xxx_sw_set_mirror_source_port,
-               .get = ar8xxx_sw_get_mirror_source_port,
-               .max = AR8327_NUM_PORTS - 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "arl_age_time",
-               .description = "ARL age time (secs)",
-               .set = ar8xxx_sw_set_arl_age_time,
-               .get = ar8xxx_sw_get_arl_age_time,
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "arl_table",
-               .description = "Get ARL table",
-               .set = NULL,
-               .get = ar8xxx_sw_get_arl_table,
-       },
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "flush_arl_table",
-               .description = "Flush ARL table",
-               .set = ar8xxx_sw_set_flush_arl_table,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "igmp_snooping",
-               .description = "Enable IGMP Snooping",
-               .set = ar8327_sw_set_igmp_snooping,
-               .get = ar8327_sw_get_igmp_snooping,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "igmp_v3",
-               .description = "Enable IGMPv3 support",
-               .set = ar8327_sw_set_igmp_v3,
-               .get = ar8327_sw_get_igmp_v3,
-               .max = 1
-       },
-};
-
-static const struct switch_attr ar8327_sw_attr_port[] = {
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mib",
-               .description = "Reset single port MIB counters",
-               .set = ar8xxx_sw_set_port_reset_mib,
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get port's MIB counters",
-               .set = NULL,
-               .get = ar8xxx_sw_get_port_mib,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_eee",
-               .description = "Enable EEE PHY sleep mode",
-               .set = ar8327_sw_set_eee,
-               .get = ar8327_sw_get_eee,
-               .max = 1,
-       },
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "flush_arl_table",
-               .description = "Flush port's ARL table entries",
-               .set = ar8xxx_sw_set_flush_port_arl_table,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "igmp_snooping",
-               .description = "Enable port's IGMP Snooping",
-               .set = ar8327_sw_set_port_igmp_snooping,
-               .get = ar8327_sw_get_port_igmp_snooping,
-               .max = 1
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "vlan_prio",
-               .description = "Port VLAN default priority (VLAN PCP) (0-7)",
-               .set = ar8327_sw_set_port_vlan_prio,
-               .get = ar8327_sw_get_port_vlan_prio,
-               .max = 7,
-       },
-};
-
-static const struct switch_dev_ops ar8327_sw_ops = {
-       .attr_global = {
-               .attr = ar8327_sw_attr_globals,
-               .n_attr = ARRAY_SIZE(ar8327_sw_attr_globals),
-       },
-       .attr_port = {
-               .attr = ar8327_sw_attr_port,
-               .n_attr = ARRAY_SIZE(ar8327_sw_attr_port),
-       },
-       .attr_vlan = {
-               .attr = ar8xxx_sw_attr_vlan,
-               .n_attr = ARRAY_SIZE(ar8xxx_sw_attr_vlan),
-       },
-       .get_port_pvid = ar8xxx_sw_get_pvid,
-       .set_port_pvid = ar8xxx_sw_set_pvid,
-       .get_vlan_ports = ar8327_sw_get_ports,
-       .set_vlan_ports = ar8327_sw_set_ports,
-       .apply_config = ar8327_sw_hw_apply,
-       .reset_switch = ar8xxx_sw_reset_switch,
-       .get_port_link = ar8xxx_sw_get_port_link,
-/* The following op is disabled as it hogs the CPU and degrades performance.
-   An implementation has been attempted in 4d8a66d but reading MIB data is slow
-   on ar8xxx switches.
-
-   The high CPU load has been traced down to the ar8xxx_reg_wait() call in
-   ar8xxx_mib_op(), which has to usleep_range() till the MIB busy flag set by
-   the request to update the MIB counter is cleared. */
-#if 0
-       .get_port_stats = ar8xxx_sw_get_port_stats,
-#endif
-};
-
-const struct ar8xxx_chip ar8327_chip = {
-       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
-       .config_at_probe = true,
-       .mii_lo_first = true,
-
-       .name = "Atheros AR8327",
-       .ports = AR8327_NUM_PORTS,
-       .vlans = AR8X16_MAX_VLANS,
-       .swops = &ar8327_sw_ops,
-
-       .reg_port_stats_start = 0x1000,
-       .reg_port_stats_length = 0x100,
-       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
-
-       .hw_init = ar8327_hw_init,
-       .cleanup = ar8327_cleanup,
-       .init_globals = ar8327_init_globals,
-       .init_port = ar8327_init_port,
-       .setup_port = ar8327_setup_port,
-       .read_port_status = ar8327_read_port_status,
-       .read_port_eee_status = ar8327_read_port_eee_status,
-       .atu_flush = ar8327_atu_flush,
-       .atu_flush_port = ar8327_atu_flush_port,
-       .vtu_flush = ar8327_vtu_flush,
-       .vtu_load_vlan = ar8327_vtu_load_vlan,
-       .phy_fixup = ar8327_phy_fixup,
-       .set_mirror_regs = ar8327_set_mirror_regs,
-       .get_arl_entry = ar8327_get_arl_entry,
-       .sw_hw_apply = ar8327_sw_hw_apply,
-
-       .num_mibs = ARRAY_SIZE(ar8236_mibs),
-       .mib_decs = ar8236_mibs,
-       .mib_func = AR8327_REG_MIB_FUNC
-};
-
-const struct ar8xxx_chip ar8337_chip = {
-       .caps = AR8XXX_CAP_GIGE | AR8XXX_CAP_MIB_COUNTERS,
-       .config_at_probe = true,
-       .mii_lo_first = true,
-
-       .name = "Atheros AR8337",
-       .ports = AR8327_NUM_PORTS,
-       .vlans = AR8X16_MAX_VLANS,
-       .swops = &ar8327_sw_ops,
-
-       .reg_port_stats_start = 0x1000,
-       .reg_port_stats_length = 0x100,
-       .reg_arl_ctrl = AR8327_REG_ARL_CTRL,
-
-       .hw_init = ar8327_hw_init,
-       .cleanup = ar8327_cleanup,
-       .init_globals = ar8327_init_globals,
-       .init_port = ar8327_init_port,
-       .setup_port = ar8327_setup_port,
-       .read_port_status = ar8327_read_port_status,
-       .read_port_eee_status = ar8327_read_port_eee_status,
-       .atu_flush = ar8327_atu_flush,
-       .atu_flush_port = ar8327_atu_flush_port,
-       .vtu_flush = ar8327_vtu_flush,
-       .vtu_load_vlan = ar8327_vtu_load_vlan,
-       .phy_fixup = ar8327_phy_fixup,
-       .set_mirror_regs = ar8327_set_mirror_regs,
-       .get_arl_entry = ar8327_get_arl_entry,
-       .sw_hw_apply = ar8327_sw_hw_apply,
-
-       .num_mibs = ARRAY_SIZE(ar8236_mibs),
-       .mib_decs = ar8236_mibs,
-       .mib_func = AR8327_REG_MIB_FUNC
-};
diff --git a/target/linux/generic/files/drivers/net/phy/ar8327.h b/target/linux/generic/files/drivers/net/phy/ar8327.h
deleted file mode 100644 (file)
index d53ef88..0000000
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * ar8327.h: AR8216 switch driver
- *
- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef __AR8327_H
-#define __AR8327_H
-
-#define AR8327_NUM_PORTS       7
-#define AR8327_NUM_LEDS                15
-#define AR8327_PORTS_ALL       0x7f
-#define AR8327_NUM_LED_CTRL_REGS       4
-
-#define AR8327_REG_MASK                                0x000
-
-#define AR8327_REG_PAD0_MODE                   0x004
-#define AR8327_REG_PAD5_MODE                   0x008
-#define AR8327_REG_PAD6_MODE                   0x00c
-#define   AR8327_PAD_MAC_MII_RXCLK_SEL         BIT(0)
-#define   AR8327_PAD_MAC_MII_TXCLK_SEL         BIT(1)
-#define   AR8327_PAD_MAC_MII_EN                        BIT(2)
-#define   AR8327_PAD_MAC_GMII_RXCLK_SEL                BIT(4)
-#define   AR8327_PAD_MAC_GMII_TXCLK_SEL                BIT(5)
-#define   AR8327_PAD_MAC_GMII_EN               BIT(6)
-#define   AR8327_PAD_SGMII_EN                  BIT(7)
-#define   AR8327_PAD_PHY_MII_RXCLK_SEL         BIT(8)
-#define   AR8327_PAD_PHY_MII_TXCLK_SEL         BIT(9)
-#define   AR8327_PAD_PHY_MII_EN                        BIT(10)
-#define   AR8327_PAD_PHY_GMII_PIPE_RXCLK_SEL   BIT(11)
-#define   AR8327_PAD_PHY_GMII_RXCLK_SEL                BIT(12)
-#define   AR8327_PAD_PHY_GMII_TXCLK_SEL                BIT(13)
-#define   AR8327_PAD_PHY_GMII_EN               BIT(14)
-#define   AR8327_PAD_PHYX_GMII_EN              BIT(16)
-#define   AR8327_PAD_PHYX_RGMII_EN             BIT(17)
-#define   AR8327_PAD_PHYX_MII_EN               BIT(18)
-#define   AR8327_PAD_SGMII_DELAY_EN            BIT(19)
-#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL     BITS(20, 2)
-#define   AR8327_PAD_RGMII_RXCLK_DELAY_SEL_S   20
-#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL     BITS(22, 2)
-#define   AR8327_PAD_RGMII_TXCLK_DELAY_SEL_S   22
-#define   AR8327_PAD_RGMII_RXCLK_DELAY_EN      BIT(24)
-#define   AR8327_PAD_RGMII_TXCLK_DELAY_EN      BIT(25)
-#define   AR8327_PAD_RGMII_EN                  BIT(26)
-
-#define AR8327_REG_POWER_ON_STRIP              0x010
-#define   AR8327_POWER_ON_STRIP_POWER_ON_SEL   BIT(31)
-#define   AR8327_POWER_ON_STRIP_LED_OPEN_EN    BIT(24)
-#define   AR8327_POWER_ON_STRIP_SERDES_AEN     BIT(7)
-
-#define AR8327_REG_INT_STATUS0                 0x020
-#define   AR8327_INT0_VT_DONE                  BIT(20)
-
-#define AR8327_REG_INT_STATUS1                 0x024
-#define AR8327_REG_INT_MASK0                   0x028
-#define AR8327_REG_INT_MASK1                   0x02c
-
-#define AR8327_REG_MODULE_EN                   0x030
-#define   AR8327_MODULE_EN_MIB                 BIT(0)
-
-#define AR8327_REG_MIB_FUNC                    0x034
-#define   AR8327_MIB_CPU_KEEP                  BIT(20)
-
-#define AR8327_REG_SERVICE_TAG                 0x048
-#define AR8327_REG_LED_CTRL(_i)                        (0x050 + (_i) * 4)
-#define AR8327_REG_LED_CTRL0                   0x050
-#define AR8327_REG_LED_CTRL1                   0x054
-#define AR8327_REG_LED_CTRL2                   0x058
-#define AR8327_REG_LED_CTRL3                   0x05c
-#define AR8327_REG_MAC_ADDR0                   0x060
-#define AR8327_REG_MAC_ADDR1                   0x064
-
-#define AR8327_REG_MAX_FRAME_SIZE              0x078
-#define   AR8327_MAX_FRAME_SIZE_MTU            BITS(0, 14)
-
-#define AR8327_REG_PORT_STATUS(_i)             (0x07c + (_i) * 4)
-#define   AR8327_PORT_STATUS_TXFLOW_AUTO       BIT(10)
-#define   AR8327_PORT_STATUS_RXFLOW_AUTO       BIT(11)
-
-#define AR8327_REG_HEADER_CTRL                 0x098
-#define AR8327_REG_PORT_HEADER(_i)             (0x09c + (_i) * 4)
-
-#define AR8327_REG_SGMII_CTRL                  0x0e0
-#define   AR8327_SGMII_CTRL_EN_PLL             BIT(1)
-#define   AR8327_SGMII_CTRL_EN_RX              BIT(2)
-#define   AR8327_SGMII_CTRL_EN_TX              BIT(3)
-
-#define AR8327_REG_EEE_CTRL                    0x100
-#define   AR8327_EEE_CTRL_DISABLE_PHY(_i)      BIT(4 + (_i) * 2)
-
-#define AR8327_REG_FRAME_ACK_CTRL0             0x210
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN0   BIT(0)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN0  BIT(1)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN0 BIT(2)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN0      BIT(3)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN0       BIT(4)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN0    BIT(5)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN0    BIT(6)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN1   BIT(8)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN1  BIT(9)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN1 BIT(10)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN1      BIT(11)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN1       BIT(12)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN1    BIT(13)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN1    BIT(14)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN2   BIT(16)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN2  BIT(17)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN2 BIT(18)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN2      BIT(19)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN2       BIT(20)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN2    BIT(21)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN2    BIT(22)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN3   BIT(24)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN3  BIT(25)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN3 BIT(26)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN3      BIT(27)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN3       BIT(28)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN3    BIT(29)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN3    BIT(30)
-
-#define AR8327_REG_FRAME_ACK_CTRL1             0x214
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN4   BIT(0)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN4  BIT(1)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN4 BIT(2)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN4      BIT(3)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN4       BIT(4)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN4    BIT(5)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN4    BIT(6)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN5   BIT(8)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN5  BIT(9)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN5 BIT(10)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN5      BIT(11)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN5       BIT(12)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN5    BIT(13)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN5    BIT(14)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD_EN6   BIT(16)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN_EN6  BIT(17)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE_EN6 BIT(18)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL_EN6      BIT(19)
-#define   AR8327_FRAME_ACK_CTRL_DHCP_EN6       BIT(20)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK_EN6    BIT(21)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ_EN6    BIT(22)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_V3_EN     BIT(24)
-#define   AR8327_FRAME_ACK_CTRL_PPPOE_EN       BIT(25)
-
-#define AR8327_REG_FRAME_ACK_CTRL(_i)          (0x210 + ((_i) / 4) * 0x4)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_MLD       BIT(0)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_JOIN      BIT(1)
-#define   AR8327_FRAME_ACK_CTRL_IGMP_LEAVE     BIT(2)
-#define   AR8327_FRAME_ACK_CTRL_EAPOL          BIT(3)
-#define   AR8327_FRAME_ACK_CTRL_DHCP           BIT(4)
-#define   AR8327_FRAME_ACK_CTRL_ARP_ACK                BIT(5)
-#define   AR8327_FRAME_ACK_CTRL_ARP_REQ                BIT(6)
-#define   AR8327_FRAME_ACK_CTRL_S(_i)          (((_i) % 4) * 8)
-
-#define AR8327_REG_PORT_VLAN0(_i)              (0x420 + (_i) * 0x8)
-#define   AR8327_PORT_VLAN0_DEF_PRI_MASK       BITS(0, 3)
-#define   AR8327_PORT_VLAN0_DEF_SVID           BITS(0, 12)
-#define   AR8327_PORT_VLAN0_DEF_SVID_S         0
-#define   AR8327_PORT_VLAN0_DEF_SPRI           BITS(13, 3)
-#define   AR8327_PORT_VLAN0_DEF_SPRI_S         13
-#define   AR8327_PORT_VLAN0_DEF_CVID           BITS(16, 12)
-#define   AR8327_PORT_VLAN0_DEF_CVID_S         16
-#define   AR8327_PORT_VLAN0_DEF_CPRI           BITS(29, 3)
-#define   AR8327_PORT_VLAN0_DEF_CPRI_S         29
-
-#define AR8327_REG_PORT_VLAN1(_i)              (0x424 + (_i) * 0x8)
-#define   AR8327_PORT_VLAN1_VLAN_PRI_PROP      BIT(4)
-#define   AR8327_PORT_VLAN1_PORT_VLAN_PROP     BIT(6)
-#define   AR8327_PORT_VLAN1_OUT_MODE           BITS(12, 2)
-#define   AR8327_PORT_VLAN1_OUT_MODE_S         12
-#define   AR8327_PORT_VLAN1_OUT_MODE_UNMOD     0
-#define   AR8327_PORT_VLAN1_OUT_MODE_UNTAG     1
-#define   AR8327_PORT_VLAN1_OUT_MODE_TAG       2
-#define   AR8327_PORT_VLAN1_OUT_MODE_UNTOUCH   3
-
-#define AR8327_REG_ATU_DATA0                   0x600
-#define   AR8327_ATU_ADDR0                     BITS(0, 8)
-#define   AR8327_ATU_ADDR0_S                   0
-#define   AR8327_ATU_ADDR1                     BITS(8, 8)
-#define   AR8327_ATU_ADDR1_S                   8
-#define   AR8327_ATU_ADDR2                     BITS(16, 8)
-#define   AR8327_ATU_ADDR2_S                   16
-#define   AR8327_ATU_ADDR3                     BITS(24, 8)
-#define   AR8327_ATU_ADDR3_S                   24
-#define AR8327_REG_ATU_DATA1                   0x604
-#define   AR8327_ATU_ADDR4                     BITS(0, 8)
-#define   AR8327_ATU_ADDR4_S                   0
-#define   AR8327_ATU_ADDR5                     BITS(8, 8)
-#define   AR8327_ATU_ADDR5_S                   8
-#define   AR8327_ATU_PORTS                     BITS(16, 7)
-#define   AR8327_ATU_PORT0                     BIT(16)
-#define   AR8327_ATU_PORT1                     BIT(17)
-#define   AR8327_ATU_PORT2                     BIT(18)
-#define   AR8327_ATU_PORT3                     BIT(19)
-#define   AR8327_ATU_PORT4                     BIT(20)
-#define   AR8327_ATU_PORT5                     BIT(21)
-#define   AR8327_ATU_PORT6                     BIT(22)
-#define AR8327_REG_ATU_DATA2                   0x608
-#define   AR8327_ATU_STATUS                    BITS(0, 4)
-
-#define AR8327_REG_ATU_FUNC                    0x60c
-#define   AR8327_ATU_FUNC_OP                   BITS(0, 4)
-#define   AR8327_ATU_FUNC_OP_NOOP              0x0
-#define   AR8327_ATU_FUNC_OP_FLUSH             0x1
-#define   AR8327_ATU_FUNC_OP_LOAD              0x2
-#define   AR8327_ATU_FUNC_OP_PURGE             0x3
-#define   AR8327_ATU_FUNC_OP_FLUSH_UNLOCKED    0x4
-#define   AR8327_ATU_FUNC_OP_FLUSH_PORT                0x5
-#define   AR8327_ATU_FUNC_OP_GET_NEXT          0x6
-#define   AR8327_ATU_FUNC_OP_SEARCH_MAC                0x7
-#define   AR8327_ATU_FUNC_OP_CHANGE_TRUNK      0x8
-#define   AR8327_ATU_PORT_NUM                  BITS(8, 4)
-#define   AR8327_ATU_PORT_NUM_S                        8
-#define   AR8327_ATU_FUNC_BUSY                 BIT(31)
-
-#define AR8327_REG_VTU_FUNC0                   0x0610
-#define   AR8327_VTU_FUNC0_EG_MODE             BITS(4, 14)
-#define   AR8327_VTU_FUNC0_EG_MODE_S(_i)       (4 + (_i) * 2)
-#define   AR8327_VTU_FUNC0_EG_MODE_KEEP                0
-#define   AR8327_VTU_FUNC0_EG_MODE_UNTAG       1
-#define   AR8327_VTU_FUNC0_EG_MODE_TAG         2
-#define   AR8327_VTU_FUNC0_EG_MODE_NOT         3
-#define   AR8327_VTU_FUNC0_IVL                 BIT(19)
-#define   AR8327_VTU_FUNC0_VALID               BIT(20)
-
-#define AR8327_REG_VTU_FUNC1                   0x0614
-#define   AR8327_VTU_FUNC1_OP                  BITS(0, 3)
-#define   AR8327_VTU_FUNC1_OP_NOOP             0
-#define   AR8327_VTU_FUNC1_OP_FLUSH            1
-#define   AR8327_VTU_FUNC1_OP_LOAD             2
-#define   AR8327_VTU_FUNC1_OP_PURGE            3
-#define   AR8327_VTU_FUNC1_OP_REMOVE_PORT      4
-#define   AR8327_VTU_FUNC1_OP_GET_NEXT         5
-#define   AR8327_VTU_FUNC1_OP_GET_ONE          6
-#define   AR8327_VTU_FUNC1_FULL                        BIT(4)
-#define   AR8327_VTU_FUNC1_PORT                        BIT(8, 4)
-#define   AR8327_VTU_FUNC1_PORT_S              8
-#define   AR8327_VTU_FUNC1_VID                 BIT(16, 12)
-#define   AR8327_VTU_FUNC1_VID_S               16
-#define   AR8327_VTU_FUNC1_BUSY                        BIT(31)
-
-#define AR8327_REG_ARL_CTRL                    0x0618
-
-#define AR8327_REG_FWD_CTRL0                   0x620
-#define   AR8327_FWD_CTRL0_CPU_PORT_EN         BIT(10)
-#define   AR8327_FWD_CTRL0_MIRROR_PORT         BITS(4, 4)
-#define   AR8327_FWD_CTRL0_MIRROR_PORT_S       4
-
-#define AR8327_REG_FWD_CTRL1                   0x624
-#define   AR8327_FWD_CTRL1_UC_FLOOD            BITS(0, 7)
-#define   AR8327_FWD_CTRL1_UC_FLOOD_S          0
-#define   AR8327_FWD_CTRL1_MC_FLOOD            BITS(8, 7)
-#define   AR8327_FWD_CTRL1_MC_FLOOD_S          8
-#define   AR8327_FWD_CTRL1_BC_FLOOD            BITS(16, 7)
-#define   AR8327_FWD_CTRL1_BC_FLOOD_S          16
-#define   AR8327_FWD_CTRL1_IGMP                        BITS(24, 7)
-#define   AR8327_FWD_CTRL1_IGMP_S              24
-
-#define AR8327_REG_PORT_LOOKUP(_i)             (0x660 + (_i) * 0xc)
-#define   AR8327_PORT_LOOKUP_MEMBER            BITS(0, 7)
-#define   AR8327_PORT_LOOKUP_IN_MODE           BITS(8, 2)
-#define   AR8327_PORT_LOOKUP_IN_MODE_S         8
-#define   AR8327_PORT_LOOKUP_STATE             BITS(16, 3)
-#define   AR8327_PORT_LOOKUP_STATE_S           16
-#define   AR8327_PORT_LOOKUP_LEARN             BIT(20)
-#define   AR8327_PORT_LOOKUP_ING_MIRROR_EN     BIT(25)
-
-#define AR8327_REG_PORT_PRIO(_i)               (0x664 + (_i) * 0xc)
-
-#define AR8327_REG_PORT_HOL_CTRL1(_i)          (0x974 + (_i) * 0x8)
-#define   AR8327_PORT_HOL_CTRL1_EG_MIRROR_EN   BIT(16)
-
-#define AR8337_PAD_MAC06_EXCHANGE_EN           BIT(31)
-
-enum ar8327_led_pattern {
-       AR8327_LED_PATTERN_OFF = 0,
-       AR8327_LED_PATTERN_BLINK,
-       AR8327_LED_PATTERN_ON,
-       AR8327_LED_PATTERN_RULE,
-};
-
-struct ar8327_led_entry {
-       unsigned reg;
-       unsigned shift;
-};
-
-struct ar8327_led {
-       struct led_classdev cdev;
-       struct ar8xxx_priv *sw_priv;
-
-       char *name;
-       bool active_low;
-       u8 led_num;
-       enum ar8327_led_mode mode;
-
-       struct mutex mutex;
-       spinlock_t lock;
-       struct work_struct led_work;
-       bool enable_hw_mode;
-       enum ar8327_led_pattern pattern;
-};
-
-struct ar8327_data {
-       u32 port0_status;
-       u32 port6_status;
-
-       struct ar8327_led **leds;
-       unsigned int num_leds;
-
-       /* all fields below are cleared on reset */
-       bool eee[AR8XXX_NUM_PHYS];
-};
-
-#endif
diff --git a/target/linux/generic/files/drivers/net/phy/b53/Kconfig b/target/linux/generic/files/drivers/net/phy/b53/Kconfig
deleted file mode 100644 (file)
index 08287e7..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-menuconfig SWCONFIG_B53
-       tristate "Broadcom bcm53xx managed switch support"
-       depends on SWCONFIG
-       help
-         This driver adds support for Broadcom managed switch chips. It supports
-         BCM5325E, BCM5365, BCM539x, BCM53115 and BCM53125 as well as BCM63XX
-         integrated switches.
-
-config SWCONFIG_B53_SPI_DRIVER
-       tristate "B53 SPI connected switch driver"
-       depends on SWCONFIG_B53 && SPI
-       help
-         Select to enable support for registering switches configured through SPI.
-
-config SWCONFIG_B53_PHY_DRIVER
-       tristate "B53 MDIO connected switch driver"
-       depends on SWCONFIG_B53
-       select SWCONFIG_B53_PHY_FIXUP
-       help
-         Select to enable support for registering switches configured through MDIO.
-
-config SWCONFIG_B53_MMAP_DRIVER
-       tristate "B53 MMAP connected switch driver"
-       depends on SWCONFIG_B53
-       help
-         Select to enable support for memory-mapped switches like the BCM63XX
-         integrated switches.
-
-config SWCONFIG_B53_SRAB_DRIVER
-       tristate "B53 SRAB connected switch driver"
-       depends on SWCONFIG_B53
-       help
-         Select to enable support for memory-mapped Switch Register Access
-         Bridge Registers (SRAB) like it is found on the BCM53010
-
-config SWCONFIG_B53_PHY_FIXUP
-       bool
diff --git a/target/linux/generic/files/drivers/net/phy/b53/Makefile b/target/linux/generic/files/drivers/net/phy/b53/Makefile
deleted file mode 100644 (file)
index 13ff366..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-obj-$(CONFIG_SWCONFIG_B53)             += b53_common.o
-
-obj-$(CONFIG_SWCONFIG_B53_PHY_FIXUP)   += b53_phy_fixup.o
-
-obj-$(CONFIG_SWCONFIG_B53_MMAP_DRIVER) += b53_mmap.o
-obj-$(CONFIG_SWCONFIG_B53_SRAB_DRIVER) += b53_srab.o
-obj-$(CONFIG_SWCONFIG_B53_PHY_DRIVER)  += b53_mdio.o
-obj-$(CONFIG_SWCONFIG_B53_SPI_DRIVER)  += b53_spi.o
-
-ccflags-y                              += -Werror
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_common.c b/target/linux/generic/files/drivers/net/phy/b53/b53_common.c
deleted file mode 100644 (file)
index 670588c..0000000
+++ /dev/null
@@ -1,1722 +0,0 @@
-/*
- * B53 switch driver main logic
- *
- * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/delay.h>
-#include <linux/export.h>
-#include <linux/gpio.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/switch.h>
-#include <linux/phy.h>
-#include <linux/of.h>
-#include <linux/of_net.h>
-#include <linux/platform_data/b53.h>
-
-#include "b53_regs.h"
-#include "b53_priv.h"
-
-/* buffer size needed for displaying all MIBs with max'd values */
-#define B53_BUF_SIZE   1188
-
-struct b53_mib_desc {
-       u8 size;
-       u8 offset;
-       const char *name;
-};
-
-/* BCM5365 MIB counters */
-static const struct b53_mib_desc b53_mibs_65[] = {
-       { 8, 0x00, "TxOctets" },
-       { 4, 0x08, "TxDropPkts" },
-       { 4, 0x10, "TxBroadcastPkts" },
-       { 4, 0x14, "TxMulticastPkts" },
-       { 4, 0x18, "TxUnicastPkts" },
-       { 4, 0x1c, "TxCollisions" },
-       { 4, 0x20, "TxSingleCollision" },
-       { 4, 0x24, "TxMultipleCollision" },
-       { 4, 0x28, "TxDeferredTransmit" },
-       { 4, 0x2c, "TxLateCollision" },
-       { 4, 0x30, "TxExcessiveCollision" },
-       { 4, 0x38, "TxPausePkts" },
-       { 8, 0x44, "RxOctets" },
-       { 4, 0x4c, "RxUndersizePkts" },
-       { 4, 0x50, "RxPausePkts" },
-       { 4, 0x54, "Pkts64Octets" },
-       { 4, 0x58, "Pkts65to127Octets" },
-       { 4, 0x5c, "Pkts128to255Octets" },
-       { 4, 0x60, "Pkts256to511Octets" },
-       { 4, 0x64, "Pkts512to1023Octets" },
-       { 4, 0x68, "Pkts1024to1522Octets" },
-       { 4, 0x6c, "RxOversizePkts" },
-       { 4, 0x70, "RxJabbers" },
-       { 4, 0x74, "RxAlignmentErrors" },
-       { 4, 0x78, "RxFCSErrors" },
-       { 8, 0x7c, "RxGoodOctets" },
-       { 4, 0x84, "RxDropPkts" },
-       { 4, 0x88, "RxUnicastPkts" },
-       { 4, 0x8c, "RxMulticastPkts" },
-       { 4, 0x90, "RxBroadcastPkts" },
-       { 4, 0x94, "RxSAChanges" },
-       { 4, 0x98, "RxFragments" },
-       { },
-};
-
-#define B63XX_MIB_TXB_ID       0       /* TxOctets */
-#define B63XX_MIB_RXB_ID       14      /* RxOctets */
-
-/* BCM63xx MIB counters */
-static const struct b53_mib_desc b53_mibs_63xx[] = {
-       { 8, 0x00, "TxOctets" },
-       { 4, 0x08, "TxDropPkts" },
-       { 4, 0x0c, "TxQoSPkts" },
-       { 4, 0x10, "TxBroadcastPkts" },
-       { 4, 0x14, "TxMulticastPkts" },
-       { 4, 0x18, "TxUnicastPkts" },
-       { 4, 0x1c, "TxCollisions" },
-       { 4, 0x20, "TxSingleCollision" },
-       { 4, 0x24, "TxMultipleCollision" },
-       { 4, 0x28, "TxDeferredTransmit" },
-       { 4, 0x2c, "TxLateCollision" },
-       { 4, 0x30, "TxExcessiveCollision" },
-       { 4, 0x38, "TxPausePkts" },
-       { 8, 0x3c, "TxQoSOctets" },
-       { 8, 0x44, "RxOctets" },
-       { 4, 0x4c, "RxUndersizePkts" },
-       { 4, 0x50, "RxPausePkts" },
-       { 4, 0x54, "Pkts64Octets" },
-       { 4, 0x58, "Pkts65to127Octets" },
-       { 4, 0x5c, "Pkts128to255Octets" },
-       { 4, 0x60, "Pkts256to511Octets" },
-       { 4, 0x64, "Pkts512to1023Octets" },
-       { 4, 0x68, "Pkts1024to1522Octets" },
-       { 4, 0x6c, "RxOversizePkts" },
-       { 4, 0x70, "RxJabbers" },
-       { 4, 0x74, "RxAlignmentErrors" },
-       { 4, 0x78, "RxFCSErrors" },
-       { 8, 0x7c, "RxGoodOctets" },
-       { 4, 0x84, "RxDropPkts" },
-       { 4, 0x88, "RxUnicastPkts" },
-       { 4, 0x8c, "RxMulticastPkts" },
-       { 4, 0x90, "RxBroadcastPkts" },
-       { 4, 0x94, "RxSAChanges" },
-       { 4, 0x98, "RxFragments" },
-       { 4, 0xa0, "RxSymbolErrors" },
-       { 4, 0xa4, "RxQoSPkts" },
-       { 8, 0xa8, "RxQoSOctets" },
-       { 4, 0xb0, "Pkts1523to2047Octets" },
-       { 4, 0xb4, "Pkts2048to4095Octets" },
-       { 4, 0xb8, "Pkts4096to8191Octets" },
-       { 4, 0xbc, "Pkts8192to9728Octets" },
-       { 4, 0xc0, "RxDiscarded" },
-       { }
-};
-
-#define B53XX_MIB_TXB_ID       0       /* TxOctets */
-#define B53XX_MIB_RXB_ID       12      /* RxOctets */
-
-/* MIB counters */
-static const struct b53_mib_desc b53_mibs[] = {
-       { 8, 0x00, "TxOctets" },
-       { 4, 0x08, "TxDropPkts" },
-       { 4, 0x10, "TxBroadcastPkts" },
-       { 4, 0x14, "TxMulticastPkts" },
-       { 4, 0x18, "TxUnicastPkts" },
-       { 4, 0x1c, "TxCollisions" },
-       { 4, 0x20, "TxSingleCollision" },
-       { 4, 0x24, "TxMultipleCollision" },
-       { 4, 0x28, "TxDeferredTransmit" },
-       { 4, 0x2c, "TxLateCollision" },
-       { 4, 0x30, "TxExcessiveCollision" },
-       { 4, 0x38, "TxPausePkts" },
-       { 8, 0x50, "RxOctets" },
-       { 4, 0x58, "RxUndersizePkts" },
-       { 4, 0x5c, "RxPausePkts" },
-       { 4, 0x60, "Pkts64Octets" },
-       { 4, 0x64, "Pkts65to127Octets" },
-       { 4, 0x68, "Pkts128to255Octets" },
-       { 4, 0x6c, "Pkts256to511Octets" },
-       { 4, 0x70, "Pkts512to1023Octets" },
-       { 4, 0x74, "Pkts1024to1522Octets" },
-       { 4, 0x78, "RxOversizePkts" },
-       { 4, 0x7c, "RxJabbers" },
-       { 4, 0x80, "RxAlignmentErrors" },
-       { 4, 0x84, "RxFCSErrors" },
-       { 8, 0x88, "RxGoodOctets" },
-       { 4, 0x90, "RxDropPkts" },
-       { 4, 0x94, "RxUnicastPkts" },
-       { 4, 0x98, "RxMulticastPkts" },
-       { 4, 0x9c, "RxBroadcastPkts" },
-       { 4, 0xa0, "RxSAChanges" },
-       { 4, 0xa4, "RxFragments" },
-       { 4, 0xa8, "RxJumboPkts" },
-       { 4, 0xac, "RxSymbolErrors" },
-       { 4, 0xc0, "RxDiscarded" },
-       { }
-};
-
-static int b53_do_vlan_op(struct b53_device *dev, u8 op)
-{
-       unsigned int i;
-
-       b53_write8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], VTA_START_CMD | op);
-
-       for (i = 0; i < 10; i++) {
-               u8 vta;
-
-               b53_read8(dev, B53_ARLIO_PAGE, dev->vta_regs[0], &vta);
-               if (!(vta & VTA_START_CMD))
-                       return 0;
-
-               usleep_range(100, 200);
-       }
-
-       return -EIO;
-}
-
-static void b53_set_vlan_entry(struct b53_device *dev, u16 vid, u16 members,
-                              u16 untag)
-{
-       if (is5325(dev)) {
-               u32 entry = 0;
-
-               if (members) {
-                       entry = ((untag & VA_UNTAG_MASK_25) << VA_UNTAG_S_25) |
-                               members;
-                       if (dev->core_rev >= 3)
-                               entry |= VA_VALID_25_R4 | vid << VA_VID_HIGH_S;
-                       else
-                               entry |= VA_VALID_25;
-               }
-
-               b53_write32(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_25, entry);
-               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, vid |
-                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
-       } else if (is5365(dev)) {
-               u16 entry = 0;
-
-               if (members)
-                       entry = ((untag & VA_UNTAG_MASK_65) << VA_UNTAG_S_65) |
-                               members | VA_VALID_65;
-
-               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_WRITE_65, entry);
-               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_65, vid |
-                           VTA_RW_STATE_WR | VTA_RW_OP_EN);
-       } else {
-               b53_write16(dev, B53_ARLIO_PAGE, dev->vta_regs[1], vid);
-               b53_write32(dev, B53_ARLIO_PAGE, dev->vta_regs[2],
-                           (untag << VTE_UNTAG_S) | members);
-
-               b53_do_vlan_op(dev, VTA_CMD_WRITE);
-       }
-}
-
-void b53_set_forwarding(struct b53_device *dev, int enable)
-{
-       u8 mgmt;
-
-       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
-
-       if (enable)
-               mgmt |= SM_SW_FWD_EN;
-       else
-               mgmt &= ~SM_SW_FWD_EN;
-
-       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
-}
-
-static void b53_enable_vlan(struct b53_device *dev, int enable)
-{
-       u8 mgmt, vc0, vc1, vc4 = 0, vc5;
-
-       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
-       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, &vc0);
-       b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, &vc1);
-
-       if (is5325(dev) || is5365(dev)) {
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, &vc5);
-       } else if (is63xx(dev)) {
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, &vc4);
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, &vc5);
-       } else {
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, &vc4);
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, &vc5);
-       }
-
-       mgmt &= ~SM_SW_FWD_MODE;
-
-       if (enable) {
-               vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
-               vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
-               vc4 &= ~VC4_ING_VID_CHECK_MASK;
-               vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
-               vc5 |= VC5_DROP_VTABLE_MISS;
-
-               if (is5325(dev))
-                       vc0 &= ~VC0_RESERVED_1;
-
-               if (is5325(dev) || is5365(dev))
-                       vc1 |= VC1_RX_MCST_TAG_EN;
-
-               if (!is5325(dev) && !is5365(dev)) {
-                       if (dev->allow_vid_4095)
-                               vc5 |= VC5_VID_FFF_EN;
-                       else
-                               vc5 &= ~VC5_VID_FFF_EN;
-               }
-       } else {
-               vc0 &= ~(VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID);
-               vc1 &= ~(VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN);
-               vc4 &= ~VC4_ING_VID_CHECK_MASK;
-               vc5 &= ~VC5_DROP_VTABLE_MISS;
-
-               if (is5325(dev) || is5365(dev))
-                       vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
-               else
-                       vc4 |= VC4_ING_VID_VIO_TO_IMP << VC4_ING_VID_CHECK_S;
-
-               if (is5325(dev) || is5365(dev))
-                       vc1 &= ~VC1_RX_MCST_TAG_EN;
-
-               if (!is5325(dev) && !is5365(dev))
-                       vc5 &= ~VC5_VID_FFF_EN;
-       }
-
-       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL0, vc0);
-       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL1, vc1);
-
-       if (is5325(dev) || is5365(dev)) {
-               /* enable the high 8 bit vid check on 5325 */
-               if (is5325(dev) && enable)
-                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3,
-                                  VC3_HIGH_8BIT_EN);
-               else
-                       b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
-
-               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, vc4);
-               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_25, vc5);
-       } else if (is63xx(dev)) {
-               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3_63XX, 0);
-               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_63XX, vc4);
-               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5_63XX, vc5);
-       } else {
-               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_CTRL3, 0);
-               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4, vc4);
-               b53_write8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL5, vc5);
-       }
-
-       b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
-}
-
-static int b53_set_jumbo(struct b53_device *dev, int enable, int allow_10_100)
-{
-       u32 port_mask = 0;
-       u16 max_size = JMS_MIN_SIZE;
-
-       if (is5325(dev) || is5365(dev))
-               return -EINVAL;
-
-       if (enable) {
-               port_mask = dev->enabled_ports;
-               max_size = JMS_MAX_SIZE;
-               if (allow_10_100)
-                       port_mask |= JPM_10_100_JUMBO_EN;
-       }
-
-       b53_write32(dev, B53_JUMBO_PAGE, dev->jumbo_pm_reg, port_mask);
-       return b53_write16(dev, B53_JUMBO_PAGE, dev->jumbo_size_reg, max_size);
-}
-
-static int b53_flush_arl(struct b53_device *dev)
-{
-       unsigned int i;
-
-       b53_write8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
-                  FAST_AGE_DONE | FAST_AGE_DYNAMIC | FAST_AGE_STATIC);
-
-       for (i = 0; i < 10; i++) {
-               u8 fast_age_ctrl;
-
-               b53_read8(dev, B53_CTRL_PAGE, B53_FAST_AGE_CTRL,
-                         &fast_age_ctrl);
-
-               if (!(fast_age_ctrl & FAST_AGE_DONE))
-                       return 0;
-
-               mdelay(1);
-       }
-
-       pr_warn("time out while flushing ARL\n");
-
-       return -EINVAL;
-}
-
-static void b53_enable_ports(struct b53_device *dev)
-{
-       unsigned i;
-
-       b53_for_each_port(dev, i) {
-               u8 port_ctrl;
-               u16 pvlan_mask;
-
-               /*
-                * prevent leaking packets between wan and lan in unmanaged
-                * mode through port vlans.
-                */
-               if (dev->enable_vlan || is_cpu_port(dev, i))
-                       pvlan_mask = 0x1ff;
-               else if (is531x5(dev) || is5301x(dev))
-                       /* BCM53115 may use a different port as cpu port */
-                       pvlan_mask = BIT(dev->sw_dev.cpu_port);
-               else
-                       pvlan_mask = BIT(B53_CPU_PORT);
-
-               /* BCM5325 CPU port is at 8 */
-               if ((is5325(dev) || is5365(dev)) && i == B53_CPU_PORT_25)
-                       i = B53_CPU_PORT;
-
-               if (dev->chip_id == BCM5398_DEVICE_ID && (i == 6 || i == 7))
-                       /* disable unused ports 6 & 7 */
-                       port_ctrl = PORT_CTRL_RX_DISABLE | PORT_CTRL_TX_DISABLE;
-               else if (i == B53_CPU_PORT)
-                       port_ctrl = PORT_CTRL_RX_BCST_EN |
-                                   PORT_CTRL_RX_MCST_EN |
-                                   PORT_CTRL_RX_UCST_EN;
-               else
-                       port_ctrl = 0;
-
-               b53_write16(dev, B53_PVLAN_PAGE, B53_PVLAN_PORT_MASK(i),
-                           pvlan_mask);
-
-               /* port state is handled by bcm63xx_enet driver */
-               if (!is63xx(dev) && !(is5301x(dev) && i == 6))
-                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_CTRL(i),
-                                  port_ctrl);
-       }
-}
-
-static void b53_enable_mib(struct b53_device *dev)
-{
-       u8 gc;
-
-       b53_read8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
-
-       gc &= ~(GC_RESET_MIB | GC_MIB_AC_EN);
-
-       b53_write8(dev, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc);
-}
-
-static int b53_apply(struct b53_device *dev)
-{
-       int i;
-
-       /* clear all vlan entries */
-       if (is5325(dev) || is5365(dev)) {
-               for (i = 1; i < dev->sw_dev.vlans; i++)
-                       b53_set_vlan_entry(dev, i, 0, 0);
-       } else {
-               b53_do_vlan_op(dev, VTA_CMD_CLEAR);
-       }
-
-       b53_enable_vlan(dev, dev->enable_vlan);
-
-       /* fill VLAN table */
-       if (dev->enable_vlan) {
-               for (i = 0; i < dev->sw_dev.vlans; i++) {
-                       struct b53_vlan *vlan = &dev->vlans[i];
-
-                       if (!vlan->members)
-                               continue;
-
-                       b53_set_vlan_entry(dev, i, vlan->members, vlan->untag);
-               }
-
-               b53_for_each_port(dev, i)
-                       b53_write16(dev, B53_VLAN_PAGE,
-                                   B53_VLAN_PORT_DEF_TAG(i),
-                                   dev->ports[i].pvid);
-       } else {
-               b53_for_each_port(dev, i)
-                       b53_write16(dev, B53_VLAN_PAGE,
-                                   B53_VLAN_PORT_DEF_TAG(i), 1);
-
-       }
-
-       b53_enable_ports(dev);
-
-       if (!is5325(dev) && !is5365(dev))
-               b53_set_jumbo(dev, dev->enable_jumbo, 1);
-
-       return 0;
-}
-
-static void b53_switch_reset_gpio(struct b53_device *dev)
-{
-       int gpio = dev->reset_gpio;
-
-       if (gpio < 0)
-               return;
-
-       /*
-        * Reset sequence: RESET low(50ms)->high(20ms)
-        */
-       gpio_set_value(gpio, 0);
-       mdelay(50);
-
-       gpio_set_value(gpio, 1);
-       mdelay(20);
-
-       dev->current_page = 0xff;
-}
-
-static int b53_configure_ports_of(struct b53_device *dev)
-{
-       struct device_node *dn, *pn;
-       u32 port_num;
-
-       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
-
-       for_each_available_child_of_node(dn, pn) {
-               struct device_node *fixed_link;
-
-               if (of_property_read_u32(pn, "reg", &port_num))
-                       continue;
-
-               if (port_num > B53_CPU_PORT)
-                       continue;
-
-               fixed_link = of_get_child_by_name(pn, "fixed-link");
-               if (fixed_link) {
-                       u32 spd;
-                       u8 po = GMII_PO_LINK;
-                       int mode = of_get_phy_mode(pn);
-
-                       if (!of_property_read_u32(fixed_link, "speed", &spd)) {
-                               switch (spd) {
-                               case 10:
-                                       po |= GMII_PO_SPEED_10M;
-                                       break;
-                               case 100:
-                                       po |= GMII_PO_SPEED_100M;
-                                       break;
-                               case 2000:
-                                       if (is_imp_port(dev, port_num))
-                                               po |= PORT_OVERRIDE_SPEED_2000M;
-                                       else
-                                               po |= GMII_PO_SPEED_2000M;
-                                       /* fall through */
-                               case 1000:
-                                       po |= GMII_PO_SPEED_1000M;
-                                       break;
-                               }
-                       }
-
-                       if (of_property_read_bool(fixed_link, "full-duplex"))
-                               po |= PORT_OVERRIDE_FULL_DUPLEX;
-                       if (of_property_read_bool(fixed_link, "pause"))
-                               po |= GMII_PO_RX_FLOW;
-                       if (of_property_read_bool(fixed_link, "asym-pause"))
-                               po |= GMII_PO_TX_FLOW;
-
-                       if (is_imp_port(dev, port_num)) {
-                               po |= PORT_OVERRIDE_EN;
-
-                               if (is5325(dev) &&
-                                   mode == PHY_INTERFACE_MODE_REVMII)
-                                       po |= PORT_OVERRIDE_RV_MII_25;
-
-                               b53_write8(dev, B53_CTRL_PAGE,
-                                          B53_PORT_OVERRIDE_CTRL, po);
-
-                               if (is5325(dev) &&
-                                   mode == PHY_INTERFACE_MODE_REVMII) {
-                                       b53_read8(dev, B53_CTRL_PAGE,
-                                                 B53_PORT_OVERRIDE_CTRL, &po);
-                                       if (!(po & PORT_OVERRIDE_RV_MII_25))
-                                       pr_err("Failed to enable reverse MII mode\n");
-                                       return -EINVAL;
-                               }
-                       } else {
-                               po |= GMII_PO_EN;
-                               b53_write8(dev, B53_CTRL_PAGE,
-                                          B53_GMII_PORT_OVERRIDE_CTRL(port_num),
-                                          po);
-                       }
-               }
-       }
-
-       return 0;
-}
-
-static int b53_configure_ports(struct b53_device *dev)
-{
-       u8 cpu_port = dev->sw_dev.cpu_port;
-
-       /* configure MII port if necessary */
-       if (is5325(dev)) {
-               u8 mii_port_override;
-
-               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                         &mii_port_override);
-               /* reverse mii needs to be enabled */
-               if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
-                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                                  mii_port_override | PORT_OVERRIDE_RV_MII_25);
-                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                                 &mii_port_override);
-
-                       if (!(mii_port_override & PORT_OVERRIDE_RV_MII_25)) {
-                               pr_err("Failed to enable reverse MII mode\n");
-                               return -EINVAL;
-                       }
-               }
-       } else if (is531x5(dev) && cpu_port == B53_CPU_PORT) {
-               u8 mii_port_override;
-
-               b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                         &mii_port_override);
-               b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                          mii_port_override | PORT_OVERRIDE_EN |
-                          PORT_OVERRIDE_LINK);
-
-               /* BCM47189 has another interface connected to the port 5 */
-               if (dev->enabled_ports & BIT(5)) {
-                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(5);
-                       u8 gmii_po;
-
-                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
-                       gmii_po |= GMII_PO_LINK |
-                                  GMII_PO_RX_FLOW |
-                                  GMII_PO_TX_FLOW |
-                                  GMII_PO_EN;
-                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
-               }
-       } else if (is5301x(dev)) {
-               if (cpu_port == 8) {
-                       u8 mii_port_override;
-
-                       b53_read8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                                 &mii_port_override);
-                       mii_port_override |= PORT_OVERRIDE_LINK |
-                                            PORT_OVERRIDE_RX_FLOW |
-                                            PORT_OVERRIDE_TX_FLOW |
-                                            PORT_OVERRIDE_SPEED_2000M |
-                                            PORT_OVERRIDE_EN;
-                       b53_write8(dev, B53_CTRL_PAGE, B53_PORT_OVERRIDE_CTRL,
-                                  mii_port_override);
-
-                       /* TODO: Ports 5 & 7 require some extra handling */
-               } else {
-                       u8 po_reg = B53_GMII_PORT_OVERRIDE_CTRL(cpu_port);
-                       u8 gmii_po;
-
-                       b53_read8(dev, B53_CTRL_PAGE, po_reg, &gmii_po);
-                       gmii_po |= GMII_PO_LINK |
-                                  GMII_PO_RX_FLOW |
-                                  GMII_PO_TX_FLOW |
-                                  GMII_PO_EN |
-                                  GMII_PO_SPEED_2000M;
-                       b53_write8(dev, B53_CTRL_PAGE, po_reg, gmii_po);
-               }
-       }
-
-       return 0;
-}
-
-static int b53_switch_reset(struct b53_device *dev)
-{
-       int ret = 0;
-       u8 mgmt;
-
-       b53_switch_reset_gpio(dev);
-
-       if (is539x(dev)) {
-               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x83);
-               b53_write8(dev, B53_CTRL_PAGE, B53_SOFTRESET, 0x00);
-       }
-
-       b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
-
-       if (!(mgmt & SM_SW_FWD_EN)) {
-               mgmt &= ~SM_SW_FWD_MODE;
-               mgmt |= SM_SW_FWD_EN;
-
-               b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
-               b53_read8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, &mgmt);
-
-               if (!(mgmt & SM_SW_FWD_EN)) {
-                       pr_err("Failed to enable switch!\n");
-                       return -EINVAL;
-               }
-       }
-
-       /* enable all ports */
-       b53_enable_ports(dev);
-
-       if (dev->dev->of_node)
-               ret = b53_configure_ports_of(dev);
-       else
-               ret = b53_configure_ports(dev);
-
-       if (ret)
-               return ret;
-
-       b53_enable_mib(dev);
-
-       return b53_flush_arl(dev);
-}
-
-/*
- * Swconfig glue functions
- */
-
-static int b53_global_get_vlan_enable(struct switch_dev *dev,
-                                     const struct switch_attr *attr,
-                                     struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       val->value.i = priv->enable_vlan;
-
-       return 0;
-}
-
-static int b53_global_set_vlan_enable(struct switch_dev *dev,
-                                     const struct switch_attr *attr,
-                                     struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       priv->enable_vlan = val->value.i;
-
-       return 0;
-}
-
-static int b53_global_get_jumbo_enable(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       val->value.i = priv->enable_jumbo;
-
-       return 0;
-}
-
-static int b53_global_set_jumbo_enable(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       priv->enable_jumbo = val->value.i;
-
-       return 0;
-}
-
-static int b53_global_get_4095_enable(struct switch_dev *dev,
-                                     const struct switch_attr *attr,
-                                     struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       val->value.i = priv->allow_vid_4095;
-
-       return 0;
-}
-
-static int b53_global_set_4095_enable(struct switch_dev *dev,
-                                     const struct switch_attr *attr,
-                                     struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       priv->allow_vid_4095 = val->value.i;
-
-       return 0;
-}
-
-static int b53_global_get_ports(struct switch_dev *dev,
-                               const struct switch_attr *attr,
-                               struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       val->len = snprintf(priv->buf, B53_BUF_SIZE, "0x%04x",
-                           priv->enabled_ports);
-       val->value.s = priv->buf;
-
-       return 0;
-}
-
-static int b53_port_get_pvid(struct switch_dev *dev, int port, int *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       *val = priv->ports[port].pvid;
-
-       return 0;
-}
-
-static int b53_port_set_pvid(struct switch_dev *dev, int port, int val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       if (val > 15 && is5325(priv))
-               return -EINVAL;
-       if (val == 4095 && !priv->allow_vid_4095)
-               return -EINVAL;
-
-       priv->ports[port].pvid = val;
-
-       return 0;
-}
-
-static int b53_vlan_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-       struct switch_port *port = &val->value.ports[0];
-       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
-       int i;
-
-       val->len = 0;
-
-       if (!vlan->members)
-               return 0;
-
-       for (i = 0; i < dev->ports; i++) {
-               if (!(vlan->members & BIT(i)))
-                       continue;
-
-
-               if (!(vlan->untag & BIT(i)))
-                       port->flags = BIT(SWITCH_PORT_FLAG_TAGGED);
-               else
-                       port->flags = 0;
-
-               port->id = i;
-               val->len++;
-               port++;
-       }
-
-       return 0;
-}
-
-static int b53_vlan_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-       struct switch_port *port;
-       struct b53_vlan *vlan = &priv->vlans[val->port_vlan];
-       int i;
-
-       /* only BCM5325 and BCM5365 supports VID 0 */
-       if (val->port_vlan == 0 && !is5325(priv) && !is5365(priv))
-               return -EINVAL;
-
-       /* VLAN 4095 needs special handling */
-       if (val->port_vlan == 4095 && !priv->allow_vid_4095)
-               return -EINVAL;
-
-       port = &val->value.ports[0];
-       vlan->members = 0;
-       vlan->untag = 0;
-       for (i = 0; i < val->len; i++, port++) {
-               vlan->members |= BIT(port->id);
-
-               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED))) {
-                       vlan->untag |= BIT(port->id);
-                       priv->ports[port->id].pvid = val->port_vlan;
-               };
-       }
-
-       /* ignore disabled ports */
-       vlan->members &= priv->enabled_ports;
-       vlan->untag &= priv->enabled_ports;
-
-       return 0;
-}
-
-static int b53_port_get_link(struct switch_dev *dev, int port,
-                            struct switch_port_link *link)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       if (is_cpu_port(priv, port)) {
-               link->link = 1;
-               link->duplex = 1;
-               link->speed = is5325(priv) || is5365(priv) ?
-                               SWITCH_PORT_SPEED_100 : SWITCH_PORT_SPEED_1000;
-               link->aneg = 0;
-       } else if (priv->enabled_ports & BIT(port)) {
-               u32 speed;
-               u16 lnk, duplex;
-
-               b53_read16(priv, B53_STAT_PAGE, B53_LINK_STAT, &lnk);
-               b53_read16(priv, B53_STAT_PAGE, priv->duplex_reg, &duplex);
-
-               lnk = (lnk >> port) & 1;
-               duplex = (duplex >> port) & 1;
-
-               if (is5325(priv) || is5365(priv)) {
-                       u16 tmp;
-
-                       b53_read16(priv, B53_STAT_PAGE, B53_SPEED_STAT, &tmp);
-                       speed = SPEED_PORT_FE(tmp, port);
-               } else {
-                       b53_read32(priv, B53_STAT_PAGE, B53_SPEED_STAT, &speed);
-                       speed = SPEED_PORT_GE(speed, port);
-               }
-
-               link->link = lnk;
-               if (lnk) {
-                       link->duplex = duplex;
-                       switch (speed) {
-                       case SPEED_STAT_10M:
-                               link->speed = SWITCH_PORT_SPEED_10;
-                               break;
-                       case SPEED_STAT_100M:
-                               link->speed = SWITCH_PORT_SPEED_100;
-                               break;
-                       case SPEED_STAT_1000M:
-                               link->speed = SWITCH_PORT_SPEED_1000;
-                               break;
-                       }
-               }
-
-               link->aneg = 1;
-       } else {
-               link->link = 0;
-       }
-
-       return 0;
-
-}
-
-static int b53_port_set_link(struct switch_dev *sw_dev, int port,
-                            struct switch_port_link *link)
-{
-       struct b53_device *dev = sw_to_b53(sw_dev);
-
-       /*
-        * TODO: BCM63XX requires special handling as it can have external phys
-        * and ports might be GE or only FE
-        */
-       if (is63xx(dev))
-               return -ENOTSUPP;
-
-       if (port == sw_dev->cpu_port)
-               return -EINVAL;
-
-       if (!(BIT(port) & dev->enabled_ports))
-               return -EINVAL;
-
-       if (link->speed == SWITCH_PORT_SPEED_1000 &&
-           (is5325(dev) || is5365(dev)))
-               return -EINVAL;
-
-       if (link->speed == SWITCH_PORT_SPEED_1000 && !link->duplex)
-               return -EINVAL;
-
-       return switch_generic_set_link(sw_dev, port, link);
-}
-
-static int b53_phy_read16(struct switch_dev *dev, int addr, u8 reg, u16 *value)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       if (priv->ops->phy_read16)
-               return priv->ops->phy_read16(priv, addr, reg, value);
-
-       return b53_read16(priv, B53_PORT_MII_PAGE(addr), reg, value);
-}
-
-static int b53_phy_write16(struct switch_dev *dev, int addr, u8 reg, u16 value)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       if (priv->ops->phy_write16)
-               return priv->ops->phy_write16(priv, addr, reg, value);
-
-       return b53_write16(priv, B53_PORT_MII_PAGE(addr), reg, value);
-}
-
-static int b53_global_reset_switch(struct switch_dev *dev)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       /* reset vlans */
-       priv->enable_vlan = 0;
-       priv->enable_jumbo = 0;
-       priv->allow_vid_4095 = 0;
-
-       memset(priv->vlans, 0, sizeof(*priv->vlans) * dev->vlans);
-       memset(priv->ports, 0, sizeof(*priv->ports) * dev->ports);
-
-       return b53_switch_reset(priv);
-}
-
-static int b53_global_apply_config(struct switch_dev *dev)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-
-       /* disable switching */
-       b53_set_forwarding(priv, 0);
-
-       b53_apply(priv);
-
-       /* enable switching */
-       b53_set_forwarding(priv, 1);
-
-       return 0;
-}
-
-
-static int b53_global_reset_mib(struct switch_dev *dev,
-                               const struct switch_attr *attr,
-                               struct switch_val *val)
-{
-       struct b53_device *priv = sw_to_b53(dev);
-       u8 gc;
-
-       b53_read8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, &gc);
-
-       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc | GC_RESET_MIB);
-       mdelay(1);
-       b53_write8(priv, B53_MGMT_PAGE, B53_GLOBAL_CONFIG, gc & ~GC_RESET_MIB);
-       mdelay(1);
-
-       return 0;
-}
-
-static int b53_port_get_mib(struct switch_dev *sw_dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val)
-{
-       struct b53_device *dev = sw_to_b53(sw_dev);
-       const struct b53_mib_desc *mibs;
-       int port = val->port_vlan;
-       int len = 0;
-
-       if (!(BIT(port) & dev->enabled_ports))
-               return -1;
-
-       if (is5365(dev)) {
-               if (port == 5)
-                       port = 8;
-
-               mibs = b53_mibs_65;
-       } else if (is63xx(dev)) {
-               mibs = b53_mibs_63xx;
-       } else {
-               mibs = b53_mibs;
-       }
-
-       dev->buf[0] = 0;
-
-       for (; mibs->size > 0; mibs++) {
-               u64 val;
-
-               if (mibs->size == 8) {
-                       b53_read64(dev, B53_MIB_PAGE(port), mibs->offset, &val);
-               } else {
-                       u32 val32;
-
-                       b53_read32(dev, B53_MIB_PAGE(port), mibs->offset,
-                                  &val32);
-                       val = val32;
-               }
-
-               len += snprintf(dev->buf + len, B53_BUF_SIZE - len,
-                               "%-20s: %llu\n", mibs->name, val);
-       }
-
-       val->len = len;
-       val->value.s = dev->buf;
-
-       return 0;
-}
-
-static int b53_port_get_stats(struct switch_dev *sw_dev, int port,
-                               struct switch_port_stats *stats)
-{
-       struct b53_device *dev = sw_to_b53(sw_dev);
-       const struct b53_mib_desc *mibs;
-       int txb_id, rxb_id;
-       u64 rxb, txb;
-
-       if (!(BIT(port) & dev->enabled_ports))
-               return -EINVAL;
-
-       txb_id = B53XX_MIB_TXB_ID;
-       rxb_id = B53XX_MIB_RXB_ID;
-
-       if (is5365(dev)) {
-               if (port == 5)
-                       port = 8;
-
-               mibs = b53_mibs_65;
-       } else if (is63xx(dev)) {
-               mibs = b53_mibs_63xx;
-               txb_id = B63XX_MIB_TXB_ID;
-               rxb_id = B63XX_MIB_RXB_ID;
-       } else {
-               mibs = b53_mibs;
-       }
-
-       dev->buf[0] = 0;
-
-       if (mibs->size == 8) {
-               b53_read64(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &txb);
-               b53_read64(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &rxb);
-       } else {
-               u32 val32;
-
-               b53_read32(dev, B53_MIB_PAGE(port), mibs[txb_id].offset, &val32);
-               txb = val32;
-
-               b53_read32(dev, B53_MIB_PAGE(port), mibs[rxb_id].offset, &val32);
-               rxb = val32;
-       }
-
-       stats->tx_bytes = txb;
-       stats->rx_bytes = rxb;
-
-       return 0;
-}
-
-static struct switch_attr b53_global_ops_25[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = b53_global_set_vlan_enable,
-               .get = b53_global_get_vlan_enable,
-               .max = 1,
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "ports",
-               .description = "Available ports (as bitmask)",
-               .get = b53_global_get_ports,
-       },
-};
-
-static struct switch_attr b53_global_ops_65[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = b53_global_set_vlan_enable,
-               .get = b53_global_get_vlan_enable,
-               .max = 1,
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "ports",
-               .description = "Available ports (as bitmask)",
-               .get = b53_global_get_ports,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "reset_mib",
-               .description = "Reset MIB counters",
-               .set = b53_global_reset_mib,
-       },
-};
-
-static struct switch_attr b53_global_ops[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = b53_global_set_vlan_enable,
-               .get = b53_global_get_vlan_enable,
-               .max = 1,
-       },
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "ports",
-               .description = "Available Ports (as bitmask)",
-               .get = b53_global_get_ports,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "reset_mib",
-               .description = "Reset MIB counters",
-               .set = b53_global_reset_mib,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_jumbo",
-               .description = "Enable Jumbo Frames",
-               .set = b53_global_set_jumbo_enable,
-               .get = b53_global_get_jumbo_enable,
-               .max = 1,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "allow_vid_4095",
-               .description = "Allow VID 4095",
-               .set = b53_global_set_4095_enable,
-               .get = b53_global_get_4095_enable,
-               .max = 1,
-       },
-};
-
-static struct switch_attr b53_port_ops[] = {
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get port's MIB counters",
-               .get = b53_port_get_mib,
-       },
-};
-
-static struct switch_attr b53_no_ops[] = {
-};
-
-static const struct switch_dev_ops b53_switch_ops_25 = {
-       .attr_global = {
-               .attr = b53_global_ops_25,
-               .n_attr = ARRAY_SIZE(b53_global_ops_25),
-       },
-       .attr_port = {
-               .attr = b53_no_ops,
-               .n_attr = ARRAY_SIZE(b53_no_ops),
-       },
-       .attr_vlan = {
-               .attr = b53_no_ops,
-               .n_attr = ARRAY_SIZE(b53_no_ops),
-       },
-
-       .get_vlan_ports = b53_vlan_get_ports,
-       .set_vlan_ports = b53_vlan_set_ports,
-       .get_port_pvid = b53_port_get_pvid,
-       .set_port_pvid = b53_port_set_pvid,
-       .apply_config = b53_global_apply_config,
-       .reset_switch = b53_global_reset_switch,
-       .get_port_link = b53_port_get_link,
-       .set_port_link = b53_port_set_link,
-       .get_port_stats = b53_port_get_stats,
-       .phy_read16 = b53_phy_read16,
-       .phy_write16 = b53_phy_write16,
-};
-
-static const struct switch_dev_ops b53_switch_ops_65 = {
-       .attr_global = {
-               .attr = b53_global_ops_65,
-               .n_attr = ARRAY_SIZE(b53_global_ops_65),
-       },
-       .attr_port = {
-               .attr = b53_port_ops,
-               .n_attr = ARRAY_SIZE(b53_port_ops),
-       },
-       .attr_vlan = {
-               .attr = b53_no_ops,
-               .n_attr = ARRAY_SIZE(b53_no_ops),
-       },
-
-       .get_vlan_ports = b53_vlan_get_ports,
-       .set_vlan_ports = b53_vlan_set_ports,
-       .get_port_pvid = b53_port_get_pvid,
-       .set_port_pvid = b53_port_set_pvid,
-       .apply_config = b53_global_apply_config,
-       .reset_switch = b53_global_reset_switch,
-       .get_port_link = b53_port_get_link,
-       .set_port_link = b53_port_set_link,
-       .get_port_stats = b53_port_get_stats,
-       .phy_read16 = b53_phy_read16,
-       .phy_write16 = b53_phy_write16,
-};
-
-static const struct switch_dev_ops b53_switch_ops = {
-       .attr_global = {
-               .attr = b53_global_ops,
-               .n_attr = ARRAY_SIZE(b53_global_ops),
-       },
-       .attr_port = {
-               .attr = b53_port_ops,
-               .n_attr = ARRAY_SIZE(b53_port_ops),
-       },
-       .attr_vlan = {
-               .attr = b53_no_ops,
-               .n_attr = ARRAY_SIZE(b53_no_ops),
-       },
-
-       .get_vlan_ports = b53_vlan_get_ports,
-       .set_vlan_ports = b53_vlan_set_ports,
-       .get_port_pvid = b53_port_get_pvid,
-       .set_port_pvid = b53_port_set_pvid,
-       .apply_config = b53_global_apply_config,
-       .reset_switch = b53_global_reset_switch,
-       .get_port_link = b53_port_get_link,
-       .set_port_link = b53_port_set_link,
-       .get_port_stats = b53_port_get_stats,
-       .phy_read16 = b53_phy_read16,
-       .phy_write16 = b53_phy_write16,
-};
-
-struct b53_chip_data {
-       u32 chip_id;
-       const char *dev_name;
-       const char *alias;
-       u16 vlans;
-       u16 enabled_ports;
-       u8 cpu_port;
-       u8 vta_regs[3];
-       u8 duplex_reg;
-       u8 jumbo_pm_reg;
-       u8 jumbo_size_reg;
-       const struct switch_dev_ops *sw_ops;
-};
-
-#define B53_VTA_REGS   \
-       { B53_VT_ACCESS, B53_VT_INDEX, B53_VT_ENTRY }
-#define B53_VTA_REGS_9798 \
-       { B53_VT_ACCESS_9798, B53_VT_INDEX_9798, B53_VT_ENTRY_9798 }
-#define B53_VTA_REGS_63XX \
-       { B53_VT_ACCESS_63XX, B53_VT_INDEX_63XX, B53_VT_ENTRY_63XX }
-
-static const struct b53_chip_data b53_switch_chips[] = {
-       {
-               .chip_id = BCM5325_DEVICE_ID,
-               .dev_name = "BCM5325",
-               .alias = "bcm5325",
-               .vlans = 16,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT_25,
-               .duplex_reg = B53_DUPLEX_STAT_FE,
-               .sw_ops = &b53_switch_ops_25,
-       },
-       {
-               .chip_id = BCM5365_DEVICE_ID,
-               .dev_name = "BCM5365",
-               .alias = "bcm5365",
-               .vlans = 256,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT_25,
-               .duplex_reg = B53_DUPLEX_STAT_FE,
-               .sw_ops = &b53_switch_ops_65,
-       },
-       {
-               .chip_id = BCM5395_DEVICE_ID,
-               .dev_name = "BCM5395",
-               .alias = "bcm5395",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT,
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM5397_DEVICE_ID,
-               .dev_name = "BCM5397",
-               .alias = "bcm5397",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT,
-               .vta_regs = B53_VTA_REGS_9798,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM5398_DEVICE_ID,
-               .dev_name = "BCM5398",
-               .alias = "bcm5398",
-               .vlans = 4096,
-               .enabled_ports = 0x7f,
-               .cpu_port = B53_CPU_PORT,
-               .vta_regs = B53_VTA_REGS_9798,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53115_DEVICE_ID,
-               .dev_name = "BCM53115",
-               .alias = "bcm53115",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .vta_regs = B53_VTA_REGS,
-               .cpu_port = B53_CPU_PORT,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53125_DEVICE_ID,
-               .dev_name = "BCM53125",
-               .alias = "bcm53125",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT,
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53128_DEVICE_ID,
-               .dev_name = "BCM53128",
-               .alias = "bcm53128",
-               .vlans = 4096,
-               .enabled_ports = 0x1ff,
-               .cpu_port = B53_CPU_PORT,
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM63XX_DEVICE_ID,
-               .dev_name = "BCM63xx",
-               .alias = "bcm63xx",
-               .vlans = 4096,
-               .enabled_ports = 0, /* pdata must provide them */
-               .cpu_port = B53_CPU_PORT,
-               .vta_regs = B53_VTA_REGS_63XX,
-               .duplex_reg = B53_DUPLEX_STAT_63XX,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK_63XX,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE_63XX,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53010_DEVICE_ID,
-               .dev_name = "BCM53010",
-               .alias = "bcm53011",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53011_DEVICE_ID,
-               .dev_name = "BCM53011",
-               .alias = "bcm53011",
-               .vlans = 4096,
-               .enabled_ports = 0x1bf,
-               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53012_DEVICE_ID,
-               .dev_name = "BCM53012",
-               .alias = "bcm53011",
-               .vlans = 4096,
-               .enabled_ports = 0x1bf,
-               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53018_DEVICE_ID,
-               .dev_name = "BCM53018",
-               .alias = "bcm53018",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-       {
-               .chip_id = BCM53019_DEVICE_ID,
-               .dev_name = "BCM53019",
-               .alias = "bcm53019",
-               .vlans = 4096,
-               .enabled_ports = 0x1f,
-               .cpu_port = B53_CPU_PORT_25, /* TODO: auto detect */
-               .vta_regs = B53_VTA_REGS,
-               .duplex_reg = B53_DUPLEX_STAT_GE,
-               .jumbo_pm_reg = B53_JUMBO_PORT_MASK,
-               .jumbo_size_reg = B53_JUMBO_MAX_SIZE,
-               .sw_ops = &b53_switch_ops,
-       },
-};
-
-static int b53_switch_init_of(struct b53_device *dev)
-{
-       struct device_node *dn, *pn;
-       const char *alias;
-       u32 port_num;
-       u16 ports = 0;
-
-       dn = of_get_child_by_name(dev_of_node(dev->dev), "ports");
-       if (!dn)
-               return -EINVAL;
-
-       for_each_available_child_of_node(dn, pn) {
-               const char *label;
-               int len;
-
-               if (of_property_read_u32(pn, "reg", &port_num))
-                       continue;
-
-               if (port_num > B53_CPU_PORT)
-                       continue;
-
-               ports |= BIT(port_num);
-
-               label = of_get_property(pn, "label", &len);
-               if (label && !strcmp(label, "cpu"))
-                       dev->sw_dev.cpu_port = port_num;
-       }
-
-       dev->enabled_ports = ports;
-
-       if (!of_property_read_string(dev_of_node(dev->dev), "lede,alias",
-                                                &alias))
-               dev->sw_dev.alias = devm_kstrdup(dev->dev, alias, GFP_KERNEL);
-
-       return 0;
-}
-
-static int b53_switch_init(struct b53_device *dev)
-{
-       struct switch_dev *sw_dev = &dev->sw_dev;
-       unsigned i;
-       int ret;
-
-       for (i = 0; i < ARRAY_SIZE(b53_switch_chips); i++) {
-               const struct b53_chip_data *chip = &b53_switch_chips[i];
-
-               if (chip->chip_id == dev->chip_id) {
-                       sw_dev->name = chip->dev_name;
-                       if (!sw_dev->alias)
-                               sw_dev->alias = chip->alias;
-                       if (!dev->enabled_ports)
-                               dev->enabled_ports = chip->enabled_ports;
-                       dev->duplex_reg = chip->duplex_reg;
-                       dev->vta_regs[0] = chip->vta_regs[0];
-                       dev->vta_regs[1] = chip->vta_regs[1];
-                       dev->vta_regs[2] = chip->vta_regs[2];
-                       dev->jumbo_pm_reg = chip->jumbo_pm_reg;
-                       sw_dev->ops = chip->sw_ops;
-                       sw_dev->cpu_port = chip->cpu_port;
-                       sw_dev->vlans = chip->vlans;
-                       break;
-               }
-       }
-
-       if (!sw_dev->name)
-               return -EINVAL;
-
-       /* check which BCM5325x version we have */
-       if (is5325(dev)) {
-               u8 vc4;
-
-               b53_read8(dev, B53_VLAN_PAGE, B53_VLAN_CTRL4_25, &vc4);
-
-               /* check reserved bits */
-               switch (vc4 & 3) {
-               case 1:
-                       /* BCM5325E */
-                       break;
-               case 3:
-                       /* BCM5325F - do not use port 4 */
-                       dev->enabled_ports &= ~BIT(4);
-                       break;
-               default:
-/* On the BCM47XX SoCs this is the supported internal switch.*/
-#ifndef CONFIG_BCM47XX
-                       /* BCM5325M */
-                       return -EINVAL;
-#else
-                       break;
-#endif
-               }
-       } else if (dev->chip_id == BCM53115_DEVICE_ID) {
-               u64 strap_value;
-
-               b53_read48(dev, B53_STAT_PAGE, B53_STRAP_VALUE, &strap_value);
-               /* use second IMP port if GMII is enabled */
-               if (strap_value & SV_GMII_CTRL_115)
-                       sw_dev->cpu_port = 5;
-       }
-
-       if (dev_of_node(dev->dev)) {
-               ret = b53_switch_init_of(dev);
-               if (ret)
-                       return ret;
-       }
-
-       dev->enabled_ports |= BIT(sw_dev->cpu_port);
-       sw_dev->ports = fls(dev->enabled_ports);
-
-       dev->ports = devm_kzalloc(dev->dev,
-                                 sizeof(struct b53_port) * sw_dev->ports,
-                                 GFP_KERNEL);
-       if (!dev->ports)
-               return -ENOMEM;
-
-       dev->vlans = devm_kzalloc(dev->dev,
-                                 sizeof(struct b53_vlan) * sw_dev->vlans,
-                                 GFP_KERNEL);
-       if (!dev->vlans)
-               return -ENOMEM;
-
-       dev->buf = devm_kzalloc(dev->dev, B53_BUF_SIZE, GFP_KERNEL);
-       if (!dev->buf)
-               return -ENOMEM;
-
-       dev->reset_gpio = b53_switch_get_reset_gpio(dev);
-       if (dev->reset_gpio >= 0) {
-               ret = devm_gpio_request_one(dev->dev, dev->reset_gpio,
-                                           GPIOF_OUT_INIT_HIGH, "robo_reset");
-               if (ret)
-                       return ret;
-       }
-
-       return b53_switch_reset(dev);
-}
-
-struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
-                                   void *priv)
-{
-       struct b53_device *dev;
-
-       dev = devm_kzalloc(base, sizeof(*dev), GFP_KERNEL);
-       if (!dev)
-               return NULL;
-
-       dev->dev = base;
-       dev->ops = ops;
-       dev->priv = priv;
-       mutex_init(&dev->reg_mutex);
-
-       return dev;
-}
-EXPORT_SYMBOL(b53_switch_alloc);
-
-int b53_switch_detect(struct b53_device *dev)
-{
-       u32 id32;
-       u16 tmp;
-       u8 id8;
-       int ret;
-
-       ret = b53_read8(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id8);
-       if (ret)
-               return ret;
-
-       switch (id8) {
-       case 0:
-               /*
-                * BCM5325 and BCM5365 do not have this register so reads
-                * return 0. But the read operation did succeed, so assume
-                * this is one of them.
-                *
-                * Next check if we can write to the 5325's VTA register; for
-                * 5365 it is read only.
-                */
-
-               b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, 0xf);
-               b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_TABLE_ACCESS_25, &tmp);
-
-               if (tmp == 0xf)
-                       dev->chip_id = BCM5325_DEVICE_ID;
-               else
-                       dev->chip_id = BCM5365_DEVICE_ID;
-               break;
-       case BCM5395_DEVICE_ID:
-       case BCM5397_DEVICE_ID:
-       case BCM5398_DEVICE_ID:
-               dev->chip_id = id8;
-               break;
-       default:
-               ret = b53_read32(dev, B53_MGMT_PAGE, B53_DEVICE_ID, &id32);
-               if (ret)
-                       return ret;
-
-               switch (id32) {
-               case BCM53115_DEVICE_ID:
-               case BCM53125_DEVICE_ID:
-               case BCM53128_DEVICE_ID:
-               case BCM53010_DEVICE_ID:
-               case BCM53011_DEVICE_ID:
-               case BCM53012_DEVICE_ID:
-               case BCM53018_DEVICE_ID:
-               case BCM53019_DEVICE_ID:
-                       dev->chip_id = id32;
-                       break;
-               default:
-                       pr_err("unsupported switch detected (BCM53%02x/BCM%x)\n",
-                              id8, id32);
-                       return -ENODEV;
-               }
-       }
-
-       if (dev->chip_id == BCM5325_DEVICE_ID)
-               return b53_read8(dev, B53_STAT_PAGE, B53_REV_ID_25,
-                                &dev->core_rev);
-       else
-               return b53_read8(dev, B53_MGMT_PAGE, B53_REV_ID,
-                                &dev->core_rev);
-}
-EXPORT_SYMBOL(b53_switch_detect);
-
-int b53_switch_register(struct b53_device *dev)
-{
-       int ret;
-
-       if (dev->pdata) {
-               dev->chip_id = dev->pdata->chip_id;
-               dev->enabled_ports = dev->pdata->enabled_ports;
-               dev->sw_dev.alias = dev->pdata->alias;
-       }
-
-       if (!dev->chip_id && b53_switch_detect(dev))
-               return -EINVAL;
-
-       ret = b53_switch_init(dev);
-       if (ret)
-               return ret;
-
-       pr_info("found switch: %s, rev %i\n", dev->sw_dev.name, dev->core_rev);
-
-       return register_switch(&dev->sw_dev, NULL);
-}
-EXPORT_SYMBOL(b53_switch_register);
-
-MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
-MODULE_DESCRIPTION("B53 switch library");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mdio.c
deleted file mode 100644 (file)
index 75bb4d9..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- * B53 register access through MII registers
- *
- * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/phy.h>
-#include <linux/module.h>
-
-#include "b53_priv.h"
-
-#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
-
-/* MII registers */
-#define REG_MII_PAGE    0x10    /* MII Page register */
-#define REG_MII_ADDR    0x11    /* MII Address register */
-#define REG_MII_DATA0   0x18    /* MII Data register 0 */
-#define REG_MII_DATA1   0x19    /* MII Data register 1 */
-#define REG_MII_DATA2   0x1a    /* MII Data register 2 */
-#define REG_MII_DATA3   0x1b    /* MII Data register 3 */
-
-#define REG_MII_PAGE_ENABLE     BIT(0)
-#define REG_MII_ADDR_WRITE      BIT(0)
-#define REG_MII_ADDR_READ       BIT(1)
-
-static int b53_mdio_op(struct b53_device *dev, u8 page, u8 reg, u16 op)
-{
-       int i;
-       u16 v;
-       int ret;
-       struct mii_bus *bus = dev->priv;
-
-       if (dev->current_page != page) {
-               /* set page number */
-               v = (page << 8) | REG_MII_PAGE_ENABLE;
-               ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_PAGE, v);
-               if (ret)
-                       return ret;
-               dev->current_page = page;
-       }
-
-       /* set register address */
-       v = (reg << 8) | op;
-       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_ADDR, v);
-       if (ret)
-               return ret;
-
-       /* check if operation completed */
-       for (i = 0; i < 5; ++i) {
-               v = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_ADDR);
-               if (!(v & (REG_MII_ADDR_WRITE | REG_MII_ADDR_READ)))
-                       break;
-               usleep_range(10, 100);
-       }
-
-       if (WARN_ON(i == 5))
-               return -EIO;
-
-       return 0;
-}
-
-static int b53_mdio_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
-{
-       struct mii_bus *bus = dev->priv;
-       int ret;
-
-       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
-       if (ret)
-               return ret;
-
-       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0) & 0xff;
-
-       return 0;
-}
-
-static int b53_mdio_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
-{
-       struct mii_bus *bus = dev->priv;
-       int ret;
-
-       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
-       if (ret)
-               return ret;
-
-       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
-
-       return 0;
-}
-
-static int b53_mdio_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
-{
-       struct mii_bus *bus = dev->priv;
-       int ret;
-
-       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
-       if (ret)
-               return ret;
-
-       *val = mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0);
-       *val |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA1) << 16;
-
-       return 0;
-}
-
-static int b53_mdio_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       struct mii_bus *bus = dev->priv;
-       u64 temp = 0;
-       int i;
-       int ret;
-
-       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
-       if (ret)
-               return ret;
-
-       for (i = 2; i >= 0; i--) {
-               temp <<= 16;
-               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
-       }
-
-       *val = temp;
-
-       return 0;
-}
-
-static int b53_mdio_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       struct mii_bus *bus = dev->priv;
-       u64 temp = 0;
-       int i;
-       int ret;
-
-       ret = b53_mdio_op(dev, page, reg, REG_MII_ADDR_READ);
-       if (ret)
-               return ret;
-
-       for (i = 3; i >= 0; i--) {
-               temp <<= 16;
-               temp |= mdiobus_read(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i);
-       }
-
-       *val = temp;
-
-       return 0;
-}
-
-static int b53_mdio_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
-{
-       struct mii_bus *bus = dev->priv;
-       int ret;
-
-       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
-       if (ret)
-               return ret;
-
-       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
-}
-
-static int b53_mdio_write16(struct b53_device *dev, u8 page, u8 reg,
-                            u16 value)
-{
-       struct mii_bus *bus = dev->priv;
-       int ret;
-
-       ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0, value);
-       if (ret)
-               return ret;
-
-       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
-}
-
-static int b53_mdio_write32(struct b53_device *dev, u8 page, u8 reg,
-                                   u32 value)
-{
-       struct mii_bus *bus = dev->priv;
-       unsigned int i;
-       u32 temp = value;
-
-       for (i = 0; i < 2; i++) {
-               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
-                                   temp & 0xffff);
-               if (ret)
-                       return ret;
-               temp >>= 16;
-       }
-
-       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
-
-}
-
-static int b53_mdio_write48(struct b53_device *dev, u8 page, u8 reg,
-                                   u64 value)
-{
-       struct mii_bus *bus = dev->priv;
-       unsigned i;
-       u64 temp = value;
-
-       for (i = 0; i < 3; i++) {
-               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
-                                   temp & 0xffff);
-               if (ret)
-                       return ret;
-               temp >>= 16;
-       }
-
-       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
-
-}
-
-static int b53_mdio_write64(struct b53_device *dev, u8 page, u8 reg,
-                            u64 value)
-{
-       struct mii_bus *bus = dev->priv;
-       unsigned i;
-       u64 temp = value;
-
-       for (i = 0; i < 4; i++) {
-               int ret = mdiobus_write(bus, B53_PSEUDO_PHY, REG_MII_DATA0 + i,
-                                   temp & 0xffff);
-               if (ret)
-                       return ret;
-               temp >>= 16;
-       }
-
-       return b53_mdio_op(dev, page, reg, REG_MII_ADDR_WRITE);
-}
-
-static int b53_mdio_phy_read16(struct b53_device *dev, int addr, u8 reg,
-                              u16 *value)
-{
-       struct mii_bus *bus = dev->priv;
-
-       *value = mdiobus_read(bus, addr, reg);
-
-       return 0;
-}
-
-static int b53_mdio_phy_write16(struct b53_device *dev, int addr, u8 reg,
-                               u16 value)
-{
-       struct mii_bus *bus = dev->priv;
-
-       return mdiobus_write(bus, addr, reg, value);
-}
-
-static struct b53_io_ops b53_mdio_ops = {
-       .read8 = b53_mdio_read8,
-       .read16 = b53_mdio_read16,
-       .read32 = b53_mdio_read32,
-       .read48 = b53_mdio_read48,
-       .read64 = b53_mdio_read64,
-       .write8 = b53_mdio_write8,
-       .write16 = b53_mdio_write16,
-       .write32 = b53_mdio_write32,
-       .write48 = b53_mdio_write48,
-       .write64 = b53_mdio_write64,
-       .phy_read16 = b53_mdio_phy_read16,
-       .phy_write16 = b53_mdio_phy_write16,
-};
-
-static int b53_phy_probe(struct phy_device *phydev)
-{
-       struct b53_device dev;
-       int ret;
-
-       /* allow the generic phy driver to take over */
-       if (phydev->mdio.addr != B53_PSEUDO_PHY && phydev->mdio.addr != 0)
-               return -ENODEV;
-
-       dev.current_page = 0xff;
-       dev.priv = phydev->mdio.bus;
-       dev.ops = &b53_mdio_ops;
-       dev.pdata = NULL;
-       mutex_init(&dev.reg_mutex);
-
-       ret = b53_switch_detect(&dev);
-       if (ret)
-               return ret;
-
-       if (is5325(&dev) || is5365(&dev))
-               phydev->supported = SUPPORTED_100baseT_Full;
-       else
-               phydev->supported = SUPPORTED_1000baseT_Full;
-
-       phydev->advertising = phydev->supported;
-
-       return 0;
-}
-
-static int b53_phy_config_init(struct phy_device *phydev)
-{
-       struct b53_device *dev;
-       int ret;
-
-       dev = b53_switch_alloc(&phydev->mdio.dev, &b53_mdio_ops, phydev->mdio.bus);
-       if (!dev)
-               return -ENOMEM;
-
-       /* we don't use page 0xff, so force a page set */
-       dev->current_page = 0xff;
-       /* force the ethX as alias */
-       dev->sw_dev.alias = phydev->attached_dev->name;
-
-       ret = b53_switch_register(dev);
-       if (ret) {
-               dev_err(dev->dev, "failed to register switch: %i\n", ret);
-               return ret;
-       }
-
-       phydev->priv = dev;
-
-       return 0;
-}
-
-static void b53_phy_remove(struct phy_device *phydev)
-{
-       struct b53_device *priv = phydev->priv;
-
-       if (!priv)
-               return;
-
-       b53_switch_remove(priv);
-
-       phydev->priv = NULL;
-}
-
-static int b53_phy_config_aneg(struct phy_device *phydev)
-{
-       return 0;
-}
-
-static int b53_phy_read_status(struct phy_device *phydev)
-{
-       struct b53_device *priv = phydev->priv;
-
-       if (is5325(priv) || is5365(priv))
-               phydev->speed = 100;
-       else
-               phydev->speed = 1000;
-
-       phydev->duplex = DUPLEX_FULL;
-       phydev->link = 1;
-       phydev->state = PHY_RUNNING;
-
-       netif_carrier_on(phydev->attached_dev);
-       phydev->adjust_link(phydev->attached_dev);
-
-       return 0;
-}
-
-/* BCM5325, BCM539x */
-static struct phy_driver b53_phy_driver_id1 = {
-       .phy_id         = 0x0143bc00,
-       .name           = "Broadcom B53 (1)",
-       .phy_id_mask    = 0x1ffffc00,
-       .features       = 0,
-       .probe          = b53_phy_probe,
-       .remove         = b53_phy_remove,
-       .config_aneg    = b53_phy_config_aneg,
-       .config_init    = b53_phy_config_init,
-       .read_status    = b53_phy_read_status,
-};
-
-/* BCM53125, BCM53128 */
-static struct phy_driver b53_phy_driver_id2 = {
-       .phy_id         = 0x03625c00,
-       .name           = "Broadcom B53 (2)",
-       .phy_id_mask    = 0x1ffffc00,
-       .features       = 0,
-       .probe          = b53_phy_probe,
-       .remove         = b53_phy_remove,
-       .config_aneg    = b53_phy_config_aneg,
-       .config_init    = b53_phy_config_init,
-       .read_status    = b53_phy_read_status,
-};
-
-/* BCM5365 */
-static struct phy_driver b53_phy_driver_id3 = {
-       .phy_id         = 0x00406000,
-       .name           = "Broadcom B53 (3)",
-       .phy_id_mask    = 0x1ffffc00,
-       .features       = 0,
-       .probe          = b53_phy_probe,
-       .remove         = b53_phy_remove,
-       .config_aneg    = b53_phy_config_aneg,
-       .config_init    = b53_phy_config_init,
-       .read_status    = b53_phy_read_status,
-};
-
-int __init b53_phy_driver_register(void)
-{
-       int ret;
-
-       ret = phy_driver_register(&b53_phy_driver_id1, THIS_MODULE);
-       if (ret)
-               return ret;
-
-       ret = phy_driver_register(&b53_phy_driver_id2, THIS_MODULE);
-       if (ret)
-               goto err1;
-
-       ret = phy_driver_register(&b53_phy_driver_id3, THIS_MODULE);
-       if (!ret)
-               return 0;
-
-       phy_driver_unregister(&b53_phy_driver_id2);
-err1:
-       phy_driver_unregister(&b53_phy_driver_id1);
-       return ret;
-}
-
-void __exit b53_phy_driver_unregister(void)
-{
-       phy_driver_unregister(&b53_phy_driver_id3);
-       phy_driver_unregister(&b53_phy_driver_id2);
-       phy_driver_unregister(&b53_phy_driver_id1);
-}
-
-module_init(b53_phy_driver_register);
-module_exit(b53_phy_driver_unregister);
-
-MODULE_DESCRIPTION("B53 MDIO access driver");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c b/target/linux/generic/files/drivers/net/phy/b53/b53_mmap.c
deleted file mode 100644 (file)
index ab1895e..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * B53 register access through memory mapped registers
- *
- * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/b53.h>
-
-#include "b53_priv.h"
-
-static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
-{
-       u8 __iomem *regs = dev->priv;
-
-       *val = readb(regs + (page << 8) + reg);
-
-       return 0;
-}
-
-static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
-{
-       u8 __iomem *regs = dev->priv;
-
-       if (WARN_ON(reg % 2))
-               return -EINVAL;
-
-       if (dev->pdata && dev->pdata->big_endian)
-               *val = readw_be(regs + (page << 8) + reg);
-       else
-               *val = readw(regs + (page << 8) + reg);
-
-       return 0;
-}
-
-static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
-{
-       u8 __iomem *regs = dev->priv;
-
-       if (WARN_ON(reg % 4))
-               return -EINVAL;
-
-       if (dev->pdata && dev->pdata->big_endian)
-               *val = readl_be(regs + (page << 8) + reg);
-       else
-               *val = readl(regs + (page << 8) + reg);
-
-       return 0;
-}
-
-static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       if (WARN_ON(reg % 2))
-               return -EINVAL;
-
-       if (reg % 4) {
-               u16 lo;
-               u32 hi;
-
-               b53_mmap_read16(dev, page, reg, &lo);
-               b53_mmap_read32(dev, page, reg + 2, &hi);
-
-               *val = ((u64)hi << 16) | lo;
-       } else {
-               u32 lo;
-               u16 hi;
-
-               b53_mmap_read32(dev, page, reg, &lo);
-               b53_mmap_read16(dev, page, reg + 4, &hi);
-
-               *val = ((u64)hi << 32) | lo;
-       }
-
-       return 0;
-}
-
-static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       u32 hi, lo;
-
-       if (WARN_ON(reg % 4))
-               return -EINVAL;
-
-       b53_mmap_read32(dev, page, reg, &lo);
-       b53_mmap_read32(dev, page, reg + 4, &hi);
-
-       *val = ((u64)hi << 32) | lo;
-
-       return 0;
-}
-
-static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
-{
-       u8 __iomem *regs = dev->priv;
-
-       writeb(value, regs + (page << 8) + reg);
-
-       return 0;
-}
-
-static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
-                            u16 value)
-{
-       u8 __iomem *regs = dev->priv;
-
-       if (WARN_ON(reg % 2))
-               return -EINVAL;
-
-       if (dev->pdata && dev->pdata->big_endian)
-               writew_be(value, regs + (page << 8) + reg);
-       else
-               writew(value, regs + (page << 8) + reg);
-
-       return 0;
-}
-
-static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
-                                   u32 value)
-{
-       u8 __iomem *regs = dev->priv;
-
-       if (WARN_ON(reg % 4))
-               return -EINVAL;
-
-       if (dev->pdata && dev->pdata->big_endian)
-               writel_be(value, regs + (page << 8) + reg);
-       else
-               writel(value, regs + (page << 8) + reg);
-
-       return 0;
-}
-
-static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
-                                   u64 value)
-{
-       if (WARN_ON(reg % 2))
-               return -EINVAL;
-
-       if (reg % 4) {
-               u32 hi = (u32)(value >> 16);
-               u16 lo = (u16)value;
-
-               b53_mmap_write16(dev, page, reg, lo);
-               b53_mmap_write32(dev, page, reg + 2, hi);
-       } else {
-               u16 hi = (u16)(value >> 32);
-               u32 lo = (u32)value;
-
-               b53_mmap_write32(dev, page, reg, lo);
-               b53_mmap_write16(dev, page, reg + 4, hi);
-       }
-
-       return 0;
-}
-
-static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
-                            u64 value)
-{
-       u32 hi, lo;
-
-       hi = (u32)(value >> 32);
-       lo = (u32)value;
-
-       if (WARN_ON(reg % 4))
-               return -EINVAL;
-
-       b53_mmap_write32(dev, page, reg, lo);
-       b53_mmap_write32(dev, page, reg + 4, hi);
-
-       return 0;
-}
-
-static struct b53_io_ops b53_mmap_ops = {
-       .read8 = b53_mmap_read8,
-       .read16 = b53_mmap_read16,
-       .read32 = b53_mmap_read32,
-       .read48 = b53_mmap_read48,
-       .read64 = b53_mmap_read64,
-       .write8 = b53_mmap_write8,
-       .write16 = b53_mmap_write16,
-       .write32 = b53_mmap_write32,
-       .write48 = b53_mmap_write48,
-       .write64 = b53_mmap_write64,
-};
-
-static int b53_mmap_probe(struct platform_device *pdev)
-{
-       struct b53_platform_data *pdata = pdev->dev.platform_data;
-       struct b53_device *dev;
-
-       if (!pdata)
-               return -EINVAL;
-
-       dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, pdata->regs);
-       if (!dev)
-               return -ENOMEM;
-
-       if (pdata)
-               dev->pdata = pdata;
-
-       platform_set_drvdata(pdev, dev);
-
-       return b53_switch_register(dev);
-}
-
-static int b53_mmap_remove(struct platform_device *pdev)
-{
-       struct b53_device *dev = platform_get_drvdata(pdev);
-
-       if (dev)
-               b53_switch_remove(dev);
-
-       return 0;
-}
-
-static struct platform_driver b53_mmap_driver = {
-       .probe = b53_mmap_probe,
-       .remove = b53_mmap_remove,
-       .driver = {
-               .name = "b53-switch",
-       },
-};
-
-module_platform_driver(b53_mmap_driver);
-MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
-MODULE_DESCRIPTION("B53 MMAP access driver");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c b/target/linux/generic/files/drivers/net/phy/b53/b53_phy_fixup.c
deleted file mode 100644 (file)
index e2f8a39..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * B53 PHY Fixup call
- *
- * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/phy.h>
-
-#define B53_PSEUDO_PHY 0x1e /* Register Access Pseudo PHY */
-
-#define B53_BRCM_OUI_1 0x0143bc00
-#define B53_BRCM_OUI_2 0x03625c00
-#define B53_BRCM_OUI_3 0x00406000
-
-static int b53_phy_fixup(struct phy_device *dev)
-{
-       struct mii_bus *bus = dev->mdio.bus;
-       u32 phy_id;
-
-       if (dev->mdio.addr != B53_PSEUDO_PHY)
-               return 0;
-
-       /* read the first port's id */
-       phy_id = mdiobus_read(bus, 0, 2) << 16;
-       phy_id |= mdiobus_read(bus, 0, 3);
-
-       if ((phy_id & 0xfffffc00) == B53_BRCM_OUI_1 ||
-           (phy_id & 0xfffffc00) == B53_BRCM_OUI_2 ||
-           (phy_id & 0xfffffc00) == B53_BRCM_OUI_3) {
-               dev->phy_id = phy_id;
-       }
-
-       return 0;
-}
-
-int __init b53_phy_fixup_register(void)
-{
-       return phy_register_fixup_for_id(PHY_ANY_ID, b53_phy_fixup);
-}
-
-subsys_initcall(b53_phy_fixup_register);
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h b/target/linux/generic/files/drivers/net/phy/b53/b53_priv.h
deleted file mode 100644 (file)
index a9296c9..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * B53 common definitions
- *
- * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef __B53_PRIV_H
-#define __B53_PRIV_H
-
-#include <linux/kernel.h>
-#include <linux/mutex.h>
-#include <linux/switch.h>
-
-struct b53_device;
-
-struct b53_io_ops {
-       int (*read8)(struct b53_device *dev, u8 page, u8 reg, u8 *value);
-       int (*read16)(struct b53_device *dev, u8 page, u8 reg, u16 *value);
-       int (*read32)(struct b53_device *dev, u8 page, u8 reg, u32 *value);
-       int (*read48)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
-       int (*read64)(struct b53_device *dev, u8 page, u8 reg, u64 *value);
-       int (*write8)(struct b53_device *dev, u8 page, u8 reg, u8 value);
-       int (*write16)(struct b53_device *dev, u8 page, u8 reg, u16 value);
-       int (*write32)(struct b53_device *dev, u8 page, u8 reg, u32 value);
-       int (*write48)(struct b53_device *dev, u8 page, u8 reg, u64 value);
-       int (*write64)(struct b53_device *dev, u8 page, u8 reg, u64 value);
-       int (*phy_read16)(struct b53_device *dev, int addr, u8 reg, u16 *value);
-       int (*phy_write16)(struct b53_device *dev, int addr, u8 reg, u16 value);
-};
-
-enum {
-       BCM5325_DEVICE_ID = 0x25,
-       BCM5365_DEVICE_ID = 0x65,
-       BCM5395_DEVICE_ID = 0x95,
-       BCM5397_DEVICE_ID = 0x97,
-       BCM5398_DEVICE_ID = 0x98,
-       BCM53115_DEVICE_ID = 0x53115,
-       BCM53125_DEVICE_ID = 0x53125,
-       BCM53128_DEVICE_ID = 0x53128,
-       BCM63XX_DEVICE_ID = 0x6300,
-       BCM53010_DEVICE_ID = 0x53010,
-       BCM53011_DEVICE_ID = 0x53011,
-       BCM53012_DEVICE_ID = 0x53012,
-       BCM53018_DEVICE_ID = 0x53018,
-       BCM53019_DEVICE_ID = 0x53019,
-};
-
-#define B53_N_PORTS    9
-#define B53_N_PORTS_25 6
-
-struct b53_vlan {
-       unsigned int    members:B53_N_PORTS;
-       unsigned int    untag:B53_N_PORTS;
-};
-
-struct b53_port {
-       unsigned int    pvid:12;
-};
-
-struct b53_device {
-       struct switch_dev sw_dev;
-       struct b53_platform_data *pdata;
-
-       struct mutex reg_mutex;
-       const struct b53_io_ops *ops;
-
-       /* chip specific data */
-       u32 chip_id;
-       u8 core_rev;
-       u8 vta_regs[3];
-       u8 duplex_reg;
-       u8 jumbo_pm_reg;
-       u8 jumbo_size_reg;
-       int reset_gpio;
-
-       /* used ports mask */
-       u16 enabled_ports;
-
-       /* connect specific data */
-       u8 current_page;
-       struct device *dev;
-       void *priv;
-
-       /* run time configuration */
-       unsigned enable_vlan:1;
-       unsigned enable_jumbo:1;
-       unsigned allow_vid_4095:1;
-
-       struct b53_port *ports;
-       struct b53_vlan *vlans;
-
-       char *buf;
-};
-
-#define b53_for_each_port(dev, i) \
-       for (i = 0; i < B53_N_PORTS; i++) \
-               if (dev->enabled_ports & BIT(i))
-
-
-
-static inline int is5325(struct b53_device *dev)
-{
-       return dev->chip_id == BCM5325_DEVICE_ID;
-}
-
-static inline int is5365(struct b53_device *dev)
-{
-#ifdef CONFIG_BCM47XX
-       return dev->chip_id == BCM5365_DEVICE_ID;
-#else
-       return 0;
-#endif
-}
-
-static inline int is5397_98(struct b53_device *dev)
-{
-       return dev->chip_id == BCM5397_DEVICE_ID ||
-               dev->chip_id == BCM5398_DEVICE_ID;
-}
-
-static inline int is539x(struct b53_device *dev)
-{
-       return dev->chip_id == BCM5395_DEVICE_ID ||
-               dev->chip_id == BCM5397_DEVICE_ID ||
-               dev->chip_id == BCM5398_DEVICE_ID;
-}
-
-static inline int is531x5(struct b53_device *dev)
-{
-       return dev->chip_id == BCM53115_DEVICE_ID ||
-               dev->chip_id == BCM53125_DEVICE_ID ||
-               dev->chip_id == BCM53128_DEVICE_ID;
-}
-
-static inline int is63xx(struct b53_device *dev)
-{
-#ifdef CONFIG_BCM63XX
-       return dev->chip_id == BCM63XX_DEVICE_ID;
-#else
-       return 0;
-#endif
-}
-
-static inline int is5301x(struct b53_device *dev)
-{
-       return dev->chip_id == BCM53010_DEVICE_ID ||
-               dev->chip_id == BCM53011_DEVICE_ID ||
-               dev->chip_id == BCM53012_DEVICE_ID ||
-               dev->chip_id == BCM53018_DEVICE_ID ||
-               dev->chip_id == BCM53019_DEVICE_ID;
-}
-
-#define B53_CPU_PORT_25        5
-#define B53_CPU_PORT   8
-
-static inline int is_cpu_port(struct b53_device *dev, int port)
-{
-       return dev->sw_dev.cpu_port == port;
-}
-
-static inline int is_imp_port(struct b53_device *dev, int port)
-{
-       if (is5325(dev) || is5365(dev))
-               return port == B53_CPU_PORT_25;
-       else
-               return port == B53_CPU_PORT;
-}
-
-static inline struct b53_device *sw_to_b53(struct switch_dev *sw)
-{
-       return container_of(sw, struct b53_device, sw_dev);
-}
-
-struct b53_device *b53_switch_alloc(struct device *base, struct b53_io_ops *ops,
-                                   void *priv);
-
-int b53_switch_detect(struct b53_device *dev);
-
-int b53_switch_register(struct b53_device *dev);
-
-static inline void b53_switch_remove(struct b53_device *dev)
-{
-       unregister_switch(&dev->sw_dev);
-}
-
-static inline int b53_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read8(dev, page, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read16(dev, page, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read32(dev, page, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read48(dev, page, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->read64(dev, page, reg, val);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write8(dev, page, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_write16(struct b53_device *dev, u8 page, u8 reg,
-                             u16 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write16(dev, page, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_write32(struct b53_device *dev, u8 page, u8 reg,
-                             u32 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write32(dev, page, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_write48(struct b53_device *dev, u8 page, u8 reg,
-                             u64 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write48(dev, page, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-static inline int b53_write64(struct b53_device *dev, u8 page, u8 reg,
-                              u64 value)
-{
-       int ret;
-
-       mutex_lock(&dev->reg_mutex);
-       ret = dev->ops->write64(dev, page, reg, value);
-       mutex_unlock(&dev->reg_mutex);
-
-       return ret;
-}
-
-#ifdef CONFIG_BCM47XX
-#include <bcm47xx_board.h>
-#endif
-
-#include <linux/version.h>
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
-#include <linux/bcm47xx_nvram.h>
-#endif
-static inline int b53_switch_get_reset_gpio(struct b53_device *dev)
-{
-#ifdef CONFIG_BCM47XX
-       enum bcm47xx_board board = bcm47xx_board_get();
-
-       switch (board) {
-       case BCM47XX_BOARD_LINKSYS_WRT300NV11:
-       case BCM47XX_BOARD_LINKSYS_WRT310NV1:
-               return 8;
-       default:
-               break;
-       }
-#endif
-
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
-       return bcm47xx_nvram_gpio_pin("robo_reset");
-#else
-       return -ENOENT;
-#endif
-}
-
-#endif
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h b/target/linux/generic/files/drivers/net/phy/b53/b53_regs.h
deleted file mode 100644 (file)
index f0bf674..0000000
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * B53 register definitions
- *
- * Copyright (C) 2004 Broadcom Corporation
- * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef __B53_REGS_H
-#define __B53_REGS_H
-
-/* Management Port (SMP) Page offsets */
-#define B53_CTRL_PAGE                  0x00 /* Control */
-#define B53_STAT_PAGE                  0x01 /* Status */
-#define B53_MGMT_PAGE                  0x02 /* Management Mode */
-#define B53_MIB_AC_PAGE                        0x03 /* MIB Autocast */
-#define B53_ARLCTRL_PAGE               0x04 /* ARL Control */
-#define B53_ARLIO_PAGE                 0x05 /* ARL Access */
-#define B53_FRAMEBUF_PAGE              0x06 /* Management frame access */
-#define B53_MEM_ACCESS_PAGE            0x08 /* Memory access */
-
-/* PHY Registers */
-#define B53_PORT_MII_PAGE(i)           (0x10 + (i)) /* Port i MII Registers */
-#define B53_IM_PORT_PAGE               0x18 /* Inverse MII Port (to EMAC) */
-#define B53_ALL_PORT_PAGE              0x19 /* All ports MII (broadcast) */
-
-/* MIB registers */
-#define B53_MIB_PAGE(i)                        (0x20 + (i))
-
-/* Quality of Service (QoS) Registers */
-#define B53_QOS_PAGE                   0x30
-
-/* Port VLAN Page */
-#define B53_PVLAN_PAGE                 0x31
-
-/* VLAN Registers */
-#define B53_VLAN_PAGE                  0x34
-
-/* Jumbo Frame Registers */
-#define B53_JUMBO_PAGE                 0x40
-
-/* CFP Configuration Registers Page */
-#define B53_CFP_PAGE                   0xa1
-
-/*************************************************************************
- * Control Page registers
- *************************************************************************/
-
-/* Port Control Register (8 bit) */
-#define B53_PORT_CTRL(i)               (0x00 + (i))
-#define   PORT_CTRL_RX_DISABLE         BIT(0)
-#define   PORT_CTRL_TX_DISABLE         BIT(1)
-#define   PORT_CTRL_RX_BCST_EN         BIT(2) /* Broadcast RX (P8 only) */
-#define   PORT_CTRL_RX_MCST_EN         BIT(3) /* Multicast RX (P8 only) */
-#define   PORT_CTRL_RX_UCST_EN         BIT(4) /* Unicast RX (P8 only) */
-#define          PORT_CTRL_STP_STATE_S         5
-#define   PORT_CTRL_STP_STATE_MASK     (0x7 << PORT_CTRL_STP_STATE_S)
-
-/* SMP Control Register (8 bit) */
-#define B53_SMP_CTRL                   0x0a
-
-/* Switch Mode Control Register (8 bit) */
-#define B53_SWITCH_MODE                        0x0b
-#define   SM_SW_FWD_MODE               BIT(0)  /* 1 = Managed Mode */
-#define   SM_SW_FWD_EN                 BIT(1)  /* Forwarding Enable */
-
-/* IMP Port state override register (8 bit) */
-#define B53_PORT_OVERRIDE_CTRL         0x0e
-#define   PORT_OVERRIDE_LINK           BIT(0)
-#define   PORT_OVERRIDE_FULL_DUPLEX    BIT(1) /* 0 = Half Duplex */
-#define   PORT_OVERRIDE_SPEED_S                2
-#define   PORT_OVERRIDE_SPEED_10M      (0 << PORT_OVERRIDE_SPEED_S)
-#define   PORT_OVERRIDE_SPEED_100M     (1 << PORT_OVERRIDE_SPEED_S)
-#define   PORT_OVERRIDE_SPEED_1000M    (2 << PORT_OVERRIDE_SPEED_S)
-#define   PORT_OVERRIDE_RV_MII_25      BIT(4) /* BCM5325 only */
-#define   PORT_OVERRIDE_RX_FLOW                BIT(4)
-#define   PORT_OVERRIDE_TX_FLOW                BIT(5)
-#define   PORT_OVERRIDE_SPEED_2000M    BIT(6) /* BCM5301X only, requires setting 1000M */
-#define   PORT_OVERRIDE_EN             BIT(7) /* Use the register contents */
-
-/* Power-down mode control */
-#define B53_PD_MODE_CTRL_25            0x0f
-
-/* IP Multicast control (8 bit) */
-#define B53_IP_MULTICAST_CTRL          0x21
-#define  B53_IPMC_FWD_EN               BIT(1)
-#define  B53_UC_FWD_EN                 BIT(6)
-#define  B53_MC_FWD_EN                 BIT(7)
-
-/* (16 bit) */
-#define B53_UC_FLOOD_MASK              0x32
-#define B53_MC_FLOOD_MASK              0x34
-#define B53_IPMC_FLOOD_MASK            0x36
-
-/*
- * Override Ports 0-7 State on devices with xMII interfaces (8 bit)
- *
- * For port 8 still use B53_PORT_OVERRIDE_CTRL
- * Please note that not all ports are available on every hardware, e.g. BCM5301X
- * don't include overriding port 6, BCM63xx also have some limitations.
- */
-#define B53_GMII_PORT_OVERRIDE_CTRL(i) (0x58 + (i))
-#define   GMII_PO_LINK                 BIT(0)
-#define   GMII_PO_FULL_DUPLEX          BIT(1) /* 0 = Half Duplex */
-#define   GMII_PO_SPEED_S              2
-#define   GMII_PO_SPEED_10M            (0 << GMII_PO_SPEED_S)
-#define   GMII_PO_SPEED_100M           (1 << GMII_PO_SPEED_S)
-#define   GMII_PO_SPEED_1000M          (2 << GMII_PO_SPEED_S)
-#define   GMII_PO_RX_FLOW              BIT(4)
-#define   GMII_PO_TX_FLOW              BIT(5)
-#define   GMII_PO_EN                   BIT(6) /* Use the register contents */
-#define   GMII_PO_SPEED_2000M          BIT(7) /* BCM5301X only, requires setting 1000M */
-
-/* Software reset register (8 bit) */
-#define B53_SOFTRESET                  0x79
-
-/* Fast Aging Control register (8 bit) */
-#define B53_FAST_AGE_CTRL              0x88
-#define   FAST_AGE_STATIC              BIT(0)
-#define   FAST_AGE_DYNAMIC             BIT(1)
-#define   FAST_AGE_PORT                        BIT(2)
-#define   FAST_AGE_VLAN                        BIT(3)
-#define   FAST_AGE_STP                 BIT(4)
-#define   FAST_AGE_MC                  BIT(5)
-#define   FAST_AGE_DONE                        BIT(7)
-
-/*************************************************************************
- * Status Page registers
- *************************************************************************/
-
-/* Link Status Summary Register (16bit) */
-#define B53_LINK_STAT                  0x00
-
-/* Link Status Change Register (16 bit) */
-#define B53_LINK_STAT_CHANGE           0x02
-
-/* Port Speed Summary Register (16 bit for FE, 32 bit for GE) */
-#define B53_SPEED_STAT                 0x04
-#define  SPEED_PORT_FE(reg, port)      (((reg) >> (port)) & 1)
-#define  SPEED_PORT_GE(reg, port)      (((reg) >> 2 * (port)) & 3)
-#define  SPEED_STAT_10M                        0
-#define  SPEED_STAT_100M               1
-#define  SPEED_STAT_1000M              2
-
-/* Duplex Status Summary (16 bit) */
-#define B53_DUPLEX_STAT_FE             0x06
-#define B53_DUPLEX_STAT_GE             0x08
-#define B53_DUPLEX_STAT_63XX           0x0c
-
-/* Revision ID register for BCM5325 */
-#define B53_REV_ID_25                  0x50
-
-/* Strap Value (48 bit) */
-#define B53_STRAP_VALUE                        0x70
-#define   SV_GMII_CTRL_115             BIT(27)
-
-/*************************************************************************
- * Management Mode Page Registers
- *************************************************************************/
-
-/* Global Management Config Register (8 bit) */
-#define B53_GLOBAL_CONFIG              0x00
-#define   GC_RESET_MIB                 0x01
-#define   GC_RX_BPDU_EN                        0x02
-#define   GC_MIB_AC_HDR_EN             0x10
-#define   GC_MIB_AC_EN                 0x20
-#define   GC_FRM_MGMT_PORT_M           0xC0
-#define   GC_FRM_MGMT_PORT_04          0x00
-#define   GC_FRM_MGMT_PORT_MII         0x80
-
-/* Broadcom Header control register (8 bit) */
-#define B53_BRCM_HDR                   0x03
-#define   BRCM_HDR_P8_EN               BIT(0) /* Enable tagging on port 8 */
-#define   BRCM_HDR_P5_EN               BIT(1) /* Enable tagging on port 5 */
-
-/* Device ID register (8 or 32 bit) */
-#define B53_DEVICE_ID                  0x30
-
-/* Revision ID register (8 bit) */
-#define B53_REV_ID                     0x40
-
-/*************************************************************************
- * ARL Access Page Registers
- *************************************************************************/
-
-/* VLAN Table Access Register (8 bit) */
-#define B53_VT_ACCESS                  0x80
-#define B53_VT_ACCESS_9798             0x60 /* for BCM5397/BCM5398 */
-#define B53_VT_ACCESS_63XX             0x60 /* for BCM6328/62/68 */
-#define   VTA_CMD_WRITE                        0
-#define   VTA_CMD_READ                 1
-#define   VTA_CMD_CLEAR                        2
-#define   VTA_START_CMD                        BIT(7)
-
-/* VLAN Table Index Register (16 bit) */
-#define B53_VT_INDEX                   0x81
-#define B53_VT_INDEX_9798              0x61
-#define B53_VT_INDEX_63XX              0x62
-
-/* VLAN Table Entry Register (32 bit) */
-#define B53_VT_ENTRY                   0x83
-#define B53_VT_ENTRY_9798              0x63
-#define B53_VT_ENTRY_63XX              0x64
-#define   VTE_MEMBERS                  0x1ff
-#define   VTE_UNTAG_S                  9
-#define   VTE_UNTAG                    (0x1ff << 9)
-
-/*************************************************************************
- * Port VLAN Registers
- *************************************************************************/
-
-/* Port VLAN mask (16 bit) IMP port is always 8, also on 5325 & co */
-#define B53_PVLAN_PORT_MASK(i)         ((i) * 2)
-
-/*************************************************************************
- * 802.1Q Page Registers
- *************************************************************************/
-
-/* Global QoS Control (8 bit) */
-#define B53_QOS_GLOBAL_CTL             0x00
-
-/* Enable 802.1Q for individual Ports (16 bit) */
-#define B53_802_1P_EN                  0x04
-
-/*************************************************************************
- * VLAN Page Registers
- *************************************************************************/
-
-/* VLAN Control 0 (8 bit) */
-#define B53_VLAN_CTRL0                 0x00
-#define   VC0_8021PF_CTRL_MASK         0x3
-#define   VC0_8021PF_CTRL_NONE         0x0
-#define   VC0_8021PF_CTRL_CHANGE_PRI   0x1
-#define   VC0_8021PF_CTRL_CHANGE_VID   0x2
-#define   VC0_8021PF_CTRL_CHANGE_BOTH  0x3
-#define   VC0_8021QF_CTRL_MASK         0xc
-#define   VC0_8021QF_CTRL_CHANGE_PRI   0x1
-#define   VC0_8021QF_CTRL_CHANGE_VID   0x2
-#define   VC0_8021QF_CTRL_CHANGE_BOTH  0x3
-#define   VC0_RESERVED_1               BIT(1)
-#define   VC0_DROP_VID_MISS            BIT(4)
-#define   VC0_VID_HASH_VID             BIT(5)
-#define   VC0_VID_CHK_EN               BIT(6)  /* Use VID,DA or VID,SA */
-#define   VC0_VLAN_EN                  BIT(7)  /* 802.1Q VLAN Enabled */
-
-/* VLAN Control 1 (8 bit) */
-#define B53_VLAN_CTRL1                 0x01
-#define   VC1_RX_MCST_TAG_EN           BIT(1)
-#define   VC1_RX_MCST_FWD_EN           BIT(2)
-#define   VC1_RX_MCST_UNTAG_EN         BIT(3)
-
-/* VLAN Control 2 (8 bit) */
-#define B53_VLAN_CTRL2                 0x02
-
-/* VLAN Control 3 (8 bit when BCM5325, 16 bit else) */
-#define B53_VLAN_CTRL3                 0x03
-#define B53_VLAN_CTRL3_63XX            0x04
-#define   VC3_MAXSIZE_1532             BIT(6) /* 5325 only */
-#define   VC3_HIGH_8BIT_EN             BIT(7) /* 5325 only */
-
-/* VLAN Control 4 (8 bit) */
-#define B53_VLAN_CTRL4                 0x05
-#define B53_VLAN_CTRL4_25              0x04
-#define B53_VLAN_CTRL4_63XX            0x06
-#define   VC4_ING_VID_CHECK_S          6
-#define   VC4_ING_VID_CHECK_MASK       (0x3 << VC4_ING_VID_CHECK_S)
-#define   VC4_ING_VID_VIO_FWD          0 /* forward, but do not learn */
-#define   VC4_ING_VID_VIO_DROP         1 /* drop VID violations */
-#define   VC4_NO_ING_VID_CHK           2 /* do not check */
-#define   VC4_ING_VID_VIO_TO_IMP       3 /* redirect to MII port */
-
-/* VLAN Control 5 (8 bit) */
-#define B53_VLAN_CTRL5                 0x06
-#define B53_VLAN_CTRL5_25              0x05
-#define B53_VLAN_CTRL5_63XX            0x07
-#define   VC5_VID_FFF_EN               BIT(2)
-#define   VC5_DROP_VTABLE_MISS         BIT(3)
-
-/* VLAN Control 6 (8 bit) */
-#define B53_VLAN_CTRL6                 0x07
-#define B53_VLAN_CTRL6_63XX            0x08
-
-/* VLAN Table Access Register (16 bit) */
-#define B53_VLAN_TABLE_ACCESS_25       0x06    /* BCM5325E/5350 */
-#define B53_VLAN_TABLE_ACCESS_65       0x08    /* BCM5365 */
-#define   VTA_VID_LOW_MASK_25          0xf
-#define   VTA_VID_LOW_MASK_65          0xff
-#define   VTA_VID_HIGH_S_25            4
-#define   VTA_VID_HIGH_S_65            8
-#define   VTA_VID_HIGH_MASK_25         (0xff << VTA_VID_HIGH_S_25E)
-#define   VTA_VID_HIGH_MASK_65         (0xf << VTA_VID_HIGH_S_65)
-#define   VTA_RW_STATE                 BIT(12)
-#define   VTA_RW_STATE_RD              0
-#define   VTA_RW_STATE_WR              BIT(12)
-#define   VTA_RW_OP_EN                 BIT(13)
-
-/* VLAN Read/Write Registers for (16/32 bit) */
-#define B53_VLAN_WRITE_25              0x08
-#define B53_VLAN_WRITE_65              0x0a
-#define B53_VLAN_READ                  0x0c
-#define   VA_MEMBER_MASK               0x3f
-#define   VA_UNTAG_S_25                        6
-#define   VA_UNTAG_MASK_25             0x3f
-#define   VA_UNTAG_S_65                        7
-#define   VA_UNTAG_MASK_65             0x1f
-#define   VA_VID_HIGH_S                        12
-#define   VA_VID_HIGH_MASK             (0xffff << VA_VID_HIGH_S)
-#define   VA_VALID_25                  BIT(20)
-#define   VA_VALID_25_R4               BIT(24)
-#define   VA_VALID_65                  BIT(14)
-
-/* VLAN Port Default Tag (16 bit) */
-#define B53_VLAN_PORT_DEF_TAG(i)       (0x10 + 2 * (i))
-
-/*************************************************************************
- * Jumbo Frame Page Registers
- *************************************************************************/
-
-/* Jumbo Enable Port Mask (bit i == port i enabled) (32 bit) */
-#define B53_JUMBO_PORT_MASK            0x01
-#define B53_JUMBO_PORT_MASK_63XX       0x04
-#define   JPM_10_100_JUMBO_EN          BIT(24) /* GigE always enabled */
-
-/* Good Frame Max Size without 802.1Q TAG (16 bit) */
-#define B53_JUMBO_MAX_SIZE             0x05
-#define B53_JUMBO_MAX_SIZE_63XX                0x08
-#define   JMS_MIN_SIZE                 1518
-#define   JMS_MAX_SIZE                 9724
-
-/*************************************************************************
- * CFP Configuration Page Registers
- *************************************************************************/
-
-/* CFP Control Register with ports map (8 bit) */
-#define B53_CFP_CTRL                   0x00
-
-#endif /* !__B53_REGS_H */
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c b/target/linux/generic/files/drivers/net/phy/b53/b53_spi.c
deleted file mode 100644 (file)
index efc8f7e..0000000
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * B53 register access through SPI
- *
- * Copyright (C) 2011-2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <asm/unaligned.h>
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/spi/spi.h>
-#include <linux/of.h>
-#include <linux/platform_data/b53.h>
-
-#include "b53_priv.h"
-
-#define B53_SPI_DATA           0xf0
-
-#define B53_SPI_STATUS         0xfe
-#define B53_SPI_CMD_SPIF       BIT(7)
-#define B53_SPI_CMD_RACK       BIT(5)
-
-#define B53_SPI_CMD_READ       0x00
-#define B53_SPI_CMD_WRITE      0x01
-#define B53_SPI_CMD_NORMAL     0x60
-#define B53_SPI_CMD_FAST       0x10
-
-#define B53_SPI_PAGE_SELECT    0xff
-
-static inline int b53_spi_read_reg(struct spi_device *spi, u8 reg, u8 *val,
-                                    unsigned len)
-{
-       u8 txbuf[2];
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_READ;
-       txbuf[1] = reg;
-
-       return spi_write_then_read(spi, txbuf, 2, val, len);
-}
-
-static inline int b53_spi_clear_status(struct spi_device *spi)
-{
-       unsigned int i;
-       u8 rxbuf;
-       int ret;
-
-       for (i = 0; i < 10; i++) {
-               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
-               if (ret)
-                       return ret;
-
-               if (!(rxbuf & B53_SPI_CMD_SPIF))
-                       break;
-
-               mdelay(1);
-       }
-
-       if (i == 10)
-               return -EIO;
-
-       return 0;
-}
-
-static inline int b53_spi_set_page(struct spi_device *spi, u8 page)
-{
-       u8 txbuf[3];
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
-       txbuf[1] = B53_SPI_PAGE_SELECT;
-       txbuf[2] = page;
-
-       return spi_write(spi, txbuf, sizeof(txbuf));
-}
-
-static inline int b53_prepare_reg_access(struct spi_device *spi, u8 page)
-{
-       int ret = b53_spi_clear_status(spi);
-
-       if (ret)
-               return ret;
-
-       return b53_spi_set_page(spi, page);
-}
-
-static int b53_spi_prepare_reg_read(struct spi_device *spi, u8 reg)
-{
-       u8 rxbuf;
-       int retry_count;
-       int ret;
-
-       ret = b53_spi_read_reg(spi, reg, &rxbuf, 1);
-       if (ret)
-               return ret;
-
-       for (retry_count = 0; retry_count < 10; retry_count++) {
-               ret = b53_spi_read_reg(spi, B53_SPI_STATUS, &rxbuf, 1);
-               if (ret)
-                       return ret;
-
-               if (rxbuf & B53_SPI_CMD_RACK)
-                       break;
-
-               mdelay(1);
-       }
-
-       if (retry_count == 10)
-               return -EIO;
-
-       return 0;
-}
-
-static int b53_spi_read(struct b53_device *dev, u8 page, u8 reg, u8 *data,
-                       unsigned len)
-{
-       struct spi_device *spi = dev->priv;
-       int ret;
-
-       ret = b53_prepare_reg_access(spi, page);
-       if (ret)
-               return ret;
-
-       ret = b53_spi_prepare_reg_read(spi, reg);
-       if (ret)
-               return ret;
-
-       return b53_spi_read_reg(spi, B53_SPI_DATA, data, len);
-}
-
-static int b53_spi_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
-{
-       return b53_spi_read(dev, page, reg, val, 1);
-}
-
-static int b53_spi_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
-{
-       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 2);
-
-       if (!ret)
-               *val = le16_to_cpu(*val);
-
-       return ret;
-}
-
-static int b53_spi_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
-{
-       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 4);
-
-       if (!ret)
-               *val = le32_to_cpu(*val);
-
-       return ret;
-}
-
-static int b53_spi_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       int ret;
-
-       *val = 0;
-       ret = b53_spi_read(dev, page, reg, (u8 *)val, 6);
-       if (!ret)
-               *val = le64_to_cpu(*val);
-
-       return ret;
-}
-
-static int b53_spi_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       int ret = b53_spi_read(dev, page, reg, (u8 *)val, 8);
-
-       if (!ret)
-               *val = le64_to_cpu(*val);
-
-       return ret;
-}
-
-static int b53_spi_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
-{
-       struct spi_device *spi = dev->priv;
-       int ret;
-       u8 txbuf[3];
-
-       ret = b53_prepare_reg_access(spi, page);
-       if (ret)
-               return ret;
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
-       txbuf[1] = reg;
-       txbuf[2] = value;
-
-       return spi_write(spi, txbuf, sizeof(txbuf));
-}
-
-static int b53_spi_write16(struct b53_device *dev, u8 page, u8 reg, u16 value)
-{
-       struct spi_device *spi = dev->priv;
-       int ret;
-       u8 txbuf[4];
-
-       ret = b53_prepare_reg_access(spi, page);
-       if (ret)
-               return ret;
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
-       txbuf[1] = reg;
-       put_unaligned_le16(value, &txbuf[2]);
-
-       return spi_write(spi, txbuf, sizeof(txbuf));
-}
-
-static int b53_spi_write32(struct b53_device *dev, u8 page, u8 reg, u32 value)
-{
-       struct spi_device *spi = dev->priv;
-       int ret;
-       u8 txbuf[6];
-
-       ret = b53_prepare_reg_access(spi, page);
-       if (ret)
-               return ret;
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
-       txbuf[1] = reg;
-       put_unaligned_le32(value, &txbuf[2]);
-
-       return spi_write(spi, txbuf, sizeof(txbuf));
-}
-
-static int b53_spi_write48(struct b53_device *dev, u8 page, u8 reg, u64 value)
-{
-       struct spi_device *spi = dev->priv;
-       int ret;
-       u8 txbuf[10];
-
-       ret = b53_prepare_reg_access(spi, page);
-       if (ret)
-               return ret;
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
-       txbuf[1] = reg;
-       put_unaligned_le64(value, &txbuf[2]);
-
-       return spi_write(spi, txbuf, sizeof(txbuf) - 2);
-}
-
-static int b53_spi_write64(struct b53_device *dev, u8 page, u8 reg, u64 value)
-{
-       struct spi_device *spi = dev->priv;
-       int ret;
-       u8 txbuf[10];
-
-       ret = b53_prepare_reg_access(spi, page);
-       if (ret)
-               return ret;
-
-       txbuf[0] = B53_SPI_CMD_NORMAL | B53_SPI_CMD_WRITE;
-       txbuf[1] = reg;
-       put_unaligned_le64(value, &txbuf[2]);
-
-       return spi_write(spi, txbuf, sizeof(txbuf));
-}
-
-static struct b53_io_ops b53_spi_ops = {
-       .read8 = b53_spi_read8,
-       .read16 = b53_spi_read16,
-       .read32 = b53_spi_read32,
-       .read48 = b53_spi_read48,
-       .read64 = b53_spi_read64,
-       .write8 = b53_spi_write8,
-       .write16 = b53_spi_write16,
-       .write32 = b53_spi_write32,
-       .write48 = b53_spi_write48,
-       .write64 = b53_spi_write64,
-};
-
-static int b53_spi_probe(struct spi_device *spi)
-{
-       struct b53_device *dev;
-       int ret;
-
-       dev = b53_switch_alloc(&spi->dev, &b53_spi_ops, spi);
-       if (!dev)
-               return -ENOMEM;
-
-       if (spi->dev.platform_data)
-               dev->pdata = spi->dev.platform_data;
-
-       ret = b53_switch_register(dev);
-       if (ret)
-               return ret;
-
-       spi_set_drvdata(spi, dev);
-
-       return 0;
-}
-
-static int b53_spi_remove(struct spi_device *spi)
-{
-       struct b53_device *dev = spi_get_drvdata(spi);
-
-       if (dev)
-               b53_switch_remove(dev);
-
-       return 0;
-}
-
-static const struct of_device_id b53_of_match[] = {
-       { .compatible = "brcm,bcm5325" },
-       { .compatible = "brcm,bcm53115" },
-       { .compatible = "brcm,bcm53125" },
-       { .compatible = "brcm,bcm53128" },
-       { .compatible = "brcm,bcm5365" },
-       { .compatible = "brcm,bcm5395" },
-       { .compatible = "brcm,bcm5397" },
-       { .compatible = "brcm,bcm5398" },
-       { /* sentinel */ },
-};
-
-static struct spi_driver b53_spi_driver = {
-       .driver = {
-               .name   = "b53-switch",
-               .bus    = &spi_bus_type,
-               .owner  = THIS_MODULE,
-               .of_match_table = b53_of_match,
-       },
-       .probe  = b53_spi_probe,
-       .remove = b53_spi_remove,
-};
-
-module_spi_driver(b53_spi_driver);
-
-MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
-MODULE_DESCRIPTION("B53 SPI access driver");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c b/target/linux/generic/files/drivers/net/phy/b53/b53_srab.c
deleted file mode 100644 (file)
index 012daa3..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * B53 register access through Switch Register Access Bridge Registers
- *
- * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/platform_data/b53.h>
-
-#include "b53_priv.h"
-
-/* command and status register of the SRAB */
-#define B53_SRAB_CMDSTAT               0x2c
-#define  B53_SRAB_CMDSTAT_RST          BIT(2)
-#define  B53_SRAB_CMDSTAT_WRITE                BIT(1)
-#define  B53_SRAB_CMDSTAT_GORDYN       BIT(0)
-#define  B53_SRAB_CMDSTAT_PAGE         24
-#define  B53_SRAB_CMDSTAT_REG          16
-
-/* high order word of write data to switch registe */
-#define B53_SRAB_WD_H                  0x30
-
-/* low order word of write data to switch registe */
-#define B53_SRAB_WD_L                  0x34
-
-/* high order word of read data from switch register */
-#define B53_SRAB_RD_H                  0x38
-
-/* low order word of read data from switch register */
-#define B53_SRAB_RD_L                  0x3c
-
-/* command and status register of the SRAB */
-#define B53_SRAB_CTRLS                 0x40
-#define  B53_SRAB_CTRLS_RCAREQ         BIT(3)
-#define  B53_SRAB_CTRLS_RCAGNT         BIT(4)
-#define  B53_SRAB_CTRLS_SW_INIT_DONE   BIT(6)
-
-/* the register captures interrupt pulses from the switch */
-#define B53_SRAB_INTR                  0x44
-
-static int b53_srab_request_grant(struct b53_device *dev)
-{
-       u8 __iomem *regs = dev->priv;
-       u32 ctrls;
-       int i;
-
-       ctrls = readl(regs + B53_SRAB_CTRLS);
-       ctrls |= B53_SRAB_CTRLS_RCAREQ;
-       writel(ctrls, regs + B53_SRAB_CTRLS);
-
-       for (i = 0; i < 20; i++) {
-               ctrls = readl(regs + B53_SRAB_CTRLS);
-               if (ctrls & B53_SRAB_CTRLS_RCAGNT)
-                       break;
-               usleep_range(10, 100);
-       }
-       if (WARN_ON(i == 5))
-               return -EIO;
-
-       return 0;
-}
-
-static void b53_srab_release_grant(struct b53_device *dev)
-{
-       u8 __iomem *regs = dev->priv;
-       u32 ctrls;
-
-       ctrls = readl(regs + B53_SRAB_CTRLS);
-       ctrls &= ~B53_SRAB_CTRLS_RCAREQ;
-       writel(ctrls, regs + B53_SRAB_CTRLS);
-}
-
-static int b53_srab_op(struct b53_device *dev, u8 page, u8 reg, u32 op)
-{
-       int i;
-       u32 cmdstat;
-       u8 __iomem *regs = dev->priv;
-
-       /* set register address */
-       cmdstat = (page << B53_SRAB_CMDSTAT_PAGE) |
-                 (reg << B53_SRAB_CMDSTAT_REG) |
-                 B53_SRAB_CMDSTAT_GORDYN |
-                 op;
-       writel(cmdstat, regs + B53_SRAB_CMDSTAT);
-
-       /* check if operation completed */
-       for (i = 0; i < 5; ++i) {
-               cmdstat = readl(regs + B53_SRAB_CMDSTAT);
-               if (!(cmdstat & B53_SRAB_CMDSTAT_GORDYN))
-                       break;
-               usleep_range(10, 100);
-       }
-
-       if (WARN_ON(i == 5))
-               return -EIO;
-
-       return 0;
-}
-
-static int b53_srab_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       ret = b53_srab_op(dev, page, reg, 0);
-       if (ret)
-               goto err;
-
-       *val = readl(regs + B53_SRAB_RD_L) & 0xff;
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       ret = b53_srab_op(dev, page, reg, 0);
-       if (ret)
-               goto err;
-
-       *val = readl(regs + B53_SRAB_RD_L) & 0xffff;
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       ret = b53_srab_op(dev, page, reg, 0);
-       if (ret)
-               goto err;
-
-       *val = readl(regs + B53_SRAB_RD_L);
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       ret = b53_srab_op(dev, page, reg, 0);
-       if (ret)
-               goto err;
-
-       *val = readl(regs + B53_SRAB_RD_L);
-       *val += ((u64)readl(regs + B53_SRAB_RD_H) & 0xffff) << 32;
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       ret = b53_srab_op(dev, page, reg, 0);
-       if (ret)
-               goto err;
-
-       *val = readl(regs + B53_SRAB_RD_L);
-       *val += (u64)readl(regs + B53_SRAB_RD_H) << 32;
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       writel(value, regs + B53_SRAB_WD_L);
-
-       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_write16(struct b53_device *dev, u8 page, u8 reg,
-                            u16 value)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       writel(value, regs + B53_SRAB_WD_L);
-
-       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static int b53_srab_write32(struct b53_device *dev, u8 page, u8 reg,
-                                   u32 value)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       writel(value, regs + B53_SRAB_WD_L);
-
-       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-
-}
-
-static int b53_srab_write48(struct b53_device *dev, u8 page, u8 reg,
-                                   u64 value)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       writel((u32)value, regs + B53_SRAB_WD_L);
-       writel((u16)(value >> 32), regs + B53_SRAB_WD_H);
-
-       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-
-}
-
-static int b53_srab_write64(struct b53_device *dev, u8 page, u8 reg,
-                            u64 value)
-{
-       u8 __iomem *regs = dev->priv;
-       int ret = 0;
-
-       ret = b53_srab_request_grant(dev);
-       if (ret)
-               goto err;
-
-       writel((u32)value, regs + B53_SRAB_WD_L);
-       writel((u32)(value >> 32), regs + B53_SRAB_WD_H);
-
-       ret = b53_srab_op(dev, page, reg, B53_SRAB_CMDSTAT_WRITE);
-
-err:
-       b53_srab_release_grant(dev);
-
-       return ret;
-}
-
-static struct b53_io_ops b53_srab_ops = {
-       .read8 = b53_srab_read8,
-       .read16 = b53_srab_read16,
-       .read32 = b53_srab_read32,
-       .read48 = b53_srab_read48,
-       .read64 = b53_srab_read64,
-       .write8 = b53_srab_write8,
-       .write16 = b53_srab_write16,
-       .write32 = b53_srab_write32,
-       .write48 = b53_srab_write48,
-       .write64 = b53_srab_write64,
-};
-
-static int b53_srab_probe(struct platform_device *pdev)
-{
-       struct b53_platform_data *pdata = pdev->dev.platform_data;
-       struct b53_device *dev;
-
-       if (!pdata)
-               return -EINVAL;
-
-       dev = b53_switch_alloc(&pdev->dev, &b53_srab_ops, pdata->regs);
-       if (!dev)
-               return -ENOMEM;
-
-       if (pdata)
-               dev->pdata = pdata;
-
-       platform_set_drvdata(pdev, dev);
-
-       return b53_switch_register(dev);
-}
-
-static int b53_srab_remove(struct platform_device *pdev)
-{
-       struct b53_device *dev = platform_get_drvdata(pdev);
-
-       if (dev)
-               b53_switch_remove(dev);
-
-       return 0;
-}
-
-static struct platform_driver b53_srab_driver = {
-       .probe = b53_srab_probe,
-       .remove = b53_srab_remove,
-       .driver = {
-               .name = "b53-srab-switch",
-       },
-};
-
-module_platform_driver(b53_srab_driver);
-MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>");
-MODULE_DESCRIPTION("B53 Switch Register Access Bridge Registers (SRAB) access driver");
-MODULE_LICENSE("Dual BSD/GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/ip17xx.c b/target/linux/generic/files/drivers/net/phy/ip17xx.c
deleted file mode 100644 (file)
index 85a9617..0000000
+++ /dev/null
@@ -1,1377 +0,0 @@
-/*
- * ip17xx.c: Swconfig configuration for IC+ IP17xx switch family
- *
- * Copyright (C) 2008 Patrick Horn <patrick.horn@gmail.com>
- * Copyright (C) 2008, 2010 Martin Mares <mj@ucw.cz>
- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
- * GNU General Public License for more details.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/skbuff.h>
-#include <linux/mii.h>
-#include <linux/phy.h>
-#include <linux/delay.h>
-#include <linux/switch.h>
-#include <linux/device.h>
-
-#define MAX_VLANS 16
-#define MAX_PORTS 9
-#undef DUMP_MII_IO
-
-typedef struct ip17xx_reg {
-       u16 p;                  // phy
-       u16 m;                  // mii
-} reg;
-typedef char bitnum;
-
-#define NOTSUPPORTED {-1,-1}
-
-#define REG_SUPP(x) (((x).m != ((u16)-1)) && ((x).p != (u16)-1))
-
-struct ip17xx_state;
-
-/*********** CONSTANTS ***********/
-struct register_mappings {
-       char *NAME;
-       u16 MODEL_NO;                   // Compare to bits 4-9 of MII register 0,3.
-       bitnum NUM_PORTS;
-       bitnum CPU_PORT;
-
-/* The default VLAN for each port.
-        Default: 0x0001 for Ports 0,1,2,3
-                 0x0002 for Ports 4,5 */
-       reg VLAN_DEFAULT_TAG_REG[MAX_PORTS];
-
-/* These ports are tagged.
-        Default: 0x00 */
-       reg ADD_TAG_REG;
-       reg REMOVE_TAG_REG;
-       bitnum ADD_TAG_BIT[MAX_PORTS];
-/* These ports are untagged.
-        Default: 0x00 (i.e. do not alter any VLAN tags...)
-        Maybe set to 0 if user disables VLANs. */
-       bitnum REMOVE_TAG_BIT[MAX_PORTS];
-
-/* Port M and Port N are on the same VLAN.
-        Default: All ports on all VLANs. */
-// Use register {29, 19+N/2}
-       reg VLAN_LOOKUP_REG;
-// Port 5 uses register {30, 18} but same as odd bits.
-       reg VLAN_LOOKUP_REG_5;          // in a different register on IP175C.
-       bitnum VLAN_LOOKUP_EVEN_BIT[MAX_PORTS];
-       bitnum VLAN_LOOKUP_ODD_BIT[MAX_PORTS];
-
-/* This VLAN corresponds to which ports.
-        Default: 0x2f,0x30,0x3f,0x3f... */
-       reg TAG_VLAN_MASK_REG;
-       bitnum TAG_VLAN_MASK_EVEN_BIT[MAX_PORTS];
-       bitnum TAG_VLAN_MASK_ODD_BIT[MAX_PORTS];
-
-       int RESET_VAL;
-       reg RESET_REG;
-
-       reg MODE_REG;
-       int MODE_VAL;
-
-/* General flags */
-       reg ROUTER_CONTROL_REG;
-       reg VLAN_CONTROL_REG;
-       bitnum TAG_VLAN_BIT;
-       bitnum ROUTER_EN_BIT;
-       bitnum NUMLAN_GROUPS_MAX;
-       bitnum NUMLAN_GROUPS_BIT;
-
-       reg MII_REGISTER_EN;
-       bitnum MII_REGISTER_EN_BIT;
-
-       // set to 1 for 178C, 0 for 175C.
-       bitnum SIMPLE_VLAN_REGISTERS;   // 175C has two vlans per register but 178C has only one.
-
-       // Pointers to functions which manipulate hardware state
-       int (*update_state)(struct ip17xx_state *state);
-       int (*set_vlan_mode)(struct ip17xx_state *state);
-       int (*reset)(struct ip17xx_state *state);
-};
-
-static int ip175c_update_state(struct ip17xx_state *state);
-static int ip175c_set_vlan_mode(struct ip17xx_state *state);
-static int ip175c_reset(struct ip17xx_state *state);
-
-static const struct register_mappings IP178C = {
-       .NAME = "IP178C",
-       .MODEL_NO = 0x18,
-       .VLAN_DEFAULT_TAG_REG = {
-               {30,3},{30,4},{30,5},{30,6},{30,7},{30,8},
-               {30,9},{30,10},{30,11},
-       },
-
-       .ADD_TAG_REG = {30,12},
-       .ADD_TAG_BIT = {0,1,2,3,4,5,6,7,8},
-       .REMOVE_TAG_REG = {30,13},
-       .REMOVE_TAG_BIT = {4,5,6,7,8,9,10,11,12},
-
-       .SIMPLE_VLAN_REGISTERS = 1,
-
-       .VLAN_LOOKUP_REG = {31,0},// +N
-       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED, // not used with SIMPLE_VLAN_REGISTERS
-       .VLAN_LOOKUP_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
-       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,5,6,7,8},
-
-       .TAG_VLAN_MASK_REG = {30,14}, // +N
-       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,6,7,8},
-       .TAG_VLAN_MASK_ODD_BIT = {0,1,2,3,4,5,6,7,8},
-
-       .RESET_VAL = 0x55AA,
-       .RESET_REG = {30,0},
-       .MODE_VAL = 0,
-       .MODE_REG = NOTSUPPORTED,
-
-       .ROUTER_CONTROL_REG = {30,30},
-       .ROUTER_EN_BIT = 11,
-       .NUMLAN_GROUPS_MAX = 8,
-       .NUMLAN_GROUPS_BIT = 8, // {0-2}
-
-       .VLAN_CONTROL_REG = {30,13},
-       .TAG_VLAN_BIT = 3,
-
-       .CPU_PORT = 8,
-       .NUM_PORTS = 9,
-
-       .MII_REGISTER_EN = NOTSUPPORTED,
-
-       .update_state = ip175c_update_state,
-       .set_vlan_mode = ip175c_set_vlan_mode,
-       .reset = ip175c_reset,
-};
-
-static const struct register_mappings IP175C = {
-       .NAME = "IP175C",
-       .MODEL_NO = 0x18,
-       .VLAN_DEFAULT_TAG_REG = {
-               {29,24},{29,25},{29,26},{29,27},{29,28},{29,30},
-               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
-       },
-
-       .ADD_TAG_REG = {29,23},
-       .REMOVE_TAG_REG = {29,23},
-       .ADD_TAG_BIT = {11,12,13,14,15,1,-1,-1,-1},
-       .REMOVE_TAG_BIT = {6,7,8,9,10,0,-1,-1,-1},
-
-       .SIMPLE_VLAN_REGISTERS = 0,
-
-       .VLAN_LOOKUP_REG = {29,19},// +N/2
-       .VLAN_LOOKUP_REG_5 = {30,18},
-       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,15,-1,-1,-1},
-       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,7,-1,-1,-1},
-
-       .TAG_VLAN_MASK_REG = {30,1}, // +N/2
-       .TAG_VLAN_MASK_EVEN_BIT = {0,1,2,3,4,5,-1,-1,-1},
-       .TAG_VLAN_MASK_ODD_BIT = {8,9,10,11,12,13,-1,-1,-1},
-
-       .RESET_VAL = 0x175C,
-       .RESET_REG = {30,0},
-       .MODE_VAL = 0x175C,
-       .MODE_REG = {29,31},
-
-       .ROUTER_CONTROL_REG = {30,9},
-       .ROUTER_EN_BIT = 3,
-       .NUMLAN_GROUPS_MAX = 8,
-       .NUMLAN_GROUPS_BIT = 0, // {0-2}
-
-       .VLAN_CONTROL_REG = {30,9},
-       .TAG_VLAN_BIT = 7,
-
-       .NUM_PORTS = 6,
-       .CPU_PORT = 5,
-
-       .MII_REGISTER_EN = NOTSUPPORTED,
-
-       .update_state = ip175c_update_state,
-       .set_vlan_mode = ip175c_set_vlan_mode,
-       .reset = ip175c_reset,
-};
-
-static const struct register_mappings IP175A = {
-       .NAME = "IP175A",
-       .MODEL_NO = 0x05,
-       .VLAN_DEFAULT_TAG_REG = {
-               {0,24},{0,25},{0,26},{0,27},{0,28},NOTSUPPORTED,
-               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED
-       },
-
-       .ADD_TAG_REG = {0,23},
-       .REMOVE_TAG_REG = {0,23},
-       .ADD_TAG_BIT = {11,12,13,14,15,-1,-1,-1,-1},
-       .REMOVE_TAG_BIT = {6,7,8,9,10,-1,-1,-1,-1},
-
-       .SIMPLE_VLAN_REGISTERS = 0,
-
-       // Only programmable via EEPROM
-       .VLAN_LOOKUP_REG = NOTSUPPORTED,// +N/2
-       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
-       .VLAN_LOOKUP_EVEN_BIT = {8,9,10,11,12,-1,-1,-1,-1},
-       .VLAN_LOOKUP_ODD_BIT = {0,1,2,3,4,-1,-1,-1,-1},
-
-       .TAG_VLAN_MASK_REG = NOTSUPPORTED, // +N/2,
-       .TAG_VLAN_MASK_EVEN_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
-       .TAG_VLAN_MASK_ODD_BIT = {-1,-1,-1,-1,-1,-1,-1,-1,-1},
-
-       .RESET_VAL = -1,
-       .RESET_REG = NOTSUPPORTED,
-       .MODE_VAL = 0,
-       .MODE_REG = NOTSUPPORTED,
-
-       .ROUTER_CONTROL_REG = NOTSUPPORTED,
-       .VLAN_CONTROL_REG = NOTSUPPORTED,
-       .TAG_VLAN_BIT = -1,
-       .ROUTER_EN_BIT = -1,
-       .NUMLAN_GROUPS_MAX = -1,
-       .NUMLAN_GROUPS_BIT = -1, // {0-2}
-
-       .NUM_PORTS = 5,
-       .CPU_PORT = 4,
-
-       .MII_REGISTER_EN = {0, 18},
-       .MII_REGISTER_EN_BIT = 7,
-
-       .update_state = ip175c_update_state,
-       .set_vlan_mode = ip175c_set_vlan_mode,
-       .reset = ip175c_reset,
-};
-
-
-static int ip175d_update_state(struct ip17xx_state *state);
-static int ip175d_set_vlan_mode(struct ip17xx_state *state);
-static int ip175d_reset(struct ip17xx_state *state);
-
-static const struct register_mappings IP175D = {
-       .NAME = "IP175D",
-       .MODEL_NO = 0x18,
-
-       // The IP175D has a completely different interface, so we leave most
-       // of the registers undefined and switch to different code paths.
-
-       .VLAN_DEFAULT_TAG_REG = {
-               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
-               NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,NOTSUPPORTED,
-       },
-
-       .ADD_TAG_REG = NOTSUPPORTED,
-       .REMOVE_TAG_REG = NOTSUPPORTED,
-
-       .SIMPLE_VLAN_REGISTERS = 0,
-
-       .VLAN_LOOKUP_REG = NOTSUPPORTED,
-       .VLAN_LOOKUP_REG_5 = NOTSUPPORTED,
-       .TAG_VLAN_MASK_REG = NOTSUPPORTED,
-
-       .RESET_VAL = 0x175D,
-       .RESET_REG = {20,2},
-       .MODE_REG = NOTSUPPORTED,
-
-       .ROUTER_CONTROL_REG = NOTSUPPORTED,
-       .ROUTER_EN_BIT = -1,
-       .NUMLAN_GROUPS_BIT = -1,
-
-       .VLAN_CONTROL_REG = NOTSUPPORTED,
-       .TAG_VLAN_BIT = -1,
-
-       .NUM_PORTS = 6,
-       .CPU_PORT = 5,
-
-       .MII_REGISTER_EN = NOTSUPPORTED,
-
-       .update_state = ip175d_update_state,
-       .set_vlan_mode = ip175d_set_vlan_mode,
-       .reset = ip175d_reset,
-};
-
-struct ip17xx_state {
-       struct switch_dev dev;
-       struct mii_bus *mii_bus;
-       bool registered;
-
-       int router_mode;                // ROUTER_EN
-       int vlan_enabled;               // TAG_VLAN_EN
-       struct port_state {
-               u16 pvid;
-               unsigned int shareports;
-       } ports[MAX_PORTS];
-       unsigned int add_tag;
-       unsigned int remove_tag;
-       int num_vlans;
-       struct vlan_state {
-               unsigned int ports;
-               unsigned int tag;       // VLAN tag (IP175D only)
-       } vlans[MAX_VLANS];
-       const struct register_mappings *regs;
-       reg proc_mii;   // phy/reg for the low level register access via swconfig
-
-       char buf[80];
-};
-
-#define get_state(_dev) container_of((_dev), struct ip17xx_state, dev)
-
-static int ip_phy_read(struct ip17xx_state *state, int port, int reg)
-{
-       int val = mdiobus_read(state->mii_bus, port, reg);
-       if (val < 0)
-               pr_warning("IP17xx: Unable to get MII register %d,%d: error %d\n", port, reg, -val);
-#ifdef DUMP_MII_IO
-       else
-               pr_debug("IP17xx: Read MII(%d,%d) -> %04x\n", port, reg, val);
-#endif
-       return val;
-}
-
-static int ip_phy_write(struct ip17xx_state *state, int port, int reg, u16 val)
-{
-       int err;
-
-#ifdef DUMP_MII_IO
-       pr_debug("IP17xx: Write MII(%d,%d) <- %04x\n", port, reg, val);
-#endif
-       err = mdiobus_write(state->mii_bus, port, reg, val);
-       if (err < 0)
-               pr_warning("IP17xx: Unable to write MII register %d,%d: error %d\n", port, reg, -err);
-       return err;
-}
-
-static int ip_phy_write_masked(struct ip17xx_state *state, int port, int reg, unsigned int mask, unsigned int data)
-{
-       int val = ip_phy_read(state, port, reg);
-       if (val < 0)
-               return 0;
-       return ip_phy_write(state, port, reg, (val & ~mask) | data);
-}
-
-static int getPhy(struct ip17xx_state *state, reg mii)
-{
-       if (!REG_SUPP(mii))
-               return -EFAULT;
-       return ip_phy_read(state, mii.p, mii.m);
-}
-
-static int setPhy(struct ip17xx_state *state, reg mii, u16 value)
-{
-       int err;
-
-       if (!REG_SUPP(mii))
-               return -EFAULT;
-       err = ip_phy_write(state, mii.p, mii.m, value);
-       if (err < 0)
-               return err;
-       mdelay(2);
-       getPhy(state, mii);
-       return 0;
-}
-
-
-/**
- * These two macros are to simplify the mapping of logical bits to the bits in hardware.
- * NOTE: these macros will return if there is an error!
- */
-
-#define GET_PORT_BITS(state, bits, addr, bit_lookup)           \
-       do {                                                    \
-               int i, val = getPhy((state), (addr));           \
-               if (val < 0)                                    \
-                       return val;                             \
-               (bits) = 0;                                     \
-               for (i = 0; i < MAX_PORTS; i++) {               \
-                       if ((bit_lookup)[i] == -1) continue;    \
-                       if (val & (1<<(bit_lookup)[i]))         \
-                               (bits) |= (1<<i);               \
-               }                                               \
-       } while (0)
-
-#define SET_PORT_BITS(state, bits, addr, bit_lookup)           \
-       do {                                                    \
-               int i, val = getPhy((state), (addr));           \
-               if (val < 0)                                    \
-                       return val;                             \
-               for (i = 0; i < MAX_PORTS; i++) {               \
-                       unsigned int newmask = ((bits)&(1<<i)); \
-                       if ((bit_lookup)[i] == -1) continue;    \
-                       val &= ~(1<<(bit_lookup)[i]);           \
-                       val |= ((newmask>>i)<<(bit_lookup)[i]); \
-               }                                               \
-               val = setPhy((state), (addr), val);             \
-               if (val < 0)                                    \
-                       return val;                             \
-       } while (0)
-
-
-static int get_model(struct ip17xx_state *state)
-{
-       int id1, id2;
-       int oui_id, model_no, rev_no, chip_no;
-
-       id1 = ip_phy_read(state, 0, 2);
-       id2 = ip_phy_read(state, 0, 3);
-       oui_id = (id1 << 6) | ((id2 >> 10) & 0x3f);
-       model_no = (id2 >> 4) & 0x3f;
-       rev_no = id2 & 0xf;
-       pr_debug("IP17xx: Identified oui=%06x model=%02x rev=%X\n", oui_id, model_no, rev_no);
-
-       if (oui_id != 0x0090c3)  // No other oui_id should have reached us anyway
-               return -ENODEV;
-
-       if (model_no == IP175A.MODEL_NO) {
-               state->regs = &IP175A;
-       } else if (model_no == IP175C.MODEL_NO) {
-               /*
-                *  Several models share the same model_no:
-                *  178C has more PHYs, so we try whether the device responds to a read from PHY5
-                *  175D has a new chip ID register
-                *  175C has neither
-                */
-               if (ip_phy_read(state, 5, 2) == 0x0243) {
-                       state->regs = &IP178C;
-               } else {
-                       chip_no = ip_phy_read(state, 20, 0);
-                       pr_debug("IP17xx: Chip ID register reads %04x\n", chip_no);
-                       if (chip_no == 0x175d) {
-                               state->regs = &IP175D;
-                       } else {
-                               state->regs = &IP175C;
-                       }
-               }
-       } else {
-               pr_warning("IP17xx: Found an unknown IC+ switch with model number %02x, revision %X.\n", model_no, rev_no);
-               return -EPERM;
-       }
-       return 0;
-}
-
-/*** Low-level functions for the older models ***/
-
-/** Only set vlan and router flags in the switch **/
-static int ip175c_set_flags(struct ip17xx_state *state)
-{
-       int val;
-
-       if (!REG_SUPP(state->regs->ROUTER_CONTROL_REG)) {
-               return 0;
-       }
-
-       val = getPhy(state, state->regs->ROUTER_CONTROL_REG);
-       if (val < 0) {
-               return val;
-       }
-       if (state->regs->ROUTER_EN_BIT >= 0) {
-               if (state->router_mode) {
-                       val |= (1<<state->regs->ROUTER_EN_BIT);
-               } else {
-                       val &= (~(1<<state->regs->ROUTER_EN_BIT));
-               }
-       }
-       if (state->regs->TAG_VLAN_BIT >= 0) {
-               if (state->vlan_enabled) {
-                       val |= (1<<state->regs->TAG_VLAN_BIT);
-               } else {
-                       val &= (~(1<<state->regs->TAG_VLAN_BIT));
-               }
-       }
-       if (state->regs->NUMLAN_GROUPS_BIT >= 0) {
-               val &= (~((state->regs->NUMLAN_GROUPS_MAX-1)<<state->regs->NUMLAN_GROUPS_BIT));
-               if (state->num_vlans > state->regs->NUMLAN_GROUPS_MAX) {
-                       val |= state->regs->NUMLAN_GROUPS_MAX << state->regs->NUMLAN_GROUPS_BIT;
-               } else if (state->num_vlans >= 1) {
-                       val |= (state->num_vlans-1) << state->regs->NUMLAN_GROUPS_BIT;
-               }
-       }
-       return setPhy(state, state->regs->ROUTER_CONTROL_REG, val);
-}
-
-/** Set all VLAN and port state.  Usually you should call "correct_vlan_state" first. **/
-static int ip175c_set_state(struct ip17xx_state *state)
-{
-       int j;
-       int i;
-       SET_PORT_BITS(state, state->add_tag,
-                                 state->regs->ADD_TAG_REG, state->regs->ADD_TAG_BIT);
-       SET_PORT_BITS(state, state->remove_tag,
-                                 state->regs->REMOVE_TAG_REG, state->regs->REMOVE_TAG_BIT);
-
-       if (REG_SUPP(state->regs->VLAN_LOOKUP_REG)) {
-               for (j=0; j<state->regs->NUM_PORTS; j++) {
-                       reg addr;
-                       const bitnum *bit_lookup = (j%2==0)?
-                               state->regs->VLAN_LOOKUP_EVEN_BIT:
-                               state->regs->VLAN_LOOKUP_ODD_BIT;
-
-                       addr = state->regs->VLAN_LOOKUP_REG;
-                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
-                               addr.m += j;
-                       } else {
-                               switch (j) {
-                               case 0:
-                               case 1:
-                                       break;
-                               case 2:
-                               case 3:
-                                       addr.m+=1;
-                                       break;
-                               case 4:
-                                       addr.m+=2;
-                                       break;
-                               case 5:
-                                       addr = state->regs->VLAN_LOOKUP_REG_5;
-                                       break;
-                               default:
-                                       addr.m = -1; // shouldn't get here, but...
-                                       break;
-                               }
-                       }
-                       //printf("shareports for %d is %02X\n",j,state->ports[j].shareports);
-                       if (REG_SUPP(addr)) {
-                               SET_PORT_BITS(state, state->ports[j].shareports, addr, bit_lookup);
-                       }
-               }
-       }
-       if (REG_SUPP(state->regs->TAG_VLAN_MASK_REG)) {
-               for (j=0; j<MAX_VLANS; j++) {
-                       reg addr = state->regs->TAG_VLAN_MASK_REG;
-                       const bitnum *bit_lookup = (j%2==0)?
-                               state->regs->TAG_VLAN_MASK_EVEN_BIT:
-                               state->regs->TAG_VLAN_MASK_ODD_BIT;
-                       unsigned int vlan_mask;
-                       if (state->regs->SIMPLE_VLAN_REGISTERS) {
-                               addr.m += j;
-                       } else {
-                               addr.m += j/2;
-                       }
-                       vlan_mask = state->vlans[j].ports;
-                       SET_PORT_BITS(state, vlan_mask, addr, bit_lookup);
-               }
-       }
-
-       for (i=0; i<MAX_PORTS; i++) {
-               if (REG_SUPP(state->regs->VLAN_DEFAULT_TAG_REG[i])) {
-                       int err = setPhy(state, state->regs->VLAN_DEFAULT_TAG_REG[i],
-                                       state->ports[i].pvid);
-                       if (err < 0) {
-                               return err;
-                       }
-               }
-       }
-
-       return ip175c_set_flags(state);
-}
-
-/**
- *  Uses only the VLAN port mask and the add tag mask to generate the other fields:
- *  which ports are part of the same VLAN, removing vlan tags, and VLAN tag ids.
- */
-static void ip175c_correct_vlan_state(struct ip17xx_state *state)
-{
-       int i, j;
-       state->num_vlans = 0;
-       for (i=0; i<MAX_VLANS; i++) {
-               if (state->vlans[i].ports != 0) {
-                       state->num_vlans = i+1; // Hack -- we need to store the "set" vlans somewhere...
-               }
-       }
-
-       for (i=0; i<state->regs->NUM_PORTS; i++) {
-               unsigned int portmask = (1<<i);
-               if (!state->vlan_enabled) {
-                       // Share with everybody!
-                       state->ports[i].shareports = (1<<state->regs->NUM_PORTS)-1;
-                       continue;
-               }
-               state->ports[i].shareports = portmask;
-               for (j=0; j<MAX_VLANS; j++) {
-                       if (state->vlans[j].ports & portmask)
-                               state->ports[i].shareports |= state->vlans[j].ports;
-               }
-       }
-}
-
-static int ip175c_update_state(struct ip17xx_state *state)
-{
-       ip175c_correct_vlan_state(state);
-       return ip175c_set_state(state);
-}
-
-static int ip175c_set_vlan_mode(struct ip17xx_state *state)
-{
-       return ip175c_update_state(state);
-}
-
-static int ip175c_reset(struct ip17xx_state *state)
-{
-       int err;
-
-       if (REG_SUPP(state->regs->MODE_REG)) {
-               err = setPhy(state, state->regs->MODE_REG, state->regs->MODE_VAL);
-               if (err < 0)
-                       return err;
-               err = getPhy(state, state->regs->MODE_REG);
-               if (err < 0)
-                       return err;
-       }
-
-       return ip175c_update_state(state);
-}
-
-/*** Low-level functions for IP175D ***/
-
-static int ip175d_update_state(struct ip17xx_state *state)
-{
-       unsigned int filter_mask = 0;
-       unsigned int ports[16], add[16], rem[16];
-       int i, j;
-       int err = 0;
-
-       for (i = 0; i < 16; i++) {
-               ports[i] = 0;
-               add[i] = 0;
-               rem[i] = 0;
-               if (!state->vlan_enabled) {
-                       err |= ip_phy_write(state, 22, 14+i, i+1);      // default tags
-                       ports[i] = 0x3f;
-                       continue;
-               }
-               if (!state->vlans[i].tag) {
-                       // Reset the filter
-                       err |= ip_phy_write(state, 22, 14+i, 0);        // tag
-                       continue;
-               }
-               filter_mask |= 1 << i;
-               err |= ip_phy_write(state, 22, 14+i, state->vlans[i].tag);
-               ports[i] = state->vlans[i].ports;
-               for (j = 0; j < 6; j++) {
-                       if (ports[i] & (1 << j)) {
-                               if (state->add_tag & (1 << j))
-                                       add[i] |= 1 << j;
-                               if (state->remove_tag & (1 << j))
-                                       rem[i] |= 1 << j;
-                       }
-               }
-       }
-
-       // Port masks, tag adds and removals
-       for (i = 0; i < 8; i++) {
-               err |= ip_phy_write(state, 23, i, ports[2*i] | (ports[2*i+1] << 8));
-               err |= ip_phy_write(state, 23, 8+i, add[2*i] | (add[2*i+1] << 8));
-               err |= ip_phy_write(state, 23, 16+i, rem[2*i] | (rem[2*i+1] << 8));
-       }
-       err |= ip_phy_write(state, 22, 10, filter_mask);
-
-       // Default VLAN tag for each port
-       for (i = 0; i < 6; i++)
-               err |= ip_phy_write(state, 22, 4+i, state->vlans[state->ports[i].pvid].tag);
-
-       return (err ? -EIO : 0);
-}
-
-static int ip175d_set_vlan_mode(struct ip17xx_state *state)
-{
-       int i;
-       int err = 0;
-
-       if (state->vlan_enabled) {
-               // VLAN classification rules: tag-based VLANs, use VID to classify,
-               // drop packets that cannot be classified.
-               err |= ip_phy_write_masked(state, 22, 0, 0x3fff, 0x003f);
-
-               // Ingress rules: CFI=1 dropped, null VID is untagged, VID=1 passed,
-               // VID=0xfff discarded, admin both tagged and untagged, ingress
-               // filters enabled.
-               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
-
-               // Egress rules: IGMP processing off, keep VLAN header off
-               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
-       } else {
-               // VLAN classification rules: everything off & clear table
-               err |= ip_phy_write_masked(state, 22, 0, 0xbfff, 0x8000);
-
-               // Ingress and egress rules: set to defaults
-               err |= ip_phy_write_masked(state, 22, 1, 0x0fff, 0x0c3f);
-               err |= ip_phy_write_masked(state, 22, 2, 0x0fff, 0x0000);
-       }
-
-       // Reset default VLAN for each port to 0
-       for (i = 0; i < 6; i++)
-               state->ports[i].pvid = 0;
-
-       err |= ip175d_update_state(state);
-
-       return (err ? -EIO : 0);
-}
-
-static int ip175d_reset(struct ip17xx_state *state)
-{
-       int err = 0;
-
-       // Disable the special tagging mode
-       err |= ip_phy_write_masked(state, 21, 22, 0x0003, 0x0000);
-
-       // Set 802.1q protocol type
-       err |= ip_phy_write(state, 22, 3, 0x8100);
-
-       state->vlan_enabled = 0;
-       err |= ip175d_set_vlan_mode(state);
-
-       return (err ? -EIO : 0);
-}
-
-/*** High-level functions ***/
-
-static int ip17xx_get_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       val->value.i = state->vlan_enabled;
-       return 0;
-}
-
-static void ip17xx_reset_vlan_config(struct ip17xx_state *state)
-{
-       int i;
-
-       state->remove_tag = (state->vlan_enabled ? ((1<<state->regs->NUM_PORTS)-1) : 0x0000);
-       state->add_tag = 0x0000;
-       for (i = 0; i < MAX_VLANS; i++) {
-               state->vlans[i].ports = 0x0000;
-               state->vlans[i].tag = (i ? i : 16);
-       }
-       for (i = 0; i < MAX_PORTS; i++)
-               state->ports[i].pvid = 0;
-}
-
-static int ip17xx_set_enable_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int enable;
-
-       enable = val->value.i;
-       if (state->vlan_enabled == enable) {
-               // Do not change any state.
-               return 0;
-       }
-       state->vlan_enabled = enable;
-
-       // Otherwise, if we are switching state, set fields to a known default.
-       ip17xx_reset_vlan_config(state);
-
-       return state->regs->set_vlan_mode(state);
-}
-
-static int ip17xx_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int b;
-       int ind;
-       unsigned int ports;
-
-       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
-               return -EINVAL;
-
-       ports = state->vlans[val->port_vlan].ports;
-       b = 0;
-       ind = 0;
-       while (b < MAX_PORTS) {
-               if (ports&1) {
-                       int istagged = ((state->add_tag >> b) & 1);
-                       val->value.ports[ind].id = b;
-                       val->value.ports[ind].flags = (istagged << SWITCH_PORT_FLAG_TAGGED);
-                       ind++;
-               }
-               b++;
-               ports >>= 1;
-       }
-       val->len = ind;
-
-       return 0;
-}
-
-static int ip17xx_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int i;
-
-       if (val->port_vlan >= dev->vlans || val->port_vlan < 0)
-               return -EINVAL;
-
-       state->vlans[val->port_vlan].ports = 0;
-       for (i = 0; i < val->len; i++) {
-               unsigned int bitmask = (1<<val->value.ports[i].id);
-               state->vlans[val->port_vlan].ports |= bitmask;
-               if (val->value.ports[i].flags & (1<<SWITCH_PORT_FLAG_TAGGED)) {
-                       state->add_tag |= bitmask;
-                       state->remove_tag &= (~bitmask);
-               } else {
-                       state->add_tag &= (~bitmask);
-                       state->remove_tag |= bitmask;
-               }
-       }
-
-       return state->regs->update_state(state);
-}
-
-static int ip17xx_apply(struct switch_dev *dev)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       if (REG_SUPP(state->regs->MII_REGISTER_EN)) {
-               int val = getPhy(state, state->regs->MII_REGISTER_EN);
-               if (val < 0) {
-                       return val;
-               }
-               val |= (1<<state->regs->MII_REGISTER_EN_BIT);
-               return setPhy(state, state->regs->MII_REGISTER_EN, val);
-       }
-       return 0;
-}
-
-static int ip17xx_reset(struct switch_dev *dev)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int i, err;
-
-       if (REG_SUPP(state->regs->RESET_REG)) {
-               err = setPhy(state, state->regs->RESET_REG, state->regs->RESET_VAL);
-               if (err < 0)
-                       return err;
-               err = getPhy(state, state->regs->RESET_REG);
-
-               /*
-                *  Data sheet specifies reset period to be 2 msec.
-                *  (I don't see any mention of the 2ms delay in the IP178C spec, only
-                *  in IP175C, but it can't hurt.)
-                */
-               mdelay(2);
-       }
-
-       /* reset switch ports */
-       for (i = 0; i < state->regs->NUM_PORTS-1; i++) {
-               err = ip_phy_write(state, i, MII_BMCR, BMCR_RESET);
-               if (err < 0)
-                       return err;
-       }
-
-       state->router_mode = 0;
-       state->vlan_enabled = 0;
-       ip17xx_reset_vlan_config(state);
-
-       return state->regs->reset(state);
-}
-
-static int ip17xx_get_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       if (state->add_tag & (1<<val->port_vlan)) {
-               if (state->remove_tag & (1<<val->port_vlan))
-                       val->value.i = 3; // shouldn't ever happen.
-               else
-                       val->value.i = 1;
-       } else {
-               if (state->remove_tag & (1<<val->port_vlan))
-                       val->value.i = 0;
-               else
-                       val->value.i = 2;
-       }
-       return 0;
-}
-
-static int ip17xx_set_tagged(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       state->add_tag &= ~(1<<val->port_vlan);
-       state->remove_tag &= ~(1<<val->port_vlan);
-
-       if (val->value.i == 0)
-               state->remove_tag |= (1<<val->port_vlan);
-       if (val->value.i == 1)
-               state->add_tag |= (1<<val->port_vlan);
-
-       return state->regs->update_state(state);
-}
-
-/** Get the current phy address */
-static int ip17xx_get_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       val->value.i = state->proc_mii.p;
-       return 0;
-}
-
-/** Set a new phy address for low level access to registers */
-static int ip17xx_set_phy(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int new_reg = val->value.i;
-
-       if (new_reg < 0 || new_reg > 31)
-               state->proc_mii.p = (u16)-1;
-       else
-               state->proc_mii.p = (u16)new_reg;
-       return 0;
-}
-
-/** Get the current register number */
-static int ip17xx_get_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       val->value.i = state->proc_mii.m;
-       return 0;
-}
-
-/** Set a new register address for low level access to registers */
-static int ip17xx_set_reg(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int new_reg = val->value.i;
-
-       if (new_reg < 0 || new_reg > 31)
-               state->proc_mii.m = (u16)-1;
-       else
-               state->proc_mii.m = (u16)new_reg;
-       return 0;
-}
-
-/** Get the register content of state->proc_mii */
-static int ip17xx_get_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int retval = -EINVAL;
-       if (REG_SUPP(state->proc_mii))
-               retval = getPhy(state, state->proc_mii);
-
-       if (retval < 0) {
-               return retval;
-       } else {
-               val->value.i = retval;
-               return 0;
-       }
-}
-
-/** Write a value to the register defined by phy/reg above */
-static int ip17xx_set_val(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int myval, err = -EINVAL;
-
-       myval = val->value.i;
-       if (myval <= 0xffff && myval >= 0 && REG_SUPP(state->proc_mii)) {
-               err = setPhy(state, state->proc_mii, (u16)myval);
-       }
-       return err;
-}
-
-static int ip17xx_read_name(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       val->value.s = state->regs->NAME; // Just a const pointer, won't be freed by swconfig.
-       return 0;
-}
-
-static int ip17xx_get_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int vlan = val->port_vlan;
-
-       if (vlan < 0 || vlan >= MAX_VLANS)
-               return -EINVAL;
-
-       val->value.i = state->vlans[vlan].tag;
-       return 0;
-}
-
-static int ip17xx_set_tag(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int vlan = val->port_vlan;
-       int tag = val->value.i;
-
-       if (vlan < 0 || vlan >= MAX_VLANS)
-               return -EINVAL;
-
-       if (tag < 0 || tag > 4095)
-               return -EINVAL;
-
-       state->vlans[vlan].tag = tag;
-       return state->regs->update_state(state);
-}
-
-static int ip17xx_set_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int nr = val->port_vlan;
-       int ctrl;
-       int autoneg;
-       int speed;
-       if (val->value.i == 100) {
-               speed = 1;
-               autoneg = 0;
-       } else if (val->value.i == 10) {
-               speed = 0;
-               autoneg = 0;
-       } else {
-               autoneg = 1;
-               speed = 1;
-       }
-
-       /* Can't set speed for cpu port */
-       if (nr == state->regs->CPU_PORT)
-               return -EINVAL;
-
-       if (nr >= dev->ports || nr < 0)
-               return -EINVAL;
-
-       ctrl = ip_phy_read(state, nr, 0);
-       if (ctrl < 0)
-               return -EIO;
-
-       ctrl &= (~(1<<12));
-       ctrl &= (~(1<<13));
-       ctrl |= (autoneg<<12);
-       ctrl |= (speed<<13);
-
-       return ip_phy_write(state, nr, 0, ctrl);
-}
-
-static int ip17xx_get_port_speed(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int nr = val->port_vlan;
-       int speed, status;
-
-       if (nr == state->regs->CPU_PORT) {
-               val->value.i = 100;
-               return 0;
-       }
-
-       if (nr >= dev->ports || nr < 0)
-               return -EINVAL;
-
-       status = ip_phy_read(state, nr, 1);
-       speed = ip_phy_read(state, nr, 18);
-       if (status < 0 || speed < 0)
-               return -EIO;
-
-       if (status & 4)
-               val->value.i = ((speed & (1<<11)) ? 100 : 10);
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int ip17xx_get_port_status(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-       int ctrl, speed, status;
-       int nr = val->port_vlan;
-       int len;
-       char *buf = state->buf; // fixed-length at 80.
-
-       if (nr == state->regs->CPU_PORT) {
-               sprintf(buf, "up, 100 Mbps, cpu port");
-               val->value.s = buf;
-               return 0;
-       }
-
-       if (nr >= dev->ports || nr < 0)
-               return -EINVAL;
-
-       ctrl = ip_phy_read(state, nr, 0);
-       status = ip_phy_read(state, nr, 1);
-       speed = ip_phy_read(state, nr, 18);
-       if (ctrl < 0 || status < 0 || speed < 0)
-               return -EIO;
-
-       if (status & 4)
-               len = sprintf(buf, "up, %d Mbps, %s duplex",
-                       ((speed & (1<<11)) ? 100 : 10),
-                       ((speed & (1<<10)) ? "full" : "half"));
-       else
-               len = sprintf(buf, "down");
-
-       if (ctrl & (1<<12)) {
-               len += sprintf(buf+len, ", auto-negotiate");
-               if (!(status & (1<<5)))
-                       len += sprintf(buf+len, " (in progress)");
-       } else {
-               len += sprintf(buf+len, ", fixed speed (%d)",
-                       ((ctrl & (1<<13)) ? 100 : 10));
-       }
-
-       buf[len] = '\0';
-       val->value.s = buf;
-       return 0;
-}
-
-static int ip17xx_get_pvid(struct switch_dev *dev, int port, int *val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       *val = state->ports[port].pvid;
-       return 0;
-}
-
-static int ip17xx_set_pvid(struct switch_dev *dev, int port, int val)
-{
-       struct ip17xx_state *state = get_state(dev);
-
-       if (val < 0 || val >= MAX_VLANS)
-               return -EINVAL;
-
-       state->ports[port].pvid = val;
-       return state->regs->update_state(state);
-}
-
-
-enum Ports {
-       IP17XX_PORT_STATUS,
-       IP17XX_PORT_LINK,
-       IP17XX_PORT_TAGGED,
-       IP17XX_PORT_PVID,
-};
-
-enum Globals {
-       IP17XX_ENABLE_VLAN,
-       IP17XX_GET_NAME,
-       IP17XX_REGISTER_PHY,
-       IP17XX_REGISTER_MII,
-       IP17XX_REGISTER_VALUE,
-       IP17XX_REGISTER_ERRNO,
-};
-
-enum Vlans {
-       IP17XX_VLAN_TAG,
-};
-
-static const struct switch_attr ip17xx_global[] = {
-       [IP17XX_ENABLE_VLAN] = {
-               .id = IP17XX_ENABLE_VLAN,
-               .type = SWITCH_TYPE_INT,
-               .name  = "enable_vlan",
-               .description = "Flag to enable or disable VLANs and tagging",
-               .get  = ip17xx_get_enable_vlan,
-               .set = ip17xx_set_enable_vlan,
-       },
-       [IP17XX_GET_NAME] = {
-               .id = IP17XX_GET_NAME,
-               .type = SWITCH_TYPE_STRING,
-               .description = "Returns the type of IC+ chip.",
-               .name  = "name",
-               .get  = ip17xx_read_name,
-               .set = NULL,
-       },
-       /* jal: added for low level debugging etc. */
-       [IP17XX_REGISTER_PHY] = {
-               .id = IP17XX_REGISTER_PHY,
-               .type = SWITCH_TYPE_INT,
-               .description = "Direct register access: set PHY (0-4, or 29,30,31)",
-               .name  = "phy",
-               .get  = ip17xx_get_phy,
-               .set = ip17xx_set_phy,
-       },
-       [IP17XX_REGISTER_MII] = {
-               .id = IP17XX_REGISTER_MII,
-               .type = SWITCH_TYPE_INT,
-               .description = "Direct register access: set MII register number (0-31)",
-               .name  = "reg",
-               .get  = ip17xx_get_reg,
-               .set = ip17xx_set_reg,
-       },
-       [IP17XX_REGISTER_VALUE] = {
-               .id = IP17XX_REGISTER_VALUE,
-               .type = SWITCH_TYPE_INT,
-               .description = "Direct register access: read/write to register (0-65535)",
-               .name  = "val",
-               .get  = ip17xx_get_val,
-               .set = ip17xx_set_val,
-       },
-};
-
-static const struct switch_attr ip17xx_vlan[] = {
-       [IP17XX_VLAN_TAG] = {
-               .id = IP17XX_VLAN_TAG,
-               .type = SWITCH_TYPE_INT,
-               .description = "VLAN ID (0-4095) [IP175D only]",
-               .name = "vid",
-               .get = ip17xx_get_tag,
-               .set = ip17xx_set_tag,
-       }
-};
-
-static const struct switch_attr ip17xx_port[] = {
-       [IP17XX_PORT_STATUS] = {
-               .id = IP17XX_PORT_STATUS,
-               .type = SWITCH_TYPE_STRING,
-               .description = "Returns Detailed port status",
-               .name  = "status",
-               .get  = ip17xx_get_port_status,
-               .set = NULL,
-       },
-       [IP17XX_PORT_LINK] = {
-               .id = IP17XX_PORT_LINK,
-               .type = SWITCH_TYPE_INT,
-               .description = "Link speed. Can write 0 for auto-negotiate, or 10 or 100",
-               .name  = "link",
-               .get  = ip17xx_get_port_speed,
-               .set = ip17xx_set_port_speed,
-       },
-       [IP17XX_PORT_TAGGED] = {
-               .id = IP17XX_PORT_LINK,
-               .type = SWITCH_TYPE_INT,
-               .description = "0 = untag, 1 = add tags, 2 = do not alter (This value is reset if vlans are altered)",
-               .name  = "tagged",
-               .get  = ip17xx_get_tagged,
-               .set = ip17xx_set_tagged,
-       },
-};
-
-static const struct switch_dev_ops ip17xx_ops = {
-       .attr_global = {
-               .attr = ip17xx_global,
-               .n_attr = ARRAY_SIZE(ip17xx_global),
-       },
-       .attr_port = {
-               .attr = ip17xx_port,
-               .n_attr = ARRAY_SIZE(ip17xx_port),
-       },
-       .attr_vlan = {
-               .attr = ip17xx_vlan,
-               .n_attr = ARRAY_SIZE(ip17xx_vlan),
-       },
-
-       .get_port_pvid = ip17xx_get_pvid,
-       .set_port_pvid = ip17xx_set_pvid,
-       .get_vlan_ports = ip17xx_get_ports,
-       .set_vlan_ports = ip17xx_set_ports,
-       .apply_config = ip17xx_apply,
-       .reset_switch = ip17xx_reset,
-};
-
-static int ip17xx_probe(struct phy_device *pdev)
-{
-       struct ip17xx_state *state;
-       struct switch_dev *dev;
-       int err;
-
-       /* We only attach to PHY 0, but use all available PHYs */
-       if (pdev->mdio.addr != 0)
-               return -ENODEV;
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return -ENOMEM;
-
-       dev = &state->dev;
-
-       pdev->priv = state;
-       state->mii_bus = pdev->mdio.bus;
-
-       err = get_model(state);
-       if (err < 0)
-               goto error;
-
-       dev->vlans = MAX_VLANS;
-       dev->cpu_port = state->regs->CPU_PORT;
-       dev->ports = state->regs->NUM_PORTS;
-       dev->name = state->regs->NAME;
-       dev->ops = &ip17xx_ops;
-
-       pr_info("IP17xx: Found %s at %s\n", dev->name, dev_name(&pdev->mdio.dev));
-       return 0;
-
-error:
-       kfree(state);
-       return err;
-}
-
-static int ip17xx_config_init(struct phy_device *pdev)
-{
-       struct ip17xx_state *state = pdev->priv;
-       struct net_device *dev = pdev->attached_dev;
-       int err;
-
-       err = register_switch(&state->dev, dev);
-       if (err < 0)
-               return err;
-
-       state->registered = true;
-       ip17xx_reset(&state->dev);
-       return 0;
-}
-
-static void ip17xx_remove(struct phy_device *pdev)
-{
-       struct ip17xx_state *state = pdev->priv;
-
-       if (state->registered)
-               unregister_switch(&state->dev);
-       kfree(state);
-}
-
-static int ip17xx_config_aneg(struct phy_device *pdev)
-{
-       return 0;
-}
-
-static int ip17xx_aneg_done(struct phy_device *pdev)
-{
-       return 1;       /* Return any positive value */
-}
-
-static int ip17xx_update_link(struct phy_device *pdev)
-{
-       pdev->link = 1;
-       return 0;
-}
-
-static int ip17xx_read_status(struct phy_device *pdev)
-{
-       pdev->speed = SPEED_100;
-       pdev->duplex = DUPLEX_FULL;
-       pdev->pause = pdev->asym_pause = 0;
-       pdev->link = 1;
-
-       return 0;
-}
-
-static struct phy_driver ip17xx_driver[] = {
-       {
-               .name           = "IC+ IP17xx",
-               .phy_id         = 0x02430c00,
-               .phy_id_mask    = 0x0ffffc00,
-               .features       = PHY_BASIC_FEATURES,
-               .probe          = ip17xx_probe,
-               .remove         = ip17xx_remove,
-               .config_init    = ip17xx_config_init,
-               .config_aneg    = ip17xx_config_aneg,
-               .aneg_done      = ip17xx_aneg_done,
-               .update_link    = ip17xx_update_link,
-               .read_status    = ip17xx_read_status,
-       }
-};
-
-module_phy_driver(ip17xx_driver);
-
-MODULE_AUTHOR("Patrick Horn <patrick.horn@gmail.com>");
-MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
-MODULE_AUTHOR("Martin Mares <mj@ucw.cz>");
-MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/mvsw61xx.c b/target/linux/generic/files/drivers/net/phy/mvsw61xx.c
deleted file mode 100644 (file)
index 9a689e6..0000000
+++ /dev/null
@@ -1,947 +0,0 @@
-/*
- * Marvell 88E61xx switch driver
- *
- * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
- * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
- *
- * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/mii.h>
-#include <linux/phy.h>
-#include <linux/of.h>
-#include <linux/of_mdio.h>
-#include <linux/delay.h>
-#include <linux/switch.h>
-#include <linux/device.h>
-#include <linux/platform_device.h>
-
-#include "mvsw61xx.h"
-
-MODULE_DESCRIPTION("Marvell 88E61xx Switch driver");
-MODULE_AUTHOR("Claudio Leite <leitec@staticky.com>");
-MODULE_AUTHOR("Nikita Nazarenko <nnazarenko@radiofid.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:mvsw61xx");
-
-/*
- * Register access is done through direct or indirect addressing,
- * depending on how the switch is physically connected.
- *
- * Direct addressing: all port and global registers directly
- *   accessible via an address/register pair
- *
- * Indirect addressing: switch is mapped at a single address,
- *   port and global registers accessible via a single command/data
- *   register pair
- */
-
-static int
-mvsw61xx_wait_mask_raw(struct mii_bus *bus, int addr,
-               int reg, u16 mask, u16 val)
-{
-       int i = 100;
-       u16 r;
-
-       do {
-               r = bus->read(bus, addr, reg);
-               if ((r & mask) == val)
-                       return 0;
-       } while (--i > 0);
-
-       return -ETIMEDOUT;
-}
-
-static u16
-r16(struct mii_bus *bus, bool indirect, int base_addr, int addr, int reg)
-{
-       u16 ind_addr;
-
-       if (!indirect)
-               return bus->read(bus, addr, reg);
-
-       /* Indirect read: First, make sure switch is free */
-       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
-                       MV_INDIRECT_INPROGRESS, 0);
-
-       /* Load address and request read */
-       ind_addr = MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg;
-       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
-                       ind_addr);
-
-       /* Wait until it's ready */
-       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
-                       MV_INDIRECT_INPROGRESS, 0);
-
-       /* Read the requested data */
-       return bus->read(bus, base_addr, MV_INDIRECT_REG_DATA);
-}
-
-static void
-w16(struct mii_bus *bus, bool indirect, int base_addr, int addr,
-               int reg, u16 val)
-{
-       u16 ind_addr;
-
-       if (!indirect) {
-               bus->write(bus, addr, reg, val);
-               return;
-       }
-
-       /* Indirect write: First, make sure switch is free */
-       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
-                       MV_INDIRECT_INPROGRESS, 0);
-
-       /* Load the data to be written */
-       bus->write(bus, base_addr, MV_INDIRECT_REG_DATA, val);
-
-       /* Wait again for switch to be free */
-       mvsw61xx_wait_mask_raw(bus, base_addr, MV_INDIRECT_REG_CMD,
-                       MV_INDIRECT_INPROGRESS, 0);
-
-       /* Load address, and issue write command */
-       ind_addr = MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg;
-       bus->write(bus, base_addr, MV_INDIRECT_REG_CMD,
-                       ind_addr);
-}
-
-/* swconfig support */
-
-static inline u16
-sr16(struct switch_dev *dev, int addr, int reg)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       return r16(state->bus, state->is_indirect, state->base_addr, addr, reg);
-}
-
-static inline void
-sw16(struct switch_dev *dev, int addr, int reg, u16 val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       w16(state->bus, state->is_indirect, state->base_addr, addr, reg, val);
-}
-
-static int
-mvsw61xx_wait_mask_s(struct switch_dev *dev, int addr,
-               int reg, u16 mask, u16 val)
-{
-       int i = 100;
-       u16 r;
-
-       do {
-               r = sr16(dev, addr, reg) & mask;
-               if (r == val)
-                       return 0;
-       } while (--i > 0);
-
-       return -ETIMEDOUT;
-}
-
-static int
-mvsw61xx_mdio_read(struct switch_dev *dev, int addr, int reg)
-{
-       sw16(dev, MV_GLOBAL2REG(SMI_OP),
-            MV_INDIRECT_READ | (addr << MV_INDIRECT_ADDR_S) | reg);
-
-       if (mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
-                                MV_INDIRECT_INPROGRESS, 0) < 0)
-               return -ETIMEDOUT;
-
-       return sr16(dev, MV_GLOBAL2REG(SMI_DATA));
-}
-
-static int
-mvsw61xx_mdio_write(struct switch_dev *dev, int addr, int reg, u16 val)
-{
-       sw16(dev, MV_GLOBAL2REG(SMI_DATA), val);
-
-       sw16(dev, MV_GLOBAL2REG(SMI_OP),
-            MV_INDIRECT_WRITE | (addr << MV_INDIRECT_ADDR_S) | reg);
-
-       return mvsw61xx_wait_mask_s(dev,  MV_GLOBAL2REG(SMI_OP),
-                                   MV_INDIRECT_INPROGRESS, 0) < 0;
-}
-
-static int
-mvsw61xx_mdio_page_read(struct switch_dev *dev, int port, int page, int reg)
-{
-       int ret;
-
-       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
-       ret = mvsw61xx_mdio_read(dev, port, reg);
-       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
-
-       return ret;
-}
-
-static void
-mvsw61xx_mdio_page_write(struct switch_dev *dev, int port, int page, int reg,
-                        u16 val)
-{
-       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, page);
-       mvsw61xx_mdio_write(dev, port, reg, val);
-       mvsw61xx_mdio_write(dev, port, MII_MV_PAGE, 0);
-}
-
-static int
-mvsw61xx_get_port_mask(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       char *buf = state->buf;
-       int port, len, i;
-       u16 reg;
-
-       port = val->port_vlan;
-       reg = sr16(dev, MV_PORTREG(VLANMAP, port)) & MV_PORTS_MASK;
-
-       len = sprintf(buf, "0x%04x: ", reg);
-
-       for (i = 0; i < MV_PORTS; i++) {
-               if (reg & (1 << i))
-                       len += sprintf(buf + len, "%d ", i);
-               else if (i == port)
-                       len += sprintf(buf + len, "(%d) ", i);
-       }
-
-       val->value.s = buf;
-
-       return 0;
-}
-
-static int
-mvsw61xx_get_port_qmode(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       val->value.i = state->ports[val->port_vlan].qmode;
-
-       return 0;
-}
-
-static int
-mvsw61xx_set_port_qmode(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       state->ports[val->port_vlan].qmode = val->value.i;
-
-       return 0;
-}
-
-static int
-mvsw61xx_get_port_pvid(struct switch_dev *dev, int port, int *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       *val = state->ports[port].pvid;
-
-       return 0;
-}
-
-static int
-mvsw61xx_set_port_pvid(struct switch_dev *dev, int port, int val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       if (val < 0 || val >= MV_VLANS)
-               return -EINVAL;
-
-       state->ports[port].pvid = (u16)val;
-
-       return 0;
-}
-
-static int
-mvsw61xx_get_port_link(struct switch_dev *dev, int port,
-               struct switch_port_link *link)
-{
-       u16 status, speed;
-
-       status = sr16(dev, MV_PORTREG(STATUS, port));
-
-       link->link = status & MV_PORT_STATUS_LINK;
-       if (!link->link)
-               return 0;
-
-       link->duplex = status & MV_PORT_STATUS_FDX;
-
-       speed = (status & MV_PORT_STATUS_SPEED_MASK) >>
-                       MV_PORT_STATUS_SPEED_SHIFT;
-
-       switch (speed) {
-       case MV_PORT_STATUS_SPEED_10:
-               link->speed = SWITCH_PORT_SPEED_10;
-               break;
-       case MV_PORT_STATUS_SPEED_100:
-               link->speed = SWITCH_PORT_SPEED_100;
-               break;
-       case MV_PORT_STATUS_SPEED_1000:
-               link->speed = SWITCH_PORT_SPEED_1000;
-               break;
-       }
-
-       return 0;
-}
-
-static int mvsw61xx_get_vlan_ports(struct switch_dev *dev,
-               struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int i, j, mode, vno;
-
-       vno = val->port_vlan;
-
-       if (vno <= 0 || vno >= dev->vlans)
-               return -EINVAL;
-
-       for (i = 0, j = 0; i < dev->ports; i++) {
-               if (state->vlans[vno].mask & (1 << i)) {
-                       val->value.ports[j].id = i;
-
-                       mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
-                       if (mode == MV_VTUCTL_EGRESS_TAGGED)
-                               val->value.ports[j].flags =
-                                       (1 << SWITCH_PORT_FLAG_TAGGED);
-                       else
-                               val->value.ports[j].flags = 0;
-
-                       j++;
-               }
-       }
-
-       val->len = j;
-
-       return 0;
-}
-
-static int mvsw61xx_set_vlan_ports(struct switch_dev *dev,
-               struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int i, mode, pno, vno;
-
-       vno = val->port_vlan;
-
-       if (vno <= 0 || vno >= dev->vlans)
-               return -EINVAL;
-
-       state->vlans[vno].mask = 0;
-       state->vlans[vno].port_mode = 0;
-       state->vlans[vno].port_sstate = 0;
-
-       if(state->vlans[vno].vid == 0)
-               state->vlans[vno].vid = vno;
-
-       for (i = 0; i < val->len; i++) {
-               pno = val->value.ports[i].id;
-
-               state->vlans[vno].mask |= (1 << pno);
-               if (val->value.ports[i].flags &
-                               (1 << SWITCH_PORT_FLAG_TAGGED))
-                       mode = MV_VTUCTL_EGRESS_TAGGED;
-               else
-                       mode = MV_VTUCTL_EGRESS_UNTAGGED;
-
-               state->vlans[vno].port_mode |= mode << (pno * 4);
-               state->vlans[vno].port_sstate |=
-                       MV_STUCTL_STATE_FORWARDING << (pno * 4 + 2);
-       }
-
-       /*
-        * DISCARD is nonzero, so it must be explicitly
-        * set on ports not in the VLAN.
-        */
-       for (i = 0; i < dev->ports; i++)
-               if (!(state->vlans[vno].mask & (1 << i)))
-                       state->vlans[vno].port_mode |=
-                               MV_VTUCTL_DISCARD << (i * 4);
-
-       return 0;
-}
-
-static int mvsw61xx_get_vlan_port_based(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int vno = val->port_vlan;
-
-       if (vno <= 0 || vno >= dev->vlans)
-               return -EINVAL;
-
-       if (state->vlans[vno].port_based)
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int mvsw61xx_set_vlan_port_based(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int vno = val->port_vlan;
-
-       if (vno <= 0 || vno >= dev->vlans)
-               return -EINVAL;
-
-       if (val->value.i == 1)
-               state->vlans[vno].port_based = true;
-       else
-               state->vlans[vno].port_based = false;
-
-       return 0;
-}
-
-static int mvsw61xx_get_vid(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int vno = val->port_vlan;
-
-       if (vno <= 0 || vno >= dev->vlans)
-               return -EINVAL;
-
-       val->value.i = state->vlans[vno].vid;
-
-       return 0;
-}
-
-static int mvsw61xx_set_vid(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int vno = val->port_vlan;
-
-       if (vno <= 0 || vno >= dev->vlans)
-               return -EINVAL;
-
-       state->vlans[vno].vid = val->value.i;
-
-       return 0;
-}
-
-static int mvsw61xx_get_enable_vlan(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       val->value.i = state->vlan_enabled;
-
-       return 0;
-}
-
-static int mvsw61xx_set_enable_vlan(struct switch_dev *dev,
-               const struct switch_attr *attr, struct switch_val *val)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-
-       state->vlan_enabled = val->value.i;
-
-       return 0;
-}
-
-static int mvsw61xx_vtu_program(struct switch_dev *dev)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       u16 v1, v2, s1, s2;
-       int i;
-
-       /* Flush */
-       mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
-                       MV_VTUOP_INPROGRESS, 0);
-       sw16(dev, MV_GLOBALREG(VTU_OP),
-                       MV_VTUOP_INPROGRESS | MV_VTUOP_PURGE);
-
-       /* Write VLAN table */
-       for (i = 1; i < dev->vlans; i++) {
-               if (state->vlans[i].mask == 0 ||
-                               state->vlans[i].vid == 0 ||
-                               state->vlans[i].port_based == true)
-                       continue;
-
-               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
-                               MV_VTUOP_INPROGRESS, 0);
-
-               /* Write per-VLAN port state into STU */
-               s1 = (u16) (state->vlans[i].port_sstate & 0xffff);
-               s2 = (u16) ((state->vlans[i].port_sstate >> 16) & 0xffff);
-
-               sw16(dev, MV_GLOBALREG(VTU_VID), MV_VTU_VID_VALID);
-               sw16(dev, MV_GLOBALREG(VTU_SID), i);
-               sw16(dev, MV_GLOBALREG(VTU_DATA1), s1);
-               sw16(dev, MV_GLOBALREG(VTU_DATA2), s2);
-               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
-
-               sw16(dev, MV_GLOBALREG(VTU_OP),
-                               MV_VTUOP_INPROGRESS | MV_VTUOP_STULOAD);
-               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
-                               MV_VTUOP_INPROGRESS, 0);
-
-               /* Write VLAN information into VTU */
-               v1 = (u16) (state->vlans[i].port_mode & 0xffff);
-               v2 = (u16) ((state->vlans[i].port_mode >> 16) & 0xffff);
-
-               sw16(dev, MV_GLOBALREG(VTU_VID),
-                               MV_VTU_VID_VALID | state->vlans[i].vid);
-               sw16(dev, MV_GLOBALREG(VTU_SID), i);
-               sw16(dev, MV_GLOBALREG(VTU_FID), i);
-               sw16(dev, MV_GLOBALREG(VTU_DATA1), v1);
-               sw16(dev, MV_GLOBALREG(VTU_DATA2), v2);
-               sw16(dev, MV_GLOBALREG(VTU_DATA3), 0);
-
-               sw16(dev, MV_GLOBALREG(VTU_OP),
-                               MV_VTUOP_INPROGRESS | MV_VTUOP_LOAD);
-               mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(VTU_OP),
-                               MV_VTUOP_INPROGRESS, 0);
-       }
-
-       return 0;
-}
-
-static void mvsw61xx_vlan_port_config(struct switch_dev *dev, int vno)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int i, mode;
-
-       for (i = 0; i < dev->ports; i++) {
-               if (!(state->vlans[vno].mask & (1 << i)))
-                       continue;
-
-               mode = (state->vlans[vno].port_mode >> (i * 4)) & 0xf;
-
-               if(mode != MV_VTUCTL_EGRESS_TAGGED)
-                       state->ports[i].pvid = state->vlans[vno].vid;
-
-               if (state->vlans[vno].port_based) {
-                       state->ports[i].mask |= state->vlans[vno].mask;
-                       state->ports[i].fdb = vno;
-               }
-               else
-                       state->ports[i].qmode = MV_8021Q_MODE_SECURE;
-       }
-}
-
-static int mvsw61xx_update_state(struct switch_dev *dev)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int i;
-       u16 reg;
-
-       if (!state->registered)
-               return -EINVAL;
-
-       /*
-        * Set 802.1q-only mode if vlan_enabled is true.
-        *
-        * Without this, even if 802.1q is enabled for
-        * a port/VLAN, it still depends on the port-based
-        * VLAN mask being set.
-        *
-        * With this setting, port-based VLANs are still
-        * functional, provided the VID is not in the VTU.
-        */
-       reg = sr16(dev, MV_GLOBAL2REG(SDET_POLARITY));
-
-       if (state->vlan_enabled)
-               reg |= MV_8021Q_VLAN_ONLY;
-       else
-               reg &= ~MV_8021Q_VLAN_ONLY;
-
-       sw16(dev, MV_GLOBAL2REG(SDET_POLARITY), reg);
-
-       /*
-        * Set port-based VLAN masks on each port
-        * based only on VLAN definitions known to
-        * the driver (i.e. in state).
-        *
-        * This means any pre-existing port mapping is
-        * wiped out once our driver is initialized.
-        */
-       for (i = 0; i < dev->ports; i++) {
-               state->ports[i].mask = 0;
-               state->ports[i].qmode = MV_8021Q_MODE_DISABLE;
-       }
-
-       for (i = 0; i < dev->vlans; i++)
-               mvsw61xx_vlan_port_config(dev, i);
-
-       for (i = 0; i < dev->ports; i++) {
-               reg = sr16(dev, MV_PORTREG(VLANID, i)) & ~MV_PVID_MASK;
-               reg |= state->ports[i].pvid;
-               sw16(dev, MV_PORTREG(VLANID, i), reg);
-
-               state->ports[i].mask &= ~(1 << i);
-
-               /* set default forwarding DB number and port mask */
-               reg = sr16(dev, MV_PORTREG(CONTROL1, i)) & ~MV_FDB_HI_MASK;
-               reg |= (state->ports[i].fdb >> MV_FDB_HI_SHIFT) &
-                       MV_FDB_HI_MASK;
-               sw16(dev, MV_PORTREG(CONTROL1, i), reg);
-
-               reg = ((state->ports[i].fdb & 0xf) << MV_FDB_LO_SHIFT) |
-                       state->ports[i].mask;
-               sw16(dev, MV_PORTREG(VLANMAP, i), reg);
-
-               reg = sr16(dev, MV_PORTREG(CONTROL2, i)) &
-                       ~MV_8021Q_MODE_MASK;
-               reg |= state->ports[i].qmode << MV_8021Q_MODE_SHIFT;
-               sw16(dev, MV_PORTREG(CONTROL2, i), reg);
-       }
-
-       mvsw61xx_vtu_program(dev);
-
-       return 0;
-}
-
-static int mvsw61xx_apply(struct switch_dev *dev)
-{
-       return mvsw61xx_update_state(dev);
-}
-
-static void mvsw61xx_enable_serdes(struct switch_dev *dev)
-{
-       int bmcr = mvsw61xx_mdio_page_read(dev, MV_REG_FIBER_SERDES,
-                                          MV_PAGE_FIBER_SERDES, MII_BMCR);
-       if (bmcr < 0)
-               return;
-
-       if (bmcr & BMCR_PDOWN)
-               mvsw61xx_mdio_page_write(dev, MV_REG_FIBER_SERDES,
-                                        MV_PAGE_FIBER_SERDES, MII_BMCR,
-                                        bmcr & ~BMCR_PDOWN);
-}
-
-static int _mvsw61xx_reset(struct switch_dev *dev, bool full)
-{
-       struct mvsw61xx_state *state = get_state(dev);
-       int i;
-       u16 reg;
-
-       /* Disable all ports before reset */
-       for (i = 0; i < dev->ports; i++) {
-               reg = sr16(dev, MV_PORTREG(CONTROL, i)) &
-                       ~MV_PORTCTRL_FORWARDING;
-               sw16(dev, MV_PORTREG(CONTROL, i), reg);
-       }
-
-       reg = sr16(dev, MV_GLOBALREG(CONTROL)) | MV_CONTROL_RESET;
-
-       sw16(dev, MV_GLOBALREG(CONTROL), reg);
-       if (mvsw61xx_wait_mask_s(dev, MV_GLOBALREG(CONTROL),
-                               MV_CONTROL_RESET, 0) < 0)
-               return -ETIMEDOUT;
-
-       for (i = 0; i < dev->ports; i++) {
-               state->ports[i].fdb = 0;
-               state->ports[i].qmode = 0;
-               state->ports[i].mask = 0;
-               state->ports[i].pvid = 0;
-
-               /* Force flow control off */
-               reg = sr16(dev, MV_PORTREG(PHYCTL, i)) & ~MV_PHYCTL_FC_MASK;
-               reg |= MV_PHYCTL_FC_DISABLE;
-               sw16(dev, MV_PORTREG(PHYCTL, i), reg);
-
-               /* Set port association vector */
-               sw16(dev, MV_PORTREG(ASSOC, i), (1 << i));
-
-               /* power up phys */
-               if (full && i < 5) {
-                       mvsw61xx_mdio_write(dev, i, MII_MV_SPEC_CTRL,
-                                           MV_SPEC_MDI_CROSS_AUTO |
-                                           MV_SPEC_ENERGY_DETECT |
-                                           MV_SPEC_DOWNSHIFT_COUNTER);
-                       mvsw61xx_mdio_write(dev, i, MII_BMCR, BMCR_RESET |
-                                           BMCR_ANENABLE | BMCR_FULLDPLX |
-                                           BMCR_SPEED1000);
-               }
-
-               /* enable SerDes if necessary */
-               if (full && i >= 5 && state->model == MV_IDENT_VALUE_6176) {
-                       u16 sts = sr16(dev, MV_PORTREG(STATUS, i));
-                       u16 mode = sts & MV_PORT_STATUS_CMODE_MASK;
-
-                       if (mode == MV_PORT_STATUS_CMODE_100BASE_X ||
-                           mode == MV_PORT_STATUS_CMODE_1000BASE_X ||
-                           mode == MV_PORT_STATUS_CMODE_SGMII) {
-                               mvsw61xx_enable_serdes(dev);
-                       }
-               }
-       }
-
-       for (i = 0; i < dev->vlans; i++) {
-               state->vlans[i].port_based = false;
-               state->vlans[i].mask = 0;
-               state->vlans[i].vid = 0;
-               state->vlans[i].port_mode = 0;
-               state->vlans[i].port_sstate = 0;
-       }
-
-       state->vlan_enabled = 0;
-
-       mvsw61xx_update_state(dev);
-
-       /* Re-enable ports */
-       for (i = 0; i < dev->ports; i++) {
-               reg = sr16(dev, MV_PORTREG(CONTROL, i)) |
-                       MV_PORTCTRL_FORWARDING;
-               sw16(dev, MV_PORTREG(CONTROL, i), reg);
-       }
-
-       return 0;
-}
-
-static int mvsw61xx_reset(struct switch_dev *dev)
-{
-       return _mvsw61xx_reset(dev, false);
-}
-
-enum {
-       MVSW61XX_ENABLE_VLAN,
-};
-
-enum {
-       MVSW61XX_VLAN_PORT_BASED,
-       MVSW61XX_VLAN_ID,
-};
-
-enum {
-       MVSW61XX_PORT_MASK,
-       MVSW61XX_PORT_QMODE,
-};
-
-static const struct switch_attr mvsw61xx_global[] = {
-       [MVSW61XX_ENABLE_VLAN] = {
-               .id = MVSW61XX_ENABLE_VLAN,
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable 802.1q VLAN support",
-               .get = mvsw61xx_get_enable_vlan,
-               .set = mvsw61xx_set_enable_vlan,
-       },
-};
-
-static const struct switch_attr mvsw61xx_vlan[] = {
-       [MVSW61XX_VLAN_PORT_BASED] = {
-               .id = MVSW61XX_VLAN_PORT_BASED,
-               .type = SWITCH_TYPE_INT,
-               .name = "port_based",
-               .description = "Use port-based (non-802.1q) VLAN only",
-               .get = mvsw61xx_get_vlan_port_based,
-               .set = mvsw61xx_set_vlan_port_based,
-       },
-       [MVSW61XX_VLAN_ID] = {
-               .id = MVSW61XX_VLAN_ID,
-               .type = SWITCH_TYPE_INT,
-               .name = "vid",
-               .description = "Get/set VLAN ID",
-               .get = mvsw61xx_get_vid,
-               .set = mvsw61xx_set_vid,
-       },
-};
-
-static const struct switch_attr mvsw61xx_port[] = {
-       [MVSW61XX_PORT_MASK] = {
-               .id = MVSW61XX_PORT_MASK,
-               .type = SWITCH_TYPE_STRING,
-               .description = "Port-based VLAN mask",
-               .name = "mask",
-               .get = mvsw61xx_get_port_mask,
-               .set = NULL,
-       },
-       [MVSW61XX_PORT_QMODE] = {
-               .id = MVSW61XX_PORT_QMODE,
-               .type = SWITCH_TYPE_INT,
-               .description = "802.1q mode: 0=off/1=fallback/2=check/3=secure",
-               .name = "qmode",
-               .get = mvsw61xx_get_port_qmode,
-               .set = mvsw61xx_set_port_qmode,
-       },
-};
-
-static const struct switch_dev_ops mvsw61xx_ops = {
-       .attr_global = {
-               .attr = mvsw61xx_global,
-               .n_attr = ARRAY_SIZE(mvsw61xx_global),
-       },
-       .attr_vlan = {
-               .attr = mvsw61xx_vlan,
-               .n_attr = ARRAY_SIZE(mvsw61xx_vlan),
-       },
-       .attr_port = {
-               .attr = mvsw61xx_port,
-               .n_attr = ARRAY_SIZE(mvsw61xx_port),
-       },
-       .get_port_link = mvsw61xx_get_port_link,
-       .get_port_pvid = mvsw61xx_get_port_pvid,
-       .set_port_pvid = mvsw61xx_set_port_pvid,
-       .get_vlan_ports = mvsw61xx_get_vlan_ports,
-       .set_vlan_ports = mvsw61xx_set_vlan_ports,
-       .apply_config = mvsw61xx_apply,
-       .reset_switch = mvsw61xx_reset,
-};
-
-/* end swconfig stuff */
-
-static int mvsw61xx_probe(struct platform_device *pdev)
-{
-       struct mvsw61xx_state *state;
-       struct device_node *np = pdev->dev.of_node;
-       struct device_node *mdio;
-       char *model_str;
-       u32 val;
-       int err;
-
-       state = kzalloc(sizeof(*state), GFP_KERNEL);
-       if (!state)
-               return -ENOMEM;
-
-       mdio = of_parse_phandle(np, "mii-bus", 0);
-       if (!mdio) {
-               dev_err(&pdev->dev, "Couldn't get MII bus handle\n");
-               err = -ENODEV;
-               goto out_err;
-       }
-
-       state->bus = of_mdio_find_bus(mdio);
-       if (!state->bus) {
-               dev_err(&pdev->dev, "Couldn't find MII bus from handle\n");
-               err = -ENODEV;
-               goto out_err;
-       }
-
-       state->is_indirect = of_property_read_bool(np, "is-indirect");
-
-       if (state->is_indirect) {
-               if (of_property_read_u32(np, "reg", &val)) {
-                       dev_err(&pdev->dev, "Switch address not specified\n");
-                       err = -ENODEV;
-                       goto out_err;
-               }
-
-               state->base_addr = val;
-       } else {
-               state->base_addr = MV_BASE;
-       }
-
-       state->model = r16(state->bus, state->is_indirect, state->base_addr,
-                               MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
-
-       switch(state->model) {
-       case MV_IDENT_VALUE_6171:
-               model_str = MV_IDENT_STR_6171;
-               break;
-       case MV_IDENT_VALUE_6172:
-               model_str = MV_IDENT_STR_6172;
-               break;
-       case MV_IDENT_VALUE_6176:
-               model_str = MV_IDENT_STR_6176;
-               break;
-       case MV_IDENT_VALUE_6352:
-               model_str = MV_IDENT_STR_6352;
-               break;
-       default:
-               dev_err(&pdev->dev, "No compatible switch found at 0x%02x\n",
-                               state->base_addr);
-               err = -ENODEV;
-               goto out_err;
-       }
-
-       platform_set_drvdata(pdev, state);
-       dev_info(&pdev->dev, "Found %s at %s:%02x\n", model_str,
-                       state->bus->id, state->base_addr);
-
-       dev_info(&pdev->dev, "Using %sdirect addressing\n",
-                       (state->is_indirect ? "in" : ""));
-
-       if (of_property_read_u32(np, "cpu-port-0", &val)) {
-               dev_err(&pdev->dev, "CPU port not set\n");
-               err = -ENODEV;
-               goto out_err;
-       }
-
-       state->cpu_port0 = val;
-
-       if (!of_property_read_u32(np, "cpu-port-1", &val))
-               state->cpu_port1 = val;
-       else
-               state->cpu_port1 = -1;
-
-       state->dev.vlans = MV_VLANS;
-       state->dev.cpu_port = state->cpu_port0;
-       state->dev.ports = MV_PORTS;
-       state->dev.name = model_str;
-       state->dev.ops = &mvsw61xx_ops;
-       state->dev.alias = dev_name(&pdev->dev);
-
-       _mvsw61xx_reset(&state->dev, true);
-
-       err = register_switch(&state->dev, NULL);
-       if (err < 0)
-               goto out_err;
-
-       state->registered = true;
-
-       return 0;
-out_err:
-       kfree(state);
-       return err;
-}
-
-static int
-mvsw61xx_remove(struct platform_device *pdev)
-{
-       struct mvsw61xx_state *state = platform_get_drvdata(pdev);
-
-       if (state->registered)
-               unregister_switch(&state->dev);
-
-       kfree(state);
-
-       return 0;
-}
-
-static const struct of_device_id mvsw61xx_match[] = {
-       { .compatible = "marvell,88e6171" },
-       { .compatible = "marvell,88e6172" },
-       { .compatible = "marvell,88e6176" },
-       { .compatible = "marvell,88e6352" },
-       { }
-};
-MODULE_DEVICE_TABLE(of, mvsw61xx_match);
-
-static struct platform_driver mvsw61xx_driver = {
-       .probe = mvsw61xx_probe,
-       .remove = mvsw61xx_remove,
-       .driver = {
-               .name = "mvsw61xx",
-               .of_match_table = of_match_ptr(mvsw61xx_match),
-               .owner = THIS_MODULE,
-       },
-};
-
-static int __init mvsw61xx_module_init(void)
-{
-       return platform_driver_register(&mvsw61xx_driver);
-}
-late_initcall(mvsw61xx_module_init);
-
-static void __exit mvsw61xx_module_exit(void)
-{
-       platform_driver_unregister(&mvsw61xx_driver);
-}
-module_exit(mvsw61xx_module_exit);
diff --git a/target/linux/generic/files/drivers/net/phy/mvsw61xx.h b/target/linux/generic/files/drivers/net/phy/mvsw61xx.h
deleted file mode 100644 (file)
index a07b09c..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Marvell 88E61xx switch driver
- *
- * Copyright (c) 2014 Claudio Leite <leitec@staticky.com>
- * Copyright (c) 2014 Nikita Nazarenko <nnazarenko@radiofid.com>
- *
- * Based on code (c) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-
-#ifndef __MVSW61XX_H
-#define __MVSW61XX_H
-
-#define MV_PORTS                       7
-#define MV_PORTS_MASK                  ((1 << MV_PORTS) - 1)
-
-#define MV_BASE                                0x10
-
-#define MV_SWITCHPORT_BASE             0x10
-#define MV_SWITCHPORT(_n)              (MV_SWITCHPORT_BASE + (_n))
-#define MV_SWITCHREGS                  (MV_BASE + 0xb)
-
-#define MV_VLANS                       64
-
-enum {
-       MV_PORT_STATUS                  = 0x00,
-       MV_PORT_PHYCTL                  = 0x01,
-       MV_PORT_JAMCTL                  = 0x02,
-       MV_PORT_IDENT                   = 0x03,
-       MV_PORT_CONTROL                 = 0x04,
-       MV_PORT_CONTROL1                = 0x05,
-       MV_PORT_VLANMAP                 = 0x06,
-       MV_PORT_VLANID                  = 0x07,
-       MV_PORT_CONTROL2                = 0x08,
-       MV_PORT_ASSOC                   = 0x0b,
-       MV_PORT_RX_DISCARD_LOW          = 0x10,
-       MV_PORT_RX_DISCARD_HIGH         = 0x11,
-       MV_PORT_IN_FILTERED             = 0x12,
-       MV_PORT_OUT_ACCEPTED            = 0x13,
-};
-#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
-
-enum {
-       MV_PORT_STATUS_FDX              = (1 << 10),
-       MV_PORT_STATUS_LINK             = (1 << 11),
-};
-
-enum {
-       MV_PORT_STATUS_CMODE_100BASE_X  = 0x8,
-       MV_PORT_STATUS_CMODE_1000BASE_X = 0x9,
-       MV_PORT_STATUS_CMODE_SGMII      = 0xa,
-};
-
-#define MV_PORT_STATUS_CMODE_MASK      0xf
-
-enum {
-       MV_PORT_STATUS_SPEED_10         = 0x00,
-       MV_PORT_STATUS_SPEED_100        = 0x01,
-       MV_PORT_STATUS_SPEED_1000       = 0x02,
-};
-#define MV_PORT_STATUS_SPEED_SHIFT     8
-#define MV_PORT_STATUS_SPEED_MASK      (3 << 8)
-
-enum {
-       MV_PORTCTRL_DISABLED            = (0 << 0),
-       MV_PORTCTRL_BLOCKING            = (1 << 0),
-       MV_PORTCTRL_LEARNING            = (2 << 0),
-       MV_PORTCTRL_FORWARDING          = (3 << 0),
-       MV_PORTCTRL_VLANTUN             = (1 << 7),
-       MV_PORTCTRL_EGRESS              = (1 << 12),
-};
-
-#define MV_PHYCTL_FC_MASK              (3 << 6)
-
-enum {
-       MV_PHYCTL_FC_ENABLE             = (3 << 6),
-       MV_PHYCTL_FC_DISABLE            = (1 << 6),
-};
-
-enum {
-       MV_8021Q_EGRESS_UNMODIFIED      = 0x00,
-       MV_8021Q_EGRESS_UNTAGGED        = 0x01,
-       MV_8021Q_EGRESS_TAGGED          = 0x02,
-       MV_8021Q_EGRESS_ADDTAG          = 0x03,
-};
-
-#define MV_8021Q_MODE_SHIFT            10
-#define MV_8021Q_MODE_MASK             (0x3 << MV_8021Q_MODE_SHIFT)
-
-enum {
-       MV_8021Q_MODE_DISABLE           = 0x00,
-       MV_8021Q_MODE_FALLBACK          = 0x01,
-       MV_8021Q_MODE_CHECK             = 0x02,
-       MV_8021Q_MODE_SECURE            = 0x03,
-};
-
-enum {
-       MV_8021Q_VLAN_ONLY              = (1 << 15),
-};
-
-#define MV_PORTASSOC_MONITOR           (1 << 15)
-
-enum {
-       MV_SWITCH_ATU_FID0              = 0x01,
-       MV_SWITCH_ATU_FID1              = 0x02,
-       MV_SWITCH_ATU_SID               = 0x03,
-       MV_SWITCH_CTRL                  = 0x04,
-       MV_SWITCH_ATU_CTRL              = 0x0a,
-       MV_SWITCH_ATU_OP                = 0x0b,
-       MV_SWITCH_ATU_DATA              = 0x0c,
-       MV_SWITCH_ATU_MAC0              = 0x0d,
-       MV_SWITCH_ATU_MAC1              = 0x0e,
-       MV_SWITCH_ATU_MAC2              = 0x0f,
-       MV_SWITCH_GLOBAL                = 0x1b,
-       MV_SWITCH_GLOBAL2               = 0x1c,
-};
-#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
-
-enum {
-       MV_SWITCHCTL_EEIE               = (1 << 0),
-       MV_SWITCHCTL_PHYIE              = (1 << 1),
-       MV_SWITCHCTL_ATUDONE            = (1 << 2),
-       MV_SWITCHCTL_ATUIE              = (1 << 3),
-       MV_SWITCHCTL_CTRMODE            = (1 << 8),
-       MV_SWITCHCTL_RELOAD             = (1 << 9),
-       MV_SWITCHCTL_MSIZE              = (1 << 10),
-       MV_SWITCHCTL_DROP               = (1 << 13),
-};
-
-enum {
-#define MV_ATUCTL_AGETIME_MIN          16
-#define MV_ATUCTL_AGETIME_MAX          4080
-#define MV_ATUCTL_AGETIME(_n)          ((((_n) / 16) & 0xff) << 4)
-       MV_ATUCTL_ATU_256               = (0 << 12),
-       MV_ATUCTL_ATU_512               = (1 << 12),
-       MV_ATUCTL_ATU_1K                = (2 << 12),
-       MV_ATUCTL_ATUMASK               = (3 << 12),
-       MV_ATUCTL_NO_LEARN              = (1 << 14),
-       MV_ATUCTL_RESET                 = (1 << 15),
-};
-
-enum {
-#define MV_ATUOP_DBNUM(_n)             ((_n) & 0x0f)
-       MV_ATUOP_NOOP                   = (0 << 12),
-       MV_ATUOP_FLUSH_ALL              = (1 << 12),
-       MV_ATUOP_FLUSH_U                = (2 << 12),
-       MV_ATUOP_LOAD_DB                = (3 << 12),
-       MV_ATUOP_GET_NEXT               = (4 << 12),
-       MV_ATUOP_FLUSH_DB               = (5 << 12),
-       MV_ATUOP_FLUSH_DB_UU            = (6 << 12),
-       MV_ATUOP_INPROGRESS             = (1 << 15),
-};
-
-enum {
-       MV_GLOBAL_STATUS                = 0x00,
-       MV_GLOBAL_ATU_FID               = 0x01,
-       MV_GLOBAL_VTU_FID               = 0x02,
-       MV_GLOBAL_VTU_SID               = 0x03,
-       MV_GLOBAL_CONTROL               = 0x04,
-       MV_GLOBAL_VTU_OP                = 0x05,
-       MV_GLOBAL_VTU_VID               = 0x06,
-       MV_GLOBAL_VTU_DATA1             = 0x07,
-       MV_GLOBAL_VTU_DATA2             = 0x08,
-       MV_GLOBAL_VTU_DATA3             = 0x09,
-       MV_GLOBAL_CONTROL2              = 0x1c,
-};
-#define MV_GLOBALREG(_type) MV_SWITCH_GLOBAL, MV_GLOBAL_##_type
-
-enum {
-       MV_GLOBAL2_SMI_OP               = 0x18,
-       MV_GLOBAL2_SMI_DATA             = 0x19,
-       MV_GLOBAL2_SDET_POLARITY        = 0x1d,
-};
-#define MV_GLOBAL2REG(_type) MV_SWITCH_GLOBAL2, MV_GLOBAL2_##_type
-
-enum {
-       MV_VTU_VID_VALID                = (1 << 12),
-};
-
-enum {
-       MV_VTUOP_PURGE                  = (1 << 12),
-       MV_VTUOP_LOAD                   = (3 << 12),
-       MV_VTUOP_INPROGRESS             = (1 << 15),
-       MV_VTUOP_STULOAD                = (5 << 12),
-       MV_VTUOP_VTU_GET_NEXT           = (4 << 12),
-       MV_VTUOP_STU_GET_NEXT           = (6 << 12),
-       MV_VTUOP_GET_VIOLATION          = (7 << 12),
-};
-
-enum {
-       MV_CONTROL_RESET                = (1 << 15),
-       MV_CONTROL_PPU_ENABLE           = (1 << 14),
-};
-
-enum {
-       MV_VTUCTL_EGRESS_UNMODIFIED     = (0 << 0),
-       MV_VTUCTL_EGRESS_UNTAGGED       = (1 << 0),
-       MV_VTUCTL_EGRESS_TAGGED         = (2 << 0),
-       MV_VTUCTL_DISCARD               = (3 << 0),
-};
-
-enum {
-       MV_STUCTL_STATE_DISABLED        = (0 << 0),
-       MV_STUCTL_STATE_BLOCKING        = (1 << 0),
-       MV_STUCTL_STATE_LEARNING        = (2 << 0),
-       MV_STUCTL_STATE_FORWARDING      = (3 << 0),
-};
-
-enum {
-       MV_INDIRECT_REG_CMD             = 0,
-       MV_INDIRECT_REG_DATA            = 1,
-};
-
-enum {
-       MV_INDIRECT_INPROGRESS          = 0x8000,
-       MV_INDIRECT_WRITE               = 0x9400,
-       MV_INDIRECT_READ                = 0x9800,
-};
-#define MV_INDIRECT_ADDR_S             5
-
-#define MV_IDENT_MASK                  0xfff0
-
-#define MV_IDENT_VALUE_6171            0x1710
-#define MV_IDENT_STR_6171              "MV88E6171"
-
-#define MV_IDENT_VALUE_6172            0x1720
-#define MV_IDENT_STR_6172              "MV88E6172"
-
-#define MV_IDENT_VALUE_6176            0x1760
-#define MV_IDENT_STR_6176              "MV88E6176"
-
-#define MV_IDENT_VALUE_6352            0x3520
-#define MV_IDENT_STR_6352              "MV88E6352"
-
-#define MV_PVID_MASK                   0x0fff
-
-#define MV_FDB_HI_MASK                 0x00ff
-#define MV_FDB_LO_MASK                 0xf000
-#define MV_FDB_HI_SHIFT                        4
-#define MV_FDB_LO_SHIFT                        12
-
-/* Marvell Specific PHY register */
-#define MII_MV_SPEC_CTRL               16
-enum {
-       MV_SPEC_MDI_CROSS_AUTO          = (0x6 << 4),
-       MV_SPEC_ENERGY_DETECT           = (0x3 << 8),
-       MV_SPEC_DOWNSHIFT_COUNTER       = (0x3 << 12),
-};
-
-#define MII_MV_PAGE                    22
-
-#define MV_REG_FIBER_SERDES            0xf
-#define MV_PAGE_FIBER_SERDES           0x1
-
-struct mvsw61xx_state {
-       struct switch_dev dev;
-       struct mii_bus *bus;
-       int base_addr;
-       u16 model;
-
-       bool registered;
-       bool is_indirect;
-
-       int cpu_port0;
-       int cpu_port1;
-
-       int vlan_enabled;
-       struct port_state {
-               u16 fdb;
-               u16 pvid;
-               u16 mask;
-               u8 qmode;
-       } ports[MV_PORTS];
-
-       struct vlan_state {
-               bool port_based;
-
-               u16 mask;
-               u16 vid;
-               u32 port_mode;
-               u32 port_sstate;
-       } vlans[MV_VLANS];
-
-       char buf[128];
-};
-
-#define get_state(_dev) container_of((_dev), struct mvsw61xx_state, dev)
-
-#endif
diff --git a/target/linux/generic/files/drivers/net/phy/mvswitch.c b/target/linux/generic/files/drivers/net/phy/mvswitch.c
deleted file mode 100644 (file)
index 043978f..0000000
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Marvell 88E6060 switch driver
- * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/unistd.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/mm.h>
-#include <linux/module.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
-#include <linux/phy.h>
-#include <linux/if_vlan.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include "mvswitch.h"
-
-/* Undefine this to use trailer mode instead.
- * I don't know if header mode works with all chips */
-#define HEADER_MODE    1
-
-MODULE_DESCRIPTION("Marvell 88E6060 Switch driver");
-MODULE_AUTHOR("Felix Fietkau");
-MODULE_LICENSE("GPL");
-
-#define MVSWITCH_MAGIC 0x88E6060
-
-struct mvswitch_priv {
-       netdev_features_t orig_features;
-       u8 vlans[16];
-};
-
-#define to_mvsw(_phy) ((struct mvswitch_priv *) (_phy)->priv)
-
-static inline u16
-r16(struct phy_device *phydev, int addr, int reg)
-{
-       struct mii_bus *bus = phydev->mdio.bus;
-
-       return bus->read(bus, addr, reg);
-}
-
-static inline void
-w16(struct phy_device *phydev, int addr, int reg, u16 val)
-{
-       struct mii_bus *bus = phydev->mdio.bus;
-
-       bus->write(bus, addr, reg, val);
-}
-
-
-static struct sk_buff *
-mvswitch_mangle_tx(struct net_device *dev, struct sk_buff *skb)
-{
-       struct mvswitch_priv *priv;
-       char *buf = NULL;
-       u16 vid;
-
-       priv = dev->phy_ptr;
-       if (unlikely(!priv))
-               goto error;
-
-       if (unlikely(skb->len < 16))
-               goto error;
-
-#ifdef HEADER_MODE
-       if (__vlan_hwaccel_get_tag(skb, &vid))
-               goto error;
-
-       if (skb_cloned(skb) || (skb->len <= 62) || (skb_headroom(skb) < MV_HEADER_SIZE)) {
-               if (pskb_expand_head(skb, MV_HEADER_SIZE, (skb->len < 62 ? 62 - skb->len : 0), GFP_ATOMIC))
-                       goto error_expand;
-               if (skb->len < 62)
-                       skb->len = 62;
-       }
-       buf = skb_push(skb, MV_HEADER_SIZE);
-#else
-       if (__vlan_get_tag(skb, &vid))
-               goto error;
-
-       if (unlikely((vid > 15 || !priv->vlans[vid])))
-               goto error;
-
-       if (skb->len <= 64) {
-               if (pskb_expand_head(skb, 0, 64 + MV_TRAILER_SIZE - skb->len, GFP_ATOMIC))
-                       goto error_expand;
-
-               buf = skb->data + 64;
-               skb->len = 64 + MV_TRAILER_SIZE;
-       } else {
-               if (skb_cloned(skb) || unlikely(skb_tailroom(skb) < 4)) {
-                       if (pskb_expand_head(skb, 0, 4, GFP_ATOMIC))
-                               goto error_expand;
-               }
-               buf = skb_put(skb, 4);
-       }
-
-       /* move the ethernet header 4 bytes forward, overwriting the vlan tag */
-       memmove(skb->data + 4, skb->data, 12);
-       skb->data += 4;
-       skb->len -= 4;
-       skb->mac_header += 4;
-#endif
-
-       if (!buf)
-               goto error;
-
-
-#ifdef HEADER_MODE
-       /* prepend the tag */
-       *((__be16 *) buf) = cpu_to_be16(
-               ((vid << MV_HEADER_VLAN_S) & MV_HEADER_VLAN_M) |
-               ((priv->vlans[vid] << MV_HEADER_PORTS_S) & MV_HEADER_PORTS_M)
-       );
-#else
-       /* append the tag */
-       *((__be32 *) buf) = cpu_to_be32((
-               (MV_TRAILER_OVERRIDE << MV_TRAILER_FLAGS_S) |
-               ((priv->vlans[vid] & MV_TRAILER_PORTS_M) << MV_TRAILER_PORTS_S)
-       ));
-#endif
-
-       return skb;
-
-error_expand:
-       if (net_ratelimit())
-               printk("%s: failed to expand/update skb for the switch\n", dev->name);
-
-error:
-       /* any errors? drop the packet! */
-       dev_kfree_skb_any(skb);
-       return NULL;
-}
-
-static void
-mvswitch_mangle_rx(struct net_device *dev, struct sk_buff *skb)
-{
-       struct mvswitch_priv *priv;
-       unsigned char *buf;
-       int vlan = -1;
-       int i;
-
-       priv = dev->phy_ptr;
-       if (WARN_ON_ONCE(!priv))
-               return;
-
-#ifdef HEADER_MODE
-       buf = skb->data;
-       skb_pull(skb, MV_HEADER_SIZE);
-#else
-       buf = skb->data + skb->len - MV_TRAILER_SIZE;
-       if (buf[0] != 0x80)
-               return;
-#endif
-
-       /* look for the vlan matching the incoming port */
-       for (i = 0; i < ARRAY_SIZE(priv->vlans); i++) {
-               if ((1 << buf[1]) & priv->vlans[i])
-                       vlan = i;
-       }
-
-       if (vlan == -1)
-               return;
-
-       __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan);
-}
-
-
-static int
-mvswitch_wait_mask(struct phy_device *pdev, int addr, int reg, u16 mask, u16 val)
-{
-       int i = 100;
-       u16 r;
-
-       do {
-               r = r16(pdev, addr, reg) & mask;
-               if (r == val)
-                       return 0;
-       } while(--i > 0);
-       return -ETIMEDOUT;
-}
-
-static int
-mvswitch_config_init(struct phy_device *pdev)
-{
-       struct mvswitch_priv *priv = to_mvsw(pdev);
-       struct net_device *dev = pdev->attached_dev;
-       u8 vlmap = 0;
-       int i;
-
-       if (!dev)
-               return -EINVAL;
-
-       printk("%s: Marvell 88E6060 PHY driver attached.\n", dev->name);
-       pdev->supported = ADVERTISED_100baseT_Full;
-       pdev->advertising = ADVERTISED_100baseT_Full;
-       dev->phy_ptr = priv;
-       pdev->irq = PHY_POLL;
-#ifdef HEADER_MODE
-       dev->flags |= IFF_PROMISC;
-#endif
-
-       /* initialize default vlans */
-       for (i = 0; i < MV_PORTS; i++)
-               priv->vlans[(i == MV_WANPORT ? 2 : 1)] |= (1 << i);
-
-       /* before entering reset, disable all ports */
-       for (i = 0; i < MV_PORTS; i++)
-               w16(pdev, MV_PORTREG(CONTROL, i), 0x00);
-
-       msleep(2); /* wait for the status change to settle in */
-
-       /* put the ATU in reset */
-       w16(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET);
-
-       i = mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_CTRL), MV_ATUCTL_RESET, 0);
-       if (i < 0) {
-               printk("%s: Timeout waiting for the switch to reset.\n", dev->name);
-               return i;
-       }
-
-       /* set the ATU flags */
-       w16(pdev, MV_SWITCHREG(ATU_CTRL),
-               MV_ATUCTL_NO_LEARN |
-               MV_ATUCTL_ATU_1K |
-               MV_ATUCTL_AGETIME(MV_ATUCTL_AGETIME_MIN) /* minimum without disabling ageing */
-       );
-
-       /* initialize the cpu port */
-       w16(pdev, MV_PORTREG(CONTROL, MV_CPUPORT),
-#ifdef HEADER_MODE
-               MV_PORTCTRL_HEADER |
-#else
-               MV_PORTCTRL_RXTR |
-               MV_PORTCTRL_TXTR |
-#endif
-               MV_PORTCTRL_ENABLED
-       );
-       /* wait for the phy change to settle in */
-       msleep(2);
-       for (i = 0; i < MV_PORTS; i++) {
-               u8 pvid = 0;
-               int j;
-
-               vlmap = 0;
-
-               /* look for the matching vlan */
-               for (j = 0; j < ARRAY_SIZE(priv->vlans); j++) {
-                       if (priv->vlans[j] & (1 << i)) {
-                               vlmap = priv->vlans[j];
-                               pvid = j;
-                       }
-               }
-               /* leave port unconfigured if it's not part of a vlan */
-               if (!vlmap)
-                       continue;
-
-               /* add the cpu port to the allowed destinations list */
-               vlmap |= (1 << MV_CPUPORT);
-
-               /* take port out of its own vlan destination map */
-               vlmap &= ~(1 << i);
-
-               /* apply vlan settings */
-               w16(pdev, MV_PORTREG(VLANMAP, i),
-                       MV_PORTVLAN_PORTS(vlmap) |
-                       MV_PORTVLAN_ID(i)
-               );
-
-               /* re-enable port */
-               w16(pdev, MV_PORTREG(CONTROL, i),
-                       MV_PORTCTRL_ENABLED
-               );
-       }
-
-       w16(pdev, MV_PORTREG(VLANMAP, MV_CPUPORT),
-               MV_PORTVLAN_ID(MV_CPUPORT)
-       );
-
-       /* set the port association vector */
-       for (i = 0; i <= MV_PORTS; i++) {
-               w16(pdev, MV_PORTREG(ASSOC, i),
-                       MV_PORTASSOC_PORTS(1 << i)
-               );
-       }
-
-       /* init switch control */
-       w16(pdev, MV_SWITCHREG(CTRL),
-               MV_SWITCHCTL_MSIZE |
-               MV_SWITCHCTL_DROP
-       );
-
-       dev->eth_mangle_rx = mvswitch_mangle_rx;
-       dev->eth_mangle_tx = mvswitch_mangle_tx;
-       priv->orig_features = dev->features;
-
-#ifdef HEADER_MODE
-       dev->priv_flags |= IFF_NO_IP_ALIGN;
-       dev->features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX;
-#else
-       dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
-#endif
-
-       return 0;
-}
-
-static int
-mvswitch_read_status(struct phy_device *pdev)
-{
-       pdev->speed = SPEED_100;
-       pdev->duplex = DUPLEX_FULL;
-       pdev->link = 1;
-
-       /* XXX ugly workaround: we can't force the switch
-        * to gracefully handle hosts moving from one port to another,
-        * so we have to regularly clear the ATU database */
-
-       /* wait for the ATU to become available */
-       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
-
-       /* flush the ATU */
-       w16(pdev, MV_SWITCHREG(ATU_OP),
-               MV_ATUOP_INPROGRESS |
-               MV_ATUOP_FLUSH_ALL
-       );
-
-       /* wait for operation to complete */
-       mvswitch_wait_mask(pdev, MV_SWITCHREG(ATU_OP), MV_ATUOP_INPROGRESS, 0);
-
-       return 0;
-}
-
-static int
-mvswitch_aneg_done(struct phy_device *phydev)
-{
-       return 1;       /* Return any positive value */
-}
-
-static int
-mvswitch_config_aneg(struct phy_device *phydev)
-{
-       return 0;
-}
-
-static void
-mvswitch_detach(struct phy_device *pdev)
-{
-       struct mvswitch_priv *priv = to_mvsw(pdev);
-       struct net_device *dev = pdev->attached_dev;
-
-       if (!dev)
-               return;
-
-       dev->phy_ptr = NULL;
-       dev->eth_mangle_rx = NULL;
-       dev->eth_mangle_tx = NULL;
-       dev->features = priv->orig_features;
-       dev->priv_flags &= ~IFF_NO_IP_ALIGN;
-}
-
-static void
-mvswitch_remove(struct phy_device *pdev)
-{
-       struct mvswitch_priv *priv = to_mvsw(pdev);
-
-       kfree(priv);
-}
-
-static int
-mvswitch_probe(struct phy_device *pdev)
-{
-       struct mvswitch_priv *priv;
-
-       priv = kzalloc(sizeof(struct mvswitch_priv), GFP_KERNEL);
-       if (priv == NULL)
-               return -ENOMEM;
-
-       pdev->priv = priv;
-
-       return 0;
-}
-
-static int
-mvswitch_fixup(struct phy_device *dev)
-{
-       struct mii_bus *bus = dev->mdio.bus;
-       u16 reg;
-
-       if (dev->mdio.addr != 0x10)
-               return 0;
-
-       reg = bus->read(bus, MV_PORTREG(IDENT, 0)) & MV_IDENT_MASK;
-       if (reg != MV_IDENT_VALUE)
-               return 0;
-
-       dev->phy_id = MVSWITCH_MAGIC;
-       return 0;
-}
-
-
-static struct phy_driver mvswitch_driver = {
-       .name           = "Marvell 88E6060",
-       .phy_id         = MVSWITCH_MAGIC,
-       .phy_id_mask    = 0xffffffff,
-       .features       = PHY_BASIC_FEATURES,
-       .probe          = &mvswitch_probe,
-       .remove         = &mvswitch_remove,
-       .detach         = &mvswitch_detach,
-       .config_init    = &mvswitch_config_init,
-       .config_aneg    = &mvswitch_config_aneg,
-       .aneg_done      = &mvswitch_aneg_done,
-       .read_status    = &mvswitch_read_status,
-};
-
-static int __init
-mvswitch_init(void)
-{
-       phy_register_fixup_for_id(PHY_ANY_ID, mvswitch_fixup);
-       return phy_driver_register(&mvswitch_driver, THIS_MODULE);
-}
-
-static void __exit
-mvswitch_exit(void)
-{
-       phy_driver_unregister(&mvswitch_driver);
-}
-
-module_init(mvswitch_init);
-module_exit(mvswitch_exit);
diff --git a/target/linux/generic/files/drivers/net/phy/mvswitch.h b/target/linux/generic/files/drivers/net/phy/mvswitch.h
deleted file mode 100644 (file)
index ab2a1a1..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Marvell 88E6060 switch driver
- * Copyright (c) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-#ifndef __MVSWITCH_H
-#define __MVSWITCH_H
-
-#define MV_HEADER_SIZE 2
-#define MV_HEADER_PORTS_M      0x001f
-#define MV_HEADER_PORTS_S      0
-#define MV_HEADER_VLAN_M       0xf000
-#define MV_HEADER_VLAN_S       12
-
-#define MV_TRAILER_SIZE        4
-#define MV_TRAILER_PORTS_M     0x1f
-#define MV_TRAILER_PORTS_S     16
-#define MV_TRAILER_FLAGS_S     24
-#define MV_TRAILER_OVERRIDE    0x80
-
-
-#define MV_PORTS       5
-#define MV_WANPORT     4
-#define MV_CPUPORT     5
-
-#define MV_BASE                0x10
-
-#define MV_PHYPORT_BASE                (MV_BASE + 0x0)
-#define MV_PHYPORT(_n)         (MV_PHYPORT_BASE + (_n))
-#define MV_SWITCHPORT_BASE     (MV_BASE + 0x8)
-#define MV_SWITCHPORT(_n)      (MV_SWITCHPORT_BASE + (_n))
-#define MV_SWITCHREGS          (MV_BASE + 0xf)
-
-enum {
-       MV_PHY_CONTROL      = 0x00,
-       MV_PHY_STATUS       = 0x01,
-       MV_PHY_IDENT0       = 0x02,
-       MV_PHY_IDENT1       = 0x03,
-       MV_PHY_ANEG         = 0x04,
-       MV_PHY_LINK_ABILITY = 0x05,
-       MV_PHY_ANEG_EXPAND  = 0x06,
-       MV_PHY_XMIT_NEXTP   = 0x07,
-       MV_PHY_LINK_NEXTP   = 0x08,
-       MV_PHY_CONTROL1     = 0x10,
-       MV_PHY_STATUS1      = 0x11,
-       MV_PHY_INTR_EN      = 0x12,
-       MV_PHY_INTR_STATUS  = 0x13,
-       MV_PHY_INTR_PORT    = 0x14,
-       MV_PHY_RECV_COUNTER = 0x16,
-       MV_PHY_LED_PARALLEL = 0x16,
-       MV_PHY_LED_STREAM   = 0x17,
-       MV_PHY_LED_CTRL     = 0x18,
-       MV_PHY_LED_OVERRIDE = 0x19,
-       MV_PHY_VCT_CTRL     = 0x1a,
-       MV_PHY_VCT_STATUS   = 0x1b,
-       MV_PHY_CONTROL2     = 0x1e
-};
-#define MV_PHYREG(_type, _port) MV_PHYPORT(_port), MV_PHY_##_type
-
-enum {
-       MV_PORT_STATUS      = 0x00,
-       MV_PORT_IDENT       = 0x03,
-       MV_PORT_CONTROL     = 0x04,
-       MV_PORT_VLANMAP     = 0x06,
-       MV_PORT_ASSOC       = 0x0b,
-       MV_PORT_RXCOUNT     = 0x10,
-       MV_PORT_TXCOUNT     = 0x11,
-};
-#define MV_PORTREG(_type, _port) MV_SWITCHPORT(_port), MV_PORT_##_type
-
-enum {
-       MV_PORTCTRL_BLOCK   =  (1 << 0),
-       MV_PORTCTRL_LEARN   =  (2 << 0),
-       MV_PORTCTRL_ENABLED =  (3 << 0),
-       MV_PORTCTRL_VLANTUN =  (1 << 7),        /* Enforce VLANs on packets */
-       MV_PORTCTRL_RXTR    =  (1 << 8),        /* Enable Marvell packet trailer for ingress */
-       MV_PORTCTRL_HEADER      = (1 << 11),    /* Enable Marvell packet header mode for port */
-       MV_PORTCTRL_TXTR    = (1 << 14),        /* Enable Marvell packet trailer for egress */
-       MV_PORTCTRL_FORCEFL = (1 << 15),        /* force flow control */
-};
-
-#define MV_PORTVLAN_ID(_n) (((_n) & 0xf) << 12)
-#define MV_PORTVLAN_PORTS(_n) ((_n) & 0x3f)
-
-#define MV_PORTASSOC_PORTS(_n) ((_n) & 0x1f)
-#define MV_PORTASSOC_MONITOR   (1 << 15)
-
-enum {
-       MV_SWITCH_MAC0      = 0x01,
-       MV_SWITCH_MAC1      = 0x02,
-       MV_SWITCH_MAC2      = 0x03,
-       MV_SWITCH_CTRL      = 0x04,
-       MV_SWITCH_ATU_CTRL  = 0x0a,
-       MV_SWITCH_ATU_OP    = 0x0b,
-       MV_SWITCH_ATU_DATA  = 0x0c,
-       MV_SWITCH_ATU_MAC0  = 0x0d,
-       MV_SWITCH_ATU_MAC1  = 0x0e,
-       MV_SWITCH_ATU_MAC2  = 0x0f,
-};
-#define MV_SWITCHREG(_type) MV_SWITCHREGS, MV_SWITCH_##_type
-
-enum {
-       MV_SWITCHCTL_EEIE   =  (1 << 0),        /* EEPROM interrupt enable */
-       MV_SWITCHCTL_PHYIE  =  (1 << 1),        /* PHY interrupt enable */
-       MV_SWITCHCTL_ATUDONE=  (1 << 2),        /* ATU done interrupt enable */
-       MV_SWITCHCTL_ATUIE  =  (1 << 3),        /* ATU interrupt enable */
-       MV_SWITCHCTL_CTRMODE=  (1 << 8),        /* statistics for rx and tx errors */
-       MV_SWITCHCTL_RELOAD =  (1 << 9),        /* reload registers from eeprom */
-       MV_SWITCHCTL_MSIZE  = (1 << 10),        /* increase maximum frame size */
-       MV_SWITCHCTL_DROP   = (1 << 13),        /* discard frames with excessive collisions */
-};
-
-enum {
-#define MV_ATUCTL_AGETIME_MIN  16
-#define MV_ATUCTL_AGETIME_MAX  4080
-#define MV_ATUCTL_AGETIME(_n)  ((((_n) / 16) & 0xff) << 4)
-       MV_ATUCTL_ATU_256   = (0 << 12),
-       MV_ATUCTL_ATU_512   = (1 << 12),
-       MV_ATUCTL_ATU_1K        = (2 << 12),
-       MV_ATUCTL_ATUMASK   = (3 << 12),
-       MV_ATUCTL_NO_LEARN  = (1 << 14),
-       MV_ATUCTL_RESET     = (1 << 15),
-};
-
-enum {
-#define MV_ATUOP_DBNUM(_n)     ((_n) & 0x0f)
-
-       MV_ATUOP_NOOP       = (0 << 12),
-       MV_ATUOP_FLUSH_ALL  = (1 << 12),
-       MV_ATUOP_FLUSH_U    = (2 << 12),
-       MV_ATUOP_LOAD_DB    = (3 << 12),
-       MV_ATUOP_GET_NEXT   = (4 << 12),
-       MV_ATUOP_FLUSH_DB   = (5 << 12),
-       MV_ATUOP_FLUSH_DB_UU= (6 << 12),
-
-       MV_ATUOP_INPROGRESS = (1 << 15),
-};
-
-#define MV_IDENT_MASK          0xfff0
-#define MV_IDENT_VALUE         0x0600
-
-#endif
diff --git a/target/linux/generic/files/drivers/net/phy/psb6970.c b/target/linux/generic/files/drivers/net/phy/psb6970.c
deleted file mode 100644 (file)
index c1a381c..0000000
+++ /dev/null
@@ -1,441 +0,0 @@
-/*
- * Lantiq PSB6970 (Tantos) Switch driver
- *
- * Copyright (c) 2009,2010 Team Embedded.
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation.
- *
- * The switch programming done in this driver follows the 
- * "Ethernet Traffic Separation using VLAN" Application Note as
- * published by Lantiq.
- */
-
-#include <linux/module.h>
-#include <linux/netdevice.h>
-#include <linux/switch.h>
-#include <linux/phy.h>
-
-#define PSB6970_MAX_VLANS              16
-#define PSB6970_NUM_PORTS              7
-#define PSB6970_DEFAULT_PORT_CPU       6
-#define PSB6970_IS_CPU_PORT(x)         ((x) > 4)
-
-#define PHYADDR(_reg)          ((_reg >> 5) & 0xff), (_reg & 0x1f)
-
-/* --- Identification --- */
-#define PSB6970_CI0            0x0100
-#define PSB6970_CI0_MASK       0x000f
-#define PSB6970_CI1            0x0101
-#define PSB6970_CI1_VAL                0x2599
-#define PSB6970_CI1_MASK       0xffff
-
-/* --- VLAN filter table --- */
-#define PSB6970_VFxL(i)                ((i)*2+0x10)    /* VLAN Filter Low */
-#define PSB6970_VFxL_VV                (1 << 15)       /* VLAN_Valid */
-
-#define PSB6970_VFxH(i)                ((i)*2+0x11)    /* VLAN Filter High */
-#define PSB6970_VFxH_TM_SHIFT  7               /* Tagged Member */
-
-/* --- Port registers --- */
-#define PSB6970_EC(p)          ((p)*0x20+2)    /* Extended Control */
-#define PSB6970_EC_IFNTE       (1 << 1)        /* Input Force No Tag Enable */
-
-#define PSB6970_PBVM(p)                ((p)*0x20+3)    /* Port Base VLAN Map */
-#define PSB6970_PBVM_VMCE      (1 << 8)
-#define PSB6970_PBVM_AOVTP     (1 << 9)
-#define PSB6970_PBVM_VSD       (1 << 10)
-#define PSB6970_PBVM_VC                (1 << 11)       /* VID Check with VID table */
-#define PSB6970_PBVM_TBVE      (1 << 13)       /* Tag-Based VLAN enable */
-
-#define PSB6970_DVID(p)                ((p)*0x20+4)    /* Default VLAN ID & Priority */
-
-struct psb6970_priv {
-       struct switch_dev dev;
-       struct phy_device *phy;
-       u16 (*read) (struct phy_device* phydev, int reg);
-       void (*write) (struct phy_device* phydev, int reg, u16 val);
-       struct mutex reg_mutex;
-
-       /* all fields below are cleared on reset */
-       bool vlan;
-       u16 vlan_id[PSB6970_MAX_VLANS];
-       u8 vlan_table[PSB6970_MAX_VLANS];
-       u8 vlan_tagged;
-       u16 pvid[PSB6970_NUM_PORTS];
-};
-
-#define to_psb6970(_dev) container_of(_dev, struct psb6970_priv, dev)
-
-static u16 psb6970_mii_read(struct phy_device *phydev, int reg)
-{
-       struct mii_bus *bus = phydev->mdio.bus;
-
-       return bus->read(bus, PHYADDR(reg));
-}
-
-static void psb6970_mii_write(struct phy_device *phydev, int reg, u16 val)
-{
-       struct mii_bus *bus = phydev->mdio.bus;
-
-       bus->write(bus, PHYADDR(reg), val);
-}
-
-static int
-psb6970_set_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                struct switch_val *val)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       priv->vlan = !!val->value.i;
-       return 0;
-}
-
-static int
-psb6970_get_vlan(struct switch_dev *dev, const struct switch_attr *attr,
-                struct switch_val *val)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       val->value.i = priv->vlan;
-       return 0;
-}
-
-static int psb6970_set_pvid(struct switch_dev *dev, int port, int vlan)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-
-       /* make sure no invalid PVIDs get set */
-       if (vlan >= dev->vlans)
-               return -EINVAL;
-
-       priv->pvid[port] = vlan;
-       return 0;
-}
-
-static int psb6970_get_pvid(struct switch_dev *dev, int port, int *vlan)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       *vlan = priv->pvid[port];
-       return 0;
-}
-
-static int
-psb6970_set_vid(struct switch_dev *dev, const struct switch_attr *attr,
-               struct switch_val *val)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       priv->vlan_id[val->port_vlan] = val->value.i;
-       return 0;
-}
-
-static int
-psb6970_get_vid(struct switch_dev *dev, const struct switch_attr *attr,
-               struct switch_val *val)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       val->value.i = priv->vlan_id[val->port_vlan];
-       return 0;
-}
-
-static struct switch_attr psb6970_globals[] = {
-       {
-        .type = SWITCH_TYPE_INT,
-        .name = "enable_vlan",
-        .description = "Enable VLAN mode",
-        .set = psb6970_set_vlan,
-        .get = psb6970_get_vlan,
-        .max = 1},
-};
-
-static struct switch_attr psb6970_port[] = {
-};
-
-static struct switch_attr psb6970_vlan[] = {
-       {
-        .type = SWITCH_TYPE_INT,
-        .name = "vid",
-        .description = "VLAN ID (0-4094)",
-        .set = psb6970_set_vid,
-        .get = psb6970_get_vid,
-        .max = 4094,
-        },
-};
-
-static int psb6970_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       u8 ports = priv->vlan_table[val->port_vlan];
-       int i;
-
-       val->len = 0;
-       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
-               struct switch_port *p;
-
-               if (!(ports & (1 << i)))
-                       continue;
-
-               p = &val->value.ports[val->len++];
-               p->id = i;
-               if (priv->vlan_tagged & (1 << i))
-                       p->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
-               else
-                       p->flags = 0;
-       }
-       return 0;
-}
-
-static int psb6970_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       u8 *vt = &priv->vlan_table[val->port_vlan];
-       int i, j;
-
-       *vt = 0;
-       for (i = 0; i < val->len; i++) {
-               struct switch_port *p = &val->value.ports[i];
-
-               if (p->flags & (1 << SWITCH_PORT_FLAG_TAGGED))
-                       priv->vlan_tagged |= (1 << p->id);
-               else {
-                       priv->vlan_tagged &= ~(1 << p->id);
-                       priv->pvid[p->id] = val->port_vlan;
-
-                       /* make sure that an untagged port does not
-                        * appear in other vlans */
-                       for (j = 0; j < PSB6970_MAX_VLANS; j++) {
-                               if (j == val->port_vlan)
-                                       continue;
-                               priv->vlan_table[j] &= ~(1 << p->id);
-                       }
-               }
-
-               *vt |= 1 << p->id;
-       }
-       return 0;
-}
-
-static int psb6970_hw_apply(struct switch_dev *dev)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       int i, j;
-
-       mutex_lock(&priv->reg_mutex);
-
-       if (priv->vlan) {
-               /* into the vlan translation unit */
-               for (j = 0; j < PSB6970_MAX_VLANS; j++) {
-                       u8 vp = priv->vlan_table[j];
-
-                       if (vp) {
-                               priv->write(priv->phy, PSB6970_VFxL(j),
-                                           PSB6970_VFxL_VV | priv->vlan_id[j]);
-                               priv->write(priv->phy, PSB6970_VFxH(j),
-                                           ((vp & priv->
-                                             vlan_tagged) <<
-                                            PSB6970_VFxH_TM_SHIFT) | vp);
-                       } else  /* clear VLAN Valid flag for unused vlans */
-                               priv->write(priv->phy, PSB6970_VFxL(j), 0);
-
-               }
-       }
-
-       /* update the port destination mask registers and tag settings */
-       for (i = 0; i < PSB6970_NUM_PORTS; i++) {
-               int dvid = 1, pbvm = 0x7f | PSB6970_PBVM_VSD, ec = 0;
-
-               if (priv->vlan) {
-                       ec = PSB6970_EC_IFNTE;
-                       dvid = priv->vlan_id[priv->pvid[i]];
-                       pbvm |= PSB6970_PBVM_TBVE | PSB6970_PBVM_VMCE;
-
-                       if ((i << 1) & priv->vlan_tagged)
-                               pbvm |= PSB6970_PBVM_AOVTP | PSB6970_PBVM_VC;
-               }
-
-               priv->write(priv->phy, PSB6970_PBVM(i), pbvm);
-
-               if (!PSB6970_IS_CPU_PORT(i)) {
-                       priv->write(priv->phy, PSB6970_EC(i), ec);
-                       priv->write(priv->phy, PSB6970_DVID(i), dvid);
-               }
-       }
-
-       mutex_unlock(&priv->reg_mutex);
-       return 0;
-}
-
-static int psb6970_reset_switch(struct switch_dev *dev)
-{
-       struct psb6970_priv *priv = to_psb6970(dev);
-       int i;
-
-       mutex_lock(&priv->reg_mutex);
-
-       memset(&priv->vlan, 0, sizeof(struct psb6970_priv) -
-              offsetof(struct psb6970_priv, vlan));
-
-       for (i = 0; i < PSB6970_MAX_VLANS; i++)
-               priv->vlan_id[i] = i;
-
-       mutex_unlock(&priv->reg_mutex);
-
-       return psb6970_hw_apply(dev);
-}
-
-static const struct switch_dev_ops psb6970_ops = {
-       .attr_global = {
-                       .attr = psb6970_globals,
-                       .n_attr = ARRAY_SIZE(psb6970_globals),
-                       },
-       .attr_port = {
-                     .attr = psb6970_port,
-                     .n_attr = ARRAY_SIZE(psb6970_port),
-                     },
-       .attr_vlan = {
-                     .attr = psb6970_vlan,
-                     .n_attr = ARRAY_SIZE(psb6970_vlan),
-                     },
-       .get_port_pvid = psb6970_get_pvid,
-       .set_port_pvid = psb6970_set_pvid,
-       .get_vlan_ports = psb6970_get_ports,
-       .set_vlan_ports = psb6970_set_ports,
-       .apply_config = psb6970_hw_apply,
-       .reset_switch = psb6970_reset_switch,
-};
-
-static int psb6970_config_init(struct phy_device *pdev)
-{
-       struct psb6970_priv *priv;
-       struct net_device *dev = pdev->attached_dev;
-       struct switch_dev *swdev;
-       int ret;
-
-       priv = kzalloc(sizeof(struct psb6970_priv), GFP_KERNEL);
-       if (priv == NULL)
-               return -ENOMEM;
-
-       priv->phy = pdev;
-
-       if (pdev->mdio.addr == 0)
-               printk(KERN_INFO "%s: psb6970 switch driver attached.\n",
-                      pdev->attached_dev->name);
-
-       if (pdev->mdio.addr != 0) {
-               kfree(priv);
-               return 0;
-       }
-
-       pdev->supported = pdev->advertising = SUPPORTED_100baseT_Full;
-
-       mutex_init(&priv->reg_mutex);
-       priv->read = psb6970_mii_read;
-       priv->write = psb6970_mii_write;
-
-       pdev->priv = priv;
-
-       swdev = &priv->dev;
-       swdev->cpu_port = PSB6970_DEFAULT_PORT_CPU;
-       swdev->ops = &psb6970_ops;
-
-       swdev->name = "Lantiq PSB6970";
-       swdev->vlans = PSB6970_MAX_VLANS;
-       swdev->ports = PSB6970_NUM_PORTS;
-
-       if ((ret = register_switch(&priv->dev, pdev->attached_dev)) < 0) {
-               kfree(priv);
-               goto done;
-       }
-
-       ret = psb6970_reset_switch(&priv->dev);
-       if (ret) {
-               kfree(priv);
-               goto done;
-       }
-
-       dev->phy_ptr = priv;
-
-done:
-       return ret;
-}
-
-static int psb6970_read_status(struct phy_device *phydev)
-{
-       phydev->speed = SPEED_100;
-       phydev->duplex = DUPLEX_FULL;
-       phydev->link = 1;
-
-       phydev->state = PHY_RUNNING;
-       netif_carrier_on(phydev->attached_dev);
-       phydev->adjust_link(phydev->attached_dev);
-
-       return 0;
-}
-
-static int psb6970_config_aneg(struct phy_device *phydev)
-{
-       return 0;
-}
-
-static int psb6970_probe(struct phy_device *pdev)
-{
-       return 0;
-}
-
-static void psb6970_remove(struct phy_device *pdev)
-{
-       struct psb6970_priv *priv = pdev->priv;
-
-       if (!priv)
-               return;
-
-       if (pdev->mdio.addr == 0)
-               unregister_switch(&priv->dev);
-       kfree(priv);
-}
-
-static int psb6970_fixup(struct phy_device *dev)
-{
-       struct mii_bus *bus = dev->mdio.bus;
-       u16 reg;
-
-       /* look for the switch on the bus */
-       reg = bus->read(bus, PHYADDR(PSB6970_CI1)) & PSB6970_CI1_MASK;
-       if (reg != PSB6970_CI1_VAL)
-               return 0;
-
-       dev->phy_id = (reg << 16);
-       dev->phy_id |= bus->read(bus, PHYADDR(PSB6970_CI0)) & PSB6970_CI0_MASK;
-
-       return 0;
-}
-
-static struct phy_driver psb6970_driver = {
-       .name = "Lantiq PSB6970",
-       .phy_id = PSB6970_CI1_VAL << 16,
-       .phy_id_mask = 0xffff0000,
-       .features = PHY_BASIC_FEATURES,
-       .probe = psb6970_probe,
-       .remove = psb6970_remove,
-       .config_init = &psb6970_config_init,
-       .config_aneg = &psb6970_config_aneg,
-       .read_status = &psb6970_read_status,
-};
-
-int __init psb6970_init(void)
-{
-       phy_register_fixup_for_id(PHY_ANY_ID, psb6970_fixup);
-       return phy_driver_register(&psb6970_driver, THIS_MODULE);
-}
-
-module_init(psb6970_init);
-
-void __exit psb6970_exit(void)
-{
-       phy_driver_unregister(&psb6970_driver);
-}
-
-module_exit(psb6970_exit);
-
-MODULE_DESCRIPTION("Lantiq PSB6970 Switch");
-MODULE_AUTHOR("Ithamar R. Adema <ithamar.adema@team-embedded.nl>");
-MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8306.c b/target/linux/generic/files/drivers/net/phy/rtl8306.c
deleted file mode 100644 (file)
index 6d09c10..0000000
+++ /dev/null
@@ -1,1066 +0,0 @@
-/*
- * rtl8306.c: RTL8306S switch driver
- *
- * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/if.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/if_ether.h>
-#include <linux/skbuff.h>
-#include <linux/netdevice.h>
-#include <linux/netlink.h>
-#include <net/genetlink.h>
-#include <linux/switch.h>
-#include <linux/delay.h>
-#include <linux/phy.h>
-#include <linux/version.h>
-
-//#define DEBUG 1
-
-/* Global (PHY0) */
-#define RTL8306_REG_PAGE               16
-#define RTL8306_REG_PAGE_LO            (1 << 15)
-#define RTL8306_REG_PAGE_HI            (1 << 1) /* inverted */
-
-#define RTL8306_NUM_VLANS              16
-#define RTL8306_NUM_PORTS              6
-#define RTL8306_PORT_CPU               5
-#define RTL8306_NUM_PAGES              4
-#define RTL8306_NUM_REGS               32
-
-#define RTL_NAME_S          "RTL8306S"
-#define RTL_NAME_SD         "RTL8306SD"
-#define RTL_NAME_SDM        "RTL8306SDM"
-#define RTL_NAME_UNKNOWN    "RTL8306(unknown)"
-
-#define RTL8306_MAGIC  0x8306
-
-static LIST_HEAD(phydevs);
-
-struct rtl_priv {
-       struct list_head list;
-       struct switch_dev dev;
-       int page;
-       int type;
-       int do_cpu;
-       struct mii_bus *bus;
-       char hwname[sizeof(RTL_NAME_UNKNOWN)];
-       bool fixup;
-};
-
-struct rtl_phyregs {
-       int nway;
-       int speed;
-       int duplex;
-};
-
-#define to_rtl(_dev) container_of(_dev, struct rtl_priv, dev)
-
-enum {
-       RTL_TYPE_S,
-       RTL_TYPE_SD,
-       RTL_TYPE_SDM,
-};
-
-struct rtl_reg {
-       int page;
-       int phy;
-       int reg;
-       int bits;
-       int shift;
-       int inverted;
-};
-
-#define RTL_VLAN_REGOFS(name) \
-       (RTL_REG_VLAN1_##name - RTL_REG_VLAN0_##name)
-
-#define RTL_PORT_REGOFS(name) \
-       (RTL_REG_PORT1_##name - RTL_REG_PORT0_##name)
-
-#define RTL_PORT_REG(id, reg) \
-       (RTL_REG_PORT0_##reg + (id * RTL_PORT_REGOFS(reg)))
-
-#define RTL_VLAN_REG(id, reg) \
-       (RTL_REG_VLAN0_##reg + (id * RTL_VLAN_REGOFS(reg)))
-
-#define RTL_GLOBAL_REGATTR(reg) \
-       .id = RTL_REG_##reg, \
-       .type = SWITCH_TYPE_INT, \
-       .ofs = 0, \
-       .set = rtl_attr_set_int, \
-       .get = rtl_attr_get_int
-
-#define RTL_PORT_REGATTR(reg) \
-       .id = RTL_REG_PORT0_##reg, \
-       .type = SWITCH_TYPE_INT, \
-       .ofs = RTL_PORT_REGOFS(reg), \
-       .set = rtl_attr_set_port_int, \
-       .get = rtl_attr_get_port_int
-
-#define RTL_VLAN_REGATTR(reg) \
-       .id = RTL_REG_VLAN0_##reg, \
-       .type = SWITCH_TYPE_INT, \
-       .ofs = RTL_VLAN_REGOFS(reg), \
-       .set = rtl_attr_set_vlan_int, \
-       .get = rtl_attr_get_vlan_int
-
-enum rtl_regidx {
-       RTL_REG_CHIPID,
-       RTL_REG_CHIPVER,
-       RTL_REG_CHIPTYPE,
-       RTL_REG_CPUPORT,
-
-       RTL_REG_EN_CPUPORT,
-       RTL_REG_EN_TAG_OUT,
-       RTL_REG_EN_TAG_CLR,
-       RTL_REG_EN_TAG_IN,
-       RTL_REG_TRAP_CPU,
-       RTL_REG_CPU_LINKUP,
-       RTL_REG_TRUNK_PORTSEL,
-       RTL_REG_EN_TRUNK,
-       RTL_REG_RESET,
-
-       RTL_REG_VLAN_ENABLE,
-       RTL_REG_VLAN_FILTER,
-       RTL_REG_VLAN_TAG_ONLY,
-       RTL_REG_VLAN_TAG_AWARE,
-#define RTL_VLAN_ENUM(id) \
-       RTL_REG_VLAN##id##_VID, \
-       RTL_REG_VLAN##id##_PORTMASK
-       RTL_VLAN_ENUM(0),
-       RTL_VLAN_ENUM(1),
-       RTL_VLAN_ENUM(2),
-       RTL_VLAN_ENUM(3),
-       RTL_VLAN_ENUM(4),
-       RTL_VLAN_ENUM(5),
-       RTL_VLAN_ENUM(6),
-       RTL_VLAN_ENUM(7),
-       RTL_VLAN_ENUM(8),
-       RTL_VLAN_ENUM(9),
-       RTL_VLAN_ENUM(10),
-       RTL_VLAN_ENUM(11),
-       RTL_VLAN_ENUM(12),
-       RTL_VLAN_ENUM(13),
-       RTL_VLAN_ENUM(14),
-       RTL_VLAN_ENUM(15),
-#define RTL_PORT_ENUM(id) \
-       RTL_REG_PORT##id##_PVID, \
-       RTL_REG_PORT##id##_NULL_VID_REPLACE, \
-       RTL_REG_PORT##id##_NON_PVID_DISCARD, \
-       RTL_REG_PORT##id##_VID_INSERT, \
-       RTL_REG_PORT##id##_TAG_INSERT, \
-       RTL_REG_PORT##id##_LINK, \
-       RTL_REG_PORT##id##_SPEED, \
-       RTL_REG_PORT##id##_NWAY, \
-       RTL_REG_PORT##id##_NRESTART, \
-       RTL_REG_PORT##id##_DUPLEX, \
-       RTL_REG_PORT##id##_RXEN, \
-       RTL_REG_PORT##id##_TXEN
-       RTL_PORT_ENUM(0),
-       RTL_PORT_ENUM(1),
-       RTL_PORT_ENUM(2),
-       RTL_PORT_ENUM(3),
-       RTL_PORT_ENUM(4),
-       RTL_PORT_ENUM(5),
-};
-
-static const struct rtl_reg rtl_regs[] = {
-       [RTL_REG_CHIPID]         = { 0, 4, 30, 16,  0, 0 },
-       [RTL_REG_CHIPVER]        = { 0, 4, 31,  8,  0, 0 },
-       [RTL_REG_CHIPTYPE]       = { 0, 4, 31,  2,  8, 0 },
-
-       /* CPU port number */
-       [RTL_REG_CPUPORT]        = { 2, 4, 21,  3,  0, 0 },
-       /* Enable CPU port function */
-       [RTL_REG_EN_CPUPORT]     = { 3, 2, 21,  1, 15, 1 },
-       /* Enable CPU port tag insertion */
-       [RTL_REG_EN_TAG_OUT]     = { 3, 2, 21,  1, 12, 0 },
-       /* Enable CPU port tag removal */
-       [RTL_REG_EN_TAG_CLR]     = { 3, 2, 21,  1, 11, 0 },
-       /* Enable CPU port tag checking */
-       [RTL_REG_EN_TAG_IN]      = { 0, 4, 21,  1,  7, 0 },
-       [RTL_REG_EN_TRUNK]       = { 0, 0, 19,  1, 11, 1 },
-       [RTL_REG_TRUNK_PORTSEL]  = { 0, 0, 16,  1,  6, 1 },
-       [RTL_REG_RESET]          = { 0, 0, 16,  1, 12, 0 },
-
-       [RTL_REG_TRAP_CPU]       = { 3, 2, 22,  1,  6, 0 },
-       [RTL_REG_CPU_LINKUP]     = { 0, 6, 22,  1, 15, 0 },
-
-       [RTL_REG_VLAN_TAG_ONLY]  = { 0, 0, 16,  1,  8, 1 },
-       [RTL_REG_VLAN_FILTER]    = { 0, 0, 16,  1,  9, 1 },
-       [RTL_REG_VLAN_TAG_AWARE] = { 0, 0, 16,  1, 10, 1 },
-       [RTL_REG_VLAN_ENABLE]    = { 0, 0, 18,  1,  8, 1 },
-
-#define RTL_VLAN_REGS(id, phy, page, regofs) \
-       [RTL_REG_VLAN##id##_VID] = { page, phy, 25 + regofs, 12, 0, 0 }, \
-       [RTL_REG_VLAN##id##_PORTMASK] = { page, phy, 24 + regofs, 6, 0, 0 }
-       RTL_VLAN_REGS( 0, 0, 0, 0),
-       RTL_VLAN_REGS( 1, 1, 0, 0),
-       RTL_VLAN_REGS( 2, 2, 0, 0),
-       RTL_VLAN_REGS( 3, 3, 0, 0),
-       RTL_VLAN_REGS( 4, 4, 0, 0),
-       RTL_VLAN_REGS( 5, 0, 1, 2),
-       RTL_VLAN_REGS( 6, 1, 1, 2),
-       RTL_VLAN_REGS( 7, 2, 1, 2),
-       RTL_VLAN_REGS( 8, 3, 1, 2),
-       RTL_VLAN_REGS( 9, 4, 1, 2),
-       RTL_VLAN_REGS(10, 0, 1, 4),
-       RTL_VLAN_REGS(11, 1, 1, 4),
-       RTL_VLAN_REGS(12, 2, 1, 4),
-       RTL_VLAN_REGS(13, 3, 1, 4),
-       RTL_VLAN_REGS(14, 4, 1, 4),
-       RTL_VLAN_REGS(15, 0, 1, 6),
-
-#define REG_PORT_SETTING(port, phy) \
-       [RTL_REG_PORT##port##_SPEED] = { 0, phy, 0, 1, 13, 0 }, \
-       [RTL_REG_PORT##port##_NWAY] = { 0, phy, 0, 1, 12, 0 }, \
-       [RTL_REG_PORT##port##_NRESTART] = { 0, phy, 0, 1, 9, 0 }, \
-       [RTL_REG_PORT##port##_DUPLEX] = { 0, phy, 0, 1, 8, 0 }, \
-       [RTL_REG_PORT##port##_TXEN] = { 0, phy, 24, 1, 11, 0 }, \
-       [RTL_REG_PORT##port##_RXEN] = { 0, phy, 24, 1, 10, 0 }, \
-       [RTL_REG_PORT##port##_LINK] = { 0, phy, 1, 1, 2, 0 }, \
-       [RTL_REG_PORT##port##_NULL_VID_REPLACE] = { 0, phy, 22, 1, 12, 0 }, \
-       [RTL_REG_PORT##port##_NON_PVID_DISCARD] = { 0, phy, 22, 1, 11, 0 }, \
-       [RTL_REG_PORT##port##_VID_INSERT] = { 0, phy, 22, 2, 9, 0 }, \
-       [RTL_REG_PORT##port##_TAG_INSERT] = { 0, phy, 22, 2, 0, 0 }
-
-       REG_PORT_SETTING(0, 0),
-       REG_PORT_SETTING(1, 1),
-       REG_PORT_SETTING(2, 2),
-       REG_PORT_SETTING(3, 3),
-       REG_PORT_SETTING(4, 4),
-       REG_PORT_SETTING(5, 6),
-
-#define REG_PORT_PVID(phy, page, regofs) \
-       { page, phy, 24 + regofs, 4, 12, 0 }
-       [RTL_REG_PORT0_PVID] = REG_PORT_PVID(0, 0, 0),
-       [RTL_REG_PORT1_PVID] = REG_PORT_PVID(1, 0, 0),
-       [RTL_REG_PORT2_PVID] = REG_PORT_PVID(2, 0, 0),
-       [RTL_REG_PORT3_PVID] = REG_PORT_PVID(3, 0, 0),
-       [RTL_REG_PORT4_PVID] = REG_PORT_PVID(4, 0, 0),
-       [RTL_REG_PORT5_PVID] = REG_PORT_PVID(0, 1, 2),
-};
-
-
-static inline void
-rtl_set_page(struct rtl_priv *priv, unsigned int page)
-{
-       struct mii_bus *bus = priv->bus;
-       u16 pgsel;
-
-       if (priv->fixup)
-               return;
-
-       if (priv->page == page)
-               return;
-
-       BUG_ON(page > RTL8306_NUM_PAGES);
-       pgsel = bus->read(bus, 0, RTL8306_REG_PAGE);
-       pgsel &= ~(RTL8306_REG_PAGE_LO | RTL8306_REG_PAGE_HI);
-       if (page & (1 << 0))
-               pgsel |= RTL8306_REG_PAGE_LO;
-       if (!(page & (1 << 1))) /* bit is inverted */
-               pgsel |= RTL8306_REG_PAGE_HI;
-       bus->write(bus, 0, RTL8306_REG_PAGE, pgsel);
-}
-
-static inline int
-rtl_w16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 val)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       struct mii_bus *bus = priv->bus;
-
-       rtl_set_page(priv, page);
-       bus->write(bus, phy, reg, val);
-       bus->read(bus, phy, reg); /* flush */
-       return 0;
-}
-
-static inline int
-rtl_r16(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       struct mii_bus *bus = priv->bus;
-
-       rtl_set_page(priv, page);
-       return bus->read(bus, phy, reg);
-}
-
-static inline u16
-rtl_rmw(struct switch_dev *dev, unsigned int page, unsigned int phy, unsigned int reg, u16 mask, u16 val)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       struct mii_bus *bus = priv->bus;
-       u16 r;
-
-       rtl_set_page(priv, page);
-       r = bus->read(bus, phy, reg);
-       r &= ~mask;
-       r |= val;
-       bus->write(bus, phy, reg, r);
-       return bus->read(bus, phy, reg); /* flush */
-}
-
-
-static inline int
-rtl_get(struct switch_dev *dev, enum rtl_regidx s)
-{
-       const struct rtl_reg *r = &rtl_regs[s];
-       u16 val;
-
-       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
-       if (r->bits == 0) /* unimplemented */
-               return 0;
-
-       val = rtl_r16(dev, r->page, r->phy, r->reg);
-
-       if (r->shift > 0)
-               val >>= r->shift;
-
-       if (r->inverted)
-               val = ~val;
-
-       val &= (1 << r->bits) - 1;
-
-       return val;
-}
-
-static int
-rtl_set(struct switch_dev *dev, enum rtl_regidx s, unsigned int val)
-{
-       const struct rtl_reg *r = &rtl_regs[s];
-       u16 mask = 0xffff;
-
-       BUG_ON(s >= ARRAY_SIZE(rtl_regs));
-
-       if (r->bits == 0) /* unimplemented */
-               return 0;
-
-       if (r->shift > 0)
-               val <<= r->shift;
-
-       if (r->inverted)
-               val = ~val;
-
-       if (r->bits != 16) {
-               mask = (1 << r->bits) - 1;
-               mask <<= r->shift;
-       }
-       val &= mask;
-       return rtl_rmw(dev, r->page, r->phy, r->reg, mask, val);
-}
-
-static void
-rtl_phy_save(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
-{
-       regs->nway = rtl_get(dev, RTL_PORT_REG(port, NWAY));
-       regs->speed = rtl_get(dev, RTL_PORT_REG(port, SPEED));
-       regs->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
-}
-
-static void
-rtl_phy_restore(struct switch_dev *dev, int port, struct rtl_phyregs *regs)
-{
-       rtl_set(dev, RTL_PORT_REG(port, NWAY), regs->nway);
-       rtl_set(dev, RTL_PORT_REG(port, SPEED), regs->speed);
-       rtl_set(dev, RTL_PORT_REG(port, DUPLEX), regs->duplex);
-}
-
-static void
-rtl_port_set_enable(struct switch_dev *dev, int port, int enabled)
-{
-       rtl_set(dev, RTL_PORT_REG(port, RXEN), enabled);
-       rtl_set(dev, RTL_PORT_REG(port, TXEN), enabled);
-
-       if ((port >= 5) || !enabled)
-               return;
-
-       /* restart autonegotiation if enabled */
-       rtl_set(dev, RTL_PORT_REG(port, NRESTART), 1);
-}
-
-static int
-rtl_hw_apply(struct switch_dev *dev)
-{
-       int i;
-       int trunk_en, trunk_psel;
-       struct rtl_phyregs port5;
-
-       rtl_phy_save(dev, 5, &port5);
-
-       /* disable rx/tx from PHYs */
-       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
-               rtl_port_set_enable(dev, i, 0);
-       }
-
-       /* save trunking status */
-       trunk_en = rtl_get(dev, RTL_REG_EN_TRUNK);
-       trunk_psel = rtl_get(dev, RTL_REG_TRUNK_PORTSEL);
-
-       /* trunk port 3 and 4
-        * XXX: Big WTF, but RealTek seems to do it */
-       rtl_set(dev, RTL_REG_EN_TRUNK, 1);
-       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 1);
-
-       /* execute the software reset */
-       rtl_set(dev, RTL_REG_RESET, 1);
-
-       /* wait for the reset to complete,
-        * but don't wait for too long */
-       for (i = 0; i < 10; i++) {
-               if (rtl_get(dev, RTL_REG_RESET) == 0)
-                       break;
-
-               msleep(1);
-       }
-
-       /* enable rx/tx from PHYs */
-       for (i = 0; i < RTL8306_NUM_PORTS - 1; i++) {
-               rtl_port_set_enable(dev, i, 1);
-       }
-
-       /* restore trunking settings */
-       rtl_set(dev, RTL_REG_EN_TRUNK, trunk_en);
-       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, trunk_psel);
-       rtl_phy_restore(dev, 5, &port5);
-
-       rtl_set(dev, RTL_REG_CPU_LINKUP, 1);
-
-       return 0;
-}
-
-static void
-rtl_hw_init(struct switch_dev *dev)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       int cpu_mask = 1 << dev->cpu_port;
-       int i;
-
-       rtl_set(dev, RTL_REG_VLAN_ENABLE, 0);
-       rtl_set(dev, RTL_REG_VLAN_FILTER, 0);
-       rtl_set(dev, RTL_REG_EN_TRUNK, 0);
-       rtl_set(dev, RTL_REG_TRUNK_PORTSEL, 0);
-
-       /* initialize cpu port settings */
-       if (priv->do_cpu) {
-               rtl_set(dev, RTL_REG_CPUPORT, dev->cpu_port);
-               rtl_set(dev, RTL_REG_EN_CPUPORT, 1);
-       } else {
-               rtl_set(dev, RTL_REG_CPUPORT, 7);
-               rtl_set(dev, RTL_REG_EN_CPUPORT, 0);
-       }
-       rtl_set(dev, RTL_REG_EN_TAG_OUT, 0);
-       rtl_set(dev, RTL_REG_EN_TAG_IN, 0);
-       rtl_set(dev, RTL_REG_EN_TAG_CLR, 0);
-
-       /* reset all vlans */
-       for (i = 0; i < RTL8306_NUM_VLANS; i++) {
-               rtl_set(dev, RTL_VLAN_REG(i, VID), i);
-               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), 0);
-       }
-
-       /* default to port isolation */
-       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
-               unsigned long mask;
-
-               if ((1 << i) == cpu_mask)
-                       mask = ((1 << RTL8306_NUM_PORTS) - 1) & ~cpu_mask; /* all bits set */
-               else
-                       mask = cpu_mask | (1 << i);
-
-               rtl_set(dev, RTL_VLAN_REG(i, PORTMASK), mask);
-               rtl_set(dev, RTL_PORT_REG(i, PVID), i);
-               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
-               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), 1);
-               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), 3);
-       }
-       rtl_hw_apply(dev);
-}
-
-#ifdef DEBUG
-static int
-rtl_set_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       priv->do_cpu = val->value.i;
-       rtl_hw_init(dev);
-       return 0;
-}
-
-static int
-rtl_get_use_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       val->value.i = priv->do_cpu;
-       return 0;
-}
-
-static int
-rtl_set_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       dev->cpu_port = val->value.i;
-       rtl_hw_init(dev);
-       return 0;
-}
-
-static int
-rtl_get_cpuport(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       val->value.i = dev->cpu_port;
-       return 0;
-}
-#endif
-
-static int
-rtl_reset(struct switch_dev *dev)
-{
-       rtl_hw_init(dev);
-       return 0;
-}
-
-static int
-rtl_attr_set_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       int idx = attr->id + (val->port_vlan * attr->ofs);
-       struct rtl_phyregs port;
-
-       if (attr->id >= ARRAY_SIZE(rtl_regs))
-               return -EINVAL;
-
-       if ((attr->max > 0) && (val->value.i > attr->max))
-               return -EINVAL;
-
-       /* access to phy register 22 on port 4/5
-        * needs phy status save/restore */
-       if ((val->port_vlan > 3) &&
-               (rtl_regs[idx].reg == 22) &&
-               (rtl_regs[idx].page == 0)) {
-
-               rtl_phy_save(dev, val->port_vlan, &port);
-               rtl_set(dev, idx, val->value.i);
-               rtl_phy_restore(dev, val->port_vlan, &port);
-       } else {
-               rtl_set(dev, idx, val->value.i);
-       }
-
-       return 0;
-}
-
-static int
-rtl_attr_get_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       int idx = attr->id + (val->port_vlan * attr->ofs);
-
-       if (idx >= ARRAY_SIZE(rtl_regs))
-               return -EINVAL;
-
-       val->value.i = rtl_get(dev, idx);
-       return 0;
-}
-
-static int
-rtl_attr_set_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       if (val->port_vlan >= RTL8306_NUM_PORTS)
-               return -EINVAL;
-
-       return rtl_attr_set_int(dev, attr, val);
-}
-
-static int
-rtl_attr_get_port_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       if (val->port_vlan >= RTL8306_NUM_PORTS)
-               return -EINVAL;
-       return rtl_attr_get_int(dev, attr, val);
-}
-
-static int 
-rtl_get_port_link(struct switch_dev *dev, int port, struct switch_port_link *link)
-{
-       if (port >= RTL8306_NUM_PORTS)
-               return -EINVAL;
-
-       /* in case the link changes from down to up, the register is only updated on read */
-       link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
-       if (!link->link)
-               link->link = rtl_get(dev, RTL_PORT_REG(port, LINK));
-
-       if (!link->link)
-               return 0;
-
-       link->duplex = rtl_get(dev, RTL_PORT_REG(port, DUPLEX));
-       link->aneg = rtl_get(dev, RTL_PORT_REG(port, NWAY));
-
-       if (rtl_get(dev, RTL_PORT_REG(port, SPEED)))
-               link->speed = SWITCH_PORT_SPEED_100;
-       else
-               link->speed = SWITCH_PORT_SPEED_10;
-
-       return 0;
-}
-
-static int
-rtl_attr_set_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       if (val->port_vlan >= dev->vlans)
-               return -EINVAL;
-
-       return rtl_attr_set_int(dev, attr, val);
-}
-
-static int
-rtl_attr_get_vlan_int(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       if (val->port_vlan >= dev->vlans)
-               return -EINVAL;
-
-       return rtl_attr_get_int(dev, attr, val);
-}
-
-static int
-rtl_get_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       unsigned int i, mask;
-
-       mask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
-       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
-               struct switch_port *port;
-
-               if (!(mask & (1 << i)))
-                       continue;
-
-               port = &val->value.ports[val->len];
-               port->id = i;
-               if (rtl_get(dev, RTL_PORT_REG(i, TAG_INSERT)) == 2 || i == dev->cpu_port)
-                       port->flags = (1 << SWITCH_PORT_FLAG_TAGGED);
-               val->len++;
-       }
-
-       return 0;
-}
-
-static int
-rtl_set_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       struct rtl_priv *priv = to_rtl(dev);
-       struct rtl_phyregs port;
-       int en = val->value.i;
-       int i;
-
-       rtl_set(dev, RTL_REG_EN_TAG_OUT, en && priv->do_cpu);
-       rtl_set(dev, RTL_REG_EN_TAG_IN, en && priv->do_cpu);
-       rtl_set(dev, RTL_REG_EN_TAG_CLR, en && priv->do_cpu);
-       rtl_set(dev, RTL_REG_VLAN_TAG_AWARE, en);
-       if (en)
-               rtl_set(dev, RTL_REG_VLAN_FILTER, en);
-
-       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
-               if (i > 3)
-                       rtl_phy_save(dev, val->port_vlan, &port);
-               rtl_set(dev, RTL_PORT_REG(i, NULL_VID_REPLACE), 1);
-               rtl_set(dev, RTL_PORT_REG(i, VID_INSERT), (en ? (i == dev->cpu_port ? 0 : 1) : 1));
-               rtl_set(dev, RTL_PORT_REG(i, TAG_INSERT), (en ? (i == dev->cpu_port ? 2 : 1) : 3));
-               if (i > 3)
-                       rtl_phy_restore(dev, val->port_vlan, &port);
-       }
-       rtl_set(dev, RTL_REG_VLAN_ENABLE, en);
-
-       return 0;
-}
-
-static int
-rtl_get_vlan(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val)
-{
-       val->value.i = rtl_get(dev, RTL_REG_VLAN_ENABLE);
-       return 0;
-}
-
-static int
-rtl_set_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       unsigned int mask = 0;
-       unsigned int oldmask;
-       int i;
-
-       for(i = 0; i < val->len; i++)
-       {
-               struct switch_port *port = &val->value.ports[i];
-               bool tagged = false;
-
-               mask |= (1 << port->id);
-
-               if (port->id == dev->cpu_port)
-                       continue;
-
-               if ((i == dev->cpu_port) ||
-                       (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
-                       tagged = true;
-
-               /* fix up PVIDs for added ports */
-               if (!tagged)
-                       rtl_set(dev, RTL_PORT_REG(port->id, PVID), val->port_vlan);
-
-               rtl_set(dev, RTL_PORT_REG(port->id, NON_PVID_DISCARD), (tagged ? 0 : 1));
-               rtl_set(dev, RTL_PORT_REG(port->id, VID_INSERT), (tagged ? 0 : 1));
-               rtl_set(dev, RTL_PORT_REG(port->id, TAG_INSERT), (tagged ? 2 : 1));
-       }
-
-       oldmask = rtl_get(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK));
-       rtl_set(dev, RTL_VLAN_REG(val->port_vlan, PORTMASK), mask);
-
-       /* fix up PVIDs for removed ports, default to last vlan */
-       oldmask &= ~mask;
-       for (i = 0; i < RTL8306_NUM_PORTS; i++) {
-               if (!(oldmask & (1 << i)))
-                       continue;
-
-               if (i == dev->cpu_port)
-                       continue;
-
-               if (rtl_get(dev, RTL_PORT_REG(i, PVID)) == val->port_vlan)
-                       rtl_set(dev, RTL_PORT_REG(i, PVID), dev->vlans - 1);
-       }
-
-       return 0;
-}
-
-static struct switch_attr rtl_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .max = 1,
-               .set = rtl_set_vlan,
-               .get = rtl_get_vlan,
-       },
-       {
-               RTL_GLOBAL_REGATTR(EN_TRUNK),
-               .name = "trunk",
-               .description = "Enable port trunking",
-               .max = 1,
-       },
-       {
-               RTL_GLOBAL_REGATTR(TRUNK_PORTSEL),
-               .name = "trunk_sel",
-               .description = "Select ports for trunking (0: 0,1 - 1: 3,4)",
-               .max = 1,
-       },
-#ifdef DEBUG
-       {
-               RTL_GLOBAL_REGATTR(VLAN_FILTER),
-               .name = "vlan_filter",
-               .description = "Filter incoming packets for allowed VLANS",
-               .max = 1,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "cpuport",
-               .description = "CPU Port",
-               .set = rtl_set_cpuport,
-               .get = rtl_get_cpuport,
-               .max = RTL8306_NUM_PORTS,
-       },
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "use_cpuport",
-               .description = "CPU Port handling flag",
-               .set = rtl_set_use_cpuport,
-               .get = rtl_get_use_cpuport,
-               .max = RTL8306_NUM_PORTS,
-       },
-       {
-               RTL_GLOBAL_REGATTR(TRAP_CPU),
-               .name = "trap_cpu",
-               .description = "VLAN trap to CPU",
-               .max = 1,
-       },
-       {
-               RTL_GLOBAL_REGATTR(VLAN_TAG_AWARE),
-               .name = "vlan_tag_aware",
-               .description = "Enable VLAN tag awareness",
-               .max = 1,
-       },
-       {
-               RTL_GLOBAL_REGATTR(VLAN_TAG_ONLY),
-               .name = "tag_only",
-               .description = "Only accept tagged packets",
-               .max = 1,
-       },
-#endif
-};
-static struct switch_attr rtl_port[] = {
-       {
-               RTL_PORT_REGATTR(PVID),
-               .name = "pvid",
-               .description = "Port VLAN ID",
-               .max = RTL8306_NUM_VLANS - 1,
-       },
-#ifdef DEBUG
-       {
-               RTL_PORT_REGATTR(NULL_VID_REPLACE),
-               .name = "null_vid",
-               .description = "NULL VID gets replaced by port default vid",
-               .max = 1,
-       },
-       {
-               RTL_PORT_REGATTR(NON_PVID_DISCARD),
-               .name = "non_pvid_discard",
-               .description = "discard packets with VID != PVID",
-               .max = 1,
-       },
-       {
-               RTL_PORT_REGATTR(VID_INSERT),
-               .name = "vid_insert_remove",
-               .description = "how should the switch insert and remove vids ?",
-               .max = 3,
-       },
-       {
-               RTL_PORT_REGATTR(TAG_INSERT),
-               .name = "tag_insert",
-               .description = "tag insertion handling",
-               .max = 3,
-       },
-#endif
-};
-
-static struct switch_attr rtl_vlan[] = {
-       {
-               RTL_VLAN_REGATTR(VID),
-               .name = "vid",
-               .description = "VLAN ID (1-4095)",
-               .max = 4095,
-       },
-};
-
-static const struct switch_dev_ops rtl8306_ops = {
-       .attr_global = {
-               .attr = rtl_globals,
-               .n_attr = ARRAY_SIZE(rtl_globals),
-       },
-       .attr_port = {
-               .attr = rtl_port,
-               .n_attr = ARRAY_SIZE(rtl_port),
-       },
-       .attr_vlan = {
-               .attr = rtl_vlan,
-               .n_attr = ARRAY_SIZE(rtl_vlan),
-       },
-
-       .get_vlan_ports = rtl_get_ports,
-       .set_vlan_ports = rtl_set_ports,
-       .apply_config = rtl_hw_apply,
-       .reset_switch = rtl_reset,
-       .get_port_link = rtl_get_port_link,
-};
-
-static int
-rtl8306_config_init(struct phy_device *pdev)
-{
-       struct net_device *netdev = pdev->attached_dev;
-       struct rtl_priv *priv = pdev->priv;
-       struct switch_dev *dev = &priv->dev;
-       struct switch_val val;
-       unsigned int chipid, chipver, chiptype;
-       int err;
-
-       /* Only init the switch for the primary PHY */
-       if (pdev->mdio.addr != 0)
-               return 0;
-
-       val.value.i = 1;
-       priv->dev.cpu_port = RTL8306_PORT_CPU;
-       priv->dev.ports = RTL8306_NUM_PORTS;
-       priv->dev.vlans = RTL8306_NUM_VLANS;
-       priv->dev.ops = &rtl8306_ops;
-       priv->do_cpu = 0;
-       priv->page = -1;
-       priv->bus = pdev->mdio.bus;
-
-       chipid = rtl_get(dev, RTL_REG_CHIPID);
-       chipver = rtl_get(dev, RTL_REG_CHIPVER);
-       chiptype = rtl_get(dev, RTL_REG_CHIPTYPE);
-       switch(chiptype) {
-       case 0:
-       case 2:
-               strncpy(priv->hwname, RTL_NAME_S, sizeof(priv->hwname));
-               priv->type = RTL_TYPE_S;
-               break;
-       case 1:
-               strncpy(priv->hwname, RTL_NAME_SD, sizeof(priv->hwname));
-               priv->type = RTL_TYPE_SD;
-               break;
-       case 3:
-               strncpy(priv->hwname, RTL_NAME_SDM, sizeof(priv->hwname));
-               priv->type = RTL_TYPE_SDM;
-               break;
-       default:
-               strncpy(priv->hwname, RTL_NAME_UNKNOWN, sizeof(priv->hwname));
-               break;
-       }
-
-       dev->name = priv->hwname;
-       rtl_hw_init(dev);
-
-       printk(KERN_INFO "Registering %s switch with Chip ID: 0x%04x, version: 0x%04x\n", priv->hwname, chipid, chipver);
-
-       err = register_switch(dev, netdev);
-       if (err < 0) {
-               kfree(priv);
-               return err;
-       }
-
-       return 0;
-}
-
-
-static int
-rtl8306_fixup(struct phy_device *pdev)
-{
-       struct rtl_priv priv;
-       u16 chipid;
-
-       /* Attach to primary LAN port and WAN port */
-       if (pdev->mdio.addr != 0 && pdev->mdio.addr != 4)
-               return 0;
-
-       memset(&priv, 0, sizeof(priv));
-       priv.fixup = true;
-       priv.page = -1;
-       priv.bus = pdev->mdio.bus;
-       chipid = rtl_get(&priv.dev, RTL_REG_CHIPID);
-       if (chipid == 0x5988)
-               pdev->phy_id = RTL8306_MAGIC;
-
-       return 0;
-}
-
-static int
-rtl8306_probe(struct phy_device *pdev)
-{
-       struct rtl_priv *priv;
-
-       list_for_each_entry(priv, &phydevs, list) {
-               /*
-                * share one rtl_priv instance between virtual phy
-                * devices on the same bus
-                */
-               if (priv->bus == pdev->mdio.bus)
-                       goto found;
-       }
-       priv = kzalloc(sizeof(struct rtl_priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->bus = pdev->mdio.bus;
-
-found:
-       pdev->priv = priv;
-       return 0;
-}
-
-static void
-rtl8306_remove(struct phy_device *pdev)
-{
-       struct rtl_priv *priv = pdev->priv;
-       unregister_switch(&priv->dev);
-       kfree(priv);
-}
-
-static int
-rtl8306_config_aneg(struct phy_device *pdev)
-{
-       struct rtl_priv *priv = pdev->priv;
-
-       /* Only for WAN */
-       if (pdev->mdio.addr == 0)
-               return 0;
-
-       /* Restart autonegotiation */
-       rtl_set(&priv->dev, RTL_PORT_REG(4, NWAY), 1);
-       rtl_set(&priv->dev, RTL_PORT_REG(4, NRESTART), 1);
-
-       return 0;
-}
-
-static int
-rtl8306_read_status(struct phy_device *pdev)
-{
-       struct rtl_priv *priv = pdev->priv;
-       struct switch_dev *dev = &priv->dev;
-
-       if (pdev->mdio.addr == 4) {
-               /* WAN */
-               pdev->speed = rtl_get(dev, RTL_PORT_REG(4, SPEED)) ? SPEED_100 : SPEED_10;
-               pdev->duplex = rtl_get(dev, RTL_PORT_REG(4, DUPLEX)) ? DUPLEX_FULL : DUPLEX_HALF;
-               pdev->link = !!rtl_get(dev, RTL_PORT_REG(4, LINK));
-       } else {
-               /* LAN */
-               pdev->speed = SPEED_100;
-               pdev->duplex = DUPLEX_FULL;
-               pdev->link = 1;
-       }
-
-       /*
-        * Bypass generic PHY status read,
-        * it doesn't work with this switch
-        */
-       if (pdev->link) {
-               pdev->state = PHY_RUNNING;
-               netif_carrier_on(pdev->attached_dev);
-               pdev->adjust_link(pdev->attached_dev);
-       } else {
-               pdev->state = PHY_NOLINK;
-               netif_carrier_off(pdev->attached_dev);
-               pdev->adjust_link(pdev->attached_dev);
-       }
-
-       return 0;
-}
-
-
-static struct phy_driver rtl8306_driver = {
-       .name           = "Realtek RTL8306S",
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(4,13,0))
-       .flags          = PHY_HAS_MAGICANEG,
-#endif
-       .phy_id         = RTL8306_MAGIC,
-       .phy_id_mask    = 0xffffffff,
-       .features       = PHY_BASIC_FEATURES,
-       .probe          = &rtl8306_probe,
-       .remove         = &rtl8306_remove,
-       .config_init    = &rtl8306_config_init,
-       .config_aneg    = &rtl8306_config_aneg,
-       .read_status    = &rtl8306_read_status,
-};
-
-
-static int __init
-rtl_init(void)
-{
-       phy_register_fixup_for_id(PHY_ANY_ID, rtl8306_fixup);
-       return phy_driver_register(&rtl8306_driver, THIS_MODULE);
-}
-
-static void __exit
-rtl_exit(void)
-{
-       phy_driver_unregister(&rtl8306_driver);
-}
-
-module_init(rtl_init);
-module_exit(rtl_exit);
-MODULE_LICENSE("GPL");
-
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.c
deleted file mode 100644 (file)
index c0cb680..0000000
+++ /dev/null
@@ -1,1628 +0,0 @@
-/*
- * Realtek RTL8366 SMI interface driver
- *
- * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/spinlock.h>
-#include <linux/skbuff.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_gpio.h>
-#include <linux/rtl8366.h>
-#include <linux/version.h>
-#include <linux/of_mdio.h>
-
-#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
-#include <linux/debugfs.h>
-#endif
-
-#include "rtl8366_smi.h"
-
-#define RTL8366_SMI_ACK_RETRY_COUNT         5
-
-#define RTL8366_SMI_HW_STOP_DELAY              25      /* msecs */
-#define RTL8366_SMI_HW_START_DELAY             100     /* msecs */
-
-static inline void rtl8366_smi_clk_delay(struct rtl8366_smi *smi)
-{
-       ndelay(smi->clk_delay);
-}
-
-static void rtl8366_smi_start(struct rtl8366_smi *smi)
-{
-       unsigned int sda = smi->gpio_sda;
-       unsigned int sck = smi->gpio_sck;
-
-       /*
-        * Set GPIO pins to output mode, with initial state:
-        * SCK = 0, SDA = 1
-        */
-       gpio_direction_output(sck, 0);
-       gpio_direction_output(sda, 1);
-       rtl8366_smi_clk_delay(smi);
-
-       /* CLK 1: 0 -> 1, 1 -> 0 */
-       gpio_set_value(sck, 1);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 0);
-       rtl8366_smi_clk_delay(smi);
-
-       /* CLK 2: */
-       gpio_set_value(sck, 1);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sda, 0);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 0);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sda, 1);
-}
-
-static void rtl8366_smi_stop(struct rtl8366_smi *smi)
-{
-       unsigned int sda = smi->gpio_sda;
-       unsigned int sck = smi->gpio_sck;
-
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sda, 0);
-       gpio_set_value(sck, 1);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sda, 1);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 1);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 0);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 1);
-
-       /* add a click */
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 0);
-       rtl8366_smi_clk_delay(smi);
-       gpio_set_value(sck, 1);
-
-       /* set GPIO pins to input mode */
-       gpio_direction_input(sda);
-       gpio_direction_input(sck);
-}
-
-static void rtl8366_smi_write_bits(struct rtl8366_smi *smi, u32 data, u32 len)
-{
-       unsigned int sda = smi->gpio_sda;
-       unsigned int sck = smi->gpio_sck;
-
-       for (; len > 0; len--) {
-               rtl8366_smi_clk_delay(smi);
-
-               /* prepare data */
-               gpio_set_value(sda, !!(data & ( 1 << (len - 1))));
-               rtl8366_smi_clk_delay(smi);
-
-               /* clocking */
-               gpio_set_value(sck, 1);
-               rtl8366_smi_clk_delay(smi);
-               gpio_set_value(sck, 0);
-       }
-}
-
-static void rtl8366_smi_read_bits(struct rtl8366_smi *smi, u32 len, u32 *data)
-{
-       unsigned int sda = smi->gpio_sda;
-       unsigned int sck = smi->gpio_sck;
-
-       gpio_direction_input(sda);
-
-       for (*data = 0; len > 0; len--) {
-               u32 u;
-
-               rtl8366_smi_clk_delay(smi);
-
-               /* clocking */
-               gpio_set_value(sck, 1);
-               rtl8366_smi_clk_delay(smi);
-               u = !!gpio_get_value(sda);
-               gpio_set_value(sck, 0);
-
-               *data |= (u << (len - 1));
-       }
-
-       gpio_direction_output(sda, 0);
-}
-
-static int rtl8366_smi_wait_for_ack(struct rtl8366_smi *smi)
-{
-       int retry_cnt;
-
-       retry_cnt = 0;
-       do {
-               u32 ack;
-
-               rtl8366_smi_read_bits(smi, 1, &ack);
-               if (ack == 0)
-                       break;
-
-               if (++retry_cnt > RTL8366_SMI_ACK_RETRY_COUNT) {
-                       dev_err(smi->parent, "ACK timeout\n");
-                       return -ETIMEDOUT;
-               }
-       } while (1);
-
-       return 0;
-}
-
-static int rtl8366_smi_write_byte(struct rtl8366_smi *smi, u8 data)
-{
-       rtl8366_smi_write_bits(smi, data, 8);
-       return rtl8366_smi_wait_for_ack(smi);
-}
-
-static int rtl8366_smi_write_byte_noack(struct rtl8366_smi *smi, u8 data)
-{
-       rtl8366_smi_write_bits(smi, data, 8);
-       return 0;
-}
-
-static int rtl8366_smi_read_byte0(struct rtl8366_smi *smi, u8 *data)
-{
-       u32 t;
-
-       /* read data */
-       rtl8366_smi_read_bits(smi, 8, &t);
-       *data = (t & 0xff);
-
-       /* send an ACK */
-       rtl8366_smi_write_bits(smi, 0x00, 1);
-
-       return 0;
-}
-
-static int rtl8366_smi_read_byte1(struct rtl8366_smi *smi, u8 *data)
-{
-       u32 t;
-
-       /* read data */
-       rtl8366_smi_read_bits(smi, 8, &t);
-       *data = (t & 0xff);
-
-       /* send an ACK */
-       rtl8366_smi_write_bits(smi, 0x01, 1);
-
-       return 0;
-}
-
-static int __rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
-{
-       unsigned long flags;
-       u8 lo = 0;
-       u8 hi = 0;
-       int ret;
-
-       spin_lock_irqsave(&smi->lock, flags);
-
-       rtl8366_smi_start(smi);
-
-       /* send READ command */
-       ret = rtl8366_smi_write_byte(smi, smi->cmd_read);
-       if (ret)
-               goto out;
-
-       /* set ADDR[7:0] */
-       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
-       if (ret)
-               goto out;
-
-       /* set ADDR[15:8] */
-       ret = rtl8366_smi_write_byte(smi, addr >> 8);
-       if (ret)
-               goto out;
-
-       /* read DATA[7:0] */
-       rtl8366_smi_read_byte0(smi, &lo);
-       /* read DATA[15:8] */
-       rtl8366_smi_read_byte1(smi, &hi);
-
-       *data = ((u32) lo) | (((u32) hi) << 8);
-
-       ret = 0;
-
- out:
-       rtl8366_smi_stop(smi);
-       spin_unlock_irqrestore(&smi->lock, flags);
-
-       return ret;
-}
-/* Read/write via mdiobus */
-#define MDC_MDIO_CTRL0_REG             31
-#define MDC_MDIO_START_REG             29
-#define MDC_MDIO_CTRL1_REG             21
-#define MDC_MDIO_ADDRESS_REG           23
-#define MDC_MDIO_DATA_WRITE_REG                24
-#define MDC_MDIO_DATA_READ_REG         25
-
-#define MDC_MDIO_START_OP              0xFFFF
-#define MDC_MDIO_ADDR_OP               0x000E
-#define MDC_MDIO_READ_OP               0x0001
-#define MDC_MDIO_WRITE_OP              0x0003
-#define MDC_REALTEK_PHY_ADDR           0x0
-
-int __rtl8366_mdio_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
-{
-       u32 phy_id = MDC_REALTEK_PHY_ADDR;
-       struct mii_bus *mbus = smi->ext_mbus;
-
-       BUG_ON(in_interrupt());
-
-       mutex_lock(&mbus->mdio_lock);
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write address control code to register 31 */
-       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
-
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write address to register 23 */
-       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
-
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write read control code to register 21 */
-       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_READ_OP);
-
-       /* Write Start command to register 29 */
-       mbus->write(smi->ext_mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Read data from register 25 */
-       *data = mbus->read(mbus, phy_id, MDC_MDIO_DATA_READ_REG);
-
-       mutex_unlock(&mbus->mdio_lock);
-
-       return 0;
-}
-
-static int __rtl8366_mdio_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
-{
-       u32 phy_id = MDC_REALTEK_PHY_ADDR;
-       struct mii_bus *mbus = smi->ext_mbus;
-
-       BUG_ON(in_interrupt());
-
-       mutex_lock(&mbus->mdio_lock);
-
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write address control code to register 31 */
-       mbus->write(mbus, phy_id, MDC_MDIO_CTRL0_REG, MDC_MDIO_ADDR_OP);
-
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write address to register 23 */
-       mbus->write(mbus, phy_id, MDC_MDIO_ADDRESS_REG, addr);
-
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write data to register 24 */
-       mbus->write(mbus, phy_id, MDC_MDIO_DATA_WRITE_REG, data);
-
-       /* Write Start command to register 29 */
-       mbus->write(mbus, phy_id, MDC_MDIO_START_REG, MDC_MDIO_START_OP);
-
-       /* Write data control code to register 21 */
-       mbus->write(mbus, phy_id, MDC_MDIO_CTRL1_REG, MDC_MDIO_WRITE_OP);
-
-       mutex_unlock(&mbus->mdio_lock);
-       return 0;
-}
-
-int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data)
-{
-       if (smi->ext_mbus)
-               return __rtl8366_mdio_read_reg(smi, addr, data);
-       else
-               return __rtl8366_smi_read_reg(smi, addr, data);
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_read_reg);
-
-static int __rtl8366_smi_write_reg(struct rtl8366_smi *smi,
-                                  u32 addr, u32 data, bool ack)
-{
-       unsigned long flags;
-       int ret;
-
-       spin_lock_irqsave(&smi->lock, flags);
-
-       rtl8366_smi_start(smi);
-
-       /* send WRITE command */
-       ret = rtl8366_smi_write_byte(smi, smi->cmd_write);
-       if (ret)
-               goto out;
-
-       /* set ADDR[7:0] */
-       ret = rtl8366_smi_write_byte(smi, addr & 0xff);
-       if (ret)
-               goto out;
-
-       /* set ADDR[15:8] */
-       ret = rtl8366_smi_write_byte(smi, addr >> 8);
-       if (ret)
-               goto out;
-
-       /* write DATA[7:0] */
-       ret = rtl8366_smi_write_byte(smi, data & 0xff);
-       if (ret)
-               goto out;
-
-       /* write DATA[15:8] */
-       if (ack)
-               ret = rtl8366_smi_write_byte(smi, data >> 8);
-       else
-               ret = rtl8366_smi_write_byte_noack(smi, data >> 8);
-       if (ret)
-               goto out;
-
-       ret = 0;
-
- out:
-       rtl8366_smi_stop(smi);
-       spin_unlock_irqrestore(&smi->lock, flags);
-
-       return ret;
-}
-
-int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data)
-{
-       if (smi->ext_mbus)
-               return __rtl8366_mdio_write_reg(smi, addr, data);
-       else
-               return __rtl8366_smi_write_reg(smi, addr, data, true);
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg);
-
-int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data)
-{
-       return __rtl8366_smi_write_reg(smi, addr, data, false);
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_write_reg_noack);
-
-int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data)
-{
-       u32 t;
-       int err;
-
-       err = rtl8366_smi_read_reg(smi, addr, &t);
-       if (err)
-               return err;
-
-       err = rtl8366_smi_write_reg(smi, addr, (t & ~mask) | data);
-       return err;
-
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_rmwr);
-
-static int rtl8366_reset(struct rtl8366_smi *smi)
-{
-       if (smi->hw_reset) {
-               smi->hw_reset(smi, true);
-               msleep(RTL8366_SMI_HW_STOP_DELAY);
-               smi->hw_reset(smi, false);
-               msleep(RTL8366_SMI_HW_START_DELAY);
-               return 0;
-       }
-
-       return smi->ops->reset_chip(smi);
-}
-
-static int rtl8366_mc_is_used(struct rtl8366_smi *smi, int mc_index, int *used)
-{
-       int err;
-       int i;
-
-       *used = 0;
-       for (i = 0; i < smi->num_ports; i++) {
-               int index = 0;
-
-               err = smi->ops->get_mc_index(smi, i, &index);
-               if (err)
-                       return err;
-
-               if (mc_index == index) {
-                       *used = 1;
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-static int rtl8366_set_vlan(struct rtl8366_smi *smi, int vid, u32 member,
-                           u32 untag, u32 fid)
-{
-       struct rtl8366_vlan_4k vlan4k;
-       int err;
-       int i;
-
-       /* Update the 4K table */
-       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
-       if (err)
-               return err;
-
-       vlan4k.member = member;
-       vlan4k.untag = untag;
-       vlan4k.fid = fid;
-       err = smi->ops->set_vlan_4k(smi, &vlan4k);
-       if (err)
-               return err;
-
-       /* Try to find an existing MC entry for this VID */
-       for (i = 0; i < smi->num_vlan_mc; i++) {
-               struct rtl8366_vlan_mc vlanmc;
-
-               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
-               if (err)
-                       return err;
-
-               if (vid == vlanmc.vid) {
-                       /* update the MC entry */
-                       vlanmc.member = member;
-                       vlanmc.untag = untag;
-                       vlanmc.fid = fid;
-
-                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
-                       break;
-               }
-       }
-
-       return err;
-}
-
-static int rtl8366_get_pvid(struct rtl8366_smi *smi, int port, int *val)
-{
-       struct rtl8366_vlan_mc vlanmc;
-       int err;
-       int index;
-
-       err = smi->ops->get_mc_index(smi, port, &index);
-       if (err)
-               return err;
-
-       err = smi->ops->get_vlan_mc(smi, index, &vlanmc);
-       if (err)
-               return err;
-
-       *val = vlanmc.vid;
-       return 0;
-}
-
-static int rtl8366_set_pvid(struct rtl8366_smi *smi, unsigned port,
-                           unsigned vid)
-{
-       struct rtl8366_vlan_mc vlanmc;
-       struct rtl8366_vlan_4k vlan4k;
-       int err;
-       int i;
-
-       /* Try to find an existing MC entry for this VID */
-       for (i = 0; i < smi->num_vlan_mc; i++) {
-               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
-               if (err)
-                       return err;
-
-               if (vid == vlanmc.vid) {
-                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
-                       if (err)
-                               return err;
-
-                       err = smi->ops->set_mc_index(smi, port, i);
-                       return err;
-               }
-       }
-
-       /* We have no MC entry for this VID, try to find an empty one */
-       for (i = 0; i < smi->num_vlan_mc; i++) {
-               err = smi->ops->get_vlan_mc(smi, i, &vlanmc);
-               if (err)
-                       return err;
-
-               if (vlanmc.vid == 0 && vlanmc.member == 0) {
-                       /* Update the entry from the 4K table */
-                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
-                       if (err)
-                               return err;
-
-                       vlanmc.vid = vid;
-                       vlanmc.member = vlan4k.member;
-                       vlanmc.untag = vlan4k.untag;
-                       vlanmc.fid = vlan4k.fid;
-                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
-                       if (err)
-                               return err;
-
-                       err = smi->ops->set_mc_index(smi, port, i);
-                       return err;
-               }
-       }
-
-       /* MC table is full, try to find an unused entry and replace it */
-       for (i = 0; i < smi->num_vlan_mc; i++) {
-               int used;
-
-               err = rtl8366_mc_is_used(smi, i, &used);
-               if (err)
-                       return err;
-
-               if (!used) {
-                       /* Update the entry from the 4K table */
-                       err = smi->ops->get_vlan_4k(smi, vid, &vlan4k);
-                       if (err)
-                               return err;
-
-                       vlanmc.vid = vid;
-                       vlanmc.member = vlan4k.member;
-                       vlanmc.untag = vlan4k.untag;
-                       vlanmc.fid = vlan4k.fid;
-                       err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
-                       if (err)
-                               return err;
-
-                       err = smi->ops->set_mc_index(smi, port, i);
-                       return err;
-               }
-       }
-
-       dev_err(smi->parent,
-               "all VLAN member configurations are in use\n");
-
-       return -ENOSPC;
-}
-
-int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable)
-{
-       int err;
-
-       err = smi->ops->enable_vlan(smi, enable);
-       if (err)
-               return err;
-
-       smi->vlan_enabled = enable;
-
-       if (!enable) {
-               smi->vlan4k_enabled = 0;
-               err = smi->ops->enable_vlan4k(smi, enable);
-       }
-
-       return err;
-}
-EXPORT_SYMBOL_GPL(rtl8366_enable_vlan);
-
-static int rtl8366_enable_vlan4k(struct rtl8366_smi *smi, int enable)
-{
-       int err;
-
-       if (enable) {
-               err = smi->ops->enable_vlan(smi, enable);
-               if (err)
-                       return err;
-
-               smi->vlan_enabled = enable;
-       }
-
-       err = smi->ops->enable_vlan4k(smi, enable);
-       if (err)
-               return err;
-
-       smi->vlan4k_enabled = enable;
-       return 0;
-}
-
-int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable)
-{
-       int port;
-       int err;
-
-       for (port = 0; port < smi->num_ports; port++) {
-               err = smi->ops->enable_port(smi, port, enable);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_enable_all_ports);
-
-int rtl8366_reset_vlan(struct rtl8366_smi *smi)
-{
-       struct rtl8366_vlan_mc vlanmc;
-       int err;
-       int i;
-
-       rtl8366_enable_vlan(smi, 0);
-       rtl8366_enable_vlan4k(smi, 0);
-
-       /* clear VLAN member configurations */
-       vlanmc.vid = 0;
-       vlanmc.priority = 0;
-       vlanmc.member = 0;
-       vlanmc.untag = 0;
-       vlanmc.fid = 0;
-       for (i = 0; i < smi->num_vlan_mc; i++) {
-               err = smi->ops->set_vlan_mc(smi, i, &vlanmc);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_reset_vlan);
-
-static int rtl8366_init_vlan(struct rtl8366_smi *smi)
-{
-       int port;
-       int err;
-
-       err = rtl8366_reset_vlan(smi);
-       if (err)
-               return err;
-
-       for (port = 0; port < smi->num_ports; port++) {
-               u32 mask;
-
-               if (port == smi->cpu_port)
-                       mask = (1 << smi->num_ports) - 1;
-               else
-                       mask = (1 << port) | (1 << smi->cpu_port);
-
-               err = rtl8366_set_vlan(smi, (port + 1), mask, mask, 0);
-               if (err)
-                       return err;
-
-               err = rtl8366_set_pvid(smi, port, (port + 1));
-               if (err)
-                       return err;
-       }
-
-       return rtl8366_enable_vlan(smi, 1);
-}
-
-#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
-int rtl8366_debugfs_open(struct inode *inode, struct file *file)
-{
-       file->private_data = inode->i_private;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_debugfs_open);
-
-static ssize_t rtl8366_read_debugfs_vlan_mc(struct file *file,
-                                             char __user *user_buf,
-                                             size_t count, loff_t *ppos)
-{
-       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
-       int i, len = 0;
-       char *buf = smi->buf;
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                       "%2s %6s %4s %6s %6s %3s\n",
-                       "id", "vid","prio", "member", "untag", "fid");
-
-       for (i = 0; i < smi->num_vlan_mc; ++i) {
-               struct rtl8366_vlan_mc vlanmc;
-
-               smi->ops->get_vlan_mc(smi, i, &vlanmc);
-
-               len += snprintf(buf + len, sizeof(smi->buf) - len,
-                               "%2d %6d %4d 0x%04x 0x%04x %3d\n",
-                               i, vlanmc.vid, vlanmc.priority,
-                               vlanmc.member, vlanmc.untag, vlanmc.fid);
-       }
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-#define RTL8366_VLAN4K_PAGE_SIZE       64
-#define RTL8366_VLAN4K_NUM_PAGES       (4096 / RTL8366_VLAN4K_PAGE_SIZE)
-
-static ssize_t rtl8366_read_debugfs_vlan_4k(struct file *file,
-                                           char __user *user_buf,
-                                           size_t count, loff_t *ppos)
-{
-       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
-       int i, len = 0;
-       int offset;
-       char *buf = smi->buf;
-
-       if (smi->dbg_vlan_4k_page >= RTL8366_VLAN4K_NUM_PAGES) {
-               len += snprintf(buf + len, sizeof(smi->buf) - len,
-                               "invalid page: %u\n", smi->dbg_vlan_4k_page);
-               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       }
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                       "%4s %6s %6s %3s\n",
-                       "vid", "member", "untag", "fid");
-
-       offset = RTL8366_VLAN4K_PAGE_SIZE * smi->dbg_vlan_4k_page;
-       for (i = 0; i < RTL8366_VLAN4K_PAGE_SIZE; i++) {
-               struct rtl8366_vlan_4k vlan4k;
-
-               smi->ops->get_vlan_4k(smi, offset + i, &vlan4k);
-
-               len += snprintf(buf + len, sizeof(smi->buf) - len,
-                               "%4d 0x%04x 0x%04x %3d\n",
-                               vlan4k.vid, vlan4k.member,
-                               vlan4k.untag, vlan4k.fid);
-       }
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t rtl8366_read_debugfs_pvid(struct file *file,
-                                        char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
-       char *buf = smi->buf;
-       int len = 0;
-       int i;
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len, "%4s %4s\n",
-                       "port", "pvid");
-
-       for (i = 0; i < smi->num_ports; i++) {
-               int pvid;
-               int err;
-
-               err = rtl8366_get_pvid(smi, i, &pvid);
-               if (err)
-                       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                               "%4d error\n", i);
-               else
-                       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                               "%4d %4d\n", i, pvid);
-       }
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t rtl8366_read_debugfs_reg(struct file *file,
-                                        char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
-       u32 t, reg = smi->dbg_reg;
-       int err, len = 0;
-       char *buf = smi->buf;
-
-       memset(buf, '\0', sizeof(smi->buf));
-
-       err = rtl8366_smi_read_reg(smi, reg, &t);
-       if (err) {
-               len += snprintf(buf, sizeof(smi->buf),
-                               "Read failed (reg: 0x%04x)\n", reg);
-               return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-       }
-
-       len += snprintf(buf, sizeof(smi->buf), "reg = 0x%04x, val = 0x%04x\n",
-                       reg, t);
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static ssize_t rtl8366_write_debugfs_reg(struct file *file,
-                                         const char __user *user_buf,
-                                         size_t count, loff_t *ppos)
-{
-       struct rtl8366_smi *smi = (struct rtl8366_smi *)file->private_data;
-       unsigned long data;
-       u32 reg = smi->dbg_reg;
-       int err;
-       size_t len;
-       char *buf = smi->buf;
-
-       len = min(count, sizeof(smi->buf) - 1);
-       if (copy_from_user(buf, user_buf, len)) {
-               dev_err(smi->parent, "copy from user failed\n");
-               return -EFAULT;
-       }
-
-       buf[len] = '\0';
-       if (len > 0 && buf[len - 1] == '\n')
-               buf[len - 1] = '\0';
-
-
-       if (kstrtoul(buf, 16, &data)) {
-               dev_err(smi->parent, "Invalid reg value %s\n", buf);
-       } else {
-               err = rtl8366_smi_write_reg(smi, reg, data);
-               if (err) {
-                       dev_err(smi->parent,
-                               "writing reg 0x%04x val 0x%04lx failed\n",
-                               reg, data);
-               }
-       }
-
-       return count;
-}
-
-static ssize_t rtl8366_read_debugfs_mibs(struct file *file,
-                                        char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct rtl8366_smi *smi = file->private_data;
-       int i, j, len = 0;
-       char *buf = smi->buf;
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s",
-                       "Counter");
-
-       for (i = 0; i < smi->num_ports; i++) {
-               char port_buf[10];
-
-               snprintf(port_buf, sizeof(port_buf), "Port %d", i);
-               len += snprintf(buf + len, sizeof(smi->buf) - len, " %12s",
-                               port_buf);
-       }
-       len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
-
-       for (i = 0; i < smi->num_mib_counters; i++) {
-               len += snprintf(buf + len, sizeof(smi->buf) - len, "%-36s ",
-                               smi->mib_counters[i].name);
-               for (j = 0; j < smi->num_ports; j++) {
-                       unsigned long long counter = 0;
-
-                       if (!smi->ops->get_mib_counter(smi, i, j, &counter))
-                               len += snprintf(buf + len,
-                                               sizeof(smi->buf) - len,
-                                               "%12llu ", counter);
-                       else
-                               len += snprintf(buf + len,
-                                               sizeof(smi->buf) - len,
-                                               "%12s ", "error");
-               }
-               len += snprintf(buf + len, sizeof(smi->buf) - len, "\n");
-       }
-
-       return simple_read_from_buffer(user_buf, count, ppos, buf, len);
-}
-
-static const struct file_operations fops_rtl8366_regs = {
-       .read   = rtl8366_read_debugfs_reg,
-       .write  = rtl8366_write_debugfs_reg,
-       .open   = rtl8366_debugfs_open,
-       .owner  = THIS_MODULE
-};
-
-static const struct file_operations fops_rtl8366_vlan_mc = {
-       .read   = rtl8366_read_debugfs_vlan_mc,
-       .open   = rtl8366_debugfs_open,
-       .owner  = THIS_MODULE
-};
-
-static const struct file_operations fops_rtl8366_vlan_4k = {
-       .read   = rtl8366_read_debugfs_vlan_4k,
-       .open   = rtl8366_debugfs_open,
-       .owner  = THIS_MODULE
-};
-
-static const struct file_operations fops_rtl8366_pvid = {
-       .read   = rtl8366_read_debugfs_pvid,
-       .open   = rtl8366_debugfs_open,
-       .owner  = THIS_MODULE
-};
-
-static const struct file_operations fops_rtl8366_mibs = {
-       .read = rtl8366_read_debugfs_mibs,
-       .open = rtl8366_debugfs_open,
-       .owner = THIS_MODULE
-};
-
-static void rtl8366_debugfs_init(struct rtl8366_smi *smi)
-{
-       struct dentry *node;
-       struct dentry *root;
-
-       if (!smi->debugfs_root)
-               smi->debugfs_root = debugfs_create_dir(dev_name(smi->parent),
-                                                      NULL);
-
-       if (!smi->debugfs_root) {
-               dev_err(smi->parent, "Unable to create debugfs dir\n");
-               return;
-       }
-       root = smi->debugfs_root;
-
-       node = debugfs_create_x16("reg", S_IRUGO | S_IWUSR, root,
-                                 &smi->dbg_reg);
-       if (!node) {
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "reg");
-               return;
-       }
-
-       node = debugfs_create_file("val", S_IRUGO | S_IWUSR, root, smi,
-                                  &fops_rtl8366_regs);
-       if (!node) {
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "val");
-               return;
-       }
-
-       node = debugfs_create_file("vlan_mc", S_IRUSR, root, smi,
-                                  &fops_rtl8366_vlan_mc);
-       if (!node) {
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "vlan_mc");
-               return;
-       }
-
-       node = debugfs_create_u8("vlan_4k_page", S_IRUGO | S_IWUSR, root,
-                                 &smi->dbg_vlan_4k_page);
-       if (!node) {
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "vlan_4k_page");
-               return;
-       }
-
-       node = debugfs_create_file("vlan_4k", S_IRUSR, root, smi,
-                                  &fops_rtl8366_vlan_4k);
-       if (!node) {
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "vlan_4k");
-               return;
-       }
-
-       node = debugfs_create_file("pvid", S_IRUSR, root, smi,
-                                  &fops_rtl8366_pvid);
-       if (!node) {
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "pvid");
-               return;
-       }
-
-       node = debugfs_create_file("mibs", S_IRUSR, smi->debugfs_root, smi,
-                                  &fops_rtl8366_mibs);
-       if (!node)
-               dev_err(smi->parent, "Creating debugfs file '%s' failed\n",
-                       "mibs");
-}
-
-static void rtl8366_debugfs_remove(struct rtl8366_smi *smi)
-{
-       if (smi->debugfs_root) {
-               debugfs_remove_recursive(smi->debugfs_root);
-               smi->debugfs_root = NULL;
-       }
-}
-#else
-static inline void rtl8366_debugfs_init(struct rtl8366_smi *smi) {}
-static inline void rtl8366_debugfs_remove(struct rtl8366_smi *smi) {}
-#endif /* CONFIG_RTL8366_SMI_DEBUG_FS */
-
-static int rtl8366_smi_mii_init(struct rtl8366_smi *smi)
-{
-       int ret;
-
-#ifdef CONFIG_OF
-       struct device_node *np = NULL;
-
-       np = of_get_child_by_name(smi->parent->of_node, "mdio-bus");
-#endif
-
-       smi->mii_bus = mdiobus_alloc();
-       if (smi->mii_bus == NULL) {
-               ret = -ENOMEM;
-               goto err;
-       }
-
-       smi->mii_bus->priv = (void *) smi;
-       smi->mii_bus->name = dev_name(smi->parent);
-       smi->mii_bus->read = smi->ops->mii_read;
-       smi->mii_bus->write = smi->ops->mii_write;
-       snprintf(smi->mii_bus->id, MII_BUS_ID_SIZE, "%s",
-                dev_name(smi->parent));
-       smi->mii_bus->parent = smi->parent;
-       smi->mii_bus->phy_mask = ~(0x1f);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0)
-       {
-               int i;
-               smi->mii_bus->irq = smi->mii_irq;
-               for (i = 0; i < PHY_MAX_ADDR; i++)
-                       smi->mii_irq[i] = PHY_POLL;
-       }
-#endif
-
-#ifdef CONFIG_OF
-       if (np)
-               ret = of_mdiobus_register(smi->mii_bus, np);
-       else
-#endif
-               ret = mdiobus_register(smi->mii_bus);
-
-       if (ret)
-               goto err_free;
-
-       return 0;
-
- err_free:
-       mdiobus_free(smi->mii_bus);
- err:
-       return ret;
-}
-
-static void rtl8366_smi_mii_cleanup(struct rtl8366_smi *smi)
-{
-       mdiobus_unregister(smi->mii_bus);
-       mdiobus_free(smi->mii_bus);
-}
-
-int rtl8366_sw_reset_switch(struct switch_dev *dev)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int err;
-
-       err = rtl8366_reset(smi);
-       if (err)
-               return err;
-
-       err = smi->ops->setup(smi);
-       if (err)
-               return err;
-
-       err = rtl8366_reset_vlan(smi);
-       if (err)
-               return err;
-
-       err = rtl8366_enable_vlan(smi, 1);
-       if (err)
-               return err;
-
-       return rtl8366_enable_all_ports(smi, 1);
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_reset_switch);
-
-int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       return rtl8366_get_pvid(smi, port, val);
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_pvid);
-
-int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       return rtl8366_set_pvid(smi, port, val);
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_set_port_pvid);
-
-int rtl8366_sw_get_port_mib(struct switch_dev *dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int i, len = 0;
-       unsigned long long counter = 0;
-       char *buf = smi->buf;
-
-       if (val->port_vlan >= smi->num_ports)
-               return -EINVAL;
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                       "Port %d MIB counters\n",
-                       val->port_vlan);
-
-       for (i = 0; i < smi->num_mib_counters; ++i) {
-               len += snprintf(buf + len, sizeof(smi->buf) - len,
-                               "%-36s: ", smi->mib_counters[i].name);
-               if (!smi->ops->get_mib_counter(smi, i, val->port_vlan,
-                                              &counter))
-                       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                                       "%llu\n", counter);
-               else
-                       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                                       "%s\n", "error");
-       }
-
-       val->value.s = buf;
-       val->len = len;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_mib);
-
-int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
-                               struct switch_port_stats *stats,
-                               int txb_id, int rxb_id)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       unsigned long long counter = 0;
-       int ret;
-
-       if (port >= smi->num_ports)
-               return -EINVAL;
-
-       ret = smi->ops->get_mib_counter(smi, txb_id, port, &counter);
-       if (ret)
-               return ret;
-
-       stats->tx_bytes = counter;
-
-       ret = smi->ops->get_mib_counter(smi, rxb_id, port, &counter);
-       if (ret)
-               return ret;
-
-       stats->rx_bytes = counter;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_port_stats);
-
-int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
-                            const struct switch_attr *attr,
-                            struct switch_val *val)
-{
-       int i;
-       u32 len = 0;
-       struct rtl8366_vlan_4k vlan4k;
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       char *buf = smi->buf;
-       int err;
-
-       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
-               return -EINVAL;
-
-       memset(buf, '\0', sizeof(smi->buf));
-
-       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
-       if (err)
-               return err;
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                       "VLAN %d: Ports: '", vlan4k.vid);
-
-       for (i = 0; i < smi->num_ports; i++) {
-               if (!(vlan4k.member & (1 << i)))
-                       continue;
-
-               len += snprintf(buf + len, sizeof(smi->buf) - len, "%d%s", i,
-                               (vlan4k.untag & (1 << i)) ? "" : "t");
-       }
-
-       len += snprintf(buf + len, sizeof(smi->buf) - len,
-                       "', members=%04x, untag=%04x, fid=%u",
-                       vlan4k.member, vlan4k.untag, vlan4k.fid);
-
-       val->value.s = buf;
-       val->len = len;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_info);
-
-int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       struct switch_port *port;
-       struct rtl8366_vlan_4k vlan4k;
-       int i;
-
-       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
-               return -EINVAL;
-
-       smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
-
-       port = &val->value.ports[0];
-       val->len = 0;
-       for (i = 0; i < smi->num_ports; i++) {
-               if (!(vlan4k.member & BIT(i)))
-                       continue;
-
-               port->id = i;
-               port->flags = (vlan4k.untag & BIT(i)) ?
-                                       0 : BIT(SWITCH_PORT_FLAG_TAGGED);
-               val->len++;
-               port++;
-       }
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_ports);
-
-int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       struct switch_port *port;
-       u32 member = 0;
-       u32 untag = 0;
-       int err;
-       int i;
-
-       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
-               return -EINVAL;
-
-       port = &val->value.ports[0];
-       for (i = 0; i < val->len; i++, port++) {
-               int pvid = 0;
-               member |= BIT(port->id);
-
-               if (!(port->flags & BIT(SWITCH_PORT_FLAG_TAGGED)))
-                       untag |= BIT(port->id);
-
-               /*
-                * To ensure that we have a valid MC entry for this VLAN,
-                * initialize the port VLAN ID here.
-                */
-               err = rtl8366_get_pvid(smi, port->id, &pvid);
-               if (err < 0)
-                       return err;
-               if (pvid == 0) {
-                       err = rtl8366_set_pvid(smi, port->id, val->port_vlan);
-                       if (err < 0)
-                               return err;
-               }
-       }
-
-       return rtl8366_set_vlan(smi, val->port_vlan, member, untag, 0);
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_ports);
-
-int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val)
-{
-       struct rtl8366_vlan_4k vlan4k;
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int err;
-
-       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
-               return -EINVAL;
-
-       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
-       if (err)
-               return err;
-
-       val->value.i = vlan4k.fid;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_fid);
-
-int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val)
-{
-       struct rtl8366_vlan_4k vlan4k;
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int err;
-
-       if (!smi->ops->is_vlan_valid(smi, val->port_vlan))
-               return -EINVAL;
-
-       if (val->value.i < 0 || val->value.i > attr->max)
-               return -EINVAL;
-
-       err = smi->ops->get_vlan_4k(smi, val->port_vlan, &vlan4k);
-       if (err)
-               return err;
-
-       return rtl8366_set_vlan(smi, val->port_vlan,
-                               vlan4k.member,
-                               vlan4k.untag,
-                               val->value.i);
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_fid);
-
-int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (attr->ofs > 2)
-               return -EINVAL;
-
-       if (attr->ofs == 1)
-               val->value.i = smi->vlan_enabled;
-       else
-               val->value.i = smi->vlan4k_enabled;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_get_vlan_enable);
-
-int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int err;
-
-       if (attr->ofs > 2)
-               return -EINVAL;
-
-       if (attr->ofs == 1)
-               err = rtl8366_enable_vlan(smi, val->value.i);
-       else
-               err = rtl8366_enable_vlan4k(smi, val->value.i);
-
-       return err;
-}
-EXPORT_SYMBOL_GPL(rtl8366_sw_set_vlan_enable);
-
-struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent)
-{
-       struct rtl8366_smi *smi;
-
-       BUG_ON(!parent);
-
-       smi = kzalloc(sizeof(*smi), GFP_KERNEL);
-       if (!smi) {
-               dev_err(parent, "no memory for private data\n");
-               return NULL;
-       }
-
-       smi->parent = parent;
-       return smi;
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_alloc);
-
-static int __rtl8366_smi_init(struct rtl8366_smi *smi, const char *name)
-{
-       int err;
-
-       if (!smi->ext_mbus) {
-               err = gpio_request(smi->gpio_sda, name);
-               if (err) {
-                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
-                               smi->gpio_sda, err);
-                       goto err_out;
-               }
-
-               err = gpio_request(smi->gpio_sck, name);
-               if (err) {
-                       printk(KERN_ERR "rtl8366_smi: gpio_request failed for %u, err=%d\n",
-                               smi->gpio_sck, err);
-                       goto err_free_sda;
-               }
-       }
-
-       spin_lock_init(&smi->lock);
-
-       /* start the switch */
-       if (smi->hw_reset) {
-               smi->hw_reset(smi, false);
-               msleep(RTL8366_SMI_HW_START_DELAY);
-       }
-
-       return 0;
-
- err_free_sda:
-       gpio_free(smi->gpio_sda);
- err_out:
-       return err;
-}
-
-static void __rtl8366_smi_cleanup(struct rtl8366_smi *smi)
-{
-       if (smi->hw_reset)
-               smi->hw_reset(smi, true);
-
-       if (!smi->ext_mbus) {
-               gpio_free(smi->gpio_sck);
-               gpio_free(smi->gpio_sda);
-       }
-}
-
-enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata)
-{
-       static struct rtl8366_smi smi;
-       enum rtl8366_type type = RTL8366_TYPE_UNKNOWN;
-       u32 reg = 0;
-
-       memset(&smi, 0, sizeof(smi));
-       smi.gpio_sda = pdata->gpio_sda;
-       smi.gpio_sck = pdata->gpio_sck;
-       smi.clk_delay = 10;
-       smi.cmd_read  = 0xa9;
-       smi.cmd_write = 0xa8;
-
-       if (__rtl8366_smi_init(&smi, "rtl8366"))
-               goto out;
-
-       if (rtl8366_smi_read_reg(&smi, 0x5c, &reg))
-               goto cleanup;
-
-       switch(reg) {
-       case 0x6027:
-               printk("Found an RTL8366S switch\n");
-               type = RTL8366_TYPE_S;
-               break;
-       case 0x5937:
-               printk("Found an RTL8366RB switch\n");
-               type = RTL8366_TYPE_RB;
-               break;
-       default:
-               printk("Found an Unknown RTL8366 switch (id=0x%04x)\n", reg);
-               break;
-       }
-
-cleanup:
-       __rtl8366_smi_cleanup(&smi);
-out:
-       return type;
-}
-
-int rtl8366_smi_init(struct rtl8366_smi *smi)
-{
-       int err;
-
-       if (!smi->ops)
-               return -EINVAL;
-
-       err = __rtl8366_smi_init(smi, dev_name(smi->parent));
-       if (err)
-               goto err_out;
-
-       if (!smi->ext_mbus)
-               dev_info(smi->parent, "using GPIO pins %u (SDA) and %u (SCK)\n",
-                        smi->gpio_sda, smi->gpio_sck);
-       else
-               dev_info(smi->parent, "using MDIO bus '%s'\n", smi->ext_mbus->name);
-
-       err = smi->ops->detect(smi);
-       if (err) {
-               dev_err(smi->parent, "chip detection failed, err=%d\n", err);
-               goto err_free_sck;
-       }
-
-       err = rtl8366_reset(smi);
-       if (err)
-               goto err_free_sck;
-
-       err = smi->ops->setup(smi);
-       if (err) {
-               dev_err(smi->parent, "chip setup failed, err=%d\n", err);
-               goto err_free_sck;
-       }
-
-       err = rtl8366_init_vlan(smi);
-       if (err) {
-               dev_err(smi->parent, "VLAN initialization failed, err=%d\n",
-                       err);
-               goto err_free_sck;
-       }
-
-       err = rtl8366_enable_all_ports(smi, 1);
-       if (err)
-               goto err_free_sck;
-
-       err = rtl8366_smi_mii_init(smi);
-       if (err)
-               goto err_free_sck;
-
-       rtl8366_debugfs_init(smi);
-
-       return 0;
-
- err_free_sck:
-       __rtl8366_smi_cleanup(smi);
- err_out:
-       return err;
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_init);
-
-void rtl8366_smi_cleanup(struct rtl8366_smi *smi)
-{
-       rtl8366_debugfs_remove(smi);
-       rtl8366_smi_mii_cleanup(smi);
-       __rtl8366_smi_cleanup(smi);
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_cleanup);
-
-#ifdef CONFIG_OF
-static void rtl8366_smi_reset(struct rtl8366_smi *smi, bool active)
-{
-       if (active)
-               reset_control_assert(smi->reset);
-       else
-               reset_control_deassert(smi->reset);
-}
-
-int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
-{
-       int sck = of_get_named_gpio(pdev->dev.of_node, "gpio-sck", 0);
-       int sda = of_get_named_gpio(pdev->dev.of_node, "gpio-sda", 0);
-       struct device_node *np = pdev->dev.of_node;
-       struct device_node *mdio_node;
-
-       mdio_node = of_parse_phandle(np, "mii-bus", 0);
-       if (!mdio_node) {
-               dev_err(&pdev->dev, "cannot find mdio node phandle");
-               goto try_gpio;
-       }
-
-       smi->ext_mbus = of_mdio_find_bus(mdio_node);
-       if (!smi->ext_mbus) {
-               dev_err(&pdev->dev,
-                       "cannot find mdio bus from bus handle");
-               goto try_gpio;
-       }
-
-       return 0;
-
-try_gpio:
-       if (!gpio_is_valid(sck) || !gpio_is_valid(sda)) {
-               dev_err(&pdev->dev, "gpios missing in devictree\n");
-               return -EINVAL;
-       }
-
-       smi->gpio_sda = sda;
-       smi->gpio_sck = sck;
-       smi->reset = devm_reset_control_get(&pdev->dev, "switch");
-       if (!IS_ERR(smi->reset))
-               smi->hw_reset = rtl8366_smi_reset;
-
-       return 0;
-}
-#else
-static inline int rtl8366_smi_probe_of(struct platform_device *pdev, struct rtl8366_smi *smi)
-{
-       return -ENODEV;
-}
-#endif
-
-int rtl8366_smi_probe_plat(struct platform_device *pdev, struct rtl8366_smi *smi)
-{
-       struct rtl8366_platform_data *pdata = pdev->dev.platform_data;
-
-       if (!pdev->dev.platform_data) {
-               dev_err(&pdev->dev, "no platform data specified\n");
-               return -EINVAL;
-       }
-
-       smi->gpio_sda = pdata->gpio_sda;
-       smi->gpio_sck = pdata->gpio_sck;
-       smi->hw_reset = pdata->hw_reset;
-
-       return 0;
-}
-
-
-struct rtl8366_smi *rtl8366_smi_probe(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi;
-       int err;
-
-       smi = rtl8366_smi_alloc(&pdev->dev);
-       if (!smi)
-               return NULL;
-
-       if (pdev->dev.of_node)
-               err = rtl8366_smi_probe_of(pdev, smi);
-       else
-               err = rtl8366_smi_probe_plat(pdev, smi);
-
-       if (err)
-               goto free_smi;
-
-       return smi;
-
-free_smi:
-       kfree(smi);
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(rtl8366_smi_probe);
-
-MODULE_DESCRIPTION("Realtek RTL8366 SMI interface driver");
-MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
-MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h b/target/linux/generic/files/drivers/net/phy/rtl8366_smi.h
deleted file mode 100644 (file)
index d1d988a..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Realtek RTL8366 SMI interface driver defines
- *
- * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#ifndef _RTL8366_SMI_H
-#define _RTL8366_SMI_H
-
-#include <linux/phy.h>
-#include <linux/switch.h>
-#include <linux/platform_device.h>
-#include <linux/reset.h>
-
-struct rtl8366_smi_ops;
-struct rtl8366_vlan_ops;
-struct mii_bus;
-struct dentry;
-struct inode;
-struct file;
-
-struct rtl8366_mib_counter {
-       unsigned        base;
-       unsigned        offset;
-       unsigned        length;
-       const char      *name;
-};
-
-struct rtl8366_smi {
-       struct device           *parent;
-       unsigned int            gpio_sda;
-       unsigned int            gpio_sck;
-       void                    (*hw_reset)(struct rtl8366_smi *smi, bool active);
-       unsigned int            clk_delay;      /* ns */
-       u8                      cmd_read;
-       u8                      cmd_write;
-       spinlock_t              lock;
-       struct mii_bus          *mii_bus;
-       int                     mii_irq[PHY_MAX_ADDR];
-       struct switch_dev       sw_dev;
-
-       unsigned int            cpu_port;
-       unsigned int            num_ports;
-       unsigned int            num_vlan_mc;
-       unsigned int            num_mib_counters;
-       struct rtl8366_mib_counter *mib_counters;
-
-       struct rtl8366_smi_ops  *ops;
-
-       int                     vlan_enabled;
-       int                     vlan4k_enabled;
-
-       char                    buf[4096];
-
-       struct reset_control    *reset;
-
-#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
-       struct dentry           *debugfs_root;
-       u16                     dbg_reg;
-       u8                      dbg_vlan_4k_page;
-#endif
-       struct mii_bus          *ext_mbus;
-};
-
-struct rtl8366_vlan_mc {
-       u16     vid;
-       u16     untag;
-       u16     member;
-       u8      fid;
-       u8      priority;
-};
-
-struct rtl8366_vlan_4k {
-       u16     vid;
-       u16     untag;
-       u16     member;
-       u8      fid;
-};
-
-struct rtl8366_smi_ops {
-       int     (*detect)(struct rtl8366_smi *smi);
-       int     (*reset_chip)(struct rtl8366_smi *smi);
-       int     (*setup)(struct rtl8366_smi *smi);
-
-       int     (*mii_read)(struct mii_bus *bus, int addr, int reg);
-       int     (*mii_write)(struct mii_bus *bus, int addr, int reg, u16 val);
-
-       int     (*get_vlan_mc)(struct rtl8366_smi *smi, u32 index,
-                              struct rtl8366_vlan_mc *vlanmc);
-       int     (*set_vlan_mc)(struct rtl8366_smi *smi, u32 index,
-                              const struct rtl8366_vlan_mc *vlanmc);
-       int     (*get_vlan_4k)(struct rtl8366_smi *smi, u32 vid,
-                              struct rtl8366_vlan_4k *vlan4k);
-       int     (*set_vlan_4k)(struct rtl8366_smi *smi,
-                              const struct rtl8366_vlan_4k *vlan4k);
-       int     (*get_mc_index)(struct rtl8366_smi *smi, int port, int *val);
-       int     (*set_mc_index)(struct rtl8366_smi *smi, int port, int index);
-       int     (*get_mib_counter)(struct rtl8366_smi *smi, int counter,
-                                  int port, unsigned long long *val);
-       int     (*is_vlan_valid)(struct rtl8366_smi *smi, unsigned vlan);
-       int     (*enable_vlan)(struct rtl8366_smi *smi, int enable);
-       int     (*enable_vlan4k)(struct rtl8366_smi *smi, int enable);
-       int     (*enable_port)(struct rtl8366_smi *smi, int port, int enable);
-};
-
-struct rtl8366_smi *rtl8366_smi_alloc(struct device *parent);
-int rtl8366_smi_init(struct rtl8366_smi *smi);
-void rtl8366_smi_cleanup(struct rtl8366_smi *smi);
-int rtl8366_smi_write_reg(struct rtl8366_smi *smi, u32 addr, u32 data);
-int rtl8366_smi_write_reg_noack(struct rtl8366_smi *smi, u32 addr, u32 data);
-int rtl8366_smi_read_reg(struct rtl8366_smi *smi, u32 addr, u32 *data);
-int rtl8366_smi_rmwr(struct rtl8366_smi *smi, u32 addr, u32 mask, u32 data);
-
-int rtl8366_reset_vlan(struct rtl8366_smi *smi);
-int rtl8366_enable_vlan(struct rtl8366_smi *smi, int enable);
-int rtl8366_enable_all_ports(struct rtl8366_smi *smi, int enable);
-
-#ifdef CONFIG_RTL8366_SMI_DEBUG_FS
-int rtl8366_debugfs_open(struct inode *inode, struct file *file);
-#endif
-
-static inline struct rtl8366_smi *sw_to_rtl8366_smi(struct switch_dev *sw)
-{
-       return container_of(sw, struct rtl8366_smi, sw_dev);
-}
-
-int rtl8366_sw_reset_switch(struct switch_dev *dev);
-int rtl8366_sw_get_port_pvid(struct switch_dev *dev, int port, int *val);
-int rtl8366_sw_set_port_pvid(struct switch_dev *dev, int port, int val);
-int rtl8366_sw_get_port_mib(struct switch_dev *dev,
-                           const struct switch_attr *attr,
-                           struct switch_val *val);
-int rtl8366_sw_get_vlan_info(struct switch_dev *dev,
-                            const struct switch_attr *attr,
-                            struct switch_val *val);
-int rtl8366_sw_get_vlan_fid(struct switch_dev *dev,
-                            const struct switch_attr *attr,
-                            struct switch_val *val);
-int rtl8366_sw_set_vlan_fid(struct switch_dev *dev,
-                            const struct switch_attr *attr,
-                            struct switch_val *val);
-int rtl8366_sw_get_vlan_ports(struct switch_dev *dev, struct switch_val *val);
-int rtl8366_sw_set_vlan_ports(struct switch_dev *dev, struct switch_val *val);
-int rtl8366_sw_get_vlan_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val);
-int rtl8366_sw_set_vlan_enable(struct switch_dev *dev,
-                              const struct switch_attr *attr,
-                              struct switch_val *val);
-int rtl8366_sw_get_port_stats(struct switch_dev *dev, int port,
-                               struct switch_port_stats *stats,
-                               int txb_id, int rxb_id);
-
-struct rtl8366_smi* rtl8366_smi_probe(struct platform_device *pdev);
-
-#endif /*  _RTL8366_SMI_H */
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366rb.c b/target/linux/generic/files/drivers/net/phy/rtl8366rb.c
deleted file mode 100644 (file)
index dc394c0..0000000
+++ /dev/null
@@ -1,1532 +0,0 @@
-/*
- * Platform driver for the Realtek RTL8366RB ethernet switch
- *
- * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
- * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
- * Copyright (C) 2010 Roman Yeryomin <roman@advem.lv>
- * Copyright (C) 2011 Colin Leitner <colin.leitner@googlemail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/rtl8366.h>
-
-#include "rtl8366_smi.h"
-
-#define RTL8366RB_DRIVER_DESC  "Realtek RTL8366RB ethernet switch driver"
-#define RTL8366RB_DRIVER_VER   "0.2.4"
-
-#define RTL8366RB_PHY_NO_MAX   4
-#define RTL8366RB_PHY_PAGE_MAX 7
-#define RTL8366RB_PHY_ADDR_MAX 31
-
-/* Switch Global Configuration register */
-#define RTL8366RB_SGCR                         0x0000
-#define RTL8366RB_SGCR_EN_BC_STORM_CTRL                BIT(0)
-#define RTL8366RB_SGCR_MAX_LENGTH(_x)          (_x << 4)
-#define RTL8366RB_SGCR_MAX_LENGTH_MASK         RTL8366RB_SGCR_MAX_LENGTH(0x3)
-#define RTL8366RB_SGCR_MAX_LENGTH_1522         RTL8366RB_SGCR_MAX_LENGTH(0x0)
-#define RTL8366RB_SGCR_MAX_LENGTH_1536         RTL8366RB_SGCR_MAX_LENGTH(0x1)
-#define RTL8366RB_SGCR_MAX_LENGTH_1552         RTL8366RB_SGCR_MAX_LENGTH(0x2)
-#define RTL8366RB_SGCR_MAX_LENGTH_9216         RTL8366RB_SGCR_MAX_LENGTH(0x3)
-#define RTL8366RB_SGCR_EN_VLAN                 BIT(13)
-#define RTL8366RB_SGCR_EN_VLAN_4KTB            BIT(14)
-
-/* Port Enable Control register */
-#define RTL8366RB_PECR                         0x0001
-
-/* Port Mirror Control Register */
-#define RTL8366RB_PMCR                         0x0007
-#define RTL8366RB_PMCR_SOURCE_PORT(_x)         (_x)
-#define RTL8366RB_PMCR_SOURCE_PORT_MASK                0x000f
-#define RTL8366RB_PMCR_MONITOR_PORT(_x)                ((_x) << 4)
-#define RTL8366RB_PMCR_MONITOR_PORT_MASK       0x00f0
-#define RTL8366RB_PMCR_MIRROR_RX               BIT(8)
-#define RTL8366RB_PMCR_MIRROR_TX               BIT(9)
-#define RTL8366RB_PMCR_MIRROR_SPC              BIT(10)
-#define RTL8366RB_PMCR_MIRROR_ISO              BIT(11)
-
-/* Switch Security Control registers */
-#define RTL8366RB_SSCR0                                0x0002
-#define RTL8366RB_SSCR1                                0x0003
-#define RTL8366RB_SSCR2                                0x0004
-#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA                BIT(0)
-
-#define RTL8366RB_RESET_CTRL_REG               0x0100
-#define RTL8366RB_CHIP_CTRL_RESET_HW           1
-#define RTL8366RB_CHIP_CTRL_RESET_SW           (1 << 1)
-
-#define RTL8366RB_CHIP_VERSION_CTRL_REG                0x050A
-#define RTL8366RB_CHIP_VERSION_MASK            0xf
-#define RTL8366RB_CHIP_ID_REG                  0x0509
-#define RTL8366RB_CHIP_ID_8366                 0x5937
-
-/* PHY registers control */
-#define RTL8366RB_PHY_ACCESS_CTRL_REG          0x8000
-#define RTL8366RB_PHY_ACCESS_DATA_REG          0x8002
-
-#define RTL8366RB_PHY_CTRL_READ                        1
-#define RTL8366RB_PHY_CTRL_WRITE               0
-
-#define RTL8366RB_PHY_REG_MASK                 0x1f
-#define RTL8366RB_PHY_PAGE_OFFSET              5
-#define RTL8366RB_PHY_PAGE_MASK                        (0xf << 5)
-#define RTL8366RB_PHY_NO_OFFSET                        9
-#define RTL8366RB_PHY_NO_MASK                  (0x1f << 9)
-
-#define RTL8366RB_VLAN_INGRESS_CTRL2_REG       0x037f
-
-/* LED control registers */
-#define RTL8366RB_LED_BLINKRATE_REG            0x0430
-#define RTL8366RB_LED_BLINKRATE_BIT            0
-#define RTL8366RB_LED_BLINKRATE_MASK           0x0007
-
-#define RTL8366RB_LED_CTRL_REG                 0x0431
-#define RTL8366RB_LED_0_1_CTRL_REG             0x0432
-#define RTL8366RB_LED_2_3_CTRL_REG             0x0433
-
-#define RTL8366RB_MIB_COUNT                    33
-#define RTL8366RB_GLOBAL_MIB_COUNT             1
-#define RTL8366RB_MIB_COUNTER_PORT_OFFSET      0x0050
-#define RTL8366RB_MIB_COUNTER_BASE             0x1000
-#define RTL8366RB_MIB_CTRL_REG                 0x13F0
-#define RTL8366RB_MIB_CTRL_USER_MASK           0x0FFC
-#define RTL8366RB_MIB_CTRL_BUSY_MASK           BIT(0)
-#define RTL8366RB_MIB_CTRL_RESET_MASK          BIT(1)
-#define RTL8366RB_MIB_CTRL_PORT_RESET(_p)      BIT(2 + (_p))
-#define RTL8366RB_MIB_CTRL_GLOBAL_RESET                BIT(11)
-
-#define RTL8366RB_PORT_VLAN_CTRL_BASE          0x0063
-#define RTL8366RB_PORT_VLAN_CTRL_REG(_p)  \
-               (RTL8366RB_PORT_VLAN_CTRL_BASE + (_p) / 4)
-#define RTL8366RB_PORT_VLAN_CTRL_MASK          0xf
-#define RTL8366RB_PORT_VLAN_CTRL_SHIFT(_p)     (4 * ((_p) % 4))
-
-
-#define RTL8366RB_VLAN_TABLE_READ_BASE         0x018C
-#define RTL8366RB_VLAN_TABLE_WRITE_BASE                0x0185
-
-
-#define RTL8366RB_TABLE_ACCESS_CTRL_REG                0x0180
-#define RTL8366RB_TABLE_VLAN_READ_CTRL         0x0E01
-#define RTL8366RB_TABLE_VLAN_WRITE_CTRL                0x0F01
-
-#define RTL8366RB_VLAN_MC_BASE(_x)             (0x0020 + (_x) * 3)
-
-
-#define RTL8366RB_PORT_LINK_STATUS_BASE                0x0014
-#define RTL8366RB_PORT_STATUS_SPEED_MASK       0x0003
-#define RTL8366RB_PORT_STATUS_DUPLEX_MASK      0x0004
-#define RTL8366RB_PORT_STATUS_LINK_MASK                0x0010
-#define RTL8366RB_PORT_STATUS_TXPAUSE_MASK     0x0020
-#define RTL8366RB_PORT_STATUS_RXPAUSE_MASK     0x0040
-#define RTL8366RB_PORT_STATUS_AN_MASK          0x0080
-
-
-#define RTL8366RB_PORT_NUM_CPU         5
-#define RTL8366RB_NUM_PORTS            6
-#define RTL8366RB_NUM_VLANS            16
-#define RTL8366RB_NUM_LEDGROUPS                4
-#define RTL8366RB_NUM_VIDS             4096
-#define RTL8366RB_PRIORITYMAX          7
-#define RTL8366RB_FIDMAX               7
-
-
-#define RTL8366RB_PORT_1               (1 << 0) /* In userspace port 0 */
-#define RTL8366RB_PORT_2               (1 << 1) /* In userspace port 1 */
-#define RTL8366RB_PORT_3               (1 << 2) /* In userspace port 2 */
-#define RTL8366RB_PORT_4               (1 << 3) /* In userspace port 3 */
-#define RTL8366RB_PORT_5               (1 << 4) /* In userspace port 4 */
-
-#define RTL8366RB_PORT_CPU             (1 << 5) /* CPU port */
-
-#define RTL8366RB_PORT_ALL             (RTL8366RB_PORT_1 |     \
-                                        RTL8366RB_PORT_2 |     \
-                                        RTL8366RB_PORT_3 |     \
-                                        RTL8366RB_PORT_4 |     \
-                                        RTL8366RB_PORT_5 |     \
-                                        RTL8366RB_PORT_CPU)
-
-#define RTL8366RB_PORT_ALL_BUT_CPU     (RTL8366RB_PORT_1 |     \
-                                        RTL8366RB_PORT_2 |     \
-                                        RTL8366RB_PORT_3 |     \
-                                        RTL8366RB_PORT_4 |     \
-                                        RTL8366RB_PORT_5)
-
-#define RTL8366RB_PORT_ALL_EXTERNAL    (RTL8366RB_PORT_1 |     \
-                                        RTL8366RB_PORT_2 |     \
-                                        RTL8366RB_PORT_3 |     \
-                                        RTL8366RB_PORT_4)
-
-#define RTL8366RB_PORT_ALL_INTERNAL     RTL8366RB_PORT_CPU
-
-#define RTL8366RB_VLAN_VID_MASK                0xfff
-#define RTL8366RB_VLAN_PRIORITY_SHIFT  12
-#define RTL8366RB_VLAN_PRIORITY_MASK   0x7
-#define RTL8366RB_VLAN_UNTAG_SHIFT     8
-#define RTL8366RB_VLAN_UNTAG_MASK      0xff
-#define RTL8366RB_VLAN_MEMBER_MASK     0xff
-#define RTL8366RB_VLAN_FID_MASK                0x7
-
-
-/* Port ingress bandwidth control */
-#define RTL8366RB_IB_BASE              0x0200
-#define RTL8366RB_IB_REG(pnum)         (RTL8366RB_IB_BASE + pnum)
-#define RTL8366RB_IB_BDTH_MASK         0x3fff
-#define RTL8366RB_IB_PREIFG_OFFSET     14
-#define RTL8366RB_IB_PREIFG_MASK       (1 << RTL8366RB_IB_PREIFG_OFFSET)
-
-/* Port egress bandwidth control */
-#define RTL8366RB_EB_BASE              0x02d1
-#define RTL8366RB_EB_REG(pnum)         (RTL8366RB_EB_BASE + pnum)
-#define RTL8366RB_EB_BDTH_MASK         0x3fff
-#define RTL8366RB_EB_PREIFG_REG        0x02f8
-#define RTL8366RB_EB_PREIFG_OFFSET     9
-#define RTL8366RB_EB_PREIFG_MASK       (1 << RTL8366RB_EB_PREIFG_OFFSET)
-
-#define RTL8366RB_BDTH_SW_MAX          1048512
-#define RTL8366RB_BDTH_UNIT            64
-#define RTL8366RB_BDTH_REG_DEFAULT     16383
-
-/* QOS */
-#define RTL8366RB_QOS_BIT              15
-#define RTL8366RB_QOS_MASK             (1 << RTL8366RB_QOS_BIT)
-/* Include/Exclude Preamble and IFG (20 bytes). 0:Exclude, 1:Include. */
-#define RTL8366RB_QOS_DEFAULT_PREIFG   1
-
-
-#define RTL8366RB_MIB_RXB_ID           0       /* IfInOctets */
-#define RTL8366RB_MIB_TXB_ID           20      /* IfOutOctets */
-
-static struct rtl8366_mib_counter rtl8366rb_mib_counters[] = {
-       { 0,  0, 4, "IfInOctets"                                },
-       { 0,  4, 4, "EtherStatsOctets"                          },
-       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
-       { 0, 10, 2, "EtherFragments"                            },
-       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
-       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
-       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
-       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
-       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
-       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
-       { 0, 24, 2, "EtherOversizeStats"                        },
-       { 0, 26, 2, "EtherStatsJabbers"                         },
-       { 0, 28, 2, "IfInUcastPkts"                             },
-       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
-       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
-       { 0, 34, 2, "EtherStatsDropEvents"                      },
-       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
-       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
-       { 0, 40, 2, "Dot3InPauseFrames"                         },
-       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
-       { 0, 44, 4, "IfOutOctets"                               },
-       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
-       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
-       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
-       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
-       { 0, 56, 2, "EtherStatsCollisions"                      },
-       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
-       { 0, 60, 2, "Dot3OutPauseFrames"                        },
-       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
-       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
-       { 0, 66, 2, "IfOutUcastPkts"                            },
-       { 0, 68, 2, "IfOutMulticastPkts"                        },
-       { 0, 70, 2, "IfOutBroadcastPkts"                        },
-};
-
-#define REG_WR(_smi, _reg, _val)                                       \
-       do {                                                            \
-               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-#define REG_RMW(_smi, _reg, _mask, _val)                               \
-       do {                                                            \
-               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-static int rtl8366rb_reset_chip(struct rtl8366_smi *smi)
-{
-       int timeout = 10;
-       u32 data;
-
-       rtl8366_smi_write_reg_noack(smi, RTL8366RB_RESET_CTRL_REG,
-                                   RTL8366RB_CHIP_CTRL_RESET_HW);
-       do {
-               msleep(1);
-               if (rtl8366_smi_read_reg(smi, RTL8366RB_RESET_CTRL_REG, &data))
-                       return -EIO;
-
-               if (!(data & RTL8366RB_CHIP_CTRL_RESET_HW))
-                       break;
-       } while (--timeout);
-
-       if (!timeout) {
-               printk("Timeout waiting for the switch to reset\n");
-               return -EIO;
-       }
-
-       return 0;
-}
-
-static int rtl8366rb_setup(struct rtl8366_smi *smi)
-{
-       int err;
-#ifdef CONFIG_OF
-       unsigned i;
-       struct device_node *np;
-       unsigned num_initvals;
-       const __be32 *paddr;
-
-       np = smi->parent->of_node;
-
-       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
-       if (paddr) {
-               dev_info(smi->parent, "applying initvals from DTS\n");
-
-               if (num_initvals < (2 * sizeof(*paddr)))
-                       return -EINVAL;
-
-               num_initvals /= sizeof(*paddr);
-
-               for (i = 0; i < num_initvals - 1; i += 2) {
-                       u32 reg = be32_to_cpup(paddr + i);
-                       u32 val = be32_to_cpup(paddr + i + 1);
-
-                       REG_WR(smi, reg, val);
-               }
-       }
-#endif
-
-       /* set maximum packet length to 1536 bytes */
-       REG_RMW(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_MAX_LENGTH_MASK,
-               RTL8366RB_SGCR_MAX_LENGTH_1536);
-
-       /* enable learning for all ports */
-       REG_WR(smi, RTL8366RB_SSCR0, 0);
-
-       /* enable auto ageing for all ports */
-       REG_WR(smi, RTL8366RB_SSCR1, 0);
-
-       /*
-        * discard VLAN tagged packets if the port is not a member of
-        * the VLAN with which the packets is associated.
-        */
-       REG_WR(smi, RTL8366RB_VLAN_INGRESS_CTRL2_REG, RTL8366RB_PORT_ALL);
-
-       /* don't drop packets whose DA has not been learned */
-       REG_RMW(smi, RTL8366RB_SSCR2, RTL8366RB_SSCR2_DROP_UNKNOWN_DA, 0);
-
-       return 0;
-}
-
-static int rtl8366rb_read_phy_reg(struct rtl8366_smi *smi,
-                                u32 phy_no, u32 page, u32 addr, u32 *data)
-{
-       u32 reg;
-       int ret;
-
-       if (phy_no > RTL8366RB_PHY_NO_MAX)
-               return -EINVAL;
-
-       if (page > RTL8366RB_PHY_PAGE_MAX)
-               return -EINVAL;
-
-       if (addr > RTL8366RB_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
-                                   RTL8366RB_PHY_CTRL_READ);
-       if (ret)
-               return ret;
-
-       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
-             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
-             (addr & RTL8366RB_PHY_REG_MASK);
-
-       ret = rtl8366_smi_write_reg(smi, reg, 0);
-       if (ret)
-               return ret;
-
-       ret = rtl8366_smi_read_reg(smi, RTL8366RB_PHY_ACCESS_DATA_REG, data);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int rtl8366rb_write_phy_reg(struct rtl8366_smi *smi,
-                                 u32 phy_no, u32 page, u32 addr, u32 data)
-{
-       u32 reg;
-       int ret;
-
-       if (phy_no > RTL8366RB_PHY_NO_MAX)
-               return -EINVAL;
-
-       if (page > RTL8366RB_PHY_PAGE_MAX)
-               return -EINVAL;
-
-       if (addr > RTL8366RB_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       ret = rtl8366_smi_write_reg(smi, RTL8366RB_PHY_ACCESS_CTRL_REG,
-                                   RTL8366RB_PHY_CTRL_WRITE);
-       if (ret)
-               return ret;
-
-       reg = 0x8000 | (1 << (phy_no + RTL8366RB_PHY_NO_OFFSET)) |
-             ((page << RTL8366RB_PHY_PAGE_OFFSET) & RTL8366RB_PHY_PAGE_MASK) |
-             (addr & RTL8366RB_PHY_REG_MASK);
-
-       ret = rtl8366_smi_write_reg(smi, reg, data);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int rtl8366rb_get_mib_counter(struct rtl8366_smi *smi, int counter,
-                                    int port, unsigned long long *val)
-{
-       int i;
-       int err;
-       u32 addr, data;
-       u64 mibvalue;
-
-       if (port > RTL8366RB_NUM_PORTS || counter >= RTL8366RB_MIB_COUNT)
-               return -EINVAL;
-
-       addr = RTL8366RB_MIB_COUNTER_BASE +
-              RTL8366RB_MIB_COUNTER_PORT_OFFSET * (port) +
-              rtl8366rb_mib_counters[counter].offset;
-
-       /*
-        * Writing access counter address first
-        * then ASIC will prepare 64bits counter wait for being retrived
-        */
-       data = 0; /* writing data will be discard by ASIC */
-       err = rtl8366_smi_write_reg(smi, addr, data);
-       if (err)
-               return err;
-
-       /* read MIB control register */
-       err =  rtl8366_smi_read_reg(smi, RTL8366RB_MIB_CTRL_REG, &data);
-       if (err)
-               return err;
-
-       if (data & RTL8366RB_MIB_CTRL_BUSY_MASK)
-               return -EBUSY;
-
-       if (data & RTL8366RB_MIB_CTRL_RESET_MASK)
-               return -EIO;
-
-       mibvalue = 0;
-       for (i = rtl8366rb_mib_counters[counter].length; i > 0; i--) {
-               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
-               if (err)
-                       return err;
-
-               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
-       }
-
-       *val = mibvalue;
-       return 0;
-}
-
-static int rtl8366rb_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
-                                struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[3];
-       int err;
-       int i;
-
-       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
-
-       if (vid >= RTL8366RB_NUM_VIDS)
-               return -EINVAL;
-
-       /* write VID */
-       err = rtl8366_smi_write_reg(smi, RTL8366RB_VLAN_TABLE_WRITE_BASE,
-                                   vid & RTL8366RB_VLAN_VID_MASK);
-       if (err)
-               return err;
-
-       /* write table access control word */
-       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
-                                   RTL8366RB_TABLE_VLAN_READ_CTRL);
-       if (err)
-               return err;
-
-       for (i = 0; i < 3; i++) {
-               err = rtl8366_smi_read_reg(smi,
-                                          RTL8366RB_VLAN_TABLE_READ_BASE + i,
-                                          &data[i]);
-               if (err)
-                       return err;
-       }
-
-       vlan4k->vid = vid;
-       vlan4k->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
-                       RTL8366RB_VLAN_UNTAG_MASK;
-       vlan4k->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
-       vlan4k->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
-
-       return 0;
-}
-
-static int rtl8366rb_set_vlan_4k(struct rtl8366_smi *smi,
-                                const struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[3];
-       int err;
-       int i;
-
-       if (vlan4k->vid >= RTL8366RB_NUM_VIDS ||
-           vlan4k->member > RTL8366RB_VLAN_MEMBER_MASK ||
-           vlan4k->untag > RTL8366RB_VLAN_UNTAG_MASK ||
-           vlan4k->fid > RTL8366RB_FIDMAX)
-               return -EINVAL;
-
-       data[0] = vlan4k->vid & RTL8366RB_VLAN_VID_MASK;
-       data[1] = (vlan4k->member & RTL8366RB_VLAN_MEMBER_MASK) |
-                 ((vlan4k->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
-                       RTL8366RB_VLAN_UNTAG_SHIFT);
-       data[2] = vlan4k->fid & RTL8366RB_VLAN_FID_MASK;
-
-       for (i = 0; i < 3; i++) {
-               err = rtl8366_smi_write_reg(smi,
-                                           RTL8366RB_VLAN_TABLE_WRITE_BASE + i,
-                                           data[i]);
-               if (err)
-                       return err;
-       }
-
-       /* write table access control word */
-       err = rtl8366_smi_write_reg(smi, RTL8366RB_TABLE_ACCESS_CTRL_REG,
-                                   RTL8366RB_TABLE_VLAN_WRITE_CTRL);
-
-       return err;
-}
-
-static int rtl8366rb_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                                struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[3];
-       int err;
-       int i;
-
-       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
-
-       if (index >= RTL8366RB_NUM_VLANS)
-               return -EINVAL;
-
-       for (i = 0; i < 3; i++) {
-               err = rtl8366_smi_read_reg(smi,
-                                          RTL8366RB_VLAN_MC_BASE(index) + i,
-                                          &data[i]);
-               if (err)
-                       return err;
-       }
-
-       vlanmc->vid = data[0] & RTL8366RB_VLAN_VID_MASK;
-       vlanmc->priority = (data[0] >> RTL8366RB_VLAN_PRIORITY_SHIFT) &
-                          RTL8366RB_VLAN_PRIORITY_MASK;
-       vlanmc->untag = (data[1] >> RTL8366RB_VLAN_UNTAG_SHIFT) &
-                       RTL8366RB_VLAN_UNTAG_MASK;
-       vlanmc->member = data[1] & RTL8366RB_VLAN_MEMBER_MASK;
-       vlanmc->fid = data[2] & RTL8366RB_VLAN_FID_MASK;
-
-       return 0;
-}
-
-static int rtl8366rb_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                                const struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[3];
-       int err;
-       int i;
-
-       if (index >= RTL8366RB_NUM_VLANS ||
-           vlanmc->vid >= RTL8366RB_NUM_VIDS ||
-           vlanmc->priority > RTL8366RB_PRIORITYMAX ||
-           vlanmc->member > RTL8366RB_VLAN_MEMBER_MASK ||
-           vlanmc->untag > RTL8366RB_VLAN_UNTAG_MASK ||
-           vlanmc->fid > RTL8366RB_FIDMAX)
-               return -EINVAL;
-
-       data[0] = (vlanmc->vid & RTL8366RB_VLAN_VID_MASK) |
-                 ((vlanmc->priority & RTL8366RB_VLAN_PRIORITY_MASK) <<
-                       RTL8366RB_VLAN_PRIORITY_SHIFT);
-       data[1] = (vlanmc->member & RTL8366RB_VLAN_MEMBER_MASK) |
-                 ((vlanmc->untag & RTL8366RB_VLAN_UNTAG_MASK) <<
-                       RTL8366RB_VLAN_UNTAG_SHIFT);
-       data[2] = vlanmc->fid & RTL8366RB_VLAN_FID_MASK;
-
-       for (i = 0; i < 3; i++) {
-               err = rtl8366_smi_write_reg(smi,
-                                           RTL8366RB_VLAN_MC_BASE(index) + i,
-                                           data[i]);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int rtl8366rb_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
-{
-       u32 data;
-       int err;
-
-       if (port >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       err = rtl8366_smi_read_reg(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
-                                  &data);
-       if (err)
-               return err;
-
-       *val = (data >> RTL8366RB_PORT_VLAN_CTRL_SHIFT(port)) &
-              RTL8366RB_PORT_VLAN_CTRL_MASK;
-
-       return 0;
-
-}
-
-static int rtl8366rb_set_mc_index(struct rtl8366_smi *smi, int port, int index)
-{
-       if (port >= RTL8366RB_NUM_PORTS || index >= RTL8366RB_NUM_VLANS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PORT_VLAN_CTRL_REG(port),
-                               RTL8366RB_PORT_VLAN_CTRL_MASK <<
-                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port),
-                               (index & RTL8366RB_PORT_VLAN_CTRL_MASK) <<
-                                       RTL8366RB_PORT_VLAN_CTRL_SHIFT(port));
-}
-
-static int rtl8366rb_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
-{
-       unsigned max = RTL8366RB_NUM_VLANS;
-
-       if (smi->vlan4k_enabled)
-               max = RTL8366RB_NUM_VIDS - 1;
-
-       if (vlan == 0 || vlan >= max)
-               return 0;
-
-       return 1;
-}
-
-static int rtl8366rb_enable_vlan(struct rtl8366_smi *smi, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_SGCR_EN_VLAN,
-                               (enable) ? RTL8366RB_SGCR_EN_VLAN : 0);
-}
-
-static int rtl8366rb_enable_vlan4k(struct rtl8366_smi *smi, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR,
-                               RTL8366RB_SGCR_EN_VLAN_4KTB,
-                               (enable) ? RTL8366RB_SGCR_EN_VLAN_4KTB : 0);
-}
-
-static int rtl8366rb_enable_port(struct rtl8366_smi *smi, int port, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, (1 << port),
-                               (enable) ? 0 : (1 << port));
-}
-
-static int rtl8366rb_sw_reset_mibs(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
-                               RTL8366RB_MIB_CTRL_GLOBAL_RESET);
-}
-
-static int rtl8366rb_sw_get_blinkrate(struct switch_dev *dev,
-                                    const struct switch_attr *attr,
-                                    struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_LED_BLINKRATE_REG, &data);
-
-       val->value.i = (data & (RTL8366RB_LED_BLINKRATE_MASK));
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_blinkrate(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (val->value.i >= 6)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_LED_BLINKRATE_REG,
-                               RTL8366RB_LED_BLINKRATE_MASK,
-                               val->value.i);
-}
-
-static int rtl8366rb_sw_get_learning_enable(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_SSCR0, &data);
-       val->value.i = !data;
-
-       return 0;
-}
-
-
-static int rtl8366rb_sw_set_learning_enable(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 portmask = 0;
-       int err = 0;
-
-       if (!val->value.i)
-               portmask = RTL8366RB_PORT_ALL;
-
-       /* set learning for all ports */
-       REG_WR(smi, RTL8366RB_SSCR0, portmask);
-
-       /* set auto ageing for all ports */
-       REG_WR(smi, RTL8366RB_SSCR1, portmask);
-
-       return 0;
-}
-
-static int rtl8366rb_sw_get_port_link(struct switch_dev *dev,
-                                    int port,
-                                    struct switch_port_link *link)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data = 0;
-       u32 speed;
-
-       if (port >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PORT_LINK_STATUS_BASE + (port / 2),
-                            &data);
-
-       if (port % 2)
-               data = data >> 8;
-
-       link->link = !!(data & RTL8366RB_PORT_STATUS_LINK_MASK);
-       if (!link->link)
-               return 0;
-
-       link->duplex = !!(data & RTL8366RB_PORT_STATUS_DUPLEX_MASK);
-       link->rx_flow = !!(data & RTL8366RB_PORT_STATUS_RXPAUSE_MASK);
-       link->tx_flow = !!(data & RTL8366RB_PORT_STATUS_TXPAUSE_MASK);
-       link->aneg = !!(data & RTL8366RB_PORT_STATUS_AN_MASK);
-
-       speed = (data & RTL8366RB_PORT_STATUS_SPEED_MASK);
-       switch (speed) {
-       case 0:
-               link->speed = SWITCH_PORT_SPEED_10;
-               break;
-       case 1:
-               link->speed = SWITCH_PORT_SPEED_100;
-               break;
-       case 2:
-               link->speed = SWITCH_PORT_SPEED_1000;
-               break;
-       default:
-               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_port_led(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-       u32 mask;
-       u32 reg;
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       if (val->port_vlan == RTL8366RB_PORT_NUM_CPU) {
-               reg = RTL8366RB_LED_BLINKRATE_REG;
-               mask = 0xF << 4;
-               data = val->value.i << 4;
-       } else {
-               reg = RTL8366RB_LED_CTRL_REG;
-               mask = 0xF << (val->port_vlan * 4),
-               data = val->value.i << (val->port_vlan * 4);
-       }
-
-       return rtl8366_smi_rmwr(smi, reg, mask, data);
-}
-
-static int rtl8366rb_sw_get_port_led(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data = 0;
-
-       if (val->port_vlan >= RTL8366RB_NUM_LEDGROUPS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_LED_CTRL_REG, &data);
-       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_port_disable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 mask, data;
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       mask = 1 << val->port_vlan ;
-       if (val->value.i)
-               data = mask;
-       else
-               data = 0;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PECR, mask, data);
-}
-
-static int rtl8366rb_sw_get_port_disable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PECR, &data);
-       if (data & (1 << val->port_vlan))
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_port_rate_in(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
-               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
-       else
-               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_IB_REG(val->port_vlan),
-               RTL8366RB_IB_BDTH_MASK | RTL8366RB_IB_PREIFG_MASK,
-               val->value.i |
-               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_IB_PREIFG_OFFSET));
-
-}
-
-static int rtl8366rb_sw_get_port_rate_in(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_IB_REG(val->port_vlan), &data);
-       data &= RTL8366RB_IB_BDTH_MASK;
-       if (data < RTL8366RB_IB_BDTH_MASK)
-               data += 1;
-
-       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_port_rate_out(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_rmwr(smi, RTL8366RB_EB_PREIFG_REG,
-               RTL8366RB_EB_PREIFG_MASK,
-               (RTL8366RB_QOS_DEFAULT_PREIFG << RTL8366RB_EB_PREIFG_OFFSET));
-
-       if (val->value.i > 0 && val->value.i < RTL8366RB_BDTH_SW_MAX)
-               val->value.i = (val->value.i - 1) / RTL8366RB_BDTH_UNIT;
-       else
-               val->value.i = RTL8366RB_BDTH_REG_DEFAULT;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_EB_REG(val->port_vlan),
-                       RTL8366RB_EB_BDTH_MASK, val->value.i );
-
-}
-
-static int rtl8366rb_sw_get_port_rate_out(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_EB_REG(val->port_vlan), &data);
-       data &= RTL8366RB_EB_BDTH_MASK;
-       if (data < RTL8366RB_EB_BDTH_MASK)
-               data += 1;
-
-       val->value.i = (int)data * RTL8366RB_BDTH_UNIT;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_qos_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->value.i)
-               data = RTL8366RB_QOS_MASK;
-       else
-               data = 0;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_SGCR, RTL8366RB_QOS_MASK, data);
-}
-
-static int rtl8366rb_sw_get_qos_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_SGCR, &data);
-       if (data & RTL8366RB_QOS_MASK)
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_mirror_rx_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->value.i)
-               data = RTL8366RB_PMCR_MIRROR_RX;
-       else
-               data = 0;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_RX, data);
-}
-
-static int rtl8366rb_sw_get_mirror_rx_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
-       if (data & RTL8366RB_PMCR_MIRROR_RX)
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_mirror_tx_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->value.i)
-               data = RTL8366RB_PMCR_MIRROR_TX;
-       else
-               data = 0;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_TX, data);
-}
-
-static int rtl8366rb_sw_get_mirror_tx_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
-       if (data & RTL8366RB_PMCR_MIRROR_TX)
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_monitor_isolation_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->value.i)
-               data = RTL8366RB_PMCR_MIRROR_ISO;
-       else
-               data = 0;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_ISO, data);
-}
-
-static int rtl8366rb_sw_get_monitor_isolation_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
-       if (data & RTL8366RB_PMCR_MIRROR_ISO)
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_mirror_pause_frames_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       if (val->value.i)
-               data = RTL8366RB_PMCR_MIRROR_SPC;
-       else
-               data = 0;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MIRROR_SPC, data);
-}
-
-static int rtl8366rb_sw_get_mirror_pause_frames_enable(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
-       if (data & RTL8366RB_PMCR_MIRROR_SPC)
-               val->value.i = 1;
-       else
-               val->value.i = 0;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_mirror_monitor_port(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       data = RTL8366RB_PMCR_MONITOR_PORT(val->value.i);
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_MONITOR_PORT_MASK, data);
-}
-
-static int rtl8366rb_sw_get_mirror_monitor_port(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
-       val->value.i = (data & RTL8366RB_PMCR_MONITOR_PORT_MASK) >> 4;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_set_mirror_source_port(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       data = RTL8366RB_PMCR_SOURCE_PORT(val->value.i);
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_PMCR, RTL8366RB_PMCR_SOURCE_PORT_MASK, data);
-}
-
-static int rtl8366rb_sw_get_mirror_source_port(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366RB_PMCR, &data);
-       val->value.i = data & RTL8366RB_PMCR_SOURCE_PORT_MASK;
-
-       return 0;
-}
-
-static int rtl8366rb_sw_reset_port_mibs(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (val->port_vlan >= RTL8366RB_NUM_PORTS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8366RB_MIB_CTRL_REG, 0,
-                               RTL8366RB_MIB_CTRL_PORT_RESET(val->port_vlan));
-}
-
-static int rtl8366rb_sw_get_port_stats(struct switch_dev *dev, int port,
-                                       struct switch_port_stats *stats)
-{
-       return (rtl8366_sw_get_port_stats(dev, port, stats,
-                               RTL8366RB_MIB_TXB_ID, RTL8366RB_MIB_RXB_ID));
-}
-
-static struct switch_attr rtl8366rb_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_learning",
-               .description = "Enable learning, enable aging",
-               .set = rtl8366rb_sw_set_learning_enable,
-               .get = rtl8366rb_sw_get_learning_enable,
-               .max = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan4k",
-               .description = "Enable VLAN 4K mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 2
-       }, {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mibs",
-               .description = "Reset all MIB counters",
-               .set = rtl8366rb_sw_reset_mibs,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "blinkrate",
-               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
-               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
-               .set = rtl8366rb_sw_set_blinkrate,
-               .get = rtl8366rb_sw_get_blinkrate,
-               .max = 5
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_qos",
-               .description = "Enable QOS",
-               .set = rtl8366rb_sw_set_qos_enable,
-               .get = rtl8366rb_sw_get_qos_enable,
-               .max = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_rx",
-               .description = "Enable mirroring of RX packets",
-               .set = rtl8366rb_sw_set_mirror_rx_enable,
-               .get = rtl8366rb_sw_get_mirror_rx_enable,
-               .max = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_tx",
-               .description = "Enable mirroring of TX packets",
-               .set = rtl8366rb_sw_set_mirror_tx_enable,
-               .get = rtl8366rb_sw_get_mirror_tx_enable,
-               .max = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_monitor_isolation",
-               .description = "Enable isolation of monitor port (TX packets will be dropped)",
-               .set = rtl8366rb_sw_set_monitor_isolation_enable,
-               .get = rtl8366rb_sw_get_monitor_isolation_enable,
-               .max = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_mirror_pause_frames",
-               .description = "Enable mirroring of RX pause frames",
-               .set = rtl8366rb_sw_set_mirror_pause_frames_enable,
-               .get = rtl8366rb_sw_get_mirror_pause_frames_enable,
-               .max = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "mirror_monitor_port",
-               .description = "Mirror monitor port",
-               .set = rtl8366rb_sw_set_mirror_monitor_port,
-               .get = rtl8366rb_sw_get_mirror_monitor_port,
-               .max = 5
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "mirror_source_port",
-               .description = "Mirror source port",
-               .set = rtl8366rb_sw_set_mirror_source_port,
-               .get = rtl8366rb_sw_get_mirror_source_port,
-               .max = 5
-       },
-};
-
-static struct switch_attr rtl8366rb_port[] = {
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mib",
-               .description = "Reset single port MIB counters",
-               .set = rtl8366rb_sw_reset_port_mibs,
-       }, {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get MIB counters for port",
-               .max = 33,
-               .set = NULL,
-               .get = rtl8366_sw_get_port_mib,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "led",
-               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
-               .max = 15,
-               .set = rtl8366rb_sw_set_port_led,
-               .get = rtl8366rb_sw_get_port_led,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "disable",
-               .description = "Get/Set port state (enabled or disabled)",
-               .max = 1,
-               .set = rtl8366rb_sw_set_port_disable,
-               .get = rtl8366rb_sw_get_port_disable,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "rate_in",
-               .description = "Get/Set port ingress (incoming) bandwidth limit in kbps",
-               .max = RTL8366RB_BDTH_SW_MAX,
-               .set = rtl8366rb_sw_set_port_rate_in,
-               .get = rtl8366rb_sw_get_port_rate_in,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "rate_out",
-               .description = "Get/Set port egress (outgoing) bandwidth limit in kbps",
-               .max = RTL8366RB_BDTH_SW_MAX,
-               .set = rtl8366rb_sw_set_port_rate_out,
-               .get = rtl8366rb_sw_get_port_rate_out,
-       },
-};
-
-static struct switch_attr rtl8366rb_vlan[] = {
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "info",
-               .description = "Get vlan information",
-               .max = 1,
-               .set = NULL,
-               .get = rtl8366_sw_get_vlan_info,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "fid",
-               .description = "Get/Set vlan FID",
-               .max = RTL8366RB_FIDMAX,
-               .set = rtl8366_sw_set_vlan_fid,
-               .get = rtl8366_sw_get_vlan_fid,
-       },
-};
-
-static const struct switch_dev_ops rtl8366_ops = {
-       .attr_global = {
-               .attr = rtl8366rb_globals,
-               .n_attr = ARRAY_SIZE(rtl8366rb_globals),
-       },
-       .attr_port = {
-               .attr = rtl8366rb_port,
-               .n_attr = ARRAY_SIZE(rtl8366rb_port),
-       },
-       .attr_vlan = {
-               .attr = rtl8366rb_vlan,
-               .n_attr = ARRAY_SIZE(rtl8366rb_vlan),
-       },
-
-       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
-       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
-       .get_port_pvid = rtl8366_sw_get_port_pvid,
-       .set_port_pvid = rtl8366_sw_set_port_pvid,
-       .reset_switch = rtl8366_sw_reset_switch,
-       .get_port_link = rtl8366rb_sw_get_port_link,
-       .get_port_stats = rtl8366rb_sw_get_port_stats,
-};
-
-static int rtl8366rb_switch_init(struct rtl8366_smi *smi)
-{
-       struct switch_dev *dev = &smi->sw_dev;
-       int err;
-
-       dev->name = "RTL8366RB";
-       dev->cpu_port = RTL8366RB_PORT_NUM_CPU;
-       dev->ports = RTL8366RB_NUM_PORTS;
-       dev->vlans = RTL8366RB_NUM_VIDS;
-       dev->ops = &rtl8366_ops;
-       dev->alias = dev_name(smi->parent);
-
-       err = register_switch(dev, NULL);
-       if (err)
-               dev_err(smi->parent, "switch registration failed\n");
-
-       return err;
-}
-
-static void rtl8366rb_switch_cleanup(struct rtl8366_smi *smi)
-{
-       unregister_switch(&smi->sw_dev);
-}
-
-static int rtl8366rb_mii_read(struct mii_bus *bus, int addr, int reg)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 val = 0;
-       int err;
-
-       err = rtl8366rb_read_phy_reg(smi, addr, 0, reg, &val);
-       if (err)
-               return 0xffff;
-
-       return val;
-}
-
-static int rtl8366rb_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 t;
-       int err;
-
-       err = rtl8366rb_write_phy_reg(smi, addr, 0, reg, val);
-       /* flush write */
-       (void) rtl8366rb_read_phy_reg(smi, addr, 0, reg, &t);
-
-       return err;
-}
-
-static int rtl8366rb_detect(struct rtl8366_smi *smi)
-{
-       u32 chip_id = 0;
-       u32 chip_ver = 0;
-       int ret;
-
-       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_ID_REG, &chip_id);
-       if (ret) {
-               dev_err(smi->parent, "unable to read chip id\n");
-               return ret;
-       }
-
-       switch (chip_id) {
-       case RTL8366RB_CHIP_ID_8366:
-               break;
-       default:
-               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
-               return -ENODEV;
-       }
-
-       ret = rtl8366_smi_read_reg(smi, RTL8366RB_CHIP_VERSION_CTRL_REG,
-                                  &chip_ver);
-       if (ret) {
-               dev_err(smi->parent, "unable to read chip version\n");
-               return ret;
-       }
-
-       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
-                chip_id, chip_ver & RTL8366RB_CHIP_VERSION_MASK);
-
-       return 0;
-}
-
-static struct rtl8366_smi_ops rtl8366rb_smi_ops = {
-       .detect         = rtl8366rb_detect,
-       .reset_chip     = rtl8366rb_reset_chip,
-       .setup          = rtl8366rb_setup,
-
-       .mii_read       = rtl8366rb_mii_read,
-       .mii_write      = rtl8366rb_mii_write,
-
-       .get_vlan_mc    = rtl8366rb_get_vlan_mc,
-       .set_vlan_mc    = rtl8366rb_set_vlan_mc,
-       .get_vlan_4k    = rtl8366rb_get_vlan_4k,
-       .set_vlan_4k    = rtl8366rb_set_vlan_4k,
-       .get_mc_index   = rtl8366rb_get_mc_index,
-       .set_mc_index   = rtl8366rb_set_mc_index,
-       .get_mib_counter = rtl8366rb_get_mib_counter,
-       .is_vlan_valid  = rtl8366rb_is_vlan_valid,
-       .enable_vlan    = rtl8366rb_enable_vlan,
-       .enable_vlan4k  = rtl8366rb_enable_vlan4k,
-       .enable_port    = rtl8366rb_enable_port,
-};
-
-static int rtl8366rb_probe(struct platform_device *pdev)
-{
-       static int rtl8366_smi_version_printed;
-       struct rtl8366_smi *smi;
-       int err;
-
-       if (!rtl8366_smi_version_printed++)
-               printk(KERN_NOTICE RTL8366RB_DRIVER_DESC
-                      " version " RTL8366RB_DRIVER_VER"\n");
-
-       smi = rtl8366_smi_probe(pdev);
-       if (!smi)
-               return -ENODEV;
-
-       smi->clk_delay = 10;
-       smi->cmd_read = 0xa9;
-       smi->cmd_write = 0xa8;
-       smi->ops = &rtl8366rb_smi_ops;
-       smi->cpu_port = RTL8366RB_PORT_NUM_CPU;
-       smi->num_ports = RTL8366RB_NUM_PORTS;
-       smi->num_vlan_mc = RTL8366RB_NUM_VLANS;
-       smi->mib_counters = rtl8366rb_mib_counters;
-       smi->num_mib_counters = ARRAY_SIZE(rtl8366rb_mib_counters);
-
-       err = rtl8366_smi_init(smi);
-       if (err)
-               goto err_free_smi;
-
-       platform_set_drvdata(pdev, smi);
-
-       err = rtl8366rb_switch_init(smi);
-       if (err)
-               goto err_clear_drvdata;
-
-       return 0;
-
- err_clear_drvdata:
-       platform_set_drvdata(pdev, NULL);
-       rtl8366_smi_cleanup(smi);
- err_free_smi:
-       kfree(smi);
-       return err;
-}
-
-static int rtl8366rb_remove(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
-
-       if (smi) {
-               rtl8366rb_switch_cleanup(smi);
-               platform_set_drvdata(pdev, NULL);
-               rtl8366_smi_cleanup(smi);
-               kfree(smi);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id rtl8366rb_match[] = {
-       { .compatible = "realtek,rtl8366rb" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, rtl8366rb_match);
-#endif
-
-static struct platform_driver rtl8366rb_driver = {
-       .driver = {
-               .name           = RTL8366RB_DRIVER_NAME,
-               .owner          = THIS_MODULE,
-               .of_match_table = of_match_ptr(rtl8366rb_match),
-       },
-       .probe          = rtl8366rb_probe,
-       .remove         = rtl8366rb_remove,
-};
-
-static int __init rtl8366rb_module_init(void)
-{
-       return platform_driver_register(&rtl8366rb_driver);
-}
-module_init(rtl8366rb_module_init);
-
-static void __exit rtl8366rb_module_exit(void)
-{
-       platform_driver_unregister(&rtl8366rb_driver);
-}
-module_exit(rtl8366rb_module_exit);
-
-MODULE_DESCRIPTION(RTL8366RB_DRIVER_DESC);
-MODULE_VERSION(RTL8366RB_DRIVER_VER);
-MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
-MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
-MODULE_AUTHOR("Roman Yeryomin <roman@advem.lv>");
-MODULE_AUTHOR("Colin Leitner <colin.leitner@googlemail.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" RTL8366RB_DRIVER_NAME);
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8366s.c b/target/linux/generic/files/drivers/net/phy/rtl8366s.c
deleted file mode 100644 (file)
index 3f458f9..0000000
+++ /dev/null
@@ -1,1320 +0,0 @@
-/*
- * Platform driver for the Realtek RTL8366S ethernet switch
- *
- * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
- * Copyright (C) 2010 Antti Seppälä <a.seppala@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/rtl8366.h>
-
-#include "rtl8366_smi.h"
-
-#define RTL8366S_DRIVER_DESC   "Realtek RTL8366S ethernet switch driver"
-#define RTL8366S_DRIVER_VER    "0.2.2"
-
-#define RTL8366S_PHY_NO_MAX    4
-#define RTL8366S_PHY_PAGE_MAX  7
-#define RTL8366S_PHY_ADDR_MAX  31
-
-/* Switch Global Configuration register */
-#define RTL8366S_SGCR                          0x0000
-#define RTL8366S_SGCR_EN_BC_STORM_CTRL         BIT(0)
-#define RTL8366S_SGCR_MAX_LENGTH(_x)           (_x << 4)
-#define RTL8366S_SGCR_MAX_LENGTH_MASK          RTL8366S_SGCR_MAX_LENGTH(0x3)
-#define RTL8366S_SGCR_MAX_LENGTH_1522          RTL8366S_SGCR_MAX_LENGTH(0x0)
-#define RTL8366S_SGCR_MAX_LENGTH_1536          RTL8366S_SGCR_MAX_LENGTH(0x1)
-#define RTL8366S_SGCR_MAX_LENGTH_1552          RTL8366S_SGCR_MAX_LENGTH(0x2)
-#define RTL8366S_SGCR_MAX_LENGTH_16000         RTL8366S_SGCR_MAX_LENGTH(0x3)
-#define RTL8366S_SGCR_EN_VLAN                  BIT(13)
-
-/* Port Enable Control register */
-#define RTL8366S_PECR                          0x0001
-
-/* Green Ethernet Feature (based on GPL_BELKIN_F5D8235-4_v1000 v1.01.24) */
-#define RTL8366S_GREEN_ETHERNET_CTRL_REG       0x000a
-#define RTL8366S_GREEN_ETHERNET_CTRL_MASK      0x0018
-#define RTL8366S_GREEN_ETHERNET_TX_BIT         (1 << 3)
-#define RTL8366S_GREEN_ETHERNET_RX_BIT         (1 << 4)
-
-/* Switch Security Control registers */
-#define RTL8366S_SSCR0                         0x0002
-#define RTL8366S_SSCR1                         0x0003
-#define RTL8366S_SSCR2                         0x0004
-#define RTL8366S_SSCR2_DROP_UNKNOWN_DA         BIT(0)
-
-#define RTL8366S_RESET_CTRL_REG                        0x0100
-#define RTL8366S_CHIP_CTRL_RESET_HW            1
-#define RTL8366S_CHIP_CTRL_RESET_SW            (1 << 1)
-
-#define RTL8366S_CHIP_VERSION_CTRL_REG         0x0104
-#define RTL8366S_CHIP_VERSION_MASK             0xf
-#define RTL8366S_CHIP_ID_REG                   0x0105
-#define RTL8366S_CHIP_ID_8366                  0x8366
-
-/* PHY registers control */
-#define RTL8366S_PHY_ACCESS_CTRL_REG           0x8028
-#define RTL8366S_PHY_ACCESS_DATA_REG           0x8029
-
-#define RTL8366S_PHY_CTRL_READ                 1
-#define RTL8366S_PHY_CTRL_WRITE                        0
-
-#define RTL8366S_PHY_REG_MASK                  0x1f
-#define RTL8366S_PHY_PAGE_OFFSET               5
-#define RTL8366S_PHY_PAGE_MASK                 (0x7 << 5)
-#define RTL8366S_PHY_NO_OFFSET                 9
-#define RTL8366S_PHY_NO_MASK                   (0x1f << 9)
-
-/* Green Ethernet Feature for PHY ports */
-#define RTL8366S_PHY_POWER_SAVING_CTRL_REG     12
-#define RTL8366S_PHY_POWER_SAVING_MASK         0x1000
-
-/* LED control registers */
-#define RTL8366S_LED_BLINKRATE_REG             0x0420
-#define RTL8366S_LED_BLINKRATE_BIT             0
-#define RTL8366S_LED_BLINKRATE_MASK            0x0007
-
-#define RTL8366S_LED_CTRL_REG                  0x0421
-#define RTL8366S_LED_0_1_CTRL_REG              0x0422
-#define RTL8366S_LED_2_3_CTRL_REG              0x0423
-
-#define RTL8366S_MIB_COUNT                     33
-#define RTL8366S_GLOBAL_MIB_COUNT              1
-#define RTL8366S_MIB_COUNTER_PORT_OFFSET       0x0040
-#define RTL8366S_MIB_COUNTER_BASE              0x1000
-#define RTL8366S_MIB_COUNTER_PORT_OFFSET2      0x0008
-#define RTL8366S_MIB_COUNTER_BASE2             0x1180
-#define RTL8366S_MIB_CTRL_REG                  0x11F0
-#define RTL8366S_MIB_CTRL_USER_MASK            0x01FF
-#define RTL8366S_MIB_CTRL_BUSY_MASK            0x0001
-#define RTL8366S_MIB_CTRL_RESET_MASK           0x0002
-
-#define RTL8366S_MIB_CTRL_GLOBAL_RESET_MASK    0x0004
-#define RTL8366S_MIB_CTRL_PORT_RESET_BIT       0x0003
-#define RTL8366S_MIB_CTRL_PORT_RESET_MASK      0x01FC
-
-
-#define RTL8366S_PORT_VLAN_CTRL_BASE           0x0058
-#define RTL8366S_PORT_VLAN_CTRL_REG(_p)  \
-               (RTL8366S_PORT_VLAN_CTRL_BASE + (_p) / 4)
-#define RTL8366S_PORT_VLAN_CTRL_MASK           0xf
-#define RTL8366S_PORT_VLAN_CTRL_SHIFT(_p)      (4 * ((_p) % 4))
-
-
-#define RTL8366S_VLAN_TABLE_READ_BASE          0x018B
-#define RTL8366S_VLAN_TABLE_WRITE_BASE         0x0185
-
-#define RTL8366S_VLAN_TB_CTRL_REG              0x010F
-
-#define RTL8366S_TABLE_ACCESS_CTRL_REG         0x0180
-#define RTL8366S_TABLE_VLAN_READ_CTRL          0x0E01
-#define RTL8366S_TABLE_VLAN_WRITE_CTRL         0x0F01
-
-#define RTL8366S_VLAN_MC_BASE(_x)              (0x0016 + (_x) * 2)
-
-#define RTL8366S_VLAN_MEMBERINGRESS_REG                0x0379
-
-#define RTL8366S_PORT_LINK_STATUS_BASE         0x0060
-#define RTL8366S_PORT_STATUS_SPEED_MASK                0x0003
-#define RTL8366S_PORT_STATUS_DUPLEX_MASK       0x0004
-#define RTL8366S_PORT_STATUS_LINK_MASK         0x0010
-#define RTL8366S_PORT_STATUS_TXPAUSE_MASK      0x0020
-#define RTL8366S_PORT_STATUS_RXPAUSE_MASK      0x0040
-#define RTL8366S_PORT_STATUS_AN_MASK           0x0080
-
-
-#define RTL8366S_PORT_NUM_CPU          5
-#define RTL8366S_NUM_PORTS             6
-#define RTL8366S_NUM_VLANS             16
-#define RTL8366S_NUM_LEDGROUPS         4
-#define RTL8366S_NUM_VIDS              4096
-#define RTL8366S_PRIORITYMAX           7
-#define RTL8366S_FIDMAX                        7
-
-
-#define RTL8366S_PORT_1                        (1 << 0) /* In userspace port 0 */
-#define RTL8366S_PORT_2                        (1 << 1) /* In userspace port 1 */
-#define RTL8366S_PORT_3                        (1 << 2) /* In userspace port 2 */
-#define RTL8366S_PORT_4                        (1 << 3) /* In userspace port 3 */
-
-#define RTL8366S_PORT_UNKNOWN          (1 << 4) /* No known connection */
-#define RTL8366S_PORT_CPU              (1 << 5) /* CPU port */
-
-#define RTL8366S_PORT_ALL              (RTL8366S_PORT_1 |      \
-                                        RTL8366S_PORT_2 |      \
-                                        RTL8366S_PORT_3 |      \
-                                        RTL8366S_PORT_4 |      \
-                                        RTL8366S_PORT_UNKNOWN | \
-                                        RTL8366S_PORT_CPU)
-
-#define RTL8366S_PORT_ALL_BUT_CPU      (RTL8366S_PORT_1 |      \
-                                        RTL8366S_PORT_2 |      \
-                                        RTL8366S_PORT_3 |      \
-                                        RTL8366S_PORT_4 |      \
-                                        RTL8366S_PORT_UNKNOWN)
-
-#define RTL8366S_PORT_ALL_EXTERNAL     (RTL8366S_PORT_1 |      \
-                                        RTL8366S_PORT_2 |      \
-                                        RTL8366S_PORT_3 |      \
-                                        RTL8366S_PORT_4)
-
-#define RTL8366S_PORT_ALL_INTERNAL     (RTL8366S_PORT_UNKNOWN | \
-                                        RTL8366S_PORT_CPU)
-
-#define RTL8366S_VLAN_VID_MASK         0xfff
-#define RTL8366S_VLAN_PRIORITY_SHIFT   12
-#define RTL8366S_VLAN_PRIORITY_MASK    0x7
-#define RTL8366S_VLAN_MEMBER_MASK      0x3f
-#define RTL8366S_VLAN_UNTAG_SHIFT      6
-#define RTL8366S_VLAN_UNTAG_MASK       0x3f
-#define RTL8366S_VLAN_FID_SHIFT                12
-#define RTL8366S_VLAN_FID_MASK         0x7
-
-#define RTL8366S_MIB_RXB_ID            0       /* IfInOctets */
-#define RTL8366S_MIB_TXB_ID            20      /* IfOutOctets */
-
-static struct rtl8366_mib_counter rtl8366s_mib_counters[] = {
-       { 0,  0, 4, "IfInOctets"                                },
-       { 0,  4, 4, "EtherStatsOctets"                          },
-       { 0,  8, 2, "EtherStatsUnderSizePkts"                   },
-       { 0, 10, 2, "EtherFragments"                            },
-       { 0, 12, 2, "EtherStatsPkts64Octets"                    },
-       { 0, 14, 2, "EtherStatsPkts65to127Octets"               },
-       { 0, 16, 2, "EtherStatsPkts128to255Octets"              },
-       { 0, 18, 2, "EtherStatsPkts256to511Octets"              },
-       { 0, 20, 2, "EtherStatsPkts512to1023Octets"             },
-       { 0, 22, 2, "EtherStatsPkts1024to1518Octets"            },
-       { 0, 24, 2, "EtherOversizeStats"                        },
-       { 0, 26, 2, "EtherStatsJabbers"                         },
-       { 0, 28, 2, "IfInUcastPkts"                             },
-       { 0, 30, 2, "EtherStatsMulticastPkts"                   },
-       { 0, 32, 2, "EtherStatsBroadcastPkts"                   },
-       { 0, 34, 2, "EtherStatsDropEvents"                      },
-       { 0, 36, 2, "Dot3StatsFCSErrors"                        },
-       { 0, 38, 2, "Dot3StatsSymbolErrors"                     },
-       { 0, 40, 2, "Dot3InPauseFrames"                         },
-       { 0, 42, 2, "Dot3ControlInUnknownOpcodes"               },
-       { 0, 44, 4, "IfOutOctets"                               },
-       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
-       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
-       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
-       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
-       { 0, 56, 2, "EtherStatsCollisions"                      },
-       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
-       { 0, 60, 2, "Dot3OutPauseFrames"                        },
-       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
-
-       /*
-        * The following counters are accessible at a different
-        * base address.
-        */
-       { 1,  0, 2, "Dot1dTpPortInDiscards"                     },
-       { 1,  2, 2, "IfOutUcastPkts"                            },
-       { 1,  4, 2, "IfOutMulticastPkts"                        },
-       { 1,  6, 2, "IfOutBroadcastPkts"                        },
-};
-
-#define REG_WR(_smi, _reg, _val)                                       \
-       do {                                                            \
-               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-#define REG_RMW(_smi, _reg, _mask, _val)                               \
-       do {                                                            \
-               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-static int rtl8366s_reset_chip(struct rtl8366_smi *smi)
-{
-       int timeout = 10;
-       u32 data;
-
-       rtl8366_smi_write_reg_noack(smi, RTL8366S_RESET_CTRL_REG,
-                                   RTL8366S_CHIP_CTRL_RESET_HW);
-       do {
-               msleep(1);
-               if (rtl8366_smi_read_reg(smi, RTL8366S_RESET_CTRL_REG, &data))
-                       return -EIO;
-
-               if (!(data & RTL8366S_CHIP_CTRL_RESET_HW))
-                       break;
-       } while (--timeout);
-
-       if (!timeout) {
-               printk("Timeout waiting for the switch to reset\n");
-               return -EIO;
-       }
-
-       return 0;
-}
-
-static int rtl8366s_read_phy_reg(struct rtl8366_smi *smi,
-                                u32 phy_no, u32 page, u32 addr, u32 *data)
-{
-       u32 reg;
-       int ret;
-
-       if (phy_no > RTL8366S_PHY_NO_MAX)
-               return -EINVAL;
-
-       if (page > RTL8366S_PHY_PAGE_MAX)
-               return -EINVAL;
-
-       if (addr > RTL8366S_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
-                                   RTL8366S_PHY_CTRL_READ);
-       if (ret)
-               return ret;
-
-       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
-             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
-             (addr & RTL8366S_PHY_REG_MASK);
-
-       ret = rtl8366_smi_write_reg(smi, reg, 0);
-       if (ret)
-               return ret;
-
-       ret = rtl8366_smi_read_reg(smi, RTL8366S_PHY_ACCESS_DATA_REG, data);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int rtl8366s_write_phy_reg(struct rtl8366_smi *smi,
-                                 u32 phy_no, u32 page, u32 addr, u32 data)
-{
-       u32 reg;
-       int ret;
-
-       if (phy_no > RTL8366S_PHY_NO_MAX)
-               return -EINVAL;
-
-       if (page > RTL8366S_PHY_PAGE_MAX)
-               return -EINVAL;
-
-       if (addr > RTL8366S_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       ret = rtl8366_smi_write_reg(smi, RTL8366S_PHY_ACCESS_CTRL_REG,
-                                   RTL8366S_PHY_CTRL_WRITE);
-       if (ret)
-               return ret;
-
-       reg = 0x8000 | (1 << (phy_no + RTL8366S_PHY_NO_OFFSET)) |
-             ((page << RTL8366S_PHY_PAGE_OFFSET) & RTL8366S_PHY_PAGE_MASK) |
-             (addr & RTL8366S_PHY_REG_MASK);
-
-       ret = rtl8366_smi_write_reg(smi, reg, data);
-       if (ret)
-               return ret;
-
-       return 0;
-}
-
-static int rtl8366s_set_green_port(struct rtl8366_smi *smi, int port, int enable)
-{
-       int err;
-       u32 phyData;
-
-       if (port >= RTL8366S_NUM_PORTS)
-               return -EINVAL;
-
-       err = rtl8366s_read_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
-       if (err)
-               return err;
-
-       if (enable)
-               phyData |= RTL8366S_PHY_POWER_SAVING_MASK;
-       else
-               phyData &= ~RTL8366S_PHY_POWER_SAVING_MASK;
-
-       err = rtl8366s_write_phy_reg(smi, port, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, phyData);
-       if (err)
-               return err;
-
-       return 0;
-}
-
-static int rtl8366s_set_green(struct rtl8366_smi *smi, int enable)
-{
-       int err;
-       unsigned i;
-       u32 data = 0;
-
-       if (!enable) {
-               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
-                       rtl8366s_set_green_port(smi, i, 0);
-               }
-       }
-
-       if (enable)
-               data = (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT);
-
-       REG_RMW(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, RTL8366S_GREEN_ETHERNET_CTRL_MASK, data);
-
-       return 0;
-}
-
-static int rtl8366s_setup(struct rtl8366_smi *smi)
-{
-       struct rtl8366_platform_data *pdata;
-       int err;
-       unsigned i;
-#ifdef CONFIG_OF
-       struct device_node *np;
-       unsigned num_initvals;
-       const __be32 *paddr;
-#endif
-
-       pdata = smi->parent->platform_data;
-       if (pdata && pdata->num_initvals && pdata->initvals) {
-               dev_info(smi->parent, "applying initvals\n");
-               for (i = 0; i < pdata->num_initvals; i++)
-                       REG_WR(smi, pdata->initvals[i].reg,
-                              pdata->initvals[i].val);
-       }
-
-#ifdef CONFIG_OF
-       np = smi->parent->of_node;
-
-       paddr = of_get_property(np, "realtek,initvals", &num_initvals);
-       if (paddr) {
-               dev_info(smi->parent, "applying initvals from DTS\n");
-
-               if (num_initvals < (2 * sizeof(*paddr)))
-                       return -EINVAL;
-
-               num_initvals /= sizeof(*paddr);
-
-               for (i = 0; i < num_initvals - 1; i += 2) {
-                       u32 reg = be32_to_cpup(paddr + i);
-                       u32 val = be32_to_cpup(paddr + i + 1);
-
-                       REG_WR(smi, reg, val);
-               }
-       }
-
-       if (of_property_read_bool(np, "realtek,green-ethernet-features")) {
-               dev_info(smi->parent, "activating Green Ethernet features\n");
-
-               err = rtl8366s_set_green(smi, 1);
-               if (err)
-                       return err;
-
-               for (i = 0; i <= RTL8366S_PHY_NO_MAX; i++) {
-                       err = rtl8366s_set_green_port(smi, i, 1);
-                       if (err)
-                               return err;
-               }
-       }
-#endif
-
-       /* set maximum packet length to 1536 bytes */
-       REG_RMW(smi, RTL8366S_SGCR, RTL8366S_SGCR_MAX_LENGTH_MASK,
-               RTL8366S_SGCR_MAX_LENGTH_1536);
-
-       /* enable learning for all ports */
-       REG_WR(smi, RTL8366S_SSCR0, 0);
-
-       /* enable auto ageing for all ports */
-       REG_WR(smi, RTL8366S_SSCR1, 0);
-
-       /*
-        * discard VLAN tagged packets if the port is not a member of
-        * the VLAN with which the packets is associated.
-        */
-       REG_WR(smi, RTL8366S_VLAN_MEMBERINGRESS_REG, RTL8366S_PORT_ALL);
-
-       /* don't drop packets whose DA has not been learned */
-       REG_RMW(smi, RTL8366S_SSCR2, RTL8366S_SSCR2_DROP_UNKNOWN_DA, 0);
-
-       return 0;
-}
-
-static int rtl8366_get_mib_counter(struct rtl8366_smi *smi, int counter,
-                                  int port, unsigned long long *val)
-{
-       int i;
-       int err;
-       u32 addr, data;
-       u64 mibvalue;
-
-       if (port > RTL8366S_NUM_PORTS || counter >= RTL8366S_MIB_COUNT)
-               return -EINVAL;
-
-       switch (rtl8366s_mib_counters[counter].base) {
-       case 0:
-               addr = RTL8366S_MIB_COUNTER_BASE +
-                      RTL8366S_MIB_COUNTER_PORT_OFFSET * port;
-               break;
-
-       case 1:
-               addr = RTL8366S_MIB_COUNTER_BASE2 +
-                       RTL8366S_MIB_COUNTER_PORT_OFFSET2 * port;
-               break;
-
-       default:
-               return -EINVAL;
-       }
-
-       addr += rtl8366s_mib_counters[counter].offset;
-
-       /*
-        * Writing access counter address first
-        * then ASIC will prepare 64bits counter wait for being retrived
-        */
-       data = 0; /* writing data will be discard by ASIC */
-       err = rtl8366_smi_write_reg(smi, addr, data);
-       if (err)
-               return err;
-
-       /* read MIB control register */
-       err =  rtl8366_smi_read_reg(smi, RTL8366S_MIB_CTRL_REG, &data);
-       if (err)
-               return err;
-
-       if (data & RTL8366S_MIB_CTRL_BUSY_MASK)
-               return -EBUSY;
-
-       if (data & RTL8366S_MIB_CTRL_RESET_MASK)
-               return -EIO;
-
-       mibvalue = 0;
-       for (i = rtl8366s_mib_counters[counter].length; i > 0; i--) {
-               err = rtl8366_smi_read_reg(smi, addr + (i - 1), &data);
-               if (err)
-                       return err;
-
-               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
-       }
-
-       *val = mibvalue;
-       return 0;
-}
-
-static int rtl8366s_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
-                               struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[2];
-       int err;
-       int i;
-
-       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
-
-       if (vid >= RTL8366S_NUM_VIDS)
-               return -EINVAL;
-
-       /* write VID */
-       err = rtl8366_smi_write_reg(smi, RTL8366S_VLAN_TABLE_WRITE_BASE,
-                                   vid & RTL8366S_VLAN_VID_MASK);
-       if (err)
-               return err;
-
-       /* write table access control word */
-       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
-                                   RTL8366S_TABLE_VLAN_READ_CTRL);
-       if (err)
-               return err;
-
-       for (i = 0; i < 2; i++) {
-               err = rtl8366_smi_read_reg(smi,
-                                          RTL8366S_VLAN_TABLE_READ_BASE + i,
-                                          &data[i]);
-               if (err)
-                       return err;
-       }
-
-       vlan4k->vid = vid;
-       vlan4k->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
-                       RTL8366S_VLAN_UNTAG_MASK;
-       vlan4k->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
-       vlan4k->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
-                       RTL8366S_VLAN_FID_MASK;
-
-       return 0;
-}
-
-static int rtl8366s_set_vlan_4k(struct rtl8366_smi *smi,
-                               const struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[2];
-       int err;
-       int i;
-
-       if (vlan4k->vid >= RTL8366S_NUM_VIDS ||
-           vlan4k->member > RTL8366S_VLAN_MEMBER_MASK ||
-           vlan4k->untag > RTL8366S_VLAN_UNTAG_MASK ||
-           vlan4k->fid > RTL8366S_FIDMAX)
-               return -EINVAL;
-
-       data[0] = vlan4k->vid & RTL8366S_VLAN_VID_MASK;
-       data[1] = (vlan4k->member & RTL8366S_VLAN_MEMBER_MASK) |
-                 ((vlan4k->untag & RTL8366S_VLAN_UNTAG_MASK) <<
-                       RTL8366S_VLAN_UNTAG_SHIFT) |
-                 ((vlan4k->fid & RTL8366S_VLAN_FID_MASK) <<
-                       RTL8366S_VLAN_FID_SHIFT);
-
-       for (i = 0; i < 2; i++) {
-               err = rtl8366_smi_write_reg(smi,
-                                           RTL8366S_VLAN_TABLE_WRITE_BASE + i,
-                                           data[i]);
-               if (err)
-                       return err;
-       }
-
-       /* write table access control word */
-       err = rtl8366_smi_write_reg(smi, RTL8366S_TABLE_ACCESS_CTRL_REG,
-                                   RTL8366S_TABLE_VLAN_WRITE_CTRL);
-
-       return err;
-}
-
-static int rtl8366s_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                               struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[2];
-       int err;
-       int i;
-
-       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
-
-       if (index >= RTL8366S_NUM_VLANS)
-               return -EINVAL;
-
-       for (i = 0; i < 2; i++) {
-               err = rtl8366_smi_read_reg(smi,
-                                          RTL8366S_VLAN_MC_BASE(index) + i,
-                                          &data[i]);
-               if (err)
-                       return err;
-       }
-
-       vlanmc->vid = data[0] & RTL8366S_VLAN_VID_MASK;
-       vlanmc->priority = (data[0] >> RTL8366S_VLAN_PRIORITY_SHIFT) &
-                          RTL8366S_VLAN_PRIORITY_MASK;
-       vlanmc->untag = (data[1] >> RTL8366S_VLAN_UNTAG_SHIFT) &
-                       RTL8366S_VLAN_UNTAG_MASK;
-       vlanmc->member = data[1] & RTL8366S_VLAN_MEMBER_MASK;
-       vlanmc->fid = (data[1] >> RTL8366S_VLAN_FID_SHIFT) &
-                     RTL8366S_VLAN_FID_MASK;
-
-       return 0;
-}
-
-static int rtl8366s_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                               const struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[2];
-       int err;
-       int i;
-
-       if (index >= RTL8366S_NUM_VLANS ||
-           vlanmc->vid >= RTL8366S_NUM_VIDS ||
-           vlanmc->priority > RTL8366S_PRIORITYMAX ||
-           vlanmc->member > RTL8366S_VLAN_MEMBER_MASK ||
-           vlanmc->untag > RTL8366S_VLAN_UNTAG_MASK ||
-           vlanmc->fid > RTL8366S_FIDMAX)
-               return -EINVAL;
-
-       data[0] = (vlanmc->vid & RTL8366S_VLAN_VID_MASK) |
-                 ((vlanmc->priority & RTL8366S_VLAN_PRIORITY_MASK) <<
-                       RTL8366S_VLAN_PRIORITY_SHIFT);
-       data[1] = (vlanmc->member & RTL8366S_VLAN_MEMBER_MASK) |
-                 ((vlanmc->untag & RTL8366S_VLAN_UNTAG_MASK) <<
-                       RTL8366S_VLAN_UNTAG_SHIFT) |
-                 ((vlanmc->fid & RTL8366S_VLAN_FID_MASK) <<
-                       RTL8366S_VLAN_FID_SHIFT);
-
-       for (i = 0; i < 2; i++) {
-               err = rtl8366_smi_write_reg(smi,
-                                           RTL8366S_VLAN_MC_BASE(index) + i,
-                                           data[i]);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int rtl8366s_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
-{
-       u32 data;
-       int err;
-
-       if (port >= RTL8366S_NUM_PORTS)
-               return -EINVAL;
-
-       err = rtl8366_smi_read_reg(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
-                                  &data);
-       if (err)
-               return err;
-
-       *val = (data >> RTL8366S_PORT_VLAN_CTRL_SHIFT(port)) &
-              RTL8366S_PORT_VLAN_CTRL_MASK;
-
-       return 0;
-}
-
-static int rtl8366s_set_mc_index(struct rtl8366_smi *smi, int port, int index)
-{
-       if (port >= RTL8366S_NUM_PORTS || index >= RTL8366S_NUM_VLANS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8366S_PORT_VLAN_CTRL_REG(port),
-                               RTL8366S_PORT_VLAN_CTRL_MASK <<
-                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port),
-                               (index & RTL8366S_PORT_VLAN_CTRL_MASK) <<
-                                       RTL8366S_PORT_VLAN_CTRL_SHIFT(port));
-}
-
-static int rtl8366s_enable_vlan(struct rtl8366_smi *smi, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR, RTL8366S_SGCR_EN_VLAN,
-                               (enable) ? RTL8366S_SGCR_EN_VLAN : 0);
-}
-
-static int rtl8366s_enable_vlan4k(struct rtl8366_smi *smi, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8366S_VLAN_TB_CTRL_REG,
-                               1, (enable) ? 1 : 0);
-}
-
-static int rtl8366s_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
-{
-       unsigned max = RTL8366S_NUM_VLANS;
-
-       if (smi->vlan4k_enabled)
-               max = RTL8366S_NUM_VIDS - 1;
-
-       if (vlan == 0 || vlan >= max)
-               return 0;
-
-       return 1;
-}
-
-static int rtl8366s_enable_port(struct rtl8366_smi *smi, int port, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8366S_PECR, (1 << port),
-                               (enable) ? 0 : (1 << port));
-}
-
-static int rtl8366s_sw_reset_mibs(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG, 0, (1 << 2));
-}
-
-static int rtl8366s_sw_get_blinkrate(struct switch_dev *dev,
-                                    const struct switch_attr *attr,
-                                    struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366S_LED_BLINKRATE_REG, &data);
-
-       val->value.i = (data & (RTL8366S_LED_BLINKRATE_MASK));
-
-       return 0;
-}
-
-static int rtl8366s_sw_set_blinkrate(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (val->value.i >= 6)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8366S_LED_BLINKRATE_REG,
-                               RTL8366S_LED_BLINKRATE_MASK,
-                               val->value.i);
-}
-
-static int rtl8366s_sw_get_max_length(struct switch_dev *dev,
-                                       const struct switch_attr *attr,
-                                       struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8366S_SGCR, &data);
-
-       val->value.i = ((data & (RTL8366S_SGCR_MAX_LENGTH_MASK)) >> 4);
-
-       return 0;
-}
-
-static int rtl8366s_sw_set_max_length(struct switch_dev *dev,
-                                       const struct switch_attr *attr,
-                                       struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       char length_code;
-
-       switch (val->value.i) {
-               case 0:
-                       length_code = RTL8366S_SGCR_MAX_LENGTH_1522;
-                       break;
-               case 1:
-                       length_code = RTL8366S_SGCR_MAX_LENGTH_1536;
-                       break;
-               case 2:
-                       length_code = RTL8366S_SGCR_MAX_LENGTH_1552;
-                       break;
-               case 3:
-                       length_code = RTL8366S_SGCR_MAX_LENGTH_16000;
-                       break;
-               default:
-                       return -EINVAL;
-       }
-
-       return rtl8366_smi_rmwr(smi, RTL8366S_SGCR,
-                       RTL8366S_SGCR_MAX_LENGTH_MASK,
-                       length_code);
-}
-
-static int rtl8366s_sw_get_learning_enable(struct switch_dev *dev,
-                                          const struct switch_attr *attr,
-                                          struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi,RTL8366S_SSCR0, &data);
-       val->value.i = !data;
-
-       return 0;
-}
-
-
-static int rtl8366s_sw_set_learning_enable(struct switch_dev *dev,
-                                          const struct switch_attr *attr,
-                                          struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 portmask = 0;
-       int err = 0;
-
-       if (!val->value.i)
-               portmask = RTL8366S_PORT_ALL;
-
-       /* set learning for all ports */
-       REG_WR(smi, RTL8366S_SSCR0, portmask);
-
-       /* set auto ageing for all ports */
-       REG_WR(smi, RTL8366S_SSCR1, portmask);
-
-       return 0;
-}
-
-static int rtl8366s_sw_get_green(struct switch_dev *dev,
-                             const struct switch_attr *attr,
-                             struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-       int err;
-
-       err = rtl8366_smi_read_reg(smi, RTL8366S_GREEN_ETHERNET_CTRL_REG, &data);
-       if (err)
-               return err;
-
-       val->value.i = ((data & (RTL8366S_GREEN_ETHERNET_TX_BIT | RTL8366S_GREEN_ETHERNET_RX_BIT)) != 0) ? 1 : 0;
-
-       return 0;
-}
-
-static int rtl8366s_sw_set_green(struct switch_dev *dev,
-                                const struct switch_attr *attr,
-                                struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       return rtl8366s_set_green(smi, val->value.i);
-}
-
-static int rtl8366s_sw_get_port_link(struct switch_dev *dev,
-                                    int port,
-                                    struct switch_port_link *link)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data = 0;
-       u32 speed;
-
-       if (port >= RTL8366S_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366S_PORT_LINK_STATUS_BASE + (port / 2),
-                            &data);
-
-       if (port % 2)
-               data = data >> 8;
-
-       link->link = !!(data & RTL8366S_PORT_STATUS_LINK_MASK);
-       if (!link->link)
-               return 0;
-
-       link->duplex = !!(data & RTL8366S_PORT_STATUS_DUPLEX_MASK);
-       link->rx_flow = !!(data & RTL8366S_PORT_STATUS_RXPAUSE_MASK);
-       link->tx_flow = !!(data & RTL8366S_PORT_STATUS_TXPAUSE_MASK);
-       link->aneg = !!(data & RTL8366S_PORT_STATUS_AN_MASK);
-
-       speed = (data & RTL8366S_PORT_STATUS_SPEED_MASK);
-       switch (speed) {
-       case 0:
-               link->speed = SWITCH_PORT_SPEED_10;
-               break;
-       case 1:
-               link->speed = SWITCH_PORT_SPEED_100;
-               break;
-       case 2:
-               link->speed = SWITCH_PORT_SPEED_1000;
-               break;
-       default:
-               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int rtl8366s_sw_set_port_led(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-       u32 mask;
-       u32 reg;
-
-       if (val->port_vlan >= RTL8366S_NUM_PORTS ||
-           (1 << val->port_vlan) == RTL8366S_PORT_UNKNOWN)
-               return -EINVAL;
-
-       if (val->port_vlan == RTL8366S_PORT_NUM_CPU) {
-               reg = RTL8366S_LED_BLINKRATE_REG;
-               mask = 0xF << 4;
-               data = val->value.i << 4;
-       } else {
-               reg = RTL8366S_LED_CTRL_REG;
-               mask = 0xF << (val->port_vlan * 4),
-               data = val->value.i << (val->port_vlan * 4);
-       }
-
-       return rtl8366_smi_rmwr(smi, reg, mask, data);
-}
-
-static int rtl8366s_sw_get_port_led(struct switch_dev *dev,
-                                   const struct switch_attr *attr,
-                                   struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data = 0;
-
-       if (val->port_vlan >= RTL8366S_NUM_LEDGROUPS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8366S_LED_CTRL_REG, &data);
-       val->value.i = (data >> (val->port_vlan * 4)) & 0x000F;
-
-       return 0;
-}
-
-static int rtl8366s_sw_get_green_port(struct switch_dev *dev,
-                                     const struct switch_attr *attr,
-                                     struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int err;
-       u32 phyData;
-
-       if (val->port_vlan >= RTL8366S_NUM_PORTS)
-               return -EINVAL;
-
-       err = rtl8366s_read_phy_reg(smi, val->port_vlan, 0, RTL8366S_PHY_POWER_SAVING_CTRL_REG, &phyData);
-       if (err)
-               return err;
-
-       val->value.i = ((phyData & RTL8366S_PHY_POWER_SAVING_MASK) != 0) ? 1 : 0;
-
-       return 0;
-}
-
-static int rtl8366s_sw_set_green_port(struct switch_dev *dev,
-                                     const struct switch_attr *attr,
-                                     struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       return rtl8366s_set_green_port(smi, val->port_vlan, val->value.i);
-}
-
-static int rtl8366s_sw_reset_port_mibs(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       if (val->port_vlan >= RTL8366S_NUM_PORTS)
-               return -EINVAL;
-
-
-       return rtl8366_smi_rmwr(smi, RTL8366S_MIB_CTRL_REG,
-                               0, (1 << (val->port_vlan + 3)));
-}
-
-static int rtl8366s_sw_get_port_stats(struct switch_dev *dev, int port,
-                                        struct switch_port_stats *stats)
-{
-       return (rtl8366_sw_get_port_stats(dev, port, stats,
-                               RTL8366S_MIB_TXB_ID, RTL8366S_MIB_RXB_ID));
-}
-
-static struct switch_attr rtl8366s_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_learning",
-               .description = "Enable learning, enable aging",
-               .set = rtl8366s_sw_set_learning_enable,
-               .get = rtl8366s_sw_get_learning_enable,
-               .max = 1,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan4k",
-               .description = "Enable VLAN 4K mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 2
-       }, {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mibs",
-               .description = "Reset all MIB counters",
-               .set = rtl8366s_sw_reset_mibs,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "blinkrate",
-               .description = "Get/Set LED blinking rate (0 = 43ms, 1 = 84ms,"
-               " 2 = 120ms, 3 = 170ms, 4 = 340ms, 5 = 670ms)",
-               .set = rtl8366s_sw_set_blinkrate,
-               .get = rtl8366s_sw_get_blinkrate,
-               .max = 5
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "max_length",
-               .description = "Get/Set the maximum length of valid packets"
-               " (0 = 1522, 1 = 1536, 2 = 1552, 3 = 16000 (9216?))",
-               .set = rtl8366s_sw_set_max_length,
-               .get = rtl8366s_sw_get_max_length,
-               .max = 3,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "green_mode",
-               .description = "Get/Set the router green feature",
-               .set = rtl8366s_sw_set_green,
-               .get = rtl8366s_sw_get_green,
-               .max = 1,
-       },
-};
-
-static struct switch_attr rtl8366s_port[] = {
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mib",
-               .description = "Reset single port MIB counters",
-               .set = rtl8366s_sw_reset_port_mibs,
-       }, {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get MIB counters for port",
-               .max = 33,
-               .set = NULL,
-               .get = rtl8366_sw_get_port_mib,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "led",
-               .description = "Get/Set port group (0 - 3) led mode (0 - 15)",
-               .max = 15,
-               .set = rtl8366s_sw_set_port_led,
-               .get = rtl8366s_sw_get_port_led,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "green_port",
-               .description = "Get/Set port green feature (0 - 1)",
-               .max = 1,
-               .set = rtl8366s_sw_set_green_port,
-               .get = rtl8366s_sw_get_green_port,
-       },
-};
-
-static struct switch_attr rtl8366s_vlan[] = {
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "info",
-               .description = "Get vlan information",
-               .max = 1,
-               .set = NULL,
-               .get = rtl8366_sw_get_vlan_info,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "fid",
-               .description = "Get/Set vlan FID",
-               .max = RTL8366S_FIDMAX,
-               .set = rtl8366_sw_set_vlan_fid,
-               .get = rtl8366_sw_get_vlan_fid,
-       },
-};
-
-static const struct switch_dev_ops rtl8366_ops = {
-       .attr_global = {
-               .attr = rtl8366s_globals,
-               .n_attr = ARRAY_SIZE(rtl8366s_globals),
-       },
-       .attr_port = {
-               .attr = rtl8366s_port,
-               .n_attr = ARRAY_SIZE(rtl8366s_port),
-       },
-       .attr_vlan = {
-               .attr = rtl8366s_vlan,
-               .n_attr = ARRAY_SIZE(rtl8366s_vlan),
-       },
-
-       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
-       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
-       .get_port_pvid = rtl8366_sw_get_port_pvid,
-       .set_port_pvid = rtl8366_sw_set_port_pvid,
-       .reset_switch = rtl8366_sw_reset_switch,
-       .get_port_link = rtl8366s_sw_get_port_link,
-       .get_port_stats = rtl8366s_sw_get_port_stats,
-};
-
-static int rtl8366s_switch_init(struct rtl8366_smi *smi)
-{
-       struct switch_dev *dev = &smi->sw_dev;
-       int err;
-
-       dev->name = "RTL8366S";
-       dev->cpu_port = RTL8366S_PORT_NUM_CPU;
-       dev->ports = RTL8366S_NUM_PORTS;
-       dev->vlans = RTL8366S_NUM_VIDS;
-       dev->ops = &rtl8366_ops;
-       dev->alias = dev_name(smi->parent);
-
-       err = register_switch(dev, NULL);
-       if (err)
-               dev_err(smi->parent, "switch registration failed\n");
-
-       return err;
-}
-
-static void rtl8366s_switch_cleanup(struct rtl8366_smi *smi)
-{
-       unregister_switch(&smi->sw_dev);
-}
-
-static int rtl8366s_mii_read(struct mii_bus *bus, int addr, int reg)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 val = 0;
-       int err;
-
-       err = rtl8366s_read_phy_reg(smi, addr, 0, reg, &val);
-       if (err)
-               return 0xffff;
-
-       return val;
-}
-
-static int rtl8366s_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 t;
-       int err;
-
-       err = rtl8366s_write_phy_reg(smi, addr, 0, reg, val);
-       /* flush write */
-       (void) rtl8366s_read_phy_reg(smi, addr, 0, reg, &t);
-
-       return err;
-}
-
-static int rtl8366s_detect(struct rtl8366_smi *smi)
-{
-       u32 chip_id = 0;
-       u32 chip_ver = 0;
-       int ret;
-
-       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_ID_REG, &chip_id);
-       if (ret) {
-               dev_err(smi->parent, "unable to read chip id\n");
-               return ret;
-       }
-
-       switch (chip_id) {
-       case RTL8366S_CHIP_ID_8366:
-               break;
-       default:
-               dev_err(smi->parent, "unknown chip id (%04x)\n", chip_id);
-               return -ENODEV;
-       }
-
-       ret = rtl8366_smi_read_reg(smi, RTL8366S_CHIP_VERSION_CTRL_REG,
-                                  &chip_ver);
-       if (ret) {
-               dev_err(smi->parent, "unable to read chip version\n");
-               return ret;
-       }
-
-       dev_info(smi->parent, "RTL%04x ver. %u chip found\n",
-                chip_id, chip_ver & RTL8366S_CHIP_VERSION_MASK);
-
-       return 0;
-}
-
-static struct rtl8366_smi_ops rtl8366s_smi_ops = {
-       .detect         = rtl8366s_detect,
-       .reset_chip     = rtl8366s_reset_chip,
-       .setup          = rtl8366s_setup,
-
-       .mii_read       = rtl8366s_mii_read,
-       .mii_write      = rtl8366s_mii_write,
-
-       .get_vlan_mc    = rtl8366s_get_vlan_mc,
-       .set_vlan_mc    = rtl8366s_set_vlan_mc,
-       .get_vlan_4k    = rtl8366s_get_vlan_4k,
-       .set_vlan_4k    = rtl8366s_set_vlan_4k,
-       .get_mc_index   = rtl8366s_get_mc_index,
-       .set_mc_index   = rtl8366s_set_mc_index,
-       .get_mib_counter = rtl8366_get_mib_counter,
-       .is_vlan_valid  = rtl8366s_is_vlan_valid,
-       .enable_vlan    = rtl8366s_enable_vlan,
-       .enable_vlan4k  = rtl8366s_enable_vlan4k,
-       .enable_port    = rtl8366s_enable_port,
-};
-
-static int rtl8366s_probe(struct platform_device *pdev)
-{
-       static int rtl8366_smi_version_printed;
-       struct rtl8366_smi *smi;
-       int err;
-
-       if (!rtl8366_smi_version_printed++)
-               printk(KERN_NOTICE RTL8366S_DRIVER_DESC
-                      " version " RTL8366S_DRIVER_VER"\n");
-
-       smi = rtl8366_smi_probe(pdev);
-       if (!smi)
-               return -ENODEV;
-
-       smi->clk_delay = 10;
-       smi->cmd_read = 0xa9;
-       smi->cmd_write = 0xa8;
-       smi->ops = &rtl8366s_smi_ops;
-       smi->cpu_port = RTL8366S_PORT_NUM_CPU;
-       smi->num_ports = RTL8366S_NUM_PORTS;
-       smi->num_vlan_mc = RTL8366S_NUM_VLANS;
-       smi->mib_counters = rtl8366s_mib_counters;
-       smi->num_mib_counters = ARRAY_SIZE(rtl8366s_mib_counters);
-
-       err = rtl8366_smi_init(smi);
-       if (err)
-               goto err_free_smi;
-
-       platform_set_drvdata(pdev, smi);
-
-       err = rtl8366s_switch_init(smi);
-       if (err)
-               goto err_clear_drvdata;
-
-       return 0;
-
- err_clear_drvdata:
-       platform_set_drvdata(pdev, NULL);
-       rtl8366_smi_cleanup(smi);
- err_free_smi:
-       kfree(smi);
-       return err;
-}
-
-static int rtl8366s_remove(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
-
-       if (smi) {
-               rtl8366s_switch_cleanup(smi);
-               platform_set_drvdata(pdev, NULL);
-               rtl8366_smi_cleanup(smi);
-               kfree(smi);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id rtl8366s_match[] = {
-       { .compatible = "realtek,rtl8366s" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, rtl8366s_match);
-#endif
-
-static struct platform_driver rtl8366s_driver = {
-       .driver = {
-               .name           = RTL8366S_DRIVER_NAME,
-               .owner          = THIS_MODULE,
-#ifdef CONFIG_OF
-               .of_match_table = of_match_ptr(rtl8366s_match),
-#endif
-       },
-       .probe          = rtl8366s_probe,
-       .remove         = rtl8366s_remove,
-};
-
-static int __init rtl8366s_module_init(void)
-{
-       return platform_driver_register(&rtl8366s_driver);
-}
-module_init(rtl8366s_module_init);
-
-static void __exit rtl8366s_module_exit(void)
-{
-       platform_driver_unregister(&rtl8366s_driver);
-}
-module_exit(rtl8366s_module_exit);
-
-MODULE_DESCRIPTION(RTL8366S_DRIVER_DESC);
-MODULE_VERSION(RTL8366S_DRIVER_VER);
-MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
-MODULE_AUTHOR("Antti Seppälä <a.seppala@gmail.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" RTL8366S_DRIVER_NAME);
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367.c b/target/linux/generic/files/drivers/net/phy/rtl8367.c
deleted file mode 100644 (file)
index 9549961..0000000
+++ /dev/null
@@ -1,1846 +0,0 @@
-/*
- * Platform driver for the Realtek RTL8367R/M ethernet switches
- *
- * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/rtl8367.h>
-
-#include "rtl8366_smi.h"
-
-#define RTL8367_RESET_DELAY    1000    /* msecs*/
-
-#define RTL8367_PHY_ADDR_MAX   8
-#define RTL8367_PHY_REG_MAX    31
-
-#define RTL8367_VID_MASK       0xffff
-#define RTL8367_FID_MASK       0xfff
-#define RTL8367_UNTAG_MASK     0xffff
-#define RTL8367_MEMBER_MASK    0xffff
-
-#define RTL8367_PORT_CFG_REG(_p)               (0x000e + 0x20 * (_p))
-#define   RTL8367_PORT_CFG_EGRESS_MODE_SHIFT   4
-#define   RTL8367_PORT_CFG_EGRESS_MODE_MASK    0x3
-#define   RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL        0
-#define   RTL8367_PORT_CFG_EGRESS_MODE_KEEP    1
-#define   RTL8367_PORT_CFG_EGRESS_MODE_PRI     2
-#define   RTL8367_PORT_CFG_EGRESS_MODE_REAL    3
-
-#define RTL8367_BYPASS_LINE_RATE_REG           0x03f7
-
-#define RTL8367_TA_CTRL_REG                    0x0500
-#define   RTL8367_TA_CTRL_STATUS               BIT(12)
-#define   RTL8367_TA_CTRL_METHOD               BIT(5)
-#define   RTL8367_TA_CTRL_CMD_SHIFT            4
-#define   RTL8367_TA_CTRL_CMD_READ             0
-#define   RTL8367_TA_CTRL_CMD_WRITE            1
-#define   RTL8367_TA_CTRL_TABLE_SHIFT          0
-#define   RTL8367_TA_CTRL_TABLE_ACLRULE                1
-#define   RTL8367_TA_CTRL_TABLE_ACLACT         2
-#define   RTL8367_TA_CTRL_TABLE_CVLAN          3
-#define   RTL8367_TA_CTRL_TABLE_L2             4
-#define   RTL8367_TA_CTRL_CVLAN_READ \
-               ((RTL8367_TA_CTRL_CMD_READ << RTL8367_TA_CTRL_CMD_SHIFT) | \
-                RTL8367_TA_CTRL_TABLE_CVLAN)
-#define   RTL8367_TA_CTRL_CVLAN_WRITE \
-               ((RTL8367_TA_CTRL_CMD_WRITE << RTL8367_TA_CTRL_CMD_SHIFT) | \
-                RTL8367_TA_CTRL_TABLE_CVLAN)
-
-#define RTL8367_TA_ADDR_REG                    0x0501
-#define   RTL8367_TA_ADDR_MASK                 0x3fff
-
-#define RTL8367_TA_DATA_REG(_x)                        (0x0503 + (_x))
-#define   RTL8367_TA_VLAN_DATA_SIZE            4
-#define   RTL8367_TA_VLAN_VID_MASK             RTL8367_VID_MASK
-#define   RTL8367_TA_VLAN_MEMBER_SHIFT         0
-#define   RTL8367_TA_VLAN_MEMBER_MASK          RTL8367_MEMBER_MASK
-#define   RTL8367_TA_VLAN_FID_SHIFT            0
-#define   RTL8367_TA_VLAN_FID_MASK             RTL8367_FID_MASK
-#define   RTL8367_TA_VLAN_UNTAG1_SHIFT         14
-#define   RTL8367_TA_VLAN_UNTAG1_MASK          0x3
-#define   RTL8367_TA_VLAN_UNTAG2_SHIFT         0
-#define   RTL8367_TA_VLAN_UNTAG2_MASK          0x3fff
-
-#define RTL8367_VLAN_PVID_CTRL_REG(_p)         (0x0700 + (_p) / 2)
-#define RTL8367_VLAN_PVID_CTRL_MASK            0x1f
-#define RTL8367_VLAN_PVID_CTRL_SHIFT(_p)       (8 * ((_p) % 2))
-
-#define RTL8367_VLAN_MC_BASE(_x)               (0x0728 + (_x) * 4)
-#define   RTL8367_VLAN_MC_DATA_SIZE            4
-#define   RTL8367_VLAN_MC_MEMBER_SHIFT         0
-#define   RTL8367_VLAN_MC_MEMBER_MASK          RTL8367_MEMBER_MASK
-#define   RTL8367_VLAN_MC_FID_SHIFT            0
-#define   RTL8367_VLAN_MC_FID_MASK             RTL8367_FID_MASK
-#define   RTL8367_VLAN_MC_EVID_SHIFT           0
-#define   RTL8367_VLAN_MC_EVID_MASK            RTL8367_VID_MASK
-
-#define RTL8367_VLAN_CTRL_REG                  0x07a8
-#define   RTL8367_VLAN_CTRL_ENABLE             BIT(0)
-
-#define RTL8367_VLAN_INGRESS_REG               0x07a9
-
-#define RTL8367_PORT_ISOLATION_REG(_p)         (0x08a2 + (_p))
-
-#define RTL8367_MIB_COUNTER_REG(_x)            (0x1000 + (_x))
-
-#define RTL8367_MIB_ADDRESS_REG                        0x1004
-
-#define RTL8367_MIB_CTRL_REG(_x)               (0x1005 + (_x))
-#define   RTL8367_MIB_CTRL_GLOBAL_RESET_MASK   BIT(11)
-#define   RTL8367_MIB_CTRL_QM_RESET_MASK       BIT(10)
-#define   RTL8367_MIB_CTRL_PORT_RESET_MASK(_p) BIT(2 + (_p))
-#define   RTL8367_MIB_CTRL_RESET_MASK          BIT(1)
-#define   RTL8367_MIB_CTRL_BUSY_MASK           BIT(0)
-
-#define RTL8367_MIB_COUNT                      36
-#define RTL8367_MIB_COUNTER_PORT_OFFSET                0x0050
-
-#define RTL8367_SWC0_REG                       0x1200
-#define   RTL8367_SWC0_MAX_LENGTH_SHIFT                13
-#define   RTL8367_SWC0_MAX_LENGTH(_x)          ((_x) << 13)
-#define   RTL8367_SWC0_MAX_LENGTH_MASK         RTL8367_SWC0_MAX_LENGTH(0x3)
-#define   RTL8367_SWC0_MAX_LENGTH_1522         RTL8367_SWC0_MAX_LENGTH(0)
-#define   RTL8367_SWC0_MAX_LENGTH_1536         RTL8367_SWC0_MAX_LENGTH(1)
-#define   RTL8367_SWC0_MAX_LENGTH_1552         RTL8367_SWC0_MAX_LENGTH(2)
-#define   RTL8367_SWC0_MAX_LENGTH_16000                RTL8367_SWC0_MAX_LENGTH(3)
-
-#define RTL8367_CHIP_NUMBER_REG                        0x1300
-
-#define RTL8367_CHIP_VER_REG                   0x1301
-#define   RTL8367_CHIP_VER_RLVID_SHIFT         12
-#define   RTL8367_CHIP_VER_RLVID_MASK          0xf
-#define   RTL8367_CHIP_VER_MCID_SHIFT          8
-#define   RTL8367_CHIP_VER_MCID_MASK           0xf
-#define   RTL8367_CHIP_VER_BOID_SHIFT          4
-#define   RTL8367_CHIP_VER_BOID_MASK           0xf
-
-#define RTL8367_CHIP_MODE_REG                  0x1302
-#define   RTL8367_CHIP_MODE_MASK               0x7
-
-#define RTL8367_CHIP_DEBUG0_REG                        0x1303
-#define   RTL8367_CHIP_DEBUG0_DUMMY0(_x)       BIT(8 + (_x))
-
-#define RTL8367_CHIP_DEBUG1_REG                        0x1304
-
-#define RTL8367_DIS_REG                                0x1305
-#define   RTL8367_DIS_SKIP_MII_RXER(_x)                BIT(12 + (_x))
-#define   RTL8367_DIS_RGMII_SHIFT(_x)          (4 * (_x))
-#define   RTL8367_DIS_RGMII_MASK               0x7
-
-#define RTL8367_EXT_RGMXF_REG(_x)              (0x1306 + (_x))
-#define   RTL8367_EXT_RGMXF_DUMMY0_SHIFT       5
-#define   RTL8367_EXT_RGMXF_DUMMY0_MASK        0x7ff
-#define   RTL8367_EXT_RGMXF_TXDELAY_SHIFT      3
-#define   RTL8367_EXT_RGMXF_TXDELAY_MASK       1
-#define   RTL8367_EXT_RGMXF_RXDELAY_MASK       0x7
-
-#define RTL8367_DI_FORCE_REG(_x)               (0x1310 + (_x))
-#define   RTL8367_DI_FORCE_MODE                        BIT(12)
-#define   RTL8367_DI_FORCE_NWAY                        BIT(7)
-#define   RTL8367_DI_FORCE_TXPAUSE             BIT(6)
-#define   RTL8367_DI_FORCE_RXPAUSE             BIT(5)
-#define   RTL8367_DI_FORCE_LINK                        BIT(4)
-#define   RTL8367_DI_FORCE_DUPLEX              BIT(2)
-#define   RTL8367_DI_FORCE_SPEED_MASK          3
-#define   RTL8367_DI_FORCE_SPEED_10            0
-#define   RTL8367_DI_FORCE_SPEED_100           1
-#define   RTL8367_DI_FORCE_SPEED_1000          2
-
-#define RTL8367_MAC_FORCE_REG(_x)              (0x1312 + (_x))
-
-#define RTL8367_CHIP_RESET_REG                 0x1322
-#define   RTL8367_CHIP_RESET_SW                        BIT(1)
-#define   RTL8367_CHIP_RESET_HW                        BIT(0)
-
-#define RTL8367_PORT_STATUS_REG(_p)            (0x1352 + (_p))
-#define   RTL8367_PORT_STATUS_NWAY             BIT(7)
-#define   RTL8367_PORT_STATUS_TXPAUSE          BIT(6)
-#define   RTL8367_PORT_STATUS_RXPAUSE          BIT(5)
-#define   RTL8367_PORT_STATUS_LINK             BIT(4)
-#define   RTL8367_PORT_STATUS_DUPLEX           BIT(2)
-#define   RTL8367_PORT_STATUS_SPEED_MASK       0x0003
-#define   RTL8367_PORT_STATUS_SPEED_10         0
-#define   RTL8367_PORT_STATUS_SPEED_100                1
-#define   RTL8367_PORT_STATUS_SPEED_1000       2
-
-#define RTL8367_RTL_NO_REG                     0x13c0
-#define   RTL8367_RTL_NO_8367R                 0x3670
-#define   RTL8367_RTL_NO_8367M                 0x3671
-
-#define RTL8367_RTL_VER_REG                    0x13c1
-#define   RTL8367_RTL_VER_MASK                 0xf
-
-#define RTL8367_RTL_MAGIC_ID_REG               0x13c2
-#define   RTL8367_RTL_MAGIC_ID_VAL             0x0249
-
-#define RTL8367_LED_SYS_CONFIG_REG             0x1b00
-#define RTL8367_LED_MODE_REG                   0x1b02
-#define   RTL8367_LED_MODE_RATE_M              0x7
-#define   RTL8367_LED_MODE_RATE_S              1
-
-#define RTL8367_LED_CONFIG_REG                 0x1b03
-#define   RTL8367_LED_CONFIG_DATA_S            12
-#define   RTL8367_LED_CONFIG_DATA_M            0x3
-#define   RTL8367_LED_CONFIG_SEL               BIT(14)
-#define   RTL8367_LED_CONFIG_LED_CFG_M         0xf
-
-#define RTL8367_PARA_LED_IO_EN1_REG            0x1b24
-#define RTL8367_PARA_LED_IO_EN2_REG            0x1b25
-#define   RTL8367_PARA_LED_IO_EN_PMASK         0xff
-
-#define RTL8367_IA_CTRL_REG                    0x1f00
-#define   RTL8367_IA_CTRL_RW(_x)               ((_x) << 1)
-#define   RTL8367_IA_CTRL_RW_READ              RTL8367_IA_CTRL_RW(0)
-#define   RTL8367_IA_CTRL_RW_WRITE             RTL8367_IA_CTRL_RW(1)
-#define   RTL8367_IA_CTRL_CMD_MASK             BIT(0)
-
-#define RTL8367_IA_STATUS_REG                  0x1f01
-#define   RTL8367_IA_STATUS_PHY_BUSY           BIT(2)
-#define   RTL8367_IA_STATUS_SDS_BUSY           BIT(1)
-#define   RTL8367_IA_STATUS_MDX_BUSY           BIT(0)
-
-#define RTL8367_IA_ADDRESS_REG                 0x1f02
-
-#define RTL8367_IA_WRITE_DATA_REG              0x1f03
-#define RTL8367_IA_READ_DATA_REG               0x1f04
-
-#define RTL8367_INTERNAL_PHY_REG(_a, _r)       (0x2000 + 32 * (_a) + (_r))
-
-#define RTL8367_CPU_PORT_NUM           9
-#define RTL8367_NUM_PORTS              10
-#define RTL8367_NUM_VLANS              32
-#define RTL8367_NUM_LEDGROUPS          4
-#define RTL8367_NUM_VIDS               4096
-#define RTL8367_PRIORITYMAX            7
-#define RTL8367_FIDMAX                 7
-
-#define RTL8367_PORT_0                 BIT(0)
-#define RTL8367_PORT_1                 BIT(1)
-#define RTL8367_PORT_2                 BIT(2)
-#define RTL8367_PORT_3                 BIT(3)
-#define RTL8367_PORT_4                 BIT(4)
-#define RTL8367_PORT_5                 BIT(5)
-#define RTL8367_PORT_6                 BIT(6)
-#define RTL8367_PORT_7                 BIT(7)
-#define RTL8367_PORT_E1                        BIT(8)  /* external port 1 */
-#define RTL8367_PORT_E0                        BIT(9)  /* external port 0 */
-
-#define RTL8367_PORTS_ALL                                      \
-       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
-        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
-        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1 |    \
-        RTL8367_PORT_E0)
-
-#define RTL8367_PORTS_ALL_BUT_CPU                              \
-       (RTL8367_PORT_0 | RTL8367_PORT_1 | RTL8367_PORT_2 |     \
-        RTL8367_PORT_3 | RTL8367_PORT_4 | RTL8367_PORT_5 |     \
-        RTL8367_PORT_6 | RTL8367_PORT_7 | RTL8367_PORT_E1)
-
-struct rtl8367_initval {
-       u16 reg;
-       u16 val;
-};
-
-#define RTL8367_MIB_RXB_ID             0       /* IfInOctets */
-#define RTL8367_MIB_TXB_ID             20      /* IfOutOctets */
-
-static struct rtl8366_mib_counter rtl8367_mib_counters[] = {
-       { 0,  0, 4, "IfInOctets"                                },
-       { 0,  4, 2, "Dot3StatsFCSErrors"                        },
-       { 0,  6, 2, "Dot3StatsSymbolErrors"                     },
-       { 0,  8, 2, "Dot3InPauseFrames"                         },
-       { 0, 10, 2, "Dot3ControlInUnknownOpcodes"               },
-       { 0, 12, 2, "EtherStatsFragments"                       },
-       { 0, 14, 2, "EtherStatsJabbers"                         },
-       { 0, 16, 2, "IfInUcastPkts"                             },
-       { 0, 18, 2, "EtherStatsDropEvents"                      },
-       { 0, 20, 4, "EtherStatsOctets"                          },
-
-       { 0, 24, 2, "EtherStatsUnderSizePkts"                   },
-       { 0, 26, 2, "EtherOversizeStats"                        },
-       { 0, 28, 2, "EtherStatsPkts64Octets"                    },
-       { 0, 30, 2, "EtherStatsPkts65to127Octets"               },
-       { 0, 32, 2, "EtherStatsPkts128to255Octets"              },
-       { 0, 34, 2, "EtherStatsPkts256to511Octets"              },
-       { 0, 36, 2, "EtherStatsPkts512to1023Octets"             },
-       { 0, 38, 2, "EtherStatsPkts1024to1518Octets"            },
-       { 0, 40, 2, "EtherStatsMulticastPkts"                   },
-       { 0, 42, 2, "EtherStatsBroadcastPkts"                   },
-
-       { 0, 44, 4, "IfOutOctets"                               },
-
-       { 0, 48, 2, "Dot3StatsSingleCollisionFrames"            },
-       { 0, 50, 2, "Dot3StatMultipleCollisionFrames"           },
-       { 0, 52, 2, "Dot3sDeferredTransmissions"                },
-       { 0, 54, 2, "Dot3StatsLateCollisions"                   },
-       { 0, 56, 2, "EtherStatsCollisions"                      },
-       { 0, 58, 2, "Dot3StatsExcessiveCollisions"              },
-       { 0, 60, 2, "Dot3OutPauseFrames"                        },
-       { 0, 62, 2, "Dot1dBasePortDelayExceededDiscards"        },
-       { 0, 64, 2, "Dot1dTpPortInDiscards"                     },
-       { 0, 66, 2, "IfOutUcastPkts"                            },
-       { 0, 68, 2, "IfOutMulticastPkts"                        },
-       { 0, 70, 2, "IfOutBroadcastPkts"                        },
-       { 0, 72, 2, "OutOampduPkts"                             },
-       { 0, 74, 2, "InOampduPkts"                              },
-       { 0, 76, 2, "PktgenPkts"                                },
-};
-
-#define REG_RD(_smi, _reg, _val)                                       \
-       do {                                                            \
-               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-#define REG_WR(_smi, _reg, _val)                                       \
-       do {                                                            \
-               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-#define REG_RMW(_smi, _reg, _mask, _val)                               \
-       do {                                                            \
-               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-static const struct rtl8367_initval rtl8367_initvals_0_0[] = {
-       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
-       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
-       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
-       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
-       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
-       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
-       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
-       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
-       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
-       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
-       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
-       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
-       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
-       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
-       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
-       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
-       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
-       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
-       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
-       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
-       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
-       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
-       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1006}, {0x121e, 0x03e8},
-       {0x121f, 0x02b3}, {0x1220, 0x028f}, {0x1221, 0x029b}, {0x1222, 0x0277},
-       {0x1223, 0x02b3}, {0x1224, 0x028f}, {0x1225, 0x029b}, {0x1226, 0x0277},
-       {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0}, {0x1230, 0x00b4},
-       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
-       {0x0219, 0x0032}, {0x0200, 0x03e8}, {0x0201, 0x03e8}, {0x0202, 0x03e8},
-       {0x0203, 0x03e8}, {0x0204, 0x03e8}, {0x0205, 0x03e8}, {0x0206, 0x03e8},
-       {0x0207, 0x03e8}, {0x0218, 0x0032}, {0x0208, 0x029b}, {0x0209, 0x029b},
-       {0x020a, 0x029b}, {0x020b, 0x029b}, {0x020c, 0x029b}, {0x020d, 0x029b},
-       {0x020e, 0x029b}, {0x020f, 0x029b}, {0x0210, 0x029b}, {0x0211, 0x029b},
-       {0x0212, 0x029b}, {0x0213, 0x029b}, {0x0214, 0x029b}, {0x0215, 0x029b},
-       {0x0216, 0x029b}, {0x0217, 0x029b}, {0x0900, 0x0000}, {0x0901, 0x0000},
-       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
-       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
-       {0x0802, 0x0100}, {0x1700, 0x014C}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
-       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
-       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
-       {0x133f, 0x0010}, {0x20A0, 0x1940}, {0x20C0, 0x1940}, {0x20E0, 0x1940},
-};
-
-static const struct rtl8367_initval rtl8367_initvals_0_1[] = {
-       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0000}, {0x2215, 0x1006},
-       {0x221f, 0x0005}, {0x2200, 0x00c6}, {0x221f, 0x0007}, {0x221e, 0x0048},
-       {0x2215, 0x6412}, {0x2216, 0x6412}, {0x2217, 0x6412}, {0x2218, 0x6412},
-       {0x2219, 0x6412}, {0x221A, 0x6412}, {0x221f, 0x0001}, {0x220c, 0xdbf0},
-       {0x2209, 0x2576}, {0x2207, 0x287E}, {0x220A, 0x68E5}, {0x221D, 0x3DA4},
-       {0x221C, 0xE7F7}, {0x2214, 0x7F52}, {0x2218, 0x7FCE}, {0x2208, 0x04B7},
-       {0x2206, 0x4072}, {0x2210, 0xF05E}, {0x221B, 0xB414}, {0x221F, 0x0003},
-       {0x221A, 0x06A6}, {0x2210, 0xF05E}, {0x2213, 0x06EB}, {0x2212, 0xF4D2},
-       {0x220E, 0xE120}, {0x2200, 0x7C00}, {0x2202, 0x5FD0}, {0x220D, 0x0207},
-       {0x221f, 0x0002}, {0x2205, 0x0978}, {0x2202, 0x8C01}, {0x2207, 0x3620},
-       {0x221C, 0x0001}, {0x2203, 0x0420}, {0x2204, 0x80C8}, {0x133e, 0x0ede},
-       {0x221f, 0x0002}, {0x220c, 0x0073}, {0x220d, 0xEB65}, {0x220e, 0x51d1},
-       {0x220f, 0x5dcb}, {0x2210, 0x3044}, {0x2211, 0x1800}, {0x2212, 0x7E00},
-       {0x2213, 0x0000}, {0x133f, 0x0010}, {0x133e, 0x0ffe}, {0x207f, 0x0002},
-       {0x2074, 0x3D22}, {0x2075, 0x2000}, {0x2076, 0x6040}, {0x2077, 0x0000},
-       {0x2078, 0x0f0a}, {0x2079, 0x50AB}, {0x207a, 0x0000}, {0x207b, 0x0f0f},
-       {0x205f, 0x0002}, {0x2054, 0xFF00}, {0x2055, 0x000A}, {0x2056, 0x000A},
-       {0x2057, 0x0005}, {0x2058, 0x0005}, {0x2059, 0x0000}, {0x205A, 0x0005},
-       {0x205B, 0x0005}, {0x205C, 0x0005}, {0x209f, 0x0002}, {0x2094, 0x00AA},
-       {0x2095, 0x00AA}, {0x2096, 0x00AA}, {0x2097, 0x00AA}, {0x2098, 0x0055},
-       {0x2099, 0x00AA}, {0x209A, 0x00AA}, {0x209B, 0x00AA}, {0x1363, 0x8354},
-       {0x1270, 0x3333}, {0x1271, 0x3333}, {0x1272, 0x3333}, {0x1330, 0x00DB},
-       {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x1b06}, {0x121e, 0x07f0},
-       {0x121f, 0x0438}, {0x1220, 0x040f}, {0x1221, 0x040f}, {0x1222, 0x03eb},
-       {0x1223, 0x0438}, {0x1224, 0x040f}, {0x1225, 0x040f}, {0x1226, 0x03eb},
-       {0x1227, 0x0144}, {0x1228, 0x0138}, {0x122f, 0x0144}, {0x1230, 0x0138},
-       {0x1229, 0x0020}, {0x122a, 0x000c}, {0x1231, 0x0030}, {0x1232, 0x0024},
-       {0x0219, 0x0032}, {0x0200, 0x07d0}, {0x0201, 0x07d0}, {0x0202, 0x07d0},
-       {0x0203, 0x07d0}, {0x0204, 0x07d0}, {0x0205, 0x07d0}, {0x0206, 0x07d0},
-       {0x0207, 0x07d0}, {0x0218, 0x0032}, {0x0208, 0x0190}, {0x0209, 0x0190},
-       {0x020a, 0x0190}, {0x020b, 0x0190}, {0x020c, 0x0190}, {0x020d, 0x0190},
-       {0x020e, 0x0190}, {0x020f, 0x0190}, {0x0210, 0x0190}, {0x0211, 0x0190},
-       {0x0212, 0x0190}, {0x0213, 0x0190}, {0x0214, 0x0190}, {0x0215, 0x0190},
-       {0x0216, 0x0190}, {0x0217, 0x0190}, {0x0900, 0x0000}, {0x0901, 0x0000},
-       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000},
-       {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100},
-       {0x0802, 0x0100}, {0x1700, 0x0125}, {0x0301, 0x00FF}, {0x12AA, 0x0096},
-       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0005}, {0x2200, 0x00C4},
-       {0x221f, 0x0000}, {0x2210, 0x05EF}, {0x2204, 0x05E1}, {0x2200, 0x1340},
-       {0x133f, 0x0010},
-};
-
-static const struct rtl8367_initval rtl8367_initvals_1_0[] = {
-       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
-       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
-       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
-       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
-       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
-       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
-       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
-       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
-       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
-       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
-       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
-       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
-       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
-       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
-       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
-       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
-       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
-       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
-       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
-       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
-       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
-       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
-       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
-       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
-       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
-       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
-       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
-       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
-       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
-       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
-       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
-       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
-       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
-       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
-       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
-       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
-       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
-       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
-       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
-       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
-       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
-       {0x121D, 0x7D16}, {0x121E, 0x03E8}, {0x121F, 0x024E}, {0x1220, 0x0230},
-       {0x1221, 0x0244}, {0x1222, 0x0226}, {0x1223, 0x024E}, {0x1224, 0x0230},
-       {0x1225, 0x0244}, {0x1226, 0x0226}, {0x1227, 0x00C0}, {0x1228, 0x00B4},
-       {0x122F, 0x00C0}, {0x1230, 0x00B4}, {0x0208, 0x03E8}, {0x0209, 0x03E8},
-       {0x020A, 0x03E8}, {0x020B, 0x03E8}, {0x020C, 0x03E8}, {0x020D, 0x03E8},
-       {0x020E, 0x03E8}, {0x020F, 0x03E8}, {0x0210, 0x03E8}, {0x0211, 0x03E8},
-       {0x0212, 0x03E8}, {0x0213, 0x03E8}, {0x0214, 0x03E8}, {0x0215, 0x03E8},
-       {0x0216, 0x03E8}, {0x0217, 0x03E8}, {0x0900, 0x0000}, {0x0901, 0x0000},
-       {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087B, 0x0000},
-       {0x087C, 0xFF00}, {0x087D, 0x0000}, {0x087E, 0x0000}, {0x0801, 0x0100},
-       {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040},
-       {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
-       {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000}, {0x2200, 0x1340},
-       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x20A0, 0x1940},
-       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
-};
-
-static const struct rtl8367_initval rtl8367_initvals_1_1[] = {
-       {0x1B24, 0x0000}, {0x1B25, 0x0000}, {0x1B26, 0x0000}, {0x1B27, 0x0000},
-       {0x207F, 0x0002}, {0x2079, 0x0200}, {0x207F, 0x0000}, {0x133F, 0x0030},
-       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2201, 0x0700}, {0x2205, 0x8B82},
-       {0x2206, 0x05CB}, {0x221F, 0x0002}, {0x2204, 0x80C2}, {0x2205, 0x0938},
-       {0x221F, 0x0003}, {0x2212, 0xC4D2}, {0x220D, 0x0207}, {0x221F, 0x0001},
-       {0x2207, 0x267E}, {0x221C, 0xE5F7}, {0x221B, 0x0424}, {0x221F, 0x0007},
-       {0x221E, 0x0040}, {0x2218, 0x0000}, {0x221F, 0x0007}, {0x221E, 0x002C},
-       {0x2218, 0x008B}, {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080},
-       {0x2205, 0x8000}, {0x2206, 0xF8E0}, {0x2206, 0xE000}, {0x2206, 0xE1E0},
-       {0x2206, 0x01AC}, {0x2206, 0x2408}, {0x2206, 0xE08B}, {0x2206, 0x84F7},
-       {0x2206, 0x20E4}, {0x2206, 0x8B84}, {0x2206, 0xFC05}, {0x2206, 0xF8FA},
-       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AC}, {0x2206, 0x201A},
-       {0x2206, 0xBF80}, {0x2206, 0x59D0}, {0x2206, 0x2402}, {0x2206, 0x803D},
-       {0x2206, 0xE0E0}, {0x2206, 0xE4E1}, {0x2206, 0xE0E5}, {0x2206, 0x5806},
-       {0x2206, 0x68C0}, {0x2206, 0xD1D2}, {0x2206, 0xE4E0}, {0x2206, 0xE4E5},
-       {0x2206, 0xE0E5}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x05FB},
-       {0x2206, 0x0BFB}, {0x2206, 0x58FF}, {0x2206, 0x9E11}, {0x2206, 0x06F0},
-       {0x2206, 0x0C81}, {0x2206, 0x8AE0}, {0x2206, 0x0019}, {0x2206, 0x1B89},
-       {0x2206, 0xCFEB}, {0x2206, 0x19EB}, {0x2206, 0x19B0}, {0x2206, 0xEFFF},
-       {0x2206, 0x0BFF}, {0x2206, 0x0425}, {0x2206, 0x0807}, {0x2206, 0x2640},
-       {0x2206, 0x7227}, {0x2206, 0x267E}, {0x2206, 0x2804}, {0x2206, 0xB729},
-       {0x2206, 0x2576}, {0x2206, 0x2A68}, {0x2206, 0xE52B}, {0x2206, 0xAD00},
-       {0x2206, 0x2CDB}, {0x2206, 0xF02D}, {0x2206, 0x67BB}, {0x2206, 0x2E7B},
-       {0x2206, 0x0F2F}, {0x2206, 0x7365}, {0x2206, 0x31AC}, {0x2206, 0xCC32},
-       {0x2206, 0x2300}, {0x2206, 0x332D}, {0x2206, 0x1734}, {0x2206, 0x7F52},
-       {0x2206, 0x3510}, {0x2206, 0x0036}, {0x2206, 0x0600}, {0x2206, 0x370C},
-       {0x2206, 0xC038}, {0x2206, 0x7FCE}, {0x2206, 0x3CE5}, {0x2206, 0xF73D},
-       {0x2206, 0x3DA4}, {0x2206, 0x6530}, {0x2206, 0x3E67}, {0x2206, 0x0053},
-       {0x2206, 0x69D2}, {0x2206, 0x0F6A}, {0x2206, 0x012C}, {0x2206, 0x6C2B},
-       {0x2206, 0x136E}, {0x2206, 0xE100}, {0x2206, 0x6F12}, {0x2206, 0xF771},
-       {0x2206, 0x006B}, {0x2206, 0x7306}, {0x2206, 0xEB74}, {0x2206, 0x94C7},
-       {0x2206, 0x7698}, {0x2206, 0x0A77}, {0x2206, 0x5000}, {0x2206, 0x788A},
-       {0x2206, 0x1579}, {0x2206, 0x7F6F}, {0x2206, 0x7A06}, {0x2206, 0xA600},
-       {0x2205, 0x8B90}, {0x2206, 0x8000}, {0x2205, 0x8B92}, {0x2206, 0x8000},
-       {0x2205, 0x8B94}, {0x2206, 0x8014}, {0x2208, 0xFFFA}, {0x2202, 0x3C65},
-       {0x2205, 0xFFF6}, {0x2206, 0x00F7}, {0x221F, 0x0000}, {0x221F, 0x0007},
-       {0x221E, 0x0042}, {0x2218, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
-       {0x221E, 0x0020}, {0x2215, 0x0000}, {0x221E, 0x0023}, {0x2216, 0x8000},
-       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE}, {0x1362, 0x0115},
-       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x1306, 0x000C}, {0x1307, 0x000C},
-       {0x1303, 0x0067}, {0x1304, 0x4444}, {0x1203, 0xFF00}, {0x1200, 0x7FC4},
-       {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000},
-       {0x0865, 0x3210}, {0x087B, 0x0000}, {0x087C, 0xFF00}, {0x087D, 0x0000},
-       {0x087E, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040},
-       {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040},
-       {0x0A25, 0x2040}, {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040},
-       {0x0A29, 0x2040}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x221F, 0x0000},
-       {0x2200, 0x1340}, {0x221F, 0x0000}, {0x133F, 0x0010}, {0x133E, 0x0FFE},
-       {0x1B03, 0x0876},
-};
-
-static const struct rtl8367_initval rtl8367_initvals_2_0[] = {
-       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
-       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
-       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
-       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
-       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
-       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
-       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
-       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
-       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
-       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
-       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
-       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
-       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
-       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
-       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
-       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
-       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
-       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
-       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
-       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
-       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
-       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
-       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
-       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
-       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
-       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
-       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
-       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
-       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
-       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
-       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
-       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
-       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
-       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
-       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
-       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
-       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
-       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x121d, 0x7D16},
-       {0x121e, 0x03e8}, {0x121f, 0x024e}, {0x1220, 0x0230}, {0x1221, 0x0244},
-       {0x1222, 0x0226}, {0x1223, 0x024e}, {0x1224, 0x0230}, {0x1225, 0x0244},
-       {0x1226, 0x0226}, {0x1227, 0x00c0}, {0x1228, 0x00b4}, {0x122f, 0x00c0},
-       {0x1230, 0x00b4}, {0x0208, 0x03e8}, {0x0209, 0x03e8}, {0x020a, 0x03e8},
-       {0x020b, 0x03e8}, {0x020c, 0x03e8}, {0x020d, 0x03e8}, {0x020e, 0x03e8},
-       {0x020f, 0x03e8}, {0x0210, 0x03e8}, {0x0211, 0x03e8}, {0x0212, 0x03e8},
-       {0x0213, 0x03e8}, {0x0214, 0x03e8}, {0x0215, 0x03e8}, {0x0216, 0x03e8},
-       {0x0217, 0x03e8}, {0x0900, 0x0000}, {0x0901, 0x0000}, {0x0902, 0x0000},
-       {0x0903, 0x0000}, {0x0865, 0x3210}, {0x087b, 0x0000}, {0x087c, 0xff00},
-       {0x087d, 0x0000}, {0x087e, 0x0000}, {0x0801, 0x0100}, {0x0802, 0x0100},
-       {0x0A20, 0x2040}, {0x0A21, 0x2040}, {0x0A22, 0x2040}, {0x0A23, 0x2040},
-       {0x0A24, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040}, {0x20A0, 0x1940},
-       {0x20C0, 0x1940}, {0x20E0, 0x1940}, {0x130c, 0x0050},
-};
-
-static const struct rtl8367_initval rtl8367_initvals_2_1[] = {
-       {0x1b24, 0x0000}, {0x1b25, 0x0000}, {0x1b26, 0x0000}, {0x1b27, 0x0000},
-       {0x133f, 0x0030}, {0x133e, 0x000e}, {0x221f, 0x0007}, {0x221e, 0x0048},
-       {0x2219, 0x4012}, {0x221f, 0x0003}, {0x2201, 0x3554}, {0x2202, 0x63e8},
-       {0x2203, 0x99c2}, {0x2204, 0x0113}, {0x2205, 0x303e}, {0x220d, 0x0207},
-       {0x220e, 0xe100}, {0x221f, 0x0007}, {0x221e, 0x0040}, {0x2218, 0x0000},
-       {0x221f, 0x0007}, {0x221e, 0x002c}, {0x2218, 0x008b}, {0x221f, 0x0005},
-       {0x2205, 0xfff6}, {0x2206, 0x0080}, {0x221f, 0x0005}, {0x2205, 0x8000},
-       {0x2206, 0x0280}, {0x2206, 0x2bf7}, {0x2206, 0x00e0}, {0x2206, 0xfff7},
-       {0x2206, 0xa080}, {0x2206, 0x02ae}, {0x2206, 0xf602}, {0x2206, 0x804e},
-       {0x2206, 0x0201}, {0x2206, 0x5002}, {0x2206, 0x0163}, {0x2206, 0x0201},
-       {0x2206, 0x79e0}, {0x2206, 0x8b8c}, {0x2206, 0xe18b}, {0x2206, 0x8d1e},
-       {0x2206, 0x01e1}, {0x2206, 0x8b8e}, {0x2206, 0x1e01}, {0x2206, 0xa000},
-       {0x2206, 0xe4ae}, {0x2206, 0xd8bf}, {0x2206, 0x8b88}, {0x2206, 0xec00},
-       {0x2206, 0x19a9}, {0x2206, 0x8b90}, {0x2206, 0xf9ee}, {0x2206, 0xfff6},
-       {0x2206, 0x00ee}, {0x2206, 0xfff7}, {0x2206, 0xfce0}, {0x2206, 0xe140},
-       {0x2206, 0xe1e1}, {0x2206, 0x41f7}, {0x2206, 0x2ff6}, {0x2206, 0x28e4},
-       {0x2206, 0xe140}, {0x2206, 0xe5e1}, {0x2206, 0x4104}, {0x2206, 0xf8fa},
-       {0x2206, 0xef69}, {0x2206, 0xe08b}, {0x2206, 0x86ac}, {0x2206, 0x201a},
-       {0x2206, 0xbf80}, {0x2206, 0x77d0}, {0x2206, 0x6c02}, {0x2206, 0x2978},
-       {0x2206, 0xe0e0}, {0x2206, 0xe4e1}, {0x2206, 0xe0e5}, {0x2206, 0x5806},
-       {0x2206, 0x68c0}, {0x2206, 0xd1d2}, {0x2206, 0xe4e0}, {0x2206, 0xe4e5},
-       {0x2206, 0xe0e5}, {0x2206, 0xef96}, {0x2206, 0xfefc}, {0x2206, 0x0425},
-       {0x2206, 0x0807}, {0x2206, 0x2640}, {0x2206, 0x7227}, {0x2206, 0x267e},
-       {0x2206, 0x2804}, {0x2206, 0xb729}, {0x2206, 0x2576}, {0x2206, 0x2a68},
-       {0x2206, 0xe52b}, {0x2206, 0xad00}, {0x2206, 0x2cdb}, {0x2206, 0xf02d},
-       {0x2206, 0x67bb}, {0x2206, 0x2e7b}, {0x2206, 0x0f2f}, {0x2206, 0x7365},
-       {0x2206, 0x31ac}, {0x2206, 0xcc32}, {0x2206, 0x2300}, {0x2206, 0x332d},
-       {0x2206, 0x1734}, {0x2206, 0x7f52}, {0x2206, 0x3510}, {0x2206, 0x0036},
-       {0x2206, 0x0600}, {0x2206, 0x370c}, {0x2206, 0xc038}, {0x2206, 0x7fce},
-       {0x2206, 0x3ce5}, {0x2206, 0xf73d}, {0x2206, 0x3da4}, {0x2206, 0x6530},
-       {0x2206, 0x3e67}, {0x2206, 0x0053}, {0x2206, 0x69d2}, {0x2206, 0x0f6a},
-       {0x2206, 0x012c}, {0x2206, 0x6c2b}, {0x2206, 0x136e}, {0x2206, 0xe100},
-       {0x2206, 0x6f12}, {0x2206, 0xf771}, {0x2206, 0x006b}, {0x2206, 0x7306},
-       {0x2206, 0xeb74}, {0x2206, 0x94c7}, {0x2206, 0x7698}, {0x2206, 0x0a77},
-       {0x2206, 0x5000}, {0x2206, 0x788a}, {0x2206, 0x1579}, {0x2206, 0x7f6f},
-       {0x2206, 0x7a06}, {0x2206, 0xa600}, {0x2201, 0x0701}, {0x2200, 0x0405},
-       {0x221f, 0x0000}, {0x2200, 0x1340}, {0x221f, 0x0000}, {0x133f, 0x0010},
-       {0x133e, 0x0ffe}, {0x1203, 0xff00}, {0x1200, 0x7fc4}, {0x0900, 0x0000},
-       {0x0901, 0x0000}, {0x0902, 0x0000}, {0x0903, 0x0000}, {0x0865, 0x3210},
-       {0x087b, 0x0000}, {0x087c, 0xff00}, {0x087d, 0x0000}, {0x087e, 0x0000},
-       {0x0801, 0x0100}, {0x0802, 0x0100}, {0x0A20, 0x2040}, {0x0A21, 0x2040},
-       {0x0A22, 0x2040}, {0x0A23, 0x2040}, {0x0A24, 0x2040}, {0x0A25, 0x2040},
-       {0x0A26, 0x2040}, {0x0A27, 0x2040}, {0x0A28, 0x2040}, {0x0A29, 0x2040},
-       {0x130c, 0x0050},
-};
-
-static int rtl8367_write_initvals(struct rtl8366_smi *smi,
-                                 const struct rtl8367_initval *initvals,
-                                 int count)
-{
-       int err;
-       int i;
-
-       for (i = 0; i < count; i++)
-               REG_WR(smi, initvals[i].reg, initvals[i].val);
-
-       return 0;
-}
-
-static int rtl8367_read_phy_reg(struct rtl8366_smi *smi,
-                               u32 phy_addr, u32 phy_reg, u32 *val)
-{
-       int timeout;
-       u32 data;
-       int err;
-
-       if (phy_addr > RTL8367_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       if (phy_reg > RTL8367_PHY_REG_MAX)
-               return -EINVAL;
-
-       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
-       if (data & RTL8367_IA_STATUS_PHY_BUSY)
-               return -ETIMEDOUT;
-
-       /* prepare address */
-       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
-              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
-
-       /* send read command */
-       REG_WR(smi, RTL8367_IA_CTRL_REG,
-              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_READ);
-
-       timeout = 5;
-       do {
-               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
-               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
-                       break;
-
-               if (timeout--) {
-                       dev_err(smi->parent, "phy read timed out\n");
-                       return -ETIMEDOUT;
-               }
-
-               udelay(1);
-       } while (1);
-
-       /* read data */
-       REG_RD(smi, RTL8367_IA_READ_DATA_REG, val);
-
-       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
-               phy_addr, phy_reg, *val);
-       return 0;
-}
-
-static int rtl8367_write_phy_reg(struct rtl8366_smi *smi,
-                                u32 phy_addr, u32 phy_reg, u32 val)
-{
-       int timeout;
-       u32 data;
-       int err;
-
-       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
-               phy_addr, phy_reg, val);
-
-       if (phy_addr > RTL8367_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       if (phy_reg > RTL8367_PHY_REG_MAX)
-               return -EINVAL;
-
-       REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
-       if (data & RTL8367_IA_STATUS_PHY_BUSY)
-               return -ETIMEDOUT;
-
-       /* preapre data */
-       REG_WR(smi, RTL8367_IA_WRITE_DATA_REG, val);
-
-       /* prepare address */
-       REG_WR(smi, RTL8367_IA_ADDRESS_REG,
-              RTL8367_INTERNAL_PHY_REG(phy_addr, phy_reg));
-
-       /* send write command */
-       REG_WR(smi, RTL8367_IA_CTRL_REG,
-              RTL8367_IA_CTRL_CMD_MASK | RTL8367_IA_CTRL_RW_WRITE);
-
-       timeout = 5;
-       do {
-               REG_RD(smi, RTL8367_IA_STATUS_REG, &data);
-               if ((data & RTL8367_IA_STATUS_PHY_BUSY) == 0)
-                       break;
-
-               if (timeout--) {
-                       dev_err(smi->parent, "phy write timed out\n");
-                       return -ETIMEDOUT;
-               }
-
-               udelay(1);
-       } while (1);
-
-       return 0;
-}
-
-static int rtl8367_init_regs0(struct rtl8366_smi *smi, unsigned mode)
-{
-       const struct rtl8367_initval *initvals;
-       int count;
-       int err;
-
-       switch (mode) {
-       case 0:
-               initvals = rtl8367_initvals_0_0;
-               count = ARRAY_SIZE(rtl8367_initvals_0_0);
-               break;
-
-       case 1:
-       case 2:
-               initvals = rtl8367_initvals_0_1;
-               count = ARRAY_SIZE(rtl8367_initvals_0_1);
-               break;
-
-       default:
-               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
-               return -ENODEV;
-       }
-
-       err = rtl8367_write_initvals(smi, initvals, count);
-       if (err)
-               return err;
-
-       /* TODO: complete this */
-
-       return 0;
-}
-
-static int rtl8367_init_regs1(struct rtl8366_smi *smi, unsigned mode)
-{
-       const struct rtl8367_initval *initvals;
-       int count;
-
-       switch (mode) {
-       case 0:
-               initvals = rtl8367_initvals_1_0;
-               count = ARRAY_SIZE(rtl8367_initvals_1_0);
-               break;
-
-       case 1:
-       case 2:
-               initvals = rtl8367_initvals_1_1;
-               count = ARRAY_SIZE(rtl8367_initvals_1_1);
-               break;
-
-       default:
-               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
-               return -ENODEV;
-       }
-
-       return rtl8367_write_initvals(smi, initvals, count);
-}
-
-static int rtl8367_init_regs2(struct rtl8366_smi *smi, unsigned mode)
-{
-       const struct rtl8367_initval *initvals;
-       int count;
-
-       switch (mode) {
-       case 0:
-               initvals = rtl8367_initvals_2_0;
-               count = ARRAY_SIZE(rtl8367_initvals_2_0);
-               break;
-
-       case 1:
-       case 2:
-               initvals = rtl8367_initvals_2_1;
-               count = ARRAY_SIZE(rtl8367_initvals_2_1);
-               break;
-
-       default:
-               dev_err(smi->parent, "%s: unknow mode %u\n", __func__, mode);
-               return -ENODEV;
-       }
-
-       return rtl8367_write_initvals(smi, initvals, count);
-}
-
-static int rtl8367_init_regs(struct rtl8366_smi *smi)
-{
-       u32 data;
-       u32 rlvid;
-       u32 mode;
-       int err;
-
-       REG_WR(smi, RTL8367_RTL_MAGIC_ID_REG, RTL8367_RTL_MAGIC_ID_VAL);
-
-       REG_RD(smi, RTL8367_CHIP_VER_REG, &data);
-       rlvid = (data >> RTL8367_CHIP_VER_RLVID_SHIFT) &
-               RTL8367_CHIP_VER_RLVID_MASK;
-
-       REG_RD(smi, RTL8367_CHIP_MODE_REG, &data);
-       mode = data & RTL8367_CHIP_MODE_MASK;
-
-       switch (rlvid) {
-       case 0:
-               err = rtl8367_init_regs0(smi, mode);
-               break;
-
-       case 1:
-               err = rtl8367_write_phy_reg(smi, 0, 31, 5);
-               if (err)
-                       break;
-
-               err = rtl8367_write_phy_reg(smi, 0, 5, 0x3ffe);
-               if (err)
-                       break;
-
-               err = rtl8367_read_phy_reg(smi, 0, 6, &data);
-               if (err)
-                       break;
-
-               if (data == 0x94eb) {
-                       err = rtl8367_init_regs1(smi, mode);
-               } else if (data == 0x2104) {
-                       err = rtl8367_init_regs2(smi, mode);
-               } else {
-                       dev_err(smi->parent, "unknow phy data %04x\n", data);
-                       return -ENODEV;
-               }
-
-               break;
-
-       default:
-               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
-               err = -ENODEV;
-               break;
-       }
-
-       return err;
-}
-
-static int rtl8367_reset_chip(struct rtl8366_smi *smi)
-{
-       int timeout = 10;
-       int err;
-       u32 data;
-
-       REG_WR(smi, RTL8367_CHIP_RESET_REG, RTL8367_CHIP_RESET_HW);
-       msleep(RTL8367_RESET_DELAY);
-
-       do {
-               REG_RD(smi, RTL8367_CHIP_RESET_REG, &data);
-               if (!(data & RTL8367_CHIP_RESET_HW))
-                       break;
-
-               msleep(1);
-       } while (--timeout);
-
-       if (!timeout) {
-               dev_err(smi->parent, "chip reset timed out\n");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
-static int rtl8367_extif_set_mode(struct rtl8366_smi *smi, int id,
-                                 enum rtl8367_extif_mode mode)
-{
-       int err;
-
-       /* set port mode */
-       switch (mode) {
-       case RTL8367_EXTIF_MODE_RGMII:
-       case RTL8367_EXTIF_MODE_RGMII_33V:
-               REG_WR(smi, RTL8367_CHIP_DEBUG0_REG, 0x0367);
-               REG_WR(smi, RTL8367_CHIP_DEBUG1_REG, 0x7777);
-               break;
-
-       case RTL8367_EXTIF_MODE_TMII_MAC:
-       case RTL8367_EXTIF_MODE_TMII_PHY:
-               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
-                       BIT((id + 1) % 2), BIT((id + 1) % 2));
-               break;
-
-       case RTL8367_EXTIF_MODE_GMII:
-               REG_RMW(smi, RTL8367_CHIP_DEBUG0_REG,
-                       RTL8367_CHIP_DEBUG0_DUMMY0(id),
-                       RTL8367_CHIP_DEBUG0_DUMMY0(id));
-               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), BIT(6));
-               break;
-
-       case RTL8367_EXTIF_MODE_MII_MAC:
-       case RTL8367_EXTIF_MODE_MII_PHY:
-       case RTL8367_EXTIF_MODE_DISABLED:
-               REG_RMW(smi, RTL8367_BYPASS_LINE_RATE_REG,
-                       BIT((id + 1) % 2), 0);
-               REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), BIT(6), 0);
-               break;
-
-       default:
-               dev_err(smi->parent,
-                       "invalid mode for external interface %d\n", id);
-               return -EINVAL;
-       }
-
-       REG_RMW(smi, RTL8367_DIS_REG,
-               RTL8367_DIS_RGMII_MASK << RTL8367_DIS_RGMII_SHIFT(id),
-               mode << RTL8367_DIS_RGMII_SHIFT(id));
-
-       return 0;
-}
-
-static int rtl8367_extif_set_force(struct rtl8366_smi *smi, int id,
-                                  struct rtl8367_port_ability *pa)
-{
-       u32 mask;
-       u32 val;
-       int err;
-
-       mask = (RTL8367_DI_FORCE_MODE |
-               RTL8367_DI_FORCE_NWAY |
-               RTL8367_DI_FORCE_TXPAUSE |
-               RTL8367_DI_FORCE_RXPAUSE |
-               RTL8367_DI_FORCE_LINK |
-               RTL8367_DI_FORCE_DUPLEX |
-               RTL8367_DI_FORCE_SPEED_MASK);
-
-       val = pa->speed;
-       val |= pa->force_mode ? RTL8367_DI_FORCE_MODE : 0;
-       val |= pa->nway ? RTL8367_DI_FORCE_NWAY : 0;
-       val |= pa->txpause ? RTL8367_DI_FORCE_TXPAUSE : 0;
-       val |= pa->rxpause ? RTL8367_DI_FORCE_RXPAUSE : 0;
-       val |= pa->link ? RTL8367_DI_FORCE_LINK : 0;
-       val |= pa->duplex ? RTL8367_DI_FORCE_DUPLEX : 0;
-
-       REG_RMW(smi, RTL8367_DI_FORCE_REG(id), mask, val);
-
-       return 0;
-}
-
-static int rtl8367_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
-                                        unsigned txdelay, unsigned rxdelay)
-{
-       u32 mask;
-       u32 val;
-       int err;
-
-       mask = (RTL8367_EXT_RGMXF_RXDELAY_MASK |
-               (RTL8367_EXT_RGMXF_TXDELAY_MASK <<
-                       RTL8367_EXT_RGMXF_TXDELAY_SHIFT));
-
-       val = rxdelay;
-       val |= txdelay << RTL8367_EXT_RGMXF_TXDELAY_SHIFT;
-
-       REG_RMW(smi, RTL8367_EXT_RGMXF_REG(id), mask, val);
-
-       return 0;
-}
-
-static int rtl8367_extif_init(struct rtl8366_smi *smi, int id,
-                             struct rtl8367_extif_config *cfg)
-{
-       enum rtl8367_extif_mode mode;
-       int err;
-
-       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
-
-       err = rtl8367_extif_set_mode(smi, id, mode);
-       if (err)
-               return err;
-
-       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
-               err = rtl8367_extif_set_force(smi, id, &cfg->ability);
-               if (err)
-                       return err;
-
-               err = rtl8367_extif_set_rgmii_delay(smi, id, cfg->txdelay,
-                                                    cfg->rxdelay);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-static int rtl8367_led_group_set_ports(struct rtl8366_smi *smi,
-                                      unsigned int group, u16 port_mask)
-{
-       u32 reg;
-       u32 s;
-       int err;
-
-       port_mask &= RTL8367_PARA_LED_IO_EN_PMASK;
-       s = (group % 2) * 8;
-       reg = RTL8367_PARA_LED_IO_EN1_REG + (group / 2);
-
-       REG_RMW(smi, reg, (RTL8367_PARA_LED_IO_EN_PMASK << s), port_mask << s);
-
-       return 0;
-}
-
-static int rtl8367_led_group_set_mode(struct rtl8366_smi *smi,
-                                     unsigned int mode)
-{
-       u16 mask;
-       u16 set;
-       int err;
-
-       mode &= RTL8367_LED_CONFIG_DATA_M;
-
-       mask = (RTL8367_LED_CONFIG_DATA_M << RTL8367_LED_CONFIG_DATA_S) |
-               RTL8367_LED_CONFIG_SEL;
-       set = (mode << RTL8367_LED_CONFIG_DATA_S) | RTL8367_LED_CONFIG_SEL;
-
-       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
-
-       return 0;
-}
-
-static int rtl8367_led_group_set_config(struct rtl8366_smi *smi,
-                                       unsigned int led, unsigned int cfg)
-{
-       u16 mask;
-       u16 set;
-       int err;
-
-       mask = (RTL8367_LED_CONFIG_LED_CFG_M << (led * 4)) |
-               RTL8367_LED_CONFIG_SEL;
-       set = (cfg & RTL8367_LED_CONFIG_LED_CFG_M) << (led * 4);
-
-       REG_RMW(smi, RTL8367_LED_CONFIG_REG, mask, set);
-       return 0;
-}
-
-static int rtl8367_led_op_select_parallel(struct rtl8366_smi *smi)
-{
-       int err;
-
-       REG_WR(smi, RTL8367_LED_SYS_CONFIG_REG, 0x1472);
-       return 0;
-}
-
-static int rtl8367_led_blinkrate_set(struct rtl8366_smi *smi, unsigned int rate)
-{
-       u16 mask;
-       u16 set;
-       int err;
-
-       mask = RTL8367_LED_MODE_RATE_M << RTL8367_LED_MODE_RATE_S;
-       set = (rate & RTL8367_LED_MODE_RATE_M) << RTL8367_LED_MODE_RATE_S;
-       REG_RMW(smi, RTL8367_LED_MODE_REG, mask, set);
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
-                                const char *name)
-{
-       struct rtl8367_extif_config *cfg;
-       const __be32 *prop;
-       int size;
-       int err;
-
-       prop = of_get_property(smi->parent->of_node, name, &size);
-       if (!prop)
-               return rtl8367_extif_init(smi, id, NULL);
-
-       if (size != (9 * sizeof(*prop))) {
-               dev_err(smi->parent, "%s property is invalid\n", name);
-               return -EINVAL;
-       }
-
-       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
-       if (!cfg)
-               return -ENOMEM;
-
-       cfg->txdelay = be32_to_cpup(prop++);
-       cfg->rxdelay = be32_to_cpup(prop++);
-       cfg->mode = be32_to_cpup(prop++);
-       cfg->ability.force_mode = be32_to_cpup(prop++);
-       cfg->ability.txpause = be32_to_cpup(prop++);
-       cfg->ability.rxpause = be32_to_cpup(prop++);
-       cfg->ability.link = be32_to_cpup(prop++);
-       cfg->ability.duplex = be32_to_cpup(prop++);
-       cfg->ability.speed = be32_to_cpup(prop++);
-
-       err = rtl8367_extif_init(smi, id, cfg);
-       kfree(cfg);
-
-       return err;
-}
-#else
-static int rtl8367_extif_init_of(struct rtl8366_smi *smi, int id,
-                                const char *name)
-{
-       return -EINVAL;
-}
-#endif
-
-static int rtl8367_setup(struct rtl8366_smi *smi)
-{
-       struct rtl8367_platform_data *pdata;
-       int err;
-       int i;
-
-       pdata = smi->parent->platform_data;
-
-       err = rtl8367_init_regs(smi);
-       if (err)
-               return err;
-
-       /* initialize external interfaces */
-       if (smi->parent->of_node) {
-               err = rtl8367_extif_init_of(smi, 0, "realtek,extif0");
-               if (err)
-                       return err;
-
-               err = rtl8367_extif_init_of(smi, 1, "realtek,extif1");
-               if (err)
-                       return err;
-       } else {
-               err = rtl8367_extif_init(smi, 0, pdata->extif0_cfg);
-               if (err)
-                       return err;
-
-               err = rtl8367_extif_init(smi, 1, pdata->extif1_cfg);
-               if (err)
-                       return err;
-       }
-
-       /* set maximum packet length to 1536 bytes */
-       REG_RMW(smi, RTL8367_SWC0_REG, RTL8367_SWC0_MAX_LENGTH_MASK,
-               RTL8367_SWC0_MAX_LENGTH_1536);
-
-       /*
-        * discard VLAN tagged packets if the port is not a member of
-        * the VLAN with which the packets is associated.
-        */
-       REG_WR(smi, RTL8367_VLAN_INGRESS_REG, RTL8367_PORTS_ALL);
-
-       /*
-        * Setup egress tag mode for each port.
-        */
-       for (i = 0; i < RTL8367_NUM_PORTS; i++)
-               REG_RMW(smi,
-                       RTL8367_PORT_CFG_REG(i),
-                       RTL8367_PORT_CFG_EGRESS_MODE_MASK <<
-                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT,
-                       RTL8367_PORT_CFG_EGRESS_MODE_ORIGINAL <<
-                               RTL8367_PORT_CFG_EGRESS_MODE_SHIFT);
-
-       /* setup LEDs */
-       err = rtl8367_led_group_set_ports(smi, 0, RTL8367_PORTS_ALL);
-       if (err)
-               return err;
-
-       err = rtl8367_led_group_set_mode(smi, 0);
-       if (err)
-               return err;
-
-       err = rtl8367_led_op_select_parallel(smi);
-       if (err)
-               return err;
-
-       err = rtl8367_led_blinkrate_set(smi, 1);
-       if (err)
-               return err;
-
-       err = rtl8367_led_group_set_config(smi, 0, 2);
-       if (err)
-               return err;
-
-       return 0;
-}
-
-static int rtl8367_get_mib_counter(struct rtl8366_smi *smi, int counter,
-                                  int port, unsigned long long *val)
-{
-       struct rtl8366_mib_counter *mib;
-       int offset;
-       int i;
-       int err;
-       u32 addr, data;
-       u64 mibvalue;
-
-       if (port > RTL8367_NUM_PORTS || counter >= RTL8367_MIB_COUNT)
-               return -EINVAL;
-
-       mib = &rtl8367_mib_counters[counter];
-       addr = RTL8367_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
-
-       /*
-        * Writing access counter address first
-        * then ASIC will prepare 64bits counter wait for being retrived
-        */
-       REG_WR(smi, RTL8367_MIB_ADDRESS_REG, addr >> 2);
-
-       /* read MIB control register */
-       REG_RD(smi, RTL8367_MIB_CTRL_REG(0), &data);
-
-       if (data & RTL8367_MIB_CTRL_BUSY_MASK)
-               return -EBUSY;
-
-       if (data & RTL8367_MIB_CTRL_RESET_MASK)
-               return -EIO;
-
-       if (mib->length == 4)
-               offset = 3;
-       else
-               offset = (mib->offset + 1) % 4;
-
-       mibvalue = 0;
-       for (i = 0; i < mib->length; i++) {
-               REG_RD(smi, RTL8367_MIB_COUNTER_REG(offset - i), &data);
-               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
-       }
-
-       *val = mibvalue;
-       return 0;
-}
-
-static int rtl8367_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
-                               struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
-       int err;
-       int i;
-
-       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
-
-       if (vid >= RTL8367_NUM_VIDS)
-               return -EINVAL;
-
-       /* write VID */
-       REG_WR(smi, RTL8367_TA_ADDR_REG, vid);
-
-       /* write table access control word */
-       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_READ);
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_RD(smi, RTL8367_TA_DATA_REG(i), &data[i]);
-
-       vlan4k->vid = vid;
-       vlan4k->member = (data[0] >> RTL8367_TA_VLAN_MEMBER_SHIFT) &
-                        RTL8367_TA_VLAN_MEMBER_MASK;
-       vlan4k->fid = (data[1] >> RTL8367_TA_VLAN_FID_SHIFT) &
-                     RTL8367_TA_VLAN_FID_MASK;
-       vlan4k->untag = (data[2] >> RTL8367_TA_VLAN_UNTAG1_SHIFT) &
-                       RTL8367_TA_VLAN_UNTAG1_MASK;
-       vlan4k->untag |= ((data[3] >> RTL8367_TA_VLAN_UNTAG2_SHIFT) &
-                         RTL8367_TA_VLAN_UNTAG2_MASK) << 2;
-
-       return 0;
-}
-
-static int rtl8367_set_vlan_4k(struct rtl8366_smi *smi,
-                               const struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[RTL8367_TA_VLAN_DATA_SIZE];
-       int err;
-       int i;
-
-       if (vlan4k->vid >= RTL8367_NUM_VIDS ||
-           vlan4k->member > RTL8367_TA_VLAN_MEMBER_MASK ||
-           vlan4k->untag > RTL8367_UNTAG_MASK ||
-           vlan4k->fid > RTL8367_FIDMAX)
-               return -EINVAL;
-
-       data[0] = (vlan4k->member & RTL8367_TA_VLAN_MEMBER_MASK) <<
-                 RTL8367_TA_VLAN_MEMBER_SHIFT;
-       data[1] = (vlan4k->fid & RTL8367_TA_VLAN_FID_MASK) <<
-                 RTL8367_TA_VLAN_FID_SHIFT;
-       data[2] = (vlan4k->untag & RTL8367_TA_VLAN_UNTAG1_MASK) <<
-                 RTL8367_TA_VLAN_UNTAG1_SHIFT;
-       data[3] = ((vlan4k->untag >> 2) & RTL8367_TA_VLAN_UNTAG2_MASK) <<
-                 RTL8367_TA_VLAN_UNTAG2_SHIFT;
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_WR(smi, RTL8367_TA_DATA_REG(i), data[i]);
-
-       /* write VID */
-       REG_WR(smi, RTL8367_TA_ADDR_REG,
-              vlan4k->vid & RTL8367_TA_VLAN_VID_MASK);
-
-       /* write table access control word */
-       REG_WR(smi, RTL8367_TA_CTRL_REG, RTL8367_TA_CTRL_CVLAN_WRITE);
-
-       return 0;
-}
-
-static int rtl8367_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                               struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
-       int err;
-       int i;
-
-       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
-
-       if (index >= RTL8367_NUM_VLANS)
-               return -EINVAL;
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_RD(smi, RTL8367_VLAN_MC_BASE(index) + i, &data[i]);
-
-       vlanmc->member = (data[0] >> RTL8367_VLAN_MC_MEMBER_SHIFT) &
-                        RTL8367_VLAN_MC_MEMBER_MASK;
-       vlanmc->fid = (data[1] >> RTL8367_VLAN_MC_FID_SHIFT) &
-                     RTL8367_VLAN_MC_FID_MASK;
-       vlanmc->vid = (data[3] >> RTL8367_VLAN_MC_EVID_SHIFT) &
-                     RTL8367_VLAN_MC_EVID_MASK;
-
-       return 0;
-}
-
-static int rtl8367_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                               const struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[RTL8367_VLAN_MC_DATA_SIZE];
-       int err;
-       int i;
-
-       if (index >= RTL8367_NUM_VLANS ||
-           vlanmc->vid >= RTL8367_NUM_VIDS ||
-           vlanmc->priority > RTL8367_PRIORITYMAX ||
-           vlanmc->member > RTL8367_VLAN_MC_MEMBER_MASK ||
-           vlanmc->untag > RTL8367_UNTAG_MASK ||
-           vlanmc->fid > RTL8367_FIDMAX)
-               return -EINVAL;
-
-       data[0] = (vlanmc->member & RTL8367_VLAN_MC_MEMBER_MASK) <<
-                 RTL8367_VLAN_MC_MEMBER_SHIFT;
-       data[1] = (vlanmc->fid & RTL8367_VLAN_MC_FID_MASK) <<
-                 RTL8367_VLAN_MC_FID_SHIFT;
-       data[2] = 0;
-       data[3] = (vlanmc->vid & RTL8367_VLAN_MC_EVID_MASK) <<
-                  RTL8367_VLAN_MC_EVID_SHIFT;
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_WR(smi, RTL8367_VLAN_MC_BASE(index) + i, data[i]);
-
-       return 0;
-}
-
-static int rtl8367_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
-{
-       u32 data;
-       int err;
-
-       if (port >= RTL8367_NUM_PORTS)
-               return -EINVAL;
-
-       REG_RD(smi, RTL8367_VLAN_PVID_CTRL_REG(port), &data);
-
-       *val = (data >> RTL8367_VLAN_PVID_CTRL_SHIFT(port)) &
-              RTL8367_VLAN_PVID_CTRL_MASK;
-
-       return 0;
-}
-
-static int rtl8367_set_mc_index(struct rtl8366_smi *smi, int port, int index)
-{
-       if (port >= RTL8367_NUM_PORTS || index >= RTL8367_NUM_VLANS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_PVID_CTRL_REG(port),
-                               RTL8367_VLAN_PVID_CTRL_MASK <<
-                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port),
-                               (index & RTL8367_VLAN_PVID_CTRL_MASK) <<
-                                       RTL8367_VLAN_PVID_CTRL_SHIFT(port));
-}
-
-static int rtl8367_enable_vlan(struct rtl8366_smi *smi, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8367_VLAN_CTRL_REG,
-                               RTL8367_VLAN_CTRL_ENABLE,
-                               (enable) ? RTL8367_VLAN_CTRL_ENABLE : 0);
-}
-
-static int rtl8367_enable_vlan4k(struct rtl8366_smi *smi, int enable)
-{
-       return 0;
-}
-
-static int rtl8367_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
-{
-       unsigned max = RTL8367_NUM_VLANS;
-
-       if (smi->vlan4k_enabled)
-               max = RTL8367_NUM_VIDS - 1;
-
-       if (vlan == 0 || vlan >= max)
-               return 0;
-
-       return 1;
-}
-
-static int rtl8367_enable_port(struct rtl8366_smi *smi, int port, int enable)
-{
-       int err;
-
-       REG_WR(smi, RTL8367_PORT_ISOLATION_REG(port),
-              (enable) ? RTL8367_PORTS_ALL : 0);
-
-       return 0;
-}
-
-static int rtl8367_sw_reset_mibs(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(0), 0,
-                               RTL8367_MIB_CTRL_GLOBAL_RESET_MASK);
-}
-
-static int rtl8367_sw_get_port_link(struct switch_dev *dev,
-                                   int port,
-                                   struct switch_port_link *link)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data = 0;
-       u32 speed;
-
-       if (port >= RTL8367_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8367_PORT_STATUS_REG(port), &data);
-
-       link->link = !!(data & RTL8367_PORT_STATUS_LINK);
-       if (!link->link)
-               return 0;
-
-       link->duplex = !!(data & RTL8367_PORT_STATUS_DUPLEX);
-       link->rx_flow = !!(data & RTL8367_PORT_STATUS_RXPAUSE);
-       link->tx_flow = !!(data & RTL8367_PORT_STATUS_TXPAUSE);
-       link->aneg = !!(data & RTL8367_PORT_STATUS_NWAY);
-
-       speed = (data & RTL8367_PORT_STATUS_SPEED_MASK);
-       switch (speed) {
-       case 0:
-               link->speed = SWITCH_PORT_SPEED_10;
-               break;
-       case 1:
-               link->speed = SWITCH_PORT_SPEED_100;
-               break;
-       case 2:
-               link->speed = SWITCH_PORT_SPEED_1000;
-               break;
-       default:
-               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int rtl8367_sw_get_max_length(struct switch_dev *dev,
-                                    const struct switch_attr *attr,
-                                    struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8367_SWC0_REG, &data);
-       val->value.i = (data & RTL8367_SWC0_MAX_LENGTH_MASK) >>
-                       RTL8367_SWC0_MAX_LENGTH_SHIFT;
-
-       return 0;
-}
-
-static int rtl8367_sw_set_max_length(struct switch_dev *dev,
-                                    const struct switch_attr *attr,
-                                    struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 max_len;
-
-       switch (val->value.i) {
-       case 0:
-               max_len = RTL8367_SWC0_MAX_LENGTH_1522;
-               break;
-       case 1:
-               max_len = RTL8367_SWC0_MAX_LENGTH_1536;
-               break;
-       case 2:
-               max_len = RTL8367_SWC0_MAX_LENGTH_1552;
-               break;
-       case 3:
-               max_len = RTL8367_SWC0_MAX_LENGTH_16000;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return rtl8366_smi_rmwr(smi, RTL8367_SWC0_REG,
-                               RTL8367_SWC0_MAX_LENGTH_MASK, max_len);
-}
-
-
-static int rtl8367_sw_reset_port_mibs(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int port;
-
-       port = val->port_vlan;
-       if (port >= RTL8367_NUM_PORTS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8367_MIB_CTRL_REG(port / 8), 0,
-                               RTL8367_MIB_CTRL_PORT_RESET_MASK(port % 8));
-}
-
-static int rtl8367_sw_get_port_stats(struct switch_dev *dev, int port,
-                                        struct switch_port_stats *stats)
-{
-       return (rtl8366_sw_get_port_stats(dev, port, stats,
-                               RTL8367_MIB_TXB_ID, RTL8367_MIB_RXB_ID));
-}
-
-static struct switch_attr rtl8367_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan4k",
-               .description = "Enable VLAN 4K mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 2
-       }, {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mibs",
-               .description = "Reset all MIB counters",
-               .set = rtl8367_sw_reset_mibs,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "max_length",
-               .description = "Get/Set the maximum length of valid packets"
-                              "(0:1522, 1:1536, 2:1552, 3:16000)",
-               .set = rtl8367_sw_set_max_length,
-               .get = rtl8367_sw_get_max_length,
-               .max = 3,
-       }
-};
-
-static struct switch_attr rtl8367_port[] = {
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mib",
-               .description = "Reset single port MIB counters",
-               .set = rtl8367_sw_reset_port_mibs,
-       }, {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get MIB counters for port",
-               .max = 33,
-               .set = NULL,
-               .get = rtl8366_sw_get_port_mib,
-       },
-};
-
-static struct switch_attr rtl8367_vlan[] = {
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "info",
-               .description = "Get vlan information",
-               .max = 1,
-               .set = NULL,
-               .get = rtl8366_sw_get_vlan_info,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "fid",
-               .description = "Get/Set vlan FID",
-               .max = RTL8367_FIDMAX,
-               .set = rtl8366_sw_set_vlan_fid,
-               .get = rtl8366_sw_get_vlan_fid,
-       },
-};
-
-static const struct switch_dev_ops rtl8367_sw_ops = {
-       .attr_global = {
-               .attr = rtl8367_globals,
-               .n_attr = ARRAY_SIZE(rtl8367_globals),
-       },
-       .attr_port = {
-               .attr = rtl8367_port,
-               .n_attr = ARRAY_SIZE(rtl8367_port),
-       },
-       .attr_vlan = {
-               .attr = rtl8367_vlan,
-               .n_attr = ARRAY_SIZE(rtl8367_vlan),
-       },
-
-       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
-       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
-       .get_port_pvid = rtl8366_sw_get_port_pvid,
-       .set_port_pvid = rtl8366_sw_set_port_pvid,
-       .reset_switch = rtl8366_sw_reset_switch,
-       .get_port_link = rtl8367_sw_get_port_link,
-       .get_port_stats = rtl8367_sw_get_port_stats,
-};
-
-static int rtl8367_switch_init(struct rtl8366_smi *smi)
-{
-       struct switch_dev *dev = &smi->sw_dev;
-       int err;
-
-       dev->name = "RTL8367";
-       dev->cpu_port = RTL8367_CPU_PORT_NUM;
-       dev->ports = RTL8367_NUM_PORTS;
-       dev->vlans = RTL8367_NUM_VIDS;
-       dev->ops = &rtl8367_sw_ops;
-       dev->alias = dev_name(smi->parent);
-
-       err = register_switch(dev, NULL);
-       if (err)
-               dev_err(smi->parent, "switch registration failed\n");
-
-       return err;
-}
-
-static void rtl8367_switch_cleanup(struct rtl8366_smi *smi)
-{
-       unregister_switch(&smi->sw_dev);
-}
-
-static int rtl8367_mii_read(struct mii_bus *bus, int addr, int reg)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 val = 0;
-       int err;
-
-       err = rtl8367_read_phy_reg(smi, addr, reg, &val);
-       if (err)
-               return 0xffff;
-
-       return val;
-}
-
-static int rtl8367_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 t;
-       int err;
-
-       err = rtl8367_write_phy_reg(smi, addr, reg, val);
-       if (err)
-               return err;
-
-       /* flush write */
-       (void) rtl8367_read_phy_reg(smi, addr, reg, &t);
-
-       return err;
-}
-
-static int rtl8367_detect(struct rtl8366_smi *smi)
-{
-       u32 rtl_no = 0;
-       u32 rtl_ver = 0;
-       char *chip_name;
-       int ret;
-
-       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_NO_REG, &rtl_no);
-       if (ret) {
-               dev_err(smi->parent, "unable to read chip number\n");
-               return ret;
-       }
-
-       switch (rtl_no) {
-       case RTL8367_RTL_NO_8367R:
-               chip_name = "8367R";
-               break;
-       case RTL8367_RTL_NO_8367M:
-               chip_name = "8367M";
-               break;
-       default:
-               dev_err(smi->parent, "unknown chip number (%04x)\n", rtl_no);
-               return -ENODEV;
-       }
-
-       ret = rtl8366_smi_read_reg(smi, RTL8367_RTL_VER_REG, &rtl_ver);
-       if (ret) {
-               dev_err(smi->parent, "unable to read chip version\n");
-               return ret;
-       }
-
-       dev_info(smi->parent, "RTL%s ver. %u chip found\n",
-                chip_name, rtl_ver & RTL8367_RTL_VER_MASK);
-
-       return 0;
-}
-
-static struct rtl8366_smi_ops rtl8367_smi_ops = {
-       .detect         = rtl8367_detect,
-       .reset_chip     = rtl8367_reset_chip,
-       .setup          = rtl8367_setup,
-
-       .mii_read       = rtl8367_mii_read,
-       .mii_write      = rtl8367_mii_write,
-
-       .get_vlan_mc    = rtl8367_get_vlan_mc,
-       .set_vlan_mc    = rtl8367_set_vlan_mc,
-       .get_vlan_4k    = rtl8367_get_vlan_4k,
-       .set_vlan_4k    = rtl8367_set_vlan_4k,
-       .get_mc_index   = rtl8367_get_mc_index,
-       .set_mc_index   = rtl8367_set_mc_index,
-       .get_mib_counter = rtl8367_get_mib_counter,
-       .is_vlan_valid  = rtl8367_is_vlan_valid,
-       .enable_vlan    = rtl8367_enable_vlan,
-       .enable_vlan4k  = rtl8367_enable_vlan4k,
-       .enable_port    = rtl8367_enable_port,
-};
-
-static int rtl8367_probe(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi;
-       int err;
-
-       smi = rtl8366_smi_probe(pdev);
-       if (!smi)
-               return -ENODEV;
-
-       smi->clk_delay = 1500;
-       smi->cmd_read = 0xb9;
-       smi->cmd_write = 0xb8;
-       smi->ops = &rtl8367_smi_ops;
-       smi->cpu_port = RTL8367_CPU_PORT_NUM;
-       smi->num_ports = RTL8367_NUM_PORTS;
-       smi->num_vlan_mc = RTL8367_NUM_VLANS;
-       smi->mib_counters = rtl8367_mib_counters;
-       smi->num_mib_counters = ARRAY_SIZE(rtl8367_mib_counters);
-
-       err = rtl8366_smi_init(smi);
-       if (err)
-               goto err_free_smi;
-
-       platform_set_drvdata(pdev, smi);
-
-       err = rtl8367_switch_init(smi);
-       if (err)
-               goto err_clear_drvdata;
-
-       return 0;
-
- err_clear_drvdata:
-       platform_set_drvdata(pdev, NULL);
-       rtl8366_smi_cleanup(smi);
- err_free_smi:
-       kfree(smi);
-       return err;
-}
-
-static int rtl8367_remove(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
-
-       if (smi) {
-               rtl8367_switch_cleanup(smi);
-               platform_set_drvdata(pdev, NULL);
-               rtl8366_smi_cleanup(smi);
-               kfree(smi);
-       }
-
-       return 0;
-}
-
-static void rtl8367_shutdown(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
-
-       if (smi)
-               rtl8367_reset_chip(smi);
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id rtl8367_match[] = {
-       { .compatible = "realtek,rtl8367" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, rtl8367_match);
-#endif
-
-static struct platform_driver rtl8367_driver = {
-       .driver = {
-               .name           = RTL8367_DRIVER_NAME,
-               .owner          = THIS_MODULE,
-#ifdef CONFIG_OF
-               .of_match_table = of_match_ptr(rtl8367_match),
-#endif
-       },
-       .probe          = rtl8367_probe,
-       .remove         = rtl8367_remove,
-       .shutdown       = rtl8367_shutdown,
-};
-
-static int __init rtl8367_module_init(void)
-{
-       return platform_driver_register(&rtl8367_driver);
-}
-module_init(rtl8367_module_init);
-
-static void __exit rtl8367_module_exit(void)
-{
-       platform_driver_unregister(&rtl8367_driver);
-}
-module_exit(rtl8367_module_exit);
-
-MODULE_DESCRIPTION("Realtek RTL8367 ethernet switch driver");
-MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" RTL8367_DRIVER_NAME);
diff --git a/target/linux/generic/files/drivers/net/phy/rtl8367b.c b/target/linux/generic/files/drivers/net/phy/rtl8367b.c
deleted file mode 100644 (file)
index e6ea650..0000000
+++ /dev/null
@@ -1,1612 +0,0 @@
-/*
- * Platform driver for the Realtek RTL8367R-VB ethernet switches
- *
- * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/delay.h>
-#include <linux/skbuff.h>
-#include <linux/rtl8367.h>
-
-#include "rtl8366_smi.h"
-
-#define RTL8367B_RESET_DELAY   1000    /* msecs*/
-
-#define RTL8367B_PHY_ADDR_MAX  8
-#define RTL8367B_PHY_REG_MAX   31
-
-#define RTL8367B_VID_MASK      0x3fff
-#define RTL8367B_FID_MASK      0xf
-#define RTL8367B_UNTAG_MASK    0xff
-#define RTL8367B_MEMBER_MASK   0xff
-
-#define RTL8367B_PORT_MISC_CFG_REG(_p)         (0x000e + 0x20 * (_p))
-#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT     4
-#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK      0x3
-#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL  0
-#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_KEEP      1
-#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_PRI       2
-#define   RTL8367B_PORT_MISC_CFG_EGRESS_MODE_REAL      3
-
-#define RTL8367B_BYPASS_LINE_RATE_REG          0x03f7
-
-#define RTL8367B_TA_CTRL_REG                   0x0500 /*GOOD*/
-#define   RTL8367B_TA_CTRL_SPA_SHIFT           8
-#define   RTL8367B_TA_CTRL_SPA_MASK            0x7
-#define   RTL8367B_TA_CTRL_METHOD              BIT(4)/*GOOD*/
-#define   RTL8367B_TA_CTRL_CMD_SHIFT           3
-#define   RTL8367B_TA_CTRL_CMD_READ            0
-#define   RTL8367B_TA_CTRL_CMD_WRITE           1
-#define   RTL8367B_TA_CTRL_TABLE_SHIFT         0 /*GOOD*/
-#define   RTL8367B_TA_CTRL_TABLE_ACLRULE       1
-#define   RTL8367B_TA_CTRL_TABLE_ACLACT                2
-#define   RTL8367B_TA_CTRL_TABLE_CVLAN         3
-#define   RTL8367B_TA_CTRL_TABLE_L2            4
-#define   RTL8367B_TA_CTRL_CVLAN_READ \
-               ((RTL8367B_TA_CTRL_CMD_READ << RTL8367B_TA_CTRL_CMD_SHIFT) | \
-                RTL8367B_TA_CTRL_TABLE_CVLAN)
-#define   RTL8367B_TA_CTRL_CVLAN_WRITE \
-               ((RTL8367B_TA_CTRL_CMD_WRITE << RTL8367B_TA_CTRL_CMD_SHIFT) | \
-                RTL8367B_TA_CTRL_TABLE_CVLAN)
-
-#define RTL8367B_TA_ADDR_REG                   0x0501/*GOOD*/
-#define   RTL8367B_TA_ADDR_MASK                        0x3fff/*GOOD*/
-
-#define RTL8367B_TA_LUT_REG                    0x0502/*GOOD*/
-
-#define RTL8367B_TA_WRDATA_REG(_x)             (0x0510 + (_x))/*GOOD*/
-#define   RTL8367B_TA_VLAN_NUM_WORDS           2
-#define   RTL8367B_TA_VLAN_VID_MASK            RTL8367B_VID_MASK
-#define   RTL8367B_TA_VLAN0_MEMBER_SHIFT       0
-#define   RTL8367B_TA_VLAN0_MEMBER_MASK                RTL8367B_MEMBER_MASK
-#define   RTL8367B_TA_VLAN0_UNTAG_SHIFT                8
-#define   RTL8367B_TA_VLAN0_UNTAG_MASK         RTL8367B_MEMBER_MASK
-#define   RTL8367B_TA_VLAN1_FID_SHIFT          0
-#define   RTL8367B_TA_VLAN1_FID_MASK           RTL8367B_FID_MASK
-
-#define RTL8367B_TA_RDDATA_REG(_x)             (0x0520 + (_x))/*GOOD*/
-
-#define RTL8367B_VLAN_PVID_CTRL_REG(_p)                (0x0700 + (_p) / 2) /*GOOD*/
-#define RTL8367B_VLAN_PVID_CTRL_MASK           0x1f /*GOOD*/
-#define RTL8367B_VLAN_PVID_CTRL_SHIFT(_p)      (8 * ((_p) % 2)) /*GOOD*/
-
-#define RTL8367B_VLAN_MC_BASE(_x)              (0x0728 + (_x) * 4) /*GOOD*/
-#define   RTL8367B_VLAN_MC_NUM_WORDS           4 /*GOOD*/
-#define   RTL8367B_VLAN_MC0_MEMBER_SHIFT       0/*GOOD*/
-#define   RTL8367B_VLAN_MC0_MEMBER_MASK                RTL8367B_MEMBER_MASK/*GOOD*/
-#define   RTL8367B_VLAN_MC1_FID_SHIFT          0/*GOOD*/
-#define   RTL8367B_VLAN_MC1_FID_MASK           RTL8367B_FID_MASK/*GOOD*/
-#define   RTL8367B_VLAN_MC3_EVID_SHIFT         0/*GOOD*/
-#define   RTL8367B_VLAN_MC3_EVID_MASK          RTL8367B_VID_MASK/*GOOD*/
-
-#define RTL8367B_VLAN_CTRL_REG                 0x07a8 /*GOOD*/
-#define   RTL8367B_VLAN_CTRL_ENABLE            BIT(0)
-
-#define RTL8367B_VLAN_INGRESS_REG              0x07a9 /*GOOD*/
-
-#define RTL8367B_PORT_ISOLATION_REG(_p)                (0x08a2 + (_p)) /*GOOD*/
-
-#define RTL8367B_MIB_COUNTER_REG(_x)           (0x1000 + (_x)) /*GOOD*/
-#define RTL8367B_MIB_COUNTER_PORT_OFFSET       0x007c /*GOOD*/
-
-#define RTL8367B_MIB_ADDRESS_REG               0x1004 /*GOOD*/
-
-#define RTL8367B_MIB_CTRL0_REG(_x)             (0x1005 + (_x)) /*GOOD*/
-#define   RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK BIT(11) /*GOOD*/
-#define   RTL8367B_MIB_CTRL0_QM_RESET_MASK     BIT(10) /*GOOD*/
-#define   RTL8367B_MIB_CTRL0_PORT_RESET_MASK(_p) BIT(2 + (_p)) /*GOOD*/
-#define   RTL8367B_MIB_CTRL0_RESET_MASK                BIT(1) /*GOOD*/
-#define   RTL8367B_MIB_CTRL0_BUSY_MASK         BIT(0) /*GOOD*/
-
-#define RTL8367B_SWC0_REG                      0x1200/*GOOD*/
-#define   RTL8367B_SWC0_MAX_LENGTH_SHIFT       13/*GOOD*/
-#define   RTL8367B_SWC0_MAX_LENGTH(_x)         ((_x) << 13) /*GOOD*/
-#define   RTL8367B_SWC0_MAX_LENGTH_MASK                RTL8367B_SWC0_MAX_LENGTH(0x3)
-#define   RTL8367B_SWC0_MAX_LENGTH_1522                RTL8367B_SWC0_MAX_LENGTH(0)
-#define   RTL8367B_SWC0_MAX_LENGTH_1536                RTL8367B_SWC0_MAX_LENGTH(1)
-#define   RTL8367B_SWC0_MAX_LENGTH_1552                RTL8367B_SWC0_MAX_LENGTH(2)
-#define   RTL8367B_SWC0_MAX_LENGTH_16000       RTL8367B_SWC0_MAX_LENGTH(3)
-
-#define RTL8367B_CHIP_NUMBER_REG               0x1300/*GOOD*/
-
-#define RTL8367B_CHIP_VER_REG                  0x1301/*GOOD*/
-#define   RTL8367B_CHIP_VER_RLVID_SHIFT                12/*GOOD*/
-#define   RTL8367B_CHIP_VER_RLVID_MASK         0xf/*GOOD*/
-#define   RTL8367B_CHIP_VER_MCID_SHIFT         8/*GOOD*/
-#define   RTL8367B_CHIP_VER_MCID_MASK          0xf/*GOOD*/
-#define   RTL8367B_CHIP_VER_BOID_SHIFT         4/*GOOD*/
-#define   RTL8367B_CHIP_VER_BOID_MASK          0xf/*GOOD*/
-#define   RTL8367B_CHIP_VER_AFE_SHIFT          0/*GOOD*/
-#define   RTL8367B_CHIP_VER_AFE_MASK           0x1/*GOOD*/
-
-#define RTL8367B_CHIP_MODE_REG                 0x1302
-#define   RTL8367B_CHIP_MODE_MASK              0x7
-
-#define RTL8367B_CHIP_DEBUG0_REG               0x1303
-#define   RTL8367B_CHIP_DEBUG0_DUMMY0(_x)      BIT(8 + (_x))
-
-#define RTL8367B_CHIP_DEBUG1_REG               0x1304
-
-#define RTL8367B_DIS_REG                       0x1305
-#define   RTL8367B_DIS_SKIP_MII_RXER(_x)       BIT(12 + (_x))
-#define   RTL8367B_DIS_RGMII_SHIFT(_x)         (4 * (_x))
-#define   RTL8367B_DIS_RGMII_MASK              0x7
-
-#define RTL8367B_EXT_RGMXF_REG(_x)             (0x1306 + (_x))
-#define   RTL8367B_EXT_RGMXF_DUMMY0_SHIFT      5
-#define   RTL8367B_EXT_RGMXF_DUMMY0_MASK       0x7ff
-#define   RTL8367B_EXT_RGMXF_TXDELAY_SHIFT     3
-#define   RTL8367B_EXT_RGMXF_TXDELAY_MASK      1
-#define   RTL8367B_EXT_RGMXF_RXDELAY_MASK      0x7
-
-#define RTL8367B_DI_FORCE_REG(_x)              (0x1310 + (_x))
-#define   RTL8367B_DI_FORCE_MODE               BIT(12)
-#define   RTL8367B_DI_FORCE_NWAY               BIT(7)
-#define   RTL8367B_DI_FORCE_TXPAUSE            BIT(6)
-#define   RTL8367B_DI_FORCE_RXPAUSE            BIT(5)
-#define   RTL8367B_DI_FORCE_LINK               BIT(4)
-#define   RTL8367B_DI_FORCE_DUPLEX             BIT(2)
-#define   RTL8367B_DI_FORCE_SPEED_MASK         3
-#define   RTL8367B_DI_FORCE_SPEED_10           0
-#define   RTL8367B_DI_FORCE_SPEED_100          1
-#define   RTL8367B_DI_FORCE_SPEED_1000         2
-
-#define RTL8367B_MAC_FORCE_REG(_x)             (0x1312 + (_x))
-
-#define RTL8367B_CHIP_RESET_REG                        0x1322 /*GOOD*/
-#define   RTL8367B_CHIP_RESET_SW               BIT(1) /*GOOD*/
-#define   RTL8367B_CHIP_RESET_HW               BIT(0) /*GOOD*/
-
-#define RTL8367B_PORT_STATUS_REG(_p)           (0x1352 + (_p)) /*GOOD*/
-#define   RTL8367B_PORT_STATUS_EN_1000_SPI     BIT(11) /*GOOD*/
-#define   RTL8367B_PORT_STATUS_EN_100_SPI      BIT(10)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_NWAY_FAULT      BIT(9)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_LINK_MASTER     BIT(8)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_NWAY            BIT(7)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_TXPAUSE         BIT(6)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_RXPAUSE         BIT(5)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_LINK            BIT(4)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_DUPLEX          BIT(2)/*GOOD*/
-#define   RTL8367B_PORT_STATUS_SPEED_MASK      0x0003/*GOOD*/
-#define   RTL8367B_PORT_STATUS_SPEED_10                0/*GOOD*/
-#define   RTL8367B_PORT_STATUS_SPEED_100       1/*GOOD*/
-#define   RTL8367B_PORT_STATUS_SPEED_1000      2/*GOOD*/
-
-#define RTL8367B_RTL_MAGIC_ID_REG              0x13c2
-#define   RTL8367B_RTL_MAGIC_ID_VAL            0x0249
-
-#define RTL8367B_IA_CTRL_REG                   0x1f00
-#define   RTL8367B_IA_CTRL_RW(_x)              ((_x) << 1)
-#define   RTL8367B_IA_CTRL_RW_READ             RTL8367B_IA_CTRL_RW(0)
-#define   RTL8367B_IA_CTRL_RW_WRITE            RTL8367B_IA_CTRL_RW(1)
-#define   RTL8367B_IA_CTRL_CMD_MASK            BIT(0)
-
-#define RTL8367B_IA_STATUS_REG                 0x1f01
-#define   RTL8367B_IA_STATUS_PHY_BUSY          BIT(2)
-#define   RTL8367B_IA_STATUS_SDS_BUSY          BIT(1)
-#define   RTL8367B_IA_STATUS_MDX_BUSY          BIT(0)
-
-#define RTL8367B_IA_ADDRESS_REG                        0x1f02
-#define RTL8367B_IA_WRITE_DATA_REG             0x1f03
-#define RTL8367B_IA_READ_DATA_REG              0x1f04
-
-#define RTL8367B_INTERNAL_PHY_REG(_a, _r)      (0x2000 + 32 * (_a) + (_r))
-
-#define RTL8367B_NUM_MIB_COUNTERS      58
-
-#define RTL8367B_CPU_PORT_NUM          5
-#define RTL8367B_NUM_PORTS             8
-#define RTL8367B_NUM_VLANS             32
-#define RTL8367B_NUM_VIDS              4096
-#define RTL8367B_PRIORITYMAX           7
-#define RTL8367B_FIDMAX                        7
-
-#define RTL8367B_PORT_0                        BIT(0)
-#define RTL8367B_PORT_1                        BIT(1)
-#define RTL8367B_PORT_2                        BIT(2)
-#define RTL8367B_PORT_3                        BIT(3)
-#define RTL8367B_PORT_4                        BIT(4)
-#define RTL8367B_PORT_E0               BIT(5)  /* External port 0 */
-#define RTL8367B_PORT_E1               BIT(6)  /* External port 1 */
-#define RTL8367B_PORT_E2               BIT(7)  /* External port 2 */
-
-#define RTL8367B_PORTS_ALL                                     \
-       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
-        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E0 | \
-        RTL8367B_PORT_E1 | RTL8367B_PORT_E2)
-
-#define RTL8367B_PORTS_ALL_BUT_CPU                             \
-       (RTL8367B_PORT_0 | RTL8367B_PORT_1 | RTL8367B_PORT_2 |  \
-        RTL8367B_PORT_3 | RTL8367B_PORT_4 | RTL8367B_PORT_E1 | \
-        RTL8367B_PORT_E2)
-
-struct rtl8367b_initval {
-       u16 reg;
-       u16 val;
-};
-
-#define RTL8367B_MIB_RXB_ID            0       /* IfInOctets */
-#define RTL8367B_MIB_TXB_ID            28      /* IfOutOctets */
-
-static struct rtl8366_mib_counter
-rtl8367b_mib_counters[RTL8367B_NUM_MIB_COUNTERS] = {
-       {0,   0, 4, "ifInOctets"                        },
-       {0,   4, 2, "dot3StatsFCSErrors"                },
-       {0,   6, 2, "dot3StatsSymbolErrors"             },
-       {0,   8, 2, "dot3InPauseFrames"                 },
-       {0,  10, 2, "dot3ControlInUnknownOpcodes"       },
-       {0,  12, 2, "etherStatsFragments"               },
-       {0,  14, 2, "etherStatsJabbers"                 },
-       {0,  16, 2, "ifInUcastPkts"                     },
-       {0,  18, 2, "etherStatsDropEvents"              },
-       {0,  20, 2, "ifInMulticastPkts"                 },
-       {0,  22, 2, "ifInBroadcastPkts"                 },
-       {0,  24, 2, "inMldChecksumError"                },
-       {0,  26, 2, "inIgmpChecksumError"               },
-       {0,  28, 2, "inMldSpecificQuery"                },
-       {0,  30, 2, "inMldGeneralQuery"                 },
-       {0,  32, 2, "inIgmpSpecificQuery"               },
-       {0,  34, 2, "inIgmpGeneralQuery"                },
-       {0,  36, 2, "inMldLeaves"                       },
-       {0,  38, 2, "inIgmpLeaves"                      },
-
-       {0,  40, 4, "etherStatsOctets"                  },
-       {0,  44, 2, "etherStatsUnderSizePkts"           },
-       {0,  46, 2, "etherOversizeStats"                },
-       {0,  48, 2, "etherStatsPkts64Octets"            },
-       {0,  50, 2, "etherStatsPkts65to127Octets"       },
-       {0,  52, 2, "etherStatsPkts128to255Octets"      },
-       {0,  54, 2, "etherStatsPkts256to511Octets"      },
-       {0,  56, 2, "etherStatsPkts512to1023Octets"     },
-       {0,  58, 2, "etherStatsPkts1024to1518Octets"    },
-
-       {0,  60, 4, "ifOutOctets"                       },
-       {0,  64, 2, "dot3StatsSingleCollisionFrames"    },
-       {0,  66, 2, "dot3StatMultipleCollisionFrames"   },
-       {0,  68, 2, "dot3sDeferredTransmissions"        },
-       {0,  70, 2, "dot3StatsLateCollisions"           },
-       {0,  72, 2, "etherStatsCollisions"              },
-       {0,  74, 2, "dot3StatsExcessiveCollisions"      },
-       {0,  76, 2, "dot3OutPauseFrames"                },
-       {0,  78, 2, "ifOutDiscards"                     },
-       {0,  80, 2, "dot1dTpPortInDiscards"             },
-       {0,  82, 2, "ifOutUcastPkts"                    },
-       {0,  84, 2, "ifOutMulticastPkts"                },
-       {0,  86, 2, "ifOutBroadcastPkts"                },
-       {0,  88, 2, "outOampduPkts"                     },
-       {0,  90, 2, "inOampduPkts"                      },
-       {0,  92, 2, "inIgmpJoinsSuccess"                },
-       {0,  94, 2, "inIgmpJoinsFail"                   },
-       {0,  96, 2, "inMldJoinsSuccess"                 },
-       {0,  98, 2, "inMldJoinsFail"                    },
-       {0, 100, 2, "inReportSuppressionDrop"           },
-       {0, 102, 2, "inLeaveSuppressionDrop"            },
-       {0, 104, 2, "outIgmpReports"                    },
-       {0, 106, 2, "outIgmpLeaves"                     },
-       {0, 108, 2, "outIgmpGeneralQuery"               },
-       {0, 110, 2, "outIgmpSpecificQuery"              },
-       {0, 112, 2, "outMldReports"                     },
-       {0, 114, 2, "outMldLeaves"                      },
-       {0, 116, 2, "outMldGeneralQuery"                },
-       {0, 118, 2, "outMldSpecificQuery"               },
-       {0, 120, 2, "inKnownMulticastPkts"              },
-};
-
-#define REG_RD(_smi, _reg, _val)                                       \
-       do {                                                            \
-               err = rtl8366_smi_read_reg(_smi, _reg, _val);           \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-#define REG_WR(_smi, _reg, _val)                                       \
-       do {                                                            \
-               err = rtl8366_smi_write_reg(_smi, _reg, _val);          \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-#define REG_RMW(_smi, _reg, _mask, _val)                               \
-       do {                                                            \
-               err = rtl8366_smi_rmwr(_smi, _reg, _mask, _val);        \
-               if (err)                                                \
-                       return err;                                     \
-       } while (0)
-
-static const struct rtl8367b_initval rtl8367r_vb_initvals_0[] = {
-       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x0301, 0x0026}, {0x1722, 0x0E14},
-       {0x205F, 0x0002}, {0x2059, 0x1A00}, {0x205F, 0x0000}, {0x207F, 0x0002},
-       {0x2077, 0x0000}, {0x2078, 0x0000}, {0x2079, 0x0000}, {0x207A, 0x0000},
-       {0x207B, 0x0000}, {0x207F, 0x0000}, {0x205F, 0x0002}, {0x2053, 0x0000},
-       {0x2054, 0x0000}, {0x2055, 0x0000}, {0x2056, 0x0000}, {0x2057, 0x0000},
-       {0x205F, 0x0000}, {0x12A4, 0x110A}, {0x12A6, 0x150A}, {0x13F1, 0x0013},
-       {0x13F4, 0x0010}, {0x13F5, 0x0000}, {0x0018, 0x0F00}, {0x0038, 0x0F00},
-       {0x0058, 0x0F00}, {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x12B6, 0x0C02},
-       {0x12B7, 0x030F}, {0x12B8, 0x11FF}, {0x12BC, 0x0004}, {0x1362, 0x0115},
-       {0x1363, 0x0002}, {0x1363, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E},
-       {0x221F, 0x0007}, {0x221E, 0x002D}, {0x2218, 0xF030}, {0x221F, 0x0007},
-       {0x221E, 0x0023}, {0x2216, 0x0005}, {0x2215, 0x00B9}, {0x2219, 0x0044},
-       {0x2215, 0x00BA}, {0x2219, 0x0020}, {0x2215, 0x00BB}, {0x2219, 0x00C1},
-       {0x2215, 0x0148}, {0x2219, 0x0096}, {0x2215, 0x016E}, {0x2219, 0x0026},
-       {0x2216, 0x0000}, {0x2216, 0x0000}, {0x221E, 0x002D}, {0x2218, 0xF010},
-       {0x221F, 0x0007}, {0x221E, 0x0020}, {0x2215, 0x0D00}, {0x221F, 0x0000},
-       {0x221F, 0x0000}, {0x2217, 0x2160}, {0x221F, 0x0001}, {0x2210, 0xF25E},
-       {0x221F, 0x0007}, {0x221E, 0x0042}, {0x2215, 0x0F00}, {0x2215, 0x0F00},
-       {0x2216, 0x7408}, {0x2215, 0x0E00}, {0x2215, 0x0F00}, {0x2215, 0x0F01},
-       {0x2216, 0x4000}, {0x2215, 0x0E01}, {0x2215, 0x0F01}, {0x2215, 0x0F02},
-       {0x2216, 0x9400}, {0x2215, 0x0E02}, {0x2215, 0x0F02}, {0x2215, 0x0F03},
-       {0x2216, 0x7408}, {0x2215, 0x0E03}, {0x2215, 0x0F03}, {0x2215, 0x0F04},
-       {0x2216, 0x4008}, {0x2215, 0x0E04}, {0x2215, 0x0F04}, {0x2215, 0x0F05},
-       {0x2216, 0x9400}, {0x2215, 0x0E05}, {0x2215, 0x0F05}, {0x2215, 0x0F06},
-       {0x2216, 0x0803}, {0x2215, 0x0E06}, {0x2215, 0x0F06}, {0x2215, 0x0D00},
-       {0x2215, 0x0100}, {0x221F, 0x0001}, {0x2210, 0xF05E}, {0x221F, 0x0000},
-       {0x2217, 0x2100}, {0x221F, 0x0000}, {0x220D, 0x0003}, {0x220E, 0x0015},
-       {0x220D, 0x4003}, {0x220E, 0x0006}, {0x221F, 0x0000}, {0x2200, 0x1340},
-       {0x133F, 0x0010}, {0x12A0, 0x0058}, {0x12A1, 0x0058}, {0x133E, 0x000E},
-       {0x133F, 0x0030}, {0x221F, 0x0000}, {0x2210, 0x0166}, {0x221F, 0x0000},
-       {0x133E, 0x000E}, {0x133F, 0x0010}, {0x133F, 0x0030}, {0x133E, 0x000E},
-       {0x221F, 0x0005}, {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8B6E},
-       {0x2206, 0x0000}, {0x220F, 0x0100}, {0x2205, 0x8000}, {0x2206, 0x0280},
-       {0x2206, 0x28F7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
-       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
-       {0x2206, 0x6602}, {0x2206, 0x80B9}, {0x2206, 0xE08B}, {0x2206, 0x8CE1},
-       {0x2206, 0x8B8D}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x8E1E},
-       {0x2206, 0x01A0}, {0x2206, 0x00E7}, {0x2206, 0xAEDB}, {0x2206, 0xEEE0},
-       {0x2206, 0x120E}, {0x2206, 0xEEE0}, {0x2206, 0x1300}, {0x2206, 0xEEE0},
-       {0x2206, 0x2001}, {0x2206, 0xEEE0}, {0x2206, 0x2166}, {0x2206, 0xEEE0},
-       {0x2206, 0xC463}, {0x2206, 0xEEE0}, {0x2206, 0xC5E8}, {0x2206, 0xEEE0},
-       {0x2206, 0xC699}, {0x2206, 0xEEE0}, {0x2206, 0xC7C2}, {0x2206, 0xEEE0},
-       {0x2206, 0xC801}, {0x2206, 0xEEE0}, {0x2206, 0xC913}, {0x2206, 0xEEE0},
-       {0x2206, 0xCA30}, {0x2206, 0xEEE0}, {0x2206, 0xCB3E}, {0x2206, 0xEEE0},
-       {0x2206, 0xDCE1}, {0x2206, 0xEEE0}, {0x2206, 0xDD00}, {0x2206, 0xEEE2},
-       {0x2206, 0x0001}, {0x2206, 0xEEE2}, {0x2206, 0x0100}, {0x2206, 0xEEE4},
-       {0x2206, 0x8860}, {0x2206, 0xEEE4}, {0x2206, 0x8902}, {0x2206, 0xEEE4},
-       {0x2206, 0x8C00}, {0x2206, 0xEEE4}, {0x2206, 0x8D30}, {0x2206, 0xEEEA},
-       {0x2206, 0x1480}, {0x2206, 0xEEEA}, {0x2206, 0x1503}, {0x2206, 0xEEEA},
-       {0x2206, 0xC600}, {0x2206, 0xEEEA}, {0x2206, 0xC706}, {0x2206, 0xEE85},
-       {0x2206, 0xEE00}, {0x2206, 0xEE85}, {0x2206, 0xEF00}, {0x2206, 0xEE8B},
-       {0x2206, 0x6750}, {0x2206, 0xEE8B}, {0x2206, 0x6632}, {0x2206, 0xEE8A},
-       {0x2206, 0xD448}, {0x2206, 0xEE8A}, {0x2206, 0xD548}, {0x2206, 0xEE8A},
-       {0x2206, 0xD649}, {0x2206, 0xEE8A}, {0x2206, 0xD7F8}, {0x2206, 0xEE8B},
-       {0x2206, 0x85E2}, {0x2206, 0xEE8B}, {0x2206, 0x8700}, {0x2206, 0xEEFF},
-       {0x2206, 0xF600}, {0x2206, 0xEEFF}, {0x2206, 0xF7FC}, {0x2206, 0x04F8},
-       {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2023}, {0x2206, 0xF620},
-       {0x2206, 0xE48B}, {0x2206, 0x8E02}, {0x2206, 0x2877}, {0x2206, 0x0225},
-       {0x2206, 0xC702}, {0x2206, 0x26A1}, {0x2206, 0x0281}, {0x2206, 0xB302},
-       {0x2206, 0x8496}, {0x2206, 0x0202}, {0x2206, 0xA102}, {0x2206, 0x27F1},
-       {0x2206, 0x0228}, {0x2206, 0xF902}, {0x2206, 0x2AA0}, {0x2206, 0x0282},
-       {0x2206, 0xB8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD21}, {0x2206, 0x08F6},
-       {0x2206, 0x21E4}, {0x2206, 0x8B8E}, {0x2206, 0x0202}, {0x2206, 0x80E0},
-       {0x2206, 0x8B8E}, {0x2206, 0xAD22}, {0x2206, 0x05F6}, {0x2206, 0x22E4},
-       {0x2206, 0x8B8E}, {0x2206, 0xE08B}, {0x2206, 0x8EAD}, {0x2206, 0x2305},
-       {0x2206, 0xF623}, {0x2206, 0xE48B}, {0x2206, 0x8EE0}, {0x2206, 0x8B8E},
-       {0x2206, 0xAD24}, {0x2206, 0x08F6}, {0x2206, 0x24E4}, {0x2206, 0x8B8E},
-       {0x2206, 0x0227}, {0x2206, 0x6AE0}, {0x2206, 0x8B8E}, {0x2206, 0xAD25},
-       {0x2206, 0x05F6}, {0x2206, 0x25E4}, {0x2206, 0x8B8E}, {0x2206, 0xE08B},
-       {0x2206, 0x8EAD}, {0x2206, 0x260B}, {0x2206, 0xF626}, {0x2206, 0xE48B},
-       {0x2206, 0x8E02}, {0x2206, 0x830D}, {0x2206, 0x021D}, {0x2206, 0x6BE0},
-       {0x2206, 0x8B8E}, {0x2206, 0xAD27}, {0x2206, 0x05F6}, {0x2206, 0x27E4},
-       {0x2206, 0x8B8E}, {0x2206, 0x0281}, {0x2206, 0x4402}, {0x2206, 0x045C},
-       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B83}, {0x2206, 0xAD23},
-       {0x2206, 0x30E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x2359},
-       {0x2206, 0x02E0}, {0x2206, 0x85EF}, {0x2206, 0xE585}, {0x2206, 0xEFAC},
-       {0x2206, 0x2907}, {0x2206, 0x1F01}, {0x2206, 0x9E51}, {0x2206, 0xAD29},
-       {0x2206, 0x20E0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x06E1},
-       {0x2206, 0x8B84}, {0x2206, 0xAD28}, {0x2206, 0x42E0}, {0x2206, 0x8B85},
-       {0x2206, 0xAD21}, {0x2206, 0x06E1}, {0x2206, 0x8B84}, {0x2206, 0xAD29},
-       {0x2206, 0x36BF}, {0x2206, 0x34BF}, {0x2206, 0x022C}, {0x2206, 0x31AE},
-       {0x2206, 0x2EE0}, {0x2206, 0x8B83}, {0x2206, 0xAD21}, {0x2206, 0x10E0},
-       {0x2206, 0x8B84}, {0x2206, 0xF620}, {0x2206, 0xE48B}, {0x2206, 0x84EE},
-       {0x2206, 0x8ADA}, {0x2206, 0x00EE}, {0x2206, 0x8ADB}, {0x2206, 0x00E0},
-       {0x2206, 0x8B85}, {0x2206, 0xAD21}, {0x2206, 0x0CE0}, {0x2206, 0x8B84},
-       {0x2206, 0xF621}, {0x2206, 0xE48B}, {0x2206, 0x84EE}, {0x2206, 0x8B72},
-       {0x2206, 0xFFBF}, {0x2206, 0x34C2}, {0x2206, 0x022C}, {0x2206, 0x31FC},
-       {0x2206, 0x04F8}, {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B85},
-       {0x2206, 0xAD21}, {0x2206, 0x42E0}, {0x2206, 0xE022}, {0x2206, 0xE1E0},
-       {0x2206, 0x2358}, {0x2206, 0xC059}, {0x2206, 0x021E}, {0x2206, 0x01E1},
-       {0x2206, 0x8B72}, {0x2206, 0x1F10}, {0x2206, 0x9E2F}, {0x2206, 0xE48B},
-       {0x2206, 0x72AD}, {0x2206, 0x2123}, {0x2206, 0xE18B}, {0x2206, 0x84F7},
-       {0x2206, 0x29E5}, {0x2206, 0x8B84}, {0x2206, 0xAC27}, {0x2206, 0x10AC},
-       {0x2206, 0x2605}, {0x2206, 0x0205}, {0x2206, 0x23AE}, {0x2206, 0x1602},
-       {0x2206, 0x0535}, {0x2206, 0x0282}, {0x2206, 0x30AE}, {0x2206, 0x0E02},
-       {0x2206, 0x056A}, {0x2206, 0x0282}, {0x2206, 0x75AE}, {0x2206, 0x0602},
-       {0x2206, 0x04DC}, {0x2206, 0x0282}, {0x2206, 0x04EF}, {0x2206, 0x96FE},
-       {0x2206, 0xFC04}, {0x2206, 0xF8F9}, {0x2206, 0xE08B}, {0x2206, 0x87AD},
-       {0x2206, 0x2321}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
-       {0x2206, 0xAD26}, {0x2206, 0x18F6}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
-       {0x2206, 0xE5EA}, {0x2206, 0x15F6}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
-       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
-       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
-       {0x2206, 0xE08B}, {0x2206, 0x87AD}, {0x2206, 0x233A}, {0x2206, 0xAD22},
-       {0x2206, 0x37E0}, {0x2206, 0xE020}, {0x2206, 0xE1E0}, {0x2206, 0x21AC},
-       {0x2206, 0x212E}, {0x2206, 0xE0EA}, {0x2206, 0x14E1}, {0x2206, 0xEA15},
-       {0x2206, 0xF627}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
-       {0x2206, 0xE2EA}, {0x2206, 0x12E3}, {0x2206, 0xEA13}, {0x2206, 0x5A8F},
-       {0x2206, 0x6A20}, {0x2206, 0xE6EA}, {0x2206, 0x12E7}, {0x2206, 0xEA13},
-       {0x2206, 0xF726}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
-       {0x2206, 0xF727}, {0x2206, 0xE4EA}, {0x2206, 0x14E5}, {0x2206, 0xEA15},
-       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B87},
-       {0x2206, 0xAD23}, {0x2206, 0x38AD}, {0x2206, 0x2135}, {0x2206, 0xE0E0},
-       {0x2206, 0x20E1}, {0x2206, 0xE021}, {0x2206, 0xAC21}, {0x2206, 0x2CE0},
-       {0x2206, 0xEA14}, {0x2206, 0xE1EA}, {0x2206, 0x15F6}, {0x2206, 0x27E4},
-       {0x2206, 0xEA14}, {0x2206, 0xE5EA}, {0x2206, 0x15E2}, {0x2206, 0xEA12},
-       {0x2206, 0xE3EA}, {0x2206, 0x135A}, {0x2206, 0x8FE6}, {0x2206, 0xEA12},
-       {0x2206, 0xE7EA}, {0x2206, 0x13F7}, {0x2206, 0x26E4}, {0x2206, 0xEA14},
-       {0x2206, 0xE5EA}, {0x2206, 0x15F7}, {0x2206, 0x27E4}, {0x2206, 0xEA14},
-       {0x2206, 0xE5EA}, {0x2206, 0x15FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
-       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2146},
-       {0x2206, 0xE0E0}, {0x2206, 0x22E1}, {0x2206, 0xE023}, {0x2206, 0x58C0},
-       {0x2206, 0x5902}, {0x2206, 0x1E01}, {0x2206, 0xE18B}, {0x2206, 0x651F},
-       {0x2206, 0x109E}, {0x2206, 0x33E4}, {0x2206, 0x8B65}, {0x2206, 0xAD21},
-       {0x2206, 0x22AD}, {0x2206, 0x272A}, {0x2206, 0xD400}, {0x2206, 0x01BF},
-       {0x2206, 0x34F2}, {0x2206, 0x022C}, {0x2206, 0xA2BF}, {0x2206, 0x34F5},
-       {0x2206, 0x022C}, {0x2206, 0xE0E0}, {0x2206, 0x8B67}, {0x2206, 0x1B10},
-       {0x2206, 0xAA14}, {0x2206, 0xE18B}, {0x2206, 0x660D}, {0x2206, 0x1459},
-       {0x2206, 0x0FAE}, {0x2206, 0x05E1}, {0x2206, 0x8B66}, {0x2206, 0x590F},
-       {0x2206, 0xBF85}, {0x2206, 0x6102}, {0x2206, 0x2CA2}, {0x2206, 0xEF96},
-       {0x2206, 0xFEFC}, {0x2206, 0x04F8}, {0x2206, 0xF9FA}, {0x2206, 0xFBEF},
-       {0x2206, 0x79E2}, {0x2206, 0x8AD2}, {0x2206, 0xAC19}, {0x2206, 0x2DE0},
-       {0x2206, 0xE036}, {0x2206, 0xE1E0}, {0x2206, 0x37EF}, {0x2206, 0x311F},
-       {0x2206, 0x325B}, {0x2206, 0x019E}, {0x2206, 0x1F7A}, {0x2206, 0x0159},
-       {0x2206, 0x019F}, {0x2206, 0x0ABF}, {0x2206, 0x348E}, {0x2206, 0x022C},
-       {0x2206, 0x31F6}, {0x2206, 0x06AE}, {0x2206, 0x0FF6}, {0x2206, 0x0302},
-       {0x2206, 0x0470}, {0x2206, 0xF703}, {0x2206, 0xF706}, {0x2206, 0xBF34},
-       {0x2206, 0x9302}, {0x2206, 0x2C31}, {0x2206, 0xAC1A}, {0x2206, 0x25E0},
-       {0x2206, 0xE022}, {0x2206, 0xE1E0}, {0x2206, 0x23EF}, {0x2206, 0x300D},
-       {0x2206, 0x311F}, {0x2206, 0x325B}, {0x2206, 0x029E}, {0x2206, 0x157A},
-       {0x2206, 0x0258}, {0x2206, 0xC4A0}, {0x2206, 0x0408}, {0x2206, 0xBF34},
-       {0x2206, 0x9E02}, {0x2206, 0x2C31}, {0x2206, 0xAE06}, {0x2206, 0xBF34},
-       {0x2206, 0x9C02}, {0x2206, 0x2C31}, {0x2206, 0xAC1B}, {0x2206, 0x4AE0},
-       {0x2206, 0xE012}, {0x2206, 0xE1E0}, {0x2206, 0x13EF}, {0x2206, 0x300D},
-       {0x2206, 0x331F}, {0x2206, 0x325B}, {0x2206, 0x1C9E}, {0x2206, 0x3AEF},
-       {0x2206, 0x325B}, {0x2206, 0x1C9F}, {0x2206, 0x09BF}, {0x2206, 0x3498},
-       {0x2206, 0x022C}, {0x2206, 0x3102}, {0x2206, 0x83C5}, {0x2206, 0x5A03},
-       {0x2206, 0x0D03}, {0x2206, 0x581C}, {0x2206, 0x1E20}, {0x2206, 0x0207},
-       {0x2206, 0xA0A0}, {0x2206, 0x000E}, {0x2206, 0x0284}, {0x2206, 0x17AD},
-       {0x2206, 0x1817}, {0x2206, 0xBF34}, {0x2206, 0x9A02}, {0x2206, 0x2C31},
-       {0x2206, 0xAE0F}, {0x2206, 0xBF34}, {0x2206, 0xC802}, {0x2206, 0x2C31},
-       {0x2206, 0xBF34}, {0x2206, 0xC502}, {0x2206, 0x2C31}, {0x2206, 0x0284},
-       {0x2206, 0x52E6}, {0x2206, 0x8AD2}, {0x2206, 0xEF97}, {0x2206, 0xFFFE},
-       {0x2206, 0xFDFC}, {0x2206, 0x04F8}, {0x2206, 0xBF34}, {0x2206, 0xDA02},
-       {0x2206, 0x2CE0}, {0x2206, 0xE58A}, {0x2206, 0xD3BF}, {0x2206, 0x34D4},
-       {0x2206, 0x022C}, {0x2206, 0xE00C}, {0x2206, 0x1159}, {0x2206, 0x02E0},
-       {0x2206, 0x8AD3}, {0x2206, 0x1E01}, {0x2206, 0xE48A}, {0x2206, 0xD3D1},
-       {0x2206, 0x00BF}, {0x2206, 0x34DA}, {0x2206, 0x022C}, {0x2206, 0xA2D1},
-       {0x2206, 0x01BF}, {0x2206, 0x34D4}, {0x2206, 0x022C}, {0x2206, 0xA2BF},
-       {0x2206, 0x34CB}, {0x2206, 0x022C}, {0x2206, 0xE0E5}, {0x2206, 0x8ACE},
-       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CE0}, {0x2206, 0xE58A},
-       {0x2206, 0xCFBF}, {0x2206, 0x8564}, {0x2206, 0x022C}, {0x2206, 0xE0E5},
-       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6A02}, {0x2206, 0x2CE0},
-       {0x2206, 0xE58A}, {0x2206, 0xD1FC}, {0x2206, 0x04F8}, {0x2206, 0xE18A},
-       {0x2206, 0xD1BF}, {0x2206, 0x856A}, {0x2206, 0x022C}, {0x2206, 0xA2E1},
-       {0x2206, 0x8AD0}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
-       {0x2206, 0xE18A}, {0x2206, 0xCFBF}, {0x2206, 0x8567}, {0x2206, 0x022C},
-       {0x2206, 0xA2E1}, {0x2206, 0x8ACE}, {0x2206, 0xBF34}, {0x2206, 0xCB02},
-       {0x2206, 0x2CA2}, {0x2206, 0xE18A}, {0x2206, 0xD3BF}, {0x2206, 0x34DA},
-       {0x2206, 0x022C}, {0x2206, 0xA2E1}, {0x2206, 0x8AD3}, {0x2206, 0x0D11},
-       {0x2206, 0xBF34}, {0x2206, 0xD402}, {0x2206, 0x2CA2}, {0x2206, 0xFC04},
-       {0x2206, 0xF9A0}, {0x2206, 0x0405}, {0x2206, 0xE38A}, {0x2206, 0xD4AE},
-       {0x2206, 0x13A0}, {0x2206, 0x0805}, {0x2206, 0xE38A}, {0x2206, 0xD5AE},
-       {0x2206, 0x0BA0}, {0x2206, 0x0C05}, {0x2206, 0xE38A}, {0x2206, 0xD6AE},
-       {0x2206, 0x03E3}, {0x2206, 0x8AD7}, {0x2206, 0xEF13}, {0x2206, 0xBF34},
-       {0x2206, 0xCB02}, {0x2206, 0x2CA2}, {0x2206, 0xEF13}, {0x2206, 0x0D11},
-       {0x2206, 0xBF85}, {0x2206, 0x6702}, {0x2206, 0x2CA2}, {0x2206, 0xEF13},
-       {0x2206, 0x0D14}, {0x2206, 0xBF85}, {0x2206, 0x6402}, {0x2206, 0x2CA2},
-       {0x2206, 0xEF13}, {0x2206, 0x0D17}, {0x2206, 0xBF85}, {0x2206, 0x6A02},
-       {0x2206, 0x2CA2}, {0x2206, 0xFD04}, {0x2206, 0xF8E0}, {0x2206, 0x8B85},
-       {0x2206, 0xAD27}, {0x2206, 0x2DE0}, {0x2206, 0xE036}, {0x2206, 0xE1E0},
-       {0x2206, 0x37E1}, {0x2206, 0x8B73}, {0x2206, 0x1F10}, {0x2206, 0x9E20},
-       {0x2206, 0xE48B}, {0x2206, 0x73AC}, {0x2206, 0x200B}, {0x2206, 0xAC21},
-       {0x2206, 0x0DAC}, {0x2206, 0x250F}, {0x2206, 0xAC27}, {0x2206, 0x0EAE},
-       {0x2206, 0x0F02}, {0x2206, 0x84CC}, {0x2206, 0xAE0A}, {0x2206, 0x0284},
-       {0x2206, 0xD1AE}, {0x2206, 0x05AE}, {0x2206, 0x0302}, {0x2206, 0x84D8},
-       {0x2206, 0xFC04}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0x0402},
-       {0x2206, 0x84E5}, {0x2206, 0x0285}, {0x2206, 0x2804}, {0x2206, 0x0285},
-       {0x2206, 0x4904}, {0x2206, 0xEE8B}, {0x2206, 0x6800}, {0x2206, 0xEE8B},
-       {0x2206, 0x6902}, {0x2206, 0x04F8}, {0x2206, 0xF9E0}, {0x2206, 0x8B85},
-       {0x2206, 0xAD26}, {0x2206, 0x38D0}, {0x2206, 0x0B02}, {0x2206, 0x2B4D},
-       {0x2206, 0x5882}, {0x2206, 0x7882}, {0x2206, 0x9F2D}, {0x2206, 0xE08B},
-       {0x2206, 0x68E1}, {0x2206, 0x8B69}, {0x2206, 0x1F10}, {0x2206, 0x9EC8},
-       {0x2206, 0x10E4}, {0x2206, 0x8B68}, {0x2206, 0xE0E0}, {0x2206, 0x00E1},
-       {0x2206, 0xE001}, {0x2206, 0xF727}, {0x2206, 0xE4E0}, {0x2206, 0x00E5},
-       {0x2206, 0xE001}, {0x2206, 0xE2E0}, {0x2206, 0x20E3}, {0x2206, 0xE021},
-       {0x2206, 0xAD30}, {0x2206, 0xF7F6}, {0x2206, 0x27E4}, {0x2206, 0xE000},
-       {0x2206, 0xE5E0}, {0x2206, 0x01FD}, {0x2206, 0xFC04}, {0x2206, 0xF8FA},
-       {0x2206, 0xEF69}, {0x2206, 0xE08B}, {0x2206, 0x86AD}, {0x2206, 0x2212},
-       {0x2206, 0xE0E0}, {0x2206, 0x14E1}, {0x2206, 0xE015}, {0x2206, 0xAD26},
-       {0x2206, 0x9CE1}, {0x2206, 0x85E0}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
-       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x04F8},
-       {0x2206, 0xFAEF}, {0x2206, 0x69E0}, {0x2206, 0x8B86}, {0x2206, 0xAD22},
-       {0x2206, 0x09E1}, {0x2206, 0x85E1}, {0x2206, 0xBF85}, {0x2206, 0x6D02},
-       {0x2206, 0x2CA2}, {0x2206, 0xEF96}, {0x2206, 0xFEFC}, {0x2206, 0x0464},
-       {0x2206, 0xE48C}, {0x2206, 0xFDE4}, {0x2206, 0x80CA}, {0x2206, 0xE480},
-       {0x2206, 0x66E0}, {0x2206, 0x8E70}, {0x2206, 0xE076}, {0x2205, 0xE142},
-       {0x2206, 0x0701}, {0x2205, 0xE140}, {0x2206, 0x0405}, {0x220F, 0x0000},
-       {0x221F, 0x0000}, {0x2200, 0x1340}, {0x133E, 0x000E}, {0x133F, 0x0010},
-       {0x13EB, 0x11BB}
-};
-
-static const struct rtl8367b_initval rtl8367r_vb_initvals_1[] = {
-       {0x1B03, 0x0876}, {0x1200, 0x7FC4}, {0x1305, 0xC000}, {0x121E, 0x03CA},
-       {0x1233, 0x0352}, {0x1234, 0x0064}, {0x1237, 0x0096}, {0x1238, 0x0078},
-       {0x1239, 0x0084}, {0x123A, 0x0030}, {0x205F, 0x0002}, {0x2059, 0x1A00},
-       {0x205F, 0x0000}, {0x207F, 0x0002}, {0x2077, 0x0000}, {0x2078, 0x0000},
-       {0x2079, 0x0000}, {0x207A, 0x0000}, {0x207B, 0x0000}, {0x207F, 0x0000},
-       {0x205F, 0x0002}, {0x2053, 0x0000}, {0x2054, 0x0000}, {0x2055, 0x0000},
-       {0x2056, 0x0000}, {0x2057, 0x0000}, {0x205F, 0x0000}, {0x133F, 0x0030},
-       {0x133E, 0x000E}, {0x221F, 0x0005}, {0x2205, 0x8B86}, {0x2206, 0x800E},
-       {0x221F, 0x0000}, {0x133F, 0x0010}, {0x12A3, 0x2200}, {0x6107, 0xE58B},
-       {0x6103, 0xA970}, {0x0018, 0x0F00}, {0x0038, 0x0F00}, {0x0058, 0x0F00},
-       {0x0078, 0x0F00}, {0x0098, 0x0F00}, {0x133F, 0x0030}, {0x133E, 0x000E},
-       {0x221F, 0x0005}, {0x2205, 0x8B6E}, {0x2206, 0x0000}, {0x220F, 0x0100},
-       {0x2205, 0xFFF6}, {0x2206, 0x0080}, {0x2205, 0x8000}, {0x2206, 0x0280},
-       {0x2206, 0x2BF7}, {0x2206, 0x00E0}, {0x2206, 0xFFF7}, {0x2206, 0xA080},
-       {0x2206, 0x02AE}, {0x2206, 0xF602}, {0x2206, 0x0153}, {0x2206, 0x0201},
-       {0x2206, 0x6602}, {0x2206, 0x8044}, {0x2206, 0x0201}, {0x2206, 0x7CE0},
-       {0x2206, 0x8B8C}, {0x2206, 0xE18B}, {0x2206, 0x8D1E}, {0x2206, 0x01E1},
-       {0x2206, 0x8B8E}, {0x2206, 0x1E01}, {0x2206, 0xA000}, {0x2206, 0xE4AE},
-       {0x2206, 0xD8EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE}, {0x2206, 0x85C1},
-       {0x2206, 0x00EE}, {0x2206, 0x8AFC}, {0x2206, 0x07EE}, {0x2206, 0x8AFD},
-       {0x2206, 0x73EE}, {0x2206, 0xFFF6}, {0x2206, 0x00EE}, {0x2206, 0xFFF7},
-       {0x2206, 0xFC04}, {0x2206, 0xF8E0}, {0x2206, 0x8B8E}, {0x2206, 0xAD20},
-       {0x2206, 0x0302}, {0x2206, 0x8050}, {0x2206, 0xFC04}, {0x2206, 0xF8F9},
-       {0x2206, 0xE08B}, {0x2206, 0x85AD}, {0x2206, 0x2548}, {0x2206, 0xE08A},
-       {0x2206, 0xE4E1}, {0x2206, 0x8AE5}, {0x2206, 0x7C00}, {0x2206, 0x009E},
-       {0x2206, 0x35EE}, {0x2206, 0x8AE4}, {0x2206, 0x00EE}, {0x2206, 0x8AE5},
-       {0x2206, 0x00E0}, {0x2206, 0x8AFC}, {0x2206, 0xE18A}, {0x2206, 0xFDE2},
-       {0x2206, 0x85C0}, {0x2206, 0xE385}, {0x2206, 0xC102}, {0x2206, 0x2DAC},
-       {0x2206, 0xAD20}, {0x2206, 0x12EE}, {0x2206, 0x8AE4}, {0x2206, 0x03EE},
-       {0x2206, 0x8AE5}, {0x2206, 0xB7EE}, {0x2206, 0x85C0}, {0x2206, 0x00EE},
-       {0x2206, 0x85C1}, {0x2206, 0x00AE}, {0x2206, 0x1115}, {0x2206, 0xE685},
-       {0x2206, 0xC0E7}, {0x2206, 0x85C1}, {0x2206, 0xAE08}, {0x2206, 0xEE85},
-       {0x2206, 0xC000}, {0x2206, 0xEE85}, {0x2206, 0xC100}, {0x2206, 0xFDFC},
-       {0x2206, 0x0400}, {0x2205, 0xE142}, {0x2206, 0x0701}, {0x2205, 0xE140},
-       {0x2206, 0x0405}, {0x220F, 0x0000}, {0x221F, 0x0000}, {0x133E, 0x000E},
-       {0x133F, 0x0010}, {0x13EB, 0x11BB}, {0x207F, 0x0002}, {0x2073, 0x1D22},
-       {0x207F, 0x0000}, {0x133F, 0x0030}, {0x133E, 0x000E}, {0x2200, 0x1340},
-       {0x133E, 0x000E}, {0x133F, 0x0010},
-};
-
-static int rtl8367b_write_initvals(struct rtl8366_smi *smi,
-                                 const struct rtl8367b_initval *initvals,
-                                 int count)
-{
-       int err;
-       int i;
-
-       for (i = 0; i < count; i++)
-               REG_WR(smi, initvals[i].reg, initvals[i].val);
-
-       return 0;
-}
-
-static int rtl8367b_read_phy_reg(struct rtl8366_smi *smi,
-                               u32 phy_addr, u32 phy_reg, u32 *val)
-{
-       int timeout;
-       u32 data;
-       int err;
-
-       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       if (phy_reg > RTL8367B_PHY_REG_MAX)
-               return -EINVAL;
-
-       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
-       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
-               return -ETIMEDOUT;
-
-       /* prepare address */
-       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
-              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
-
-       /* send read command */
-       REG_WR(smi, RTL8367B_IA_CTRL_REG,
-              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_READ);
-
-       timeout = 5;
-       do {
-               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
-               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
-                       break;
-
-               if (timeout--) {
-                       dev_err(smi->parent, "phy read timed out\n");
-                       return -ETIMEDOUT;
-               }
-
-               udelay(1);
-       } while (1);
-
-       /* read data */
-       REG_RD(smi, RTL8367B_IA_READ_DATA_REG, val);
-
-       dev_dbg(smi->parent, "phy_read: addr:%02x, reg:%02x, val:%04x\n",
-               phy_addr, phy_reg, *val);
-       return 0;
-}
-
-static int rtl8367b_write_phy_reg(struct rtl8366_smi *smi,
-                                u32 phy_addr, u32 phy_reg, u32 val)
-{
-       int timeout;
-       u32 data;
-       int err;
-
-       dev_dbg(smi->parent, "phy_write: addr:%02x, reg:%02x, val:%04x\n",
-               phy_addr, phy_reg, val);
-
-       if (phy_addr > RTL8367B_PHY_ADDR_MAX)
-               return -EINVAL;
-
-       if (phy_reg > RTL8367B_PHY_REG_MAX)
-               return -EINVAL;
-
-       REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
-       if (data & RTL8367B_IA_STATUS_PHY_BUSY)
-               return -ETIMEDOUT;
-
-       /* preapre data */
-       REG_WR(smi, RTL8367B_IA_WRITE_DATA_REG, val);
-
-       /* prepare address */
-       REG_WR(smi, RTL8367B_IA_ADDRESS_REG,
-              RTL8367B_INTERNAL_PHY_REG(phy_addr, phy_reg));
-
-       /* send write command */
-       REG_WR(smi, RTL8367B_IA_CTRL_REG,
-              RTL8367B_IA_CTRL_CMD_MASK | RTL8367B_IA_CTRL_RW_WRITE);
-
-       timeout = 5;
-       do {
-               REG_RD(smi, RTL8367B_IA_STATUS_REG, &data);
-               if ((data & RTL8367B_IA_STATUS_PHY_BUSY) == 0)
-                       break;
-
-               if (timeout--) {
-                       dev_err(smi->parent, "phy write timed out\n");
-                       return -ETIMEDOUT;
-               }
-
-               udelay(1);
-       } while (1);
-
-       return 0;
-}
-
-static int rtl8367b_init_regs(struct rtl8366_smi *smi)
-{
-       const struct rtl8367b_initval *initvals;
-       u32 chip_ver;
-       u32 rlvid;
-       int count;
-       int err;
-
-       REG_WR(smi, RTL8367B_RTL_MAGIC_ID_REG, RTL8367B_RTL_MAGIC_ID_VAL);
-       REG_RD(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
-
-       rlvid = (chip_ver >> RTL8367B_CHIP_VER_RLVID_SHIFT) &
-               RTL8367B_CHIP_VER_RLVID_MASK;
-
-       switch (rlvid) {
-       case 0:
-               initvals = rtl8367r_vb_initvals_0;
-               count = ARRAY_SIZE(rtl8367r_vb_initvals_0);
-               break;
-
-       case 1:
-               initvals = rtl8367r_vb_initvals_1;
-               count = ARRAY_SIZE(rtl8367r_vb_initvals_1);
-               break;
-
-       default:
-               dev_err(smi->parent, "unknow rlvid %u\n", rlvid);
-               return -ENODEV;
-       }
-
-       /* TODO: disable RLTP */
-
-       return rtl8367b_write_initvals(smi, initvals, count);
-}
-
-static int rtl8367b_reset_chip(struct rtl8366_smi *smi)
-{
-       int timeout = 10;
-       int err;
-       u32 data;
-
-       REG_WR(smi, RTL8367B_CHIP_RESET_REG, RTL8367B_CHIP_RESET_HW);
-       msleep(RTL8367B_RESET_DELAY);
-
-       do {
-               REG_RD(smi, RTL8367B_CHIP_RESET_REG, &data);
-               if (!(data & RTL8367B_CHIP_RESET_HW))
-                       break;
-
-               msleep(1);
-       } while (--timeout);
-
-       if (!timeout) {
-               dev_err(smi->parent, "chip reset timed out\n");
-               return -ETIMEDOUT;
-       }
-
-       return 0;
-}
-
-static int rtl8367b_extif_set_mode(struct rtl8366_smi *smi, int id,
-                                  enum rtl8367_extif_mode mode)
-{
-       int err;
-
-       /* set port mode */
-       switch (mode) {
-       case RTL8367_EXTIF_MODE_RGMII:
-       case RTL8367_EXTIF_MODE_RGMII_33V:
-               REG_WR(smi, RTL8367B_CHIP_DEBUG0_REG, 0x0367);
-               REG_WR(smi, RTL8367B_CHIP_DEBUG1_REG, 0x7777);
-               break;
-
-       case RTL8367_EXTIF_MODE_TMII_MAC:
-       case RTL8367_EXTIF_MODE_TMII_PHY:
-               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
-                       BIT((id + 1) % 2), BIT((id + 1) % 2));
-               break;
-
-       case RTL8367_EXTIF_MODE_GMII:
-               REG_RMW(smi, RTL8367B_CHIP_DEBUG0_REG,
-                       RTL8367B_CHIP_DEBUG0_DUMMY0(id),
-                       RTL8367B_CHIP_DEBUG0_DUMMY0(id));
-               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), BIT(6));
-               break;
-
-       case RTL8367_EXTIF_MODE_MII_MAC:
-       case RTL8367_EXTIF_MODE_MII_PHY:
-       case RTL8367_EXTIF_MODE_DISABLED:
-               REG_RMW(smi, RTL8367B_BYPASS_LINE_RATE_REG,
-                       BIT((id + 1) % 2), 0);
-               REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), BIT(6), 0);
-               break;
-
-       default:
-               dev_err(smi->parent,
-                       "invalid mode for external interface %d\n", id);
-               return -EINVAL;
-       }
-
-       REG_RMW(smi, RTL8367B_DIS_REG,
-               RTL8367B_DIS_RGMII_MASK << RTL8367B_DIS_RGMII_SHIFT(id),
-               mode << RTL8367B_DIS_RGMII_SHIFT(id));
-
-       return 0;
-}
-
-static int rtl8367b_extif_set_force(struct rtl8366_smi *smi, int id,
-                                   struct rtl8367_port_ability *pa)
-{
-       u32 mask;
-       u32 val;
-       int err;
-
-       mask = (RTL8367B_DI_FORCE_MODE |
-               RTL8367B_DI_FORCE_NWAY |
-               RTL8367B_DI_FORCE_TXPAUSE |
-               RTL8367B_DI_FORCE_RXPAUSE |
-               RTL8367B_DI_FORCE_LINK |
-               RTL8367B_DI_FORCE_DUPLEX |
-               RTL8367B_DI_FORCE_SPEED_MASK);
-
-       val = pa->speed;
-       val |= pa->force_mode ? RTL8367B_DI_FORCE_MODE : 0;
-       val |= pa->nway ? RTL8367B_DI_FORCE_NWAY : 0;
-       val |= pa->txpause ? RTL8367B_DI_FORCE_TXPAUSE : 0;
-       val |= pa->rxpause ? RTL8367B_DI_FORCE_RXPAUSE : 0;
-       val |= pa->link ? RTL8367B_DI_FORCE_LINK : 0;
-       val |= pa->duplex ? RTL8367B_DI_FORCE_DUPLEX : 0;
-
-       REG_RMW(smi, RTL8367B_DI_FORCE_REG(id), mask, val);
-
-       return 0;
-}
-
-static int rtl8367b_extif_set_rgmii_delay(struct rtl8366_smi *smi, int id,
-                                        unsigned txdelay, unsigned rxdelay)
-{
-       u32 mask;
-       u32 val;
-       int err;
-
-       mask = (RTL8367B_EXT_RGMXF_RXDELAY_MASK |
-               (RTL8367B_EXT_RGMXF_TXDELAY_MASK <<
-                       RTL8367B_EXT_RGMXF_TXDELAY_SHIFT));
-
-       val = rxdelay;
-       val |= txdelay << RTL8367B_EXT_RGMXF_TXDELAY_SHIFT;
-
-       REG_RMW(smi, RTL8367B_EXT_RGMXF_REG(id), mask, val);
-
-       return 0;
-}
-
-static int rtl8367b_extif_init(struct rtl8366_smi *smi, int id,
-                              struct rtl8367_extif_config *cfg)
-{
-       enum rtl8367_extif_mode mode;
-       int err;
-
-       mode = (cfg) ? cfg->mode : RTL8367_EXTIF_MODE_DISABLED;
-
-       err = rtl8367b_extif_set_mode(smi, id, mode);
-       if (err)
-               return err;
-
-       if (mode != RTL8367_EXTIF_MODE_DISABLED) {
-               err = rtl8367b_extif_set_force(smi, id, &cfg->ability);
-               if (err)
-                       return err;
-
-               err = rtl8367b_extif_set_rgmii_delay(smi, id, cfg->txdelay,
-                                                    cfg->rxdelay);
-               if (err)
-                       return err;
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_OF
-static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
-                                 const char *name)
-{
-       struct rtl8367_extif_config *cfg;
-       const __be32 *prop;
-       int size;
-       int err;
-
-       prop = of_get_property(smi->parent->of_node, name, &size);
-       if (!prop)
-               return rtl8367b_extif_init(smi, id, NULL);
-
-       if (size != (9 * sizeof(*prop))) {
-               dev_err(smi->parent, "%s property is invalid\n", name);
-               return -EINVAL;
-       }
-
-       cfg = kzalloc(sizeof(struct rtl8367_extif_config), GFP_KERNEL);
-       if (!cfg)
-               return -ENOMEM;
-
-       cfg->txdelay = be32_to_cpup(prop++);
-       cfg->rxdelay = be32_to_cpup(prop++);
-       cfg->mode = be32_to_cpup(prop++);
-       cfg->ability.force_mode = be32_to_cpup(prop++);
-       cfg->ability.txpause = be32_to_cpup(prop++);
-       cfg->ability.rxpause = be32_to_cpup(prop++);
-       cfg->ability.link = be32_to_cpup(prop++);
-       cfg->ability.duplex = be32_to_cpup(prop++);
-       cfg->ability.speed = be32_to_cpup(prop++);
-
-       err = rtl8367b_extif_init(smi, id, cfg);
-       kfree(cfg);
-
-       return err;
-}
-#else
-static int rtl8367b_extif_init_of(struct rtl8366_smi *smi, int id,
-                                 const char *name)
-{
-       return -EINVAL;
-}
-#endif
-
-static int rtl8367b_setup(struct rtl8366_smi *smi)
-{
-       struct rtl8367_platform_data *pdata;
-       int err;
-       int i;
-
-       pdata = smi->parent->platform_data;
-
-       err = rtl8367b_init_regs(smi);
-       if (err)
-               return err;
-
-       /* initialize external interfaces */
-       if (smi->parent->of_node) {
-               err = rtl8367b_extif_init_of(smi, 0, "realtek,extif0");
-               if (err)
-                       return err;
-
-               err = rtl8367b_extif_init_of(smi, 1, "realtek,extif1");
-               if (err)
-                       return err;
-       } else {
-               err = rtl8367b_extif_init(smi, 0, pdata->extif0_cfg);
-               if (err)
-                       return err;
-
-               err = rtl8367b_extif_init(smi, 1, pdata->extif1_cfg);
-               if (err)
-                       return err;
-       }
-
-       /* set maximum packet length to 1536 bytes */
-       REG_RMW(smi, RTL8367B_SWC0_REG, RTL8367B_SWC0_MAX_LENGTH_MASK,
-               RTL8367B_SWC0_MAX_LENGTH_1536);
-
-       /*
-        * discard VLAN tagged packets if the port is not a member of
-        * the VLAN with which the packets is associated.
-        */
-       REG_WR(smi, RTL8367B_VLAN_INGRESS_REG, RTL8367B_PORTS_ALL);
-
-       /*
-        * Setup egress tag mode for each port.
-        */
-       for (i = 0; i < RTL8367B_NUM_PORTS; i++)
-               REG_RMW(smi,
-                       RTL8367B_PORT_MISC_CFG_REG(i),
-                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_MASK <<
-                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT,
-                       RTL8367B_PORT_MISC_CFG_EGRESS_MODE_ORIGINAL <<
-                               RTL8367B_PORT_MISC_CFG_EGRESS_MODE_SHIFT);
-
-       return 0;
-}
-
-static int rtl8367b_get_mib_counter(struct rtl8366_smi *smi, int counter,
-                                   int port, unsigned long long *val)
-{
-       struct rtl8366_mib_counter *mib;
-       int offset;
-       int i;
-       int err;
-       u32 addr, data;
-       u64 mibvalue;
-
-       if (port > RTL8367B_NUM_PORTS ||
-           counter >= RTL8367B_NUM_MIB_COUNTERS)
-               return -EINVAL;
-
-       mib = &rtl8367b_mib_counters[counter];
-       addr = RTL8367B_MIB_COUNTER_PORT_OFFSET * port + mib->offset;
-
-       /*
-        * Writing access counter address first
-        * then ASIC will prepare 64bits counter wait for being retrived
-        */
-       REG_WR(smi, RTL8367B_MIB_ADDRESS_REG, addr >> 2);
-
-       /* read MIB control register */
-       REG_RD(smi, RTL8367B_MIB_CTRL0_REG(0), &data);
-
-       if (data & RTL8367B_MIB_CTRL0_BUSY_MASK)
-               return -EBUSY;
-
-       if (data & RTL8367B_MIB_CTRL0_RESET_MASK)
-               return -EIO;
-
-       if (mib->length == 4)
-               offset = 3;
-       else
-               offset = (mib->offset + 1) % 4;
-
-       mibvalue = 0;
-       for (i = 0; i < mib->length; i++) {
-               REG_RD(smi, RTL8367B_MIB_COUNTER_REG(offset - i), &data);
-               mibvalue = (mibvalue << 16) | (data & 0xFFFF);
-       }
-
-       *val = mibvalue;
-       return 0;
-}
-
-static int rtl8367b_get_vlan_4k(struct rtl8366_smi *smi, u32 vid,
-                               struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
-       int err;
-       int i;
-
-       memset(vlan4k, '\0', sizeof(struct rtl8366_vlan_4k));
-
-       if (vid >= RTL8367B_NUM_VIDS)
-               return -EINVAL;
-
-       /* write VID */
-       REG_WR(smi, RTL8367B_TA_ADDR_REG, vid);
-
-       /* write table access control word */
-       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_READ);
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_RD(smi, RTL8367B_TA_RDDATA_REG(i), &data[i]);
-
-       vlan4k->vid = vid;
-       vlan4k->member = (data[0] >> RTL8367B_TA_VLAN0_MEMBER_SHIFT) &
-                        RTL8367B_TA_VLAN0_MEMBER_MASK;
-       vlan4k->untag = (data[0] >> RTL8367B_TA_VLAN0_UNTAG_SHIFT) &
-                       RTL8367B_TA_VLAN0_UNTAG_MASK;
-       vlan4k->fid = (data[1] >> RTL8367B_TA_VLAN1_FID_SHIFT) &
-                     RTL8367B_TA_VLAN1_FID_MASK;
-
-       return 0;
-}
-
-static int rtl8367b_set_vlan_4k(struct rtl8366_smi *smi,
-                               const struct rtl8366_vlan_4k *vlan4k)
-{
-       u32 data[RTL8367B_TA_VLAN_NUM_WORDS];
-       int err;
-       int i;
-
-       if (vlan4k->vid >= RTL8367B_NUM_VIDS ||
-           vlan4k->member > RTL8367B_TA_VLAN0_MEMBER_MASK ||
-           vlan4k->untag > RTL8367B_UNTAG_MASK ||
-           vlan4k->fid > RTL8367B_FIDMAX)
-               return -EINVAL;
-
-       memset(data, 0, sizeof(data));
-
-       data[0] = (vlan4k->member & RTL8367B_TA_VLAN0_MEMBER_MASK) <<
-                 RTL8367B_TA_VLAN0_MEMBER_SHIFT;
-       data[0] |= (vlan4k->untag & RTL8367B_TA_VLAN0_UNTAG_MASK) <<
-                  RTL8367B_TA_VLAN0_UNTAG_SHIFT;
-       data[1] = (vlan4k->fid & RTL8367B_TA_VLAN1_FID_MASK) <<
-                 RTL8367B_TA_VLAN1_FID_SHIFT;
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_WR(smi, RTL8367B_TA_WRDATA_REG(i), data[i]);
-
-       /* write VID */
-       REG_WR(smi, RTL8367B_TA_ADDR_REG,
-              vlan4k->vid & RTL8367B_TA_VLAN_VID_MASK);
-
-       /* write table access control word */
-       REG_WR(smi, RTL8367B_TA_CTRL_REG, RTL8367B_TA_CTRL_CVLAN_WRITE);
-
-       return 0;
-}
-
-static int rtl8367b_get_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                               struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
-       int err;
-       int i;
-
-       memset(vlanmc, '\0', sizeof(struct rtl8366_vlan_mc));
-
-       if (index >= RTL8367B_NUM_VLANS)
-               return -EINVAL;
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_RD(smi, RTL8367B_VLAN_MC_BASE(index) + i, &data[i]);
-
-       vlanmc->member = (data[0] >> RTL8367B_VLAN_MC0_MEMBER_SHIFT) &
-                        RTL8367B_VLAN_MC0_MEMBER_MASK;
-       vlanmc->fid = (data[1] >> RTL8367B_VLAN_MC1_FID_SHIFT) &
-                     RTL8367B_VLAN_MC1_FID_MASK;
-       vlanmc->vid = (data[3] >> RTL8367B_VLAN_MC3_EVID_SHIFT) &
-                     RTL8367B_VLAN_MC3_EVID_MASK;
-
-       return 0;
-}
-
-static int rtl8367b_set_vlan_mc(struct rtl8366_smi *smi, u32 index,
-                               const struct rtl8366_vlan_mc *vlanmc)
-{
-       u32 data[RTL8367B_VLAN_MC_NUM_WORDS];
-       int err;
-       int i;
-
-       if (index >= RTL8367B_NUM_VLANS ||
-           vlanmc->vid >= RTL8367B_NUM_VIDS ||
-           vlanmc->priority > RTL8367B_PRIORITYMAX ||
-           vlanmc->member > RTL8367B_VLAN_MC0_MEMBER_MASK ||
-           vlanmc->untag > RTL8367B_UNTAG_MASK ||
-           vlanmc->fid > RTL8367B_FIDMAX)
-               return -EINVAL;
-
-       data[0] = (vlanmc->member & RTL8367B_VLAN_MC0_MEMBER_MASK) <<
-                 RTL8367B_VLAN_MC0_MEMBER_SHIFT;
-       data[1] = (vlanmc->fid & RTL8367B_VLAN_MC1_FID_MASK) <<
-                 RTL8367B_VLAN_MC1_FID_SHIFT;
-       data[2] = 0;
-       data[3] = (vlanmc->vid & RTL8367B_VLAN_MC3_EVID_MASK) <<
-                  RTL8367B_VLAN_MC3_EVID_SHIFT;
-
-       for (i = 0; i < ARRAY_SIZE(data); i++)
-               REG_WR(smi, RTL8367B_VLAN_MC_BASE(index) + i, data[i]);
-
-       return 0;
-}
-
-static int rtl8367b_get_mc_index(struct rtl8366_smi *smi, int port, int *val)
-{
-       u32 data;
-       int err;
-
-       if (port >= RTL8367B_NUM_PORTS)
-               return -EINVAL;
-
-       REG_RD(smi, RTL8367B_VLAN_PVID_CTRL_REG(port), &data);
-
-       *val = (data >> RTL8367B_VLAN_PVID_CTRL_SHIFT(port)) &
-              RTL8367B_VLAN_PVID_CTRL_MASK;
-
-       return 0;
-}
-
-static int rtl8367b_set_mc_index(struct rtl8366_smi *smi, int port, int index)
-{
-       if (port >= RTL8367B_NUM_PORTS || index >= RTL8367B_NUM_VLANS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_PVID_CTRL_REG(port),
-                               RTL8367B_VLAN_PVID_CTRL_MASK <<
-                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port),
-                               (index & RTL8367B_VLAN_PVID_CTRL_MASK) <<
-                                       RTL8367B_VLAN_PVID_CTRL_SHIFT(port));
-}
-
-static int rtl8367b_enable_vlan(struct rtl8366_smi *smi, int enable)
-{
-       return rtl8366_smi_rmwr(smi, RTL8367B_VLAN_CTRL_REG,
-                               RTL8367B_VLAN_CTRL_ENABLE,
-                               (enable) ? RTL8367B_VLAN_CTRL_ENABLE : 0);
-}
-
-static int rtl8367b_enable_vlan4k(struct rtl8366_smi *smi, int enable)
-{
-       return 0;
-}
-
-static int rtl8367b_is_vlan_valid(struct rtl8366_smi *smi, unsigned vlan)
-{
-       unsigned max = RTL8367B_NUM_VLANS;
-
-       if (smi->vlan4k_enabled)
-               max = RTL8367B_NUM_VIDS - 1;
-
-       if (vlan == 0 || vlan >= max)
-               return 0;
-
-       return 1;
-}
-
-static int rtl8367b_enable_port(struct rtl8366_smi *smi, int port, int enable)
-{
-       int err;
-
-       REG_WR(smi, RTL8367B_PORT_ISOLATION_REG(port),
-              (enable) ? RTL8367B_PORTS_ALL : 0);
-
-       return 0;
-}
-
-static int rtl8367b_sw_reset_mibs(struct switch_dev *dev,
-                                 const struct switch_attr *attr,
-                                 struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-
-       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(0), 0,
-                               RTL8367B_MIB_CTRL0_GLOBAL_RESET_MASK);
-}
-
-static int rtl8367b_sw_get_port_link(struct switch_dev *dev,
-                                   int port,
-                                   struct switch_port_link *link)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data = 0;
-       u32 speed;
-
-       if (port >= RTL8367B_NUM_PORTS)
-               return -EINVAL;
-
-       rtl8366_smi_read_reg(smi, RTL8367B_PORT_STATUS_REG(port), &data);
-
-       link->link = !!(data & RTL8367B_PORT_STATUS_LINK);
-       if (!link->link)
-               return 0;
-
-       link->duplex = !!(data & RTL8367B_PORT_STATUS_DUPLEX);
-       link->rx_flow = !!(data & RTL8367B_PORT_STATUS_RXPAUSE);
-       link->tx_flow = !!(data & RTL8367B_PORT_STATUS_TXPAUSE);
-       link->aneg = !!(data & RTL8367B_PORT_STATUS_NWAY);
-
-       speed = (data & RTL8367B_PORT_STATUS_SPEED_MASK);
-       switch (speed) {
-       case 0:
-               link->speed = SWITCH_PORT_SPEED_10;
-               break;
-       case 1:
-               link->speed = SWITCH_PORT_SPEED_100;
-               break;
-       case 2:
-               link->speed = SWITCH_PORT_SPEED_1000;
-               break;
-       default:
-               link->speed = SWITCH_PORT_SPEED_UNKNOWN;
-               break;
-       }
-
-       return 0;
-}
-
-static int rtl8367b_sw_get_max_length(struct switch_dev *dev,
-                                    const struct switch_attr *attr,
-                                    struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 data;
-
-       rtl8366_smi_read_reg(smi, RTL8367B_SWC0_REG, &data);
-       val->value.i = (data & RTL8367B_SWC0_MAX_LENGTH_MASK) >>
-                       RTL8367B_SWC0_MAX_LENGTH_SHIFT;
-
-       return 0;
-}
-
-static int rtl8367b_sw_set_max_length(struct switch_dev *dev,
-                                    const struct switch_attr *attr,
-                                    struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       u32 max_len;
-
-       switch (val->value.i) {
-       case 0:
-               max_len = RTL8367B_SWC0_MAX_LENGTH_1522;
-               break;
-       case 1:
-               max_len = RTL8367B_SWC0_MAX_LENGTH_1536;
-               break;
-       case 2:
-               max_len = RTL8367B_SWC0_MAX_LENGTH_1552;
-               break;
-       case 3:
-               max_len = RTL8367B_SWC0_MAX_LENGTH_16000;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return rtl8366_smi_rmwr(smi, RTL8367B_SWC0_REG,
-                               RTL8367B_SWC0_MAX_LENGTH_MASK, max_len);
-}
-
-
-static int rtl8367b_sw_reset_port_mibs(struct switch_dev *dev,
-                                      const struct switch_attr *attr,
-                                      struct switch_val *val)
-{
-       struct rtl8366_smi *smi = sw_to_rtl8366_smi(dev);
-       int port;
-
-       port = val->port_vlan;
-       if (port >= RTL8367B_NUM_PORTS)
-               return -EINVAL;
-
-       return rtl8366_smi_rmwr(smi, RTL8367B_MIB_CTRL0_REG(port / 8), 0,
-                               RTL8367B_MIB_CTRL0_PORT_RESET_MASK(port % 8));
-}
-
-static int rtl8367b_sw_get_port_stats(struct switch_dev *dev, int port,
-                                        struct switch_port_stats *stats)
-{
-       return (rtl8366_sw_get_port_stats(dev, port, stats,
-                               RTL8367B_MIB_TXB_ID, RTL8367B_MIB_RXB_ID));
-}
-
-static struct switch_attr rtl8367b_globals[] = {
-       {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan",
-               .description = "Enable VLAN mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 1
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "enable_vlan4k",
-               .description = "Enable VLAN 4K mode",
-               .set = rtl8366_sw_set_vlan_enable,
-               .get = rtl8366_sw_get_vlan_enable,
-               .max = 1,
-               .ofs = 2
-       }, {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mibs",
-               .description = "Reset all MIB counters",
-               .set = rtl8367b_sw_reset_mibs,
-       }, {
-               .type = SWITCH_TYPE_INT,
-               .name = "max_length",
-               .description = "Get/Set the maximum length of valid packets"
-                              "(0:1522, 1:1536, 2:1552, 3:16000)",
-               .set = rtl8367b_sw_set_max_length,
-               .get = rtl8367b_sw_get_max_length,
-               .max = 3,
-       }
-};
-
-static struct switch_attr rtl8367b_port[] = {
-       {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset_mib",
-               .description = "Reset single port MIB counters",
-               .set = rtl8367b_sw_reset_port_mibs,
-       }, {
-               .type = SWITCH_TYPE_STRING,
-               .name = "mib",
-               .description = "Get MIB counters for port",
-               .max = 33,
-               .set = NULL,
-               .get = rtl8366_sw_get_port_mib,
-       },
-};
-
-static struct switch_attr rtl8367b_vlan[] = {
-       {
-               .type = SWITCH_TYPE_STRING,
-               .name = "info",
-               .description = "Get vlan information",
-               .max = 1,
-               .set = NULL,
-               .get = rtl8366_sw_get_vlan_info,
-       },
-};
-
-static const struct switch_dev_ops rtl8367b_sw_ops = {
-       .attr_global = {
-               .attr = rtl8367b_globals,
-               .n_attr = ARRAY_SIZE(rtl8367b_globals),
-       },
-       .attr_port = {
-               .attr = rtl8367b_port,
-               .n_attr = ARRAY_SIZE(rtl8367b_port),
-       },
-       .attr_vlan = {
-               .attr = rtl8367b_vlan,
-               .n_attr = ARRAY_SIZE(rtl8367b_vlan),
-       },
-
-       .get_vlan_ports = rtl8366_sw_get_vlan_ports,
-       .set_vlan_ports = rtl8366_sw_set_vlan_ports,
-       .get_port_pvid = rtl8366_sw_get_port_pvid,
-       .set_port_pvid = rtl8366_sw_set_port_pvid,
-       .reset_switch = rtl8366_sw_reset_switch,
-       .get_port_link = rtl8367b_sw_get_port_link,
-       .get_port_stats = rtl8367b_sw_get_port_stats,
-};
-
-static int rtl8367b_switch_init(struct rtl8366_smi *smi)
-{
-       struct switch_dev *dev = &smi->sw_dev;
-       int err;
-
-       dev->name = "RTL8367B";
-       dev->cpu_port = RTL8367B_CPU_PORT_NUM;
-       dev->ports = RTL8367B_NUM_PORTS;
-       dev->vlans = RTL8367B_NUM_VIDS;
-       dev->ops = &rtl8367b_sw_ops;
-       dev->alias = dev_name(smi->parent);
-
-       err = register_switch(dev, NULL);
-       if (err)
-               dev_err(smi->parent, "switch registration failed\n");
-
-       return err;
-}
-
-static void rtl8367b_switch_cleanup(struct rtl8366_smi *smi)
-{
-       unregister_switch(&smi->sw_dev);
-}
-
-static int rtl8367b_mii_read(struct mii_bus *bus, int addr, int reg)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 val = 0;
-       int err;
-
-       err = rtl8367b_read_phy_reg(smi, addr, reg, &val);
-       if (err)
-               return 0xffff;
-
-       return val;
-}
-
-static int rtl8367b_mii_write(struct mii_bus *bus, int addr, int reg, u16 val)
-{
-       struct rtl8366_smi *smi = bus->priv;
-       u32 t;
-       int err;
-
-       err = rtl8367b_write_phy_reg(smi, addr, reg, val);
-       if (err)
-               return err;
-
-       /* flush write */
-       (void) rtl8367b_read_phy_reg(smi, addr, reg, &t);
-
-       return err;
-}
-
-static int rtl8367b_detect(struct rtl8366_smi *smi)
-{
-       const char *chip_name;
-       u32 chip_num;
-       u32 chip_ver;
-       u32 chip_mode;
-       int ret;
-
-       /* TODO: improve chip detection */
-       rtl8366_smi_write_reg(smi, RTL8367B_RTL_MAGIC_ID_REG,
-                             RTL8367B_RTL_MAGIC_ID_VAL);
-
-       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_NUMBER_REG, &chip_num);
-       if (ret) {
-               dev_err(smi->parent, "unable to read %s register\n",
-                       "chip number");
-               return ret;
-       }
-
-       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_VER_REG, &chip_ver);
-       if (ret) {
-               dev_err(smi->parent, "unable to read %s register\n",
-                       "chip version");
-               return ret;
-       }
-
-       ret = rtl8366_smi_read_reg(smi, RTL8367B_CHIP_MODE_REG, &chip_mode);
-       if (ret) {
-               dev_err(smi->parent, "unable to read %s register\n",
-                       "chip mode");
-               return ret;
-       }
-
-       switch (chip_ver) {
-       case 0x1000:
-               chip_name = "8367RB";
-               break;
-       case 0x1010:
-               chip_name = "8367R-VB";
-               break;
-       default:
-               dev_err(smi->parent,
-                       "unknown chip num:%04x ver:%04x, mode:%04x\n",
-                       chip_num, chip_ver, chip_mode);
-               return -ENODEV;
-       }
-
-       dev_info(smi->parent, "RTL%s chip found\n", chip_name);
-
-       return 0;
-}
-
-static struct rtl8366_smi_ops rtl8367b_smi_ops = {
-       .detect         = rtl8367b_detect,
-       .reset_chip     = rtl8367b_reset_chip,
-       .setup          = rtl8367b_setup,
-
-       .mii_read       = rtl8367b_mii_read,
-       .mii_write      = rtl8367b_mii_write,
-
-       .get_vlan_mc    = rtl8367b_get_vlan_mc,
-       .set_vlan_mc    = rtl8367b_set_vlan_mc,
-       .get_vlan_4k    = rtl8367b_get_vlan_4k,
-       .set_vlan_4k    = rtl8367b_set_vlan_4k,
-       .get_mc_index   = rtl8367b_get_mc_index,
-       .set_mc_index   = rtl8367b_set_mc_index,
-       .get_mib_counter = rtl8367b_get_mib_counter,
-       .is_vlan_valid  = rtl8367b_is_vlan_valid,
-       .enable_vlan    = rtl8367b_enable_vlan,
-       .enable_vlan4k  = rtl8367b_enable_vlan4k,
-       .enable_port    = rtl8367b_enable_port,
-};
-
-static int  rtl8367b_probe(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi;
-       int err;
-
-       smi = rtl8366_smi_probe(pdev);
-       if (!smi)
-               return -ENODEV;
-
-       smi->clk_delay = 1500;
-       smi->cmd_read = 0xb9;
-       smi->cmd_write = 0xb8;
-       smi->ops = &rtl8367b_smi_ops;
-       smi->cpu_port = RTL8367B_CPU_PORT_NUM;
-       smi->num_ports = RTL8367B_NUM_PORTS;
-       smi->num_vlan_mc = RTL8367B_NUM_VLANS;
-       smi->mib_counters = rtl8367b_mib_counters;
-       smi->num_mib_counters = ARRAY_SIZE(rtl8367b_mib_counters);
-
-       err = rtl8366_smi_init(smi);
-       if (err)
-               goto err_free_smi;
-
-       platform_set_drvdata(pdev, smi);
-
-       err = rtl8367b_switch_init(smi);
-       if (err)
-               goto err_clear_drvdata;
-
-       return 0;
-
- err_clear_drvdata:
-       platform_set_drvdata(pdev, NULL);
-       rtl8366_smi_cleanup(smi);
- err_free_smi:
-       kfree(smi);
-       return err;
-}
-
-static int rtl8367b_remove(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
-
-       if (smi) {
-               rtl8367b_switch_cleanup(smi);
-               platform_set_drvdata(pdev, NULL);
-               rtl8366_smi_cleanup(smi);
-               kfree(smi);
-       }
-
-       return 0;
-}
-
-static void rtl8367b_shutdown(struct platform_device *pdev)
-{
-       struct rtl8366_smi *smi = platform_get_drvdata(pdev);
-
-       if (smi)
-               rtl8367b_reset_chip(smi);
-}
-
-#ifdef CONFIG_OF
-static const struct of_device_id rtl8367b_match[] = {
-       { .compatible = "realtek,rtl8367b" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, rtl8367b_match);
-#endif
-
-static struct platform_driver rtl8367b_driver = {
-       .driver = {
-               .name           = RTL8367B_DRIVER_NAME,
-               .owner          = THIS_MODULE,
-#ifdef CONFIG_OF
-               .of_match_table = of_match_ptr(rtl8367b_match),
-#endif
-       },
-       .probe          = rtl8367b_probe,
-       .remove         = rtl8367b_remove,
-       .shutdown       = rtl8367b_shutdown,
-};
-
-module_platform_driver(rtl8367b_driver);
-
-MODULE_DESCRIPTION("Realtek RTL8367B ethernet switch driver");
-MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" RTL8367B_DRIVER_NAME);
-
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig.c b/target/linux/generic/files/drivers/net/phy/swconfig.c
deleted file mode 100644 (file)
index e8a6847..0000000
+++ /dev/null
@@ -1,1256 +0,0 @@
-/*
- * swconfig.c: Switch configuration API
- *
- * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include <linux/types.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/list.h>
-#include <linux/if.h>
-#include <linux/if_ether.h>
-#include <linux/capability.h>
-#include <linux/skbuff.h>
-#include <linux/switch.h>
-#include <linux/of.h>
-#include <linux/version.h>
-#include <uapi/linux/mii.h>
-
-#define SWCONFIG_DEVNAME       "switch%d"
-
-#include "swconfig_leds.c"
-
-MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
-MODULE_LICENSE("GPL");
-
-static int swdev_id;
-static struct list_head swdevs;
-static DEFINE_MUTEX(swdevs_lock);
-struct swconfig_callback;
-
-struct swconfig_callback {
-       struct sk_buff *msg;
-       struct genlmsghdr *hdr;
-       struct genl_info *info;
-       int cmd;
-
-       /* callback for filling in the message data */
-       int (*fill)(struct swconfig_callback *cb, void *arg);
-
-       /* callback for closing the message before sending it */
-       int (*close)(struct swconfig_callback *cb, void *arg);
-
-       struct nlattr *nest[4];
-       int args[4];
-};
-
-/* defaults */
-
-static int
-swconfig_get_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       int ret;
-       if (val->port_vlan >= dev->vlans)
-               return -EINVAL;
-
-       if (!dev->ops->get_vlan_ports)
-               return -EOPNOTSUPP;
-
-       ret = dev->ops->get_vlan_ports(dev, val);
-       return ret;
-}
-
-static int
-swconfig_set_vlan_ports(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       struct switch_port *ports = val->value.ports;
-       const struct switch_dev_ops *ops = dev->ops;
-       int i;
-
-       if (val->port_vlan >= dev->vlans)
-               return -EINVAL;
-
-       /* validate ports */
-       if (val->len > dev->ports)
-               return -EINVAL;
-
-       if (!ops->set_vlan_ports)
-               return -EOPNOTSUPP;
-
-       for (i = 0; i < val->len; i++) {
-               if (ports[i].id >= dev->ports)
-                       return -EINVAL;
-
-               if (ops->set_port_pvid &&
-                   !(ports[i].flags & (1 << SWITCH_PORT_FLAG_TAGGED)))
-                       ops->set_port_pvid(dev, ports[i].id, val->port_vlan);
-       }
-
-       return ops->set_vlan_ports(dev, val);
-}
-
-static int
-swconfig_set_pvid(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       if (val->port_vlan >= dev->ports)
-               return -EINVAL;
-
-       if (!dev->ops->set_port_pvid)
-               return -EOPNOTSUPP;
-
-       return dev->ops->set_port_pvid(dev, val->port_vlan, val->value.i);
-}
-
-static int
-swconfig_get_pvid(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       if (val->port_vlan >= dev->ports)
-               return -EINVAL;
-
-       if (!dev->ops->get_port_pvid)
-               return -EOPNOTSUPP;
-
-       return dev->ops->get_port_pvid(dev, val->port_vlan, &val->value.i);
-}
-
-static int
-swconfig_set_link(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       if (!dev->ops->set_port_link)
-               return -EOPNOTSUPP;
-
-       return dev->ops->set_port_link(dev, val->port_vlan, val->value.link);
-}
-
-static int
-swconfig_get_link(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       struct switch_port_link *link = val->value.link;
-
-       if (val->port_vlan >= dev->ports)
-               return -EINVAL;
-
-       if (!dev->ops->get_port_link)
-               return -EOPNOTSUPP;
-
-       memset(link, 0, sizeof(*link));
-       return dev->ops->get_port_link(dev, val->port_vlan, link);
-}
-
-static int
-swconfig_apply_config(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       /* don't complain if not supported by the switch driver */
-       if (!dev->ops->apply_config)
-               return 0;
-
-       return dev->ops->apply_config(dev);
-}
-
-static int
-swconfig_reset_switch(struct switch_dev *dev, const struct switch_attr *attr,
-                       struct switch_val *val)
-{
-       /* don't complain if not supported by the switch driver */
-       if (!dev->ops->reset_switch)
-               return 0;
-
-       return dev->ops->reset_switch(dev);
-}
-
-enum global_defaults {
-       GLOBAL_APPLY,
-       GLOBAL_RESET,
-};
-
-enum vlan_defaults {
-       VLAN_PORTS,
-};
-
-enum port_defaults {
-       PORT_PVID,
-       PORT_LINK,
-};
-
-static struct switch_attr default_global[] = {
-       [GLOBAL_APPLY] = {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "apply",
-               .description = "Activate changes in the hardware",
-               .set = swconfig_apply_config,
-       },
-       [GLOBAL_RESET] = {
-               .type = SWITCH_TYPE_NOVAL,
-               .name = "reset",
-               .description = "Reset the switch",
-               .set = swconfig_reset_switch,
-       }
-};
-
-static struct switch_attr default_port[] = {
-       [PORT_PVID] = {
-               .type = SWITCH_TYPE_INT,
-               .name = "pvid",
-               .description = "Primary VLAN ID",
-               .set = swconfig_set_pvid,
-               .get = swconfig_get_pvid,
-       },
-       [PORT_LINK] = {
-               .type = SWITCH_TYPE_LINK,
-               .name = "link",
-               .description = "Get port link information",
-               .set = swconfig_set_link,
-               .get = swconfig_get_link,
-       }
-};
-
-static struct switch_attr default_vlan[] = {
-       [VLAN_PORTS] = {
-               .type = SWITCH_TYPE_PORTS,
-               .name = "ports",
-               .description = "VLAN port mapping",
-               .set = swconfig_set_vlan_ports,
-               .get = swconfig_get_vlan_ports,
-       },
-};
-
-static const struct switch_attr *
-swconfig_find_attr_by_name(const struct switch_attrlist *alist,
-                               const char *name)
-{
-       int i;
-
-       for (i = 0; i < alist->n_attr; i++)
-               if (strcmp(name, alist->attr[i].name) == 0)
-                       return &alist->attr[i];
-
-       return NULL;
-}
-
-static void swconfig_defaults_init(struct switch_dev *dev)
-{
-       const struct switch_dev_ops *ops = dev->ops;
-
-       dev->def_global = 0;
-       dev->def_vlan = 0;
-       dev->def_port = 0;
-
-       if (ops->get_vlan_ports || ops->set_vlan_ports)
-               set_bit(VLAN_PORTS, &dev->def_vlan);
-
-       if (ops->get_port_pvid || ops->set_port_pvid)
-               set_bit(PORT_PVID, &dev->def_port);
-
-       if (ops->get_port_link &&
-           !swconfig_find_attr_by_name(&ops->attr_port, "link"))
-               set_bit(PORT_LINK, &dev->def_port);
-
-       /* always present, can be no-op */
-       set_bit(GLOBAL_APPLY, &dev->def_global);
-       set_bit(GLOBAL_RESET, &dev->def_global);
-}
-
-
-static struct genl_family switch_fam;
-
-static const struct nla_policy switch_policy[SWITCH_ATTR_MAX+1] = {
-       [SWITCH_ATTR_ID] = { .type = NLA_U32 },
-       [SWITCH_ATTR_OP_ID] = { .type = NLA_U32 },
-       [SWITCH_ATTR_OP_PORT] = { .type = NLA_U32 },
-       [SWITCH_ATTR_OP_VLAN] = { .type = NLA_U32 },
-       [SWITCH_ATTR_OP_VALUE_INT] = { .type = NLA_U32 },
-       [SWITCH_ATTR_OP_VALUE_STR] = { .type = NLA_NUL_STRING },
-       [SWITCH_ATTR_OP_VALUE_PORTS] = { .type = NLA_NESTED },
-       [SWITCH_ATTR_TYPE] = { .type = NLA_U32 },
-};
-
-static const struct nla_policy port_policy[SWITCH_PORT_ATTR_MAX+1] = {
-       [SWITCH_PORT_ID] = { .type = NLA_U32 },
-       [SWITCH_PORT_FLAG_TAGGED] = { .type = NLA_FLAG },
-};
-
-static struct nla_policy link_policy[SWITCH_LINK_ATTR_MAX] = {
-       [SWITCH_LINK_FLAG_DUPLEX] = { .type = NLA_FLAG },
-       [SWITCH_LINK_FLAG_ANEG] = { .type = NLA_FLAG },
-       [SWITCH_LINK_SPEED] = { .type = NLA_U32 },
-};
-
-static inline void
-swconfig_lock(void)
-{
-       mutex_lock(&swdevs_lock);
-}
-
-static inline void
-swconfig_unlock(void)
-{
-       mutex_unlock(&swdevs_lock);
-}
-
-static struct switch_dev *
-swconfig_get_dev(struct genl_info *info)
-{
-       struct switch_dev *dev = NULL;
-       struct switch_dev *p;
-       int id;
-
-       if (!info->attrs[SWITCH_ATTR_ID])
-               goto done;
-
-       id = nla_get_u32(info->attrs[SWITCH_ATTR_ID]);
-       swconfig_lock();
-       list_for_each_entry(p, &swdevs, dev_list) {
-               if (id != p->id)
-                       continue;
-
-               dev = p;
-               break;
-       }
-       if (dev)
-               mutex_lock(&dev->sw_mutex);
-       else
-               pr_debug("device %d not found\n", id);
-       swconfig_unlock();
-done:
-       return dev;
-}
-
-static inline void
-swconfig_put_dev(struct switch_dev *dev)
-{
-       mutex_unlock(&dev->sw_mutex);
-}
-
-static int
-swconfig_dump_attr(struct swconfig_callback *cb, void *arg)
-{
-       struct switch_attr *op = arg;
-       struct genl_info *info = cb->info;
-       struct sk_buff *msg = cb->msg;
-       int id = cb->args[0];
-       void *hdr;
-
-       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
-                       NLM_F_MULTI, SWITCH_CMD_NEW_ATTR);
-       if (IS_ERR(hdr))
-               return -1;
-
-       if (nla_put_u32(msg, SWITCH_ATTR_OP_ID, id))
-               goto nla_put_failure;
-       if (nla_put_u32(msg, SWITCH_ATTR_OP_TYPE, op->type))
-               goto nla_put_failure;
-       if (nla_put_string(msg, SWITCH_ATTR_OP_NAME, op->name))
-               goto nla_put_failure;
-       if (op->description)
-               if (nla_put_string(msg, SWITCH_ATTR_OP_DESCRIPTION,
-                       op->description))
-                       goto nla_put_failure;
-
-       genlmsg_end(msg, hdr);
-       return msg->len;
-nla_put_failure:
-       genlmsg_cancel(msg, hdr);
-       return -EMSGSIZE;
-}
-
-/* spread multipart messages across multiple message buffers */
-static int
-swconfig_send_multipart(struct swconfig_callback *cb, void *arg)
-{
-       struct genl_info *info = cb->info;
-       int restart = 0;
-       int err;
-
-       do {
-               if (!cb->msg) {
-                       cb->msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-                       if (cb->msg == NULL)
-                               goto error;
-               }
-
-               if (!(cb->fill(cb, arg) < 0))
-                       break;
-
-               /* fill failed, check if this was already the second attempt */
-               if (restart)
-                       goto error;
-
-               /* try again in a new message, send the current one */
-               restart = 1;
-               if (cb->close) {
-                       if (cb->close(cb, arg) < 0)
-                               goto error;
-               }
-               err = genlmsg_reply(cb->msg, info);
-               cb->msg = NULL;
-               if (err < 0)
-                       goto error;
-
-       } while (restart);
-
-       return 0;
-
-error:
-       if (cb->msg)
-               nlmsg_free(cb->msg);
-       return -1;
-}
-
-static int
-swconfig_list_attrs(struct sk_buff *skb, struct genl_info *info)
-{
-       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
-       const struct switch_attrlist *alist;
-       struct switch_dev *dev;
-       struct swconfig_callback cb;
-       int err = -EINVAL;
-       int i;
-
-       /* defaults */
-       struct switch_attr *def_list;
-       unsigned long *def_active;
-       int n_def;
-
-       dev = swconfig_get_dev(info);
-       if (!dev)
-               return -EINVAL;
-
-       switch (hdr->cmd) {
-       case SWITCH_CMD_LIST_GLOBAL:
-               alist = &dev->ops->attr_global;
-               def_list = default_global;
-               def_active = &dev->def_global;
-               n_def = ARRAY_SIZE(default_global);
-               break;
-       case SWITCH_CMD_LIST_VLAN:
-               alist = &dev->ops->attr_vlan;
-               def_list = default_vlan;
-               def_active = &dev->def_vlan;
-               n_def = ARRAY_SIZE(default_vlan);
-               break;
-       case SWITCH_CMD_LIST_PORT:
-               alist = &dev->ops->attr_port;
-               def_list = default_port;
-               def_active = &dev->def_port;
-               n_def = ARRAY_SIZE(default_port);
-               break;
-       default:
-               WARN_ON(1);
-               goto out;
-       }
-
-       memset(&cb, 0, sizeof(cb));
-       cb.info = info;
-       cb.fill = swconfig_dump_attr;
-       for (i = 0; i < alist->n_attr; i++) {
-               if (alist->attr[i].disabled)
-                       continue;
-               cb.args[0] = i;
-               err = swconfig_send_multipart(&cb, (void *) &alist->attr[i]);
-               if (err < 0)
-                       goto error;
-       }
-
-       /* defaults */
-       for (i = 0; i < n_def; i++) {
-               if (!test_bit(i, def_active))
-                       continue;
-               cb.args[0] = SWITCH_ATTR_DEFAULTS_OFFSET + i;
-               err = swconfig_send_multipart(&cb, (void *) &def_list[i]);
-               if (err < 0)
-                       goto error;
-       }
-       swconfig_put_dev(dev);
-
-       if (!cb.msg)
-               return 0;
-
-       return genlmsg_reply(cb.msg, info);
-
-error:
-       if (cb.msg)
-               nlmsg_free(cb.msg);
-out:
-       swconfig_put_dev(dev);
-       return err;
-}
-
-static const struct switch_attr *
-swconfig_lookup_attr(struct switch_dev *dev, struct genl_info *info,
-               struct switch_val *val)
-{
-       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
-       const struct switch_attrlist *alist;
-       const struct switch_attr *attr = NULL;
-       unsigned int attr_id;
-
-       /* defaults */
-       struct switch_attr *def_list;
-       unsigned long *def_active;
-       int n_def;
-
-       if (!info->attrs[SWITCH_ATTR_OP_ID])
-               goto done;
-
-       switch (hdr->cmd) {
-       case SWITCH_CMD_SET_GLOBAL:
-       case SWITCH_CMD_GET_GLOBAL:
-               alist = &dev->ops->attr_global;
-               def_list = default_global;
-               def_active = &dev->def_global;
-               n_def = ARRAY_SIZE(default_global);
-               break;
-       case SWITCH_CMD_SET_VLAN:
-       case SWITCH_CMD_GET_VLAN:
-               alist = &dev->ops->attr_vlan;
-               def_list = default_vlan;
-               def_active = &dev->def_vlan;
-               n_def = ARRAY_SIZE(default_vlan);
-               if (!info->attrs[SWITCH_ATTR_OP_VLAN])
-                       goto done;
-               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_VLAN]);
-               if (val->port_vlan >= dev->vlans)
-                       goto done;
-               break;
-       case SWITCH_CMD_SET_PORT:
-       case SWITCH_CMD_GET_PORT:
-               alist = &dev->ops->attr_port;
-               def_list = default_port;
-               def_active = &dev->def_port;
-               n_def = ARRAY_SIZE(default_port);
-               if (!info->attrs[SWITCH_ATTR_OP_PORT])
-                       goto done;
-               val->port_vlan = nla_get_u32(info->attrs[SWITCH_ATTR_OP_PORT]);
-               if (val->port_vlan >= dev->ports)
-                       goto done;
-               break;
-       default:
-               WARN_ON(1);
-               goto done;
-       }
-
-       if (!alist)
-               goto done;
-
-       attr_id = nla_get_u32(info->attrs[SWITCH_ATTR_OP_ID]);
-       if (attr_id >= SWITCH_ATTR_DEFAULTS_OFFSET) {
-               attr_id -= SWITCH_ATTR_DEFAULTS_OFFSET;
-               if (attr_id >= n_def)
-                       goto done;
-               if (!test_bit(attr_id, def_active))
-                       goto done;
-               attr = &def_list[attr_id];
-       } else {
-               if (attr_id >= alist->n_attr)
-                       goto done;
-               attr = &alist->attr[attr_id];
-       }
-
-       if (attr->disabled)
-               attr = NULL;
-
-done:
-       if (!attr)
-               pr_debug("attribute lookup failed\n");
-       val->attr = attr;
-       return attr;
-}
-
-static int
-swconfig_parse_ports(struct sk_buff *msg, struct nlattr *head,
-               struct switch_val *val, int max)
-{
-       struct nlattr *nla;
-       int rem;
-
-       val->len = 0;
-       nla_for_each_nested(nla, head, rem) {
-               struct nlattr *tb[SWITCH_PORT_ATTR_MAX+1];
-               struct switch_port *port;
-
-               if (val->len >= max)
-                       return -EINVAL;
-
-               port = &val->value.ports[val->len];
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
-               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
-                               port_policy))
-#else
-               if (nla_parse_nested(tb, SWITCH_PORT_ATTR_MAX, nla,
-                               port_policy, NULL))
-#endif
-                       return -EINVAL;
-
-               if (!tb[SWITCH_PORT_ID])
-                       return -EINVAL;
-
-               port->id = nla_get_u32(tb[SWITCH_PORT_ID]);
-               if (tb[SWITCH_PORT_FLAG_TAGGED])
-                       port->flags |= (1 << SWITCH_PORT_FLAG_TAGGED);
-               val->len++;
-       }
-
-       return 0;
-}
-
-static int
-swconfig_parse_link(struct sk_buff *msg, struct nlattr *nla,
-                   struct switch_port_link *link)
-{
-       struct nlattr *tb[SWITCH_LINK_ATTR_MAX + 1];
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,12,0)
-       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy))
-#else
-       if (nla_parse_nested(tb, SWITCH_LINK_ATTR_MAX, nla, link_policy, NULL))
-#endif
-               return -EINVAL;
-
-       link->duplex = !!tb[SWITCH_LINK_FLAG_DUPLEX];
-       link->aneg = !!tb[SWITCH_LINK_FLAG_ANEG];
-       link->speed = nla_get_u32(tb[SWITCH_LINK_SPEED]);
-
-       return 0;
-}
-
-static int
-swconfig_set_attr(struct sk_buff *skb, struct genl_info *info)
-{
-       const struct switch_attr *attr;
-       struct switch_dev *dev;
-       struct switch_val val;
-       int err = -EINVAL;
-
-       if (!capable(CAP_NET_ADMIN))
-               return -EPERM;
-
-       dev = swconfig_get_dev(info);
-       if (!dev)
-               return -EINVAL;
-
-       memset(&val, 0, sizeof(val));
-       attr = swconfig_lookup_attr(dev, info, &val);
-       if (!attr || !attr->set)
-               goto error;
-
-       val.attr = attr;
-       switch (attr->type) {
-       case SWITCH_TYPE_NOVAL:
-               break;
-       case SWITCH_TYPE_INT:
-               if (!info->attrs[SWITCH_ATTR_OP_VALUE_INT])
-                       goto error;
-               val.value.i =
-                       nla_get_u32(info->attrs[SWITCH_ATTR_OP_VALUE_INT]);
-               break;
-       case SWITCH_TYPE_STRING:
-               if (!info->attrs[SWITCH_ATTR_OP_VALUE_STR])
-                       goto error;
-               val.value.s =
-                       nla_data(info->attrs[SWITCH_ATTR_OP_VALUE_STR]);
-               break;
-       case SWITCH_TYPE_PORTS:
-               val.value.ports = dev->portbuf;
-               memset(dev->portbuf, 0,
-                       sizeof(struct switch_port) * dev->ports);
-
-               /* TODO: implement multipart? */
-               if (info->attrs[SWITCH_ATTR_OP_VALUE_PORTS]) {
-                       err = swconfig_parse_ports(skb,
-                               info->attrs[SWITCH_ATTR_OP_VALUE_PORTS],
-                               &val, dev->ports);
-                       if (err < 0)
-                               goto error;
-               } else {
-                       val.len = 0;
-                       err = 0;
-               }
-               break;
-       case SWITCH_TYPE_LINK:
-               val.value.link = &dev->linkbuf;
-               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
-
-               if (info->attrs[SWITCH_ATTR_OP_VALUE_LINK]) {
-                       err = swconfig_parse_link(skb,
-                                                 info->attrs[SWITCH_ATTR_OP_VALUE_LINK],
-                                                 val.value.link);
-                       if (err < 0)
-                               goto error;
-               } else {
-                       val.len = 0;
-                       err = 0;
-               }
-               break;
-       default:
-               goto error;
-       }
-
-       err = attr->set(dev, attr, &val);
-error:
-       swconfig_put_dev(dev);
-       return err;
-}
-
-static int
-swconfig_close_portlist(struct swconfig_callback *cb, void *arg)
-{
-       if (cb->nest[0])
-               nla_nest_end(cb->msg, cb->nest[0]);
-       return 0;
-}
-
-static int
-swconfig_send_port(struct swconfig_callback *cb, void *arg)
-{
-       const struct switch_port *port = arg;
-       struct nlattr *p = NULL;
-
-       if (!cb->nest[0]) {
-               cb->nest[0] = nla_nest_start(cb->msg, cb->cmd);
-               if (!cb->nest[0])
-                       return -1;
-       }
-
-       p = nla_nest_start(cb->msg, SWITCH_ATTR_PORT);
-       if (!p)
-               goto error;
-
-       if (nla_put_u32(cb->msg, SWITCH_PORT_ID, port->id))
-               goto nla_put_failure;
-       if (port->flags & (1 << SWITCH_PORT_FLAG_TAGGED)) {
-               if (nla_put_flag(cb->msg, SWITCH_PORT_FLAG_TAGGED))
-                       goto nla_put_failure;
-       }
-
-       nla_nest_end(cb->msg, p);
-       return 0;
-
-nla_put_failure:
-               nla_nest_cancel(cb->msg, p);
-error:
-       nla_nest_cancel(cb->msg, cb->nest[0]);
-       return -1;
-}
-
-static int
-swconfig_send_ports(struct sk_buff **msg, struct genl_info *info, int attr,
-               const struct switch_val *val)
-{
-       struct swconfig_callback cb;
-       int err = 0;
-       int i;
-
-       if (!val->value.ports)
-               return -EINVAL;
-
-       memset(&cb, 0, sizeof(cb));
-       cb.cmd = attr;
-       cb.msg = *msg;
-       cb.info = info;
-       cb.fill = swconfig_send_port;
-       cb.close = swconfig_close_portlist;
-
-       cb.nest[0] = nla_nest_start(cb.msg, cb.cmd);
-       for (i = 0; i < val->len; i++) {
-               err = swconfig_send_multipart(&cb, &val->value.ports[i]);
-               if (err)
-                       goto done;
-       }
-       err = val->len;
-       swconfig_close_portlist(&cb, NULL);
-       *msg = cb.msg;
-
-done:
-       return err;
-}
-
-static int
-swconfig_send_link(struct sk_buff *msg, struct genl_info *info, int attr,
-                  const struct switch_port_link *link)
-{
-       struct nlattr *p = NULL;
-       int err = 0;
-
-       p = nla_nest_start(msg, attr);
-       if (link->link) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_LINK))
-                       goto nla_put_failure;
-       }
-       if (link->duplex) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_DUPLEX))
-                       goto nla_put_failure;
-       }
-       if (link->aneg) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_ANEG))
-                       goto nla_put_failure;
-       }
-       if (link->tx_flow) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_TX_FLOW))
-                       goto nla_put_failure;
-       }
-       if (link->rx_flow) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_RX_FLOW))
-                       goto nla_put_failure;
-       }
-       if (nla_put_u32(msg, SWITCH_LINK_SPEED, link->speed))
-               goto nla_put_failure;
-       if (link->eee & ADVERTISED_100baseT_Full) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_100BASET))
-                       goto nla_put_failure;
-       }
-       if (link->eee & ADVERTISED_1000baseT_Full) {
-               if (nla_put_flag(msg, SWITCH_LINK_FLAG_EEE_1000BASET))
-                       goto nla_put_failure;
-       }
-       nla_nest_end(msg, p);
-
-       return err;
-
-nla_put_failure:
-       nla_nest_cancel(msg, p);
-       return -1;
-}
-
-static int
-swconfig_get_attr(struct sk_buff *skb, struct genl_info *info)
-{
-       struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
-       const struct switch_attr *attr;
-       struct switch_dev *dev;
-       struct sk_buff *msg = NULL;
-       struct switch_val val;
-       int err = -EINVAL;
-       int cmd = hdr->cmd;
-
-       dev = swconfig_get_dev(info);
-       if (!dev)
-               return -EINVAL;
-
-       memset(&val, 0, sizeof(val));
-       attr = swconfig_lookup_attr(dev, info, &val);
-       if (!attr || !attr->get)
-               goto error;
-
-       if (attr->type == SWITCH_TYPE_PORTS) {
-               val.value.ports = dev->portbuf;
-               memset(dev->portbuf, 0,
-                       sizeof(struct switch_port) * dev->ports);
-       } else if (attr->type == SWITCH_TYPE_LINK) {
-               val.value.link = &dev->linkbuf;
-               memset(&dev->linkbuf, 0, sizeof(struct switch_port_link));
-       }
-
-       err = attr->get(dev, attr, &val);
-       if (err)
-               goto error;
-
-       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
-       if (!msg)
-               goto error;
-
-       hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &switch_fam,
-                       0, cmd);
-       if (IS_ERR(hdr))
-               goto nla_put_failure;
-
-       switch (attr->type) {
-       case SWITCH_TYPE_INT:
-               if (nla_put_u32(msg, SWITCH_ATTR_OP_VALUE_INT, val.value.i))
-                       goto nla_put_failure;
-               break;
-       case SWITCH_TYPE_STRING:
-               if (nla_put_string(msg, SWITCH_ATTR_OP_VALUE_STR, val.value.s))
-                       goto nla_put_failure;
-               break;
-       case SWITCH_TYPE_PORTS:
-               err = swconfig_send_ports(&msg, info,
-                               SWITCH_ATTR_OP_VALUE_PORTS, &val);
-               if (err < 0)
-                       goto nla_put_failure;
-               break;
-       case SWITCH_TYPE_LINK:
-               err = swconfig_send_link(msg, info,
-                                        SWITCH_ATTR_OP_VALUE_LINK, val.value.link);
-               if (err < 0)
-                       goto nla_put_failure;
-               break;
-       default:
-               pr_debug("invalid type in attribute\n");
-               err = -EINVAL;
-               goto nla_put_failure;
-       }
-       genlmsg_end(msg, hdr);
-       err = msg->len;
-       if (err < 0)
-               goto nla_put_failure;
-
-       swconfig_put_dev(dev);
-       return genlmsg_reply(msg, info);
-
-nla_put_failure:
-       if (msg)
-               nlmsg_free(msg);
-error:
-       swconfig_put_dev(dev);
-       if (!err)
-               err = -ENOMEM;
-       return err;
-}
-
-static int
-swconfig_send_switch(struct sk_buff *msg, u32 pid, u32 seq, int flags,
-               const struct switch_dev *dev)
-{
-       struct nlattr *p = NULL, *m = NULL;
-       void *hdr;
-       int i;
-
-       hdr = genlmsg_put(msg, pid, seq, &switch_fam, flags,
-                       SWITCH_CMD_NEW_ATTR);
-       if (IS_ERR(hdr))
-               return -1;
-
-       if (nla_put_u32(msg, SWITCH_ATTR_ID, dev->id))
-               goto nla_put_failure;
-       if (nla_put_string(msg, SWITCH_ATTR_DEV_NAME, dev->devname))
-               goto nla_put_failure;
-       if (nla_put_string(msg, SWITCH_ATTR_ALIAS, dev->alias))
-               goto nla_put_failure;
-       if (nla_put_string(msg, SWITCH_ATTR_NAME, dev->name))
-               goto nla_put_failure;
-       if (nla_put_u32(msg, SWITCH_ATTR_VLANS, dev->vlans))
-               goto nla_put_failure;
-       if (nla_put_u32(msg, SWITCH_ATTR_PORTS, dev->ports))
-               goto nla_put_failure;
-       if (nla_put_u32(msg, SWITCH_ATTR_CPU_PORT, dev->cpu_port))
-               goto nla_put_failure;
-
-       m = nla_nest_start(msg, SWITCH_ATTR_PORTMAP);
-       if (!m)
-               goto nla_put_failure;
-       for (i = 0; i < dev->ports; i++) {
-               p = nla_nest_start(msg, SWITCH_ATTR_PORTS);
-               if (!p)
-                       continue;
-               if (dev->portmap[i].s) {
-                       if (nla_put_string(msg, SWITCH_PORTMAP_SEGMENT,
-                                               dev->portmap[i].s))
-                               goto nla_put_failure;
-                       if (nla_put_u32(msg, SWITCH_PORTMAP_VIRT,
-                                               dev->portmap[i].virt))
-                               goto nla_put_failure;
-               }
-               nla_nest_end(msg, p);
-       }
-       nla_nest_end(msg, m);
-       genlmsg_end(msg, hdr);
-       return msg->len;
-nla_put_failure:
-       genlmsg_cancel(msg, hdr);
-       return -EMSGSIZE;
-}
-
-static int swconfig_dump_switches(struct sk_buff *skb,
-               struct netlink_callback *cb)
-{
-       struct switch_dev *dev;
-       int start = cb->args[0];
-       int idx = 0;
-
-       swconfig_lock();
-       list_for_each_entry(dev, &swdevs, dev_list) {
-               if (++idx <= start)
-                       continue;
-               if (swconfig_send_switch(skb, NETLINK_CB(cb->skb).portid,
-                               cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                               dev) < 0)
-                       break;
-       }
-       swconfig_unlock();
-       cb->args[0] = idx;
-
-       return skb->len;
-}
-
-static int
-swconfig_done(struct netlink_callback *cb)
-{
-       return 0;
-}
-
-static struct genl_ops swconfig_ops[] = {
-       {
-               .cmd = SWITCH_CMD_LIST_GLOBAL,
-               .doit = swconfig_list_attrs,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_LIST_VLAN,
-               .doit = swconfig_list_attrs,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_LIST_PORT,
-               .doit = swconfig_list_attrs,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_GET_GLOBAL,
-               .doit = swconfig_get_attr,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_GET_VLAN,
-               .doit = swconfig_get_attr,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_GET_PORT,
-               .doit = swconfig_get_attr,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_SET_GLOBAL,
-               .flags = GENL_ADMIN_PERM,
-               .doit = swconfig_set_attr,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_SET_VLAN,
-               .flags = GENL_ADMIN_PERM,
-               .doit = swconfig_set_attr,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_SET_PORT,
-               .flags = GENL_ADMIN_PERM,
-               .doit = swconfig_set_attr,
-               .policy = switch_policy,
-       },
-       {
-               .cmd = SWITCH_CMD_GET_SWITCH,
-               .dumpit = swconfig_dump_switches,
-               .policy = switch_policy,
-               .done = swconfig_done,
-       }
-};
-
-static struct genl_family switch_fam = {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
-       .id = GENL_ID_GENERATE,
-#endif
-       .name = "switch",
-       .hdrsize = 0,
-       .version = 1,
-       .maxattr = SWITCH_ATTR_MAX,
-       .module = THIS_MODULE,
-       .ops = swconfig_ops,
-       .n_ops = ARRAY_SIZE(swconfig_ops),
-};
-
-#ifdef CONFIG_OF
-void
-of_switch_load_portmap(struct switch_dev *dev)
-{
-       struct device_node *port;
-
-       if (!dev->of_node)
-               return;
-
-       for_each_child_of_node(dev->of_node, port) {
-               const __be32 *prop;
-               const char *segment;
-               int size, phys;
-
-               if (!of_device_is_compatible(port, "swconfig,port"))
-                       continue;
-
-               if (of_property_read_string(port, "swconfig,segment", &segment))
-                       continue;
-
-               prop = of_get_property(port, "swconfig,portmap", &size);
-               if (!prop)
-                       continue;
-
-               if (size != (2 * sizeof(*prop))) {
-                       pr_err("%s: failed to parse port mapping\n",
-                                       port->name);
-                       continue;
-               }
-
-               phys = be32_to_cpup(prop++);
-               if ((phys < 0) | (phys >= dev->ports)) {
-                       pr_err("%s: physical port index out of range\n",
-                                       port->name);
-                       continue;
-               }
-
-               dev->portmap[phys].s = kstrdup(segment, GFP_KERNEL);
-               dev->portmap[phys].virt = be32_to_cpup(prop);
-               pr_debug("Found port: %s, physical: %d, virtual: %d\n",
-                       segment, phys, dev->portmap[phys].virt);
-       }
-}
-#endif
-
-int
-register_switch(struct switch_dev *dev, struct net_device *netdev)
-{
-       struct switch_dev *sdev;
-       const int max_switches = 8 * sizeof(unsigned long);
-       unsigned long in_use = 0;
-       int err;
-       int i;
-
-       INIT_LIST_HEAD(&dev->dev_list);
-       if (netdev) {
-               dev->netdev = netdev;
-               if (!dev->alias)
-                       dev->alias = netdev->name;
-       }
-       BUG_ON(!dev->alias);
-
-       /* Make sure swdev_id doesn't overflow */
-       if (swdev_id == INT_MAX) {
-               return -ENOMEM;
-       }
-
-       if (dev->ports > 0) {
-               dev->portbuf = kzalloc(sizeof(struct switch_port) *
-                               dev->ports, GFP_KERNEL);
-               if (!dev->portbuf)
-                       return -ENOMEM;
-               dev->portmap = kzalloc(sizeof(struct switch_portmap) *
-                               dev->ports, GFP_KERNEL);
-               if (!dev->portmap) {
-                       kfree(dev->portbuf);
-                       return -ENOMEM;
-               }
-       }
-       swconfig_defaults_init(dev);
-       mutex_init(&dev->sw_mutex);
-       swconfig_lock();
-       dev->id = ++swdev_id;
-
-       list_for_each_entry(sdev, &swdevs, dev_list) {
-               if (!sscanf(sdev->devname, SWCONFIG_DEVNAME, &i))
-                       continue;
-               if (i < 0 || i > max_switches)
-                       continue;
-
-               set_bit(i, &in_use);
-       }
-       i = find_first_zero_bit(&in_use, max_switches);
-
-       if (i == max_switches) {
-               swconfig_unlock();
-               return -ENFILE;
-       }
-
-#ifdef CONFIG_OF
-       if (dev->ports)
-               of_switch_load_portmap(dev);
-#endif
-
-       /* fill device name */
-       snprintf(dev->devname, IFNAMSIZ, SWCONFIG_DEVNAME, i);
-
-       list_add_tail(&dev->dev_list, &swdevs);
-       swconfig_unlock();
-
-       err = swconfig_create_led_trigger(dev);
-       if (err)
-               return err;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(register_switch);
-
-void
-unregister_switch(struct switch_dev *dev)
-{
-       swconfig_destroy_led_trigger(dev);
-       kfree(dev->portbuf);
-       mutex_lock(&dev->sw_mutex);
-       swconfig_lock();
-       list_del(&dev->dev_list);
-       swconfig_unlock();
-       mutex_unlock(&dev->sw_mutex);
-}
-EXPORT_SYMBOL_GPL(unregister_switch);
-
-int
-switch_generic_set_link(struct switch_dev *dev, int port,
-                       struct switch_port_link *link)
-{
-       if (WARN_ON(!dev->ops->phy_write16))
-               return -ENOTSUPP;
-
-       /* Generic implementation */
-       if (link->aneg) {
-               dev->ops->phy_write16(dev, port, MII_BMCR, 0x0000);
-               dev->ops->phy_write16(dev, port, MII_BMCR, BMCR_ANENABLE | BMCR_ANRESTART);
-       } else {
-               u16 bmcr = 0;
-
-               if (link->duplex)
-                       bmcr |= BMCR_FULLDPLX;
-
-               switch (link->speed) {
-               case SWITCH_PORT_SPEED_10:
-                       break;
-               case SWITCH_PORT_SPEED_100:
-                       bmcr |= BMCR_SPEED100;
-                       break;
-               case SWITCH_PORT_SPEED_1000:
-                       bmcr |= BMCR_SPEED1000;
-                       break;
-               default:
-                       return -ENOTSUPP;
-               }
-
-               dev->ops->phy_write16(dev, port, MII_BMCR, bmcr);
-       }
-
-       return 0;
-}
-
-static int __init
-swconfig_init(void)
-{
-       INIT_LIST_HEAD(&swdevs);
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4,10,0)
-       return genl_register_family_with_ops(&switch_fam, swconfig_ops);
-#else
-       return genl_register_family(&switch_fam);
-#endif
-}
-
-static void __exit
-swconfig_exit(void)
-{
-       genl_unregister_family(&switch_fam);
-}
-
-module_init(swconfig_init);
-module_exit(swconfig_exit);
diff --git a/target/linux/generic/files/drivers/net/phy/swconfig_leds.c b/target/linux/generic/files/drivers/net/phy/swconfig_leds.c
deleted file mode 100644 (file)
index 91824b7..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- * swconfig_led.c: LED trigger support for the switch configuration API
- *
- * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- */
-
-#ifdef CONFIG_SWCONFIG_LEDS
-
-#include <linux/leds.h>
-#include <linux/ctype.h>
-#include <linux/device.h>
-#include <linux/workqueue.h>
-
-#define SWCONFIG_LED_TIMER_INTERVAL    (HZ / 10)
-#define SWCONFIG_LED_NUM_PORTS         32
-
-#define SWCONFIG_LED_PORT_SPEED_NA     0x01    /* unknown speed */
-#define SWCONFIG_LED_PORT_SPEED_10     0x02    /* 10 Mbps */
-#define SWCONFIG_LED_PORT_SPEED_100    0x04    /* 100 Mbps */
-#define SWCONFIG_LED_PORT_SPEED_1000   0x08    /* 1000 Mbps */
-#define SWCONFIG_LED_PORT_SPEED_ALL    (SWCONFIG_LED_PORT_SPEED_NA | \
-                                        SWCONFIG_LED_PORT_SPEED_10 | \
-                                        SWCONFIG_LED_PORT_SPEED_100 | \
-                                        SWCONFIG_LED_PORT_SPEED_1000)
-
-#define SWCONFIG_LED_MODE_LINK         0x01
-#define SWCONFIG_LED_MODE_TX           0x02
-#define SWCONFIG_LED_MODE_RX           0x04
-#define SWCONFIG_LED_MODE_TXRX         (SWCONFIG_LED_MODE_TX   | \
-                                        SWCONFIG_LED_MODE_RX)
-#define SWCONFIG_LED_MODE_ALL          (SWCONFIG_LED_MODE_LINK | \
-                                        SWCONFIG_LED_MODE_TX   | \
-                                        SWCONFIG_LED_MODE_RX)
-
-struct switch_led_trigger {
-       struct led_trigger trig;
-       struct switch_dev *swdev;
-
-       struct delayed_work sw_led_work;
-       u32 port_mask;
-       u32 port_link;
-       unsigned long long port_tx_traffic[SWCONFIG_LED_NUM_PORTS];
-       unsigned long long port_rx_traffic[SWCONFIG_LED_NUM_PORTS];
-       u8 link_speed[SWCONFIG_LED_NUM_PORTS];
-};
-
-struct swconfig_trig_data {
-       struct led_classdev *led_cdev;
-       struct switch_dev *swdev;
-
-       rwlock_t lock;
-       u32 port_mask;
-
-       bool prev_link;
-       unsigned long prev_traffic;
-       enum led_brightness prev_brightness;
-       u8 mode;
-       u8 speed_mask;
-};
-
-static void
-swconfig_trig_set_brightness(struct swconfig_trig_data *trig_data,
-                            enum led_brightness brightness)
-{
-       led_set_brightness(trig_data->led_cdev, brightness);
-       trig_data->prev_brightness = brightness;
-}
-
-static void
-swconfig_trig_update_port_mask(struct led_trigger *trigger)
-{
-       struct list_head *entry;
-       struct switch_led_trigger *sw_trig;
-       u32 port_mask;
-
-       if (!trigger)
-               return;
-
-       sw_trig = (void *) trigger;
-
-       port_mask = 0;
-       read_lock(&trigger->leddev_list_lock);
-       list_for_each(entry, &trigger->led_cdevs) {
-               struct led_classdev *led_cdev;
-               struct swconfig_trig_data *trig_data;
-
-               led_cdev = list_entry(entry, struct led_classdev, trig_list);
-               trig_data = led_cdev->trigger_data;
-               if (trig_data) {
-                       read_lock(&trig_data->lock);
-                       port_mask |= trig_data->port_mask;
-                       read_unlock(&trig_data->lock);
-               }
-       }
-       read_unlock(&trigger->leddev_list_lock);
-
-       sw_trig->port_mask = port_mask;
-
-       if (port_mask)
-               schedule_delayed_work(&sw_trig->sw_led_work,
-                                     SWCONFIG_LED_TIMER_INTERVAL);
-       else
-               cancel_delayed_work_sync(&sw_trig->sw_led_work);
-}
-
-static ssize_t
-swconfig_trig_port_mask_store(struct device *dev, struct device_attribute *attr,
-                             const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
-       unsigned long port_mask;
-       int ret;
-       bool changed;
-
-       ret = kstrtoul(buf, 0, &port_mask);
-       if (ret)
-               return ret;
-
-       write_lock(&trig_data->lock);
-       changed = (trig_data->port_mask != port_mask);
-       trig_data->port_mask = port_mask;
-       write_unlock(&trig_data->lock);
-
-       if (changed) {
-               if (port_mask == 0)
-                       swconfig_trig_set_brightness(trig_data, LED_OFF);
-
-               swconfig_trig_update_port_mask(led_cdev->trigger);
-       }
-
-       return size;
-}
-
-static ssize_t
-swconfig_trig_port_mask_show(struct device *dev, struct device_attribute *attr,
-                            char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
-       u32 port_mask;
-
-       read_lock(&trig_data->lock);
-       port_mask = trig_data->port_mask;
-       read_unlock(&trig_data->lock);
-
-       sprintf(buf, "%#x\n", port_mask);
-
-       return strlen(buf) + 1;
-}
-
-static DEVICE_ATTR(port_mask, 0644, swconfig_trig_port_mask_show,
-                  swconfig_trig_port_mask_store);
-
-/* speed_mask file handler - display value */
-static ssize_t swconfig_trig_speed_mask_show(struct device *dev,
-                                            struct device_attribute *attr,
-                                            char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
-       u8 speed_mask;
-
-       read_lock(&trig_data->lock);
-       speed_mask = trig_data->speed_mask;
-       read_unlock(&trig_data->lock);
-
-       sprintf(buf, "%#x\n", speed_mask);
-
-       return strlen(buf) + 1;
-}
-
-/* speed_mask file handler - store value */
-static ssize_t swconfig_trig_speed_mask_store(struct device *dev,
-                                             struct device_attribute *attr,
-                                             const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
-       u8 speed_mask;
-       int ret;
-
-       ret = kstrtou8(buf, 0, &speed_mask);
-       if (ret)
-               return ret;
-
-       write_lock(&trig_data->lock);
-       trig_data->speed_mask = speed_mask & SWCONFIG_LED_PORT_SPEED_ALL;
-       write_unlock(&trig_data->lock);
-
-       return size;
-}
-
-/* speed_mask special file */
-static DEVICE_ATTR(speed_mask, 0644, swconfig_trig_speed_mask_show,
-                  swconfig_trig_speed_mask_store);
-
-static ssize_t swconfig_trig_mode_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
-       u8 mode;
-
-       read_lock(&trig_data->lock);
-       mode = trig_data->mode;
-       read_unlock(&trig_data->lock);
-
-       if (mode == 0) {
-               strcpy(buf, "none\n");
-       } else {
-               if (mode & SWCONFIG_LED_MODE_LINK)
-                       strcat(buf, "link ");
-               if (mode & SWCONFIG_LED_MODE_TX)
-                       strcat(buf, "tx ");
-               if (mode & SWCONFIG_LED_MODE_RX)
-                       strcat(buf, "rx ");
-               strcat(buf, "\n");
-       }
-
-       return strlen(buf)+1;
-}
-
-static ssize_t swconfig_trig_mode_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t size)
-{
-       struct led_classdev *led_cdev = dev_get_drvdata(dev);
-       struct swconfig_trig_data *trig_data = led_cdev->trigger_data;
-       char copybuf[128];
-       int new_mode = -1;
-       char *p, *token;
-
-       /* take a copy since we don't want to trash the inbound buffer when using strsep */
-       strncpy(copybuf, buf, sizeof(copybuf));
-       copybuf[sizeof(copybuf) - 1] = 0;
-       p = copybuf;
-
-       while ((token = strsep(&p, " \t\n")) != NULL) {
-               if (!*token)
-                       continue;
-
-               if (new_mode < 0)
-                       new_mode = 0;
-
-               if (!strcmp(token, "none"))
-                       new_mode = 0;
-               else if (!strcmp(token, "tx"))
-                       new_mode |= SWCONFIG_LED_MODE_TX;
-               else if (!strcmp(token, "rx"))
-                       new_mode |= SWCONFIG_LED_MODE_RX;
-               else if (!strcmp(token, "link"))
-                       new_mode |= SWCONFIG_LED_MODE_LINK;
-               else
-                       return -EINVAL;
-       }
-
-       if (new_mode < 0)
-               return -EINVAL;
-
-       write_lock(&trig_data->lock);
-       trig_data->mode = (u8)new_mode;
-       write_unlock(&trig_data->lock);
-
-       return size;
-}
-
-/* mode special file */
-static DEVICE_ATTR(mode, 0644, swconfig_trig_mode_show,
-                  swconfig_trig_mode_store);
-
-static void
-swconfig_trig_activate(struct led_classdev *led_cdev)
-{
-       struct switch_led_trigger *sw_trig;
-       struct swconfig_trig_data *trig_data;
-       int err;
-
-       if (led_cdev->trigger->activate != swconfig_trig_activate)
-               return;
-
-       trig_data = kzalloc(sizeof(struct swconfig_trig_data), GFP_KERNEL);
-       if (!trig_data)
-               return;
-
-       sw_trig = (void *) led_cdev->trigger;
-
-       rwlock_init(&trig_data->lock);
-       trig_data->led_cdev = led_cdev;
-       trig_data->swdev = sw_trig->swdev;
-       trig_data->speed_mask = SWCONFIG_LED_PORT_SPEED_ALL;
-       trig_data->mode = SWCONFIG_LED_MODE_ALL;
-       led_cdev->trigger_data = trig_data;
-
-       err = device_create_file(led_cdev->dev, &dev_attr_port_mask);
-       if (err)
-               goto err_free;
-
-       err = device_create_file(led_cdev->dev, &dev_attr_speed_mask);
-       if (err)
-               goto err_dev_free;
-
-       err = device_create_file(led_cdev->dev, &dev_attr_mode);
-       if (err)
-               goto err_mode_free;
-
-       return;
-
-err_mode_free:
-       device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
-
-err_dev_free:
-       device_remove_file(led_cdev->dev, &dev_attr_port_mask);
-
-err_free:
-       led_cdev->trigger_data = NULL;
-       kfree(trig_data);
-}
-
-static void
-swconfig_trig_deactivate(struct led_classdev *led_cdev)
-{
-       struct swconfig_trig_data *trig_data;
-
-       swconfig_trig_update_port_mask(led_cdev->trigger);
-
-       trig_data = (void *) led_cdev->trigger_data;
-       if (trig_data) {
-               device_remove_file(led_cdev->dev, &dev_attr_port_mask);
-               device_remove_file(led_cdev->dev, &dev_attr_speed_mask);
-               device_remove_file(led_cdev->dev, &dev_attr_mode);
-               kfree(trig_data);
-       }
-}
-
-/*
- * link off -> led off (can't be any other reason to turn it on)
- * link on:
- *     mode link: led on by default only if speed matches, else off
- *     mode txrx: blink only if speed matches, else off
- */
-static void
-swconfig_trig_led_event(struct switch_led_trigger *sw_trig,
-                       struct led_classdev *led_cdev)
-{
-       struct swconfig_trig_data *trig_data;
-       u32 port_mask;
-       bool link;
-       u8 speed_mask, mode;
-       enum led_brightness led_base, led_blink;
-
-       trig_data = led_cdev->trigger_data;
-       if (!trig_data)
-               return;
-
-       read_lock(&trig_data->lock);
-       port_mask = trig_data->port_mask;
-       speed_mask = trig_data->speed_mask;
-       mode = trig_data->mode;
-       read_unlock(&trig_data->lock);
-
-       link = !!(sw_trig->port_link & port_mask);
-       if (!link) {
-               if (trig_data->prev_brightness != LED_OFF)
-                       swconfig_trig_set_brightness(trig_data, LED_OFF); /* and stop */
-       }
-       else {
-               unsigned long traffic;
-               int speedok;    /* link speed flag */
-               int i;
-
-               led_base = LED_FULL;
-               led_blink = LED_OFF;
-               traffic = 0;
-               speedok = 0;
-               for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
-                       if (port_mask & (1 << i)) {
-                               if (sw_trig->link_speed[i] & speed_mask) {
-                                       traffic += ((mode & SWCONFIG_LED_MODE_TX) ?
-                                                   sw_trig->port_tx_traffic[i] : 0) +
-                                               ((mode & SWCONFIG_LED_MODE_RX) ?
-                                                sw_trig->port_rx_traffic[i] : 0);
-                                       speedok = 1;
-                               }
-                       }
-               }
-
-               if (speedok) {
-                       /* At least one port speed matches speed_mask */
-                       if (!(mode & SWCONFIG_LED_MODE_LINK)) {
-                               led_base = LED_OFF;
-                               led_blink = LED_FULL;
-                       }
-
-                       if (trig_data->prev_brightness != led_base)
-                               swconfig_trig_set_brightness(trig_data,
-                                                            led_base);
-                       else if (traffic != trig_data->prev_traffic)
-                               swconfig_trig_set_brightness(trig_data,
-                                                            led_blink);
-               } else if (trig_data->prev_brightness != LED_OFF)
-                       swconfig_trig_set_brightness(trig_data, LED_OFF);
-
-               trig_data->prev_traffic = traffic;
-       }
-
-       trig_data->prev_link = link;
-}
-
-static void
-swconfig_trig_update_leds(struct switch_led_trigger *sw_trig)
-{
-       struct list_head *entry;
-       struct led_trigger *trigger;
-
-       trigger = &sw_trig->trig;
-       read_lock(&trigger->leddev_list_lock);
-       list_for_each(entry, &trigger->led_cdevs) {
-               struct led_classdev *led_cdev;
-
-               led_cdev = list_entry(entry, struct led_classdev, trig_list);
-               swconfig_trig_led_event(sw_trig, led_cdev);
-       }
-       read_unlock(&trigger->leddev_list_lock);
-}
-
-static void
-swconfig_led_work_func(struct work_struct *work)
-{
-       struct switch_led_trigger *sw_trig;
-       struct switch_dev *swdev;
-       u32 port_mask;
-       u32 link;
-       int i;
-
-       sw_trig = container_of(work, struct switch_led_trigger,
-                              sw_led_work.work);
-
-       port_mask = sw_trig->port_mask;
-       swdev = sw_trig->swdev;
-
-       link = 0;
-       for (i = 0; i < SWCONFIG_LED_NUM_PORTS; i++) {
-               u32 port_bit;
-
-               sw_trig->link_speed[i] = 0;
-
-               port_bit = BIT(i);
-               if ((port_mask & port_bit) == 0)
-                       continue;
-
-               if (swdev->ops->get_port_link) {
-                       struct switch_port_link port_link;
-
-                       memset(&port_link, '\0', sizeof(port_link));
-                       swdev->ops->get_port_link(swdev, i, &port_link);
-
-                       if (port_link.link) {
-                               link |= port_bit;
-                               switch (port_link.speed) {
-                               case SWITCH_PORT_SPEED_UNKNOWN:
-                                       sw_trig->link_speed[i] =
-                                               SWCONFIG_LED_PORT_SPEED_NA;
-                                       break;
-                               case SWITCH_PORT_SPEED_10:
-                                       sw_trig->link_speed[i] =
-                                               SWCONFIG_LED_PORT_SPEED_10;
-                                       break;
-                               case SWITCH_PORT_SPEED_100:
-                                       sw_trig->link_speed[i] =
-                                               SWCONFIG_LED_PORT_SPEED_100;
-                                       break;
-                               case SWITCH_PORT_SPEED_1000:
-                                       sw_trig->link_speed[i] =
-                                               SWCONFIG_LED_PORT_SPEED_1000;
-                                       break;
-                               }
-                       }
-               }
-
-               if (swdev->ops->get_port_stats) {
-                       struct switch_port_stats port_stats;
-
-                       memset(&port_stats, '\0', sizeof(port_stats));
-                       swdev->ops->get_port_stats(swdev, i, &port_stats);
-                       sw_trig->port_tx_traffic[i] = port_stats.tx_bytes;
-                       sw_trig->port_rx_traffic[i] = port_stats.rx_bytes;
-               }
-       }
-
-       sw_trig->port_link = link;
-
-       swconfig_trig_update_leds(sw_trig);
-
-       schedule_delayed_work(&sw_trig->sw_led_work,
-                             SWCONFIG_LED_TIMER_INTERVAL);
-}
-
-static int
-swconfig_create_led_trigger(struct switch_dev *swdev)
-{
-       struct switch_led_trigger *sw_trig;
-       int err;
-
-       if (!swdev->ops->get_port_link)
-               return 0;
-
-       sw_trig = kzalloc(sizeof(struct switch_led_trigger), GFP_KERNEL);
-       if (!sw_trig)
-               return -ENOMEM;
-
-       sw_trig->swdev = swdev;
-       sw_trig->trig.name = swdev->devname;
-       sw_trig->trig.activate = swconfig_trig_activate;
-       sw_trig->trig.deactivate = swconfig_trig_deactivate;
-
-       INIT_DELAYED_WORK(&sw_trig->sw_led_work, swconfig_led_work_func);
-
-       err = led_trigger_register(&sw_trig->trig);
-       if (err)
-               goto err_free;
-
-       swdev->led_trigger = sw_trig;
-
-       return 0;
-
-err_free:
-       kfree(sw_trig);
-       return err;
-}
-
-static void
-swconfig_destroy_led_trigger(struct switch_dev *swdev)
-{
-       struct switch_led_trigger *sw_trig;
-
-       sw_trig = swdev->led_trigger;
-       if (sw_trig) {
-               cancel_delayed_work_sync(&sw_trig->sw_led_work);
-               led_trigger_unregister(&sw_trig->trig);
-               kfree(sw_trig);
-       }
-}
-
-#else /* SWCONFIG_LEDS */
-static inline int
-swconfig_create_led_trigger(struct switch_dev *swdev) { return 0; }
-
-static inline void
-swconfig_destroy_led_trigger(struct switch_dev *swdev) { }
-#endif /* CONFIG_SWCONFIG_LEDS */
diff --git a/target/linux/generic/files/include/linux/ar8216_platform.h b/target/linux/generic/files/include/linux/ar8216_platform.h
deleted file mode 100644 (file)
index 24bc442..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * AR8216 switch driver platform data
- *
- * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef AR8216_PLATFORM_H
-#define AR8216_PLATFORM_H
-
-enum ar8327_pad_mode {
-       AR8327_PAD_NC = 0,
-       AR8327_PAD_MAC2MAC_MII,
-       AR8327_PAD_MAC2MAC_GMII,
-       AR8327_PAD_MAC_SGMII,
-       AR8327_PAD_MAC2PHY_MII,
-       AR8327_PAD_MAC2PHY_GMII,
-       AR8327_PAD_MAC_RGMII,
-       AR8327_PAD_PHY_GMII,
-       AR8327_PAD_PHY_RGMII,
-       AR8327_PAD_PHY_MII,
-};
-
-enum ar8327_clk_delay_sel {
-       AR8327_CLK_DELAY_SEL0 = 0,
-       AR8327_CLK_DELAY_SEL1,
-       AR8327_CLK_DELAY_SEL2,
-       AR8327_CLK_DELAY_SEL3,
-};
-
-struct ar8327_pad_cfg {
-       enum ar8327_pad_mode mode;
-       bool rxclk_sel;
-       bool txclk_sel;
-       bool pipe_rxclk_sel;
-       bool txclk_delay_en;
-       bool rxclk_delay_en;
-       bool sgmii_delay_en;
-       enum ar8327_clk_delay_sel txclk_delay_sel;
-       enum ar8327_clk_delay_sel rxclk_delay_sel;
-       bool mac06_exchange_dis;
-};
-
-enum ar8327_port_speed {
-       AR8327_PORT_SPEED_10 = 0,
-       AR8327_PORT_SPEED_100,
-       AR8327_PORT_SPEED_1000,
-};
-
-struct ar8327_port_cfg {
-       int force_link:1;
-       enum ar8327_port_speed speed;
-       int txpause:1;
-       int rxpause:1;
-       int duplex:1;
-};
-
-struct ar8327_sgmii_cfg {
-       u32 sgmii_ctrl;
-       bool serdes_aen;
-};
-
-struct ar8327_led_cfg {
-       u32 led_ctrl0;
-       u32 led_ctrl1;
-       u32 led_ctrl2;
-       u32 led_ctrl3;
-       bool open_drain;
-};
-
-enum ar8327_led_num {
-       AR8327_LED_PHY0_0 = 0,
-       AR8327_LED_PHY0_1,
-       AR8327_LED_PHY0_2,
-       AR8327_LED_PHY1_0,
-       AR8327_LED_PHY1_1,
-       AR8327_LED_PHY1_2,
-       AR8327_LED_PHY2_0,
-       AR8327_LED_PHY2_1,
-       AR8327_LED_PHY2_2,
-       AR8327_LED_PHY3_0,
-       AR8327_LED_PHY3_1,
-       AR8327_LED_PHY3_2,
-       AR8327_LED_PHY4_0,
-       AR8327_LED_PHY4_1,
-       AR8327_LED_PHY4_2,
-};
-
-enum ar8327_led_mode {
-       AR8327_LED_MODE_HW = 0,
-       AR8327_LED_MODE_SW,
-};
-
-struct ar8327_led_info {
-       const char *name;
-       const char *default_trigger;
-       bool active_low;
-       enum ar8327_led_num led_num;
-       enum ar8327_led_mode mode;
-};
-
-#define AR8327_LED_INFO(_led, _mode, _name) {  \
-       .name = (_name),                        \
-       .led_num = AR8327_LED_ ## _led,         \
-       .mode = AR8327_LED_MODE_ ## _mode       \
-}
-
-struct ar8327_platform_data {
-       struct ar8327_pad_cfg *pad0_cfg;
-       struct ar8327_pad_cfg *pad5_cfg;
-       struct ar8327_pad_cfg *pad6_cfg;
-       struct ar8327_sgmii_cfg *sgmii_cfg;
-       struct ar8327_port_cfg port0_cfg;
-       struct ar8327_port_cfg port6_cfg;
-       struct ar8327_led_cfg *led_cfg;
-
-       int (*get_port_link)(unsigned port);
-
-       unsigned num_leds;
-       const struct ar8327_led_info *leds;
-};
-
-#endif /* AR8216_PLATFORM_H */
-
diff --git a/target/linux/generic/files/include/linux/ath5k_platform.h b/target/linux/generic/files/include/linux/ath5k_platform.h
deleted file mode 100644 (file)
index ec85224..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) 2008 Atheros Communications Inc.
- * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
- * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
- * Copyright (c) 2010 Daniel Golle <daniel.golle@gmail.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _LINUX_ATH5K_PLATFORM_H
-#define _LINUX_ATH5K_PLATFORM_H
-
-#define ATH5K_PLAT_EEP_MAX_WORDS       2048
-
-struct ath5k_platform_data {
-       u16 *eeprom_data;
-       u8 *macaddr;
-};
-
-#endif /* _LINUX_ATH5K_PLATFORM_H */
diff --git a/target/linux/generic/files/include/linux/ath9k_platform.h b/target/linux/generic/files/include/linux/ath9k_platform.h
deleted file mode 100644 (file)
index f1f2ad4..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (c) 2008 Atheros Communications Inc.
- * Copyright (c) 2009 Gabor Juhos <juhosg@openwrt.org>
- * Copyright (c) 2009 Imre Kaloz <kaloz@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _LINUX_ATH9K_PLATFORM_H
-#define _LINUX_ATH9K_PLATFORM_H
-
-#define ATH9K_PLAT_EEP_MAX_WORDS       2048
-
-struct ath9k_platform_data {
-       const char *eeprom_name;
-
-       u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
-       u8 *macaddr;
-
-       int led_pin;
-       u32 gpio_mask;
-       u32 gpio_val;
-
-       u32 bt_active_pin;
-       u32 bt_priority_pin;
-       u32 wlan_active_pin;
-
-       bool endian_check;
-       bool is_clk_25mhz;
-       bool tx_gain_buffalo;
-       bool disable_2ghz;
-       bool disable_5ghz;
-       bool led_active_high;
-
-       int (*get_mac_revision)(void);
-       int (*external_reset)(void);
-
-       bool use_eeprom;
-
-       int num_leds;
-       const struct gpio_led *leds;
-
-       unsigned num_btns;
-       const struct gpio_keys_button *btns;
-       unsigned btn_poll_interval;
-
-       bool ubnt_hsr;
-};
-
-#endif /* _LINUX_ATH9K_PLATFORM_H */
diff --git a/target/linux/generic/files/include/linux/myloader.h b/target/linux/generic/files/include/linux/myloader.h
deleted file mode 100644 (file)
index d89e415..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *  Compex's MyLoader specific definitions
- *
- *  Copyright (C) 2006-2008 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#ifndef _MYLOADER_H_
-#define _MYLOADER_H_
-
-/* Myloader specific magic numbers */
-#define MYLO_MAGIC_SYS_PARAMS  0x20021107
-#define MYLO_MAGIC_PARTITIONS  0x20021103
-#define MYLO_MAGIC_BOARD_PARAMS        0x20021103
-
-/* Vendor ID's (seems to be same as the PCI vendor ID's) */
-#define VENID_COMPEX           0x11F6
-
-/* Devices based on the ADM5120 */
-#define DEVID_COMPEX_NP27G     0x0078
-#define DEVID_COMPEX_NP28G     0x044C
-#define DEVID_COMPEX_NP28GHS   0x044E
-#define DEVID_COMPEX_WP54Gv1C  0x0514
-#define DEVID_COMPEX_WP54G     0x0515
-#define DEVID_COMPEX_WP54AG    0x0546
-#define DEVID_COMPEX_WPP54AG   0x0550
-#define DEVID_COMPEX_WPP54G    0x0555
-
-/* Devices based on the Atheros AR2317 */
-#define DEVID_COMPEX_NP25G     0x05E6
-#define DEVID_COMPEX_WPE53G    0x05DC
-
-/* Devices based on the Atheros AR71xx */
-#define DEVID_COMPEX_WP543     0x0640
-#define DEVID_COMPEX_WPE72     0x0672
-
-/* Devices based on the IXP422 */
-#define DEVID_COMPEX_WP18      0x047E
-#define DEVID_COMPEX_NP18A     0x0489
-
-/* Other devices */
-#define DEVID_COMPEX_NP26G8M   0x03E8
-#define DEVID_COMPEX_NP26G16M  0x03E9
-
-struct mylo_partition {
-       uint16_t        flags;  /* partition flags */
-       uint16_t        type;   /* type of the partition */
-       uint32_t        addr;   /* relative address of the partition from the
-                                  flash start */
-       uint32_t        size;   /* size of the partition in bytes */
-       uint32_t        param;  /* if this is the active partition, the
-                                  MyLoader load code to this address */
-};
-
-#define PARTITION_FLAG_ACTIVE  0x8000 /* this is the active partition,
-                                       * MyLoader loads firmware from here */
-#define PARTITION_FLAG_ISRAM   0x2000 /* FIXME: this is a RAM partition? */
-#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */
-#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM
-                                       * before decompression */
-#define PARTITION_FLAG_LZMA    0x0100 /* partition data compressed by LZMA */
-#define PARTITION_FLAG_HAVEHDR  0x0002 /* the partition data have a header */
-
-#define PARTITION_TYPE_FREE    0
-#define PARTITION_TYPE_USED    1
-
-#define MYLO_MAX_PARTITIONS    8       /* maximum number of partitions in the
-                                          partition table */
-
-struct mylo_partition_table {
-       uint32_t        magic;          /* must be MYLO_MAGIC_PARTITIONS */
-       uint32_t        res0;           /* unknown/unused */
-       uint32_t        res1;           /* unknown/unused */
-       uint32_t        res2;           /* unknown/unused */
-       struct mylo_partition partitions[MYLO_MAX_PARTITIONS];
-};
-
-struct mylo_partition_header {
-       uint32_t        len;            /* length of the partition data */
-       uint32_t        crc;            /* CRC value of the partition data */
-};
-
-struct mylo_system_params {
-       uint32_t        magic;          /* must be MYLO_MAGIC_SYS_PARAMS */
-       uint32_t        res0;
-       uint32_t        res1;
-       uint32_t        mylo_ver;
-       uint16_t        vid;            /* Vendor ID */
-       uint16_t        did;            /* Device ID */
-       uint16_t        svid;           /* Sub Vendor ID */
-       uint16_t        sdid;           /* Sub Device ID */
-       uint32_t        rev;            /* device revision */
-       uint32_t        fwhi;
-       uint32_t        fwlo;
-       uint32_t        tftp_addr;
-       uint32_t        prog_start;
-       uint32_t        flash_size;     /* size of boot FLASH in bytes */
-       uint32_t        dram_size;      /* size of onboard RAM in bytes */
-};
-
-struct mylo_eth_addr {
-       uint8_t mac[6];
-       uint8_t csum[2];
-};
-
-#define MYLO_ETHADDR_COUNT     8       /* maximum number of ethernet address
-                                          in the board parameters */
-
-struct mylo_board_params {
-       uint32_t        magic;  /* must be MYLO_MAGIC_BOARD_PARAMS */
-       uint32_t        res0;
-       uint32_t        res1;
-       uint32_t        res2;
-       struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT];
-};
-
-#endif /* _MYLOADER_H_*/
diff --git a/target/linux/generic/files/include/linux/platform_data/adm6996-gpio.h b/target/linux/generic/files/include/linux/platform_data/adm6996-gpio.h
deleted file mode 100644 (file)
index d5af9bb..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * ADM6996 GPIO platform data
- *
- * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of the GNU General Public License v2 as published by the
- * Free Software Foundation
- */
-
-#ifndef __PLATFORM_ADM6996_GPIO_H
-#define __PLATFORM_ADM6996_GPIO_H
-
-#include <linux/kernel.h>
-
-enum adm6996_model {
-       ADM6996FC = 1,
-       ADM6996M = 2,
-       ADM6996L = 3,
-};
-
-struct adm6996_gpio_platform_data {
-       u8 eecs;
-       u8 eesk;
-       u8 eedi;
-       enum adm6996_model model;
-};
-
-#endif
diff --git a/target/linux/generic/files/include/linux/platform_data/b53.h b/target/linux/generic/files/include/linux/platform_data/b53.h
deleted file mode 100644 (file)
index 7842741..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * B53 platform data
- *
- * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef __B53_H
-#define __B53_H
-
-#include <linux/kernel.h>
-
-struct b53_platform_data {
-       u32 chip_id;
-       u16 enabled_ports;
-
-       /* allow to specify an ethX alias */
-       const char *alias;
-
-       /* only used by MMAP'd driver */
-       unsigned big_endian:1;
-       void __iomem *regs;
-};
-
-#endif
diff --git a/target/linux/generic/files/include/linux/routerboot.h b/target/linux/generic/files/include/linux/routerboot.h
deleted file mode 100644 (file)
index 3cda858..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- *  Mikrotik's RouterBOOT definitions
- *
- *  Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org>
- *
- *  This program is free software; you can redistribute it and/or modify it
- *  under the terms of the GNU General Public License version 2 as published
- *  by the Free Software Foundation.
- *
- */
-
-#ifndef _ROUTERBOOT_H
-#define _ROUTERBOOT_H
-
-#define RB_MAC_SIZE            6
-
-/*
- * Magic numbers
- */
-#define RB_MAGIC_HARD  0x64726148 /* "Hard" */
-#define RB_MAGIC_SOFT  0x74666F53 /* "Soft" */
-#define RB_MAGIC_DAWN  0x6E776144 /* "Dawn" */
-
-#define RB_ID_TERMINATOR       0
-
-/*
- * ID values for Hardware settings
- */
-#define RB_ID_HARD_01          1
-#define RB_ID_HARD_02          2
-#define RB_ID_FLASH_INFO       3
-#define RB_ID_MAC_ADDRESS_PACK 4
-#define RB_ID_BOARD_NAME       5
-#define RB_ID_BIOS_VERSION     6
-#define RB_ID_HARD_07          7
-#define RB_ID_SDRAM_TIMINGS    8
-#define RB_ID_DEVICE_TIMINGS   9
-#define RB_ID_SOFTWARE_ID      10
-#define RB_ID_SERIAL_NUMBER    11
-#define RB_ID_HARD_12          12
-#define RB_ID_MEMORY_SIZE      13
-#define RB_ID_MAC_ADDRESS_COUNT        14
-#define RB_ID_HW_OPTIONS       21
-#define RB_ID_WLAN_DATA                22
-
-/*
- * ID values for Software settings
- */
-#define RB_ID_UART_SPEED       1
-#define RB_ID_BOOT_DELAY       2
-#define RB_ID_BOOT_DEVICE      3
-#define RB_ID_BOOT_KEY         4
-#define RB_ID_CPU_MODE         5
-#define RB_ID_FW_VERSION       6
-#define RB_ID_SOFT_07          7
-#define RB_ID_SOFT_08          8
-#define RB_ID_BOOT_PROTOCOL    9
-#define RB_ID_SOFT_10          10
-#define RB_ID_SOFT_11          11
-
-/*
- * UART_SPEED values
- */
-#define RB_UART_SPEED_115200   0
-#define RB_UART_SPEED_57600    1
-#define RB_UART_SPEED_38400    2
-#define RB_UART_SPEED_19200    3
-#define RB_UART_SPEED_9600     4
-#define RB_UART_SPEED_4800     5
-#define RB_UART_SPEED_2400     6
-#define RB_UART_SPEED_1200     7
-
-/*
- * BOOT_DELAY values
- */
-#define RB_BOOT_DELAY_0SEC     0
-#define RB_BOOT_DELAY_1SEC     1
-#define RB_BOOT_DELAY_2SEC     2
-
-/*
- * BOOT_DEVICE values
- */
-#define RB_BOOT_DEVICE_ETHER   0
-#define RB_BOOT_DEVICE_NANDETH 1
-#define RB_BOOT_DEVICE_ETHONCE 2
-#define RB_BOOT_DEVICE_NANDONLY        3
-
-/*
- * BOOT_KEY values
- */
-#define RB_BOOT_KEY_ANY                0
-#define RB_BOOT_KEY_DEL                1
-
-/*
- * CPU_MODE values
- */
-#define RB_CPU_MODE_POWERSAVE  0
-#define RB_CPU_MODE_REGULAR    1
-
-/*
- * BOOT_PROTOCOL values
- */
-#define RB_BOOT_PROTOCOL_BOOTP 0
-#define RB_BOOT_PROTOCOL_DHCP  1
-
-#endif /* _ROUTERBOOT_H */
diff --git a/target/linux/generic/files/include/linux/rt2x00_platform.h b/target/linux/generic/files/include/linux/rt2x00_platform.h
deleted file mode 100644 (file)
index e10377e..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Platform data definition for the rt2x00 driver
- *
- * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- *
- */
-
-#ifndef _RT2X00_PLATFORM_H
-#define _RT2X00_PLATFORM_H
-
-struct rt2x00_platform_data {
-       char *eeprom_file_name;
-       const u8 *mac_address;
-
-       int disable_2ghz;
-       int disable_5ghz;
-};
-
-#endif /* _RT2X00_PLATFORM_H */
diff --git a/target/linux/generic/files/include/linux/rtl8366.h b/target/linux/generic/files/include/linux/rtl8366.h
deleted file mode 100644 (file)
index e3ce8f5..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Platform data definition for the Realtek RTL8366RB/S ethernet switch driver
- *
- * Copyright (C) 2009-2010 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#ifndef _RTL8366_H
-#define _RTL8366_H
-
-#define RTL8366_DRIVER_NAME    "rtl8366"
-#define RTL8366S_DRIVER_NAME   "rtl8366s"
-#define RTL8366RB_DRIVER_NAME  "rtl8366rb"
-
-struct rtl8366_smi;
-
-enum rtl8366_type {
-       RTL8366_TYPE_UNKNOWN,
-       RTL8366_TYPE_S,
-       RTL8366_TYPE_RB,
-};
-
-struct rtl8366_initval {
-       unsigned        reg;
-       u16             val;
-};
-
-struct rtl8366_platform_data {
-       unsigned        gpio_sda;
-       unsigned        gpio_sck;
-       void            (*hw_reset)(struct rtl8366_smi *smi, bool active);
-
-       unsigned        num_initvals;
-       struct rtl8366_initval *initvals;
-};
-
-enum rtl8366_type rtl8366_smi_detect(struct rtl8366_platform_data *pdata);
-
-#endif /*  _RTL8366_H */
diff --git a/target/linux/generic/files/include/linux/rtl8367.h b/target/linux/generic/files/include/linux/rtl8367.h
deleted file mode 100644 (file)
index 855de6a..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Platform data definition for the Realtek RTL8367 ethernet switch driver
- *
- * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
-
-#ifndef _RTL8367_H
-#define _RTL8367_H
-
-#define RTL8367_DRIVER_NAME    "rtl8367"
-#define RTL8367B_DRIVER_NAME   "rtl8367b"
-
-enum rtl8367_port_speed {
-       RTL8367_PORT_SPEED_10 = 0,
-       RTL8367_PORT_SPEED_100,
-       RTL8367_PORT_SPEED_1000,
-};
-
-struct rtl8367_port_ability {
-       int force_mode;
-       int nway;
-       int txpause;
-       int rxpause;
-       int link;
-       int duplex;
-       enum rtl8367_port_speed speed;
-};
-
-enum rtl8367_extif_mode {
-       RTL8367_EXTIF_MODE_DISABLED = 0,
-       RTL8367_EXTIF_MODE_RGMII,
-       RTL8367_EXTIF_MODE_MII_MAC,
-       RTL8367_EXTIF_MODE_MII_PHY,
-       RTL8367_EXTIF_MODE_TMII_MAC,
-       RTL8367_EXTIF_MODE_TMII_PHY,
-       RTL8367_EXTIF_MODE_GMII,
-       RTL8367_EXTIF_MODE_RGMII_33V,
-};
-
-struct rtl8367_extif_config {
-       unsigned int txdelay;
-       unsigned int rxdelay;
-       enum rtl8367_extif_mode mode;
-       struct rtl8367_port_ability ability;
-};
-
-struct rtl8367_platform_data {
-       unsigned gpio_sda;
-       unsigned gpio_sck;
-       void (*hw_reset)(bool active);
-
-       struct rtl8367_extif_config *extif0_cfg;
-       struct rtl8367_extif_config *extif1_cfg;
-};
-
-#endif /*  _RTL8367_H */
diff --git a/target/linux/generic/files/include/linux/switch.h b/target/linux/generic/files/include/linux/switch.h
deleted file mode 100644 (file)
index 4e62384..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * switch.h: Switch configuration API
- *
- * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-#ifndef _LINUX_SWITCH_H
-#define _LINUX_SWITCH_H
-
-#include <net/genetlink.h>
-#include <uapi/linux/switch.h>
-
-struct switch_dev;
-struct switch_op;
-struct switch_val;
-struct switch_attr;
-struct switch_attrlist;
-struct switch_led_trigger;
-
-int register_switch(struct switch_dev *dev, struct net_device *netdev);
-void unregister_switch(struct switch_dev *dev);
-
-/**
- * struct switch_attrlist - attribute list
- *
- * @n_attr: number of attributes
- * @attr: pointer to the attributes array
- */
-struct switch_attrlist {
-       int n_attr;
-       const struct switch_attr *attr;
-};
-
-enum switch_port_speed {
-       SWITCH_PORT_SPEED_UNKNOWN = 0,
-       SWITCH_PORT_SPEED_10 = 10,
-       SWITCH_PORT_SPEED_100 = 100,
-       SWITCH_PORT_SPEED_1000 = 1000,
-};
-
-struct switch_port_link {
-       bool link;
-       bool duplex;
-       bool aneg;
-       bool tx_flow;
-       bool rx_flow;
-       enum switch_port_speed speed;
-       /* in ethtool adv_t format */
-       u32 eee;
-};
-
-struct switch_port_stats {
-       unsigned long long tx_bytes;
-       unsigned long long rx_bytes;
-};
-
-/**
- * struct switch_dev_ops - switch driver operations
- *
- * @attr_global: global switch attribute list
- * @attr_port: port attribute list
- * @attr_vlan: vlan attribute list
- *
- * Callbacks:
- *
- * @get_vlan_ports: read the port list of a VLAN
- * @set_vlan_ports: set the port list of a VLAN
- *
- * @get_port_pvid: get the primary VLAN ID of a port
- * @set_port_pvid: set the primary VLAN ID of a port
- *
- * @apply_config: apply all changed settings to the switch
- * @reset_switch: resetting the switch
- */
-struct switch_dev_ops {
-       struct switch_attrlist attr_global, attr_port, attr_vlan;
-
-       int (*get_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
-       int (*set_vlan_ports)(struct switch_dev *dev, struct switch_val *val);
-
-       int (*get_port_pvid)(struct switch_dev *dev, int port, int *val);
-       int (*set_port_pvid)(struct switch_dev *dev, int port, int val);
-
-       int (*apply_config)(struct switch_dev *dev);
-       int (*reset_switch)(struct switch_dev *dev);
-
-       int (*get_port_link)(struct switch_dev *dev, int port,
-                            struct switch_port_link *link);
-       int (*set_port_link)(struct switch_dev *dev, int port,
-                            struct switch_port_link *link);
-       int (*get_port_stats)(struct switch_dev *dev, int port,
-                             struct switch_port_stats *stats);
-
-       int (*phy_read16)(struct switch_dev *dev, int addr, u8 reg, u16 *value);
-       int (*phy_write16)(struct switch_dev *dev, int addr, u8 reg, u16 value);
-};
-
-struct switch_dev {
-       struct device_node *of_node;
-       const struct switch_dev_ops *ops;
-       /* will be automatically filled */
-       char devname[IFNAMSIZ];
-
-       const char *name;
-       /* NB: either alias or netdev must be set */
-       const char *alias;
-       struct net_device *netdev;
-
-       unsigned int ports;
-       unsigned int vlans;
-       unsigned int cpu_port;
-
-       /* the following fields are internal for swconfig */
-       unsigned int id;
-       struct list_head dev_list;
-       unsigned long def_global, def_port, def_vlan;
-
-       struct mutex sw_mutex;
-       struct switch_port *portbuf;
-       struct switch_portmap *portmap;
-       struct switch_port_link linkbuf;
-
-       char buf[128];
-
-#ifdef CONFIG_SWCONFIG_LEDS
-       struct switch_led_trigger *led_trigger;
-#endif
-};
-
-struct switch_port {
-       u32 id;
-       u32 flags;
-};
-
-struct switch_portmap {
-       u32 virt;
-       const char *s;
-};
-
-struct switch_val {
-       const struct switch_attr *attr;
-       unsigned int port_vlan;
-       unsigned int len;
-       union {
-               const char *s;
-               u32 i;
-               struct switch_port *ports;
-               struct switch_port_link *link;
-       } value;
-};
-
-struct switch_attr {
-       int disabled;
-       int type;
-       const char *name;
-       const char *description;
-
-       int (*set)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
-       int (*get)(struct switch_dev *dev, const struct switch_attr *attr, struct switch_val *val);
-
-       /* for driver internal use */
-       int id;
-       int ofs;
-       int max;
-};
-
-int switch_generic_set_link(struct switch_dev *dev, int port,
-                           struct switch_port_link *link);
-
-#endif /* _LINUX_SWITCH_H */
diff --git a/target/linux/generic/files/include/uapi/linux/switch.h b/target/linux/generic/files/include/uapi/linux/switch.h
deleted file mode 100644 (file)
index ea44965..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * switch.h: Switch configuration API
- *
- * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#ifndef _UAPI_LINUX_SWITCH_H
-#define _UAPI_LINUX_SWITCH_H
-
-#include <linux/types.h>
-#include <linux/netdevice.h>
-#include <linux/netlink.h>
-#include <linux/genetlink.h>
-#ifndef __KERNEL__
-#include <netlink/netlink.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/ctrl.h>
-#endif
-
-/* main attributes */
-enum {
-       SWITCH_ATTR_UNSPEC,
-       /* global */
-       SWITCH_ATTR_TYPE,
-       /* device */
-       SWITCH_ATTR_ID,
-       SWITCH_ATTR_DEV_NAME,
-       SWITCH_ATTR_ALIAS,
-       SWITCH_ATTR_NAME,
-       SWITCH_ATTR_VLANS,
-       SWITCH_ATTR_PORTS,
-       SWITCH_ATTR_PORTMAP,
-       SWITCH_ATTR_CPU_PORT,
-       /* attributes */
-       SWITCH_ATTR_OP_ID,
-       SWITCH_ATTR_OP_TYPE,
-       SWITCH_ATTR_OP_NAME,
-       SWITCH_ATTR_OP_PORT,
-       SWITCH_ATTR_OP_VLAN,
-       SWITCH_ATTR_OP_VALUE_INT,
-       SWITCH_ATTR_OP_VALUE_STR,
-       SWITCH_ATTR_OP_VALUE_PORTS,
-       SWITCH_ATTR_OP_VALUE_LINK,
-       SWITCH_ATTR_OP_DESCRIPTION,
-       /* port lists */
-       SWITCH_ATTR_PORT,
-       SWITCH_ATTR_MAX
-};
-
-enum {
-       /* port map */
-       SWITCH_PORTMAP_PORTS,
-       SWITCH_PORTMAP_SEGMENT,
-       SWITCH_PORTMAP_VIRT,
-       SWITCH_PORTMAP_MAX
-};
-
-/* commands */
-enum {
-       SWITCH_CMD_UNSPEC,
-       SWITCH_CMD_GET_SWITCH,
-       SWITCH_CMD_NEW_ATTR,
-       SWITCH_CMD_LIST_GLOBAL,
-       SWITCH_CMD_GET_GLOBAL,
-       SWITCH_CMD_SET_GLOBAL,
-       SWITCH_CMD_LIST_PORT,
-       SWITCH_CMD_GET_PORT,
-       SWITCH_CMD_SET_PORT,
-       SWITCH_CMD_LIST_VLAN,
-       SWITCH_CMD_GET_VLAN,
-       SWITCH_CMD_SET_VLAN
-};
-
-/* data types */
-enum switch_val_type {
-       SWITCH_TYPE_UNSPEC,
-       SWITCH_TYPE_INT,
-       SWITCH_TYPE_STRING,
-       SWITCH_TYPE_PORTS,
-       SWITCH_TYPE_LINK,
-       SWITCH_TYPE_NOVAL,
-};
-
-/* port nested attributes */
-enum {
-       SWITCH_PORT_UNSPEC,
-       SWITCH_PORT_ID,
-       SWITCH_PORT_FLAG_TAGGED,
-       SWITCH_PORT_ATTR_MAX
-};
-
-/* link nested attributes */
-enum {
-       SWITCH_LINK_UNSPEC,
-       SWITCH_LINK_FLAG_LINK,
-       SWITCH_LINK_FLAG_DUPLEX,
-       SWITCH_LINK_FLAG_ANEG,
-       SWITCH_LINK_FLAG_TX_FLOW,
-       SWITCH_LINK_FLAG_RX_FLOW,
-       SWITCH_LINK_SPEED,
-       SWITCH_LINK_FLAG_EEE_100BASET,
-       SWITCH_LINK_FLAG_EEE_1000BASET,
-       SWITCH_LINK_ATTR_MAX,
-};
-
-#define SWITCH_ATTR_DEFAULTS_OFFSET    0x1000
-
-
-#endif /* _UAPI_LINUX_SWITCH_H */