diff mbox

[LEDE-DEV,v1,2/3] apm821xx: add linux 4.9 apm821xx patches

Message ID mailman.9702.1486745263.26984.lede-dev@lists.infradead.org
State Accepted
Headers show

Commit Message

Michael Yartys via Lede-dev Feb. 10, 2017, 4:47 p.m. UTC
The sender domain has a DMARC Reject/Quarantine policy which disallows
sending mailing list messages using the original "From" header.

To mitigate this problem, the original message has been wrapped
automatically by the mailing list software.
This patch updates the apm821xx target to use the 4.9 kernel.

Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
---
 target/linux/apm821xx/Makefile                     |    2 +-
 target/linux/apm821xx/config-4.9                   |  327 +++++++
 .../200-add-meraki-mr24-ikarem-support.patch       |   32 +
 .../201-add-amcc-apollo3g-support.patch            |   30 +
 .../202-add-netgear-wndr4700-support.patch         |   32 +
 .../203-add-meraki-mx60-buckminster-support.patch  |   32 +
 .../300-fix-atheros-nics-on-apm82181.patch         |   51 +
 .../patches-4.9/301-fix-memory-map-wndr4700.patch  |   14 +
 .../701-powerpc_ibm_apm82181_phyclk_fix.patch      |   52 +
 .../702-powerpc_ibm_phy_add_dt_parser.patch        |  328 +++++++
 ...d-firmware-loader-for-uPD720201-and-uPD72.patch |  545 +++++++++++
 .../802-usb-xhci-force-msi-renesas-xhci.patch      |   54 +
 .../804-usb-dwc2-add-amcc-usb-otg-405ex.patch      |   48 +
 ...river-for-Microchip-TC654-TC655-PWM-fan-c.patch | 1027 ++++++++++++++++++++
 14 files changed, 2573 insertions(+), 1 deletion(-)
 create mode 100644 target/linux/apm821xx/config-4.9
 create mode 100644 target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/203-add-meraki-mx60-buckminster-support.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/701-powerpc_ibm_apm82181_phyclk_fix.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch
 create mode 100644 target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch
diff mbox

Patch

diff --git a/target/linux/apm821xx/Makefile b/target/linux/apm821xx/Makefile
index 026466d86d..6d711543c4 100644
--- a/target/linux/apm821xx/Makefile
+++ b/target/linux/apm821xx/Makefile
@@ -13,7 +13,7 @@  MAINTAINER:=Chris Blake <chrisrblake93@gmail.com>, \
 	    Christian Lamparter <chunkeey@gmail.com>
 SUBTARGETS:=nand sata
 
-KERNEL_PATCHVER:=4.4
+KERNEL_PATCHVER:=4.9
 
 define Target/Description
 	Build images for AppliedMicro APM821xx based boards.
diff --git a/target/linux/apm821xx/config-4.9 b/target/linux/apm821xx/config-4.9
new file mode 100644
index 0000000000..d9b3283f29
--- /dev/null
+++ b/target/linux/apm821xx/config-4.9
@@ -0,0 +1,327 @@ 
+# CONFIG_40x is not set
+CONFIG_44x=y
+CONFIG_460EX=y
+CONFIG_4xx=y
+CONFIG_4xx_SOC=y
+# CONFIG_ADVANCED_OPTIONS is not set
+CONFIG_APM821xx=y
+CONFIG_APOLLO3G=y
+# CONFIG_ARCHES is not set
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
+CONFIG_ARCH_HAS_DMA_SET_COHERENT_MASK=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
+CONFIG_ARCH_HAS_ILOG2_U32=y
+CONFIG_ARCH_HAS_SG_CHAIN=y
+CONFIG_ARCH_HAS_WALK_MEMORY=y
+CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
+CONFIG_ARCH_PHYS_ADDR_T_64BIT=y
+# CONFIG_ARCH_RANDOM is not set
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
+CONFIG_ARCH_SUPPORTS_DEFERRED_STRUCT_PAGE_INIT=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_AUDIT_ARCH=y
+# CONFIG_BAMBOO is not set
+CONFIG_BCH=y
+CONFIG_BLK_MQ_PCI=y
+# CONFIG_BLUESTONE is not set
+CONFIG_BOOKE=y
+CONFIG_BOOKE_WDT=y
+# CONFIG_BOUNCE is not set
+CONFIG_BUCKMINSTER=y
+# CONFIG_CANYONLANDS is not set
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_CMDLINE="rootfstype=squashfs noinitrd"
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CONSISTENT_SIZE=0x00200000
+CONFIG_CPU_BIG_ENDIAN=y
+CONFIG_CRC16=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_DEV_PPC4XX=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_MD5_PPC=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_SHA1_PPC=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_DECOMPRESS_GZIP=y
+# CONFIG_DEFAULT_UIMAGE is not set
+CONFIG_DTC=y
+# CONFIG_E200 is not set
+CONFIG_EARLY_PRINTK=y
+# CONFIG_EBONY is not set
+CONFIG_EDAC_ATOMIC_SCRUB=y
+CONFIG_EDAC_SUPPORT=y
+# CONFIG_EIGER is not set
+# CONFIG_EPAPR_BOOT is not set
+CONFIG_EXTRA_TARGETS="uImage"
+CONFIG_FIXED_PHY=y
+CONFIG_FREEZER=y
+# CONFIG_FSL_LBC is not set
+# CONFIG_FSL_ULI1575 is not set
+CONFIG_GENERIC_ATOMIC64=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_GENERIC_CPU_AUTOPROBE=y
+# CONFIG_GENERIC_CSUM is not set
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+# CONFIG_GENERIC_TBSYNC is not set
+CONFIG_GENERIC_TIME_VSYSCALL_OLD=y
+# CONFIG_GEN_RTC is not set
+# CONFIG_GE_FPGA is not set
+# CONFIG_GLACIER is not set
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_GENERIC=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAS_RAPIDIO is not set
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+# CONFIG_HAVE_ARCH_BITREVERSE is not set
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_CBPF_JIT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
+CONFIG_HAVE_GENERIC_RCU_GUP=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK=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_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+# CONFIG_HAVE_SETUP_PER_CPU_AREA is not set
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING=y
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_PPC4XX=y
+CONFIG_HZ=1000
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_1000=y
+CONFIG_HZ_PERIODIC=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_IBM_IIC=y
+CONFIG_IBM_EMAC=y
+CONFIG_IBM_EMAC_EMAC4=y
+CONFIG_IBM_EMAC_POLL_WEIGHT=32
+CONFIG_IBM_EMAC_RGMII=y
+CONFIG_IBM_EMAC_RXB=128
+CONFIG_IBM_EMAC_RX_COPY_THRESHOLD=256
+CONFIG_IBM_EMAC_RX_SKB_HEADROOM=0
+CONFIG_IBM_EMAC_TAH=y
+CONFIG_IBM_EMAC_TXB=128
+# CONFIG_ICON is not set
+CONFIG_IKAREM=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_IOMMU_HELPER is not set
+# CONFIG_IPIC is not set
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+CONFIG_ISA_DMA_API=y
+# CONFIG_JFFS2_FS is not set
+# CONFIG_KATMAI is not set
+CONFIG_KERNEL_GZIP=y
+CONFIG_KERNEL_START=0xc0000000
+CONFIG_LEDS_TRIGGER_MTD=y
+CONFIG_LIBFDT=y
+CONFIG_LOWMEM_SIZE=0x30000000
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MDIO_BOARDINFO=y
+# CONFIG_MDIO_HISI_FEMAC is not set
+# CONFIG_MFD_ACT8945A is not set
+# CONFIG_MFD_MAX77620 is not set
+# CONFIG_MMIO_NVRAM is not set
+CONFIG_MODULES_USE_ELF_RELA=y
+# CONFIG_MPIC is not set
+# CONFIG_MPIC_U3_HT_IRQS is not set
+# CONFIG_MPIC_WEIRD is not set
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+# CONFIG_MTD_CFI_GEOMETRY is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_BCH=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_ECC_BCH=y
+CONFIG_MTD_NAND_ECC_SMC=y
+CONFIG_MTD_NAND_NDFC=y
+# CONFIG_MTD_SPLIT is not set
+# CONFIG_MTD_SPLIT_SQUASHFS_ROOT is not set
+CONFIG_NEED_DMA_MAP_STATE=y
+# CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK is not set
+CONFIG_NEED_PER_CPU_KM=y
+CONFIG_NEED_SG_DMA_LENGTH=y
+# CONFIG_NONSTATIC_KERNEL is not set
+CONFIG_NOT_COHERENT_CACHE=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_NR_IRQS=512
+CONFIG_OF=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_ADDRESS_PCI=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_GPIO=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_NET=y
+CONFIG_OF_PCI=y
+CONFIG_OF_PCI_IRQ=y
+CONFIG_OF_RESERVED_MEM=y
+CONFIG_OLD_SIGACTION=y
+CONFIG_OLD_SIGSUSPEND=y
+CONFIG_PAGE_OFFSET=0xc0000000
+CONFIG_PCI=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIE_PME=y
+CONFIG_PCI_BUS_ADDR_T_64BIT=y
+CONFIG_PCI_DISABLE_COMMON_QUIRKS=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_MSI=y
+# CONFIG_PCI_MSI_IRQ_DOMAIN is not set
+CONFIG_PGTABLE_LEVELS=2
+CONFIG_PHYLIB=y
+CONFIG_PHYSICAL_START=0x00000000
+CONFIG_PHYS_64BIT=y
+CONFIG_PHYS_ADDR_T_64BIT=y
+CONFIG_PM=y
+CONFIG_PM_AUTOSLEEP=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_WAKELOCKS_GC=y
+CONFIG_PM_WAKELOCKS_LIMIT=100
+CONFIG_PPC=y
+CONFIG_PPC32=y
+CONFIG_PPC44x_SIMPLE=y
+CONFIG_PPC4xx_CPM=y
+CONFIG_PPC4xx_GPIO=y
+# CONFIG_PPC4xx_HSTA_MSI is not set
+CONFIG_PPC4xx_MSI=y
+CONFIG_PPC4xx_OCM=y
+CONFIG_PPC4xx_PCI_EXPRESS=y
+# CONFIG_PPC64 is not set
+# CONFIG_PPC_47x is not set
+# CONFIG_PPC_85xx is not set
+# CONFIG_PPC_8xx is not set
+# CONFIG_PPC_970_NAP is not set
+CONFIG_PPC_ADV_DEBUG_DACS=2
+CONFIG_PPC_ADV_DEBUG_DAC_RANGE=y
+CONFIG_PPC_ADV_DEBUG_DVCS=2
+CONFIG_PPC_ADV_DEBUG_IACS=4
+CONFIG_PPC_ADV_DEBUG_REGS=y
+# CONFIG_PPC_BOOK3S_32 is not set
+# CONFIG_PPC_CELL is not set
+# CONFIG_PPC_CELL_NATIVE is not set
+# CONFIG_PPC_COPRO_BASE is not set
+CONFIG_PPC_DCR=y
+# CONFIG_PPC_DCR_MMIO is not set
+CONFIG_PPC_DCR_NATIVE=y
+# CONFIG_PPC_DOORBELL is not set
+# CONFIG_PPC_EARLY_DEBUG is not set
+# CONFIG_PPC_EPAPR_HV_PIC is not set
+CONFIG_PPC_FPU=y
+# CONFIG_PPC_I8259 is not set
+# CONFIG_PPC_ICP_HV is not set
+# CONFIG_PPC_ICP_NATIVE is not set
+# CONFIG_PPC_ICS_RTAS is not set
+CONFIG_PPC_INDIRECT_PCI=y
+CONFIG_PPC_LIB_RHEAP=y
+CONFIG_PPC_MMU_NOHASH=y
+# CONFIG_PPC_MM_SLICES is not set
+# CONFIG_PPC_MPC106 is not set
+CONFIG_PPC_MSI_BITMAP=y
+# CONFIG_PPC_P7_NAP is not set
+CONFIG_PPC_PCI_CHOICE=y
+# CONFIG_PPC_RTAS is not set
+CONFIG_PPC_UDBG_16550=y
+CONFIG_PPC_WERROR=y
+# CONFIG_PPC_XICS is not set
+# CONFIG_PQ2ADS is not set
+CONFIG_PTE_64BIT=y
+# CONFIG_QORIQ_THERMAL is not set
+# CONFIG_RAINIER is not set
+CONFIG_RAS=y
+# CONFIG_RCU_STALL_COMMON is not set
+CONFIG_RD_GZIP=y
+# CONFIG_RELOCATABLE is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+# CONFIG_SAM440EP is not set
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCHED_INFO is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SEQUOIA is not set
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_FSL=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_OF_PLATFORM=y
+# CONFIG_SG_POOL is not set
+CONFIG_SIMPLE_GPIO=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SRCU=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+# CONFIG_SWIOTLB is not set
+CONFIG_SWPHY=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+# CONFIG_TAISHAN is not set
+CONFIG_TASK_SIZE=0xc0000000
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_USB_SUPPORT=y
+CONFIG_VDSO32=y
+# CONFIG_VIRT_CPU_ACCOUNTING_NATIVE is not set
+# CONFIG_WARP is not set
+CONFIG_WATCHDOG_CORE=y
+CONFIG_WNDR4700=y
+# CONFIG_XILINX_SYSACE is not set
+# CONFIG_XILINX_VIRTEX440_GENERIC_BOARD is not set
+CONFIG_XZ_DEC_BCJ=y
+CONFIG_XZ_DEC_POWERPC=y
+# CONFIG_YOSEMITE is not set
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
diff --git a/target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch b/target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch
new file mode 100644
index 0000000000..018bcf21a0
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/200-add-meraki-mr24-ikarem-support.patch
@@ -0,0 +1,32 @@ 
+--- a/arch/powerpc/platforms/44x/Kconfig
++++ b/arch/powerpc/platforms/44x/Kconfig
+@@ -40,6 +40,19 @@ config EBONY
+ 	help
+ 	  This option enables support for the IBM PPC440GP evaluation board.
+ 
++config IKAREM
++	bool "Ikarem"
++	depends on 44x
++	default n
++	select PPC44x_SIMPLE
++	select APM821xx
++	select PCI_MSI
++	select PPC4xx_MSI
++	select PPC4xx_PCI_EXPRESS
++	select IBM_EMAC_RGMII
++	help
++	  This option enables support for the Cisco Meraki MR24 (Ikarem) Access Point.
++
+ config SAM440EP
+         bool "Sam440ep"
+ 	depends on 44x
+--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
++++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
+@@ -62,6 +62,7 @@ static char *board[] __initdata = {
+ 	"amcc,sequoia",
+ 	"amcc,taishan",
+ 	"amcc,yosemite",
++	"meraki,ikarem",
+ 	"mosaixtech,icon"
+ };
+ 
diff --git a/target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch b/target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch
new file mode 100644
index 0000000000..0581a43e33
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/201-add-amcc-apollo3g-support.patch
@@ -0,0 +1,30 @@ 
+--- a/arch/powerpc/platforms/44x/Kconfig
++++ b/arch/powerpc/platforms/44x/Kconfig
+@@ -143,6 +143,17 @@ config CANYONLANDS
+ 	help
+ 	  This option enables support for the AMCC PPC460EX evaluation board.
+ 
++config APOLLO3G
++	bool "Apollo3G"
++	depends on 44x
++	default n
++	select PPC44x_SIMPLE
++	select APM821xx
++	select IBM_EMAC_RGMII
++	select 460EX
++	help
++	  This option enables support for the AMCC Apollo 3G board.
++
+ config GLACIER
+ 	bool "Glacier"
+ 	depends on 44x
+--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
++++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
+@@ -50,6 +50,7 @@ machine_device_initcall(ppc44x_simple, p
+  * board.c file for it rather than adding it to this list.
+  */
+ static char *board[] __initdata = {
++	"amcc,apollo3g",
+ 	"amcc,arches",
+ 	"amcc,bamboo",
+ 	"apm,bluestone",
diff --git a/target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch b/target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch
new file mode 100644
index 0000000000..6b6db1dddb
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/202-add-netgear-wndr4700-support.patch
@@ -0,0 +1,32 @@ 
+--- a/arch/powerpc/platforms/44x/Makefile
++++ b/arch/powerpc/platforms/44x/Makefile
+@@ -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
++++ b/arch/powerpc/platforms/44x/Kconfig
+@@ -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.9/203-add-meraki-mx60-buckminster-support.patch b/target/linux/apm821xx/patches-4.9/203-add-meraki-mx60-buckminster-support.patch
new file mode 100644
index 0000000000..8c072387d4
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/203-add-meraki-mx60-buckminster-support.patch
@@ -0,0 +1,32 @@ 
+--- a/arch/powerpc/platforms/44x/Kconfig
++++ b/arch/powerpc/platforms/44x/Kconfig
+@@ -30,6 +30,19 @@ config BLUESTONE
+ 	help
+ 	  This option enables support for the APM APM821xx Evaluation board.
+ 
++config BUCKMINSTER
++	bool "Buckminster"
++	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 Cisco Meraki MX60/MX60W (Buckminster) Security Appliance
++
+ config EBONY
+ 	bool "Ebony"
+ 	depends on 44x
+--- a/arch/powerpc/platforms/44x/ppc44x_simple.c
++++ b/arch/powerpc/platforms/44x/ppc44x_simple.c
+@@ -63,6 +63,7 @@ static char *board[] __initdata = {
+ 	"amcc,sequoia",
+ 	"amcc,taishan",
+ 	"amcc,yosemite",
++	"meraki,buckminster",
+ 	"meraki,ikarem",
+ 	"mosaixtech,icon"
+ };
diff --git a/target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch b/target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch
new file mode 100644
index 0000000000..0b1affcfe7
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/300-fix-atheros-nics-on-apm82181.patch
@@ -0,0 +1,51 @@ 
+--- a/arch/powerpc/sysdev/ppc4xx_pci.c
++++ b/arch/powerpc/sysdev/ppc4xx_pci.c
+@@ -1066,15 +1066,24 @@ static int __init apm821xx_pciex_init_po
+ 	u32 val;
+ 
+ 	/*
+-	 * Do a software reset on PCIe ports.
+-	 * This code is to fix the issue that pci drivers doesn't re-assign
+-	 * bus number for PCIE devices after Uboot
+-	 * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000
+-	 * PT quad port, SAS LSI 1064E)
++	 * Only reset the PHY when no link is currently established.
++	 * This is for the Atheros PCIe board which has problems to establish
++	 * the link (again) after this PHY reset. All other currently tested
++	 * PCIe boards don't show this problem.
+ 	 */
+-
+-	mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0);
+-	mdelay(10);
++	val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
++	if (!(val & 0x00001000)) {
++		/*
++		 * Do a software reset on PCIe ports.
++		 * This code is to fix the issue that pci drivers doesn't re-assign
++		 * bus number for PCIE devices after Uboot
++		 * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000
++		 * PT quad port, SAS LSI 1064E)
++		 */
++
++		mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0);
++		mdelay(10);
++	}
+ 
+ 	if (port->endpoint)
+ 		val = PTYPE_LEGACY_ENDPOINT << 20;
+@@ -1091,9 +1100,12 @@ static int __init apm821xx_pciex_init_po
+ 	mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130);
+ 	mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006);
+ 
+-	mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000);
+-	mdelay(50);
+-	mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000);
++	val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
++	if (!(val & 0x00001000)) {
++		mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000);
++		mdelay(50);
++		mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000);
++	}
+ 
+ 	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+ 		mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) |
diff --git a/target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch b/target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch
new file mode 100644
index 0000000000..d6a1006177
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/301-fix-memory-map-wndr4700.patch
@@ -0,0 +1,14 @@ 
+--- a/arch/powerpc/sysdev/ppc4xx_pci.c
++++ b/arch/powerpc/sysdev/ppc4xx_pci.c
+@@ -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.9/701-powerpc_ibm_apm82181_phyclk_fix.patch b/target/linux/apm821xx/patches-4.9/701-powerpc_ibm_apm82181_phyclk_fix.patch
new file mode 100644
index 0000000000..93a0858b26
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/701-powerpc_ibm_apm82181_phyclk_fix.patch
@@ -0,0 +1,52 @@ 
+--- a/drivers/net/ethernet/ibm/emac/core.c
++++ b/drivers/net/ethernet/ibm/emac/core.c
+@@ -129,6 +129,7 @@ static inline void emac_report_timeout_e
+ {
+ 	if (emac_has_feature(dev, EMAC_FTR_440GX_PHY_CLK_FIX |
+ 				  EMAC_FTR_460EX_PHY_CLK_FIX |
++				  EMAC_FTR_APM821XX_PHY_CLK_FIX |
+ 				  EMAC_FTR_440EP_PHY_CLK_FIX))
+ 		DBG(dev, "%s" NL, error);
+ 	else if (net_ratelimit())
+@@ -146,6 +147,10 @@ static inline void emac_rx_clk_tx(struct
+ 	if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
+ 		dcri_clrset(SDR0, SDR0_MFR,
+ 			    0, SDR0_MFR_ECS >> dev->cell_index);
++
++	if (emac_has_feature(dev, EMAC_FTR_APM821XX_PHY_CLK_FIX))
++                dcri_clrset(SDR0, SDR0_ETH_CFG,
++                        0, 0x00000100 >> dev->cell_index);
+ #endif
+ }
+ 
+@@ -155,6 +160,10 @@ static inline void emac_rx_clk_default(s
+ 	if (emac_has_feature(dev, EMAC_FTR_440EP_PHY_CLK_FIX))
+ 		dcri_clrset(SDR0, SDR0_MFR,
+ 			    SDR0_MFR_ECS >> dev->cell_index, 0);
++
++	if (emac_has_feature(dev, EMAC_FTR_APM821XX_PHY_CLK_FIX))
++		dcri_clrset(SDR0, SDR0_ETH_CFG,
++			    0x00000100 >> dev->cell_index, 0);
+ #endif
+ }
+ 
+@@ -2617,7 +2626,7 @@ static int emac_init_config(struct emac_
+ 		if (of_device_is_compatible(np, "ibm,emac-apm821xx")) {
+ 			dev->features |= (EMAC_APM821XX_REQ_JUMBO_FRAME_SIZE |
+ 					  EMAC_FTR_APM821XX_NO_HALF_DUPLEX |
+-					  EMAC_FTR_460EX_PHY_CLK_FIX);
++					  EMAC_FTR_APM821XX_PHY_CLK_FIX);
+ 		}
+ 	} else if (of_device_is_compatible(np, "ibm,emac4")) {
+ 		dev->features |= EMAC_FTR_EMAC4;
+--- a/drivers/net/ethernet/ibm/emac/core.h
++++ b/drivers/net/ethernet/ibm/emac/core.h
+@@ -333,6 +333,8 @@ struct emac_instance {
+  */
+ #define EMAC_FTR_APM821XX_NO_HALF_DUPLEX	0x00001000
+ 
++#define EMAC_FTR_APM821XX_PHY_CLK_FIX	0x000002000
++
+ /* Right now, we don't quite handle the always/possible masks on the
+  * most optimal way as we don't have a way to say something like
+  * always EMAC4. Patches welcome.
diff --git a/target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch b/target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch
new file mode 100644
index 0000000000..f1edb9cc7a
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/702-powerpc_ibm_phy_add_dt_parser.patch
@@ -0,0 +1,328 @@ 
+From b1c54da602ae9215cfbde1c3ed3b6296b76f07fc Mon Sep 17 00:00:00 2001
+Message-Id: <b1c54da602ae9215cfbde1c3ed3b6296b76f07fc.1486337989.git.chunkeey@googlemail.com>
+In-Reply-To: <246bd6614529d28dc48b11981ab5dae7a7364fc2.1486337989.git.chunkeey@googlemail.com>
+References: <246bd6614529d28dc48b11981ab5dae7a7364fc2.1486337989.git.chunkeey@googlemail.com>
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Mon, 13 Jun 2016 15:42:21 +0200
+Subject: [RFC 2/2] net: emac: add support for device-tree based PHY discovery
+ and setup
+To: netdev@vger.kernel.org,
+    devicetree@vger.kernel.org
+Cc: David S. Miller <davem@davemloft.net>,
+    Ivan Mikhaylov <ivan@de.ibm.com>,
+    Mark Rutland <mark.rutland@arm.com>,
+    Rob Herring <robh+dt@kernel.org>
+
+This patch adds glue-code that allows the EMAC driver to interface
+with the existing dt-supported PHYs in drivers/net/phy.
+
+Because currently, the emac driver maintains a small library of
+supported phys for in a private phy.c file located in the drivers
+directory.
+
+The support is limited to mostly single ethernet transceiver like the:
+CIS8201, BCM5248, ET1011C, Marvell 88E1111 and 88E1112, AR8035.
+However, routers like the Netgear WNDR4700 and Cisco Meraki MX60(W)
+have a 5-port switch (QCA8327N) attached to the MDIO of the EMAC.
+The switch chip has already a proper phy-driver (ar8216) that uses
+the generic phy library.
+
+Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
+---
+--- 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>
+@@ -2422,6 +2423,229 @@ static int emac_read_uint_prop(struct de
+ 	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);
++	int err;
++
++	err = emac_reset(dev);
++	if (err)
++		return err;
++	/* Meraki MX60(W)'s uboot will disable the switch and
++	 * a bus reset won't do anything. */
++	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->ofdev->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 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->ofdev->dev, "no mdio definition found.");
++		return -ENODEV;
++	}
++
++	if (!of_device_is_available(mii_np)) {
++		res = 1;
++		goto put_node;
++	}
++
++	dev->mii_bus = devm_mdiobus_alloc(&dev->ofdev->dev);
++	if (!dev->mii_bus) {
++		res = -ENOMEM;
++		goto put_node;
++	}
++
++	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->ofdev->name);
++	res = of_mdiobus_register(dev->mii_bus, mii_np);
++	if (res) {
++		dev_err(&dev->ofdev->dev, "cannot register MDIO bus %s (%d)",
++			dev->mii_bus->name, res);
++	}
++
++ 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 = devm_kzalloc(&dev->ofdev->dev, 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->ofdev->dev, "failed to connect to PHY.\n");
++		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->mdio.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) {
++			res = emac_dt_phy_probe(dev, phy_handle);
++			if (!res)
++				res = 1;
++			else
++				mdiobus_unregister(dev->mii_bus);
++		}
++	}
++
++	of_node_put(phy_handle);
++	/* if no phy device was specified 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;
+@@ -2492,6 +2716,22 @@ static int emac_init_phy(struct emac_ins
+ 
+ 	emac_configure(dev);
+ 
++	if (emac_has_feature(dev, EMAC_FTR_HAS_RGMII)) {
++		int res = emac_probe_dt_phy(dev);
++
++		if (res == 1) {
++			mutex_unlock(&emac_phy_map_lock);
++			goto init_phy;
++		} else if (res < 0) {
++			mutex_unlock(&emac_phy_map_lock);
++			dev_err(&dev->ofdev->dev, "failed to attach dt phy (%d).\n",
++			       res);
++			return res;
++		}
++
++		/* continue with old code */
++	}
++
+ 	if (dev->phy_address != 0xffffffff)
+ 		phy_map = ~(1 << dev->phy_address);
+ 
+@@ -2519,6 +2759,7 @@ static int emac_init_phy(struct emac_ins
+ 		return -ENXIO;
+ 	}
+ 
++ init_phy:
+ 	/* Init PHY */
+ 	if (dev->phy.def->ops->init)
+ 		dev->phy.def->ops->init(&dev->phy);
+@@ -2987,6 +3228,12 @@ static int emac_remove(struct platform_d
+ 	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);
++
++	if (dev->mii_bus)
++		mdiobus_unregister(dev->mii_bus);
++
+ 	busy_phy_map &= ~(1 << dev->phy.address);
+ 	DBG(dev, "busy_phy_map now %#x" NL, busy_phy_map);
+ 
+--- 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;
diff --git a/target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch b/target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch
new file mode 100644
index 0000000000..be5cb838aa
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/801-usb-xhci-add-firmware-loader-for-uPD720201-and-uPD72.patch
@@ -0,0 +1,545 @@ 
+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(+)
+
+--- 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"
+@@ -221,6 +223,458 @@ static void xhci_pme_acpi_rtd3_enable(st
+ 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)
+ {
+@@ -260,6 +714,22 @@ static int xhci_pci_probe(struct pci_dev
+ 	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 */
+@@ -317,6 +787,16 @@ static void xhci_pci_remove(struct pci_d
+ {
+ 	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) {
diff --git a/target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch b/target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch
new file mode 100644
index 0000000000..a201d79104
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/802-usb-xhci-force-msi-renesas-xhci.patch
@@ -0,0 +1,54 @@ 
+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(+)
+
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -188,7 +188,7 @@ static void xhci_pci_quirks(struct devic
+ 	}
+ 	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;
+ 
+--- a/drivers/usb/host/xhci.c
++++ b/drivers/usb/host/xhci.c
+@@ -387,10 +387,14 @@ static int xhci_try_enable_msi(struct us
+ 		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 */
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1674,6 +1674,7 @@ struct xhci_hcd {
+ 	/* support xHCI 0.96 spec USB2 software LPM */
+ 	unsigned		sw_lpm_support:1;
+ 	/* support xHCI 1.0 spec USB2 hardware LPM */
++#define XHCI_FORCE_MSI		(1 << 24)
+ 	unsigned		hw_lpm_support:1;
+ 	/* cached usb2 extened protocol capabilites */
+ 	u32                     *ext_caps;
diff --git a/target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch b/target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch
new file mode 100644
index 0000000000..3dd2ed5734
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/804-usb-dwc2-add-amcc-usb-otg-405ex.patch
@@ -0,0 +1,48 @@ 
+--- a/drivers/usb/dwc2/platform.c
++++ b/drivers/usb/dwc2/platform.c
+@@ -279,6 +279,37 @@ static int dwc2_get_dr_mode(struct dwc2_
+ 	return 0;
+ }
+ 
++static const struct dwc2_core_params params_amcc_dwc_otg = {
++	.otg_cap			= DWC2_CAP_PARAM_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);
+@@ -511,6 +542,7 @@ static void dwc2_driver_shutdown(struct
+ }
+ 
+ static const struct of_device_id dwc2_of_match_table[] = {
++	{ .compatible = "amcc,usb-otg-405ex", .data = &params_amcc_dwc_otg },
+ 	{ .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
+ 	{ .compatible = "hisilicon,hi6220-usb", .data = &params_hi6220 },
+ 	{ .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
diff --git a/target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch b/target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch
new file mode 100644
index 0000000000..ceacde9dda
--- /dev/null
+++ b/target/linux/apm821xx/patches-4.9/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch
@@ -0,0 +1,1027 @@ 
+From 5ea2e152d846bf60901107fefd81a58f792f3bc2 Mon Sep 17 00:00:00 2001
+From: Christian Lamparter <chunkeey@gmail.com>
+Date: Fri, 10 Jun 2016 03:00:46 +0200
+Subject: [PATCH] hwmon: add driver for Microchip TC654/TC655 PWM fan
+ controllers
+
+This patch adds a hwmon driver for the Microchip TC654 and TC655
+Dual SMBus PWM Fan Speed Controllers with Fan Fault detection.
+
+The chip is described in the DS2001734C Spec Document from Microchip.
+It supports:
+	- Shared PWM Fan Drive for two fans
+	- Provides RPM
+	- automatic PWM controller (needs additional
+	  NTC/PTC Thermistors.)
+	- Overtemperature alarm (when using NTC/PTC
+	  Thermistors)
+
+Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
+---
+ drivers/hwmon/Kconfig  |  10 +
+ drivers/hwmon/Makefile |   1 +
+ drivers/hwmon/tc654.c  | 969 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 980 insertions(+)
+ create mode 100644 drivers/hwmon/tc654.c
+
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -1549,6 +1549,16 @@ config SENSORS_INA3221
+ 	  This driver can also be built as a module.  If so, the module
+ 	  will be called ina3221.
+ 
++config SENSORS_TC654
++	tristate "Microchip TC654 and TC655"
++	depends on I2C
++	help
++	  If you say yes here you get support for Microchip TC655 and TC654
++	  Dual PWM Fan Speed Controllers and sensor chips.
++
++	  This driver can also be built as a module.  If so, the module
++	  will be called tc654.
++
+ config SENSORS_TC74
+ 	tristate "Microchip TC74"
+ 	depends on I2C
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -148,6 +148,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc4
+ obj-$(CONFIG_SENSORS_SMSC47M1)	+= smsc47m1.o
+ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+ obj-$(CONFIG_SENSORS_AMC6821)	+= amc6821.o
++obj-$(CONFIG_SENSORS_TC654)	+= tc654.o
+ obj-$(CONFIG_SENSORS_TC74)	+= tc74.o
+ obj-$(CONFIG_SENSORS_THMC50)	+= thmc50.o
+ obj-$(CONFIG_SENSORS_TMP102)	+= tmp102.o
+--- /dev/null
++++ b/drivers/hwmon/tc654.c
+@@ -0,0 +1,969 @@
++/*
++ * tc654.c - Support for Microchip TC654/TC655
++ * "A Dual SMBus PWM FAN Speed Controllers with Fan Fault Detection"
++ *
++ * Copyright (c) 2016 Christian Lamparter <chunkeey@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation version 2 of the License.
++ *
++ * 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.
++ *
++ * The chip is described in the DS2001734C Spec Document from Microchip.
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/jiffies.h>
++#include <linux/i2c.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/err.h>
++#include <linux/mutex.h>
++#include <linux/thermal.h>
++
++/* Hardware definitions */
++/* 5.1.4 Address Byte stats that TC654/TC655 are fixed at 0x1b */
++static const unsigned short normal_i2c[] = { 0x1b, I2C_CLIENT_END };
++
++enum TC654_REGS {
++	TC654_REG_RPM1 = 0x00,
++	TC654_REG_RPM2,
++	TC654_REG_FAN1_FAULT_THRESH,
++	TC654_REG_FAN2_FAULT_THRESH,
++	TC654_REG_CONFIG,
++	TC654_REG_STATUS,
++	TC654_REG_DUTY_CYCLE,
++	TC654_REG_MFR_ID,
++	TC654_REG_VER_ID,
++
++	/* keep last */
++	__TC654_REG_NUM,
++};
++
++#define TC654_MFR_ID_MICROCHIP		0x84
++#define TC654_VER_ID			0x00
++#define TC655_VER_ID			0x01
++
++enum TC654_CONTROL_BITS {
++	TC654_CTRL_SDM		= BIT(0),
++	TC654_CTRL_F1PPR_S	= 1,
++	TC654_CTRL_F1PPR_M	= (BIT(1) | BIT(2)),
++	TC654_CTRL_F2PPR_S	= 3,
++	TC654_CTRL_F2PPR_M	= (BIT(3) | BIT(4)),
++	TC654_CTRL_DUTYC	= BIT(5),
++	TC654_CTRL_RES		= BIT(6),
++	TC654_CTRL_FFCLR	= BIT(7),
++};
++
++enum TC654_STATUS_BITS {
++	TC654_STATUS_F1F	= BIT(0),
++	TC654_STATUS_F2F	= BIT(1),
++	TC654_STATUS_VSTAT	= BIT(2),
++	TC654_STATUS_R1CO	= BIT(3),
++	TC654_STATUS_R2CO	= BIT(4),
++	TC654_STATUS_OTF	= BIT(5),
++};
++
++enum TC654_FAN {
++	TC654_FAN1 = 0,
++	TC654_FAN2,
++
++	/* keep last */
++	__NUM_TC654_FAN,
++};
++
++enum TC654_FAN_MODE {
++	TC654_PWM_OFF,	/* Shutdown Mode - switch of both fans */
++	TC654_PWM_VIN,	/* Fans will be controlled via V_in analog input pin */
++	TC654_PWM_3000,	/* sets fans to 30% duty cycle */
++	TC654_PWM_3467,
++	TC654_PWM_3933, /* default case - if V_in pin is open */
++	TC654_PWM_4400,
++	TC654_PWM_4867,
++	TC654_PWM_5333,
++	TC654_PWM_5800,
++	TC654_PWM_6267,
++	TC654_PWM_6733,
++	TC654_PWM_7200,
++	TC654_PWM_7667,
++	TC654_PWM_8133,
++	TC654_PWM_8600,
++	TC654_PWM_9067,
++	TC654_PWM_9533,
++	TC654_PWM_10000, /* sets fans to 100% duty cycle */
++};
++
++enum TC654_ALARMS {
++	TC654_ALARM_FAN1_FAULT,
++	TC654_ALARM_FAN2_FAULT,
++	TC654_ALARM_FAN1_COUNTER_OVERFLOW,
++	TC654_ALARM_FAN2_COUNTER_OVERFLOW,
++	TC654_ALARM_OVER_TEMPERATURE,
++
++	/* KEEP LAST */
++	__NUM_TC654_ALARMS,
++};
++
++static const struct pwm_table_entry {
++	u8 min;
++	enum TC654_FAN_MODE mode;
++} pwm_table[] = {
++	{  0, TC654_PWM_OFF  },
++	{  1, TC654_PWM_3000 },
++	{ 88, TC654_PWM_3467 },
++	{101, TC654_PWM_3933 },
++	{113, TC654_PWM_4400 },
++	{125, TC654_PWM_4867 },
++	{137, TC654_PWM_5333 },
++	{148, TC654_PWM_5800 },
++	{160, TC654_PWM_6267 },
++	{172, TC654_PWM_6733 },
++	{184, TC654_PWM_7200 },
++	{196, TC654_PWM_7667 },
++	{208, TC654_PWM_8133 },
++	{220, TC654_PWM_8600 },
++	{232, TC654_PWM_9067 },
++	{244, TC654_PWM_9533 },
++	{255, TC654_PWM_10000 },
++};
++
++/* driver context */
++struct tc654 {
++	struct i2c_client	*client;
++
++	struct mutex		update_lock;
++
++	unsigned long		last_updated;	/* in jiffies */
++	u8			cached_regs[__TC654_REG_NUM];
++
++	bool			valid;	/* monitored registers are valid */
++	u16			fan_input[__NUM_TC654_FAN];
++	bool			alarms[__NUM_TC654_ALARMS];
++	bool			vin_status;
++	bool			pwm_manual;
++
++	/* optional cooling device */
++	struct thermal_cooling_device *cdev;
++};
++
++/* hardware accessors and functions */
++static int read_tc(struct tc654 *tc, u8 reg)
++{
++	s32 status;
++
++	if (reg <= TC654_REG_VER_ID) {
++		/* Table 6.1 states that all registers are readable */
++		status = i2c_smbus_read_byte_data(tc->client, reg);
++	} else
++		status = -EINVAL;
++
++	if (status < 0) {
++		dev_warn(&tc->client->dev, "can't read register 0x%02x due to error (%d)",
++			 reg, status);
++	} else {
++		tc->cached_regs[reg] = status;
++	}
++
++	return status;
++}
++
++static int write_tc(struct tc654 *tc, u8 i2c_reg, u8 val)
++{
++	s32 status;
++
++	/*
++	 * Table 6.1 states that both fan threshold registers,
++	 * the Config and Duty Cycle are writeable.
++	 */
++	switch (i2c_reg) {
++	case TC654_REG_FAN1_FAULT_THRESH:
++	case TC654_REG_FAN2_FAULT_THRESH:
++	case TC654_REG_DUTY_CYCLE:
++	case TC654_REG_CONFIG:
++		status = i2c_smbus_write_byte_data(tc->client, i2c_reg, val);
++		break;
++
++	default:
++		return -EINVAL;
++	}
++
++	if (status < 0) {
++		dev_warn(&tc->client->dev, "can't write register 0x%02x with value 0x%02x due to error (%d)",
++			 i2c_reg, val, status);
++	} else {
++		tc->cached_regs[i2c_reg] = val;
++	}
++
++	return status;
++}
++
++static int mod_config(struct tc654 *tc, u8 set, u8 clear)
++{
++	u8 val = 0;
++
++	/* a bit can't be set and cleared on the same time. */
++	if (set & clear)
++		return -EINVAL;
++
++	/* invalidate data to force re-read from hardware */
++	tc->valid = false;
++	val = (tc->cached_regs[TC654_REG_CONFIG] | set) & (~clear);
++	return write_tc(tc, TC654_REG_CONFIG, val);
++}
++
++static int read_fan_rpm(struct tc654 *tc, enum TC654_FAN fan)
++{
++	int ret;
++
++	/* 6.1 RPM-OUTPUT1 and RPM-OUTPUT2 registers */
++	ret = read_tc(tc, fan == TC654_FAN1 ? TC654_REG_RPM1 : TC654_REG_RPM2);
++	if (ret < 0)
++		return ret;
++
++	/*
++	 * The Resolution Selection Bit in 6.3 CONFIGURATION REGISTER
++	 * is needed to convert the raw value to the RPM.
++	 * 0 = RPM1 and RPM2 use (8-Bit) resolution => * 50 RPM
++	 * 1 = RPM1 and RPM2 use (9-Bit) resolution => * 25 RPM
++	 */
++	return ret * (25 <<
++		!(tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES));
++}
++
++static int write_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan,
++				  u16 rpm)
++{
++	u8 converted_rpm;
++
++	if (rpm > 12750)
++		return -EINVAL;
++
++	/*
++	 * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers
++	 *
++	 * Both registers operate in 50 RPM mode exclusively.
++	 */
++	converted_rpm = rpm / 50;
++
++	/* invalidate data to force re-read from hardware */
++	tc->valid = false;
++	return write_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH :
++		TC654_REG_FAN2_FAULT_THRESH, converted_rpm);
++}
++
++
++static int read_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan)
++{
++	/*
++	 * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers
++	 *
++	 * Both registers operate in 50 RPM mode exclusively.
++	 */
++	return read_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH :
++		TC654_REG_FAN2_FAULT_THRESH) * 50;
++}
++
++static enum TC654_FAN_MODE get_fan_mode(struct tc654 *tc)
++{
++	if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) {
++		return TC654_PWM_OFF;
++	} else if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_DUTYC) {
++		return TC654_PWM_3000 + tc->cached_regs[TC654_REG_DUTY_CYCLE];
++	} else if (tc->vin_status == 0)
++		return TC654_PWM_VIN;
++
++	return -EINVAL;
++}
++
++static int write_fan_mode(struct tc654 *tc, enum TC654_FAN_MODE mode)
++{
++	int err;
++	u8 pwm_mode;
++	bool in_sdm;
++
++	in_sdm = !!(tc->cached_regs[TC654_REG_CONFIG] &
++		TC654_CTRL_SDM);
++
++	switch (mode) {
++	case TC654_PWM_OFF:
++		if (in_sdm)
++			return 0;
++
++		/* Enter Shutdown Mode - Switches off all fans */
++		err = mod_config(tc, TC654_CTRL_SDM, TC654_CTRL_DUTYC);
++		if (err)
++			return err;
++
++		return 0;
++
++	case TC654_PWM_VIN:
++		if (tc->vin_status) {
++			dev_err(&tc->client->dev,
++				"V_in pin is open, can't enable automatic mode.");
++			return -EINVAL;
++		}
++
++		err = mod_config(tc, 0, TC654_CTRL_SDM | TC654_CTRL_DUTYC);
++		if (err)
++			return err;
++
++		tc->pwm_manual = false;
++		return 0;
++
++	case TC654_PWM_3000:
++	case TC654_PWM_3467:
++	case TC654_PWM_3933:
++	case TC654_PWM_4400:
++	case TC654_PWM_4867:
++	case TC654_PWM_5333:
++	case TC654_PWM_5800:
++	case TC654_PWM_6267:
++	case TC654_PWM_6733:
++	case TC654_PWM_7200:
++	case TC654_PWM_7667:
++	case TC654_PWM_8133:
++	case TC654_PWM_8600:
++	case TC654_PWM_9067:
++	case TC654_PWM_9533:
++	case TC654_PWM_10000:
++		pwm_mode = mode - TC654_PWM_3000;
++		if (!in_sdm) {
++			err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode);
++			if (err)
++				return err;
++		}
++
++		err = mod_config(tc, TC654_CTRL_DUTYC, TC654_CTRL_SDM);
++		if (err)
++			return err;
++
++		tc->pwm_manual = true;
++
++		if (in_sdm) {
++			/*
++			 * In case the TC654/TC655 was in SDM mode, the write
++			 * above into the TC654_REG_DUTY_CYCLE register will
++			 * have no effect because the chip was switched off.
++			 *
++			 * Note: The TC654/TC655 have a special "power-on"
++			 * feature where the PWM will be forced to 100% for
++			 * one full second in order to spin-up a resting fan.
++			 */
++			err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode);
++			if (err)
++				return err;
++		}
++
++		return 0;
++
++	default:
++		return -EINVAL;
++	}
++}
++
++static struct tc654 *tc654_update_device(struct device *dev)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++
++	mutex_lock(&tc->update_lock);
++
++	/*
++	 * In Chapter "1.0 Electrical Characteristics",
++	 * the "Fault Output Response Time" is specified as 2.4 seconds.
++	 */
++	if (time_after(jiffies, tc->last_updated + 2 * HZ + (HZ * 2) / 5)
++	    || !tc->valid) {
++		size_t i;
++		int ret;
++		bool alarm_triggered;
++
++		tc->valid = false;
++
++		for (i = 0; i < __NUM_TC654_FAN; i++) {
++			ret = read_fan_rpm(tc, i);
++			if (ret < 0)
++				goto out;
++
++			tc->fan_input[i] = ret;
++		}
++
++		ret = read_tc(tc, TC654_REG_STATUS);
++		if (ret < 0)
++			goto out;
++
++		alarm_triggered = !!(ret & (TC654_STATUS_F1F |
++			TC654_STATUS_F2F | TC654_STATUS_R1CO |
++			TC654_STATUS_R2CO | TC654_STATUS_OTF));
++
++		tc->alarms[TC654_ALARM_FAN1_FAULT] = !!(ret & TC654_STATUS_F1F);
++		tc->alarms[TC654_ALARM_FAN2_FAULT] = !!(ret & TC654_STATUS_F2F);
++		tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] =
++			!!(ret & TC654_STATUS_R1CO);
++		tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW] =
++			!!(ret & TC654_STATUS_R2CO);
++		tc->alarms[TC654_ALARM_OVER_TEMPERATURE] =
++			!!(ret & TC654_STATUS_OTF);
++		tc->vin_status = !!(ret & TC654_STATUS_VSTAT);
++
++		/*
++		 * From 4.5 and 6.3
++		 *
++		 * ... "If the V_in pin is open when TC654_CTRL_DUTYC is not
++		 * selected, then V_out duty cycle will default to 39.33%.".
++		 *
++		 * and most importantly 6.5:
++		 * ... "V_in pin is open, the duty cycle will go to the default
++		 * setting of this register, which is 0010 (39.33%)."
++		 */
++		tc->pwm_manual |= tc->vin_status &&
++			(tc->cached_regs[TC654_REG_CONFIG] &
++			 TC654_CTRL_DUTYC);
++
++		if (alarm_triggered) {
++			/*
++			 * as the name implies, this FLAG needs to be
++			 * set in order to clear the FAN Fault error.
++			 */
++			ret = mod_config(tc, TC654_CTRL_FFCLR, 0);
++			if (ret < 0)
++				goto out;
++		}
++
++		tc->last_updated = jiffies;
++		tc->valid = true;
++	}
++
++out:
++	mutex_unlock(&tc->update_lock);
++	return tc;
++}
++
++static u8 get_fan_pulse(struct tc654 *tc, enum TC654_FAN fan)
++{
++	u8 fan_pulse_mask = fan == TC654_FAN1 ?
++		TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M;
++	u8 fan_pulse_shift = fan  == TC654_FAN1 ?
++		TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S;
++
++	return 1 << ((tc->cached_regs[TC654_REG_CONFIG] & fan_pulse_mask) >>
++		     fan_pulse_shift);
++}
++
++static int
++set_fan_pulse(struct tc654 *tc, enum TC654_FAN fan, int pulses)
++{
++	int old_pulses;
++	int err;
++	u8 new_pulse_per_rotation;
++	u8 fan_pulse_mask = fan == TC654_FAN1 ?
++		TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M;
++	u8 fan_pulse_shift = fan  == TC654_FAN1 ?
++		TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S;
++
++	switch (pulses) {
++	case 1:
++		new_pulse_per_rotation = 0;
++		break;
++	case 2:
++		new_pulse_per_rotation = 1;
++		break;
++	case 4:
++		new_pulse_per_rotation = 2;
++		break;
++	case 8:
++		new_pulse_per_rotation = 3;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	new_pulse_per_rotation <<= fan_pulse_shift;
++	new_pulse_per_rotation &= fan_pulse_mask;
++
++	old_pulses = tc->cached_regs[TC654_REG_CONFIG];
++	old_pulses &= fan_pulse_mask;
++
++	if (new_pulse_per_rotation == old_pulses)
++		return 0;
++
++	mutex_lock(&tc->update_lock);
++	err = mod_config(tc, new_pulse_per_rotation,
++			    old_pulses & (~new_pulse_per_rotation));
++	mutex_unlock(&tc->update_lock);
++
++	/* invalidate RPM data to force re-read from hardware */
++	tc->valid = false;
++
++	return err;
++}
++
++static int get_fan_speed(struct tc654 *tc)
++{
++	enum TC654_FAN_MODE mode;
++	size_t i;
++
++	mode = get_fan_mode(tc);
++	for (i = 0; i < ARRAY_SIZE(pwm_table); i++) {
++		if (mode == pwm_table[i].mode)
++			return pwm_table[i].min;
++	}
++
++	return -EINVAL;
++}
++
++static int set_fan_speed(struct tc654 *tc, int new_value)
++{
++	int result;
++	size_t i;
++
++	if (new_value > pwm_table[ARRAY_SIZE(pwm_table) - 1].min ||
++	    new_value < pwm_table[0].min)
++		return -EINVAL;
++
++	for (i = 0; i < ARRAY_SIZE(pwm_table); i++) {
++		/* exact match */
++		if (pwm_table[i].min == new_value)
++			break;
++
++		/* a little bit too big - go with the previous entry */
++		if (pwm_table[i].min > new_value) {
++			--i;
++			break;
++		}
++	}
++
++	mutex_lock(&tc->update_lock);
++	result = write_fan_mode(tc, pwm_table[i].mode);
++	mutex_unlock(&tc->update_lock);
++	if (result < 0)
++		return result;
++
++	return 0;
++}
++
++/* sysfs */
++
++static ssize_t
++show_fan_input(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++	int nr = to_sensor_dev_attr(da)->index;
++
++	return sprintf(buf, "%d\n", tc->fan_input[nr]);
++}
++
++static ssize_t
++show_fan_min(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	int nr = to_sensor_dev_attr(da)->index;
++
++	return sprintf(buf, "%d\n", read_fan_fault_thresh(tc, nr));
++}
++
++static ssize_t
++show_fan_min_alarm(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++	int nr = to_sensor_dev_attr(da)->index;
++
++	return sprintf(buf, "%d\n", nr == TC654_FAN1 ?
++		       tc->alarms[TC654_ALARM_FAN1_FAULT] :
++		       tc->alarms[TC654_ALARM_FAN2_FAULT]);
++}
++
++static ssize_t
++show_fan_max_alarm(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++	int nr = to_sensor_dev_attr(da)->index;
++
++	return sprintf(buf, "%d\n", nr == TC654_FAN1 ?
++		       tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] :
++		       tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW]);
++}
++
++static ssize_t
++set_fan_min(struct device *dev, struct device_attribute *da,
++	    const char *buf, size_t count)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	long new_min;
++	int nr = to_sensor_dev_attr(da)->index;
++	int old_min = read_fan_fault_thresh(tc, nr);
++	int status = kstrtol(buf, 10, &new_min);
++
++	if (status < 0)
++		return status;
++
++	new_min = (new_min / 50) * 50;
++	if (new_min == old_min) /* No change */
++		return count;
++
++	if (new_min < 0 || new_min > 12750)
++		return -EINVAL;
++
++	mutex_lock(&tc->update_lock);
++	status = write_fan_fault_thresh(tc, nr, new_min);
++	mutex_unlock(&tc->update_lock);
++	return count;
++}
++
++static ssize_t
++show_fan_max(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	int max_rpm = tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES ?
++		(((1 << 9) - 1) * 25) /* ((2**9) - 1) * 25 RPM */:
++		(((1 << 8) - 1) * 50) /* ((2**8) - 1) * 50 RPM */;
++
++	return sprintf(buf, "%d\n", max_rpm);
++}
++
++static ssize_t
++show_fan_fault(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++	int nr = to_sensor_dev_attr(da)->index;
++	u8 fan_fault_mask = nr == TC654_FAN1 ?
++		TC654_STATUS_F1F : TC654_STATUS_F2F;
++
++	return sprintf(buf, "%d\n",
++		!!(tc->cached_regs[TC654_REG_STATUS] & fan_fault_mask));
++}
++
++static ssize_t
++show_fan_pulses(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	int nr = to_sensor_dev_attr(da)->index;
++
++	return sprintf(buf, "%d\n", get_fan_pulse(tc, nr));
++}
++
++static ssize_t
++set_fan_pulses(struct device *dev, struct device_attribute *da,
++	      const char *buf, size_t count)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	long new_pulse;
++	int nr = to_sensor_dev_attr(da)->index;
++	int status = kstrtol(buf, 10, &new_pulse);
++
++	if (status < 0)
++		return status;
++
++	status = set_fan_pulse(tc, nr, new_pulse);
++	if (status < 0)
++		return status;
++
++	return count;
++}
++
++static ssize_t
++show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++	int pwm_enabled;
++
++	if ((tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) &&
++	    !tc->pwm_manual) {
++		pwm_enabled = 0; /* full off */
++	} else {
++		if (tc->valid && tc->vin_status == 0)
++			pwm_enabled = 2; /* automatic fan speed control */
++
++		pwm_enabled = 1; /* PWM Mode */
++	}
++
++	return sprintf(buf, "%d\n", pwm_enabled);
++}
++
++static ssize_t
++set_pwm_enable(struct device *dev, struct device_attribute *da,
++	       const char *buf, size_t count)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	long new_value;
++
++	int result = kstrtol(buf, 10, &new_value);
++
++	if (result < 0)
++		return result;
++
++	mutex_lock(&tc->update_lock);
++	switch (new_value) {
++	case 0: /* no fan control (i.e. is OFF) */
++		result = write_fan_mode(tc, TC654_PWM_OFF);
++		tc->pwm_manual = false;
++		break;
++
++	case 1: /* manual fan control enabled (using pwm) */
++		result = write_fan_mode(tc, TC654_PWM_10000);
++		break;
++
++	case 2: /* automatic fan speed control enabled */
++		result = write_fan_mode(tc, TC654_PWM_VIN);
++		break;
++
++	default:
++		result = -EINVAL;
++	}
++
++	mutex_unlock(&tc->update_lock);
++	return result < 0 ? result : count;
++}
++
++static ssize_t
++show_pwm(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++	int ret;
++
++	ret = get_fan_speed(tc);
++	if (ret < 0)
++		return ret;
++
++	return sprintf(buf, "%d\n", ret);
++}
++
++static ssize_t
++set_pwm(struct device *dev, struct device_attribute *da,
++	const char *buf, size_t count)
++{
++	struct tc654 *tc = dev_get_drvdata(dev);
++	long new_value = -1;
++	int result = kstrtol(buf, 10, &new_value);
++
++	if (result < 0)
++		return result;
++
++	if (new_value < 0 || new_value > INT_MAX)
++		return -EINVAL;
++
++	if (!tc->pwm_manual)
++		return -EINVAL;
++
++	result = set_fan_speed(tc, new_value);
++	if (result < 0)
++		return result;
++
++	return count;
++}
++
++static ssize_t
++show_temp_alarm_otf(struct device *dev, struct device_attribute *da, char *buf)
++{
++	struct tc654 *tc = tc654_update_device(dev);
++
++	return sprintf(buf, "%d\n", tc->alarms[TC654_ALARM_OVER_TEMPERATURE]);
++}
++
++static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input,
++			  NULL, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, show_fan_min,
++			  set_fan_min, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_fan_min_alarm,
++			  NULL, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, show_fan_max_alarm,
++			  NULL, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO, show_fan_max, NULL, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault,
++			  NULL, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan1_pulses, S_IRUGO | S_IWUSR, show_fan_pulses,
++			  set_fan_pulses, TC654_FAN1);
++static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input,
++			  NULL, TC654_FAN2);
++static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, show_fan_min,
++			  set_fan_min, TC654_FAN2);
++static SENSOR_DEVICE_ATTR(fan2_max, S_IRUGO, show_fan_max,
++			  NULL, TC654_FAN2);
++static SENSOR_DEVICE_ATTR(fan2_min_alarm, S_IRUGO, show_fan_min_alarm,
++			  NULL, TC654_FAN2);
++static SENSOR_DEVICE_ATTR(fan2_max_alarm, S_IRUGO, show_fan_max_alarm,
++			  NULL, TC654_FAN2);
++static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault,
++			  NULL, TC654_FAN2);
++static SENSOR_DEVICE_ATTR(fan2_pulses, S_IRUGO | S_IWUSR, show_fan_pulses,
++			  set_fan_pulses, TC654_FAN2);
++
++static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
++	set_pwm_enable);
++static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
++
++static DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_temp_alarm_otf, NULL);
++
++/* sensors present on all models */
++static struct attribute *tc654_attrs[] = {
++	&sensor_dev_attr_fan1_input.dev_attr.attr,
++	&sensor_dev_attr_fan1_min.dev_attr.attr,
++	&sensor_dev_attr_fan1_max.dev_attr.attr,
++	&sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
++	&sensor_dev_attr_fan1_max_alarm.dev_attr.attr,
++	&sensor_dev_attr_fan1_fault.dev_attr.attr,
++	&sensor_dev_attr_fan1_pulses.dev_attr.attr,
++	&sensor_dev_attr_fan2_input.dev_attr.attr,
++	&sensor_dev_attr_fan2_min.dev_attr.attr,
++	&sensor_dev_attr_fan2_max.dev_attr.attr,
++	&sensor_dev_attr_fan2_min_alarm.dev_attr.attr,
++	&sensor_dev_attr_fan2_max_alarm.dev_attr.attr,
++	&sensor_dev_attr_fan2_fault.dev_attr.attr,
++	&sensor_dev_attr_fan2_pulses.dev_attr.attr,
++
++	&dev_attr_pwm1_enable.attr,
++	&dev_attr_pwm1.attr,
++
++	&dev_attr_temp1_emergency_alarm.attr,
++	NULL
++};
++
++ATTRIBUTE_GROUPS(tc654);
++
++/* cooling device */
++
++static int tc654_get_max_state(struct thermal_cooling_device *cdev,
++			       unsigned long *state)
++{
++	*state = 255;
++	return 0;
++}
++
++static int tc654_get_cur_state(struct thermal_cooling_device *cdev,
++			       unsigned long *state)
++{
++	struct tc654 *tc = cdev->devdata;
++	int ret;
++
++	if (!tc)
++		return -EINVAL;
++
++	ret = get_fan_speed(tc);
++	if (ret < 0)
++		return ret;
++
++	*state = ret;
++	return 0;
++}
++
++static int tc654_set_cur_state(struct thermal_cooling_device *cdev,
++			       unsigned long state)
++{
++	struct tc654 *tc = cdev->devdata;
++
++	if (!tc)
++		return -EINVAL;
++
++	if (state > INT_MAX)
++		return -EINVAL;
++
++	return set_fan_speed(tc, state);
++}
++
++static const struct thermal_cooling_device_ops tc654_fan_cool_ops = {
++	.get_max_state = tc654_get_max_state,
++	.get_cur_state = tc654_get_cur_state,
++	.set_cur_state = tc654_set_cur_state,
++};
++
++
++/* hardware probe and detection */
++
++static int
++tc654_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++	struct tc654 *tc;
++	struct device *hwmon_dev;
++	int ret, i;
++
++	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
++		return -EIO;
++
++	tc = devm_kzalloc(&client->dev, sizeof(*tc), GFP_KERNEL);
++	if (!tc)
++		return -ENOMEM;
++
++	i2c_set_clientdata(client, tc);
++	tc->client = client;
++	mutex_init(&tc->update_lock);
++
++	/* cache all 8 registers */
++	for (i = 0; i < __TC654_REG_NUM; i++) {
++		ret = read_tc(tc, i);
++		if (ret < 0)
++			return ret;
++	}
++
++	/* sysfs hooks */
++	hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
++							   client->name, tc,
++							   tc654_groups);
++	if (IS_ERR(hwmon_dev))
++		return PTR_ERR(hwmon_dev);
++
++#if IS_ENABLED(CONFIG_OF)
++	/* Optional cooling device register for Device tree platforms */
++	tc->cdev = thermal_of_cooling_device_register(client->dev.of_node,
++						      "tc654", tc,
++						      &tc654_fan_cool_ops);
++#else /* CONFIG_OF */
++	/* Optional cooling device register for non Device tree platforms */
++	tc->cdev = thermal_cooling_device_register("tc654", tc,
++						   &tc654_fan_cool_ops);
++#endif /* CONFIG_OF */
++
++	dev_info(&client->dev, "%s: sensor '%s'\n",
++		 dev_name(hwmon_dev), client->name);
++
++	return 0;
++}
++
++static const struct i2c_device_id tc654_ids[] = {
++	{ "tc654", 0, },
++	{ }
++};
++MODULE_DEVICE_TABLE(i2c, tc654_ids);
++
++/* Return 0 if detection is successful, -ENODEV otherwise */
++static int
++tc654_detect(struct i2c_client *new_client, struct i2c_board_info *info)
++{
++	struct i2c_adapter *adapter = new_client->adapter;
++	int manufacturer, product;
++
++	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
++		return -ENODEV;
++
++	manufacturer = i2c_smbus_read_byte_data(new_client, TC654_REG_MFR_ID);
++	if (manufacturer != TC654_MFR_ID_MICROCHIP)
++		return -ENODEV;
++
++	product = i2c_smbus_read_byte_data(new_client, TC654_REG_VER_ID);
++	if (!((product == TC654_VER_ID) || (product == TC655_VER_ID)))
++		return -ENODEV;
++
++	strlcpy(info->type, "tc654", I2C_NAME_SIZE);
++	return 0;
++}
++
++static struct i2c_driver tc654_driver = {
++	.class		= I2C_CLASS_HWMON,
++	.driver = {
++		.name	= "tc654",
++	},
++	.probe		= tc654_probe,
++	.id_table	= tc654_ids,
++	.detect		= tc654_detect,
++	.address_list	= normal_i2c,
++};
++
++module_i2c_driver(tc654_driver);
++
++MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
++MODULE_DESCRIPTION("Microchip TC654/TC655 hwmon driver");
++MODULE_LICENSE("GPL");