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

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


Forget this one, I didn't want to send it :D

El 09/10/2015 a las 22:29, Álvaro Fernández Rojas escribió:
> 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))

_______________________________________________
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