ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/target/linux/bcm47xx/Makefile b/target/linux/bcm47xx/Makefile
new file mode 100644
index 0000000..9ffd4f3
--- /dev/null
+++ b/target/linux/bcm47xx/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2008 OpenWrt.org
+
+include $(TOPDIR)/rules.mk
+
+ARCH:=mipsel
+BOARD:=bcm47xx
+BOARDNAME:=Broadcom BCM47xx/53xx (MIPS)
+FEATURES:=squashfs usb
+SUBTARGETS:=generic mips74k legacy
+
+KERNEL_PATCHVER:=5.4
+KERNEL_TESTING_PATCHVER:=5.4
+
+define Target/Description
+	Build firmware images for Broadcom based BCM47xx/53xx routers with MIPS CPU, *not* ARM.
+endef
+
+include $(INCLUDE_DIR)/target.mk
+
+DEFAULT_PACKAGES += swconfig nvram otrx \
+	kmod-leds-gpio kmod-gpio-button-hotplug
+
+$(eval $(call BuildTarget))
diff --git a/target/linux/bcm47xx/base-files/etc/board.d/01_network b/target/linux/bcm47xx/base-files/etc/board.d/01_network
new file mode 100755
index 0000000..242055f
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/etc/board.d/01_network
@@ -0,0 +1,212 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2006-2015 OpenWrt.org
+
+. /lib/functions/system.sh
+. /lib/functions/uci-defaults.sh
+
+configure_by_vlanports() {
+	local vlan0ports="$(nvram get vlan0ports)"
+	local vlan1ports="$(nvram get vlan1ports)"
+	local vlan2ports="$(nvram get vlan2ports)"
+	local cpuport="$(swconfig dev switch0 help 2>/dev/null | sed -ne "s|.*cpu @ \([0-9]*\).*|\1|p")"
+
+	if [ "${vlan0ports:0:9}" = "0 1 2 3 8" -a "${vlan1ports:0:3}" = "4 8" -a ${cpuport:-0} -eq 8 ] || \
+	   [ "${vlan1ports:0:9}" = "0 1 2 3 8" -a "${vlan2ports:0:3}" = "4 8" -a ${cpuport:-0} -eq 8 ] || \
+	   [ "${vlan2ports:0:9}" = "0 1 2 3 8" -a "${vlan1ports:0:3}" = "4 8" -a ${cpuport:-0} -eq 8 ];
+	then
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "8@eth0"
+
+	elif [ "${vlan0ports:0:9}" = "1 2 3 4 8" -a "${vlan1ports:0:3}" = "0 8" -a ${cpuport:-0} -eq 8 ] || \
+	     [ "${vlan1ports:0:9}" = "1 2 3 4 8" -a "${vlan2ports:0:3}" = "0 8" -a ${cpuport:-0} -eq 8 ] || \
+	     [ "${vlan2ports:0:9}" = "1 2 3 4 8" -a "${vlan1ports:0:3}" = "0 8" -a ${cpuport:-0} -eq 8 ];
+	then
+		ucidef_add_switch "switch0" \
+			"1:lan" "2:lan" "3:lan" "4:lan" "0:wan" "8@eth0"
+
+	elif [ "${vlan0ports:0:9}" = "0 1 2 3 5" -a "${vlan1ports:0:3}" = "4 5" -a ${cpuport:-0} -eq 5 ] || \
+		 [ "${vlan1ports:0:9}" = "0 1 2 3 5" -a "${vlan2ports:0:3}" = "4 5" -a ${cpuport:-0} -eq 5 ] || \
+		 [ "${vlan2ports:0:9}" = "0 1 2 3 5" -a "${vlan1ports:0:3}" = "4 5" -a ${cpuport:-0} -eq 5 ];
+	then
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "5@eth0"
+
+	elif [ "${vlan0ports:0:9}" = "1 2 3 4 5" -a "${vlan1ports:0:3}" = "0 5" -a ${cpuport:-0} -eq 5 ] || \
+	     [ "${vlan1ports:0:9}" = "1 2 3 4 5" -a "${vlan2ports:0:3}" = "0 5" -a ${cpuport:-0} -eq 5 ] || \
+	     [ "${vlan2ports:0:9}" = "1 2 3 4 5" -a "${vlan1ports:0:3}" = "0 5" -a ${cpuport:-0} -eq 5 ];
+	then
+		ucidef_add_switch "switch0" \
+			"1:lan" "2:lan" "3:lan" "4:lan" "0:wan" "5@eth0"
+
+	else
+		logger -t "01_network" "Unable to determine network configuration"
+		ucidef_set_interface_lan "eth0"
+	fi
+}
+
+configure_by_boardnum() {
+	local boardnum="$1"
+
+	case "$boardnum" in
+	# WAP54G, Sitecom WL-105b
+	"2" | \
+	"1024")
+		ucidef_set_interface_lan "eth0"
+		;;
+
+	# Generic detection fallback
+	*)
+		configure_by_vlanports
+		;;
+	esac
+}
+
+configure_by_boardtype() {
+	local boardtype="$1"
+	local boardnum="$2"
+
+	case "$boardtype" in
+	"bcm94710r4")
+		ucidef_set_interfaces_lan_wan "eth0" "eth1"
+		;;
+
+	"0x0467")
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "5@eth0"
+		;;
+
+	"0x042f" | \
+	"0x0472")
+		# WL-500gP
+		if [ "$boardnum" = "45" ]; then
+			ucidef_add_switch "switch0" \
+				"0:wan" "1:lan" "2:lan" "3:lan" "4:lan" "5@eth0"
+
+		# Generic BCM94704
+		else
+			ucidef_set_interface_wan "eth1"
+			ucidef_add_switch "switch0" \
+				"0:lan" "1:lan" "2:lan" "3:lan" "4:lan" "5@eth0"
+
+			# MAC addresses on 4704 tend to be screwed up. Add a workaround here
+			local mac="$(nvram get et0macaddr)"
+			local pat="[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]"
+				  pat="$pat:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]"
+				  pat="$pat:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]"
+
+			case "$mac" in
+			$pat)
+				ucidef_set_interface_macaddr "lan" "$mac"
+				ucidef_set_interface_macaddr "wan" "$(macaddr_add "$mac" 1)"
+				;;
+			esac
+		fi
+		;;
+
+	# Buffalo WBR-B11 and Buffalo WBR-G54
+	"bcm94710ap")
+		ucidef_set_interface_wan "eth1"
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "4:lan" "5@eth0"
+		;;
+
+	*)
+		configure_by_boardnum "$boardnum"
+		;;
+	esac
+}
+
+configure_by_model() {
+	local model="$1"
+	local boardtype="$2"
+	local boardnum="$3"
+
+	# Netgear WGT634U exception
+	if grep -sqE 'mtd0: 000(6|a)0000' /proc/mtd; then
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "5@eth0"
+		return
+	fi
+
+	case "$model" in
+	"Asus WLHDD" | \
+	"Asus WL300G")
+		ucidef_set_interface_lan "eth1"
+		;;
+
+	"Asus WL330GE")
+		ucidef_add_switch "switch0" "4:lan" "5t@eth0"
+		;;
+
+	"Asus WL500G" | \
+	"Microsoft MN-700")
+		ucidef_set_interfaces_lan_wan "eth0" "eth1"
+		;;
+
+	"Asus WL500GP V2")
+		ucidef_add_switch "switch0" \
+			"0:lan:4" "1:lan:3" "2:lan:2" "3:lan:1" "4:wan" "5@eth0"
+		;;
+
+	"Asus RT-N12"* | \
+	"Buffalo WHR-G125" | \
+	"D-Link DIR-330" | \
+	"Motorola WR850G" | \
+	"Siemens SE505 V2")
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "5@eth0"
+		;;
+
+	"Asus WL700")
+		ucidef_add_switch "switch0" \
+			"0:wan" "1:lan" "2:lan" "3:lan" "4:lan" "5@eth0"
+		;;
+
+	"Asus WL500W" | \
+	"Dell TrueMobile 2300")
+		ucidef_set_interface_wan "eth1"
+		ucidef_add_switch "switch0" \
+			"0:lan" "1:lan" "2:lan" "3:lan" "5@eth0"
+		;;
+
+	"Asus RT-N16"* | \
+	"Linksys E3000 V1" | \
+	"Linksys WRT610N V2" | \
+	"Netgear WNR3500 V2" | \
+	"Netgear WNR3500L")
+		ucidef_add_switch "switch0" \
+			"0:wan" "1:lan:4" "2:lan:3" "3:lan:2" "4:lan:1" "8@eth0"
+		;;
+
+	"Netgear R6200 V1")
+		ucidef_add_switch "switch0" \
+			"0:lan:4" "1:lan:3" "2:lan:2" "3:lan:1" "4:wan" "8@eth0"
+		;;
+
+	"Netgear WN2500RP V1")
+		ucidef_add_switch "switch0" \
+			"0:lan:4" "1:lan:3" "2:lan:2" "3:lan:1" "5@eth0"
+		;;
+
+	*)
+		configure_by_boardtype "$boardtype" "$boardnum"
+		;;
+	esac
+}
+
+
+model="$(cat /tmp/sysinfo/model)"
+boardtype="$(board_name)"
+
+case "$boardtype" in
+	*:*)
+		boardnum="${boardtype##*:}"
+		boardtype="${boardtype%:*}"
+	;;
+esac
+
+board_config_update
+
+configure_by_model "$model" "$boardtype" "$boardnum"
+
+board_config_flush
diff --git a/target/linux/bcm47xx/base-files/etc/diag.sh b/target/linux/bcm47xx/base-files/etc/diag.sh
new file mode 100644
index 0000000..91cf4bd
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/etc/diag.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Copyright (C) 2006 OpenWrt.org
+
+. /lib/functions/leds.sh
+
+get_status_led() {
+	for led in dmz power diag wps; do
+		status_led_file=$(find /sys/class/leds/ -name "*${led}*" | head -n1)
+		if [ ! -f $status_led_file ]; then
+			status_led=$(basename $status_led_file)
+			return
+		fi;
+	done
+}
+
+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/bcm47xx/base-files/etc/init.d/wmacfixup b/target/linux/bcm47xx/base-files/etc/init.d/wmacfixup
new file mode 100755
index 0000000..ee1b752
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/etc/init.d/wmacfixup
@@ -0,0 +1,33 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2010 OpenWrt.org
+
+START=41
+
+boot() {
+	[ -d /sys/class/ieee80211 ] || exit
+
+	commit=0
+
+	fixup_wmac() {
+		local cfg="$1"
+		local cfmac
+
+		config_get cfmac "$cfg" macaddr
+
+		[ "$cfmac" != "00:90:4c:5f:00:2a" ] || {
+			local nvmac="$(nvram get il0macaddr 2>/dev/null)"
+			[ -n "$nvmac" ] && [ "$nvmac != "$cfmac ] && {
+				uci set wireless.$cfg.macaddr="$nvmac"
+				commit=1
+			}
+		}
+	}
+
+	config_load wireless
+	config_foreach fixup_wmac wifi-device
+
+	[ "$commit" = 1 ] && uci commit wireless
+}
+
+start() { :; }
+stop()  { :; }
diff --git a/target/linux/bcm47xx/base-files/etc/uci-defaults/03_network_migration b/target/linux/bcm47xx/base-files/etc/uci-defaults/03_network_migration
new file mode 100644
index 0000000..3b380e0
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/etc/uci-defaults/03_network_migration
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2014-2015 OpenWrt.org
+#
+
+uci show network | grep "\.vlan=0"
+[ $? -ne 0 ] && exit 0
+
+logger -t network "network config is invalid, creating new one"
+
+lan_proto="$(uci -q get network.lan.proto)"
+lan_ipaddr="$(uci -q get network.lan.ipaddr)"
+lan_netmask="$(uci -q get network.lan.netmask)"
+wan_proto="$(uci -q get network.wan.proto)"
+wan_ipaddr="$(uci -q get network.wan.ipaddr)"
+wan_netmask="$(uci -q get network.wan.netmask)"
+
+echo "" > /etc/config/network
+config_generate
+
+uci set network.lan.proto=$lan_proto
+uci set network.lan.ipaddr=$lan_ipaddr
+uci set network.lan.netmask=$lan_netmask
+uci set network.wan.proto=$wan_proto
+uci set network.wan.ipaddr=$wan_ipaddr
+uci set network.wan.netmask=$wan_netmask
+uci commit network
+
+exit 0
diff --git a/target/linux/bcm47xx/base-files/etc/uci-defaults/09_fix_crc b/target/linux/bcm47xx/base-files/etc/uci-defaults/09_fix_crc
new file mode 100644
index 0000000..e9104a5
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/etc/uci-defaults/09_fix_crc
@@ -0,0 +1,5 @@
+#
+# Copyright (C) 2007 OpenWrt.org
+#
+
+mtd fixtrx firmware
diff --git a/target/linux/bcm47xx/base-files/lib/preinit/01_sysinfo b/target/linux/bcm47xx/base-files/lib/preinit/01_sysinfo
new file mode 100644
index 0000000..7cd0da5
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/lib/preinit/01_sysinfo
@@ -0,0 +1,14 @@
+do_sysinfo_bcm47xx() {
+	local boardtype="$(nvram get boardtype)"
+	local boardnum="$(nvram get boardnum)"
+	local model="$(sed -ne 's/^machine[ \t]*: //p' /proc/cpuinfo)"
+
+	[ -z "$model" ] && model="unknown"
+	[ -z "$boardtype" ] && boardtype="unknown"
+
+	[ -e "/tmp/sysinfo/" ] || mkdir -p "/tmp/sysinfo/"
+	echo "$boardtype${boardnum:+:$boardnum}" > /tmp/sysinfo/board_name
+	echo "$model" > /tmp/sysinfo/model
+}
+
+boot_hook_add preinit_main do_sysinfo_bcm47xx
diff --git a/target/linux/bcm47xx/base-files/lib/upgrade/platform.sh b/target/linux/bcm47xx/base-files/lib/upgrade/platform.sh
new file mode 100644
index 0000000..b7e7e45
--- /dev/null
+++ b/target/linux/bcm47xx/base-files/lib/upgrade/platform.sh
@@ -0,0 +1,248 @@
+PART_NAME=firmware
+
+LXL_FLAGS_VENDOR_LUXUL=0x00000001
+
+# $(1): file to read magic from
+# $(2): offset in bytes
+get_magic_long_at() {
+	dd if="$1" skip=$2 bs=1 count=4 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
+}
+
+# $(1): file to read LE long number from
+# $(2): offset in bytes
+get_le_long_at() {
+	echo $((0x$(dd if="$1" skip=$2 bs=1 count=4 2>/dev/null | hexdump -v -e '1/4 "%02x"')))
+}
+
+platform_expected_image() {
+	local model="$(cat /tmp/sysinfo/model)"
+
+	case "$model" in
+		"Netgear R6200 V1")	echo "chk U12H192T00_NETGEAR"; return;;
+		"Netgear WGR614 V8")	echo "chk U12H072T00_NETGEAR"; return;;
+		"Netgear WGR614 V9")	echo "chk U12H094T00_NETGEAR"; return;;
+		"Netgear WGR614 V10")	echo "chk U12H139T01_NETGEAR"; return;;
+		"Netgear WN2500RP V1")	echo "chk U12H197T00_NETGEAR"; return;;
+		"Netgear WN2500RP V2")	echo "chk U12H294T00_NETGEAR"; return;;
+		"Netgear WNDR3300")	echo "chk U12H093T00_NETGEAR"; return;;
+		"Netgear WNDR3400 V1")	echo "chk U12H155T00_NETGEAR"; return;;
+		"Netgear WNDR3400 V2")	echo "chk U12H187T00_NETGEAR"; return;;
+		"Netgear WNDR3400 V3")	echo "chk U12H208T00_NETGEAR"; return;;
+		"Netgear WNDR3400 Vcna")	echo "chk U12H155T01_NETGEAR"; return;;
+		"Netgear WNDR3700 V3")	echo "chk U12H194T00_NETGEAR"; return;;
+		"Netgear WNDR4000")	echo "chk U12H181T00_NETGEAR"; return;;
+		"Netgear WNDR4500 V1")	echo "chk U12H189T00_NETGEAR"; return;;
+		"Netgear WNDR4500 V2")	echo "chk U12H224T00_NETGEAR"; return;;
+		"Netgear WNR2000 V2")	echo "chk U12H114T00_NETGEAR"; return;;
+		"Netgear WNR3500L")	echo "chk U12H136T99_NETGEAR"; return;;
+		"Netgear WNR3500U")	echo "chk U12H136T00_NETGEAR"; return;;
+		"Netgear WNR3500 V2")	echo "chk U12H127T00_NETGEAR"; return;;
+		"Netgear WNR3500 V2vc")	echo "chk U12H127T70_NETGEAR"; return;;
+		"Netgear WNR834B V2")	echo "chk U12H081T00_NETGEAR"; return;;
+		"Linksys E900 V1")	echo "cybertan E900"; return;;
+		"Linksys E1000 V1")	echo "cybertan E100"; return;;
+		"Linksys E1000 V2")	echo "cybertan E100"; return;;
+		"Linksys E1000 V2.1")	echo "cybertan E100"; return;;
+		"Linksys E1200 V2")	echo "cybertan E122"; return;;
+		"Linksys E2000 V1")	echo "cybertan 32XN"; return;;
+		"Linksys E3000 V1")	echo "cybertan 61XN"; return;;
+		"Linksys E3200 V1")	echo "cybertan 3200"; return;;
+		"Linksys E4200 V1")	echo "cybertan 4200"; return;;
+		"Linksys WRT150N V1.1")	echo "cybertan N150"; return;;
+		"Linksys WRT150N V1")	echo "cybertan N150"; return;;
+		"Linksys WRT160N V1")	echo "cybertan N150"; return;;
+		"Linksys WRT160N V3")	echo "cybertan N150"; return;;
+		"Linksys WRT300N V1")	echo "cybertan EWCB"; return;;
+		"Linksys WRT300N V1.1")	echo "cybertan EWC2"; return;;
+		"Linksys WRT310N V1")	echo "cybertan 310N"; return;;
+		"Linksys WRT310N V2")	echo "cybertan 310N"; return;;
+		"Linksys WRT610N V1")	echo "cybertan 610N"; return;;
+		"Linksys WRT610N V2")	echo "cybertan 610N"; return;;
+		"Luxul XAP-310 V1")	echo "lxl XAP-310"; return;;
+		"Luxul XAP-1210 V1")	echo "lxl XAP-1210"; return;;
+		"Luxul XAP-1230 V1")	echo "lxl XAP-1230"; return;;
+		"Luxul XAP-1240 V1")	echo "lxl XAP-1240"; return;;
+		"Luxul XAP-1500 V1")	echo "lxl XAP-1500"; return;;
+		"Luxul ABR-4400 V1")	echo "lxl ABR-4400"; return;;
+		"Luxul XBR-4400 V1")	echo "lxl XBR-4400"; return;;
+		"Luxul XVW-P30 V1")	echo "lxl XVW-P30"; return;;
+		"Luxul XWR-600 V1")	echo "lxl XWR-600"; return;;
+		"Luxul XWR-1750 V1")	echo "lxl XWR-1750"; return;;
+	esac
+}
+
+bcm47xx_identify() {
+	local magic
+
+	magic=$(get_magic_long "$1")
+	case "$magic" in
+		"48445230")
+			echo "trx"
+			return
+			;;
+		"2a23245e")
+			echo "chk"
+			return
+			;;
+		"4c584c23")
+			echo "lxl"
+			return
+			;;
+	esac
+
+	magic=$(get_magic_long_at "$1" 14)
+	[ "$magic" = "55324e44" ] && {
+		echo "cybertan"
+		return
+	}
+
+	magic=$(get_magic_long_at "$1" 60)
+	[ "$magic" = "4c584c23" ] && {
+		echo "lxlold"
+		return
+	}
+
+	echo "unknown"
+}
+
+platform_check_image() {
+	[ "$#" -gt 1 ] && return 1
+
+	local file_type=$(bcm47xx_identify "$1")
+	local magic
+	local error=0
+
+	case "$file_type" in
+		"chk")
+			local header_len=$((0x$(get_magic_long_at "$1" 4)))
+			local board_id_len=$(($header_len - 40))
+			local board_id=$(dd if="$1" skip=40 bs=1 count=$board_id_len 2>/dev/null | hexdump -v -e '1/1 "%c"')
+			local dev_board_id=$(platform_expected_image)
+			echo "Found CHK image with device board_id $board_id"
+
+			[ -n "$dev_board_id" -a "chk $board_id" != "$dev_board_id" ] && {
+				echo "Firmware board_id doesn't match device board_id ($dev_board_id)"
+				error=1
+			}
+
+			if ! otrx check "$1" -o "$header_len"; then
+				echo "No valid TRX firmware in the CHK image"
+				notify_firmware_test_result "trx_valid" 0
+				error=1
+			else
+				notify_firmware_test_result "trx_valid" 1
+			fi
+		;;
+		"cybertan")
+			local pattern=$(dd if="$1" bs=1 count=4 2>/dev/null | hexdump -v -e '1/1 "%c"')
+			local dev_pattern=$(platform_expected_image)
+			echo "Found CyberTAN image with device pattern: $pattern"
+
+			[ -n "$dev_pattern" -a "cybertan $pattern" != "$dev_pattern" ] && {
+				echo "Firmware pattern doesn't match device pattern ($dev_pattern)"
+				error=1
+			}
+
+			if ! otrx check "$1" -o 32; then
+				echo "No valid TRX firmware in the CyberTAN image"
+				notify_firmware_test_result "trx_valid" 0
+				error=1
+			else
+				notify_firmware_test_result "trx_valid" 1
+			fi
+		;;
+		"lxl")
+			local hdr_len=$(get_le_long_at "$1" 8)
+			local flags=$(get_le_long_at "$1" 12)
+			local board=$(dd if="$1" skip=16 bs=1 count=16 2>/dev/null | hexdump -v -e '1/1 "%c"')
+			local dev_board=$(platform_expected_image)
+			echo "Found Luxul image for board $board"
+
+			[ -n "$dev_board" -a "lxl $board" != "$dev_board" ] && {
+				echo "Firmware ($board) doesn't match device ($dev_board)"
+				error=1
+			}
+
+			[ $((flags & LXL_FLAGS_VENDOR_LUXUL)) -gt 0 ] && notify_firmware_no_backup
+
+			if ! otrx check "$1" -o "$hdr_len"; then
+				echo "No valid TRX firmware in the Luxul image"
+				notify_firmware_test_result "trx_valid" 0
+				error=1
+			else
+				notify_firmware_test_result "trx_valid" 1
+			fi
+		;;
+		"lxlold")
+			local board_id=$(dd if="$1" skip=48 bs=1 count=12 2>/dev/null | hexdump -v -e '1/1 "%c"')
+			local dev_board_id=$(platform_expected_image)
+			echo "Found Luxul image with device board_id $board_id"
+
+			[ -n "$dev_board_id" -a "lxl $board_id" != "$dev_board_id" ] && {
+				echo "Firmware board_id doesn't match device board_id ($dev_board_id)"
+				error=1
+			}
+
+			notify_firmware_no_backup
+
+			if ! otrx check "$1" -o 64; then
+				echo "No valid TRX firmware in the Luxul image"
+				notify_firmware_test_result "trx_valid" 0
+				error=1
+			else
+				notify_firmware_test_result "trx_valid" 1
+			fi
+		;;
+		"trx")
+			if ! otrx check "$1"; then
+				echo "Invalid (corrupted?) TRX firmware"
+				notify_firmware_test_result "trx_valid" 0
+				error=1
+			else
+				notify_firmware_test_result "trx_valid" 1
+			fi
+		;;
+		*)
+			echo "Invalid image type. Please use firmware specific for this device."
+			notify_firmware_broken
+			error=1
+		;;
+	esac
+
+	return $error
+}
+
+platform_trx_from_chk_cmd() {
+	local header_len=$((0x$(get_magic_long_at "$1" 4)))
+
+	echo -n dd bs=$header_len skip=1
+}
+
+platform_trx_from_cybertan_cmd() {
+	echo -n dd bs=32 skip=1
+}
+
+platform_trx_from_lxl_cmd() {
+	local hdr_len=$(get_le_long_at "$1" 8)
+
+	echo -n dd skip=$hdr_len iflag=skip_bytes
+}
+
+platform_trx_from_lxlold_cmd() {
+	echo -n dd bs=64 skip=1
+}
+
+platform_do_upgrade() {
+	local file_type=$(bcm47xx_identify "$1")
+	local trx="$1"
+	local cmd=""
+
+	case "$file_type" in
+		"chk")		cmd=$(platform_trx_from_chk_cmd "$trx");;
+		"cybertan")	cmd=$(platform_trx_from_cybertan_cmd "$trx");;
+		"lxl")		cmd=$(platform_trx_from_lxl_cmd "$trx");;
+		"lxlold")	cmd=$(platform_trx_from_lxlold_cmd "$trx");;
+	esac
+
+	default_do_upgrade "$trx" "$cmd"
+}
diff --git a/target/linux/bcm47xx/config-5.4 b/target/linux/bcm47xx/config-5.4
new file mode 100644
index 0000000..2747efc
--- /dev/null
+++ b/target/linux/bcm47xx/config-5.4
@@ -0,0 +1,208 @@
+CONFIG_ADM6996_PHY=y
+CONFIG_ARCH_BINFMT_ELF_STATE=y
+CONFIG_ARCH_CLOCKSOURCE_DATA=y
+CONFIG_ARCH_DISCARD_MEMBLOCK=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MMAP_RND_BITS_MAX=15
+CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=15
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
+CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_BCM47XX=y
+CONFIG_BCM47XX_BCMA=y
+CONFIG_BCM47XX_NVRAM=y
+CONFIG_BCM47XX_SPROM=y
+CONFIG_BCM47XX_SSB=y
+CONFIG_BCM47XX_WDT=y
+CONFIG_BCMA=y
+CONFIG_BCMA_BLOCKIO=y
+CONFIG_BCMA_DEBUG=y
+CONFIG_BCMA_DRIVER_GMAC_CMN=y
+CONFIG_BCMA_DRIVER_GPIO=y
+CONFIG_BCMA_DRIVER_MIPS=y
+CONFIG_BCMA_DRIVER_PCI=y
+CONFIG_BCMA_DRIVER_PCI_HOSTMODE=y
+CONFIG_BCMA_HOST_PCI=y
+CONFIG_BCMA_HOST_PCI_POSSIBLE=y
+CONFIG_BCMA_HOST_SOC=y
+CONFIG_BCMA_NFLASH=y
+CONFIG_BCMA_PFLASH=y
+CONFIG_BCMA_SFLASH=y
+# CONFIG_BGMAC_BCMA is not set
+CONFIG_BLK_MQ_PCI=y
+CONFIG_CEVT_R4K=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_CMDLINE="noinitrd console=ttyS0,115200"
+CONFIG_CMDLINE_BOOL=y
+# CONFIG_CMDLINE_OVERRIDE is not set
+# CONFIG_CPU_BMIPS is not set
+CONFIG_CPU_GENERIC_DUMP_TLB=y
+CONFIG_CPU_HAS_PREFETCH=y
+CONFIG_CPU_HAS_SYNC=y
+CONFIG_CPU_LITTLE_ENDIAN=y
+CONFIG_CPU_MIPS32=y
+CONFIG_CPU_MIPS32_R1=y
+# CONFIG_CPU_MIPS32_R2 is not set
+CONFIG_CPU_MIPSR1=y
+CONFIG_CPU_MIPSR2_IRQ_VI=y
+CONFIG_CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS=y
+CONFIG_CPU_R4K_CACHE_TLB=y
+CONFIG_CPU_R4K_FPU=y
+CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
+CONFIG_CPU_SUPPORTS_HIGHMEM=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_CSRC_R4K=y
+CONFIG_DMA_DIRECT_OPS=y
+CONFIG_DMA_NONCOHERENT=y
+CONFIG_DMA_NONCOHERENT_CACHE_SYNC=y
+CONFIG_DMA_NONCOHERENT_MMAP=y
+CONFIG_DMA_NONCOHERENT_OPS=y
+# CONFIG_EARLY_PRINTK is not set
+CONFIG_FIXED_PHY=y
+CONFIG_GENERIC_ATOMIC64=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_CPU_AUTOPROBE=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_LIB_ASHLDI3=y
+CONFIG_GENERIC_LIB_ASHRDI3=y
+CONFIG_GENERIC_LIB_CMPDI2=y
+CONFIG_GENERIC_LIB_LSHRDI3=y
+CONFIG_GENERIC_LIB_UCMPDI2=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIOLIB_IRQCHIP=y
+CONFIG_GPIO_WDT=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDWARE_WATCHPOINTS=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+CONFIG_HAVE_ARCH_COMPILER_H=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_CBPF_JIT=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_COPY_THREAD_TLS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DEBUG_STACKOVERFLOW=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_EXIT_ON_IRQ_STACK=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_HAVE_LD_DEAD_CODE_DATA_ELIMINATION=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_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_RSEQ=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HW_HAS_PCI=y
+CONFIG_HW_RANDOM=y
+CONFIG_HZ_PERIODIC=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_MIPS_CPU=y
+CONFIG_IRQ_WORK=y
+CONFIG_LEDS_GPIO_REGISTER=y
+CONFIG_LOCK_DEBUGGING_SUPPORT=y
+CONFIG_MDIO_BUS=y
+CONFIG_MDIO_DEVICE=y
+CONFIG_MEMFD_CREATE=y
+CONFIG_MIGRATION=y
+CONFIG_MIPS=y
+CONFIG_MIPS_ASID_BITS=8
+CONFIG_MIPS_ASID_SHIFT=0
+CONFIG_MIPS_CLOCK_VSYSCALL=y
+# CONFIG_MIPS_CMDLINE_BUILTIN_EXTEND is not set
+CONFIG_MIPS_CMDLINE_FROM_BOOTLOADER=y
+CONFIG_MIPS_L1_CACHE_SHIFT=5
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MTD_BCM47XXSFLASH=y
+CONFIG_MTD_BCM47XX_PARTS=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BCM47XXNFLASH=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_PARSER_TRX=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_PER_CPU_KM=y
+CONFIG_NO_EXCEPT_FILL=y
+CONFIG_NO_GENERIC_PCI_IOPORT_MAP=y
+# CONFIG_OF is not set
+CONFIG_PCI=y
+CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DRIVERS_LEGACY=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PGTABLE_LEVELS=2
+CONFIG_PHYLIB=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SRCU=y
+CONFIG_SSB=y
+CONFIG_SSB_B43_PCI_BRIDGE=y
+CONFIG_SSB_BLOCKIO=y
+CONFIG_SSB_DRIVER_EXTIF=y
+CONFIG_SSB_DRIVER_GIGE=y
+CONFIG_SSB_DRIVER_GPIO=y
+CONFIG_SSB_DRIVER_MIPS=y
+CONFIG_SSB_DRIVER_PCICORE=y
+CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y
+CONFIG_SSB_EMBEDDED=y
+CONFIG_SSB_HOST_SOC=y
+CONFIG_SSB_PCICORE_HOSTMODE=y
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_PCIHOST_POSSIBLE=y
+CONFIG_SSB_SERIAL=y
+CONFIG_SSB_SFLASH=y
+CONFIG_SSB_SPROM=y
+CONFIG_SWCONFIG=y
+CONFIG_SWCONFIG_B53=y
+# CONFIG_SWCONFIG_B53_MMAP_DRIVER is not set
+CONFIG_SWCONFIG_B53_PHY_DRIVER=y
+CONFIG_SWCONFIG_B53_PHY_FIXUP=y
+# CONFIG_SWCONFIG_B53_SRAB_DRIVER is not set
+CONFIG_SWPHY=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_SYS_HAS_CPU_BMIPS=y
+CONFIG_SYS_HAS_CPU_BMIPS32_3300=y
+CONFIG_SYS_HAS_CPU_MIPS32_R1=y
+CONFIG_SYS_HAS_CPU_MIPS32_R2=y
+CONFIG_SYS_HAS_EARLY_PRINTK=y
+CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
+CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
+CONFIG_SYS_SUPPORTS_HIGHMEM=y
+CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
+CONFIG_SYS_SUPPORTS_MIPS16=y
+CONFIG_SYS_SUPPORTS_ZBOOT=y
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TINY_SRCU=y
+CONFIG_USB_SUPPORT=y
+CONFIG_USE_GENERIC_EARLY_PRINTK_8250=y
+CONFIG_WATCHDOG_CORE=y
diff --git a/target/linux/bcm47xx/generic/profiles/100-Broadcom-b43.mk b/target/linux/bcm47xx/generic/profiles/100-Broadcom-b43.mk
new file mode 100644
index 0000000..8ff95b4
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/100-Broadcom-b43.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+
+define Profile/Broadcom-b43
+  NAME:=Broadcom SoC, all Ethernet, BCM43xx WiFi (b43, default)
+  PACKAGES:=kmod-b44 kmod-tg3 kmod-bgmac kmod-b43 kmod-b43legacy
+endef
+
+define Profile/Broadcom-b43/Description
+	Package set compatible with hardware any Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the mac80211, b43 and
+	b43legacy drivers and b44, tg3 or bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-b43))
+
diff --git a/target/linux/bcm47xx/generic/profiles/101-Broadcom-wl.mk b/target/linux/bcm47xx/generic/profiles/101-Broadcom-wl.mk
new file mode 100644
index 0000000..498bc7b
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/101-Broadcom-wl.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2010-2013 OpenWrt.org
+
+define Profile/Broadcom-wl
+  NAME:=Broadcom SoC, all Ethernet, BCM43xx WiFi (wl, proprietary)
+  PACKAGES:=-wpad-basic-wolfssl kmod-b44 kmod-tg3 kmod-bgmac kmod-brcm-wl wlc nas
+endef
+
+define Profile/Broadcom-wl/Description
+	Package set compatible with hardware any Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the proprietary Broadcom
+	wireless "wl" driver and b44, tg3 or bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-wl))
+
diff --git a/target/linux/bcm47xx/generic/profiles/104-Broadcom-ath5k.mk b/target/linux/bcm47xx/generic/profiles/104-Broadcom-ath5k.mk
new file mode 100644
index 0000000..e66b251
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/104-Broadcom-ath5k.mk
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+
+define Profile/Broadcom-ath5k
+  NAME:=Broadcom SoC, all Ethernet, Atheros WiFi (ath5k)
+  PACKAGES:=kmod-b44 kmod-tg3 kmod-bgmac kmod-ath5k
+endef
+
+define Profile/Broadcom-ath5k/Description
+	Package set compatible with hardware any Broadcom BCM47xx or BCM535x
+	SoC with Atheros Wifi cards using the mac80211 and ath5k drivers and
+	b44, tg3 or bgmac Ethernet driver.
+endef
+$(eval $(call Profile,Broadcom-ath5k))
+
diff --git a/target/linux/bcm47xx/generic/profiles/105-Broadcom-none.mk b/target/linux/bcm47xx/generic/profiles/105-Broadcom-none.mk
new file mode 100644
index 0000000..0b88382
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/105-Broadcom-none.mk
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+
+define Profile/Broadcom-none
+  NAME:=Broadcom SoC, all Ethernet, No WiFi
+  PACKAGES:=-wpad-basic-wolfssl kmod-b44 kmod-tg3 kmod-bgmac
+endef
+
+define Profile/Broadcom-none/Description
+	Package set compatible with hardware any Broadcom BCM47xx or BCM535x
+	SoC without any Wifi cards and b44, tg3 or bgmac Ethernet driver.
+endef
+$(eval $(call Profile,Broadcom-none))
+
diff --git a/target/linux/bcm47xx/generic/profiles/200-Broadcom-b44-b43.mk b/target/linux/bcm47xx/generic/profiles/200-Broadcom-b44-b43.mk
new file mode 100644
index 0000000..0231bff
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/200-Broadcom-b44-b43.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+
+define Profile/Broadcom-b44-b43
+  NAME:=Broadcom SoC, b44 Ethernet, BCM43xx WiFi (b43, default)
+  PACKAGES:=kmod-b44 kmod-b43 kmod-b43legacy
+endef
+
+define Profile/Broadcom-b44-b43/Description
+	Package set compatible with hardware older Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the mac80211, b43 and
+	b43legacy drivers and b44 Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-b44-b43))
+
diff --git a/target/linux/bcm47xx/generic/profiles/201-Broadcom-b44-wl.mk b/target/linux/bcm47xx/generic/profiles/201-Broadcom-b44-wl.mk
new file mode 100644
index 0000000..5d55398
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/201-Broadcom-b44-wl.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2010-2013 OpenWrt.org
+
+define Profile/Broadcom-b44-wl
+  NAME:=Broadcom SoC, b44 Ethernet, BCM43xx WiFi (wl, proprietary)
+  PACKAGES:=-wpad-basic-wolfssl kmod-b44 kmod-brcm-wl wlc nas
+endef
+
+define Profile/Broadcom-b44-wl/Description
+	Package set compatible with hardware older Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the proprietary Broadcom
+	wireless "wl" driver and b44 Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-b44-wl))
+
diff --git a/target/linux/bcm47xx/generic/profiles/204-Broadcom-b44-ath5k.mk b/target/linux/bcm47xx/generic/profiles/204-Broadcom-b44-ath5k.mk
new file mode 100644
index 0000000..fd9ee6f
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/204-Broadcom-b44-ath5k.mk
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+
+define Profile/Broadcom-b44-ath5k
+  NAME:=Broadcom SoC, b44 Ethernet, Atheros WiFi (ath5k)
+  PACKAGES:=kmod-b44 kmod-ath5k
+endef
+
+define Profile/Broadcom-b44-ath5k/Description
+	Package set compatible with hardware older Broadcom BCM47xx or BCM535x
+	SoC with Atheros Wifi cards using the mac80211 and ath5k drivers and
+	b44 Ethernet driver.
+endef
+$(eval $(call Profile,Broadcom-b44-ath5k))
+
diff --git a/target/linux/bcm47xx/generic/profiles/205-Broadcom-b44-none.mk b/target/linux/bcm47xx/generic/profiles/205-Broadcom-b44-none.mk
new file mode 100644
index 0000000..79a223a
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/205-Broadcom-b44-none.mk
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+
+define Profile/Broadcom-b44-none
+  NAME:=Broadcom SoC, b44 Ethernet, No WiFi
+  PACKAGES:=-wpad-basic-wolfssl kmod-b44
+endef
+
+define Profile/Broadcom-b44-none/Description
+	Package set compatible with hardware older Broadcom BCM47xx or BCM535x
+	SoC without any Wifi cards and b44 Ethernet driver.
+endef
+$(eval $(call Profile,Broadcom-b44-none))
+
diff --git a/target/linux/bcm47xx/generic/profiles/210-Broadcom-tg3-b43.mk b/target/linux/bcm47xx/generic/profiles/210-Broadcom-tg3-b43.mk
new file mode 100644
index 0000000..69c59c5
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/210-Broadcom-tg3-b43.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+
+define Profile/Broadcom-tg3-b43
+  NAME:=Broadcom SoC, tg3 Ethernet, BCM43xx WiFi (b43)
+  PACKAGES:=kmod-b43 kmod-tg3
+endef
+
+define Profile/Broadcom-tg3-b43/Description
+	Package set compatible with hardware Broadcom BCM4705/BCM4785
+	SoCs with Broadcom BCM43xx Wifi cards using the mac80211 and b43
+	driver and tg3 Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-tg3-b43))
+
diff --git a/target/linux/bcm47xx/generic/profiles/211-Broadcom-tg3-wl.mk b/target/linux/bcm47xx/generic/profiles/211-Broadcom-tg3-wl.mk
new file mode 100644
index 0000000..f594027
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/211-Broadcom-tg3-wl.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2010-2013 OpenWrt.org
+
+define Profile/Broadcom-tg3-wl
+  NAME:=Broadcom SoC, tg3 Ethernet, BCM43xx WiFi (wl, proprietary)
+  PACKAGES:=-wpad-basic-wolfssl kmod-brcm-wl wlc nas kmod-tg3
+endef
+
+define Profile/Broadcom-tg3-wl/Description
+	Package set compatible with hardware Broadcom BCM4705/BCM4785
+	SoC with Broadcom BCM43xx Wifi cards using the proprietary Broadcom
+	wireless "wl" driver and tg3 Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-tg3-wl))
+
diff --git a/target/linux/bcm47xx/generic/profiles/215-Broadcom-tg3-none.mk b/target/linux/bcm47xx/generic/profiles/215-Broadcom-tg3-none.mk
new file mode 100644
index 0000000..a75e49c
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/215-Broadcom-tg3-none.mk
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+
+define Profile/Broadcom-tg3-none
+  NAME:=Broadcom SoC, tg3 Ethernet, no WiFi
+  PACKAGES:=-wpad-basic-wolfssl kmod-tg3
+endef
+
+define Profile/Broadcom-tg3-none/Description
+	Package set compatible with hardware Broadcom BCM4705/BCM4785
+	SoC without any Wifi cards and tg3 Ethernet driver.
+endef
+$(eval $(call Profile,Broadcom-tg3-none))
+
diff --git a/target/linux/bcm47xx/generic/profiles/220-Broadcom-bgmac-b43.mk b/target/linux/bcm47xx/generic/profiles/220-Broadcom-bgmac-b43.mk
new file mode 100644
index 0000000..4d05421
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/220-Broadcom-bgmac-b43.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+
+define Profile/Broadcom-bgmac-b43
+  NAME:=Broadcom SoC, bgmac Ethernet, BCM43xx WiFi (b43)
+  PACKAGES:=kmod-bgmac kmod-b43
+endef
+
+define Profile/Broadcom-bgmac-b43/Description
+	Package set compatible with hardware newer Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the mac80211 and b43
+	drivers and bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-bgmac-b43))
+
diff --git a/target/linux/bcm47xx/generic/profiles/221-Broadcom-bgmac-wl.mk b/target/linux/bcm47xx/generic/profiles/221-Broadcom-bgmac-wl.mk
new file mode 100644
index 0000000..b426b99
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/221-Broadcom-bgmac-wl.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2010-2013 OpenWrt.org
+
+define Profile/Broadcom-bgmac-wl
+  NAME:=Broadcom SoC, bgmac Ethernet, BCM43xx WiFi (wl, proprietary)
+  PACKAGES:=-wpad-basic-wolfssl kmod-bgmac kmod-brcm-wl wlc nas
+endef
+
+define Profile/Broadcom-bgmac-wl/Description
+	Package set compatible with hardware newer Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the proprietary Broadcom
+	wireless "wl" driver and bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-bgmac-wl))
+
diff --git a/target/linux/bcm47xx/generic/profiles/225-Broadcom-bgmac-none.mk b/target/linux/bcm47xx/generic/profiles/225-Broadcom-bgmac-none.mk
new file mode 100644
index 0000000..8ce6755
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/225-Broadcom-bgmac-none.mk
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+
+define Profile/Broadcom-bgmac-none
+  NAME:=Broadcom SoC, bgmac Ethernet, No WiFi
+  PACKAGES:=-wpad-basic-wolfssl kmod-bgmac
+endef
+
+define Profile/Broadcom-bgmac-none/Description
+	Package set compatible with hardware newer Broadcom BCM47xx or BCM535x
+	SoC without any Wifi cards and bgmac Ethernet driver.
+endef
+$(eval $(call Profile,Broadcom-bgmac-none))
+
diff --git a/target/linux/bcm47xx/generic/profiles/226-Broadcom-bgmac-brcsmac.mk b/target/linux/bcm47xx/generic/profiles/226-Broadcom-bgmac-brcsmac.mk
new file mode 100644
index 0000000..93a76dc
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/226-Broadcom-bgmac-brcsmac.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+
+define Profile/Broadcom-bgmac-brcmsmac
+  NAME:=Broadcom SoC, bgmac Ethernet, BCM43xx WiFi (brcmsmac)
+  PACKAGES:=kmod-bgmac kmod-brcmsmac
+endef
+
+define Profile/Broadcom-bgmac-brcmsmac/Description
+	Package set compatable with newer gigabit + N based bcm47xx SoCs with
+	Broadcom BCM43xx Wifi cards using the mac80211 brcmsmac driver and
+	bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-bgmac-brcmsmac))
+
diff --git a/target/linux/bcm47xx/generic/profiles/PS-1208MFG.mk b/target/linux/bcm47xx/generic/profiles/PS-1208MFG.mk
new file mode 100644
index 0000000..88b4178
--- /dev/null
+++ b/target/linux/bcm47xx/generic/profiles/PS-1208MFG.mk
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2010 OpenWrt.org
+
+define Profile/Ps1208mfg
+  NAME:=Edimax PS-1208MFG
+  PACKAGES:=-firewall -dropbear -dnsmasq -mtd -ppp -wpad-basic-wolfssl kmod-b44 block-mount kmod-usb-storage kmod-usb2 kmod-usb-ohci -iptables -swconfig kmod-fs-ext4
+endef
+
+define Profile/Ps1208mfg/Description
+	Package set optimize for edimax PS-1208MFG printserver
+endef
+
+$(eval $(call Profile,Ps1208mfg))
+
diff --git a/target/linux/bcm47xx/generic/target.mk b/target/linux/bcm47xx/generic/target.mk
new file mode 100644
index 0000000..70397de
--- /dev/null
+++ b/target/linux/bcm47xx/generic/target.mk
@@ -0,0 +1,9 @@
+BOARDNAME:=Generic
+FEATURES+=pcmcia
+
+DEFAULT_PACKAGES += wpad-basic-wolfssl
+
+define Target/Description
+	Build generic firmware for all Broadcom BCM47xx and BCM53xx MIPS
+	devices. It runs on both architectures BMIPS3300 and MIPS 74K.
+endef
diff --git a/target/linux/bcm47xx/image/Makefile b/target/linux/bcm47xx/image/Makefile
new file mode 100644
index 0000000..2aeef17
--- /dev/null
+++ b/target/linux/bcm47xx/image/Makefile
@@ -0,0 +1,218 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2016 OpenWrt.org
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/image.mk
+
+USB1_PACKAGES := kmod-usb-ohci
+USB2_PACKAGES := $(USB1_PACKAGES) kmod-usb2
+
+define Build/Clean
+	$(MAKE) -C lzma-loader clean
+endef
+
+define Image/Prepare
+	# Optimized LZMA compression (with dictionary), handled by lzma-loader.
+	cat $(KDIR)/vmlinux | $(STAGING_DIR_HOST)/bin/lzma e -si -so -eos -lc1 -lp2 -pb2 > $(KDIR)/vmlinux.lzma
+
+	# Less optimal LZMA compression (no dictionary), handled by CFE.
+	$(STAGING_DIR_HOST)/bin/lzma e -so -d16 $(KDIR)/vmlinux > $(KDIR)/vmlinux-nodictionary.lzma
+
+	gzip -nc9 $(KDIR)/vmlinux > $(KDIR)/vmlinux.gz
+ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
+	cat $(KDIR)/vmlinux-initramfs | $(STAGING_DIR_HOST)/bin/lzma e -si -so -eos -lc1 -lp2 -pb2 > $(KDIR)/vmlinux-initramfs.lzma
+	$(STAGING_DIR_HOST)/bin/lzma e -so -d16 $(KDIR)/vmlinux-initramfs > $(KDIR)/vmlinux-initramfs-nodictionary.lzma
+endif
+	rm -f $(KDIR)/loader.gz
+	$(MAKE) -C lzma-loader \
+		BUILD_DIR="$(KDIR)" \
+		TARGET="$(KDIR)" \
+		clean install
+	echo -ne "\\x00" >> $(KDIR)/loader.gz
+	rm -f $(KDIR)/fs_mark
+	echo -ne '\xde\xad\xc0\xde' > $(KDIR)/fs_mark
+	$(call prepare_generic_squashfs,$(KDIR)/fs_mark)
+endef
+
+define trxalign/jffs2-128k
+-a 0x20000 -f $(KDIR)/root.$(1)
+endef
+define trxalign/jffs2-64k
+-a 0x10000 -f $(KDIR)/root.$(1)
+endef
+define trxalign/squashfs
+-a 1024 -f $(1) $(if $(2),-f $(2)) -a 0x10000 -A $(KDIR)/fs_mark
+endef
+
+#################################################
+# Images
+#################################################
+
+define Build/trx-with-loader
+	$(STAGING_DIR_HOST)/bin/trx \
+		-m 33554432 \
+		-o $@.new \
+		-f $(KDIR)/loader.gz \
+		-f $(IMAGE_KERNEL) \
+		$(call trxalign/$(FILESYSTEM),$@)
+	mv $@.new $@
+endef
+
+define Build/trx-v2-with-loader
+	$(STAGING_DIR_HOST)/bin/trx \
+		-2 \
+		-m 33554432 \
+		-o $@.new \
+		-f $(KDIR)/loader.gz \
+		-f $(KDIR)/vmlinux.lzma \
+		$(call trxalign/$(FILESYSTEM),$@,$@.pattern)
+	mv $@.new $@
+endef
+
+define Build/trx-without-loader
+	$(STAGING_DIR_HOST)/bin/trx \
+		-m 33554432 \
+		-o $@.new \
+		-f $(IMAGE_KERNEL) \
+		$(call trxalign/$(FILESYSTEM),$@)
+	mv $@.new $@
+endef
+
+define Build/asus-trx
+	$(STAGING_DIR_HOST)/bin/asustrx -p $(PRODUCTID) -i $@ -o $@.new
+	mv $@.new $@
+endef
+
+define Build/edimax-bin
+	$(STAGING_DIR_HOST)/bin/trx2edips $@ $@.new
+	mv $@.new $@
+endef
+
+define Build/huawei-bin
+	dd if=/dev/zero of=$@.new bs=92 count=1
+	echo -ne 'HDR0\x08\x00\x00\x00' >> $@.new
+	cat $@ >> $@.new
+	mv $@.new $@
+endef
+
+define Build/linksys-bin
+	$(STAGING_DIR_HOST)/bin/addpattern -4 -p $(DEVICE_ID) -v v$(VERSION) $(if $(SERIAL),-s $(SERIAL)) -i $@ -o $@.new
+	mv $@.new $@
+endef
+
+define Build/linksys-pattern-partition
+	$(STAGING_DIR_HOST)/bin/addpattern -5 -p $(DEVICE_ID) -v v$(VERSION) $(if $(SERIAL),-s $(SERIAL)) -i /dev/null -o $@.pattern
+endef
+
+define Build/motorola-bin
+	$(STAGING_DIR_HOST)/bin/motorola-bin -$(MOTOROLA_DEVICE) $@ $@.new
+	mv $@.new $@
+endef
+
+define Build/prepend-with-elf
+	mv $@ $@.old
+	dd if=$(KDIR)/loader.elf of=$@ bs=131072 conv=sync
+	cat $@.old >> $@
+endef
+
+define Build/tailed-bin
+	echo $(BIN_TAIL) >> $@
+endef
+
+define Build/usrobotics-bin
+	$(STAGING_DIR_HOST)/bin/trx2usr $@ $@.new
+	mv $@.new $@
+endef
+
+#################################################
+# Devices
+#################################################
+
+DEVICE_VARS += PRODUCTID
+DEVICE_VARS += DEVICE_ID VERSION SERIAL
+DEVICE_VARS += NETGEAR_BOARD_ID NETGEAR_REGION
+DEVICE_VARS += MOTOROLA_DEVICE
+DEVICE_VARS += BIN_TAIL
+
+define Device/Default
+	KERNEL := kernel-bin
+	IMAGE_NAME = $$(IMAGE_PREFIX)-$$(1).$$(2)
+	KERNEL_NAME = vmlinux.lzma
+	KERNEL_INITRAMFS_NAME = vmlinux-initramfs.lzma
+	FILESYSTEMS := $(FS_64K)
+	IMAGES := trx
+	IMAGE/trx := append-rootfs | trx-with-loader
+endef
+
+define Device/standard
+  DEVICE_TITLE := Image with LZMA loader and LZMA compressed kernel
+endef
+
+define Device/standard-noloader-gz
+  DEVICE_TITLE := Image with gzipped kernel
+  KERNEL_NAME = vmlinux.gz
+  IMAGE/trx := append-rootfs | trx-without-loader
+endef
+
+define Device/standard-noloader-nodictionarylzma
+  DEVICE_TITLE := Image with LZMA compressed kernel matching CFE decompressor
+  KERNEL_NAME = vmlinux-nodictionary.lzma
+  IMAGE/trx := append-rootfs | trx-without-loader
+endef
+
+define Device/asus
+	DEVICE_VENDOR := ASUS
+	IMAGES := trx
+	IMAGE/trx := append-rootfs | trx-with-loader | asus-trx
+endef
+
+define Device/linksys
+	DEVICE_VENDOR := Linksys
+	IMAGES := bin
+	IMAGE/bin := append-rootfs | trx-with-loader | linksys-bin
+endef
+
+define Device/motorola
+	DEVICE_VENDOR := Motorola
+	IMAGES := bin
+	IMAGE/bin := append-rootfs | trx-with-loader | motorola-bin
+endef
+
+define Device/netgear
+	DEVICE_VENDOR := NETGEAR
+	IMAGES := chk
+	IMAGE/chk := append-rootfs | trx-with-loader | netgear-chk
+endef
+
+#################################################
+# Subtarget devices
+#################################################
+
+include $(SUBTARGET).mk
+
+#################################################
+# Shared BuildImage defines
+#################################################
+
+define Image/Build/Initramfs
+	$(STAGING_DIR_HOST)/bin/trx \
+		-m 33554432 \
+		-o $(BIN_DIR)/$(IMG_PREFIX)-initramfs.trx \
+		-f $(KDIR)/loader.gz \
+		-f $(KDIR)/vmlinux-initramfs.lzma
+	$(STAGING_DIR_HOST)/bin/trx \
+		-m 33554432 \
+		-o $(BIN_DIR)/$(IMG_PREFIX)-initramfs-noloader-nodictionary.trx \
+		-f $(KDIR)/vmlinux-initramfs-nodictionary.lzma
+endef
+
+# $(1): filesystem type.
+define Image/Build
+	# TODO: Move it to Device/*
+ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
+	$(call Image/Build/Initramfs)
+endif
+endef
+
+$(eval $(call BuildImage))
diff --git a/target/linux/bcm47xx/image/generic.mk b/target/linux/bcm47xx/image/generic.mk
new file mode 100644
index 0000000..61204bf
--- /dev/null
+++ b/target/linux/bcm47xx/image/generic.mk
@@ -0,0 +1,72 @@
+#################################################
+# Subtarget generic
+#################################################
+
+  # BCM4705 with tg3
+define Device/linksys_wrt300n-v1.1
+  DEVICE_MODEL := WRT300N
+  DEVICE_VARIANT := v1.1
+  DEVICE_PACKAGES := kmod-tg3 kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := EWC2
+  VERSION := 1.51.2
+endef
+TARGET_DEVICES += linksys_wrt300n-v1.1
+
+define Device/linksys_wrt310n-v1
+  DEVICE_MODEL := WRT310N
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-tg3 kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 310N
+  VERSION := 1.0.10
+endef
+TARGET_DEVICES += linksys_wrt310n-v1
+
+define Device/linksys_wrt350n-v1
+  DEVICE_MODEL := WRT350N
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-tg3 kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  DEVICE_ID := EWCG
+  VERSION := 1.04.1
+endef
+TARGET_DEVICES += linksys_wrt350n-v1
+
+define Device/linksys_wrt610n-v1
+  DEVICE_MODEL := WRT610N
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-tg3 kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  DEVICE_ID := 610N
+  VERSION := 1.0.1
+endef
+TARGET_DEVICES += linksys_wrt610n-v1
+
+  # BCMA SoC with SSB WiFi
+define Device/linksys_wrt610n-v2
+  DEVICE_MODEL := WRT610N
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-bgmac kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  DEVICE_ID := 610N
+  VERSION := 2.0.0
+endef
+TARGET_DEVICES += linksys_wrt610n-v2
+
+define Device/linksys_e3000-v1
+  DEVICE_MODEL := E3000
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-bgmac kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  DEVICE_ID := 61XN
+  VERSION := 1.0.3
+endef
+TARGET_DEVICES += linksys_e3000-v1
+
+# generic has Ethernet drivers as modules so overwrite standard image
+define Device/standard
+  DEVICE_TITLE := Image with LZMA loader and LZMA compressed kernel
+  DEVICE_PACKAGES := kmod-b44 kmod-bgmac kmod-tg3
+endef
+TARGET_DEVICES += standard
diff --git a/target/linux/bcm47xx/image/legacy.mk b/target/linux/bcm47xx/image/legacy.mk
new file mode 100644
index 0000000..4538813
--- /dev/null
+++ b/target/linux/bcm47xx/image/legacy.mk
@@ -0,0 +1,292 @@
+#################################################
+# Subtarget legacy
+#################################################
+
+define Device/asus_wl-300g
+  DEVICE_MODEL := WL-300g
+  DEVICE_PACKAGES := kmod-b43 kmod-b43legacy
+  $(Device/asus)
+  PRODUCTID := "WL300g      "
+endef
+TARGET_DEVICES += asus_wl-300g
+
+define Device/asus_wl-320gp
+  DEVICE_MODEL := WL-320gP
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := "WL320gP     "
+endef
+TARGET_DEVICES += asus_wl-320gp
+
+define Device/asus_wl-330ge
+  DEVICE_MODEL := WL-330gE
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := "WL-330gE    "
+endef
+TARGET_DEVICES += asus_wl-330ge
+
+define Device/asus_wl-500gd
+  DEVICE_MODEL := WL-500g Deluxe
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := "WL500gx     "
+endef
+TARGET_DEVICES += asus_wl-500gd
+
+define Device/asus_wl-500gp-v1
+  DEVICE_MODEL := WL-500gP
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := "WL500gp     "
+endef
+TARGET_DEVICES += asus_wl-500gp-v1
+
+define Device/asus_wl-500gp-v2
+  DEVICE_MODEL := WL-500gP
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := "WL500gpv2   "
+endef
+TARGET_DEVICES += asus_wl-500gp-v2
+
+define Device/asus_wl-500w
+  DEVICE_MODEL := WL-500W
+  DEVICE_PACKAGES := kmod-b43 kmod-usb-uhci kmod-usb2-pci
+  $(Device/asus)
+  PRODUCTID := "WL500W      "
+endef
+TARGET_DEVICES += asus_wl-500w
+
+define Device/asus_wl-520gu
+  DEVICE_MODEL := WL-520gU
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := "WL520gu     "
+endef
+TARGET_DEVICES += asus_wl-520gu
+
+define Device/asus_wl-550ge
+  DEVICE_MODEL := WL-550gE
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := "WL550gE     "
+endef
+TARGET_DEVICES += asus_wl-550ge
+
+define Device/asus_wl-hdd25
+  DEVICE_MODEL := WL-HDD25
+  DEVICE_PACKAGES := kmod-b43 kmod-b43legacy $(USB1_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := "WLHDD       "
+endef
+TARGET_DEVICES += asus_wl-hdd25
+
+define Device/dlink_dwl-3150
+  DEVICE_VENDOR := D-Link
+  DEVICE_MODEL := DWL-3150
+  IMAGES := bin
+  IMAGE/bin := append-rootfs | trx-with-loader | tailed-bin
+  BIN_TAIL := BCM-5352-2050-0000000-01
+endef
+TARGET_DEVICES += dlink_dwl-3150
+
+define Device/edimax_ps1208-mfg
+  DEVICE_VENDOR := Edimax
+  DEVICE_MODEL := PS-1208MFg
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  IMAGES := bin
+  IMAGE/bin := append-rootfs | trx-with-loader | edimax-bin
+endef
+TARGET_DEVICES += edimax_ps1208-mfg
+
+define Device/huawei_e970
+  DEVICE_VENDOR := Huawei
+  DEVICE_MODEL := E970
+  DEVICE_PACKAGES := kmod-b43
+  KERNEL_NAME = vmlinux.gz
+  IMAGES := bin
+  IMAGE/bin := append-rootfs | trx-without-loader | huawei-bin
+endef
+TARGET_DEVICES += huawei_e970
+
+define Device/linksys_wrt54g3g
+  DEVICE_MODEL := WRT54G3G
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := W54F
+  VERSION := 2.20.1
+endef
+TARGET_DEVICES += linksys_wrt54g3g
+
+define Device/linksys_wrt54g3g-em
+  DEVICE_MODEL := WRT54G3G-EM
+  $(Device/linksys)
+  DEVICE_ID := W3GN
+  VERSION := 2.20.1
+endef
+TARGET_DEVICES += linksys_wrt54g3g-em
+
+define Device/linksys_wrt54g3gv2-vf
+  DEVICE_VENDOR := Linksys
+  DEVICE_MODEL := WRT54G3GV2-VF
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  FILESYSTEMS := $(FS_128K)
+  IMAGES := noheader.bin bin
+  IMAGE/noheader.bin := linksys-pattern-partition | append-rootfs | trx-v2-with-loader
+  IMAGE/bin := linksys-pattern-partition | append-rootfs | trx-v2-with-loader | linksys-bin
+  DEVICE_ID := 3G2V
+  VERSION := 3.00.24
+  SERIAL := 6
+endef
+TARGET_DEVICES += linksys_wrt54g3gv2-vf
+
+define Device/linksys_wrt54g
+  DEVICE_MODEL := WRT54G
+  DEVICE_PACKAGES := kmod-b43 kmod-b43legacy
+  $(Device/linksys)
+  DEVICE_ID := W54G
+  VERSION := 4.71.1
+endef
+TARGET_DEVICES += linksys_wrt54g
+
+define Device/linksys_wrt54gs
+  DEVICE_MODEL := WRT54GS
+  DEVICE_VARIANT := v1/v2/v3
+  DEVICE_ALT0_VENDOR := Linksys
+  DEVICE_ALT0_MODEL := WRT54G-TM
+  DEVICE_ALT0_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  FILESYSTEMS := $(FS_128K)
+  DEVICE_ID := W54S
+  VERSION := 4.80.1
+endef
+TARGET_DEVICES += linksys_wrt54gs
+
+define Device/linksys_wrt54gs-v4
+  DEVICE_MODEL := WRT54GS
+  DEVICE_VARIANT := v4
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := W54s
+  VERSION := 1.09.1
+endef
+TARGET_DEVICES += linksys_wrt54gs-v4
+
+define Device/linksys_wrtsl54gs
+  DEVICE_MODEL := WRTSL54GS
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  FILESYSTEMS := $(FS_128K)
+  DEVICE_ID := W54U
+  VERSION := 2.08.1
+endef
+TARGET_DEVICES += linksys_wrtsl54gs
+
+define Device/linksys_wrt150n
+  DEVICE_MODEL := WRT150N
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := N150
+  VERSION := 1.51.3
+endef
+TARGET_DEVICES += linksys_wrt150n
+
+define Device/linksys_wrt160n-v1
+  DEVICE_MODEL := WRT160N
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := N150
+  VERSION := 1.50.1
+endef
+TARGET_DEVICES += linksys_wrt160n-v1
+
+define Device/linksys_wrt300n-v1
+  DEVICE_MODEL := WRT300N
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  IMAGES := bin trx
+  DEVICE_ID := EWCB
+  VERSION := 1.03.6
+endef
+TARGET_DEVICES += linksys_wrt300n-v1
+
+define Device/motorola_wa840g
+  DEVICE_MODEL := WA840G
+  DEVICE_PACKAGES := kmod-b43 kmod-b43legacy
+  $(Device/motorola)
+  MOTOROLA_DEVICE := 2
+endef
+TARGET_DEVICES += motorola_wa840g
+
+define Device/motorola_we800g
+  DEVICE_MODEL := WE800G
+  DEVICE_PACKAGES := kmod-b43 kmod-b43legacy
+  $(Device/motorola)
+  MOTOROLA_DEVICE := 3
+endef
+TARGET_DEVICES += motorola_we800g
+
+define Device/motorola_wr850g
+  DEVICE_MODEL := WR850G
+  DEVICE_PACKAGES := kmod-b43 kmod-b43legacy
+  $(Device/motorola)
+  MOTOROLA_DEVICE := 1
+endef
+TARGET_DEVICES += motorola_wr850g
+
+define Device/netgear_wgr614-v8
+  DEVICE_MODEL := WGR614
+  DEVICE_VARIANT := v8
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H072T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wgr614-v8
+
+define Device/netgear_wgt634u
+  DEVICE_VENDOR := NETGEAR
+  DEVICE_MODEL := WGT634U
+  DEVICE_PACKAGES := kmod-ath5k $(USB2_PACKAGES)
+  FILESYSTEMS := $(FS_128K)
+  IMAGES := bin
+  IMAGE/bin := append-rootfs | trx-with-loader | prepend-with-elf
+endef
+TARGET_DEVICES += netgear_wgt634u
+
+define Device/netgear_wndr3300-v1
+  DEVICE_MODEL := WNDR3300
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H093T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wndr3300-v1
+
+define Device/netgear_wnr834b-v2
+  DEVICE_MODEL := WNR834B
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H081T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wnr834b-v2
+
+define Device/usrobotics_usr5461
+  DEVICE_VENDOR := US Robotics
+  DEVICE_MODEL := USR5461
+  DEVICE_PACKAGES := kmod-b43 $(USB1_PACKAGES)
+  IMAGES := bin
+  IMAGE/bin := append-rootfs | trx-with-loader | usrobotics-bin
+endef
+TARGET_DEVICES += usrobotics_usr5461
+
+TARGET_DEVICES += standard standard-noloader-gz
diff --git a/target/linux/bcm47xx/image/lzma-loader/Makefile b/target/linux/bcm47xx/image/lzma-loader/Makefile
new file mode 100644
index 0000000..5dd6f50
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/Makefile
@@ -0,0 +1,33 @@
+# 
+# Copyright (C) 2006 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME := lzma-loader
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+$(PKG_BUILD_DIR)/.prepared:
+	mkdir $(PKG_BUILD_DIR)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+	touch $@
+
+$(PKG_BUILD_DIR)/loader.gz: $(PKG_BUILD_DIR)/.prepared
+	$(MAKE) -C $(PKG_BUILD_DIR) CC="$(TARGET_CC)" \
+		LD="$(TARGET_CROSS)ld" CROSS_COMPILE="$(TARGET_CROSS)"
+
+download: 
+prepare: $(PKG_BUILD_DIR)/.prepared
+compile: $(PKG_BUILD_DIR)/loader.gz
+install:
+
+ifneq ($(TARGET),)
+install: compile
+	$(CP) $(PKG_BUILD_DIR)/loader.gz $(PKG_BUILD_DIR)/loader.elf $(TARGET)/
+endif
+
+clean:
+	rm -rf $(PKG_BUILD_DIR)
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/LzmaDecode.c b/target/linux/bcm47xx/image/lzma-loader/src/LzmaDecode.c
new file mode 100644
index 0000000..951700b
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/LzmaDecode.c
@@ -0,0 +1,663 @@
+/*
+  LzmaDecode.c
+  LZMA Decoder
+  
+  LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25)
+  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"
+
+#ifndef Byte
+#define Byte unsigned char
+#endif
+
+#define kNumTopBits 24
+#define kTopValue ((UInt32)1 << kNumTopBits)
+
+#define kNumBitModelTotalBits 11
+#define kBitModelTotal (1 << kNumBitModelTotalBits)
+#define kNumMoveBits 5
+
+typedef struct _CRangeDecoder
+{
+  Byte *Buffer;
+  Byte *BufferLim;
+  UInt32 Range;
+  UInt32 Code;
+  #ifdef _LZMA_IN_CB
+  ILzmaInCallback *InCallback;
+  int Result;
+  #endif
+  int ExtraBytes;
+} CRangeDecoder;
+
+Byte RangeDecoderReadByte(CRangeDecoder *rd)
+{
+  if (rd->Buffer == rd->BufferLim)
+  {
+    #ifdef _LZMA_IN_CB
+    UInt32 size;
+    rd->Result = rd->InCallback->Read(rd->InCallback, &rd->Buffer, &size);
+    rd->BufferLim = rd->Buffer + size;
+    if (size == 0)
+    #endif
+    {
+      rd->ExtraBytes = 1;
+      return 0xFF;
+    }
+  }
+  return (*rd->Buffer++);
+}
+
+/* #define ReadByte (*rd->Buffer++) */
+#define ReadByte (RangeDecoderReadByte(rd))
+
+void RangeDecoderInit(CRangeDecoder *rd,
+  #ifdef _LZMA_IN_CB
+    ILzmaInCallback *inCallback
+  #else
+    Byte *stream, UInt32 bufferSize
+  #endif
+    )
+{
+  int i;
+  #ifdef _LZMA_IN_CB
+  rd->InCallback = inCallback;
+  rd->Buffer = rd->BufferLim = 0;
+  #else
+  rd->Buffer = stream;
+  rd->BufferLim = stream + bufferSize;
+  #endif
+  rd->ExtraBytes = 0;
+  rd->Code = 0;
+  rd->Range = (0xFFFFFFFF);
+  for(i = 0; i < 5; i++)
+    rd->Code = (rd->Code << 8) | ReadByte;
+}
+
+#define RC_INIT_VAR UInt32 range = rd->Range; UInt32 code = rd->Code;        
+#define RC_FLUSH_VAR rd->Range = range; rd->Code = code;
+#define RC_NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | ReadByte; }
+
+UInt32 RangeDecoderDecodeDirectBits(CRangeDecoder *rd, int numTotalBits)
+{
+  RC_INIT_VAR
+  UInt32 result = 0;
+  int i;
+  for (i = numTotalBits; i > 0; i--)
+  {
+    /* UInt32 t; */
+    range >>= 1;
+
+    result <<= 1;
+    if (code >= range)
+    {
+      code -= range;
+      result |= 1;
+    }
+    /*
+    t = (code - range) >> 31;
+    t &= 1;
+    code -= range & (t - 1);
+    result = (result + result) | (1 - t);
+    */
+    RC_NORMALIZE
+  }
+  RC_FLUSH_VAR
+  return result;
+}
+
+int RangeDecoderBitDecode(CProb *prob, CRangeDecoder *rd)
+{
+  UInt32 bound = (rd->Range >> kNumBitModelTotalBits) * *prob;
+  if (rd->Code < bound)
+  {
+    rd->Range = bound;
+    *prob += (kBitModelTotal - *prob) >> kNumMoveBits;
+    if (rd->Range < kTopValue)
+    {
+      rd->Code = (rd->Code << 8) | ReadByte;
+      rd->Range <<= 8;
+    }
+    return 0;
+  }
+  else
+  {
+    rd->Range -= bound;
+    rd->Code -= bound;
+    *prob -= (*prob) >> kNumMoveBits;
+    if (rd->Range < kTopValue)
+    {
+      rd->Code = (rd->Code << 8) | ReadByte;
+      rd->Range <<= 8;
+    }
+    return 1;
+  }
+}
+
+#define RC_GET_BIT2(prob, mi, A0, A1) \
+  UInt32 bound = (range >> kNumBitModelTotalBits) * *prob; \
+  if (code < bound) \
+    { A0; range = bound; *prob += (kBitModelTotal - *prob) >> kNumMoveBits; mi <<= 1; } \
+  else \
+    { A1; range -= bound; code -= bound; *prob -= (*prob) >> kNumMoveBits; mi = (mi + mi) + 1; } \
+  RC_NORMALIZE
+
+#define RC_GET_BIT(prob, mi) RC_GET_BIT2(prob, mi, ; , ;)               
+
+int RangeDecoderBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd)
+{
+  int mi = 1;
+  int i;
+  #ifdef _LZMA_LOC_OPT
+  RC_INIT_VAR
+  #endif
+  for(i = numLevels; i > 0; i--)
+  {
+    #ifdef _LZMA_LOC_OPT
+    CProb *prob = probs + mi;
+    RC_GET_BIT(prob, mi)
+    #else
+    mi = (mi + mi) + RangeDecoderBitDecode(probs + mi, rd);
+    #endif
+  }
+  #ifdef _LZMA_LOC_OPT
+  RC_FLUSH_VAR
+  #endif
+  return mi - (1 << numLevels);
+}
+
+int RangeDecoderReverseBitTreeDecode(CProb *probs, int numLevels, CRangeDecoder *rd)
+{
+  int mi = 1;
+  int i;
+  int symbol = 0;
+  #ifdef _LZMA_LOC_OPT
+  RC_INIT_VAR
+  #endif
+  for(i = 0; i < numLevels; i++)
+  {
+    #ifdef _LZMA_LOC_OPT
+    CProb *prob = probs + mi;
+    RC_GET_BIT2(prob, mi, ; , symbol |= (1 << i))
+    #else
+    int bit = RangeDecoderBitDecode(probs + mi, rd);
+    mi = mi + mi + bit;
+    symbol |= (bit << i);
+    #endif
+  }
+  #ifdef _LZMA_LOC_OPT
+  RC_FLUSH_VAR
+  #endif
+  return symbol;
+}
+
+Byte LzmaLiteralDecode(CProb *probs, CRangeDecoder *rd)
+{ 
+  int symbol = 1;
+  #ifdef _LZMA_LOC_OPT
+  RC_INIT_VAR
+  #endif
+  do
+  {
+    #ifdef _LZMA_LOC_OPT
+    CProb *prob = probs + symbol;
+    RC_GET_BIT(prob, symbol)
+    #else
+    symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd);
+    #endif
+  }
+  while (symbol < 0x100);
+  #ifdef _LZMA_LOC_OPT
+  RC_FLUSH_VAR
+  #endif
+  return symbol;
+}
+
+Byte LzmaLiteralDecodeMatch(CProb *probs, CRangeDecoder *rd, Byte matchByte)
+{ 
+  int symbol = 1;
+  #ifdef _LZMA_LOC_OPT
+  RC_INIT_VAR
+  #endif
+  do
+  {
+    int bit;
+    int matchBit = (matchByte >> 7) & 1;
+    matchByte <<= 1;
+    #ifdef _LZMA_LOC_OPT
+    {
+      CProb *prob = probs + ((1 + matchBit) << 8) + symbol;
+      RC_GET_BIT2(prob, symbol, bit = 0, bit = 1)
+    }
+    #else
+    bit = RangeDecoderBitDecode(probs + ((1 + matchBit) << 8) + symbol, rd);
+    symbol = (symbol << 1) | bit;
+    #endif
+    if (matchBit != bit)
+    {
+      while (symbol < 0x100)
+      {
+        #ifdef _LZMA_LOC_OPT
+        CProb *prob = probs + symbol;
+        RC_GET_BIT(prob, symbol)
+        #else
+        symbol = (symbol + symbol) | RangeDecoderBitDecode(probs + symbol, rd);
+        #endif
+      }
+      break;
+    }
+  }
+  while (symbol < 0x100);
+  #ifdef _LZMA_LOC_OPT
+  RC_FLUSH_VAR
+  #endif
+  return symbol;
+}
+
+#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) 
+
+int LzmaLenDecode(CProb *p, CRangeDecoder *rd, int posState)
+{
+  if(RangeDecoderBitDecode(p + LenChoice, rd) == 0)
+    return RangeDecoderBitTreeDecode(p + LenLow +
+        (posState << kLenNumLowBits), kLenNumLowBits, rd);
+  if(RangeDecoderBitDecode(p + LenChoice2, rd) == 0)
+    return kLenNumLowSymbols + RangeDecoderBitTreeDecode(p + LenMid +
+        (posState << kLenNumMidBits), kLenNumMidBits, rd);
+  return kLenNumLowSymbols + kLenNumMidSymbols + 
+      RangeDecoderBitTreeDecode(p + LenHigh, kLenNumHighBits, rd);
+}
+
+#define kNumStates 12
+
+#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
+
+#ifdef _LZMA_OUT_READ
+
+typedef struct _LzmaVarState
+{
+  CRangeDecoder RangeDecoder;
+  Byte *Dictionary;
+  UInt32 DictionarySize;
+  UInt32 DictionaryPos;
+  UInt32 GlobalPos;
+  UInt32 Reps[4];
+  int lc;
+  int lp;
+  int pb;
+  int State;
+  int PreviousIsMatch;
+  int RemainLen;
+} LzmaVarState;
+
+int LzmaDecoderInit(
+    unsigned char *buffer, UInt32 bufferSize,
+    int lc, int lp, int pb,
+    unsigned char *dictionary, UInt32 dictionarySize,
+    #ifdef _LZMA_IN_CB
+    ILzmaInCallback *inCallback
+    #else
+    unsigned char *inStream, UInt32 inSize
+    #endif
+    )
+{
+  LzmaVarState *vs = (LzmaVarState *)buffer;
+  CProb *p = (CProb *)(buffer + sizeof(LzmaVarState));
+  UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp));
+  UInt32 i;
+  if (bufferSize < numProbs * sizeof(CProb) + sizeof(LzmaVarState))
+    return LZMA_RESULT_NOT_ENOUGH_MEM;
+  vs->Dictionary = dictionary;
+  vs->DictionarySize = dictionarySize;
+  vs->DictionaryPos = 0;
+  vs->GlobalPos = 0;
+  vs->Reps[0] = vs->Reps[1] = vs->Reps[2] = vs->Reps[3] = 1;
+  vs->lc = lc;
+  vs->lp = lp;
+  vs->pb = pb;
+  vs->State = 0;
+  vs->PreviousIsMatch = 0;
+  vs->RemainLen = 0;
+  dictionary[dictionarySize - 1] = 0;
+  for (i = 0; i < numProbs; i++)
+    p[i] = kBitModelTotal >> 1; 
+  RangeDecoderInit(&vs->RangeDecoder, 
+      #ifdef _LZMA_IN_CB
+      inCallback
+      #else
+      inStream, inSize
+      #endif
+  );
+  return LZMA_RESULT_OK;
+}
+
+int LzmaDecode(unsigned char *buffer, 
+    unsigned char *outStream, UInt32 outSize,
+    UInt32 *outSizeProcessed)
+{
+  LzmaVarState *vs = (LzmaVarState *)buffer;
+  CProb *p = (CProb *)(buffer + sizeof(LzmaVarState));
+  CRangeDecoder rd = vs->RangeDecoder;
+  int state = vs->State;
+  int previousIsMatch = vs->PreviousIsMatch;
+  Byte previousByte;
+  UInt32 rep0 = vs->Reps[0], rep1 = vs->Reps[1], rep2 = vs->Reps[2], rep3 = vs->Reps[3];
+  UInt32 nowPos = 0;
+  UInt32 posStateMask = (1 << (vs->pb)) - 1;
+  UInt32 literalPosMask = (1 << (vs->lp)) - 1;
+  int lc = vs->lc;
+  int len = vs->RemainLen;
+  UInt32 globalPos = vs->GlobalPos;
+
+  Byte *dictionary = vs->Dictionary;
+  UInt32 dictionarySize = vs->DictionarySize;
+  UInt32 dictionaryPos = vs->DictionaryPos;
+
+  if (len == -1)
+  {
+    *outSizeProcessed = 0;
+    return LZMA_RESULT_OK;
+  }
+
+  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
+
+int LzmaDecode(
+    Byte *buffer, UInt32 bufferSize,
+    int lc, int lp, int pb,
+    #ifdef _LZMA_IN_CB
+    ILzmaInCallback *inCallback,
+    #else
+    unsigned char *inStream, UInt32 inSize,
+    #endif
+    unsigned char *outStream, UInt32 outSize,
+    UInt32 *outSizeProcessed)
+{
+  UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (lc + lp));
+  CProb *p = (CProb *)buffer;
+  CRangeDecoder rd;
+  UInt32 i;
+  int state = 0;
+  int previousIsMatch = 0;
+  Byte previousByte = 0;
+  UInt32 rep0 = 1, rep1 = 1, rep2 = 1, rep3 = 1;
+  UInt32 nowPos = 0;
+  UInt32 posStateMask = (1 << pb) - 1;
+  UInt32 literalPosMask = (1 << lp) - 1;
+  int len = 0;
+  if (bufferSize < numProbs * sizeof(CProb))
+    return LZMA_RESULT_NOT_ENOUGH_MEM;
+  for (i = 0; i < numProbs; i++)
+    p[i] = kBitModelTotal >> 1; 
+  RangeDecoderInit(&rd, 
+      #ifdef _LZMA_IN_CB
+      inCallback
+      #else
+      inStream, inSize
+      #endif
+      );
+#endif
+
+  *outSizeProcessed = 0;
+  while(nowPos < outSize)
+  {
+    int posState = (int)(
+        (nowPos 
+        #ifdef _LZMA_OUT_READ
+        + globalPos
+        #endif
+        )
+        & posStateMask);
+    #ifdef _LZMA_IN_CB
+    if (rd.Result != LZMA_RESULT_OK)
+      return rd.Result;
+    #endif
+    if (rd.ExtraBytes != 0)
+      return LZMA_RESULT_DATA_ERROR;
+    if (RangeDecoderBitDecode(p + IsMatch + (state << kNumPosBitsMax) + posState, &rd) == 0)
+    {
+      CProb *probs = p + Literal + (LZMA_LIT_SIZE * 
+        (((
+        (nowPos 
+        #ifdef _LZMA_OUT_READ
+        + globalPos
+        #endif
+        )
+        & literalPosMask) << lc) + (previousByte >> (8 - lc))));
+
+      if (state < 4) state = 0;
+      else if (state < 10) state -= 3;
+      else state -= 6;
+      if (previousIsMatch)
+      {
+        Byte matchByte;
+        #ifdef _LZMA_OUT_READ
+        UInt32 pos = dictionaryPos - rep0;
+        if (pos >= dictionarySize)
+          pos += dictionarySize;
+        matchByte = dictionary[pos];
+        #else
+        matchByte = outStream[nowPos - rep0];
+        #endif
+        previousByte = LzmaLiteralDecodeMatch(probs, &rd, matchByte);
+        previousIsMatch = 0;
+      }
+      else
+        previousByte = LzmaLiteralDecode(probs, &rd);
+      outStream[nowPos++] = previousByte;
+      #ifdef _LZMA_OUT_READ
+      dictionary[dictionaryPos] = previousByte;
+      if (++dictionaryPos == dictionarySize)
+        dictionaryPos = 0;
+      #endif
+    }
+    else             
+    {
+      previousIsMatch = 1;
+      if (RangeDecoderBitDecode(p + IsRep + state, &rd) == 1)
+      {
+        if (RangeDecoderBitDecode(p + IsRepG0 + state, &rd) == 0)
+        {
+          if (RangeDecoderBitDecode(p + IsRep0Long + (state << kNumPosBitsMax) + posState, &rd) == 0)
+          {
+            #ifdef _LZMA_OUT_READ
+            UInt32 pos;
+            #endif
+            if (
+               (nowPos 
+                #ifdef _LZMA_OUT_READ
+                + globalPos
+                #endif
+               )
+               == 0)
+              return LZMA_RESULT_DATA_ERROR;
+            state = state < 7 ? 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;
+            continue;
+          }
+        }
+        else
+        {
+          UInt32 distance;
+          if(RangeDecoderBitDecode(p + IsRepG1 + state, &rd) == 0)
+            distance = rep1;
+          else 
+          {
+            if(RangeDecoderBitDecode(p + IsRepG2 + state, &rd) == 0)
+              distance = rep2;
+            else
+            {
+              distance = rep3;
+              rep3 = rep2;
+            }
+            rep2 = rep1;
+          }
+          rep1 = rep0;
+          rep0 = distance;
+        }
+        len = LzmaLenDecode(p + RepLenCoder, &rd, posState);
+        state = state < 7 ? 8 : 11;
+      }
+      else
+      {
+        int posSlot;
+        rep3 = rep2;
+        rep2 = rep1;
+        rep1 = rep0;
+        state = state < 7 ? 7 : 10;
+        len = LzmaLenDecode(p + LenCoder, &rd, posState);
+        posSlot = RangeDecoderBitTreeDecode(p + PosSlot +
+            ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << 
+            kNumPosSlotBits), kNumPosSlotBits, &rd);
+        if (posSlot >= kStartPosModelIndex)
+        {
+          int numDirectBits = ((posSlot >> 1) - 1);
+          rep0 = ((2 | ((UInt32)posSlot & 1)) << numDirectBits);
+          if (posSlot < kEndPosModelIndex)
+          {
+            rep0 += RangeDecoderReverseBitTreeDecode(
+                p + SpecPos + rep0 - posSlot - 1, numDirectBits, &rd);
+          }
+          else
+          {
+            rep0 += RangeDecoderDecodeDirectBits(&rd, 
+                numDirectBits - kNumAlignBits) << kNumAlignBits;
+            rep0 += RangeDecoderReverseBitTreeDecode(p + Align, kNumAlignBits, &rd);
+          }
+        }
+        else
+          rep0 = posSlot;
+        rep0++;
+      }
+      if (rep0 == (UInt32)(0))
+      {
+        /* it's for stream version */
+        len = -1;
+        break;
+      }
+      if (rep0 > nowPos 
+        #ifdef _LZMA_OUT_READ
+        + globalPos
+        #endif
+        )
+      {
+        return LZMA_RESULT_DATA_ERROR;
+      }
+      len += kMatchMinLen;
+      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
+        outStream[nowPos++] = previousByte;
+        len--;
+      }
+      while(len > 0 && nowPos < outSize);
+    }
+  }
+
+  #ifdef _LZMA_OUT_READ
+  vs->RangeDecoder = rd;
+  vs->DictionaryPos = dictionaryPos;
+  vs->GlobalPos = globalPos + nowPos;
+  vs->Reps[0] = rep0;
+  vs->Reps[1] = rep1;
+  vs->Reps[2] = rep2;
+  vs->Reps[3] = rep3;
+  vs->State = state;
+  vs->PreviousIsMatch = previousIsMatch;
+  vs->RemainLen = len;
+  #endif
+
+  *outSizeProcessed = nowPos;
+  return LZMA_RESULT_OK;
+}
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/LzmaDecode.h b/target/linux/bcm47xx/image/lzma-loader/src/LzmaDecode.h
new file mode 100644
index 0000000..f58944e
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/LzmaDecode.h
@@ -0,0 +1,100 @@
+/* 
+  LzmaDecode.h
+  LZMA Decoder interface
+
+  LZMA SDK 4.05 Copyright (c) 1999-2004 Igor Pavlov (2004-08-25)
+  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
+
+/* #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 */
+
+#ifndef UInt32
+#ifdef _LZMA_UINT32_IS_ULONG
+#define UInt32 unsigned long
+#else
+#define UInt32 unsigned int
+#endif
+#endif
+
+#ifdef _LZMA_PROB32
+#define CProb UInt32
+#else
+#define CProb unsigned short
+#endif
+
+#define LZMA_RESULT_OK 0
+#define LZMA_RESULT_DATA_ERROR 1
+#define LZMA_RESULT_NOT_ENOUGH_MEM 2
+
+#ifdef _LZMA_IN_CB
+typedef struct _ILzmaInCallback
+{
+  int (*Read)(void *object, unsigned char **buffer, UInt32 *bufferSize);
+} ILzmaInCallback;
+#endif
+
+#define LZMA_BASE_SIZE 1846
+#define LZMA_LIT_SIZE 768
+
+/* 
+bufferSize = (LZMA_BASE_SIZE + (LZMA_LIT_SIZE << (lc + lp)))* sizeof(CProb)
+bufferSize += 100 in case of _LZMA_OUT_READ
+by default CProb is unsigned short, 
+but if specify _LZMA_PROB_32, CProb will be UInt32(unsigned int)
+*/
+
+#ifdef _LZMA_OUT_READ
+int LzmaDecoderInit(
+    unsigned char *buffer, UInt32 bufferSize,
+    int lc, int lp, int pb,
+    unsigned char *dictionary, UInt32 dictionarySize,
+  #ifdef _LZMA_IN_CB
+    ILzmaInCallback *inCallback
+  #else
+    unsigned char *inStream, UInt32 inSize
+  #endif
+);
+#endif
+
+int LzmaDecode(
+    unsigned char *buffer, 
+  #ifndef _LZMA_OUT_READ
+    UInt32 bufferSize,
+    int lc, int lp, int pb,
+  #ifdef _LZMA_IN_CB
+    ILzmaInCallback *inCallback,
+  #else
+    unsigned char *inStream, UInt32 inSize,
+  #endif
+  #endif
+    unsigned char *outStream, UInt32 outSize,
+    UInt32 *outSizeProcessed);
+
+#endif
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/Makefile b/target/linux/bcm47xx/image/lzma-loader/src/Makefile
new file mode 100644
index 0000000..a08fc05
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/Makefile
@@ -0,0 +1,78 @@
+#
+# Makefile for Broadcom BCM947XX boards
+#
+# Copyright 2001-2003, Broadcom Corporation
+# All Rights Reserved.
+# 
+# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
+# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
+# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
+# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
+#
+# Copyright 2004  Manuel Novoa III <mjn3@codepoet.org>
+#   Modified to support bzip'd kernels.
+#   Of course, it would be better to integrate bunzip capability into CFE.
+#
+# Copyright 2005  Oleg I. Vdovikin <oleg@cs.msu.su>
+#   Cleaned up, modified for lzma support, removed from kernel
+#
+
+TEXT_START	:= 0x80001000
+BZ_TEXT_START	:= 0x80600000
+BZ_STACK_START	:= 0x80700000
+
+OBJCOPY		:= $(CROSS_COMPILE)objcopy -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 -mlong-calls -fno-common \
+		  -mabi=32 -march=mips32 -Wa,-32 -Wa,-march=mips32 -Wa,-mips32 -Wa,--trap
+CFLAGS		+= -DLOADADDR=$(TEXT_START) -D_LZMA_IN_CB
+
+ASFLAGS		= $(CFLAGS) -D__ASSEMBLY__ -DBZ_TEXT_START=$(BZ_TEXT_START) -DBZ_STACK_START=$(BZ_STACK_START)
+
+SEDFLAGS	:= s/BZ_TEXT_START/$(BZ_TEXT_START)/;s/BZ_STACK_START/$(BZ_STACK_START)/;s/TEXT_START/$(TEXT_START)/
+
+OBJECTS		:= head.o data.o
+
+all: loader.gz loader.elf
+
+# Don't build dependencies, this may die if $(CC) isn't gcc
+dep:
+
+install:
+
+loader.gz: loader
+	gzip -nc9 $< > $@
+
+loader.elf: loader.o
+	cp $< $@
+
+loader: loader.o
+	$(OBJCOPY) $< $@
+
+loader.o: loader.lds $(OBJECTS)
+	$(LD) -static --gc-sections -no-warn-mismatch -T loader.lds -o $@ $(OBJECTS)
+
+loader.lds: loader.lds.in Makefile
+	@sed "$(SEDFLAGS)" < $< > $@
+
+data.o: data.lds decompress.image
+	$(LD) -no-warn-mismatch -T data.lds -r -o $@ -b binary decompress.image -b elf32-tradlittlemips
+
+data.lds:
+	@echo "SECTIONS { .data : { code_start = .; *(.data) code_stop = .; }}" > $@
+
+decompress.image: decompress
+	$(OBJCOPY) $< $@
+
+decompress: decompress.lds decompress.o LzmaDecode.o
+	$(LD) -static --gc-sections -no-warn-mismatch -T decompress.lds -o $@ decompress.o LzmaDecode.o
+
+decompress.lds: decompress.lds.in Makefile
+	@sed "$(SEDFLAGS)" < $< > $@
+
+mrproper: clean
+
+clean:
+	rm -f loader.gz loader decompress *.lds *.o *.image
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/README b/target/linux/bcm47xx/image/lzma-loader/src/README
new file mode 100644
index 0000000..16649e9
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/README
@@ -0,0 +1,55 @@
+/*
+ * LZMA compressed kernel decompressor for bcm947xx boards
+ *
+ * Copyright (C) 2005 by Oleg I. Vdovikin <oleg@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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+The code is intended to decompress kernel, being compressed using lzma utility
+build using 7zip LZMA SDK. This utility is located in the LZMA_Alone directory
+
+decompressor code expects that your .trx file consist of three partitions: 
+
+1) decompressor itself (this is gziped code which pmon/cfe will extract and run
+on boot-up instead of real kernel)
+2) LZMA compressed kernel (both streamed and regular modes are supported now)
+3) Root filesystem
+
+Please be sure to apply the following patch for use this new trx layout (it will
+allow using both new and old trx files for root filesystem lookup code)
+
+--- linuz/arch/mips/brcm-boards/bcm947xx/setup.c        2005-01-23 19:24:27.503322896 +0300
++++ linux/arch/mips/brcm-boards/bcm947xx/setup.c        2005-01-23 19:29:05.237100944 +0300
+@@ -221,7 +221,9 @@
+                /* Try looking at TRX header for rootfs offset */
+                if (le32_to_cpu(trx->magic) == TRX_MAGIC) {
+                        bcm947xx_parts[1].offset = off;
+-                       if (le32_to_cpu(trx->offsets[1]) > off)
++                       if (le32_to_cpu(trx->offsets[2]) > off)
++                               off = le32_to_cpu(trx->offsets[2]);
++                       else if (le32_to_cpu(trx->offsets[1]) > off)
+                                off = le32_to_cpu(trx->offsets[1]);
+                        continue;
+                }
+
+
+Revision history:
+	0.02	Initial release
+	0.03	Added Mineharu Takahara <mtakahar@yahoo.com> patch to pass actual
+		output size to decoder (stream mode compressed input is not 
+		a requirement anymore)
+	0.04	Reordered functions using lds script
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/decompress.c b/target/linux/bcm47xx/image/lzma-loader/src/decompress.c
new file mode 100644
index 0000000..05681b1
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/decompress.c
@@ -0,0 +1,186 @@
+/*
+ * LZMA compressed kernel decompressor for bcm947xx boards
+ *
+ * Copyright (C) 2005 by Oleg I. Vdovikin <oleg@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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * Please note, this was code based on the bunzip2 decompressor code
+ * by Manuel Novoa III  (mjn3@codepoet.org), although the only thing left
+ * is an idea and part of original vendor code
+ *
+ *
+ * 12-Mar-2005  Mineharu Takahara <mtakahar@yahoo.com>
+ *   pass actual output size to decoder (stream mode
+ *   compressed input is not a requirement anymore)
+ *
+ * 24-Apr-2005 Oleg I. Vdovikin
+ *   reordered functions using lds script, removed forward decl
+ *
+ */
+
+#include "LzmaDecode.h"
+
+#define BCM4710_FLASH		0x1fc00000	/* Flash */
+
+#define KSEG0			0x80000000
+#define KSEG1			0xa0000000
+
+#define KSEG1ADDR(a)		((((unsigned)(a)) & 0x1fffffffU) | KSEG1)
+
+#define Index_Invalidate_I	0x00
+#define Index_Writeback_Inv_D   0x01
+
+#define cache_unroll(base,op)	\
+	__asm__ __volatile__(		\
+		".set noreorder;\n"		\
+		".set mips3;\n"			\
+		"cache %1, (%0);\n"		\
+		".set mips0;\n"			\
+		".set reorder\n"		\
+		:						\
+		: "r" (base),			\
+		  "i" (op));
+
+static __inline__ void blast_icache(unsigned long size, unsigned long lsize)
+{
+	unsigned long start = KSEG0;
+	unsigned long end = (start + size);
+
+	while(start < end) {
+		cache_unroll(start,Index_Invalidate_I);
+		start += lsize;
+	}
+}
+
+static __inline__ void blast_dcache(unsigned long size, unsigned long lsize)
+{
+	unsigned long start = KSEG0;
+	unsigned long end = (start + size);
+
+	while(start < end) {
+		cache_unroll(start,Index_Writeback_Inv_D);
+		start += lsize;
+	}
+}
+
+#define TRX_MAGIC       0x30524448      /* "HDR0" */
+
+struct trx_header {
+	unsigned int magic;		/* "HDR0" */
+	unsigned int len;		/* Length of file including header */
+	unsigned int crc32;		/* 32-bit CRC from flag_version to end of file */
+	unsigned int flag_version;	/* 0:15 flags, 16:31 version */
+	unsigned int offsets[3];	/* Offsets of partitions from start of header */
+};
+
+#define EDIMAX_PS_HEADER_MAGIC	0x36315350 /*  "PS16"  */
+#define EDIMAX_PS_HEADER_LEN	0xc /* 12 bytes long for edimax header */
+
+/* beyound the image end, size not known in advance */
+extern unsigned char workspace[];
+
+unsigned int offset;
+unsigned char *data;
+
+/* flash access should be aligned, so wrapper is used */
+/* read byte from the flash, all accesses are 32-bit aligned */
+static int read_byte(void *object, unsigned char **buffer, UInt32 *bufferSize)
+{
+	static unsigned int val;
+
+	if (((unsigned int)offset % 4) == 0) {
+		val = *(unsigned int *)data;
+		data += 4;
+	}
+	
+	*bufferSize = 1;
+	*buffer = ((unsigned char *)&val) + (offset++ & 3);
+	
+	return LZMA_RESULT_OK;
+}
+
+static __inline__ unsigned char get_byte(void)
+{
+	unsigned char *buffer;
+	UInt32 fake;
+	
+	return read_byte(0, &buffer, &fake), *buffer;
+}
+
+/* should be the first function */
+void entry(unsigned long icache_size, unsigned long icache_lsize, 
+	unsigned long dcache_size, unsigned long dcache_lsize,
+	unsigned long fw_arg0, unsigned long fw_arg1,
+	unsigned long fw_arg2, unsigned long fw_arg3)
+{
+	unsigned int i;  /* temp value */
+	unsigned int lc; /* literal context bits */
+	unsigned int lp; /* literal pos state bits */
+	unsigned int pb; /* pos state bits */
+	unsigned int osize; /* uncompressed size */
+
+	ILzmaInCallback callback;
+	callback.Read = read_byte;
+
+	/* look for trx header, 32-bit data access */
+	for (data = ((unsigned char *) KSEG1ADDR(BCM4710_FLASH));
+		((struct trx_header *)data)->magic != TRX_MAGIC &&
+		((struct trx_header *)data)->magic != EDIMAX_PS_HEADER_MAGIC;
+		 data += 65536);
+
+	if (((struct trx_header *)data)->magic == EDIMAX_PS_HEADER_MAGIC)
+		data += EDIMAX_PS_HEADER_LEN;
+	/* compressed kernel is in the partition 0 or 1 */
+	if (((struct trx_header *)data)->offsets[1] > 65536) 
+		data += ((struct trx_header *)data)->offsets[0];
+	else
+		data += ((struct trx_header *)data)->offsets[1];
+
+	offset = 0;
+
+	/* lzma args */
+	i = get_byte();
+	lc = i % 9, i = i / 9;
+	lp = i % 5, pb = i / 5;
+
+	/* skip rest of the LZMA coder property */
+	for (i = 0; i < 4; i++)
+		get_byte();
+
+	/* read the lower half of uncompressed size in the header */
+	osize = ((unsigned int)get_byte()) +
+		((unsigned int)get_byte() << 8) +
+		((unsigned int)get_byte() << 16) +
+		((unsigned int)get_byte() << 24);
+
+	/* skip rest of the header (upper half of uncompressed size) */
+	for (i = 0; i < 4; i++) 
+		get_byte();
+
+	/* decompress kernel */
+	if (LzmaDecode(workspace, ~0, lc, lp, pb, &callback,
+		(unsigned char*)LOADADDR, osize, &i) == LZMA_RESULT_OK)
+	{
+		blast_dcache(dcache_size, dcache_lsize);
+		blast_icache(icache_size, icache_lsize);
+
+		/* Jump to load address */
+		((void (*)(unsigned long, unsigned long, unsigned long,
+			unsigned long)) LOADADDR)(fw_arg0, fw_arg1, fw_arg2,
+				fw_arg3);
+	}
+}
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/decompress.lds.in b/target/linux/bcm47xx/image/lzma-loader/src/decompress.lds.in
new file mode 100644
index 0000000..33f56f8
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/decompress.lds.in
@@ -0,0 +1,20 @@
+OUTPUT_ARCH(mips)
+ENTRY(entry)
+SECTIONS {
+	. = BZ_TEXT_START;
+	.text : {
+		*(.text.entry)
+		*(.text)
+		*(.rodata)
+	}
+
+	.data : {
+		*(.data)
+	}
+
+	.bss : {
+		*(.bss)
+	}
+
+	workspace = .;
+}
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/head.S b/target/linux/bcm47xx/image/lzma-loader/src/head.S
new file mode 100644
index 0000000..50c159c
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/head.S
@@ -0,0 +1,161 @@
+/* Copyright 2005 Oleg I. Vdovikin (oleg@cs.msu.su)	*/
+/* cache manipulation adapted from Broadcom code 	*/
+/* idea taken from original bunzip2 decompressor code	*/
+/* Copyright 2004 Manuel Novoa III (mjn3@codepoet.org)	*/
+/* Licensed under the linux kernel's version of the GPL.*/
+
+#include <asm/asm.h>
+#include <asm/regdef.h>
+
+#define KSEG0		0x80000000
+
+#define C0_CONFIG	$16
+#define C0_TAGLO	$28
+#define C0_TAGHI	$29
+
+#define	CONF1_DA_SHIFT	7			/* D$ associativity */
+#define CONF1_DA_MASK	0x00000380
+#define CONF1_DA_BASE	1
+#define CONF1_DL_SHIFT	10			/* D$ line size */
+#define CONF1_DL_MASK	0x00001c00
+#define CONF1_DL_BASE	2
+#define CONF1_DS_SHIFT	13			/* D$ sets/way */
+#define CONF1_DS_MASK	0x0000e000
+#define CONF1_DS_BASE	64
+#define CONF1_IA_SHIFT	16			/* I$ associativity */
+#define CONF1_IA_MASK	0x00070000
+#define CONF1_IA_BASE	1
+#define CONF1_IL_SHIFT	19			/* I$ line size */
+#define CONF1_IL_MASK	0x00380000
+#define CONF1_IL_BASE	2
+#define CONF1_IS_SHIFT	22			/* Instruction cache sets/way */
+#define CONF1_IS_MASK	0x01c00000
+#define CONF1_IS_BASE	64
+
+#define Index_Invalidate_I	0x00
+#define Index_Writeback_Inv_D   0x01
+
+	.text
+	LEAF(startup)
+	.set noreorder
+	li	sp, BZ_STACK_START
+	addi    sp, -48
+	sw      a0, 16(sp)
+	sw      a1, 20(sp)
+	sw      a2, 24(sp)
+	sw      a3, 28(sp)
+
+	/* Copy decompressor code to the right place */
+	li	t2, BZ_TEXT_START
+	add	a0, t2, 0
+	la      a1, code_start
+	la	a2, code_stop
+$L1:
+	lw	t0, 0(a1)
+	sw	t0, 0(a0)
+	add	a1, 4
+	add	a0, 4
+	blt	a1, a2, $L1
+	nop
+
+	/* At this point we need to invalidate dcache and */
+	/* icache before jumping to new code */
+
+1:	/* Get cache sizes */
+	.set	mips32
+	mfc0	s0,C0_CONFIG,1
+	.set	mips0
+
+	li	s1,CONF1_DL_MASK
+	and	s1,s0
+	beq	s1,zero,nodc
+	nop
+
+	srl	s1,CONF1_DL_SHIFT
+	li	t0,CONF1_DL_BASE
+	sll	s1,t0,s1		/* s1 has D$ cache line size */
+
+	li	s2,CONF1_DA_MASK
+	and	s2,s0
+	srl	s2,CONF1_DA_SHIFT
+	addiu	s2,CONF1_DA_BASE	/* s2 now has D$ associativity */
+
+	li	t0,CONF1_DS_MASK
+	and	t0,s0
+	srl	t0,CONF1_DS_SHIFT
+	li	s3,CONF1_DS_BASE
+	sll	s3,s3,t0		/* s3 has D$ sets per way */
+
+	multu	s2,s3			/* sets/way * associativity */
+	mflo	t0			/* total cache lines */
+
+	multu	s1,t0			/* D$ linesize * lines */
+	mflo	s2			/* s2 is now D$ size in bytes */
+
+	/* Initilize the D$: */
+	mtc0	zero,C0_TAGLO
+	mtc0	zero,C0_TAGHI
+
+	li	t0,KSEG0		/* Just an address for the first $ line */
+	addu	t1,t0,s2		/*  + size of cache == end */
+
+	.set	mips3
+1:	cache	Index_Writeback_Inv_D,0(t0)
+	.set	mips0
+	bne	t0,t1,1b
+	addu	t0,s1
+
+nodc:
+	/* Now we get to do it all again for the I$ */
+
+	move	s3,zero			/* just in case there is no icache */
+	move	s4,zero
+
+	li	t0,CONF1_IL_MASK
+	and	t0,s0
+	beq	t0,zero,noic
+	nop
+
+	srl	t0,CONF1_IL_SHIFT
+	li	s3,CONF1_IL_BASE
+	sll	s3,t0			/* s3 has I$ cache line size */
+
+	li	t0,CONF1_IA_MASK
+	and	t0,s0
+	srl	t0,CONF1_IA_SHIFT
+	addiu	s4,t0,CONF1_IA_BASE	/* s4 now has I$ associativity */
+
+	li	t0,CONF1_IS_MASK
+	and	t0,s0
+	srl	t0,CONF1_IS_SHIFT
+	li	s5,CONF1_IS_BASE
+	sll	s5,t0			/* s5 has I$ sets per way */
+
+	multu	s4,s5			/* sets/way * associativity */
+	mflo	t0			/* s4 is now total cache lines */
+
+	multu	s3,t0			/* I$ linesize * lines */
+	mflo	s4			/* s4 is cache size in bytes */
+
+	/* Initilize the I$: */
+	mtc0	zero,C0_TAGLO
+	mtc0	zero,C0_TAGHI
+
+	li	t0,KSEG0		/* Just an address for the first $ line */
+	addu	t1,t0,s4		/*  + size of cache == end */
+
+	.set	mips3
+1:	cache	Index_Invalidate_I,0(t0)
+	.set	mips0
+	bne	t0,t1,1b
+	addu	t0,s3
+
+noic:
+	move	a0,s4			/* icache size */
+	move	a1,s3			/* icache line size */
+	move	a2,s2			/* dcache size */
+	jal	t2
+	move	a3,s1			/* dcache line size */
+
+	.set reorder
+	END(startup)
diff --git a/target/linux/bcm47xx/image/lzma-loader/src/loader.lds.in b/target/linux/bcm47xx/image/lzma-loader/src/loader.lds.in
new file mode 100644
index 0000000..20f2ea9
--- /dev/null
+++ b/target/linux/bcm47xx/image/lzma-loader/src/loader.lds.in
@@ -0,0 +1,17 @@
+OUTPUT_ARCH(mips)
+ENTRY(startup)
+SECTIONS {
+	. = TEXT_START;
+	.text : {
+		*(.text)
+		*(.rodata)
+	}
+
+	.data : {
+		*(.data)
+	}
+
+	.bss : {
+		*(.bss)
+	}
+}
diff --git a/target/linux/bcm47xx/image/mips74k.mk b/target/linux/bcm47xx/image/mips74k.mk
new file mode 100644
index 0000000..53e7b53
--- /dev/null
+++ b/target/linux/bcm47xx/image/mips74k.mk
@@ -0,0 +1,497 @@
+#################################################
+# Subtarget mips74k
+#################################################
+
+define Device/asus_rt-ac53u
+  DEVICE_MODEL := RT-AC53U
+  DEVICE_PACKAGES := $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-AC53U
+endef
+TARGET_DEVICES += asus_rt-ac53u
+
+define Device/asus_rt-ac66u
+  DEVICE_MODEL := RT-AC66U
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-AC66U
+  DEFAULT := n
+endef
+TARGET_DEVICES += asus_rt-ac66u
+
+define Device/asus_rt-n10
+  DEVICE_MODEL := RT-N10
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := "RT-N10      "
+endef
+TARGET_DEVICES += asus_rt-n10
+
+define Device/asus_rt-n10p
+  DEVICE_MODEL := RT-N10P
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := RT-N10P
+endef
+TARGET_DEVICES += asus_rt-n10p
+
+define Device/asus_rt-n10p-v2
+  DEVICE_MODEL := RT-N10P
+  DEVICE_VARIANT := v2
+  $(Device/asus)
+  PRODUCTID := RT-N10PV2
+endef
+TARGET_DEVICES += asus_rt-n10p-v2
+
+define Device/asus_rt-n10u
+  DEVICE_MODEL := RT-N10U
+  DEVICE_VARIANT := A
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N10U
+endef
+TARGET_DEVICES += asus_rt-n10u
+
+define Device/asus_rt-n10u-b
+  DEVICE_MODEL := RT-N10U
+  DEVICE_VARIANT := B
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N10U
+endef
+TARGET_DEVICES += asus_rt-n10u-b
+
+define Device/asus_rt-n12
+  DEVICE_MODEL := RT-N12
+  DEVICE_VARIANT := A1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := "RT-N12      "
+endef
+TARGET_DEVICES += asus_rt-n12
+
+define Device/asus_rt-n12-b1
+  DEVICE_MODEL := RT-N12
+  DEVICE_VARIANT := B1
+  $(Device/asus)
+  PRODUCTID := RT-N12B1
+endef
+TARGET_DEVICES += asus_rt-n12-b1
+
+define Device/asus_rt-n12-c1
+  DEVICE_MODEL := RT-N12
+  DEVICE_VARIANT := C1
+  $(Device/asus)
+  PRODUCTID := RT-N12C1
+endef
+TARGET_DEVICES += asus_rt-n12-c1
+
+define Device/asus_rt-n12-d1
+  DEVICE_MODEL := RT-N12
+  DEVICE_VARIANT := D1
+  $(Device/asus)
+  PRODUCTID := RT-N12D1
+endef
+TARGET_DEVICES += asus_rt-n12-d1
+
+define Device/asus_rt-n12hp
+  DEVICE_MODEL := RT-N12HP
+  $(Device/asus)
+  PRODUCTID := RT-N12HP
+endef
+TARGET_DEVICES += asus_rt-n12hp
+
+define Device/asus_rt-n14uhp
+  DEVICE_MODEL := RT-N14UHP
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N14UHP
+endef
+TARGET_DEVICES += asus_rt-n14uhp
+
+define Device/asus_rt-n15u
+  DEVICE_MODEL := RT-N15U
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N15U
+endef
+TARGET_DEVICES += asus_rt-n15u
+
+define Device/asus_rt-n16
+  DEVICE_MODEL := RT-N16
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N16
+endef
+TARGET_DEVICES += asus_rt-n16
+
+define Device/asus_rt-n53
+  DEVICE_MODEL := RT-N53
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/asus)
+  PRODUCTID := RT-N53
+endef
+TARGET_DEVICES += asus_rt-n53
+
+define Device/asus_rt-n66u
+  DEVICE_MODEL := RT-N66U
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N66U
+endef
+TARGET_DEVICES += asus_rt-n66u
+
+define Device/asus_rt-n66w
+  DEVICE_MODEL := RT-N66W
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/asus)
+  PRODUCTID := RT-N66U
+endef
+TARGET_DEVICES += asus_rt-n66w
+
+define Device/linksys_wrt160n-v3
+  DEVICE_MODEL := WRT160N
+  DEVICE_VARIANT := v3
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := N150
+  VERSION := 3.0.3
+  DEFAULT := n
+endef
+TARGET_DEVICES += linksys_wrt160n-v3
+
+define Device/linksys_wrt310n-v2
+  DEVICE_MODEL := WRT310N
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 310N
+  VERSION := 2.0.1
+endef
+TARGET_DEVICES += linksys_wrt310n-v2
+
+define Device/linksys_wrt320n-v1
+  DEVICE_MODEL := WRT320N
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 320N
+  VERSION := 1.0.5
+endef
+TARGET_DEVICES += linksys_wrt320n-v1
+
+define Device/linksys_e900-v1
+  DEVICE_MODEL := E900
+  DEVICE_VARIANT := v1
+  $(Device/linksys)
+  DEVICE_ID := E900
+  VERSION := 1.0.4
+endef
+TARGET_DEVICES += linksys_e900-v1
+
+define Device/linksys_e1000
+  DEVICE_MODEL := E1000
+  DEVICE_VARIANT := v1/v2/v2.1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := E100
+  VERSION := 1.1.3
+endef
+TARGET_DEVICES += linksys_e1000
+
+define Device/linksys_e1200-v1
+  DEVICE_MODEL := E1200
+  DEVICE_VARIANT := v1
+  $(Device/linksys)
+  DEVICE_ID := E120
+  VERSION := 1.0.3
+endef
+TARGET_DEVICES += linksys_e1200-v1
+
+define Device/linksys_e1200-v2
+  DEVICE_MODEL := E1200
+  DEVICE_VARIANT := v2
+  $(Device/linksys)
+  DEVICE_ID := E122
+  VERSION := 1.0.4
+endef
+TARGET_DEVICES += linksys_e1200-v2
+
+define Device/linksys_e1500-v1
+  DEVICE_MODEL := E1500
+  DEVICE_VARIANT := v1
+  $(Device/linksys)
+  DEVICE_ID := E150
+  VERSION := 1.0.5
+endef
+TARGET_DEVICES += linksys_e1500-v1
+
+define Device/linksys_e1550-v1
+  DEVICE_MODEL := E1550
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  DEVICE_ID := 1550
+  VERSION := 1.0.3
+endef
+TARGET_DEVICES += linksys_e1550-v1
+
+define Device/linksys_e2000-v1
+  DEVICE_MODEL := E2000
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 32XN
+  VERSION := 1.0.4
+endef
+TARGET_DEVICES += linksys_e2000-v1
+
+define Device/linksys_e2500-v1
+  DEVICE_MODEL := E2500
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := E25X
+  VERSION := 1.0.7
+endef
+TARGET_DEVICES += linksys_e2500-v1
+
+define Device/linksys_e2500-v2
+  DEVICE_MODEL := E2500
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := E25X
+  VERSION := 2.0.0
+endef
+TARGET_DEVICES += linksys_e2500-v2
+
+define Device/linksys_e2500-v2.1
+  DEVICE_MODEL := E2500
+  DEVICE_VARIANT := v2.1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 25RU
+  VERSION := 2.1.0
+endef
+TARGET_DEVICES += linksys_e2500-v2.1
+
+define Device/linksys_e2500-v3
+  DEVICE_MODEL := E2500
+  DEVICE_VARIANT := v3
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 25V3
+  VERSION := 3.0.0
+endef
+TARGET_DEVICES += linksys_e2500-v3
+
+define Device/linksys_e3200-v1
+  DEVICE_MODEL := E3200
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/linksys)
+  DEVICE_ID := 3200
+  VERSION := 1.0.1
+endef
+TARGET_DEVICES += linksys_e3200-v1
+
+define Device/linksys_e4200-v1
+  DEVICE_MODEL := E4200
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/linksys)
+  DEVICE_ID := 4200
+  VERSION := 1.0.5
+endef
+TARGET_DEVICES += linksys_e4200-v1
+
+define Device/netgear_r6200-v1
+  DEVICE_MODEL := R6200
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H192T00_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_r6200-v1
+
+define Device/netgear_wgr614-v10-na
+  DEVICE_MODEL := WGR614
+  DEVICE_VARIANT := v10 (NA)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H139T01_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wgr614-v10-na
+
+define Device/netgear_wgr614-v10
+  DEVICE_MODEL := WGR614
+  DEVICE_VARIANT := v10
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H139T01_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_wgr614-v10
+
+define Device/netgear_wn2500rp-v1
+  DEVICE_MODEL := WN2500RP
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H197T00_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_wn2500rp-v1
+
+define Device/netgear_wn3000rp
+  DEVICE_MODEL := WN3000RP
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H163T01_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_wn3000rp
+
+define Device/netgear_wndr3400-v1
+  DEVICE_MODEL := WNDR3400
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H155T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wndr3400-v1
+
+define Device/netgear_wndr3400-v2
+  DEVICE_MODEL := WNDR3400
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H187T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wndr3400-v2
+
+define Device/netgear_wndr3400-v3
+  DEVICE_MODEL := WNDR3400
+  DEVICE_VARIANT := v3
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H208T00_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_wndr3400-v3
+
+define Device/netgear_wndr3700-v3
+  DEVICE_MODEL := WNDR3700
+  DEVICE_VARIANT := v3
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H194T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wndr3700-v3
+
+define Device/netgear_wndr3400-vcna
+  DEVICE_MODEL := WNDR3400
+  DEVICE_VARIANT := vcna
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H155T01_NETGEAR
+  NETGEAR_REGION := 2
+  DEFAULT := n
+endef
+TARGET_DEVICES += netgear_wndr3400-vcna
+
+define Device/netgear_wndr4000
+  DEVICE_MODEL := WNDR4000
+  DEVICE_VARIANT := v1
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H181T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wndr4000
+
+define Device/netgear_wnr1000-v3
+  DEVICE_MODEL := WNR1000
+  DEVICE_VARIANT := v3
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H139T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wnr1000-v3
+
+define Device/netgear_wnr2000v2
+  DEVICE_MODEL := WNR2000
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H114T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wnr2000v2
+
+define Device/netgear_wnr3500l-v1-na
+  DEVICE_MODEL := WNR3500L
+  DEVICE_VARIANT := v1 (NA)
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H136T99_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wnr3500l-v1-na
+
+define Device/netgear_wnr3500l-v1
+  DEVICE_MODEL := WNR3500L
+  DEVICE_VARIANT := v1 (ROW)
+  DEVICE_PACKAGES := kmod-b43 $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H136T99_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_wnr3500l-v1
+
+define Device/netgear_wnr3500l-v2
+  DEVICE_MODEL := WNR3500L
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H172T00_NETGEAR
+  NETGEAR_REGION := 1
+endef
+TARGET_DEVICES += netgear_wnr3500l-v2
+
+define Device/netgear_wnr3500u
+  DEVICE_MODEL := WNR3500U
+  DEVICE_PACKAGES := $(USB2_PACKAGES)
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H136T00_NETGEAR
+  NETGEAR_REGION := 2
+  DEFAULT := n
+endef
+TARGET_DEVICES += netgear_wnr3500u
+
+define Device/netgear_wnr3500-v2
+  DEVICE_MODEL := WNR3500
+  DEVICE_VARIANT := v2
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H127T00_NETGEAR
+  NETGEAR_REGION := 2
+endef
+TARGET_DEVICES += netgear_wnr3500-v2
+
+define Device/netgear_wnr3500-v2-vc
+  DEVICE_MODEL := WNR3500
+  DEVICE_VARIANT := v2 (VC)
+  DEVICE_PACKAGES := kmod-b43
+  $(Device/netgear)
+  NETGEAR_BOARD_ID := U12H127T70_NETGEAR
+  NETGEAR_REGION := 2
+  DEFAULT := n
+endef
+TARGET_DEVICES += netgear_wnr3500-v2-vc
+
+TARGET_DEVICES += standard standard-noloader-nodictionarylzma
diff --git a/target/linux/bcm47xx/legacy/config-default b/target/linux/bcm47xx/legacy/config-default
new file mode 100644
index 0000000..8a52e47
--- /dev/null
+++ b/target/linux/bcm47xx/legacy/config-default
@@ -0,0 +1,8 @@
+CONFIG_B44=y
+CONFIG_B44_PCI=y
+CONFIG_B44_PCICORE_AUTOSELECT=y
+CONFIG_B44_PCI_AUTOSELECT=y
+# CONFIG_BCM47XX_BCMA is not set
+# CONFIG_BCMA is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_SSB_DRIVER_GIGE is not set
diff --git a/target/linux/bcm47xx/legacy/profiles/100-Broadcom-b43.mk b/target/linux/bcm47xx/legacy/profiles/100-Broadcom-b43.mk
new file mode 100644
index 0000000..c54f11b
--- /dev/null
+++ b/target/linux/bcm47xx/legacy/profiles/100-Broadcom-b43.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+
+define Profile/Broadcom-b43
+  NAME:=Broadcom SoC, all Ethernet, BCM43xx WiFi (b43, default)
+  PACKAGES:=kmod-b43 kmod-b43legacy
+endef
+
+define Profile/Broadcom-b43/Description
+	Package set compatible with hardware any Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the mac80211, b43 and
+	b43legacy drivers and b44, tg3 or bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-b43))
+
diff --git a/target/linux/bcm47xx/legacy/profiles/101-Broadcom-wl.mk b/target/linux/bcm47xx/legacy/profiles/101-Broadcom-wl.mk
new file mode 100644
index 0000000..a4c58f6
--- /dev/null
+++ b/target/linux/bcm47xx/legacy/profiles/101-Broadcom-wl.mk
@@ -0,0 +1,17 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2010-2013 OpenWrt.org
+
+define Profile/Broadcom-wl
+  NAME:=Broadcom SoC, all Ethernet, BCM43xx WiFi (wl, proprietary)
+  PACKAGES:=-wpad-basic-wolfssl kmod-brcm-wl-mini wlc nas
+endef
+
+define Profile/Broadcom-wl/Description
+	Package set compatible with hardware any Broadcom BCM47xx or BCM535x
+	SoC with Broadcom BCM43xx Wifi cards using the proprietary Broadcom
+	wireless "wl" driver and b44, tg3 or bgmac Ethernet driver.
+endef
+
+$(eval $(call Profile,Broadcom-wl))
+
diff --git a/target/linux/bcm47xx/legacy/target.mk b/target/linux/bcm47xx/legacy/target.mk
new file mode 100644
index 0000000..e3586a8
--- /dev/null
+++ b/target/linux/bcm47xx/legacy/target.mk
@@ -0,0 +1,10 @@
+FEATURES += low_mem pcmcia small_flash
+BOARDNAME:=Legacy (BMIPS3300)
+
+DEFAULT_PACKAGES += wpad-basic-wolfssl
+
+define Target/Description
+	Build firmware for Broadcom BCM47xx and BCM53xx devices with
+	BMIPS3300 CPU except for BCM4705 SoC.
+	Supported SoCs: BCM5352E, BCM5354, BCM5365?, BCM4712, BCM4704.
+endef
diff --git a/target/linux/bcm47xx/mips74k/config-default b/target/linux/bcm47xx/mips74k/config-default
new file mode 100644
index 0000000..2c3c403
--- /dev/null
+++ b/target/linux/bcm47xx/mips74k/config-default
@@ -0,0 +1,20 @@
+# CONFIG_ADM6996_PHY is not set
+# CONFIG_BCM47XX_SSB is not set
+CONFIG_BGMAC=y
+CONFIG_BGMAC_BCMA=y
+CONFIG_BOUNCE=y
+# CONFIG_CPU_MIPS32_R1 is not set
+# CONFIG_CPU_MIPSR1 is not set
+CONFIG_CPU_MIPS32_R2=y
+CONFIG_CPU_MIPSR2=y
+# CONFIG_FIXED_PHY is not set
+# CONFIG_GPIO_WDT is not set
+CONFIG_HIGHMEM=y
+# CONFIG_SSB is not set
+# CONFIG_SSB_DRIVER_EXTIF is not set
+# CONFIG_SSB_DRIVER_GIGE is not set
+# CONFIG_SSB_DRIVER_MIPS is not set
+# CONFIG_SSB_EMBEDDED is not set
+# CONFIG_SSB_PCICORE_HOSTMODE is not set
+# CONFIG_SSB_SERIAL is not set
+# CONFIG_SSB_SFLASH is not set
diff --git a/target/linux/bcm47xx/mips74k/profiles/100-Broadcom-b43.mk b/target/linux/bcm47xx/mips74k/profiles/100-Broadcom-b43.mk
new file mode 100644
index 0000000..b91179c
--- /dev/null
+++ b/target/linux/bcm47xx/mips74k/profiles/100-Broadcom-b43.mk
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2014 OpenWrt.org
+
+define Profile/Broadcom-mips74k-b43
+  NAME:=Broadcom SoC, BCM43xx WiFi (b43)
+  PACKAGES:=kmod-b43
+endef
+
+define Profile/Broadcom-mips74k-b43/Description
+	Package set for devices with BCM43xx WiFi including mac80211 and b43
+	driver.
+endef
+
+$(eval $(call Profile,Broadcom-mips74k-b43))
+
diff --git a/target/linux/bcm47xx/mips74k/profiles/101-Broadcom-brcsmac.mk b/target/linux/bcm47xx/mips74k/profiles/101-Broadcom-brcsmac.mk
new file mode 100644
index 0000000..e31b372
--- /dev/null
+++ b/target/linux/bcm47xx/mips74k/profiles/101-Broadcom-brcsmac.mk
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2014 OpenWrt.org
+
+define Profile/Broadcom-mips74k-brcmsmac
+  NAME:=Broadcom SoC, BCM43xx WiFi (brcmsmac)
+  PACKAGES:=kmod-brcmsmac
+endef
+
+define Profile/Broadcom-mips74k-brcmsmac/Description
+	Package set for devices with BCM43xx WiFi including mac80211 and
+	brcmsmac driver.
+endef
+
+$(eval $(call Profile,Broadcom-mips74k-brcmsmac))
+
diff --git a/target/linux/bcm47xx/mips74k/profiles/102-Broadcom-wl.mk b/target/linux/bcm47xx/mips74k/profiles/102-Broadcom-wl.mk
new file mode 100644
index 0000000..66a44dc
--- /dev/null
+++ b/target/linux/bcm47xx/mips74k/profiles/102-Broadcom-wl.mk
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2014 OpenWrt.org
+
+define Profile/Broadcom-mips74k-wl
+  NAME:=Broadcom SoC, BCM43xx WiFi (proprietary wl)
+  PACKAGES:=-wpad-basic-wolfssl kmod-brcm-wl wlc nas
+endef
+
+define Profile/Broadcom-mips74k-wl/Description
+	Package set for devices with BCM43xx WiFi including proprietary (and
+	closed source) driver "wl".
+endef
+
+$(eval $(call Profile,Broadcom-mips74k-wl))
+
diff --git a/target/linux/bcm47xx/mips74k/profiles/103-Broadcom-none.mk b/target/linux/bcm47xx/mips74k/profiles/103-Broadcom-none.mk
new file mode 100644
index 0000000..d5e3f57
--- /dev/null
+++ b/target/linux/bcm47xx/mips74k/profiles/103-Broadcom-none.mk
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2014 OpenWrt.org
+
+define Profile/Broadcom-mips74k-none
+  NAME:=Broadcom SoC, No WiFi
+  PACKAGES:=-wpad-basic-wolfssl
+endef
+
+define Profile/Broadcom-mips74k-none/Description
+	Package set for devices without a WiFi.
+endef
+
+$(eval $(call Profile,Broadcom-mips74k-none))
+
diff --git a/target/linux/bcm47xx/mips74k/target.mk b/target/linux/bcm47xx/mips74k/target.mk
new file mode 100644
index 0000000..ee20982
--- /dev/null
+++ b/target/linux/bcm47xx/mips74k/target.mk
@@ -0,0 +1,9 @@
+BOARDNAME:=MIPS 74K
+CPU_TYPE:=74kc
+
+DEFAULT_PACKAGES += wpad-basic-wolfssl
+
+define Target/Description
+	Build firmware for Broadcom BCM47xx and BCM53xx devices with
+	MIPS 74K CPU.
+endef
diff --git a/target/linux/bcm47xx/modules.mk b/target/linux/bcm47xx/modules.mk
new file mode 100644
index 0000000..5924694
--- /dev/null
+++ b/target/linux/bcm47xx/modules.mk
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Copyright (C) 2006-2012 OpenWrt.org
+
+define KernelPackage/bgmac
+  TITLE:=Broadcom bgmac driver
+  KCONFIG:=CONFIG_BGMAC CONFIG_BGMAC_BCMA
+  DEPENDS:=@TARGET_bcm47xx @!TARGET_bcm47xx_legacy
+  SUBMENU:=$(NETWORK_DEVICES_MENU)
+  FILES:= \
+	$(LINUX_DIR)/drivers/net/ethernet/broadcom/bgmac-bcma.ko \
+	$(LINUX_DIR)/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.ko \
+	$(LINUX_DIR)/drivers/net/ethernet/broadcom/bgmac.ko
+  AUTOLOAD:=$(call AutoProbe,bgmac-bcma)
+endef
+
+define KernelPackage/bgmac/description
+ Kernel modules for Broadcom bgmac Ethernet adapters.
+endef
+
+$(eval $(call KernelPackage,bgmac))
diff --git a/target/linux/bcm47xx/patches-5.4/159-cpu_fixes.patch b/target/linux/bcm47xx/patches-5.4/159-cpu_fixes.patch
new file mode 100644
index 0000000..59d7834
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/159-cpu_fixes.patch
@@ -0,0 +1,492 @@
+--- a/arch/mips/include/asm/r4kcache.h
++++ b/arch/mips/include/asm/r4kcache.h
+@@ -26,6 +26,38 @@
+ extern void (*r4k_blast_dcache)(void);
+ extern void (*r4k_blast_icache)(void);
+ 
++#if defined(CONFIG_BCM47XX) && !defined(CONFIG_CPU_MIPS32_R2)
++#include <asm/paccess.h>
++#include <linux/ssb/ssb.h>
++#define BCM4710_DUMMY_RREG() bcm4710_dummy_rreg()
++
++static inline unsigned long bcm4710_dummy_rreg(void)
++{
++      return *(volatile unsigned long *)(KSEG1ADDR(SSB_ENUM_BASE));
++}
++
++#define BCM4710_FILL_TLB(addr) bcm4710_fill_tlb((void *)(addr))
++
++static inline unsigned long bcm4710_fill_tlb(void *addr)
++{
++      return *(unsigned long *)addr;
++}
++
++#define BCM4710_PROTECTED_FILL_TLB(addr) bcm4710_protected_fill_tlb((void *)(addr))
++
++static inline void bcm4710_protected_fill_tlb(void *addr)
++{
++      unsigned long x;
++      get_dbe(x, (unsigned long *)addr);;
++}
++
++#else
++#define BCM4710_DUMMY_RREG()
++
++#define BCM4710_FILL_TLB(addr)
++#define BCM4710_PROTECTED_FILL_TLB(addr)
++#endif
++
+ /*
+  * This macro return a properly sign-extended address suitable as base address
+  * for indexed cache operations.  Two issues here:
+@@ -56,6 +88,7 @@ static inline void flush_icache_line_ind
+ 
+ static inline void flush_dcache_line_indexed(unsigned long addr)
+ {
++	BCM4710_DUMMY_RREG();
+ 	cache_op(Index_Writeback_Inv_D, addr);
+ }
+ 
+@@ -79,11 +112,13 @@ static inline void flush_icache_line(uns
+ 
+ static inline void flush_dcache_line(unsigned long addr)
+ {
++	BCM4710_DUMMY_RREG();
+ 	cache_op(Hit_Writeback_Inv_D, addr);
+ }
+ 
+ static inline void invalidate_dcache_line(unsigned long addr)
+ {
++	BCM4710_DUMMY_RREG();
+ 	cache_op(Hit_Invalidate_D, addr);
+ }
+ 
+@@ -156,6 +191,7 @@ static inline int protected_flush_icache
+ #ifdef CONFIG_EVA
+ 		return protected_cachee_op(Hit_Invalidate_I, addr);
+ #else
++		BCM4710_DUMMY_RREG();
+ 		return protected_cache_op(Hit_Invalidate_I, addr);
+ #endif
+ 	}
+@@ -169,6 +205,7 @@ static inline int protected_flush_icache
+  */
+ static inline int protected_writeback_dcache_line(unsigned long addr)
+ {
++	BCM4710_DUMMY_RREG();
+ #ifdef CONFIG_EVA
+ 	return protected_cachee_op(Hit_Writeback_Inv_D, addr);
+ #else
+@@ -526,8 +563,51 @@ static inline void invalidate_tcache_pag
+ 		: "r" (base),						\
+ 		  "i" (op));
+ 
++static inline void blast_dcache(void)
++{
++	unsigned long start = KSEG0;
++	unsigned long dcache_size = current_cpu_data.dcache.waysize * current_cpu_data.dcache.ways;
++	unsigned long end = (start + dcache_size);
++
++	do {
++		BCM4710_DUMMY_RREG();
++		cache_op(Index_Writeback_Inv_D, start);
++		start += current_cpu_data.dcache.linesz;
++	} while(start < end);
++}
++
++static inline void blast_dcache_page(unsigned long page)
++{
++	unsigned long start = page;
++	unsigned long end = start + PAGE_SIZE;
++
++	BCM4710_FILL_TLB(start);
++	do {
++		BCM4710_DUMMY_RREG();
++		cache_op(Hit_Writeback_Inv_D, start);
++		start += current_cpu_data.dcache.linesz;
++	} while(start < end);
++}
++
++static inline void blast_dcache_page_indexed(unsigned long page)
++{
++	unsigned long start = page;
++	unsigned long end = start + PAGE_SIZE;
++	unsigned long ws_inc = 1UL << current_cpu_data.dcache.waybit;
++	unsigned long ws_end = current_cpu_data.dcache.ways <<
++	                       current_cpu_data.dcache.waybit;
++	unsigned long ws, addr;
++	for (ws = 0; ws < ws_end; ws += ws_inc) {
++		start = page + ws;
++		for (addr = start; addr < end; addr += current_cpu_data.dcache.linesz) {
++			BCM4710_DUMMY_RREG();
++			cache_op(Index_Writeback_Inv_D, addr);
++		}
++	}
++}
++
+ /* build blast_xxx, blast_xxx_page, blast_xxx_page_indexed */
+-#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, extra)	\
++#define __BUILD_BLAST_CACHE(pfx, desc, indexop, hitop, lsize, extra, war) \
+ static inline void extra##blast_##pfx##cache##lsize(void)		\
+ {									\
+ 	unsigned long start = INDEX_BASE;				\
+@@ -537,6 +617,7 @@ static inline void extra##blast_##pfx##c
+ 			       current_cpu_data.desc.waybit;		\
+ 	unsigned long ws, addr;						\
+ 									\
++	war								\
+ 	for (ws = 0; ws < ws_end; ws += ws_inc)				\
+ 		for (addr = start; addr < end; addr += lsize * 32)	\
+ 			cache##lsize##_unroll32(addr|ws, indexop);	\
+@@ -547,6 +628,7 @@ static inline void extra##blast_##pfx##c
+ 	unsigned long start = page;					\
+ 	unsigned long end = page + PAGE_SIZE;				\
+ 									\
++	war								\
+ 	do {								\
+ 		cache##lsize##_unroll32(start, hitop);			\
+ 		start += lsize * 32;					\
+@@ -563,31 +645,32 @@ static inline void extra##blast_##pfx##c
+ 			       current_cpu_data.desc.waybit;		\
+ 	unsigned long ws, addr;						\
+ 									\
++	war								\
+ 	for (ws = 0; ws < ws_end; ws += ws_inc)				\
+ 		for (addr = start; addr < end; addr += lsize * 32)	\
+ 			cache##lsize##_unroll32(addr|ws, indexop);	\
+ }
+ 
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, )
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, )
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I_Loongson2, 32, loongson2_)
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, )
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, )
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, )
+-__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 128, )
+-__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 128, )
+-__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, )
+-
+-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, )
+-__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, )
+-__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 16, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 16, , BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 16, , )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 32, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 32, , BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I_Loongson2, 32, loongson2_, BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 32, , )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 64, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64, , BCM4710_FILL_TLB(start);)
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 64, , )
++__BUILD_BLAST_CACHE(d, dcache, Index_Writeback_Inv_D, Hit_Writeback_Inv_D, 128, , )
++__BUILD_BLAST_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 128, , )
++__BUILD_BLAST_CACHE(s, scache, Index_Writeback_Inv_SD, Hit_Writeback_Inv_SD, 128, , )
++
++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 16, , )
++__BUILD_BLAST_CACHE(inv_d, dcache, Index_Writeback_Inv_D, Hit_Invalidate_D, 32, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 16, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 32, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 64, , )
++__BUILD_BLAST_CACHE(inv_s, scache, Index_Writeback_Inv_SD, Hit_Invalidate_SD, 128, , )
+ 
+ #define __BUILD_BLAST_USER_CACHE(pfx, desc, indexop, hitop, lsize) \
+ static inline void blast_##pfx##cache##lsize##_user_page(unsigned long page) \
+@@ -612,58 +695,29 @@ __BUILD_BLAST_USER_CACHE(d, dcache, Inde
+ __BUILD_BLAST_USER_CACHE(i, icache, Index_Invalidate_I, Hit_Invalidate_I, 64)
+ 
+ /* build blast_xxx_range, protected_blast_xxx_range */
+-#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, extra)	\
++#define __BUILD_BLAST_CACHE_RANGE(pfx, desc, hitop, prot, extra, war, war2)	\
+ static inline void prot##extra##blast_##pfx##cache##_range(unsigned long start, \
+ 						    unsigned long end)	\
+ {									\
+ 	unsigned long lsize = cpu_##desc##_line_size();			\
+-	unsigned long lsize_2 = lsize * 2;				\
+-	unsigned long lsize_3 = lsize * 3;				\
+-	unsigned long lsize_4 = lsize * 4;				\
+-	unsigned long lsize_5 = lsize * 5;				\
+-	unsigned long lsize_6 = lsize * 6;				\
+-	unsigned long lsize_7 = lsize * 7;				\
+-	unsigned long lsize_8 = lsize * 8;				\
+ 	unsigned long addr = start & ~(lsize - 1);			\
+-	unsigned long aend = (end + lsize - 1) & ~(lsize - 1);		\
+-	int lines = (aend - addr) / lsize;				\
+-									\
+-	while (lines >= 8) {						\
+-		prot##cache_op(hitop, addr);				\
+-		prot##cache_op(hitop, addr + lsize);			\
+-		prot##cache_op(hitop, addr + lsize_2);			\
+-		prot##cache_op(hitop, addr + lsize_3);			\
+-		prot##cache_op(hitop, addr + lsize_4);			\
+-		prot##cache_op(hitop, addr + lsize_5);			\
+-		prot##cache_op(hitop, addr + lsize_6);			\
+-		prot##cache_op(hitop, addr + lsize_7);			\
+-		addr += lsize_8;					\
+-		lines -= 8;						\
+-	}								\
+-									\
+-	if (lines & 0x4) {						\
+-		prot##cache_op(hitop, addr);				\
+-		prot##cache_op(hitop, addr + lsize);			\
+-		prot##cache_op(hitop, addr + lsize_2);			\
+-		prot##cache_op(hitop, addr + lsize_3);			\
+-		addr += lsize_4;					\
+-	}								\
++	unsigned long aend = (end - 1) & ~(lsize - 1);			\
+ 									\
+-	if (lines & 0x2) {						\
+-		prot##cache_op(hitop, addr);				\
+-		prot##cache_op(hitop, addr + lsize);			\
+-		addr += lsize_2;					\
+-	}								\
++	war								\
+ 									\
+-	if (lines & 0x1) {						\
++	while (1) {							\
++		war2							\
+ 		prot##cache_op(hitop, addr);				\
++		if (addr == aend)					\
++			break;						\
++		addr += lsize;						\
+ 	}								\
+ }
+ 
+ #ifndef CONFIG_EVA
+ 
+-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, )
+-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_, )
++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, protected_, , BCM4710_PROTECTED_FILL_TLB(addr); BCM4710_PROTECTED_FILL_TLB(aend);, BCM4710_DUMMY_RREG();)
++__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, protected_, , , )
+ 
+ #else
+ 
+@@ -697,15 +751,15 @@ __BUILD_PROT_BLAST_CACHE_RANGE(d, dcache
+ __BUILD_PROT_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I)
+ 
+ #endif
+-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_, )
++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, protected_, , , )
+ __BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I_Loongson2, \
+-	protected_, loongson2_)
+-__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, , )
+-__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, , )
+-__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, , )
++	protected_, loongson2_, , )
++__BUILD_BLAST_CACHE_RANGE(d, dcache, Hit_Writeback_Inv_D, , , BCM4710_FILL_TLB(addr); BCM4710_FILL_TLB(aend);, BCM4710_DUMMY_RREG();)
++__BUILD_BLAST_CACHE_RANGE(i, icache, Hit_Invalidate_I, , , , )
++__BUILD_BLAST_CACHE_RANGE(s, scache, Hit_Writeback_Inv_SD, , , , )
+ /* blast_inv_dcache_range */
+-__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, , )
+-__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, , )
++__BUILD_BLAST_CACHE_RANGE(inv_d, dcache, Hit_Invalidate_D, , , , BCM4710_DUMMY_RREG();)
++__BUILD_BLAST_CACHE_RANGE(inv_s, scache, Hit_Invalidate_SD, , , , )
+ 
+ /* Currently, this is very specific to Loongson-3 */
+ #define __BUILD_BLAST_CACHE_NODE(pfx, desc, indexop, hitop, lsize)	\
+--- a/arch/mips/include/asm/stackframe.h
++++ b/arch/mips/include/asm/stackframe.h
+@@ -429,6 +429,10 @@
+ #else
+ 		.set	push
+ 		.set	arch=r4000
++#ifdef CONFIG_BCM47XX
++		nop
++		nop
++#endif
+ 		eret
+ 		.set	pop
+ #endif
+--- a/arch/mips/kernel/genex.S
++++ b/arch/mips/kernel/genex.S
+@@ -21,6 +21,19 @@
+ #include <asm/war.h>
+ #include <asm/thread_info.h>
+ 
++#ifdef CONFIG_BCM47XX
++# ifdef eret
++#  undef eret
++# endif
++# define eret 					\
++	.set push;				\
++	.set noreorder;				\
++	 nop; 					\
++	 nop;					\
++	 eret;					\
++	.set pop;
++#endif
++
+ 	__INIT
+ 
+ /*
+@@ -32,6 +45,9 @@
+ NESTED(except_vec3_generic, 0, sp)
+ 	.set	push
+ 	.set	noat
++#ifdef CONFIG_BCM47XX
++	nop
++#endif
+ 	mfc0	k1, CP0_CAUSE
+ 	andi	k1, k1, 0x7c
+ #ifdef CONFIG_64BIT
+@@ -52,6 +68,9 @@ NESTED(except_vec3_r4000, 0, sp)
+ 	.set	push
+ 	.set	arch=r4000
+ 	.set	noat
++#ifdef CONFIG_BCM47XX
++	nop
++#endif
+ 	mfc0	k1, CP0_CAUSE
+ 	li	k0, 31<<2
+ 	andi	k1, k1, 0x7c
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -39,6 +39,9 @@
+ #include <asm/dma-coherence.h>
+ #include <asm/mips-cps.h>
+ 
++/* For enabling BCM4710 cache workarounds */
++static int bcm4710 = 0;
++
+ /*
+  * Bits describing what cache ops an SMP callback function may perform.
+  *
+@@ -190,6 +193,9 @@ static void r4k_blast_dcache_user_page_s
+ {
+ 	unsigned long  dc_lsize = cpu_dcache_line_size();
+ 
++	if (bcm4710)
++		r4k_blast_dcache_page = blast_dcache_page;
++	else
+ 	if (dc_lsize == 0)
+ 		r4k_blast_dcache_user_page = (void *)cache_noop;
+ 	else if (dc_lsize == 16)
+@@ -208,6 +214,9 @@ static void r4k_blast_dcache_page_indexe
+ {
+ 	unsigned long dc_lsize = cpu_dcache_line_size();
+ 
++	if (bcm4710)
++		r4k_blast_dcache_page_indexed = blast_dcache_page_indexed;
++	else
+ 	if (dc_lsize == 0)
+ 		r4k_blast_dcache_page_indexed = (void *)cache_noop;
+ 	else if (dc_lsize == 16)
+@@ -227,6 +236,9 @@ static void r4k_blast_dcache_setup(void)
+ {
+ 	unsigned long dc_lsize = cpu_dcache_line_size();
+ 
++	if (bcm4710)
++		r4k_blast_dcache = blast_dcache;
++	else
+ 	if (dc_lsize == 0)
+ 		r4k_blast_dcache = (void *)cache_noop;
+ 	else if (dc_lsize == 16)
+@@ -1779,6 +1791,17 @@ static void coherency_setup(void)
+ 	 * silly idea of putting something else there ...
+ 	 */
+ 	switch (current_cpu_type()) {
++	case CPU_BMIPS3300:
++		{
++			u32 cm;
++			cm = read_c0_diag();
++			/* Enable icache */
++			cm |= (1 << 31);
++			/* Enable dcache */
++			cm |= (1 << 30);
++			write_c0_diag(cm);
++		}
++		break;
+ 	case CPU_R4000PC:
+ 	case CPU_R4000SC:
+ 	case CPU_R4000MC:
+@@ -1825,6 +1848,15 @@ void r4k_cache_init(void)
+ 	extern void build_copy_page(void);
+ 	struct cpuinfo_mips *c = &current_cpu_data;
+ 
++	/* Check if special workarounds are required */
++#if defined(CONFIG_BCM47XX) && !defined(CONFIG_CPU_MIPS32_R2)
++	if (current_cpu_data.cputype == CPU_BMIPS32 && (current_cpu_data.processor_id & 0xff) == 0) {
++		printk("Enabling BCM4710A0 cache workarounds.\n");
++		bcm4710 = 1;
++	} else
++#endif
++		bcm4710 = 0;
++
+ 	probe_pcache();
+ 	probe_vcache();
+ 	setup_scache();
+@@ -1901,7 +1933,15 @@ void r4k_cache_init(void)
+ 	 */
+ 	local_r4k___flush_cache_all(NULL);
+ 
++#ifdef CONFIG_BCM47XX
++	{
++		static void (*_coherency_setup)(void);
++		_coherency_setup = (void (*)(void)) KSEG1ADDR(coherency_setup);
++		_coherency_setup();
++	}
++#else
+ 	coherency_setup();
++#endif
+ 	board_cache_error_setup = r4k_cache_error_setup;
+ 
+ 	/*
+--- a/arch/mips/mm/tlbex.c
++++ b/arch/mips/mm/tlbex.c
+@@ -980,6 +980,9 @@ void build_get_pgde32(u32 **p, unsigned
+ 		uasm_i_srl(p, ptr, ptr, SMP_CPUID_PTRSHIFT);
+ 		uasm_i_addu(p, ptr, tmp, ptr);
+ #else
++#ifdef CONFIG_BCM47XX
++		uasm_i_nop(p);
++#endif
+ 		UASM_i_LA_mostly(p, ptr, pgdc);
+ #endif
+ 		uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
+@@ -1341,6 +1344,9 @@ static void build_r4000_tlb_refill_handl
+ #ifdef CONFIG_64BIT
+ 		build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
+ #else
++# ifdef CONFIG_BCM47XX
++		uasm_i_nop(&p);
++# endif
+ 		build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
+ #endif
+ 
+@@ -1352,6 +1358,9 @@ static void build_r4000_tlb_refill_handl
+ 		build_update_entries(&p, K0, K1);
+ 		build_tlb_write_entry(&p, &l, &r, tlb_random);
+ 		uasm_l_leave(&l, p);
++#ifdef CONFIG_BCM47XX
++		uasm_i_nop(&p);
++#endif
+ 		uasm_i_eret(&p); /* return from trap */
+ 	}
+ #ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
+@@ -2052,6 +2061,9 @@ build_r4000_tlbchange_handler_head(u32 *
+ #ifdef CONFIG_64BIT
+ 	build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */
+ #else
++# ifdef CONFIG_BCM47XX
++	uasm_i_nop(p);
++# endif
+ 	build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */
+ #endif
+ 
+@@ -2098,6 +2110,9 @@ build_r4000_tlbchange_handler_tail(u32 *
+ 	build_tlb_write_entry(p, l, r, tlb_indexed);
+ 	uasm_l_leave(l, *p);
+ 	build_restore_work_registers(p);
++#ifdef CONFIG_BCM47XX
++	uasm_i_nop(p);
++#endif
+ 	uasm_i_eret(p); /* return from trap */
+ 
+ #ifdef CONFIG_64BIT
diff --git a/target/linux/bcm47xx/patches-5.4/160-kmap_coherent.patch b/target/linux/bcm47xx/patches-5.4/160-kmap_coherent.patch
new file mode 100644
index 0000000..55e99db
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/160-kmap_coherent.patch
@@ -0,0 +1,78 @@
+From: Jeff Hansen <jhansen@cardaccess-inc.com>
+Subject: [PATCH] kmap_coherent
+
+On ASUS WL-500gP there are some "Data bus error"s when executing simple
+commands liks "ps" or "cat /proc/1/cmdline".
+
+This fixes OpenWrt ticket #1485: https://dev.openwrt.org/ticket/1485
+---
+--- a/arch/mips/include/asm/cpu-features.h
++++ b/arch/mips/include/asm/cpu-features.h
+@@ -243,6 +243,9 @@
+ #ifndef cpu_has_pindexed_dcache
+ #define cpu_has_pindexed_dcache	(cpu_data[0].dcache.flags & MIPS_CACHE_PINDEX)
+ #endif
++#ifndef cpu_use_kmap_coherent
++#define cpu_use_kmap_coherent 1
++#endif
+ 
+ /*
+  * I-Cache snoops remote store.	 This only matters on SMP.  Some multiprocessors
+--- a/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
++++ b/arch/mips/include/asm/mach-bcm47xx/cpu-feature-overrides.h
+@@ -80,4 +80,6 @@
+ #define cpu_scache_line_size()		0
+ #define cpu_has_vz			0
+ 
++#define cpu_use_kmap_coherent		0
++
+ #endif /* __ASM_MACH_BCM47XX_CPU_FEATURE_OVERRIDES_H */
+--- a/arch/mips/mm/c-r4k.c
++++ b/arch/mips/mm/c-r4k.c
+@@ -697,7 +697,7 @@ static inline void local_r4k_flush_cache
+ 		map_coherent = (cpu_has_dc_aliases &&
+ 				page_mapcount(page) &&
+ 				!Page_dcache_dirty(page));
+-		if (map_coherent)
++		if (map_coherent && cpu_use_kmap_coherent)
+ 			vaddr = kmap_coherent(page, addr);
+ 		else
+ 			vaddr = kmap_atomic(page);
+@@ -719,7 +719,7 @@ static inline void local_r4k_flush_cache
+ 	}
+ 
+ 	if (vaddr) {
+-		if (map_coherent)
++		if (map_coherent && cpu_use_kmap_coherent)
+ 			kunmap_coherent();
+ 		else
+ 			kunmap_atomic(vaddr);
+--- a/arch/mips/mm/init.c
++++ b/arch/mips/mm/init.c
+@@ -174,7 +174,7 @@ void copy_user_highpage(struct page *to,
+ 	void *vfrom, *vto;
+ 
+ 	vto = kmap_atomic(to);
+-	if (cpu_has_dc_aliases &&
++	if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+ 	    page_mapcount(from) && !Page_dcache_dirty(from)) {
+ 		vfrom = kmap_coherent(from, vaddr);
+ 		copy_page(vto, vfrom);
+@@ -196,7 +196,7 @@ void copy_to_user_page(struct vm_area_st
+ 	struct page *page, unsigned long vaddr, void *dst, const void *src,
+ 	unsigned long len)
+ {
+-	if (cpu_has_dc_aliases &&
++	if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+ 	    page_mapcount(page) && !Page_dcache_dirty(page)) {
+ 		void *vto = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ 		memcpy(vto, src, len);
+@@ -214,7 +214,7 @@ void copy_from_user_page(struct vm_area_
+ 	struct page *page, unsigned long vaddr, void *dst, const void *src,
+ 	unsigned long len)
+ {
+-	if (cpu_has_dc_aliases &&
++	if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+ 	    page_mapcount(page) && !Page_dcache_dirty(page)) {
+ 		void *vfrom = kmap_coherent(page, vaddr) + (vaddr & ~PAGE_MASK);
+ 		memcpy(dst, vfrom, len);
diff --git a/target/linux/bcm47xx/patches-5.4/209-b44-register-adm-switch.patch b/target/linux/bcm47xx/patches-5.4/209-b44-register-adm-switch.patch
new file mode 100644
index 0000000..2b47501
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/209-b44-register-adm-switch.patch
@@ -0,0 +1,121 @@
+From b36f694256f41bc71571f467646d015dda128d14 Mon Sep 17 00:00:00 2001
+From: Hauke Mehrtens <hauke@hauke-m.de>
+Date: Sat, 9 Nov 2013 17:03:59 +0100
+Subject: [PATCH 210/210] b44: register adm switch
+
+---
+ drivers/net/ethernet/broadcom/b44.c |   57 +++++++++++++++++++++++++++++++++++
+ drivers/net/ethernet/broadcom/b44.h |    3 ++
+ 2 files changed, 60 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/b44.c
++++ b/drivers/net/ethernet/broadcom/b44.c
+@@ -31,6 +31,8 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/slab.h>
+ #include <linux/phy.h>
++#include <linux/platform_device.h>
++#include <linux/platform_data/adm6996-gpio.h>
+ 
+ #include <linux/uaccess.h>
+ #include <asm/io.h>
+@@ -2249,6 +2251,69 @@ static void b44_adjust_link(struct net_d
+ 	}
+ }
+ 
++#ifdef CONFIG_BCM47XX
++static int b44_register_adm_switch(struct b44 *bp)
++{
++	int gpio;
++	struct platform_device *pdev;
++	struct adm6996_gpio_platform_data adm_data = {0};
++	struct platform_device_info info = {0};
++
++	adm_data.model = ADM6996L;
++	gpio = bcm47xx_nvram_gpio_pin("adm_eecs");
++	if (gpio >= 0)
++		adm_data.eecs = gpio;
++	else
++		adm_data.eecs = 2;
++
++	gpio = bcm47xx_nvram_gpio_pin("adm_eesk");
++	if (gpio >= 0)
++		adm_data.eesk = gpio;
++	else
++		adm_data.eesk = 3;
++
++	gpio = bcm47xx_nvram_gpio_pin("adm_eedi");
++	if (gpio >= 0)
++		adm_data.eedi = gpio;
++	else
++		adm_data.eedi = 4;
++
++	/*
++	 * We ignore the "adm_rc" GPIO here. The driver does not use it,
++	 * and it conflicts with the Reset button GPIO on the Linksys WRT54GSv1.
++	 */
++
++	info.parent = bp->sdev->dev;
++	info.name = "adm6996_gpio";
++	info.id = -1;
++	info.data = &adm_data;
++	info.size_data = sizeof(adm_data);
++
++	if (!bp->adm_switch) {
++		pdev = platform_device_register_full(&info);
++		if (IS_ERR(pdev))
++			return PTR_ERR(pdev);
++
++		bp->adm_switch = pdev;
++	}
++	return 0;
++}
++static void b44_unregister_adm_switch(struct b44 *bp)
++{
++	if (bp->adm_switch)
++		platform_device_unregister(bp->adm_switch);
++}
++#else
++static int b44_register_adm_switch(struct b44 *bp)
++{
++	return 0;
++}
++static void b44_unregister_adm_switch(struct b44 *bp)
++{
++
++}
++#endif /* CONFIG_BCM47XX */
++
+ static int b44_register_phy_one(struct b44 *bp)
+ {
+ 	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+@@ -2285,6 +2350,9 @@ static int b44_register_phy_one(struct b
+ 	if (!mdiobus_is_registered_device(bp->mii_bus, bp->phy_addr) &&
+ 	    (sprom->boardflags_lo & (B44_BOARDFLAG_ROBO | B44_BOARDFLAG_ADM))) {
+ 
++		if (sprom->boardflags_lo & B44_BOARDFLAG_ADM)
++			b44_register_adm_switch(bp);
++
+ 		dev_info(sdev->dev,
+ 			 "could not find PHY at %i, use fixed one\n",
+ 			 bp->phy_addr);
+@@ -2481,6 +2549,7 @@ static void b44_remove_one(struct ssb_de
+ 	unregister_netdev(dev);
+ 	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
+ 		b44_unregister_phy_one(bp);
++	b44_unregister_adm_switch(bp);
+ 	ssb_device_disable(sdev, 0);
+ 	ssb_bus_may_powerdown(sdev->bus);
+ 	netif_napi_del(&bp->napi);
+--- a/drivers/net/ethernet/broadcom/b44.h
++++ b/drivers/net/ethernet/broadcom/b44.h
+@@ -408,6 +408,9 @@ struct b44 {
+ 	struct mii_bus		*mii_bus;
+ 	int			old_link;
+ 	struct mii_if_info	mii_if;
++
++	/* platform device for associated switch */
++	struct platform_device *adm_switch;
+ };
+ 
+ #endif /* _B44_H */
diff --git a/target/linux/bcm47xx/patches-5.4/210-b44_phy_fix.patch b/target/linux/bcm47xx/patches-5.4/210-b44_phy_fix.patch
new file mode 100644
index 0000000..8c7a73a
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/210-b44_phy_fix.patch
@@ -0,0 +1,54 @@
+--- a/drivers/net/ethernet/broadcom/b44.c
++++ b/drivers/net/ethernet/broadcom/b44.c
+@@ -431,10 +431,34 @@ static void b44_wap54g10_workaround(stru
+ error:
+ 	pr_warn("PHY: cannot reset MII transceiver isolate bit\n");
+ }
++
++static void b44_bcm47xx_workarounds(struct b44 *bp)
++{
++	char buf[20];
++	struct ssb_device *sdev = bp->sdev;
++
++	/* Toshiba WRC-1000, Siemens SE505 v1, Askey RT-210W, RT-220W */
++	if (sdev->bus->sprom.board_num == 100) {
++		bp->phy_addr = B44_PHY_ADDR_NO_LOCAL_PHY;
++	} else {
++		/* WL-HDD */
++		if (bcm47xx_nvram_getenv("hardware_version", buf, sizeof(buf)) >= 0 &&
++		    !strncmp(buf, "WL300-", strlen("WL300-"))) {
++			if (sdev->bus->sprom.et0phyaddr == 0 &&
++			    sdev->bus->sprom.et1phyaddr == 1)
++				bp->phy_addr = B44_PHY_ADDR_NO_LOCAL_PHY;
++		}
++	}
++	return;
++}
+ #else
+ static inline void b44_wap54g10_workaround(struct b44 *bp)
+ {
+ }
++
++static inline void b44_bcm47xx_workarounds(struct b44 *bp)
++{
++}
+ #endif
+ 
+ static int b44_setup_phy(struct b44 *bp)
+@@ -443,6 +467,7 @@ static int b44_setup_phy(struct b44 *bp)
+ 	int err;
+ 
+ 	b44_wap54g10_workaround(bp);
++	b44_bcm47xx_workarounds(bp);
+ 
+ 	if (bp->flags & B44_FLAG_EXTERNAL_PHY)
+ 		return 0;
+@@ -2179,6 +2204,8 @@ static int b44_get_invariants(struct b44
+ 	 * valid PHY address. */
+ 	bp->phy_addr &= 0x1F;
+ 
++	b44_bcm47xx_workarounds(bp);
++
+ 	memcpy(bp->dev->dev_addr, addr, ETH_ALEN);
+ 
+ 	if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){
diff --git a/target/linux/bcm47xx/patches-5.4/280-activate_ssb_support_in_usb.patch b/target/linux/bcm47xx/patches-5.4/280-activate_ssb_support_in_usb.patch
new file mode 100644
index 0000000..de8bb42
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/280-activate_ssb_support_in_usb.patch
@@ -0,0 +1,25 @@
+This prevents the options from being delete with make kernel_oldconfig.
+---
+ drivers/ssb/Kconfig |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/bcma/Kconfig
++++ b/drivers/bcma/Kconfig
+@@ -32,6 +32,7 @@ config BCMA_HOST_PCI
+ config BCMA_HOST_SOC
+ 	bool "Support for BCMA in a SoC"
+ 	depends on HAS_IOMEM
++	select USB_HCD_BCMA if USB_EHCI_HCD || USB_OHCI_HCD
+ 	help
+ 	  Host interface for a Broadcom AIX bus directly mapped into
+ 	  the memory. This only works with the Broadcom SoCs from the
+--- a/drivers/ssb/Kconfig
++++ b/drivers/ssb/Kconfig
+@@ -136,6 +136,7 @@ config SSB_SFLASH
+ config SSB_EMBEDDED
+ 	bool
+ 	depends on SSB_DRIVER_MIPS && SSB_PCICORE_HOSTMODE
++	select USB_HCD_SSB if USB_EHCI_HCD || USB_OHCI_HCD
+ 	default y
+ 
+ config SSB_DRIVER_EXTIF
diff --git a/target/linux/bcm47xx/patches-5.4/300-fork_cacheflush.patch b/target/linux/bcm47xx/patches-5.4/300-fork_cacheflush.patch
new file mode 100644
index 0000000..daa2c1a
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/300-fork_cacheflush.patch
@@ -0,0 +1,21 @@
+From: Wolfram Joost <dbox2@frokaschwei.de>
+Subject: [PATCH] fork_cacheflush
+
+On ASUS WL-500gP there are many unexpected "Segmentation fault"s that
+seem to be caused by a kernel. They can be avoided by:
+1) Disabling highpage
+2) Using flush_cache_mm in flush_cache_dup_mm
+
+For details see OpenWrt ticket #2035 https://dev.openwrt.org/ticket/2035
+---
+--- a/arch/mips/include/asm/cacheflush.h
++++ b/arch/mips/include/asm/cacheflush.h
+@@ -46,7 +46,7 @@
+ extern void (*flush_cache_all)(void);
+ extern void (*__flush_cache_all)(void);
+ extern void (*flush_cache_mm)(struct mm_struct *mm);
+-#define flush_cache_dup_mm(mm)	do { (void) (mm); } while (0)
++#define flush_cache_dup_mm(mm) flush_cache_mm(mm)
+ extern void (*flush_cache_range)(struct vm_area_struct *vma,
+ 	unsigned long start, unsigned long end);
+ extern void (*flush_cache_page)(struct vm_area_struct *vma, unsigned long page, unsigned long pfn);
diff --git a/target/linux/bcm47xx/patches-5.4/310-no_highpage.patch b/target/linux/bcm47xx/patches-5.4/310-no_highpage.patch
new file mode 100644
index 0000000..4eea606
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/310-no_highpage.patch
@@ -0,0 +1,74 @@
+From: Jeff Hansen <jhansen@cardaccess-inc.com>
+Subject: [PATCH] no highpage
+
+On ASUS WL-500gP there are many unexpected "Segmentation fault"s that
+seem to be caused by a kernel. They can be avoided by:
+1) Disabling highpage
+2) Using flush_cache_mm in flush_cache_dup_mm
+
+For details see OpenWrt ticket #2035 https://dev.openwrt.org/ticket/2035
+---
+--- a/arch/mips/include/asm/page.h
++++ b/arch/mips/include/asm/page.h
+@@ -71,6 +71,7 @@ static inline unsigned int page_size_ftl
+ #endif /* CONFIG_MIPS_HUGE_TLB_SUPPORT */
+ 
+ #include <linux/pfn.h>
++#include <asm/cpu-features.h>
+ 
+ extern void build_clear_page(void);
+ extern void build_copy_page(void);
+@@ -110,11 +111,16 @@ static inline void clear_user_page(void
+ 		flush_data_cache_page((unsigned long)addr);
+ }
+ 
+-struct vm_area_struct;
+-extern void copy_user_highpage(struct page *to, struct page *from,
+-	unsigned long vaddr, struct vm_area_struct *vma);
++static inline void copy_user_page(void *vto, void *vfrom, unsigned long vaddr,
++	struct page *to)
++{
++	extern void (*flush_data_cache_page)(unsigned long addr);
+ 
+-#define __HAVE_ARCH_COPY_USER_HIGHPAGE
++	copy_page(vto, vfrom);
++	if (!cpu_has_ic_fills_f_dc ||
++	    pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
++		flush_data_cache_page((unsigned long)vto);
++}
+ 
+ /*
+  * These are used to make use of C type-checking..
+--- a/arch/mips/mm/init.c
++++ b/arch/mips/mm/init.c
+@@ -168,30 +168,6 @@ void kunmap_coherent(void)
+ 	preempt_enable();
+ }
+ 
+-void copy_user_highpage(struct page *to, struct page *from,
+-	unsigned long vaddr, struct vm_area_struct *vma)
+-{
+-	void *vfrom, *vto;
+-
+-	vto = kmap_atomic(to);
+-	if (cpu_has_dc_aliases && cpu_use_kmap_coherent &&
+-	    page_mapcount(from) && !Page_dcache_dirty(from)) {
+-		vfrom = kmap_coherent(from, vaddr);
+-		copy_page(vto, vfrom);
+-		kunmap_coherent();
+-	} else {
+-		vfrom = kmap_atomic(from);
+-		copy_page(vto, vfrom);
+-		kunmap_atomic(vfrom);
+-	}
+-	if ((!cpu_has_ic_fills_f_dc) ||
+-	    pages_do_alias((unsigned long)vto, vaddr & PAGE_MASK))
+-		flush_data_cache_page((unsigned long)vto);
+-	kunmap_atomic(vto);
+-	/* Make sure this page is cleared on other CPU's too before using it */
+-	smp_wmb();
+-}
+-
+ void copy_to_user_page(struct vm_area_struct *vma,
+ 	struct page *page, unsigned long vaddr, void *dst, const void *src,
+ 	unsigned long len)
diff --git a/target/linux/bcm47xx/patches-5.4/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch b/target/linux/bcm47xx/patches-5.4/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch
new file mode 100644
index 0000000..bde811c
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/320-MIPS-BCM47XX-Devices-database-update-for-4.x.patch
@@ -0,0 +1,185 @@
+--- a/arch/mips/bcm47xx/board.c
++++ b/arch/mips/bcm47xx/board.c
+@@ -141,6 +141,7 @@ struct bcm47xx_board_type_list2 bcm47xx_
+ 	{{BCM47XX_BOARD_LINKSYS_WRT300NV11, "Linksys WRT300N V1.1"}, "WRT300N", "1.1"},
+ 	{{BCM47XX_BOARD_LINKSYS_WRT310NV1, "Linksys WRT310N V1"}, "WRT310N", "1.0"},
+ 	{{BCM47XX_BOARD_LINKSYS_WRT310NV2, "Linksys WRT310N V2"}, "WRT310N", "2.0"},
++	{{BCM47XX_BOARD_LINKSYS_WRT320N_V1, "Linksys WRT320N V1"}, "WRT320N", "1.0"},
+ 	{{BCM47XX_BOARD_LINKSYS_WRT54G3GV2, "Linksys WRT54G3GV2-VF"}, "WRT54G3GV2-VF", "1.0"},
+ 	{{BCM47XX_BOARD_LINKSYS_WRT610NV1, "Linksys WRT610N V1"}, "WRT610N", "1.0"},
+ 	{{BCM47XX_BOARD_LINKSYS_WRT610NV2, "Linksys WRT610N V2"}, "WRT610N", "2.0"},
+@@ -161,9 +162,12 @@ struct bcm47xx_board_type_list1 bcm47xx_
+ 	{{BCM47XX_BOARD_LUXUL_XWR_600_V1, "Luxul XWR-600 V1"}, "luxul_xwr600_v1"},
+ 	{{BCM47XX_BOARD_LUXUL_XWR_1750_V1, "Luxul XWR-1750 V1"}, "luxul_xwr1750_v1"},
+ 	{{BCM47XX_BOARD_NETGEAR_R6200_V1, "Netgear R6200 V1"}, "U12H192T00_NETGEAR"},
++	{{BCM47XX_BOARD_NETGEAR_R6300_V1, "Netgear R6300 V1"}, "U12H218T00_NETGEAR"},
+ 	{{BCM47XX_BOARD_NETGEAR_WGR614V8, "Netgear WGR614 V8"}, "U12H072T00_NETGEAR"},
+ 	{{BCM47XX_BOARD_NETGEAR_WGR614V9, "Netgear WGR614 V9"}, "U12H094T00_NETGEAR"},
+ 	{{BCM47XX_BOARD_NETGEAR_WGR614_V10, "Netgear WGR614 V10"}, "U12H139T01_NETGEAR"},
++	{{BCM47XX_BOARD_NETGEAR_WN2500RP_V1, "Netgear WN2500RP V1"}, "U12H197T00_NETGEAR"},
++	{{BCM47XX_BOARD_NETGEAR_WN2500RP_V2, "Netgear WN2500RP V2"}, "U12H294T00_NETGEAR"},
+ 	{{BCM47XX_BOARD_NETGEAR_WNDR3300, "Netgear WNDR3300"}, "U12H093T00_NETGEAR"},
+ 	{{BCM47XX_BOARD_NETGEAR_WNDR3400V1, "Netgear WNDR3400 V1"}, "U12H155T00_NETGEAR"},
+ 	{{BCM47XX_BOARD_NETGEAR_WNDR3400V2, "Netgear WNDR3400 V2"}, "U12H187T00_NETGEAR"},
+--- a/arch/mips/bcm47xx/buttons.c
++++ b/arch/mips/bcm47xx/buttons.c
+@@ -27,6 +27,12 @@
+ /* Asus */
+ 
+ static const struct gpio_keys_button
++bcm47xx_buttons_asus_rtn10u[] __initconst = {
++	BCM47XX_GPIO_KEY(20, KEY_WPS_BUTTON),
++	BCM47XX_GPIO_KEY(21, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
+ bcm47xx_buttons_asus_rtn12[] __initconst = {
+ 	BCM47XX_GPIO_KEY(0, KEY_WPS_BUTTON),
+ 	BCM47XX_GPIO_KEY(1, KEY_RESTART),
+@@ -277,6 +283,18 @@ bcm47xx_buttons_linksys_wrt310nv1[] __in
+ };
+ 
+ static const struct gpio_keys_button
++bcm47xx_buttons_linksys_wrt310n_v2[] __initconst = {
++	BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON),
++	BCM47XX_GPIO_KEY(6, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
++bcm47xx_buttons_linksys_wrt320n_v1[] __initconst = {
++	BCM47XX_GPIO_KEY(5, KEY_WPS_BUTTON),
++	BCM47XX_GPIO_KEY(8, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
+ bcm47xx_buttons_linksys_wrt54g3gv2[] __initconst = {
+ 	BCM47XX_GPIO_KEY(5, KEY_WIMAX),
+ 	BCM47XX_GPIO_KEY(6, KEY_RESTART),
+@@ -392,6 +410,17 @@ bcm47xx_buttons_netgear_r6200_v1[] __ini
+ };
+ 
+ static const struct gpio_keys_button
++bcm47xx_buttons_netgear_r6300_v1[] __initconst = {
++	BCM47XX_GPIO_KEY(6, KEY_RESTART),
++};
++
++static const struct gpio_keys_button
++bcm47xx_buttons_netgear_wn2500rp_v1[] __initconst = {
++	BCM47XX_GPIO_KEY(12, KEY_RESTART),
++	BCM47XX_GPIO_KEY(31, KEY_WPS_BUTTON),
++};
++
++static const struct gpio_keys_button
+ bcm47xx_buttons_netgear_wndr3400v1[] __initconst = {
+ 	BCM47XX_GPIO_KEY(4, KEY_RESTART),
+ 	BCM47XX_GPIO_KEY(6, KEY_WPS_BUTTON),
+@@ -478,6 +507,9 @@ int __init bcm47xx_buttons_register(void
+ 	int err;
+ 
+ 	switch (board) {
++	case BCM47XX_BOARD_ASUS_RTN10U:
++		err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn10u);
++		break;
+ 	case BCM47XX_BOARD_ASUS_RTN12:
+ 		err = bcm47xx_copy_bdata(bcm47xx_buttons_asus_rtn12);
+ 		break;
+@@ -608,6 +640,12 @@ int __init bcm47xx_buttons_register(void
+ 	case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+ 		err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt310nv1);
+ 		break;
++	case BCM47XX_BOARD_LINKSYS_WRT310NV2:
++		err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt310n_v2);
++		break;
++	case BCM47XX_BOARD_LINKSYS_WRT320N_V1:
++		err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt320n_v1);
++		break;
+ 	case BCM47XX_BOARD_LINKSYS_WRT54G3GV2:
+ 		err = bcm47xx_copy_bdata(bcm47xx_buttons_linksys_wrt54g3gv2);
+ 		break;
+@@ -674,6 +712,12 @@ int __init bcm47xx_buttons_register(void
+ 	case BCM47XX_BOARD_NETGEAR_R6200_V1:
+ 		err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_r6200_v1);
+ 		break;
++	case BCM47XX_BOARD_NETGEAR_R6300_V1:
++		err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_r6300_v1);
++		break;
++	case BCM47XX_BOARD_NETGEAR_WN2500RP_V1:
++		err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wn2500rp_v1);
++		break;
+ 	case BCM47XX_BOARD_NETGEAR_WNDR3400V1:
+ 		err = bcm47xx_copy_bdata(bcm47xx_buttons_netgear_wndr3400v1);
+ 		break;
+--- a/arch/mips/include/asm/mach-bcm47xx/bcm47xx_board.h
++++ b/arch/mips/include/asm/mach-bcm47xx/bcm47xx_board.h
+@@ -72,6 +72,7 @@ enum bcm47xx_board {
+ 	BCM47XX_BOARD_LINKSYS_WRT300NV11,
+ 	BCM47XX_BOARD_LINKSYS_WRT310NV1,
+ 	BCM47XX_BOARD_LINKSYS_WRT310NV2,
++	BCM47XX_BOARD_LINKSYS_WRT320N_V1,
+ 	BCM47XX_BOARD_LINKSYS_WRT54G3GV2,
+ 	BCM47XX_BOARD_LINKSYS_WRT54G_TYPE_0101,
+ 	BCM47XX_BOARD_LINKSYS_WRT54G_TYPE_0467,
+@@ -99,9 +100,12 @@ enum bcm47xx_board {
+ 	BCM47XX_BOARD_MOTOROLA_WR850GV2V3,
+ 
+ 	BCM47XX_BOARD_NETGEAR_R6200_V1,
++	BCM47XX_BOARD_NETGEAR_R6300_V1,
+ 	BCM47XX_BOARD_NETGEAR_WGR614V8,
+ 	BCM47XX_BOARD_NETGEAR_WGR614V9,
+ 	BCM47XX_BOARD_NETGEAR_WGR614_V10,
++	BCM47XX_BOARD_NETGEAR_WN2500RP_V1,
++	BCM47XX_BOARD_NETGEAR_WN2500RP_V2,
+ 	BCM47XX_BOARD_NETGEAR_WNDR3300,
+ 	BCM47XX_BOARD_NETGEAR_WNDR3400V1,
+ 	BCM47XX_BOARD_NETGEAR_WNDR3400V2,
+--- a/arch/mips/bcm47xx/leds.c
++++ b/arch/mips/bcm47xx/leds.c
+@@ -30,6 +30,14 @@
+ /* Asus */
+ 
+ static const struct gpio_led
++bcm47xx_leds_asus_rtn10u[] __initconst = {
++	BCM47XX_GPIO_LED(5, "green", "wlan", 0, LEDS_GPIO_DEFSTATE_OFF),
++	BCM47XX_GPIO_LED(6, "green", "power", 1, LEDS_GPIO_DEFSTATE_ON),
++	BCM47XX_GPIO_LED(7, "green", "wps", 0, LEDS_GPIO_DEFSTATE_OFF),
++	BCM47XX_GPIO_LED(8, "green", "usb", 0, LEDS_GPIO_DEFSTATE_OFF),
++};
++
++static const struct gpio_led
+ bcm47xx_leds_asus_rtn12[] __initconst = {
+ 	BCM47XX_GPIO_LED(2, "unk", "power", 1, LEDS_GPIO_DEFSTATE_ON),
+ 	BCM47XX_GPIO_LED(7, "unk", "wlan", 0, LEDS_GPIO_DEFSTATE_OFF),
+@@ -314,6 +322,13 @@ bcm47xx_leds_linksys_wrt310nv1[] __initc
+ };
+ 
+ static const struct gpio_led
++bcm47xx_leds_linksys_wrt320n_v1[] __initconst = {
++	BCM47XX_GPIO_LED(1, "blue", "wlan", 1, LEDS_GPIO_DEFSTATE_OFF),
++	BCM47XX_GPIO_LED(2, "blue", "power", 0, LEDS_GPIO_DEFSTATE_ON),
++	BCM47XX_GPIO_LED(4, "amber", "wps", 1, LEDS_GPIO_DEFSTATE_OFF),
++};
++
++static const struct gpio_led
+ bcm47xx_leds_linksys_wrt54g_generic[] __initconst = {
+ 	BCM47XX_GPIO_LED(0, "unk", "dmz", 1, LEDS_GPIO_DEFSTATE_OFF),
+ 	BCM47XX_GPIO_LED(1, "unk", "power", 0, LEDS_GPIO_DEFSTATE_ON),
+@@ -556,6 +571,9 @@ void __init bcm47xx_leds_register(void)
+ 	enum bcm47xx_board board = bcm47xx_board_get();
+ 
+ 	switch (board) {
++	case BCM47XX_BOARD_ASUS_RTN10U:
++		bcm47xx_set_pdata(bcm47xx_leds_asus_rtn10u);
++		break;
+ 	case BCM47XX_BOARD_ASUS_RTN12:
+ 		bcm47xx_set_pdata(bcm47xx_leds_asus_rtn12);
+ 		break;
+@@ -689,6 +707,9 @@ void __init bcm47xx_leds_register(void)
+ 	case BCM47XX_BOARD_LINKSYS_WRT310NV1:
+ 		bcm47xx_set_pdata(bcm47xx_leds_linksys_wrt310nv1);
+ 		break;
++	case BCM47XX_BOARD_LINKSYS_WRT320N_V1:
++		bcm47xx_set_pdata(bcm47xx_leds_linksys_wrt320n_v1);
++		break;
+ 	case BCM47XX_BOARD_LINKSYS_WRT54G3GV2:
+ 		bcm47xx_set_pdata(bcm47xx_leds_linksys_wrt54g3gv2);
+ 		break;
diff --git a/target/linux/bcm47xx/patches-5.4/400-mtd-bcm47xxpart-get-nvram.patch b/target/linux/bcm47xx/patches-5.4/400-mtd-bcm47xxpart-get-nvram.patch
new file mode 100644
index 0000000..17abe89
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/400-mtd-bcm47xxpart-get-nvram.patch
@@ -0,0 +1,34 @@
+--- a/drivers/mtd/parsers/bcm47xxpart.c
++++ b/drivers/mtd/parsers/bcm47xxpart.c
+@@ -98,6 +98,7 @@ static int bcm47xxpart_parse(struct mtd_
+ 	int trx_num = 0; /* Number of found TRX partitions */
+ 	int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
+ 	int err;
++	bool found_nvram = false;
+ 
+ 	/*
+ 	 * Some really old flashes (like AT45DB*) had smaller erasesize-s, but
+@@ -279,12 +280,23 @@ static int bcm47xxpart_parse(struct mtd_
+ 		if (buf[0] == NVRAM_HEADER) {
+ 			bcm47xxpart_add_part(&parts[curr_part++], "nvram",
+ 					     master->size - blocksize, 0);
++			found_nvram = true;
+ 			break;
+ 		}
+ 	}
+ 
+ 	kfree(buf);
+ 
++	if (!found_nvram) {
++		pr_err("can not find a nvram partition reserve last block\n");
++		bcm47xxpart_add_part(&parts[curr_part++], "nvram_guess",
++				     master->size - blocksize * 2, MTD_WRITEABLE);
++		for (i = 0; i < curr_part; i++) {
++			if (parts[i].size + parts[i].offset == master->size)
++				parts[i].offset -= blocksize * 2;
++		}
++	}
++
+ 	/*
+ 	 * Assume that partitions end at the beginning of the one they are
+ 	 * followed by.
diff --git a/target/linux/bcm47xx/patches-5.4/610-pci_ide_fix.patch b/target/linux/bcm47xx/patches-5.4/610-pci_ide_fix.patch
new file mode 100644
index 0000000..520828e
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/610-pci_ide_fix.patch
@@ -0,0 +1,41 @@
+From: b.sander
+Subject: [PATCH] pci: IDE fix
+
+These are standard probing messages when using pdc202xx_old:
+pdc202xx_old 0000:00:01.0: IDE controller (0x105a:0x0d30 rev 0x02)
+PCI: Enabling device 0000:00:01.0 (0004 -> 0007)
+PCI: Fixing up device 0000:00:01.0
+0000:00:01.0: (U)DMA Burst Bit DISABLED Primary PCI Mode Secondary PCI Mode.
+0000:00:01.0: FORCING BURST BIT 0x00->0x01 ACTIVE
+pdc202xx_old 0000:00:01.0: 100% native mode on irq 6
+
+With the default MAX_HWIFS value after above we get:
+    ide2: BM-DMA at 0x0400-0x0407
+    ide3: BM-DMA at 0x0408-0x040f
+Probing IDE interface ide2...
+hde: CF500, CFA DISK drive
+
+As you can see it's ide2 + ide3 and hde.
+
+With this patch applied we get:
+    ide0: BM-DMA at 0x0400-0x0407
+    ide1: BM-DMA at 0x0408-0x040f
+Probing IDE interface ide0...
+hda: CF500, CFA DISK drive
+
+This fixes OpenWrt ticket #7061: https://dev.openwrt.org/ticket/7061
+---
+--- a/include/linux/ide.h
++++ b/include/linux/ide.h
+@@ -236,7 +236,11 @@ static inline void ide_std_init_ports(st
+ 	hw->io_ports.ctl_addr = ctl_addr;
+ }
+ 
++#if defined CONFIG_BCM47XX
++# define MAX_HWIFS	2
++#else
+ #define MAX_HWIFS	10
++#endif
+ 
+ /*
+  * Now for the data we need to maintain per-drive:  ide_drive_t
diff --git a/target/linux/bcm47xx/patches-5.4/700-net-bgmac-connect-to-PHY-even-if-it-is-BGMAC_PHY_NOR.patch b/target/linux/bcm47xx/patches-5.4/700-net-bgmac-connect-to-PHY-even-if-it-is-BGMAC_PHY_NOR.patch
new file mode 100644
index 0000000..2fcfbb7
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/700-net-bgmac-connect-to-PHY-even-if-it-is-BGMAC_PHY_NOR.patch
@@ -0,0 +1,42 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sun, 7 Nov 2021 14:20:40 +0100
+Subject: [PATCH] net: bgmac: connect to PHY even if it is BGMAC_PHY_NOREGS
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Recent bgmac change was meant to just fix a race between "Generic PHY"
+and "bcm53xx" drivers after -EPROBE_DEFER. It modified bgmac to use
+phy_connect() only if there is a real PHY device connected.
+
+That change broke bgmac on bcm47xx. bcma_phy_connect() now registers a
+fixed PHY with the bgmac_phy_connect_direct(). That fails as another
+fixed PHY (also using address 0) is already registered - by bcm47xx arch
+code bcm47xx_register_bus_complete().
+
+This change brings origial behaviour. It connects Ethernet interface
+with pseudo-PHY (switch device) and adjusts Ethernet interface link to
+match connected switch.
+
+This fixes:
+[    2.548098] bgmac_bcma bcma0:1: Failed to register fixed PHY device
+[    2.554584] bgmac_bcma bcma0:1: Cannot connect to phy
+
+Fixes: b5375509184d ("net: bgmac: improve handling PHY")
+Link: https://lore.kernel.org/netdev/3639116e-9292-03ca-b9d9-d741118a4541@gmail.com/T/#u
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -94,7 +94,7 @@ static int bcma_phy_connect(struct bgmac
+ 		return 0;
+ 
+ 	/* Connect to the PHY */
+-	if (bgmac->mii_bus && bgmac->phyaddr != BGMAC_PHY_NOREGS) {
++	if (bgmac->mii_bus) {
+ 		snprintf(bus_id, sizeof(bus_id), PHY_ID_FMT, bgmac->mii_bus->id,
+ 			 bgmac->phyaddr);
+ 		phy_dev = phy_connect(bgmac->net_dev, bus_id, bgmac_adjust_link,
diff --git a/target/linux/bcm47xx/patches-5.4/791-tg3-no-pci-sleep.patch b/target/linux/bcm47xx/patches-5.4/791-tg3-no-pci-sleep.patch
new file mode 100644
index 0000000..b34f208
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/791-tg3-no-pci-sleep.patch
@@ -0,0 +1,17 @@
+When the Ethernet controller is powered down and someone wants to 
+access the mdio bus like the witch driver (b53) the system crashed if 
+PCI_D3hot was set before. This patch deactivates this power sawing mode 
+when a switch driver is in use.
+
+--- a/drivers/net/ethernet/broadcom/tg3.c
++++ b/drivers/net/ethernet/broadcom/tg3.c
+@@ -4279,7 +4279,8 @@ static int tg3_power_down_prepare(struct
+ static void tg3_power_down(struct tg3 *tp)
+ {
+ 	pci_wake_from_d3(tp->pdev, tg3_flag(tp, WOL_ENABLE));
+-	pci_set_power_state(tp->pdev, PCI_D3hot);
++	if (!tg3_flag(tp, ROBOSWITCH))
++		pci_set_power_state(tp->pdev, PCI_D3hot);
+ }
+ 
+ static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u32 *speed, u8 *duplex)
diff --git a/target/linux/bcm47xx/patches-5.4/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch b/target/linux/bcm47xx/patches-5.4/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch
new file mode 100644
index 0000000..318dc55
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/800-bcma-add-table-of-serial-flashes-with-smaller-blocks.patch
@@ -0,0 +1,73 @@
+From 597715c61ae75a05ab3310a34ff3857a006f0f63 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Thu, 20 Nov 2014 21:32:42 +0100
+Subject: [PATCH] bcma: add table of serial flashes with smaller blocks
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/bcma/driver_chipcommon_sflash.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/drivers/bcma/driver_chipcommon_sflash.c
++++ b/drivers/bcma/driver_chipcommon_sflash.c
+@@ -9,6 +9,7 @@
+ 
+ #include <linux/platform_device.h>
+ #include <linux/bcma/bcma.h>
++#include <bcm47xx_board.h>
+ 
+ static struct resource bcma_sflash_resource = {
+ 	.name	= "bcma_sflash",
+@@ -42,6 +43,13 @@ static const struct bcma_sflash_tbl_e bc
+ 	{ NULL },
+ };
+ 
++/* Some devices use smaller blocks (and have more of them) */
++static const struct bcma_sflash_tbl_e bcma_sflash_st_shrink_tbl[] = {
++	{ "M25P16", 0x14, 0x1000, 512, },
++	{ "M25P32", 0x15, 0x1000, 1024, },
++	{ NULL },
++};
++
+ static const struct bcma_sflash_tbl_e bcma_sflash_sst_tbl[] = {
+ 	{ "SST25WF512", 1, 0x1000, 16, },
+ 	{ "SST25VF512", 0x48, 0x1000, 16, },
+@@ -85,6 +93,24 @@ static void bcma_sflash_cmd(struct bcma_
+ 	bcma_err(cc->core->bus, "SFLASH control command failed (timeout)!\n");
+ }
+ 
++const struct bcma_sflash_tbl_e *bcma_sflash_shrink_flash(u32 id)
++{
++	enum bcm47xx_board board = bcm47xx_board_get();
++	const struct bcma_sflash_tbl_e *e;
++
++	switch (board) {
++	case BCM47XX_BOARD_NETGEAR_WGR614_V10:
++	case BCM47XX_BOARD_NETGEAR_WNR1000_V3:
++		for (e = bcma_sflash_st_shrink_tbl; e->name; e++) {
++			if (e->id == id)
++				return e;
++		}
++		return NULL;
++	default:
++		return NULL;
++	}
++}
++
+ /* Initialize serial flash access */
+ int bcma_sflash_init(struct bcma_drv_cc *cc)
+ {
+@@ -115,6 +141,10 @@ int bcma_sflash_init(struct bcma_drv_cc
+ 		case 0x13:
+ 			return -ENOTSUPP;
+ 		default:
++			e = bcma_sflash_shrink_flash(id);
++			if (e)
++				break;
++
+ 			for (e = bcma_sflash_st_tbl; e->name; e++) {
+ 				if (e->id == id)
+ 					break;
diff --git a/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch b/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch
new file mode 100644
index 0000000..5f4af85
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/820-wgt634u-nvram-fix.patch
@@ -0,0 +1,295 @@
+The Netgear wgt634u uses a different format for storing the 
+configuration. This patch is needed to read out the correct 
+configuration. The cfe_env.c file uses a different method way to read 
+out the configuration than the in kernel cfe config reader.
+
+--- a/drivers/firmware/broadcom/Makefile
++++ b/drivers/firmware/broadcom/Makefile
+@@ -1,3 +1,3 @@
+ # SPDX-License-Identifier: GPL-2.0-only
+-obj-$(CONFIG_BCM47XX_NVRAM)		+= bcm47xx_nvram.o
++obj-$(CONFIG_BCM47XX_NVRAM)		+= bcm47xx_nvram.o cfe_env.o
+ obj-$(CONFIG_BCM47XX_SPROM)		+= bcm47xx_sprom.o
+--- /dev/null
++++ b/drivers/firmware/broadcom/cfe_env.c
+@@ -0,0 +1,228 @@
++/*
++ * CFE environment variable access
++ *
++ * Copyright 2001-2003, Broadcom Corporation
++ * Copyright 2006, Felix Fietkau <nbd@nbd.name>
++ * 
++ * This program is free software; you can redistribute  it and/or modify it
++ * under  the terms of  the GNU General  Public License as published by the
++ * Free Software Foundation;  either version 2 of the  License, or (at your
++ * option) any later version.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <asm/io.h>
++#include <linux/uaccess.h>
++
++#define NVRAM_SIZE       (0x1ff0)
++static char _nvdata[NVRAM_SIZE];
++static char _valuestr[256];
++
++/*
++ * TLV types.  These codes are used in the "type-length-value"
++ * encoding of the items stored in the NVRAM device (flash or EEPROM)
++ *
++ * The layout of the flash/nvram is as follows:
++ *
++ * <type> <length> <data ...> <type> <length> <data ...> <type_end>
++ *
++ * The type code of "ENV_TLV_TYPE_END" marks the end of the list.
++ * The "length" field marks the length of the data section, not
++ * including the type and length fields.
++ *
++ * Environment variables are stored as follows:
++ *
++ * <type_env> <length> <flags> <name> = <value>
++ *
++ * If bit 0 (low bit) is set, the length is an 8-bit value.
++ * If bit 0 (low bit) is clear, the length is a 16-bit value
++ * 
++ * Bit 7 set indicates "user" TLVs.  In this case, bit 0 still
++ * indicates the size of the length field.  
++ *
++ * Flags are from the constants below:
++ *
++ */
++#define ENV_LENGTH_16BITS	0x00	/* for low bit */
++#define ENV_LENGTH_8BITS	0x01
++
++#define ENV_TYPE_USER		0x80
++
++#define ENV_CODE_SYS(n,l) (((n)<<1)|(l))
++#define ENV_CODE_USER(n,l) ((((n)<<1)|(l)) | ENV_TYPE_USER)
++
++/*
++ * The actual TLV types we support
++ */
++
++#define ENV_TLV_TYPE_END	0x00	
++#define ENV_TLV_TYPE_ENV	ENV_CODE_SYS(0,ENV_LENGTH_8BITS)
++
++/*
++ * Environment variable flags 
++ */
++
++#define ENV_FLG_NORMAL		0x00	/* normal read/write */
++#define ENV_FLG_BUILTIN		0x01	/* builtin - not stored in flash */
++#define ENV_FLG_READONLY	0x02	/* read-only - cannot be changed */
++
++#define ENV_FLG_MASK		0xFF	/* mask of attributes we keep */
++#define ENV_FLG_ADMIN		0x100	/* lets us internally override permissions */
++
++
++/*  *********************************************************************
++    *  _nvram_read(buffer,offset,length)
++    *  
++    *  Read data from the NVRAM device
++    *  
++    *  Input parameters: 
++    *  	   buffer - destination buffer
++    *  	   offset - offset of data to read
++    *  	   length - number of bytes to read
++    *  	   
++    *  Return value:
++    *  	   number of bytes read, or <0 if error occured
++    ********************************************************************* */
++static int
++_nvram_read(unsigned char *nv_buf, unsigned char *buffer, int offset, int length)
++{
++    int i;
++    if (offset > NVRAM_SIZE)
++	return -1; 
++
++    for ( i = 0; i < length; i++) {
++	buffer[i] = ((volatile unsigned char*)nv_buf)[offset + i];
++    }
++    return length;
++}
++
++
++static char*
++_strnchr(const char *dest,int c,size_t cnt)
++{
++	while (*dest && (cnt > 0)) {
++	if (*dest == c) return (char *) dest;
++	dest++;
++	cnt--;
++	}
++	return NULL;
++}
++
++
++
++/*
++ * Core support API: Externally visible.
++ */
++
++/*
++ * Get the value of an NVRAM variable
++ * @param	name	name of variable to get
++ * @return	value of variable or NULL if undefined
++ */
++
++char *cfe_env_get(unsigned char *nv_buf, const char *name)
++{
++    int size;
++    unsigned char *buffer;
++    unsigned char *ptr;
++    unsigned char *envval;
++    unsigned int reclen;
++    unsigned int rectype;
++    int offset;
++    int flg;
++    
++	if (!strcmp(name, "nvram_type"))
++		return "cfe";
++	
++    size = NVRAM_SIZE;
++    buffer = &_nvdata[0];
++
++    ptr = buffer;
++    offset = 0;
++
++    /* Read the record type and length */
++    if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
++	goto error;
++    }
++    
++    while ((*ptr != ENV_TLV_TYPE_END)  && (size > 1)) {
++
++	/* Adjust pointer for TLV type */
++	rectype = *(ptr);
++	offset++;
++	size--;
++
++	/* 
++	 * Read the length.  It can be either 1 or 2 bytes
++	 * depending on the code 
++	 */
++	if (rectype & ENV_LENGTH_8BITS) {
++	    /* Read the record type and length - 8 bits */
++	    if (_nvram_read(nv_buf, ptr,offset,1) != 1) {
++		goto error;
++	    }
++	    reclen = *(ptr);
++	    size--;
++	    offset++;
++	}
++	else {
++	    /* Read the record type and length - 16 bits, MSB first */
++	    if (_nvram_read(nv_buf, ptr,offset,2) != 2) {
++		goto error;
++	    }
++	    reclen = (((unsigned int) *(ptr)) << 8) + (unsigned int) *(ptr+1);
++	    size -= 2;
++	    offset += 2;
++	}
++
++	if (reclen > size)
++	    break;	/* should not happen, bad NVRAM */
++
++	switch (rectype) {
++	    case ENV_TLV_TYPE_ENV:
++		/* Read the TLV data */
++		if (_nvram_read(nv_buf, ptr,offset,reclen) != reclen)
++		    goto error;
++		flg = *ptr++;
++		envval = (unsigned char *) _strnchr(ptr,'=',(reclen-1));
++		if (envval) {
++		    *envval++ = '\0';
++		    memcpy(_valuestr,envval,(reclen-1)-(envval-ptr));
++		    _valuestr[(reclen-1)-(envval-ptr)] = '\0';
++#if 0			
++		    printk(KERN_INFO "NVRAM:%s=%s\n", ptr, _valuestr);
++#endif
++		    if(!strcmp(ptr, name)){
++			return _valuestr;
++		    }
++		    if((strlen(ptr) > 1) && !strcmp(&ptr[1], name))
++			return _valuestr;
++		}
++		break;
++		
++	    default: 
++		/* Unknown TLV type, skip it. */
++		break;
++	    }
++
++	/*
++	 * Advance to next TLV 
++	 */
++		
++	size -= (int)reclen;
++	offset += reclen;
++
++	/* Read the next record type */
++	ptr = buffer;
++	if (_nvram_read(nv_buf, ptr,offset,1) != 1)
++	    goto error;
++	}
++
++error:
++    return NULL;
++
++}
++
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -33,6 +33,8 @@ struct nvram_header {
+ static char nvram_buf[NVRAM_SPACE];
+ static size_t nvram_len;
+ static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000};
++static int cfe_env;
++extern char *cfe_env_get(char *nv_buf, const char *name);
+ 
+ /**
+  * bcm47xx_nvram_is_valid - check for a valid NVRAM at specified memory
+@@ -80,6 +82,26 @@ static int bcm47xx_nvram_find_and_copy(v
+ 		return -EEXIST;
+ 	}
+ 
++	cfe_env = 0;
++
++	/* XXX: hack for supporting the CFE environment stuff on WGT634U */
++	if (res_size >= 8 * 1024 * 1024) {
++		u32 *src = (u32 *)(flash_start + 8 * 1024 * 1024 - 0x2000);
++		u32 *dst = (u32 *)nvram_buf;
++
++		if ((*src & 0xff00ff) == 0x000001) {
++			printk("early_nvram_init: WGT634U NVRAM found.\n");
++
++			for (i = 0; i < 0x1ff0; i++) {
++				if (*src == 0xFFFFFFFF)
++					break;
++				*dst++ = *src++;
++			}
++			cfe_env = 1;
++			return 0;
++		}
++	}
++
+ 	/* TODO: when nvram is on nand flash check for bad blocks first. */
+ 
+ 	/* Try every possible flash size and check for NVRAM at its end */
+@@ -172,6 +194,13 @@ int bcm47xx_nvram_getenv(const char *nam
+ 	if (!name)
+ 		return -EINVAL;
+ 
++	if (cfe_env) {
++		value = cfe_env_get(nvram_buf, name);
++		if (!value)
++			return -ENOENT;
++		return snprintf(val, val_len, "%s", value);
++	}
++
+ 	if (!nvram_len) {
+ 		err = nvram_init();
+ 		if (err)
diff --git a/target/linux/bcm47xx/patches-5.4/830-huawei_e970_support.patch b/target/linux/bcm47xx/patches-5.4/830-huawei_e970_support.patch
new file mode 100644
index 0000000..1746fee
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/830-huawei_e970_support.patch
@@ -0,0 +1,101 @@
+--- a/arch/mips/bcm47xx/setup.c
++++ b/arch/mips/bcm47xx/setup.c
+@@ -37,6 +37,7 @@
+ #include <linux/ssb/ssb.h>
+ #include <linux/ssb/ssb_embedded.h>
+ #include <linux/bcma/bcma_soc.h>
++#include <linux/old_gpio_wdt.h>
+ #include <asm/bootinfo.h>
+ #include <asm/idle.h>
+ #include <asm/prom.h>
+@@ -254,6 +255,33 @@ static struct fixed_phy_status bcm47xx_f
+ 	.duplex	= DUPLEX_FULL,
+ };
+ 
++static struct gpio_wdt_platform_data gpio_wdt_data;
++
++static struct platform_device gpio_wdt_device = {
++	.name			= "gpio-wdt",
++	.id			= 0,
++	.dev			= {
++		.platform_data	= &gpio_wdt_data,
++	},
++};
++
++static int __init bcm47xx_register_gpio_watchdog(void)
++{
++	enum bcm47xx_board board = bcm47xx_board_get();
++
++	switch (board) {
++	case BCM47XX_BOARD_HUAWEI_E970:
++		pr_info("bcm47xx: detected Huawei E970 or similar, starting early gpio_wdt timer\n");
++		gpio_wdt_data.gpio = 7;
++		gpio_wdt_data.interval = HZ;
++		gpio_wdt_data.first_interval = HZ / 5;
++		return platform_device_register(&gpio_wdt_device);
++	default:
++		/* Nothing to do */
++		return 0;
++	}
++}
++
+ static int __init bcm47xx_register_bus_complete(void)
+ {
+ 	switch (bcm47xx_bus_type) {
+@@ -275,6 +303,7 @@ static int __init bcm47xx_register_bus_c
+ 	bcm47xx_workarounds();
+ 
+ 	fixed_phy_add(PHY_POLL, 0, &bcm47xx_fixed_phy_status);
++	bcm47xx_register_gpio_watchdog();
+ 	return 0;
+ }
+ device_initcall(bcm47xx_register_bus_complete);
+--- a/arch/mips/configs/bcm47xx_defconfig
++++ b/arch/mips/configs/bcm47xx_defconfig
+@@ -63,6 +63,7 @@ CONFIG_HW_RANDOM=y
+ CONFIG_GPIO_SYSFS=y
+ CONFIG_WATCHDOG=y
+ CONFIG_BCM47XX_WDT=y
++CONFIG_GPIO_WDT=y
+ CONFIG_SSB_DRIVER_GIGE=y
+ CONFIG_BCMA_DRIVER_GMAC_CMN=y
+ CONFIG_USB=y
+--- a/drivers/ssb/embedded.c
++++ b/drivers/ssb/embedded.c
+@@ -34,11 +34,36 @@ int ssb_watchdog_timer_set(struct ssb_bu
+ }
+ EXPORT_SYMBOL(ssb_watchdog_timer_set);
+ 
++#ifdef CONFIG_BCM47XX
++#include <bcm47xx_board.h>
++
++static bool ssb_watchdog_supported(void)
++{
++	enum bcm47xx_board board = bcm47xx_board_get();
++
++	/* The Huawei E970 has a hardware watchdog using a GPIO */
++	switch (board) {
++	case BCM47XX_BOARD_HUAWEI_E970:
++		return false;
++	default:
++		return true;
++	}
++}
++#else
++static bool ssb_watchdog_supported(void)
++{
++	return true;
++}
++#endif
++
+ int ssb_watchdog_register(struct ssb_bus *bus)
+ {
+ 	struct bcm47xx_wdt wdt = {};
+ 	struct platform_device *pdev;
+ 
++	if (!ssb_watchdog_supported())
++		return 0;
++
+ 	if (ssb_chipco_available(&bus->chipco)) {
+ 		wdt.driver_data = &bus->chipco;
+ 		wdt.timer_set = ssb_chipco_watchdog_timer_set_wdt;
diff --git a/target/linux/bcm47xx/patches-5.4/831-old_gpio_wdt.patch b/target/linux/bcm47xx/patches-5.4/831-old_gpio_wdt.patch
new file mode 100644
index 0000000..8414a18
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/831-old_gpio_wdt.patch
@@ -0,0 +1,360 @@
+This generic GPIO watchdog is used on Huawei E970 (bcm47xx)
+
+Signed-off-by: Mathias Adam <m.adam--openwrt@adamis.de>
+
+--- a/drivers/watchdog/Kconfig
++++ b/drivers/watchdog/Kconfig
+@@ -1657,6 +1657,15 @@ config WDT_MTX1
+ 	  Hardware driver for the MTX-1 boards. This is a watchdog timer that
+ 	  will reboot the machine after a 100 seconds timer expired.
+ 
++config GPIO_WDT
++	tristate "GPIO Hardware Watchdog"
++ 	help
++	  Hardware driver for GPIO-controlled watchdogs. GPIO pin and
++	  toggle interval settings are platform-specific. The driver
++	  will stop toggling the GPIO (i.e. machine reboots) after a
++	  100 second timer expired and no process has written to
++	  /dev/watchdog during that time.
++
+ config PNX833X_WDT
+ 	tristate "PNX833x Hardware Watchdog"
+ 	depends on SOC_PNX8335
+--- a/drivers/watchdog/Makefile
++++ b/drivers/watchdog/Makefile
+@@ -158,6 +158,7 @@ obj-$(CONFIG_RC32434_WDT) += rc32434_wdt
+ obj-$(CONFIG_INDYDOG) += indydog.o
+ obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o
+ obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
++obj-$(CONFIG_GPIO_WDT) += old_gpio_wdt.o
+ obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
+ obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
+ obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
+--- /dev/null
++++ b/drivers/watchdog/old_gpio_wdt.c
+@@ -0,0 +1,301 @@
++/*
++ *      Driver for GPIO-controlled Hardware Watchdogs.
++ *
++ *      Copyright (C) 2013 Mathias Adam <m.adam--linux@adamis.de>
++ *
++ *      Replaces mtx1_wdt (driver for the MTX-1 Watchdog):
++ *
++ *      (C) Copyright 2005 4G Systems <info@4g-systems.biz>,
++ *                              All Rights Reserved.
++ *                              http://www.4g-systems.biz
++ *
++ *      (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@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.
++ *
++ *      Neither Michael Stickel nor 4G Systems admit liability nor provide
++ *      warranty for any of this software. This material is provided
++ *      "AS-IS" and at no charge.
++ *
++ *      (c) Copyright 2005    4G Systems <info@4g-systems.biz>
++ *
++ *      Release 0.01.
++ *      Author: Michael Stickel  michael.stickel@4g-systems.biz
++ *
++ *      Release 0.02.
++ *      Author: Florian Fainelli florian@openwrt.org
++ *              use the Linux watchdog/timer APIs
++ *
++ *      Release 0.03.
++ *      Author: Mathias Adam <m.adam--linux@adamis.de>
++ *              make it a generic gpio watchdog driver
++ *
++ *      The Watchdog is configured to reset the MTX-1
++ *      if it is not triggered for 100 seconds.
++ *      It should not be triggered more often than 1.6 seconds.
++ *
++ *      A timer triggers the watchdog every 5 seconds, until
++ *      it is opened for the first time. After the first open
++ *      it MUST be triggered every 2..95 seconds.
++ */
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/miscdevice.h>
++#include <linux/fs.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/timer.h>
++#include <linux/completion.h>
++#include <linux/jiffies.h>
++#include <linux/watchdog.h>
++#include <linux/platform_device.h>
++#include <linux/io.h>
++#include <linux/uaccess.h>
++#include <linux/gpio.h>
++#include <linux/old_gpio_wdt.h>
++
++static int ticks = 100 * HZ;
++
++static struct {
++	struct completion stop;
++	spinlock_t lock;
++	int running;
++	struct timer_list timer;
++	int queue;
++	int default_ticks;
++	unsigned long inuse;
++	unsigned gpio;
++	unsigned int gstate;
++	int interval;
++	int first_interval;
++} gpio_wdt_device;
++
++static void gpio_wdt_trigger(struct timer_list *unused)
++{
++	spin_lock(&gpio_wdt_device.lock);
++	if (gpio_wdt_device.running && ticks > 0)
++		ticks -= gpio_wdt_device.interval;
++
++	/* toggle wdt gpio */
++	gpio_wdt_device.gstate = !gpio_wdt_device.gstate;
++	gpio_set_value(gpio_wdt_device.gpio, gpio_wdt_device.gstate);
++
++	if (gpio_wdt_device.queue && ticks > 0)
++		mod_timer(&gpio_wdt_device.timer, jiffies + gpio_wdt_device.interval);
++	else
++		complete(&gpio_wdt_device.stop);
++	spin_unlock(&gpio_wdt_device.lock);
++}
++
++static void gpio_wdt_reset(void)
++{
++	ticks = gpio_wdt_device.default_ticks;
++}
++
++
++static void gpio_wdt_start(void)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&gpio_wdt_device.lock, flags);
++	if (!gpio_wdt_device.queue) {
++		gpio_wdt_device.queue = 1;
++		gpio_wdt_device.gstate = 1;
++		gpio_set_value(gpio_wdt_device.gpio, 1);
++		mod_timer(&gpio_wdt_device.timer, jiffies + gpio_wdt_device.first_interval);
++	}
++	gpio_wdt_device.running++;
++	spin_unlock_irqrestore(&gpio_wdt_device.lock, flags);
++}
++
++static int gpio_wdt_stop(void)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&gpio_wdt_device.lock, flags);
++	if (gpio_wdt_device.queue) {
++		gpio_wdt_device.queue = 0;
++		gpio_wdt_device.gstate = 0;
++		gpio_set_value(gpio_wdt_device.gpio, 0);
++	}
++	ticks = gpio_wdt_device.default_ticks;
++	spin_unlock_irqrestore(&gpio_wdt_device.lock, flags);
++	return 0;
++}
++
++/* Filesystem functions */
++
++static int gpio_wdt_open(struct inode *inode, struct file *file)
++{
++	if (test_and_set_bit(0, &gpio_wdt_device.inuse))
++		return -EBUSY;
++	return nonseekable_open(inode, file);
++}
++
++
++static int gpio_wdt_release(struct inode *inode, struct file *file)
++{
++	clear_bit(0, &gpio_wdt_device.inuse);
++	return 0;
++}
++
++static long gpio_wdt_ioctl(struct file *file, unsigned int cmd,
++							unsigned long arg)
++{
++	void __user *argp = (void __user *)arg;
++	int __user *p = (int __user *)argp;
++	unsigned int value;
++	static const struct watchdog_info ident = {
++		.options = WDIOF_CARDRESET,
++		.identity = "GPIO WDT",
++	};
++
++	switch (cmd) {
++	case WDIOC_GETSUPPORT:
++		if (copy_to_user(argp, &ident, sizeof(ident)))
++			return -EFAULT;
++		break;
++	case WDIOC_GETSTATUS:
++	case WDIOC_GETBOOTSTATUS:
++		put_user(0, p);
++		break;
++	case WDIOC_SETOPTIONS:
++		if (get_user(value, p))
++			return -EFAULT;
++		if (value & WDIOS_ENABLECARD)
++			gpio_wdt_start();
++		else if (value & WDIOS_DISABLECARD)
++			gpio_wdt_stop();
++		else
++			return -EINVAL;
++		return 0;
++	case WDIOC_KEEPALIVE:
++		gpio_wdt_reset();
++		break;
++	default:
++		return -ENOTTY;
++	}
++	return 0;
++}
++
++
++static ssize_t gpio_wdt_write(struct file *file, const char *buf,
++						size_t count, loff_t *ppos)
++{
++	if (!count)
++		return -EIO;
++	gpio_wdt_reset();
++	return count;
++}
++
++static const struct file_operations gpio_wdt_fops = {
++	.owner		= THIS_MODULE,
++	.llseek		= no_llseek,
++	.unlocked_ioctl	= gpio_wdt_ioctl,
++	.open		= gpio_wdt_open,
++	.write		= gpio_wdt_write,
++	.release	= gpio_wdt_release,
++};
++
++
++static struct miscdevice gpio_wdt_misc = {
++	.minor	= WATCHDOG_MINOR,
++	.name	= "watchdog",
++	.fops	= &gpio_wdt_fops,
++};
++
++
++static int gpio_wdt_probe(struct platform_device *pdev)
++{
++	int ret;
++	struct gpio_wdt_platform_data *gpio_wdt_data = pdev->dev.platform_data;
++
++	gpio_wdt_device.gpio = gpio_wdt_data->gpio;
++	gpio_wdt_device.interval = gpio_wdt_data->interval;
++	gpio_wdt_device.first_interval = gpio_wdt_data->first_interval;
++	if (gpio_wdt_device.first_interval <= 0) {
++		gpio_wdt_device.first_interval = gpio_wdt_device.interval;
++	}
++
++	ret = gpio_request(gpio_wdt_device.gpio, "gpio-wdt");
++	if (ret < 0) {
++		dev_err(&pdev->dev, "failed to request gpio");
++		return ret;
++	}
++
++	spin_lock_init(&gpio_wdt_device.lock);
++	init_completion(&gpio_wdt_device.stop);
++	gpio_wdt_device.queue = 0;
++	clear_bit(0, &gpio_wdt_device.inuse);
++	timer_setup(&gpio_wdt_device.timer, gpio_wdt_trigger, 0L);
++	gpio_wdt_device.default_ticks = ticks;
++
++	gpio_wdt_start();
++	dev_info(&pdev->dev, "GPIO Hardware Watchdog driver (gpio=%i interval=%i/%i)\n",
++		gpio_wdt_data->gpio, gpio_wdt_data->first_interval, gpio_wdt_data->interval);
++	return 0;
++}
++
++static int gpio_wdt_remove(struct platform_device *pdev)
++{
++	/* FIXME: do we need to lock this test ? */
++	if (gpio_wdt_device.queue) {
++		gpio_wdt_device.queue = 0;
++		wait_for_completion(&gpio_wdt_device.stop);
++	}
++
++	gpio_free(gpio_wdt_device.gpio);
++	misc_deregister(&gpio_wdt_misc);
++	return 0;
++}
++
++static struct platform_driver gpio_wdt_driver = {
++	.probe = gpio_wdt_probe,
++	.remove = gpio_wdt_remove,
++	.driver.name = "gpio-wdt",
++	.driver.owner = THIS_MODULE,
++};
++
++static int __init gpio_wdt_init(void)
++{
++	return platform_driver_register(&gpio_wdt_driver);
++}
++arch_initcall(gpio_wdt_init);
++
++/*
++ * We do wdt initialization in two steps: arch_initcall probes the wdt
++ * very early to start pinging the watchdog (misc devices are not yet
++ * available), and later module_init() just registers the misc device.
++ */
++static int gpio_wdt_init_late(void)
++{
++	int ret;
++
++	ret = misc_register(&gpio_wdt_misc);
++	if (ret < 0) {
++		pr_err("GPIO_WDT: failed to register misc device\n");
++		return ret;
++	}
++	return 0;
++}
++#ifndef MODULE
++module_init(gpio_wdt_init_late);
++#endif
++
++static void __exit gpio_wdt_exit(void)
++{
++	platform_driver_unregister(&gpio_wdt_driver);
++}
++module_exit(gpio_wdt_exit);
++
++MODULE_AUTHOR("Michael Stickel, Florian Fainelli, Mathias Adam");
++MODULE_DESCRIPTION("Driver for GPIO hardware watchdogs");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
++MODULE_ALIAS("platform:gpio-wdt");
+--- /dev/null
++++ b/include/linux/old_gpio_wdt.h
+@@ -0,0 +1,21 @@
++/*
++ *  Definitions for the GPIO watchdog driver
++ *
++ *  Copyright (C) 2013 Mathias Adam <m.adam--linux@adamis.de>
++ *
++ *  This program is free software; you can redistribute it and/or modify
++ *  it under the terms of the GNU General Public License version 2 as
++ *  published by the Free Software Foundation.
++ *
++ */
++
++#ifndef _GPIO_WDT_H_
++#define _GPIO_WDT_H_
++
++struct gpio_wdt_platform_data {
++	int	gpio;		/* GPIO line number */
++	int	interval;	/* watchdog reset interval in system ticks */
++	int	first_interval;	/* first wd reset interval in system ticks */
++};
++
++#endif /* _GPIO_WDT_H_ */
diff --git a/target/linux/bcm47xx/patches-5.4/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch b/target/linux/bcm47xx/patches-5.4/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch
new file mode 100644
index 0000000..6b7ee06
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/900-ssb-reject-PCI-writes-setting-CardBus-bridge-resourc.patch
@@ -0,0 +1,30 @@
+From 5c81397a0147ea59c778d1de14ef54e2268221f6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
+Date: Wed, 8 Apr 2015 06:58:11 +0200
+Subject: [PATCH] ssb: reject PCI writes setting CardBus bridge resources
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+If SoC has a CardBus we can set resources of device at slot 1 only. It's
+impossigle to set bridge resources as it simply overwrites device 1
+configuration and usually results in Data bus error-s.
+
+Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
+---
+ drivers/ssb/driver_pcicore.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/ssb/driver_pcicore.c
++++ b/drivers/ssb/driver_pcicore.c
+@@ -164,6 +164,10 @@ static int ssb_extpci_write_config(struc
+ 	WARN_ON(!pc->hostmode);
+ 	if (unlikely(len != 1 && len != 2 && len != 4))
+ 		goto out;
++	/* CardBus SoCs allow configuring dev 1 resources only */
++	if (extpci_core->cardbusmode && dev != 1 &&
++	    off >= PCI_BASE_ADDRESS_0 && off <= PCI_BASE_ADDRESS_5)
++		goto out;
+ 	addr = get_cfgspace_addr(pc, bus, dev, func, off);
+ 	if (unlikely(!addr))
+ 		goto out;
diff --git a/target/linux/bcm47xx/patches-5.4/940-bcm47xx-yenta.patch b/target/linux/bcm47xx/patches-5.4/940-bcm47xx-yenta.patch
new file mode 100644
index 0000000..c7b4358
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/940-bcm47xx-yenta.patch
@@ -0,0 +1,46 @@
+--- a/drivers/pcmcia/yenta_socket.c
++++ b/drivers/pcmcia/yenta_socket.c
+@@ -921,6 +921,8 @@ static unsigned int yenta_probe_irq(stru
+ 	 * Probe for usable interrupts using the force
+ 	 * register to generate bogus card status events.
+ 	 */
++#ifndef CONFIG_BCM47XX
++	/* WRT54G3G does not like this */
+ 	cb_writel(socket, CB_SOCKET_EVENT, -1);
+ 	cb_writel(socket, CB_SOCKET_MASK, CB_CSTSMASK);
+ 	reg = exca_readb(socket, I365_CSCINT);
+@@ -936,6 +938,7 @@ static unsigned int yenta_probe_irq(stru
+ 	}
+ 	cb_writel(socket, CB_SOCKET_MASK, 0);
+ 	exca_writeb(socket, I365_CSCINT, reg);
++#endif
+ 
+ 	mask = probe_irq_mask(val) & 0xffff;
+ 
+@@ -1020,6 +1023,10 @@ static void yenta_get_socket_capabilitie
+ 	else
+ 		socket->socket.irq_mask = 0;
+ 
++	/* irq mask probing is broken for the WRT54G3G */
++	if (socket->socket.irq_mask == 0)
++		socket->socket.irq_mask = 0x6f8;
++
+ 	dev_info(&socket->dev->dev, "ISA IRQ mask 0x%04x, PCI irq %d\n",
+ 		 socket->socket.irq_mask, socket->cb_irq);
+ }
+@@ -1251,6 +1258,15 @@ static int yenta_probe(struct pci_dev *d
+ 	dev_info(&dev->dev, "Socket status: %08x\n",
+ 		 cb_readl(socket, CB_SOCKET_STATE));
+ 
++	/* Generate an interrupt on card insert/remove */
++	config_writew(socket, CB_SOCKET_MASK, CB_CSTSMASK | CB_CDMASK);
++
++	/* Set up Multifunction Routing Status Register */
++	config_writew(socket, 0x8C, 0x1000 /* MFUNC3 to GPIO3 */ | 0x2 /* MFUNC0 to INTA */);
++
++	/* Switch interrupts to parallelized */
++	config_writeb(socket, 0x92, 0x64);
++
+ 	yenta_fixup_parent_bridge(dev->subordinate);
+ 
+ 	/* Register it with the pcmcia layer.. */
diff --git a/target/linux/bcm47xx/patches-5.4/976-ssb_increase_pci_delay.patch b/target/linux/bcm47xx/patches-5.4/976-ssb_increase_pci_delay.patch
new file mode 100644
index 0000000..99aa188
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/976-ssb_increase_pci_delay.patch
@@ -0,0 +1,11 @@
+--- a/drivers/ssb/driver_pcicore.c
++++ b/drivers/ssb/driver_pcicore.c
+@@ -390,7 +390,7 @@ static void ssb_pcicore_init_hostmode(st
+ 	set_io_port_base(ssb_pcicore_controller.io_map_base);
+ 	/* Give some time to the PCI controller to configure itself with the new
+ 	 * values. Not waiting at this point causes crashes of the machine. */
+-	mdelay(10);
++	mdelay(300);
+ 	register_pci_controller(&ssb_pcicore_controller);
+ }
+ 
diff --git a/target/linux/bcm47xx/patches-5.4/999-wl_exports.patch b/target/linux/bcm47xx/patches-5.4/999-wl_exports.patch
new file mode 100644
index 0000000..7dcf028
--- /dev/null
+++ b/target/linux/bcm47xx/patches-5.4/999-wl_exports.patch
@@ -0,0 +1,24 @@
+--- a/drivers/firmware/broadcom/bcm47xx_nvram.c
++++ b/drivers/firmware/broadcom/bcm47xx_nvram.c
+@@ -30,7 +30,8 @@ struct nvram_header {
+ 	u32 config_ncdl;	/* ncdl values for memc */
+ };
+ 
+-static char nvram_buf[NVRAM_SPACE];
++char nvram_buf[NVRAM_SPACE];
++EXPORT_SYMBOL(nvram_buf);
+ static size_t nvram_len;
+ static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000};
+ static int cfe_env;
+--- a/arch/mips/mm/cache.c
++++ b/arch/mips/mm/cache.c
+@@ -62,6 +62,9 @@ void (*_dma_cache_wback_inv)(unsigned lo
+ void (*_dma_cache_wback)(unsigned long start, unsigned long size);
+ void (*_dma_cache_inv)(unsigned long start, unsigned long size);
+ 
++EXPORT_SYMBOL(_dma_cache_wback_inv);
++EXPORT_SYMBOL(_dma_cache_inv);
++
+ #endif /* CONFIG_DMA_NONCOHERENT */
+ 
+ /*