[OpenWrt-Devel] [PATCH] bmips: add new target

Álvaro Fernández Rojas noltari at gmail.com
Fri Oct 9 16:29:44 EDT 2015


Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
---
 target/linux/bmips/Makefile                        |   25 +
 target/linux/bmips/base-files/etc/diag.sh          |   66 +
 .../bmips/base-files/etc/uci-defaults/02_network   |   16 +
 target/linux/bmips/base-files/lib/bmips.sh         |   76 +
 .../base-files/lib/preinit/03_preinit_do_bmips.sh  |   12 +
 target/linux/bmips/config-4.1                      |  222 ++
 target/linux/bmips/dts/ar-5381u.dts                |   59 +
 target/linux/bmips/dts/ar-5387un.dts               |   65 +
 target/linux/bmips/dts/bcm3368.dtsi                |  130 ++
 target/linux/bmips/dts/bcm6318.dtsi                |  137 ++
 target/linux/bmips/dts/bcm63268.dtsi               |  159 ++
 target/linux/bmips/dts/bcm6328.dtsi                |  158 ++
 target/linux/bmips/dts/bcm6338.dtsi                |  102 +
 target/linux/bmips/dts/bcm6345.dtsi                |  102 +
 target/linux/bmips/dts/bcm6348.dtsi                |  127 ++
 target/linux/bmips/dts/bcm6358.dtsi                |  179 ++
 target/linux/bmips/dts/bcm6362.dtsi                |  157 ++
 target/linux/bmips/dts/bcm6368.dtsi                |  179 ++
 target/linux/bmips/dts/fast1704.dts                |   24 +
 target/linux/bmips/dts/hg520v.dts                  |   67 +
 target/linux/bmips/dts/hg556a-a.dts                |  138 ++
 target/linux/bmips/dts/hg556a-b.dts                |  138 ++
 target/linux/bmips/dts/hg556a-c.dts                |  133 ++
 target/linux/bmips/dts/nb4-fxc-r1.dts              |  104 +
 target/linux/bmips/dts/nb4-ser-r0.dts              |  104 +
 target/linux/bmips/dts/vg-8050.dts                 |  125 ++
 target/linux/bmips/dts/vr-3025u.dts                |   82 +
 target/linux/bmips/dts/vr-3025un.dts               |   82 +
 target/linux/bmips/dts/vr-3032u.dts                |  153 ++
 target/linux/bmips/dts/wap-5813n.dts               |   94 +
 target/linux/bmips/image/Makefile                  |  245 +++
 target/linux/bmips/image/lzma-loader/Makefile      |   62 +
 .../linux/bmips/image/lzma-loader/src/LzmaDecode.c |  584 +++++
 .../linux/bmips/image/lzma-loader/src/LzmaDecode.h |  113 +
 .../linux/bmips/image/lzma-loader/src/LzmaTypes.h  |   45 +
 target/linux/bmips/image/lzma-loader/src/Makefile  |   86 +
 target/linux/bmips/image/lzma-loader/src/board.c   |  103 +
 target/linux/bmips/image/lzma-loader/src/cache.c   |   46 +
 target/linux/bmips/image/lzma-loader/src/cache.h   |   17 +
 .../linux/bmips/image/lzma-loader/src/cacheops.h   |   85 +
 target/linux/bmips/image/lzma-loader/src/config.h  |   31 +
 .../linux/bmips/image/lzma-loader/src/cp0regdef.h  |   54 +
 target/linux/bmips/image/lzma-loader/src/head.S    |  118 +
 target/linux/bmips/image/lzma-loader/src/loader.c  |  175 ++
 .../linux/bmips/image/lzma-loader/src/loader.lds   |   34 +
 .../linux/bmips/image/lzma-loader/src/loader2.lds  |   10 +
 .../bmips/image/lzma-loader/src/lzma-data.lds      |    8 +
 target/linux/bmips/image/lzma-loader/src/printf.c  |  350 +++
 target/linux/bmips/image/lzma-loader/src/printf.h  |   18 +
 ...-Add-support-for-vmlinux.bin-appended-dtb.patch |  112 +
 ...PS-BMIPS-Build-all-dtbs-if-no-builtin-dtb.patch |   48 +
 ...PS-Accept-UHI-interface-for-passing-a-dtb.patch |   37 +
 ...IPS-Add-support-for-Broadcom-BCM97435SVMB.patch |  112 +
 .../020-leds-add-BCM6328-LED-driver.patch          |  464 ++++
 .../021-leds-add-BCM6358-LED-driver.patch          |  293 +++
 .../030-mtd-nand-add-common-DT-init-code.patch     |  111 +
 ...d-NAND-driver-library-for-Broadcom-STB-NA.patch | 2323 ++++++++++++++++++++
 ...32-mtd-brcmnand-add-support-for-STB-chips.patch |   68 +
 ...brcmnand-add-extra-SoC-support-to-library.patch |  167 ++
 ...d-add-support-for-Broadcom-s-IPROC-family.patch |  173 ++
 .../035-mtd-brcmnand-add-BCM63138-support.patch    |  137 ++
 ...rcmnand-remove-double-new-line-from-print.patch |   25 +
 ...rcmnand-do-not-make-local-variable-static.patch |   26 +
 ...-brcmnand-drop-unnecessary-initialization.patch |   21 +
 ...039-mtd-brcmnand-Fix-misuse-of-IS_ENABLED.patch |   38 +
 .../patches-4.1/100-BMIPS-select-gpiolib.patch     |   10 +
 ...-support-for-bcm6345-style-periphery-irq-.patch |  455 ++++
 ...-support-for-bcm6345-style-external-inter.patch |  380 ++++
 .../122-MIPS-BMIPS-enable-bcm6345-irq.patch        |   12 +
 ...cm6345-style-peripher-irq-ensure-disabled.patch |   12 +
 .../210-MIPS-BMIPS-BCM3368-basic-support.patch     |   22 +
 .../212-MIPS-BMIPS-BCM6338-basic-support.patch     |   22 +
 .../213-MIPS-BMIPS-BCM6345-basic-support.patch     |   22 +
 .../214-MIPS-BMIPS-BCM6348-basic-support.patch     |   22 +
 .../215-MIPS-BMIPS-BCM6358-basic-support.patch     |   22 +
 .../216-MIPS-BMIPS-BCM6362-basic-support.patch     |   22 +
 .../217-MIPS-BMIPS-BCM63268-basic-support.patch    |   22 +
 .../218-MIPS-BMIPS-BCM6318-basic-support.patch     |   22 +
 .../patches-4.1/220-MIPS-BMIPS-uart-hacks.patch    |   26 +
 .../221-MIPS-BMIPS-export-dma_mem_page.patch       |   10 +
 .../bmips/patches-4.1/222-MIPS-BMIPS-ioremap.patch |   35 +
 ...Revert-provide-a-plat_post_dma_flush-hook.patch |   12 +
 ...gpio-add-a-simple-GPIO-driver-for-BCM63XX.patch |  160 ++
 ...240-mtd-brcmnand-add-polling-mode-support.patch |  136 ++
 ...8-add-more-configuration-options-and-fix-.patch |  106 +
 target/linux/bmips/profiles/00-default.mk          |   15 +
 target/linux/bmips/profiles/comtrend.mk            |   69 +
 target/linux/bmips/profiles/huawei.mk              |   33 +
 target/linux/bmips/profiles/sagem.mk               |   15 +
 target/linux/bmips/profiles/sfr.mk                 |   15 +
 90 files changed, 11231 insertions(+)
 create mode 100644 target/linux/bmips/Makefile
 create mode 100644 target/linux/bmips/base-files/etc/diag.sh
 create mode 100644 target/linux/bmips/base-files/etc/uci-defaults/02_network
 create mode 100644 target/linux/bmips/base-files/lib/bmips.sh
 create mode 100644 target/linux/bmips/base-files/lib/preinit/03_preinit_do_bmips.sh
 create mode 100644 target/linux/bmips/config-4.1
 create mode 100644 target/linux/bmips/dts/ar-5381u.dts
 create mode 100644 target/linux/bmips/dts/ar-5387un.dts
 create mode 100644 target/linux/bmips/dts/bcm3368.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6318.dtsi
 create mode 100644 target/linux/bmips/dts/bcm63268.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6328.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6338.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6345.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6348.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6358.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6362.dtsi
 create mode 100644 target/linux/bmips/dts/bcm6368.dtsi
 create mode 100644 target/linux/bmips/dts/fast1704.dts
 create mode 100644 target/linux/bmips/dts/hg520v.dts
 create mode 100644 target/linux/bmips/dts/hg556a-a.dts
 create mode 100644 target/linux/bmips/dts/hg556a-b.dts
 create mode 100644 target/linux/bmips/dts/hg556a-c.dts
 create mode 100644 target/linux/bmips/dts/nb4-fxc-r1.dts
 create mode 100644 target/linux/bmips/dts/nb4-ser-r0.dts
 create mode 100644 target/linux/bmips/dts/vg-8050.dts
 create mode 100644 target/linux/bmips/dts/vr-3025u.dts
 create mode 100644 target/linux/bmips/dts/vr-3025un.dts
 create mode 100644 target/linux/bmips/dts/vr-3032u.dts
 create mode 100644 target/linux/bmips/dts/wap-5813n.dts
 create mode 100644 target/linux/bmips/image/Makefile
 create mode 100644 target/linux/bmips/image/lzma-loader/Makefile
 create mode 100644 target/linux/bmips/image/lzma-loader/src/LzmaDecode.c
 create mode 100644 target/linux/bmips/image/lzma-loader/src/LzmaDecode.h
 create mode 100644 target/linux/bmips/image/lzma-loader/src/LzmaTypes.h
 create mode 100644 target/linux/bmips/image/lzma-loader/src/Makefile
 create mode 100644 target/linux/bmips/image/lzma-loader/src/board.c
 create mode 100644 target/linux/bmips/image/lzma-loader/src/cache.c
 create mode 100644 target/linux/bmips/image/lzma-loader/src/cache.h
 create mode 100644 target/linux/bmips/image/lzma-loader/src/cacheops.h
 create mode 100644 target/linux/bmips/image/lzma-loader/src/config.h
 create mode 100644 target/linux/bmips/image/lzma-loader/src/cp0regdef.h
 create mode 100644 target/linux/bmips/image/lzma-loader/src/head.S
 create mode 100644 target/linux/bmips/image/lzma-loader/src/loader.c
 create mode 100644 target/linux/bmips/image/lzma-loader/src/loader.lds
 create mode 100644 target/linux/bmips/image/lzma-loader/src/loader2.lds
 create mode 100644 target/linux/bmips/image/lzma-loader/src/lzma-data.lds
 create mode 100644 target/linux/bmips/image/lzma-loader/src/printf.c
 create mode 100644 target/linux/bmips/image/lzma-loader/src/printf.h
 create mode 100644 target/linux/bmips/patches-4.1/001-MIPS-Add-support-for-vmlinux.bin-appended-dtb.patch
 create mode 100644 target/linux/bmips/patches-4.1/010-MIPS-BMIPS-Build-all-dtbs-if-no-builtin-dtb.patch
 create mode 100644 target/linux/bmips/patches-4.1/011-MIPS-BMIPS-Accept-UHI-interface-for-passing-a-dtb.patch
 create mode 100644 target/linux/bmips/patches-4.1/012-MIPS-BMIPS-Add-support-for-Broadcom-BCM97435SVMB.patch
 create mode 100644 target/linux/bmips/patches-4.1/020-leds-add-BCM6328-LED-driver.patch
 create mode 100644 target/linux/bmips/patches-4.1/021-leds-add-BCM6358-LED-driver.patch
 create mode 100644 target/linux/bmips/patches-4.1/030-mtd-nand-add-common-DT-init-code.patch
 create mode 100644 target/linux/bmips/patches-4.1/031-mtd-nand-add-NAND-driver-library-for-Broadcom-STB-NA.patch
 create mode 100644 target/linux/bmips/patches-4.1/032-mtd-brcmnand-add-support-for-STB-chips.patch
 create mode 100644 target/linux/bmips/patches-4.1/033-mtd-brcmnand-add-extra-SoC-support-to-library.patch
 create mode 100644 target/linux/bmips/patches-4.1/034-mtd-brcmnand-add-support-for-Broadcom-s-IPROC-family.patch
 create mode 100644 target/linux/bmips/patches-4.1/035-mtd-brcmnand-add-BCM63138-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/036-mtd-brcmnand-remove-double-new-line-from-print.patch
 create mode 100644 target/linux/bmips/patches-4.1/037-mtd-brcmnand-do-not-make-local-variable-static.patch
 create mode 100644 target/linux/bmips/patches-4.1/038-mtd-brcmnand-drop-unnecessary-initialization.patch
 create mode 100644 target/linux/bmips/patches-4.1/039-mtd-brcmnand-Fix-misuse-of-IS_ENABLED.patch
 create mode 100644 target/linux/bmips/patches-4.1/100-BMIPS-select-gpiolib.patch
 create mode 100644 target/linux/bmips/patches-4.1/120-irqchip-add-support-for-bcm6345-style-periphery-irq-.patch
 create mode 100644 target/linux/bmips/patches-4.1/121-irqchip-add-support-for-bcm6345-style-external-inter.patch
 create mode 100644 target/linux/bmips/patches-4.1/122-MIPS-BMIPS-enable-bcm6345-irq.patch
 create mode 100644 target/linux/bmips/patches-4.1/200-irqchip-bcm6345-style-peripher-irq-ensure-disabled.patch
 create mode 100644 target/linux/bmips/patches-4.1/210-MIPS-BMIPS-BCM3368-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/212-MIPS-BMIPS-BCM6338-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/213-MIPS-BMIPS-BCM6345-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/214-MIPS-BMIPS-BCM6348-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/215-MIPS-BMIPS-BCM6358-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/216-MIPS-BMIPS-BCM6362-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/217-MIPS-BMIPS-BCM63268-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/218-MIPS-BMIPS-BCM6318-basic-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/220-MIPS-BMIPS-uart-hacks.patch
 create mode 100644 target/linux/bmips/patches-4.1/221-MIPS-BMIPS-export-dma_mem_page.patch
 create mode 100644 target/linux/bmips/patches-4.1/222-MIPS-BMIPS-ioremap.patch
 create mode 100644 target/linux/bmips/patches-4.1/223-MIPS-BMIPS-Revert-provide-a-plat_post_dma_flush-hook.patch
 create mode 100644 target/linux/bmips/patches-4.1/230-gpio-add-a-simple-GPIO-driver-for-BCM63XX.patch
 create mode 100644 target/linux/bmips/patches-4.1/240-mtd-brcmnand-add-polling-mode-support.patch
 create mode 100644 target/linux/bmips/patches-4.1/250-leds-bcm6328-add-more-configuration-options-and-fix-.patch
 create mode 100644 target/linux/bmips/profiles/00-default.mk
 create mode 100644 target/linux/bmips/profiles/comtrend.mk
 create mode 100644 target/linux/bmips/profiles/huawei.mk
 create mode 100644 target/linux/bmips/profiles/sagem.mk
 create mode 100644 target/linux/bmips/profiles/sfr.mk

diff --git a/target/linux/bmips/Makefile b/target/linux/bmips/Makefile
new file mode 100644
index 0000000..7ce78a9
--- /dev/null
+++ b/target/linux/bmips/Makefile
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+
+ARCH:=mips
+BOARD:=bmips
+BOARDNAME:=Broadcom Generic BMIPS
+FEATURES:=squashfs usb atm pci pcmcia usbgadget
+KERNEL_PATCHVER:=4.1
+CPU_TYPE:=mips32
+
+include $(INCLUDE_DIR)/target.mk
+
+DEFAULT_PACKAGES += swconfig kmod-input-gpio-keys-polled kmod-button-hotplug
+
+define Target/Description
+	Build firmware images for BCM33xx cable modem chips,
+	BCM63xx DSL chips, and BCM7xxx set-top box chips.
+endef
+
+$(eval $(call BuildTarget))
diff --git a/target/linux/bmips/base-files/etc/diag.sh b/target/linux/bmips/base-files/etc/diag.sh
new file mode 100644
index 0000000..4498da8
--- /dev/null
+++ b/target/linux/bmips/base-files/etc/diag.sh
@@ -0,0 +1,66 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+
+. /lib/functions/leds.sh
+. /lib/bmips.sh
+
+get_status_led() {
+	case $(bmips_board_name) in
+	"ar-5381u")
+		status_led="AR-5381u:green:power"
+		;;
+	"ar-5387un")
+		status_led="AR-5387un:green:power"
+		;;
+	"hg520v")
+		status_led="HG520v:green:net"
+		;;
+	"hg556a-a" |\
+	"hg556a-b" |\
+	"hg556a-c")
+		status_led="HG556a:red:power"
+		;;
+	"nb4-fxc")
+		status_led="NB4-FXC-r1:green:service"
+		;;
+	"nb4-ser")
+		status_led="NB4-SER-r0:green:service"
+		;;
+	"vg-8050")
+		status_led="VG-8050:green:power"
+		;;
+	"vr-3025u")
+		status_led="VR-3025u:green:power"
+		;;
+	"vr-3025un")
+		status_led="VR-3025un:green:power"
+		;;
+	"vr-3032u")
+		status_led="VR-3032u:green:power"
+		;;
+	"wap-5813n")
+		status_led="WAP-5813n:green:power"
+		;;
+	esac
+}
+
+set_state() {
+	get_status_led
+
+	case "$1" in
+	preinit)
+		status_led_blink_preinit
+		;;
+	failsafe)
+		status_led_blink_failsafe
+		;;
+	preinit_regular)
+		status_led_blink_preinit_regular
+		;;
+	done)
+		status_led_on
+		;;
+	esac
+}
diff --git a/target/linux/bmips/base-files/etc/uci-defaults/02_network b/target/linux/bmips/base-files/etc/uci-defaults/02_network
new file mode 100644
index 0000000..956f355
--- /dev/null
+++ b/target/linux/bmips/base-files/etc/uci-defaults/02_network
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+
+[ -e /etc/config/network ] && exit 0
+
+touch /etc/config/network
+
+. /lib/functions/uci-defaults.sh
+
+ucidef_set_interface_loopback
+
+uci commit network
+
+exit 0
diff --git a/target/linux/bmips/base-files/lib/bmips.sh b/target/linux/bmips/base-files/lib/bmips.sh
new file mode 100644
index 0000000..ce23e66
--- /dev/null
+++ b/target/linux/bmips/base-files/lib/bmips.sh
@@ -0,0 +1,76 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+
+BMIPS_BOARD_NAME=
+BMIPS_MODEL=
+
+bmips_board_detect() {
+	local machine
+	local name
+
+	machine=$(awk 'BEGIN{FS="[ \t]+:[ \t]"} /machine/ {print $2}' /proc/cpuinfo)
+
+	case "$machine" in
+	"Comtrend AR-5381u")
+		name="ar-5381u"
+		;;
+	"Comtrend AR-5387un")
+		name="ar-5387un"
+		;;
+	"Comtrend VG-8050")
+		name="vg-8050"
+		;;
+	"Comtrend VR-3025u")
+		name="vr-3025u"
+		;;
+	"Comtrend VR-3025un")
+		name="vr-3025un"
+		;;
+	"Comtrend VR-3032u")
+		name="vr-3032u"
+		;;
+	"Comtrend WAP-5813n")
+		name="wap-5813n"
+		;;
+	"Huawei EchoLife HG520v")
+		name="hg520v"
+		;;
+	"Huawei EchoLife HG556a (version A)")
+		name="hg556a-a"
+		;;
+	"Huawei EchoLife HG556a (version B)")
+		name="hg556a-b"
+		;;
+	"Huawei EchoLife HG556a (version C)")
+		name="hg556a-c"
+		;;
+	"SFR Neuf Box 4 (Foxconn)")
+		name="nb4-fxc"
+		;;
+	"SFR Neuf Box 4 (Sercomm)")
+		name="nb4-ser"
+		;;
+	*)
+		name="generic"
+		;;
+	esac
+
+	[ -z "$BMIPS_BOARD_NAME" ] && BMIPS_BOARD_NAME="$name"
+	[ -z "$BMIPS_MODEL" ] && BMIPS_MODEL="$machine"
+
+	[ -e "/tmp/sysinfo/" ] || mkdir -p "/tmp/sysinfo/"
+
+	echo "$BMIPS_BOARD_NAME" > /tmp/sysinfo/board_name
+	echo "$BMIPS_MODEL" > /tmp/sysinfo/model
+}
+
+bmips_board_name() {
+	local name
+
+	[ -f /tmp/sysinfo/board_name ] && name=$(cat /tmp/sysinfo/board_name)
+	[ -z "$name" ] && name="unknown"
+
+	echo "$name"
+}
diff --git a/target/linux/bmips/base-files/lib/preinit/03_preinit_do_bmips.sh b/target/linux/bmips/base-files/lib/preinit/03_preinit_do_bmips.sh
new file mode 100644
index 0000000..3422fa6
--- /dev/null
+++ b/target/linux/bmips/base-files/lib/preinit/03_preinit_do_bmips.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+
+do_bmips() {
+	. /lib/bmips.sh
+
+	bmips_board_detect
+}
+
+boot_hook_add preinit_main do_bmips
diff --git a/target/linux/bmips/config-4.1 b/target/linux/bmips/config-4.1
new file mode 100644
index 0000000..0cfb84a
--- /dev/null
+++ b/target/linux/bmips/config-4.1
@@ -0,0 +1,222 @@
+CONFIG_ARCH_BINFMT_ELF_STATE=y
+CONFIG_ARCH_DISCARD_MEMBLOCK=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+# CONFIG_ARCH_HAS_GCOV_PROFILE_ALL is not set
+# CONFIG_ARCH_HAS_SG_CHAIN is not set
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_BCM6345_EXT_IRQ=y
+CONFIG_BCM6345_PERIPH_IRQ=y
+CONFIG_BCM7038_L1_IRQ=y
+CONFIG_BCM7120_L2_IRQ=y
+CONFIG_BMIPS_GENERIC=y
+CONFIG_BOARD_SCACHE=y
+CONFIG_BRCMSTB_L2_IRQ=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_CEVT_R4K=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_CMDLINE="earlycon noinitrd"
+CONFIG_CMDLINE_BOOL=y
+# CONFIG_CMDLINE_OVERRIDE is not set
+CONFIG_COMMON_CLK=y
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_CPU_BMIPS=y
+CONFIG_CPU_BMIPS32_3300=y
+CONFIG_CPU_BMIPS4350=y
+CONFIG_CPU_BMIPS4380=y
+CONFIG_CPU_BMIPS5000=y
+CONFIG_CPU_GENERIC_DUMP_TLB=y
+CONFIG_CPU_HAS_PREFETCH=y
+CONFIG_CPU_HAS_SYNC=y
+# CONFIG_CPU_LITTLE_ENDIAN is not set
+CONFIG_CPU_MIPS32=y
+CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
+CONFIG_CPU_R4K_CACHE_TLB=y
+CONFIG_CPU_R4K_FPU=y
+CONFIG_CPU_RMAP=y
+CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
+CONFIG_CPU_SUPPORTS_HIGHMEM=y
+CONFIG_CRASH_DUMP=y
+CONFIG_CSRC_R4K=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DMA_NONCOHERENT=y
+CONFIG_DTC=y
+# CONFIG_DT_BCM93384WVG is not set
+# CONFIG_DT_BCM93384WVG_VIPER is not set
+# CONFIG_DT_BCM96368MVWG is not set
+# CONFIG_DT_BCM97125CBMB is not set
+# CONFIG_DT_BCM97346DBSMB is not set
+# CONFIG_DT_BCM97358SVMB is not set
+# CONFIG_DT_BCM97360SVMB is not set
+# CONFIG_DT_BCM97362SVMB is not set
+# CONFIG_DT_BCM97420C is not set
+# CONFIG_DT_BCM97425SVMB is not set
+# CONFIG_DT_BCM97435SVMB is not set
+# CONFIG_DT_BCM9EJTAGPRB is not set
+CONFIG_DT_NONE=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_GENERIC_ATOMIC64=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_BCM63XX=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_GPIO_GENERIC=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+# CONFIG_HAVE_ARCH_BITREVERSE is not set
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_BPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HZ=250
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+CONFIG_HZ_PERIODIC=y
+CONFIG_IMAGE_CMDLINE_HACK=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_CPU=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_LEDS_BCM6328=y
+CONFIG_LEDS_BCM6358=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LIBFDT=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MIPS=y
+CONFIG_MIPS_CPU_SCACHE=y
+# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set
+CONFIG_MIPS_L1_CACHE_SHIFT=7
+CONFIG_MIPS_L1_CACHE_SHIFT_6=y
+CONFIG_MIPS_L1_CACHE_SHIFT_7=y
+# CONFIG_MIPS_MACHINE is not set
+# CONFIG_MIPS_NO_APPENDED_DTB is not set
+CONFIG_MIPS_O32_FP64_SUPPORT=y
+CONFIG_MIPS_RAW_APPENDED_DTB=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_BE_BYTE_SWAP=y
+# CONFIG_MTD_CFI_GEOMETRY is not set
+# CONFIG_MTD_CFI_NOSWAP is not set
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BRCMNAND=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NO_EXCEPT_FILL=y
+# CONFIG_NO_IOPORT_MAP is not set
+CONFIG_NR_CPUS=2
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+CONFIG_OF_NET=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PGTABLE_LEVELS=2
+CONFIG_PHYLIB=y
+CONFIG_PHYSICAL_START=0x80010000
+CONFIG_POSIX_MQUEUE=y
+CONFIG_POSIX_MQUEUE_SYSCTL=y
+CONFIG_PROC_VMCORE=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_RELAY=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCSI_DMA is not set
+CONFIG_SERIAL_BCM63XX=y
+CONFIG_SERIAL_BCM63XX_CONSOLE=y
+CONFIG_SMP=y
+CONFIG_SMP_UP=y
+CONFIG_SPI=y
+CONFIG_SPI_BITBANG=y
+CONFIG_SPI_GPIO=y
+CONFIG_SPI_MASTER=y
+CONFIG_SQUASHFS_EMBEDDED=y
+CONFIG_SRCU=y
+CONFIG_STOP_MACHINE=y
+CONFIG_SWAP_IO_SPACE=y
+CONFIG_SWCONFIG=y
+CONFIG_SYNC_R4K=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_SYS_HAS_CPU_BMIPS=y
+CONFIG_SYS_HAS_CPU_BMIPS32_3300=y
+CONFIG_SYS_HAS_CPU_BMIPS4350=y
+CONFIG_SYS_HAS_CPU_BMIPS4380=y
+CONFIG_SYS_HAS_CPU_BMIPS5000=y
+CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
+CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
+CONFIG_SYS_SUPPORTS_BIG_ENDIAN=y
+CONFIG_SYS_SUPPORTS_HIGHMEM=y
+CONFIG_SYS_SUPPORTS_HOTPLUG_CPU=y
+CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
+CONFIG_SYS_SUPPORTS_SMP=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TREE_RCU=y
+CONFIG_USB_EHCI_BIG_ENDIAN_DESC=y
+CONFIG_USB_EHCI_BIG_ENDIAN_MMIO=y
+CONFIG_USB_OHCI_BIG_ENDIAN_DESC=y
+CONFIG_USB_OHCI_BIG_ENDIAN_MMIO=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USE_OF=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+CONFIG_WEAK_ORDERING=y
+CONFIG_XPS=y
+CONFIG_ZONE_DMA_FLAG=0
diff --git a/target/linux/bmips/dts/ar-5381u.dts b/target/linux/bmips/dts/ar-5381u.dts
new file mode 100644
index 0000000..9d72109
--- /dev/null
+++ b/target/linux/bmips/dts/ar-5381u.dts
@@ -0,0 +1,59 @@
+/dts-v1/;
+
+#include "bcm6328.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,ar-5381u", "brcm,bcm6328";
+	model = "Comtrend AR-5381u";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio0 23 1>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+};
+
+&leds0 {
+	status = "ok";
+
+	alarm_red at 2 {
+		reg = <2>;
+		active-low;
+		label = "AR-5381u:red:alarm";
+	};
+	inet_green at 3 {
+		reg = <3>;
+		active-low;
+		label = "AR-5381u:green:inet";
+	};
+	power_green at 4 {
+		reg = <4>;
+		active-low;
+		label = "AR-5381u:green:power";
+		default-state = "on";
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/ar-5387un.dts b/target/linux/bmips/dts/ar-5387un.dts
new file mode 100644
index 0000000..c421996
--- /dev/null
+++ b/target/linux/bmips/dts/ar-5387un.dts
@@ -0,0 +1,65 @@
+/dts-v1/;
+
+#include "bcm6328.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,ar-5387un", "brcm,bcm6328";
+	model = "Comtrend AR-5387un";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio0 23 1>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+};
+
+&leds0 {
+	status = "ok";
+
+	inet_red at 1 {
+		reg = <1>;
+		label = "AR-5387un:red:inet";
+	};
+	power_red at 4 {
+		reg = <4>;
+		label = "AR-5387un:red:power";
+	};
+	inet_green at 7 {
+		reg = <7>;
+		label = "AR-5387un:green:inet";
+	};
+	power_green at 8 {
+		reg = <8>;
+		label = "AR-5387un:green:power";
+		default-state = "on";
+	};
+	dsl_green at 11 {
+		reg = <11>;
+		active-low;
+		label = "AR-5387un:green:dsl";
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/bcm3368.dtsi b/target/linux/bmips/dts/bcm3368.dtsi
new file mode 100644
index 0000000..fc1b76e
--- /dev/null
+++ b/target/linux/bmips/dts/bcm3368.dtsi
@@ -0,0 +1,130 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm3368";
+
+	aliases {
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		pflash = &pflash;
+		uart0 = &uart0;
+		uart1 = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <150000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+
+		cpu at 1 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <1>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	pflash: nor at 1e000000 {
+		compatible = "cfi-flash";
+		reg = <0x1e000000 0x2000000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		status = "disabled";
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		periph_intc: interrupt-controller at fff8c00c {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0xfffe000c 0x8>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>;
+		};
+
+		ext_intc0: interrupt-controller at fff8c014 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0xfffe0014 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <24>, <25>, <26>, <27>;
+		};
+
+		gpio1: gpio-controller at fff8c080 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfff8c080 4>, <0xfff8c088 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <8>;
+		};
+
+		gpio0: gpio-controller at fff8c084 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfff8c084 4>, <0xfff8c08c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		uart0: serial at fff8c100 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfff8c100 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <2>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		uart1: serial at fff8c120 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfff8c120 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <3>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6318.dtsi b/target/linux/bmips/dts/bcm6318.dtsi
new file mode 100644
index 0000000..9fdd3a5
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6318.dtsi
@@ -0,0 +1,137 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6318";
+
+	aliases {
+		ehci0 = &ehci0;
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		leds0 = &leds0;
+		ohci0 = &ohci0;
+		uart0 = &uart0;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <160000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips3300", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		ext_intc: interrupt-controller at 10000018 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0x10000018 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <24>, <25>, <26>, <27>;
+		};
+
+		periph_intc: interrupt-controller at 10000020 {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0x10000020 0x20>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>;
+		};
+
+		gpio1: gpio-controller at 10000080 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000080 4>, <0x10000088 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <18>;
+		};
+
+		gpio0: gpio-controller at 10000084 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000084 4>, <0x1000008c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		uart0: serial at 10000100 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000100 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <28>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		leds0: led-controller at 10000200 {
+			compatible = "brcm,bcm6328-leds";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x10000200 0x24>;
+
+			status = "disabled";
+		};
+
+		ehci0: usb at 10005000 {
+			compatible = "brcm,bcm6328-ehci", "generic-ehci";
+			reg = <0x10005000 0x100>;
+			num-ports = <1>;
+			big-endian;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <42>;
+
+			status = "disabled";
+		};
+
+		ohci0: usb at 10005100 {
+			compatible = "brcm,bcm6328-ohci", "generic-ohci";
+			reg = <0x10005100 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <41>;
+
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm63268.dtsi b/target/linux/bmips/dts/bcm63268.dtsi
new file mode 100644
index 0000000..ebc4cd2
--- /dev/null
+++ b/target/linux/bmips/dts/bcm63268.dtsi
@@ -0,0 +1,159 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm63268";
+
+	aliases {
+		ehci0 = &ehci0;
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		leds0 = &leds0;
+		nflash = &nflash;
+		ohci0 = &ohci0;
+		uart0 = &uart0;
+		uart1 = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <200000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+
+		cpu at 1 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <1>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		ext_intc: interrupt-controller at 10000018 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0x10000018 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <44>, <45>, <46>, <47>;
+		};
+
+		periph_intc: interrupt-controller at 10000020 {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0x10000020 0x20>,
+			      <0x10000040 0x20>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>, <3>;
+		};
+
+		gpio1: gpio-controller at 100000c0 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x100000c0 4>, <0x100000c8 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <20>;
+		};
+
+		gpio0: gpio-controller at 100000c4 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x100000c4 4>, <0x100000cc 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		uart0: serial at 10000180 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000180 0x18>;
+			interrupt-parent = <&periph_intc>;
+			interrupts = <5>;
+			clocks = <&periph_clk>;
+			status = "disabled";
+		};
+
+		uart1: serial at 100001a0 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x100001a0 0x18>;
+			interrupt-parent = <&periph_intc>;
+			interrupts = <34>;
+			clocks = <&periph_clk>;
+			status = "disabled";
+		};
+
+		nflash: nand at 10000200 {
+			compatible = "brcm,brcmnand-v4.0", "brcm,brcmnand";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x10000200 0x400>, <0x10000600 0x200>;
+			reg-names = "nand", "nand-cache";
+			brcm,cmd-poll;
+
+			status = "disabled";
+		};
+
+		leds0: led-controller at 10001900 {
+			compatible = "brcm,bcm6328-leds";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x10001900 0x24>;
+
+			status = "disabled";
+		};
+
+		ehci0: usb at 10002500 {
+			compatible = "brcm,bcm63268-ehci", "generic-ehci";
+			reg = <0x10002500 0x100>;
+			num-ports = <1>;
+			big-endian;
+			interrupt-parent = <&periph_intc>;
+			interrupts = <10>;
+			status = "disabled";
+		};
+
+		ohci0: usb at 10002600 {
+			compatible = "brcm,bcm63268-ohci", "generic-ohci";
+			reg = <0x10002600 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+			interrupt-parent = <&periph_intc>;
+			interrupts = <9>;
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6328.dtsi b/target/linux/bmips/dts/bcm6328.dtsi
new file mode 100644
index 0000000..fd46877
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6328.dtsi
@@ -0,0 +1,158 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6328";
+
+	aliases {
+		ehci0 = &ehci0;
+		gpio0 = &gpio0;
+		leds0 = &leds0;
+		ohci0 = &ohci0;
+		uart0 = &uart0;
+		uart1 = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <160000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+
+		cpu at 1 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <1>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		ext_intc: interrupt-controller at 10000018 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0x10000018 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <24>, <25>, <26>, <27>;
+		};
+
+		periph_intc: interrupt-controller at 10000020 {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0x10000020 0x10>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>;
+		};
+
+		timer: timer at 10000040 {
+			compatible = "syscon";
+			reg = <0x10000040 0x2c>;
+			little-endian;
+		};
+
+		reboot {
+			compatible = "syscon-reboot";
+			regmap = <&timer>;
+			offset = <0x28>;
+			mask = <0x1>;
+		};
+
+		gpio0: gpio-controller at 10000084 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000084 4>, <0x1000008c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		uart0: serial at 10000100 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000100 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <28>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		uart1: serial at 10000120 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000120 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <39>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		leds0: led-controller at 10000800 {
+			compatible = "brcm,bcm6328-leds";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x10000800 0x24>;
+
+			status = "disabled";
+		};
+
+		ehci0: usb at 10002500 {
+			compatible = "brcm,bcm6328-ehci", "generic-ehci";
+			reg = <0x10002500 0x100>;
+			num-ports = <1>;
+			big-endian;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <42>;
+
+			status = "disabled";
+		};
+
+		ohci0: usb at 10002600 {
+			compatible = "brcm,bcm6328-ohci", "generic-ohci";
+			reg = <0x10002600 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <41>;
+
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6338.dtsi b/target/linux/bmips/dts/bcm6338.dtsi
new file mode 100644
index 0000000..be2c3dd
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6338.dtsi
@@ -0,0 +1,102 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6338";
+
+	aliases {
+		gpio0 = &gpio0;
+		pflash = &pflash;
+		uart0 = &uart0;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <120000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips3300", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	pflash: nor at 1fc00000 {
+		compatible = "cfi-flash";
+		reg = <0x1fc00000 0x400000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		status = "disabled";
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		periph_intc: interrupt-controller at fffe000c {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0xfffe000c 0x8>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>;
+		};
+
+		ext_intc: interrupt-controller at fffe0014 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0xfffe0014 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <3>, <4>, <5>, <6>;
+		};
+
+		uart0: serial at fffe0300 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfffe0300 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <2>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		gpio0: gpio-controller at fffe0404 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfffe0404 4>, <0xfffe040c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <8>;
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6345.dtsi b/target/linux/bmips/dts/bcm6345.dtsi
new file mode 100644
index 0000000..2d97b77
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6345.dtsi
@@ -0,0 +1,102 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6345";
+
+	aliases {
+		gpio0 = &gpio0;
+		pflash = &pflash;
+		uart0 = &uart0;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <70000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips32", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	pflash: nor at 1fc00000 {
+		compatible = "cfi-flash";
+		reg = <0x1fc00000 0x400000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		status = "disabled";
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		periph_intc: interrupt-controller at fffe000c {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0xfffe000c 0x9>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>;
+		};
+
+		ext_intc: interrupt-controller at fffe0014 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0xfffe0014 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <3>, <4>, <5>, <6>;
+		};
+
+		uart0: serial at fffe0300 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfffe0300 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <2>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		gpio0: gpio-controller at fffe0404 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfffe0404 4>, <0xfffe0408 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <16>;
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6348.dtsi b/target/linux/bmips/dts/bcm6348.dtsi
new file mode 100644
index 0000000..82ac37a
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6348.dtsi
@@ -0,0 +1,127 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6348";
+
+	aliases {
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		ohci0 = &ohci0;
+		pflash = &pflash;
+		uart0 = &uart0;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <120000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips3300", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	pflash: nor at 1fc00000 {
+		compatible = "cfi-flash";
+		reg = <0x1fc00000 0x400000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		status = "disabled";
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		periph_intc: interrupt-controller at fffe000c {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0xfffe000c 0x8>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>;
+		};
+
+		ext_intc: interrupt-controller at fffe0014 {
+			compatible = "brcm,bcm6348-ext-intc";
+			reg = <0xfffe0014 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <3>, <4>, <5>, <6>;
+
+			brcm,field-width = <5>;
+		};
+
+		uart0: serial at fffe0300 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfffe0300 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <2>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		gpio1: gpio-controller at fffe0400 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfffe0400 4>, <0xfffe0408 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <5>;
+		};
+
+		gpio0: gpio-controller at fffe0404 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfffe0404 4>, <0xfffe040c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		ohci0: usb at fffe1b00 {
+			compatible = "brcm,bcm6348-ohci", "generic-ohci";
+			reg = <0xfffe1b00 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <12>;
+
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6358.dtsi b/target/linux/bmips/dts/bcm6358.dtsi
new file mode 100644
index 0000000..452ef35
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6358.dtsi
@@ -0,0 +1,179 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6358";
+
+	aliases {
+		ehci0 = &ehci0;
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		les0 = &leds0;
+		ohci0 = &ohci0;
+		pflash = &pflash;
+		uart0 = &uart0;
+		uart1 = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <150000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+
+		cpu at 1 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <1>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	pflash: nor at 1e000000 {
+		compatible = "cfi-flash";
+		reg = <0x1e000000 0x2000000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		status = "disabled";
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		periph_intc: interrupt-controller at fffe000c {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0xfffe000c 0x8>,
+			      <0xfffe0038 0x8>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>, <3>;
+		};
+
+		ext_intc0: interrupt-controller at fffe0014 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0xfffe0014 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <25>, <26>, <27>, <28>;
+		};
+
+		ext_intc1: interrupt-controller at fffe001c {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0xfffe001c 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <20>, <21>;
+		};
+
+		gpio1: gpio-controller at fffe0080 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfffe0080 4>, <0xfffe0088 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <8>;
+		};
+
+		gpio0: gpio-controller at fffe0084 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0xfffe0084 4>, <0xfffe008c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		leds0: led-controller at fffe00d0 {
+			compatible = "brcm,bcm6358-leds";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0xfffe00d0 0x8>;
+
+			status = "disabled";
+		};
+
+		uart0: serial at fffe0100 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfffe0100 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <2>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		uart1: serial at fffe0120 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0xfffe0120 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <3>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		ehci0: usb at fffe1300 {
+			compatible = "brcm,bcm6358-ehci", "generic-ehci";
+			reg = <0xfffe1300 0x100>;
+			num-ports = <1>;
+			big-endian;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <10>;
+
+			status = "disabled";
+		};
+
+		ohci0: usb at fffe1400 {
+			compatible = "brcm,bcm6358-ohci", "generic-ohci";
+			reg = <0xfffe1400 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <5>;
+
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6362.dtsi b/target/linux/bmips/dts/bcm6362.dtsi
new file mode 100644
index 0000000..0b0e8ec
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6362.dtsi
@@ -0,0 +1,157 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6362";
+
+	aliases {
+		ehci0 = &ehci0;
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		leds0 = &leds0;
+		ohci0 = &ohci0;
+		uart0 = &uart0;
+		uart1 = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <200000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+
+		cpu at 1 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <1>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		ext_intc: interrupt-controller at 10000018 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0x10000018 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <40>, <41>, <42>, <43>;
+		};
+
+		periph_intc: interrupt-controller at 10000020 {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0x10000020 0x10>,
+			      <0x10000030 0x10>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>, <3>;
+		};
+
+		gpio1: gpio-controller at 10000080 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000080 4>, <0x10000088 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <16>;
+		};
+
+		gpio0: gpio-controller at 10000084 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000084 4>, <0x1000008c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		uart0: serial at 10000100 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000100 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <3>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		uart1: serial at 10000120 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000120 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <4>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		leds0: led-controller at 10001900 {
+			compatible = "brcm,bcm6328-leds";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x10001900 0x24>;
+
+			status = "disabled";
+		};
+
+		ehci0: usb at 10002500 {
+			compatible = "brcm,bcm6362-ehci", "generic-ehci";
+			reg = <0x10002500 0x100>;
+			num-ports = <1>;
+			big-endian;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <10>;
+
+			status = "disabled";
+		};
+
+		ohci0: usb at 10002600 {
+			compatible = "brcm,bcm6362-ohci", "generic-ohci";
+			reg = <0x10002600 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <9>;
+
+			status = "disabled";
+		};
+	};
+};
diff --git a/target/linux/bmips/dts/bcm6368.dtsi b/target/linux/bmips/dts/bcm6368.dtsi
new file mode 100644
index 0000000..35ab8ad
--- /dev/null
+++ b/target/linux/bmips/dts/bcm6368.dtsi
@@ -0,0 +1,179 @@
+/ {
+	#address-cells = <1>;
+	#size-cells = <1>;
+	compatible = "brcm,bcm6368";
+
+	aliases {
+		ehci0 = &ehci0;
+		gpio0 = &gpio0;
+		gpio1 = &gpio1;
+		leds0 = &leds0;
+		ohci0 = &ohci0;
+		pflash = &pflash;
+		uart0 = &uart0;
+		uart1 = &uart1;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		mips-hpt-frequency = <200000000>;
+
+		cpu at 0 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <0>;
+		};
+
+		cpu at 1 {
+			compatible = "brcm,bmips4350", "mips,mips4Kc";
+			device_type = "cpu";
+			reg = <1>;
+		};
+	};
+
+	clocks {
+		periph_clk: periph_clk {
+			compatible = "fixed-clock";
+			#clock-cells = <0>;
+			clock-frequency = <50000000>;
+		};
+	};
+
+	cpu_intc: cpu_intc {
+		#address-cells = <0>;
+		compatible = "mti,cpu-interrupt-controller";
+
+		interrupt-controller;
+		#interrupt-cells = <1>;
+	};
+
+	ubus {
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		compatible = "simple-bus";
+		ranges;
+
+		ext_intc0: interrupt-controller at 10000018 {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0x10000018 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <20>, <21>, <22>, <23>;
+		};
+
+		ext_intc1: interrupt-controller at 1000001c {
+			compatible = "brcm,bcm6345-ext-intc";
+			reg = <0x1000001c 0x4>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <24>, <25>;
+		};
+
+		periph_intc: interrupt-controller at 10000020 {
+			compatible = "brcm,bcm6345-periph-intc";
+			reg = <0x10000020 0x10>,
+			      <0x10000030 0x10>;
+
+			interrupt-controller;
+			#interrupt-cells = <1>;
+
+			interrupt-parent = <&cpu_intc>;
+			interrupts = <2>, <3>;
+		};
+
+		gpio1: gpio-controller at 10000080 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000080 4>, <0x10000088 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+
+			ngpios = <6>;
+		};
+
+		gpio0: gpio-controller at 10000084 {
+			compatible = "brcm,bcm6345-gpio";
+			reg = <0x10000084 4>, <0x1000008c 4>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+		};
+
+		leds0: led-controller at 100000d0 {
+			compatible = "brcm,bcm6358-leds";
+			#address-cells = <1>;
+			#size-cells = <0>;
+			reg = <0x100000d0 0x8>;
+
+			status = "disabled";
+		};
+
+		uart0: serial at 10000100 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000100 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <2>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		uart1: serial at 10000120 {
+			compatible = "brcm,bcm6345-uart";
+			reg = <0x10000120 0x18>;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <3>;
+
+			clocks = <&periph_clk>;
+
+			status = "disabled";
+		};
+
+		ehci0: usb at 10001500 {
+			compatible = "brcm,bcm6368-ehci", "generic-ehci";
+			reg = <0x10001500 0x100>;
+			num-ports = <1>;
+			big-endian;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <7>;
+
+			status = "disabled";
+		};
+
+		ohci0: usb at 10001600 {
+			compatible = "brcm,bcm6368-ohci", "generic-ohci";
+			reg = <0x10001600 0x100>;
+			num-ports = <1>;
+			big-endian;
+			no-big-frame-no;
+
+			interrupt-parent = <&periph_intc>;
+			interrupts = <5>;
+
+			status = "disabled";
+		};
+	};
+
+	pflash: nor at 18000000 {
+		compatible = "cfi-flash";
+		reg = <0x18000000 0x2000000>;
+		bank-width = <2>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		status = "disabled";
+	};
+};
diff --git a/target/linux/bmips/dts/fast1704.dts b/target/linux/bmips/dts/fast1704.dts
new file mode 100644
index 0000000..9d56e50
--- /dev/null
+++ b/target/linux/bmips/dts/fast1704.dts
@@ -0,0 +1,24 @@
+/dts-v1/;
+
+#include "bcm6338.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "sagem,f at st1704", "brcm,bcm6338";
+	model = "Sagem F at ST1704";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x01000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/hg520v.dts b/target/linux/bmips/dts/hg520v.dts
new file mode 100644
index 0000000..f6c6630
--- /dev/null
+++ b/target/linux/bmips/dts/hg520v.dts
@@ -0,0 +1,67 @@
+/dts-v1/;
+
+#include "bcm6358.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "huawei,hg520v", "brcm,bcm6358";
+	model = "Huawei EchoLife HG520v";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio1 5 1>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		inet_green {
+			label = "HG520v:green:net";
+			gpios = <&gpio1 0 1>;
+		};
+	};
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "CFE";
+		reg = <0x000000 0x010000>;
+		read-only;
+	};
+
+	linux at 10000 {
+		label = "linux";
+		reg = <0x010000 0x3e0000>;
+	};
+
+	nvram at 3f0000 {
+		label = "nvram";
+		reg = <0x3f0000 0x010000>;
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/hg556a-a.dts b/target/linux/bmips/dts/hg556a-a.dts
new file mode 100644
index 0000000..cf6b6b3
--- /dev/null
+++ b/target/linux/bmips/dts/hg556a-a.dts
@@ -0,0 +1,138 @@
+/dts-v1/;
+
+#include "bcm6358.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "huawei,hg556a-a", "brcm,bcm6358";
+	model = "Huawei EchoLife HG556a (version A)";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		help {
+			label = "help";
+			gpios = <&gpio0 8 1>;
+			linux,code = <KEY_HELP>;
+		};
+		wlan {
+			label = "wlan";
+			gpios = <&gpio0 9 1>;
+			linux,code = <KEY_WLAN>;
+		};
+		restart {
+			label = "restart";
+			gpios = <&gpio0 10 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		reset {
+			label = "reset";
+			gpios = <&gpio0 11 1>;
+			linux,code = <KEY_CONFIG>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		message_red {
+			label = "HG556a:red:message";
+			gpios = <&gpio0 0 1>;
+		};
+		hspa_red {
+			label = "HG556a:red:hspa";
+			gpios = <&gpio0 1 1>;
+		};
+		dsl_red {
+			label = "HG556a:red:dsl";
+			gpios = <&gpio0 2 1>;
+		};
+		power_red {
+			label = "HG556a:red:power";
+			gpios = <&gpio0 3 1>;
+			default-state = "on";
+		};
+		all_red {
+			label = "HG556a:red:all";
+			gpios = <&gpio0 6 1>;
+			default-state = "on";
+		};
+		lan1_green {
+			label = "HG556a:green:lan1";
+			gpios = <&gpio0 12 1>;
+		};
+		lan1_red {
+			label = "HG556a:red:lan1";
+			gpios = <&gpio0 13 1>;
+		};
+		lan2_green {
+			label = "HG556a:green:lan2";
+			gpios = <&gpio0 15 1>;
+		};
+		lan2_red {
+			label = "HG556a:red:lan2";
+			gpios = <&gpio0 22 1>;
+		};
+		lan3_green {
+			label = "HG556a:green:lan3";
+			gpios = <&gpio0 23 1>;
+		};
+		lan3_red {
+			label = "HG556a:red:lan3";
+			gpios = <&gpio0 26 1>;
+		};
+		lan4_green {
+			label = "HG556a:green:lan4";
+			gpios = <&gpio0 27 1>;
+		};
+		lan4_red {
+			label = "HG556a:red:lan4";
+			gpios = <&gpio0 28 1>;
+		};
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "CFE";
+		reg = <0x000000 0x020000>;
+		read-only;
+	};
+
+	linux at 20000 {
+		label = "linux";
+		reg = <0x020000 0xec0000>;
+	};
+
+	cal_data at ee0000 {
+		label = "cal_data";
+		reg = <0xee0000 0x100000>;
+		read-only;
+	};
+
+	nvram at fe0000 {
+		label = "nvram";
+		reg = <0xfe0000 0x020000>;
+	};
+};
diff --git a/target/linux/bmips/dts/hg556a-b.dts b/target/linux/bmips/dts/hg556a-b.dts
new file mode 100644
index 0000000..0b7ac55
--- /dev/null
+++ b/target/linux/bmips/dts/hg556a-b.dts
@@ -0,0 +1,138 @@
+/dts-v1/;
+
+#include "bcm6358.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "huawei,hg556a-b", "brcm,bcm6358";
+	model = "Huawei EchoLife HG556a (version B)";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		help {
+			label = "help";
+			gpios = <&gpio0 8 1>;
+			linux,code = <KEY_HELP>;
+		};
+		wlan {
+			label = "wlan";
+			gpios = <&gpio0 9 1>;
+			linux,code = <KEY_WLAN>;
+		};
+		restart {
+			label = "restart";
+			gpios = <&gpio0 10 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		reset {
+			label = "reset";
+			gpios = <&gpio0 11 1>;
+			linux,code = <KEY_CONFIG>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		message_red {
+			label = "HG556a:red:message";
+			gpios = <&gpio0 0 1>;
+		};
+		hspa_red {
+			label = "HG556a:red:hspa";
+			gpios = <&gpio0 1 1>;
+		};
+		dsl_red {
+			label = "HG556a:red:dsl";
+			gpios = <&gpio0 2 1>;
+		};
+		power_red {
+			label = "HG556a:red:power";
+			gpios = <&gpio0 3 1>;
+			default-state = "on";
+		};
+		all_red {
+			label = "HG556a:red:all";
+			gpios = <&gpio0 6 1>;
+			default-state = "on";
+		};
+		lan1_green {
+			label = "HG556a:green:lan1";
+			gpios = <&gpio0 12 1>;
+		};
+		lan1_red {
+			label = "HG556a:red:lan1";
+			gpios = <&gpio0 13 1>;
+		};
+		lan2_green {
+			label = "HG556a:green:lan2";
+			gpios = <&gpio0 15 1>;
+		};
+		lan2_red {
+			label = "HG556a:red:lan2";
+			gpios = <&gpio0 22 1>;
+		};
+		lan3_green {
+			label = "HG556a:green:lan3";
+			gpios = <&gpio0 23 1>;
+		};
+		lan3_red {
+			label = "HG556a:red:lan3";
+			gpios = <&gpio0 26 1>;
+		};
+		lan4_green {
+			label = "HG556a:green:lan4";
+			gpios = <&gpio0 27 1>;
+		};
+		lan4_red {
+			label = "HG556a:red:lan4";
+			gpios = <&gpio0 28 1>;
+		};
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "CFE";
+		reg = <0x000000 0x020000>;
+		read-only;
+	};
+
+	linux at 20000 {
+		label = "linux";
+		reg = <0x020000 0xec0000>;
+	};
+
+	cal_data at ee0000 {
+		label = "cal_data";
+		reg = <0xee0000 0x100000>;
+		read-only;
+	};
+
+	nvram at fe0000 {
+		label = "nvram";
+		reg = <0xfe0000 0x020000>;
+	};
+};
diff --git a/target/linux/bmips/dts/hg556a-c.dts b/target/linux/bmips/dts/hg556a-c.dts
new file mode 100644
index 0000000..1ad5a84
--- /dev/null
+++ b/target/linux/bmips/dts/hg556a-c.dts
@@ -0,0 +1,133 @@
+/dts-v1/;
+
+#include "bcm6358.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "huawei,hg556a-c", "brcm,bcm6358";
+	model = "Huawei EchoLife HG556a (version C)";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		help {
+			label = "help";
+			gpios = <&gpio0 8 1>;
+			linux,code = <KEY_HELP>;
+		};
+		wlan {
+			label = "wlan";
+			gpios = <&gpio0 9 1>;
+			linux,code = <KEY_WLAN>;
+		};
+		restart {
+			label = "restart";
+			gpios = <&gpio0 10 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		reset {
+			label = "reset";
+			gpios = <&gpio0 11 1>;
+			linux,code = <KEY_CONFIG>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		lan1_green {
+			label = "HG556a:green:lan1";
+			gpios = <&gpio0 0 1>;
+		};
+		lan2_green {
+			label = "HG556a:green:lan2";
+			gpios = <&gpio0 1 1>;
+		};
+		dsl_red {
+			label = "HG556a:red:dsl";
+			gpios = <&gpio0 2 1>;
+		};
+		power_red {
+			label = "HG556a:red:power";
+			gpios = <&gpio0 3 1>;
+			default-state = "on";
+		};
+		message_red {
+			label = "HG556a:red:message";
+			gpios = <&gpio0 12 1>;
+		};
+		lan1_red {
+			label = "HG556a:red:lan1";
+			gpios = <&gpio0 13 1>;
+		};
+		hspa_red {
+			label = "HG556a:red:hspa";
+			gpios = <&gpio0 15 1>;
+		};
+		lan2_red {
+			label = "HG556a:red:lan2";
+			gpios = <&gpio0 22 1>;
+		};
+		lan3_green {
+			label = "HG556a:green:lan3";
+			gpios = <&gpio0 23 1>;
+		};
+		lan3_red {
+			label = "HG556a:red:lan3";
+			gpios = <&gpio0 26 1>;
+		};
+		lan4_green {
+			label = "HG556a:green:lan4";
+			gpios = <&gpio0 27 1>;
+		};
+		lan4_red {
+			label = "HG556a:red:lan4";
+			gpios = <&gpio0 28 1>;
+		};
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "CFE";
+		reg = <0x000000 0x020000>;
+		read-only;
+	};
+
+	linux at 20000 {
+		label = "linux";
+		reg = <0x020000 0xec0000>;
+	};
+
+	cal_data at ee0000 {
+		label = "cal_data";
+		reg = <0xee0000 0x100000>;
+		read-only;
+	};
+
+	nvram at fe0000 {
+		label = "nvram";
+		reg = <0xfe0000 0x020000>;
+	};
+};
diff --git a/target/linux/bmips/dts/nb4-fxc-r1.dts b/target/linux/bmips/dts/nb4-fxc-r1.dts
new file mode 100644
index 0000000..2cad77c
--- /dev/null
+++ b/target/linux/bmips/dts/nb4-fxc-r1.dts
@@ -0,0 +1,104 @@
+/dts-v1/;
+
+#include "bcm6358.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	model = "SFR Neuf Box 4 (Foxconn)";
+	compatible = "sfr,nb4-fxc-r1", "brcm,bcm6358";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		service {
+			label = "service";
+			gpios = <&gpio0 27 1>;
+			linux,code = <BTN_0>;
+		};
+		clip {
+			label = "clip";
+			gpios = <&gpio0 31 1>;
+			linux,code = <BTN_1>;
+		};
+		reset {
+			label = "reset";
+			gpios = <&gpio1 2 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		wps {
+			label = "wps";
+			gpios = <&gpio1 5 1>;
+			linux,code = <KEY_WPS_BUTTON>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		traffic_white {
+			label = "NB4-FXC-r1:white:traffic";
+			gpios = <&gpio0 2 0>;
+		};
+		service_blue {
+			label = "NB4-FXC-r1:blue:service";
+			gpios = <&gpio0 4 0>;
+		};
+		wifi_white {
+			label = "NB4-FXC-r1:white:wifi";
+			gpios = <&gpio0 15 0>;
+		};
+		service_red {
+			label = "NB4-FXC-r1:red:service";
+			gpios = <&gpio0 29 0>;
+		};
+		service_green {
+			label = "NB4-FXC-r1:green:service";
+			gpios = <&gpio0 30 0>;
+		};
+	};
+};
+
+&leds0 {
+	status = "ok";
+	brcm,clk-div = <1>;
+
+	alarm_white at 0 {
+		reg = <0>;
+		active-low;
+		label = "NB4-FXC-r1:white:alarm";
+	};
+	tv_white at 2 {
+		reg = <2>;
+		active-low;
+		label = "NB4-FXC-r1:white:tv";
+	};
+	tel_white at 3 {
+		reg = <3>;
+		active-low;
+		label = "NB4-FXC-r1:white:tel";
+	};
+	adsl_white at 4 {
+		reg = <4>;
+		active-low;
+		label = "NB4-FXC-r0:white:adsl";
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/nb4-ser-r0.dts b/target/linux/bmips/dts/nb4-ser-r0.dts
new file mode 100644
index 0000000..0c1111f
--- /dev/null
+++ b/target/linux/bmips/dts/nb4-ser-r0.dts
@@ -0,0 +1,104 @@
+/dts-v1/;
+
+#include "bcm6358.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	model = "SFR Neuf Box 4 (Sercomm)";
+	compatible = "sfr,nb4-ser-r0", "brcm,bcm6358";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x02000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		service {
+			label = "service";
+			gpios = <&gpio0 27 1>;
+			linux,code = <BTN_0>;
+		};
+		clip {
+			label = "clip";
+			gpios = <&gpio0 31 1>;
+			linux,code = <BTN_1>;
+		};
+		reset {
+			label = "reset";
+			gpios = <&gpio1 2 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		wps {
+			label = "wps";
+			gpios = <&gpio1 5 1>;
+			linux,code = <KEY_WPS_BUTTON>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		traffic_white {
+			label = "NB4-SER-r0:white:traffic";
+			gpios = <&gpio0 2 1>;
+		};
+		service_blue {
+			label = "NB4-SER-r0:blue:service";
+			gpios = <&gpio0 4 1>;
+		};
+		wifi_white {
+			label = "NB4-SER-r0:white:wifi";
+			gpios = <&gpio0 15 1>;
+		};
+		service_red {
+			label = "NB4-SER-r0:red:service";
+			gpios = <&gpio0 29 1>;
+		};
+		service_green {
+			label = "NB4-SER-r0:green:service";
+			gpios = <&gpio0 30 1>;
+		};
+	};
+};
+
+&leds0 {
+	status = "ok";
+	brcm,clk-div = <1>;
+
+	alarm_white at 0 {
+		reg = <0>;
+		active-low;
+		label = "NB4-SER-r0:white:alarm";
+	};
+	tv_white at 2 {
+		reg = <2>;
+		active-low;
+		label = "NB4-SER-r0:white:tv";
+	};
+	tel_white at 3 {
+		reg = <3>;
+		active-low;
+		label = "NB4-SER-r0:white:tel";
+	};
+	adsl_white at 4 {
+		reg = <4>;
+		active-low;
+		label = "NB4-SER-r0:white:adsl";
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/vg-8050.dts b/target/linux/bmips/dts/vg-8050.dts
new file mode 100644
index 0000000..0a355b5
--- /dev/null
+++ b/target/linux/bmips/dts/vg-8050.dts
@@ -0,0 +1,125 @@
+/dts-v1/;
+
+#include "bcm63268.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,vg-8050", "brcm,bcm63268";
+	model = "Comtrend VG-8050";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x08000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio1 0 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		wps {
+			label = "wps";
+			gpios = <&gpio1 1 1>;
+			linux,code = <KEY_WPS_BUTTON>;
+		};
+	};
+};
+
+&leds0 {
+	status = "ok";
+	brcm,serial-leds;
+	brcm,serial-dat-low;
+	brcm,serial-shift-inv;
+
+	inet_red at 2 {
+		reg = <2>;
+		active-low;
+		label = "VG-8050:red:inet";
+	};
+	power_red at 3 {
+		reg = <3>;
+		active-low;
+		label = "VG-8050:red:power";
+	};
+	power_green at 6 {
+		reg = <6>;
+		active-low;
+		label = "VG-8050:green:power";
+		default-state = "on";
+	};
+	wps_green at 7 {
+		reg = <7>;
+		active-low;
+		label = "VG-8050:green:wps";
+	};
+	inet_green at 8 {
+		reg = <8>;
+		active-low;
+		label = "VG-8050:green:inet";
+	};
+	voip_green at 10 {
+		reg = <10>;
+		active-low;
+		label = "VG-8050:green:voip";
+	};
+	voip_red at 12 {
+		reg = <12>;
+		active-low;
+		label = "VG-8050:red:voip";
+	};
+	wps_red at 14 {
+		reg = <14>;
+		active-low;
+		label = "VG-8050:red:wps";
+	};
+};
+
+&nflash {
+	status = "ok";
+
+	nandcs at 0 {
+		compatible = "brcm,nandcs";
+		#size-cells = <1>;
+		#address-cells = <1>;
+		reg = <0>;
+		nand-ecc-step-size = <512>;
+		nand-ecc-strength = <15>;
+		nand-on-flash-bbt;
+		brcm,nand-oob-sector-size = <64>;
+
+		cfe at 0 {
+			label = "cfe";
+			reg = <0x0000000 0x0020000>;
+			read-only;
+		};
+
+		linux at 20000 {
+			label = "linux";
+			reg = <0x0020000 0x7ae0000>;
+			read-only;
+		};
+
+		data at 7b00000 {
+			label = "data";
+			reg = <0x7b00000 0x0500000>;
+			read-only;
+		};
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/vr-3025u.dts b/target/linux/bmips/dts/vr-3025u.dts
new file mode 100644
index 0000000..e06f3d0
--- /dev/null
+++ b/target/linux/bmips/dts/vr-3025u.dts
@@ -0,0 +1,82 @@
+/dts-v1/;
+
+#include "bcm6368.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,vr-3025u", "brcm,bcm6368";
+	model = "Comtrend VR-3025u";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio1 2 1>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		dsl_green {
+			label = "VR-3025u:green:dsl";
+			gpios = <&gpio0 2 1>;
+		};
+		inet_green {
+			label = "VR-3025u:green:inet";
+			gpios = <&gpio0 5 0>;
+		};
+		power_green {
+			label = "VR-3025u:green:power";
+			gpios = <&gpio0 22 0>;
+			default-state = "on";
+		};
+		power_red {
+			label = "VR-3025u:red:power";
+			gpios = <&gpio0 24 0>;
+		};
+		inet_red {
+			label = "VR-3025u:red:inet";
+			gpios = <&gpio0 31 0>;
+		};
+	};
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "cfe";
+		reg = <0x0000000 0x0020000>;
+		read-only;
+	};
+	linux at 20000 {
+		label = "linux";
+		reg = <0x0020000 0x1fc0000>;
+	};
+	nvram at 1fe0000 {
+		label = "nvram";
+		reg = <0x1fe0000 0x020000>;
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/vr-3025un.dts b/target/linux/bmips/dts/vr-3025un.dts
new file mode 100644
index 0000000..9a3b1df
--- /dev/null
+++ b/target/linux/bmips/dts/vr-3025un.dts
@@ -0,0 +1,82 @@
+/dts-v1/;
+
+#include "bcm6368.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,vr-3025un", "brcm,bcm6368";
+	model = "Comtrend VR-3025un";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio1 2 1>;
+			linux,code = <KEY_RESTART>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		dsl_green {
+			label = "VR-3025un:green:dsl";
+			gpios = <&gpio0 2 1>;
+		};
+		inet_green {
+			label = "VR-3025un:green:inet";
+			gpios = <&gpio0 5 0>;
+		};
+		power_green {
+			label = "VR-3025un:green:power";
+			gpios = <&gpio0 22 0>;
+			default-state = "on";
+		};
+		power_red {
+			label = "VR-3025un:red:power";
+			gpios = <&gpio0 24 0>;
+		};
+		inet_red {
+			label = "VR-3025un:red:inet";
+			gpios = <&gpio0 31 0>;
+		};
+	};
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "cfe";
+		reg = <0x000000 0x010000>;
+		read-only;
+	};
+	linux at 10000 {
+		label = "linux";
+		reg = <0x010000 0x7e0000>;
+	};
+	nvram at 7f0000 {
+		label = "nvram";
+		reg = <0x7f0000 0x010000>;
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/vr-3032u.dts b/target/linux/bmips/dts/vr-3032u.dts
new file mode 100644
index 0000000..e43f1ac
--- /dev/null
+++ b/target/linux/bmips/dts/vr-3032u.dts
@@ -0,0 +1,153 @@
+/dts-v1/;
+
+#include "bcm63268.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,vr-3032u", "brcm,bcm63268";
+	model = "Comtrend VR-3032u";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		reset {
+			label = "reset";
+			gpios = <&gpio1 0 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		wps {
+			label = "wps";
+			gpios = <&gpio1 1 1>;
+			linux,code = <KEY_WPS_BUTTON>;
+		};
+	};
+};
+
+&leds0 {
+	status = "ok";
+	brcm,serial-leds;
+	brcm,serial-dat-low;
+	brcm,serial-shift-inv;
+
+	gphy0_spd0 at 0 {
+		reg = <0>;
+		brcm,hardware-controlled;
+		brcm,link-signal-sources = <0>;
+	};
+	gphy0_spd1 at 1 {
+		reg = <1>;
+		brcm,hardware-controlled;
+		brcm,link-signal-sources = <1>;
+	};
+	inet_red at 2 {
+		reg = <2>;
+		active-low;
+		label = "VR-3032u:red:inet";
+	};
+	dsl_green at 3 {
+		reg = <3>;
+		active-low;
+		label = "VR-3032u:green:dsl";
+	};
+	usb_green at 4 {
+		reg = <4>;
+		active-low;
+		label = "VR-3032u:green:usb";
+	};
+	wps_green at 7 {
+		reg = <7>;
+		active-low;
+		label = "VR-3032u:green:wps";
+	};
+	inet_green at 8 {
+		reg = <8>;
+		active-low;
+		label = "VR-3032u:green:inet";
+	};
+	ephy0_act at 9 {
+		reg = <9>;
+		brcm,hardware-controlled;
+	};
+	ephy1_act at 10 {
+		reg = <10>;
+		brcm,hardware-controlled;
+	};
+	ephy2_act at 11 {
+		reg = <11>;
+		brcm,hardware-controlled;
+	};
+	gphy0_act at 12 {
+		reg = <12>;
+		brcm,hardware-controlled;
+	};
+	ephy0_spd at 13 {
+		reg = <13>;
+		brcm,hardware-controlled;
+	};
+	ephy1_spd at 14 {
+		reg = <14>;
+		brcm,hardware-controlled;
+	};
+	ephy2_spd at 15 {
+		reg = <15>;
+		brcm,hardware-controlled;
+	};
+	power_green at 20 {
+		reg = <20>;
+		active-low;
+		label = "VR-3032u:green:power";
+		default-state = "on";
+	};
+};
+
+&nflash {
+	status = "ok";
+
+	nandcs at 0 {
+		compatible = "brcm,nandcs";
+		#size-cells = <1>;
+		#address-cells = <1>;
+		reg = <0>;
+		nand-ecc-step-size = <512>;
+		nand-ecc-strength = <15>;
+		nand-on-flash-bbt;
+		brcm,nand-oob-sector-size = <64>;
+
+		cfe at 0 {
+			label = "cfe";
+			reg = <0x0000000 0x0020000>;
+			read-only;
+		};
+
+		linux at 20000 {
+			label = "linux";
+			reg = <0x0020000 0x7ae0000>;
+			read-only;
+		};
+
+		data at 7b00000 {
+			label = "data";
+			reg = <0x7b00000 0x0500000>;
+			read-only;
+		};
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/dts/wap-5813n.dts b/target/linux/bmips/dts/wap-5813n.dts
new file mode 100644
index 0000000..6ace557
--- /dev/null
+++ b/target/linux/bmips/dts/wap-5813n.dts
@@ -0,0 +1,94 @@
+/dts-v1/;
+
+#include "bcm6368.dtsi"
+
+#include <dt-bindings/input/input.h>
+
+/ {
+	compatible = "comtrend,wap-5813n", "brcm,bcm6368";
+	model = "Comtrend WAP-5813n";
+
+	memory at 0 {
+		device_type = "memory";
+		reg = <0x00000000 0x04000000>;
+	};
+
+	chosen {
+		bootargs = "console=ttyS0,115200";
+		stdout-path = &uart0;
+	};
+
+	gpio-keys-polled {
+		compatible = "gpio-keys-polled";
+		#address-cells = <1>;
+		#size-cells = <0>;
+		poll-interval = <20>;
+		debounce-interval = <60>;
+
+		wlan {
+			label = "wlan";
+			gpios = <&gpio1 0 1>;
+			linux,code = <KEY_WLAN>;
+		};
+		reset {
+			label = "reset";
+			gpios = <&gpio1 2 1>;
+			linux,code = <KEY_RESTART>;
+		};
+		wps {
+			label = "wps";
+			gpios = <&gpio1 3 1>;
+			linux,code = <KEY_WPS_BUTTON>;
+		};
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		inet_green {
+			label = "WAP-5813n:green:inet";
+			gpios = <&gpio0 5 0>;
+		};
+		power_green {
+			label = "WAP-5813n:green:power";
+			gpios = <&gpio0 22 0>;
+			default-state = "on";
+		};
+		wps_green {
+			label = "WAP-5813n:green:wps";
+			gpios = <&gpio0 23 1>;
+		};
+		power_red {
+			label = "WAP-5813n:red:power";
+			gpios = <&gpio0 24 0>;
+		};
+		inet_red {
+			label = "WAP-5813n:red:inet";
+			gpios = <&gpio0 31 0>;
+		};
+	};
+};
+
+&pflash {
+	status = "ok";
+
+	cfe at 0 {
+		label = "CFE";
+		reg = <0x000000 0x010000>;
+		read-only;
+	};
+
+	linux at 10000 {
+		label = "linux";
+		reg = <0x010000 0x7e0000>;
+	};
+
+	nvram at 7f0000 {
+		label = "nvram";
+		reg = <0x7f0000 0x010000>;
+	};
+};
+
+&uart0 {
+	status = "ok";
+};
diff --git a/target/linux/bmips/image/Makefile b/target/linux/bmips/image/Makefile
new file mode 100644
index 0000000..bfb8395
--- /dev/null
+++ b/target/linux/bmips/image/Makefile
@@ -0,0 +1,245 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+LOADADDR = 0x80010000		# RAM start + 64K
+KERNEL_ENTRY = $(LOADADDR)	# Newer kernels add a jmp to the kernel_entry at the start of the binary
+LOADER_ENTRY = 0x80a00000	# RAM start + 10M, for relocate
+RAMSIZE = 0x02000000		# 32MB
+LZMA_TEXT_START = 0x81800000	# 32MB - 8MB
+
+LOADER_MAKEOPTS= \
+		KDIR=$(KDIR) \
+		LOADADDR=$(LOADADDR) \
+		KERNEL_ENTRY=$(KERNEL_ENTRY) \
+		RAMSIZE=$(RAMSIZE) \
+		LZMA_TEXT_START=$(LZMA_TEXT_START) \
+
+RELOCATE_MAKEOPTS= \
+		CACHELINE_SIZE=16 \
+		KERNEL_ADDR=$(KERNEL_ENTRY) \
+		CROSS_COMPILE=$(TARGET_CROSS) \
+		LZMA_TEXT_START=$(LOADER_ENTRY)
+
+define Build/Compile
+	rm -rf $(KDIR)/relocate
+	$(CP) ../../generic/image/relocate $(KDIR)
+	$(MAKE) -C $(KDIR)/relocate $(RELOCATE_MAKEOPTS)
+endef
+
+### Kernel scripts ###
+define Build/append-dtb
+	$(call Image/BuildDTB,../dts/$(DEVICE_DTS).dts,$@.dtb)
+	cat $@.dtb >> $@
+endef
+
+define Build/gzip
+	gzip -9 -c $@ > $@.gz
+	mv $@.gz $@
+endef
+
+define Build/hcs-initramfs
+	$(STAGING_DIR_HOST)/bin/hcsmakeimage --magic_bytes=$(HCS_MAGIC_BYTES) \
+		--rev_maj=$(HCS_REV_MAJ) --rev_min=$(HCS_REV_MIN) --input_file=$@ \
+		--output_file=$@.hcs --ldaddress=$(LOADADDR)
+	mv $@.hcs $@
+endef
+
+define Build/loader-lzma
+	rm -rf $@.src
+	$(MAKE) -C lzma-loader \
+		$(LOADER_MAKEOPTS) \
+		PKG_BUILD_DIR="$@.src" \
+		TARGET_DIR="$(dir $@)" \
+		LOADER_DATA="$@" \
+		LOADER_NAME="$(notdir $@)" \
+		compile loader.$(1)
+	mv "$@.$(1)" "$@"
+	rm -rf $@.src
+endef
+
+define Build/lzma
+	# CFE is a LZMA nazi! It took me hours to find out the parameters!
+	# Also I think lzma has a bug cause it generates different output depending on
+	# if you use stdin / stdout or not. Use files instead of stdio here, cause
+	# otherwise CFE will complain and not boot the image.
+	$(STAGING_DIR_HOST)/bin/lzma e $@ -d22 -fb64 -a1 $@.lzma
+	mv $@.lzma $@
+endef
+
+define Build/lzma-cfe
+	# Strip out the length, CFE doesn't like this
+	dd if=$@ of=$@.lzma.cfe bs=5 count=1
+	dd if=$@ of=$@.lzma.cfe ibs=13 obs=5 skip=1 seek=1 conv=notrunc
+	mv $@.lzma.cfe $@
+endef
+
+define Build/relocate-kernel
+	# CFE only allows ~4 MiB for the uncompressed kernels, but uncompressed
+	# kernel might get larger than that, so let CFE unpack and load at a
+	# higher address and make the kernel relocate itself to the expected
+	# location.
+	( \
+		dd if=$(KDIR)/relocate/loader.bin bs=32 conv=sync && \
+		perl -e '@s = stat("$@"); print pack("N", @s[7])' && \
+		cat $@ \
+	) > $@.relocate
+	mv $@.relocate $@
+endef
+
+### Image scripts ###
+define rootfspad/jffs2-128k
+--align-rootfs
+endef
+define rootfspad/jffs2-64k
+--align-rootfs
+endef
+define rootfspad/squashfs
+endef
+
+define Image/LimitName16
+$(shell expr substr "$(1)" 1 16)
+endef
+
+define Image/FileSystemStrip
+$(subst root.,,$(notdir $(1)))
+endef
+
+define Build/cfe-bin
+	$(STAGING_DIR_HOST)/bin/imagetag -i $(word 1,$^) -f $(word 2,$^) \
+		--output $@ --boardid $(CFE_BOARD_ID) --chipid $(CFE_CHIP_ID) \
+		--entry $(LOADER_ENTRY) --load-addr $(LOADER_ENTRY) \
+		--info1 "$(call Image/LimitName16,$(DEVICE_NAME))" \
+		--info2 "$(call Image/FileSystemStrip,$(word 2,$^))" \
+		$(call rootfspad/$(call Image/FileSystemStrip,$(word 2,$^))) \
+		$(CFE_EXTRAS) $(1)
+endef
+
+# Shared device definition: applies to every defined device
+define Device/Default
+  PROFILES = Default $$(DEVICE_PROFILE)
+  KERNEL_INITRAMFS_IMAGE = $$(KERNEL_INITRAMFS_PREFIX).elf
+  DEVICE_PROFILE :=
+  DEVICE_NAME :=
+  DEVICE_DTS :=
+endef
+DEVICE_VARS += DEVICE_PROFILE DEVICE_NAME DEVICE_DTS
+
+# BCM33xx HCS devices: only generates ramdisks (unsupported bin images)
+define Device/bcm33xxHcsRamdisk
+  KERNEL_INITRAMFS := kernel-bin | append-dtb | lzma | loader-lzma bin | hcs-initramfs
+  IMAGES :=
+  HCS_MAGIC_BYTES :=
+  HCS_REV_MIN :=
+  HCS_REV_MAJ :=
+endef
+DEVICE_VARS += HCS_MAGIC_BYTES HCS_REV_MIN HCS_REV_MAJ
+
+# Shared BCM63xx CFE device definitios
+define Device/bcm63xxCfeCommon
+  FILESYSTEMS := squashfs jffs2-64k jffs2-128k
+  KERNEL := kernel-bin | append-dtb | relocate-kernel | lzma | lzma-cfe
+  KERNEL_INITRAMFS := kernel-bin | append-dtb | lzma | loader-lzma elf
+endef
+
+# BCM63xx CFE devices: only generates ramdisks (unsupported bin images)
+define Device/bcm63xxCfeRamdisk
+  $(Device/bcm63xxCfeCommon)
+  IMAGES :=
+endef
+
+# BCM63xx CFE devices: both ramdisks and parallel/spi bin images
+# New versions of CFE bootloader compatible with imagetag
+define Device/bcm63xxCfe
+  $(Device/bcm63xxCfeCommon)
+  IMAGES := cfe.bin
+  IMAGE/cfe.bin := cfe-bin
+  CFE_BOARD_ID :=
+  CFE_CHIP_ID :=
+  CFE_EXTRAS :=
+endef
+DEVICE_VARS += CFE_BOARD_ID CFE_CHIP_ID CFE_EXTRAS
+
+# $(2) = image name
+# $(3) = dts
+# $(4) = hcs magic bytes
+# $(5) = hcs rev min
+# $(6) = hcs rev major
+define bcm33xxHcsRamdisk
+  define Device/$(2)
+    $$(Device/bcm33xxHcsRamdisk)
+    DEVICE_PROFILE := $(1)
+    DEVICE_NAME := $(2)
+    DEVICE_DTS := $(3)
+    HCS_MAGIC_BYTES := $(4)
+    HCS_REV_MIN := $(5)
+    HCS_REV_MAJ := $(6)
+  endef
+  TARGET_DEVICES += $(2)
+endef
+
+# $(1) = profile
+# $(2) = image name
+# $(3) = dts
+define bcm63xxCfeRamdisk
+  define Device/$(2)
+    $$(Device/bcm63xxCfeRamdisk)
+    DEVICE_PROFILE := $(1)
+    DEVICE_NAME := $(2)
+    DEVICE_DTS := $(3)
+  endef
+  TARGET_DEVICES += $(2)
+endef
+
+# $(1) = profile
+# $(2) = image name
+# $(3) = dts
+# $(4) = cfe board name
+# $(5) = cfe chip id
+# $(6) = cfe additional options
+define bcm63xxCfe
+  define Device/$(2)
+    $$(Device/bcm63xxCfe)
+    DEVICE_PROFILE := $(1)
+    DEVICE_NAME := $(2)
+    DEVICE_DTS := $(3)
+    CFE_BOARD_ID := $(4)
+    CFE_CHIP_ID := $(5)
+    CFE_EXTRAS := $(6)
+  endef
+  TARGET_DEVICES += $(2)
+endef
+
+### Devices ###
+# Comtrend AR-5381u
+$(eval $(call bcm63xxCfe,AR5381u,AR-5381u,ar-5381u,96328A-1241N,6328,--pad 8))
+# Comtrend AR-5387un
+$(eval $(call bcm63xxCfe,AR5387un,AR-5387un,ar-5387un,96328A-1441N1,6328,--pad 8))
+# Comtrend VG-8050
+$(eval $(call bcm63xxCfeRamdisk,VG8050,VG-8050,vg-8050,963169P-1861N5,63268))
+# Comtrend VR-3025u
+$(eval $(call bcm63xxCfe,VR3025u,VR-3025u,vr-3025u,96368M-1541N,6368,--pad 16 --image-offset 0x20000 --block-size 0x20000))
+# Comtrend VR-3025un
+$(eval $(call bcm63xxCfe,VR3025un,VR-3025un,vr-3025un,96368M-1341N,6368,--pad 4))
+# Comtrend VR-3032u
+$(eval $(call bcm63xxCfeRamdisk,VR3032u,VR-3032u,vr-3032u,963168M-1841N1,63268))
+# Comtrend WAP-5813n
+$(eval $(call bcm63xxCfe,WAP5813n,WAP-5813n,wap-5813n,96369R-1231N,6368,--pad 4))
+# Huawei HG520v
+$(eval $(call bcm63xxCfe,HG520v,HG520v,hg520v,HW6358GW_B,6358,--rsa-signature "EchoLife_HG520v"))
+# Huawei HG556a
+$(eval $(call bcm63xxCfe,HG556a_AB,HG556a_A,hg556a-a,HW556,6358,--rsa-signature "EchoLife_HG556a" --image-offset 0x20000 --block-size 0x10000 --tag-version 8))
+$(eval $(call bcm63xxCfe,HG556a_AB,HG556a_B,hg556a-b,HW556,6358,--rsa-signature "EchoLife_HG556a" --image-offset 0x20000 --block-size 0x20000 --tag-version 8))
+$(eval $(call bcm63xxCfe,HG556a_C,HG556a_C,hg556a-c,HW556,6358,--rsa-signature "EchoLife_HG556a" --image-offset 0x20000 --block-size 0x20000 --tag-version 8))
+# SFR Neufbox 4
+$(eval $(call bcm63xxCfe,Neufbox4,NEUFBOX4-SER,nb4-ser-r0,96358VW,6358,--rsa-signature "OpenWRT-$(REVISION)"))
+$(eval $(call bcm63xxCfe,Neufbox4,NEUFBOX4-FXC,nb4-fxc-r1,96358VW,6358,--rsa-signature "OpenWRT-$(REVISION)"))
+# Sagem F at ST1704
+$(eval $(call bcm63xxCfe,FAST1704,F at ST1704,fast1704,F at ST1704,6338))
+
+$(eval $(call BuildImage))
diff --git a/target/linux/bmips/image/lzma-loader/Makefile b/target/linux/bmips/image/lzma-loader/Makefile
new file mode 100644
index 0000000..8d36691
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/Makefile
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2011 OpenWrt.org
+# Copyright (C) 2011 Gabor Juhos <juhosg at openwrt.org>
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+LZMA_TEXT_START	:= 0x80a00000
+LOADER		:= loader.bin
+LOADER_NAME	:= $(basename $(notdir $(LOADER)))
+LOADER_DATA 	:=
+TARGET_DIR	:=
+FLASH_OFFS	:=
+FLASH_MAX	:=
+
+ifeq ($(TARGET_DIR),)
+TARGET_DIR	:= $(KDIR)
+endif
+
+LOADER_BIN	:= $(TARGET_DIR)/$(LOADER_NAME).bin
+LOADER_GZ	:= $(TARGET_DIR)/$(LOADER_NAME).gz
+LOADER_ELF	:= $(TARGET_DIR)/$(LOADER_NAME).elf
+
+PKG_NAME := lzma-loader
+PKG_BUILD_DIR := $(KDIR)/$(PKG_NAME)
+
+.PHONY : loader-compile loader.bin loader.elf loader.gz
+
+$(PKG_BUILD_DIR)/.prepared:
+	mkdir $(PKG_BUILD_DIR)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+	touch $@
+
+loader-compile: $(PKG_BUILD_DIR)/.prepared
+	$(MAKE) -C $(PKG_BUILD_DIR) CROSS_COMPILE="$(TARGET_CROSS)" \
+		LZMA_TEXT_START=$(LZMA_TEXT_START) \
+		LOADER_DATA=$(LOADER_DATA) \
+		FLASH_OFFS=$(FLASH_OFFS) \
+		FLASH_MAX=$(FLASH_MAX) \
+		clean all
+
+loader.gz: $(PKG_BUILD_DIR)/loader.bin
+	gzip -nc9 $< > $(LOADER_GZ)
+
+loader.elf: $(PKG_BUILD_DIR)/loader.elf
+	$(CP) $< $(LOADER_ELF)
+
+loader.bin: $(PKG_BUILD_DIR)/loader.bin
+	$(CP) $< $(LOADER_BIN)
+
+download:
+prepare: $(PKG_BUILD_DIR)/.prepared
+compile: loader-compile
+
+install:
+
+clean:
+	rm -rf $(PKG_BUILD_DIR)
+
diff --git a/target/linux/bmips/image/lzma-loader/src/LzmaDecode.c b/target/linux/bmips/image/lzma-loader/src/LzmaDecode.c
new file mode 100644
index 0000000..cb83453
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/LzmaDecode.c
@@ -0,0 +1,584 @@
+/*
+  LzmaDecode.c
+  LZMA Decoder (optimized for Speed version)
+  
+  LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01)
+  http://www.7-zip.org/
+
+  LZMA SDK is licensed under two licenses:
+  1) GNU Lesser General Public License (GNU LGPL)
+  2) Common Public License (CPL)
+  It means that you can select one of these two licenses and 
+  follow rules of that license.
+
+  SPECIAL EXCEPTION:
+  Igor Pavlov, as the author of this Code, expressly permits you to 
+  statically or dynamically link your Code (or bind by name) to the 
+  interfaces of this file without subjecting your linked Code to the 
+  terms of the CPL or GNU LGPL. Any modifications or additions 
+  to this file, however, are subject to the LGPL or CPL terms.
+*/
+
+#include "LzmaDecode.h"
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+#define RC_READ_BYTE (*Buffer++)
+
+#define RC_INIT2 Code = 0; Range = 0xFFFFFFFF; \
+  { int i; for(i = 0; i < 5; i++) { RC_TEST; Code = (Code << 8) | RC_READ_BYTE; }}
+
+#ifdef _LZMA_IN_CB
+
+#define RC_TEST { if (Buffer == BufferLim) \
+  { SizeT size; int result = InCallback->Read(InCallback, &Buffer, &size); if (result != LZMA_RESULT_OK) return result; \
+  BufferLim = Buffer + size; if (size == 0) return LZMA_RESULT_DATA_ERROR; }}
+
+#define RC_INIT Buffer = BufferLim = 0; RC_INIT2
+
+#else
+
+#define RC_TEST { if (Buffer == BufferLim) return LZMA_RESULT_DATA_ERROR; }
+
+#define RC_INIT(buffer, bufferSize) Buffer = buffer; BufferLim = buffer + bufferSize; RC_INIT2
+ 
+#endif
+
+#define RC_NORMALIZE if (Range < kTopValue) { RC_TEST; Range <<= 8; Code = (Code << 8) | RC_READ_BYTE; }
+
+#define IfBit0(p) RC_NORMALIZE; bound = (Range >> kNumBitModelTotalBits) * *(p); if (Code < bound)
+#define UpdateBit0(p) Range = bound; *(p) += (kBitModelTotal - *(p)) >> kNumMoveBits;
+#define UpdateBit1(p) Range -= bound; Code -= bound; *(p) -= (*(p)) >> kNumMoveBits;
+
+#define RC_GET_BIT2(p, mi, A0, A1) IfBit0(p) \
+  { UpdateBit0(p); mi <<= 1; A0; } else \
+  { UpdateBit1(p); mi = (mi + mi) + 1; A1; } 
+  
+#define RC_GET_BIT(p, mi) RC_GET_BIT2(p, mi, ; , ;)               
+
+#define RangeDecoderBitTreeDecode(probs, numLevels, res) \
+  { int i = numLevels; res = 1; \
+  do { CProb *p = probs + res; RC_GET_BIT(p, res) } while(--i != 0); \
+  res -= (1 << numLevels); }
+
+
+#define kNumPosBitsMax 4
+#define kNumPosStatesMax (1 << kNumPosBitsMax)
+
+#define kLenNumLowBits 3
+#define kLenNumLowSymbols (1 << kLenNumLowBits)
+#define kLenNumMidBits 3
+#define kLenNumMidSymbols (1 << kLenNumMidBits)
+#define kLenNumHighBits 8
+#define kLenNumHighSymbols (1 << kLenNumHighBits)
+
+#define LenChoice 0
+#define LenChoice2 (LenChoice + 1)
+#define LenLow (LenChoice2 + 1)
+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
+#define kNumLenProbs (LenHigh + kLenNumHighSymbols) 
+
+
+#define kNumStates 12
+#define kNumLitStates 7
+
+#define kStartPosModelIndex 4
+#define kEndPosModelIndex 14
+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
+
+#define kNumPosSlotBits 6
+#define kNumLenToPosStates 4
+
+#define kNumAlignBits 4
+#define kAlignTableSize (1 << kNumAlignBits)
+
+#define kMatchMinLen 2
+
+#define IsMatch 0
+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
+#define IsRepG0 (IsRep + kNumStates)
+#define IsRepG1 (IsRepG0 + kNumStates)
+#define IsRepG2 (IsRepG1 + kNumStates)
+#define IsRep0Long (IsRepG2 + kNumStates)
+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
+#define LenCoder (Align + kAlignTableSize)
+#define RepLenCoder (LenCoder + kNumLenProbs)
+#define Literal (RepLenCoder + kNumLenProbs)
+
+#if Literal != LZMA_BASE_SIZE
+StopCompilingDueBUG
+#endif
+
+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size)
+{
+  unsigned char prop0;
+  if (size < LZMA_PROPERTIES_SIZE)
+    return LZMA_RESULT_DATA_ERROR;
+  prop0 = propsData[0];
+  if (prop0 >= (9 * 5 * 5))
+    return LZMA_RESULT_DATA_ERROR;
+  {
+    for (propsRes->pb = 0; prop0 >= (9 * 5); propsRes->pb++, prop0 -= (9 * 5));
+    for (propsRes->lp = 0; prop0 >= 9; propsRes->lp++, prop0 -= 9);
+    propsRes->lc = prop0;
+    /*
+    unsigned char remainder = (unsigned char)(prop0 / 9);
+    propsRes->lc = prop0 % 9;
+    propsRes->pb = remainder / 5;
+    propsRes->lp = remainder % 5;
+    */
+  }
+
+  #ifdef _LZMA_OUT_READ
+  {
+    int i;
+    propsRes->DictionarySize = 0;
+    for (i = 0; i < 4; i++)
+      propsRes->DictionarySize += (UInt32)(propsData[1 + i]) << (i * 8);
+    if (propsRes->DictionarySize == 0)
+      propsRes->DictionarySize = 1;
+  }
+  #endif
+  return LZMA_RESULT_OK;
+}
+
+#define kLzmaStreamWasFinishedId (-1)
+
+int LzmaDecode(CLzmaDecoderState *vs,
+    #ifdef _LZMA_IN_CB
+    ILzmaInCallback *InCallback,
+    #else
+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
+    #endif
+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed)
+{
+  CProb *p = vs->Probs;
+  SizeT nowPos = 0;
+  Byte previousByte = 0;
+  UInt32 posStateMask = (1 << (vs->Properties.pb)) - 1;
+  UInt32 literalPosMask = (1 << (vs->Properties.lp)) - 1;
+  int lc = vs->Properties.lc;
+
+  #ifdef _LZMA_OUT_READ
+  
+  UInt32 Range = vs->Range;
+  UInt32 Code = vs->Code;
+  #ifdef _LZMA_IN_CB
+  const Byte *Buffer = vs->Buffer;
+  const Byte *BufferLim = vs->BufferLim;
+  #else
+  const Byte *Buffer = inStream;
+  const Byte *BufferLim = inStream + inSize;
+  #endif
+  int state = vs->State;
+  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
+  int len = vs->RemainLen;
+  UInt32 globalPos = vs->GlobalPos;
+  UInt32 distanceLimit = vs->DistanceLimit;
+
+  Byte *dictionary = vs->Dictionary;
+  UInt32 dictionarySize = vs->Properties.DictionarySize;
+  UInt32 dictionaryPos = vs->DictionaryPos;
+
+  Byte tempDictionary[4];
+
+  #ifndef _LZMA_IN_CB
+  *inSizeProcessed = 0;
+  #endif
+  *outSizeProcessed = 0;
+  if (len == kLzmaStreamWasFinishedId)
+    return LZMA_RESULT_OK;
+
+  if (dictionarySize == 0)
+  {
+    dictionary = tempDictionary;
+    dictionarySize = 1;
+    tempDictionary[0] = vs->TempDictionary[0];
+  }
+
+  if (len == kLzmaNeedInitId)
+  {
+    {
+      UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
+      UInt32 i;
+      for (i = 0; i < numProbs; i++)
+        p[i] = kBitModelTotal >> 1; 
+      rep0 = rep1 = rep2 = rep3 = 1;
+      state = 0;
+      globalPos = 0;
+      distanceLimit = 0;
+      dictionaryPos = 0;
+      dictionary[dictionarySize - 1] = 0;
+      #ifdef _LZMA_IN_CB
+      RC_INIT;
+      #else
+      RC_INIT(inStream, inSize);
+      #endif
+    }
+    len = 0;
+  }
+  while(len != 0 && nowPos < outSize)
+  {
+    UInt32 pos = dictionaryPos - rep0;
+    if (pos >= dictionarySize)
+      pos += dictionarySize;
+    outStream[nowPos++] = dictionary[dictionaryPos] = dictionary[pos];
+    if (++dictionaryPos == dictionarySize)
+      dictionaryPos = 0;
+    len--;
+  }
+  if (dictionaryPos == 0)
+    previousByte = dictionary[dictionarySize - 1];
+  else
+    previousByte = dictionary[dictionaryPos - 1];
+
+  #else /* if !_LZMA_OUT_READ */
+
+  int state = 0;
+  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
+  int len = 0;
+  const Byte *Buffer;
+  const Byte *BufferLim;
+  UInt32 Range;
+  UInt32 Code;
+
+  #ifndef _LZMA_IN_CB
+  *inSizeProcessed = 0;
+  #endif
+  *outSizeProcessed = 0;
+
+  {
+    UInt32 i;
+    UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + vs->Properties.lp));
+    for (i = 0; i < numProbs; i++)
+      p[i] = kBitModelTotal >> 1;
+  }
+  
+  #ifdef _LZMA_IN_CB
+  RC_INIT;
+  #else
+  RC_INIT(inStream, inSize);
+  #endif
+
+  #endif /* _LZMA_OUT_READ */
+
+  while(nowPos < outSize)
+  {
+    CProb *prob;
+    UInt32 bound;
+    int posState = (int)(
+        (nowPos 
+        #ifdef _LZMA_OUT_READ
+        + globalPos
+        #endif
+        )
+        & posStateMask);
+
+    prob = p + IsMatch + (state << kNumPosBitsMax) + posState;
+    IfBit0(prob)
+    {
+      int symbol = 1;
+      UpdateBit0(prob)
+      prob = p + Literal + (LZMA_LIT_SIZE * 
+        (((
+        (nowPos 
+        #ifdef _LZMA_OUT_READ
+        + globalPos
+        #endif
+        )
+        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
+
+      if (state >= kNumLitStates)
+      {
+        int matchByte;
+        #ifdef _LZMA_OUT_READ
+        UInt32 pos = dictionaryPos - rep0;
+        if (pos >= dictionarySize)
+          pos += dictionarySize;
+        matchByte = dictionary[pos];
+        #else
+        matchByte = outStream[nowPos - rep0];
+        #endif
+        do
+        {
+          int bit;
+          CProb *probLit;
+          matchByte <<= 1;
+          bit = (matchByte & 0x100);
+          probLit = prob + 0x100 + bit + symbol;
+          RC_GET_BIT2(probLit, symbol, if (bit != 0) break, if (bit == 0) break)
+        }
+        while (symbol < 0x100);
+      }
+      while (symbol < 0x100)
+      {
+        CProb *probLit = prob + symbol;
+        RC_GET_BIT(probLit, symbol)
+      }
+      previousByte = (Byte)symbol;
+
+      outStream[nowPos++] = previousByte;
+      #ifdef _LZMA_OUT_READ
+      if (distanceLimit < dictionarySize)
+        distanceLimit++;
+
+      dictionary[dictionaryPos] = previousByte;
+      if (++dictionaryPos == dictionarySize)
+        dictionaryPos = 0;
+      #endif
+      if (state < 4) state = 0;
+      else if (state < 10) state -= 3;
+      else state -= 6;
+    }
+    else             
+    {
+      UpdateBit1(prob);
+      prob = p + IsRep + state;
+      IfBit0(prob)
+      {
+        UpdateBit0(prob);
+        rep3 = rep2;
+        rep2 = rep1;
+        rep1 = rep0;
+        state = state < kNumLitStates ? 0 : 3;
+        prob = p + LenCoder;
+      }
+      else
+      {
+        UpdateBit1(prob);
+        prob = p + IsRepG0 + state;
+        IfBit0(prob)
+        {
+          UpdateBit0(prob);
+          prob = p + IsRep0Long + (state << kNumPosBitsMax) + posState;
+          IfBit0(prob)
+          {
+            #ifdef _LZMA_OUT_READ
+            UInt32 pos;
+            #endif
+            UpdateBit0(prob);
+            
+            #ifdef _LZMA_OUT_READ
+            if (distanceLimit == 0)
+            #else
+            if (nowPos == 0)
+            #endif
+              return LZMA_RESULT_DATA_ERROR;
+            
+            state = state < kNumLitStates ? 9 : 11;
+            #ifdef _LZMA_OUT_READ
+            pos = dictionaryPos - rep0;
+            if (pos >= dictionarySize)
+              pos += dictionarySize;
+            previousByte = dictionary[pos];
+            dictionary[dictionaryPos] = previousByte;
+            if (++dictionaryPos == dictionarySize)
+              dictionaryPos = 0;
+            #else
+            previousByte = outStream[nowPos - rep0];
+            #endif
+            outStream[nowPos++] = previousByte;
+            #ifdef _LZMA_OUT_READ
+            if (distanceLimit < dictionarySize)
+              distanceLimit++;
+            #endif
+
+            continue;
+          }
+          else
+          {
+            UpdateBit1(prob);
+          }
+        }
+        else
+        {
+          UInt32 distance;
+          UpdateBit1(prob);
+          prob = p + IsRepG1 + state;
+          IfBit0(prob)
+          {
+            UpdateBit0(prob);
+            distance = rep1;
+          }
+          else 
+          {
+            UpdateBit1(prob);
+            prob = p + IsRepG2 + state;
+            IfBit0(prob)
+            {
+              UpdateBit0(prob);
+              distance = rep2;
+            }
+            else
+            {
+              UpdateBit1(prob);
+              distance = rep3;
+              rep3 = rep2;
+            }
+            rep2 = rep1;
+          }
+          rep1 = rep0;
+          rep0 = distance;
+        }
+        state = state < kNumLitStates ? 8 : 11;
+        prob = p + RepLenCoder;
+      }
+      {
+        int numBits, offset;
+        CProb *probLen = prob + LenChoice;
+        IfBit0(probLen)
+        {
+          UpdateBit0(probLen);
+          probLen = prob + LenLow + (posState << kLenNumLowBits);
+          offset = 0;
+          numBits = kLenNumLowBits;
+        }
+        else
+        {
+          UpdateBit1(probLen);
+          probLen = prob + LenChoice2;
+          IfBit0(probLen)
+          {
+            UpdateBit0(probLen);
+            probLen = prob + LenMid + (posState << kLenNumMidBits);
+            offset = kLenNumLowSymbols;
+            numBits = kLenNumMidBits;
+          }
+          else
+          {
+            UpdateBit1(probLen);
+            probLen = prob + LenHigh;
+            offset = kLenNumLowSymbols + kLenNumMidSymbols;
+            numBits = kLenNumHighBits;
+          }
+        }
+        RangeDecoderBitTreeDecode(probLen, numBits, len);
+        len += offset;
+      }
+
+      if (state < 4)
+      {
+        int posSlot;
+        state += kNumLitStates;
+        prob = p + PosSlot +
+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 
+            kNumPosSlotBits);
+        RangeDecoderBitTreeDecode(prob, kNumPosSlotBits, posSlot);
+        if (posSlot >= kStartPosModelIndex)
+        {
+          int numDirectBits = ((posSlot >> 1) - 1);
+          rep0 = (2 | ((UInt32)posSlot & 1));
+          if (posSlot < kEndPosModelIndex)
+          {
+            rep0 <<= numDirectBits;
+            prob = p + SpecPos + rep0 - posSlot - 1;
+          }
+          else
+          {
+            numDirectBits -= kNumAlignBits;
+            do
+            {
+              RC_NORMALIZE
+              Range >>= 1;
+              rep0 <<= 1;
+              if (Code >= Range)
+              {
+                Code -= Range;
+                rep0 |= 1;
+              }
+            }
+            while (--numDirectBits != 0);
+            prob = p + Align;
+            rep0 <<= kNumAlignBits;
+            numDirectBits = kNumAlignBits;
+          }
+          {
+            int i = 1;
+            int mi = 1;
+            do
+            {
+              CProb *prob3 = prob + mi;
+              RC_GET_BIT2(prob3, mi, ; , rep0 |= i);
+              i <<= 1;
+            }
+            while(--numDirectBits != 0);
+          }
+        }
+        else
+          rep0 = posSlot;
+        if (++rep0 == (UInt32)(0))
+        {
+          /* it's for stream version */
+          len = kLzmaStreamWasFinishedId;
+          break;
+        }
+      }
+
+      len += kMatchMinLen;
+      #ifdef _LZMA_OUT_READ
+      if (rep0 > distanceLimit) 
+      #else
+      if (rep0 > nowPos)
+      #endif
+        return LZMA_RESULT_DATA_ERROR;
+
+      #ifdef _LZMA_OUT_READ
+      if (dictionarySize - distanceLimit > (UInt32)len)
+        distanceLimit += len;
+      else
+        distanceLimit = dictionarySize;
+      #endif
+
+      do
+      {
+        #ifdef _LZMA_OUT_READ
+        UInt32 pos = dictionaryPos - rep0;
+        if (pos >= dictionarySize)
+          pos += dictionarySize;
+        previousByte = dictionary[pos];
+        dictionary[dictionaryPos] = previousByte;
+        if (++dictionaryPos == dictionarySize)
+          dictionaryPos = 0;
+        #else
+        previousByte = outStream[nowPos - rep0];
+        #endif
+        len--;
+        outStream[nowPos++] = previousByte;
+      }
+      while(len != 0 && nowPos < outSize);
+    }
+  }
+  RC_NORMALIZE;
+
+  #ifdef _LZMA_OUT_READ
+  vs->Range = Range;
+  vs->Code = Code;
+  vs->DictionaryPos = dictionaryPos;
+  vs->GlobalPos = globalPos + (UInt32)nowPos;
+  vs->DistanceLimit = distanceLimit;
+  vs->Reps[0] = rep0;
+  vs->Reps[1] = rep1;
+  vs->Reps[2] = rep2;
+  vs->Reps[3] = rep3;
+  vs->State = state;
+  vs->RemainLen = len;
+  vs->TempDictionary[0] = tempDictionary[0];
+  #endif
+
+  #ifdef _LZMA_IN_CB
+  vs->Buffer = Buffer;
+  vs->BufferLim = BufferLim;
+  #else
+  *inSizeProcessed = (SizeT)(Buffer - inStream);
+  #endif
+  *outSizeProcessed = nowPos;
+  return LZMA_RESULT_OK;
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/LzmaDecode.h b/target/linux/bmips/image/lzma-loader/src/LzmaDecode.h
new file mode 100644
index 0000000..2870eeb
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/LzmaDecode.h
@@ -0,0 +1,113 @@
+/* 
+  LzmaDecode.h
+  LZMA Decoder interface
+
+  LZMA SDK 4.40 Copyright (c) 1999-2006 Igor Pavlov (2006-05-01)
+  http://www.7-zip.org/
+
+  LZMA SDK is licensed under two licenses:
+  1) GNU Lesser General Public License (GNU LGPL)
+  2) Common Public License (CPL)
+  It means that you can select one of these two licenses and 
+  follow rules of that license.
+
+  SPECIAL EXCEPTION:
+  Igor Pavlov, as the author of this code, expressly permits you to 
+  statically or dynamically link your code (or bind by name) to the 
+  interfaces of this file without subjecting your linked code to the 
+  terms of the CPL or GNU LGPL. Any modifications or additions 
+  to this file, however, are subject to the LGPL or CPL terms.
+*/
+
+#ifndef __LZMADECODE_H
+#define __LZMADECODE_H
+
+#include "LzmaTypes.h"
+
+/* #define _LZMA_IN_CB */
+/* Use callback for input data */
+
+/* #define _LZMA_OUT_READ */
+/* Use read function for output data */
+
+/* #define _LZMA_PROB32 */
+/* It can increase speed on some 32-bit CPUs, 
+   but memory usage will be doubled in that case */
+
+/* #define _LZMA_LOC_OPT */
+/* Enable local speed optimizations inside code */
+
+#ifdef _LZMA_PROB32
+#define CProb UInt32
+#else
+#define CProb UInt16
+#endif
+
+#define LZMA_RESULT_OK 0
+#define LZMA_RESULT_DATA_ERROR 1
+
+#ifdef _LZMA_IN_CB
+typedef struct _ILzmaInCallback
+{
+  int (*Read)(void *object, const unsigned char **buffer, SizeT *bufferSize);
+} ILzmaInCallback;
+#endif
+
+#define LZMA_BASE_SIZE 1846
+#define LZMA_LIT_SIZE 768
+
+#define LZMA_PROPERTIES_SIZE 5
+
+typedef struct _CLzmaProperties
+{
+  int lc;
+  int lp;
+  int pb;
+  #ifdef _LZMA_OUT_READ
+  UInt32 DictionarySize;
+  #endif
+}CLzmaProperties;
+
+int LzmaDecodeProperties(CLzmaProperties *propsRes, const unsigned char *propsData, int size);
+
+#define LzmaGetNumProbs(Properties) (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((Properties)->lc + (Properties)->lp)))
+
+#define kLzmaNeedInitId (-2)
+
+typedef struct _CLzmaDecoderState
+{
+  CLzmaProperties Properties;
+  CProb *Probs;
+
+  #ifdef _LZMA_IN_CB
+  const unsigned char *Buffer;
+  const unsigned char *BufferLim;
+  #endif
+
+  #ifdef _LZMA_OUT_READ
+  unsigned char *Dictionary;
+  UInt32 Range;
+  UInt32 Code;
+  UInt32 DictionaryPos;
+  UInt32 GlobalPos;
+  UInt32 DistanceLimit;
+  UInt32 Reps[4];
+  int State;
+  int RemainLen;
+  unsigned char TempDictionary[4];
+  #endif
+} CLzmaDecoderState;
+
+#ifdef _LZMA_OUT_READ
+#define LzmaDecoderInit(vs) { (vs)->RemainLen = kLzmaNeedInitId; }
+#endif
+
+int LzmaDecode(CLzmaDecoderState *vs,
+    #ifdef _LZMA_IN_CB
+    ILzmaInCallback *inCallback,
+    #else
+    const unsigned char *inStream, SizeT inSize, SizeT *inSizeProcessed,
+    #endif
+    unsigned char *outStream, SizeT outSize, SizeT *outSizeProcessed);
+
+#endif
diff --git a/target/linux/bmips/image/lzma-loader/src/LzmaTypes.h b/target/linux/bmips/image/lzma-loader/src/LzmaTypes.h
new file mode 100644
index 0000000..9c27290
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/LzmaTypes.h
@@ -0,0 +1,45 @@
+/* 
+LzmaTypes.h 
+
+Types for LZMA Decoder
+
+This file written and distributed to public domain by Igor Pavlov.
+This file is part of LZMA SDK 4.40 (2006-05-01)
+*/
+
+#ifndef __LZMATYPES_H
+#define __LZMATYPES_H
+
+#ifndef _7ZIP_BYTE_DEFINED
+#define _7ZIP_BYTE_DEFINED
+typedef unsigned char Byte;
+#endif 
+
+#ifndef _7ZIP_UINT16_DEFINED
+#define _7ZIP_UINT16_DEFINED
+typedef unsigned short UInt16;
+#endif 
+
+#ifndef _7ZIP_UINT32_DEFINED
+#define _7ZIP_UINT32_DEFINED
+#ifdef _LZMA_UINT32_IS_ULONG
+typedef unsigned long UInt32;
+#else
+typedef unsigned int UInt32;
+#endif
+#endif 
+
+/* #define _LZMA_NO_SYSTEM_SIZE_T */
+/* You can use it, if you don't want <stddef.h> */
+
+#ifndef _7ZIP_SIZET_DEFINED
+#define _7ZIP_SIZET_DEFINED
+#ifdef _LZMA_NO_SYSTEM_SIZE_T
+typedef UInt32 SizeT;
+#else
+#include <stddef.h>
+typedef size_t SizeT;
+#endif
+#endif
+
+#endif
diff --git a/target/linux/bmips/image/lzma-loader/src/Makefile b/target/linux/bmips/image/lzma-loader/src/Makefile
new file mode 100644
index 0000000..50c22d8
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/Makefile
@@ -0,0 +1,86 @@
+#
+# Makefile for the LZMA compressed kernel loader for
+# Atheros AR7XXX/AR9XXX based boards
+#
+# Copyright (C) 2011 Gabor Juhos <juhosg at openwrt.org>
+#
+# Some parts of this file was based on the OpenWrt specific lzma-loader
+# for the BCM47xx and ADM5120 based boards:
+#	Copyright (C) 2004 Manuel Novoa III (mjn3 at codepoet.org)
+#	Copyright (C) 2005 Mineharu Takahara <mtakahar at yahoo.com>
+#	Copyright (C) 2005 by Oleg I. Vdovikin <oleg at cs.msu.su>
+#
+# 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.
+#
+
+LOADADDR	:=
+LZMA_TEXT_START	:= 0x80a00000
+LOADER_DATA	:=
+
+CC		:= $(CROSS_COMPILE)gcc
+LD		:= $(CROSS_COMPILE)ld
+OBJCOPY		:= $(CROSS_COMPILE)objcopy
+OBJDUMP		:= $(CROSS_COMPILE)objdump
+
+BIN_FLAGS	:= -O binary -R .reginfo -R .note -R .comment -R .mdebug -S
+
+CFLAGS		= -D__KERNEL__ -Wall -Wstrict-prototypes -Wno-trigraphs -Os \
+		  -fno-strict-aliasing -fno-common -fomit-frame-pointer -G 0 \
+		  -mno-abicalls -fno-pic -ffunction-sections -pipe \
+		  -ffreestanding -fhonour-copts \
+		  -mabi=32 -march=mips32 \
+		  -Wa,-32 -Wa,-march=mips32 -Wa,-mips32 -Wa,--trap
+CFLAGS		+= -D_LZMA_PROB32
+
+ASFLAGS		= $(CFLAGS) -D__ASSEMBLY__
+
+LDFLAGS		= -static --gc-sections -no-warn-mismatch
+LDFLAGS		+= -e startup -T loader.lds -Ttext $(LZMA_TEXT_START)
+
+O_FORMAT 	= $(shell $(OBJDUMP) -i | head -2 | grep elf32)
+
+OBJECTS		:= head.o loader.o cache.o board.o printf.o LzmaDecode.o
+
+ifneq ($(strip $(LOADER_DATA)),)
+OBJECTS		+= data.o
+CFLAGS		+= -DLZMA_WRAPPER=1 -DLOADADDR=$(LOADADDR)
+endif
+
+
+all: loader.elf
+
+# Don't build dependencies, this may die if $(CC) isn't gcc
+dep:
+
+install:
+
+%.o : %.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+%.o : %.S
+	$(CC) $(ASFLAGS) -c -o $@ $<
+
+data.o: $(LOADER_DATA)
+	$(LD) -r -b binary --oformat $(O_FORMAT) -T lzma-data.lds -o $@ $<
+
+loader: $(OBJECTS)
+	$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
+
+loader.bin: loader
+	$(OBJCOPY) $(BIN_FLAGS) $< $@
+
+loader2.o: loader.bin
+	$(LD) -r -b binary --oformat $(O_FORMAT) -o $@ $<
+
+loader.elf: loader2.o
+	$(LD) -e startup -T loader2.lds -Ttext $(LOADADDR) -o $@ $<
+
+mrproper: clean
+
+clean:
+	rm -f loader *.elf *.bin *.o
+
+
+
diff --git a/target/linux/bmips/image/lzma-loader/src/board.c b/target/linux/bmips/image/lzma-loader/src/board.c
new file mode 100644
index 0000000..1c715e3
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/board.c
@@ -0,0 +1,103 @@
+/*
+ * BCM63XX specific implementation parts
+ *
+ * Copyright (C) 2014 Jonas Gorski <jogo at 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 <stddef.h>
+#include "config.h"
+#include "cp0regdef.h"
+
+#define READREG(r)	*(volatile unsigned int *)(r)
+#define WRITEREG(r,v)	*(volatile unsigned int *)(r) = v
+
+#define UART_IR_REG	0x10
+#define UART_FIFO_REG	0x14
+
+unsigned long uart_base;
+
+static void wait_xfered(void)
+{
+        unsigned int val;
+
+        do {
+                val = READREG(uart_base + UART_IR_REG);
+                if (val & (1 << 5))
+                        break;
+        } while (1);
+}
+
+void board_putc(int ch)
+{
+	if (!uart_base)
+		return;
+
+	wait_xfered();
+        WRITEREG(uart_base + UART_FIFO_REG, ch);
+	wait_xfered();
+}
+
+#define PRID_IMP_BMIPS32_REV4	0x4000
+#define PRID_IMP_BMIPS32_REV8	0x8000
+#define PRID_IMP_BMIPS3300	0x9000
+#define PRID_IMP_BMIPS3300_ALT	0x9100
+#define PRID_IMP_BMIPS3300_BUG	0x0000
+#define PRID_IMP_BMIPS43XX	0xa000
+
+void board_init(void)
+{
+	unsigned long prid, chipid, chipid_reg;
+
+	prid = read_32bit_c0_register($15, 0);
+
+	switch (prid & 0xff00) {
+	case PRID_IMP_BMIPS32_REV4:
+	case PRID_IMP_BMIPS32_REV8:
+	case PRID_IMP_BMIPS3300_ALT:
+	case PRID_IMP_BMIPS3300_BUG:
+		chipid_reg = 0xfffe0000;
+		break;
+	case PRID_IMP_BMIPS3300:
+		if ((prid & 0xff) >= 0x33)
+			chipid_reg = 0xb0000000;
+		else
+			chipid_reg = 0xfffe0000;
+		break;
+	case PRID_IMP_BMIPS43XX:
+		if ((prid & 0xff) >= 0x30)
+			chipid_reg = 0xb0000000;
+		else
+			chipid_reg = 0xfffe0000;
+		break;
+	default:
+		return;
+	}
+
+	chipid = READREG(chipid_reg);
+
+	switch (chipid >> 16) {
+	case 0x6318:
+	case 0x6328:
+	case 0x6358:
+	case 0x6362:
+	case 0x6368:
+	case 0x6369:
+		uart_base = chipid_reg + 0x100;
+		break;
+	case 0x6316:
+	case 0x6326:
+		uart_base = chipid_reg + 0x180;
+		break;
+	case 0x6338:
+	case 0x6345:
+	case 0x6348:
+		uart_base = chipid_reg + 0x300;
+		break;
+	default:
+		return;
+	}
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/cache.c b/target/linux/bmips/image/lzma-loader/src/cache.c
new file mode 100644
index 0000000..93751c3
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/cache.c
@@ -0,0 +1,46 @@
+/*
+ * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg at openwrt.org>
+ *
+ * The cache manipulation routine has been taken from the U-Boot project.
+ *	(C) Copyright 2003
+ *	Wolfgang Denk, DENX Software Engineering, <wd at denx.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.
+ *
+ */
+
+#include "cache.h"
+#include "cacheops.h"
+#include "config.h"
+#include "printf.h"
+
+#define cache_op(op,addr)						\
+	__asm__ __volatile__(						\
+	"	.set	push					\n"	\
+	"	.set	noreorder				\n"	\
+	"	.set	mips3\n\t				\n"	\
+	"	cache	%0, %1					\n"	\
+	"	.set	pop					\n"	\
+	:								\
+	: "i" (op), "R" (*(unsigned char *)(addr)))
+
+void flush_cache(unsigned long start_addr, unsigned long size)
+{
+	unsigned long lsize = CONFIG_CACHELINE_SIZE;
+	unsigned long addr = start_addr & ~(lsize - 1);
+	unsigned long aend = (start_addr + size + (lsize - 1)) & ~(lsize - 1);
+
+	printf("blasting from 0x%08x to 0x%08x (0x%08x - 0x%08x)\n", start_addr, size, addr, aend);
+
+	while (1) {
+		cache_op(Hit_Writeback_Inv_D, addr);
+		cache_op(Hit_Invalidate_I, addr);
+		if (addr == aend)
+			break;
+		addr += lsize;
+	}
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/cache.h b/target/linux/bmips/image/lzma-loader/src/cache.h
new file mode 100644
index 0000000..506a235
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/cache.h
@@ -0,0 +1,17 @@
+/*
+ * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg at 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 __CACHE_H
+#define __CACHE_H
+
+void flush_cache(unsigned long start_addr, unsigned long size);
+
+#endif /* __CACHE_H */
diff --git a/target/linux/bmips/image/lzma-loader/src/cacheops.h b/target/linux/bmips/image/lzma-loader/src/cacheops.h
new file mode 100644
index 0000000..70bcad7
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/cacheops.h
@@ -0,0 +1,85 @@
+/*
+ * Cache operations for the cache instruction.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * (C) Copyright 1996, 97, 99, 2002, 03 Ralf Baechle
+ * (C) Copyright 1999 Silicon Graphics, Inc.
+ */
+#ifndef	__ASM_CACHEOPS_H
+#define	__ASM_CACHEOPS_H
+
+/*
+ * Cache Operations available on all MIPS processors with R4000-style caches
+ */
+#define Index_Invalidate_I      0x00
+#define Index_Writeback_Inv_D   0x01
+#define Index_Load_Tag_I	0x04
+#define Index_Load_Tag_D	0x05
+#define Index_Store_Tag_I	0x08
+#define Index_Store_Tag_D	0x09
+#if defined(CONFIG_CPU_LOONGSON2)
+#define Hit_Invalidate_I	0x00
+#else
+#define Hit_Invalidate_I	0x10
+#endif
+#define Hit_Invalidate_D	0x11
+#define Hit_Writeback_Inv_D	0x15
+
+/*
+ * R4000-specific cacheops
+ */
+#define Create_Dirty_Excl_D	0x0d
+#define Fill			0x14
+#define Hit_Writeback_I		0x18
+#define Hit_Writeback_D		0x19
+
+/*
+ * R4000SC and R4400SC-specific cacheops
+ */
+#define Index_Invalidate_SI     0x02
+#define Index_Writeback_Inv_SD  0x03
+#define Index_Load_Tag_SI	0x06
+#define Index_Load_Tag_SD	0x07
+#define Index_Store_Tag_SI	0x0A
+#define Index_Store_Tag_SD	0x0B
+#define Create_Dirty_Excl_SD	0x0f
+#define Hit_Invalidate_SI	0x12
+#define Hit_Invalidate_SD	0x13
+#define Hit_Writeback_Inv_SD	0x17
+#define Hit_Writeback_SD	0x1b
+#define Hit_Set_Virtual_SI	0x1e
+#define Hit_Set_Virtual_SD	0x1f
+
+/*
+ * R5000-specific cacheops
+ */
+#define R5K_Page_Invalidate_S	0x17
+
+/*
+ * RM7000-specific cacheops
+ */
+#define Page_Invalidate_T	0x16
+
+/*
+ * R10000-specific cacheops
+ *
+ * Cacheops 0x02, 0x06, 0x0a, 0x0c-0x0e, 0x16, 0x1a and 0x1e are unused.
+ * Most of the _S cacheops are identical to the R4000SC _SD cacheops.
+ */
+#define Index_Writeback_Inv_S	0x03
+#define Index_Load_Tag_S	0x07
+#define Index_Store_Tag_S	0x0B
+#define Hit_Invalidate_S	0x13
+#define Cache_Barrier		0x14
+#define Hit_Writeback_Inv_S	0x17
+#define Index_Load_Data_I	0x18
+#define Index_Load_Data_D	0x19
+#define Index_Load_Data_S	0x1b
+#define Index_Store_Data_I	0x1c
+#define Index_Store_Data_D	0x1d
+#define Index_Store_Data_S	0x1f
+
+#endif	/* __ASM_CACHEOPS_H */
diff --git a/target/linux/bmips/image/lzma-loader/src/config.h b/target/linux/bmips/image/lzma-loader/src/config.h
new file mode 100644
index 0000000..ce391f8
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/config.h
@@ -0,0 +1,31 @@
+/*
+ * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg at 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 _CONFIG_H_
+#define _CONFIG_H_
+
+#define CONFIG_ICACHE_SIZE	(32 * 1024)
+#define CONFIG_DCACHE_SIZE	(32 * 1024)
+#define CONFIG_CACHELINE_SIZE	16
+
+#ifndef CONFIG_FLASH_OFFS
+#define CONFIG_FLASH_OFFS	0
+#endif
+
+#ifndef CONFIG_FLASH_MAX
+#define CONFIG_FLASH_MAX	0
+#endif
+
+#ifndef CONFIG_FLASH_STEP
+#define CONFIG_FLASH_STEP	0x1000
+#endif
+
+#endif /* _CONFIG_H_ */
diff --git a/target/linux/bmips/image/lzma-loader/src/cp0regdef.h b/target/linux/bmips/image/lzma-loader/src/cp0regdef.h
new file mode 100644
index 0000000..0d824f4
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/cp0regdef.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 1994, 1995, 1996, 1997, 2000, 2001 by Ralf Baechle
+ *
+ * Copyright (C) 2001, Monta Vista Software
+ * Author: jsun at mvista.com or jsun at junsun.net
+ */
+#ifndef _cp0regdef_h_
+#define _cp0regdef_h_
+
+#define CP0_INDEX $0
+#define CP0_RANDOM $1
+#define CP0_ENTRYLO0 $2
+#define CP0_ENTRYLO1 $3
+#define CP0_CONTEXT $4
+#define CP0_PAGEMASK $5
+#define CP0_WIRED $6
+#define CP0_BADVADDR $8
+#define CP0_COUNT $9
+#define CP0_ENTRYHI $10
+#define CP0_COMPARE $11
+#define CP0_STATUS $12
+#define CP0_CAUSE $13
+#define CP0_EPC $14
+#define CP0_PRID $15
+#define CP0_CONFIG $16
+#define CP0_LLADDR $17
+#define CP0_WATCHLO $18
+#define CP0_WATCHHI $19
+#define CP0_XCONTEXT $20
+#define CP0_FRAMEMASK $21
+#define CP0_DIAGNOSTIC $22
+#define CP0_PERFORMANCE $25
+#define CP0_ECC $26
+#define CP0_CACHEERR $27
+#define CP0_TAGLO $28
+#define CP0_TAGHI $29
+#define CP0_ERROREPC $30
+
+#define read_32bit_c0_register(reg,sel)					\
+({	int __res;							\
+	if (sel == 0)							\
+		__asm__ __volatile__(					\
+			"mfc0\t%0, " #reg "\n\t"			\
+			: "=r" (__res));				\
+	else								\
+		__asm__ __volatile__(					\
+			".set\tmips32\n\t"				\
+			"mfc0\t%0, " #reg ", " #sel "\n\t"		\
+			".set mips0\n\t"				\
+			: "=r" (__res));				\
+	__res;								\
+})
+
+#endif
diff --git a/target/linux/bmips/image/lzma-loader/src/head.S b/target/linux/bmips/image/lzma-loader/src/head.S
new file mode 100644
index 0000000..543996a
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/head.S
@@ -0,0 +1,118 @@
+/*
+ * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg at openwrt.org>
+ *
+ * Some parts of this code was based on the OpenWrt specific lzma-loader
+ * for the BCM47xx and ADM5120 based boards:
+ *	Copyright (C) 2004 Manuel Novoa III (mjn3 at codepoet.org)
+ *	Copyright (C) 2005 by Oleg I. Vdovikin <oleg at cs.msu.su>
+ *
+ * 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 <asm/asm.h>
+#include <asm/regdef.h>
+#include "cp0regdef.h"
+#include "cacheops.h"
+#include "config.h"
+
+#define KSEG0		0x80000000
+
+	.macro	ehb
+	sll     zero, 3
+	.endm
+
+	.text
+
+LEAF(startup)
+	.set noreorder
+	.set mips32
+
+	mtc0	zero, CP0_WATCHLO	# clear watch registers
+	mtc0	zero, CP0_WATCHHI
+	mtc0	zero, CP0_CAUSE		# clear before writing status register
+
+	mfc0	t0, CP0_STATUS
+	li	t1, 0x1000001f
+	or	t0, t1
+	xori	t0, 0x1f
+	mtc0	t0, CP0_STATUS
+	ehb
+
+	mtc0	zero, CP0_COUNT
+	mtc0	zero, CP0_COMPARE
+	ehb
+
+	la	t0, __reloc_label	# get linked address of label
+	bal	__reloc_label		# branch and link to label to
+	nop				# get actual address
+__reloc_label:
+	subu	t0, ra, t0		# get reloc_delta
+
+	beqz	t0, __reloc_done         # if delta is 0 we are in the right place
+	nop
+
+	/* Copy our code to the right place */
+	la	t1, _code_start		# get linked address of _code_start
+	la	t2, _code_end		# get linked address of _code_end
+	addu	t0, t0, t1		# calculate actual address of _code_start
+
+__reloc_copy:
+	lw	t3, 0(t0)
+	sw	t3, 0(t1)
+	add	t1, 4
+	blt	t1, t2, __reloc_copy
+	add	t0, 4
+
+	/* flush cache */
+	la	t0, _code_start
+	la	t1, _code_end
+
+	li	t2, ~(CONFIG_CACHELINE_SIZE - 1)
+	and	t0, t2
+	and	t1, t2
+	li	t2, CONFIG_CACHELINE_SIZE
+
+	b	__flush_check
+	nop
+
+__flush_line:
+	cache	Hit_Writeback_Inv_D, 0(t0)
+	cache	Hit_Invalidate_I, 0(t0)
+	add	t0, t2
+
+__flush_check:
+	bne	t0, t1, __flush_line
+	nop
+
+	sync
+
+__reloc_done:
+
+	/* clear bss */
+	la	t0, _bss_start
+	la	t1, _bss_end
+	b	__bss_check
+	nop
+
+__bss_fill:
+	sw	zero, 0(t0)
+	addi	t0, 4
+
+__bss_check:
+	bne	t0, t1, __bss_fill
+	nop
+
+	/* Setup new "C" stack */
+	la	sp, _stack
+
+	/* jump to the decompressor routine */
+	la	t0, loader_main
+	jr	t0
+	nop
+
+	.set reorder
+END(startup)
diff --git a/target/linux/bmips/image/lzma-loader/src/loader.c b/target/linux/bmips/image/lzma-loader/src/loader.c
new file mode 100644
index 0000000..0848ce6
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/loader.c
@@ -0,0 +1,175 @@
+/*
+ * LZMA compressed kernel loader for Atheros AR7XXX/AR9XXX based boards
+ *
+ * Copyright (C) 2011 Gabor Juhos <juhosg at openwrt.org>
+ *
+ * Some parts of this code was based on the OpenWrt specific lzma-loader
+ * for the BCM47xx and ADM5120 based boards:
+ *	Copyright (C) 2004 Manuel Novoa III (mjn3 at codepoet.org)
+ *	Copyright (C) 2005 Mineharu Takahara <mtakahar at yahoo.com>
+ *	Copyright (C) 2005 by Oleg I. Vdovikin <oleg at cs.msu.su>
+ *
+ * The image_header structure has been taken from the U-Boot project.
+ *	(C) Copyright 2008 Semihalf
+ *	(C) Copyright 2000-2005
+ *	Wolfgang Denk, DENX Software Engineering, wd at denx.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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "cache.h"
+#include "printf.h"
+#include "LzmaDecode.h"
+
+#define KSEG0			0x80000000
+#define KSEG1			0xa0000000
+
+#define KSEG1ADDR(a)		((((unsigned)(a)) & 0x1fffffffU) | KSEG1)
+
+#undef LZMA_DEBUG
+
+#ifdef LZMA_DEBUG
+#  define DBG(f, a...)	printf(f, ## a)
+#else
+#  define DBG(f, a...)	do {} while (0)
+#endif
+
+/* beyond the image end, size not known in advance */
+extern unsigned char workspace[];
+
+
+extern void board_init(void);
+
+static CLzmaDecoderState lzma_state;
+static unsigned char *lzma_data;
+static unsigned long lzma_datasize;
+static unsigned long lzma_outsize;
+static unsigned long kernel_la;
+
+static void halt(void)
+{
+	printf("\nSystem halted!\n");
+	for(;;);
+}
+
+static __inline__ unsigned char lzma_get_byte(void)
+{
+	unsigned char c;
+
+	lzma_datasize--;
+	c = *lzma_data++;
+
+	return c;
+}
+
+static int lzma_init_props(void)
+{
+	unsigned char props[LZMA_PROPERTIES_SIZE];
+	int res;
+	int i;
+
+	/* read lzma properties */
+	for (i = 0; i < LZMA_PROPERTIES_SIZE; i++)
+		props[i] = lzma_get_byte();
+
+	/* read the lower half of uncompressed size in the header */
+	lzma_outsize = ((SizeT) lzma_get_byte()) +
+		       ((SizeT) lzma_get_byte() << 8) +
+		       ((SizeT) lzma_get_byte() << 16) +
+		       ((SizeT) lzma_get_byte() << 24);
+
+	/* skip rest of the header (upper half of uncompressed size) */
+	for (i = 0; i < 4; i++)
+		lzma_get_byte();
+
+	res = LzmaDecodeProperties(&lzma_state.Properties, props,
+					LZMA_PROPERTIES_SIZE);
+	return res;
+}
+
+static int lzma_decompress(unsigned char *outStream)
+{
+	SizeT ip, op;
+	int ret;
+
+	lzma_state.Probs = (CProb *) workspace;
+
+	ret = LzmaDecode(&lzma_state, lzma_data, lzma_datasize, &ip, outStream,
+			 lzma_outsize, &op);
+
+	if (ret != LZMA_RESULT_OK) {
+		int i;
+
+		DBG("LzmaDecode error %d at %08x, osize:%d ip:%d op:%d\n",
+		    ret, lzma_data + ip, lzma_outsize, ip, op);
+
+		for (i = 0; i < 16; i++)
+			DBG("%02x ", lzma_data[ip + i]);
+
+		DBG("\n");
+	}
+
+	return ret;
+}
+
+static void lzma_init_data(void)
+{
+	extern unsigned char _lzma_data_start[];
+	extern unsigned char _lzma_data_end[];
+
+	kernel_la = LOADADDR;
+	lzma_data = _lzma_data_start;
+	lzma_datasize = _lzma_data_end - _lzma_data_start;
+}
+
+void loader_main(unsigned long reg_a0, unsigned long reg_a1,
+		 unsigned long reg_a2, unsigned long reg_a3)
+{
+	void (*kernel_entry) (unsigned long, unsigned long, unsigned long,
+			      unsigned long);
+	int res;
+
+	board_init();
+
+	printf("\n\nOpenWrt kernel loader for BCM63XX\n");
+	printf("Copyright (C) 2011 Gabor Juhos <juhosg at openwrt.org>\n");
+	printf("Copyright (C) 2014 Jonas Gorski <jogo at openwrt.org>\n");
+
+	lzma_init_data();
+
+	res = lzma_init_props();
+	if (res != LZMA_RESULT_OK) {
+		printf("Incorrect LZMA stream properties!\n");
+		halt();
+	}
+
+	printf("Decompressing kernel... ");
+
+	res = lzma_decompress((unsigned char *) kernel_la);
+	if (res != LZMA_RESULT_OK) {
+		printf("failed, ");
+		switch (res) {
+		case LZMA_RESULT_DATA_ERROR:
+			printf("data error!\n");
+			break;
+		default:
+			printf("unknown error %d!\n", res);
+		}
+		halt();
+	} else {
+		printf("done!\n");
+	}
+
+	flush_cache(kernel_la, lzma_outsize);
+
+	printf("Starting kernel at %08x...\n\n", kernel_la);
+
+	kernel_entry = (void *) kernel_la;
+	kernel_entry(reg_a0, reg_a1, reg_a2, reg_a3);
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/loader.lds b/target/linux/bmips/image/lzma-loader/src/loader.lds
new file mode 100644
index 0000000..01ff852
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/loader.lds
@@ -0,0 +1,34 @@
+OUTPUT_ARCH(mips)
+SECTIONS {
+	.text : {
+		_code_start = .;
+		*(.text)
+		*(.text.*)
+		*(.rodata)
+		*(.rodata.*)
+		*(.data.lzma)
+	}
+
+	. = ALIGN(32);
+	.data : {
+		*(.data)
+		*(.data.*)
+	}
+
+	. = ALIGN(32);
+	_code_end = .;
+
+	_bss_start = .;
+	.bss : {
+		*(.bss)
+		*(.bss.*)
+	}
+
+	. = ALIGN(32);
+	_bss_end = .;
+
+	. = . + 8192;
+	_stack = .;
+
+	workspace = .;
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/loader2.lds b/target/linux/bmips/image/lzma-loader/src/loader2.lds
new file mode 100644
index 0000000..db0bb46
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/loader2.lds
@@ -0,0 +1,10 @@
+OUTPUT_ARCH(mips)
+SECTIONS {
+	.text : {
+		startup = .;
+		*(.text)
+		*(.text.*)
+		*(.data)
+		*(.data.*)
+	}
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/lzma-data.lds b/target/linux/bmips/image/lzma-loader/src/lzma-data.lds
new file mode 100644
index 0000000..abf756b
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/lzma-data.lds
@@ -0,0 +1,8 @@
+OUTPUT_ARCH(mips)
+SECTIONS {
+	.data.lzma : {
+		_lzma_data_start = .;
+		*(.data)
+		_lzma_data_end = .;
+	}
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/printf.c b/target/linux/bmips/image/lzma-loader/src/printf.c
new file mode 100644
index 0000000..7bb5a86
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/printf.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2001 MontaVista Software Inc.
+ * Author: Jun Sun, jsun at mvista.com or jsun at junsun.net
+ *
+ * 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.
+ *
+ */
+
+#include	"printf.h"
+
+extern void board_putc(int ch);
+
+/* this is the maximum width for a variable */
+#define		LP_MAX_BUF	256
+
+/* macros */
+#define		IsDigit(x)	( ((x) >= '0') && ((x) <= '9') )
+#define		Ctod(x)		( (x) - '0')
+
+/* forward declaration */
+static int PrintChar(char *, char, int, int);
+static int PrintString(char *, char *, int, int);
+static int PrintNum(char *, unsigned long, int, int, int, int, char, int);
+
+/* private variable */
+static const char theFatalMsg[] = "fatal error in lp_Print!";
+
+/* -*-
+ * A low level printf() function.
+ */
+static void
+lp_Print(void (*output)(void *, char *, int),
+	 void * arg,
+	 char *fmt,
+	 va_list ap)
+{
+
+#define 	OUTPUT(arg, s, l)  \
+  { if (((l) < 0) || ((l) > LP_MAX_BUF)) { \
+       (*output)(arg, (char*)theFatalMsg, sizeof(theFatalMsg)-1); for(;;); \
+    } else { \
+      (*output)(arg, s, l); \
+    } \
+  }
+
+    char buf[LP_MAX_BUF];
+
+    char c;
+    char *s;
+    long int num;
+
+    int longFlag;
+    int negFlag;
+    int width;
+    int prec;
+    int ladjust;
+    char padc;
+
+    int length;
+
+    for(;;) {
+	{
+	    /* scan for the next '%' */
+	    char *fmtStart = fmt;
+	    while ( (*fmt != '\0') && (*fmt != '%')) {
+		fmt ++;
+	    }
+
+	    /* flush the string found so far */
+	    OUTPUT(arg, fmtStart, fmt-fmtStart);
+
+	    /* are we hitting the end? */
+	    if (*fmt == '\0') break;
+	}
+
+	/* we found a '%' */
+	fmt ++;
+
+	/* check for long */
+	if (*fmt == 'l') {
+	    longFlag = 1;
+	    fmt ++;
+	} else {
+	    longFlag = 0;
+	}
+
+	/* check for other prefixes */
+	width = 0;
+	prec = -1;
+	ladjust = 0;
+	padc = ' ';
+
+	if (*fmt == '-') {
+	    ladjust = 1;
+	    fmt ++;
+	}
+
+	if (*fmt == '0') {
+	    padc = '0';
+	    fmt++;
+	}
+
+	if (IsDigit(*fmt)) {
+	    while (IsDigit(*fmt)) {
+		width = 10 * width + Ctod(*fmt++);
+	    }
+	}
+
+	if (*fmt == '.') {
+	    fmt ++;
+	    if (IsDigit(*fmt)) {
+		prec = 0;
+		while (IsDigit(*fmt)) {
+		    prec = prec*10 + Ctod(*fmt++);
+		}
+	    }
+	}
+
+
+	/* check format flag */
+	negFlag = 0;
+	switch (*fmt) {
+	 case 'b':
+	    if (longFlag) {
+		num = va_arg(ap, long int);
+	    } else {
+		num = va_arg(ap, int);
+	    }
+	    length = PrintNum(buf, num, 2, 0, width, ladjust, padc, 0);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 'd':
+	 case 'D':
+	    if (longFlag) {
+		num = va_arg(ap, long int);
+	    } else {
+		num = va_arg(ap, int);
+	    }
+	    if (num < 0) {
+		num = - num;
+		negFlag = 1;
+	    }
+	    length = PrintNum(buf, num, 10, negFlag, width, ladjust, padc, 0);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 'o':
+	 case 'O':
+	    if (longFlag) {
+		num = va_arg(ap, long int);
+	    } else {
+		num = va_arg(ap, int);
+	    }
+	    length = PrintNum(buf, num, 8, 0, width, ladjust, padc, 0);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 'u':
+	 case 'U':
+	    if (longFlag) {
+		num = va_arg(ap, long int);
+	    } else {
+		num = va_arg(ap, int);
+	    }
+	    length = PrintNum(buf, num, 10, 0, width, ladjust, padc, 0);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 'x':
+	    if (longFlag) {
+		num = va_arg(ap, long int);
+	    } else {
+		num = va_arg(ap, int);
+	    }
+	    length = PrintNum(buf, num, 16, 0, width, ladjust, padc, 0);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 'X':
+	    if (longFlag) {
+		num = va_arg(ap, long int);
+	    } else {
+		num = va_arg(ap, int);
+	    }
+	    length = PrintNum(buf, num, 16, 0, width, ladjust, padc, 1);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 'c':
+	    c = (char)va_arg(ap, int);
+	    length = PrintChar(buf, c, width, ladjust);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case 's':
+	    s = (char*)va_arg(ap, char *);
+	    length = PrintString(buf, s, width, ladjust);
+	    OUTPUT(arg, buf, length);
+	    break;
+
+	 case '\0':
+	    fmt --;
+	    break;
+
+	 default:
+	    /* output this char as it is */
+	    OUTPUT(arg, fmt, 1);
+	}	/* switch (*fmt) */
+
+	fmt ++;
+    }		/* for(;;) */
+
+    /* special termination call */
+    OUTPUT(arg, "\0", 1);
+}
+
+
+/* --------------- local help functions --------------------- */
+static int
+PrintChar(char * buf, char c, int length, int ladjust)
+{
+    int i;
+
+    if (length < 1) length = 1;
+    if (ladjust) {
+	*buf = c;
+	for (i=1; i< length; i++) buf[i] = ' ';
+    } else {
+	for (i=0; i< length-1; i++) buf[i] = ' ';
+	buf[length - 1] = c;
+    }
+    return length;
+}
+
+static int
+PrintString(char * buf, char* s, int length, int ladjust)
+{
+    int i;
+    int len=0;
+    char* s1 = s;
+    while (*s1++) len++;
+    if (length < len) length = len;
+
+    if (ladjust) {
+	for (i=0; i< len; i++) buf[i] = s[i];
+	for (i=len; i< length; i++) buf[i] = ' ';
+    } else {
+	for (i=0; i< length-len; i++) buf[i] = ' ';
+	for (i=length-len; i < length; i++) buf[i] = s[i-length+len];
+    }
+    return length;
+}
+
+static int
+PrintNum(char * buf, unsigned long u, int base, int negFlag,
+	 int length, int ladjust, char padc, int upcase)
+{
+    /* algorithm :
+     *  1. prints the number from left to right in reverse form.
+     *  2. fill the remaining spaces with padc if length is longer than
+     *     the actual length
+     *     TRICKY : if left adjusted, no "0" padding.
+     *		    if negtive, insert  "0" padding between "0" and number.
+     *  3. if (!ladjust) we reverse the whole string including paddings
+     *  4. otherwise we only reverse the actual string representing the num.
+     */
+
+    int actualLength =0;
+    char *p = buf;
+    int i;
+
+    do {
+	int tmp = u %base;
+	if (tmp <= 9) {
+	    *p++ = '0' + tmp;
+	} else if (upcase) {
+	    *p++ = 'A' + tmp - 10;
+	} else {
+	    *p++ = 'a' + tmp - 10;
+	}
+	u /= base;
+    } while (u != 0);
+
+    if (negFlag) {
+	*p++ = '-';
+    }
+
+    /* figure out actual length and adjust the maximum length */
+    actualLength = p - buf;
+    if (length < actualLength) length = actualLength;
+
+    /* add padding */
+    if (ladjust) {
+	padc = ' ';
+    }
+    if (negFlag && !ladjust && (padc == '0')) {
+	for (i = actualLength-1; i< length-1; i++) buf[i] = padc;
+	buf[length -1] = '-';
+    } else {
+	for (i = actualLength; i< length; i++) buf[i] = padc;
+    }
+
+
+    /* prepare to reverse the string */
+    {
+	int begin = 0;
+	int end;
+	if (ladjust) {
+	    end = actualLength - 1;
+	} else {
+	    end = length -1;
+	}
+
+	while (end > begin) {
+	    char tmp = buf[begin];
+	    buf[begin] = buf[end];
+	    buf[end] = tmp;
+	    begin ++;
+	    end --;
+	}
+    }
+
+    /* adjust the string pointer */
+    return length;
+}
+
+static void printf_output(void *arg, char *s, int l)
+{
+    int i;
+
+    // special termination call
+    if ((l==1) && (s[0] == '\0')) return;
+
+    for (i=0; i< l; i++) {
+	board_putc(s[i]);
+	if (s[i] == '\n') board_putc('\r');
+    }
+}
+
+void printf(char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    lp_Print(printf_output, 0, fmt, ap);
+    va_end(ap);
+}
diff --git a/target/linux/bmips/image/lzma-loader/src/printf.h b/target/linux/bmips/image/lzma-loader/src/printf.h
new file mode 100644
index 0000000..9b1c1df
--- /dev/null
+++ b/target/linux/bmips/image/lzma-loader/src/printf.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2001 MontaVista Software Inc.
+ * Author: Jun Sun, jsun at mvista.com or jsun at junsun.net
+ *
+ * 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.
+ *
+ */
+
+#ifndef _printf_h_
+#define _printf_h_
+
+#include <stdarg.h>
+void printf(char *fmt, ...);
+
+#endif /* _printf_h_ */
diff --git a/target/linux/bmips/patches-4.1/001-MIPS-Add-support-for-vmlinux.bin-appended-dtb.patch b/target/linux/bmips/patches-4.1/001-MIPS-Add-support-for-vmlinux.bin-appended-dtb.patch
new file mode 100644
index 0000000..fa7732b
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/001-MIPS-Add-support-for-vmlinux.bin-appended-dtb.patch
@@ -0,0 +1,112 @@
+From 1da8f1798e307fb8422753984339beb00025f97d Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo at openwrt.org>
+Date: Sun, 12 Apr 2015 12:24:58 +0200
+Subject: [PATCH] MIPS: Add support for vmlinux.bin appended dtb
+
+Add support for detecting a vmlinux.bin appended dtb and overriding
+the boot arguments to match the UHI interface.
+
+Due to the PERCPU section being empty for !SMP, but still modifying
+the current address by aligning it to the page size, do not define
+it for !SMP builds to allow __appended_dtb to still point to
+the actual end of the data.
+
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+Cc: linux-mips at linux-mips.org
+Cc: devicetree at vger.kernel.org
+Cc: John Crispin <blogic at openwrt.org>
+Cc: Kevin Cernekee <cernekee at gmail.com>
+Cc: Florian Fainelli <f.fainelli at gmail.com>
+Cc: Aaro Koskinen <aaro.koskinen at iki.fi>
+Cc: Markos Chandras <markos.chandras at imgtec.com>
+Cc: Andrew Bresticker <abrestic at chromium.org>
+Cc: Daniel Schwierzeck <daniel.schwierzeck at gmail.com>
+Cc: Paul Burton <paul.burton at imgtec.com>
+Cc: James Hartley <James.Hartley at imgtec.com>
+Patchwork: https://patchwork.linux-mips.org/patch/9739/
+Signed-off-by: Ralf Baechle <ralf at linux-mips.org>
+---
+ arch/mips/Kconfig              | 27 +++++++++++++++++++++++++++
+ arch/mips/kernel/head.S        | 16 ++++++++++++++++
+ arch/mips/kernel/vmlinux.lds.S |  8 +++++++-
+ 3 files changed, 50 insertions(+), 1 deletion(-)
+
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -2703,6 +2703,33 @@ config BOOT_RAW
+ 
+ 
+ 
++choice
++	prompt "Kernel appended dtb support" if OF
++	default MIPS_NO_APPENDED_DTB
++
++	config MIPS_NO_APPENDED_DTB
++		bool "None"
++		help
++		  Do not enable appended dtb support.
++
++	config MIPS_RAW_APPENDED_DTB
++		bool "vmlinux.bin"
++		help
++		  With this option, the boot code will look for a device tree binary
++		  DTB) appended to raw vmlinux.bin (without decompressor).
++		  (e.g. cat vmlinux.bin <filename>.dtb > vmlinux_w_dtb).
++
++		  This is meant as a backward compatibility convenience for those
++		  systems with a bootloader that can't be upgraded to accommodate
++		  the documented boot protocol using a device tree.
++
++		  Beware that there is very little in terms of protection against
++		  this option being confused by leftover garbage in memory that might
++		  look like a DTB header after a reboot if no actual DTB is appended
++		  to vmlinux.bin.  Do not leave this option active in a production kernel
++		  if you don't intend to always append a DTB.
++endchoice
++
+ endmenu
+ 
+ config LOCKDEP_SUPPORT
+--- a/arch/mips/kernel/head.S
++++ b/arch/mips/kernel/head.S
+@@ -100,6 +100,22 @@ NESTED(kernel_entry, 16, sp)			# kernel
+ 	jr	t0
+ 0:
+ 
++#ifdef CONFIG_MIPS_RAW_APPENDED_DTB
++	PTR_LA		t0, __appended_dtb
++
++#ifdef CONFIG_CPU_BIG_ENDIAN
++	li		t1, 0xd00dfeed
++#else
++	li		t1, 0xedfe0dd0
++#endif
++	lw		t2, (t0)
++	bne		t1, t2, not_found
++	 nop
++
++	move		a1, t0
++	PTR_LI		a0, -2
++not_found:
++#endif
+ 	PTR_LA		t0, __bss_start		# clear .bss
+ 	LONG_S		zero, (t0)
+ 	PTR_LA		t1, __bss_stop - LONGSIZE
+--- a/arch/mips/kernel/vmlinux.lds.S
++++ b/arch/mips/kernel/vmlinux.lds.S
+@@ -125,8 +125,14 @@ SECTIONS
+ 	.exit.data : {
+ 		EXIT_DATA
+ 	}
+-
++#ifdef CONFIG_SMP
+ 	PERCPU_SECTION(1 << CONFIG_MIPS_L1_CACHE_SHIFT)
++#endif
++#ifdef CONFIG_MIPS_RAW_APPENDED_DTB
++	__appended_dtb = .;
++	/* leave space for appended DTB */
++	. += 0x100000;
++#endif
+ 	/*
+ 	 * Align to 64K in attempt to eliminate holes before the
+ 	 * .bss..swapper_pg_dir section at the start of .bss.  This
diff --git a/target/linux/bmips/patches-4.1/010-MIPS-BMIPS-Build-all-dtbs-if-no-builtin-dtb.patch b/target/linux/bmips/patches-4.1/010-MIPS-BMIPS-Build-all-dtbs-if-no-builtin-dtb.patch
new file mode 100644
index 0000000..a918dc9
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/010-MIPS-BMIPS-Build-all-dtbs-if-no-builtin-dtb.patch
@@ -0,0 +1,48 @@
+From 0e12f4a3ab52f8be48c5ebbc556f53acb1afb280 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo at openwrt.org>
+Date: Sun, 12 Apr 2015 12:25:00 +0200
+Subject: [PATCH] MIPS: BMIPS: Build all dtbs if no builtin dtb
+
+Build all available dtbs to allow them to be appended to the resulting
+kernel in case there is no builtin dtb.
+
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+Cc: linux-mips at linux-mips.org
+Cc: devicetree at vger.kernel.org
+Cc: John Crispin <blogic at openwrt.org>
+Cc: Kevin Cernekee <cernekee at gmail.com>
+Cc: Florian Fainelli <f.fainelli at gmail.com>
+Cc: Aaro Koskinen <aaro.koskinen at iki.fi>
+Cc: Markos Chandras <markos.chandras at imgtec.com>
+Cc: Andrew Bresticker <abrestic at chromium.org>
+Cc: Daniel Schwierzeck <daniel.schwierzeck at gmail.com>
+Cc: Paul Burton <paul.burton at imgtec.com>
+Cc: James Hartley <James.Hartley at imgtec.com>
+Patchwork: https://patchwork.linux-mips.org/patch/9740/
+Signed-off-by: Ralf Baechle <ralf at linux-mips.org>
+---
+ arch/mips/boot/dts/brcm/Makefile | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/arch/mips/boot/dts/brcm/Makefile
++++ b/arch/mips/boot/dts/brcm/Makefile
+@@ -10,6 +10,19 @@ dtb-$(CONFIG_DT_BCM97362SVMB)		+= bcm973
+ dtb-$(CONFIG_DT_BCM97420C)		+= bcm97420c.dtb
+ dtb-$(CONFIG_DT_BCM97425SVMB)		+= bcm97425svmb.dtb
+ 
++dtb-$(CONFIG_DT_NONE)			+= \
++						bcm93384wvg.dtb		\
++						bcm93384wvg_viper.dtb	\
++						bcm96368mvwg.dtb	\
++						bcm9ejtagprb.dtb	\
++						bcm97125cbmb.dtb	\
++						bcm97346dbsmb.dtb	\
++						bcm97358svmb.dtb	\
++						bcm97360svmb.dtb	\
++						bcm97362svmb.dtb	\
++						bcm97420c.dtb		\
++						bcm97425svmb.dtb
++
+ obj-y				+= $(patsubst %.dtb, %.dtb.o, $(dtb-y))
+ 
+ # Force kbuild to make empty built-in.o if necessary
diff --git a/target/linux/bmips/patches-4.1/011-MIPS-BMIPS-Accept-UHI-interface-for-passing-a-dtb.patch b/target/linux/bmips/patches-4.1/011-MIPS-BMIPS-Accept-UHI-interface-for-passing-a-dtb.patch
new file mode 100644
index 0000000..72aafe5
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/011-MIPS-BMIPS-Accept-UHI-interface-for-passing-a-dtb.patch
@@ -0,0 +1,37 @@
+From ca668a2da4687f23e65ce630742b6784a5fca595 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo at openwrt.org>
+Date: Sun, 12 Apr 2015 12:25:01 +0200
+Subject: [PATCH] MIPS: BMIPS: Accept UHI interface for passing a dtb
+
+Detect and use passed dtb address using the UHI interface. This allows for
+booting with a vmlinux.bin appended dtb instead of using a built-in one.
+
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+Cc: linux-mips at linux-mips.org
+Cc: devicetree at vger.kernel.org
+Cc: John Crispin <blogic at openwrt.org>
+Cc: Kevin Cernekee <cernekee at gmail.com>
+Cc: Florian Fainelli <f.fainelli at gmail.com>
+Cc: Aaro Koskinen <aaro.koskinen at iki.fi>
+Cc: Markos Chandras <markos.chandras at imgtec.com>
+Cc: Andrew Bresticker <abrestic at chromium.org>
+Cc: Daniel Schwierzeck <daniel.schwierzeck at gmail.com>
+Cc: Paul Burton <paul.burton at imgtec.com>
+Cc: James Hartley <James.Hartley at imgtec.com>
+Patchwork: https://patchwork.linux-mips.org/patch/9742/
+Signed-off-by: Ralf Baechle <ralf at linux-mips.org>
+---
+ arch/mips/bmips/setup.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -149,6 +149,8 @@ void __init plat_mem_setup(void)
+ 	/* intended to somewhat resemble ARM; see Documentation/arm/Booting */
+ 	if (fw_arg0 == 0 && fw_arg1 == 0xffffffff)
+ 		dtb = phys_to_virt(fw_arg2);
++	else if (fw_arg0 == -2) /* UHI interface */
++		dtb = (void *)fw_arg1;
+ 	else if (__dtb_start != __dtb_end)
+ 		dtb = (void *)__dtb_start;
+ 	else
diff --git a/target/linux/bmips/patches-4.1/012-MIPS-BMIPS-Add-support-for-Broadcom-BCM97435SVMB.patch b/target/linux/bmips/patches-4.1/012-MIPS-BMIPS-Add-support-for-Broadcom-BCM97435SVMB.patch
new file mode 100644
index 0000000..a6d7968
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/012-MIPS-BMIPS-Add-support-for-Broadcom-BCM97435SVMB.patch
@@ -0,0 +1,112 @@
+From 380e4270f53b1ce848de7c3c9f21c7d6ccab3d2e Mon Sep 17 00:00:00 2001
+From: Florian Fainelli <f.fainelli at gmail.com>
+Date: Mon, 4 May 2015 18:10:57 -0700
+Subject: [PATCH] MIPS: BMIPS: Add support for Broadcom BCM97435SVMB
+
+Add a DTS file and Kconfig entry for the BCM97435SVMB evaluation board
+using bcm7435.dtsi as an example.
+
+The current code needs some tweaking to allow us to use the
+dual-threaded dual BMIPS5200 CPUs, so for now we limit ourselves to
+allowing just a single CPU to be booted.
+
+Signed-off-by: Florian Fainelli <f.fainelli at gmail.com>
+Cc: linux-mips at linux-mips.org
+Cc: blogic at openwrt.org
+Cc: cernekee at chromium.org
+Cc: Steven.Hill at imgtec.com
+Patchwork: https://patchwork.linux-mips.org/patch/9972/
+Signed-off-by: Ralf Baechle <ralf at linux-mips.org>
+---
+ arch/mips/bmips/Kconfig                  |  4 +++
+ arch/mips/boot/dts/brcm/Makefile         |  1 +
+ arch/mips/boot/dts/brcm/bcm97435svmb.dts | 60 ++++++++++++++++++++++++++++++++
+ 3 files changed, 65 insertions(+)
+ create mode 100644 arch/mips/boot/dts/brcm/bcm97435svmb.dts
+
+--- a/arch/mips/bmips/Kconfig
++++ b/arch/mips/bmips/Kconfig
+@@ -57,6 +57,10 @@ config DT_BCM97425SVMB
+ 	bool "BCM97425SVMB"
+ 	select BUILTIN_DTB
+ 
++config DT_BCM97435SVMB
++	bool "BCM97435SVMB"
++	select BUILTIN_DTB
++
+ endchoice
+ 
+ endif
+--- a/arch/mips/boot/dts/brcm/Makefile
++++ b/arch/mips/boot/dts/brcm/Makefile
+@@ -9,6 +9,7 @@ dtb-$(CONFIG_DT_BCM97360SVMB)		+= bcm973
+ dtb-$(CONFIG_DT_BCM97362SVMB)		+= bcm97362svmb.dtb
+ dtb-$(CONFIG_DT_BCM97420C)		+= bcm97420c.dtb
+ dtb-$(CONFIG_DT_BCM97425SVMB)		+= bcm97425svmb.dtb
++dtb-$(CONFIG_DT_BCM97435SVMB)		+= bcm97435svmb.dtb
+ 
+ dtb-$(CONFIG_DT_NONE)			+= \
+ 						bcm93384wvg.dtb		\
+--- /dev/null
++++ b/arch/mips/boot/dts/brcm/bcm97435svmb.dts
+@@ -0,0 +1,60 @@
++/dts-v1/;
++
++/include/ "bcm7435.dtsi"
++
++/ {
++	compatible = "brcm,bcm97435svmb", "brcm,bcm7435";
++	model = "Broadcom BCM97435SVMB";
++
++	memory at 0 {
++		device_type = "memory";
++		reg = <0x00000000 0x10000000>,
++		      <0x20000000 0x30000000>,
++		      <0x90000000 0x40000000>;
++	};
++
++	chosen {
++		bootargs = "console=ttyS0,115200 maxcpus=1";
++		stdout-path = &uart0;
++	};
++};
++
++&uart0 {
++	status = "okay";
++};
++
++&enet0 {
++	status = "okay";
++};
++
++&ehci0 {
++	status = "okay";
++};
++
++&ohci0 {
++	status = "okay";
++};
++
++&ehci1 {
++	status = "okay";
++};
++
++&ohci1 {
++	status = "okay";
++};
++
++&ehci2 {
++	status = "okay";
++};
++
++&ohci2 {
++	status = "okay";
++};
++
++&ehci3 {
++	status = "okay";
++};
++
++&ohci3 {
++	status = "okay";
++};
diff --git a/target/linux/bmips/patches-4.1/020-leds-add-BCM6328-LED-driver.patch b/target/linux/bmips/patches-4.1/020-leds-add-BCM6328-LED-driver.patch
new file mode 100644
index 0000000..fc3d03a
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/020-leds-add-BCM6328-LED-driver.patch
@@ -0,0 +1,464 @@
+From fd7b025a238d0a5440bfa26c585eb78097bf48dc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari at gmail.com>
+Date: Tue, 28 Apr 2015 09:50:50 -0700
+Subject: [PATCH] leds: add BCM6328 LED driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for the LED controller on Broadcom's BCM6328.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+Acked-by: Jacek Anaszewski <j.anaszewski at samsung.com>
+Signed-off-by: Bryan Wu <cooloney at gmail.com>
+---
+ drivers/leds/Kconfig        |   8 +
+ drivers/leds/Makefile       |   1 +
+ drivers/leds/leds-bcm6328.c | 413 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 422 insertions(+)
+ create mode 100644 drivers/leds/leds-bcm6328.c
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -42,6 +42,14 @@ config LEDS_88PM860X
+ 	  This option enables support for on-chip LED drivers found on Marvell
+ 	  Semiconductor 88PM8606 PMIC.
+ 
++config LEDS_BCM6328
++	tristate "LED Support for Broadcom BCM6328"
++	depends on LEDS_CLASS
++	depends on OF
++	help
++	  This option enables support for LEDs connected to the BCM6328
++	  LED HW controller accessed via MMIO registers.
++
+ config LEDS_LM3530
+ 	tristate "LCD Backlight driver for LM3530"
+ 	depends on LEDS_CLASS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -7,6 +7,7 @@ obj-$(CONFIG_LEDS_TRIGGERS)		+= led-trig
+ 
+ # LED Platform Drivers
+ obj-$(CONFIG_LEDS_88PM860X)		+= leds-88pm860x.o
++obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
+ obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
+ obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
+ obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
+--- /dev/null
++++ b/drivers/leds/leds-bcm6328.c
+@@ -0,0 +1,413 @@
++/*
++ * Driver for BCM6328 memory-mapped LEDs, based on leds-syscon.c
++ *
++ * Copyright 2015 Álvaro Fernández Rojas <noltari at gmail.com>
++ * Copyright 2015 Jonas Gorski <jogo at 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.
++ */
++#include <linux/io.h>
++#include <linux/leds.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define BCM6328_REG_INIT		0x00
++#define BCM6328_REG_MODE_HI		0x04
++#define BCM6328_REG_MODE_LO		0x08
++#define BCM6328_REG_HWDIS		0x0c
++#define BCM6328_REG_STROBE		0x10
++#define BCM6328_REG_LNKACTSEL_HI	0x14
++#define BCM6328_REG_LNKACTSEL_LO	0x18
++#define BCM6328_REG_RBACK		0x1c
++#define BCM6328_REG_SERMUX		0x20
++
++#define BCM6328_LED_MAX_COUNT		24
++#define BCM6328_LED_DEF_DELAY		500
++#define BCM6328_LED_INTERVAL_MS		20
++
++#define BCM6328_LED_INTV_MASK		0x3f
++#define BCM6328_LED_FAST_INTV_SHIFT	6
++#define BCM6328_LED_FAST_INTV_MASK	(BCM6328_LED_INTV_MASK << \
++					 BCM6328_LED_FAST_INTV_SHIFT)
++#define BCM6328_SERIAL_LED_EN		BIT(12)
++#define BCM6328_SERIAL_LED_MUX		BIT(13)
++#define BCM6328_SERIAL_LED_CLK_NPOL	BIT(14)
++#define BCM6328_SERIAL_LED_DATA_PPOL	BIT(15)
++#define BCM6328_SERIAL_LED_SHIFT_DIR	BIT(16)
++#define BCM6328_LED_SHIFT_TEST		BIT(30)
++#define BCM6328_LED_TEST		BIT(31)
++
++#define BCM6328_LED_MODE_MASK		3
++#define BCM6328_LED_MODE_OFF		0
++#define BCM6328_LED_MODE_FAST		1
++#define BCM6328_LED_MODE_BLINK		2
++#define BCM6328_LED_MODE_ON		3
++#define BCM6328_LED_SHIFT(X)		((X) << 1)
++
++/**
++ * struct bcm6328_led - state container for bcm6328 based LEDs
++ * @cdev: LED class device for this LED
++ * @mem: memory resource
++ * @lock: memory lock
++ * @pin: LED pin number
++ * @blink_leds: blinking LEDs
++ * @blink_delay: blinking delay
++ * @active_low: LED is active low
++ */
++struct bcm6328_led {
++	struct led_classdev cdev;
++	void __iomem *mem;
++	spinlock_t *lock;
++	unsigned long pin;
++	unsigned long *blink_leds;
++	unsigned long *blink_delay;
++	bool active_low;
++};
++
++static void bcm6328_led_write(void __iomem *reg, unsigned long data)
++{
++	iowrite32be(data, reg);
++}
++
++static unsigned long bcm6328_led_read(void __iomem *reg)
++{
++	return ioread32be(reg);
++}
++
++/**
++ * LEDMode 64 bits / 24 LEDs
++ * bits [31:0] -> LEDs 8-23
++ * bits [47:32] -> LEDs 0-7
++ * bits [63:48] -> unused
++ */
++static unsigned long bcm6328_pin2shift(unsigned long pin)
++{
++	if (pin < 8)
++		return pin + 16; /* LEDs 0-7 (bits 47:32) */
++	else
++		return pin - 8; /* LEDs 8-23 (bits 31:0) */
++}
++
++static void bcm6328_led_mode(struct bcm6328_led *led, unsigned long value)
++{
++	void __iomem *mode;
++	unsigned long val, shift;
++
++	shift = bcm6328_pin2shift(led->pin);
++	if (shift / 16)
++		mode = led->mem + BCM6328_REG_MODE_HI;
++	else
++		mode = led->mem + BCM6328_REG_MODE_LO;
++
++	val = bcm6328_led_read(mode);
++	val &= ~(BCM6328_LED_MODE_MASK << BCM6328_LED_SHIFT(shift % 16));
++	val |= (value << BCM6328_LED_SHIFT(shift % 16));
++	bcm6328_led_write(mode, val);
++}
++
++static void bcm6328_led_set(struct led_classdev *led_cdev,
++			    enum led_brightness value)
++{
++	struct bcm6328_led *led =
++		container_of(led_cdev, struct bcm6328_led, cdev);
++	unsigned long flags;
++
++	spin_lock_irqsave(led->lock, flags);
++	*(led->blink_leds) &= ~BIT(led->pin);
++	if ((led->active_low && value == LED_OFF) ||
++	    (!led->active_low && value != LED_OFF))
++		bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
++	else
++		bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
++	spin_unlock_irqrestore(led->lock, flags);
++}
++
++static int bcm6328_blink_set(struct led_classdev *led_cdev,
++			     unsigned long *delay_on, unsigned long *delay_off)
++{
++	struct bcm6328_led *led =
++		container_of(led_cdev, struct bcm6328_led, cdev);
++	unsigned long delay, flags;
++
++	if (!*delay_on)
++		*delay_on = BCM6328_LED_DEF_DELAY;
++	if (!*delay_off)
++		*delay_off = BCM6328_LED_DEF_DELAY;
++
++	if (*delay_on != *delay_off) {
++		dev_dbg(led_cdev->dev,
++			"fallback to soft blinking (delay_on != delay_off)\n");
++		return -EINVAL;
++	}
++
++	delay = *delay_on / BCM6328_LED_INTERVAL_MS;
++	if (delay == 0)
++		delay = 1;
++	else if (delay > BCM6328_LED_INTV_MASK) {
++		dev_dbg(led_cdev->dev,
++			"fallback to soft blinking (delay > %ums)\n",
++			BCM6328_LED_INTV_MASK * BCM6328_LED_INTERVAL_MS);
++		return -EINVAL;
++	}
++
++	spin_lock_irqsave(led->lock, flags);
++	if (*(led->blink_leds) == 0 ||
++	    *(led->blink_leds) == BIT(led->pin) ||
++	    *(led->blink_delay) == delay) {
++		unsigned long val;
++
++		*(led->blink_leds) |= BIT(led->pin);
++		*(led->blink_delay) = delay;
++
++		val = bcm6328_led_read(led->mem + BCM6328_REG_INIT);
++		val &= ~BCM6328_LED_FAST_INTV_MASK;
++		val |= (delay << BCM6328_LED_FAST_INTV_SHIFT);
++		bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);
++
++		bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK);
++
++		spin_unlock_irqrestore(led->lock, flags);
++	} else {
++		spin_unlock_irqrestore(led->lock, flags);
++		dev_dbg(led_cdev->dev,
++			"fallback to soft blinking (delay already set)\n");
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int bcm6328_hwled(struct device *dev, struct device_node *nc, u32 reg,
++			 void __iomem *mem, spinlock_t *lock)
++{
++	int i, cnt;
++	unsigned long flags, val;
++
++	spin_lock_irqsave(lock, flags);
++	val = bcm6328_led_read(mem + BCM6328_REG_HWDIS);
++	val &= ~BIT(reg);
++	bcm6328_led_write(mem + BCM6328_REG_HWDIS, val);
++	spin_unlock_irqrestore(lock, flags);
++
++	/* Only LEDs 0-7 can be activity/link controlled */
++	if (reg >= 8)
++		return 0;
++
++	cnt = of_property_count_elems_of_size(nc, "brcm,link-signal-sources",
++					      sizeof(u32));
++	for (i = 0; i < cnt; i++) {
++		u32 sel;
++		void __iomem *addr;
++
++		if (reg < 4)
++			addr = mem + BCM6328_REG_LNKACTSEL_LO;
++		else
++			addr = mem + BCM6328_REG_LNKACTSEL_HI;
++
++		of_property_read_u32_index(nc, "brcm,link-signal-sources", i,
++					   &sel);
++
++		if (reg / 4 != sel / 4) {
++			dev_warn(dev, "invalid link signal source\n");
++			continue;
++		}
++
++		spin_lock_irqsave(lock, flags);
++		val = bcm6328_led_read(addr);
++		val |= (BIT(reg) << (((sel % 4) * 4) + 16));
++		bcm6328_led_write(addr, val);
++		spin_unlock_irqrestore(lock, flags);
++	}
++
++	cnt = of_property_count_elems_of_size(nc,
++					      "brcm,activity-signal-sources",
++					      sizeof(u32));
++	for (i = 0; i < cnt; i++) {
++		u32 sel;
++		void __iomem *addr;
++
++		if (reg < 4)
++			addr = mem + BCM6328_REG_LNKACTSEL_LO;
++		else
++			addr = mem + BCM6328_REG_LNKACTSEL_HI;
++
++		of_property_read_u32_index(nc, "brcm,activity-signal-sources",
++					   i, &sel);
++
++		if (reg / 4 != sel / 4) {
++			dev_warn(dev, "invalid activity signal source\n");
++			continue;
++		}
++
++		spin_lock_irqsave(lock, flags);
++		val = bcm6328_led_read(addr);
++		val |= (BIT(reg) << ((sel % 4) * 4));
++		bcm6328_led_write(addr, val);
++		spin_unlock_irqrestore(lock, flags);
++	}
++
++	return 0;
++}
++
++static int bcm6328_led(struct device *dev, struct device_node *nc, u32 reg,
++		       void __iomem *mem, spinlock_t *lock,
++		       unsigned long *blink_leds, unsigned long *blink_delay)
++{
++	struct bcm6328_led *led;
++	unsigned long flags;
++	const char *state;
++	int rc;
++
++	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
++	if (!led)
++		return -ENOMEM;
++
++	led->pin = reg;
++	led->mem = mem;
++	led->lock = lock;
++	led->blink_leds = blink_leds;
++	led->blink_delay = blink_delay;
++
++	if (of_property_read_bool(nc, "active-low"))
++		led->active_low = true;
++
++	led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
++	led->cdev.default_trigger = of_get_property(nc,
++						    "linux,default-trigger",
++						    NULL);
++
++	if (!of_property_read_string(nc, "default-state", &state)) {
++		spin_lock_irqsave(lock, flags);
++		if (!strcmp(state, "on")) {
++			led->cdev.brightness = LED_FULL;
++			bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
++		} else if (!strcmp(state, "keep")) {
++			void __iomem *mode;
++			unsigned long val, shift;
++
++			shift = bcm6328_pin2shift(led->pin);
++			if (shift / 16)
++				mode = mem + BCM6328_REG_MODE_HI;
++			else
++				mode = mem + BCM6328_REG_MODE_LO;
++
++			val = bcm6328_led_read(mode) >> (shift % 16);
++			val &= BCM6328_LED_MODE_MASK;
++			if (val == BCM6328_LED_MODE_ON)
++				led->cdev.brightness = LED_FULL;
++			else {
++				led->cdev.brightness = LED_OFF;
++				bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
++			}
++		} else {
++			led->cdev.brightness = LED_OFF;
++			bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
++		}
++		spin_unlock_irqrestore(lock, flags);
++	}
++
++	led->cdev.brightness_set = bcm6328_led_set;
++	led->cdev.blink_set = bcm6328_blink_set;
++
++	rc = led_classdev_register(dev, &led->cdev);
++	if (rc < 0)
++		return rc;
++
++	dev_dbg(dev, "registered LED %s\n", led->cdev.name);
++
++	return 0;
++}
++
++static int bcm6328_leds_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = pdev->dev.of_node;
++	struct device_node *child;
++	struct resource *mem_r;
++	void __iomem *mem;
++	spinlock_t *lock;
++	unsigned long val, *blink_leds, *blink_delay;
++
++	mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!mem_r)
++		return -EINVAL;
++
++	mem = devm_ioremap_resource(dev, mem_r);
++	if (IS_ERR(mem))
++		return PTR_ERR(mem);
++
++	lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
++	if (!lock)
++		return -ENOMEM;
++
++	blink_leds = devm_kzalloc(dev, sizeof(*blink_leds), GFP_KERNEL);
++	if (!blink_leds)
++		return -ENOMEM;
++
++	blink_delay = devm_kzalloc(dev, sizeof(*blink_delay), GFP_KERNEL);
++	if (!blink_delay)
++		return -ENOMEM;
++
++	spin_lock_init(lock);
++
++	bcm6328_led_write(mem + BCM6328_REG_HWDIS, ~0);
++	bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_HI, 0);
++	bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0);
++
++	val = bcm6328_led_read(mem + BCM6328_REG_INIT);
++	val &= ~BCM6328_SERIAL_LED_EN;
++	if (of_property_read_bool(np, "brcm,serial-leds"))
++		val |= BCM6328_SERIAL_LED_EN;
++	bcm6328_led_write(mem + BCM6328_REG_INIT, val);
++
++	for_each_available_child_of_node(np, child) {
++		int rc;
++		u32 reg;
++
++		if (of_property_read_u32(child, "reg", &reg))
++			continue;
++
++		if (reg >= BCM6328_LED_MAX_COUNT) {
++			dev_err(dev, "invalid LED (>= %d)\n",
++				BCM6328_LED_MAX_COUNT);
++			continue;
++		}
++
++		if (of_property_read_bool(child, "brcm,hardware-controlled"))
++			rc = bcm6328_hwled(dev, child, reg, mem, lock);
++		else
++			rc = bcm6328_led(dev, child, reg, mem, lock,
++					 blink_leds, blink_delay);
++
++		if (rc < 0)
++			return rc;
++	}
++
++	return 0;
++}
++
++static const struct of_device_id bcm6328_leds_of_match[] = {
++	{ .compatible = "brcm,bcm6328-leds", },
++	{ },
++};
++
++static struct platform_driver bcm6328_leds_driver = {
++	.probe = bcm6328_leds_probe,
++	.driver = {
++		.name = "leds-bcm6328",
++		.of_match_table = bcm6328_leds_of_match,
++	},
++};
++
++module_platform_driver(bcm6328_leds_driver);
++
++MODULE_AUTHOR("Álvaro Fernández Rojas <noltari at gmail.com>");
++MODULE_AUTHOR("Jonas Gorski <jogo at openwrt.org>");
++MODULE_DESCRIPTION("LED driver for BCM6328 controllers");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:leds-bcm6328");
diff --git a/target/linux/bmips/patches-4.1/021-leds-add-BCM6358-LED-driver.patch b/target/linux/bmips/patches-4.1/021-leds-add-BCM6358-LED-driver.patch
new file mode 100644
index 0000000..dead890
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/021-leds-add-BCM6358-LED-driver.patch
@@ -0,0 +1,293 @@
+From 589fca16c14adec7ebeb601e22850826e18b8f8d Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari at gmail.com>
+Date: Thu, 21 May 2015 10:11:10 -0700
+Subject: [PATCH] leds: add BCM6358 LED driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for the LED controller on Broadcom's BCM6358.
+
+Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
+Acked-by: Jacek Anaszewski <j.anaszewski at samsung.com>
+Signed-off-by: Bryan Wu <cooloney at gmail.com>
+---
+ drivers/leds/Kconfig        |   8 ++
+ drivers/leds/Makefile       |   1 +
+ drivers/leds/leds-bcm6358.c | 243 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 252 insertions(+)
+ create mode 100644 drivers/leds/leds-bcm6358.c
+
+--- a/drivers/leds/Kconfig
++++ b/drivers/leds/Kconfig
+@@ -50,6 +50,14 @@ config LEDS_BCM6328
+ 	  This option enables support for LEDs connected to the BCM6328
+ 	  LED HW controller accessed via MMIO registers.
+ 
++config LEDS_BCM6358
++	tristate "LED Support for Broadcom BCM6358"
++	depends on LEDS_CLASS
++	depends on OF
++	help
++	  This option enables support for LEDs connected to the BCM6358
++	  LED HW controller accessed via MMIO registers.
++
+ config LEDS_LM3530
+ 	tristate "LCD Backlight driver for LM3530"
+ 	depends on LEDS_CLASS
+--- a/drivers/leds/Makefile
++++ b/drivers/leds/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_LEDS_TRIGGERS)		+= led-trig
+ # LED Platform Drivers
+ obj-$(CONFIG_LEDS_88PM860X)		+= leds-88pm860x.o
+ obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
++obj-$(CONFIG_LEDS_BCM6358)		+= leds-bcm6358.o
+ obj-$(CONFIG_LEDS_BD2802)		+= leds-bd2802.o
+ obj-$(CONFIG_LEDS_LOCOMO)		+= leds-locomo.o
+ obj-$(CONFIG_LEDS_LM3530)		+= leds-lm3530.o
+--- /dev/null
++++ b/drivers/leds/leds-bcm6358.c
+@@ -0,0 +1,243 @@
++/*
++ * Driver for BCM6358 memory-mapped LEDs, based on leds-syscon.c
++ *
++ * Copyright 2015 Álvaro Fernández Rojas <noltari at gmail.com>
++ *
++ * 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.
++ */
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/leds.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define BCM6358_REG_MODE		0x0
++#define BCM6358_REG_CTRL		0x4
++
++#define BCM6358_SLED_CLKDIV_MASK	3
++#define BCM6358_SLED_CLKDIV_1		0
++#define BCM6358_SLED_CLKDIV_2		1
++#define BCM6358_SLED_CLKDIV_4		2
++#define BCM6358_SLED_CLKDIV_8		3
++
++#define BCM6358_SLED_POLARITY		BIT(2)
++#define BCM6358_SLED_BUSY		BIT(3)
++
++#define BCM6358_SLED_MAX_COUNT		32
++#define BCM6358_SLED_WAIT		100
++
++/**
++ * struct bcm6358_led - state container for bcm6358 based LEDs
++ * @cdev: LED class device for this LED
++ * @mem: memory resource
++ * @lock: memory lock
++ * @pin: LED pin number
++ * @active_low: LED is active low
++ */
++struct bcm6358_led {
++	struct led_classdev cdev;
++	void __iomem *mem;
++	spinlock_t *lock;
++	unsigned long pin;
++	bool active_low;
++};
++
++static void bcm6358_led_write(void __iomem *reg, unsigned long data)
++{
++	iowrite32be(data, reg);
++}
++
++static unsigned long bcm6358_led_read(void __iomem *reg)
++{
++	return ioread32be(reg);
++}
++
++static unsigned long bcm6358_led_busy(void __iomem *mem)
++{
++	unsigned long val;
++
++	while ((val = bcm6358_led_read(mem + BCM6358_REG_CTRL)) &
++		BCM6358_SLED_BUSY)
++		udelay(BCM6358_SLED_WAIT);
++
++	return val;
++}
++
++static void bcm6358_led_mode(struct bcm6358_led *led, unsigned long value)
++{
++	unsigned long val;
++
++	bcm6358_led_busy(led->mem);
++
++	val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
++	if ((led->active_low && value == LED_OFF) ||
++	    (!led->active_low && value != LED_OFF))
++		val |= BIT(led->pin);
++	else
++		val &= ~(BIT(led->pin));
++	bcm6358_led_write(led->mem + BCM6358_REG_MODE, val);
++}
++
++static void bcm6358_led_set(struct led_classdev *led_cdev,
++			    enum led_brightness value)
++{
++	struct bcm6358_led *led =
++		container_of(led_cdev, struct bcm6358_led, cdev);
++	unsigned long flags;
++
++	spin_lock_irqsave(led->lock, flags);
++	bcm6358_led_mode(led, value);
++	spin_unlock_irqrestore(led->lock, flags);
++}
++
++static int bcm6358_led(struct device *dev, struct device_node *nc, u32 reg,
++		       void __iomem *mem, spinlock_t *lock)
++{
++	struct bcm6358_led *led;
++	unsigned long flags;
++	const char *state;
++	int rc;
++
++	led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
++	if (!led)
++		return -ENOMEM;
++
++	led->pin = reg;
++	led->mem = mem;
++	led->lock = lock;
++
++	if (of_property_read_bool(nc, "active-low"))
++		led->active_low = true;
++
++	led->cdev.name = of_get_property(nc, "label", NULL) ? : nc->name;
++	led->cdev.default_trigger = of_get_property(nc,
++						    "linux,default-trigger",
++						    NULL);
++
++	spin_lock_irqsave(lock, flags);
++	if (!of_property_read_string(nc, "default-state", &state)) {
++		if (!strcmp(state, "on")) {
++			led->cdev.brightness = LED_FULL;
++		} else if (!strcmp(state, "keep")) {
++			unsigned long val;
++
++			bcm6358_led_busy(led->mem);
++
++			val = bcm6358_led_read(led->mem + BCM6358_REG_MODE);
++			val &= BIT(led->pin);
++			if ((led->active_low && !val) ||
++			    (!led->active_low && val))
++				led->cdev.brightness = LED_FULL;
++			else
++				led->cdev.brightness = LED_OFF;
++		} else {
++			led->cdev.brightness = LED_OFF;
++		}
++	} else {
++		led->cdev.brightness = LED_OFF;
++	}
++	bcm6358_led_mode(led, led->cdev.brightness);
++	spin_unlock_irqrestore(lock, flags);
++
++	led->cdev.brightness_set = bcm6358_led_set;
++
++	rc = led_classdev_register(dev, &led->cdev);
++	if (rc < 0)
++		return rc;
++
++	dev_dbg(dev, "registered LED %s\n", led->cdev.name);
++
++	return 0;
++}
++
++static int bcm6358_leds_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *np = pdev->dev.of_node;
++	struct device_node *child;
++	struct resource *mem_r;
++	void __iomem *mem;
++	spinlock_t *lock; /* memory lock */
++	unsigned long val;
++	u32 clk_div;
++
++	mem_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!mem_r)
++		return -EINVAL;
++
++	mem = devm_ioremap_resource(dev, mem_r);
++	if (IS_ERR(mem))
++		return PTR_ERR(mem);
++
++	lock = devm_kzalloc(dev, sizeof(*lock), GFP_KERNEL);
++	if (!lock)
++		return -ENOMEM;
++
++	spin_lock_init(lock);
++
++	val = bcm6358_led_busy(mem);
++	val &= ~(BCM6358_SLED_POLARITY | BCM6358_SLED_CLKDIV_MASK);
++	if (of_property_read_bool(np, "brcm,clk-dat-low"))
++		val |= BCM6358_SLED_POLARITY;
++	of_property_read_u32(np, "brcm,clk-div", &clk_div);
++	switch (clk_div) {
++	case 8:
++		val |= BCM6358_SLED_CLKDIV_8;
++		break;
++	case 4:
++		val |= BCM6358_SLED_CLKDIV_4;
++		break;
++	case 2:
++		val |= BCM6358_SLED_CLKDIV_2;
++		break;
++	default:
++		val |= BCM6358_SLED_CLKDIV_1;
++		break;
++	}
++	bcm6358_led_write(mem + BCM6358_REG_CTRL, val);
++
++	for_each_available_child_of_node(np, child) {
++		int rc;
++		u32 reg;
++
++		if (of_property_read_u32(child, "reg", &reg))
++			continue;
++
++		if (reg >= BCM6358_SLED_MAX_COUNT) {
++			dev_err(dev, "invalid LED (%u >= %d)\n", reg,
++				BCM6358_SLED_MAX_COUNT);
++			continue;
++		}
++
++		rc = bcm6358_led(dev, child, reg, mem, lock);
++		if (rc < 0)
++			return rc;
++	}
++
++	return 0;
++}
++
++static const struct of_device_id bcm6358_leds_of_match[] = {
++	{ .compatible = "brcm,bcm6358-leds", },
++	{ },
++};
++
++static struct platform_driver bcm6358_leds_driver = {
++	.probe = bcm6358_leds_probe,
++	.driver = {
++		.name = "leds-bcm6358",
++		.of_match_table = bcm6358_leds_of_match,
++	},
++};
++
++module_platform_driver(bcm6358_leds_driver);
++
++MODULE_AUTHOR("Álvaro Fernández Rojas <noltari at gmail.com>");
++MODULE_DESCRIPTION("LED driver for BCM6358 controllers");
++MODULE_LICENSE("GPL v2");
++MODULE_ALIAS("platform:leds-bcm6358");
diff --git a/target/linux/bmips/patches-4.1/030-mtd-nand-add-common-DT-init-code.patch b/target/linux/bmips/patches-4.1/030-mtd-nand-add-common-DT-init-code.patch
new file mode 100644
index 0000000..9a5ad1f
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/030-mtd-nand-add-common-DT-init-code.patch
@@ -0,0 +1,111 @@
+From 5844feeaa4154d1c46d3462c7a4653d22356d8b4 Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Fri, 23 Jan 2015 00:22:27 -0800
+Subject: [PATCH] mtd: nand: add common DT init code
+
+These are already-documented common bindings for NAND chips. Let's
+handle them in nand_base.
+
+If NAND controller drivers need to act on this data before bringing up
+the NAND chip (e.g., fill out ECC callback functions, change HW modes,
+etc.), then they can do so between calling nand_scan_ident() and
+nand_scan_tail().
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/nand_base.c | 41 +++++++++++++++++++++++++++++++++++++++++
+ include/linux/mtd/nand.h     |  5 +++++
+ 2 files changed, 46 insertions(+)
+
+--- a/drivers/mtd/nand/nand_base.c
++++ b/drivers/mtd/nand/nand_base.c
+@@ -48,6 +48,7 @@
+ #include <linux/leds.h>
+ #include <linux/io.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/of_mtd.h>
+ 
+ /* Define default oob placement schemes for large and small page devices */
+ static struct nand_ecclayout nand_oob_8 = {
+@@ -3798,6 +3799,39 @@ ident_done:
+ 	return type;
+ }
+ 
++static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip,
++			struct device_node *dn)
++{
++	int ecc_mode, ecc_strength, ecc_step;
++
++	if (of_get_nand_bus_width(dn) == 16)
++		chip->options |= NAND_BUSWIDTH_16;
++
++	if (of_get_nand_on_flash_bbt(dn))
++		chip->bbt_options |= NAND_BBT_USE_FLASH;
++
++	ecc_mode = of_get_nand_ecc_mode(dn);
++	ecc_strength = of_get_nand_ecc_strength(dn);
++	ecc_step = of_get_nand_ecc_step_size(dn);
++
++	if ((ecc_step >= 0 && !(ecc_strength >= 0)) ||
++	    (!(ecc_step >= 0) && ecc_strength >= 0)) {
++		pr_err("must set both strength and step size in DT\n");
++		return -EINVAL;
++	}
++
++	if (ecc_mode >= 0)
++		chip->ecc.mode = ecc_mode;
++
++	if (ecc_strength >= 0)
++		chip->ecc.strength = ecc_strength;
++
++	if (ecc_step > 0)
++		chip->ecc.size = ecc_step;
++
++	return 0;
++}
++
+ /**
+  * nand_scan_ident - [NAND Interface] Scan for the NAND device
+  * @mtd: MTD device structure
+@@ -3815,6 +3849,13 @@ int nand_scan_ident(struct mtd_info *mtd
+ 	int i, nand_maf_id, nand_dev_id;
+ 	struct nand_chip *chip = mtd->priv;
+ 	struct nand_flash_dev *type;
++	int ret;
++
++	if (chip->dn) {
++		ret = nand_dt_init(mtd, chip, chip->dn);
++		if (ret)
++			return ret;
++	}
+ 
+ 	/* Set the default functions */
+ 	nand_set_defaults(chip, chip->options & NAND_BUSWIDTH_16);
+--- a/include/linux/mtd/nand.h
++++ b/include/linux/mtd/nand.h
+@@ -26,6 +26,8 @@
+ 
+ struct mtd_info;
+ struct nand_flash_dev;
++struct device_node;
++
+ /* Scan and identify a NAND device */
+ extern int nand_scan(struct mtd_info *mtd, int max_chips);
+ /*
+@@ -542,6 +544,7 @@ struct nand_buffers {
+  *			flash device
+  * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the
+  *			flash device.
++ * @dn:			[BOARDSPECIFIC] device node describing this instance
+  * @read_byte:		[REPLACEABLE] read one byte from the chip
+  * @read_word:		[REPLACEABLE] read one word from the chip
+  * @write_byte:		[REPLACEABLE] write a single byte to the chip on the
+@@ -644,6 +647,8 @@ struct nand_chip {
+ 	void __iomem *IO_ADDR_R;
+ 	void __iomem *IO_ADDR_W;
+ 
++	struct device_node *dn;
++
+ 	uint8_t (*read_byte)(struct mtd_info *mtd);
+ 	u16 (*read_word)(struct mtd_info *mtd);
+ 	void (*write_byte)(struct mtd_info *mtd, uint8_t byte);
diff --git a/target/linux/bmips/patches-4.1/031-mtd-nand-add-NAND-driver-library-for-Broadcom-STB-NA.patch b/target/linux/bmips/patches-4.1/031-mtd-nand-add-NAND-driver-library-for-Broadcom-STB-NA.patch
new file mode 100644
index 0000000..552be21
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/031-mtd-nand-add-NAND-driver-library-for-Broadcom-STB-NA.patch
@@ -0,0 +1,2323 @@
+From 27c5b17cd1b10564fa36f8f51e4b4b41436ecc32 Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Fri, 6 Mar 2015 11:38:08 -0800
+Subject: [PATCH] mtd: nand: add NAND driver "library" for Broadcom STB NAND
+ controller
+
+This core originated in Set-Top Box chips (BCM7xxx) but is used in a
+variety of other Broadcom chips, including some BCM63xxx, BCM33xx, and
+iProc/Cygnus. It's been used only on ARM and MIPS SoCs, so restrict it
+to those architectures.
+
+There are multiple revisions of this core throughout the years, and
+almost every version broke register compatibility in some small way, but
+with some effort, this driver is able to support v4.0, v5.0, v6.x, v7.0,
+and v7.1. It's been tested on v5.0, v6.0, v6.1, v7.0, and v7.1 recently,
+so there hopefully are no more lurking inconsistencies.
+
+This patch adds just some library support, on which platform drivers can
+be built.
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Tested-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/Kconfig             |    8 +
+ drivers/mtd/nand/Makefile            |    1 +
+ drivers/mtd/nand/brcmnand/Makefile   |    1 +
+ drivers/mtd/nand/brcmnand/brcmnand.c | 2195 ++++++++++++++++++++++++++++++++++
+ drivers/mtd/nand/brcmnand/brcmnand.h |   58 +
+ 5 files changed, 2263 insertions(+)
+ create mode 100644 drivers/mtd/nand/brcmnand/Makefile
+ create mode 100644 drivers/mtd/nand/brcmnand/brcmnand.c
+ create mode 100644 drivers/mtd/nand/brcmnand/brcmnand.h
+
+--- a/drivers/mtd/nand/Kconfig
++++ b/drivers/mtd/nand/Kconfig
+@@ -394,6 +394,14 @@ config MTD_NAND_GPMI_NAND
+ 	 block, such as SD card. So pay attention to it when you enable
+ 	 the GPMI.
+ 
++config MTD_NAND_BRCMNAND
++	tristate "Broadcom STB NAND controller"
++	depends on ARM || MIPS
++	help
++	  Enables the Broadcom NAND controller driver. The controller was
++	  originally designed for Set-Top Box but is used on various BCM7xxx,
++	  BCM3xxx, BCM63xxx, iProc/Cygnus and more.
++
+ config MTD_NAND_BCM47XXNFLASH
+ 	tristate "Support for NAND flash on BCM4706 BCMA bus"
+ 	depends on BCMA_NFLASH
+--- a/drivers/mtd/nand/Makefile
++++ b/drivers/mtd/nand/Makefile
+@@ -52,5 +52,6 @@ obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nan
+ obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
+ obj-$(CONFIG_MTD_NAND_SUNXI)		+= sunxi_nand.o
+ obj-$(CONFIG_MTD_NAND_HISI504)	        += hisi504_nand.o
++obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand/
+ 
+ nand-objs := nand_base.o nand_bbt.o nand_timings.o
+--- /dev/null
++++ b/drivers/mtd/nand/brcmnand/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand.o
+--- /dev/null
++++ b/drivers/mtd/nand/brcmnand/brcmnand.c
+@@ -0,0 +1,2195 @@
++/*
++ * Copyright © 2010-2015 Broadcom Corporation
++ *
++ * 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/version.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/err.h>
++#include <linux/completion.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/dma-mapping.h>
++#include <linux/ioport.h>
++#include <linux/bug.h>
++#include <linux/kernel.h>
++#include <linux/bitops.h>
++#include <linux/mm.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/of.h>
++#include <linux/of_mtd.h>
++#include <linux/of_platform.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++#include <linux/log2.h>
++
++#include "brcmnand.h"
++
++/*
++ * This flag controls if WP stays on between erase/write commands to mitigate
++ * flash corruption due to power glitches. Values:
++ * 0: NAND_WP is not used or not available
++ * 1: NAND_WP is set by default, cleared for erase/write operations
++ * 2: NAND_WP is always cleared
++ */
++static int wp_on = 1;
++module_param(wp_on, int, 0444);
++
++/***********************************************************************
++ * Definitions
++ ***********************************************************************/
++
++#define DRV_NAME			"brcmnand"
++
++#define CMD_NULL			0x00
++#define CMD_PAGE_READ			0x01
++#define CMD_SPARE_AREA_READ		0x02
++#define CMD_STATUS_READ			0x03
++#define CMD_PROGRAM_PAGE		0x04
++#define CMD_PROGRAM_SPARE_AREA		0x05
++#define CMD_COPY_BACK			0x06
++#define CMD_DEVICE_ID_READ		0x07
++#define CMD_BLOCK_ERASE			0x08
++#define CMD_FLASH_RESET			0x09
++#define CMD_BLOCKS_LOCK			0x0a
++#define CMD_BLOCKS_LOCK_DOWN		0x0b
++#define CMD_BLOCKS_UNLOCK		0x0c
++#define CMD_READ_BLOCKS_LOCK_STATUS	0x0d
++#define CMD_PARAMETER_READ		0x0e
++#define CMD_PARAMETER_CHANGE_COL	0x0f
++#define CMD_LOW_LEVEL_OP		0x10
++
++struct brcm_nand_dma_desc {
++	u32 next_desc;
++	u32 next_desc_ext;
++	u32 cmd_irq;
++	u32 dram_addr;
++	u32 dram_addr_ext;
++	u32 tfr_len;
++	u32 total_len;
++	u32 flash_addr;
++	u32 flash_addr_ext;
++	u32 cs;
++	u32 pad2[5];
++	u32 status_valid;
++} __packed;
++
++/* Bitfields for brcm_nand_dma_desc::status_valid */
++#define FLASH_DMA_ECC_ERROR	(1 << 8)
++#define FLASH_DMA_CORR_ERROR	(1 << 9)
++
++/* 512B flash cache in the NAND controller HW */
++#define FC_SHIFT		9U
++#define FC_BYTES		512U
++#define FC_WORDS		(FC_BYTES >> 2)
++
++#define BRCMNAND_MIN_PAGESIZE	512
++#define BRCMNAND_MIN_BLOCKSIZE	(8 * 1024)
++#define BRCMNAND_MIN_DEVSIZE	(4ULL * 1024 * 1024)
++
++/* Controller feature flags */
++enum {
++	BRCMNAND_HAS_1K_SECTORS			= BIT(0),
++	BRCMNAND_HAS_PREFETCH			= BIT(1),
++	BRCMNAND_HAS_CACHE_MODE			= BIT(2),
++	BRCMNAND_HAS_WP				= BIT(3),
++};
++
++struct brcmnand_controller {
++	struct device		*dev;
++	struct nand_hw_control	controller;
++	void __iomem		*nand_base;
++	void __iomem		*nand_fc; /* flash cache */
++	void __iomem		*flash_dma_base;
++	unsigned int		irq;
++	unsigned int		dma_irq;
++	int			nand_version;
++
++	int			cmd_pending;
++	bool			dma_pending;
++	struct completion	done;
++	struct completion	dma_done;
++
++	/* List of NAND hosts (one for each chip-select) */
++	struct list_head host_list;
++
++	struct brcm_nand_dma_desc *dma_desc;
++	dma_addr_t		dma_pa;
++
++	/* in-memory cache of the FLASH_CACHE, used only for some commands */
++	u32			flash_cache[FC_WORDS];
++
++	/* Controller revision details */
++	const u16		*reg_offsets;
++	unsigned int		reg_spacing; /* between CS1, CS2, ... regs */
++	const u8		*cs_offsets; /* within each chip-select */
++	const u8		*cs0_offsets; /* within CS0, if different */
++	unsigned int		max_block_size;
++	const unsigned int	*block_sizes;
++	unsigned int		max_page_size;
++	const unsigned int	*page_sizes;
++	unsigned int		max_oob;
++	u32			features;
++
++	/* for low-power standby/resume only */
++	u32			nand_cs_nand_select;
++	u32			nand_cs_nand_xor;
++	u32			corr_stat_threshold;
++	u32			flash_dma_mode;
++};
++
++struct brcmnand_cfg {
++	u64			device_size;
++	unsigned int		block_size;
++	unsigned int		page_size;
++	unsigned int		spare_area_size;
++	unsigned int		device_width;
++	unsigned int		col_adr_bytes;
++	unsigned int		blk_adr_bytes;
++	unsigned int		ful_adr_bytes;
++	unsigned int		sector_size_1k;
++	unsigned int		ecc_level;
++	/* use for low-power standby/resume only */
++	u32			acc_control;
++	u32			config;
++	u32			config_ext;
++	u32			timing_1;
++	u32			timing_2;
++};
++
++struct brcmnand_host {
++	struct list_head	node;
++	struct device_node	*of_node;
++
++	struct nand_chip	chip;
++	struct mtd_info		mtd;
++	struct platform_device	*pdev;
++	int			cs;
++
++	unsigned int		last_cmd;
++	unsigned int		last_byte;
++	u64			last_addr;
++	struct brcmnand_cfg	hwcfg;
++	struct brcmnand_controller *ctrl;
++};
++
++enum brcmnand_reg {
++	BRCMNAND_CMD_START = 0,
++	BRCMNAND_CMD_EXT_ADDRESS,
++	BRCMNAND_CMD_ADDRESS,
++	BRCMNAND_INTFC_STATUS,
++	BRCMNAND_CS_SELECT,
++	BRCMNAND_CS_XOR,
++	BRCMNAND_LL_OP,
++	BRCMNAND_CS0_BASE,
++	BRCMNAND_CS1_BASE,		/* CS1 regs, if non-contiguous */
++	BRCMNAND_CORR_THRESHOLD,
++	BRCMNAND_CORR_THRESHOLD_EXT,
++	BRCMNAND_UNCORR_COUNT,
++	BRCMNAND_CORR_COUNT,
++	BRCMNAND_CORR_EXT_ADDR,
++	BRCMNAND_CORR_ADDR,
++	BRCMNAND_UNCORR_EXT_ADDR,
++	BRCMNAND_UNCORR_ADDR,
++	BRCMNAND_SEMAPHORE,
++	BRCMNAND_ID,
++	BRCMNAND_ID_EXT,
++	BRCMNAND_LL_RDATA,
++	BRCMNAND_OOB_READ_BASE,
++	BRCMNAND_OOB_READ_10_BASE,	/* offset 0x10, if non-contiguous */
++	BRCMNAND_OOB_WRITE_BASE,
++	BRCMNAND_OOB_WRITE_10_BASE,	/* offset 0x10, if non-contiguous */
++	BRCMNAND_FC_BASE,
++};
++
++/* BRCMNAND v4.0 */
++static const u16 brcmnand_regs_v40[] = {
++	[BRCMNAND_CMD_START]		=  0x04,
++	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
++	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
++	[BRCMNAND_INTFC_STATUS]		=  0x6c,
++	[BRCMNAND_CS_SELECT]		=  0x14,
++	[BRCMNAND_CS_XOR]		=  0x18,
++	[BRCMNAND_LL_OP]		= 0x178,
++	[BRCMNAND_CS0_BASE]		=  0x40,
++	[BRCMNAND_CS1_BASE]		=  0xd0,
++	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
++	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
++	[BRCMNAND_UNCORR_COUNT]		=     0,
++	[BRCMNAND_CORR_COUNT]		=     0,
++	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
++	[BRCMNAND_CORR_ADDR]		=  0x74,
++	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
++	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
++	[BRCMNAND_SEMAPHORE]		=  0x58,
++	[BRCMNAND_ID]			=  0x60,
++	[BRCMNAND_ID_EXT]		=  0x64,
++	[BRCMNAND_LL_RDATA]		= 0x17c,
++	[BRCMNAND_OOB_READ_BASE]	=  0x20,
++	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
++	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
++	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
++	[BRCMNAND_FC_BASE]		= 0x200,
++};
++
++/* BRCMNAND v5.0 */
++static const u16 brcmnand_regs_v50[] = {
++	[BRCMNAND_CMD_START]		=  0x04,
++	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
++	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
++	[BRCMNAND_INTFC_STATUS]		=  0x6c,
++	[BRCMNAND_CS_SELECT]		=  0x14,
++	[BRCMNAND_CS_XOR]		=  0x18,
++	[BRCMNAND_LL_OP]		= 0x178,
++	[BRCMNAND_CS0_BASE]		=  0x40,
++	[BRCMNAND_CS1_BASE]		=  0xd0,
++	[BRCMNAND_CORR_THRESHOLD]	=  0x84,
++	[BRCMNAND_CORR_THRESHOLD_EXT]	=     0,
++	[BRCMNAND_UNCORR_COUNT]		=     0,
++	[BRCMNAND_CORR_COUNT]		=     0,
++	[BRCMNAND_CORR_EXT_ADDR]	=  0x70,
++	[BRCMNAND_CORR_ADDR]		=  0x74,
++	[BRCMNAND_UNCORR_EXT_ADDR]	=  0x78,
++	[BRCMNAND_UNCORR_ADDR]		=  0x7c,
++	[BRCMNAND_SEMAPHORE]		=  0x58,
++	[BRCMNAND_ID]			=  0x60,
++	[BRCMNAND_ID_EXT]		=  0x64,
++	[BRCMNAND_LL_RDATA]		= 0x17c,
++	[BRCMNAND_OOB_READ_BASE]	=  0x20,
++	[BRCMNAND_OOB_READ_10_BASE]	= 0x130,
++	[BRCMNAND_OOB_WRITE_BASE]	=  0x30,
++	[BRCMNAND_OOB_WRITE_10_BASE]	= 0x140,
++	[BRCMNAND_FC_BASE]		= 0x200,
++};
++
++/* BRCMNAND v6.0 - v7.1 */
++static const u16 brcmnand_regs_v60[] = {
++	[BRCMNAND_CMD_START]		=  0x04,
++	[BRCMNAND_CMD_EXT_ADDRESS]	=  0x08,
++	[BRCMNAND_CMD_ADDRESS]		=  0x0c,
++	[BRCMNAND_INTFC_STATUS]		=  0x14,
++	[BRCMNAND_CS_SELECT]		=  0x18,
++	[BRCMNAND_CS_XOR]		=  0x1c,
++	[BRCMNAND_LL_OP]		=  0x20,
++	[BRCMNAND_CS0_BASE]		=  0x50,
++	[BRCMNAND_CS1_BASE]		=     0,
++	[BRCMNAND_CORR_THRESHOLD]	=  0xc0,
++	[BRCMNAND_CORR_THRESHOLD_EXT]	=  0xc4,
++	[BRCMNAND_UNCORR_COUNT]		=  0xfc,
++	[BRCMNAND_CORR_COUNT]		= 0x100,
++	[BRCMNAND_CORR_EXT_ADDR]	= 0x10c,
++	[BRCMNAND_CORR_ADDR]		= 0x110,
++	[BRCMNAND_UNCORR_EXT_ADDR]	= 0x114,
++	[BRCMNAND_UNCORR_ADDR]		= 0x118,
++	[BRCMNAND_SEMAPHORE]		= 0x150,
++	[BRCMNAND_ID]			= 0x194,
++	[BRCMNAND_ID_EXT]		= 0x198,
++	[BRCMNAND_LL_RDATA]		= 0x19c,
++	[BRCMNAND_OOB_READ_BASE]	= 0x200,
++	[BRCMNAND_OOB_READ_10_BASE]	=     0,
++	[BRCMNAND_OOB_WRITE_BASE]	= 0x280,
++	[BRCMNAND_OOB_WRITE_10_BASE]	=     0,
++	[BRCMNAND_FC_BASE]		= 0x400,
++};
++
++enum brcmnand_cs_reg {
++	BRCMNAND_CS_CFG_EXT = 0,
++	BRCMNAND_CS_CFG,
++	BRCMNAND_CS_ACC_CONTROL,
++	BRCMNAND_CS_TIMING1,
++	BRCMNAND_CS_TIMING2,
++};
++
++/* Per chip-select offsets for v7.1 */
++static const u8 brcmnand_cs_offsets_v71[] = {
++	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
++	[BRCMNAND_CS_CFG_EXT]		= 0x04,
++	[BRCMNAND_CS_CFG]		= 0x08,
++	[BRCMNAND_CS_TIMING1]		= 0x0c,
++	[BRCMNAND_CS_TIMING2]		= 0x10,
++};
++
++/* Per chip-select offsets for pre v7.1, except CS0 on <= v5.0 */
++static const u8 brcmnand_cs_offsets[] = {
++	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
++	[BRCMNAND_CS_CFG_EXT]		= 0x04,
++	[BRCMNAND_CS_CFG]		= 0x04,
++	[BRCMNAND_CS_TIMING1]		= 0x08,
++	[BRCMNAND_CS_TIMING2]		= 0x0c,
++};
++
++/* Per chip-select offset for <= v5.0 on CS0 only */
++static const u8 brcmnand_cs_offsets_cs0[] = {
++	[BRCMNAND_CS_ACC_CONTROL]	= 0x00,
++	[BRCMNAND_CS_CFG_EXT]		= 0x08,
++	[BRCMNAND_CS_CFG]		= 0x08,
++	[BRCMNAND_CS_TIMING1]		= 0x10,
++	[BRCMNAND_CS_TIMING2]		= 0x14,
++};
++
++/* BRCMNAND_INTFC_STATUS */
++enum {
++	INTFC_FLASH_STATUS		= GENMASK(7, 0),
++
++	INTFC_ERASED			= BIT(27),
++	INTFC_OOB_VALID			= BIT(28),
++	INTFC_CACHE_VALID		= BIT(29),
++	INTFC_FLASH_READY		= BIT(30),
++	INTFC_CTLR_READY		= BIT(31),
++};
++
++static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
++{
++	return brcmnand_readl(ctrl->nand_base + offs);
++}
++
++static inline void nand_writereg(struct brcmnand_controller *ctrl, u32 offs,
++				 u32 val)
++{
++	brcmnand_writel(val, ctrl->nand_base + offs);
++}
++
++static int brcmnand_revision_init(struct brcmnand_controller *ctrl)
++{
++	static const unsigned int block_sizes_v6[] = { 8, 16, 128, 256, 512, 1024, 2048, 0 };
++	static const unsigned int block_sizes_v4[] = { 16, 128, 8, 512, 256, 1024, 2048, 0 };
++	static const unsigned int page_sizes[] = { 512, 2048, 4096, 8192, 0 };
++
++	ctrl->nand_version = nand_readreg(ctrl, 0) & 0xffff;
++
++	/* Only support v4.0+? */
++	if (ctrl->nand_version < 0x0400) {
++		dev_err(ctrl->dev, "version %#x not supported\n",
++			ctrl->nand_version);
++		return -ENODEV;
++	}
++
++	/* Register offsets */
++	if (ctrl->nand_version >= 0x0600)
++		ctrl->reg_offsets = brcmnand_regs_v60;
++	else if (ctrl->nand_version >= 0x0500)
++		ctrl->reg_offsets = brcmnand_regs_v50;
++	else if (ctrl->nand_version >= 0x0400)
++		ctrl->reg_offsets = brcmnand_regs_v40;
++
++	/* Chip-select stride */
++	if (ctrl->nand_version >= 0x0701)
++		ctrl->reg_spacing = 0x14;
++	else
++		ctrl->reg_spacing = 0x10;
++
++	/* Per chip-select registers */
++	if (ctrl->nand_version >= 0x0701) {
++		ctrl->cs_offsets = brcmnand_cs_offsets_v71;
++	} else {
++		ctrl->cs_offsets = brcmnand_cs_offsets;
++
++		/* v5.0 and earlier has a different CS0 offset layout */
++		if (ctrl->nand_version <= 0x0500)
++			ctrl->cs0_offsets = brcmnand_cs_offsets_cs0;
++	}
++
++	/* Page / block sizes */
++	if (ctrl->nand_version >= 0x0701) {
++		/* >= v7.1 use nice power-of-2 values! */
++		ctrl->max_page_size = 16 * 1024;
++		ctrl->max_block_size = 2 * 1024 * 1024;
++	} else {
++		ctrl->page_sizes = page_sizes;
++		if (ctrl->nand_version >= 0x0600)
++			ctrl->block_sizes = block_sizes_v6;
++		else
++			ctrl->block_sizes = block_sizes_v4;
++
++		if (ctrl->nand_version < 0x0400) {
++			ctrl->max_page_size = 4096;
++			ctrl->max_block_size = 512 * 1024;
++		}
++	}
++
++	/* Maximum spare area sector size (per 512B) */
++	if (ctrl->nand_version >= 0x0600)
++		ctrl->max_oob = 64;
++	else if (ctrl->nand_version >= 0x0500)
++		ctrl->max_oob = 32;
++	else
++		ctrl->max_oob = 16;
++
++	/* v6.0 and newer (except v6.1) have prefetch support */
++	if (ctrl->nand_version >= 0x0600 && ctrl->nand_version != 0x0601)
++		ctrl->features |= BRCMNAND_HAS_PREFETCH;
++
++	/*
++	 * v6.x has cache mode, but it's implemented differently. Ignore it for
++	 * now.
++	 */
++	if (ctrl->nand_version >= 0x0700)
++		ctrl->features |= BRCMNAND_HAS_CACHE_MODE;
++
++	if (ctrl->nand_version >= 0x0500)
++		ctrl->features |= BRCMNAND_HAS_1K_SECTORS;
++
++	if (ctrl->nand_version >= 0x0700)
++		ctrl->features |= BRCMNAND_HAS_WP;
++	else if (of_property_read_bool(ctrl->dev->of_node, "brcm,nand-has-wp"))
++		ctrl->features |= BRCMNAND_HAS_WP;
++
++	return 0;
++}
++
++static inline u32 brcmnand_read_reg(struct brcmnand_controller *ctrl,
++		enum brcmnand_reg reg)
++{
++	u16 offs = ctrl->reg_offsets[reg];
++
++	if (offs)
++		return nand_readreg(ctrl, offs);
++	else
++		return 0;
++}
++
++static inline void brcmnand_write_reg(struct brcmnand_controller *ctrl,
++				      enum brcmnand_reg reg, u32 val)
++{
++	u16 offs = ctrl->reg_offsets[reg];
++
++	if (offs)
++		nand_writereg(ctrl, offs, val);
++}
++
++static inline void brcmnand_rmw_reg(struct brcmnand_controller *ctrl,
++				    enum brcmnand_reg reg, u32 mask, unsigned
++				    int shift, u32 val)
++{
++	u32 tmp = brcmnand_read_reg(ctrl, reg);
++
++	tmp &= ~mask;
++	tmp |= val << shift;
++	brcmnand_write_reg(ctrl, reg, tmp);
++}
++
++static inline u32 brcmnand_read_fc(struct brcmnand_controller *ctrl, int word)
++{
++	return __raw_readl(ctrl->nand_fc + word * 4);
++}
++
++static inline void brcmnand_write_fc(struct brcmnand_controller *ctrl,
++				     int word, u32 val)
++{
++	__raw_writel(val, ctrl->nand_fc + word * 4);
++}
++
++static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs,
++				     enum brcmnand_cs_reg reg)
++{
++	u16 offs_cs0 = ctrl->reg_offsets[BRCMNAND_CS0_BASE];
++	u16 offs_cs1 = ctrl->reg_offsets[BRCMNAND_CS1_BASE];
++	u8 cs_offs;
++
++	if (cs == 0 && ctrl->cs0_offsets)
++		cs_offs = ctrl->cs0_offsets[reg];
++	else
++		cs_offs = ctrl->cs_offsets[reg];
++
++	if (cs && offs_cs1)
++		return offs_cs1 + (cs - 1) * ctrl->reg_spacing + cs_offs;
++
++	return offs_cs0 + cs * ctrl->reg_spacing + cs_offs;
++}
++
++static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl)
++{
++	if (ctrl->nand_version < 0x0600)
++		return 1;
++	return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT);
++}
++
++static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	unsigned int shift = 0, bits;
++	enum brcmnand_reg reg = BRCMNAND_CORR_THRESHOLD;
++	int cs = host->cs;
++
++	if (ctrl->nand_version >= 0x0600)
++		bits = 6;
++	else if (ctrl->nand_version >= 0x0500)
++		bits = 5;
++	else
++		bits = 4;
++
++	if (ctrl->nand_version >= 0x0600) {
++		if (cs >= 5)
++			reg = BRCMNAND_CORR_THRESHOLD_EXT;
++		shift = (cs % 5) * bits;
++	}
++	brcmnand_rmw_reg(ctrl, reg, (bits - 1) << shift, shift, val);
++}
++
++static inline int brcmnand_cmd_shift(struct brcmnand_controller *ctrl)
++{
++	if (ctrl->nand_version < 0x0700)
++		return 24;
++	return 0;
++}
++
++/***********************************************************************
++ * NAND ACC CONTROL bitfield
++ *
++ * Some bits have remained constant throughout hardware revision, while
++ * others have shifted around.
++ ***********************************************************************/
++
++/* Constant for all versions (where supported) */
++enum {
++	/* See BRCMNAND_HAS_CACHE_MODE */
++	ACC_CONTROL_CACHE_MODE				= BIT(22),
++
++	/* See BRCMNAND_HAS_PREFETCH */
++	ACC_CONTROL_PREFETCH				= BIT(23),
++
++	ACC_CONTROL_PAGE_HIT				= BIT(24),
++	ACC_CONTROL_WR_PREEMPT				= BIT(25),
++	ACC_CONTROL_PARTIAL_PAGE			= BIT(26),
++	ACC_CONTROL_RD_ERASED				= BIT(27),
++	ACC_CONTROL_FAST_PGM_RDIN			= BIT(28),
++	ACC_CONTROL_WR_ECC				= BIT(30),
++	ACC_CONTROL_RD_ECC				= BIT(31),
++};
++
++static inline u32 brcmnand_spare_area_mask(struct brcmnand_controller *ctrl)
++{
++	if (ctrl->nand_version >= 0x0600)
++		return GENMASK(6, 0);
++	else
++		return GENMASK(5, 0);
++}
++
++#define NAND_ACC_CONTROL_ECC_SHIFT	16
++
++static inline u32 brcmnand_ecc_level_mask(struct brcmnand_controller *ctrl)
++{
++	u32 mask = (ctrl->nand_version >= 0x0600) ? 0x1f : 0x0f;
++
++	return mask << NAND_ACC_CONTROL_ECC_SHIFT;
++}
++
++static void brcmnand_set_ecc_enabled(struct brcmnand_host *host, int en)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	u16 offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
++	u32 acc_control = nand_readreg(ctrl, offs);
++	u32 ecc_flags = ACC_CONTROL_WR_ECC | ACC_CONTROL_RD_ECC;
++
++	if (en) {
++		acc_control |= ecc_flags; /* enable RD/WR ECC */
++		acc_control |= host->hwcfg.ecc_level
++			       << NAND_ACC_CONTROL_ECC_SHIFT;
++	} else {
++		acc_control &= ~ecc_flags; /* disable RD/WR ECC */
++		acc_control &= ~brcmnand_ecc_level_mask(ctrl);
++	}
++
++	nand_writereg(ctrl, offs, acc_control);
++}
++
++static inline int brcmnand_sector_1k_shift(struct brcmnand_controller *ctrl)
++{
++	if (ctrl->nand_version >= 0x0600)
++		return 7;
++	else if (ctrl->nand_version >= 0x0500)
++		return 6;
++	else
++		return -1;
++}
++
++static int brcmnand_get_sector_size_1k(struct brcmnand_host *host)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	int shift = brcmnand_sector_1k_shift(ctrl);
++	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
++						  BRCMNAND_CS_ACC_CONTROL);
++
++	if (shift < 0)
++		return 0;
++
++	return (nand_readreg(ctrl, acc_control_offs) >> shift) & 0x1;
++}
++
++static void brcmnand_set_sector_size_1k(struct brcmnand_host *host, int val)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	int shift = brcmnand_sector_1k_shift(ctrl);
++	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
++						  BRCMNAND_CS_ACC_CONTROL);
++	u32 tmp;
++
++	if (shift < 0)
++		return;
++
++	tmp = nand_readreg(ctrl, acc_control_offs);
++	tmp &= ~(1 << shift);
++	tmp |= (!!val) << shift;
++	nand_writereg(ctrl, acc_control_offs, tmp);
++}
++
++/***********************************************************************
++ * CS_NAND_SELECT
++ ***********************************************************************/
++
++enum {
++	CS_SELECT_NAND_WP			= BIT(29),
++	CS_SELECT_AUTO_DEVICE_ID_CFG		= BIT(30),
++};
++
++static inline void brcmnand_set_wp(struct brcmnand_controller *ctrl, bool en)
++{
++	u32 val = en ? CS_SELECT_NAND_WP : 0;
++
++	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT, CS_SELECT_NAND_WP, 0, val);
++}
++
++/***********************************************************************
++ * Flash DMA
++ ***********************************************************************/
++
++enum flash_dma_reg {
++	FLASH_DMA_REVISION		= 0x00,
++	FLASH_DMA_FIRST_DESC		= 0x04,
++	FLASH_DMA_FIRST_DESC_EXT	= 0x08,
++	FLASH_DMA_CTRL			= 0x0c,
++	FLASH_DMA_MODE			= 0x10,
++	FLASH_DMA_STATUS		= 0x14,
++	FLASH_DMA_INTERRUPT_DESC	= 0x18,
++	FLASH_DMA_INTERRUPT_DESC_EXT	= 0x1c,
++	FLASH_DMA_ERROR_STATUS		= 0x20,
++	FLASH_DMA_CURRENT_DESC		= 0x24,
++	FLASH_DMA_CURRENT_DESC_EXT	= 0x28,
++};
++
++static inline bool has_flash_dma(struct brcmnand_controller *ctrl)
++{
++	return ctrl->flash_dma_base;
++}
++
++static inline bool flash_dma_buf_ok(const void *buf)
++{
++	return buf && !is_vmalloc_addr(buf) &&
++		likely(IS_ALIGNED((uintptr_t)buf, 4));
++}
++
++static inline void flash_dma_writel(struct brcmnand_controller *ctrl, u8 offs,
++				    u32 val)
++{
++	brcmnand_writel(val, ctrl->flash_dma_base + offs);
++}
++
++static inline u32 flash_dma_readl(struct brcmnand_controller *ctrl, u8 offs)
++{
++	return brcmnand_readl(ctrl->flash_dma_base + offs);
++}
++
++/* Low-level operation types: command, address, write, or read */
++enum brcmnand_llop_type {
++	LL_OP_CMD,
++	LL_OP_ADDR,
++	LL_OP_WR,
++	LL_OP_RD,
++};
++
++/***********************************************************************
++ * Internal support functions
++ ***********************************************************************/
++
++static inline bool is_hamming_ecc(struct brcmnand_cfg *cfg)
++{
++	return cfg->sector_size_1k == 0 && cfg->spare_area_size == 16 &&
++		cfg->ecc_level == 15;
++}
++
++/*
++ * Returns a nand_ecclayout strucutre for the given layout/configuration.
++ * Returns NULL on failure.
++ */
++static struct nand_ecclayout *brcmnand_create_layout(int ecc_level,
++						     struct brcmnand_host *host)
++{
++	struct brcmnand_cfg *cfg = &host->hwcfg;
++	int i, j;
++	struct nand_ecclayout *layout;
++	int req;
++	int sectors;
++	int sas;
++	int idx1, idx2;
++
++	layout = devm_kzalloc(&host->pdev->dev, sizeof(*layout), GFP_KERNEL);
++	if (!layout)
++		return NULL;
++
++	sectors = cfg->page_size / (512 << cfg->sector_size_1k);
++	sas = cfg->spare_area_size << cfg->sector_size_1k;
++
++	/* Hamming */
++	if (is_hamming_ecc(cfg)) {
++		for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
++			/* First sector of each page may have BBI */
++			if (i == 0) {
++				layout->oobfree[idx2].offset = i * sas + 1;
++				/* Small-page NAND use byte 6 for BBI */
++				if (cfg->page_size == 512)
++					layout->oobfree[idx2].offset--;
++				layout->oobfree[idx2].length = 5;
++			} else {
++				layout->oobfree[idx2].offset = i * sas;
++				layout->oobfree[idx2].length = 6;
++			}
++			idx2++;
++			layout->eccpos[idx1++] = i * sas + 6;
++			layout->eccpos[idx1++] = i * sas + 7;
++			layout->eccpos[idx1++] = i * sas + 8;
++			layout->oobfree[idx2].offset = i * sas + 9;
++			layout->oobfree[idx2].length = 7;
++			idx2++;
++			/* Leave zero-terminated entry for OOBFREE */
++			if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
++				    idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
++				break;
++		}
++		goto out;
++	}
++
++	/*
++	 * CONTROLLER_VERSION:
++	 *   < v5.0: ECC_REQ = ceil(BCH_T * 13/8)
++	 *  >= v5.0: ECC_REQ = ceil(BCH_T * 14/8)
++	 * But we will just be conservative.
++	 */
++	req = DIV_ROUND_UP(ecc_level * 14, 8);
++	if (req >= sas) {
++		dev_err(&host->pdev->dev,
++			"error: ECC too large for OOB (ECC bytes %d, spare sector %d)\n",
++			req, sas);
++		return NULL;
++	}
++
++	layout->eccbytes = req * sectors;
++	for (i = 0, idx1 = 0, idx2 = 0; i < sectors; i++) {
++		for (j = sas - req; j < sas && idx1 <
++				MTD_MAX_ECCPOS_ENTRIES_LARGE; j++, idx1++)
++			layout->eccpos[idx1] = i * sas + j;
++
++		/* First sector of each page may have BBI */
++		if (i == 0) {
++			if (cfg->page_size == 512 && (sas - req >= 6)) {
++				/* Small-page NAND use byte 6 for BBI */
++				layout->oobfree[idx2].offset = 0;
++				layout->oobfree[idx2].length = 5;
++				idx2++;
++				if (sas - req > 6) {
++					layout->oobfree[idx2].offset = 6;
++					layout->oobfree[idx2].length =
++						sas - req - 6;
++					idx2++;
++				}
++			} else if (sas > req + 1) {
++				layout->oobfree[idx2].offset = i * sas + 1;
++				layout->oobfree[idx2].length = sas - req - 1;
++				idx2++;
++			}
++		} else if (sas > req) {
++			layout->oobfree[idx2].offset = i * sas;
++			layout->oobfree[idx2].length = sas - req;
++			idx2++;
++		}
++		/* Leave zero-terminated entry for OOBFREE */
++		if (idx1 >= MTD_MAX_ECCPOS_ENTRIES_LARGE ||
++				idx2 >= MTD_MAX_OOBFREE_ENTRIES_LARGE - 1)
++			break;
++	}
++out:
++	/* Sum available OOB */
++	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE; i++)
++		layout->oobavail += layout->oobfree[i].length;
++	return layout;
++}
++
++static struct nand_ecclayout *brcmstb_choose_ecc_layout(
++		struct brcmnand_host *host)
++{
++	struct nand_ecclayout *layout;
++	struct brcmnand_cfg *p = &host->hwcfg;
++	unsigned int ecc_level = p->ecc_level;
++
++	if (p->sector_size_1k)
++		ecc_level <<= 1;
++
++	layout = brcmnand_create_layout(ecc_level, host);
++	if (!layout) {
++		dev_err(&host->pdev->dev,
++				"no proper ecc_layout for this NAND cfg\n");
++		return NULL;
++	}
++
++	return layout;
++}
++
++static void brcmnand_wp(struct mtd_info *mtd, int wp)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++
++	if ((ctrl->features & BRCMNAND_HAS_WP) && wp_on == 1) {
++		static int old_wp = -1;
++
++		if (old_wp != wp) {
++			dev_dbg(ctrl->dev, "WP %s\n", wp ? "on" : "off");
++			old_wp = wp;
++		}
++		brcmnand_set_wp(ctrl, wp);
++	}
++}
++
++/* Helper functions for reading and writing OOB registers */
++static inline u8 oob_reg_read(struct brcmnand_controller *ctrl, u32 offs)
++{
++	u16 offset0, offset10, reg_offs;
++
++	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_READ_BASE];
++	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_READ_10_BASE];
++
++	if (offs >= ctrl->max_oob)
++		return 0x77;
++
++	if (offs >= 16 && offset10)
++		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
++	else
++		reg_offs = offset0 + (offs & ~0x03);
++
++	return nand_readreg(ctrl, reg_offs) >> (24 - ((offs & 0x03) << 3));
++}
++
++static inline void oob_reg_write(struct brcmnand_controller *ctrl, u32 offs,
++				 u32 data)
++{
++	u16 offset0, offset10, reg_offs;
++
++	offset0 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_BASE];
++	offset10 = ctrl->reg_offsets[BRCMNAND_OOB_WRITE_10_BASE];
++
++	if (offs >= ctrl->max_oob)
++		return;
++
++	if (offs >= 16 && offset10)
++		reg_offs = offset10 + ((offs - 0x10) & ~0x03);
++	else
++		reg_offs = offset0 + (offs & ~0x03);
++
++	nand_writereg(ctrl, reg_offs, data);
++}
++
++/*
++ * read_oob_from_regs - read data from OOB registers
++ * @ctrl: NAND controller
++ * @i: sub-page sector index
++ * @oob: buffer to read to
++ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
++ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
++ */
++static int read_oob_from_regs(struct brcmnand_controller *ctrl, int i, u8 *oob,
++			      int sas, int sector_1k)
++{
++	int tbytes = sas << sector_1k;
++	int j;
++
++	/* Adjust OOB values for 1K sector size */
++	if (sector_1k && (i & 0x01))
++		tbytes = max(0, tbytes - (int)ctrl->max_oob);
++	tbytes = min_t(int, tbytes, ctrl->max_oob);
++
++	for (j = 0; j < tbytes; j++)
++		oob[j] = oob_reg_read(ctrl, j);
++	return tbytes;
++}
++
++/*
++ * write_oob_to_regs - write data to OOB registers
++ * @i: sub-page sector index
++ * @oob: buffer to write from
++ * @sas: spare area sector size (i.e., OOB size per FLASH_CACHE)
++ * @sector_1k: 1 for 1KiB sectors, 0 for 512B, other values are illegal
++ */
++static int write_oob_to_regs(struct brcmnand_controller *ctrl, int i,
++			     const u8 *oob, int sas, int sector_1k)
++{
++	int tbytes = sas << sector_1k;
++	int j;
++
++	/* Adjust OOB values for 1K sector size */
++	if (sector_1k && (i & 0x01))
++		tbytes = max(0, tbytes - (int)ctrl->max_oob);
++	tbytes = min_t(int, tbytes, ctrl->max_oob);
++
++	for (j = 0; j < tbytes; j += 4)
++		oob_reg_write(ctrl, j,
++				(oob[j + 0] << 24) |
++				(oob[j + 1] << 16) |
++				(oob[j + 2] <<  8) |
++				(oob[j + 3] <<  0));
++	return tbytes;
++}
++
++static irqreturn_t brcmnand_ctlrdy_irq(int irq, void *data)
++{
++	struct brcmnand_controller *ctrl = data;
++
++	/* Discard all NAND_CTLRDY interrupts during DMA */
++	if (ctrl->dma_pending)
++		return IRQ_HANDLED;
++
++	complete(&ctrl->done);
++	return IRQ_HANDLED;
++}
++
++static irqreturn_t brcmnand_dma_irq(int irq, void *data)
++{
++	struct brcmnand_controller *ctrl = data;
++
++	complete(&ctrl->dma_done);
++
++	return IRQ_HANDLED;
++}
++
++static void brcmnand_send_cmd(struct brcmnand_host *host, int cmd)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	u32 intfc;
++
++	dev_dbg(ctrl->dev, "send native cmd %d addr_lo 0x%x\n", cmd,
++		brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS));
++	BUG_ON(ctrl->cmd_pending != 0);
++	ctrl->cmd_pending = cmd;
++
++	intfc = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
++	BUG_ON(!(intfc & INTFC_CTLR_READY));
++
++	mb(); /* flush previous writes */
++	brcmnand_write_reg(ctrl, BRCMNAND_CMD_START,
++			   cmd << brcmnand_cmd_shift(ctrl));
++}
++
++/***********************************************************************
++ * NAND MTD API: read/program/erase
++ ***********************************************************************/
++
++static void brcmnand_cmd_ctrl(struct mtd_info *mtd, int dat,
++	unsigned int ctrl)
++{
++	/* intentionally left blank */
++}
++
++static int brcmnand_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	unsigned long timeo = msecs_to_jiffies(100);
++
++	dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
++	if (ctrl->cmd_pending &&
++			wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
++		u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
++					>> brcmnand_cmd_shift(ctrl);
++
++		dev_err_ratelimited(ctrl->dev,
++			"timeout waiting for command %#02x\n", cmd);
++		dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
++			brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
++	}
++	ctrl->cmd_pending = 0;
++	return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
++				 INTFC_FLASH_STATUS;
++}
++
++enum {
++	LLOP_RE				= BIT(16),
++	LLOP_WE				= BIT(17),
++	LLOP_ALE			= BIT(18),
++	LLOP_CLE			= BIT(19),
++	LLOP_RETURN_IDLE		= BIT(31),
++
++	LLOP_DATA_MASK			= GENMASK(15, 0),
++};
++
++static int brcmnand_low_level_op(struct brcmnand_host *host,
++				 enum brcmnand_llop_type type, u32 data,
++				 bool last_op)
++{
++	struct mtd_info *mtd = &host->mtd;
++	struct nand_chip *chip = &host->chip;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	u32 tmp;
++
++	tmp = data & LLOP_DATA_MASK;
++	switch (type) {
++	case LL_OP_CMD:
++		tmp |= LLOP_WE | LLOP_CLE;
++		break;
++	case LL_OP_ADDR:
++		/* WE | ALE */
++		tmp |= LLOP_WE | LLOP_ALE;
++		break;
++	case LL_OP_WR:
++		/* WE */
++		tmp |= LLOP_WE;
++		break;
++	case LL_OP_RD:
++		/* RE */
++		tmp |= LLOP_RE;
++		break;
++	}
++	if (last_op)
++		/* RETURN_IDLE */
++		tmp |= LLOP_RETURN_IDLE;
++
++	dev_dbg(ctrl->dev, "ll_op cmd %#x\n", tmp);
++
++	brcmnand_write_reg(ctrl, BRCMNAND_LL_OP, tmp);
++	(void)brcmnand_read_reg(ctrl, BRCMNAND_LL_OP);
++
++	brcmnand_send_cmd(host, CMD_LOW_LEVEL_OP);
++	return brcmnand_waitfunc(mtd, chip);
++}
++
++static void brcmnand_cmdfunc(struct mtd_info *mtd, unsigned command,
++			     int column, int page_addr)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	u64 addr = (u64)page_addr << chip->page_shift;
++	int native_cmd = 0;
++
++	if (command == NAND_CMD_READID || command == NAND_CMD_PARAM ||
++			command == NAND_CMD_RNDOUT)
++		addr = (u64)column;
++	/* Avoid propagating a negative, don't-care address */
++	else if (page_addr < 0)
++		addr = 0;
++
++	dev_dbg(ctrl->dev, "cmd 0x%x addr 0x%llx\n", command,
++		(unsigned long long)addr);
++
++	host->last_cmd = command;
++	host->last_byte = 0;
++	host->last_addr = addr;
++
++	switch (command) {
++	case NAND_CMD_RESET:
++		native_cmd = CMD_FLASH_RESET;
++		break;
++	case NAND_CMD_STATUS:
++		native_cmd = CMD_STATUS_READ;
++		break;
++	case NAND_CMD_READID:
++		native_cmd = CMD_DEVICE_ID_READ;
++		break;
++	case NAND_CMD_READOOB:
++		native_cmd = CMD_SPARE_AREA_READ;
++		break;
++	case NAND_CMD_ERASE1:
++		native_cmd = CMD_BLOCK_ERASE;
++		brcmnand_wp(mtd, 0);
++		break;
++	case NAND_CMD_PARAM:
++		native_cmd = CMD_PARAMETER_READ;
++		break;
++	case NAND_CMD_SET_FEATURES:
++	case NAND_CMD_GET_FEATURES:
++		brcmnand_low_level_op(host, LL_OP_CMD, command, false);
++		brcmnand_low_level_op(host, LL_OP_ADDR, column, false);
++		break;
++	case NAND_CMD_RNDOUT:
++		native_cmd = CMD_PARAMETER_CHANGE_COL;
++		addr &= ~((u64)(FC_BYTES - 1));
++		/*
++		 * HW quirk: PARAMETER_CHANGE_COL requires SECTOR_SIZE_1K=0
++		 * NB: hwcfg.sector_size_1k may not be initialized yet
++		 */
++		if (brcmnand_get_sector_size_1k(host)) {
++			host->hwcfg.sector_size_1k =
++				brcmnand_get_sector_size_1k(host);
++			brcmnand_set_sector_size_1k(host, 0);
++		}
++		break;
++	}
++
++	if (!native_cmd)
++		return;
++
++	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
++		(host->cs << 16) | ((addr >> 32) & 0xffff));
++	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
++	brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS, lower_32_bits(addr));
++	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
++
++	brcmnand_send_cmd(host, native_cmd);
++	brcmnand_waitfunc(mtd, chip);
++
++	if (native_cmd == CMD_PARAMETER_READ ||
++			native_cmd == CMD_PARAMETER_CHANGE_COL) {
++		int i;
++		/*
++		 * Must cache the FLASH_CACHE now, since changes in
++		 * SECTOR_SIZE_1K may invalidate it
++		 */
++		for (i = 0; i < FC_WORDS; i++)
++			ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i);
++		/* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
++		if (host->hwcfg.sector_size_1k)
++			brcmnand_set_sector_size_1k(host,
++						    host->hwcfg.sector_size_1k);
++	}
++
++	/* Re-enable protection is necessary only after erase */
++	if (command == NAND_CMD_ERASE1)
++		brcmnand_wp(mtd, 1);
++}
++
++static uint8_t brcmnand_read_byte(struct mtd_info *mtd)
++{
++	struct nand_chip *chip = mtd->priv;
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	uint8_t ret = 0;
++	int addr, offs;
++
++	switch (host->last_cmd) {
++	case NAND_CMD_READID:
++		if (host->last_byte < 4)
++			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID) >>
++				(24 - (host->last_byte << 3));
++		else if (host->last_byte < 8)
++			ret = brcmnand_read_reg(ctrl, BRCMNAND_ID_EXT) >>
++				(56 - (host->last_byte << 3));
++		break;
++
++	case NAND_CMD_READOOB:
++		ret = oob_reg_read(ctrl, host->last_byte);
++		break;
++
++	case NAND_CMD_STATUS:
++		ret = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
++					INTFC_FLASH_STATUS;
++		if (wp_on) /* hide WP status */
++			ret |= NAND_STATUS_WP;
++		break;
++
++	case NAND_CMD_PARAM:
++	case NAND_CMD_RNDOUT:
++		addr = host->last_addr + host->last_byte;
++		offs = addr & (FC_BYTES - 1);
++
++		/* At FC_BYTES boundary, switch to next column */
++		if (host->last_byte > 0 && offs == 0)
++			chip->cmdfunc(mtd, NAND_CMD_RNDOUT, addr, -1);
++
++		ret = ctrl->flash_cache[offs >> 2] >>
++					(24 - ((offs & 0x03) << 3));
++		break;
++	case NAND_CMD_GET_FEATURES:
++		if (host->last_byte >= ONFI_SUBFEATURE_PARAM_LEN) {
++			ret = 0;
++		} else {
++			bool last = host->last_byte ==
++				ONFI_SUBFEATURE_PARAM_LEN - 1;
++			brcmnand_low_level_op(host, LL_OP_RD, 0, last);
++			ret = brcmnand_read_reg(ctrl, BRCMNAND_LL_RDATA) & 0xff;
++		}
++	}
++
++	dev_dbg(ctrl->dev, "read byte = 0x%02x\n", ret);
++	host->last_byte++;
++
++	return ret;
++}
++
++static void brcmnand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
++{
++	int i;
++
++	for (i = 0; i < len; i++, buf++)
++		*buf = brcmnand_read_byte(mtd);
++}
++
++static void brcmnand_write_buf(struct mtd_info *mtd, const uint8_t *buf,
++				   int len)
++{
++	int i;
++	struct nand_chip *chip = mtd->priv;
++	struct brcmnand_host *host = chip->priv;
++
++	switch (host->last_cmd) {
++	case NAND_CMD_SET_FEATURES:
++		for (i = 0; i < len; i++)
++			brcmnand_low_level_op(host, LL_OP_WR, buf[i],
++						  (i + 1) == len);
++		break;
++	default:
++		BUG();
++		break;
++	}
++}
++
++/**
++ * Construct a FLASH_DMA descriptor as part of a linked list. You must know the
++ * following ahead of time:
++ *  - Is this descriptor the beginning or end of a linked list?
++ *  - What is the (DMA) address of the next descriptor in the linked list?
++ */
++static int brcmnand_fill_dma_desc(struct brcmnand_host *host,
++				  struct brcm_nand_dma_desc *desc, u64 addr,
++				  dma_addr_t buf, u32 len, u8 dma_cmd,
++				  bool begin, bool end,
++				  dma_addr_t next_desc)
++{
++	memset(desc, 0, sizeof(*desc));
++	/* Descriptors are written in native byte order (wordwise) */
++	desc->next_desc = lower_32_bits(next_desc);
++	desc->next_desc_ext = upper_32_bits(next_desc);
++	desc->cmd_irq = (dma_cmd << 24) |
++		(end ? (0x03 << 8) : 0) | /* IRQ | STOP */
++		(!!begin) | ((!!end) << 1); /* head, tail */
++#ifdef CONFIG_CPU_BIG_ENDIAN
++	desc->cmd_irq |= 0x01 << 12;
++#endif
++	desc->dram_addr = lower_32_bits(buf);
++	desc->dram_addr_ext = upper_32_bits(buf);
++	desc->tfr_len = len;
++	desc->total_len = len;
++	desc->flash_addr = lower_32_bits(addr);
++	desc->flash_addr_ext = upper_32_bits(addr);
++	desc->cs = host->cs;
++	desc->status_valid = 0x01;
++	return 0;
++}
++
++/**
++ * Kick the FLASH_DMA engine, with a given DMA descriptor
++ */
++static void brcmnand_dma_run(struct brcmnand_host *host, dma_addr_t desc)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	unsigned long timeo = msecs_to_jiffies(100);
++
++	flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC, lower_32_bits(desc));
++	(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC);
++	flash_dma_writel(ctrl, FLASH_DMA_FIRST_DESC_EXT, upper_32_bits(desc));
++	(void)flash_dma_readl(ctrl, FLASH_DMA_FIRST_DESC_EXT);
++
++	/* Start FLASH_DMA engine */
++	ctrl->dma_pending = true;
++	mb(); /* flush previous writes */
++	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0x03); /* wake | run */
++
++	if (wait_for_completion_timeout(&ctrl->dma_done, timeo) <= 0) {
++		dev_err(ctrl->dev,
++				"timeout waiting for DMA; status %#x, error status %#x\n",
++				flash_dma_readl(ctrl, FLASH_DMA_STATUS),
++				flash_dma_readl(ctrl, FLASH_DMA_ERROR_STATUS));
++	}
++	ctrl->dma_pending = false;
++	flash_dma_writel(ctrl, FLASH_DMA_CTRL, 0); /* force stop */
++}
++
++static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf,
++			      u32 len, u8 dma_cmd)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	dma_addr_t buf_pa;
++	int dir = dma_cmd == CMD_PAGE_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
++
++	buf_pa = dma_map_single(ctrl->dev, buf, len, dir);
++	if (dma_mapping_error(ctrl->dev, buf_pa)) {
++		dev_err(ctrl->dev, "unable to map buffer for DMA\n");
++		return -ENOMEM;
++	}
++
++	brcmnand_fill_dma_desc(host, ctrl->dma_desc, addr, buf_pa, len,
++				   dma_cmd, true, true, 0);
++
++	brcmnand_dma_run(host, ctrl->dma_pa);
++
++	dma_unmap_single(ctrl->dev, buf_pa, len, dir);
++
++	if (ctrl->dma_desc->status_valid & FLASH_DMA_ECC_ERROR)
++		return -EBADMSG;
++	else if (ctrl->dma_desc->status_valid & FLASH_DMA_CORR_ERROR)
++		return -EUCLEAN;
++
++	return 0;
++}
++
++/*
++ * Assumes proper CS is already set
++ */
++static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip,
++				u64 addr, unsigned int trans, u32 *buf,
++				u8 *oob, u64 *err_addr)
++{
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	int i, j, ret = 0;
++
++	/* Clear error addresses */
++	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_ADDR, 0);
++	brcmnand_write_reg(ctrl, BRCMNAND_CORR_ADDR, 0);
++
++	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
++			(host->cs << 16) | ((addr >> 32) & 0xffff));
++	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
++
++	for (i = 0; i < trans; i++, addr += FC_BYTES) {
++		brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
++				   lower_32_bits(addr));
++		(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
++		/* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */
++		brcmnand_send_cmd(host, CMD_PAGE_READ);
++		brcmnand_waitfunc(mtd, chip);
++
++		if (likely(buf))
++			for (j = 0; j < FC_WORDS; j++, buf++)
++				*buf = brcmnand_read_fc(ctrl, j);
++
++		if (oob)
++			oob += read_oob_from_regs(ctrl, i, oob,
++					mtd->oobsize / trans,
++					host->hwcfg.sector_size_1k);
++
++		if (!ret) {
++			*err_addr = brcmnand_read_reg(ctrl,
++					BRCMNAND_UNCORR_ADDR) |
++				((u64)(brcmnand_read_reg(ctrl,
++						BRCMNAND_UNCORR_EXT_ADDR)
++					& 0xffff) << 32);
++			if (*err_addr)
++				ret = -EBADMSG;
++		}
++
++		if (!ret) {
++			*err_addr = brcmnand_read_reg(ctrl,
++					BRCMNAND_CORR_ADDR) |
++				((u64)(brcmnand_read_reg(ctrl,
++						BRCMNAND_CORR_EXT_ADDR)
++					& 0xffff) << 32);
++			if (*err_addr)
++				ret = -EUCLEAN;
++		}
++	}
++
++	return ret;
++}
++
++static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip,
++			 u64 addr, unsigned int trans, u32 *buf, u8 *oob)
++{
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	u64 err_addr = 0;
++	int err;
++
++	dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf);
++
++	brcmnand_write_reg(ctrl, BRCMNAND_UNCORR_COUNT, 0);
++
++	if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
++		err = brcmnand_dma_trans(host, addr, buf, trans * FC_BYTES,
++					     CMD_PAGE_READ);
++		if (err) {
++			if (mtd_is_bitflip_or_eccerr(err))
++				err_addr = addr;
++			else
++				return -EIO;
++		}
++	} else {
++		if (oob)
++			memset(oob, 0x99, mtd->oobsize);
++
++		err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf,
++					       oob, &err_addr);
++	}
++
++	if (mtd_is_eccerr(err)) {
++		dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n",
++			(unsigned long long)err_addr);
++		mtd->ecc_stats.failed++;
++		/* NAND layer expects zero on ECC errors */
++		return 0;
++	}
++
++	if (mtd_is_bitflip(err)) {
++		unsigned int corrected = brcmnand_count_corrected(ctrl);
++
++		dev_dbg(ctrl->dev, "corrected error at 0x%llx\n",
++			(unsigned long long)err_addr);
++		mtd->ecc_stats.corrected += corrected;
++		/* Always exceed the software-imposed threshold */
++		return max(mtd->bitflip_threshold, corrected);
++	}
++
++	return 0;
++}
++
++static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
++			      uint8_t *buf, int oob_required, int page)
++{
++	struct brcmnand_host *host = chip->priv;
++	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
++
++	return brcmnand_read(mtd, chip, host->last_addr,
++			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
++}
++
++static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				  uint8_t *buf, int oob_required, int page)
++{
++	struct brcmnand_host *host = chip->priv;
++	u8 *oob = oob_required ? (u8 *)chip->oob_poi : NULL;
++	int ret;
++
++	brcmnand_set_ecc_enabled(host, 0);
++	ret = brcmnand_read(mtd, chip, host->last_addr,
++			mtd->writesize >> FC_SHIFT, (u32 *)buf, oob);
++	brcmnand_set_ecc_enabled(host, 1);
++	return ret;
++}
++
++static int brcmnand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
++			     int page)
++{
++	return brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
++			mtd->writesize >> FC_SHIFT,
++			NULL, (u8 *)chip->oob_poi);
++}
++
++static int brcmnand_read_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				 int page)
++{
++	struct brcmnand_host *host = chip->priv;
++
++	brcmnand_set_ecc_enabled(host, 0);
++	brcmnand_read(mtd, chip, (u64)page << chip->page_shift,
++		mtd->writesize >> FC_SHIFT,
++		NULL, (u8 *)chip->oob_poi);
++	brcmnand_set_ecc_enabled(host, 1);
++	return 0;
++}
++
++static int brcmnand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
++				 uint32_t data_offs, uint32_t readlen,
++				 uint8_t *bufpoi, int page)
++{
++	struct brcmnand_host *host = chip->priv;
++
++	return brcmnand_read(mtd, chip, host->last_addr + data_offs,
++			readlen >> FC_SHIFT, (u32 *)bufpoi, NULL);
++}
++
++static int brcmnand_write(struct mtd_info *mtd, struct nand_chip *chip,
++			  u64 addr, const u32 *buf, u8 *oob)
++{
++	struct brcmnand_host *host = chip->priv;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	unsigned int i, j, trans = mtd->writesize >> FC_SHIFT;
++	int status, ret = 0;
++
++	dev_dbg(ctrl->dev, "write %llx <- %p\n", (unsigned long long)addr, buf);
++
++	if (unlikely((u32)buf & 0x03)) {
++		dev_warn(ctrl->dev, "unaligned buffer: %p\n", buf);
++		buf = (u32 *)((u32)buf & ~0x03);
++	}
++
++	brcmnand_wp(mtd, 0);
++
++	for (i = 0; i < ctrl->max_oob; i += 4)
++		oob_reg_write(ctrl, i, 0xffffffff);
++
++	if (has_flash_dma(ctrl) && !oob && flash_dma_buf_ok(buf)) {
++		if (brcmnand_dma_trans(host, addr, (u32 *)buf,
++					mtd->writesize, CMD_PROGRAM_PAGE))
++			ret = -EIO;
++		goto out;
++	}
++
++	brcmnand_write_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS,
++			(host->cs << 16) | ((addr >> 32) & 0xffff));
++	(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_EXT_ADDRESS);
++
++	for (i = 0; i < trans; i++, addr += FC_BYTES) {
++		/* full address MUST be set before populating FC */
++		brcmnand_write_reg(ctrl, BRCMNAND_CMD_ADDRESS,
++				   lower_32_bits(addr));
++		(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
++
++		if (buf)
++			for (j = 0; j < FC_WORDS; j++, buf++)
++				brcmnand_write_fc(ctrl, j, *buf);
++		else if (oob)
++			for (j = 0; j < FC_WORDS; j++)
++				brcmnand_write_fc(ctrl, j, 0xffffffff);
++
++		if (oob) {
++			oob += write_oob_to_regs(ctrl, i, oob,
++					mtd->oobsize / trans,
++					host->hwcfg.sector_size_1k);
++		}
++
++		/* we cannot use SPARE_AREA_PROGRAM when PARTIAL_PAGE_EN=0 */
++		brcmnand_send_cmd(host, CMD_PROGRAM_PAGE);
++		status = brcmnand_waitfunc(mtd, chip);
++
++		if (status & NAND_STATUS_FAIL) {
++			dev_info(ctrl->dev, "program failed at %llx\n",
++				(unsigned long long)addr);
++			ret = -EIO;
++			goto out;
++		}
++	}
++out:
++	brcmnand_wp(mtd, 1);
++	return ret;
++}
++
++static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
++			       const uint8_t *buf, int oob_required)
++{
++	struct brcmnand_host *host = chip->priv;
++	void *oob = oob_required ? chip->oob_poi : NULL;
++
++	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
++	return 0;
++}
++
++static int brcmnand_write_page_raw(struct mtd_info *mtd,
++				   struct nand_chip *chip, const uint8_t *buf,
++				   int oob_required)
++{
++	struct brcmnand_host *host = chip->priv;
++	void *oob = oob_required ? chip->oob_poi : NULL;
++
++	brcmnand_set_ecc_enabled(host, 0);
++	brcmnand_write(mtd, chip, host->last_addr, (const u32 *)buf, oob);
++	brcmnand_set_ecc_enabled(host, 1);
++	return 0;
++}
++
++static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
++				  int page)
++{
++	return brcmnand_write(mtd, chip, (u64)page << chip->page_shift,
++				  NULL, chip->oob_poi);
++}
++
++static int brcmnand_write_oob_raw(struct mtd_info *mtd, struct nand_chip *chip,
++				  int page)
++{
++	struct brcmnand_host *host = chip->priv;
++	int ret;
++
++	brcmnand_set_ecc_enabled(host, 0);
++	ret = brcmnand_write(mtd, chip, (u64)page << chip->page_shift, NULL,
++				 (u8 *)chip->oob_poi);
++	brcmnand_set_ecc_enabled(host, 1);
++
++	return ret;
++}
++
++/***********************************************************************
++ * Per-CS setup (1 NAND device)
++ ***********************************************************************/
++
++static int brcmnand_set_cfg(struct brcmnand_host *host,
++			    struct brcmnand_cfg *cfg)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	struct nand_chip *chip = &host->chip;
++	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
++	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
++			BRCMNAND_CS_CFG_EXT);
++	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
++			BRCMNAND_CS_ACC_CONTROL);
++	u8 block_size = 0, page_size = 0, device_size = 0;
++	u32 tmp;
++
++	if (ctrl->block_sizes) {
++		int i, found;
++
++		for (i = 0, found = 0; ctrl->block_sizes[i]; i++)
++			if (ctrl->block_sizes[i] * 1024 == cfg->block_size) {
++				block_size = i;
++				found = 1;
++			}
++		if (!found) {
++			dev_warn(ctrl->dev, "invalid block size %u\n",
++					cfg->block_size);
++			return -EINVAL;
++		}
++	} else {
++		block_size = ffs(cfg->block_size) - ffs(BRCMNAND_MIN_BLOCKSIZE);
++	}
++
++	if (cfg->block_size < BRCMNAND_MIN_BLOCKSIZE || (ctrl->max_block_size &&
++				cfg->block_size > ctrl->max_block_size)) {
++		dev_warn(ctrl->dev, "invalid block size %u\n",
++				cfg->block_size);
++		block_size = 0;
++	}
++
++	if (ctrl->page_sizes) {
++		int i, found;
++
++		for (i = 0, found = 0; ctrl->page_sizes[i]; i++)
++			if (ctrl->page_sizes[i] == cfg->page_size) {
++				page_size = i;
++				found = 1;
++			}
++		if (!found) {
++			dev_warn(ctrl->dev, "invalid page size %u\n",
++					cfg->page_size);
++			return -EINVAL;
++		}
++	} else {
++		page_size = ffs(cfg->page_size) - ffs(BRCMNAND_MIN_PAGESIZE);
++	}
++
++	if (cfg->page_size < BRCMNAND_MIN_PAGESIZE || (ctrl->max_page_size &&
++				cfg->page_size > ctrl->max_page_size)) {
++		dev_warn(ctrl->dev, "invalid page size %u\n", cfg->page_size);
++		return -EINVAL;
++	}
++
++	if (fls64(cfg->device_size) < fls64(BRCMNAND_MIN_DEVSIZE)) {
++		dev_warn(ctrl->dev, "invalid device size 0x%llx\n",
++			(unsigned long long)cfg->device_size);
++		return -EINVAL;
++	}
++	device_size = fls64(cfg->device_size) - fls64(BRCMNAND_MIN_DEVSIZE);
++
++	tmp = (cfg->blk_adr_bytes << 8) |
++		(cfg->col_adr_bytes << 12) |
++		(cfg->ful_adr_bytes << 16) |
++		(!!(cfg->device_width == 16) << 23) |
++		(device_size << 24);
++	if (cfg_offs == cfg_ext_offs) {
++		tmp |= (page_size << 20) | (block_size << 28);
++		nand_writereg(ctrl, cfg_offs, tmp);
++	} else {
++		nand_writereg(ctrl, cfg_offs, tmp);
++		tmp = page_size | (block_size << 4);
++		nand_writereg(ctrl, cfg_ext_offs, tmp);
++	}
++
++	tmp = nand_readreg(ctrl, acc_control_offs);
++	tmp &= ~brcmnand_ecc_level_mask(ctrl);
++	tmp |= cfg->ecc_level << NAND_ACC_CONTROL_ECC_SHIFT;
++	tmp &= ~brcmnand_spare_area_mask(ctrl);
++	tmp |= cfg->spare_area_size;
++	nand_writereg(ctrl, acc_control_offs, tmp);
++
++	brcmnand_set_sector_size_1k(host, cfg->sector_size_1k);
++
++	/* threshold = ceil(BCH-level * 0.75) */
++	brcmnand_wr_corr_thresh(host, DIV_ROUND_UP(chip->ecc.strength * 3, 4));
++
++	return 0;
++}
++
++static void brcmnand_print_cfg(char *buf, struct brcmnand_cfg *cfg)
++{
++	buf += sprintf(buf,
++		"%lluMiB total, %uKiB blocks, %u%s pages, %uB OOB, %u-bit",
++		(unsigned long long)cfg->device_size >> 20,
++		cfg->block_size >> 10,
++		cfg->page_size >= 1024 ? cfg->page_size >> 10 : cfg->page_size,
++		cfg->page_size >= 1024 ? "KiB" : "B",
++		cfg->spare_area_size, cfg->device_width);
++
++	/* Account for Hamming ECC and for BCH 512B vs 1KiB sectors */
++	if (is_hamming_ecc(cfg))
++		sprintf(buf, ", Hamming ECC");
++	else if (cfg->sector_size_1k)
++		sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
++	else
++		sprintf(buf, ", BCH-%u\n", cfg->ecc_level);
++}
++
++/*
++ * Minimum number of bytes to address a page. Calculated as:
++ *     roundup(log2(size / page-size) / 8)
++ *
++ * NB: the following does not "round up" for non-power-of-2 'size'; but this is
++ *     OK because many other things will break if 'size' is irregular...
++ */
++static inline int get_blk_adr_bytes(u64 size, u32 writesize)
++{
++	return ALIGN(ilog2(size) - ilog2(writesize), 8) >> 3;
++}
++
++static int brcmnand_setup_dev(struct brcmnand_host *host)
++{
++	struct mtd_info *mtd = &host->mtd;
++	struct nand_chip *chip = &host->chip;
++	struct brcmnand_controller *ctrl = host->ctrl;
++	struct brcmnand_cfg *cfg = &host->hwcfg;
++	char msg[128];
++	u32 offs, tmp, oob_sector;
++	int ret;
++
++	memset(cfg, 0, sizeof(*cfg));
++
++	ret = of_property_read_u32(chip->dn, "brcm,nand-oob-sector-size",
++				   &oob_sector);
++	if (ret) {
++		/* Use detected size */
++		cfg->spare_area_size = mtd->oobsize /
++					(mtd->writesize >> FC_SHIFT);
++	} else {
++		cfg->spare_area_size = oob_sector;
++	}
++	if (cfg->spare_area_size > ctrl->max_oob)
++		cfg->spare_area_size = ctrl->max_oob;
++	/*
++	 * Set oobsize to be consistent with controller's spare_area_size, as
++	 * the rest is inaccessible.
++	 */
++	mtd->oobsize = cfg->spare_area_size * (mtd->writesize >> FC_SHIFT);
++
++	cfg->device_size = mtd->size;
++	cfg->block_size = mtd->erasesize;
++	cfg->page_size = mtd->writesize;
++	cfg->device_width = (chip->options & NAND_BUSWIDTH_16) ? 16 : 8;
++	cfg->col_adr_bytes = 2;
++	cfg->blk_adr_bytes = get_blk_adr_bytes(mtd->size, mtd->writesize);
++
++	switch (chip->ecc.size) {
++	case 512:
++		if (chip->ecc.strength == 1) /* Hamming */
++			cfg->ecc_level = 15;
++		else
++			cfg->ecc_level = chip->ecc.strength;
++		cfg->sector_size_1k = 0;
++		break;
++	case 1024:
++		if (!(ctrl->features & BRCMNAND_HAS_1K_SECTORS)) {
++			dev_err(ctrl->dev, "1KB sectors not supported\n");
++			return -EINVAL;
++		}
++		if (chip->ecc.strength & 0x1) {
++			dev_err(ctrl->dev,
++				"odd ECC not supported with 1KB sectors\n");
++			return -EINVAL;
++		}
++
++		cfg->ecc_level = chip->ecc.strength >> 1;
++		cfg->sector_size_1k = 1;
++		break;
++	default:
++		dev_err(ctrl->dev, "unsupported ECC size: %d\n",
++			chip->ecc.size);
++		return -EINVAL;
++	}
++
++	cfg->ful_adr_bytes = cfg->blk_adr_bytes;
++	if (mtd->writesize > 512)
++		cfg->ful_adr_bytes += cfg->col_adr_bytes;
++	else
++		cfg->ful_adr_bytes += 1;
++
++	ret = brcmnand_set_cfg(host, cfg);
++	if (ret)
++		return ret;
++
++	brcmnand_set_ecc_enabled(host, 1);
++
++	brcmnand_print_cfg(msg, cfg);
++	dev_info(ctrl->dev, "detected %s\n", msg);
++
++	/* Configure ACC_CONTROL */
++	offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_ACC_CONTROL);
++	tmp = nand_readreg(ctrl, offs);
++	tmp &= ~ACC_CONTROL_PARTIAL_PAGE;
++	tmp &= ~ACC_CONTROL_RD_ERASED;
++	tmp &= ~ACC_CONTROL_FAST_PGM_RDIN;
++	if (ctrl->features & BRCMNAND_HAS_PREFETCH) {
++		/*
++		 * FIXME: Flash DMA + prefetch may see spurious erased-page ECC
++		 * errors
++		 */
++		if (has_flash_dma(ctrl))
++			tmp &= ~ACC_CONTROL_PREFETCH;
++		else
++			tmp |= ACC_CONTROL_PREFETCH;
++	}
++	nand_writereg(ctrl, offs, tmp);
++
++	return 0;
++}
++
++static int brcmnand_init_cs(struct brcmnand_host *host)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	struct device_node *dn = host->of_node;
++	struct platform_device *pdev = host->pdev;
++	struct mtd_info *mtd;
++	struct nand_chip *chip;
++	int ret = 0;
++	struct mtd_part_parser_data ppdata = { .of_node = dn };
++
++	ret = of_property_read_u32(dn, "reg", &host->cs);
++	if (ret) {
++		dev_err(&pdev->dev, "can't get chip-select\n");
++		return -ENXIO;
++	}
++
++	mtd = &host->mtd;
++	chip = &host->chip;
++
++	chip->dn = dn;
++	chip->priv = host;
++	mtd->priv = chip;
++	mtd->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "brcmnand.%d",
++				   host->cs);
++	mtd->owner = THIS_MODULE;
++	mtd->dev.parent = &pdev->dev;
++
++	chip->IO_ADDR_R = (void __iomem *)0xdeadbeef;
++	chip->IO_ADDR_W = (void __iomem *)0xdeadbeef;
++
++	chip->cmd_ctrl = brcmnand_cmd_ctrl;
++	chip->cmdfunc = brcmnand_cmdfunc;
++	chip->waitfunc = brcmnand_waitfunc;
++	chip->read_byte = brcmnand_read_byte;
++	chip->read_buf = brcmnand_read_buf;
++	chip->write_buf = brcmnand_write_buf;
++
++	chip->ecc.mode = NAND_ECC_HW;
++	chip->ecc.read_page = brcmnand_read_page;
++	chip->ecc.read_subpage = brcmnand_read_subpage;
++	chip->ecc.write_page = brcmnand_write_page;
++	chip->ecc.read_page_raw = brcmnand_read_page_raw;
++	chip->ecc.write_page_raw = brcmnand_write_page_raw;
++	chip->ecc.write_oob_raw = brcmnand_write_oob_raw;
++	chip->ecc.read_oob_raw = brcmnand_read_oob_raw;
++	chip->ecc.read_oob = brcmnand_read_oob;
++	chip->ecc.write_oob = brcmnand_write_oob;
++
++	chip->controller = &ctrl->controller;
++
++	if (nand_scan_ident(mtd, 1, NULL))
++		return -ENXIO;
++
++	chip->options |= NAND_NO_SUBPAGE_WRITE;
++	/*
++	 * Avoid (for instance) kmap()'d buffers from JFFS2, which we can't DMA
++	 * to/from, and have nand_base pass us a bounce buffer instead, as
++	 * needed.
++	 */
++	chip->options |= NAND_USE_BOUNCE_BUFFER;
++
++	if (of_get_nand_on_flash_bbt(dn))
++		chip->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB;
++
++	if (brcmnand_setup_dev(host))
++		return -ENXIO;
++
++	chip->ecc.size = host->hwcfg.sector_size_1k ? 1024 : 512;
++	/* only use our internal HW threshold */
++	mtd->bitflip_threshold = 1;
++
++	chip->ecc.layout = brcmstb_choose_ecc_layout(host);
++	if (!chip->ecc.layout)
++		return -ENXIO;
++
++	if (nand_scan_tail(mtd))
++		return -ENXIO;
++
++	return mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
++}
++
++static void brcmnand_save_restore_cs_config(struct brcmnand_host *host,
++					    int restore)
++{
++	struct brcmnand_controller *ctrl = host->ctrl;
++	u16 cfg_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_CFG);
++	u16 cfg_ext_offs = brcmnand_cs_offset(ctrl, host->cs,
++			BRCMNAND_CS_CFG_EXT);
++	u16 acc_control_offs = brcmnand_cs_offset(ctrl, host->cs,
++			BRCMNAND_CS_ACC_CONTROL);
++	u16 t1_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING1);
++	u16 t2_offs = brcmnand_cs_offset(ctrl, host->cs, BRCMNAND_CS_TIMING2);
++
++	if (restore) {
++		nand_writereg(ctrl, cfg_offs, host->hwcfg.config);
++		if (cfg_offs != cfg_ext_offs)
++			nand_writereg(ctrl, cfg_ext_offs,
++				      host->hwcfg.config_ext);
++		nand_writereg(ctrl, acc_control_offs, host->hwcfg.acc_control);
++		nand_writereg(ctrl, t1_offs, host->hwcfg.timing_1);
++		nand_writereg(ctrl, t2_offs, host->hwcfg.timing_2);
++	} else {
++		host->hwcfg.config = nand_readreg(ctrl, cfg_offs);
++		if (cfg_offs != cfg_ext_offs)
++			host->hwcfg.config_ext =
++				nand_readreg(ctrl, cfg_ext_offs);
++		host->hwcfg.acc_control = nand_readreg(ctrl, acc_control_offs);
++		host->hwcfg.timing_1 = nand_readreg(ctrl, t1_offs);
++		host->hwcfg.timing_2 = nand_readreg(ctrl, t2_offs);
++	}
++}
++
++static int brcmnand_suspend(struct device *dev)
++{
++	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
++	struct brcmnand_host *host;
++
++	list_for_each_entry(host, &ctrl->host_list, node)
++		brcmnand_save_restore_cs_config(host, 0);
++
++	ctrl->nand_cs_nand_select = brcmnand_read_reg(ctrl, BRCMNAND_CS_SELECT);
++	ctrl->nand_cs_nand_xor = brcmnand_read_reg(ctrl, BRCMNAND_CS_XOR);
++	ctrl->corr_stat_threshold =
++		brcmnand_read_reg(ctrl, BRCMNAND_CORR_THRESHOLD);
++
++	if (has_flash_dma(ctrl))
++		ctrl->flash_dma_mode = flash_dma_readl(ctrl, FLASH_DMA_MODE);
++
++	return 0;
++}
++
++static int brcmnand_resume(struct device *dev)
++{
++	struct brcmnand_controller *ctrl = dev_get_drvdata(dev);
++	struct brcmnand_host *host;
++
++	if (has_flash_dma(ctrl)) {
++		flash_dma_writel(ctrl, FLASH_DMA_MODE, ctrl->flash_dma_mode);
++		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
++	}
++
++	brcmnand_write_reg(ctrl, BRCMNAND_CS_SELECT, ctrl->nand_cs_nand_select);
++	brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
++	brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
++			ctrl->corr_stat_threshold);
++
++	list_for_each_entry(host, &ctrl->host_list, node) {
++		struct mtd_info *mtd = &host->mtd;
++		struct nand_chip *chip = mtd->priv;
++
++		brcmnand_save_restore_cs_config(host, 1);
++
++		/* Reset the chip, required by some chips after power-up */
++		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
++	}
++
++	return 0;
++}
++
++const struct dev_pm_ops brcmnand_pm_ops = {
++	.suspend		= brcmnand_suspend,
++	.resume			= brcmnand_resume,
++};
++EXPORT_SYMBOL_GPL(brcmnand_pm_ops);
++
++static const struct of_device_id brcmnand_of_match[] = {
++	{ .compatible = "brcm,brcmnand-v4.0" },
++	{ .compatible = "brcm,brcmnand-v5.0" },
++	{ .compatible = "brcm,brcmnand-v6.0" },
++	{ .compatible = "brcm,brcmnand-v6.1" },
++	{ .compatible = "brcm,brcmnand-v7.0" },
++	{ .compatible = "brcm,brcmnand-v7.1" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, brcmnand_of_match);
++
++/***********************************************************************
++ * Platform driver setup (per controller)
++ ***********************************************************************/
++
++int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc)
++{
++	struct device *dev = &pdev->dev;
++	struct device_node *dn = dev->of_node, *child;
++	static struct brcmnand_controller *ctrl;
++	struct resource *res;
++	int ret;
++
++	/* We only support device-tree instantiation */
++	if (!dn)
++		return -ENODEV;
++
++	if (!of_match_node(brcmnand_of_match, dn))
++		return -ENODEV;
++
++	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
++	if (!ctrl)
++		return -ENOMEM;
++
++	dev_set_drvdata(dev, ctrl);
++	ctrl->dev = dev;
++
++	init_completion(&ctrl->done);
++	init_completion(&ctrl->dma_done);
++	spin_lock_init(&ctrl->controller.lock);
++	init_waitqueue_head(&ctrl->controller.wq);
++	INIT_LIST_HEAD(&ctrl->host_list);
++
++	/* NAND register range */
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	ctrl->nand_base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(ctrl->nand_base))
++		return PTR_ERR(ctrl->nand_base);
++
++	/* Initialize NAND revision */
++	ret = brcmnand_revision_init(ctrl);
++	if (ret)
++		return ret;
++
++	/*
++	 * Most chips have this cache at a fixed offset within 'nand' block.
++	 * Some must specify this region separately.
++	 */
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-cache");
++	if (res) {
++		ctrl->nand_fc = devm_ioremap_resource(dev, res);
++		if (IS_ERR(ctrl->nand_fc))
++			return PTR_ERR(ctrl->nand_fc);
++	} else {
++		ctrl->nand_fc = ctrl->nand_base +
++				ctrl->reg_offsets[BRCMNAND_FC_BASE];
++	}
++
++	/* FLASH_DMA */
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "flash-dma");
++	if (res) {
++		ctrl->flash_dma_base = devm_ioremap_resource(dev, res);
++		if (IS_ERR(ctrl->flash_dma_base))
++			return PTR_ERR(ctrl->flash_dma_base);
++
++		flash_dma_writel(ctrl, FLASH_DMA_MODE, 1); /* linked-list */
++		flash_dma_writel(ctrl, FLASH_DMA_ERROR_STATUS, 0);
++
++		/* Allocate descriptor(s) */
++		ctrl->dma_desc = dmam_alloc_coherent(dev,
++						     sizeof(*ctrl->dma_desc),
++						     &ctrl->dma_pa, GFP_KERNEL);
++		if (!ctrl->dma_desc)
++			return -ENOMEM;
++
++		ctrl->dma_irq = platform_get_irq(pdev, 1);
++		if ((int)ctrl->dma_irq < 0) {
++			dev_err(dev, "missing FLASH_DMA IRQ\n");
++			return -ENODEV;
++		}
++
++		ret = devm_request_irq(dev, ctrl->dma_irq,
++				brcmnand_dma_irq, 0, DRV_NAME,
++				ctrl);
++		if (ret < 0) {
++			dev_err(dev, "can't allocate IRQ %d: error %d\n",
++					ctrl->dma_irq, ret);
++			return ret;
++		}
++
++		dev_info(dev, "enabling FLASH_DMA\n");
++	}
++
++	/* Disable automatic device ID config, direct addressing */
++	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_SELECT,
++			 CS_SELECT_AUTO_DEVICE_ID_CFG | 0xff, 0, 0);
++	/* Disable XOR addressing */
++	brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
++
++	if (ctrl->features & BRCMNAND_HAS_WP) {
++		/* Permanently disable write protection */
++		if (wp_on == 2)
++			brcmnand_set_wp(ctrl, false);
++	} else {
++		wp_on = 0;
++	}
++
++	/* IRQ */
++	ctrl->irq = platform_get_irq(pdev, 0);
++	if ((int)ctrl->irq < 0) {
++		dev_err(dev, "no IRQ defined\n");
++		return -ENODEV;
++	}
++
++	ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
++			DRV_NAME, ctrl);
++	if (ret < 0) {
++		dev_err(dev, "can't allocate IRQ %d: error %d\n",
++			ctrl->irq, ret);
++		return ret;
++	}
++
++	for_each_available_child_of_node(dn, child) {
++		if (of_device_is_compatible(child, "brcm,nandcs")) {
++			struct brcmnand_host *host;
++
++			host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
++			if (!host)
++				return -ENOMEM;
++			host->pdev = pdev;
++			host->ctrl = ctrl;
++			host->of_node = child;
++
++			ret = brcmnand_init_cs(host);
++			if (ret)
++				continue; /* Try all chip-selects */
++
++			list_add_tail(&host->node, &ctrl->host_list);
++		}
++	}
++
++	/* No chip-selects could initialize properly */
++	if (list_empty(&ctrl->host_list))
++		return -ENODEV;
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(brcmnand_probe);
++
++int brcmnand_remove(struct platform_device *pdev)
++{
++	struct brcmnand_controller *ctrl = dev_get_drvdata(&pdev->dev);
++	struct brcmnand_host *host;
++
++	list_for_each_entry(host, &ctrl->host_list, node)
++		nand_release(&host->mtd);
++
++	dev_set_drvdata(&pdev->dev, NULL);
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(brcmnand_remove);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Kevin Cernekee");
++MODULE_AUTHOR("Brian Norris");
++MODULE_DESCRIPTION("NAND driver for Broadcom chips");
++MODULE_ALIAS("platform:brcmnand");
+--- /dev/null
++++ b/drivers/mtd/nand/brcmnand/brcmnand.h
+@@ -0,0 +1,58 @@
++/*
++ * Copyright © 2015 Broadcom Corporation
++ *
++ * 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.
++ */
++
++#ifndef __BRCMNAND_H__
++#define __BRCMNAND_H__
++
++#include <linux/types.h>
++#include <linux/io.h>
++
++struct platform_device;
++struct dev_pm_ops;
++
++struct brcmnand_soc {
++	struct platform_device *pdev;
++	void *priv;
++};
++
++static inline u32 brcmnand_readl(void __iomem *addr)
++{
++	/*
++	 * MIPS endianness is configured by boot strap, which also reverses all
++	 * bus endianness (i.e., big-endian CPU + big endian bus ==> native
++	 * endian I/O).
++	 *
++	 * Other architectures (e.g., ARM) either do not support big endian, or
++	 * else leave I/O in little endian mode.
++	 */
++	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
++		return __raw_readl(addr);
++	else
++		return readl_relaxed(addr);
++}
++
++static inline void brcmnand_writel(u32 val, void __iomem *addr)
++{
++	/* See brcmnand_readl() comments */
++	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
++		__raw_writel(val, addr);
++	else
++		writel_relaxed(val, addr);
++}
++
++int brcmnand_probe(struct platform_device *pdev, struct brcmnand_soc *soc);
++int brcmnand_remove(struct platform_device *pdev);
++
++extern const struct dev_pm_ops brcmnand_pm_ops;
++
++#endif /* __BRCMNAND_H__ */
diff --git a/target/linux/bmips/patches-4.1/032-mtd-brcmnand-add-support-for-STB-chips.patch b/target/linux/bmips/patches-4.1/032-mtd-brcmnand-add-support-for-STB-chips.patch
new file mode 100644
index 0000000..f0a3c19
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/032-mtd-brcmnand-add-support-for-STB-chips.patch
@@ -0,0 +1,68 @@
+From 303b4420ff1896b444017b5b0eb8252ce197797d Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Tue, 12 May 2015 17:00:57 -0700
+Subject: [PATCH] mtd: brcmnand: add support for STB chips
+
+BCM7xxx chips are supported entirely by the library code, since they use
+generic irqchip interfaces and don't need any extra SoC-specific
+configuration.
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/Makefile       |  1 +
+ drivers/mtd/nand/brcmnand/brcmstb_nand.c | 44 ++++++++++++++++++++++++++++++++
+ 2 files changed, 45 insertions(+)
+ create mode 100644 drivers/mtd/nand/brcmnand/brcmstb_nand.c
+
+--- a/drivers/mtd/nand/brcmnand/Makefile
++++ b/drivers/mtd/nand/brcmnand/Makefile
+@@ -1 +1,2 @@
++obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmstb_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand.o
+--- /dev/null
++++ b/drivers/mtd/nand/brcmnand/brcmstb_nand.c
+@@ -0,0 +1,44 @@
++/*
++ * Copyright © 2015 Broadcom Corporation
++ *
++ * 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/device.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include "brcmnand.h"
++
++static const struct of_device_id brcmstb_nand_of_match[] = {
++	{ .compatible = "brcm,brcmnand" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, brcmstb_nand_of_match);
++
++static int brcmstb_nand_probe(struct platform_device *pdev)
++{
++	return brcmnand_probe(pdev, NULL);
++}
++
++static struct platform_driver brcmstb_nand_driver = {
++	.probe			= brcmstb_nand_probe,
++	.remove			= brcmnand_remove,
++	.driver = {
++		.name		= "brcmstb_nand",
++		.pm		= &brcmnand_pm_ops,
++		.of_match_table = brcmstb_nand_of_match,
++	}
++};
++module_platform_driver(brcmstb_nand_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Brian Norris");
++MODULE_DESCRIPTION("NAND driver for Broadcom STB chips");
diff --git a/target/linux/bmips/patches-4.1/033-mtd-brcmnand-add-extra-SoC-support-to-library.patch b/target/linux/bmips/patches-4.1/033-mtd-brcmnand-add-extra-SoC-support-to-library.patch
new file mode 100644
index 0000000..afc8413
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/033-mtd-brcmnand-add-extra-SoC-support-to-library.patch
@@ -0,0 +1,167 @@
+From c26211d37f11d5913d9803fdede6d053f918ba7b Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Tue, 12 May 2015 12:09:28 -0700
+Subject: [PATCH] mtd: brcmnand: add extra SoC support to library
+
+There are a few small hooks required for chips like BCM63138 and the
+iProc family. Let's introduce those now.
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Tested-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/brcmnand.c | 61 +++++++++++++++++++++++++++++++++---
+ drivers/mtd/nand/brcmnand/brcmnand.h | 15 +++++++++
+ 2 files changed, 71 insertions(+), 5 deletions(-)
+
+--- a/drivers/mtd/nand/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/brcmnand/brcmnand.c
+@@ -119,6 +119,9 @@ struct brcmnand_controller {
+ 	unsigned int		dma_irq;
+ 	int			nand_version;
+ 
++	/* Some SoCs provide custom interrupt status register(s) */
++	struct brcmnand_soc	*soc;
++
+ 	int			cmd_pending;
+ 	bool			dma_pending;
+ 	struct completion	done;
+@@ -965,6 +968,17 @@ static irqreturn_t brcmnand_ctlrdy_irq(i
+ 	return IRQ_HANDLED;
+ }
+ 
++/* Handle SoC-specific interrupt hardware */
++static irqreturn_t brcmnand_irq(int irq, void *data)
++{
++	struct brcmnand_controller *ctrl = data;
++
++	if (ctrl->soc->ctlrdy_ack(ctrl->soc))
++		return brcmnand_ctlrdy_irq(irq, data);
++
++	return IRQ_NONE;
++}
++
+ static irqreturn_t brcmnand_dma_irq(int irq, void *data)
+ {
+ 	struct brcmnand_controller *ctrl = data;
+@@ -1153,12 +1167,18 @@ static void brcmnand_cmdfunc(struct mtd_
+ 	if (native_cmd == CMD_PARAMETER_READ ||
+ 			native_cmd == CMD_PARAMETER_CHANGE_COL) {
+ 		int i;
++
++		brcmnand_soc_data_bus_prepare(ctrl->soc);
++
+ 		/*
+ 		 * Must cache the FLASH_CACHE now, since changes in
+ 		 * SECTOR_SIZE_1K may invalidate it
+ 		 */
+ 		for (i = 0; i < FC_WORDS; i++)
+ 			ctrl->flash_cache[i] = brcmnand_read_fc(ctrl, i);
++
++		brcmnand_soc_data_bus_unprepare(ctrl->soc);
++
+ 		/* Cleanup from HW quirk: restore SECTOR_SIZE_1K */
+ 		if (host->hwcfg.sector_size_1k)
+ 			brcmnand_set_sector_size_1k(host,
+@@ -1371,10 +1391,15 @@ static int brcmnand_read_by_pio(struct m
+ 		brcmnand_send_cmd(host, CMD_PAGE_READ);
+ 		brcmnand_waitfunc(mtd, chip);
+ 
+-		if (likely(buf))
++		if (likely(buf)) {
++			brcmnand_soc_data_bus_prepare(ctrl->soc);
++
+ 			for (j = 0; j < FC_WORDS; j++, buf++)
+ 				*buf = brcmnand_read_fc(ctrl, j);
+ 
++			brcmnand_soc_data_bus_unprepare(ctrl->soc);
++		}
++
+ 		if (oob)
+ 			oob += read_oob_from_regs(ctrl, i, oob,
+ 					mtd->oobsize / trans,
+@@ -1546,12 +1571,17 @@ static int brcmnand_write(struct mtd_inf
+ 				   lower_32_bits(addr));
+ 		(void)brcmnand_read_reg(ctrl, BRCMNAND_CMD_ADDRESS);
+ 
+-		if (buf)
++		if (buf) {
++			brcmnand_soc_data_bus_prepare(ctrl->soc);
++
+ 			for (j = 0; j < FC_WORDS; j++, buf++)
+ 				brcmnand_write_fc(ctrl, j, *buf);
+-		else if (oob)
++
++			brcmnand_soc_data_bus_unprepare(ctrl->soc);
++		} else if (oob) {
+ 			for (j = 0; j < FC_WORDS; j++)
+ 				brcmnand_write_fc(ctrl, j, 0xffffffff);
++		}
+ 
+ 		if (oob) {
+ 			oob += write_oob_to_regs(ctrl, i, oob,
+@@ -1995,6 +2025,11 @@ static int brcmnand_resume(struct device
+ 	brcmnand_write_reg(ctrl, BRCMNAND_CS_XOR, ctrl->nand_cs_nand_xor);
+ 	brcmnand_write_reg(ctrl, BRCMNAND_CORR_THRESHOLD,
+ 			ctrl->corr_stat_threshold);
++	if (ctrl->soc) {
++		/* Clear/re-enable interrupt */
++		ctrl->soc->ctlrdy_ack(ctrl->soc);
++		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
++	}
+ 
+ 	list_for_each_entry(host, &ctrl->host_list, node) {
+ 		struct mtd_info *mtd = &host->mtd;
+@@ -2139,8 +2174,24 @@ int brcmnand_probe(struct platform_devic
+ 		return -ENODEV;
+ 	}
+ 
+-	ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+-			DRV_NAME, ctrl);
++	/*
++	 * Some SoCs integrate this controller (e.g., its interrupt bits) in
++	 * interesting ways
++	 */
++	if (soc) {
++		ctrl->soc = soc;
++
++		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
++				       DRV_NAME, ctrl);
++
++		/* Enable interrupt */
++		ctrl->soc->ctlrdy_ack(ctrl->soc);
++		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
++	} else {
++		/* Use standard interrupt infrastructure */
++		ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
++				       DRV_NAME, ctrl);
++	}
+ 	if (ret < 0) {
+ 		dev_err(dev, "can't allocate IRQ %d: error %d\n",
+ 			ctrl->irq, ret);
+--- a/drivers/mtd/nand/brcmnand/brcmnand.h
++++ b/drivers/mtd/nand/brcmnand/brcmnand.h
+@@ -23,8 +23,23 @@ struct dev_pm_ops;
+ struct brcmnand_soc {
+ 	struct platform_device *pdev;
+ 	void *priv;
++	bool (*ctlrdy_ack)(struct brcmnand_soc *soc);
++	void (*ctlrdy_set_enabled)(struct brcmnand_soc *soc, bool en);
++	void (*prepare_data_bus)(struct brcmnand_soc *soc, bool prepare);
+ };
+ 
++static inline void brcmnand_soc_data_bus_prepare(struct brcmnand_soc *soc)
++{
++	if (soc && soc->prepare_data_bus)
++		soc->prepare_data_bus(soc, true);
++}
++
++static inline void brcmnand_soc_data_bus_unprepare(struct brcmnand_soc *soc)
++{
++	if (soc && soc->prepare_data_bus)
++		soc->prepare_data_bus(soc, false);
++}
++
+ static inline u32 brcmnand_readl(void __iomem *addr)
+ {
+ 	/*
diff --git a/target/linux/bmips/patches-4.1/034-mtd-brcmnand-add-support-for-Broadcom-s-IPROC-family.patch b/target/linux/bmips/patches-4.1/034-mtd-brcmnand-add-support-for-Broadcom-s-IPROC-family.patch
new file mode 100644
index 0000000..550e320
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/034-mtd-brcmnand-add-support-for-Broadcom-s-IPROC-family.patch
@@ -0,0 +1,173 @@
+From ca22f040dd145fc4d8069ce174f6eb0bc3ebd19f Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Tue, 12 May 2015 12:12:02 -0700
+Subject: [PATCH] mtd: brcmnand: add support for Broadcom's IPROC family
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/Makefile     |   3 +
+ drivers/mtd/nand/brcmnand/iproc_nand.c | 150 +++++++++++++++++++++++++++++++++
+ 2 files changed, 153 insertions(+)
+ create mode 100644 drivers/mtd/nand/brcmnand/iproc_nand.c
+
+--- a/drivers/mtd/nand/brcmnand/Makefile
++++ b/drivers/mtd/nand/brcmnand/Makefile
+@@ -1,2 +1,5 @@
++# link order matters; don't link the more generic brcmstb_nand.o before the
++# more specific iproc_nand.o, for instance
++obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= iproc_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmstb_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand.o
+--- /dev/null
++++ b/drivers/mtd/nand/brcmnand/iproc_nand.c
+@@ -0,0 +1,150 @@
++/*
++ * Copyright © 2015 Broadcom Corporation
++ *
++ * 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/device.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#include "brcmnand.h"
++
++struct iproc_nand_soc_priv {
++	void __iomem *idm_base;
++	void __iomem *ext_base;
++	spinlock_t idm_lock;
++};
++
++#define IPROC_NAND_CTLR_READY_OFFSET       0x10
++#define IPROC_NAND_CTLR_READY              BIT(0)
++
++#define IPROC_NAND_IO_CTRL_OFFSET          0x00
++#define IPROC_NAND_APB_LE_MODE             BIT(24)
++#define IPROC_NAND_INT_CTRL_READ_ENABLE    BIT(6)
++
++static bool iproc_nand_intc_ack(struct brcmnand_soc *soc)
++{
++	struct iproc_nand_soc_priv *priv = soc->priv;
++	void __iomem *mmio = priv->ext_base + IPROC_NAND_CTLR_READY_OFFSET;
++	u32 val = brcmnand_readl(mmio);
++
++	if (val & IPROC_NAND_CTLR_READY) {
++		brcmnand_writel(IPROC_NAND_CTLR_READY, mmio);
++		return true;
++	}
++
++	return false;
++}
++
++static void iproc_nand_intc_set(struct brcmnand_soc *soc, bool en)
++{
++	struct iproc_nand_soc_priv *priv = soc->priv;
++	void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
++	u32 val;
++	unsigned long flags;
++
++	spin_lock_irqsave(&priv->idm_lock, flags);
++
++	val = brcmnand_readl(mmio);
++
++	if (en)
++		val |= IPROC_NAND_INT_CTRL_READ_ENABLE;
++	else
++		val &= ~IPROC_NAND_INT_CTRL_READ_ENABLE;
++
++	brcmnand_writel(val, mmio);
++
++	spin_unlock_irqrestore(&priv->idm_lock, flags);
++}
++
++static void iproc_nand_apb_access(struct brcmnand_soc *soc, bool prepare)
++{
++	struct iproc_nand_soc_priv *priv = soc->priv;
++	void __iomem *mmio = priv->idm_base + IPROC_NAND_IO_CTRL_OFFSET;
++	u32 val;
++	unsigned long flags;
++
++	spin_lock_irqsave(&priv->idm_lock, flags);
++
++	val = brcmnand_readl(mmio);
++
++	if (prepare)
++		val |= IPROC_NAND_APB_LE_MODE;
++	else
++		val &= ~IPROC_NAND_APB_LE_MODE;
++
++	brcmnand_writel(val, mmio);
++
++	spin_unlock_irqrestore(&priv->idm_lock, flags);
++}
++
++static int iproc_nand_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct iproc_nand_soc_priv *priv;
++	struct brcmnand_soc *soc;
++	struct resource *res;
++
++	soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
++	if (!soc)
++		return -ENOMEM;
++
++	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	spin_lock_init(&priv->idm_lock);
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-idm");
++	priv->idm_base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(priv->idm_base))
++		return PTR_ERR(priv->idm_base);
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "iproc-ext");
++	priv->ext_base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(priv->ext_base))
++		return PTR_ERR(priv->ext_base);
++
++	soc->pdev = pdev;
++	soc->priv = priv;
++	soc->ctlrdy_ack = iproc_nand_intc_ack;
++	soc->ctlrdy_set_enabled = iproc_nand_intc_set;
++	soc->prepare_data_bus = iproc_nand_apb_access;
++
++	return brcmnand_probe(pdev, soc);
++}
++
++static const struct of_device_id iproc_nand_of_match[] = {
++	{ .compatible = "brcm,nand-iproc" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, iproc_nand_of_match);
++
++static struct platform_driver iproc_nand_driver = {
++	.probe			= iproc_nand_probe,
++	.remove			= brcmnand_remove,
++	.driver = {
++		.name		= "iproc_nand",
++		.pm		= &brcmnand_pm_ops,
++		.of_match_table	= iproc_nand_of_match,
++	}
++};
++module_platform_driver(iproc_nand_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Brian Norris");
++MODULE_AUTHOR("Ray Jui");
++MODULE_DESCRIPTION("NAND driver for Broadcom IPROC-based SoCs");
diff --git a/target/linux/bmips/patches-4.1/035-mtd-brcmnand-add-BCM63138-support.patch b/target/linux/bmips/patches-4.1/035-mtd-brcmnand-add-BCM63138-support.patch
new file mode 100644
index 0000000..b6c25a2
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/035-mtd-brcmnand-add-BCM63138-support.patch
@@ -0,0 +1,137 @@
+From f628ece6636c2f0354a52566cafdea6d2f963b3d Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Tue, 12 May 2015 12:13:14 -0700
+Subject: [PATCH] mtd: brcmnand: add BCM63138 support
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Tested-by: Florian Fainelli <f.fainelli at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/Makefile        |   1 +
+ drivers/mtd/nand/brcmnand/bcm63138_nand.c | 111 ++++++++++++++++++++++++++++++
+ 2 files changed, 112 insertions(+)
+ create mode 100644 drivers/mtd/nand/brcmnand/bcm63138_nand.c
+
+--- a/drivers/mtd/nand/brcmnand/Makefile
++++ b/drivers/mtd/nand/brcmnand/Makefile
+@@ -1,5 +1,6 @@
+ # link order matters; don't link the more generic brcmstb_nand.o before the
+ # more specific iproc_nand.o, for instance
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= iproc_nand.o
++obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= bcm63138_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmstb_nand.o
+ obj-$(CONFIG_MTD_NAND_BRCMNAND)		+= brcmnand.o
+--- /dev/null
++++ b/drivers/mtd/nand/brcmnand/bcm63138_nand.c
+@@ -0,0 +1,111 @@
++/*
++ * Copyright © 2015 Broadcom Corporation
++ *
++ * 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/device.h>
++#include <linux/io.h>
++#include <linux/ioport.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/slab.h>
++
++#include "brcmnand.h"
++
++struct bcm63138_nand_soc_priv {
++	void __iomem *base;
++};
++
++#define BCM63138_NAND_INT_STATUS		0x00
++#define BCM63138_NAND_INT_EN			0x04
++
++enum {
++	BCM63138_CTLRDY		= BIT(4),
++};
++
++static bool bcm63138_nand_intc_ack(struct brcmnand_soc *soc)
++{
++	struct bcm63138_nand_soc_priv *priv = soc->priv;
++	void __iomem *mmio = priv->base + BCM63138_NAND_INT_STATUS;
++	u32 val = brcmnand_readl(mmio);
++
++	if (val & BCM63138_CTLRDY) {
++		brcmnand_writel(val & ~BCM63138_CTLRDY, mmio);
++		return true;
++	}
++
++	return false;
++}
++
++static void bcm63138_nand_intc_set(struct brcmnand_soc *soc, bool en)
++{
++	struct bcm63138_nand_soc_priv *priv = soc->priv;
++	void __iomem *mmio = priv->base + BCM63138_NAND_INT_EN;
++	u32 val = brcmnand_readl(mmio);
++
++	if (en)
++		val |= BCM63138_CTLRDY;
++	else
++		val &= ~BCM63138_CTLRDY;
++
++	brcmnand_writel(val, mmio);
++}
++
++static int bcm63138_nand_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct bcm63138_nand_soc_priv *priv;
++	struct brcmnand_soc *soc;
++	struct resource *res;
++
++	soc = devm_kzalloc(dev, sizeof(*soc), GFP_KERNEL);
++	if (!soc)
++		return -ENOMEM;
++
++	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++	if (!priv)
++		return -ENOMEM;
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand-int-base");
++	priv->base = devm_ioremap_resource(dev, res);
++	if (IS_ERR(priv->base))
++		return PTR_ERR(priv->base);
++
++	soc->pdev = pdev;
++	soc->priv = priv;
++	soc->ctlrdy_ack = bcm63138_nand_intc_ack;
++	soc->ctlrdy_set_enabled = bcm63138_nand_intc_set;
++
++	return brcmnand_probe(pdev, soc);
++}
++
++static const struct of_device_id bcm63138_nand_of_match[] = {
++	{ .compatible = "brcm,nand-bcm63138" },
++	{},
++};
++MODULE_DEVICE_TABLE(of, bcm63138_nand_of_match);
++
++static struct platform_driver bcm63138_nand_driver = {
++	.probe			= bcm63138_nand_probe,
++	.remove			= brcmnand_remove,
++	.driver = {
++		.name		= "bcm63138_nand",
++		.pm		= &brcmnand_pm_ops,
++		.of_match_table	= bcm63138_nand_of_match,
++	}
++};
++module_platform_driver(bcm63138_nand_driver);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Brian Norris");
++MODULE_DESCRIPTION("NAND driver for BCM63138");
diff --git a/target/linux/bmips/patches-4.1/036-mtd-brcmnand-remove-double-new-line-from-print.patch b/target/linux/bmips/patches-4.1/036-mtd-brcmnand-remove-double-new-line-from-print.patch
new file mode 100644
index 0000000..ccf9e27
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/036-mtd-brcmnand-remove-double-new-line-from-print.patch
@@ -0,0 +1,25 @@
+From 802041247a0abbeaf1dddb8a8d56f491762ae357 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke at hauke-m.de>
+Date: Sun, 17 May 2015 17:41:00 +0200
+Subject: [PATCH] mtd: brcmnand: remove double new line from print
+
+The caller already adds a new line and in the other cases there is no
+new line added.
+
+Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/brcmnand.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/brcmnand/brcmnand.c
+@@ -1765,7 +1765,7 @@ static void brcmnand_print_cfg(char *buf
+ 	else if (cfg->sector_size_1k)
+ 		sprintf(buf, ", BCH-%u (1KiB sector)", cfg->ecc_level << 1);
+ 	else
+-		sprintf(buf, ", BCH-%u\n", cfg->ecc_level);
++		sprintf(buf, ", BCH-%u", cfg->ecc_level);
+ }
+ 
+ /*
diff --git a/target/linux/bmips/patches-4.1/037-mtd-brcmnand-do-not-make-local-variable-static.patch b/target/linux/bmips/patches-4.1/037-mtd-brcmnand-do-not-make-local-variable-static.patch
new file mode 100644
index 0000000..7d0f1d6
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/037-mtd-brcmnand-do-not-make-local-variable-static.patch
@@ -0,0 +1,26 @@
+From bcb83a19d3ac95fe3c0e79e942fb628120738853 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke at hauke-m.de>
+Date: Sun, 17 May 2015 17:41:01 +0200
+Subject: [PATCH] mtd: brcmnand: do not make local variable static
+
+Remove static in front of ctrl. This variable should not be shared
+between different instances of brcmnand_probe(), it should be local to
+this function and stored on the stack.
+
+Signed-off-by: Hauke Mehrtens <hauke at hauke-m.de>
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/brcmnand.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/brcmnand/brcmnand.c
+@@ -2069,7 +2069,7 @@ int brcmnand_probe(struct platform_devic
+ {
+ 	struct device *dev = &pdev->dev;
+ 	struct device_node *dn = dev->of_node, *child;
+-	static struct brcmnand_controller *ctrl;
++	struct brcmnand_controller *ctrl;
+ 	struct resource *res;
+ 	int ret;
+ 
diff --git a/target/linux/bmips/patches-4.1/038-mtd-brcmnand-drop-unnecessary-initialization.patch b/target/linux/bmips/patches-4.1/038-mtd-brcmnand-drop-unnecessary-initialization.patch
new file mode 100644
index 0000000..fa22b03
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/038-mtd-brcmnand-drop-unnecessary-initialization.patch
@@ -0,0 +1,21 @@
+From 5e65d48b6049e090fdc57490db9d797124f2a9eb Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace at gmail.com>
+Date: Thu, 28 May 2015 15:07:45 -0700
+Subject: [PATCH] mtd: brcmnand: drop unnecessary initialization
+
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/brcmnand.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/nand/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/brcmnand/brcmnand.c
+@@ -1887,7 +1887,7 @@ static int brcmnand_init_cs(struct brcmn
+ 	struct platform_device *pdev = host->pdev;
+ 	struct mtd_info *mtd;
+ 	struct nand_chip *chip;
+-	int ret = 0;
++	int ret;
+ 	struct mtd_part_parser_data ppdata = { .of_node = dn };
+ 
+ 	ret = of_property_read_u32(dn, "reg", &host->cs);
diff --git a/target/linux/bmips/patches-4.1/039-mtd-brcmnand-Fix-misuse-of-IS_ENABLED.patch b/target/linux/bmips/patches-4.1/039-mtd-brcmnand-Fix-misuse-of-IS_ENABLED.patch
new file mode 100644
index 0000000..51e9065
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/039-mtd-brcmnand-Fix-misuse-of-IS_ENABLED.patch
@@ -0,0 +1,38 @@
+From ddb2c42b677eb2883e532f0928e445fc205d0019 Mon Sep 17 00:00:00 2001
+From: Axel Lin <axel.lin at ingics.com>
+Date: Thu, 6 Aug 2015 12:29:37 +0800
+Subject: [PATCH] mtd: brcmnand: Fix misuse of IS_ENABLED
+
+While IS_ENABLED() is perfectly fine for CONFIG_* symbols, it is not
+for other symbols such as __BIG_ENDIAN that is provided directly by
+the compiler.
+
+Switch to use CONFIG_CPU_BIG_ENDIAN instead of __BIG_ENDIAN.
+
+Fixes: 27c5b17cd1b1 ("mtd: nand: add NAND driver "library" for Broadcom STB NAND controller")
+Signed-off-by: Axel Lin <axel.lin at ingics.com>
+Signed-off-by: Brian Norris <computersforpeace at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/brcmnand.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/nand/brcmnand/brcmnand.h
++++ b/drivers/mtd/nand/brcmnand/brcmnand.h
+@@ -50,7 +50,7 @@ static inline u32 brcmnand_readl(void __
+ 	 * Other architectures (e.g., ARM) either do not support big endian, or
+ 	 * else leave I/O in little endian mode.
+ 	 */
+-	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
++	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ 		return __raw_readl(addr);
+ 	else
+ 		return readl_relaxed(addr);
+@@ -59,7 +59,7 @@ static inline u32 brcmnand_readl(void __
+ static inline void brcmnand_writel(u32 val, void __iomem *addr)
+ {
+ 	/* See brcmnand_readl() comments */
+-	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
++	if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
+ 		__raw_writel(val, addr);
+ 	else
+ 		writel_relaxed(val, addr);
diff --git a/target/linux/bmips/patches-4.1/100-BMIPS-select-gpiolib.patch b/target/linux/bmips/patches-4.1/100-BMIPS-select-gpiolib.patch
new file mode 100644
index 0000000..d6b506a
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/100-BMIPS-select-gpiolib.patch
@@ -0,0 +1,10 @@
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -149,6 +149,7 @@ config BMIPS_GENERIC
+ 	select IRQ_CPU
+ 	select RAW_IRQ_ACCESSORS
+ 	select DMA_NONCOHERENT
++	select GPIOLIB
+ 	select SYS_SUPPORTS_32BIT_KERNEL
+ 	select SYS_SUPPORTS_LITTLE_ENDIAN
+ 	select SYS_SUPPORTS_BIG_ENDIAN
diff --git a/target/linux/bmips/patches-4.1/120-irqchip-add-support-for-bcm6345-style-periphery-irq-.patch b/target/linux/bmips/patches-4.1/120-irqchip-add-support-for-bcm6345-style-periphery-irq-.patch
new file mode 100644
index 0000000..4a5b629
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/120-irqchip-add-support-for-bcm6345-style-periphery-irq-.patch
@@ -0,0 +1,455 @@
+From 301744ecbeece89ab3a9d6beef7802fa22598f00 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo at openwrt.org>
+Date: Sun, 30 Nov 2014 14:53:12 +0100
+Subject: [PATCH 1/5] irqchip: add support for bcm6345-style periphery irq
+ controller
+
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+---
+ .../brcm,bcm6345-periph-intc.txt                   |   50 +++
+ drivers/irqchip/Kconfig                            |    4 +
+ drivers/irqchip/Makefile                           |    1 +
+ drivers/irqchip/irq-bcm6345-periph.c               |  339 ++++++++++++++++++++
+ include/linux/irqchip/irq-bcm6345-periph.h         |   16 +
+ 5 files changed, 410 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-periph-intc.txt
+ create mode 100644 drivers/irqchip/irq-bcm6345-periph.c
+ create mode 100644 include/linux/irqchip/irq-bcm6345-periph.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-periph-intc.txt
+@@ -0,0 +1,50 @@
++Broadcom BCM6345 Level 1 periphery interrupt controller
++
++This block is a  interrupt controller that is typically connected directly
++to one of the HW INT lines on each CPU.  Every BCM63XX xDSL chip since
++BCM6345 has contained this hardware.
++
++Key elements of the hardware design include:
++
++- 32, 64, or 128 incoming level IRQ lines
++
++- All onchip peripherals are wired directly to an L2 input
++
++- A separate instance of the register set for each CPU, allowing individual
++  peripheral IRQs to be routed to any CPU
++
++- No atomic mask/unmask operations
++
++- No polarity/level/edge settings
++
++- No FIFO or priority encoder logic; software is expected to read all
++  1-4 status words to determine which IRQs are pending
++
++Required properties:
++
++- compatible: Should be "brcm,bcm6345-periph-intc".
++- reg: Specifies the base physical address and size of the registers.
++  Multiple register addresses may be specified, and must match the amount of
++  parent interrupts.
++- interrupt-controller: Identifies the node as an interrupt controller.
++- #interrupt-cells: Specifies the number of cells needed to encode an interrupt
++  source, should be 1.
++- interrupt-parent: Specifies the phandle to the parent interrupt controller
++  this one is cascaded from.
++- interrupts: Specifies the interrupt line(s) in the interrupt-parent controller
++  node, valid values depend on the type of parent interrupt controller.
++  Multiple lines are used to route interrupts to different cpus, with the first
++  assumed to be for the boot CPU.
++
++Example:
++
++periph_intc: interrupt-controller at f0406800 {
++	compatible = "brcm,bcm6345-periph-intc";
++	reg = <0x10000020 0x10>, <0x10000030 0x10>;
++
++	interrupt-controller;
++	#interrupt-cells = <1>;
++
++	interrupt-parent = <&cpu_intc>;
++	interrupts = <2>, <3>;
++};
+--- a/drivers/irqchip/Kconfig
++++ b/drivers/irqchip/Kconfig
+@@ -75,6 +75,10 @@ config BRCMSTB_L2_IRQ
+ 	select GENERIC_IRQ_CHIP
+ 	select IRQ_DOMAIN
+ 
++config BCM6345_PERIPH_IRQ
++	bool
++	select IRQ_DOMAIN
++
+ config DW_APB_ICTL
+ 	bool
+ 	select GENERIC_IRQ_CHIP
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_ARCH_MVEBU)		+= irq-armada-
+ obj-$(CONFIG_ARCH_MXS)			+= irq-mxs.o
+ obj-$(CONFIG_ARCH_TEGRA)		+= irq-tegra.o
+ obj-$(CONFIG_ARCH_S3C24XX)		+= irq-s3c24xx.o
++obj-$(CONFIG_BCM6345_PERIPH_IRQ)	+= irq-bcm6345-periph.o
+ obj-$(CONFIG_DW_APB_ICTL)		+= irq-dw-apb-ictl.o
+ obj-$(CONFIG_METAG)			+= irq-metag-ext.o
+ obj-$(CONFIG_METAG_PERFCOUNTER_IRQS)	+= irq-metag.o
+--- /dev/null
++++ b/drivers/irqchip/irq-bcm6345-periph.c
+@@ -0,0 +1,339 @@
++/*
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 2014 Jonas Gorski <jogo at openwrt.org>
++ */
++
++#include <linux/ioport.h>
++#include <linux/irq.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqchip/irq-bcm6345-periph.h>
++#include <linux/kernel.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/of_address.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++
++#ifdef CONFIG_BCM63XX
++#include <asm/mach-bcm63xx/bcm63xx_irq.h>
++
++#define VIRQ_BASE	IRQ_INTERNAL_BASE
++#else
++#define VIRQ_BASE	0
++#endif
++
++#include "irqchip.h"
++
++#define MAX_WORDS	4
++#define MAX_PARENT_IRQS	2
++#define IRQS_PER_WORD	32
++
++struct intc_block {
++	int parent_irq;
++	void __iomem *base;
++	void __iomem *en_reg[MAX_WORDS];
++	void __iomem *status_reg[MAX_WORDS];
++	u32 mask_cache[MAX_WORDS];
++};
++
++struct intc_data {
++	struct irq_chip chip;
++	struct intc_block block[MAX_PARENT_IRQS];
++
++	int num_words;
++
++	struct irq_domain *domain;
++	raw_spinlock_t lock;
++};
++
++static void bcm6345_periph_irq_handle(unsigned int irq, struct irq_desc *desc)
++{
++	struct intc_data *data = irq_desc_get_handler_data(desc);
++	struct irq_chip *chip = irq_desc_get_chip(desc);
++	struct intc_block *block;
++	unsigned int idx;
++
++	chained_irq_enter(chip, desc);
++
++	for (idx = 0; idx < MAX_PARENT_IRQS; idx++)
++		if (irq == data->block[idx].parent_irq)
++			block = &data->block[idx];
++
++	for (idx = 0; idx < data->num_words; idx++) {
++		int base = idx * IRQS_PER_WORD;
++		unsigned long pending;
++		int hw_irq;
++
++		raw_spin_lock(&data->lock);
++		pending = __raw_readl(block->en_reg[idx]) &
++			  __raw_readl(block->status_reg[idx]);
++		raw_spin_unlock(&data->lock);
++
++		for_each_set_bit(hw_irq, &pending, IRQS_PER_WORD) {
++			int virq;
++
++			virq  = irq_find_mapping(data->domain, base + hw_irq);
++			generic_handle_irq(virq);
++		}
++	}
++
++	chained_irq_exit(chip, desc);
++}
++
++static void __bcm6345_periph_enable(struct intc_block *block, int reg, int bit,
++				    bool enable)
++{
++	u32 val;
++
++	val = __raw_readl(block->en_reg[reg]);
++	if (enable)
++		val |= BIT(bit);
++	else
++		val &= ~BIT(bit);
++	__raw_writel(val, block->en_reg[reg]);
++}
++
++static void bcm6345_periph_irq_mask(struct irq_data *data)
++{
++	unsigned int i, reg, bit;
++	struct intc_data *priv = data->domain->host_data;
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++
++	reg = hwirq / IRQS_PER_WORD;
++	bit = hwirq % IRQS_PER_WORD;
++
++	raw_spin_lock(&priv->lock);
++	for (i = 0; i < MAX_PARENT_IRQS; i++) {
++		struct intc_block *block = &priv->block[i];
++
++		if (!block->parent_irq)
++			break;
++
++		__bcm6345_periph_enable(block, reg, bit, false);
++	}
++	raw_spin_unlock(&priv->lock);
++}
++
++static void bcm6345_periph_irq_unmask(struct irq_data *data)
++{
++	struct intc_data *priv = data->domain->host_data;
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++	unsigned int i, reg, bit;
++
++	reg = hwirq / IRQS_PER_WORD;
++	bit = hwirq % IRQS_PER_WORD;
++
++	raw_spin_lock(&priv->lock);
++	for (i = 0; i < MAX_PARENT_IRQS; i++) {
++		struct intc_block *block = &priv->block[i];
++
++		if (!block->parent_irq)
++			break;
++
++		if (block->mask_cache[reg] & BIT(bit))
++			__bcm6345_periph_enable(block, reg, bit, true);
++		else
++			__bcm6345_periph_enable(block, reg, bit, false);
++	}
++	raw_spin_unlock(&priv->lock);
++}
++
++#ifdef CONFIG_SMP
++static int bcm6345_periph_set_affinity(struct irq_data *data,
++				       const struct cpumask *mask, bool force)
++{
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++	struct intc_data *priv = data->domain->host_data;
++	unsigned int i, reg, bit;
++	unsigned long flags;
++	bool enabled;
++	int cpu;
++
++	reg = hwirq / IRQS_PER_WORD;
++	bit = hwirq % IRQS_PER_WORD;
++
++	/* we could route to more than one cpu, but performance
++	   suffers, so fix it to one.
++	 */
++	cpu = cpumask_any_and(mask, cpu_online_mask);
++	if (cpu >= nr_cpu_ids)
++		return -EINVAL;
++
++	if (cpu >= MAX_PARENT_IRQS)
++		return -EINVAL;
++
++	if (!priv->block[cpu].parent_irq)
++		return -EINVAL;
++
++	raw_spin_lock_irqsave(&priv->lock, flags);
++	enabled = !irqd_irq_masked(data);
++	for (i = 0; i < MAX_PARENT_IRQS; i++) {
++		struct intc_block *block = &priv->block[i];
++
++		if (!block->parent_irq)
++			break;
++
++		if (i == cpu) {
++			block->mask_cache[reg] |= BIT(bit);
++			__bcm6345_periph_enable(block, reg, bit, enabled);
++		} else {
++			block->mask_cache[reg] &= ~BIT(bit);
++			__bcm6345_periph_enable(block, reg, bit, false);
++		}
++	}
++	raw_spin_unlock_irqrestore(&priv->lock, flags);
++
++	return 0;
++}
++#endif
++
++static int bcm6345_periph_map(struct irq_domain *d, unsigned int irq,
++			      irq_hw_number_t hw)
++{
++	struct intc_data *priv = d->host_data;
++
++	irq_set_chip_and_handler(irq, &priv->chip, handle_level_irq);
++
++	return 0;
++}
++
++static const struct irq_domain_ops bcm6345_periph_domain_ops = {
++	.xlate = irq_domain_xlate_onecell,
++	.map = bcm6345_periph_map,
++};
++
++static int __init __bcm6345_periph_intc_init(struct device_node *node,
++					     int num_blocks, int *irq,
++					     void __iomem **base, int num_words)
++{
++	struct intc_data *data;
++	unsigned int i, w, status_offset;
++
++	data = kzalloc(sizeof(*data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	raw_spin_lock_init(&data->lock);
++
++	status_offset = num_words * sizeof(u32);
++
++	for (i = 0; i < num_blocks; i++) {
++		struct intc_block *block = &data->block[i];
++
++		block->parent_irq = irq[i];
++		block->base = base[i];
++
++		for (w = 0; w < num_words; w++) {
++			int word_offset = sizeof(u32) * ((num_words - w) - 1);
++
++			block->en_reg[w] = base[i] + word_offset;
++			block->status_reg[w] = base[i] + status_offset;
++			block->status_reg[w] += word_offset;
++
++			/* route all interrupts to line 0 by default */
++			if (i == 0)
++				block->mask_cache[w] = 0xffffffff;
++		}
++
++		irq_set_handler_data(block->parent_irq, data);
++		irq_set_chained_handler(block->parent_irq,
++					bcm6345_periph_irq_handle);
++	}
++
++	data->num_words = num_words;
++
++	data->chip.name = "bcm6345-periph-intc";
++	data->chip.irq_mask = bcm6345_periph_irq_mask;
++	data->chip.irq_unmask = bcm6345_periph_irq_unmask;
++
++#ifdef CONFIG_SMP
++	if (num_blocks > 1)
++		data->chip.irq_set_affinity = bcm6345_periph_set_affinity;
++#endif
++
++	data->domain = irq_domain_add_simple(node, IRQS_PER_WORD * num_words,
++					     VIRQ_BASE,
++					     &bcm6345_periph_domain_ops, data);
++	if (!data->domain) {
++		kfree(data);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++void __init bcm6345_periph_intc_init(int num_blocks, int *irq,
++				     void __iomem **base, int num_words)
++{
++	__bcm6345_periph_intc_init(NULL, num_blocks, irq, base, num_words);
++}
++
++#ifdef CONFIG_OF
++static int __init bcm6345_periph_of_init(struct device_node *node,
++					 struct device_node *parent)
++{
++	struct resource res;
++	int num_irqs, ret = -EINVAL;
++	int irqs[MAX_PARENT_IRQS] = { 0 };
++	void __iomem *bases[MAX_PARENT_IRQS] = { NULL };
++	int words = 0;
++	int i;
++
++	num_irqs = of_irq_count(node);
++
++	if (num_irqs < 1 || num_irqs > MAX_PARENT_IRQS)
++		return -EINVAL;
++
++	for (i = 0; i < num_irqs; i++) {
++		resource_size_t size;
++
++		irqs[i] = irq_of_parse_and_map(node, i);
++		if (!irqs[i])
++			goto out_unmap;
++
++		if (of_address_to_resource(node, i, &res))
++			goto out_unmap;
++
++		size = resource_size(&res);
++		switch (size) {
++		case 8:
++		case 16:
++		case 32:
++			size = size / 8;
++			break;
++		default:
++			goto out_unmap;
++		}
++
++		if (words && words != size) {
++			ret = -EINVAL;
++			goto out_unmap;
++		}
++		words = size;
++
++		bases[i] = of_iomap(node, i);
++		if (!bases[i]) {
++			ret = -ENOMEM;
++			goto out_unmap;
++		}
++	}
++
++	ret = __bcm6345_periph_intc_init(node, num_irqs, irqs, bases, words);
++	if (!ret)
++		return 0;
++
++out_unmap:
++	for (i = 0; i < num_irqs; i++) {
++		iounmap(bases[i]);
++		irq_dispose_mapping(irqs[i]);
++	}
++
++	return ret;
++}
++
++IRQCHIP_DECLARE(bcm6345_periph_intc, "brcm,bcm6345-periph-intc",
++		bcm6345_periph_of_init);
++#endif
+--- /dev/null
++++ b/include/linux/irqchip/irq-bcm6345-periph.h
+@@ -0,0 +1,16 @@
++/*
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 2008 Maxime Bizon <mbizon at freebox.fr>
++ * Copyright (C) 2008 Nicolas Schichan <nschichan at freebox.fr>
++ */
++
++#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_PERIPH_H
++#define __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_PERIPH_H
++
++void bcm6345_periph_intc_init(int num_blocks, int *irq, void __iomem **base,
++			      int num_words);
++
++#endif /* __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_PERIPH_H */
diff --git a/target/linux/bmips/patches-4.1/121-irqchip-add-support-for-bcm6345-style-external-inter.patch b/target/linux/bmips/patches-4.1/121-irqchip-add-support-for-bcm6345-style-external-inter.patch
new file mode 100644
index 0000000..7eca81b
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/121-irqchip-add-support-for-bcm6345-style-external-inter.patch
@@ -0,0 +1,380 @@
+From cf908990d4a8ccdb73ee4484aa8cadad379ca314 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo at openwrt.org>
+Date: Sun, 30 Nov 2014 14:54:27 +0100
+Subject: [PATCH 2/5] irqchip: add support for bcm6345-style external
+ interrupt controller
+
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+---
+ .../interrupt-controller/brcm,bcm6345-ext-intc.txt |   29 ++
+ drivers/irqchip/Kconfig                            |    4 +
+ drivers/irqchip/Makefile                           |    1 +
+ drivers/irqchip/irq-bcm6345-ext.c                  |  287 ++++++++++++++++++++
+ include/linux/irqchip/irq-bcm6345-ext.h            |   14 +
+ 5 files changed, 335 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-ext-intc.txt
+ create mode 100644 drivers/irqchip/irq-bcm6345-ext.c
+ create mode 100644 include/linux/irqchip/irq-bcm6345-ext.h
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/interrupt-controller/brcm,bcm6345-ext-intc.txt
+@@ -0,0 +1,29 @@
++Broadcom BCM6345-style external interrupt controller
++
++Required properties:
++
++- compatible: Should be "brcm,bcm6345-l2-intc".
++- reg: Specifies the base physical addresses and size of the registers.
++- interrupt-controller: identifies the node as an interrupt controller.
++- #interrupt-cells: Specifies the number of cells needed to encode an interrupt
++  source, Should be 2.
++- interrupt-parent: Specifies the phandle to the parent interrupt controller
++  this one is cascaded from.
++- interrupts: Specifies the interrupt line(s) in the interrupt-parent controller
++  node, valid values depend on the type of parent interrupt controller.
++
++Optional properties:
++
++- brcm,field-width: Size of each field (mask, clear, sense, ...) in bits in the
++  register. Defaults to 4.
++
++Example:
++
++ext_intc: interrupt-controller at 10000018 {
++	compatible = "brcm,bcm6345-l2-intc";
++	interrupt-parent = <&periph_intc>;
++	#interrupt-cells = <2>;
++	reg = <0x10000018 0x4>;
++	interrupt-controller;
++	interrupts = <24>, <25>, <26>, <27>;
++};
+--- a/drivers/irqchip/Kconfig
++++ b/drivers/irqchip/Kconfig
+@@ -75,6 +75,10 @@ config BRCMSTB_L2_IRQ
+ 	select GENERIC_IRQ_CHIP
+ 	select IRQ_DOMAIN
+ 
++config BCM6345_EXT_IRQ
++	bool
++	select IRQ_DOMAIN
++
+ config BCM6345_PERIPH_IRQ
+ 	bool
+ 	select IRQ_DOMAIN
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_ARCH_MVEBU)		+= irq-armada-
+ obj-$(CONFIG_ARCH_MXS)			+= irq-mxs.o
+ obj-$(CONFIG_ARCH_TEGRA)		+= irq-tegra.o
+ obj-$(CONFIG_ARCH_S3C24XX)		+= irq-s3c24xx.o
++obj-$(CONFIG_BCM6345_EXT_IRQ)		+= irq-bcm6345-ext.o
+ obj-$(CONFIG_BCM6345_PERIPH_IRQ)	+= irq-bcm6345-periph.o
+ obj-$(CONFIG_DW_APB_ICTL)		+= irq-dw-apb-ictl.o
+ obj-$(CONFIG_METAG)			+= irq-metag-ext.o
+--- /dev/null
++++ b/drivers/irqchip/irq-bcm6345-ext.c
+@@ -0,0 +1,287 @@
++/*
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 2014 Jonas Gorski <jogo at openwrt.org>
++ */
++
++#include <linux/ioport.h>
++#include <linux/irq.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqchip/irq-bcm6345-ext.h>
++#include <linux/kernel.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/of_address.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++
++#include "irqchip.h"
++
++#ifdef CONFIG_BCM63XX
++#include <asm/mach-bcm63xx/bcm63xx_irq.h>
++
++#define VIRQ_BASE		IRQ_EXTERNAL_BASE
++#else
++#define VIRQ_BASE		0
++#endif
++
++#define MAX_IRQS		4
++
++#define EXTIRQ_CFG_SENSE	0
++#define EXTIRQ_CFG_STAT		1
++#define EXTIRQ_CFG_CLEAR	2
++#define EXTIRQ_CFG_MASK		3
++#define EXTIRQ_CFG_BOTHEDGE	4
++#define EXTIRQ_CFG_LEVELSENSE	5
++
++struct intc_data {
++	struct irq_chip chip;
++	struct irq_domain *domain;
++	raw_spinlock_t lock;
++
++	int parent_irq[MAX_IRQS];
++	void __iomem *reg;
++	int shift;
++};
++
++static void bcm6345_ext_intc_irq_handle(unsigned int irq, struct irq_desc *desc)
++{
++	struct intc_data *data = irq_desc_get_handler_data(desc);
++	struct irq_chip *chip = irq_desc_get_chip(desc);
++	unsigned int idx;
++
++	chained_irq_enter(chip, desc);
++
++	for (idx = 0; idx < MAX_IRQS; idx++) {
++		if (data->parent_irq[idx] != irq)
++			continue;
++
++		generic_handle_irq(irq_find_mapping(data->domain, idx));
++	}
++
++	chained_irq_exit(chip, desc);
++}
++
++static void bcm6345_ext_intc_irq_ack(struct irq_data *data)
++{
++	struct intc_data *priv = data->domain->host_data;
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++	u32 reg;
++
++	raw_spin_lock(&priv->lock);
++	reg = __raw_readl(priv->reg);
++	reg |= hwirq << (EXTIRQ_CFG_CLEAR * priv->shift);
++	__raw_writel(reg, priv->reg);
++	raw_spin_unlock(&priv->lock);
++}
++
++static void bcm6345_ext_intc_irq_mask(struct irq_data *data)
++{
++	struct intc_data *priv = data->domain->host_data;
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++	u32 reg;
++
++	raw_spin_lock(&priv->lock);
++	reg = __raw_readl(priv->reg);
++	reg &= ~(hwirq << (EXTIRQ_CFG_MASK * priv->shift));
++	__raw_writel(reg, priv->reg);
++	raw_spin_unlock(&priv->lock);
++}
++
++static void bcm6345_ext_intc_irq_unmask(struct irq_data *data)
++{
++	struct intc_data *priv = data->domain->host_data;
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++	u32 reg;
++
++	raw_spin_lock(&priv->lock);
++	reg = __raw_readl(priv->reg);
++	reg |= hwirq << (EXTIRQ_CFG_MASK * priv->shift);
++	__raw_writel(reg, priv->reg);
++	raw_spin_unlock(&priv->lock);
++}
++
++static int bcm6345_ext_intc_set_type(struct irq_data *data,
++				     unsigned int flow_type)
++{
++	struct intc_data *priv = data->domain->host_data;
++	irq_hw_number_t hwirq = irqd_to_hwirq(data);
++	bool levelsense = 0, sense = 0, bothedge = 0;
++	u32 reg;
++
++	flow_type &= IRQ_TYPE_SENSE_MASK;
++
++	if (flow_type == IRQ_TYPE_NONE)
++		flow_type = IRQ_TYPE_LEVEL_LOW;
++
++	switch (flow_type) {
++	case IRQ_TYPE_EDGE_BOTH:
++		bothedge = 1;
++		break;
++
++	case IRQ_TYPE_EDGE_RISING:
++		break;
++
++	case IRQ_TYPE_EDGE_FALLING:
++		sense = 1;
++		break;
++
++	case IRQ_TYPE_LEVEL_HIGH:
++		levelsense = 1;
++		sense = 1;
++		break;
++
++	case IRQ_TYPE_LEVEL_LOW:
++		levelsense = 1;
++		break;
++
++	default:
++		pr_err("bogus flow type combination given!\n");
++		return -EINVAL;
++	}
++
++	raw_spin_lock(&priv->lock);
++	reg = __raw_readl(priv->reg);
++
++	if (levelsense)
++		reg |= hwirq << (EXTIRQ_CFG_LEVELSENSE * priv->shift);
++	else
++		reg &= ~(hwirq << (EXTIRQ_CFG_LEVELSENSE * priv->shift));
++	if (sense)
++		reg |= hwirq << (EXTIRQ_CFG_SENSE * priv->shift);
++	else
++		reg &= ~(hwirq << (EXTIRQ_CFG_SENSE * priv->shift));
++	if (bothedge)
++		reg |= hwirq << (EXTIRQ_CFG_BOTHEDGE * priv->shift);
++	else
++		reg &= ~(hwirq << (EXTIRQ_CFG_BOTHEDGE * priv->shift));
++
++	__raw_writel(reg, priv->reg);
++	raw_spin_unlock(&priv->lock);
++
++	irqd_set_trigger_type(data, flow_type);
++	if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
++		__irq_set_handler_locked(data->irq, handle_level_irq);
++	else
++		__irq_set_handler_locked(data->irq, handle_edge_irq);
++
++	return 0;
++}
++
++static int bcm6345_ext_intc_map(struct irq_domain *d, unsigned int irq,
++				irq_hw_number_t hw)
++{
++	struct intc_data *priv = d->host_data;
++
++	irq_set_chip_and_handler(irq, &priv->chip, handle_level_irq);
++
++	return 0;
++}
++
++static const struct irq_domain_ops bcm6345_ext_domain_ops = {
++	.xlate = irq_domain_xlate_twocell,
++	.map = bcm6345_ext_intc_map,
++};
++
++static int __init __bcm6345_ext_intc_init(struct device_node *node,
++					  int num_irqs, int *irqs,
++					  void __iomem *reg, int shift)
++{
++	struct intc_data *data;
++	unsigned int i;
++	int start = VIRQ_BASE;
++
++	data = kzalloc(sizeof(*data), GFP_KERNEL);
++	if (!data)
++		return -ENOMEM;
++
++	raw_spin_lock_init(&data->lock);
++
++	for (i = 0; i < num_irqs; i++) {
++		data->parent_irq[i] = irqs[i];
++
++		irq_set_handler_data(irqs[i], data);
++		irq_set_chained_handler(irqs[i], bcm6345_ext_intc_irq_handle);
++	}
++
++	data->reg = reg;
++
++	data->chip.name = "bcm6345-ext-intc";
++	data->chip.irq_ack = bcm6345_ext_intc_irq_ack;
++	data->chip.irq_mask = bcm6345_ext_intc_irq_mask;
++	data->chip.irq_unmask = bcm6345_ext_intc_irq_unmask;
++	data->chip.irq_set_type = bcm6345_ext_intc_set_type;
++
++	/*
++	 * If we have less than 4 irqs, this is the second controller on
++	 * bcm63xx. So increase the VIRQ start to not overlap with the first
++	 * one, but only do so if we actually use a non-zero start.
++	 *
++	 * This can be removed when bcm63xx has no legacy users anymore.
++	 */
++	if (start && num_irqs < 4)
++		start += 4;
++
++	data->domain = irq_domain_add_simple(node, num_irqs, start,
++					     &bcm6345_ext_domain_ops, data);
++	if (!data->domain) {
++		kfree(data);
++		return -ENOMEM;
++	}
++
++	return 0;
++}
++
++void __init bcm6345_ext_intc_init(int num_irqs, int *irqs, void __iomem *reg,
++				  int shift)
++{
++	__bcm6345_ext_intc_init(NULL, num_irqs, irqs, reg, shift);
++}
++
++#ifdef CONFIG_OF
++static int __init bcm6345_ext_intc_of_init(struct device_node *node,
++					   struct device_node *parent)
++{
++	int num_irqs, ret = -EINVAL;
++	unsigned i;
++	void __iomem *base;
++	int irqs[MAX_IRQS] = { 0 };
++	u32 shift;
++
++	num_irqs = of_irq_count(node);
++
++	if (!num_irqs || num_irqs > MAX_IRQS)
++		return -EINVAL;
++
++	if (of_property_read_u32(node, "brcm,field-width", &shift))
++		shift = 4;
++
++	for (i = 0; i < num_irqs; i++) {
++		irqs[i] = irq_of_parse_and_map(node, i);
++		if (!irqs[i]) {
++			ret = -ENOMEM;
++			goto out_unmap;
++		}
++	}
++
++	base = of_iomap(node, 0);
++	if (!base)
++		goto out_unmap;
++
++	ret = __bcm6345_ext_intc_init(node, num_irqs, irqs, base, shift);
++	if (!ret)
++		return 0;
++out_unmap:
++	iounmap(base);
++
++	for (i = 0; i < num_irqs; i++)
++		irq_dispose_mapping(irqs[i]);
++
++	return ret;
++}
++
++IRQCHIP_DECLARE(bcm6345_ext_intc, "brcm,bcm6345-ext-intc",
++		bcm6345_ext_intc_of_init);
++#endif
+--- /dev/null
++++ b/include/linux/irqchip/irq-bcm6345-ext.h
+@@ -0,0 +1,14 @@
++/*
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.  See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 2014 Jonas Gorski <jogo at openwrt.org>
++ */
++
++#ifndef __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_EXT_H
++#define __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_EXT_H
++
++void bcm6345_ext_intc_init(int n_irqs, int *irqs, void __iomem *reg, int shift);
++
++#endif /* __INCLUDE_LINUX_IRQCHIP_IRQ_BCM6345_EXT_H */
diff --git a/target/linux/bmips/patches-4.1/122-MIPS-BMIPS-enable-bcm6345-irq.patch b/target/linux/bmips/patches-4.1/122-MIPS-BMIPS-enable-bcm6345-irq.patch
new file mode 100644
index 0000000..697c20a
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/122-MIPS-BMIPS-enable-bcm6345-irq.patch
@@ -0,0 +1,12 @@
+--- a/arch/mips/Kconfig
++++ b/arch/mips/Kconfig
+@@ -147,6 +147,9 @@ config BMIPS_GENERIC
+ 	select BCM7120_L2_IRQ
+ 	select BRCMSTB_L2_IRQ
+ 	select IRQ_CPU
++	select BCM6345_EXT_IRQ
++	select BCM6345_PERIPH_IRQ
++	select IRQ_DOMAIN
+ 	select RAW_IRQ_ACCESSORS
+ 	select DMA_NONCOHERENT
+ 	select GPIOLIB
diff --git a/target/linux/bmips/patches-4.1/200-irqchip-bcm6345-style-peripher-irq-ensure-disabled.patch b/target/linux/bmips/patches-4.1/200-irqchip-bcm6345-style-peripher-irq-ensure-disabled.patch
new file mode 100644
index 0000000..7aa6128
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/200-irqchip-bcm6345-style-peripher-irq-ensure-disabled.patch
@@ -0,0 +1,12 @@
+--- a/drivers/irqchip/irq-bcm6345-periph.c
++++ b/drivers/irqchip/irq-bcm6345-periph.c
+@@ -236,6 +236,9 @@ static int __init __bcm6345_periph_intc_
+ 			/* route all interrupts to line 0 by default */
+ 			if (i == 0)
+ 				block->mask_cache[w] = 0xffffffff;
++
++			/* ensure all interrupts are disabled */
++			__raw_writel(0, block->en_reg[w]);
+ 		}
+ 
+ 		irq_set_handler_data(block->parent_irq, data);
diff --git a/target/linux/bmips/patches-4.1/210-MIPS-BMIPS-BCM3368-basic-support.patch b/target/linux/bmips/patches-4.1/210-MIPS-BMIPS-BCM3368-basic-support.patch
new file mode 100644
index 0000000..f50bd73
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/210-MIPS-BMIPS-BCM3368-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -47,6 +47,11 @@ static void kbase_setup(void)
+ 	ebase = kbase;
+ }
+ 
++static void bcm3368_quirks(void)
++{
++	bmips_smp_enabled = 0;
++}
++
+ static void bcm3384_viper_quirks(void)
+ {
+ 	/*
+@@ -101,6 +106,7 @@ static void bcm6368_quirks(void)
+ }
+ 
+ static const struct bmips_quirk bmips_quirk_list[] = {
++	{ "brcm,bcm3368",		&bcm3368_quirks			},
+ 	{ "brcm,bcm3384-viper",		&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm33843-viper",	&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm6328",		&bcm6328_quirks			},
diff --git a/target/linux/bmips/patches-4.1/212-MIPS-BMIPS-BCM6338-basic-support.patch b/target/linux/bmips/patches-4.1/212-MIPS-BMIPS-BCM6338-basic-support.patch
new file mode 100644
index 0000000..4c7131e
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/212-MIPS-BMIPS-BCM6338-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -100,6 +100,11 @@ static void bcm6328_quirks(void)
+ 		bcm63xx_fixup_cpu1();
+ }
+ 
++static void bcm6338_quirks(void)
++{
++	bmips_smp_enabled = 0;
++}
++
+ static void bcm6368_quirks(void)
+ {
+ 	bcm63xx_fixup_cpu1();
+@@ -110,6 +115,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm3384-viper",		&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm33843-viper",	&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm6328",		&bcm6328_quirks			},
++	{ "brcm,bcm6338",		&bcm6338_quirks			},
+ 	{ "brcm,bcm6368",		&bcm6368_quirks			},
+ 	{ },
+ };
diff --git a/target/linux/bmips/patches-4.1/213-MIPS-BMIPS-BCM6345-basic-support.patch b/target/linux/bmips/patches-4.1/213-MIPS-BMIPS-BCM6345-basic-support.patch
new file mode 100644
index 0000000..75aeef4
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/213-MIPS-BMIPS-BCM6345-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -105,6 +105,11 @@ static void bcm6338_quirks(void)
+ 	bmips_smp_enabled = 0;
+ }
+ 
++static void bcm6345_quirks(void)
++{
++	bmips_smp_enabled = 0;
++}
++
+ static void bcm6368_quirks(void)
+ {
+ 	bcm63xx_fixup_cpu1();
+@@ -116,6 +121,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm33843-viper",	&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm6328",		&bcm6328_quirks			},
+ 	{ "brcm,bcm6338",		&bcm6338_quirks			},
++	{ "brcm,bcm6345",		&bcm6345_quirks			},
+ 	{ "brcm,bcm6368",		&bcm6368_quirks			},
+ 	{ },
+ };
diff --git a/target/linux/bmips/patches-4.1/214-MIPS-BMIPS-BCM6348-basic-support.patch b/target/linux/bmips/patches-4.1/214-MIPS-BMIPS-BCM6348-basic-support.patch
new file mode 100644
index 0000000..fdf1e0e
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/214-MIPS-BMIPS-BCM6348-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -110,6 +110,11 @@ static void bcm6345_quirks(void)
+ 	bmips_smp_enabled = 0;
+ }
+ 
++static void bcm6348_quirks(void)
++{
++	bmips_smp_enabled = 0;
++}
++
+ static void bcm6368_quirks(void)
+ {
+ 	bcm63xx_fixup_cpu1();
+@@ -122,6 +127,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm6328",		&bcm6328_quirks			},
+ 	{ "brcm,bcm6338",		&bcm6338_quirks			},
+ 	{ "brcm,bcm6345",		&bcm6345_quirks			},
++	{ "brcm,bcm6348",		&bcm6348_quirks			},
+ 	{ "brcm,bcm6368",		&bcm6368_quirks			},
+ 	{ },
+ };
diff --git a/target/linux/bmips/patches-4.1/215-MIPS-BMIPS-BCM6358-basic-support.patch b/target/linux/bmips/patches-4.1/215-MIPS-BMIPS-BCM6358-basic-support.patch
new file mode 100644
index 0000000..1cdf84c
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/215-MIPS-BMIPS-BCM6358-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -115,6 +115,11 @@ static void bcm6348_quirks(void)
+ 	bmips_smp_enabled = 0;
+ }
+ 
++static void bcm6358_quirks(void)
++{
++	bmips_smp_enabled = 0;
++}
++
+ static void bcm6368_quirks(void)
+ {
+ 	bcm63xx_fixup_cpu1();
+@@ -128,6 +133,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm6338",		&bcm6338_quirks			},
+ 	{ "brcm,bcm6345",		&bcm6345_quirks			},
+ 	{ "brcm,bcm6348",		&bcm6348_quirks			},
++	{ "brcm,bcm6358",		&bcm6358_quirks			},
+ 	{ "brcm,bcm6368",		&bcm6368_quirks			},
+ 	{ },
+ };
diff --git a/target/linux/bmips/patches-4.1/216-MIPS-BMIPS-BCM6362-basic-support.patch b/target/linux/bmips/patches-4.1/216-MIPS-BMIPS-BCM6362-basic-support.patch
new file mode 100644
index 0000000..2bdcd04
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/216-MIPS-BMIPS-BCM6362-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -120,6 +120,11 @@ static void bcm6358_quirks(void)
+ 	bmips_smp_enabled = 0;
+ }
+ 
++static void bcm6362_quirks(void)
++{
++	bcm63xx_fixup_cpu1();
++}
++
+ static void bcm6368_quirks(void)
+ {
+ 	bcm63xx_fixup_cpu1();
+@@ -134,6 +139,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm6345",		&bcm6345_quirks			},
+ 	{ "brcm,bcm6348",		&bcm6348_quirks			},
+ 	{ "brcm,bcm6358",		&bcm6358_quirks			},
++	{ "brcm,bcm6362",		&bcm6362_quirks			},
+ 	{ "brcm,bcm6368",		&bcm6368_quirks			},
+ 	{ },
+ };
diff --git a/target/linux/bmips/patches-4.1/217-MIPS-BMIPS-BCM63268-basic-support.patch b/target/linux/bmips/patches-4.1/217-MIPS-BMIPS-BCM63268-basic-support.patch
new file mode 100644
index 0000000..06de96e
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/217-MIPS-BMIPS-BCM63268-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -91,6 +91,11 @@ static void bcm63xx_fixup_cpu1(void)
+ 	cpumask_set_cpu(1, &bmips_booted_mask);
+ }
+ 
++static void bcm63268_quirks(void)
++{
++	bcm63xx_fixup_cpu1();
++}
++
+ static void bcm6328_quirks(void)
+ {
+ 	/* Check CPU1 status in OTP (it is usually disabled) */
+@@ -135,6 +140,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm3384-viper",		&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm33843-viper",	&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm6328",		&bcm6328_quirks			},
++	{ "brcm,bcm63268",		&bcm63268_quirks			},
+ 	{ "brcm,bcm6338",		&bcm6338_quirks			},
+ 	{ "brcm,bcm6345",		&bcm6345_quirks			},
+ 	{ "brcm,bcm6348",		&bcm6348_quirks			},
diff --git a/target/linux/bmips/patches-4.1/218-MIPS-BMIPS-BCM6318-basic-support.patch b/target/linux/bmips/patches-4.1/218-MIPS-BMIPS-BCM6318-basic-support.patch
new file mode 100644
index 0000000..091bc7d
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/218-MIPS-BMIPS-BCM6318-basic-support.patch
@@ -0,0 +1,22 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -91,6 +91,11 @@ static void bcm63xx_fixup_cpu1(void)
+ 	cpumask_set_cpu(1, &bmips_booted_mask);
+ }
+ 
++static void bcm6318_quirks(void)
++{
++	bmips_smp_enabled = 0;
++}
++
+ static void bcm63268_quirks(void)
+ {
+ 	bcm63xx_fixup_cpu1();
+@@ -139,6 +144,7 @@ static const struct bmips_quirk bmips_qu
+ 	{ "brcm,bcm3368",		&bcm3368_quirks			},
+ 	{ "brcm,bcm3384-viper",		&bcm3384_viper_quirks		},
+ 	{ "brcm,bcm33843-viper",	&bcm3384_viper_quirks		},
++	{ "brcm,bcm6318",		&bcm6318_quirks			},
+ 	{ "brcm,bcm6328",		&bcm6328_quirks			},
+ 	{ "brcm,bcm63268",		&bcm63268_quirks			},
+ 	{ "brcm,bcm6338",		&bcm6338_quirks			},
diff --git a/target/linux/bmips/patches-4.1/220-MIPS-BMIPS-uart-hacks.patch b/target/linux/bmips/patches-4.1/220-MIPS-BMIPS-uart-hacks.patch
new file mode 100644
index 0000000..4b1446a
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/220-MIPS-BMIPS-uart-hacks.patch
@@ -0,0 +1,26 @@
+--- a/arch/mips/bmips/setup.c
++++ b/arch/mips/bmips/setup.c
+@@ -241,4 +241,4 @@ static int __init plat_dev_init(void)
+ 	return 0;
+ }
+ 
+-device_initcall(plat_dev_init);
++arch_initcall(plat_dev_init);
+--- a/drivers/tty/serial/bcm63xx_uart.c
++++ b/drivers/tty/serial/bcm63xx_uart.c
+@@ -905,14 +905,7 @@ static int __init bcm_uart_init(void)
+ 	return ret;
+ }
+ 
+-static void __exit bcm_uart_exit(void)
+-{
+-	platform_driver_unregister(&bcm_uart_platform_driver);
+-	uart_unregister_driver(&bcm_uart_driver);
+-}
+-
+-module_init(bcm_uart_init);
+-module_exit(bcm_uart_exit);
++subsys_initcall(bcm_uart_init);
+ 
+ MODULE_AUTHOR("Maxime Bizon <mbizon at freebox.fr>");
+ MODULE_DESCRIPTION("Broadcom 63xx integrated uart driver");
diff --git a/target/linux/bmips/patches-4.1/221-MIPS-BMIPS-export-dma_mem_page.patch b/target/linux/bmips/patches-4.1/221-MIPS-BMIPS-export-dma_mem_page.patch
new file mode 100644
index 0000000..81a9f15
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/221-MIPS-BMIPS-export-dma_mem_page.patch
@@ -0,0 +1,10 @@
+--- a/arch/mips/bmips/dma.c
++++ b/arch/mips/bmips/dma.c
+@@ -61,6 +61,7 @@ dma_addr_t plat_map_dma_mem_page(struct
+ {
+ 	return bmips_phys_to_dma(dev, page_to_phys(page));
+ }
++EXPORT_SYMBOL(plat_map_dma_mem_page);
+ 
+ unsigned long plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr)
+ {
diff --git a/target/linux/bmips/patches-4.1/222-MIPS-BMIPS-ioremap.patch b/target/linux/bmips/patches-4.1/222-MIPS-BMIPS-ioremap.patch
new file mode 100644
index 0000000..a2a17b1
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/222-MIPS-BMIPS-ioremap.patch
@@ -0,0 +1,35 @@
+--- /dev/null
++++ b/arch/mips/include/asm/mach-bmips/ioremap.h
+@@ -0,0 +1,32 @@
++#ifndef __ASM_MACH_BMIPS_IOREMAP_H
++#define __ASM_MACH_BMIPS_IOREMAP_H
++
++#include <linux/types.h>
++
++static inline phys_addr_t fixup_bigphys_addr(phys_addr_t phys_addr, phys_addr_t size)
++{
++	return phys_addr;
++}
++
++static inline int is_bmips_internal_registers(phys_addr_t offset)
++{
++	if (offset >= 0xfff00000)
++		return 1;
++
++	return 0;
++}
++
++static inline void __iomem *plat_ioremap(phys_addr_t offset, unsigned long size,
++					 unsigned long flags)
++{
++	if (is_bmips_internal_registers(offset))
++		return (void __iomem *)offset;
++	return NULL;
++}
++
++static inline int plat_iounmap(const volatile void __iomem *addr)
++{
++	return is_bmips_internal_registers((unsigned long)addr);
++}
++
++#endif /* __ASM_MACH_BMIPS_IOREMAP_H */
diff --git a/target/linux/bmips/patches-4.1/223-MIPS-BMIPS-Revert-provide-a-plat_post_dma_flush-hook.patch b/target/linux/bmips/patches-4.1/223-MIPS-BMIPS-Revert-provide-a-plat_post_dma_flush-hook.patch
new file mode 100644
index 0000000..ab5538f
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/223-MIPS-BMIPS-Revert-provide-a-plat_post_dma_flush-hook.patch
@@ -0,0 +1,12 @@
+--- a/arch/mips/include/asm/mach-bmips/dma-coherence.h
++++ b/arch/mips/include/asm/mach-bmips/dma-coherence.h
+@@ -49,6 +49,8 @@ static inline int plat_device_is_coheren
+ 	return 0;
+ }
+ 
+-#define plat_post_dma_flush	bmips_post_dma_flush
++static inline void plat_post_dma_flush(struct device *dev)
++{
++}
+ 
+ #endif /* __ASM_MACH_BMIPS_DMA_COHERENCE_H */
diff --git a/target/linux/bmips/patches-4.1/230-gpio-add-a-simple-GPIO-driver-for-BCM63XX.patch b/target/linux/bmips/patches-4.1/230-gpio-add-a-simple-GPIO-driver-for-BCM63XX.patch
new file mode 100644
index 0000000..ce9438b
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/230-gpio-add-a-simple-GPIO-driver-for-BCM63XX.patch
@@ -0,0 +1,160 @@
+From dbe94a8daaa63ef81b7414f2a17bca8e36dd6daa Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jogo at openwrt.org>
+Date: Fri, 20 Feb 2015 19:55:32 +0100
+Subject: [PATCH 1/6] gpio: add a simple GPIO driver for bcm63xx
+
+
+Signed-off-by: Jonas Gorski <jogo at openwrt.org>
+---
+ drivers/gpio/Kconfig        |    8 +++
+ drivers/gpio/Makefile       |    1 +
+ drivers/gpio/gpio-bcm63xx.c |  117 +++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 126 insertions(+)
+ create mode 100644 drivers/gpio/gpio-bcm63xx.c
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -126,6 +126,13 @@ config GPIO_BCM_KONA
+ 	help
+ 	  Turn on GPIO support for Broadcom "Kona" chips.
+ 
++config GPIO_BCM63XX
++	bool "Broadcom BCM63XX GPIO"
++	depends on MIPS || COMPILE_TEST
++	select GPIO_GENERIC
++	help
++	  Turn on GPIO support for Broadcom BCM63XX xDSL chips.
++
+ config GPIO_CLPS711X
+ 	tristate "CLPS711X GPIO support"
+ 	depends on ARCH_CLPS711X || COMPILE_TEST
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -21,6 +21,7 @@ obj-$(CONFIG_GPIO_ALTERA)  	+= gpio-alte
+ obj-$(CONFIG_GPIO_AMD8111)	+= gpio-amd8111.o
+ obj-$(CONFIG_GPIO_ARIZONA)	+= gpio-arizona.o
+ obj-$(CONFIG_GPIO_BCM_KONA)	+= gpio-bcm-kona.o
++obj-$(CONFIG_GPIO_BCM63XX)	+= gpio-bcm63xx.o
+ obj-$(CONFIG_GPIO_BT8XX)	+= gpio-bt8xx.o
+ obj-$(CONFIG_GPIO_CLPS711X)	+= gpio-clps711x.o
+ obj-$(CONFIG_GPIO_CS5535)	+= gpio-cs5535.o
+--- /dev/null
++++ b/drivers/gpio/gpio-bcm63xx.c
+@@ -0,0 +1,117 @@
++/*
++ * Driver for BCM63XX memory-mapped GPIO controllers, based on
++ * Generic driver for memory-mapped GPIO controllers.
++ *
++ * Copyright 2008 MontaVista Software, Inc.
++ * Copyright 2008,2010 Anton Vorontsov <cbouatmailru at gmail.com>
++ * Copyright 2015 Jonas Gorski <jogo at 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.
++ */
++
++#include <linux/init.h>
++#include <linux/err.h>
++#include <linux/bug.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/spinlock.h>
++#include <linux/compiler.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/log2.h>
++#include <linux/ioport.h>
++#include <linux/io.h>
++#include <linux/gpio.h>
++#include <linux/slab.h>
++#include <linux/platform_device.h>
++#include <linux/mod_devicetable.h>
++#include <linux/basic_mmio_gpio.h>
++#include <linux/of.h>
++#include <linux/of_gpio.h>
++
++static int bcm63xx_gpio_probe(struct platform_device *pdev)
++{
++	struct device *dev = &pdev->dev;
++	struct resource *dat_r, *dirout_r;
++	void __iomem *dat;
++	void __iomem *dirout;
++	unsigned long sz;
++	int err;
++	struct bgpio_chip *bgc;
++	struct bgpio_pdata *pdata = dev_get_platdata(dev);
++
++	dirout_r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	dat_r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
++	if (!dat_r || !dirout_r)
++		return -EINVAL;
++
++	if (resource_size(dat_r) != resource_size(dirout_r))
++		return -EINVAL;
++
++	sz = resource_size(dat_r);
++
++	dat = devm_ioremap_resource(dev, dat_r);
++	if (IS_ERR(dat))
++		return PTR_ERR(dat);
++
++	dirout = devm_ioremap_resource(dev, dirout_r);
++	if (IS_ERR(dirout))
++		return PTR_ERR(dirout);
++
++	bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL);
++	if (!bgc)
++		return -ENOMEM;
++
++	err = bgpio_init(bgc, dev, sz, dat, NULL, NULL, dirout, NULL,
++			 BGPIOF_BIG_ENDIAN_BYTE_ORDER);
++	if (err)
++		return err;
++
++	platform_set_drvdata(pdev, bgc);
++
++	if (dev->of_node) {
++		u32 ngpios;
++
++		if (!of_property_read_u32(dev->of_node, "ngpios", &ngpios))
++			bgc->gc.ngpio = ngpios;
++
++	} else if (pdata) {
++		bgc->gc.base = pdata->base;
++		if (pdata->ngpio > 0)
++			bgc->gc.ngpio = pdata->ngpio;
++	}
++
++	return gpiochip_add(&bgc->gc);
++}
++
++static int bcm63xx_gpio_remove(struct platform_device *pdev)
++{
++	struct bgpio_chip *bgc = platform_get_drvdata(pdev);
++
++	return bgpio_remove(bgc);
++}
++
++#ifdef CONFIG_OF
++static struct of_device_id bcm63xx_gpio_of_match[] = {
++	{ .compatible = "brcm,bcm6345-gpio" },
++	{ },
++};
++#endif
++
++static struct platform_driver bcm63xx_gpio_driver = {
++	.probe = bcm63xx_gpio_probe,
++	.remove = bcm63xx_gpio_remove,
++	.driver = {
++		.name = "bcm63xx-gpio",
++		.of_match_table = of_match_ptr(bcm63xx_gpio_of_match),
++	},
++};
++
++module_platform_driver(bcm63xx_gpio_driver);
++
++MODULE_DESCRIPTION("Driver for BCM63XX memory-mapped GPIO controllers");
++MODULE_AUTHOR("Jonas Gorski <jogo at openwrt.org>");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/bmips/patches-4.1/240-mtd-brcmnand-add-polling-mode-support.patch b/target/linux/bmips/patches-4.1/240-mtd-brcmnand-add-polling-mode-support.patch
new file mode 100644
index 0000000..f778441
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/240-mtd-brcmnand-add-polling-mode-support.patch
@@ -0,0 +1,136 @@
+From e797c468f4a7ac63053ea0e70494d9c02d82e257 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari at gmail.com>
+Date: Fri, 31 Jul 2015 09:41:53 +0200
+Subject: [PATCH] brcmnand: add polling mode support
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
+---
+ drivers/mtd/nand/brcmnand/brcmnand.c | 89 +++++++++++++++++++++++-------------
+ 1 file changed, 56 insertions(+), 33 deletions(-)
+
+--- a/drivers/mtd/nand/brcmnand/brcmnand.c
++++ b/drivers/mtd/nand/brcmnand/brcmnand.c
+@@ -123,6 +123,7 @@ struct brcmnand_controller {
+ 	struct brcmnand_soc	*soc;
+ 
+ 	int			cmd_pending;
++	bool			cmd_poll;
+ 	bool			dma_pending;
+ 	struct completion	done;
+ 	struct completion	dma_done;
+@@ -355,6 +356,13 @@ enum {
+ 	INTFC_CTLR_READY		= BIT(31),
+ };
+ 
++#define BRCMNAND_POLL_MASK	(INTFC_OOB_VALID | \
++				 INTFC_CACHE_VALID | \
++				 INTFC_FLASH_READY | \
++				 INTFC_CTLR_READY)
++#define BRCMNAND_POLL_RETRIES	10000
++#define BRCMNAND_POLL_TIMEOUT	100
++
+ static inline u32 nand_readreg(struct brcmnand_controller *ctrl, u32 offs)
+ {
+ 	return brcmnand_readl(ctrl->nand_base + offs);
+@@ -1024,15 +1032,24 @@ static int brcmnand_waitfunc(struct mtd_
+ 	unsigned long timeo = msecs_to_jiffies(100);
+ 
+ 	dev_dbg(ctrl->dev, "wait on native cmd %d\n", ctrl->cmd_pending);
+-	if (ctrl->cmd_pending &&
+-			wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
+-		u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
+-					>> brcmnand_cmd_shift(ctrl);
+-
+-		dev_err_ratelimited(ctrl->dev,
+-			"timeout waiting for command %#02x\n", cmd);
+-		dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
+-			brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
++
++	if (ctrl->cmd_pending) {
++		if (ctrl->cmd_poll) {
++			int i;
++			for (i = 0; i < BRCMNAND_POLL_RETRIES; i++) {
++				u32 ready = brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS);
++				if ((ready & BRCMNAND_POLL_MASK) == BRCMNAND_POLL_MASK)
++					break;
++				udelay(BRCMNAND_POLL_TIMEOUT);
++			}
++		} else if (wait_for_completion_timeout(&ctrl->done, timeo) <= 0) {
++			u32 cmd = brcmnand_read_reg(ctrl, BRCMNAND_CMD_START)
++						>> brcmnand_cmd_shift(ctrl);
++			dev_err_ratelimited(ctrl->dev,
++				"timeout waiting for command %#02x\n", cmd);
++			dev_err_ratelimited(ctrl->dev, "intfc status %08x\n",
++				brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS));
++		}
+ 	}
+ 	ctrl->cmd_pending = 0;
+ 	return brcmnand_read_reg(ctrl, BRCMNAND_INTFC_STATUS) &
+@@ -2168,34 +2185,40 @@ int brcmnand_probe(struct platform_devic
+ 	}
+ 
+ 	/* IRQ */
+-	ctrl->irq = platform_get_irq(pdev, 0);
+-	if ((int)ctrl->irq < 0) {
+-		dev_err(dev, "no IRQ defined\n");
+-		return -ENODEV;
+-	}
++	if (of_property_read_bool(dn, "brcm,cmd-poll")) {
++		/* Polling mode for non-IRQ systems */
++		ctrl->cmd_poll = true;
++		dev_info(dev, "enabling polling mode\n");
++	} else {
++		ctrl->irq = platform_get_irq(pdev, 0);
++		if ((int)ctrl->irq < 0) {
++			dev_err(dev, "no IRQ defined\n");
++			return -ENODEV;
++		}
+ 
+-	/*
+-	 * Some SoCs integrate this controller (e.g., its interrupt bits) in
+-	 * interesting ways
+-	 */
+-	if (soc) {
+-		ctrl->soc = soc;
++		/*
++		 * Some SoCs integrate this controller (e.g., its interrupt bits) in
++		 * interesting ways
++		 */
++		if (soc) {
++			ctrl->soc = soc;
+ 
+-		ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
++			ret = devm_request_irq(dev, ctrl->irq, brcmnand_irq, 0,
+ 				       DRV_NAME, ctrl);
+ 
+-		/* Enable interrupt */
+-		ctrl->soc->ctlrdy_ack(ctrl->soc);
+-		ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
+-	} else {
+-		/* Use standard interrupt infrastructure */
+-		ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
+-				       DRV_NAME, ctrl);
+-	}
+-	if (ret < 0) {
+-		dev_err(dev, "can't allocate IRQ %d: error %d\n",
+-			ctrl->irq, ret);
+-		return ret;
++			/* Enable interrupt */
++			ctrl->soc->ctlrdy_ack(ctrl->soc);
++			ctrl->soc->ctlrdy_set_enabled(ctrl->soc, true);
++		} else {
++			/* Use standard interrupt infrastructure */
++			ret = devm_request_irq(dev, ctrl->irq, brcmnand_ctlrdy_irq, 0,
++					       DRV_NAME, ctrl);
++		}
++		if (ret < 0) {
++			dev_err(dev, "can't allocate IRQ %d: error %d\n",
++				ctrl->irq, ret);
++			return ret;
++		}
+ 	}
+ 
+ 	for_each_available_child_of_node(dn, child) {
diff --git a/target/linux/bmips/patches-4.1/250-leds-bcm6328-add-more-configuration-options-and-fix-.patch b/target/linux/bmips/patches-4.1/250-leds-bcm6328-add-more-configuration-options-and-fix-.patch
new file mode 100644
index 0000000..0371122
--- /dev/null
+++ b/target/linux/bmips/patches-4.1/250-leds-bcm6328-add-more-configuration-options-and-fix-.patch
@@ -0,0 +1,106 @@
+From 7a4f726f42d600f526155d6c66b8c4e5a0feb5f6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari at gmail.com>
+Date: Thu, 30 Jul 2015 08:38:33 +0200
+Subject: [PATCH] leds-bcm6328: add more configuration options and fix default
+ state initialization
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Álvaro Fernández Rojas <noltari at gmail.com>
+---
+ .../devicetree/bindings/leds/leds-bcm6328.txt      |  8 +++++
+ drivers/leds/leds-bcm6328.c                        | 41 ++++++++++++++++------
+ 1 files changed, 30 insertions(+), 11 deletions(-)
+
+--- a/drivers/leds/leds-bcm6328.c
++++ b/drivers/leds/leds-bcm6328.c
+@@ -41,6 +41,11 @@
+ #define BCM6328_SERIAL_LED_SHIFT_DIR	BIT(16)
+ #define BCM6328_LED_SHIFT_TEST		BIT(30)
+ #define BCM6328_LED_TEST		BIT(31)
++#define BCM6328_INIT_MASK		(BCM6328_SERIAL_LED_EN | \
++					 BCM6328_SERIAL_LED_MUX  | \
++					 BCM6328_SERIAL_LED_CLK_NPOL | \
++					 BCM6328_SERIAL_LED_DATA_PPOL | \
++					 BCM6328_SERIAL_LED_SHIFT_DIR)
+ 
+ #define BCM6328_LED_MODE_MASK		3
+ #define BCM6328_LED_MODE_OFF		0
+@@ -281,11 +286,10 @@ static int bcm6328_led(struct device *de
+ 						    "linux,default-trigger",
+ 						    NULL);
+ 
++	spin_lock_irqsave(lock, flags);
+ 	if (!of_property_read_string(nc, "default-state", &state)) {
+-		spin_lock_irqsave(lock, flags);
+ 		if (!strcmp(state, "on")) {
+ 			led->cdev.brightness = LED_FULL;
+-			bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
+ 		} else if (!strcmp(state, "keep")) {
+ 			void __iomem *mode;
+ 			unsigned long val, shift;
+@@ -296,21 +300,28 @@ static int bcm6328_led(struct device *de
+ 			else
+ 				mode = mem + BCM6328_REG_MODE_LO;
+ 
+-			val = bcm6328_led_read(mode) >> (shift % 16);
++			val = bcm6328_led_read(mode) >>
++			      BCM6328_LED_SHIFT(shift % 16);
+ 			val &= BCM6328_LED_MODE_MASK;
+-			if (val == BCM6328_LED_MODE_ON)
++			if ((led->active_low && val == BCM6328_LED_MODE_ON) ||
++			    (!led->active_low && val == BCM6328_LED_MODE_OFF))
+ 				led->cdev.brightness = LED_FULL;
+-			else {
++			else
+ 				led->cdev.brightness = LED_OFF;
+-				bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
+-			}
+ 		} else {
+ 			led->cdev.brightness = LED_OFF;
+-			bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
+ 		}
+-		spin_unlock_irqrestore(lock, flags);
++	} else {
++		led->cdev.brightness = LED_OFF;
+ 	}
+ 
++	if ((led->active_low && led->cdev.brightness == LED_FULL) ||
++	    (!led->active_low && led->cdev.brightness == LED_OFF))
++		bcm6328_led_mode(led, BCM6328_LED_MODE_ON);
++	else
++		bcm6328_led_mode(led, BCM6328_LED_MODE_OFF);
++	spin_unlock_irqrestore(lock, flags);
++
+ 	led->cdev.brightness_set = bcm6328_led_set;
+ 	led->cdev.blink_set = bcm6328_blink_set;
+ 
+@@ -360,9 +371,17 @@ static int bcm6328_leds_probe(struct pla
+ 	bcm6328_led_write(mem + BCM6328_REG_LNKACTSEL_LO, 0);
+ 
+ 	val = bcm6328_led_read(mem + BCM6328_REG_INIT);
+-	val &= ~BCM6328_SERIAL_LED_EN;
++	val &= ~(BCM6328_INIT_MASK);
+ 	if (of_property_read_bool(np, "brcm,serial-leds"))
+ 		val |= BCM6328_SERIAL_LED_EN;
++	if (of_property_read_bool(np, "brcm,serial-mux"))
++		val |= BCM6328_SERIAL_LED_MUX;
++	if (of_property_read_bool(np, "brcm,serial-clk-low"))
++		val |= BCM6328_SERIAL_LED_CLK_NPOL;
++	if (!of_property_read_bool(np, "brcm,serial-dat-low"))
++		val |= BCM6328_SERIAL_LED_DATA_PPOL;
++	if (!of_property_read_bool(np, "brcm,serial-shift-inv"))
++		val |= BCM6328_SERIAL_LED_SHIFT_DIR;
+ 	bcm6328_led_write(mem + BCM6328_REG_INIT, val);
+ 
+ 	for_each_available_child_of_node(np, child) {
+@@ -373,7 +392,7 @@ static int bcm6328_leds_probe(struct pla
+ 			continue;
+ 
+ 		if (reg >= BCM6328_LED_MAX_COUNT) {
+-			dev_err(dev, "invalid LED (>= %d)\n",
++			dev_err(dev, "invalid LED (%u >= %d)\n", reg,
+ 				BCM6328_LED_MAX_COUNT);
+ 			continue;
+ 		}
diff --git a/target/linux/bmips/profiles/00-default.mk b/target/linux/bmips/profiles/00-default.mk
new file mode 100644
index 0000000..65d47be
--- /dev/null
+++ b/target/linux/bmips/profiles/00-default.mk
@@ -0,0 +1,15 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/Default
+  NAME:=Default Profile
+  PACKAGES:=
+endef
+define Profile/Default/description
+  Package set compatible with most boards.
+endef
+$(eval $(call Profile,Default))
diff --git a/target/linux/bmips/profiles/comtrend.mk b/target/linux/bmips/profiles/comtrend.mk
new file mode 100644
index 0000000..ac20c06
--- /dev/null
+++ b/target/linux/bmips/profiles/comtrend.mk
@@ -0,0 +1,69 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/AR5381u
+  NAME:=Comtrend AR-5381u
+  PACKAGES:=
+endef
+define Profile/AR5381u/Description
+  Package set optimized for AR-5381u.
+endef
+$(eval $(call Profile,AR5381u))
+
+define Profile/AR5387un
+  NAME:=Comtrend AR-5387un
+  PACKAGES:=
+endef
+define Profile/AR5387un/Description
+  Package set optimized for AR-5387un.
+endef
+$(eval $(call Profile,AR5387un))
+
+define Profile/VG8050
+  NAME:=Comtrend VG-8050
+  PACKAGES:=
+endef
+define Profile/VG8050/Description
+  Package set optimized for VG-8050.
+endef
+$(eval $(call Profile,VG8050))
+
+define Profile/VR3025u
+  NAME:=Comtrend VR-3025u
+  PACKAGES:=
+endef
+define Profile/VR3025u/Description
+  Package set optimized for VR-3025u.
+endef
+$(eval $(call Profile,VR3025u))
+
+define Profile/VR3025un
+  NAME:=Comtrend VR-3025un
+  PACKAGES:=
+endef
+define Profile/VR3025un/Description
+  Package set optimized for VR-3025un.
+endef
+$(eval $(call Profile,VR3025un))
+
+define Profile/VR3032u
+  NAME:=Comtrend VR-3032u
+  PACKAGES:=
+endef
+define Profile/VR3032u/Description
+  Package set optimized for VR-3032u.
+endef
+$(eval $(call Profile,VR3032u))
+
+define Profile/WAP5813n
+  NAME:=Comtrend WAP-5813n
+  PACKAGES:=
+endef
+define Profile/WAP5813n/Description
+  Package set optimized for WAP-5813n.
+endef
+$(eval $(call Profile,WAP5813n))
diff --git a/target/linux/bmips/profiles/huawei.mk b/target/linux/bmips/profiles/huawei.mk
new file mode 100644
index 0000000..853fd35
--- /dev/null
+++ b/target/linux/bmips/profiles/huawei.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/HG520v
+  NAME:=Huawei EchoLife HG520v
+  PACKAGES:=
+endef
+define Profile/HG520v/Description
+  Package set optimized for Huawei HG520v.
+endef
+$(eval $(call Profile,HG520v))
+
+define Profile/HG556a_AB
+  NAME:=Huawei EchoLife HG556a (version A/B - Atheros)
+  PACKAGES:=
+endef
+define Profile/HG556a_AB/Description
+  Package set optimized for Huawei HG556a version A/B (Atheros).
+endef
+$(eval $(call Profile,HG556a_AB))
+
+define Profile/HG556a_C
+  NAME:=Huawei EchoLife HG556a (version C - Ralink)
+  PACKAGES:=
+endef
+define Profile/HG556a_C/Description
+  Package set optimized for Huawei HG556a version C (Ralink).
+endef
+$(eval $(call Profile,HG556a_C))
diff --git a/target/linux/bmips/profiles/sagem.mk b/target/linux/bmips/profiles/sagem.mk
new file mode 100644
index 0000000..0034ac2
--- /dev/null
+++ b/target/linux/bmips/profiles/sagem.mk
@@ -0,0 +1,15 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/FAST1704
+  NAME:=Sagem F at ST1704
+  PACKAGES:=
+endef
+define Profile/FAST1704/Description
+  Package set optimized for Sagem F at ST1704.
+endef
+$(eval $(call Profile,FAST1704))
diff --git a/target/linux/bmips/profiles/sfr.mk b/target/linux/bmips/profiles/sfr.mk
new file mode 100644
index 0000000..d5bf054
--- /dev/null
+++ b/target/linux/bmips/profiles/sfr.mk
@@ -0,0 +1,15 @@
+#
+# Copyright (C) 2015 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+define Profile/Neufbox4
+  NAME:=SFR Neufbox4
+  PACKAGES:=
+endef
+define Profile/Neufbox4/Description
+  Package set optimized for Neufbox4.
+endef
+$(eval $(call Profile,Neufbox4))
-- 
1.9.1

_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel


More information about the openwrt-devel mailing list