diff --git a/target/linux/apm821xx/base-files/etc/board.d/01_leds b/target/linux/apm821xx/base-files/etc/board.d/01_leds
index bcc7a9f1015..48fe139ce96 100755
--- a/target/linux/apm821xx/base-files/etc/board.d/01_leds
+++ b/target/linux/apm821xx/base-files/etc/board.d/01_leds
@@ -19,6 +19,14 @@ mr24)
 mbl)
 	;;
 
+wndr4700)
+	ucidef_set_led_netdev "wan" "WAN (green)" "wndr4700:green:wan" "eth0.2"
+	ucidef_set_led_usbdev "usb3-1" "USB3-1" "wndr4700:blue:usb" "2-1"
+	ucidef_set_led_usbdev "usb3-2" "USB3-2" "wndr4700:blue:usb" "3-1"
+	ucidef_set_led_wlan "wlan2g" "WLAN2G" "wndr4700:blue:wlan" "phy0tpt"
+	ucidef_set_led_wlan "wlan5g" "WLAN5G" "wndr4700:blue:wlan" "phy1tpt"
+	;;
+
 *)
 	;;
 esac
diff --git a/target/linux/apm821xx/base-files/etc/board.d/02_network b/target/linux/apm821xx/base-files/etc/board.d/02_network
index 9525d8c5f21..3a25709a2a3 100755
--- a/target/linux/apm821xx/base-files/etc/board.d/02_network
+++ b/target/linux/apm821xx/base-files/etc/board.d/02_network
@@ -13,6 +13,12 @@ mbl | \
 mr24)
 	ucidef_set_interface_lan "eth0"
 	;;
+
+wndr4700)
+	ucidef_add_switch "switch0" \
+		"0@eth0" "4:lan" "3:lan" "2:lan" "1:lan" "5:wan"
+	;;
+
 *)
 	ucidef_set_interfaces_lan_wan "eth0" "eth1"
 	;;
diff --git a/target/linux/apm821xx/base-files/etc/diag.sh b/target/linux/apm821xx/base-files/etc/diag.sh
index bea66eb14de..3ddd21d844b 100755
--- a/target/linux/apm821xx/base-files/etc/diag.sh
+++ b/target/linux/apm821xx/base-files/etc/diag.sh
@@ -13,6 +13,10 @@ get_status_led() {
 		status_led="mr24:green:power"
 		;;
 
+	wndr4700)
+		status_led="wndr4700:green:power"
+		;;
+
 	*)
 		;;
 	esac
diff --git a/target/linux/apm821xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/apm821xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom
new file mode 100644
index 00000000000..dfdc548d636
--- /dev/null
+++ b/target/linux/apm821xx/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom
@@ -0,0 +1,89 @@
+#!/bin/sh
+
+[ -e /lib/firmware/$FIRMWARE ] && exit 0
+
+. /lib/apm821xx.sh
+. /lib/functions.sh
+. /lib/functions/system.sh
+
+ath9k_eeprom_die() {
+	echo "ath9k eeprom: " "$*"
+	exit 1
+}
+
+ath9k_eeprom_extract() {
+	local part=$1
+	local offset=$2
+	local count=$3
+	local mtd
+
+	mtd=$(find_mtd_chardev $part)
+	[ -n "$mtd" ] || \
+		ath9k_eeprom_die "no mtd device found for partition $part"
+
+	dd if=$mtd of=/lib/firmware/$FIRMWARE bs=1 skip=$offset count=$count 2>/dev/null || \
+		ath9k_eeprom_die "failed to extract from $mtd"
+}
+
+ath9k_ubi_eeprom_extract() {
+	local part=$1
+	local offset=$2
+	local count=$3
+	local ubidev=$(nand_find_ubi $CI_UBIPART)
+	local ubi
+
+	ubi=$(nand_find_volume $ubidev $part)
+	[ -n "$ubi" ] || \
+		ath9k_eeprom_die "no UBI volume found for $part"
+
+	dd if=/dev/$ubi of=/lib/firmware/$FIRMWARE bs=1 skip=$offset count=$count 2>/dev/null || \
+		ath9k_eeprom_die "failed to extract from $ubi"
+}
+
+ath9k_patch_firmware_mac() {
+        local mac=$1
+
+        [ -z "$mac" ] && return
+
+        macaddr_2bin $mac | dd of=/lib/firmware/$FIRMWARE conv=notrunc bs=1 seek=2 count=6
+}
+
+board=$(apm821xx_board_name)
+
+case "$FIRMWARE" in
+"pci_wmac0.eeprom")
+	case $board in
+	wndr4700)
+		. /lib/upgrade/nand.sh
+
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_ubi_eeprom_extract "caldata" 20480 4096
+		else
+			ath9k_eeprom_extract "wifi_data" 20480 4096
+			ath9k_patch_firmware_mac $(mtd_get_mac_binary wifi_data 12)
+		fi
+		;;
+	*)
+		ath9k_eeprom_die "board $board is not supported yet"
+		;;
+	esac
+	;;
+
+"pci_wmac1.eeprom")
+	case $board in
+	wndr4700)
+		. /lib/upgrade/nand.sh
+
+		if [ -n "$(nand_find_volume ubi0 caldata)" ]; then
+			ath9k_ubi_eeprom_extract "caldata" 4096 4096
+		else
+			ath9k_eeprom_extract "wifi_data" 4096 4096
+			ath9k_patch_firmware_mac $(mtd_get_mac_binary wifi_data 0)
+		fi
+		;;
+	*)
+		ath9k_eeprom_die "board $board is not supported yet"
+		;;
+	esac
+	;;
+esac
diff --git a/target/linux/apm821xx/base-files/lib/apm821xx.sh b/target/linux/apm821xx/base-files/lib/apm821xx.sh
index 98e88cff6ed..78fe452184f 100755
--- a/target/linux/apm821xx/base-files/lib/apm821xx.sh
+++ b/target/linux/apm821xx/base-files/lib/apm821xx.sh
@@ -18,6 +18,10 @@ apm821xx_board_detect() {
 		name="mbl"
 		;;
 
+	*"Netgear WNDR4700/WNDR4720 Series")
+		name="wndr4700"
+		;;
+
 	*)
 		name="unknown"
 		;;
diff --git a/target/linux/apm821xx/base-files/lib/upgrade/platform.sh b/target/linux/apm821xx/base-files/lib/upgrade/platform.sh
index d5b0986dd5a..5559767a266 100755
--- a/target/linux/apm821xx/base-files/lib/upgrade/platform.sh
+++ b/target/linux/apm821xx/base-files/lib/upgrade/platform.sh
@@ -21,6 +21,11 @@ platform_check_image() {
 		return $?;
 		;;
 
+	wndr4700)
+		nand_do_platform_check $board "$1"
+		return $?;
+		;;
+
 	*)
 		;;
 	esac
@@ -37,6 +42,10 @@ platform_pre_upgrade() {
 		merakinand_do_upgrade "$1"
 		;;
 
+	wndr4700)
+		nand_do_upgrade "$1"
+		;;
+
 	*)
 		;;
 	esac
diff --git a/target/linux/apm821xx/config-4.4 b/target/linux/apm821xx/config-4.4
index 804e7e852f4..1515dda73bd 100644
--- a/target/linux/apm821xx/config-4.4
+++ b/target/linux/apm821xx/config-4.4
@@ -319,6 +319,7 @@ CONFIG_TICK_CPU_ACCOUNTING=y
 CONFIG_USB_SUPPORT=y
 CONFIG_VDSO32=y
 # CONFIG_WARP is not set
+# CONFIG_WNDR4700 is not set
 CONFIG_WORD_SIZE=32
 # CONFIG_XILINX_SYSACE is not set
 # CONFIG_XILINX_VIRTEX440_GENERIC_BOARD is not set
@@ -328,3 +329,4 @@ CONFIG_XZ_DEC_POWERPC=y
 CONFIG_ZLIB_DEFLATE=y
 CONFIG_ZLIB_INFLATE=y
 # CONFIG_JFFS2_FS is not set
+# CONFIG_SENSORS_TC654 is not set
diff --git a/target/linux/apm821xx/dts/wndr4700.dts b/target/linux/apm821xx/dts/wndr4700.dts
new file mode 100644
index 00000000000..9a2ceb9d75f
--- /dev/null
+++ b/target/linux/apm821xx/dts/wndr4700.dts
@@ -0,0 +1,762 @@
+/*
+ * Device Tree Source for Netgear WNDR4700/WNDR4720 Series
+ *
+ * Copyright 2008 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without
+ * any warranty of any kind, whether express or implied.
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/thermal/thermal.h>
+
+/ {
+	#address-cells = <2>;
+	#size-cells = <1>;
+	model = "Netgear WNDR4700/WNDR4720 Series";
+	compatible = "netgear,wndr4700";
+	dcr-parent = <&{/cpus/cpu@0}>;
+
+	aliases {
+		ethernet0 = &EMAC0;
+		serial0 = &UART0;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			model = "PowerPC,apm82181";
+			reg = <0x00000000>;
+			clock-frequency = <0>; /* Filled in by U-Boot */
+			timebase-frequency = <0>; /* Filled in by U-Boot */
+			i-cache-line-size = <32>;
+			d-cache-line-size = <32>;
+			i-cache-size = <32768>;
+			d-cache-size = <32768>;
+			dcr-controller;
+			dcr-access-method = "native";
+			next-level-cache = <&L2C0>;
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x00000000 0x00000000 0x00000000>; /* Filled in by U-Boot */
+	};
+
+	UIC0: interrupt-controller0 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <0>;
+		dcr-reg = <0x0c0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+	};
+
+	UIC1: interrupt-controller1 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <1>;
+		dcr-reg = <0x0d0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+		interrupts = <0x1e 0x4 0x1f 0x4>; /* cascade */
+		interrupt-parent = <&UIC0>;
+	};
+
+	UIC2: interrupt-controller2 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <2>;
+		dcr-reg = <0x0e0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+		interrupts = <0xa 0x4 0xb 0x4>; /* cascade */
+		interrupt-parent = <&UIC0>;
+	};
+
+	UIC3: interrupt-controller3 {
+		compatible = "ibm,uic-460ex","ibm,uic";
+		interrupt-controller;
+		cell-index = <3>;
+		dcr-reg = <0x0f0 0x009>;
+		#address-cells = <0>;
+		#size-cells = <0>;
+		#interrupt-cells = <2>;
+		interrupts = <0x10 0x4 0x11 0x4>; /* cascade */
+		interrupt-parent = <&UIC0>;
+	};
+
+	OCM1: ocm@400040000 {
+		compatible = "ibm,ocm";
+		status = "okay";
+		cell-index = <1>;
+		/* configured in U-Boot */
+		reg = <4 0x00040000 0x8000>; /* 32K */
+	};
+
+	SDR0: sdr {
+		compatible = "ibm,sdr-460ex";
+		dcr-reg = <0x00e 0x002>;
+	};
+
+	CPR0: cpr {
+		compatible = "ibm,cpr-460ex";
+		dcr-reg = <0x00c 0x002>;
+	};
+
+	L2C0: l2c {
+		compatible = "ibm,l2-cache-apm82181", "ibm,l2-cache";
+		dcr-reg = <0x020 0x008
+			   0x030 0x008>;
+		cache-line-size = <32>;
+		cache-size = <262144>;
+		interrupt-parent = <&UIC1>;
+		interrupts = <11 1>;
+	};
+
+	CPM0: cpm {
+		compatible = "ibm,cpm-apm821xx", "ibm,cpm";
+		cell-index = <0>;
+		dcr-reg = <0x160 0x003>;
+		pm-cpu = <0x02000000>;
+		pm-doze = <0x302570F0>;
+		pm-nap = <0x302570F0>;
+		pm-deepsleep = <0x302570F0>;
+		pm-iic-device = <&IIC0>;
+		pm-emac-device = <&EMAC0>;
+	};
+
+	plb {
+		compatible = "ibm,plb-460ex", "ibm,plb4";
+		#address-cells = <2>;
+		#size-cells = <1>;
+		ranges;
+		clock-frequency = <0>; /* Filled in by U-Boot */
+
+		SDRAM0: sdram {
+			compatible = "ibm,sdram-460ex", "ibm,sdram-405gp";
+			dcr-reg = <0x010 0x002>;
+		};
+
+		RTC: rtc {
+			compatible = "ibm,rtc";
+			dcr-reg = <0x240 0x009>;
+			interrupts = <0x1a 0x4>;
+			interrupt-parent = <&UIC2>;
+
+		};
+
+		CRYPTO: crypto@180000 {
+			compatible = "amcc,ppc460ex-crypto", "amcc,ppc4xx-crypto";
+			reg = <4 0x00180000 0x80400>;
+			interrupt-parent = <&UIC0>;
+			interrupts = <0x1d 0x4>;
+		};
+
+		PKA: pka@114000 {
+			device_type = "pka";
+			compatible = "ppc4xx-pka", "amcc,ppc4xx-pka", "amcc, ppc4xx-pka";
+			reg = <4 0x00114000 0x4000>;
+			interrupt-parent = <&UIC0>;
+			interrupts = <0x14 0x1>;
+		};
+
+		TRNG: trng@110000 {
+			device_type = "trng";
+			compatible = "amcc,ppc460ex-rng", "ppc4xx-rng", "amcc, ppc4xx-trng";
+			reg = <4 0x00110000 0x100>;
+			interrupt-parent = <&UIC1>;
+			interrupts = <0x3 0x4>;
+		};
+
+		MAL0: mcmal {
+			compatible = "ibm,mcmal-460ex", "ibm,mcmal2";
+			descriptor-memory = "ocm";
+			dcr-reg = <0x180 0x062>;
+			num-tx-chans = <1>;
+			num-rx-chans = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-parent = <&UIC2>;
+			interrupts = <	/*TXEOB*/ 0x6 0x4
+					/*RXEOB*/ 0x7 0x4
+					/*SERR*/  0x3 0x4
+					/*TXDE*/  0x4 0x4
+					/*RXDE*/  0x5 0x4
+					/*TX0 COAL*/  0x8 0x2
+                                        /*TX1 COAL  0x9 0x2*/
+                                        /*RX0 COAL*/  0xc 0x2
+                                        /*RX1 COAL  0xd 0x2*/>;
+		};
+
+		AHBDMA: dma@bffd0800 {
+			compatible = "snps,dma-spear1340";
+			reg = <4 0xbffd0800 0x400>;
+			interrupt-parent = <&UIC0>;
+			interrupts = <25 4>;
+			#dma-cells = <3>;
+			/* use autoconfiguration for the dma setup */
+		};
+
+		SATA0: sata@bffd1000 {
+			compatible = "amcc,sata-460ex";
+			reg = <4 0xbffd1000 0x800>;
+			interrupt-parent = <&UIC0>;
+			interrupts = <26 4>;
+			dmas = <&AHBDMA 0 0 1>;
+			dma-names = "sata-dma";
+			status = "disabled"; /* disabled by uboot */
+		};
+
+		SATA1: sata@bffd1800 {
+			compatible = "amcc,sata-460ex";
+			reg = <4 0xbffd1800 0x800>;
+			interrupt-parent = <&UIC0>;
+			interrupts = <27 4>;
+			dmas = <&AHBDMA 1 0 2>;
+			dma-names = "sata-dma";
+		};
+
+		USBOTG0: usbotg@bff80000 {
+			compatible = "netgear,wndr4700-usb";
+			reg = <4 0xbff80000 0x10000>;
+			interrupt-parent = <&USBOTG0>;
+			interrupts = <0 1 2>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = </* USB-OTG */		0 &UIC2 0x1c 4
+					 /* HIGH-POWER */	1 &UIC1 0x1a 8
+					 /* DMA */		2 &UIC0 0xc 4>;
+			dr_mode = "host";
+		};
+
+		POB0: opb {
+			compatible = "ibm,opb-460ex", "ibm,opb";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0xb0000000 0x00000004 0xb0000000 0x50000000>;
+			clock-frequency = <0>; /* Filled in by U-Boot */
+
+			EBC0: ebc {
+				compatible = "ibm,ebc-460ex", "ibm,ebc";
+				dcr-reg = <0x012 0x002>;
+				#address-cells = <2>;
+				#size-cells = <1>;
+				clock-frequency = <0>; /* Filled in by U-Boot */
+				/* ranges property is supplied by U-Boot */
+				ranges = < 0x00000003 0x00000000 0xe0000000 0x8000000>;
+				interrupts = <0x6 0x4>;
+				interrupt-parent = <&UIC1>;
+
+				nor_flash@0,0 {
+					compatible = "amd,s29gl512n", "cfi-flash";
+					bank-width = <1>;
+					reg = <0x00000000 0x00000000 0x00100000>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					status = "disabled";
+
+					partition@0 {
+						label = "back-up";
+						reg = <0x00000000 0x00080000>;
+					};
+					partition@1 {
+						label = "u-boot";
+						reg = <0x00080000 0x0080000>;
+					};
+				};
+				ndfc@1,0 {
+					compatible = "ibm,ndfc";
+					reg = <00000003 00000000 00002000>;
+					ccr = <0x00001000>;
+					bank-settings = <0x80002222>;
+					#address-cells = <1>;
+					#size-cells = <1>;
+					/*128 MiB Nand Flash*/
+					nand {
+						#address-cells = <1>;
+						#size-cells = <1>;
+
+						partition0,0@0x00000000 {
+							label = "NAND 128MiB 3,3V 8-bit";
+							reg = <0x00000000 0x08000000>;
+							read-only;
+						};
+
+						partition0,1@0x00000000 {
+							label = "uboot";
+							reg = <0x00000000 0x00180000>;
+							read-only;
+						};
+
+						partition0,2@0x00180000 {
+							label = "device-tree";
+							reg = <0x00180000 0x00020000>;
+							read-only;
+						};
+
+						partition0,3@0x001a0000 {
+							label = "kernel";
+							reg = <0x001a0000 0x001e0000>;
+							/*
+							 * will also contain a fake/empty
+							 * rootfs to fool Netgear's uboot
+							 * rootfs integrety checks.
+							 */
+						};
+
+						partition0,4@0x00380000 {
+							label = "ubi";
+							reg = <0x00380000 0x01660000>;
+						};
+
+						partition0,5@0x019e0000 {
+							label = "config";
+							reg = <0x019e0000 0x00080000>;
+							read-only;
+						};
+
+						partition0,6@0x01a60000 {
+							label = "pot";
+							reg = <0x01a60000 0x00080000>;
+							read-only;
+						};
+
+						partition0,7@0x01ae0000 {
+							label = "traffic_meter";
+							reg = <0x01ae0000 0x00300000>;
+							read-only;
+						};
+
+						partition0,8@0x01de0000 {
+							label = "language";
+							reg = <0x01de0000 0x001c0000>;
+							read-only;
+						};
+
+						partition0,9@0x01fa0000 {
+							label = "ecos";
+							reg = <0x01fa0000 0x06020000>;
+							read-only;
+						};
+
+						partition0,10@0x07fc0000 {
+							label = "wifi_data";
+							reg = <0x07fc0000 0x00040000>;
+							read-only;
+						};
+
+						partition0,11@0x00180000 {
+							label = "firmware";
+							reg = <0x00180000 0x01860000>;
+							read-only;
+						};
+					};
+				};
+			};
+
+			UART0: serial@ef600300 {
+				device_type = "serial";
+				compatible = "ns16550";
+				reg = <0xef600300 0x00000008>;
+				virtual-reg = <0xef600300>;
+				clock-frequency = <0>; /* Filled in by U-Boot */
+				current-speed = <0>; /* Filled in by U-Boot */
+				interrupt-parent = <&UIC1>;
+				interrupts = <0x1 0x4>;
+			};
+
+			GPIO0: gpio@ef600b00 {
+				compatible = "ibm,ppc4xx-gpio";
+				reg = <0xef600b00 0x00000048>;
+				#gpio-cells = <2>;
+				gpio-controller;
+				#interrupt-cells = <2>;
+				interrupt-controller;
+
+				interrupts-extended = <&UIC1 0x14>,
+						      <&UIC1 0x1e>,
+						      <&UIC1 0x1f>,
+						      <&UIC2 0x19>;
+			};
+
+			gpio_keys_polled {
+				compatible = "gpio-keys-polled";
+				#address-cells = <1>;
+				#size-cells = <0>;
+				#interrupt-cells = <2>;
+				autorepeat;
+				poll-interval = <60>;   /* 3 * 20 = 60ms */
+
+				reset {
+					label = "Reset button";
+					linux,code = <0x198>; /* KEY_RESTART */
+					gpios = <&GPIO0 15 0>;
+					interrupt-parent = <&UIC1>;
+					interrupts = <0x14 0x2>;
+				};
+
+				backup_hd {
+					label = "Backup HD button";
+					gpios = <&GPIO0 19 0>;
+					linux,code = <0x100>; /* BTN_0 */
+					interrupt-parent = <&UIC1>;
+					interrupts = <0x1e 0x2>;
+				};
+
+				rfkill {
+					label = "RFKILL button";
+					gpios = <&GPIO0 20 0>;
+					linux,code = <0xf7>; /* KEY_RFKILL */
+					interrupt-parent = <&UIC1>;
+					interrupts = <0x1f 0x2>;
+				};
+
+				wps {
+					label = "WPS button";
+					gpios = <&GPIO0 23 0>;
+					linux,code = <0x211>; /* KEY_WPS_BUTTON */
+					interrupt-parent = <&UIC2>;
+					interrupts = <0x19 0x2>;
+				};
+
+				sdcard {
+					label = "SDCard inserted";
+					gpios = <&GPIO0 7 0>;
+					linux,code = <0x101>; /* BTN_1 */
+				};
+			};
+
+			gpio-leds {
+				compatible = "gpio-leds";
+				power-green {
+					label = "wndr4700:green:power";
+					gpios = <&GPIO0 8 0>;
+				};
+
+				power-orange {
+					label = "wndr4700:orange:power";
+					gpios = <&GPIO0 9 1>;
+					linux,default-trigger = "panic";
+				};
+
+				usb-blue {
+					label = "wndr4700:blue:usb";
+					gpios = <&GPIO0 10 0>;
+				};
+
+				logo-white {
+					label = "wndr4700:white:logo";
+					gpios = <&GPIO0 11 0>;
+				};
+
+				wan-yellow {
+					label = "wndr4700:yellow:wan";
+					gpios = <&GPIO0 3 0>;
+				};
+
+				wan-green {
+					label = "wndr4700:green:wan";
+					gpios = <&GPIO0 12 0>;
+				};
+
+				hd-green {
+					label = "wndr4700:green:hd";
+					gpios = <&GPIO0 14 0>;
+				};
+
+				hd-red {
+					label = "wndr4700:red:hd";
+					gpios = <&GPIO0 17 0>;
+				};
+
+				wlan-blue {
+					label = "wndr4700:blue:wlan";
+					gpios = <&GPIO0 18 0>;
+				};
+			};
+
+			IIC0: i2c@ef600700 {
+				compatible = "ibm,iic-460ex", "ibm,iic";
+				reg = <0xef600700 0x00000014>;
+				interrupt-parent = <&UIC0>;
+				interrupts = <0x2 0x4>;
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				fan0: fan@1b {
+					compatible = "microchip,tc654";
+					reg = <0x1b>;
+					cooling-min-level = <0>;
+					cooling-max-level = <255>;
+					#cooling-cells = <2>; /* min followed by max */
+
+					gpios = <&GPIO0 16 1>; /* fan status */
+					alarm-gpios = <&GPIO0 5 1>; /* fault */
+					interrupt-parent = <&UIC3>;
+					interrupts = <0x16 0x2>; /* fault */
+				};
+
+				temp0: temp@4d {
+					compatible = "gmt,g781";
+					reg = <0x4d>;
+					#thermal-sensor-cells = <1>;
+
+					/*
+					 * The LM90 has two sensors:
+					 *   temp0 -> internal to LM90
+					 *   temp1 -> external NTC near CPU
+					 */
+				};
+			};
+
+			IIC1: i2c@ef600800 {
+				compatible = "ibm,iic-460ex", "ibm,iic";
+				reg = <0xef600800 0x00000014>;
+				interrupt-parent = <&UIC0>;
+				interrupts = <0x3 0x4>;
+			};
+
+			RGMII0: emac-rgmii@ef601500 {
+				compatible = "ibm,rgmii-405ex", "ibm,rgmii";
+				reg = <0xef601500 0x00000008>;
+				has-mdio;
+			};
+
+			TAH0: emac-tah@ef601350 {
+				compatible = "ibm,tah-460ex", "ibm,tah";
+				reg = <0xef601350 0x00000030>;
+			};
+
+			EMAC0: ethernet@ef600c00 {
+				device_type = "network";
+				compatible = "ibm,emac-apm821xx", "ibm,emac4sync";
+				interrupt-parent = <&EMAC0>;
+				interrupts = <0x0 0x1>;
+				#interrupt-cells = <1>;
+				#address-cells = <0>;
+				#size-cells = <0>;
+				interrupt-map = </*Status*/ 0x0 &UIC2 0x10 0x4
+						 /*Wake*/   0x1 &UIC2 0x14 0x4>;
+				reg = <0xef600c00 0x000000c4>;
+				local-mac-address = [000000000000]; /* Filled in by U-Boot */
+				mal-device = <&MAL0>;
+				mal-tx-channel = <0>;
+				mal-rx-channel = <0>;
+				cell-index = <0>;
+				max-frame-size = <9000>;
+				rx-fifo-size = <16384>;
+				tx-fifo-size = <2048>;
+				fifo-entry-size = <10>;
+				phy-mode = "rgmii";
+				phy-handle = <&phy0>;
+				phy-map = <0x00000000>;
+				rgmii-device = <&RGMII0>;
+				rgmii-channel = <0>;
+				tah-device = <&TAH0>;
+				tah-channel = <0>;
+				has-inverted-stacr-oc;
+				has-new-stacr-staopc;
+
+				mdio {
+					#address-cells = <1>;
+					#size-cells = <0>;
+
+					phy0: ethernet-phy@0 {
+						device_type = "ethernet-phy";
+						reg = <0>;
+						qca,ar8327-initvals = <
+							0x0010 0x40000000
+							0x0624 0x007f7f7f
+							0x0004 0x07a00000	/* PAD0_MODE */
+							0x000c 0x01000000	/* PAD6_MODE */
+							0x007c 0x0000007e	/* PORT0_STATUS */
+						>;
+					};
+				};
+			};
+		};
+
+		PCIE0: pciex@d00000000 {
+			device_type = "pci";
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			compatible = "ibm,plb-pciex-apm821xx", "ibm,plb-pciex";
+			primary;
+			port = <0x0>; /* port number */
+			reg = <0x0000000d 0x00000000 0x20000000	/* Config space access */
+			       0x0000000c 0x08010000 0x00001000>;	/* Registers */
+			dcr-reg = <0x100 0x020>;
+			sdr-base = <0x300>;
+
+			/* Outbound ranges, one memory and one IO,
+			 * later cannot be changed
+			 */
+			ranges = <0x02000000 0x00000000 0x80000000 0x0000000e 0x00000000 0x00000000 0x80000000
+				  0x02000000 0x00000000 0x00000000 0x0000000f 0x00000000 0x00000000 0x00100000
+				  0x01000000 0x00000000 0x00000000 0x0000000f 0x80000000 0x00000000 0x00010000>;
+
+			/* Inbound 2GB range starting at 0 */
+			dma-ranges = <0x42000000 0x0 0x0 0x0 0x0 0x0 0x80000000>;
+
+			/* This drives busses 40 to 0x7f */
+			bus-range = <0x40 0x7f>;
+
+			/* Legacy interrupts (note the weird polarity, the bridge seems
+			 * to invert PCIe legacy interrupts).
+			 * We are de-swizzling here because the numbers are actually for
+			 * port of the root complex virtual P2P bridge. But I want
+			 * to avoid putting a node for it in the tree, so the numbers
+			 * below are basically de-swizzled numbers.
+			 * The real slot is on idsel 0, so the swizzling is 1:1
+			 */
+			interrupt-map-mask = <0x0 0x0 0x0 0x7>;
+			interrupt-map = <
+				0x0 0x0 0x0 0x1 &UIC3 0xc 0x4 /* swizzled int A */
+				0x0 0x0 0x0 0x2 &UIC3 0xd 0x4 /* swizzled int B */
+				0x0 0x0 0x0 0x3 &UIC3 0xe 0x4 /* swizzled int C */
+				0x0 0x0 0x0 0x4 &UIC3 0xf 0x4 /* swizzled int D */>;
+		};
+
+		MSI: ppc4xx-msi@C10000000 {
+			compatible = "amcc,ppc4xx-msi", "ppc4xx-msi";
+			reg = < 0xC 0x10000000 0x100
+				0xC 0x10000000 0x100>;
+			sdr-base = <0x36C>;
+			msi-data = <0x00004440>;
+			msi-mask = <0x0000ffe0>;
+			interrupts =<0 1 2 3 4 5 6 7>;
+			interrupt-parent = <&MSI>;
+			#interrupt-cells = <1>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			msi-available-ranges = <0x0 0x100>;
+			interrupt-map = <
+				0 &UIC3 0x18 1
+				1 &UIC3 0x19 1
+				2 &UIC3 0x1A 1
+				3 &UIC3 0x1B 1
+				4 &UIC3 0x1C 1
+				5 &UIC3 0x1D 1
+				6 &UIC3 0x1E 1
+				7 &UIC3 0x1F 1
+			>;
+		};
+	};
+
+	chosen {
+		linux,stdout-path = "/plb/opb/serial@ef600300";
+	};
+
+	thermal-zones {
+		cpu_thermal: cpu-thermal {
+			polling-delay-passive = <10000>; /* milliseconds */
+			polling-delay = <20000>; /* milliseconds */
+
+			thermal-sensors = <&temp0 1>;
+
+			trips {
+				/*
+				 * Once the thermal governers are a bit smarter
+				 * and do hysteresis properly, we can disable
+				 * the fan when the HDD and CPU has < 39 C.
+				 */
+				cpu_alert0: cpu-alert0 {
+					temperature = <25000>;
+					hysteresis = <2000>;
+					type = "active";
+				};
+
+				cpu_alert1: cpu-alert1 {
+					temperature = <27000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "active";
+				};
+
+				cpu_alert2: cpu-alert2 {
+					temperature = <65000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "active";
+				};
+
+				cpu_alert3: cpu-alert3 {
+					temperature = <70000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "active";
+				};
+
+				cpu_alert4: cpu-alert4 {
+					temperature = <75000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "active";
+				};
+
+				cpu_alert5: cpu-alert5 {
+					temperature = <80000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "active";
+				};
+
+				cpu_alert6: cpu-alert6 {
+					temperature = <850000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "active";
+				};
+
+				cpu_crit: cpu-crit {
+					temperature = <90000>; /* millicelsius */
+					hysteresis = <2000>; /* millicelsius */
+					type = "critical";
+				};
+			};
+
+			cooling-maps {
+				map0 {
+					trip = <&cpu_alert0>;
+					cooling-device = <&fan0 THERMAL_NO_LIMIT 0>;
+				};
+
+				map1 {
+					trip = <&cpu_alert1>;
+					cooling-device = <&fan0 1 87>;
+				};
+
+				map2 {
+					trip = <&cpu_alert2>;
+					cooling-device = <&fan0 88 100>;
+				};
+
+				map3 {
+					trip = <&cpu_alert3>;
+					cooling-device = <&fan0 101 147>;
+				};
+
+				map4 {
+					trip = <&cpu_alert4>;
+					cooling-device = <&fan0 148 207>;
+				};
+
+				map5 {
+					trip = <&cpu_alert5>;
+					cooling-device = <&fan0 208 231>;
+				};
+
+				map6 {
+					trip = <&cpu_alert6>;
+					cooling-device =<&fan0 232 THERMAL_NO_LIMIT>;
+				};
+			};
+		};
+	};
+};
diff --git a/target/linux/apm821xx/files/arch/powerpc/platforms/44x/wndr4700.c b/target/linux/apm821xx/files/arch/powerpc/platforms/44x/wndr4700.c
new file mode 100644
index 00000000000..975ac461fe7
--- /dev/null
+++ b/target/linux/apm821xx/files/arch/powerpc/platforms/44x/wndr4700.c
@@ -0,0 +1,96 @@
+/*
+ * Netgear Centria N900 WNDR4700/WNDR4720 platform support
+ *
+ * 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; version 2 of the License.
+ *
+ * This implemention is based on the simple platform support for the
+ * PowerPC 44x chips.
+ */
+
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/ppc4xx.h>
+#include <asm/prom.h>
+#include <asm/time.h>
+#include <asm/udbg.h>
+#include <asm/uic.h>
+
+#include <linux/init.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/ath9k_platform.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <asm/unaligned.h>
+
+static const struct of_device_id ppc44x_of_bus[] __initconst = {
+	{ .compatible = "ibm,plb4", },
+	{ .compatible = "ibm,opb", },
+	{ .compatible = "ibm,ebc", },
+	{ .compatible = "simple-bus", },
+	{},
+};
+
+static int __init ppc44x_device_probe(void)
+{
+	of_platform_bus_probe(NULL, ppc44x_of_bus, NULL);
+
+	return 0;
+}
+machine_device_initcall(wndr4700, ppc44x_device_probe);
+
+static char *board[] __initdata = {
+	"netgear,wndr4700",
+};
+
+static int __init ppc44x_probe(void)
+{
+	unsigned long root = of_get_flat_dt_root();
+	int i = 0;
+
+	pcie_bus_config = PCIE_BUS_PEER2PEER; /* force 128 Byte MPS */
+
+	for (i = 0; i < ARRAY_SIZE(board); i++) {
+		if (of_flat_dt_is_compatible(root, board[i])) {
+			pci_set_flags(PCI_REASSIGN_ALL_RSRC); /* PCI_PROBE_ONLY */
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+define_machine(wndr4700) {
+	.name = "WNDR4700 Platform",
+	.probe = ppc44x_probe,
+	.progress = udbg_progress,
+	.init_IRQ = uic_init_tree,
+	.get_irq = uic_get_irq,
+	.restart = ppc4xx_reset_system,
+	.calibrate_decr = generic_calibrate_decr,
+};
+
+static struct ath9k_platform_data ar9380_wmac0_data = {
+	.led_pin = -1,
+	.eeprom_name = "pci_wmac1.eeprom",
+};
+static struct ath9k_platform_data ar9580_wmac1_data = {
+	.led_pin = -1,
+	.eeprom_name = "pci_wmac0.eeprom",
+};
+
+static void load_eeprom_ar9380(struct pci_dev *dev)
+{
+	dev->dev.platform_data = &ar9380_wmac0_data;
+}
+
+static void load_eeprom_ar9580(struct pci_dev *dev)
+{
+	dev->dev.platform_data = &ar9580_wmac1_data;
+}
+
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATHEROS, 0x0030, load_eeprom_ar9380);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATHEROS, 0x0033, load_eeprom_ar9580);
diff --git a/target/linux/apm821xx/image/Makefile b/target/linux/apm821xx/image/Makefile
index 9ba43d9db26..08173c603fd 100644
--- a/target/linux/apm821xx/image/Makefile
+++ b/target/linux/apm821xx/image/Makefile
@@ -78,6 +78,88 @@ define Device/mr24
 endef
 TARGET_DEVICES += mr24
 
+define Build/create-uImage-dtb
+	# flat_dt target expect FIT image - which WNDR4700's uboot doesn't support
+	-$(STAGING_DIR_HOST)/bin/mkimage -A $(LINUX_KARCH) \
+		-O linux -T kernel -C none \
+		-n '$(call toupper,$(LINUX_KARCH)) LEDE Linux-$(LINUX_VERSION)' \
+		-d $@.dtb $@.new
+	@mv $@.new $@
+endef
+
+define Build/append-fakerootfs
+	rm -rf $@.fakerootsquashfs $@.fakefs
+
+	# append a fake/empty rootfs to fool netgear's uboot
+	# CHECK_DNI_FIRMWARE_ROOTFS_INTEGRITY in do_chk_dniimg()
+	dd if=/dev/zero of=$@.fakerd bs=16 count=1 conv=sync
+
+	-$(STAGING_DIR_HOST)/bin/mkimage \
+		-A $(LINUX_KARCH) -O linux -T filesystem -C none \
+		-a 0x00000000 -e 0x00000000 \
+		-n '$(DEVICE_PROFILE) fakerootfs' \
+		-d $@.fakerd $@.fakefs
+
+	cat $@.fakefs >> $@
+	rm -rf $@.fakerootsquashfs $@.fakefs
+endef
+
+define Build/wndr4700-specialImage
+	rm -rf $@.fakerd $@.new
+
+	dd if=/dev/zero of=$@.fakerd bs=32 count=1 conv=sync
+
+	# Netgear used an old uboot that doesn't have FIT support.
+	# So we are stuck with either a full ext2/4 fs in a initrd.
+	# ... or we try to make the "multi" image approach to work
+	# for us.
+	#
+	# Sadly, the "multi" image has to consists of three
+	# "fixed" parts in the following "fixed" order:
+	# 1. The kernel which is in $@
+	# 2. The (fake) initrd which is in $@.fakerd
+	# 3. The device tree binary which is in $@.dtb
+	#
+	# Now, given that we use the function for the kernel which
+	# already has a initramfs image inside, we still have to
+	# add a "fake" initrd (which a mkimage header) in the second
+	# part of the legacy multi image. Since we need to put the
+	# device tree stuff into part 3.
+
+	-$(STAGING_DIR_HOST)/bin/mkimage -A $(LINUX_KARCH) -O linux -T multi \
+		-C $(1) -a $(KERNEL_LOADADDR) -e $(KERNEL_ENTRY) \
+		-n '$(DEVICE_PROFILE) initramfs' -d $@:$@.fakerd:$@.dtb $@.new
+	mv $@.new $@
+	rm -rf $@.fakerd
+endef
+
+define Device/WNDR4700
+  DEVICE_TITLE := Netgear Centria N900 WNDR4700/WNDR4720
+  DEVICE_PACKAGES := badblocks block-mount e2fsprogs \
+	kmod-ath9k kmod-dm kmod-fs-ext4 kmod-fs-vfat kmod-ledtrig-usbdev \
+	kmod-md-mod kmod-nls-cp437 kmod-nls-iso8859-1 kmod-nls-iso8859-15 \
+	kmod-nls-utf8 kmod-usb3 kmod-usb-dwc2 kmod-usb-storage \
+	partx-utils swconfig wpad-mini
+  DEVICE_NAME := wndr4700
+  DEVICE_PROFILE := wndr4700
+  DEVICE_DTS := wndr4700
+  PAGESIZE := 2048
+  SUBPAGESIZE := 512
+  BLOCKSIZE := 131072
+  DTB_SIZE := 131008
+  IMAGE_SIZE:=25559040
+  IMAGES := factory.img sysupgrade.tar
+  KERNEL_SIZE := 1920k
+  KERNEL := kernel-bin | lzma | uImage lzma | pad-offset $$(BLOCKSIZE) 64 | append-fakerootfs
+  KERNEL_INITRAMFS := kernel-bin | gzip | dtb | wndr4700-specialImage gzip
+  IMAGE/factory.img := dtb | create-uImage-dtb | append-kernel | pad-to 2M | append-ubi | \
+		       netgear-dni | check-size $$$$(IMAGE_SIZE)
+  IMAGE/sysupgrade.tar := sysupgrade-nand
+  NETGEAR_BOARD_ID := WNDR4700
+  NETGEAR_HW_ID := 29763875+128+256
+endef
+TARGET_DEVICES += WNDR4700
+
 endif
 
 ifeq ($(SUBTARGET),sata)
@@ -161,4 +243,3 @@ TARGET_DEVICES += MyBookLiveDuo
 endif
 
 $(eval $(call BuildImage))
-
diff --git a/target/linux/apm821xx/nand/config-default b/target/linux/apm821xx/nand/config-default
index 582d1295bcb..9c93680f5f3 100644
--- a/target/linux/apm821xx/nand/config-default
+++ b/target/linux/apm821xx/nand/config-default
@@ -1,4 +1,32 @@
+CONFIG_AR8216_PHY=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DW_DMAC_CORE=y
+CONFIG_DW_DMAC=y
+# CONFIG_SATA_DWC_OLD_DMA is not set
+# CONFIG_DW_DMAC_PCI is not set
 CONFIG_IKAREM=y
+CONFIG_ATA=y
+CONFIG_ATA_SFF=y
+CONFIG_ATA_BMDMA=y
+CONFIG_SATA_PMP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_SATA_DWC=y
+# CONFIG_SATA_DWC_DEBUG is not set
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_GENERIC=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_M48T86=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_OF=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+CONFIG_THERMAL_HWMON=y
+CONFIG_HWMON=y
+CONFIG_SENSORS_GPIO_FAN=y
+CONFIG_CMDLINE="rootfstype=squashfs noinitrd"
 CONFIG_MTD_UBI=y
 CONFIG_MTD_UBI_BEB_LIMIT=20
 CONFIG_MTD_UBI_BLOCK=y
@@ -7,3 +35,6 @@ CONFIG_MTD_UBI_BLOCK=y
 CONFIG_MTD_UBI_WL_THRESHOLD=4096
 CONFIG_UBIFS_FS=y
 # CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_SENSORS_LM90=y
+CONFIG_SENSORS_TC654=y
+CONFIG_WNDR4700=y
diff --git a/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch b/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch
new file mode 100644
index 00000000000..81531915ba9
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/202-add-netgear-wndr4700-support.patch
@@ -0,0 +1,32 @@
+--- a/arch/powerpc/platforms/44x/Makefile	2016-05-31 19:28:28.825973250 +0200
++++ b/arch/powerpc/platforms/44x/Makefile	2016-05-31 19:28:22.135960329 +0200
+@@ -3,6 +3,7 @@ ifneq ($(CONFIG_PPC4xx_CPM),y)
+ obj-$(CONFIG_44x)	+= idle.o
+ endif
+ obj-$(CONFIG_PPC44x_SIMPLE) += ppc44x_simple.o
++obj-$(CONFIG_WNDR4700) += wndr4700.o
+ obj-$(CONFIG_EBONY)	+= ebony.o
+ obj-$(CONFIG_SAM440EP) 	+= sam440ep.o
+ obj-$(CONFIG_WARP)	+= warp.o
+--- a/arch/powerpc/platforms/44x/Kconfig	2016-05-31 19:33:57.049940191 +0200
++++ b/arch/powerpc/platforms/44x/Kconfig	2016-05-31 19:30:01.699485861 +0200
+@@ -260,6 +260,19 @@ config ICON
+ 	help
+ 	  This option enables support for the AMCC PPC440SPe evaluation board.
+ 
++config WNDR4700
++	bool "WNDR4700"
++	depends on 44x
++	default n
++	select APM821xx
++	select PCI_MSI
++	select PPC4xx_MSI
++	select PPC4xx_PCI_EXPRESS
++	select IBM_EMAC_RGMII
++	select 460EX
++	help
++	  This option enables support for the Netgear WNDR4700/WNDR4720 board.
++
+ config XILINX_VIRTEX440_GENERIC_BOARD
+ 	bool "Generic Xilinx Virtex 5 FXT board support"
+ 	depends on 44x
diff --git a/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch b/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch
new file mode 100644
index 00000000000..b44f8bba8a5
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/301-fix-memory-map-wndr4700.patch
@@ -0,0 +1,14 @@
+--- a/arch/powerpc/sysdev/ppc4xx_pci.c	2016-05-30 17:57:30.125498459 +0200
++++ b/arch/powerpc/sysdev/ppc4xx_pci.c	2016-05-30 18:00:39.236007798 +0200
+@@ -1913,9 +1913,9 @@ static void __init ppc4xx_configure_pcie
+ 		 * if it works
+ 		 */
+ 		out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
+-		out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
++		out_le32(mbase + PECFG_PIM0LAH, 0x00000008);
+ 		out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
+-		out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
++		out_le32(mbase + PECFG_PIM1LAH, 0x0000000c);
+ 		out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
+ 		out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
+ 
diff --git a/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch b/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch
new file mode 100644
index 00000000000..a1ca7c610e7
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/702-powerpc_ibm_phy_add_dt_parser.patch
@@ -0,0 +1,335 @@
+From 59b394d0d2b2e11687e757820c52d6470d15a9c5 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Mon, 13 Jun 2016 15:42:21 +0200
+Subject: [PATCH] phy device tree support for emac
+
+---
+ drivers/net/ethernet/ibm/emac/core.c | 261 +++++++++++++++++++++++++++++++++++
+ drivers/net/ethernet/ibm/emac/core.h |   4 +
+ 2 files changed, 265 insertions(+)
+
+diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c
+index 4c9771d..5a8a26c 100644
+--- a/drivers/net/ethernet/ibm/emac/core.c
++++ b/drivers/net/ethernet/ibm/emac/core.c
+@@ -42,6 +42,7 @@
+ #include <linux/of_address.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_net.h>
++#include <linux/of_mdio.h>
+ #include <linux/slab.h>
+ 
+ #include <asm/processor.h>
+@@ -2383,6 +2384,246 @@ static int emac_read_uint_prop(struct device_node *np, const char *name,
+ 	return 0;
+ }
+ 
++static void emac_adjust_link(struct net_device *ndev)
++{
++	struct emac_instance *dev = netdev_priv(ndev);
++	struct phy_device *phy = dev->phy_dev;
++
++	dev->phy.autoneg = phy->autoneg;
++	dev->phy.speed = phy->speed;
++	dev->phy.duplex = phy->duplex;
++	dev->phy.pause = phy->pause;
++	dev->phy.asym_pause = phy->asym_pause;
++	dev->phy.advertising = phy->advertising;
++}
++
++static int emac_mii_bus_read(struct mii_bus *bus, int addr, int regnum)
++{
++	return emac_mdio_read(bus->priv, addr, regnum);
++}
++
++static int emac_mii_bus_write(struct mii_bus *bus, int addr, int regnum, u16 val)
++{
++	emac_mdio_write(bus->priv, addr, regnum, val);
++	return 0;
++}
++
++static int emac_mii_bus_reset(struct mii_bus *bus)
++{
++	struct emac_instance *dev = netdev_priv(bus->priv);
++
++	emac_mii_reset_phy(&dev->phy);
++	return 0;
++}
++
++static int emac_mdio_setup_aneg(struct mii_phy *phy, u32 advertise)
++{
++	struct net_device *ndev = phy->dev;
++	struct emac_instance *dev = netdev_priv(ndev);
++
++	dev->phy.autoneg = AUTONEG_ENABLE;
++	dev->phy.speed = SPEED_1000;
++	dev->phy.duplex = DUPLEX_FULL;
++	dev->phy.advertising = advertise;
++	phy->autoneg = AUTONEG_ENABLE;
++	phy->speed = dev->phy.speed;
++	phy->duplex = dev->phy.duplex;
++	phy->advertising = advertise;
++	return phy_start_aneg(dev->phy_dev);
++}
++
++static int emac_mdio_setup_forced(struct mii_phy *phy, int speed, int fd)
++{
++	struct net_device *ndev = phy->dev;
++	struct emac_instance *dev = netdev_priv(ndev);
++
++	dev->phy.autoneg =  AUTONEG_DISABLE;
++	dev->phy.speed = speed;
++	dev->phy.duplex = fd;
++	phy->autoneg = AUTONEG_DISABLE;
++	phy->speed = speed;
++	phy->duplex = fd;
++	return phy_start_aneg(dev->phy_dev);
++}
++
++static int emac_mdio_poll_link(struct mii_phy *phy)
++{
++	struct net_device *ndev = phy->dev;
++	struct emac_instance *dev = netdev_priv(ndev);
++	int res;
++
++	res = phy_read_status(dev->phy_dev);
++	if (res) {
++		dev_err(&dev->ndev->dev, "link update failed (%d).", res);
++		return ethtool_op_get_link(ndev);
++	}
++
++	return dev->phy_dev->link;
++}
++
++static int emac_mdio_read_link(struct mii_phy *phy)
++{
++	struct net_device *ndev = phy->dev;
++	struct emac_instance *dev = netdev_priv(ndev);
++	int res;
++
++	res = phy_read_status(dev->phy_dev);
++	if (res)
++		return res;
++
++	dev->phy.speed = phy->speed;
++	dev->phy.duplex = phy->duplex;
++	dev->phy.pause = phy->pause;
++	dev->phy.asym_pause = phy->asym_pause;
++	return 0;
++}
++
++static int emac_mdio_init_phy(struct mii_phy *phy)
++{
++	struct net_device *ndev = phy->dev;
++	struct emac_instance *dev = netdev_priv(ndev);
++
++	phy_start(dev->phy_dev);
++	dev->phy.autoneg = phy->autoneg;
++	dev->phy.speed = phy->speed;
++	dev->phy.duplex = phy->duplex;
++	dev->phy.advertising = phy->advertising;
++	dev->phy.pause = phy->pause;
++	dev->phy.asym_pause = phy->asym_pause;
++
++	return phy_init_hw(dev->phy_dev);
++}
++
++static const struct mii_phy_ops emac_dt_mdio_phy_ops = {
++	.init		= emac_mdio_init_phy,
++	.setup_aneg	= emac_mdio_setup_aneg,
++	.setup_forced	= emac_mdio_setup_forced,
++	.poll_link	= emac_mdio_poll_link,
++	.read_link	= emac_mdio_read_link,
++};
++
++static void emac_dt_phy_mdio_cleanup(struct emac_instance *dev)
++{
++	if (dev->mii_bus) {
++		if (dev->mii_bus->state == MDIOBUS_REGISTERED)
++			mdiobus_unregister(dev->mii_bus);
++		mdiobus_free(dev->mii_bus);
++		dev->mii_bus = NULL;
++	}
++	kfree(dev->phy.def);
++	dev->phy.def = NULL;
++}
++
++static int emac_dt_mdio_probe(struct emac_instance *dev)
++{
++	struct device_node *mii_np;
++	int res;
++
++	mii_np = of_get_child_by_name(dev->ofdev->dev.of_node, "mdio");
++	if (!mii_np) {
++		dev_err(&dev->ndev->dev, "no mdio definition found.");
++		return -ENODEV;
++	}
++
++	if (!of_device_is_available(mii_np)) {
++		res = 1;
++		goto err_put_node;
++	}
++
++	dev->mii_bus = mdiobus_alloc();
++	if (!dev->mii_bus) {
++		res = -ENOMEM;
++		goto err_cleanup_mdio;
++	}
++
++	dev->mii_bus->priv = dev->ndev;
++	dev->mii_bus->parent = dev->ndev->dev.parent;
++	dev->mii_bus->name = "emac_mdio";
++	dev->mii_bus->read = &emac_mii_bus_read;
++	dev->mii_bus->write = &emac_mii_bus_write;
++	dev->mii_bus->reset = &emac_mii_bus_reset;
++	snprintf(dev->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev->mii_bus->name);
++
++	res = of_mdiobus_register(dev->mii_bus, mii_np);
++	if (res) {
++		dev_err(&dev->ndev->dev, "cannot register MDIO bus %s (%d)",
++			dev->mii_bus->name, res);
++		goto err_cleanup_mdio;
++	}
++	of_node_put(mii_np);
++	return 0;
++
++ err_cleanup_mdio:
++	emac_dt_phy_mdio_cleanup(dev);
++ err_put_node:
++	of_node_put(mii_np);
++	return res;
++}
++
++static int emac_dt_phy_probe(struct emac_instance *dev,
++			     struct device_node *phy_handle)
++{
++	u32 phy_flags = 0;
++	int res;
++
++	res = of_property_read_u32(phy_handle, "phy-flags", &phy_flags);
++	if (res < 0 && res != -EINVAL)
++		return res;
++
++	dev->phy.def = kzalloc(sizeof(*dev->phy.def), GFP_KERNEL);
++	if (!dev->phy.def)
++		return -ENOMEM;
++
++	dev->phy_dev = of_phy_connect(dev->ndev, phy_handle,
++				      &emac_adjust_link, phy_flags,
++				      PHY_INTERFACE_MODE_RGMII);
++	if (!dev->phy_dev) {
++		dev_err(&dev->ndev->dev, "failed to connect to PHY.");
++		kfree(dev->phy.dev);
++		dev->phy.dev = NULL;
++		return -ENODEV;
++	}
++
++	dev->phy.def->phy_id = dev->phy_dev->drv->phy_id;
++	dev->phy.def->phy_id_mask = dev->phy_dev->drv->phy_id_mask;
++	dev->phy.def->name = dev->phy_dev->drv->name;
++	dev->phy.def->ops = &emac_dt_mdio_phy_ops;
++	dev->phy.features = dev->phy_dev->supported;
++	dev->phy.address = dev->phy_dev->addr;
++	dev->phy.mode = dev->phy_dev->interface;
++	return 0;
++}
++
++static int emac_probe_dt_phy(struct emac_instance *dev)
++{
++	struct device_node *np = dev->ofdev->dev.of_node;
++	struct device_node *phy_handle;
++	int res = 0;
++
++	phy_handle = of_parse_phandle(np, "phy-handle", 0);
++
++	if (phy_handle) {
++		res = emac_dt_mdio_probe(dev);
++		if (res)
++			goto out;
++
++		res = emac_dt_phy_probe(dev, phy_handle);
++		if (res) {
++			emac_dt_phy_mdio_cleanup(dev);
++			goto out;
++		}
++
++		return 1;
++	}
++
++ out:
++	of_node_put(phy_handle);
++	/* if no phy device was specifie in the device tree, then we fallback
++	 * to the old emac_phy.c probe code for compatibility reasons.
++	 */
++	return res;
++}
++
+ static int emac_init_phy(struct emac_instance *dev)
+ {
+ 	struct device_node *np = dev->ofdev->dev.of_node;
+@@ -2453,6 +2694,18 @@ static int emac_init_phy(struct emac_instance *dev)
+ 
+ 	emac_configure(dev);
+ 
++	if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) {
++		int res = emac_probe_dt_phy(dev);
++
++		if (res == 1)
++			goto init_phy;
++		if (res < 0)
++			dev_err(&dev->ndev->dev, "failed to attach dt phy (%d).",
++				res);
++
++		/* continue with old code */
++	}
++
+ 	if (dev->phy_address != 0xffffffff)
+ 		phy_map = ~(1 << dev->phy_address);
+ 
+@@ -2480,6 +2733,7 @@ static int emac_init_phy(struct emac_instance *dev)
+ 		return -ENXIO;
+ 	}
+ 
++ init_phy:
+ 	/* Init PHY */
+ 	if (dev->phy.def->ops->init)
+ 		dev->phy.def->ops->init(&dev->phy);
+@@ -2898,6 +3152,8 @@ static int emac_probe(struct platform_device *ofdev)
+ 	/* I have a bad feeling about this ... */
+ 
+  err_detach_tah:
++	emac_dt_phy_mdio_cleanup(dev);
++
+ 	if (emac_has_feature(dev, EMAC_FTR_HAS_TAH))
+ 		tah_detach(dev->tah_dev, dev->tah_port);
+  err_detach_rgmii:
+@@ -2948,6 +3204,11 @@ static int emac_remove(struct platform_device *ofdev)
+ 	if (emac_has_feature(dev, EMAC_FTR_HAS_ZMII))
+ 		zmii_detach(dev->zmii_dev, dev->zmii_port);
+ 
++	if (dev->phy_dev)
++		phy_disconnect(dev->phy_dev);
++
++	emac_dt_phy_mdio_cleanup(dev);
++
+ 	busy_phy_map &= ~(1 << dev->phy.address);
+ 	DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map);
+ 
+diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h
+index 93ae114..0710a66 100644
+--- a/drivers/net/ethernet/ibm/emac/core.h
++++ b/drivers/net/ethernet/ibm/emac/core.h
+@@ -199,6 +199,10 @@ struct emac_instance {
+ 	struct emac_instance		*mdio_instance;
+ 	struct mutex			mdio_lock;
+ 
++	/* Device-tree based phy configuration */
++	struct mii_bus			*mii_bus;
++	struct phy_device		*phy_dev;
++
+ 	/* ZMII infos if any */
+ 	u32				zmii_ph;
+ 	u32				zmii_port;
+-- 
+2.1.4
+
diff --git a/target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch b/target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch
new file mode 100644
index 00000000000..24d267ef4a3
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/800-usb-dwc2-add-wndr4700-otg.patch
@@ -0,0 +1,48 @@
+--- a/drivers/usb/dwc2/platform.c	2016-05-26 21:39:41.347838639 +0200
++++ b/drivers/usb/dwc2/platform.c	2016-05-26 21:44:01.554907417 +0200
+@@ -115,6 +115,37 @@ static const struct dwc2_core_params par
+ 	.hibernation			= -1,
+ };
+ 
++static const struct dwc2_core_params params_wndr4700 = {
++	.otg_cap			= DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE,
++	.otg_ver			= -1,
++	.dma_enable			= -1,
++	.dma_desc_enable		= -1,
++	.speed				= -1,
++	.enable_dynamic_fifo		= -1,
++	.en_multiple_tx_fifo		= -1,
++	.host_rx_fifo_size		= -1,
++	.host_nperio_tx_fifo_size	= -1,
++	.host_perio_tx_fifo_size	= -1,
++	.max_transfer_size		= -1,
++	.max_packet_count		= -1,
++	.host_channels			= -1,
++	.phy_type			= -1,
++	.phy_utmi_width			= -1,
++	.phy_ulpi_ddr			= -1,
++	.phy_ulpi_ext_vbus		= -1,
++	.i2c_enable			= -1,
++	.ulpi_fs_ls			= -1,
++	.host_support_fs_ls_low_power	= -1,
++	.host_ls_low_power_phy_clk	= -1,
++	.ts_dline			= -1,
++	.reload_ctl			= -1,
++	.ahbcfg				= GAHBCFG_HBSTLEN_INCR16 <<
++					  GAHBCFG_HBSTLEN_SHIFT,
++	.uframe_sched			= -1,
++	.external_id_pin_ctl		= -1,
++	.hibernation			= -1,
++};
++
+ static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
+ {
+ 	struct platform_device *pdev = to_platform_device(hsotg->dev);
+@@ -309,6 +340,7 @@ static int dwc2_driver_remove(struct pla
+ static const struct of_device_id dwc2_of_match_table[] = {
+ 	{ .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
+ 	{ .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
++	{ .compatible = "netgear,wndr4700-usb", .data = &params_wndr4700 },
+ 	{ .compatible = "snps,dwc2", .data = NULL },
+ 	{ .compatible = "samsung,s3c6400-hsotg", .data = NULL},
+ 	{},
diff --git a/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch b/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch
new file mode 100644
index 00000000000..e56e95576d4
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch
@@ -0,0 +1,550 @@
+From 419992bae5aaa4e06402e0b7c79fcf7bcb6b4764 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@googlemail.com>
+Date: Thu, 2 Jun 2016 00:48:46 +0200
+Subject: [PATCH] usb: xhci: add firmware loader for uPD720201 and uPD720202
+ w/o ROM
+
+This patch adds a firmware loader for the uPD720201K8-711-BAC-A
+and uPD720202K8-711-BAA-A variant. Both of these chips are listed
+in Renesas' R19UH0078EJ0500 Rev.5.00 "User's Manual: Hardware" as
+devices which need the firmware loader on page 2 in order to
+work as they "do not support the External ROM".
+
+The "Firmware Download Sequence" is describe in chapter
+"7.1 FW Download Interface" R19UH0078EJ0500 Rev.5.00 page 131.
+
+The firmware "K2013080.mem" is available from a USB3.0 Host to
+PCIe Adapter (PP2U-E card) "Firmware download" archive. An
+alternative version can be sourced from Netgear's WNDR4700 GPL
+archives.
+
+The release notes of the PP2U-E's "Firmware Download" ver 2.0.1.3
+(2012-06-15) state that the firmware is for the following devices:
+ - uPD720201 ES 2.0 sample whose revision ID is 2.
+ - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3.
+ - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2.
+
+If someone from Renesas is listening: It would be great, if these
+firmwares could be added to linux-firmware.git.
+
+Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
+---
+ drivers/usb/host/xhci-pci.c | 492 ++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 492 insertions(+)
+
+diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
+index 48672fa..328c891 100644
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -24,6 +24,8 @@
+ #include <linux/slab.h>
+ #include <linux/module.h>
+ #include <linux/acpi.h>
++#include <linux/firmware.h>
++#include <asm/unaligned.h>
+ 
+ #include "xhci.h"
+ #include "xhci-trace.h"
+@@ -207,6 +209,458 @@ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev)
+ static void xhci_pme_acpi_rtd3_enable(struct pci_dev *dev) { }
+ #endif /* CONFIG_ACPI */
+ 
++static const struct renesas_fw_entry {
++	const char *firmware_name;
++	u16 device;
++	u8 revision;
++	u16 expected_version;
++} renesas_fw_table[] = {
++	/*
++	 * Only the uPD720201K8-711-BAC-A or uPD720202K8-711-BAA-A
++	 * are listed in R19UH0078EJ0500 Rev.5.00 as devices which
++	 * need the software loader.
++	 *
++	 * PP2U/ReleaseNote_USB3-201-202-FW.txt:
++	 *
++	 * Note: This firmware is for the following devices.
++	 *  - uPD720201 ES 2.0 sample whose revision ID is 2.
++	 *  - uPD720201 ES 2.1 sample & CS sample & Mass product, ID is 3.
++	 *  - uPD720202 ES 2.0 sample & CS sample & Mass product, ID is 2.
++	 */
++	{ "K2013080.mem", 0x0014, 0x02, 0x2013 },
++	{ "K2013080.mem", 0x0014, 0x03, 0x2013 },
++	{ "K2013080.mem", 0x0015, 0x02, 0x2013 },
++};
++
++static const struct renesas_fw_entry *renesas_needs_fw_dl(struct pci_dev *dev)
++{
++	const struct renesas_fw_entry *entry;
++	size_t i;
++
++	/* This loader will only work with a RENESAS device. */
++	if (!(dev->vendor == PCI_VENDOR_ID_RENESAS))
++		return NULL;
++
++	for (i = 0; i < ARRAY_SIZE(renesas_fw_table); i++) {
++		entry = &renesas_fw_table[i];
++		if (entry->device == dev->device &&
++		    entry->revision == dev->revision)
++			return entry;
++	}
++
++	return NULL;
++}
++
++static int renesas_fw_download_image(struct pci_dev *dev,
++				     const u32 *fw,
++				     size_t step)
++{
++	size_t i;
++	int err;
++	u8 fw_status;
++	bool data0_or_data1;
++
++	/*
++	 * The hardware does alternate between two 32-bit pages.
++	 * (This is because each row of the firmware is 8 bytes).
++	 *
++	 * for even steps we use DATA0, for odd steps DATA1.
++	 */
++	data0_or_data1 = (step & 1) == 1;
++
++	/* step+1. Read "Set DATAX" and confirm it is cleared. */
++	for (i = 0; i < 10000; i++) {
++		err = pci_read_config_byte(dev, 0xF5, &fw_status);
++		if (err)
++			return pcibios_err_to_errno(err);
++		if (!(fw_status & BIT(data0_or_data1)))
++			break;
++
++		udelay(1);
++	}
++	if (i == 10000)
++		return -ETIMEDOUT;
++
++	/*
++	 * step+2. Write FW data to "DATAX".
++	 * "LSB is left" => force little endian
++	 */
++	err = pci_write_config_dword(dev, data0_or_data1 ? 0xFC : 0xF8,
++				     (__force u32) cpu_to_le32(fw[step]));
++	if (err)
++		return pcibios_err_to_errno(err);
++
++	udelay(100);
++
++	/* step+3. Set "Set DATAX". */
++	err = pci_write_config_byte(dev, 0xF5, BIT(data0_or_data1));
++	if (err)
++		return pcibios_err_to_errno(err);
++
++	return 0;
++}
++
++static int renesas_fw_verify(struct pci_dev *dev,
++			     const void *fw_data,
++			     size_t length)
++{
++	const struct renesas_fw_entry *entry = renesas_needs_fw_dl(dev);
++	u16 fw_version_pointer;
++	u16 fw_version;
++
++	if (!entry)
++		return -EINVAL;
++
++	/*
++	 * The Firmware's Data Format is describe in
++	 * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124
++	 */
++
++	/* "Each row is 8 bytes". => firmware size must be a multiple of 8. */
++	if (length % 8 != 0) {
++		dev_err(&dev->dev, "firmware size is not a multipe of 8.");
++		return -EINVAL;
++	}
++
++	/*
++	 * The bootrom chips of the big brother have sizes up to 64k, let's
++	 * assume that's the biggest the firmware can get.
++	 */
++	if (length < 0x1000 || length >= 0x10000) {
++		dev_err(&dev->dev, "firmware is size %zd is not (4k - 64k).",
++			length);
++		return -EINVAL;
++	}
++
++	/* The First 2 bytes are fixed value (55aa). "LSB on Left" */
++	if (get_unaligned_le16(fw_data) != 0x55aa) {
++		dev_err(&dev->dev, "no valid firmware header found.");
++		return -EINVAL;
++	}
++
++	/* verify the firmware version position and print it. */
++	fw_version_pointer = get_unaligned_le16(fw_data + 4);
++	if (fw_version_pointer + 2 >= length) {
++		dev_err(&dev->dev, "firmware version pointer is outside of the firmware image.");
++		return -EINVAL;
++	}
++
++	fw_version = get_unaligned_le16(fw_data + fw_version_pointer);
++	dev_dbg(&dev->dev, "got firmware version: %02x.", fw_version);
++
++	if (fw_version != entry->expected_version) {
++		dev_err(&dev->dev, "firmware version mismatch, expected version: %02x.",
++			 entry->expected_version);
++		return -EINVAL;
++	}
++
++	return 0;
++}
++
++static int renesas_fw_check_running(struct pci_dev *pdev)
++{
++	int err;
++	u8 fw_state;
++
++	/*
++	 * Test if the device is actually needing the firmware. As most
++	 * BIOSes will initialize the device for us. If the device is
++	 * initialized.
++	 */
++	err = pci_read_config_byte(pdev, 0xF4, &fw_state);
++	if (err)
++		return pcibios_err_to_errno(err);
++
++	/*
++	 * Check if "FW Download Lock" is locked. If it is and the FW is
++	 * ready we can simply continue. If the FW is not ready, we have
++	 * to give up.
++	 */
++	if (fw_state & BIT(1)) {
++		dev_dbg(&pdev->dev, "FW Download Lock is engaged.");
++
++		if (fw_state & BIT(4))
++			return 0;
++
++		dev_err(&pdev->dev, "FW Download Lock is set and FW is not ready. Giving Up.");
++		return -EIO;
++	}
++
++	/*
++	 * Check if "FW Download Enable" is set. If someone (us?) tampered
++	 * with it and it can't be resetted, we have to give up too... and
++	 * ask for a forgiveness and a reboot.
++	 */
++	if (fw_state & BIT(0)) {
++		dev_err(&pdev->dev, "FW Download Enable is stale. Giving Up (poweroff/reboot needed).");
++		return -EIO;
++	}
++
++	/* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */
++	switch ((fw_state & 0x70)) {
++	case 0: /* No result yet */
++		dev_dbg(&pdev->dev, "FW is not ready/loaded yet.");
++
++		/* tell the caller, that this device needs the firmware. */
++		return 1;
++
++	case BIT(4): /* Success, device should be working. */
++		dev_dbg(&pdev->dev, "FW is ready.");
++		return 0;
++
++	case BIT(5): /* Error State */
++		dev_err(&pdev->dev, "hardware is in an error state. Giving up (poweroff/reboot needed).");
++		return -ENODEV;
++
++	default: /* All other states are marked as "Reserved states" */
++		dev_err(&pdev->dev, "hardware is in an invalid state %x. Giving up (poweroff/reboot needed).",
++			(fw_state & 0x70) >> 4);
++		return -EINVAL;
++	}
++}
++
++static int renesas_hw_check_run_stop_busy(struct pci_dev *pdev)
++{
++#if 0
++	u32 val;
++
++	/*
++	 * 7.1.3 Note 3: "... must not set 'FW Download Enable' when
++	 * 'RUN/STOP' of USBCMD Register is set"
++	 */
++	val = readl(hcd->regs + 0x20);
++	if (val & BIT(0)) {
++		dev_err(&pdev->dev, "hardware is busy and can't receive a FW.");
++		return -EBUSY;
++	}
++#endif
++	return 0;
++}
++
++static int renesas_fw_download(struct pci_dev *pdev,
++	const struct firmware *fw, unsigned int retry_counter)
++{
++	const u32 *fw_data = (const u32 *) fw->data;
++	size_t i;
++	int err;
++	u8 fw_status;
++
++	/*
++	 * For more information and the big picture: please look at the
++	 * "Firmware Download Sequence" in "7.1 FW Download Interface"
++	 * of R19UH0078EJ0500 Rev.5.00 page 131
++	 */
++	err = renesas_hw_check_run_stop_busy(pdev);
++	if (err)
++		return err;
++
++	/*
++	 * 0. Set "FW Download Enable" bit in the
++	 * "FW Download Control & Status Register" at 0xF4
++	 */
++	err = pci_write_config_byte(pdev, 0xF4, BIT(0));
++	if (err)
++		return pcibios_err_to_errno(err);
++
++	/* 1 - 10 follow one step after the other. */
++	for (i = 0; i < fw->size / 4; i++) {
++		err = renesas_fw_download_image(pdev, fw_data, i);
++		if (err) {
++			dev_err(&pdev->dev, "Firmware Download Step %zd failed at position %zd bytes with (%d).",
++				 i, i * 4, err);
++			return err;
++		}
++	}
++
++	/*
++	 * This sequence continues until the last data is written to
++	 * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1"
++	 * is cleared by the hardware beforehand.
++	 */
++	for (i = 0; i < 10000; i++) {
++		err = pci_read_config_byte(pdev, 0xF5, &fw_status);
++		if (err)
++			return pcibios_err_to_errno(err);
++		if (!(fw_status & (BIT(0) | BIT(1))))
++			break;
++
++		udelay(1);
++	}
++	if (i == 10000)
++		dev_warn(&pdev->dev, "Final Firmware Download step timed out.");
++
++	/*
++	 * 11. After finishing writing the last data of FW, the
++	 * System Software must clear "FW Download Enable"
++	 */
++	err = pci_write_config_byte(pdev, 0xF4, 0);
++	if (err)
++		return pcibios_err_to_errno(err);
++
++	/* 12. Read "Result Code" and confirm it is good. */
++	for (i = 0; i < 10000; i++) {
++		err = pci_read_config_byte(pdev, 0xF4, &fw_status);
++		if (err)
++			return pcibios_err_to_errno(err);
++		if (fw_status & BIT(4))
++			break;
++
++		udelay(1);
++	}
++	if (i == 10000) {
++		/* Timed out / Error - let's see if we can fix this */
++		err = renesas_fw_check_running(pdev);
++		switch (err) {
++		case 0: /*
++			 * we shouldn't end up here.
++			 * maybe it took a little bit longer.
++			 * But all should be well?
++			 */
++			break;
++
++		case 1: /* (No result yet? - we can try to retry) */
++			if (retry_counter < 10) {
++				retry_counter++;
++				dev_warn(&pdev->dev, "Retry Firmware download: %d try.",
++					  retry_counter);
++				return renesas_fw_download(pdev, fw,
++							   retry_counter);
++			}
++			return -ETIMEDOUT;
++
++		default:
++			return err;
++		}
++	}
++	/*
++	 * Optional last step: Engage Firmware Lock
++	 *
++	 * err = pci_write_config_byte(pdev, 0xF4, BIT(2));
++	 * if (err)
++	 *	return pcibios_err_to_errno(err);
++	 */
++
++	return 0;
++}
++
++struct renesas_fw_ctx {
++	struct pci_dev *pdev;
++	const struct pci_device_id *id;
++	bool resume;
++};
++
++static int xhci_pci_probe(struct pci_dev *pdev,
++			  const struct pci_device_id *id);
++
++static void renesas_fw_callback(const struct firmware *fw,
++				void *context)
++{
++	struct renesas_fw_ctx *ctx = context;
++	struct pci_dev *pdev = ctx->pdev;
++	struct device *parent = pdev->dev.parent;
++	int err = -ENOENT;
++
++	if (fw) {
++		err = renesas_fw_verify(pdev, fw->data, fw->size);
++		if (!err) {
++			err = renesas_fw_download(pdev, fw, 0);
++			release_firmware(fw);
++			if (!err) {
++				if (ctx->resume)
++					return;
++
++				err = xhci_pci_probe(pdev, ctx->id);
++				if (!err) {
++					/* everything worked */
++					devm_kfree(&pdev->dev, ctx);
++					return;
++				}
++
++				/* in case of an error - fall through */
++			} else {
++				dev_err(&pdev->dev, "firmware failed to download (%d).",
++					err);
++			}
++		}
++	} else {
++		dev_err(&pdev->dev, "firmware failed to load (%d).", err);
++	}
++
++	dev_info(&pdev->dev, "Unloading driver");
++
++	if (parent)
++		device_lock(parent);
++
++	device_release_driver(&pdev->dev);
++
++	if (parent)
++		device_unlock(parent);
++
++	pci_dev_put(pdev);
++}
++
++static int renesas_fw_alive_check(struct pci_dev *pdev)
++{
++	const struct renesas_fw_entry *entry;
++	int err;
++
++	/* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */
++	entry = renesas_needs_fw_dl(pdev);
++	if (!entry)
++		return 0;
++
++	err = renesas_fw_check_running(pdev);
++	/* Also go ahead, if the firmware is running */
++	if (err == 0)
++		return 0;
++
++	/* At this point, we can be sure that the FW isn't ready. */
++	return err;
++}
++
++static int renesas_fw_download_to_hw(struct pci_dev *pdev,
++				     const struct pci_device_id *id,
++				     bool do_resume)
++{
++	const struct renesas_fw_entry *entry;
++	struct renesas_fw_ctx *ctx;
++	int err;
++
++	/* check if we have a eligible RENESAS' uPD720201/2 w/o FW. */
++	entry = renesas_needs_fw_dl(pdev);
++	if (!entry)
++		return 0;
++
++	err = renesas_fw_check_running(pdev);
++	/* Continue ahead, if the firmware is already running. */
++	if (err == 0)
++		return 0;
++
++	if (err != 1)
++		return err;
++
++	ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
++	if (!ctx)
++		return -ENOMEM;
++	ctx->pdev = pdev;
++	ctx->resume = do_resume;
++	ctx->id = id;
++
++	pci_dev_get(pdev);
++	err = request_firmware_nowait(THIS_MODULE, 1, entry->firmware_name,
++		&pdev->dev, GFP_KERNEL, ctx, renesas_fw_callback);
++	if (err) {
++		pci_dev_put(pdev);
++		return err;
++	}
++
++	/*
++	 * The renesas_fw_callback() callback will continue the probe
++	 * process, once it aquires the firmware.
++	 */
++	return 1;
++}
++
+ /* called during probe() after chip reset completes */
+ static int xhci_pci_setup(struct usb_hcd *hcd)
+ {
+@@ -246,6 +700,22 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+ 	struct hc_driver *driver;
+ 	struct usb_hcd *hcd;
+ 
++	/*
++	 * Check if this device is a RENESAS uPD720201/2 device.
++	 * Otherwise, we can continue with xhci_pci_probe as usual.
++	 */
++	retval = renesas_fw_download_to_hw(dev, id, false);
++	switch (retval) {
++	case 0:
++		break;
++
++	case 1: /* let it load the firmware and recontinue the probe. */
++		return 0;
++
++	default:
++		return retval;
++	};
++
+ 	driver = (struct hc_driver *)id->driver_data;
+ 
+ 	/* Prevent runtime suspending between USB-2 and USB-3 initialization */
+@@ -303,6 +773,16 @@ static void xhci_pci_remove(struct pci_dev *dev)
+ {
+ 	struct xhci_hcd *xhci;
+ 
++	if (renesas_fw_alive_check(dev)) {
++		/*
++		 * bail out early, if this was a renesas device w/o FW.
++		 * Else we might hit the NMI watchdog in xhci_handsake
++		 * during xhci_reset as part of the driver's unloading.
++		 * which we forced in the renesas_fw_callback().
++		 */
++		return;
++	}
++
+ 	xhci = hcd_to_xhci(pci_get_drvdata(dev));
+ 	xhci->xhc_state |= XHCI_STATE_REMOVING;
+ 	if (xhci->shared_hcd) {
+-- 
+2.8.1
+
diff --git a/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch b/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch
new file mode 100644
index 00000000000..a09e9dc41c9
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.4/802-usb-xhci-force-msi-renesas-xhci.patch
@@ -0,0 +1,57 @@
+From a0dc613140bab907a3d5787a7ae7b0638bf674d0 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Thu, 23 Jun 2016 20:28:20 +0200
+Subject: [PATCH] usb: xhci: force MSI for uPD720201 and
+ uPD720202
+
+The APM82181 does not support MSI-X. When probed, it will
+produce a noisy warning.
+
+---
+ drivers/usb/host/pci-quirks.c | 362 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 362 insertions(+)
+
+diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
+index 1c4d89e..555bd3f 100644
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -172,7 +172,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
+ 	}
+ 	if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
+ 			pdev->device == 0x0015)
+-		xhci->quirks |= XHCI_RESET_ON_RESUME;
++		xhci->quirks |= XHCI_RESET_ON_RESUME | XHCI_FORCE_MSI;
+ 	if (pdev->vendor == PCI_VENDOR_ID_VIA)
+ 		xhci->quirks |= XHCI_RESET_ON_RESUME;
+ 
+diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
+index 9e71c96..27cfcb9 100644
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -389,10 +389,14 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
+ 		free_irq(hcd->irq, hcd);
+ 	hcd->irq = 0;
+ 
+-	ret = xhci_setup_msix(xhci);
+-	if (ret)
+-		/* fall back to msi*/
++	if (xhci->quirks & XHCI_FORCE_MSI) {
+ 		ret = xhci_setup_msi(xhci);
++	} else {
++		ret = xhci_setup_msix(xhci);
++		if (ret)
++			/* fall back to msi*/
++			ret = xhci_setup_msi(xhci);
++	}
+ 
+ 	if (!ret)
+ 		/* hcd->irq is 0, we have MSI */
+diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
+index 6b085aa..514dc3f 100644
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1649,3 +1649,4 @@ struct xhci_hcd {
+ #define XHCI_BROKEN_STREAMS     (1 << 19)
+ #define XHCI_PME_STUCK_QUIRK    (1 << 20)
++#define XHCI_FORCE_MSI		(1 << 24)
+ 	unsigned int		num_active_eps;