[source] mvebu: add linux 4.9 support

LEDE Commits lede-commits at lists.infradead.org
Thu Feb 16 08:18:15 PST 2017


nbd pushed a commit to source.git, branch master:
https://git.lede-project.org/2bf9ea6a31736db059aa97addce3a34b933dacc1

commit 2bf9ea6a31736db059aa97addce3a34b933dacc1
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Mon Feb 6 11:46:48 2017 +0100

    mvebu: add linux 4.9 support
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
    Signed-off-by: Jonas Gorski <jonas.gorski at gmail.com>
---
 target/linux/mvebu/Makefile                        |    2 +-
 .../mvebu/base-files/lib/preinit/06_set_iface_mac  |   20 +
 target/linux/mvebu/config-4.9                      |  454 ++++++
 .../mvebu/patches-4.9/002-add_powertables.patch    |  748 ++++++++++
 .../mvebu/patches-4.9/003-add_switch_nodes.patch   |   40 +
 .../mvebu/patches-4.9/010-build_new_dtbs.patch     |   11 +
 .../mvebu/patches-4.9/100-find_active_root.patch   |   60 +
 .../mvebu/patches-4.9/102-revert_i2c_delay.patch   |   15 +
 .../patches-4.9/103-remove-nand-driver-bug.patch   |   13 +
 .../104-linksys_mamba_disable_keep_config.patch    |   10 +
 .../106-enable-bm-on-linksys-devices.patch         |  107 ++
 .../110-pxa3xxx_revert_irq_thread.patch            |   69 +
 .../120-net-mvneta-add-BQL-support.patch           |   83 ++
 .../200-gpio_mvebu_add_limited_pwm_support.patch   |  433 ++++++
 ..._extend_mvebu_gpio_documentation_with_pwm.patch |   52 +
 ...mvebu_xp_add_pwm_properties_to_dtsi_files.patch |  149 ++
 .../203-arm_mvebu_enable_pwm_in_defconfig.patch    |   18 +
 ...rt1900ac_use_pwm-fan_rather_than_gpio-fan.patch |   28 +
 .../205-armada-385-rd-mtd-partitions.patch         |   19 +
 .../206-ARM-mvebu-385-ap-Add-partitions.patch      |   35 +
 .../patches-4.9/210-clearfog_switch_node.patch     |   21 +
 ...armada388-clearfog-add-SFP-module-support.patch |   84 ++
 .../300-mvneta-tx-queue-workaround.patch           |   36 +
 ...ovide-a-hook-for-link-up-link-down-events.patch |  177 +++
 ...-phy-move-phy-MMD-accessors-to-phy-core.c.patch |  292 ++++
 ...e-phy_-read-write-_mmd-generic-MMD-access.patch |   97 ++
 ...oid-setting-unsupported-EEE-advertisments.patch |   52 +
 ...tart-phy-autonegotiation-after-EEE-advert.patch |   50 +
 ...-phy-allow-EEE-with-SGMII-interface-modes.patch |   21 +
 ...rove-phylib-correctness-for-non-autoneg-s.patch |  199 +++
 ...phy-add-802.3-clause-45-support-to-phylib.patch |  323 +++++
 ...hook-up-clause-45-autonegotiation-restart.patch |   54 +
 ...n-t-double-read-clause-45-status-register.patch |   35 +
 ...ow-settings-table-to-support-more-than-32.patch |  142 ++
 ...it-out-PHY-speed-and-duplex-string-genera.patch |  103 ++
 ...e-phy_lookup_setting-and-guts-of-phy_supp.patch |  329 +++++
 ...-phy-export-phy_start_machine-for-phylink.patch |   22 +
 .../patches-4.9/414-phy-add-I2C-mdio-bus.patch     |  180 +++
 .../415-phylink-add-phylink-infrastructure.patch   | 1120 +++++++++++++++
 .../416-phylink-add-hooks-for-SFP-support.patch    |  177 +++
 ...-sfp-add-phylink-based-SFP-module-support.patch | 1477 ++++++++++++++++++++
 .../418-sfp-display-SFP-module-information.patch   |  280 ++++
 .../419-net-mvneta-convert-to-phylink.patch        |  743 ++++++++++
 ...disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch |   58 +
 ...21-phylink-add-ethtool-nway_reset-support.patch |   44 +
 .../422-net-mvneta-add-nway_reset-support.patch    |   35 +
 .../423-phylink-add-flow-control-support.patch     |  232 +++
 ...neta-add-flow-control-support-via-phylink.patch |   76 +
 ...a-enable-flow-control-for-PHY-connections.patch |   28 +
 ...enable-flow-control-for-fixed-connections.patch |   32 +
 .../patches-4.9/427-phylink-add-EEE-support.patch  |  116 ++
 .../428-net-mvneta-add-EEE-support.patch           |  179 +++
 .../429-phylink-add-module-EEPROM-support.patch    |  123 ++
 ...-mvneta-add-module-EEPROM-reading-support.patch |   41 +
 .../431-sfp-phylink-hook-up-eeprom-functions.patch |   65 +
 ...-marvell-88E1512-add-flow-control-support.patch |   24 +
 ...-marvell-88E1111-add-flow-control-support.patch |   22 +
 ...-marvell-88E1540-add-flow-control-support.patch |   22 +
 ...35-net-phy-fix-marvell-phy-status-reading.patch |   26 +
 ...ropagate-PHY-interface-mode-to-MAC-driver.patch |  126 ++
 ...37-phylink-ensure-link-drops-are-reported.patch |   52 +
 .../mvebu/patches-4.9/450-reprobe_sfp_phy.patch    |  119 ++
 62 files changed, 9799 insertions(+), 1 deletion(-)

diff --git a/target/linux/mvebu/Makefile b/target/linux/mvebu/Makefile
index e319a8f..a07aa7c 100644
--- a/target/linux/mvebu/Makefile
+++ b/target/linux/mvebu/Makefile
@@ -14,7 +14,7 @@ CPU_TYPE:=cortex-a9
 CPU_SUBTYPE:=vfpv3
 MAINTAINER:=Imre Kaloz <kaloz at openwrt.org>
 
-KERNEL_PATCHVER:=4.4
+KERNEL_PATCHVER:=4.9
 
 include $(INCLUDE_DIR)/target.mk
 
diff --git a/target/linux/mvebu/base-files/lib/preinit/06_set_iface_mac b/target/linux/mvebu/base-files/lib/preinit/06_set_iface_mac
index 3e870e8..ae34ef4 100644
--- a/target/linux/mvebu/base-files/lib/preinit/06_set_iface_mac
+++ b/target/linux/mvebu/base-files/lib/preinit/06_set_iface_mac
@@ -16,11 +16,31 @@ preinit_set_mac_address() {
 		ifconfig eth1 hw ether $mac 2>/dev/null
 		;;
 	armada-385-linksys-caiman|armada-385-linksys-cobra|armada-385-linksys-rango|armada-385-linksys-shelby)
+		# rename interfaces back to the way they were with 4.4
+		case "$(readlink /sys/class/net/eth0)" in
+			*f1070000*)
+				ip link set eth0 name tmp0
+				ip link set eth1 name eth0
+				ip link set tmp0 name eth1
+			;;
+		esac
+
 		mac=$(mtd_get_mac_ascii devinfo hw_mac_addr)
 		mac_wan=$(macaddr_setbit_la "$mac")
 		ifconfig eth1 hw ether $mac 2>/dev/null
 		ifconfig eth0 hw ether $mac_wan 2>/dev/null
 		;;
+	armada-385-db-ap|armada-388-clearfog)
+		# rename interfaces back to the way they were with 4.4
+		case "$(readlink /sys/class/net/eth0)" in
+			*f1070000*)
+				ip link set eth0 name tmp0
+				ip link set eth1 name eth0
+				ip link set eth2 name eth1
+				ip link set tmp0 name eth2
+			;;
+		esac
+		;;
 	esac
 }
 
diff --git a/target/linux/mvebu/config-4.9 b/target/linux/mvebu/config-4.9
new file mode 100644
index 0000000..6b0369d
--- /dev/null
+++ b/target/linux/mvebu/config-4.9
@@ -0,0 +1,454 @@
+CONFIG_AHCI_MVEBU=y
+CONFIG_ALIGNMENT_TRAP=y
+CONFIG_ARCH_CLOCKSOURCE_DATA=y
+CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
+CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
+CONFIG_ARCH_HAS_SG_CHAIN=y
+CONFIG_ARCH_HAS_TICK_BROADCAST=y
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+CONFIG_ARCH_MULTI_V6_V7=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MVEBU=y
+CONFIG_ARCH_NR_GPIO=0
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
+CONFIG_ARCH_SUPPORTS_BIG_ENDIAN=y
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_ARM=y
+CONFIG_ARMADA_370_CLK=y
+CONFIG_ARMADA_370_XP_IRQ=y
+CONFIG_ARMADA_370_XP_TIMER=y
+CONFIG_ARMADA_38X_CLK=y
+CONFIG_ARMADA_THERMAL=y
+CONFIG_ARMADA_XP_CLK=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_ARM_ATAG_DTB_COMPAT=y
+# CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_EXTEND is not set
+CONFIG_ARM_ATAG_DTB_COMPAT_CMDLINE_FROM_BOOTLOADER=y
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_ARM_CRYPTO=y
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_ARM_GIC=y
+CONFIG_ARM_HAS_SG_CHAIN=y
+CONFIG_ARM_HEAVY_MB=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+# CONFIG_ARM_LPAE is not set
+CONFIG_ARM_MVEBU_V7_CPUIDLE=y
+CONFIG_ARM_PATCH_IDIV=y
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_ARM_VIRT_EXT=y
+CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH=y
+CONFIG_ATA=y
+CONFIG_ATAGS=y
+CONFIG_AUTO_ZRELADDR=y
+# CONFIG_BINFMT_FLAT is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_MQ_PCI=y
+CONFIG_BOUNCE=y
+# CONFIG_CACHE_FEROCEON_L2 is not set
+CONFIG_CACHE_L2X0=y
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_CLKSRC_MMIO=y
+CONFIG_CLKSRC_OF=y
+CONFIG_CLKSRC_PROBE=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_COMMON_CLK=y
+CONFIG_CPUFREQ_DT=y
+CONFIG_CPUFREQ_DT_PLATDEV=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_GOV_ATTR_SET=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_HAS_ASID=y
+# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_PJ4B=y
+CONFIG_CPU_PM=y
+CONFIG_CPU_RMAP=y
+CONFIG_CPU_THERMAL=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_V7=y
+CONFIG_CRC16=y
+CONFIG_CRYPTO_ABLK_HELPER=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_AES_ARM=y
+CONFIG_CRYPTO_AES_ARM_BS=y
+# CONFIG_CRYPTO_AES_ARM_CE is not set
+CONFIG_CRYPTO_CRC32=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CRYPTD=y
+CONFIG_CRYPTO_DEFLATE=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_DEV_MARVELL_CESA=y
+# CONFIG_CRYPTO_GHASH_ARM_CE is not set
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_LZO=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+CONFIG_CRYPTO_NULL2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA1_ARM=y
+# CONFIG_CRYPTO_SHA1_ARM_CE is not set
+CONFIG_CRYPTO_SHA1_ARM_NEON=y
+CONFIG_CRYPTO_SHA256_ARM=y
+# CONFIG_CRYPTO_SHA2_ARM_CE is not set
+CONFIG_CRYPTO_SHA512_ARM=y
+CONFIG_CRYPTO_WORKQUEUE=y
+CONFIG_DCACHE_WORD_ACCESS=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_LL_INCLUDE="debug/8250.S"
+CONFIG_DEBUG_MVEBU_UART0=y
+# CONFIG_DEBUG_MVEBU_UART0_ALTERNATE is not set
+# CONFIG_DEBUG_MVEBU_UART1_ALTERNATE is not set
+CONFIG_DEBUG_UART_8250=y
+# CONFIG_DEBUG_UART_8250_FLOW_CONTROL is not set
+CONFIG_DEBUG_UART_8250_SHIFT=2
+# CONFIG_DEBUG_UART_8250_WORD is not set
+CONFIG_DEBUG_UART_PHYS=0xd0012000
+CONFIG_DEBUG_UART_VIRT=0xfec12000
+CONFIG_DEBUG_UNCOMPRESS=y
+CONFIG_DEBUG_USER=y
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_ENGINE_RAID=y
+CONFIG_DMA_OF=y
+CONFIG_DTC=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_EDAC_ATOMIC_SCRUB=y
+CONFIG_EDAC_SUPPORT=y
+CONFIG_EXT4_FS=y
+# CONFIG_F2FS_CHECK_FS is not set
+CONFIG_F2FS_FS=y
+# CONFIG_F2FS_FS_SECURITY is not set
+CONFIG_F2FS_FS_XATTR=y
+CONFIG_F2FS_STAT_FS=y
+CONFIG_FIXED_PHY=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_FS_MBCACHE=y
+CONFIG_GENERIC_ALLOCATOR=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_EARLY_IOREMAP=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_GENERIC_IO=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
+CONFIG_GENERIC_MSI_IRQ=y
+CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_PHY=y
+CONFIG_GENERIC_SCHED_CLOCK=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GLOB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_MVEBU=y
+CONFIG_GPIO_MVEBU_PWM=y
+CONFIG_GPIO_PCA953X=y
+# CONFIG_GPIO_PCA953X_IRQ is not set
+CONFIG_GPIO_SYSFS=y
+CONFIG_HANDLE_DOMAIN_IRQ=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_HAS_DMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT_MAP=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_ARCH_AUDITSYSCALL=y
+CONFIG_HAVE_ARCH_BITREVERSE=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_HAVE_ARCH_PFN_VALID=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_ARM_SCU=y
+CONFIG_HAVE_ARM_SMCCC=y
+CONFIG_HAVE_ARM_TWD=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_HAVE_CBPF_JIT=y
+CONFIG_HAVE_CC_STACKPROTECTOR=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_DMA_CONTIGUOUS=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=y
+CONFIG_HAVE_IDE=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
+CONFIG_HAVE_NET_DSA=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_HAVE_OPTPROBES=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_SMP=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_UID16=y
+CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHPTE is not set
+CONFIG_HOTPLUG_CPU=y
+CONFIG_HWBM=y
+CONFIG_HWMON=y
+CONFIG_HZ_FIXED=0
+CONFIG_HZ_PERIODIC=y
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MV64XXX=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_IOMMU_HELPER=y
+CONFIG_IRQCHIP=y
+CONFIG_IRQ_DOMAIN=y
+CONFIG_IRQ_DOMAIN_DEBUG=y
+CONFIG_IRQ_DOMAIN_HIERARCHY=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_IRQ_WORK=y
+# CONFIG_IWMMXT is not set
+CONFIG_JBD2=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_PCA963X=y
+CONFIG_LEDS_TLC591XX=y
+CONFIG_LIBFDT=y
+CONFIG_LOCK_SPIN_ON_OWNER=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_MACH_ARMADA_370=y
+# CONFIG_MACH_ARMADA_375 is not set
+CONFIG_MACH_ARMADA_38X=y
+# CONFIG_MACH_ARMADA_39X is not set
+CONFIG_MACH_ARMADA_XP=y
+# CONFIG_MACH_DOVE is not set
+CONFIG_MACH_MVEBU_ANY=y
+CONFIG_MACH_MVEBU_V7=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MANGLE_BOOTARGS=y
+CONFIG_MARVELL_PHY=y
+CONFIG_MDIO_BOARDINFO=y
+CONFIG_MDIO_I2C=y
+CONFIG_MEMORY=y
+# CONFIG_MFD_MAX77620 is not set
+CONFIG_MIGHT_HAVE_CACHE_L2X0=y
+CONFIG_MIGHT_HAVE_PCI=y
+CONFIG_MMC=y
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_MVSDIO=y
+CONFIG_MMC_SDHCI=y
+# CONFIG_MMC_SDHCI_PCI is not set
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_PXAV3=y
+# CONFIG_MMC_TIFM_SD is not set
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_ECC=y
+CONFIG_MTD_NAND_PXA3xx=y
+CONFIG_MTD_SPI_NOR=y
+CONFIG_MTD_SPLIT_FIRMWARE=y
+CONFIG_MTD_UBI=y
+CONFIG_MTD_UBI_BEB_LIMIT=20
+CONFIG_MTD_UBI_BLOCK=y
+# CONFIG_MTD_UBI_FASTMAP is not set
+# CONFIG_MTD_UBI_GLUEBI is not set
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MULTI_IRQ_HANDLER=y
+CONFIG_MUTEX_SPIN_ON_OWNER=y
+CONFIG_MVEBU_CLK_COMMON=y
+CONFIG_MVEBU_CLK_COREDIV=y
+CONFIG_MVEBU_CLK_CPU=y
+CONFIG_MVEBU_DEVBUS=y
+CONFIG_MVEBU_MBUS=y
+CONFIG_MVMDIO=y
+CONFIG_MVNETA=y
+CONFIG_MVNETA_BM=y
+CONFIG_MVNETA_BM_ENABLE=y
+CONFIG_MVSW61XX_PHY=y
+CONFIG_MV_XOR=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEON=y
+CONFIG_NET_FLOW_LIMIT=y
+CONFIG_NLS=y
+CONFIG_NOP_USB_XCEIV=y
+CONFIG_NO_BOOTMEM=y
+CONFIG_NR_CPUS=4
+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_SIGSUSPEND3=y
+CONFIG_ORION_WATCHDOG=y
+CONFIG_OUTER_CACHE=y
+CONFIG_OUTER_CACHE_SYNC=y
+CONFIG_PADATA=y
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PCI=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCI_DOMAINS_GENERIC=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_MSI_IRQ_DOMAIN=y
+CONFIG_PCI_MVEBU=y
+CONFIG_PERF_USE_VMALLOC=y
+CONFIG_PGTABLE_LEVELS=2
+CONFIG_PHYLIB=y
+CONFIG_PHYLINK=y
+CONFIG_PINCTRL=y
+CONFIG_PINCTRL_ARMADA_370=y
+CONFIG_PINCTRL_ARMADA_38X=y
+CONFIG_PINCTRL_ARMADA_XP=y
+CONFIG_PINCTRL_MVEBU=y
+# CONFIG_PINCTRL_SINGLE is not set
+CONFIG_PJ4B_ERRATA_4742=y
+# CONFIG_PL310_ERRATA_588369 is not set
+# CONFIG_PL310_ERRATA_727915 is not set
+# CONFIG_PL310_ERRATA_753970 is not set
+# CONFIG_PL310_ERRATA_769419 is not set
+CONFIG_PLAT_ORION=y
+CONFIG_PM_OPP=y
+CONFIG_PWM=y
+CONFIG_PWM_SYSFS=y
+CONFIG_RATIONAL=y
+CONFIG_RCU_STALL_COMMON=y
+CONFIG_REGMAP=y
+CONFIG_REGMAP_I2C=y
+CONFIG_REGMAP_SPI=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_RFS_ACCEL=y
+CONFIG_RPS=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_ARMADA38X=y
+CONFIG_RTC_DRV_MV=y
+CONFIG_RTC_I2C_AND_SPI=y
+CONFIG_RTC_MC146818_LIB=y
+CONFIG_RWSEM_SPIN_ON_OWNER=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_SATA_MV=y
+CONFIG_SCHED_HRTICK=y
+# CONFIG_SCHED_INFO is not set
+CONFIG_SCSI=y
+CONFIG_SENSORS_PWM_FAN=y
+CONFIG_SENSORS_TMP421=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_8250_FSL=y
+CONFIG_SERIAL_MVEBU_CONSOLE=y
+CONFIG_SERIAL_MVEBU_UART=y
+CONFIG_SFP=y
+CONFIG_SMP=y
+CONFIG_SMP_ON_UP=y
+CONFIG_SOC_BUS=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_SPI=y
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_ORION=y
+CONFIG_SRAM=y
+CONFIG_SRCU=y
+CONFIG_SWCONFIG=y
+CONFIG_SWIOTLB=y
+CONFIG_SWPHY=y
+CONFIG_SWP_EMULATE=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+CONFIG_THERMAL_GOV_STEP_WISE=y
+CONFIG_THERMAL_HWMON=y
+CONFIG_THERMAL_OF=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_TICK_CPU_ACCOUNTING=y
+CONFIG_TIMER_STATS=y
+CONFIG_TREE_RCU=y
+CONFIG_UBIFS_FS=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+CONFIG_UBIFS_FS_LZO=y
+CONFIG_UBIFS_FS_ZLIB=y
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+CONFIG_USB=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_HCD_ORION=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_EHCI_PCI=y
+CONFIG_USB_LEDS_TRIGGER_USBPORT=y
+CONFIG_USB_PHY=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_SUPPORT=y
+# CONFIG_USB_UHCI_HCD is not set
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_XHCI_MVEBU=y
+CONFIG_USB_XHCI_PCI=y
+CONFIG_USB_XHCI_PLATFORM=y
+CONFIG_USE_OF=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_WATCHDOG_CORE=y
+CONFIG_XPS=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_BCJ=y
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
diff --git a/target/linux/mvebu/patches-4.9/002-add_powertables.patch b/target/linux/mvebu/patches-4.9/002-add_powertables.patch
new file mode 100644
index 0000000..a5a47e4
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/002-add_powertables.patch
@@ -0,0 +1,748 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -86,12 +86,100 @@
+ 			pcie at 2,0 {
+ 				/* Port 0, Lane 1 */
+ 				status = "okay";
++
++				mwlwifi {
++					marvell,5ghz = <0>;
++					marvell,chainmask = <4 4>;
++					marvell,powertable {
++						FCC =
++							<1 0 0x17 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0x17 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0x17 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>;
++
++						ETSI =
++							<1 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0xb 0x0 0x0 0x0 0x0 0 0xf>;
++					};
++				};
+ 			};
+ 
+ 			/* Second mini-PCIe port */
+ 			pcie at 3,0 {
+ 				/* Port 0, Lane 3 */
+ 				status = "okay";
++
++				mwlwifi {
++					marvell,2ghz = <0>;
++					marvell,chainmask = <4 4>;
++					marvell,powertable {
++						FCC =
++							<36 0 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<40 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<44 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<48 0 0x8 0x8 0x8 0x8 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<52 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++							<56 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++							<60 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++							<64 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0 0xf>,
++							<100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<149 0 0x16 0x16 0x16 0x16 0x14 0x14 0x14 0x14 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++							<153 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++							<157 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++							<161 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>,
++							<165 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0 0xf>;
++
++						ETSI =
++							<36 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<40 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<44 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<48 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<52 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<56 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<60 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<64 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<100 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<104 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<108 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<112 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<116 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<120 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<124 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<128 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<132 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<136 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<140 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>,
++							<149 0 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xc 0xd 0xd 0xd 0xd 0xc 0xc 0xc 0xc 0 0xf>;
++					};
++				};
+ 			};
+ 		};
+ 
+--- a/arch/arm/boot/dts/armada-385-linksys-cobra.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-cobra.dts
+@@ -100,6 +100,212 @@
+ 				};
+ 			};
+ 		};
++
++		pcie-controller {
++			pcie at 1,0 {
++				mwlwifi {
++					marvell,2ghz = <0>;
++					marvell,chainmask = <4 4>;
++					marvell,powertable {
++						AU =
++							<36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>;
++						CA =
++							<36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++						CN =
++							<36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>;
++						ETSI =
++							<36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>;
++						FCC =
++							<36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>,
++							<40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++							<44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++							<48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++					};
++				};
++			};
++
++			pcie at 2,0 {
++				mwlwifi {
++					marvell,5ghz = <0>;
++					marvell,chainmask = <4 4>;
++					marvell,powertable {
++						AU =
++							<1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++						CA =
++							<1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++						CN =
++							<1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++						ETSI =
++							<1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++						FCC =
++							<1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++					};
++				};
++			};
++		};
+ 	};
+ 
+ 	gpio-leds {
+--- a/arch/arm/boot/dts/armada-385-linksys-caiman.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-caiman.dts
+@@ -100,6 +100,212 @@
+ 				};
+ 			};
+ 		};
++
++		pcie-controller {
++			pcie at 1,0 {
++				mwlwifi {
++					marvell,2ghz = <0>;
++					marvell,chainmask = <2 2>;
++					marvell,powertable {
++						AU =
++							<36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<100 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<104 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<108 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<112 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<116 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<120 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<124 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<128 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<132 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<136 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<140 0 0x17 0x17 0x17 0x17 0x17 0x17 0x17 0x15 0x17 0x17 0x17 0x14 0x17 0x17 0x17 0x14 0 0xf>,
++							<149 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++							<153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++							<157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++							<161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>,
++							<165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x1a 0x1a 0x17 0x14 0 0xf>;
++						CA =
++							<36 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++							<40 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++							<44 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++							<48 0 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0xf 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>;
++						CN =
++							<36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<149 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++							<153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++							<157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++							<161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>,
++							<165 0 0x15 0x15 0x15 0x15 0x16 0x16 0x16 0x15 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0 0xf>;
++						ETSI =
++							<36 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<40 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<44 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<48 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<100 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>,
++							<149 0 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0x11 0 0xf>;
++						FCC =
++							<36 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<40 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<44 0 0x19 0x19 0x18 0x17 0x19 0x19 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<48 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x17 0x17 0x17 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<149 0 0x1a 0x1a 0x18 0x17 0x19 0x19 0x17 0x15 0x18 0x18 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<153 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<157 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<161 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>,
++							<165 0 0x1a 0x1a 0x18 0x17 0x1a 0x1a 0x17 0x15 0x1a 0x1a 0x17 0x14 0x15 0x15 0x15 0x14 0 0xf>;
++					};
++				};
++			};
++
++			pcie at 2,0 {
++				mwlwifi {
++					marvell,5ghz = <0>;
++					marvell,chainmask = <2 2>;
++					marvell,powertable {
++						AU =
++							<1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++						CA =
++							<1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x00 0x00 0x00 0x00 0 0xf>,
++							<2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x00 0x00 0x00 0x00 0 0xf>,
++							<11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x00 0x00 0x00 0x00 0 0xf>;
++						CN =
++							<1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<14 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++						ETSI =
++							<1 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0x0 0x0 0x0 0x0 0 0xf>;
++						FCC =
++							<1 0 0x19 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x10 0x10 0x10 0x10 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0x1a 0x19 0x18 0x17 0x19 0x19 0x17 0x16 0x14 0x14 0x14 0x14 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0x19 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x0 0x0 0x0 0x0 0 0xf>;
++					};
++				};
++			};
++		};
+ 	};
+ 
+ 	gpio-leds {
+--- a/arch/arm/boot/dts/armada-385-linksys-shelby.dts
++++ b/arch/arm/boot/dts/armada-385-linksys-shelby.dts
+@@ -100,6 +100,212 @@
+ 				};
+ 			};
+ 		};
++
++		pcie-controller {
++			pcie at 1,0 {
++				mwlwifi {
++					marvell,2ghz = <0>;
++					marvell,chainmask = <4 4>;
++					marvell,powertable {
++						AU =
++							<36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<100 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<104 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<108 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<112 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<116 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<120 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<124 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<128 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<132 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<136 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<140 0 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0x14 0 0xf>,
++							<149 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<153 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<157 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<161 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>,
++							<165 0 0x19 0x19 0x19 0x17 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0x19 0x19 0x16 0x15 0 0xf>;
++						CA =
++							<36 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<40 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<44 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<48 0 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0x9 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++						CN =
++							<36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<149 0 0x14 0x14 0x14 0x14 0x13 0x13 0x13 0x13 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>,
++							<165 0 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x13 0x14 0x14 0x14 0x14 0x10 0x10 0x10 0x10 0 0xf>;
++						ETSI =
++							<36 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<40 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<44 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<48 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<52 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<56 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<60 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<64 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<100 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<104 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<108 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<112 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<116 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<120 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<124 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<128 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<132 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<136 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<140 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>,
++							<149 0 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xd 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0xe 0 0xf>;
++						FCC =
++							<36 0 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0 0xf>,
++							<40 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++							<44 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++							<48 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0xf 0xf 0xf 0xf 0 0xf>,
++							<52 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<56 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<60 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<64 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<100 0 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<104 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x12 0x12 0x12 0x12 0x10 0x10 0x10 0x10 0 0xf>,
++							<108 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<112 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<116 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<120 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<124 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<128 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<132 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<136 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<140 0 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0x11 0x11 0x11 0x11 0x10 0x10 0x10 0x10 0 0xf>,
++							<149 0 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<153 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<157 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<161 0 0x16 0x16 0x16 0x16 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>,
++							<165 0 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0x15 0 0xf>;
++					};
++				};
++			};
++
++			pcie at 2,0 {
++				mwlwifi {
++					marvell,5ghz = <0>;
++					marvell,chainmask = <4 4>;
++					marvell,powertable {
++						AU =
++							<1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++						CA =
++							<1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++						CN =
++							<1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<14 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++						ETSI =
++							<1 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<12 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>,
++							<13 0 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0xa 0x0 0x0 0x0 0x0 0 0xf>;
++						FCC =
++							<1 0 0x17 0x10 0x10 0x10 0xf 0xf 0xf 0xf 0xe 0xe 0xe 0xe 0x0 0x0 0x0 0x0 0 0xf>,
++							<2 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<3 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<4 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<5 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<6 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<7 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<8 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<9 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<10 0 0x18 0x16 0x16 0x16 0x16 0x16 0x16 0x14 0x11 0x11 0x11 0x11 0x0 0x0 0x0 0x0 0 0xf>,
++							<11 0 0x17 0x12 0x12 0x12 0x13 0x13 0x13 0x13 0xf 0xf 0xf 0xf 0x0 0x0 0x0 0x0 0 0xf>;
++					};
++				};
++			};
++		};
+ 	};
+ 
+ 	gpio-leds {
diff --git a/target/linux/mvebu/patches-4.9/003-add_switch_nodes.patch b/target/linux/mvebu/patches-4.9/003-add_switch_nodes.patch
new file mode 100644
index 0000000..38a48d3
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/003-add_switch_nodes.patch
@@ -0,0 +1,40 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -455,6 +455,16 @@
+ 			};
+ 		};
+ 	};
++
++	mvsw61xx {
++		compatible = "marvell,88e6172";
++		status = "okay";
++		reg = <0x10>;
++
++		mii-bus = <&mdio>;
++		cpu-port-0 = <5>;
++		cpu-port-1 = <6>;
++	};
+ };
+ 
+ &pinctrl {
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -304,6 +304,18 @@
+ 			};
+ 		};
+ 	};
++
++	mvsw61xx {
++		#address-cells = <1>;
++		#size-cells = <0>;
++		compatible = "marvell,88e6176";
++		status = "okay";
++		reg = <0x10>;
++
++		mii-bus = <&mdio>;
++		cpu-port-0 = <5>;
++		cpu-port-1 = <6>;
++	};
+ };
+ 
+ &pinctrl {
diff --git a/target/linux/mvebu/patches-4.9/010-build_new_dtbs.patch b/target/linux/mvebu/patches-4.9/010-build_new_dtbs.patch
new file mode 100644
index 0000000..1eb647d
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/010-build_new_dtbs.patch
@@ -0,0 +1,11 @@
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -921,6 +921,8 @@ dtb-$(CONFIG_MACH_ARMADA_38X) += \
+ 	armada-385-db-ap.dtb \
+ 	armada-385-linksys-caiman.dtb \
+ 	armada-385-linksys-cobra.dtb \
++	armada-385-linksys-rango.dtb \
++	armada-385-linksys-shelby.dtb \
+ 	armada-388-clearfog.dtb \
+ 	armada-388-db.dtb \
+ 	armada-388-gp.dtb \
diff --git a/target/linux/mvebu/patches-4.9/100-find_active_root.patch b/target/linux/mvebu/patches-4.9/100-find_active_root.patch
new file mode 100644
index 0000000..b8e8c92
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/100-find_active_root.patch
@@ -0,0 +1,60 @@
+The WRT1900AC among other Linksys routers uses a dual-firmware layout.
+Dynamically rename the active partition to "ubi".
+
+Signed-off-by: Imre Kaloz <kaloz at openwrt.org>
+
+--- a/drivers/mtd/ofpart.c
++++ b/drivers/mtd/ofpart.c
+@@ -25,6 +25,8 @@ static bool node_has_compatible(struct d
+ 	return of_get_property(pp, "compatible", NULL);
+ }
+ 
++static int mangled_rootblock;
++
+ static int parse_ofpart_partitions(struct mtd_info *master,
+ 				   const struct mtd_partition **pparts,
+ 				   struct mtd_part_parser_data *data)
+@@ -33,6 +35,7 @@ static int parse_ofpart_partitions(struc
+ 	struct device_node *mtd_node;
+ 	struct device_node *ofpart_node;
+ 	const char *partname;
++	const char *owrtpart = "ubi";
+ 	struct device_node *pp;
+ 	int nr_parts, i, ret = 0;
+ 	bool dedicated = true;
+@@ -109,9 +112,13 @@ static int parse_ofpart_partitions(struc
+ 		parts[i].offset = of_read_number(reg, a_cells);
+ 		parts[i].size = of_read_number(reg + a_cells, s_cells);
+ 
+-		partname = of_get_property(pp, "label", &len);
+-		if (!partname)
+-			partname = of_get_property(pp, "name", &len);
++		if (mangled_rootblock && (i == mangled_rootblock)) {
++			partname = owrtpart;
++		} else {
++			partname = of_get_property(pp, "label", &len);
++			if (!partname)
++				partname = of_get_property(pp, "name", &len);
++		}
+ 		parts[i].name = partname;
+ 
+ 		if (of_get_property(pp, "read-only", &len))
+@@ -212,6 +219,18 @@ static int __init ofpart_parser_init(voi
+ 	return 0;
+ }
+ 
++static int __init active_root(char *str)
++{
++	get_option(&str, &mangled_rootblock);
++
++	if (!mangled_rootblock)
++		return 1;
++
++	return 1;
++}
++
++__setup("mangled_rootblock=", active_root);
++
+ static void __exit ofpart_parser_exit(void)
+ {
+ 	deregister_mtd_parser(&ofpart_parser);
diff --git a/target/linux/mvebu/patches-4.9/102-revert_i2c_delay.patch b/target/linux/mvebu/patches-4.9/102-revert_i2c_delay.patch
new file mode 100644
index 0000000..473bdd9
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/102-revert_i2c_delay.patch
@@ -0,0 +1,15 @@
+--- a/arch/arm/boot/dts/armada-xp.dtsi
++++ b/arch/arm/boot/dts/armada-xp.dtsi
+@@ -85,12 +85,10 @@
+ 			};
+ 
+ 			i2c0: i2c at 11000 {
+-				compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ 				reg = <0x11000 0x100>;
+ 			};
+ 
+ 			i2c1: i2c at 11100 {
+-				compatible = "marvell,mv78230-i2c", "marvell,mv64xxx-i2c";
+ 				reg = <0x11100 0x100>;
+ 			};
+ 
diff --git a/target/linux/mvebu/patches-4.9/103-remove-nand-driver-bug.patch b/target/linux/mvebu/patches-4.9/103-remove-nand-driver-bug.patch
new file mode 100644
index 0000000..19a2a1e
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/103-remove-nand-driver-bug.patch
@@ -0,0 +1,13 @@
+Remove a BUG() call that would crash on a race condition that should
+otherwise be harmless.
+
+--- a/drivers/mtd/nand/pxa3xx_nand.c
++++ b/drivers/mtd/nand/pxa3xx_nand.c
+@@ -727,7 +727,6 @@ static void handle_data_pio(struct pxa3x
+ 	default:
+ 		dev_err(&info->pdev->dev, "%s: invalid state %d\n", __func__,
+ 				info->state);
+-		BUG();
+ 	}
+ 
+ 	/* Update buffer pointers for multi-page read/write */
diff --git a/target/linux/mvebu/patches-4.9/104-linksys_mamba_disable_keep_config.patch b/target/linux/mvebu/patches-4.9/104-linksys_mamba_disable_keep_config.patch
new file mode 100644
index 0000000..4c6b311
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/104-linksys_mamba_disable_keep_config.patch
@@ -0,0 +1,10 @@
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -298,7 +298,6 @@
+ 			nand at d0000 {
+ 				status = "okay";
+ 				num-cs = <1>;
+-				marvell,nand-keep-config;
+ 				marvell,nand-enable-arbiter;
+ 				nand-on-flash-bbt;
+ 				nand-ecc-strength = <4>;
diff --git a/target/linux/mvebu/patches-4.9/106-enable-bm-on-linksys-devices.patch b/target/linux/mvebu/patches-4.9/106-enable-bm-on-linksys-devices.patch
new file mode 100644
index 0000000..39e7138
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/106-enable-bm-on-linksys-devices.patch
@@ -0,0 +1,107 @@
+--- a/arch/arm/boot/dts/armada-385-linksys.dtsi
++++ b/arch/arm/boot/dts/armada-385-linksys.dtsi
+@@ -59,7 +59,8 @@
+ 		ranges = <MBUS_ID(0xf0, 0x01) 0 0xf1000000 0x100000
+ 			  MBUS_ID(0x01, 0x1d) 0 0xfff00000 0x100000
+ 			  MBUS_ID(0x09, 0x19) 0 0xf1100000 0x10000
+-			  MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000>;
++			  MBUS_ID(0x09, 0x15) 0 0xf1110000 0x10000
++			  MBUS_ID(0x0c, 0x04) 0 0xf1200000 0x100000>;
+ 
+ 		internal-regs {
+ 			i2c at 11000 {
+@@ -88,6 +89,9 @@
+ 			ethernet at 70000 {
+ 				status = "okay";
+ 				phy-mode = "rgmii-id";
++				buffer-manager = <&bm>;
++				bm,pool-long = <0>;
++				bm,pool-short = <3>;
+ 				fixed-link {
+ 					speed = <1000>;
+ 					full-duplex;
+@@ -97,6 +101,9 @@
+ 			ethernet at 34000 {
+ 				status = "okay";
+ 				phy-mode = "sgmii";
++				buffer-manager = <&bm>;
++				bm,pool-long = <2>;
++				bm,pool-short = <3>;
+ 				fixed-link {
+ 					speed = <1000>;
+ 					full-duplex;
+@@ -107,6 +114,10 @@
+ 				status = "okay";
+ 			};
+ 
++			bm at c8000 {
++				status = "okay";
++			};
++
+ 			sata at a8000 {
+ 				status = "okay";
+ 			};
+@@ -193,6 +204,10 @@
+ 			};
+ 		};
+ 
++		bm-bppi {
++			status = "okay";
++		};
++
+ 		pcie-controller {
+ 			status = "okay";
+ 
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -71,7 +71,8 @@
+ 		ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xf1000000 0x100000
+ 			  MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
+ 			  MBUS_ID(0x09, 0x09) 0 0 0xf1100000 0x10000
+-			  MBUS_ID(0x09, 0x05) 0 0 0xf1110000 0x10000>;
++			  MBUS_ID(0x09, 0x05) 0 0 0xf1110000 0x10000
++			  MBUS_ID(0x0c, 0x04) 0 0 0xf1200000 0x100000>;
+ 
+ 		pcie-controller {
+ 			status = "okay";
+@@ -205,6 +206,9 @@
+ 				pinctrl-names = "default";
+ 				status = "okay";
+ 				phy-mode = "rgmii-id";
++				buffer-manager = <&bm>;
++				bm,pool-long = <0>;
++				bm,pool-short = <3>;
+ 				fixed-link {
+ 					speed = <1000>;
+ 					full-duplex;
+@@ -216,12 +220,19 @@
+ 				pinctrl-names = "default";
+ 				status = "okay";
+ 				phy-mode = "rgmii-id";
++				buffer-manager = <&bm>;
++				bm,pool-long = <1>;
++				bm,pool-short = <3>;
+ 				fixed-link {
+ 					speed = <1000>;
+ 					full-duplex;
+ 				};
+ 			};
+ 
++			bm at c0000 {
++				status = "okay";
++			};
++
+ 			/* USB part of the eSATA/USB 2.0 port */
+ 			usb at 50000 {
+ 				status = "okay";
+@@ -367,6 +378,10 @@
+ 				};
+ 			};
+ 		};
++
++		bm-bppi {
++			status = "okay";
++		};
+ 	};
+ 
+ 	gpio_keys {
diff --git a/target/linux/mvebu/patches-4.9/110-pxa3xxx_revert_irq_thread.patch b/target/linux/mvebu/patches-4.9/110-pxa3xxx_revert_irq_thread.patch
new file mode 100644
index 0000000..419306e
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/110-pxa3xxx_revert_irq_thread.patch
@@ -0,0 +1,69 @@
+Revert "mtd: pxa3xx-nand: handle PIO in threaded interrupt"
+
+This reverts commit 24542257a3b987025d4b998ec2d15e556c98ad3f
+This upstream change has been causing spurious timeouts on accesses
+to the NAND flash if something else on the system is causing
+significant latency.
+
+Nothing guarantees that the thread will run in time, so the
+usual timeout is unreliable.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+
+--- a/drivers/mtd/nand/pxa3xx_nand.c
++++ b/drivers/mtd/nand/pxa3xx_nand.c
+@@ -791,24 +791,11 @@ static void start_data_dma(struct pxa3xx
+ 		__func__, direction, info->dma_cookie, info->sg.length);
+ }
+ 
+-static irqreturn_t pxa3xx_nand_irq_thread(int irq, void *data)
+-{
+-	struct pxa3xx_nand_info *info = data;
+-
+-	handle_data_pio(info);
+-
+-	info->state = STATE_CMD_DONE;
+-	nand_writel(info, NDSR, NDSR_WRDREQ | NDSR_RDDREQ);
+-
+-	return IRQ_HANDLED;
+-}
+-
+ static irqreturn_t pxa3xx_nand_irq(int irq, void *devid)
+ {
+ 	struct pxa3xx_nand_info *info = devid;
+ 	unsigned int status, is_completed = 0, is_ready = 0;
+ 	unsigned int ready, cmd_done;
+-	irqreturn_t ret = IRQ_HANDLED;
+ 
+ 	if (info->cs == 0) {
+ 		ready           = NDSR_FLASH_RDY;
+@@ -850,8 +837,7 @@ static irqreturn_t pxa3xx_nand_irq(int i
+ 		} else {
+ 			info->state = (status & NDSR_RDDREQ) ?
+ 				      STATE_PIO_READING : STATE_PIO_WRITING;
+-			ret = IRQ_WAKE_THREAD;
+-			goto NORMAL_IRQ_EXIT;
++			handle_data_pio(info);
+ 		}
+ 	}
+ 	if (status & cmd_done) {
+@@ -896,7 +882,7 @@ static irqreturn_t pxa3xx_nand_irq(int i
+ 	if (is_ready)
+ 		complete(&info->dev_ready);
+ NORMAL_IRQ_EXIT:
+-	return ret;
++	return IRQ_HANDLED;
+ }
+ 
+ static inline int is_buf_blank(uint8_t *buf, size_t len)
+@@ -1856,9 +1842,7 @@ static int alloc_nand_resource(struct pl
+ 	/* initialize all interrupts to be disabled */
+ 	disable_int(info, NDSR_MASK);
+ 
+-	ret = request_threaded_irq(irq, pxa3xx_nand_irq,
+-				   pxa3xx_nand_irq_thread, IRQF_ONESHOT,
+-				   pdev->name, info);
++	ret = request_irq(irq, pxa3xx_nand_irq, 0, pdev->name, info);
+ 	if (ret < 0) {
+ 		dev_err(&pdev->dev, "failed to request IRQ\n");
+ 		goto fail_free_buf;
diff --git a/target/linux/mvebu/patches-4.9/120-net-mvneta-add-BQL-support.patch b/target/linux/mvebu/patches-4.9/120-net-mvneta-add-BQL-support.patch
new file mode 100644
index 0000000..0b822f3
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/120-net-mvneta-add-BQL-support.patch
@@ -0,0 +1,83 @@
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -1719,8 +1719,10 @@ static struct mvneta_tx_queue *mvneta_tx
+ 
+ /* Free tx queue skbuffs */
+ static void mvneta_txq_bufs_free(struct mvneta_port *pp,
+-				 struct mvneta_tx_queue *txq, int num)
++				 struct mvneta_tx_queue *txq, int num,
++				 struct netdev_queue *nq)
+ {
++	unsigned int bytes_compl = 0, pkts_compl = 0;
+ 	int i;
+ 
+ 	for (i = 0; i < num; i++) {
+@@ -1728,6 +1730,11 @@ static void mvneta_txq_bufs_free(struct
+ 			txq->txq_get_index;
+ 		struct sk_buff *skb = txq->tx_skb[txq->txq_get_index];
+ 
++		if (skb) {
++			bytes_compl += skb->len;
++			pkts_compl++;
++		}
++
+ 		mvneta_txq_inc_get(txq);
+ 
+ 		if (!IS_TSO_HEADER(txq, tx_desc->buf_phys_addr))
+@@ -1738,6 +1745,8 @@ static void mvneta_txq_bufs_free(struct
+ 			continue;
+ 		dev_kfree_skb_any(skb);
+ 	}
++
++	netdev_tx_completed_queue(nq, pkts_compl, bytes_compl);
+ }
+ 
+ /* Handle end of transmission */
+@@ -1751,7 +1760,7 @@ static void mvneta_txq_done(struct mvnet
+ 	if (!tx_done)
+ 		return;
+ 
+-	mvneta_txq_bufs_free(pp, txq, tx_done);
++	mvneta_txq_bufs_free(pp, txq, tx_done, nq);
+ 
+ 	txq->count -= tx_done;
+ 
+@@ -2358,6 +2367,8 @@ out:
+ 		struct mvneta_pcpu_stats *stats = this_cpu_ptr(pp->stats);
+ 		struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id);
+ 
++		netdev_tx_sent_queue(nq, len);
++
+ 		txq->count += frags;
+ 		mvneta_txq_pend_desc_add(pp, txq, frags);
+ 
+@@ -2382,9 +2393,10 @@ static void mvneta_txq_done_force(struct
+ 				  struct mvneta_tx_queue *txq)
+ 
+ {
++	struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id);
+ 	int tx_done = txq->count;
+ 
+-	mvneta_txq_bufs_free(pp, txq, tx_done);
++	mvneta_txq_bufs_free(pp, txq, tx_done, nq);
+ 
+ 	/* reset txq */
+ 	txq->count = 0;
+@@ -2882,6 +2894,8 @@ static int mvneta_txq_init(struct mvneta
+ static void mvneta_txq_deinit(struct mvneta_port *pp,
+ 			      struct mvneta_tx_queue *txq)
+ {
++	struct netdev_queue *nq = netdev_get_tx_queue(pp->dev, txq->id);
++
+ 	kfree(txq->tx_skb);
+ 
+ 	if (txq->tso_hdrs)
+@@ -2893,6 +2907,8 @@ static void mvneta_txq_deinit(struct mvn
+ 				  txq->size * MVNETA_DESC_ALIGNED_SIZE,
+ 				  txq->descs, txq->descs_phys);
+ 
++	netdev_tx_reset_queue(nq);
++
+ 	txq->descs             = NULL;
+ 	txq->last_desc         = 0;
+ 	txq->next_desc_to_proc = 0;
diff --git a/target/linux/mvebu/patches-4.9/200-gpio_mvebu_add_limited_pwm_support.patch b/target/linux/mvebu/patches-4.9/200-gpio_mvebu_add_limited_pwm_support.patch
new file mode 100644
index 0000000..0261b1e
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/200-gpio_mvebu_add_limited_pwm_support.patch
@@ -0,0 +1,433 @@
+Armada 370/XP devices can 'blink' gpio lines with a configurable on
+and off period. This can be modelled as a PWM.
+
+However, there are only two sets of PWM configuration registers for
+all the gpio lines. This driver simply allows a single gpio line per
+gpio chip of 32 lines to be used as a PWM. Attempts to use more return
+EBUSY.
+
+Due to the interleaving of registers it is not simple to separate the
+PWM driver from the gpio driver. Thus the gpio driver has been
+extended with a PWM driver.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+---
+ drivers/gpio/Kconfig          |   5 ++
+ drivers/gpio/Makefile         |   1 +
+ drivers/gpio/gpio-mvebu-pwm.c | 202 ++++++++++++++++++++++++++++++++++++++++++
+ drivers/gpio/gpio-mvebu.c     |  37 +++-----
+ drivers/gpio/gpio-mvebu.h     |  79 +++++++++++++++++
+ 5 files changed, 299 insertions(+), 25 deletions(-)
+ create mode 100644 drivers/gpio/gpio-mvebu-pwm.c
+ create mode 100644 drivers/gpio/gpio-mvebu.h
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -319,6 +319,11 @@ config GPIO_MVEBU
+ 	depends on OF_GPIO
+ 	select GENERIC_IRQ_CHIP
+ 
++config GPIO_MVEBU_PWM
++	def_bool y
++	depends on GPIO_MVEBU
++	depends on PWM
++
+ config GPIO_MXC
+ 	def_bool y
+ 	depends on ARCH_MXC
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -81,6 +81,7 @@ obj-$(CONFIG_GPIO_MPC5200)	+= gpio-mpc52
+ obj-$(CONFIG_GPIO_MPC8XXX)	+= gpio-mpc8xxx.o
+ obj-$(CONFIG_GPIO_MSIC)		+= gpio-msic.o
+ obj-$(CONFIG_GPIO_MVEBU)        += gpio-mvebu.o
++obj-$(CONFIG_GPIO_MVEBU_PWM)	+= gpio-mvebu-pwm.o
+ obj-$(CONFIG_GPIO_MXC)		+= gpio-mxc.o
+ obj-$(CONFIG_GPIO_MXS)		+= gpio-mxs.o
+ obj-$(CONFIG_GPIO_OCTEON)	+= gpio-octeon.o
+--- /dev/null
++++ b/drivers/gpio/gpio-mvebu-pwm.c
+@@ -0,0 +1,202 @@
++#include <linux/err.h>
++#include <linux/module.h>
++#include <linux/gpio.h>
++#include <linux/pwm.h>
++#include <linux/clk.h>
++#include <linux/platform_device.h>
++#include "gpio-mvebu.h"
++#include "gpiolib.h"
++
++static void __iomem *mvebu_gpioreg_blink_select(struct mvebu_gpio_chip *mvchip)
++{
++	return mvchip->membase + GPIO_BLINK_CNT_SELECT;
++}
++
++static inline struct mvebu_pwm *to_mvebu_pwm(struct pwm_chip *chip)
++{
++	return container_of(chip, struct mvebu_pwm, chip);
++}
++
++static inline struct mvebu_gpio_chip *to_mvchip(struct mvebu_pwm *pwm)
++{
++	return container_of(pwm, struct mvebu_gpio_chip, pwm);
++}
++
++static int mvebu_pwm_request(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++	struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
++	unsigned long flags;
++	int ret = 0;
++
++	spin_lock_irqsave(&pwm->lock, flags);
++	if (pwm->used) {
++		ret = -EBUSY;
++	} else {
++		if (!desc) {
++			ret = -ENODEV;
++			goto out;
++		}
++		ret = gpiod_request(desc, "mvebu-pwm");
++		if (ret)
++			goto out;
++
++		ret = gpiod_direction_output(desc, 0);
++		if (ret) {
++			gpiod_free(desc);
++			goto out;
++		}
++
++		pwm->pin = pwmd->pwm - mvchip->chip.base;
++		pwm->used = true;
++	}
++
++out:
++	spin_unlock_irqrestore(&pwm->lock, flags);
++	return ret;
++}
++
++static void mvebu_pwm_free(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct gpio_desc *desc = gpio_to_desc(pwmd->pwm);
++	unsigned long flags;
++
++	spin_lock_irqsave(&pwm->lock, flags);
++	gpiod_free(desc);
++	pwm->used = false;
++	spin_unlock_irqrestore(&pwm->lock, flags);
++}
++
++static int mvebu_pwm_config(struct pwm_chip *chip, struct pwm_device *pwmd,
++			    int duty_ns, int period_ns)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++	unsigned int on, off;
++	unsigned long long val;
++	u32 u;
++
++	val = (unsigned long long) pwm->clk_rate * duty_ns;
++	do_div(val, NSEC_PER_SEC);
++	if (val > UINT_MAX)
++		return -EINVAL;
++	if (val)
++		on = val;
++	else
++		on = 1;
++
++	val = (unsigned long long) pwm->clk_rate * (period_ns - duty_ns);
++	do_div(val, NSEC_PER_SEC);
++	if (val > UINT_MAX)
++		return -EINVAL;
++	if (val)
++		off = val;
++	else
++		off = 1;
++
++	u = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
++	u &= ~(1 << pwm->pin);
++	u |= (pwm->id << pwm->pin);
++	writel_relaxed(u, mvebu_gpioreg_blink_select(mvchip));
++
++	writel_relaxed(on, pwm->membase + BLINK_ON_DURATION);
++	writel_relaxed(off, pwm->membase + BLINK_OFF_DURATION);
++
++	return 0;
++}
++
++static int mvebu_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++
++	mvebu_gpio_blink(&mvchip->chip, pwm->pin, 1);
++
++	return 0;
++}
++
++static void mvebu_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwmd)
++{
++	struct mvebu_pwm *pwm = to_mvebu_pwm(chip);
++	struct mvebu_gpio_chip *mvchip = to_mvchip(pwm);
++
++	mvebu_gpio_blink(&mvchip->chip, pwm->pin, 0);
++}
++
++static const struct pwm_ops mvebu_pwm_ops = {
++	.request = mvebu_pwm_request,
++	.free = mvebu_pwm_free,
++	.config = mvebu_pwm_config,
++	.enable = mvebu_pwm_enable,
++	.disable = mvebu_pwm_disable,
++	.owner = THIS_MODULE,
++};
++
++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
++{
++	struct mvebu_pwm *pwm = &mvchip->pwm;
++
++	pwm->blink_select = readl_relaxed(mvebu_gpioreg_blink_select(mvchip));
++	pwm->blink_on_duration =
++		readl_relaxed(pwm->membase + BLINK_ON_DURATION);
++	pwm->blink_off_duration =
++		readl_relaxed(pwm->membase + BLINK_OFF_DURATION);
++}
++
++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
++{
++	struct mvebu_pwm *pwm = &mvchip->pwm;
++
++	writel_relaxed(pwm->blink_select, mvebu_gpioreg_blink_select(mvchip));
++	writel_relaxed(pwm->blink_on_duration,
++		       pwm->membase + BLINK_ON_DURATION);
++	writel_relaxed(pwm->blink_off_duration,
++		       pwm->membase + BLINK_OFF_DURATION);
++}
++
++/*
++ * Armada 370/XP has simple PWM support for gpio lines. Other SoCs
++ * don't have this hardware. So if we don't have the necessary
++ * resource, it is not an error.
++ */
++int mvebu_pwm_probe(struct platform_device *pdev,
++		    struct mvebu_gpio_chip *mvchip,
++		    int id)
++{
++	struct device *dev = &pdev->dev;
++	struct mvebu_pwm *pwm = &mvchip->pwm;
++	struct resource *res;
++
++	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
++	if (!res)
++		return 0;
++
++	mvchip->pwm.membase = devm_ioremap_resource(&pdev->dev, res);
++	if (IS_ERR(mvchip->pwm.membase))
++		return PTR_ERR(mvchip->percpu_membase);
++
++	if (id < 0 || id > 1)
++		return -EINVAL;
++	pwm->id = id;
++
++	if (IS_ERR(mvchip->clk))
++		return PTR_ERR(mvchip->clk);
++
++	pwm->clk_rate = clk_get_rate(mvchip->clk);
++	if (!pwm->clk_rate) {
++		dev_err(dev, "failed to get clock rate\n");
++		return -EINVAL;
++	}
++
++	pwm->chip.dev = dev;
++	pwm->chip.ops = &mvebu_pwm_ops;
++	pwm->chip.base = mvchip->chip.base;
++	pwm->chip.npwm = mvchip->chip.ngpio;
++	pwm->chip.can_sleep = false;
++
++	spin_lock_init(&pwm->lock);
++
++	return pwmchip_add(&pwm->chip);
++}
+--- a/drivers/gpio/gpio-mvebu.c
++++ b/drivers/gpio/gpio-mvebu.c
+@@ -42,10 +42,11 @@
+ #include <linux/io.h>
+ #include <linux/of_irq.h>
+ #include <linux/of_device.h>
++#include <linux/pwm.h>
+ #include <linux/clk.h>
+ #include <linux/pinctrl/consumer.h>
+ #include <linux/irqchip/chained_irq.h>
+-
++#include "gpio-mvebu.h"
+ /*
+  * GPIO unit register offsets.
+  */
+@@ -75,24 +76,6 @@
+ 
+ #define MVEBU_MAX_GPIO_PER_BANK		32
+ 
+-struct mvebu_gpio_chip {
+-	struct gpio_chip   chip;
+-	spinlock_t	   lock;
+-	void __iomem	  *membase;
+-	void __iomem	  *percpu_membase;
+-	int		   irqbase;
+-	struct irq_domain *domain;
+-	int		   soc_variant;
+-
+-	/* Used to preserve GPIO registers across suspend/resume */
+-	u32                out_reg;
+-	u32                io_conf_reg;
+-	u32                blink_en_reg;
+-	u32                in_pol_reg;
+-	u32                edge_mask_regs[4];
+-	u32                level_mask_regs[4];
+-};
+-
+ /*
+  * Functions returning addresses of individual registers for a given
+  * GPIO controller.
+@@ -216,7 +199,7 @@ static int mvebu_gpio_get(struct gpio_ch
+ 	return (u >> pin) & 1;
+ }
+ 
+-static void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
++void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value)
+ {
+ 	struct mvebu_gpio_chip *mvchip = gpiochip_get_data(chip);
+ 	unsigned long flags;
+@@ -596,6 +579,8 @@ static int mvebu_gpio_suspend(struct pla
+ 		BUG();
+ 	}
+ 
++	mvebu_pwm_suspend(mvchip);
++
+ 	return 0;
+ }
+ 
+@@ -639,6 +624,8 @@ static int mvebu_gpio_resume(struct plat
+ 		BUG();
+ 	}
+ 
++	mvebu_pwm_resume(mvchip);
++
+ 	return 0;
+ }
+ 
+@@ -650,7 +637,6 @@ static int mvebu_gpio_probe(struct platf
+ 	struct resource *res;
+ 	struct irq_chip_generic *gc;
+ 	struct irq_chip_type *ct;
+-	struct clk *clk;
+ 	unsigned int ngpios;
+ 	bool have_irqs;
+ 	int soc_variant;
+@@ -684,10 +670,10 @@ static int mvebu_gpio_probe(struct platf
+ 		return id;
+ 	}
+ 
+-	clk = devm_clk_get(&pdev->dev, NULL);
++	mvchip->clk = devm_clk_get(&pdev->dev, NULL);
+ 	/* Not all SoCs require a clock.*/
+-	if (!IS_ERR(clk))
+-		clk_prepare_enable(clk);
++	if (!IS_ERR(mvchip->clk))
++		clk_prepare_enable(mvchip->clk);
+ 
+ 	mvchip->soc_variant = soc_variant;
+ 	mvchip->chip.label = dev_name(&pdev->dev);
+@@ -814,7 +800,8 @@ static int mvebu_gpio_probe(struct platf
+ 						 mvchip);
+ 	}
+ 
+-	return 0;
++	/* Armada 370/XP has simple PWM support for gpio lines */
++	return mvebu_pwm_probe(pdev, mvchip, id);
+ 
+ err_domain:
+ 	irq_domain_remove(mvchip->domain);
+--- /dev/null
++++ b/drivers/gpio/gpio-mvebu.h
+@@ -0,0 +1,79 @@
++/*
++ * Interface between MVEBU GPIO driver and PWM driver for GPIO pins
++ *
++ * Copyright (C) 2015, Andrew Lunn <andrew at lunn.ch>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#ifndef MVEBU_GPIO_PWM_H
++#define MVEBU_GPIO_PWM_H
++
++#define BLINK_ON_DURATION	0x0
++#define BLINK_OFF_DURATION	0x4
++#define GPIO_BLINK_CNT_SELECT	0x0020
++
++struct mvebu_pwm {
++	void __iomem	*membase;
++	unsigned long	 clk_rate;
++	bool		 used;
++	unsigned	 pin;
++	struct pwm_chip	 chip;
++	int		 id;
++	spinlock_t	 lock;
++
++	/* Used to preserve GPIO/PWM registers across suspend /
++	 * resume */
++	u32		 blink_select;
++	u32		 blink_on_duration;
++	u32		 blink_off_duration;
++};
++
++struct mvebu_gpio_chip {
++	struct gpio_chip   chip;
++	spinlock_t	   lock;
++	void __iomem	  *membase;
++	void __iomem	  *percpu_membase;
++	int		   irqbase;
++	struct irq_domain *domain;
++	int		   soc_variant;
++	struct clk	  *clk;
++#ifdef CONFIG_PWM
++	struct mvebu_pwm pwm;
++#endif
++	/* Used to preserve GPIO registers across suspend/resume */
++	u32		   out_reg;
++	u32		   io_conf_reg;
++	u32		   blink_en_reg;
++	u32		   in_pol_reg;
++	u32		   edge_mask_regs[4];
++	u32		   level_mask_regs[4];
++};
++
++void mvebu_gpio_blink(struct gpio_chip *chip, unsigned pin, int value);
++
++#ifdef CONFIG_PWM
++int mvebu_pwm_probe(struct platform_device *pdev,
++		    struct mvebu_gpio_chip *mvchip,
++		    int id);
++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip);
++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip);
++#else
++int mvebu_pwm_probe(struct platform_device *pdev,
++		    struct mvebu_gpio_chip *mvchip,
++		    int id)
++{
++	return 0;
++}
++
++void mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip)
++{
++}
++
++void mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip)
++{
++}
++#endif
++#endif
diff --git a/target/linux/mvebu/patches-4.9/201-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch b/target/linux/mvebu/patches-4.9/201-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch
new file mode 100644
index 0000000..48f9394
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/201-dt_bindings_extend_mvebu_gpio_documentation_with_pwm.patch
@@ -0,0 +1,52 @@
+Document the optional parameters needed for PWM operation of gpio
+lines.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+---
+ .../devicetree/bindings/gpio/gpio-mvebu.txt        | 31 ++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
++++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt
+@@ -38,6 +38,23 @@ Required properties:
+ - #gpio-cells: Should be two. The first cell is the pin number. The
+   second cell is reserved for flags, unused at the moment.
+ 
++Optional properties:
++
++In order to use the gpio lines in PWM mode, some additional optional
++properties are required. Only Armada 370 and XP supports these
++properties.
++
++- reg: an additional register set is needed, for the GPIO Blink
++  Counter on/off registers.
++
++- reg-names: Must contain an entry "pwm" corresponding to the
++  additional register range needed for pwm operation.
++
++- #pwm-cells: Should be two. The first cell is the pin number. The
++  second cell is reserved for flags, unused at the moment.
++
++- clocks: Must be a phandle to the clock for the gpio controller.
++
+ Example:
+ 
+ 		gpio0: gpio at d0018100 {
+@@ -51,3 +68,17 @@ Example:
+ 			#interrupt-cells = <2>;
+ 			interrupts = <16>, <17>, <18>, <19>;
+ 		};
++
++		gpio1: gpio at 18140 {
++			compatible = "marvell,orion-gpio";
++			reg = <0x18140 0x40>, <0x181c8 0x08>;
++			reg-names = "gpio", "pwm";
++			ngpios = <17>;
++			gpio-controller;
++			#gpio-cells = <2>;
++			#pwm-cells = <2>;
++			interrupt-controller;
++			#interrupt-cells = <2>;
++			interrupts = <87>, <88>, <89>;
++			clocks = <&coreclk 0>;
++		};
diff --git a/target/linux/mvebu/patches-4.9/202-mvebu_xp_add_pwm_properties_to_dtsi_files.patch b/target/linux/mvebu/patches-4.9/202-mvebu_xp_add_pwm_properties_to_dtsi_files.patch
new file mode 100644
index 0000000..90e951e
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/202-mvebu_xp_add_pwm_properties_to_dtsi_files.patch
@@ -0,0 +1,149 @@
+Add properties to the gpio nodes to allow them to be also used
+as pwm lines.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+---
+ arch/arm/boot/dts/armada-370.dtsi        | 10 ++++++++--
+ arch/arm/boot/dts/armada-xp-mv78230.dtsi | 10 ++++++++--
+ arch/arm/boot/dts/armada-xp-mv78260.dtsi |  8 ++++++--
+ arch/arm/boot/dts/armada-xp-mv78460.dtsi | 10 ++++++++--
+ 4 files changed, 30 insertions(+), 8 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-370.dtsi
++++ b/arch/arm/boot/dts/armada-370.dtsi
+@@ -144,24 +144,30 @@
+ 
+ 			gpio0: gpio at 18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio at 18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>, <90>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio2: gpio at 18180 {
+--- a/arch/arm/boot/dts/armada-xp-mv78230.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78230.dtsi
+@@ -203,24 +203,30 @@
+ 		internal-regs {
+ 			gpio0: gpio at 18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio at 18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <17>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>;
++				clocks = <&coreclk 0>;
+ 			};
+ 		};
+ 	};
+--- a/arch/arm/boot/dts/armada-xp-mv78260.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78260.dtsi
+@@ -286,24 +286,28 @@
+ 		internal-regs {
+ 			gpio0: gpio at 18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio at 18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>, <90>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio2: gpio at 18180 {
+--- a/arch/arm/boot/dts/armada-xp-mv78460.dtsi
++++ b/arch/arm/boot/dts/armada-xp-mv78460.dtsi
+@@ -324,24 +324,30 @@
+ 		internal-regs {
+ 			gpio0: gpio at 18100 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18100 0x40>;
++				reg = <0x18100 0x40>, <0x181c0 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <82>, <83>, <84>, <85>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio1: gpio at 18140 {
+ 				compatible = "marvell,orion-gpio";
+-				reg = <0x18140 0x40>;
++				reg = <0x18140 0x40>, <0x181c8 0x08>;
++				reg-names = "gpio", "pwm";
+ 				ngpios = <32>;
+ 				gpio-controller;
+ 				#gpio-cells = <2>;
++				#pwm-cells = <2>;
+ 				interrupt-controller;
+ 				#interrupt-cells = <2>;
+ 				interrupts = <87>, <88>, <89>, <90>;
++				clocks = <&coreclk 0>;
+ 			};
+ 
+ 			gpio2: gpio at 18180 {
diff --git a/target/linux/mvebu/patches-4.9/203-arm_mvebu_enable_pwm_in_defconfig.patch b/target/linux/mvebu/patches-4.9/203-arm_mvebu_enable_pwm_in_defconfig.patch
new file mode 100644
index 0000000..21eb24d
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/203-arm_mvebu_enable_pwm_in_defconfig.patch
@@ -0,0 +1,18 @@
+Now that the gpio driver also supports PWM operation, enable
+the PWM framework in mvebu_v7_defconfig.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+---
+ arch/arm/configs/mvebu_v7_defconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/configs/mvebu_v7_defconfig
++++ b/arch/arm/configs/mvebu_v7_defconfig
+@@ -135,6 +135,7 @@ CONFIG_DMADEVICES=y
+ CONFIG_MV_XOR=y
+ # CONFIG_IOMMU_SUPPORT is not set
+ CONFIG_MEMORY=y
++CONFIG_PWM=y
+ CONFIG_EXT4_FS=y
+ CONFIG_ISO9660_FS=y
+ CONFIG_JOLIET=y
diff --git a/target/linux/mvebu/patches-4.9/204-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch b/target/linux/mvebu/patches-4.9/204-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch
new file mode 100644
index 0000000..b46697b
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/204-mvebu_wrt1900ac_use_pwm-fan_rather_than_gpio-fan.patch
@@ -0,0 +1,28 @@
+The mvebu gpio driver can also perform PWM on some pins. Us the
+pwm-fan driver to control the fan of the WRT1900AC, giving us fine
+grain control over its speed and hence noise.
+
+Signed-off-by: Andrew Lunn <andrew at lunn.ch>
+---
+ arch/arm/boot/dts/armada-xp-wrt1900ac.dts | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
++++ b/arch/arm/boot/dts/armada-xp-linksys-mamba.dts
+@@ -416,13 +416,11 @@
+ 		};
+ 	};
+ 
+-	gpio_fan {
++	pwm_fan {
+ 		/* SUNON HA4010V4-0000-C99 */
+-		compatible = "gpio-fan";
+-		gpios = <&gpio0 24 0>;
+ 
+-		gpio-fan,speed-map = <0    0
+-				      4500 1>;
++		compatible = "pwm-fan";
++		pwms = <&gpio0 24 4000 0>;
+ 	};
+ 
+ 	dsa at 0 {
diff --git a/target/linux/mvebu/patches-4.9/205-armada-385-rd-mtd-partitions.patch b/target/linux/mvebu/patches-4.9/205-armada-385-rd-mtd-partitions.patch
new file mode 100644
index 0000000..e75a5ee
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/205-armada-385-rd-mtd-partitions.patch
@@ -0,0 +1,19 @@
+--- a/arch/arm/boot/dts/armada-388-rd.dts
++++ b/arch/arm/boot/dts/armada-388-rd.dts
+@@ -140,6 +140,16 @@
+ 		compatible = "st,m25p128", "jedec,spi-nor";
+ 		reg = <0>; /* Chip select 0 */
+ 		spi-max-frequency = <108000000>;
++
++		partition at 0 {
++			label = "uboot";
++			reg = <0 0x400000>;
++		};
++
++		partition at 1 {
++			label = "firmware";
++			reg = <0x400000 0xc00000>;
++		};
+ 	};
+ };
+ 
diff --git a/target/linux/mvebu/patches-4.9/206-ARM-mvebu-385-ap-Add-partitions.patch b/target/linux/mvebu/patches-4.9/206-ARM-mvebu-385-ap-Add-partitions.patch
new file mode 100644
index 0000000..b7af272
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/206-ARM-mvebu-385-ap-Add-partitions.patch
@@ -0,0 +1,35 @@
+From 9861f93a59142a3131870df2521eb2deb73026d7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime.ripard at free-electrons.com>
+Date: Tue, 13 Jan 2015 11:14:09 +0100
+Subject: [PATCH 2/2] ARM: mvebu: 385-ap: Add partitions
+
+Signed-off-by: Maxime Ripard <maxime.ripard at free-electrons.com>
+---
+ arch/arm/boot/dts/armada-385-db-ap.dts | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/arch/arm/boot/dts/armada-385-db-ap.dts
++++ b/arch/arm/boot/dts/armada-385-db-ap.dts
+@@ -181,19 +181,19 @@
+ 					#size-cells = <1>;
+ 
+ 					partition at 0 {
+-						label = "U-Boot";
++						label = "u-boot";
+ 						reg = <0x00000000 0x00800000>;
+ 						read-only;
+ 					};
+ 
+ 					partition at 800000 {
+-						label = "uImage";
++						label = "kernel";
+ 						reg = <0x00800000 0x00400000>;
+ 						read-only;
+ 					};
+ 
+ 					partition at c00000 {
+-						label = "Root";
++						label = "ubi";
+ 						reg = <0x00c00000 0x3f400000>;
+ 					};
+ 				};
diff --git a/target/linux/mvebu/patches-4.9/210-clearfog_switch_node.patch b/target/linux/mvebu/patches-4.9/210-clearfog_switch_node.patch
new file mode 100644
index 0000000..022dc6f
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/210-clearfog_switch_node.patch
@@ -0,0 +1,21 @@
+--- a/arch/arm/boot/dts/armada-388-clearfog.dts
++++ b/arch/arm/boot/dts/armada-388-clearfog.dts
+@@ -406,6 +406,18 @@
+ 		};
+ 	};
+ 
++	mvsw61xx {
++		#address-cells = <1>;
++		#size-cells = <0>;
++		compatible = "marvell,88e6176";
++		status = "okay";
++		reg = <0x4>;
++		is-indirect;
++
++		mii-bus = <&mdio>;
++		cpu-port-0 = <5>;
++	};
++
+ 	gpio-keys {
+ 		compatible = "gpio-keys";
+ 		pinctrl-0 = <&rear_button_pins>;
diff --git a/target/linux/mvebu/patches-4.9/220-ARM-dts-armada388-clearfog-add-SFP-module-support.patch b/target/linux/mvebu/patches-4.9/220-ARM-dts-armada388-clearfog-add-SFP-module-support.patch
new file mode 100644
index 0000000..e7aa5c4
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/220-ARM-dts-armada388-clearfog-add-SFP-module-support.patch
@@ -0,0 +1,84 @@
+From 63ff73593c2f5d3fc1cba479321d192caaca48aa Mon Sep 17 00:00:00 2001
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Sat, 12 Sep 2015 18:43:39 +0100
+Subject: [PATCH 738/744] ARM: dts: armada388-clearfog: add SFP module support
+
+Add SFP module support for Clearfog using the SFP phylink support.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+ arch/arm/boot/dts/armada-388-clearfog.dts | 44 ++++++++-----------------------
+ 1 file changed, 11 insertions(+), 33 deletions(-)
+
+--- a/arch/arm/boot/dts/armada-388-clearfog.dts
++++ b/arch/arm/boot/dts/armada-388-clearfog.dts
+@@ -90,16 +90,12 @@
+ 			};
+ 
+ 			ethernet at 34000 {
++				managed = "in-band-status";
+ 				phy-mode = "sgmii";
+ 				buffer-manager = <&bm>;
+ 				bm,pool-long = <3>;
+ 				bm,pool-short = <1>;
+ 				status = "okay";
+-
+-				fixed-link {
+-					speed = <1000>;
+-					full-duplex;
+-				};
+ 			};
+ 
+ 			i2c at 11000 {
+@@ -183,34 +179,6 @@
+ 						output-low;
+ 						line-name = "m.2 devslp";
+ 					};
+-					sfp_los {
+-						/* SFP loss of signal */
+-						gpio-hog;
+-						gpios = <12 GPIO_ACTIVE_HIGH>;
+-						input;
+-						line-name = "sfp-los";
+-					};
+-					sfp_tx_fault {
+-						/* SFP laser fault */
+-						gpio-hog;
+-						gpios = <13 GPIO_ACTIVE_HIGH>;
+-						input;
+-						line-name = "sfp-tx-fault";
+-					};
+-					sfp_tx_disable {
+-						/* SFP transmit disable */
+-						gpio-hog;
+-						gpios = <14 GPIO_ACTIVE_HIGH>;
+-						output-low;
+-						line-name = "sfp-tx-disable";
+-					};
+-					sfp_mod_def0 {
+-						/* SFP module present */
+-						gpio-hog;
+-						gpios = <15 GPIO_ACTIVE_LOW>;
+-						input;
+-						line-name = "sfp-mod-def0";
+-					};
+ 				};
+ 
+ 				/* The MCP3021 is 100kHz clock only */
+@@ -350,6 +318,16 @@
+ 		};
+ 	};
+ 
++	sfp: sfp {
++		compatible = "sff,sfp";
++		i2c-bus = <&i2c1>;
++		los-gpio = <&expander0 12 GPIO_ACTIVE_HIGH>;
++		moddef0-gpio = <&expander0 15 GPIO_ACTIVE_LOW>;
++		sfp,ethernet = <&eth2>;
++		tx-disable-gpio = <&expander0 14 GPIO_ACTIVE_HIGH>;
++		tx-fault-gpio = <&expander0 13 GPIO_ACTIVE_HIGH>;
++	};
++
+ 	dsa at 0 {
+ 		compatible = "marvell,dsa";
+ 		dsa,ethernet = <&eth1>;
diff --git a/target/linux/mvebu/patches-4.9/300-mvneta-tx-queue-workaround.patch b/target/linux/mvebu/patches-4.9/300-mvneta-tx-queue-workaround.patch
new file mode 100644
index 0000000..4801c4a
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/300-mvneta-tx-queue-workaround.patch
@@ -0,0 +1,36 @@
+The hardware queue scheduling is apparently configured with fixed
+priorities, which creates a nasty fairness issue where traffic from one
+CPU can starve traffic from all other CPUs.
+
+Work around this issue by forcing all tx packets to go through one CPU,
+until this issue is fixed properly.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3835,6 +3835,16 @@ static int mvneta_ethtool_get_rxfh(struc
+ 	return 0;
+ }
+ 
++static u16 mvneta_select_queue(struct net_device *dev, struct sk_buff *skb,
++			       void *accel_priv,
++			       select_queue_fallback_t fallback)
++{
++	/* XXX: hardware queue scheduling is broken,
++	 * use only one queue until it is fixed */
++	return 0;
++}
++
++
+ static const struct net_device_ops mvneta_netdev_ops = {
+ 	.ndo_open            = mvneta_open,
+ 	.ndo_stop            = mvneta_stop,
+@@ -3845,6 +3855,7 @@ static const struct net_device_ops mvnet
+ 	.ndo_fix_features    = mvneta_fix_features,
+ 	.ndo_get_stats64     = mvneta_get_stats64,
+ 	.ndo_do_ioctl        = mvneta_ioctl,
++	.ndo_select_queue    = mvneta_select_queue,
+ };
+ 
+ const struct ethtool_ops mvneta_eth_tool_ops = {
diff --git a/target/linux/mvebu/patches-4.9/400-phy-provide-a-hook-for-link-up-link-down-events.patch b/target/linux/mvebu/patches-4.9/400-phy-provide-a-hook-for-link-up-link-down-events.patch
new file mode 100644
index 0000000..3e30d49
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/400-phy-provide-a-hook-for-link-up-link-down-events.patch
@@ -0,0 +1,177 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Sun, 12 Feb 2017 17:27:17 +0100
+Subject: [PATCH] phy: provide a hook for link up/link down events
+
+Sometimes, we need to do additional work between the PHY coming up and
+marking the carrier present - for example, we may need to wait for the
+PHY to MAC link to finish negotiation.  This changes phylib to provide
+a notification function pointer which avoids the built-in
+netif_carrier_on() and netif_carrier_off() functions.
+
+Standard ->adjust_link functionality is provided by hooking a helper
+into the new ->phy_link_change method.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -955,6 +955,16 @@ void phy_start(struct phy_device *phydev
+ }
+ EXPORT_SYMBOL(phy_start);
+ 
++static void phy_link_up(struct phy_device *phydev)
++{
++	phydev->phy_link_change(phydev, true, true);
++}
++
++static void phy_link_down(struct phy_device *phydev, bool do_carrier)
++{
++	phydev->phy_link_change(phydev, false, do_carrier);
++}
++
+ /**
+  * phy_state_machine - Handle the state machine
+  * @work: work_struct that describes the work to be done
+@@ -996,8 +1006,7 @@ void phy_state_machine(struct work_struc
+ 		/* If the link is down, give up on negotiation for now */
+ 		if (!phydev->link) {
+ 			phydev->state = PHY_NOLINK;
+-			netif_carrier_off(phydev->attached_dev);
+-			phydev->adjust_link(phydev->attached_dev);
++			phy_link_down(phydev, true);
+ 			break;
+ 		}
+ 
+@@ -1009,9 +1018,7 @@ void phy_state_machine(struct work_struc
+ 		/* If AN is done, we're running */
+ 		if (err > 0) {
+ 			phydev->state = PHY_RUNNING;
+-			netif_carrier_on(phydev->attached_dev);
+-			phydev->adjust_link(phydev->attached_dev);
+-
++			phy_link_up(phydev);
+ 		} else if (0 == phydev->link_timeout--)
+ 			needs_aneg = true;
+ 		break;
+@@ -1036,8 +1043,7 @@ void phy_state_machine(struct work_struc
+ 				}
+ 			}
+ 			phydev->state = PHY_RUNNING;
+-			netif_carrier_on(phydev->attached_dev);
+-			phydev->adjust_link(phydev->attached_dev);
++			phy_link_up(phydev);
+ 		}
+ 		break;
+ 	case PHY_FORCING:
+@@ -1047,13 +1053,12 @@ void phy_state_machine(struct work_struc
+ 
+ 		if (phydev->link) {
+ 			phydev->state = PHY_RUNNING;
+-			netif_carrier_on(phydev->attached_dev);
++			phy_link_up(phydev);
+ 		} else {
+ 			if (0 == phydev->link_timeout--)
+ 				needs_aneg = true;
++			phy_link_down(phydev, false);
+ 		}
+-
+-		phydev->adjust_link(phydev->attached_dev);
+ 		break;
+ 	case PHY_RUNNING:
+ 		/* Only register a CHANGE if we are polling and link changed
+@@ -1076,14 +1081,12 @@ void phy_state_machine(struct work_struc
+ 
+ 		if (phydev->link) {
+ 			phydev->state = PHY_RUNNING;
+-			netif_carrier_on(phydev->attached_dev);
++			phy_link_up(phydev);
+ 		} else {
+ 			phydev->state = PHY_NOLINK;
+-			netif_carrier_off(phydev->attached_dev);
++			phy_link_down(phydev, true);
+ 		}
+ 
+-		phydev->adjust_link(phydev->attached_dev);
+-
+ 		if (phy_interrupt_is_valid(phydev))
+ 			err = phy_config_interrupt(phydev,
+ 						   PHY_INTERRUPT_ENABLED);
+@@ -1091,8 +1094,7 @@ void phy_state_machine(struct work_struc
+ 	case PHY_HALTED:
+ 		if (phydev->link) {
+ 			phydev->link = 0;
+-			netif_carrier_off(phydev->attached_dev);
+-			phydev->adjust_link(phydev->attached_dev);
++			phy_link_down(phydev, true);
+ 			do_suspend = true;
+ 		}
+ 		break;
+@@ -1112,11 +1114,11 @@ void phy_state_machine(struct work_struc
+ 
+ 				if (phydev->link) {
+ 					phydev->state = PHY_RUNNING;
+-					netif_carrier_on(phydev->attached_dev);
++					phy_link_up(phydev);
+ 				} else	{
+ 					phydev->state = PHY_NOLINK;
++					phy_link_down(phydev, false);
+ 				}
+-				phydev->adjust_link(phydev->attached_dev);
+ 			} else {
+ 				phydev->state = PHY_AN;
+ 				phydev->link_timeout = PHY_AN_TIMEOUT;
+@@ -1128,11 +1130,11 @@ void phy_state_machine(struct work_struc
+ 
+ 			if (phydev->link) {
+ 				phydev->state = PHY_RUNNING;
+-				netif_carrier_on(phydev->attached_dev);
++				phy_link_up(phydev);
+ 			} else	{
+ 				phydev->state = PHY_NOLINK;
++				phy_link_down(phydev, false);
+ 			}
+-			phydev->adjust_link(phydev->attached_dev);
+ 		}
+ 		break;
+ 	}
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -645,6 +645,19 @@ struct phy_device *phy_find_first(struct
+ }
+ EXPORT_SYMBOL(phy_find_first);
+ 
++static void phy_link_change(struct phy_device *phydev, bool up, bool do_carrier)
++{
++	struct net_device *netdev = phydev->attached_dev;
++
++	if (do_carrier) {
++		if (up)
++			netif_carrier_on(netdev);
++		else
++			netif_carrier_off(netdev);
++	}
++	phydev->adjust_link(netdev);
++}
++
+ /**
+  * phy_prepare_link - prepares the PHY layer to monitor link status
+  * @phydev: target phy_device struct
+@@ -899,6 +912,7 @@ int phy_attach_direct(struct net_device
+ 		goto error;
+ 	}
+ 
++	phydev->phy_link_change = phy_link_change;
+ 	phydev->attached_dev = dev;
+ 	dev->phydev = phydev;
+ 
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -426,6 +426,7 @@ struct phy_device {
+ 
+ 	u8 mdix;
+ 
++	void (*phy_link_change)(struct phy_device *, bool up, bool do_carrier);
+ 	void (*adjust_link)(struct net_device *dev);
+ };
+ #define to_phy_device(d) container_of(to_mdio_device(d), \
diff --git a/target/linux/mvebu/patches-4.9/401-net-phy-move-phy-MMD-accessors-to-phy-core.c.patch b/target/linux/mvebu/patches-4.9/401-net-phy-move-phy-MMD-accessors-to-phy-core.c.patch
new file mode 100644
index 0000000..970aec1
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/401-net-phy-move-phy-MMD-accessors-to-phy-core.c.patch
@@ -0,0 +1,292 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Wed, 4 Jan 2017 10:46:43 +0000
+Subject: [PATCH] net: phy: move phy MMD accessors to phy-core.c
+
+Move the phy_(read|write)__mmd() helpers out of line, they will become
+our main MMD accessor functions, and so will be a little more complex.
+This complexity doesn't belong in an inline function.  Also move the
+_indirect variants as well to keep like functionality together.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+ create mode 100644 drivers/net/phy/phy-core.c
+
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -1,6 +1,7 @@
+ # Makefile for Linux PHY drivers and MDIO bus drivers
+ 
+-libphy-y			:= phy.o phy_device.o mdio_bus.o mdio_device.o
++libphy-y			:= phy.o phy_device.o mdio_bus.o mdio_device.o \
++				   phy-core.o
+ libphy-$(CONFIG_SWPHY)		+= swphy.o
+ 
+ obj-$(CONFIG_MDIO_BOARDINFO)	+= mdio-boardinfo.o
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -1171,91 +1171,6 @@ void phy_mac_interrupt(struct phy_device
+ }
+ EXPORT_SYMBOL(phy_mac_interrupt);
+ 
+-static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
+-				    int addr)
+-{
+-	/* Write the desired MMD Devad */
+-	bus->write(bus, addr, MII_MMD_CTRL, devad);
+-
+-	/* Write the desired MMD register address */
+-	bus->write(bus, addr, MII_MMD_DATA, prtad);
+-
+-	/* Select the Function : DATA with no post increment */
+-	bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
+-}
+-
+-/**
+- * phy_read_mmd_indirect - reads data from the MMD registers
+- * @phydev: The PHY device bus
+- * @prtad: MMD Address
+- * @devad: MMD DEVAD
+- *
+- * Description: it reads data from the MMD registers (clause 22 to access to
+- * clause 45) of the specified phy address.
+- * To read these register we have:
+- * 1) Write reg 13 // DEVAD
+- * 2) Write reg 14 // MMD Address
+- * 3) Write reg 13 // MMD Data Command for MMD DEVAD
+- * 3) Read  reg 14 // Read MMD data
+- */
+-int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad)
+-{
+-	struct phy_driver *phydrv = phydev->drv;
+-	int addr = phydev->mdio.addr;
+-	int value = -1;
+-
+-	if (!phydrv->read_mmd_indirect) {
+-		struct mii_bus *bus = phydev->mdio.bus;
+-
+-		mutex_lock(&bus->mdio_lock);
+-		mmd_phy_indirect(bus, prtad, devad, addr);
+-
+-		/* Read the content of the MMD's selected register */
+-		value = bus->read(bus, addr, MII_MMD_DATA);
+-		mutex_unlock(&bus->mdio_lock);
+-	} else {
+-		value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr);
+-	}
+-	return value;
+-}
+-EXPORT_SYMBOL(phy_read_mmd_indirect);
+-
+-/**
+- * phy_write_mmd_indirect - writes data to the MMD registers
+- * @phydev: The PHY device
+- * @prtad: MMD Address
+- * @devad: MMD DEVAD
+- * @data: data to write in the MMD register
+- *
+- * Description: Write data from the MMD registers of the specified
+- * phy address.
+- * To write these register we have:
+- * 1) Write reg 13 // DEVAD
+- * 2) Write reg 14 // MMD Address
+- * 3) Write reg 13 // MMD Data Command for MMD DEVAD
+- * 3) Write reg 14 // Write MMD data
+- */
+-void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
+-				   int devad, u32 data)
+-{
+-	struct phy_driver *phydrv = phydev->drv;
+-	int addr = phydev->mdio.addr;
+-
+-	if (!phydrv->write_mmd_indirect) {
+-		struct mii_bus *bus = phydev->mdio.bus;
+-
+-		mutex_lock(&bus->mdio_lock);
+-		mmd_phy_indirect(bus, prtad, devad, addr);
+-
+-		/* Write the data into MMD's selected register */
+-		bus->write(bus, addr, MII_MMD_DATA, data);
+-		mutex_unlock(&bus->mdio_lock);
+-	} else {
+-		phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data);
+-	}
+-}
+-EXPORT_SYMBOL(phy_write_mmd_indirect);
+-
+ /**
+  * phy_init_eee - init and check the EEE feature
+  * @phydev: target phy_device struct
+--- /dev/null
++++ b/drivers/net/phy/phy-core.c
+@@ -0,0 +1,135 @@
++/*
++ * Core PHY library, taken from phy.c
++ *
++ * This program is free software; you can redistribute  it and/or modify it
++ * under  the terms of  the GNU General  Public License as published by the
++ * Free Software Foundation;  either version 2 of the  License, or (at your
++ * option) any later version.
++ */
++#include <linux/export.h>
++#include <linux/phy.h>
++
++static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
++				    int addr)
++{
++	/* Write the desired MMD Devad */
++	bus->write(bus, addr, MII_MMD_CTRL, devad);
++
++	/* Write the desired MMD register address */
++	bus->write(bus, addr, MII_MMD_DATA, prtad);
++
++	/* Select the Function : DATA with no post increment */
++	bus->write(bus, addr, MII_MMD_CTRL, (devad | MII_MMD_CTRL_NOINCR));
++}
++
++/**
++ * phy_read_mmd_indirect - reads data from the MMD registers
++ * @phydev: The PHY device bus
++ * @prtad: MMD Address
++ * @devad: MMD DEVAD
++ *
++ * Description: it reads data from the MMD registers (clause 22 to access to
++ * clause 45) of the specified phy address.
++ * To read these register we have:
++ * 1) Write reg 13 // DEVAD
++ * 2) Write reg 14 // MMD Address
++ * 3) Write reg 13 // MMD Data Command for MMD DEVAD
++ * 3) Read  reg 14 // Read MMD data
++ */
++int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int devad)
++{
++	struct phy_driver *phydrv = phydev->drv;
++	int addr = phydev->mdio.addr;
++	int value = -1;
++
++	if (!phydrv->read_mmd_indirect) {
++		struct mii_bus *bus = phydev->mdio.bus;
++
++		mutex_lock(&bus->mdio_lock);
++		mmd_phy_indirect(bus, prtad, devad, addr);
++
++		/* Read the content of the MMD's selected register */
++		value = bus->read(bus, addr, MII_MMD_DATA);
++		mutex_unlock(&bus->mdio_lock);
++	} else {
++		value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr);
++	}
++	return value;
++}
++EXPORT_SYMBOL(phy_read_mmd_indirect);
++
++/**
++ * phy_read_mmd - Convenience function for reading a register
++ * from an MMD on a given PHY.
++ * @phydev: The phy_device struct
++ * @devad: The MMD to read from
++ * @regnum: The register on the MMD to read
++ *
++ * Same rules as for phy_read();
++ */
++int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
++{
++	if (!phydev->is_c45)
++		return -EOPNOTSUPP;
++
++	return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
++			    MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff));
++}
++EXPORT_SYMBOL(phy_read_mmd);
++
++/**
++ * phy_write_mmd_indirect - writes data to the MMD registers
++ * @phydev: The PHY device
++ * @prtad: MMD Address
++ * @devad: MMD DEVAD
++ * @data: data to write in the MMD register
++ *
++ * Description: Write data from the MMD registers of the specified
++ * phy address.
++ * To write these register we have:
++ * 1) Write reg 13 // DEVAD
++ * 2) Write reg 14 // MMD Address
++ * 3) Write reg 13 // MMD Data Command for MMD DEVAD
++ * 3) Write reg 14 // Write MMD data
++ */
++void phy_write_mmd_indirect(struct phy_device *phydev, int prtad,
++				   int devad, u32 data)
++{
++	struct phy_driver *phydrv = phydev->drv;
++	int addr = phydev->mdio.addr;
++
++	if (!phydrv->write_mmd_indirect) {
++		struct mii_bus *bus = phydev->mdio.bus;
++
++		mutex_lock(&bus->mdio_lock);
++		mmd_phy_indirect(bus, prtad, devad, addr);
++
++		/* Write the data into MMD's selected register */
++		bus->write(bus, addr, MII_MMD_DATA, data);
++		mutex_unlock(&bus->mdio_lock);
++	} else {
++		phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data);
++	}
++}
++EXPORT_SYMBOL(phy_write_mmd_indirect);
++
++/**
++ * phy_write_mmd - Convenience function for writing a register
++ * on an MMD on a given PHY.
++ * @phydev: The phy_device struct
++ * @devad: The MMD to read from
++ * @regnum: The register on the MMD to read
++ * @val: value to write to @regnum
++ *
++ * Same rules as for phy_write();
++ */
++int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
++{
++	if (!phydev->is_c45)
++		return -EOPNOTSUPP;
++
++	regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff);
++
++	return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
++}
++EXPORT_SYMBOL(phy_write_mmd);
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -627,14 +627,7 @@ struct phy_fixup {
+  *
+  * Same rules as for phy_read();
+  */
+-static inline int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
+-{
+-	if (!phydev->is_c45)
+-		return -EOPNOTSUPP;
+-
+-	return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
+-			    MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff));
+-}
++int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum);
+ 
+ /**
+  * phy_read_mmd_indirect - reads data from the MMD registers
+@@ -728,16 +721,7 @@ static inline bool phy_is_pseudo_fixed_l
+  *
+  * Same rules as for phy_write();
+  */
+-static inline int phy_write_mmd(struct phy_device *phydev, int devad,
+-				u32 regnum, u16 val)
+-{
+-	if (!phydev->is_c45)
+-		return -EOPNOTSUPP;
+-
+-	regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff);
+-
+-	return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
+-}
++int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val);
+ 
+ /**
+  * phy_write_mmd_indirect - writes data to the MMD registers
diff --git a/target/linux/mvebu/patches-4.9/402-net-phy-make-phy_-read-write-_mmd-generic-MMD-access.patch b/target/linux/mvebu/patches-4.9/402-net-phy-make-phy_-read-write-_mmd-generic-MMD-access.patch
new file mode 100644
index 0000000..a37b0a7
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/402-net-phy-make-phy_-read-write-_mmd-generic-MMD-access.patch
@@ -0,0 +1,97 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Wed, 4 Jan 2017 19:20:21 +0000
+Subject: [PATCH] net: phy: make phy_(read|write)_mmd() generic MMD
+ accessors
+
+Make phy_(read|write)_mmd() generic 802.3 clause 45 register accessors
+for both Clause 22 and Clause 45 PHYs, using either the direct register
+reading for Clause 45, or the indirect method for Clause 22 PHYs.
+Allow this behaviour to be overriden by PHY drivers where necessary.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -69,11 +69,18 @@ EXPORT_SYMBOL(phy_read_mmd_indirect);
+  */
+ int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum)
+ {
+-	if (!phydev->is_c45)
+-		return -EOPNOTSUPP;
++	if (regnum > (u16)~0 || devad > 32)
++		return -EINVAL;
+ 
+-	return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr,
+-			    MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff));
++	if (phydev->drv->read_mmd)
++		return phydev->drv->read_mmd(phydev, devad, regnum);
++
++	if (phydev->is_c45) {
++		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff);
++		return mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr);
++	}
++
++	return phy_read_mmd_indirect(phydev, regnum, devad);
+ }
+ EXPORT_SYMBOL(phy_read_mmd);
+ 
+@@ -125,11 +132,21 @@ EXPORT_SYMBOL(phy_write_mmd_indirect);
+  */
+ int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val)
+ {
+-	if (!phydev->is_c45)
+-		return -EOPNOTSUPP;
++	if (regnum > (u16)~0 || devad > 32)
++		return -EINVAL;
++
++	if (phydev->drv->read_mmd)
++		return phydev->drv->write_mmd(phydev, devad, regnum, val);
++
++	if (phydev->is_c45) {
++		u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff);
++
++		return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr,
++				     addr, val);
++	}
+ 
+-	regnum = MII_ADDR_C45 | ((devad & 0x1f) << 16) | (regnum & 0xffff);
++	phy_write_mmd_indirect(phydev, regnum, devad, val);
+ 
+-	return mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, regnum, val);
++	return 0;
+ }
+ EXPORT_SYMBOL(phy_write_mmd);
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -570,6 +570,30 @@ struct phy_driver {
+ 	 */
+ 	void (*link_change_notify)(struct phy_device *dev);
+ 
++	/*
++	 * Phy specific driver override for reading a MMD register.
++	 * This function is optional for PHY specific drivers.  When
++	 * not provided, the default MMD read function will be used
++	 * by phy_read_mmd(), which will use either a direct read for
++	 * Clause 45 PHYs or an indirect read for Clause 22 PHYs.
++	 *  devnum is the MMD device number within the PHY device,
++	 *  regnum is the register within the selected MMD device.
++	 */
++	int (*read_mmd)(struct phy_device *dev, int devnum, u16 regnum);
++
++	/*
++	 * Phy specific driver override for writing a MMD register.
++	 * This function is optional for PHY specific drivers.  When
++	 * not provided, the default MMD write function will be used
++	 * by phy_write_mmd(), which will use either a direct write for
++	 * Clause 45 PHYs, or an indirect write for Clause 22 PHYs.
++	 *  devnum is the MMD device number within the PHY device,
++	 *  regnum is the register within the selected MMD device.
++	 *  val is the value to be written.
++	 */
++	int (*write_mmd)(struct phy_device *dev, int devnum, u16 regnum,
++			 u16 val);
++
+ 	/* A function provided by a phy specific driver to override the
+ 	 * the PHY driver framework support for reading a MMD register
+ 	 * from the PHY. If not supported, return -1. This function is
diff --git a/target/linux/mvebu/patches-4.9/403-net-phy-avoid-setting-unsupported-EEE-advertisments.patch b/target/linux/mvebu/patches-4.9/403-net-phy-avoid-setting-unsupported-EEE-advertisments.patch
new file mode 100644
index 0000000..3547900
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/403-net-phy-avoid-setting-unsupported-EEE-advertisments.patch
@@ -0,0 +1,52 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Wed, 4 Jan 2017 21:00:51 +0000
+Subject: [PATCH] net: phy: avoid setting unsupported EEE advertisments
+
+We currently allow userspace to set any EEE advertisments it desires,
+whether or not the PHY supports them.  For example:
+
+ # ethtool --set-eee eth1 advertise 0xffffffff
+ # ethtool --show-eee eth1
+ EEE Settings for eth1:
+        EEE status: disabled
+        Tx LPI: disabled
+        Supported EEE link modes:  100baseT/Full
+                                   1000baseT/Full
+                                   10000baseT/Full
+        Advertised EEE link modes:  100baseT/Full
+                                    1000baseT/Full
+                                    1000baseKX/Full
+                                    10000baseT/Full
+                                    10000baseKX4/Full
+                                    10000baseKR/Full
+
+Clearly, this is not sane, we should only allow link modes that are
+supported to be advertised (as we do elsewhere.)  Ensure that we mask
+the MDIO_AN_EEE_ADV value with the capabilities retrieved from the
+MDIO_PCS_EEE_ABLE register.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -1307,11 +1307,16 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
+  */
+ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
+ {
+-	int val = ethtool_adv_to_mmd_eee_adv_t(data->advertised);
++	int cap, adv;
+ 
+-	phy_write_mmd_indirect(phydev, MDIO_AN_EEE_ADV, MDIO_MMD_AN, val);
++	/* Get Supported EEE */
++	cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
++	if (cap < 0)
++		return cap;
+ 
+-	return 0;
++	adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
++
++	return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
+ }
+ EXPORT_SYMBOL(phy_ethtool_set_eee);
+ 
diff --git a/target/linux/mvebu/patches-4.9/404-net-phy-restart-phy-autonegotiation-after-EEE-advert.patch b/target/linux/mvebu/patches-4.9/404-net-phy-restart-phy-autonegotiation-after-EEE-advert.patch
new file mode 100644
index 0000000..5290e71
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/404-net-phy-restart-phy-autonegotiation-after-EEE-advert.patch
@@ -0,0 +1,50 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 5 Jan 2017 12:21:09 +0000
+Subject: [PATCH] net: phy: restart phy autonegotiation after EEE
+ advertisment change
+
+When the EEE advertisment is changed, we should restart autonegotiation
+to update the link partner with the new EEE settings.  Add this trigger
+but only if the advertisment has changed.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -1307,16 +1307,33 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
+  */
+ int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
+ {
+-	int cap, adv;
++	int cap, old_adv, adv, ret;
+ 
+ 	/* Get Supported EEE */
+ 	cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
+ 	if (cap < 0)
+ 		return cap;
+ 
++	old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
++	if (old_adv < 0)
++		return old_adv;
++
+ 	adv = ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
+ 
+-	return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
++	if (old_adv != adv) {
++		ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
++		if (ret < 0)
++			return ret;
++
++		/* Restart autonegotiation so the new modes get sent to the
++		 * link partner.
++		 */
++		ret = phy_restart_aneg(phydev);
++		if (ret < 0)
++			return ret;
++	}
++
++	return 0;
+ }
+ EXPORT_SYMBOL(phy_ethtool_set_eee);
+ 
diff --git a/target/linux/mvebu/patches-4.9/405-net-phy-allow-EEE-with-SGMII-interface-modes.patch b/target/linux/mvebu/patches-4.9/405-net-phy-allow-EEE-with-SGMII-interface-modes.patch
new file mode 100644
index 0000000..db7b004
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/405-net-phy-allow-EEE-with-SGMII-interface-modes.patch
@@ -0,0 +1,21 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 5 Jan 2017 09:34:42 +0000
+Subject: [PATCH] net: phy: allow EEE with SGMII interface modes
+
+As EEE is able to work in SGMII mode as well, add it to the list of
+permissable EEE modes that phy_init_eee() will accept.  This is
+necessary so that EEE can work with an 88E1512 connected in SGMII mode.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -1191,6 +1191,7 @@ int phy_init_eee(struct phy_device *phyd
+ 	if ((phydev->duplex == DUPLEX_FULL) &&
+ 	    ((phydev->interface == PHY_INTERFACE_MODE_MII) ||
+ 	    (phydev->interface == PHY_INTERFACE_MODE_GMII) ||
++	     phydev->interface == PHY_INTERFACE_MODE_SGMII ||
+ 	     phy_interface_is_rgmii(phydev) ||
+ 	     phy_is_internal(phydev))) {
+ 		int eee_lp, eee_cap, eee_adv;
diff --git a/target/linux/mvebu/patches-4.9/406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch b/target/linux/mvebu/patches-4.9/406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch
new file mode 100644
index 0000000..16516c6
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/406-net-phy-improve-phylib-correctness-for-non-autoneg-s.patch
@@ -0,0 +1,199 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 5 Jan 2017 16:32:14 +0000
+Subject: [PATCH] net: phy: improve phylib correctness for non-autoneg
+ settings
+
+phylib has some undesirable behaviour when forcing a link mode through
+ethtool.  phylib uses this code:
+
+	idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
+			features);
+
+to find an index in the settings table.  phy_find_setting() starts at
+index 0, and scans upwards looking for an exact speed and duplex match.
+When it doesn't find it, it returns MAX_NUM_SETTINGS - 1, which is
+10baseT-Half duplex.
+
+phy_find_valid() then scans from the point (and effectively only checks
+one entry) before bailing out, returning MAX_NUM_SETTINGS - 1.
+
+phy_sanitize_settings() then sets ->speed to SPEED_10 and ->duplex to
+DUPLEX_HALF whether or not 10baseT-Half is supported or not.  This goes
+against all the comments against these functions, and 10baseT-Half may
+not even be supported by the hardware.
+
+Rework these functions, introducing a new method of scanning the table.
+There are two modes of lookup that phylib wants: exact, and inexact.
+
+- in exact mode, we return either an exact match or failure
+- in inexact mode, we return an exact match if it exists, a match at
+  the highest speed that is not greater than the requested speed
+  (ignoring duplex), or failing that, the lowest supported speed, or
+  failure.
+
+The biggest difference is that we always check whether the entry is
+supported before further consideration, so all unsupported entries are
+not considered as candidates.
+
+This results in arguably saner behaviour, better matches the comments,
+and is probably what users would expect.
+
+This becomes important as ethernet speeds increase, PHYs exist which do
+not support the 10Mbit speeds, and half-duplex is likely to become
+obsolete - it's already not even an option on 10Gbit and faster links.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -160,7 +160,9 @@ struct phy_setting {
+ 	u32 setting;
+ };
+ 
+-/* A mapping of all SUPPORTED settings to speed/duplex */
++/* A mapping of all SUPPORTED settings to speed/duplex.  This table
++ * must be grouped by speed and sorted in descending match priority
++ * - iow, descending speed. */
+ static const struct phy_setting settings[] = {
+ 	{
+ 		.speed = SPEED_10000,
+@@ -219,45 +221,70 @@ static const struct phy_setting settings
+ 	},
+ };
+ 
+-#define MAX_NUM_SETTINGS ARRAY_SIZE(settings)
+-
+ /**
+- * phy_find_setting - find a PHY settings array entry that matches speed & duplex
++ * phy_lookup_setting - lookup a PHY setting
+  * @speed: speed to match
+  * @duplex: duplex to match
++ * @feature: allowed link modes
++ * @exact: an exact match is required
++ *
++ * Search the settings array for a setting that matches the speed and
++ * duplex, and which is supported.
++ *
++ * If @exact is unset, either an exact match or %NULL for no match will
++ * be returned.
+  *
+- * Description: Searches the settings array for the setting which
+- *   matches the desired speed and duplex, and returns the index
+- *   of that setting.  Returns the index of the last setting if
+- *   none of the others match.
++ * If @exact is set, an exact match, the fastest supported setting at
++ * or below the specified speed, the slowest supported setting, or if
++ * they all fail, %NULL will be returned.
+  */
+-static inline unsigned int phy_find_setting(int speed, int duplex)
++static const struct phy_setting *
++phy_lookup_setting(int speed, int duplex, u32 features, bool exact)
+ {
+-	unsigned int idx = 0;
++	const struct phy_setting *p, *match = NULL, *last = NULL;
++	int i;
+ 
+-	while (idx < ARRAY_SIZE(settings) &&
+-	       (settings[idx].speed != speed || settings[idx].duplex != duplex))
+-		idx++;
++	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
++		if (p->setting & features) {
++			last = p;
++			if (p->speed == speed && p->duplex == duplex) {
++				/* Exact match for speed and duplex */
++				match = p;
++				break;
++			} else if (!exact) {
++				if (!match && p->speed <= speed)
++					/* Candidate */
++					match = p;
++
++				if (p->speed < speed)
++					break;
++			}
++		}
++	}
+ 
+-	return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
++	if (!match && !exact)
++		match = last;
++
++	return match;
+ }
+ 
+ /**
+- * phy_find_valid - find a PHY setting that matches the requested features mask
+- * @idx: The first index in settings[] to search
+- * @features: A mask of the valid settings
++ * phy_find_valid - find a PHY setting that matches the requested parameters
++ * @speed: desired speed
++ * @duplex: desired duplex
++ * @supported: mask of supported link modes
+  *
+- * Description: Returns the index of the first valid setting less
+- *   than or equal to the one pointed to by idx, as determined by
+- *   the mask in features.  Returns the index of the last setting
+- *   if nothing else matches.
++ * Locate a supported phy setting that is, in priority order:
++ * - an exact match for the specified speed and duplex mode
++ * - a match for the specified speed, or slower speed
++ * - the slowest supported speed
++ * Returns the matched phy_setting entry, or %NULL if no supported phy
++ * settings were found.
+  */
+-static inline unsigned int phy_find_valid(unsigned int idx, u32 features)
++static const struct phy_setting *
++phy_find_valid(int speed, int duplex, u32 supported)
+ {
+-	while (idx < MAX_NUM_SETTINGS && !(settings[idx].setting & features))
+-		idx++;
+-
+-	return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1;
++	return phy_lookup_setting(speed, duplex, supported, false);
+ }
+ 
+ /**
+@@ -271,12 +298,7 @@ static inline unsigned int phy_find_vali
+  */
+ static inline bool phy_check_valid(int speed, int duplex, u32 features)
+ {
+-	unsigned int idx;
+-
+-	idx = phy_find_valid(phy_find_setting(speed, duplex), features);
+-
+-	return settings[idx].speed == speed && settings[idx].duplex == duplex &&
+-		(settings[idx].setting & features);
++	return !!phy_lookup_setting(speed, duplex, features, true);
+ }
+ 
+ /**
+@@ -289,18 +311,22 @@ static inline bool phy_check_valid(int s
+  */
+ static void phy_sanitize_settings(struct phy_device *phydev)
+ {
++	const struct phy_setting *setting;
+ 	u32 features = phydev->supported;
+-	unsigned int idx;
+ 
+ 	/* Sanitize settings based on PHY capabilities */
+ 	if ((features & SUPPORTED_Autoneg) == 0)
+ 		phydev->autoneg = AUTONEG_DISABLE;
+ 
+-	idx = phy_find_valid(phy_find_setting(phydev->speed, phydev->duplex),
+-			features);
+-
+-	phydev->speed = settings[idx].speed;
+-	phydev->duplex = settings[idx].duplex;
++	setting = phy_find_valid(phydev->speed, phydev->duplex, features);
++	if (setting) {
++		phydev->speed = setting->speed;
++		phydev->duplex = setting->duplex;
++	} else {
++		/* We failed to find anything (no supported speeds?) */
++		phydev->speed = SPEED_UNKNOWN;
++		phydev->duplex = DUPLEX_UNKNOWN;
++	}
+ }
+ 
+ /**
diff --git a/target/linux/mvebu/patches-4.9/407-net-phy-add-802.3-clause-45-support-to-phylib.patch b/target/linux/mvebu/patches-4.9/407-net-phy-add-802.3-clause-45-support-to-phylib.patch
new file mode 100644
index 0000000..8e983cc
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/407-net-phy-add-802.3-clause-45-support-to-phylib.patch
@@ -0,0 +1,323 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 29 Dec 2016 11:03:09 +0000
+Subject: [PATCH] net: phy: add 802.3 clause 45 support to phylib
+
+Add generic helpers for 802.3 clause 45 PHYs for >= 10Gbps support.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+ create mode 100644 drivers/net/phy/phy-c45.c
+
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -1,7 +1,7 @@
+ # Makefile for Linux PHY drivers and MDIO bus drivers
+ 
+ libphy-y			:= phy.o phy_device.o mdio_bus.o mdio_device.o \
+-				   phy-core.o
++				   phy-c45.o phy-core.o
+ libphy-$(CONFIG_SWPHY)		+= swphy.o
+ 
+ obj-$(CONFIG_MDIO_BOARDINFO)	+= mdio-boardinfo.o
+--- /dev/null
++++ b/drivers/net/phy/phy-c45.c
+@@ -0,0 +1,234 @@
++/*
++ * Clause 45 PHY support
++ */
++#include <linux/ethtool.h>
++#include <linux/export.h>
++#include <linux/mdio.h>
++#include <linux/mii.h>
++#include <linux/phy.h>
++
++/**
++ * genphy_c45_setup_forced - configures a forced speed
++ * @phydev: target phy_device struct
++ */
++int genphy_c45_pma_setup_forced(struct phy_device *phydev)
++{
++	int ctrl1, ctrl2, ret;
++
++	/* Half duplex is not supported */
++	if (phydev->duplex != DUPLEX_FULL)
++		return -EINVAL;
++
++	ctrl1 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
++	if (ctrl1 < 0)
++		return ctrl1;
++
++	ctrl2 = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2);
++	if (ctrl2 < 0)
++		return ctrl2;
++
++	ctrl1 &= ~MDIO_CTRL1_SPEEDSEL;
++	/* PMA/PMD type selection is 1.7.5:0 not 1.7.3:0.  See 45.2.1.6.1. */
++	ctrl2 &= ~(MDIO_PMA_CTRL2_TYPE | 0x30);
++
++	switch (phydev->speed) {
++	case SPEED_10:
++		ctrl2 |= MDIO_PMA_CTRL2_10BT;
++		break;
++	case SPEED_100:
++		ctrl1 |= MDIO_PMA_CTRL1_SPEED100;
++		ctrl2 |= MDIO_PMA_CTRL2_100BTX;
++		break;
++	case SPEED_1000:
++		ctrl1 |= MDIO_PMA_CTRL1_SPEED1000;
++		/* Assume 1000base-T */
++		ctrl2 |= MDIO_PMA_CTRL2_1000BT;
++		break;
++	case SPEED_10000:
++		ctrl1 |= MDIO_CTRL1_SPEED10G;
++		/* Assume 10Gbase-T */
++		ctrl2 |= MDIO_PMA_CTRL2_10GBT;
++		break;
++	default:
++		return -EINVAL;
++	}
++
++	ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1, ctrl1);
++	if (ret < 0)
++		return ret;
++
++	return phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL2, ctrl2);
++}
++EXPORT_SYMBOL_GPL(genphy_c45_pma_setup_forced);
++
++/**
++ * genphy_c45_an_disable_aneg - disable auto-negotiation
++ * @phydev: target phy_device struct
++ *
++ * Disable auto-negotiation in the Clause 45 PHY. The link parameters
++ * parameters are controlled through the PMA/PMD MMD registers.
++ *
++ * Returns zero on success, negative errno code on failure.
++ */
++int genphy_c45_an_disable_aneg(struct phy_device *phydev)
++{
++	int val;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
++	if (val < 0)
++		return val;
++
++	val &= ~(MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART);
++
++	return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
++}
++EXPORT_SYMBOL_GPL(genphy_c45_an_disable_aneg);
++
++/**
++ * genphy_c45_restart_aneg - Enable and restart auto-negotiation
++ * @phydev: target phy_device struct
++ *
++ * This assumes that the auto-negotiation MMD is present.
++ *
++ * Enable and restart auto-negotiation.
++ */
++int genphy_c45_restart_aneg(struct phy_device *phydev)
++{
++	int val;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
++	if (val < 0)
++		return val;
++
++	val |= MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_RESTART;
++
++	return phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, val);
++}
++EXPORT_SYMBOL_GPL(genphy_c45_restart_aneg);
++
++/**
++ * genphy_c45_aneg_done - return auto-negotiation complete status
++ * @phydev: target phy_device struct
++ *
++ * This assumes that the auto-negotiation MMD is present.
++ *
++ * Reads the status register from the auto-negotiation MMD, returning:
++ * - positive if auto-negotiation is complete
++ * - negative errno code on error
++ * - zero otherwise
++ */
++int genphy_c45_aneg_done(struct phy_device *phydev)
++{
++	int val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
++
++	return val < 0 ? val : val & MDIO_AN_STAT1_COMPLETE ? 1 : 0;
++}
++EXPORT_SYMBOL_GPL(genphy_c45_aneg_done);
++
++/**
++ * genphy_c45_read_link - read the overall link status from the MMDs
++ * @phydev: target phy_device struct
++ * @mmd_mask: MMDs to read status from
++ *
++ * Read the link status from the specified MMDs, and if they all indicate
++ * that the link is up, return positive.  If an error is encountered,
++ * a negative errno will be returned, otherwise zero.
++ */
++int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask)
++{
++	int val, devad;
++	bool link = true;
++
++	while (mmd_mask) {
++		devad = __ffs(mmd_mask);
++		mmd_mask &= ~BIT(devad);
++
++		val = phy_read_mmd(phydev, devad, MDIO_STAT1);
++		if (val < 0)
++			return val;
++
++		/* Read twice because link state is latched and a
++		 * read moves the current state into the register
++		 */
++		val = phy_read_mmd(phydev, devad, MDIO_STAT1);
++		if (val < 0)
++			return val;
++
++		if (!(val & MDIO_STAT1_LSTATUS))
++			link = false;
++	}
++
++	return link;
++}
++EXPORT_SYMBOL_GPL(genphy_c45_read_link);
++
++/**
++ * genphy_c45_read_lpa - read the link partner advertisment and pause
++ * @phydev: target phy_device struct
++ *
++ * Read the Clause 45 defined base (7.19) and 10G (7.33) status registers,
++ * filling in the link partner advertisment, pause and asym_pause members
++ * in @phydev.  This assumes that the auto-negotiation MMD is present, and
++ * the backplane bit (7.48.0) is clear.  Clause 45 PHY drivers are expected
++ * to fill in the remainder of the link partner advert from vendor registers.
++ */
++int genphy_c45_read_lpa(struct phy_device *phydev)
++{
++	int val;
++
++	/* Read the link partner's base page advertisment */
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
++	if (val < 0)
++		return val;
++
++	phydev->lp_advertising = mii_lpa_to_ethtool_lpa_t(val);
++	phydev->pause = val & LPA_PAUSE_CAP ? 1 : 0;
++	phydev->asym_pause = val & LPA_PAUSE_ASYM ? 1 : 0;
++
++	/* Read the link partner's 10G advertisment */
++	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
++	if (val < 0)
++		return val;
++
++	if (val & MDIO_AN_10GBT_STAT_LP10G)
++		phydev->lp_advertising |= ADVERTISED_10000baseT_Full;
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(genphy_c45_read_lpa);
++
++/**
++ * genphy_c45_read_pma - read link speed etc from PMA
++ * @phydev: target phy_device struct
++ */
++int genphy_c45_read_pma(struct phy_device *phydev)
++{
++	int val;
++
++	val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1);
++	if (val < 0)
++		return val;
++
++	switch (val & MDIO_CTRL1_SPEEDSEL) {
++	case 0:
++		phydev->speed = SPEED_10;
++		break;
++	case MDIO_PMA_CTRL1_SPEED100:
++		phydev->speed = SPEED_100;
++		break;
++	case MDIO_PMA_CTRL1_SPEED1000:
++		phydev->speed = SPEED_1000;
++		break;
++	case MDIO_CTRL1_SPEED10G:
++		phydev->speed = SPEED_10000;
++		break;
++	default:
++		phydev->speed = SPEED_UNKNOWN;
++		break;
++	}
++
++	phydev->duplex = DUPLEX_FULL;
++
++	return 0;
++}
++EXPORT_SYMBOL_GPL(genphy_c45_read_pma);
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1390,27 +1390,19 @@ EXPORT_SYMBOL(genphy_read_status);
+ 
+ static int gen10g_read_status(struct phy_device *phydev)
+ {
+-	int devad, reg;
+ 	u32 mmd_mask = phydev->c45_ids.devices_in_package;
+-
+-	phydev->link = 1;
++	int ret;
+ 
+ 	/* For now just lie and say it's 10G all the time */
+ 	phydev->speed = SPEED_10000;
+ 	phydev->duplex = DUPLEX_FULL;
+ 
+-	for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) {
+-		if (!(mmd_mask & 1))
+-			continue;
+-
+-		/* Read twice because link state is latched and a
+-		 * read moves the current state into the register
+-		 */
+-		phy_read_mmd(phydev, devad, MDIO_STAT1);
+-		reg = phy_read_mmd(phydev, devad, MDIO_STAT1);
+-		if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
+-			phydev->link = 0;
+-	}
++	/* Avoid reading the vendor MMDs */
++	mmd_mask &= ~(BIT(MDIO_MMD_VEND1) | BIT(MDIO_MMD_VEND2));
++
++	ret = genphy_c45_read_link(phydev, mmd_mask);
++
++	phydev->link = ret > 0 ? 1 : 0;
+ 
+ 	return 0;
+ }
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -807,6 +807,8 @@ static inline const char *phydev_name(co
+ void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
+ 	__printf(2, 3);
+ void phy_attached_info(struct phy_device *phydev);
++
++/* Clause 22 PHY */
+ int genphy_config_init(struct phy_device *phydev);
+ int genphy_setup_forced(struct phy_device *phydev);
+ int genphy_restart_aneg(struct phy_device *phydev);
+@@ -817,6 +819,16 @@ int genphy_read_status(struct phy_device
+ int genphy_suspend(struct phy_device *phydev);
+ int genphy_resume(struct phy_device *phydev);
+ int genphy_soft_reset(struct phy_device *phydev);
++
++/* Clause 45 PHY */
++int genphy_c45_restart_aneg(struct phy_device *phydev);
++int genphy_c45_aneg_done(struct phy_device *phydev);
++int genphy_c45_read_link(struct phy_device *phydev, u32 mmd_mask);
++int genphy_c45_read_lpa(struct phy_device *phydev);
++int genphy_c45_read_pma(struct phy_device *phydev);
++int genphy_c45_pma_setup_forced(struct phy_device *phydev);
++int genphy_c45_an_disable_aneg(struct phy_device *phydev);
++
+ void phy_driver_unregister(struct phy_driver *drv);
+ void phy_drivers_unregister(struct phy_driver *drv, int n);
+ int phy_driver_register(struct phy_driver *new_driver, struct module *owner);
diff --git a/target/linux/mvebu/patches-4.9/408-net-phy-hook-up-clause-45-autonegotiation-restart.patch b/target/linux/mvebu/patches-4.9/408-net-phy-hook-up-clause-45-autonegotiation-restart.patch
new file mode 100644
index 0000000..7a16bd5
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/408-net-phy-hook-up-clause-45-autonegotiation-restart.patch
@@ -0,0 +1,54 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Fri, 13 Jan 2017 11:17:30 +0000
+Subject: [PATCH] net: phy: hook up clause 45 autonegotiation restart
+
+genphy_restart_aneg() can only restart autonegotiation on clause 22
+PHYs.  Add a phy_restart_aneg() function which selects between the
+clause 22 and clause 45 restart functionality depending on the PHY
+type.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -134,6 +134,24 @@ static int phy_config_interrupt(struct p
+ 	return 0;
+ }
+ 
++/**
++ * phy_restart_aneg - restart auto-negotiation
++ * @phydev: target phy_device struct
++ *
++ * Restart the autonegotiation on @phydev.  Returns >= 0 on success or
++ * negative errno on error.
++ */
++static int phy_restart_aneg(struct phy_device *phydev)
++{
++	int ret;
++
++	if (phydev->is_c45)
++		ret = genphy_c45_restart_aneg(phydev);
++	else
++		ret = genphy_restart_aneg(phydev);
++
++	return ret;
++}
+ 
+ /**
+  * phy_aneg_done - return auto-negotiation status
+@@ -1403,3 +1421,14 @@ int phy_ethtool_set_link_ksettings(struc
+ 	return phy_ethtool_ksettings_set(phydev, cmd);
+ }
+ EXPORT_SYMBOL(phy_ethtool_set_link_ksettings);
++
++int phy_ethtool_nway_reset(struct net_device *ndev)
++{
++	struct phy_device *phydev = ndev->phydev;
++
++	if (!phydev)
++		return -ENODEV;
++
++	return phy_restart_aneg(phydev);
++}
++EXPORT_SYMBOL(phy_ethtool_nway_reset);
diff --git a/target/linux/mvebu/patches-4.9/409-net-phy-don-t-double-read-clause-45-status-register.patch b/target/linux/mvebu/patches-4.9/409-net-phy-don-t-double-read-clause-45-status-register.patch
new file mode 100644
index 0000000..ee53fe0
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/409-net-phy-don-t-double-read-clause-45-status-register.patch
@@ -0,0 +1,35 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 5 Jan 2017 00:23:40 +0000
+Subject: [PATCH] net: phy: don't double-read clause 45 status register
+
+One of the design decisions behind the link status bit in the status
+register is that it latches low on link loss.  This is so that link loss
+events are not missed.  Double-reading the status register means that we
+always read the current state of the link, clearing any link loss event.
+
+This can cause problems - for example, if the link has negotiated a
+different set of operating parameters, these will not be communicated
+to the MAC as the PHY state machine will still think that the link has
+remained active.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy-c45.c
++++ b/drivers/net/phy/phy-c45.c
+@@ -143,12 +143,9 @@ int genphy_c45_read_link(struct phy_devi
+ 		devad = __ffs(mmd_mask);
+ 		mmd_mask &= ~BIT(devad);
+ 
+-		val = phy_read_mmd(phydev, devad, MDIO_STAT1);
+-		if (val < 0)
+-			return val;
+-
+-		/* Read twice because link state is latched and a
+-		 * read moves the current state into the register
++		/* The link state is latched low so that momentary link
++		 * drops can be detected.  Do not double-read the status
++		 * register if the link is down.
+ 		 */
+ 		val = phy_read_mmd(phydev, devad, MDIO_STAT1);
+ 		if (val < 0)
diff --git a/target/linux/mvebu/patches-4.9/410-net-phy-allow-settings-table-to-support-more-than-32.patch b/target/linux/mvebu/patches-4.9/410-net-phy-allow-settings-table-to-support-more-than-32.patch
new file mode 100644
index 0000000..b6d8505
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/410-net-phy-allow-settings-table-to-support-more-than-32.patch
@@ -0,0 +1,142 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 5 Jan 2017 16:32:14 +0000
+Subject: [PATCH] net: phy: allow settings table to support more than 32
+ link modes
+
+Allow the phy settings table to support more than 32 link modes by
+switching to the ethtool link mode bit number representation, rather
+than storing the mask.  This will allow phylink and other ethtool
+code to share the settings table to look up settings.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -175,7 +175,7 @@ static inline int phy_aneg_done(struct p
+ struct phy_setting {
+ 	int speed;
+ 	int duplex;
+-	u32 setting;
++	int bit;
+ };
+ 
+ /* A mapping of all SUPPORTED settings to speed/duplex.  This table
+@@ -185,57 +185,57 @@ static const struct phy_setting settings
+ 	{
+ 		.speed = SPEED_10000,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_10000baseKR_Full,
++		.bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_10000,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_10000baseKX4_Full,
++		.bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_10000,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_10000baseT_Full,
++		.bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_2500,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_2500baseX_Full,
++		.bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_1000,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_1000baseKX_Full,
++		.bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_1000,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_1000baseT_Full,
++		.bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_1000,
+ 		.duplex = DUPLEX_HALF,
+-		.setting = SUPPORTED_1000baseT_Half,
++		.bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_100,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_100baseT_Full,
++		.bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_100,
+ 		.duplex = DUPLEX_HALF,
+-		.setting = SUPPORTED_100baseT_Half,
++		.bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_10,
+ 		.duplex = DUPLEX_FULL,
+-		.setting = SUPPORTED_10baseT_Full,
++		.bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+ 	},
+ 	{
+ 		.speed = SPEED_10,
+ 		.duplex = DUPLEX_HALF,
+-		.setting = SUPPORTED_10baseT_Half,
++		.bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+ 	},
+ };
+ 
+@@ -243,7 +243,8 @@ static const struct phy_setting settings
+  * phy_lookup_setting - lookup a PHY setting
+  * @speed: speed to match
+  * @duplex: duplex to match
+- * @feature: allowed link modes
++ * @mask: allowed link modes
++ * @maxbit: bit size of link modes
+  * @exact: an exact match is required
+  *
+  * Search the settings array for a setting that matches the speed and
+@@ -257,13 +258,14 @@ static const struct phy_setting settings
+  * they all fail, %NULL will be returned.
+  */
+ static const struct phy_setting *
+-phy_lookup_setting(int speed, int duplex, u32 features, bool exact)
++phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
++		   size_t maxbit, bool exact)
+ {
+ 	const struct phy_setting *p, *match = NULL, *last = NULL;
+ 	int i;
+ 
+ 	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
+-		if (p->setting & features) {
++		if (p->bit < maxbit && test_bit(p->bit, mask)) {
+ 			last = p;
+ 			if (p->speed == speed && p->duplex == duplex) {
+ 				/* Exact match for speed and duplex */
+@@ -302,7 +304,9 @@ phy_lookup_setting(int speed, int duplex
+ static const struct phy_setting *
+ phy_find_valid(int speed, int duplex, u32 supported)
+ {
+-	return phy_lookup_setting(speed, duplex, supported, false);
++	unsigned long mask = supported;
++
++	return phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, false);
+ }
+ 
+ /**
+@@ -316,7 +320,9 @@ phy_find_valid(int speed, int duplex, u3
+  */
+ static inline bool phy_check_valid(int speed, int duplex, u32 features)
+ {
+-	return !!phy_lookup_setting(speed, duplex, features, true);
++	unsigned long mask = features;
++
++	return !!phy_lookup_setting(speed, duplex, &mask, BITS_PER_LONG, true);
+ }
+ 
+ /**
diff --git a/target/linux/mvebu/patches-4.9/411-net-phy-split-out-PHY-speed-and-duplex-string-genera.patch b/target/linux/mvebu/patches-4.9/411-net-phy-split-out-PHY-speed-and-duplex-string-genera.patch
new file mode 100644
index 0000000..4111b00
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/411-net-phy-split-out-PHY-speed-and-duplex-string-genera.patch
@@ -0,0 +1,103 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Mon, 2 Jan 2017 17:52:18 +0000
+Subject: [PATCH] net: phy: split out PHY speed and duplex string
+ generation
+
+Other code would like to make use of this, so make the speed and duplex
+string generation visible, and place it in a separate file.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -38,26 +38,6 @@
+ 
+ #include <asm/irq.h>
+ 
+-static const char *phy_speed_to_str(int speed)
+-{
+-	switch (speed) {
+-	case SPEED_10:
+-		return "10Mbps";
+-	case SPEED_100:
+-		return "100Mbps";
+-	case SPEED_1000:
+-		return "1Gbps";
+-	case SPEED_2500:
+-		return "2.5Gbps";
+-	case SPEED_10000:
+-		return "10Gbps";
+-	case SPEED_UNKNOWN:
+-		return "Unknown";
+-	default:
+-		return "Unsupported (update phy.c)";
+-	}
+-}
+-
+ #define PHY_STATE_STR(_state)			\
+ 	case PHY_##_state:			\
+ 		return __stringify(_state);	\
+@@ -93,7 +73,7 @@ void phy_print_status(struct phy_device
+ 		netdev_info(phydev->attached_dev,
+ 			"Link is Up - %s/%s - flow control %s\n",
+ 			phy_speed_to_str(phydev->speed),
+-			DUPLEX_FULL == phydev->duplex ? "Full" : "Half",
++			phy_duplex_to_str(phydev->duplex),
+ 			phydev->pause ? "rx/tx" : "off");
+ 	} else	{
+ 		netdev_info(phydev->attached_dev, "Link is Down\n");
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -9,6 +9,39 @@
+ #include <linux/export.h>
+ #include <linux/phy.h>
+ 
++const char *phy_speed_to_str(int speed)
++{
++	switch (speed) {
++	case SPEED_10:
++		return "10Mbps";
++	case SPEED_100:
++		return "100Mbps";
++	case SPEED_1000:
++		return "1Gbps";
++	case SPEED_2500:
++		return "2.5Gbps";
++	case SPEED_10000:
++		return "10Gbps";
++	case SPEED_UNKNOWN:
++		return "Unknown";
++	default:
++		return "Unsupported (update phy-core.c)";
++	}
++}
++EXPORT_SYMBOL_GPL(phy_speed_to_str);
++
++const char *phy_duplex_to_str(unsigned int duplex)
++{
++	if (duplex == DUPLEX_HALF)
++		return "Half";
++	if (duplex == DUPLEX_FULL)
++		return "Full";
++	if (duplex == DUPLEX_UNKNOWN)
++		return "Unknown";
++	return "Unsupported (update phy-core.c)";
++}
++EXPORT_SYMBOL_GPL(phy_duplex_to_str);
++
+ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
+ 				    int addr)
+ {
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -642,6 +642,9 @@ struct phy_fixup {
+ 	int (*run)(struct phy_device *phydev);
+ };
+ 
++const char *phy_speed_to_str(int speed);
++const char *phy_duplex_to_str(unsigned int duplex);
++
+ /**
+  * phy_read_mmd - Convenience function for reading a register
+  * from an MMD on a given PHY.
diff --git a/target/linux/mvebu/patches-4.9/412-net-phy-move-phy_lookup_setting-and-guts-of-phy_supp.patch b/target/linux/mvebu/patches-4.9/412-net-phy-move-phy_lookup_setting-and-guts-of-phy_supp.patch
new file mode 100644
index 0000000..02a8fa5
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/412-net-phy-move-phy_lookup_setting-and-guts-of-phy_supp.patch
@@ -0,0 +1,329 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Thu, 5 Jan 2017 16:47:39 +0000
+Subject: [PATCH] net: phy: move phy_lookup_setting() and guts of
+ phy_supported_speeds() to phy-core
+
+phy_lookup_setting() provides useful functionality in ethtool code
+outside phylib.  Move it to phy-core and allow it to be re-used (eg,
+in phylink) rather than duplicated elsewhere.  Note that this supports
+the larger linkmode space.
+
+As we move the phy settings table, we also need to move the guts of
+phy_supported_speeds() as well.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -149,125 +149,6 @@ static inline int phy_aneg_done(struct p
+ 	return genphy_aneg_done(phydev);
+ }
+ 
+-/* A structure for mapping a particular speed and duplex
+- * combination to a particular SUPPORTED and ADVERTISED value
+- */
+-struct phy_setting {
+-	int speed;
+-	int duplex;
+-	int bit;
+-};
+-
+-/* A mapping of all SUPPORTED settings to speed/duplex.  This table
+- * must be grouped by speed and sorted in descending match priority
+- * - iow, descending speed. */
+-static const struct phy_setting settings[] = {
+-	{
+-		.speed = SPEED_10000,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_10000,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_10000,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_2500,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_1000,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_1000,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_1000,
+-		.duplex = DUPLEX_HALF,
+-		.bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
+-	},
+-	{
+-		.speed = SPEED_100,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_100,
+-		.duplex = DUPLEX_HALF,
+-		.bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT,
+-	},
+-	{
+-		.speed = SPEED_10,
+-		.duplex = DUPLEX_FULL,
+-		.bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT,
+-	},
+-	{
+-		.speed = SPEED_10,
+-		.duplex = DUPLEX_HALF,
+-		.bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT,
+-	},
+-};
+-
+-/**
+- * phy_lookup_setting - lookup a PHY setting
+- * @speed: speed to match
+- * @duplex: duplex to match
+- * @mask: allowed link modes
+- * @maxbit: bit size of link modes
+- * @exact: an exact match is required
+- *
+- * Search the settings array for a setting that matches the speed and
+- * duplex, and which is supported.
+- *
+- * If @exact is unset, either an exact match or %NULL for no match will
+- * be returned.
+- *
+- * If @exact is set, an exact match, the fastest supported setting at
+- * or below the specified speed, the slowest supported setting, or if
+- * they all fail, %NULL will be returned.
+- */
+-static const struct phy_setting *
+-phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
+-		   size_t maxbit, bool exact)
+-{
+-	const struct phy_setting *p, *match = NULL, *last = NULL;
+-	int i;
+-
+-	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
+-		if (p->bit < maxbit && test_bit(p->bit, mask)) {
+-			last = p;
+-			if (p->speed == speed && p->duplex == duplex) {
+-				/* Exact match for speed and duplex */
+-				match = p;
+-				break;
+-			} else if (!exact) {
+-				if (!match && p->speed <= speed)
+-					/* Candidate */
+-					match = p;
+-
+-				if (p->speed < speed)
+-					break;
+-			}
+-		}
+-	}
+-
+-	if (!match && !exact)
+-		match = last;
+-
+-	return match;
+-}
+-
+ /**
+  * phy_find_valid - find a PHY setting that matches the requested parameters
+  * @speed: desired speed
+@@ -290,6 +171,25 @@ phy_find_valid(int speed, int duplex, u3
+ }
+ 
+ /**
++ * phy_supported_speeds - return all speeds currently supported by a phy device
++ * @phy: The phy device to return supported speeds of.
++ * @speeds: buffer to store supported speeds in.
++ * @size:   size of speeds buffer.
++ *
++ * Description: Returns the number of supported speeds, and fills the speeds
++ * buffer with the supported speeds. If speeds buffer is too small to contain
++ * all currently supported speeds, will return as many speeds as can fit.
++ */
++unsigned int phy_supported_speeds(struct phy_device *phy,
++				  unsigned int *speeds,
++				  unsigned int size)
++{
++	unsigned long supported = phy->supported;
++
++	return phy_speeds(speeds, size, &supported, BITS_PER_LONG);
++}
++
++/**
+  * phy_check_valid - check if there is a valid PHY setting which matches
+  *		     speed, duplex, and feature mask
+  * @speed: speed to match
+--- a/drivers/net/phy/phy-core.c
++++ b/drivers/net/phy/phy-core.c
+@@ -42,6 +42,132 @@ const char *phy_duplex_to_str(unsigned i
+ }
+ EXPORT_SYMBOL_GPL(phy_duplex_to_str);
+ 
++/* A mapping of all SUPPORTED settings to speed/duplex.  This table
++ * must be grouped by speed and sorted in descending match priority
++ * - iow, descending speed. */
++static const struct phy_setting settings[] = {
++	{
++		.speed = SPEED_10000,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
++	},
++	{
++		.speed = SPEED_10000,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
++	},
++	{
++		.speed = SPEED_10000,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
++	},
++	{
++		.speed = SPEED_2500,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT,
++	},
++	{
++		.speed = SPEED_1000,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
++	},
++	{
++		.speed = SPEED_1000,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
++	},
++	{
++		.speed = SPEED_1000,
++		.duplex = DUPLEX_HALF,
++		.bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
++	},
++	{
++		.speed = SPEED_100,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT,
++	},
++	{
++		.speed = SPEED_100,
++		.duplex = DUPLEX_HALF,
++		.bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT,
++	},
++	{
++		.speed = SPEED_10,
++		.duplex = DUPLEX_FULL,
++		.bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT,
++	},
++	{
++		.speed = SPEED_10,
++		.duplex = DUPLEX_HALF,
++		.bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT,
++	},
++};
++
++/**
++ * phy_lookup_setting - lookup a PHY setting
++ * @speed: speed to match
++ * @duplex: duplex to match
++ * @mask: allowed link modes
++ * @maxbit: bit size of link modes
++ * @exact: an exact match is required
++ *
++ * Search the settings array for a setting that matches the speed and
++ * duplex, and which is supported.
++ *
++ * If @exact is unset, either an exact match or %NULL for no match will
++ * be returned.
++ *
++ * If @exact is set, an exact match, the fastest supported setting at
++ * or below the specified speed, the slowest supported setting, or if
++ * they all fail, %NULL will be returned.
++ */
++const struct phy_setting *
++phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
++		   size_t maxbit, bool exact)
++{
++	const struct phy_setting *p, *match = NULL, *last = NULL;
++	int i;
++
++	for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) {
++		if (p->bit < maxbit && test_bit(p->bit, mask)) {
++			last = p;
++			if (p->speed == speed && p->duplex == duplex) {
++				/* Exact match for speed and duplex */
++				match = p;
++				break;
++			} else if (!exact) {
++				if (!match && p->speed <= speed)
++					/* Candidate */
++					match = p;
++
++				if (p->speed < speed)
++					break;
++			}
++		}
++	}
++
++	if (!match && !exact)
++		match = last;
++
++	return match;
++}
++EXPORT_SYMBOL_GPL(phy_lookup_setting);
++
++size_t phy_speeds(unsigned int *speeds, size_t size,
++		  unsigned long *mask, size_t maxbit)
++{
++	size_t count;
++	int i;
++
++	for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++)
++		if (settings[i].bit < maxbit &&
++		    test_bit(settings[i].bit, mask) &&
++		    (count == 0 || speeds[count - 1] != settings[i].speed))
++			speeds[count++] = settings[i].speed;
++
++	return count;
++}
++
+ static inline void mmd_phy_indirect(struct mii_bus *bus, int prtad, int devad,
+ 				    int addr)
+ {
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -645,6 +645,21 @@ struct phy_fixup {
+ const char *phy_speed_to_str(int speed);
+ const char *phy_duplex_to_str(unsigned int duplex);
+ 
++/* A structure for mapping a particular speed and duplex
++ * combination to a particular SUPPORTED and ADVERTISED value
++ */
++struct phy_setting {
++	u32 speed;
++	u8 duplex;
++	u8 bit;
++};
++
++const struct phy_setting *
++phy_lookup_setting(int speed, int duplex, const unsigned long *mask,
++		   size_t maxbit, bool exact);
++size_t phy_speeds(unsigned int *speeds, size_t size,
++		  unsigned long *mask, size_t maxbit);
++
+ /**
+  * phy_read_mmd - Convenience function for reading a register
+  * from an MMD on a given PHY.
diff --git a/target/linux/mvebu/patches-4.9/413-phy-export-phy_start_machine-for-phylink.patch b/target/linux/mvebu/patches-4.9/413-phy-export-phy_start_machine-for-phylink.patch
new file mode 100644
index 0000000..93f979a
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/413-phy-export-phy_start_machine-for-phylink.patch
@@ -0,0 +1,22 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Fri, 16 Oct 2015 12:18:41 +0100
+Subject: [PATCH] phy: export phy_start_machine() for phylink
+
+phylink will need phy_start_machine exported, so lets export it as a
+GPL symbol.  Documentation/networking/phy.txt indicates that this
+should be a PHY API function.
+
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/phy.c
++++ b/drivers/net/phy/phy.c
+@@ -580,6 +580,7 @@ void phy_start_machine(struct phy_device
+ {
+ 	queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, HZ);
+ }
++EXPORT_SYMBOL_GPL(phy_start_machine);
+ 
+ /**
+  * phy_trigger_machine - trigger the state machine to run
diff --git a/target/linux/mvebu/patches-4.9/414-phy-add-I2C-mdio-bus.patch b/target/linux/mvebu/patches-4.9/414-phy-add-I2C-mdio-bus.patch
new file mode 100644
index 0000000..c389817
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/414-phy-add-I2C-mdio-bus.patch
@@ -0,0 +1,180 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Fri, 25 Sep 2015 17:43:52 +0100
+Subject: [PATCH] phy: add I2C mdio bus
+
+Add an I2C MDIO bus bridge library, to allow phylib to access PHYs which
+are connected to an I2C bus instead of the more conventional MDIO bus.
+Such PHYs can be found in SFP adapters and SFF modules.
+
+Since PHYs appear at I2C bus address 0x40..0x5f, and 0x50/0x51 are
+reserved for SFP EEPROMs/diagnostics, we must not allow the MDIO bus
+to access these I2C addresses.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+ create mode 100644 drivers/net/phy/mdio-i2c.c
+ create mode 100644 drivers/net/phy/mdio-i2c.h
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -105,6 +105,16 @@ config MDIO_HISI_FEMAC
+ 	  This module provides a driver for the MDIO busses found in the
+ 	  Hisilicon SoC that have an Fast Ethernet MAC.
+ 
++config MDIO_I2C
++	tristate
++	depends on I2C
++	help
++	  Support I2C based PHYs.  This provides a MDIO bus bridged
++	  to I2C to allow PHYs connected in I2C mode to be accessed
++	  using the existing infrastructure.
++
++	  This is library mode.
++
+ config MDIO_MOXART
+         tristate "MOXA ART MDIO interface support"
+         depends on ARCH_MOXART
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -33,6 +33,7 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += md
+ obj-$(CONFIG_MDIO_CAVIUM)	+= mdio-cavium.o
+ obj-$(CONFIG_MDIO_GPIO)		+= mdio-gpio.o
+ obj-$(CONFIG_MDIO_HISI_FEMAC)	+= mdio-hisi-femac.o
++obj-$(CONFIG_MDIO_I2C)		+= mdio-i2c.o
+ obj-$(CONFIG_MDIO_MOXART)	+= mdio-moxart.o
+ obj-$(CONFIG_MDIO_OCTEON)	+= mdio-octeon.o
+ obj-$(CONFIG_MDIO_SUN4I)	+= mdio-sun4i.o
+--- /dev/null
++++ b/drivers/net/phy/mdio-i2c.c
+@@ -0,0 +1,109 @@
++/*
++ * MDIO I2C bridge
++ *
++ * Copyright (C) 2015-2016 Russell King
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Network PHYs can appear on I2C buses when they are part of SFP module.
++ * This driver exposes these PHYs to the networking PHY code, allowing
++ * our PHY drivers access to these PHYs, and so allowing configuration
++ * of their settings.
++ */
++#include <linux/i2c.h>
++#include <linux/phy.h>
++
++#include "mdio-i2c.h"
++
++/*
++ * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is
++ * specified to be present in SFP modules.  These correspond with PHY
++ * addresses 16 and 17.  Disallow access to these "phy" addresses.
++ */
++static bool i2c_mii_valid_phy_id(int phy_id)
++{
++	return phy_id != 0x10 && phy_id != 0x11;
++}
++
++static unsigned int i2c_mii_phy_addr(int phy_id)
++{
++	return phy_id + 0x40;
++}
++
++static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg)
++{
++	struct i2c_adapter *i2c = bus->priv;
++	struct i2c_msg msgs[2];
++	u8 data[2], dev_addr = reg;
++	int bus_addr, ret;
++
++	if (!i2c_mii_valid_phy_id(phy_id))
++		return 0xffff;
++
++	bus_addr = i2c_mii_phy_addr(phy_id);
++	msgs[0].addr = bus_addr;
++	msgs[0].flags = 0;
++	msgs[0].len = 1;
++	msgs[0].buf = &dev_addr;
++	msgs[1].addr = bus_addr;
++	msgs[1].flags = I2C_M_RD;
++	msgs[1].len = sizeof(data);
++	msgs[1].buf = data;
++
++	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
++	if (ret != ARRAY_SIZE(msgs))
++		return 0xffff;
++
++	return data[0] << 8 | data[1];
++}
++
++static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
++{
++	struct i2c_adapter *i2c = bus->priv;
++	struct i2c_msg msg;
++	int ret;
++	u8 data[3];
++
++	if (!i2c_mii_valid_phy_id(phy_id))
++		return 0;
++
++	data[0] = reg;
++	data[1] = val >> 8;
++	data[2] = val;
++
++	msg.addr = i2c_mii_phy_addr(phy_id);
++	msg.flags = 0;
++	msg.len = 3;
++	msg.buf = data;
++
++	ret = i2c_transfer(i2c, &msg, 1);
++
++	return ret < 0 ? ret : 0;
++}
++
++struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c)
++{
++	struct mii_bus *mii;
++
++	if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
++		return ERR_PTR(-EINVAL);
++
++	mii = mdiobus_alloc();
++	if (!mii)
++		return ERR_PTR(-ENOMEM);
++
++	snprintf(mii->id, MII_BUS_ID_SIZE, "i2c:%s", dev_name(parent));
++	mii->parent = parent;
++	mii->read = i2c_mii_read;
++	mii->write = i2c_mii_write;
++	mii->priv = i2c;
++
++	return mii;
++}
++EXPORT_SYMBOL_GPL(mdio_i2c_alloc);
++
++MODULE_AUTHOR("Russell King");
++MODULE_DESCRIPTION("MDIO I2C bridge library");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/drivers/net/phy/mdio-i2c.h
+@@ -0,0 +1,19 @@
++/*
++ * MDIO I2C bridge
++ *
++ * Copyright (C) 2015 Russell King
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#ifndef MDIO_I2C_H
++#define MDIO_I2C_H
++
++struct device;
++struct i2c_adapter;
++struct mii_bus;
++
++struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
++
++#endif
diff --git a/target/linux/mvebu/patches-4.9/415-phylink-add-phylink-infrastructure.patch b/target/linux/mvebu/patches-4.9/415-phylink-add-phylink-infrastructure.patch
new file mode 100644
index 0000000..87c0daf
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/415-phylink-add-phylink-infrastructure.patch
@@ -0,0 +1,1120 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Tue, 22 Sep 2015 20:52:18 +0100
+Subject: [PATCH] phylink: add phylink infrastructure
+
+The link between the ethernet MAC and its PHY has become more complex
+as the interface evolves.  This is especially true with serdes links,
+where the part of the PHY is effectively integrated into the MAC.
+
+Serdes links can be connected to a variety of devices, including SFF
+modules soldered down onto the board with the MAC, a SFP cage with
+a hotpluggable SFP module which may contain a PHY or directly modulate
+the serdes signals onto optical media with or without a PHY, or even
+a classical PHY connection.
+
+Moreover, the negotiation information on serdes links comes in two
+varieties - SGMII mode, where the PHY provides its speed/duplex/flow
+control information to the MAC, and 1000base-X mode where both ends
+exchange their abilities and each resolve the link capabilities.
+
+This means we need a more flexible means to support these arrangements,
+particularly with the hotpluggable nature of SFP, where the PHY can
+be attached or detached after the network device has been brought up.
+
+Ethtool information can come from multiple sources:
+- we may have a PHY operating in either SGMII or 1000base-X mode, in
+  which case we take ethtool/mii data directly from the PHY.
+- we may have a optical SFP module without a PHY, with the MAC
+  operating in 1000base-X mode - the ethtool/mii data needs to come
+  from the MAC.
+- we may have a copper SFP module with a PHY whic can't be accessed,
+  which means we need to take ethtool/mii data from the MAC.
+
+Phylink aims to solve this by providing an intermediary between the
+MAC and PHY, providing a safe way for PHYs to be hotplugged, and
+allowing a SFP driver to reconfigure the serdes connection.
+
+Phylink also takes over support of fixed link connections, where the
+speed/duplex/flow control are fixed, but link status may be controlled
+by a GPIO signal.  By avoiding the fixed-phy implementation, phylink
+can provide a faster response to link events: fixed-phy has to wait for
+phylib to operate its state machine, which can take several seconds.
+In comparison, phylink takes milliseconds.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+
+- remove sync status
+- rework supported and advertisment handling
+- add 1000base-x speed for fixed links
+- use functionality exported from phy-core, reworking
+  __phylink_ethtool_ksettings_set for it
+---
+ create mode 100644 drivers/net/phy/phylink.c
+ create mode 100644 include/linux/phylink.h
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -10,6 +10,16 @@ menuconfig PHYLIB
+ 	  devices.  This option provides infrastructure for
+ 	  managing PHY devices.
+ 
++config PHYLINK
++	tristate
++	depends on NETDEVICES
++	select PHYLIB
++	select SWPHY
++	help
++	  PHYlink models the link between the PHY and MAC, allowing fixed
++	  configuration links, PHYs, and Serdes links with MAC level
++	  autonegotiation modes.
++
+ if PHYLIB
+ 
+ config SWPHY
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -6,6 +6,7 @@ libphy-$(CONFIG_SWPHY)		+= swphy.o
+ 
+ obj-$(CONFIG_MDIO_BOARDINFO)	+= mdio-boardinfo.o
+ 
++obj-$(CONFIG_PHYLINK)		+= phylink.o
+ obj-$(CONFIG_PHYLIB)		+= libphy.o
+ 
+ obj-$(CONFIG_SWCONFIG)		+= swconfig.o
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -1003,6 +1003,7 @@ void phy_detach(struct phy_device *phyde
+ 	phydev->attached_dev->phydev = NULL;
+ 	phydev->attached_dev = NULL;
+ 	phy_suspend(phydev);
++	phydev->phylink = NULL;
+ 
+ 	/* If the device had no specific driver before (i.e. - it
+ 	 * was using the generic driver), we unbind the device
+--- /dev/null
++++ b/drivers/net/phy/phylink.c
+@@ -0,0 +1,903 @@
++/*
++ * phylink models the MAC to optional PHY connection, supporting
++ * technologies such as SFP cages where the PHY is hot-pluggable.
++ *
++ * Copyright (C) 2015 Russell King
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++#include <linux/ethtool.h>
++#include <linux/export.h>
++#include <linux/gpio/consumer.h>
++#include <linux/netdevice.h>
++#include <linux/of.h>
++#include <linux/of_mdio.h>
++#include <linux/phy.h>
++#include <linux/phy_fixed.h>
++#include <linux/phylink.h>
++#include <linux/spinlock.h>
++#include <linux/workqueue.h>
++
++#include "swphy.h"
++
++#define SUPPORTED_INTERFACES \
++	(SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_FIBRE | \
++	 SUPPORTED_BNC | SUPPORTED_AUI | SUPPORTED_Backplane)
++#define ADVERTISED_INTERFACES \
++	(ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \
++	 ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane)
++
++enum {
++	PHYLINK_DISABLE_STOPPED,
++};
++
++struct phylink {
++	struct net_device *netdev;
++	const struct phylink_mac_ops *ops;
++	struct mutex config_mutex;
++
++	unsigned long phylink_disable_state; /* bitmask of disables */
++	struct phy_device *phydev;
++	phy_interface_t link_interface;	/* PHY_INTERFACE_xxx */
++	u8 link_an_mode;		/* MLO_AN_xxx */
++	u8 link_port;			/* The current non-phy ethtool port */
++	/* ethtool supported mask for ports */
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
++
++	/* The link configuration settings */
++	struct phylink_link_state link_config;
++	struct gpio_desc *link_gpio;
++
++	struct mutex state_mutex;	/* may be taken within config_mutex */
++	struct phylink_link_state phy_state;
++	struct work_struct resolve;
++
++	const struct phylink_module_ops *module_ops;
++	void *module_data;
++};
++
++static inline void linkmode_zero(unsigned long *dst)
++{
++	bitmap_zero(dst, __ETHTOOL_LINK_MODE_MASK_NBITS);
++}
++
++static inline void linkmode_copy(unsigned long *dst, const unsigned long *src)
++{
++	bitmap_copy(dst, src, __ETHTOOL_LINK_MODE_MASK_NBITS);
++}
++
++static inline void linkmode_and(unsigned long *dst, const unsigned long *a,
++				const unsigned long *b)
++{
++	bitmap_and(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS);
++}
++
++static inline void linkmode_or(unsigned long *dst, const unsigned long *a,
++				const unsigned long *b)
++{
++	bitmap_or(dst, a, b, __ETHTOOL_LINK_MODE_MASK_NBITS);
++}
++
++static inline bool linkmode_empty(const unsigned long *src)
++{
++	return bitmap_empty(src, __ETHTOOL_LINK_MODE_MASK_NBITS);
++}
++
++static void phylink_set_port_bits(unsigned long *bits)
++{
++	__set_bit(ETHTOOL_LINK_MODE_TP_BIT, bits);
++	__set_bit(ETHTOOL_LINK_MODE_AUI_BIT, bits);
++	__set_bit(ETHTOOL_LINK_MODE_MII_BIT, bits);
++	__set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, bits);
++	__set_bit(ETHTOOL_LINK_MODE_BNC_BIT, bits);
++	__set_bit(ETHTOOL_LINK_MODE_Backplane_BIT, bits);
++}
++
++static const char *phylink_an_mode_str(unsigned int mode)
++{
++	static const char *modestr[] = {
++		[MLO_AN_PHY] = "phy",
++		[MLO_AN_FIXED] = "fixed",
++		[MLO_AN_SGMII] = "SGMII",
++		[MLO_AN_8023Z] = "802.3z",
++	};
++
++	return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
++}
++
++static int phylink_parse_fixedlink(struct phylink *pl, struct device_node *np)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
++	struct device_node *fixed_node;
++	const struct phy_setting *s;
++	struct gpio_desc *desc;
++	const __be32 *fixed_prop;
++	u32 speed;
++	int ret, len;
++
++	fixed_node = of_get_child_by_name(np, "fixed-link");
++	if (fixed_node) {
++		ret = of_property_read_u32(fixed_node, "speed", &speed);
++
++		pl->link_config.speed = speed;
++		pl->link_config.duplex = DUPLEX_HALF;
++
++		if (of_property_read_bool(fixed_node, "full-duplex"))
++			pl->link_config.duplex = DUPLEX_FULL;
++		if (of_property_read_bool(fixed_node, "pause"))
++			pl->link_config.pause |= MLO_PAUSE_SYM;
++		if (of_property_read_bool(fixed_node, "asym-pause"))
++			pl->link_config.pause |= MLO_PAUSE_ASYM;
++
++		if (ret == 0) {
++			desc = fwnode_get_named_gpiod(&fixed_node->fwnode,
++						      "link-gpios");
++
++			if (!IS_ERR(desc))
++				pl->link_gpio = desc;
++			else if (desc == ERR_PTR(-EPROBE_DEFER))
++				ret = -EPROBE_DEFER;
++		}
++		of_node_put(fixed_node);
++
++		if (ret)
++			return ret;
++	} else {
++		fixed_prop = of_get_property(np, "fixed-link", &len);
++		if (!fixed_prop) {
++			netdev_err(pl->netdev, "broken fixed-link?\n");
++			return -EINVAL;
++		}
++		if (len == 5 * sizeof(*fixed_prop)) {
++			pl->link_config.duplex = be32_to_cpu(fixed_prop[1]) ?
++						DUPLEX_FULL : DUPLEX_HALF;
++			pl->link_config.speed = be32_to_cpu(fixed_prop[2]);
++			if (be32_to_cpu(fixed_prop[3]))
++				pl->link_config.pause |= MLO_PAUSE_SYM;
++			if (be32_to_cpu(fixed_prop[4]))
++				pl->link_config.pause |= MLO_PAUSE_ASYM;
++		}
++	}
++
++	bitmap_fill(mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
++	pl->ops->validate_support(pl->netdev, MLO_AN_FIXED, mask);
++
++	pl->link_config.link = 1;
++	pl->link_config.an_complete = 1;
++
++	if (pl->link_config.speed > SPEED_1000 &&
++	    pl->link_config.duplex != DUPLEX_FULL)
++		netdev_warn(pl->netdev, "fixed link specifies half duplex for %dMbps link?\n",
++			    pl->link_config.speed);
++
++	s = phy_lookup_setting(pl->link_config.speed, pl->link_config.duplex,
++				mask, __ETHTOOL_LINK_MODE_MASK_NBITS, true);
++	if (s) {
++		__set_bit(s->bit, pl->supported);
++	} else {
++		netdev_warn(pl->netdev, "fixed link %s duplex %dMbps not recognised\n",
++			    pl->link_config.duplex == DUPLEX_FULL ? "full" : "half",
++			    pl->link_config.speed);
++	}
++	return 0;
++}
++
++static int phylink_parse_mode(struct phylink *pl, struct device_node *np)
++{
++	struct device_node *dn;
++	const char *managed;
++
++	dn = of_get_child_by_name(np, "fixed-link");
++	if (dn || of_find_property(np, "fixed-link", NULL))
++		pl->link_an_mode = MLO_AN_FIXED;
++	of_node_put(dn);
++
++	if (of_property_read_string(np, "managed", &managed) == 0 &&
++	    strcmp(managed, "in-band-status") == 0) {
++		if (pl->link_an_mode == MLO_AN_FIXED) {
++			netdev_err(pl->netdev,
++				   "can't use both fixed-link and in-band-status\n");
++			return -EINVAL;
++		}
++		phylink_set(pl->supported, 10baseT_Half);
++		phylink_set(pl->supported, 10baseT_Full);
++		phylink_set(pl->supported, 100baseT_Half);
++		phylink_set(pl->supported, 100baseT_Full);
++		phylink_set(pl->supported, 1000baseT_Half);
++		phylink_set(pl->supported, 1000baseT_Full);
++		phylink_set(pl->supported, Asym_Pause);
++		phylink_set(pl->supported, Pause);
++		pl->link_an_mode = MLO_AN_SGMII;
++		pl->link_config.an_enabled = true;
++		pl->ops->validate_support(pl->netdev, pl->link_an_mode,
++					  pl->supported);
++	}
++
++	return 0;
++}
++
++
++static void phylink_init_advert(struct phylink *pl, unsigned int mode,
++				const unsigned long *supported,
++				unsigned long *advertising)
++{
++	linkmode_copy(advertising, supported);
++	if (pl->ops->validate_advert)
++		pl->ops->validate_advert(pl->netdev, mode, supported,
++					 advertising);
++}
++
++static void phylink_mac_config(struct phylink *pl,
++			       const struct phylink_link_state *state)
++{
++	netdev_dbg(pl->netdev,
++		   "%s: mode=%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
++		   __func__, phylink_an_mode_str(pl->link_an_mode),
++		   phy_speed_to_str(state->speed),
++		   phy_duplex_to_str(state->duplex),
++		   __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
++		   state->pause, state->link, state->an_enabled);
++
++	pl->ops->mac_config(pl->netdev, pl->link_an_mode, state);
++}
++
++static void phylink_mac_an_restart(struct phylink *pl)
++{
++	if (pl->link_config.an_enabled)
++		pl->ops->mac_an_restart(pl->netdev, pl->link_an_mode);
++}
++
++static int phylink_get_mac_state(struct phylink *pl, struct phylink_link_state *state)
++{
++	struct net_device *ndev = pl->netdev;
++
++	linkmode_copy(state->advertising, pl->link_config.advertising);
++	linkmode_zero(state->lp_advertising);
++	state->an_enabled = pl->link_config.an_enabled;
++	state->link = 1;
++
++	return pl->ops->mac_link_state(ndev, state);
++}
++
++/* The fixed state is... fixed except for the link state,
++ * which may be determined by a GPIO.
++ */
++static void phylink_get_fixed_state(struct phylink *pl, struct phylink_link_state *state)
++{
++	*state = pl->link_config;
++	if (pl->link_gpio)
++		state->link = !!gpiod_get_value(pl->link_gpio);
++}
++
++static void phylink_resolve(struct work_struct *w)
++{
++	struct phylink *pl = container_of(w, struct phylink, resolve);
++	struct phylink_link_state link_state;
++	struct net_device *ndev = pl->netdev;
++
++	mutex_lock(&pl->state_mutex);
++	if (pl->phylink_disable_state) {
++		link_state.link = false;
++	} else {
++		switch (pl->link_an_mode) {
++		case MLO_AN_PHY:
++			link_state = pl->phy_state;
++			break;
++
++		case MLO_AN_FIXED:
++			phylink_get_fixed_state(pl, &link_state);
++			break;
++
++		case MLO_AN_SGMII:
++			phylink_get_mac_state(pl, &link_state);
++			if (pl->phydev)
++				link_state.link = link_state.link &&
++						  pl->phy_state.link;
++			break;
++
++		case MLO_AN_8023Z:
++			phylink_get_mac_state(pl, &link_state);
++			break;
++		}
++	}
++
++	if (link_state.link != netif_carrier_ok(ndev)) {
++		if (!link_state.link) {
++			netif_carrier_off(ndev);
++			pl->ops->mac_link_down(ndev, pl->link_an_mode);
++			netdev_info(ndev, "Link is Down\n");
++		} else {
++			/* If we have a PHY, we need the MAC updated with
++			 * the current link parameters (eg, in SGMII mode,
++			 * with flow control status.)
++			 */
++			if (pl->phydev)
++				phylink_mac_config(pl, &link_state);
++
++			pl->ops->mac_link_up(ndev, pl->link_an_mode);
++
++			netif_carrier_on(ndev);
++
++			netdev_info(ndev,
++				    "Link is Up - %s/%s - flow control %s\n",
++				    phy_speed_to_str(link_state.speed),
++				    phy_duplex_to_str(link_state.duplex),
++				    link_state.pause ? "rx/tx" : "off");
++		}
++	}
++	mutex_unlock(&pl->state_mutex);
++}
++
++static void phylink_run_resolve(struct phylink *pl)
++{
++	if (!pl->phylink_disable_state)
++		queue_work(system_power_efficient_wq, &pl->resolve);
++}
++
++struct phylink *phylink_create(struct net_device *ndev, struct device_node *np,
++	phy_interface_t iface, const struct phylink_mac_ops *ops)
++{
++	struct phylink *pl;
++	int ret;
++
++	pl = kzalloc(sizeof(*pl), GFP_KERNEL);
++	if (!pl)
++		return ERR_PTR(-ENOMEM);
++
++	mutex_init(&pl->state_mutex);
++	mutex_init(&pl->config_mutex);
++	INIT_WORK(&pl->resolve, phylink_resolve);
++	pl->netdev = ndev;
++	pl->link_interface = iface;
++	pl->link_port = PORT_MII;
++	pl->link_config.speed = SPEED_UNKNOWN;
++	pl->link_config.duplex = DUPLEX_UNKNOWN;
++	pl->ops = ops;
++	__set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
++
++	ret = phylink_parse_mode(pl, np);
++	if (ret < 0) {
++		kfree(pl);
++		return ERR_PTR(ret);
++	}
++
++	if (pl->link_an_mode == MLO_AN_FIXED) {
++		ret = phylink_parse_fixedlink(pl, np);
++		if (ret < 0) {
++			kfree(pl);
++			return ERR_PTR(ret);
++		}
++	}
++
++	phylink_set(pl->supported, MII);
++	phylink_init_advert(pl, pl->link_an_mode, pl->supported,
++			    pl->link_config.advertising);
++
++	return pl;
++}
++EXPORT_SYMBOL_GPL(phylink_create);
++
++void phylink_destroy(struct phylink *pl)
++{
++	cancel_work_sync(&pl->resolve);
++	kfree(pl);
++}
++EXPORT_SYMBOL_GPL(phylink_destroy);
++
++void phylink_phy_change(struct phy_device *phydev, bool up, bool do_carrier)
++{
++	struct phylink *pl = phydev->phylink;
++
++	mutex_lock(&pl->state_mutex);
++	pl->phy_state.speed = phydev->speed;
++	pl->phy_state.duplex = phydev->duplex;
++	pl->phy_state.pause = MLO_PAUSE_NONE;
++	if (phydev->pause)
++		pl->phy_state.pause |= MLO_PAUSE_SYM;
++	if (phydev->asym_pause)
++		pl->phy_state.pause |= MLO_PAUSE_ASYM;
++	pl->phy_state.link = up;
++	mutex_unlock(&pl->state_mutex);
++
++	phylink_run_resolve(pl);
++
++	netdev_dbg(pl->netdev, "phy link %s %s/%s\n", up ? "up" : "down",
++		   phy_speed_to_str(phydev->speed),
++		   phy_duplex_to_str(phydev->duplex));
++}
++
++static int phylink_empty_linkmode(const unsigned long *linkmode)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp) = { 0, };
++
++	phylink_set_port_bits(tmp);
++	phylink_set(tmp, Autoneg);
++	phylink_set(tmp, Pause);
++	phylink_set(tmp, Asym_Pause);
++
++	bitmap_andnot(tmp, linkmode, tmp, __ETHTOOL_LINK_MODE_MASK_NBITS);
++
++	return linkmode_empty(tmp);
++}
++
++static int phylink_validate_support(struct phylink *pl, int mode,
++				    unsigned long *mask)
++{
++	pl->ops->validate_support(pl->netdev, mode, mask);
++
++	return phylink_empty_linkmode(mask) ? -EINVAL : 0;
++}
++
++static int phylink_bringup_phy(struct phylink *pl, struct phy_device *phy)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
++	u32 advertising;
++	int ret;
++
++	ethtool_convert_legacy_u32_to_link_mode(mask, phy->supported);
++	ret = phylink_validate_support(pl, pl->link_an_mode, mask);
++	if (ret)
++		return ret;
++
++	mutex_lock(&pl->config_mutex);
++	phy->phylink = pl;
++	phy->phy_link_change = phylink_phy_change;
++
++	netdev_info(pl->netdev,
++		    "PHY [%s] driver [%s]\n", dev_name(&phy->mdio.dev),
++		    phy->drv->name);
++
++	mutex_lock(&pl->state_mutex);
++	pl->phydev = phy;
++	linkmode_copy(pl->supported, mask);
++
++	/* Restrict the phy advertisment according to the MAC support. */
++	ethtool_convert_link_mode_to_legacy_u32(&advertising, mask);
++	phy->advertising &= ADVERTISED_INTERFACES | advertising;
++	ethtool_convert_legacy_u32_to_link_mode(pl->link_config.advertising,
++						phy->advertising);
++	mutex_unlock(&pl->state_mutex);
++
++	netdev_dbg(pl->netdev,
++		   "phy: setting supported %*pb advertising 0x%08x\n",
++		   __ETHTOOL_LINK_MODE_MASK_NBITS, pl->supported,
++		   phy->advertising);
++
++	phy_start_machine(phy);
++	if (phy->irq > 0)
++		phy_start_interrupts(phy);
++
++	mutex_unlock(&pl->config_mutex);
++
++	return 0;
++}
++
++int phylink_connect_phy(struct phylink *pl, struct phy_device *phy)
++{
++	int ret;
++
++	ret = phy_attach_direct(pl->netdev, phy, 0, pl->link_interface);
++	if (ret)
++		return ret;
++
++	ret = phylink_bringup_phy(pl, phy);
++	if (ret)
++		phy_detach(phy);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_connect_phy);
++
++int phylink_of_phy_connect(struct phylink *pl, struct device_node *dn)
++{
++	struct device_node *phy_node;
++	struct phy_device *phy_dev;
++	int ret;
++
++	/* Fixed links are handled without needing a PHY */
++	if (pl->link_an_mode == MLO_AN_FIXED)
++		return 0;
++
++	phy_node = of_parse_phandle(dn, "phy-handle", 0);
++	if (!phy_node)
++		phy_node = of_parse_phandle(dn, "phy", 0);
++	if (!phy_node)
++		phy_node = of_parse_phandle(dn, "phy-device", 0);
++
++	if (!phy_node) {
++		if (pl->link_an_mode == MLO_AN_PHY) {
++			netdev_err(pl->netdev, "unable to find PHY node\n");
++			return -ENODEV;
++		}
++		return 0;
++	}
++
++	phy_dev = of_phy_attach(pl->netdev, phy_node, 0, pl->link_interface);
++	/* We're done with the phy_node handle */
++	of_node_put(phy_node);
++
++	if (!phy_dev)
++		return -ENODEV;
++
++	ret = phylink_bringup_phy(pl, phy_dev);
++	if (ret)
++		phy_detach(phy_dev);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_of_phy_connect);
++
++void phylink_disconnect_phy(struct phylink *pl)
++{
++	struct phy_device *phy;
++
++	mutex_lock(&pl->config_mutex);
++	phy = pl->phydev;
++
++	mutex_lock(&pl->state_mutex);
++	pl->phydev = NULL;
++	mutex_unlock(&pl->state_mutex);
++	flush_work(&pl->resolve);
++
++	if (phy)
++		phy_disconnect(phy);
++
++	mutex_unlock(&pl->config_mutex);
++}
++EXPORT_SYMBOL_GPL(phylink_disconnect_phy);
++
++void phylink_mac_change(struct phylink *pl, bool up)
++{
++	phylink_run_resolve(pl);
++	netdev_dbg(pl->netdev, "mac link %s\n", up ? "up" : "down");
++}
++EXPORT_SYMBOL_GPL(phylink_mac_change);
++
++void phylink_start(struct phylink *pl)
++{
++	mutex_lock(&pl->config_mutex);
++
++	netdev_info(pl->netdev, "configuring for %s link mode\n",
++		    phylink_an_mode_str(pl->link_an_mode));
++
++	/* Apply the link configuration to the MAC when starting. This allows
++	 * a fixed-link to start with the correct parameters, and also
++	 * ensures that we set the appropriate advertisment for Serdes links.
++	 */
++	phylink_mac_config(pl, &pl->link_config);
++
++	clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
++	phylink_run_resolve(pl);
++
++	if (pl->phydev)
++		phy_start(pl->phydev);
++
++	mutex_unlock(&pl->config_mutex);
++}
++EXPORT_SYMBOL_GPL(phylink_start);
++
++void phylink_stop(struct phylink *pl)
++{
++	mutex_lock(&pl->config_mutex);
++
++	if (pl->phydev)
++		phy_stop(pl->phydev);
++
++	set_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
++	flush_work(&pl->resolve);
++
++	mutex_unlock(&pl->config_mutex);
++}
++EXPORT_SYMBOL_GPL(phylink_stop);
++
++static void phylink_merge_link_mode(unsigned long *dst, const unsigned long *b)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
++
++	linkmode_zero(mask);
++	phylink_set_port_bits(mask);
++
++	linkmode_and(dst, dst, mask);
++	linkmode_or(dst, dst, b);
++}
++
++static void phylink_get_ksettings(const struct phylink_link_state *state,
++				  struct ethtool_link_ksettings *kset)
++{
++	phylink_merge_link_mode(kset->link_modes.advertising, state->advertising);
++	linkmode_copy(kset->link_modes.lp_advertising, state->lp_advertising);
++	kset->base.speed = state->speed;
++	kset->base.duplex = state->duplex;
++	kset->base.autoneg = state->an_enabled ? AUTONEG_ENABLE :
++				AUTONEG_DISABLE;
++}
++
++static int __phylink_ethtool_ksettings_get(struct phylink *pl,
++					   struct ethtool_link_ksettings *kset)
++{
++	struct phylink_link_state link_state;
++	int ret;
++
++	if (pl->phydev) {
++		ret = phy_ethtool_ksettings_get(pl->phydev, kset);
++		if (ret)
++			return ret;
++	} else {
++		kset->base.port = pl->link_port;
++	}
++
++	linkmode_copy(kset->link_modes.supported, pl->supported);
++
++	switch (pl->link_an_mode) {
++	case MLO_AN_FIXED:
++		/* We are using fixed settings. Report these as the
++		 * current link settings - and note that these also
++		 * represent the supported speeds/duplex/pause modes.
++		 */
++		phylink_get_fixed_state(pl, &link_state);
++		phylink_get_ksettings(&link_state, kset);
++		break;
++
++	case MLO_AN_SGMII:
++		/* If there is a phy attached, then use the reported
++		 * settings from the phy with no modification.
++		 */
++		if (pl->phydev)
++			break;
++
++	case MLO_AN_8023Z:
++		phylink_get_mac_state(pl, &link_state);
++
++		/* The MAC is reporting the link results from its own PCS
++		 * layer via in-band status. Report these as the current
++		 * link settings.
++		 */
++		phylink_get_ksettings(&link_state, kset);
++		break;
++	}
++
++	return 0;
++}
++
++int phylink_ethtool_ksettings_get(struct phylink *pl,
++	struct ethtool_link_ksettings *kset)
++{
++	int ret;
++
++	mutex_lock(&pl->config_mutex);
++	ret = __phylink_ethtool_ksettings_get(pl, kset);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_get);
++
++static int __phylink_ethtool_ksettings_set(struct phylink *pl,
++					   const struct ethtool_link_ksettings *kset)
++{
++	struct ethtool_link_ksettings our_kset = *kset;
++	int ret;
++
++	/* Mask out unsupported advertisments */
++	linkmode_and(our_kset.link_modes.advertising,
++		     kset->link_modes.advertising, pl->supported);
++
++	if (pl->ops->validate_advert)
++		pl->ops->validate_advert(pl->netdev, pl->link_an_mode,
++					 pl->supported,
++					 our_kset.link_modes.advertising);
++
++	/* FIXME: should we reject autoneg if phy/mac does not support it? */
++
++	if (kset->base.autoneg == AUTONEG_DISABLE) {
++		const struct phy_setting *s;
++
++		/* Autonegotiation disabled, select a suitable speed and
++		 * duplex.
++		 */
++		s = phy_lookup_setting(kset->base.speed, kset->base.duplex,
++				       pl->supported,
++				       __ETHTOOL_LINK_MODE_MASK_NBITS, false);
++		if (!s)
++			return -EINVAL;
++
++		/* If we have a fixed link (as specified by firmware), refuse
++		 * to change link parameters.
++		 */
++		if (pl->link_an_mode == MLO_AN_FIXED &&
++		    (s->speed != pl->link_config.speed ||
++		     s->duplex != pl->link_config.duplex))
++			return -EINVAL;
++
++		our_kset.base.speed = s->speed;
++		our_kset.base.duplex = s->duplex;
++
++		__clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
++			    our_kset.link_modes.advertising);
++	} else {
++		/* If we have a fixed link, refuse to enable autonegotiation */
++		if (pl->link_an_mode == MLO_AN_FIXED)
++			return -EINVAL;
++
++		/* Autonegotiation enabled, validate advertisment */
++		if (phylink_empty_linkmode(our_kset.link_modes.advertising))
++			return -EINVAL;
++
++		__set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
++			  our_kset.link_modes.advertising);
++	}
++
++	/* If we have a PHY, configure the phy */
++	if (pl->phydev) {
++		ret = phy_ethtool_ksettings_set(pl->phydev, &our_kset);
++		if (ret)
++			return ret;
++	}
++
++	mutex_lock(&pl->state_mutex);
++	/* Configure the MAC to match the new settings */
++	linkmode_copy(pl->link_config.advertising, our_kset.link_modes.advertising);
++	pl->link_config.speed = our_kset.base.speed;
++	pl->link_config.duplex = our_kset.base.duplex;
++	pl->link_config.an_enabled = our_kset.base.autoneg != AUTONEG_DISABLE;
++
++	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
++		phylink_mac_config(pl, &pl->link_config);
++		phylink_mac_an_restart(pl);
++	}
++	mutex_unlock(&pl->state_mutex);
++
++	return ret;
++}
++
++int phylink_ethtool_ksettings_set(struct phylink *pl,
++	const struct ethtool_link_ksettings *kset)
++{
++	int ret;
++
++	if (kset->base.autoneg != AUTONEG_DISABLE &&
++	    kset->base.autoneg != AUTONEG_ENABLE)
++		return -EINVAL;
++
++	mutex_lock(&pl->config_mutex);
++	ret = __phylink_ethtool_ksettings_set(pl, kset);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set);
++
++/* This emulates MII registers for a fixed-mode phy operating as per the
++ * passed in state. "aneg" defines if we report negotiation is possible.
++ *
++ * FIXME: should deal with negotiation state too.
++ */
++static int phylink_mii_emul_read(struct net_device *ndev, unsigned int reg,
++				 struct phylink_link_state *state, bool aneg)
++{
++	struct fixed_phy_status fs;
++	int val;
++
++	fs.link = state->link;
++	fs.speed = state->speed;
++	fs.duplex = state->duplex;
++	fs.pause = state->pause & MLO_PAUSE_SYM;
++	fs.asym_pause = state->pause & MLO_PAUSE_ASYM;
++
++	val = swphy_read_reg(reg, &fs);
++	if (reg == MII_BMSR) {
++		if (!state->an_complete)
++			val &= ~BMSR_ANEGCOMPLETE;
++		if (!aneg)
++			val &= ~BMSR_ANEGCAPABLE;
++	}
++	return val;
++}
++
++static int phylink_mii_read(struct phylink *pl, unsigned int phy_id,
++			    unsigned int reg)
++{
++	struct phylink_link_state state;
++	int val = 0xffff;
++
++	/* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */
++	if (pl->phydev)
++		return mdiobus_read(pl->phydev->mdio.bus, phy_id, reg);
++
++	switch (pl->link_an_mode) {
++	case MLO_AN_FIXED:
++		if (phy_id == 0) {
++			phylink_get_fixed_state(pl, &state);
++			val = phylink_mii_emul_read(pl->netdev, reg, &state,
++						    true);
++		}
++		break;
++
++	case MLO_AN_PHY:
++		return -EOPNOTSUPP;
++
++	case MLO_AN_SGMII:
++		/* No phy, fall through to 8023z method */
++	case MLO_AN_8023Z:
++		if (phy_id == 0) {
++			val = phylink_get_mac_state(pl, &state);
++			if (val < 0)
++				return val;
++
++			val = phylink_mii_emul_read(pl->netdev, reg, &state,
++						    true);
++		}
++		break;
++	}
++
++	return val & 0xffff;
++}
++
++static int phylink_mii_write(struct phylink *pl, unsigned int phy_id,
++			     unsigned int reg, unsigned int val)
++{
++	/* PHYs only exist for MLO_AN_PHY and MLO_AN_SGMII */
++	if (pl->phydev) {
++		mdiobus_write(pl->phydev->mdio.bus, phy_id, reg, val);
++		return 0;
++	}
++
++	switch (pl->link_an_mode) {
++	case MLO_AN_FIXED:
++		break;
++
++	case MLO_AN_PHY:
++		return -EOPNOTSUPP;
++
++	case MLO_AN_SGMII:
++		/* No phy, fall through to 8023z method */
++	case MLO_AN_8023Z:
++		break;
++	}
++
++	return 0;
++}
++
++int phylink_mii_ioctl(struct phylink *pl, struct ifreq *ifr, int cmd)
++{
++	struct mii_ioctl_data *mii_data = if_mii(ifr);
++	int val, ret;
++
++	mutex_lock(&pl->config_mutex);
++
++	switch (cmd) {
++	case SIOCGMIIPHY:
++		mii_data->phy_id = pl->phydev ? pl->phydev->mdio.addr : 0;
++		/* fallthrough */
++
++	case SIOCGMIIREG:
++		val = phylink_mii_read(pl, mii_data->phy_id, mii_data->reg_num);
++		if (val < 0) {
++			ret = val;
++		} else {
++			mii_data->val_out = val;
++			ret = 0;
++		}
++		break;
++
++	case SIOCSMIIREG:
++		ret = phylink_mii_write(pl, mii_data->phy_id, mii_data->reg_num,
++					mii_data->val_in);
++		break;
++
++	default:
++		ret = -EOPNOTSUPP;
++		if (pl->phydev)
++			ret = phy_mii_ioctl(pl->phydev, ifr, cmd);
++		break;
++	}
++
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
++
++MODULE_LICENSE("GPL");
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -153,6 +153,7 @@ static inline const char *phy_modes(phy_
+ #define MII_ADDR_C45 (1<<30)
+ 
+ struct device;
++struct phylink;
+ struct sk_buff;
+ 
+ /*
+@@ -422,6 +423,7 @@ struct phy_device {
+ 
+ 	struct mutex lock;
+ 
++	struct phylink *phylink;
+ 	struct net_device *attached_dev;
+ 
+ 	u8 mdix;
+--- /dev/null
++++ b/include/linux/phylink.h
+@@ -0,0 +1,100 @@
++#ifndef NETDEV_PCS_H
++#define NETDEV_PCS_H
++
++#include <linux/phy.h>
++#include <linux/spinlock.h>
++#include <linux/workqueue.h>
++
++struct device_node;
++struct ethtool_cmd;
++struct net_device;
++
++enum {
++	MLO_PAUSE_NONE,
++	MLO_PAUSE_ASYM = BIT(0),
++	MLO_PAUSE_SYM = BIT(1),
++
++	MLO_AN_PHY = 0,
++	MLO_AN_FIXED,
++	MLO_AN_SGMII,
++	MLO_AN_8023Z,
++};
++
++struct phylink_link_state {
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
++	int speed;
++	int duplex;
++	int pause;
++	unsigned int link:1;
++	unsigned int an_enabled:1;
++	unsigned int an_complete:1;
++};
++
++struct phylink_mac_ops {
++	/**
++	 * @validate_support:
++	 *
++	 * Validate and update the support mask provided by a PHY or
++	 * module.  Unsupported link modes should be cleared by the
++	 * MAC.
++	 *
++	 * Note: the PHY may be able to transform from one connection
++	 * technology to another, so, eg, don't clear 1000base-X just
++	 * because the MAC is unable to support it.  This is more about
++	 * clearing unsupported speeds and duplex settings.
++	 */
++	void (*validate_support)(struct net_device *, unsigned int mode,
++				 unsigned long *support);
++
++	/**
++	 * @validate_advert:
++	 *
++	 * Validate and update the advertisment mask, clearing bits that
++	 * can not be advertised in the chosen mode or with each other.
++	 */
++	void (*validate_advert)(struct net_device *, unsigned int mode,
++				const unsigned long *support,
++				unsigned long *advert);
++
++	/* Read the current link state from the hardware */
++	int (*mac_link_state)(struct net_device *, struct phylink_link_state *);
++
++	/* Configure the MAC */
++	void (*mac_config)(struct net_device *, unsigned int mode,
++			   const struct phylink_link_state *);
++	void (*mac_an_restart)(struct net_device *, unsigned int mode);
++
++	void (*mac_link_down)(struct net_device *, unsigned int mode);
++	void (*mac_link_up)(struct net_device *, unsigned int mode);
++};
++
++struct phylink *phylink_create(struct net_device *, struct device_node *,
++	phy_interface_t iface, const struct phylink_mac_ops *ops);
++void phylink_destroy(struct phylink *);
++
++int phylink_connect_phy(struct phylink *, struct phy_device *);
++int phylink_of_phy_connect(struct phylink *, struct device_node *);
++void phylink_disconnect_phy(struct phylink *);
++
++void phylink_mac_change(struct phylink *, bool up);
++
++void phylink_start(struct phylink *);
++void phylink_stop(struct phylink *);
++
++int phylink_ethtool_ksettings_get(struct phylink *,
++				  struct ethtool_link_ksettings *);
++int phylink_ethtool_ksettings_set(struct phylink *,
++				  const struct ethtool_link_ksettings *);
++int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
++
++#define phylink_zero(bm) \
++	bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS)
++#define __phylink_do_bit(op, bm, mode) \
++	op(ETHTOOL_LINK_MODE_ ## mode ## _BIT, bm)
++
++#define phylink_set(bm, mode)	__phylink_do_bit(__set_bit, bm, mode)
++#define phylink_clear(bm, mode)	__phylink_do_bit(__clear_bit, bm, mode)
++#define phylink_test(bm, mode)	__phylink_do_bit(test_bit, bm, mode)
++
++#endif
diff --git a/target/linux/mvebu/patches-4.9/416-phylink-add-hooks-for-SFP-support.patch b/target/linux/mvebu/patches-4.9/416-phylink-add-hooks-for-SFP-support.patch
new file mode 100644
index 0000000..da44f75
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/416-phylink-add-hooks-for-SFP-support.patch
@@ -0,0 +1,177 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 24 Sep 2015 11:01:13 +0100
+Subject: [PATCH] phylink: add hooks for SFP support
+
+Add support to phylink for SFP, which needs to control and configure
+the ethernet MAC link state.  Specifically, SFP needs to:
+
+1. set the negotiation mode between SGMII and 1000base-X
+2. attach and detach the module PHY
+3. prevent the link coming up when errors are reported
+
+In the absence of a PHY, we also need to set the ethtool port type
+according to the module plugged in.
+
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+
+- rework phylink_set_link_*(), combining into a single function.
+---
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -11,6 +11,7 @@
+ #include <linux/ethtool.h>
+ #include <linux/export.h>
+ #include <linux/gpio/consumer.h>
++#include <linux/list.h>
+ #include <linux/netdevice.h>
+ #include <linux/of.h>
+ #include <linux/of_mdio.h>
+@@ -29,11 +30,16 @@
+ 	(ADVERTISED_TP | ADVERTISED_MII | ADVERTISED_FIBRE | \
+ 	 ADVERTISED_BNC | ADVERTISED_AUI | ADVERTISED_Backplane)
+ 
++static LIST_HEAD(phylinks);
++static DEFINE_MUTEX(phylink_mutex);
++
+ enum {
+ 	PHYLINK_DISABLE_STOPPED,
++	PHYLINK_DISABLE_LINK,
+ };
+ 
+ struct phylink {
++	struct list_head node;
+ 	struct net_device *netdev;
+ 	const struct phylink_mac_ops *ops;
+ 	struct mutex config_mutex;
+@@ -375,12 +381,20 @@ struct phylink *phylink_create(struct ne
+ 	phylink_init_advert(pl, pl->link_an_mode, pl->supported,
+ 			    pl->link_config.advertising);
+ 
++	mutex_lock(&phylink_mutex);
++	list_add_tail(&pl->node, &phylinks);
++	mutex_unlock(&phylink_mutex);
++
+ 	return pl;
+ }
+ EXPORT_SYMBOL_GPL(phylink_create);
+ 
+ void phylink_destroy(struct phylink *pl)
+ {
++	mutex_lock(&phylink_mutex);
++	list_del(&pl->node);
++	mutex_unlock(&phylink_mutex);
++
+ 	cancel_work_sync(&pl->resolve);
+ 	kfree(pl);
+ }
+@@ -900,4 +914,93 @@ int phylink_mii_ioctl(struct phylink *pl
+ }
+ EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
+ 
++
++
++void phylink_disable(struct phylink *pl)
++{
++	set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
++	flush_work(&pl->resolve);
++
++	netif_carrier_off(pl->netdev);
++}
++EXPORT_SYMBOL_GPL(phylink_disable);
++
++void phylink_enable(struct phylink *pl)
++{
++	clear_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
++	phylink_run_resolve(pl);
++}
++EXPORT_SYMBOL_GPL(phylink_enable);
++
++int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port,
++		     const unsigned long *support)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask);
++	int ret = 0;
++
++	netdev_dbg(pl->netdev, "requesting link mode %s with support %*pb\n",
++		   phylink_an_mode_str(mode),
++		   __ETHTOOL_LINK_MODE_MASK_NBITS, support);
++
++	if (mode == MLO_AN_FIXED)
++		return -EINVAL;
++
++	linkmode_copy(mask, support);
++
++	/* Ignore errors if we're expecting a PHY to attach later */
++	ret = phylink_validate_support(pl, mode, mask);
++	if (ret && mode != MLO_AN_PHY)
++		return ret;
++
++	mutex_lock(&pl->config_mutex);
++	if (mode == MLO_AN_8023Z && pl->phydev) {
++		ret = -EINVAL;
++	} else {
++		bool changed = !bitmap_equal(pl->supported, mask,
++					     __ETHTOOL_LINK_MODE_MASK_NBITS);
++		if (changed) {
++			linkmode_copy(pl->supported, mask);
++
++			phylink_init_advert(pl, mode, mask,
++					    pl->link_config.advertising);
++		}
++
++		if (pl->link_an_mode != mode) {
++			pl->link_an_mode = mode;
++
++			changed = true;
++
++			netdev_info(pl->netdev, "switched to %s link mode\n",
++				    phylink_an_mode_str(mode));
++		}
++
++		pl->link_port = port;
++
++		if (changed && !test_bit(PHYLINK_DISABLE_STOPPED,
++					 &pl->phylink_disable_state))
++			phylink_mac_config(pl, &pl->link_config);
++	}
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_set_link);
++
++struct phylink *phylink_lookup_by_netdev(struct net_device *ndev)
++{
++	struct phylink *pl, *found = NULL;
++
++	mutex_lock(&phylink_mutex);
++	list_for_each_entry(pl, &phylinks, node)
++		if (pl->netdev == ndev) {
++			found = pl;
++			break;
++		}
++
++	mutex_unlock(&phylink_mutex);
++
++	return found;
++}
++EXPORT_SYMBOL_GPL(phylink_lookup_by_netdev);
++
+ MODULE_LICENSE("GPL");
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -88,6 +88,12 @@ int phylink_ethtool_ksettings_set(struct
+ 				  const struct ethtool_link_ksettings *);
+ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
+ 
++int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port,
++		     const unsigned long *support);
++void phylink_disable(struct phylink *pl);
++void phylink_enable(struct phylink *pl);
++struct phylink *phylink_lookup_by_netdev(struct net_device *ndev);
++
+ #define phylink_zero(bm) \
+ 	bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS)
+ #define __phylink_do_bit(op, bm, mode) \
diff --git a/target/linux/mvebu/patches-4.9/417-sfp-add-phylink-based-SFP-module-support.patch b/target/linux/mvebu/patches-4.9/417-sfp-add-phylink-based-SFP-module-support.patch
new file mode 100644
index 0000000..4abefe6
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/417-sfp-add-phylink-based-SFP-module-support.patch
@@ -0,0 +1,1477 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Sat, 12 Sep 2015 18:43:39 +0100
+Subject: [PATCH] sfp: add phylink based SFP module support
+
+Add support for SFP hotpluggable modules via phylink.  This supports
+both copper and optical SFP modules, which require different Serdes
+modes in order to properly negotiate the link.
+
+Optical SFP modules typically require the Serdes link to be talking
+1000base-X mode - this is the gigabit ethernet mode defined by the
+802.3 standard.
+
+Copper SFP modules typically integrate a PHY in the module to convert
+from Serdes to copper, and the PHY will be configured by the vendor
+to either present a 1000base-X Serdes link (for fixed 1000base-T) or
+a SGMII Serdes link.  However, this is vendor defined, so we instead
+detect the PHY, switch the link to SGMII mode, and use traditional
+PHY based negotiation.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+
+- set port and port capability depending on connector type
+- move autoneg mode setting to probe function
+- set "supported" speed capabilities depending on reported ethernet
+  capabilities
+- checks for short read
+- dump eeprom base ID when checksum fails
+---
+ create mode 100644 drivers/net/phy/sfp.c
+ create mode 100644 include/linux/sfp.h
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -256,6 +256,11 @@ endif # RTL8366_SMI
+ 
+ comment "MII PHY device drivers"
+ 
++config SFP
++	tristate "SFP cage support"
++	depends on I2C && PHYLINK
++	select MDIO_I2C
++
+ config AMD_PHY
+ 	tristate "AMD PHYs"
+ 	---help---
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -41,6 +41,8 @@ obj-$(CONFIG_MDIO_SUN4I)	+= mdio-sun4i.o
+ obj-$(CONFIG_MDIO_THUNDER)	+= mdio-thunder.o
+ obj-$(CONFIG_MDIO_XGENE)	+= mdio-xgene.o
+ 
++obj-$(CONFIG_SFP)		+= sfp.o
++
+ obj-$(CONFIG_AMD_PHY)		+= amd.o
+ obj-$(CONFIG_AQUANTIA_PHY)	+= aquantia.o
+ obj-$(CONFIG_AT803X_PHY)	+= at803x.o
+--- /dev/null
++++ b/drivers/net/phy/sfp.c
+@@ -0,0 +1,1071 @@
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/i2c.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
++#include <linux/netdevice.h>
++#include <linux/of.h>
++#include <linux/of_net.h>
++#include <linux/phylink.h>
++#include <linux/platform_device.h>
++#include <linux/sfp.h>
++#include <linux/slab.h>
++#include <linux/workqueue.h>
++
++#include "mdio-i2c.h"
++#include "swphy.h"
++
++enum {
++	GPIO_MODDEF0,
++	GPIO_LOS,
++	GPIO_TX_FAULT,
++	GPIO_TX_DISABLE,
++	GPIO_RATE_SELECT,
++	GPIO_MAX,
++
++	SFP_F_PRESENT = BIT(GPIO_MODDEF0),
++	SFP_F_LOS = BIT(GPIO_LOS),
++	SFP_F_TX_FAULT = BIT(GPIO_TX_FAULT),
++	SFP_F_TX_DISABLE = BIT(GPIO_TX_DISABLE),
++	SFP_F_RATE_SELECT = BIT(GPIO_RATE_SELECT),
++
++	SFP_E_INSERT = 0,
++	SFP_E_REMOVE,
++	SFP_E_DEV_DOWN,
++	SFP_E_DEV_UP,
++	SFP_E_TX_FAULT,
++	SFP_E_TX_CLEAR,
++	SFP_E_LOS_HIGH,
++	SFP_E_LOS_LOW,
++	SFP_E_TIMEOUT,
++
++	SFP_MOD_EMPTY = 0,
++	SFP_MOD_PROBE,
++	SFP_MOD_PRESENT,
++	SFP_MOD_ERROR,
++
++	SFP_DEV_DOWN = 0,
++	SFP_DEV_UP,
++
++	SFP_S_DOWN = 0,
++	SFP_S_INIT,
++	SFP_S_WAIT_LOS,
++	SFP_S_LINK_UP,
++	SFP_S_TX_FAULT,
++	SFP_S_REINIT,
++	SFP_S_TX_DISABLE,
++};
++
++static const char *gpio_of_names[] = {
++	"moddef0",
++	"los",
++	"tx-fault",
++	"tx-disable",
++	"rate-select",
++};
++
++static const enum gpiod_flags gpio_flags[] = {
++	GPIOD_IN,
++	GPIOD_IN,
++	GPIOD_IN,
++	GPIOD_ASIS,
++	GPIOD_ASIS,
++};
++
++#define T_INIT_JIFFIES	msecs_to_jiffies(300)
++#define T_RESET_US	10
++#define T_FAULT_RECOVER	msecs_to_jiffies(1000)
++
++/* SFP module presence detection is poor: the three MOD DEF signals are
++ * the same length on the PCB, which means it's possible for MOD DEF 0 to
++ * connect before the I2C bus on MOD DEF 1/2.
++ *
++ * The SFP MSA specifies 300ms as t_init (the time taken for TX_FAULT to
++ * be deasserted) but makes no mention of the earliest time before we can
++ * access the I2C EEPROM.  However, Avago modules require 300ms.
++ */
++#define T_PROBE_INIT	msecs_to_jiffies(300)
++#define T_PROBE_RETRY	msecs_to_jiffies(100)
++
++/*
++ * SFP modules appear to always have their PHY configured for bus address
++ * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
++ */
++#define SFP_PHY_ADDR	22
++
++/*
++ * Give this long for the PHY to reset.
++ */
++#define T_PHY_RESET_MS	50
++
++static DEFINE_MUTEX(sfp_mutex);
++
++struct sfp {
++	struct device *dev;
++	struct i2c_adapter *i2c;
++	struct mii_bus *i2c_mii;
++	struct net_device *ndev;
++	struct phylink *phylink;
++	struct phy_device *mod_phy;
++
++	unsigned int (*get_state)(struct sfp *);
++	void (*set_state)(struct sfp *, unsigned int);
++	int (*read)(struct sfp *, bool, u8, void *, size_t);
++
++	struct gpio_desc *gpio[GPIO_MAX];
++
++	unsigned int state;
++	struct delayed_work poll;
++	struct delayed_work timeout;
++	struct mutex sm_mutex;
++	unsigned char sm_mod_state;
++	unsigned char sm_dev_state;
++	unsigned short sm_state;
++	unsigned int sm_retries;
++
++	struct sfp_eeprom_id id;
++
++	struct notifier_block netdev_nb;
++};
++
++static unsigned long poll_jiffies;
++
++static unsigned int sfp_gpio_get_state(struct sfp *sfp)
++{
++	unsigned int i, state, v;
++
++	for (i = state = 0; i < GPIO_MAX; i++) {
++		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++			continue;
++
++		v = gpiod_get_value_cansleep(sfp->gpio[i]);
++		if (v)
++			state |= BIT(i);
++	}
++
++	return state;
++}
++
++static void sfp_gpio_set_state(struct sfp *sfp, unsigned int state)
++{
++	if (state & SFP_F_PRESENT) {
++		/* If the module is present, drive the signals */
++		if (sfp->gpio[GPIO_TX_DISABLE])
++			gpiod_direction_output(sfp->gpio[GPIO_TX_DISABLE],
++						state & SFP_F_TX_DISABLE);
++		if (state & SFP_F_RATE_SELECT)
++			gpiod_direction_output(sfp->gpio[GPIO_RATE_SELECT],
++						state & SFP_F_RATE_SELECT);
++	} else {
++		/* Otherwise, let them float to the pull-ups */
++		if (sfp->gpio[GPIO_TX_DISABLE])
++			gpiod_direction_input(sfp->gpio[GPIO_TX_DISABLE]);
++		if (state & SFP_F_RATE_SELECT)
++			gpiod_direction_input(sfp->gpio[GPIO_RATE_SELECT]);
++	}
++}
++
++static int sfp__i2c_read(struct i2c_adapter *i2c, u8 bus_addr, u8 dev_addr,
++	void *buf, size_t len)
++{
++	struct i2c_msg msgs[2];
++	int ret;
++
++	msgs[0].addr = bus_addr;
++	msgs[0].flags = 0;
++	msgs[0].len = 1;
++	msgs[0].buf = &dev_addr;
++	msgs[1].addr = bus_addr;
++	msgs[1].flags = I2C_M_RD;
++	msgs[1].len = len;
++	msgs[1].buf = buf;
++
++	ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs));
++	if (ret < 0)
++		return ret;
++
++	return ret == ARRAY_SIZE(msgs) ? len : 0;
++}
++
++static int sfp_i2c_read(struct sfp *sfp, bool a2, u8 addr, void *buf,
++	size_t len)
++{
++	return sfp__i2c_read(sfp->i2c, a2 ? 0x51 : 0x50, addr, buf, len);
++}
++
++static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
++{
++	struct mii_bus *i2c_mii;
++	int ret;
++
++	if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
++		return -EINVAL;
++
++	sfp->i2c = i2c;
++	sfp->read = sfp_i2c_read;
++
++	i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
++	if (IS_ERR(i2c_mii))
++		return PTR_ERR(i2c_mii);
++
++	i2c_mii->name = "SFP I2C Bus";
++	i2c_mii->phy_mask = ~0;
++
++	ret = mdiobus_register(i2c_mii);
++	if (ret < 0) {
++		mdiobus_free(i2c_mii);
++		return ret;
++	}
++
++	sfp->i2c_mii = i2c_mii;
++
++	return 0;
++}
++
++
++/* Interface */
++static unsigned int sfp_get_state(struct sfp *sfp)
++{
++	return sfp->get_state(sfp);
++}
++
++static void sfp_set_state(struct sfp *sfp, unsigned int state)
++{
++	sfp->set_state(sfp, state);
++}
++
++static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
++{
++	return sfp->read(sfp, a2, addr, buf, len);
++}
++
++static unsigned int sfp_check(void *buf, size_t len)
++{
++	u8 *p, check;
++
++	for (p = buf, check = 0; len; p++, len--)
++		check += *p;
++
++	return check;
++}
++
++/* Helpers */
++static void sfp_module_tx_disable(struct sfp *sfp)
++{
++	dev_dbg(sfp->dev, "tx disable %u -> %u\n",
++		sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 1);
++	sfp->state |= SFP_F_TX_DISABLE;
++	sfp_set_state(sfp, sfp->state);
++}
++
++static void sfp_module_tx_enable(struct sfp *sfp)
++{
++	dev_dbg(sfp->dev, "tx disable %u -> %u\n",
++		sfp->state & SFP_F_TX_DISABLE ? 1 : 0, 0);
++	sfp->state &= ~SFP_F_TX_DISABLE;
++	sfp_set_state(sfp, sfp->state);
++}
++
++static void sfp_module_tx_fault_reset(struct sfp *sfp)
++{
++	unsigned int state = sfp->state;
++
++	if (state & SFP_F_TX_DISABLE)
++		return;
++
++	sfp_set_state(sfp, state | SFP_F_TX_DISABLE);
++
++	udelay(T_RESET_US);
++
++	sfp_set_state(sfp, state);
++}
++
++/* SFP state machine */
++static void sfp_sm_set_timer(struct sfp *sfp, unsigned int timeout)
++{
++	if (timeout)
++		mod_delayed_work(system_power_efficient_wq, &sfp->timeout,
++				 timeout);
++	else
++		cancel_delayed_work(&sfp->timeout);
++}
++
++static void sfp_sm_next(struct sfp *sfp, unsigned int state,
++			unsigned int timeout)
++{
++	sfp->sm_state = state;
++	sfp_sm_set_timer(sfp, timeout);
++}
++
++static void sfp_sm_ins_next(struct sfp *sfp, unsigned int state, unsigned int timeout)
++{
++	sfp->sm_mod_state = state;
++	sfp_sm_set_timer(sfp, timeout);
++}
++
++static void sfp_sm_phy_detach(struct sfp *sfp)
++{
++	phy_stop(sfp->mod_phy);
++	if (sfp->phylink)
++		phylink_disconnect_phy(sfp->phylink);
++	phy_device_remove(sfp->mod_phy);
++	phy_device_free(sfp->mod_phy);
++	sfp->mod_phy = NULL;
++}
++
++static void sfp_sm_probe_phy(struct sfp *sfp)
++{
++	struct phy_device *phy;
++	int err;
++
++	msleep(T_PHY_RESET_MS);
++
++	phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
++	if (IS_ERR(phy)) {
++		dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
++		return;
++	}
++	if (!phy) {
++		dev_info(sfp->dev, "no PHY detected\n");
++		return;
++	}
++
++	if (sfp->phylink) {
++		err = phylink_connect_phy(sfp->phylink, phy);
++		if (err) {
++			phy_device_remove(phy);
++			phy_device_free(phy);
++			dev_err(sfp->dev, "phylink_connect_phy failed: %d\n",
++				err);
++			return;
++		}
++	}
++
++	sfp->mod_phy = phy;
++	phy_start(phy);
++}
++
++static void sfp_sm_link_up(struct sfp *sfp)
++{
++	if (sfp->phylink)
++		phylink_enable(sfp->phylink);
++
++	sfp_sm_next(sfp, SFP_S_LINK_UP, 0);
++}
++
++static void sfp_sm_link_down(struct sfp *sfp)
++{
++	if (sfp->phylink)
++		phylink_disable(sfp->phylink);
++}
++
++static void sfp_sm_link_check_los(struct sfp *sfp)
++{
++	unsigned int los = sfp->state & SFP_F_LOS;
++
++	/* FIXME: what if neither SFP_OPTIONS_LOS_INVERTED nor
++	 * SFP_OPTIONS_LOS_NORMAL are set?  For now, we assume
++	 * the same as SFP_OPTIONS_LOS_NORMAL set.
++	 */
++	if (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED)
++		los ^= SFP_F_LOS;
++
++	if (los)
++		sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
++	else
++		sfp_sm_link_up(sfp);
++}
++
++static void sfp_sm_fault(struct sfp *sfp, bool warn)
++{
++	if (sfp->sm_retries && !--sfp->sm_retries) {
++		dev_err(sfp->dev, "module persistently indicates fault, disabling\n");
++		sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
++	} else {
++		if (warn)
++			dev_err(sfp->dev, "module transmit fault indicated\n");
++
++		sfp_sm_next(sfp, SFP_S_TX_FAULT, T_FAULT_RECOVER);
++	}
++}
++
++static void sfp_sm_mod_init(struct sfp *sfp)
++{
++	sfp_module_tx_enable(sfp);
++
++	/* Wait t_init before indicating that the link is up, provided the
++	 * current state indicates no TX_FAULT.  If TX_FAULT clears before
++	 * this time, that's fine too.
++	 */
++	sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
++	sfp->sm_retries = 5;
++
++	if (sfp->phylink) {
++		/* Setting the serdes link mode is guesswork: there's no
++		 * field in the EEPROM which indicates what mode should
++		 * be used.
++		 *
++		 * If it's a gigabit-only fiber module, it probably does
++		 * not have a PHY, so switch to 802.3z negotiation mode.
++		 * Otherwise, switch to SGMII mode (which is required to
++		 * support non-gigabit speeds) and probe for a PHY.
++		 */
++		if (sfp->id.base.e1000_base_t ||
++		    sfp->id.base.e100_base_lx ||
++		    sfp->id.base.e100_base_fx)
++			sfp_sm_probe_phy(sfp);
++	}
++}
++
++static int sfp_sm_mod_probe(struct sfp *sfp)
++{
++	/* SFP module inserted - read I2C data */
++	struct sfp_eeprom_id id;
++	char vendor[17];
++	char part[17];
++	char sn[17];
++	char date[9];
++	char rev[5];
++	u8 check;
++	int err;
++
++	err = sfp_read(sfp, false, 0, &id, sizeof(id));
++	if (err < 0) {
++		dev_err(sfp->dev, "failed to read EEPROM: %d\n", err);
++		return -EAGAIN;
++	}
++
++	if (err != sizeof(id)) {
++		dev_err(sfp->dev, "EEPROM short read: %d\n", err);
++		return -EAGAIN;
++	}
++
++	/* Validate the checksum over the base structure */
++	check = sfp_check(&id.base, sizeof(id.base) - 1);
++	if (check != id.base.cc_base) {
++		dev_err(sfp->dev,
++			"EEPROM base structure checksum failure: 0x%02x\n",
++			check);
++		print_hex_dump(KERN_ERR, "sfp EE: ", DUMP_PREFIX_OFFSET,
++			       16, 1, &id, sizeof(id.base) - 1, true);
++		return -EINVAL;
++	}
++
++	check = sfp_check(&id.ext, sizeof(id.ext) - 1);
++	if (check != id.ext.cc_ext) {
++		dev_err(sfp->dev,
++			"EEPROM extended structure checksum failure: 0x%02x\n",
++			check);
++		memset(&id.ext, 0, sizeof(id.ext));
++	}
++
++	sfp->id = id;
++
++	memcpy(vendor, sfp->id.base.vendor_name, 16);
++	vendor[16] = '\0';
++	memcpy(part, sfp->id.base.vendor_pn, 16);
++	part[16] = '\0';
++	memcpy(rev, sfp->id.base.vendor_rev, 4);
++	rev[4] = '\0';
++	memcpy(sn, sfp->id.ext.vendor_sn, 16);
++	sn[16] = '\0';
++	memcpy(date, sfp->id.ext.datecode, 8);
++	date[8] = '\0';
++
++	dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date);
++
++	/* We only support SFP modules, not the legacy GBIC modules. */
++	if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP ||
++	    sfp->id.base.phys_ext_id != SFP_PHYS_EXT_ID_SFP) {
++		dev_err(sfp->dev, "module is not SFP - phys id 0x%02x 0x%02x\n",
++			sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
++		return -EINVAL;
++	}
++
++	/*
++	 * What isn't clear from the SFP documentation is whether this
++	 * specifies the encoding expected on the TD/RD lines, or whether
++	 * the TD/RD lines are always 8b10b encoded, but the transceiver
++	 * converts.  Eg, think of a copper SFP supporting 1G/100M/10M
++	 * ethernet: this requires 8b10b encoding for 1G, 4b5b for 100M,
++	 * and manchester for 10M.
++	 */
++	/* 1Gbit ethernet requires 8b10b encoding */
++	if (sfp->id.base.encoding != SFP_ENCODING_8B10B) {
++		dev_err(sfp->dev, "module does not support 8B10B encoding\n");
++		return -EINVAL;
++	}
++
++	if (sfp->phylink) {
++		__ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
++		int mode;
++		u8 port;
++
++		phylink_set(support, Autoneg);
++		phylink_set(support, Pause);
++		phylink_set(support, Asym_Pause);
++
++		/* Set ethtool support from the compliance fields. */
++		if (sfp->id.base.e10g_base_sr)
++			phylink_set(support, 10000baseSR_Full);
++		if (sfp->id.base.e10g_base_lr)
++			phylink_set(support, 10000baseLR_Full);
++		if (sfp->id.base.e10g_base_lrm)
++			phylink_set(support, 10000baseLRM_Full);
++		if (sfp->id.base.e10g_base_er)
++			phylink_set(support, 10000baseER_Full);
++		if (sfp->id.base.e1000_base_sx ||
++		    sfp->id.base.e1000_base_lx ||
++		    sfp->id.base.e1000_base_cx)
++			phylink_set(support, 1000baseX_Full);
++		if (sfp->id.base.e1000_base_t) {
++			phylink_set(support, 1000baseT_Half);
++			phylink_set(support, 1000baseT_Full);
++		}
++
++		/* port is the physical connector, set this from the
++		 * connector field.
++		 */
++		switch (sfp->id.base.connector) {
++		case SFP_CONNECTOR_SC:
++		case SFP_CONNECTOR_FIBERJACK:
++		case SFP_CONNECTOR_LC:
++		case SFP_CONNECTOR_MT_RJ:
++		case SFP_CONNECTOR_MU:
++		case SFP_CONNECTOR_OPTICAL_PIGTAIL:
++			phylink_set(support, FIBRE);
++			port = PORT_FIBRE;
++			break;
++
++		case SFP_CONNECTOR_RJ45:
++			phylink_set(support, TP);
++			port = PORT_TP;
++			break;
++
++		case SFP_CONNECTOR_UNSPEC:
++			if (sfp->id.base.e1000_base_t) {
++				phylink_set(support, TP);
++				port = PORT_TP;
++				break;
++			}
++			/* fallthrough */
++		case SFP_CONNECTOR_SG: /* guess */
++		case SFP_CONNECTOR_MPO_1X12:
++		case SFP_CONNECTOR_MPO_2X16:
++		case SFP_CONNECTOR_HSSDC_II:
++		case SFP_CONNECTOR_COPPER_PIGTAIL:
++		case SFP_CONNECTOR_NOSEPARATE:
++		case SFP_CONNECTOR_MXC_2X16:
++		default:
++			/* a guess at the supported link modes */
++			dev_warn(sfp->dev, "Guessing link modes, please report...\n");
++			phylink_set(support, 1000baseT_Half);
++			phylink_set(support, 1000baseT_Full);
++			port = PORT_OTHER;
++			break;
++		}
++
++		/* Setting the serdes link mode is guesswork: there's no
++		 * field in the EEPROM which indicates what mode should
++		 * be used.
++		 *
++		 * If it's a gigabit-only fiber module, it probably does
++		 * not have a PHY, so switch to 802.3z negotiation mode.
++		 * Otherwise, switch to SGMII mode (which is required to
++		 * support non-gigabit speeds) and probe for a PHY.
++		 */
++		if (!sfp->id.base.e1000_base_t &&
++		    !sfp->id.base.e100_base_lx &&
++		    !sfp->id.base.e100_base_fx) {
++			mode = MLO_AN_8023Z;
++		} else {
++			mode = MLO_AN_SGMII;
++		}
++
++		phylink_set_link(sfp->phylink, mode, port, support);
++	}
++
++	return 0;
++}
++
++static void sfp_sm_mod_remove(struct sfp *sfp)
++{
++	if (sfp->mod_phy)
++		sfp_sm_phy_detach(sfp);
++
++	sfp_module_tx_disable(sfp);
++
++	memset(&sfp->id, 0, sizeof(sfp->id));
++
++	dev_info(sfp->dev, "module removed\n");
++}
++
++static void sfp_sm_event(struct sfp *sfp, unsigned int event)
++{
++	mutex_lock(&sfp->sm_mutex);
++
++	dev_dbg(sfp->dev, "SM: enter %u:%u:%u event %u\n",
++		sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state, event);
++
++	/* This state machine tracks the insert/remove state of
++	 * the module, and handles probing the on-board EEPROM.
++	 */
++	switch (sfp->sm_mod_state) {
++	default:
++		if (event == SFP_E_INSERT) {
++			sfp_module_tx_disable(sfp);
++			sfp_sm_ins_next(sfp, SFP_MOD_PROBE, T_PROBE_INIT);
++		}
++		break;
++
++	case SFP_MOD_PROBE:
++		if (event == SFP_E_REMOVE) {
++			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
++		} else if (event == SFP_E_TIMEOUT) {
++			int err = sfp_sm_mod_probe(sfp);
++
++			if (err == 0)
++				sfp_sm_ins_next(sfp, SFP_MOD_PRESENT, 0);
++			else if (err == -EAGAIN)
++				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
++			else
++				sfp_sm_ins_next(sfp, SFP_MOD_ERROR, 0);
++		}
++		break;
++
++	case SFP_MOD_PRESENT:
++	case SFP_MOD_ERROR:
++		if (event == SFP_E_REMOVE) {
++			sfp_sm_mod_remove(sfp);
++			sfp_sm_ins_next(sfp, SFP_MOD_EMPTY, 0);
++		}
++		break;
++	}
++
++	/* This state machine tracks the netdev up/down state */
++	switch (sfp->sm_dev_state) {
++	default:
++		if (event == SFP_E_DEV_UP)
++			sfp->sm_dev_state = SFP_DEV_UP;
++		break;
++
++	case SFP_DEV_UP:
++		if (event == SFP_E_DEV_DOWN) {
++			/* If the module has a PHY, avoid raising TX disable
++			 * as this resets the PHY. Otherwise, raise it to
++			 * turn the laser off.
++			 */
++			if (!sfp->mod_phy)
++				sfp_module_tx_disable(sfp);
++			sfp->sm_dev_state = SFP_DEV_DOWN;
++		}
++		break;
++	}
++
++	/* Some events are global */
++	if (sfp->sm_state != SFP_S_DOWN &&
++	    (sfp->sm_mod_state != SFP_MOD_PRESENT ||
++	     sfp->sm_dev_state != SFP_DEV_UP)) {
++		if (sfp->sm_state == SFP_S_LINK_UP &&
++		    sfp->sm_dev_state == SFP_DEV_UP)
++			sfp_sm_link_down(sfp);
++		if (sfp->mod_phy)
++			sfp_sm_phy_detach(sfp);
++		sfp_sm_next(sfp, SFP_S_DOWN, 0);
++		mutex_unlock(&sfp->sm_mutex);
++		return;
++	}
++
++	/* The main state machine */
++	switch (sfp->sm_state) {
++	case SFP_S_DOWN:
++		if (sfp->sm_mod_state == SFP_MOD_PRESENT &&
++		    sfp->sm_dev_state == SFP_DEV_UP)
++			sfp_sm_mod_init(sfp);
++		break;
++
++	case SFP_S_INIT:
++		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT)
++			sfp_sm_fault(sfp, true);
++		else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR)
++			sfp_sm_link_check_los(sfp);
++		break;
++
++	case SFP_S_WAIT_LOS:
++		if (event == SFP_E_TX_FAULT)
++			sfp_sm_fault(sfp, true);
++		else if (event ==
++			 (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ?
++			  SFP_E_LOS_HIGH : SFP_E_LOS_LOW))
++			sfp_sm_link_up(sfp);
++		break;
++
++	case SFP_S_LINK_UP:
++		if (event == SFP_E_TX_FAULT) {
++			sfp_sm_link_down(sfp);
++			sfp_sm_fault(sfp, true);
++		} else if (event ==
++			   (sfp->id.ext.options & SFP_OPTIONS_LOS_INVERTED ?
++			    SFP_E_LOS_LOW : SFP_E_LOS_HIGH)) {
++			sfp_sm_link_down(sfp);
++			sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
++		}
++		break;
++
++	case SFP_S_TX_FAULT:
++		if (event == SFP_E_TIMEOUT) {
++			sfp_module_tx_fault_reset(sfp);
++			sfp_sm_next(sfp, SFP_S_REINIT, T_INIT_JIFFIES);
++		}
++		break;
++
++	case SFP_S_REINIT:
++		if (event == SFP_E_TIMEOUT && sfp->state & SFP_F_TX_FAULT) {
++			sfp_sm_fault(sfp, false);
++		} else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
++			dev_info(sfp->dev, "module transmit fault recovered\n");
++			sfp_sm_link_check_los(sfp);
++		}
++		break;
++
++	case SFP_S_TX_DISABLE:
++		break;
++	}
++
++	dev_dbg(sfp->dev, "SM: exit %u:%u:%u\n",
++		sfp->sm_mod_state, sfp->sm_dev_state, sfp->sm_state);
++
++	mutex_unlock(&sfp->sm_mutex);
++}
++
++#if 0
++static int sfp_phy_module_info(struct phy_device *phy,
++			       struct ethtool_modinfo *modinfo)
++{
++	struct sfp *sfp = phy->priv;
++
++	/* locking... and check module is present */
++
++	if (sfp->id.ext.sff8472_compliance) {
++		modinfo->type = ETH_MODULE_SFF_8472;
++		modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN;
++	} else {
++		modinfo->type = ETH_MODULE_SFF_8079;
++		modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN;
++	}
++	return 0;
++}
++
++static int sfp_phy_module_eeprom(struct phy_device *phy,
++	struct ethtool_eeprom *ee, u8 *data)
++{
++	struct sfp *sfp = phy->priv;
++	unsigned int first, last, len;
++	int ret;
++
++	if (ee->len == 0)
++		return -EINVAL;
++
++	first = ee->offset;
++	last = ee->offset + ee->len;
++	if (first < ETH_MODULE_SFF_8079_LEN) {
++		len = last;
++		if (len > ETH_MODULE_SFF_8079_LEN)
++			len = ETH_MODULE_SFF_8079_LEN;
++		len -= first;
++
++		ret = sfp->read(sfp, false, first, data, len);
++		if (ret < 0)
++			return ret;
++
++		first += len;
++		data += len;
++	}
++	if (first >= ETH_MODULE_SFF_8079_LEN && last > first) {
++		len = last - first;
++
++		ret = sfp->read(sfp, true, first, data, len);
++		if (ret < 0)
++			return ret;
++	}
++	return 0;
++}
++#endif
++
++static void sfp_timeout(struct work_struct *work)
++{
++	struct sfp *sfp = container_of(work, struct sfp, timeout.work);
++
++	sfp_sm_event(sfp, SFP_E_TIMEOUT);
++}
++
++static void sfp_check_state(struct sfp *sfp)
++{
++	unsigned int state, i, changed;
++
++	state = sfp_get_state(sfp);
++	changed = state ^ sfp->state;
++	changed &= SFP_F_PRESENT | SFP_F_LOS | SFP_F_TX_FAULT;
++
++	for (i = 0; i < GPIO_MAX; i++)
++		if (changed & BIT(i))
++			dev_dbg(sfp->dev, "%s %u -> %u\n", gpio_of_names[i],
++				!!(sfp->state & BIT(i)), !!(state & BIT(i)));
++
++	state |= sfp->state & (SFP_F_TX_DISABLE | SFP_F_RATE_SELECT);
++	sfp->state = state;
++
++	if (changed & SFP_F_PRESENT)
++		sfp_sm_event(sfp, state & SFP_F_PRESENT ?
++				SFP_E_INSERT : SFP_E_REMOVE);
++
++	if (changed & SFP_F_TX_FAULT)
++		sfp_sm_event(sfp, state & SFP_F_TX_FAULT ?
++				SFP_E_TX_FAULT : SFP_E_TX_CLEAR);
++
++	if (changed & SFP_F_LOS)
++		sfp_sm_event(sfp, state & SFP_F_LOS ?
++				SFP_E_LOS_HIGH : SFP_E_LOS_LOW);
++}
++
++static irqreturn_t sfp_irq(int irq, void *data)
++{
++	struct sfp *sfp = data;
++
++	sfp_check_state(sfp);
++
++	return IRQ_HANDLED;
++}
++
++static void sfp_poll(struct work_struct *work)
++{
++	struct sfp *sfp = container_of(work, struct sfp, poll.work);
++
++	sfp_check_state(sfp);
++	mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
++}
++
++static int sfp_netdev_notify(struct notifier_block *nb, unsigned long act, void *data)
++{
++	struct sfp *sfp = container_of(nb, struct sfp, netdev_nb);
++	struct netdev_notifier_info *info = data;
++	struct net_device *ndev = info->dev;
++
++	if (!sfp->ndev || ndev != sfp->ndev)
++		return NOTIFY_DONE;
++
++	switch (act) {
++	case NETDEV_UP:
++		sfp_sm_event(sfp, SFP_E_DEV_UP);
++		break;
++
++	case NETDEV_GOING_DOWN:
++		sfp_sm_event(sfp, SFP_E_DEV_DOWN);
++		break;
++
++	case NETDEV_UNREGISTER:
++		if (sfp->mod_phy && sfp->phylink)
++			phylink_disconnect_phy(sfp->phylink);
++		sfp->phylink = NULL;
++		dev_put(sfp->ndev);
++		sfp->ndev = NULL;
++		break;
++	}
++	return NOTIFY_OK;
++}
++
++static struct sfp *sfp_alloc(struct device *dev)
++{
++	struct sfp *sfp;
++
++	sfp = kzalloc(sizeof(*sfp), GFP_KERNEL);
++	if (!sfp)
++		return ERR_PTR(-ENOMEM);
++
++	sfp->dev = dev;
++
++	mutex_init(&sfp->sm_mutex);
++	INIT_DELAYED_WORK(&sfp->poll, sfp_poll);
++	INIT_DELAYED_WORK(&sfp->timeout, sfp_timeout);
++
++	sfp->netdev_nb.notifier_call = sfp_netdev_notify;
++
++	return sfp;
++}
++
++static void sfp_destroy(struct sfp *sfp)
++{
++	cancel_delayed_work_sync(&sfp->poll);
++	cancel_delayed_work_sync(&sfp->timeout);
++	if (sfp->i2c_mii) {
++		mdiobus_unregister(sfp->i2c_mii);
++		mdiobus_free(sfp->i2c_mii);
++	}
++	if (sfp->i2c)
++		i2c_put_adapter(sfp->i2c);
++	of_node_put(sfp->dev->of_node);
++	kfree(sfp);
++}
++
++static void sfp_cleanup(void *data)
++{
++	struct sfp *sfp = data;
++
++	sfp_destroy(sfp);
++}
++
++static int sfp_probe(struct platform_device *pdev)
++{
++	struct sfp *sfp;
++	bool poll = false;
++	int irq, err, i;
++
++	sfp = sfp_alloc(&pdev->dev);
++	if (IS_ERR(sfp))
++		return PTR_ERR(sfp);
++
++	platform_set_drvdata(pdev, sfp);
++
++	err = devm_add_action(sfp->dev, sfp_cleanup, sfp);
++	if (err < 0)
++		return err;
++
++	if (pdev->dev.of_node) {
++		struct device_node *node = pdev->dev.of_node;
++		struct device_node *np;
++
++		np = of_parse_phandle(node, "i2c-bus", 0);
++		if (np) {
++			struct i2c_adapter *i2c;
++
++			i2c = of_find_i2c_adapter_by_node(np);
++			of_node_put(np);
++			if (!i2c)
++				return -EPROBE_DEFER;
++
++			err = sfp_i2c_configure(sfp, i2c);
++			if (err < 0) {
++				i2c_put_adapter(i2c);
++				return err;
++			}
++		}
++
++		for (i = 0; i < GPIO_MAX; i++) {
++			sfp->gpio[i] = devm_gpiod_get_optional(sfp->dev,
++					   gpio_of_names[i], gpio_flags[i]);
++			if (IS_ERR(sfp->gpio[i]))
++				return PTR_ERR(sfp->gpio[i]);
++		}
++
++		sfp->get_state = sfp_gpio_get_state;
++		sfp->set_state = sfp_gpio_set_state;
++
++		np = of_parse_phandle(node, "sfp,ethernet", 0);
++		if (!np) {
++			dev_err(sfp->dev, "missing sfp,ethernet property\n");
++			return -EINVAL;
++		}
++
++		sfp->ndev = of_find_net_device_by_node(np);
++		if (!sfp->ndev) {
++			dev_err(sfp->dev, "ethernet device not found\n");
++			return -EPROBE_DEFER;
++		}
++
++		dev_hold(sfp->ndev);
++		put_device(&sfp->ndev->dev);
++
++		sfp->phylink = phylink_lookup_by_netdev(sfp->ndev);
++		if (!sfp->phylink) {
++			dev_err(sfp->dev, "phylink for %s not found\n",
++				netdev_name(sfp->ndev));
++			return -EPROBE_DEFER;
++		}
++
++		phylink_disable(sfp->phylink);
++	}
++
++	sfp->state = sfp_get_state(sfp);
++	if (sfp->gpio[GPIO_TX_DISABLE] &&
++	    gpiod_get_value_cansleep(sfp->gpio[GPIO_TX_DISABLE]))
++		sfp->state |= SFP_F_TX_DISABLE;
++	if (sfp->gpio[GPIO_RATE_SELECT] &&
++	    gpiod_get_value_cansleep(sfp->gpio[GPIO_RATE_SELECT]))
++		sfp->state |= SFP_F_RATE_SELECT;
++	sfp_set_state(sfp, sfp->state);
++	sfp_module_tx_disable(sfp);
++	if (sfp->state & SFP_F_PRESENT)
++		sfp_sm_event(sfp, SFP_E_INSERT);
++
++	for (i = 0; i < GPIO_MAX; i++) {
++		if (gpio_flags[i] != GPIOD_IN || !sfp->gpio[i])
++			continue;
++
++		irq = gpiod_to_irq(sfp->gpio[i]);
++		if (!irq) {
++			poll = true;
++			continue;
++		}
++
++		err = devm_request_threaded_irq(sfp->dev, irq, NULL, sfp_irq,
++						IRQF_ONESHOT |
++						IRQF_TRIGGER_RISING |
++						IRQF_TRIGGER_FALLING,
++						dev_name(sfp->dev), sfp);
++		if (err)
++			poll = true;
++	}
++
++	if (poll)
++		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
++
++	register_netdevice_notifier(&sfp->netdev_nb);
++
++	return 0;
++}
++
++static int sfp_remove(struct platform_device *pdev)
++{
++	struct sfp *sfp = platform_get_drvdata(pdev);
++
++	unregister_netdevice_notifier(&sfp->netdev_nb);
++	if (sfp->ndev)
++		dev_put(sfp->ndev);
++
++	return 0;
++}
++
++static const struct of_device_id sfp_of_match[] = {
++	{ .compatible = "sff,sfp", },
++	{ },
++};
++MODULE_DEVICE_TABLE(of, sfp_of_match);
++
++static struct platform_driver sfp_driver = {
++	.probe = sfp_probe,
++	.remove = sfp_remove,
++	.driver = {
++		.name = "sfp",
++		.of_match_table = sfp_of_match,
++	},
++};
++
++static int sfp_init(void)
++{
++	poll_jiffies = msecs_to_jiffies(100);
++
++	return platform_driver_register(&sfp_driver);
++}
++module_init(sfp_init);
++
++static void sfp_exit(void)
++{
++	platform_driver_unregister(&sfp_driver);
++}
++module_exit(sfp_exit);
++
++MODULE_ALIAS("platform:sfp");
++MODULE_AUTHOR("Russell King");
++MODULE_LICENSE("GPL v2");
+--- /dev/null
++++ b/include/linux/sfp.h
+@@ -0,0 +1,344 @@
++#ifndef LINUX_SFP_H
++#define LINUX_SFP_H
++
++struct __packed sfp_eeprom_base {
++	u8 phys_id;
++	u8 phys_ext_id;
++	u8 connector;
++#if defined __BIG_ENDIAN_BITFIELD
++	u8 e10g_base_er:1;
++	u8 e10g_base_lrm:1;
++	u8 e10g_base_lr:1;
++	u8 e10g_base_sr:1;
++	u8 if_1x_sx:1;
++	u8 if_1x_lx:1;
++	u8 if_1x_copper_active:1;
++	u8 if_1x_copper_passive:1;
++
++	u8 escon_mmf_1310_led:1;
++	u8 escon_smf_1310_laser:1;
++	u8 sonet_oc192_short_reach:1;
++	u8 sonet_reach_bit1:1;
++	u8 sonet_reach_bit2:1;
++	u8 sonet_oc48_long_reach:1;
++	u8 sonet_oc48_intermediate_reach:1;
++	u8 sonet_oc48_short_reach:1;
++
++	u8 unallocated_5_7:1;
++	u8 sonet_oc12_smf_long_reach:1;
++	u8 sonet_oc12_smf_intermediate_reach:1;
++	u8 sonet_oc12_short_reach:1;
++	u8 unallocated_5_3:1;
++	u8 sonet_oc3_smf_long_reach:1;
++	u8 sonet_oc3_smf_intermediate_reach:1;
++	u8 sonet_oc3_short_reach:1;
++
++	u8 e_base_px:1;
++	u8 e_base_bx10:1;
++	u8 e100_base_fx:1;
++	u8 e100_base_lx:1;
++	u8 e1000_base_t:1;
++	u8 e1000_base_cx:1;
++	u8 e1000_base_lx:1;
++	u8 e1000_base_sx:1;
++
++	u8 fc_ll_v:1;
++	u8 fc_ll_s:1;
++	u8 fc_ll_i:1;
++	u8 fc_ll_l:1;
++	u8 fc_ll_m:1;
++	u8 fc_tech_sa:1;
++	u8 fc_tech_lc:1;
++	u8 fc_tech_electrical_inter_enclosure:1;
++
++	u8 fc_tech_electrical_intra_enclosure:1;
++	u8 fc_tech_sn:1;
++	u8 fc_tech_sl:1;
++	u8 fc_tech_ll:1;
++	u8 sfp_ct_active:1;
++	u8 sfp_ct_passive:1;
++	u8 unallocated_8_1:1;
++	u8 unallocated_8_0:1;
++
++	u8 fc_media_tw:1;
++	u8 fc_media_tp:1;
++	u8 fc_media_mi:1;
++	u8 fc_media_tv:1;
++	u8 fc_media_m6:1;
++	u8 fc_media_m5:1;
++	u8 unallocated_9_1:1;
++	u8 fc_media_sm:1;
++
++	u8 fc_speed_1200:1;
++	u8 fc_speed_800:1;
++	u8 fc_speed_1600:1;
++	u8 fc_speed_400:1;
++	u8 fc_speed_3200:1;
++	u8 fc_speed_200:1;
++	u8 unallocated_10_1:1;
++	u8 fc_speed_100:1;
++#elif defined __LITTLE_ENDIAN_BITFIELD
++	u8 if_1x_copper_passive:1;
++	u8 if_1x_copper_active:1;
++	u8 if_1x_lx:1;
++	u8 if_1x_sx:1;
++	u8 e10g_base_sr:1;
++	u8 e10g_base_lr:1;
++	u8 e10g_base_lrm:1;
++	u8 e10g_base_er:1;
++
++	u8 sonet_oc3_short_reach:1;
++	u8 sonet_oc3_smf_intermediate_reach:1;
++	u8 sonet_oc3_smf_long_reach:1;
++	u8 unallocated_5_3:1;
++	u8 sonet_oc12_short_reach:1;
++	u8 sonet_oc12_smf_intermediate_reach:1;
++	u8 sonet_oc12_smf_long_reach:1;
++	u8 unallocated_5_7:1;
++
++	u8 sonet_oc48_short_reach:1;
++	u8 sonet_oc48_intermediate_reach:1;
++	u8 sonet_oc48_long_reach:1;
++	u8 sonet_reach_bit2:1;
++	u8 sonet_reach_bit1:1;
++	u8 sonet_oc192_short_reach:1;
++	u8 escon_smf_1310_laser:1;
++	u8 escon_mmf_1310_led:1;
++
++	u8 e1000_base_sx:1;
++	u8 e1000_base_lx:1;
++	u8 e1000_base_cx:1;
++	u8 e1000_base_t:1;
++	u8 e100_base_lx:1;
++	u8 e100_base_fx:1;
++	u8 e_base_bx10:1;
++	u8 e_base_px:1;
++
++	u8 fc_tech_electrical_inter_enclosure:1;
++	u8 fc_tech_lc:1;
++	u8 fc_tech_sa:1;
++	u8 fc_ll_m:1;
++	u8 fc_ll_l:1;
++	u8 fc_ll_i:1;
++	u8 fc_ll_s:1;
++	u8 fc_ll_v:1;
++
++	u8 unallocated_8_0:1;
++	u8 unallocated_8_1:1;
++	u8 sfp_ct_passive:1;
++	u8 sfp_ct_active:1;
++	u8 fc_tech_ll:1;
++	u8 fc_tech_sl:1;
++	u8 fc_tech_sn:1;
++	u8 fc_tech_electrical_intra_enclosure:1;
++
++	u8 fc_media_sm:1;
++	u8 unallocated_9_1:1;
++	u8 fc_media_m5:1;
++	u8 fc_media_m6:1;
++	u8 fc_media_tv:1;
++	u8 fc_media_mi:1;
++	u8 fc_media_tp:1;
++	u8 fc_media_tw:1;
++
++	u8 fc_speed_100:1;
++	u8 unallocated_10_1:1;
++	u8 fc_speed_200:1;
++	u8 fc_speed_3200:1;
++	u8 fc_speed_400:1;
++	u8 fc_speed_1600:1;
++	u8 fc_speed_800:1;
++	u8 fc_speed_1200:1;
++#else
++#error Unknown Endian
++#endif
++	u8 encoding;
++	u8 br_nominal;
++	u8 rate_id;
++	u8 link_len[6];
++	char vendor_name[16];
++	u8 reserved36;
++	char vendor_oui[3];
++	char vendor_pn[16];
++	char vendor_rev[4];
++	union {
++		__be16 optical_wavelength;
++		u8 cable_spec;
++	};
++	u8 reserved62;
++	u8 cc_base;
++};
++
++struct __packed sfp_eeprom_ext {
++	__be16 options;
++	u8 br_max;
++	u8 br_min;
++	char vendor_sn[16];
++	char datecode[8];
++	u8 diagmon;
++	u8 enhopts;
++	u8 sff8472_compliance;
++	u8 cc_ext;
++};
++
++struct __packed sfp_eeprom_id {
++	struct sfp_eeprom_base base;
++	struct sfp_eeprom_ext ext;
++};
++
++/* SFP EEPROM registers */
++enum {
++	SFP_PHYS_ID			= 0x00,
++	SFP_PHYS_EXT_ID			= 0x01,
++	SFP_CONNECTOR			= 0x02,
++	SFP_COMPLIANCE			= 0x03,
++	SFP_ENCODING			= 0x0b,
++	SFP_BR_NOMINAL			= 0x0c,
++	SFP_RATE_ID			= 0x0d,
++	SFP_LINK_LEN_SM_KM		= 0x0e,
++	SFP_LINK_LEN_SM_100M		= 0x0f,
++	SFP_LINK_LEN_50UM_OM2_10M	= 0x10,
++	SFP_LINK_LEN_62_5UM_OM1_10M	= 0x11,
++	SFP_LINK_LEN_COPPER_1M		= 0x12,
++	SFP_LINK_LEN_50UM_OM4_10M	= 0x12,
++	SFP_LINK_LEN_50UM_OM3_10M	= 0x13,
++	SFP_VENDOR_NAME			= 0x14,
++	SFP_VENDOR_OUI			= 0x25,
++	SFP_VENDOR_PN			= 0x28,
++	SFP_VENDOR_REV			= 0x38,
++	SFP_OPTICAL_WAVELENGTH_MSB	= 0x3c,
++	SFP_OPTICAL_WAVELENGTH_LSB	= 0x3d,
++	SFP_CABLE_SPEC			= 0x3c,
++	SFP_CC_BASE			= 0x3f,
++	SFP_OPTIONS			= 0x40,	/* 2 bytes, MSB, LSB */
++	SFP_BR_MAX			= 0x42,
++	SFP_BR_MIN			= 0x43,
++	SFP_VENDOR_SN			= 0x44,
++	SFP_DATECODE			= 0x54,
++	SFP_DIAGMON			= 0x5c,
++	SFP_ENHOPTS			= 0x5d,
++	SFP_SFF8472_COMPLIANCE		= 0x5e,
++	SFP_CC_EXT			= 0x5f,
++
++	SFP_PHYS_ID_SFP			= 0x03,
++	SFP_PHYS_EXT_ID_SFP		= 0x04,
++	SFP_CONNECTOR_UNSPEC		= 0x00,
++	/* codes 01-05 not supportable on SFP, but some modules have single SC */
++	SFP_CONNECTOR_SC		= 0x01,
++	SFP_CONNECTOR_FIBERJACK		= 0x06,
++	SFP_CONNECTOR_LC		= 0x07,
++	SFP_CONNECTOR_MT_RJ		= 0x08,
++	SFP_CONNECTOR_MU		= 0x09,
++	SFP_CONNECTOR_SG		= 0x0a,
++	SFP_CONNECTOR_OPTICAL_PIGTAIL	= 0x0b,
++	SFP_CONNECTOR_MPO_1X12		= 0x0c,
++	SFP_CONNECTOR_MPO_2X16		= 0x0d,
++	SFP_CONNECTOR_HSSDC_II		= 0x20,
++	SFP_CONNECTOR_COPPER_PIGTAIL	= 0x21,
++	SFP_CONNECTOR_RJ45		= 0x22,
++	SFP_CONNECTOR_NOSEPARATE	= 0x23,
++	SFP_CONNECTOR_MXC_2X16		= 0x24,
++	SFP_ENCODING_UNSPEC		= 0x00,
++	SFP_ENCODING_8B10B		= 0x01,
++	SFP_ENCODING_4B5B		= 0x02,
++	SFP_ENCODING_NRZ		= 0x03,
++	SFP_ENCODING_MANCHESTER		= 0x04,
++	SFP_OPTIONS_HIGH_POWER_LEVEL	= BIT(13),
++	SFP_OPTIONS_PAGING_A2		= BIT(12),
++	SFP_OPTIONS_RETIMER		= BIT(11),
++	SFP_OPTIONS_COOLED_XCVR		= BIT(10),
++	SFP_OPTIONS_POWER_DECL		= BIT(9),
++	SFP_OPTIONS_RX_LINEAR_OUT	= BIT(8),
++	SFP_OPTIONS_RX_DECISION_THRESH	= BIT(7),
++	SFP_OPTIONS_TUNABLE_TX		= BIT(6),
++	SFP_OPTIONS_RATE_SELECT		= BIT(5),
++	SFP_OPTIONS_TX_DISABLE		= BIT(4),
++	SFP_OPTIONS_TX_FAULT		= BIT(3),
++	SFP_OPTIONS_LOS_INVERTED	= BIT(2),
++	SFP_OPTIONS_LOS_NORMAL		= BIT(1),
++	SFP_DIAGMON_DDM			= BIT(6),
++	SFP_DIAGMON_INT_CAL		= BIT(5),
++	SFP_DIAGMON_EXT_CAL		= BIT(4),
++	SFP_DIAGMON_RXPWR_AVG		= BIT(3),
++	SFP_DIAGMON_ADDRMODE		= BIT(2),
++	SFP_ENHOPTS_ALARMWARN		= BIT(7),
++	SFP_ENHOPTS_SOFT_TX_DISABLE	= BIT(6),
++	SFP_ENHOPTS_SOFT_TX_FAULT	= BIT(5),
++	SFP_ENHOPTS_SOFT_RX_LOS		= BIT(4),
++	SFP_ENHOPTS_SOFT_RATE_SELECT	= BIT(3),
++	SFP_ENHOPTS_APP_SELECT_SFF8079	= BIT(2),
++	SFP_ENHOPTS_SOFT_RATE_SFF8431	= BIT(1),
++	SFP_SFF8472_COMPLIANCE_NONE	= 0x00,
++	SFP_SFF8472_COMPLIANCE_REV9_3	= 0x01,
++	SFP_SFF8472_COMPLIANCE_REV9_5	= 0x02,
++	SFP_SFF8472_COMPLIANCE_REV10_2	= 0x03,
++	SFP_SFF8472_COMPLIANCE_REV10_4	= 0x04,
++	SFP_SFF8472_COMPLIANCE_REV11_0	= 0x05,
++	SFP_SFF8472_COMPLIANCE_REV11_3	= 0x06,
++	SFP_SFF8472_COMPLIANCE_REV11_4	= 0x07,
++	SFP_SFF8472_COMPLIANCE_REV12_0	= 0x08,
++};
++
++/* SFP Diagnostics */
++enum {
++	/* Alarm and warnings stored MSB at lower address then LSB */
++	SFP_TEMP_HIGH_ALARM		= 0x00,
++	SFP_TEMP_LOW_ALARM		= 0x02,
++	SFP_TEMP_HIGH_WARN		= 0x04,
++	SFP_TEMP_LOW_WARN		= 0x06,
++	SFP_VOLT_HIGH_ALARM		= 0x08,
++	SFP_VOLT_LOW_ALARM		= 0x0a,
++	SFP_VOLT_HIGH_WARN		= 0x0c,
++	SFP_VOLT_LOW_WARN		= 0x0e,
++	SFP_BIAS_HIGH_ALARM		= 0x10,
++	SFP_BIAS_LOW_ALARM		= 0x12,
++	SFP_BIAS_HIGH_WARN		= 0x14,
++	SFP_BIAS_LOW_WARN		= 0x16,
++	SFP_TXPWR_HIGH_ALARM		= 0x18,
++	SFP_TXPWR_LOW_ALARM		= 0x1a,
++	SFP_TXPWR_HIGH_WARN		= 0x1c,
++	SFP_TXPWR_LOW_WARN		= 0x1e,
++	SFP_RXPWR_HIGH_ALARM		= 0x20,
++	SFP_RXPWR_LOW_ALARM		= 0x22,
++	SFP_RXPWR_HIGH_WARN		= 0x24,
++	SFP_RXPWR_LOW_WARN		= 0x26,
++	SFP_LASER_TEMP_HIGH_ALARM	= 0x28,
++	SFP_LASER_TEMP_LOW_ALARM	= 0x2a,
++	SFP_LASER_TEMP_HIGH_WARN	= 0x2c,
++	SFP_LASER_TEMP_LOW_WARN		= 0x2e,
++	SFP_TEC_CUR_HIGH_ALARM		= 0x30,
++	SFP_TEC_CUR_LOW_ALARM		= 0x32,
++	SFP_TEC_CUR_HIGH_WARN		= 0x34,
++	SFP_TEC_CUR_LOW_WARN		= 0x36,
++	SFP_CAL_RXPWR4			= 0x38,
++	SFP_CAL_RXPWR3			= 0x3c,
++	SFP_CAL_RXPWR2			= 0x40,
++	SFP_CAL_RXPWR1			= 0x44,
++	SFP_CAL_RXPWR0			= 0x48,
++	SFP_CAL_TXI_SLOPE		= 0x4c,
++	SFP_CAL_TXI_OFFSET		= 0x4e,
++	SFP_CAL_TXPWR_SLOPE		= 0x50,
++	SFP_CAL_TXPWR_OFFSET		= 0x52,
++	SFP_CAL_T_SLOPE			= 0x54,
++	SFP_CAL_T_OFFSET		= 0x56,
++	SFP_CAL_V_SLOPE			= 0x58,
++	SFP_CAL_V_OFFSET		= 0x5a,
++	SFP_CHKSUM			= 0x5f,
++
++	SFP_TEMP			= 0x60,
++	SFP_VCC				= 0x62,
++	SFP_TX_BIAS			= 0x64,
++	SFP_TX_POWER			= 0x66,
++	SFP_RX_POWER			= 0x68,
++	SFP_LASER_TEMP			= 0x6a,
++	SFP_TEC_CUR			= 0x6c,
++
++	SFP_STATUS			= 0x6e,
++	SFP_ALARM			= 0x70,
++
++	SFP_EXT_STATUS			= 0x76,
++	SFP_VSL				= 0x78,
++	SFP_PAGE			= 0x7f,
++};
++
++#endif
diff --git a/target/linux/mvebu/patches-4.9/418-sfp-display-SFP-module-information.patch b/target/linux/mvebu/patches-4.9/418-sfp-display-SFP-module-information.patch
new file mode 100644
index 0000000..f0f2eda
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/418-sfp-display-SFP-module-information.patch
@@ -0,0 +1,280 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Sun, 13 Sep 2015 01:06:31 +0100
+Subject: [PATCH] sfp: display SFP module information
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -251,6 +251,182 @@ static unsigned int sfp_check(void *buf,
+ 	return check;
+ }
+ 
++static const char *sfp_link_len(char *buf, size_t size, unsigned int length,
++	unsigned int multiplier)
++{
++	if (length == 0)
++		return "unsupported/unspecified";
++
++	if (length == 255) {
++		*buf++ = '>';
++		size -= 1;
++		length -= 1;
++	}
++
++	length *= multiplier;
++
++	if (length >= 1000)
++		snprintf(buf, size, "%u.%0*ukm",
++			length / 1000,
++			multiplier > 100 ? 1 :
++			multiplier > 10 ? 2 : 3,
++			length % 1000);
++	else
++		snprintf(buf, size, "%um", length);
++
++	return buf;
++}
++
++struct bitfield {
++	unsigned int mask;
++	unsigned int val;
++	const char *str;
++};
++
++static const struct bitfield sfp_options[] = {
++	{
++		.mask = SFP_OPTIONS_HIGH_POWER_LEVEL,
++		.val = SFP_OPTIONS_HIGH_POWER_LEVEL,
++		.str = "hpl",
++	}, {
++		.mask = SFP_OPTIONS_PAGING_A2,
++		.val = SFP_OPTIONS_PAGING_A2,
++		.str = "paginga2",
++	}, {
++		.mask = SFP_OPTIONS_RETIMER,
++		.val = SFP_OPTIONS_RETIMER,
++		.str = "retimer",
++	}, {
++		.mask = SFP_OPTIONS_COOLED_XCVR,
++		.val = SFP_OPTIONS_COOLED_XCVR,
++		.str = "cooled",
++	}, {
++		.mask = SFP_OPTIONS_POWER_DECL,
++		.val = SFP_OPTIONS_POWER_DECL,
++		.str = "powerdecl",
++	}, {
++		.mask = SFP_OPTIONS_RX_LINEAR_OUT,
++		.val = SFP_OPTIONS_RX_LINEAR_OUT,
++		.str = "rxlinear",
++	}, {
++		.mask = SFP_OPTIONS_RX_DECISION_THRESH,
++		.val = SFP_OPTIONS_RX_DECISION_THRESH,
++		.str = "rxthresh",
++	}, {
++		.mask = SFP_OPTIONS_TUNABLE_TX,
++		.val = SFP_OPTIONS_TUNABLE_TX,
++		.str = "tunabletx",
++	}, {
++		.mask = SFP_OPTIONS_RATE_SELECT,
++		.val = SFP_OPTIONS_RATE_SELECT,
++		.str = "ratesel",
++	}, {
++		.mask = SFP_OPTIONS_TX_DISABLE,
++		.val = SFP_OPTIONS_TX_DISABLE,
++		.str = "txdisable",
++	}, {
++		.mask = SFP_OPTIONS_TX_FAULT,
++		.val = SFP_OPTIONS_TX_FAULT,
++		.str = "txfault",
++	}, {
++		.mask = SFP_OPTIONS_LOS_INVERTED,
++		.val = SFP_OPTIONS_LOS_INVERTED,
++		.str = "los-",
++	}, {
++		.mask = SFP_OPTIONS_LOS_NORMAL,
++		.val = SFP_OPTIONS_LOS_NORMAL,
++		.str = "los+",
++	}, { }
++};
++
++static const struct bitfield diagmon[] = {
++	{
++		.mask = SFP_DIAGMON_DDM,
++		.val = SFP_DIAGMON_DDM,
++		.str = "ddm",
++	}, {
++		.mask = SFP_DIAGMON_INT_CAL,
++		.val = SFP_DIAGMON_INT_CAL,
++		.str = "intcal",
++	}, {
++		.mask = SFP_DIAGMON_EXT_CAL,
++		.val = SFP_DIAGMON_EXT_CAL,
++		.str = "extcal",
++	}, {
++		.mask = SFP_DIAGMON_RXPWR_AVG,
++		.val = SFP_DIAGMON_RXPWR_AVG,
++		.str = "rxpwravg",
++	}, { }
++};
++
++static const char *sfp_bitfield(char *out, size_t outsz, const struct bitfield *bits, unsigned int val)
++{
++	char *p = out;
++	int n;
++
++	*p = '\0';
++	while (bits->mask) {
++		if ((val & bits->mask) == bits->val) {
++			n = snprintf(p, outsz, "%s%s",
++				     out != p ? ", " : "",
++				     bits->str);
++			if (n == outsz)
++				break;
++			p += n;
++			outsz -= n;
++		}
++		bits++;
++	}
++
++	return out;
++}
++
++static const char *sfp_connector(unsigned int connector)
++{
++	switch (connector) {
++	case SFP_CONNECTOR_UNSPEC:
++		return "unknown/unspecified";
++	case SFP_CONNECTOR_SC:
++		return "SC";
++	case SFP_CONNECTOR_FIBERJACK:
++		return "Fiberjack";
++	case SFP_CONNECTOR_LC:
++		return "LC";
++	case SFP_CONNECTOR_MT_RJ:
++		return "MT-RJ";
++	case SFP_CONNECTOR_MU:
++		return "MU";
++	case SFP_CONNECTOR_SG:
++		return "SG";
++	case SFP_CONNECTOR_OPTICAL_PIGTAIL:
++		return "Optical pigtail";
++	case SFP_CONNECTOR_HSSDC_II:
++		return "HSSDC II";
++	case SFP_CONNECTOR_COPPER_PIGTAIL:
++		return "Copper pigtail";
++	default:
++		return "unknown";
++	}
++}
++
++static const char *sfp_encoding(unsigned int encoding)
++{
++	switch (encoding) {
++	case SFP_ENCODING_UNSPEC:
++		return "unspecified";
++	case SFP_ENCODING_8B10B:
++		return "8b10b";
++	case SFP_ENCODING_4B5B:
++		return "4b5b";
++	case SFP_ENCODING_NRZ:
++		return "NRZ";
++	case SFP_ENCODING_MANCHESTER:
++		return "MANCHESTER";
++	default:
++		return "unknown";
++	}
++}
++
+ /* Helpers */
+ static void sfp_module_tx_disable(struct sfp *sfp)
+ {
+@@ -428,6 +604,7 @@ static int sfp_sm_mod_probe(struct sfp *
+ 	char sn[17];
+ 	char date[9];
+ 	char rev[5];
++	char options[80];
+ 	u8 check;
+ 	int err;
+ 
+@@ -471,10 +648,78 @@ static int sfp_sm_mod_probe(struct sfp *
+ 	rev[4] = '\0';
+ 	memcpy(sn, sfp->id.ext.vendor_sn, 16);
+ 	sn[16] = '\0';
+-	memcpy(date, sfp->id.ext.datecode, 8);
++	date[0] = sfp->id.ext.datecode[4];
++	date[1] = sfp->id.ext.datecode[5];
++	date[2] = '-';
++	date[3] = sfp->id.ext.datecode[2];
++	date[4] = sfp->id.ext.datecode[3];
++	date[5] = '-';
++	date[6] = sfp->id.ext.datecode[0];
++	date[7] = sfp->id.ext.datecode[1];
+ 	date[8] = '\0';
+ 
+ 	dev_info(sfp->dev, "module %s %s rev %s sn %s dc %s\n", vendor, part, rev, sn, date);
++	dev_info(sfp->dev, "  %s connector, encoding %s, nominal bitrate %u.%uGbps +%u%% -%u%%\n",
++		 sfp_connector(sfp->id.base.connector),
++		 sfp_encoding(sfp->id.base.encoding),
++		 sfp->id.base.br_nominal / 10,
++		 sfp->id.base.br_nominal % 10,
++		 sfp->id.ext.br_max, sfp->id.ext.br_min);
++	dev_info(sfp->dev, "  1000BaseSX%c 1000BaseLX%c 1000BaseCX%c 1000BaseT%c 100BaseTLX%c 1000BaseFX%c BaseBX10%c BasePX%c\n",
++		 sfp->id.base.e1000_base_sx ? '+' : '-',
++		 sfp->id.base.e1000_base_lx ? '+' : '-',
++		 sfp->id.base.e1000_base_cx ? '+' : '-',
++		 sfp->id.base.e1000_base_t ? '+' : '-',
++		 sfp->id.base.e100_base_lx ? '+' : '-',
++		 sfp->id.base.e100_base_fx ? '+' : '-',
++		 sfp->id.base.e_base_bx10 ? '+' : '-',
++		 sfp->id.base.e_base_px ? '+' : '-');
++
++	if (!sfp->id.base.sfp_ct_passive && !sfp->id.base.sfp_ct_active &&
++	    !sfp->id.base.e1000_base_t) {
++		char len_9um[16], len_om[16];
++
++		dev_info(sfp->dev, "  Wavelength %unm, fiber lengths:\n",
++			 be16_to_cpup(&sfp->id.base.optical_wavelength));
++
++		if (sfp->id.base.link_len[0] == 255)
++			strcpy(len_9um, ">254km");
++		else if (sfp->id.base.link_len[1] && sfp->id.base.link_len[1] != 255)
++			sprintf(len_9um, "%um",
++				sfp->id.base.link_len[1] * 100);
++		else if (sfp->id.base.link_len[0])
++			sprintf(len_9um, "%ukm", sfp->id.base.link_len[0]);
++		else if (sfp->id.base.link_len[1] == 255)
++			strcpy(len_9um, ">25.4km");
++		else
++			strcpy(len_9um, "unsupported");
++
++		dev_info(sfp->dev, "    9µm SM    : %s\n", len_9um);
++		dev_info(sfp->dev, " 62.5µm MM OM1: %s\n",
++			 sfp_link_len(len_om, sizeof(len_om),
++				      sfp->id.base.link_len[3], 10));
++		dev_info(sfp->dev, "   50µm MM OM2: %s\n",
++			 sfp_link_len(len_om, sizeof(len_om),
++				      sfp->id.base.link_len[2], 10));
++		dev_info(sfp->dev, "   50µm MM OM3: %s\n",
++			 sfp_link_len(len_om, sizeof(len_om),
++				      sfp->id.base.link_len[5], 10));
++		dev_info(sfp->dev, "   50µm MM OM4: %s\n",
++			 sfp_link_len(len_om, sizeof(len_om),
++				      sfp->id.base.link_len[4], 10));
++	} else {
++		char len[16];
++		dev_info(sfp->dev, "  Copper length: %s\n",
++			 sfp_link_len(len, sizeof(len),
++				      sfp->id.base.link_len[4], 1));
++	}
++
++	dev_info(sfp->dev, "  Options: %s\n",
++		 sfp_bitfield(options, sizeof(options), sfp_options,
++			      be16_to_cpu(sfp->id.ext.options)));
++	dev_info(sfp->dev, "  Diagnostics: %s\n",
++		 sfp_bitfield(options, sizeof(options), diagmon,
++			      sfp->id.ext.diagmon));
+ 
+ 	/* We only support SFP modules, not the legacy GBIC modules. */
+ 	if (sfp->id.base.phys_id != SFP_PHYS_ID_SFP ||
diff --git a/target/linux/mvebu/patches-4.9/419-net-mvneta-convert-to-phylink.patch b/target/linux/mvebu/patches-4.9/419-net-mvneta-convert-to-phylink.patch
new file mode 100644
index 0000000..dca4fa2
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/419-net-mvneta-convert-to-phylink.patch
@@ -0,0 +1,743 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Wed, 16 Sep 2015 21:27:10 +0100
+Subject: [PATCH] net: mvneta: convert to phylink
+
+Convert mvneta to use phylink, which models the MAC to PHY link in
+a generic, reusable form.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/Kconfig
++++ b/drivers/net/ethernet/marvell/Kconfig
+@@ -57,7 +57,7 @@ config MVNETA
+ 	tristate "Marvell Armada 370/38x/XP network interface support"
+ 	depends on PLAT_ORION
+ 	select MVMDIO
+-	select FIXED_PHY
++	select PHYLINK
+ 	---help---
+ 	  This driver supports the network interface units in the
+ 	  Marvell ARMADA XP, ARMADA 370 and ARMADA 38x SoC family.
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -28,6 +28,7 @@
+ #include <linux/of_mdio.h>
+ #include <linux/of_net.h>
+ #include <linux/phy.h>
++#include <linux/phylink.h>
+ #include <linux/platform_device.h>
+ #include <linux/skbuff.h>
+ #include <net/hwbm.h>
+@@ -188,6 +189,7 @@
+ #define MVNETA_GMAC_CTRL_0                       0x2c00
+ #define      MVNETA_GMAC_MAX_RX_SIZE_SHIFT       2
+ #define      MVNETA_GMAC_MAX_RX_SIZE_MASK        0x7ffc
++#define      MVNETA_GMAC0_PORT_1000BASE_X        BIT(1)
+ #define      MVNETA_GMAC0_PORT_ENABLE            BIT(0)
+ #define MVNETA_GMAC_CTRL_2                       0x2c08
+ #define      MVNETA_GMAC2_INBAND_AN_ENABLE       BIT(0)
+@@ -203,13 +205,19 @@
+ #define      MVNETA_GMAC_TX_FLOW_CTRL_ENABLE     BIT(5)
+ #define      MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE     BIT(6)
+ #define      MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE     BIT(7)
++#define      MVNETA_GMAC_AN_COMPLETE             BIT(11)
++#define      MVNETA_GMAC_SYNC_OK                 BIT(14)
+ #define MVNETA_GMAC_AUTONEG_CONFIG               0x2c0c
+ #define      MVNETA_GMAC_FORCE_LINK_DOWN         BIT(0)
+ #define      MVNETA_GMAC_FORCE_LINK_PASS         BIT(1)
+ #define      MVNETA_GMAC_INBAND_AN_ENABLE        BIT(2)
++#define      MVNETA_GMAC_AN_BYPASS_ENABLE        BIT(3)
++#define      MVNETA_GMAC_INBAND_RESTART_AN       BIT(4)
+ #define      MVNETA_GMAC_CONFIG_MII_SPEED        BIT(5)
+ #define      MVNETA_GMAC_CONFIG_GMII_SPEED       BIT(6)
+ #define      MVNETA_GMAC_AN_SPEED_EN             BIT(7)
++#define      MVNETA_GMAC_CONFIG_FLOW_CTRL        BIT(8)
++#define      MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL    BIT(9)
+ #define      MVNETA_GMAC_AN_FLOW_CTRL_EN         BIT(11)
+ #define      MVNETA_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
+ #define      MVNETA_GMAC_AN_DUPLEX_EN            BIT(13)
+@@ -399,14 +407,9 @@ struct mvneta_port {
+ 	u16 tx_ring_size;
+ 	u16 rx_ring_size;
+ 
+-	struct mii_bus *mii_bus;
+-	phy_interface_t phy_interface;
+-	struct device_node *phy_node;
+-	unsigned int link;
+-	unsigned int duplex;
+-	unsigned int speed;
++	struct device_node *dn;
+ 	unsigned int tx_csum_limit;
+-	unsigned int use_inband_status:1;
++	struct phylink *phylink;
+ 
+ 	struct mvneta_bm *bm_priv;
+ 	struct mvneta_bm_pool *pool_long;
+@@ -1240,44 +1243,6 @@ static void mvneta_set_other_mcast_table
+ 		mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val);
+ }
+ 
+-static void mvneta_set_autoneg(struct mvneta_port *pp, int enable)
+-{
+-	u32 val;
+-
+-	if (enable) {
+-		val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+-		val &= ~(MVNETA_GMAC_FORCE_LINK_PASS |
+-			 MVNETA_GMAC_FORCE_LINK_DOWN |
+-			 MVNETA_GMAC_AN_FLOW_CTRL_EN);
+-		val |= MVNETA_GMAC_INBAND_AN_ENABLE |
+-		       MVNETA_GMAC_AN_SPEED_EN |
+-		       MVNETA_GMAC_AN_DUPLEX_EN;
+-		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+-
+-		val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+-		val |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
+-		mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+-
+-		val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+-		val |= MVNETA_GMAC2_INBAND_AN_ENABLE;
+-		mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+-	} else {
+-		val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+-		val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE |
+-		       MVNETA_GMAC_AN_SPEED_EN |
+-		       MVNETA_GMAC_AN_DUPLEX_EN);
+-		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+-
+-		val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
+-		val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
+-		mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val);
+-
+-		val = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
+-		val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE;
+-		mvreg_write(pp, MVNETA_GMAC_CTRL_2, val);
+-	}
+-}
+-
+ static void mvneta_percpu_unmask_interrupt(void *arg)
+ {
+ 	struct mvneta_port *pp = arg;
+@@ -1425,7 +1390,6 @@ static void mvneta_defaults_set(struct m
+ 	val &= ~MVNETA_PHY_POLLING_ENABLE;
+ 	mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
+ 
+-	mvneta_set_autoneg(pp, pp->use_inband_status);
+ 	mvneta_set_ucast_table(pp, -1);
+ 	mvneta_set_special_mcast_table(pp, -1);
+ 	mvneta_set_other_mcast_table(pp, -1);
+@@ -2630,26 +2594,11 @@ static irqreturn_t mvneta_isr(int irq, v
+ 	return IRQ_HANDLED;
+ }
+ 
+-static int mvneta_fixed_link_update(struct mvneta_port *pp,
+-				    struct phy_device *phy)
++static void mvneta_link_change(struct mvneta_port *pp)
+ {
+-	struct fixed_phy_status status;
+-	struct fixed_phy_status changed = {};
+ 	u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
+ 
+-	status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
+-	if (gmac_stat & MVNETA_GMAC_SPEED_1000)
+-		status.speed = SPEED_1000;
+-	else if (gmac_stat & MVNETA_GMAC_SPEED_100)
+-		status.speed = SPEED_100;
+-	else
+-		status.speed = SPEED_10;
+-	status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+-	changed.link = 1;
+-	changed.speed = 1;
+-	changed.duplex = 1;
+-	fixed_phy_update_state(phy, &status, &changed);
+-	return 0;
++	phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP));
+ }
+ 
+ /* NAPI handler
+@@ -2665,7 +2614,6 @@ static int mvneta_poll(struct napi_struc
+ 	u32 cause_rx_tx;
+ 	int rx_queue;
+ 	struct mvneta_port *pp = netdev_priv(napi->dev);
+-	struct net_device *ndev = pp->dev;
+ 	struct mvneta_pcpu_port *port = this_cpu_ptr(pp->ports);
+ 
+ 	if (!netif_running(pp->dev)) {
+@@ -2679,12 +2627,11 @@ static int mvneta_poll(struct napi_struc
+ 		u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE);
+ 
+ 		mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
+-		if (pp->use_inband_status && (cause_misc &
+-				(MVNETA_CAUSE_PHY_STATUS_CHANGE |
+-				 MVNETA_CAUSE_LINK_CHANGE |
+-				 MVNETA_CAUSE_PSC_SYNC_CHANGE))) {
+-			mvneta_fixed_link_update(pp, ndev->phydev);
+-		}
++
++		if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE |
++				  MVNETA_CAUSE_LINK_CHANGE |
++				  MVNETA_CAUSE_PSC_SYNC_CHANGE))
++			mvneta_link_change(pp);
+ 	}
+ 
+ 	/* Release Tx descriptors */
+@@ -2982,7 +2929,6 @@ static int mvneta_setup_txqs(struct mvne
+ static void mvneta_start_dev(struct mvneta_port *pp)
+ {
+ 	int cpu;
+-	struct net_device *ndev = pp->dev;
+ 
+ 	mvneta_max_rx_size_set(pp, pp->pkt_size);
+ 	mvneta_txq_max_tx_size_set(pp, pp->pkt_size);
+@@ -3005,16 +2951,15 @@ static void mvneta_start_dev(struct mvne
+ 		    MVNETA_CAUSE_LINK_CHANGE |
+ 		    MVNETA_CAUSE_PSC_SYNC_CHANGE);
+ 
+-	phy_start(ndev->phydev);
++	phylink_start(pp->phylink);
+ 	netif_tx_start_all_queues(pp->dev);
+ }
+ 
+ static void mvneta_stop_dev(struct mvneta_port *pp)
+ {
+ 	unsigned int cpu;
+-	struct net_device *ndev = pp->dev;
+ 
+-	phy_stop(ndev->phydev);
++	phylink_stop(pp->phylink);
+ 
+ 	for_each_online_cpu(cpu) {
+ 		struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu);
+@@ -3184,99 +3129,210 @@ static int mvneta_set_mac_addr(struct ne
+ 	return 0;
+ }
+ 
+-static void mvneta_adjust_link(struct net_device *ndev)
++static void mvneta_validate_support(struct net_device *ndev, unsigned int mode,
++				    unsigned long *support)
++{
++	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
++
++	/* Allow all the expected bits */
++	phylink_set(mask, Autoneg);
++	phylink_set(mask, TP);
++	phylink_set(mask, AUI);
++	phylink_set(mask, MII);
++	phylink_set(mask, FIBRE);
++	phylink_set(mask, BNC);
++	phylink_set(mask, Backplane);
++
++	/* Half-duplex at speeds higher than 100Mbit is unsupported */
++	phylink_set(mask, 1000baseT_Full);
++	phylink_set(mask, 1000baseX_Full);
++
++	if (mode != MLO_AN_8023Z) {
++		/* 10M and 100M are only supported in non-802.3z mode */
++		phylink_set(mask, 10baseT_Half);
++		phylink_set(mask, 10baseT_Full);
++		phylink_set(mask, 100baseT_Half);
++		phylink_set(mask, 100baseT_Full);
++	} else {
++		phylink_set(mask, Pause);
++	}
++
++	bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
++}
++
++static int mvneta_mac_link_state(struct net_device *ndev,
++				 struct phylink_link_state *state)
+ {
+ 	struct mvneta_port *pp = netdev_priv(ndev);
+-	struct phy_device *phydev = ndev->phydev;
+-	int status_change = 0;
++	u32 gmac_stat;
++
++	gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS);
++
++	if (gmac_stat & MVNETA_GMAC_SPEED_1000)
++		state->speed = SPEED_1000;
++	else if (gmac_stat & MVNETA_GMAC_SPEED_100)
++		state->speed = SPEED_100;
++	else
++		state->speed = SPEED_10;
+ 
+-	if (phydev->link) {
+-		if ((pp->speed != phydev->speed) ||
+-		    (pp->duplex != phydev->duplex)) {
+-			u32 val;
+-
+-			val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+-			val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
+-				 MVNETA_GMAC_CONFIG_GMII_SPEED |
+-				 MVNETA_GMAC_CONFIG_FULL_DUPLEX);
+-
+-			if (phydev->duplex)
+-				val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+-
+-			if (phydev->speed == SPEED_1000)
+-				val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
+-			else if (phydev->speed == SPEED_100)
+-				val |= MVNETA_GMAC_CONFIG_MII_SPEED;
++	state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE);
++	state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
++	state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+ 
+-			mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
++	return 1;
++}
+ 
+-			pp->duplex = phydev->duplex;
+-			pp->speed  = phydev->speed;
+-		}
++static void mvneta_mac_an_restart(struct net_device *ndev, unsigned int mode)
++{
++	struct mvneta_port *pp = netdev_priv(ndev);
++
++	if (mode == MLO_AN_8023Z) {
++		u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
++
++		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
++			    gmac_an | MVNETA_GMAC_INBAND_RESTART_AN);
++		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
++			    gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN);
+ 	}
++}
+ 
+-	if (phydev->link != pp->link) {
+-		if (!phydev->link) {
+-			pp->duplex = -1;
+-			pp->speed = 0;
+-		}
++static void mvneta_mac_config(struct net_device *ndev, unsigned int mode,
++	const struct phylink_link_state *state)
++{
++	struct mvneta_port *pp = netdev_priv(ndev);
++	u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0);
++	u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2);
++	u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER);
++	u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
++
++	new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X;
++	new_ctrl2 = gmac_ctrl2 & ~MVNETA_GMAC2_INBAND_AN_ENABLE;
++	new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE;
++	new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE |
++			     MVNETA_GMAC_INBAND_RESTART_AN |
++			     MVNETA_GMAC_CONFIG_MII_SPEED |
++			     MVNETA_GMAC_CONFIG_GMII_SPEED |
++			     MVNETA_GMAC_AN_SPEED_EN |
++			     MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL |
++			     MVNETA_GMAC_CONFIG_FLOW_CTRL |
++			     MVNETA_GMAC_AN_FLOW_CTRL_EN |
++			     MVNETA_GMAC_CONFIG_FULL_DUPLEX |
++			     MVNETA_GMAC_AN_DUPLEX_EN);
++
++	if (phylink_test(state->advertising, Pause))
++		new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
++
++	switch (mode) {
++	case MLO_AN_SGMII:
++		/* SGMII mode receives the state from the PHY */
++		new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE;
++		new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
++		new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
++				     MVNETA_GMAC_FORCE_LINK_PASS)) |
++			 MVNETA_GMAC_INBAND_AN_ENABLE |
++			 MVNETA_GMAC_AN_SPEED_EN |
++			 MVNETA_GMAC_AN_DUPLEX_EN;
++		break;
+ 
+-		pp->link = phydev->link;
+-		status_change = 1;
++	case MLO_AN_8023Z:
++		/* 802.3z negotiation - only 1000base-X */
++		new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X;
++		new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE;
++		new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN |
++				     MVNETA_GMAC_FORCE_LINK_PASS)) |
++			 MVNETA_GMAC_INBAND_AN_ENABLE |
++			 MVNETA_GMAC_CONFIG_GMII_SPEED |
++			 /* The MAC only supports FD mode */
++			 MVNETA_GMAC_CONFIG_FULL_DUPLEX;
++
++		if (state->an_enabled)
++			new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
++		break;
++
++	default:
++		/* Phy or fixed speed */
++		if (state->duplex)
++			new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
++
++		if (state->speed == SPEED_1000)
++			new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED;
++		else if (state->speed == SPEED_100)
++			new_an |= MVNETA_GMAC_CONFIG_MII_SPEED;
++		break;
+ 	}
+ 
+-	if (status_change) {
+-		if (phydev->link) {
+-			if (!pp->use_inband_status) {
+-				u32 val = mvreg_read(pp,
+-						  MVNETA_GMAC_AUTONEG_CONFIG);
+-				val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
+-				val |= MVNETA_GMAC_FORCE_LINK_PASS;
+-				mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+-					    val);
+-			}
+-			mvneta_port_up(pp);
+-		} else {
+-			if (!pp->use_inband_status) {
+-				u32 val = mvreg_read(pp,
+-						  MVNETA_GMAC_AUTONEG_CONFIG);
+-				val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
+-				val |= MVNETA_GMAC_FORCE_LINK_DOWN;
+-				mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
+-					    val);
+-			}
+-			mvneta_port_down(pp);
+-		}
+-		phy_print_status(phydev);
++	/* Armada 370 documentation says we can only change the port mode
++	 * and in-band enable when the link is down, so force it down
++	 * while making these changes. We also do this for GMAC_CTRL2 */
++	if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X ||
++	    (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE ||
++	    (new_an  ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) {
++		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG,
++			    (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) |
++			    MVNETA_GMAC_FORCE_LINK_DOWN);
++	}
++
++	if (new_ctrl0 != gmac_ctrl0)
++		mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0);
++	if (new_ctrl2 != gmac_ctrl2)
++		mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2);
++	if (new_clk != gmac_clk)
++		mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk);
++	if (new_an != gmac_an)
++		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
++}
++
++static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
++{
++	struct mvneta_port *pp = netdev_priv(ndev);
++	u32 val;
++
++	mvneta_port_down(pp);
++
++	if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) {
++		val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
++		val &= ~MVNETA_GMAC_FORCE_LINK_PASS;
++		val |= MVNETA_GMAC_FORCE_LINK_DOWN;
++		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ 	}
+ }
+ 
+-static int mvneta_mdio_probe(struct mvneta_port *pp)
++static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode)
+ {
+-	struct phy_device *phy_dev;
++	struct mvneta_port *pp = netdev_priv(ndev);
++	u32 val;
+ 
+-	phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0,
+-				 pp->phy_interface);
+-	if (!phy_dev) {
+-		netdev_err(pp->dev, "could not find the PHY\n");
+-		return -ENODEV;
++	if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) {
++		val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
++		val &= ~MVNETA_GMAC_FORCE_LINK_DOWN;
++		val |= MVNETA_GMAC_FORCE_LINK_PASS;
++		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ 	}
+ 
+-	phy_dev->supported &= PHY_GBIT_FEATURES;
+-	phy_dev->advertising = phy_dev->supported;
++	mvneta_port_up(pp);
++}
+ 
+-	pp->link    = 0;
+-	pp->duplex  = 0;
+-	pp->speed   = 0;
++static const struct phylink_mac_ops mvneta_phylink_ops = {
++	.validate_support = mvneta_validate_support,
++	.mac_link_state = mvneta_mac_link_state,
++	.mac_an_restart = mvneta_mac_an_restart,
++	.mac_config = mvneta_mac_config,
++	.mac_link_down = mvneta_mac_link_down,
++	.mac_link_up = mvneta_mac_link_up,
++};
+ 
+-	return 0;
++static int mvneta_mdio_probe(struct mvneta_port *pp)
++{
++	int err = phylink_of_phy_connect(pp->phylink, pp->dn);
++	if (err)
++		netdev_err(pp->dev, "could not attach PHY\n");
++
++	return err;
+ }
+ 
+ static void mvneta_mdio_remove(struct mvneta_port *pp)
+ {
+-	struct net_device *ndev = pp->dev;
+-
+-	phy_disconnect(ndev->phydev);
++	phylink_disconnect_phy(pp->phylink);
+ }
+ 
+ /* Electing a CPU must be done in an atomic way: it should be done
+@@ -3534,10 +3590,9 @@ static int mvneta_stop(struct net_device
+ 
+ static int mvneta_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+ {
+-	if (!dev->phydev)
+-		return -ENOTSUPP;
++	struct mvneta_port *pp = netdev_priv(dev);
+ 
+-	return phy_mii_ioctl(dev->phydev, ifr, cmd);
++	return phylink_mii_ioctl(pp->phylink, ifr, cmd);
+ }
+ 
+ /* Ethtool methods */
+@@ -3548,44 +3603,18 @@ mvneta_ethtool_set_link_ksettings(struct
+ 				  const struct ethtool_link_ksettings *cmd)
+ {
+ 	struct mvneta_port *pp = netdev_priv(ndev);
+-	struct phy_device *phydev = ndev->phydev;
+-
+-	if (!phydev)
+-		return -ENODEV;
+ 
+-	if ((cmd->base.autoneg == AUTONEG_ENABLE) != pp->use_inband_status) {
+-		u32 val;
+-
+-		mvneta_set_autoneg(pp, cmd->base.autoneg == AUTONEG_ENABLE);
+-
+-		if (cmd->base.autoneg == AUTONEG_DISABLE) {
+-			val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG);
+-			val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED |
+-				 MVNETA_GMAC_CONFIG_GMII_SPEED |
+-				 MVNETA_GMAC_CONFIG_FULL_DUPLEX);
+-
+-			if (phydev->duplex)
+-				val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+-
+-			if (phydev->speed == SPEED_1000)
+-				val |= MVNETA_GMAC_CONFIG_GMII_SPEED;
+-			else if (phydev->speed == SPEED_100)
+-				val |= MVNETA_GMAC_CONFIG_MII_SPEED;
+-
+-			mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+-		}
++	return phylink_ethtool_ksettings_set(pp->phylink, cmd);
++}
+ 
+-		pp->use_inband_status = (cmd->base.autoneg == AUTONEG_ENABLE);
+-		netdev_info(pp->dev, "autoneg status set to %i\n",
+-			    pp->use_inband_status);
+-
+-		if (netif_running(ndev)) {
+-			mvneta_port_down(pp);
+-			mvneta_port_up(pp);
+-		}
+-	}
++/* Get link ksettings for ethtools */
++static int
++mvneta_ethtool_get_link_ksettings(struct net_device *ndev,
++				  struct ethtool_link_ksettings *cmd)
++{
++	struct mvneta_port *pp = netdev_priv(ndev);
+ 
+-	return phy_ethtool_ksettings_set(ndev->phydev, cmd);
++	return phylink_ethtool_ksettings_get(pp->phylink, cmd);
+ }
+ 
+ /* Set interrupt coalescing for ethtools */
+@@ -3693,26 +3722,28 @@ static void mvneta_ethtool_update_stats(
+ {
+ 	const struct mvneta_statistic *s;
+ 	void __iomem *base = pp->base;
+-	u32 high, low, val;
+-	u64 val64;
++	u32 high, low;
++	u64 val;
+ 	int i;
+ 
+ 	for (i = 0, s = mvneta_statistics;
+ 	     s < mvneta_statistics + ARRAY_SIZE(mvneta_statistics);
+ 	     s++, i++) {
++		val = 0;
++
+ 		switch (s->type) {
+ 		case T_REG_32:
+ 			val = readl_relaxed(base + s->offset);
+-			pp->ethtool_stats[i] += val;
+ 			break;
+ 		case T_REG_64:
+ 			/* Docs say to read low 32-bit then high */
+ 			low = readl_relaxed(base + s->offset);
+ 			high = readl_relaxed(base + s->offset + 4);
+-			val64 = (u64)high << 32 | low;
+-			pp->ethtool_stats[i] += val64;
++			val = (u64)high << 32 | low;
+ 			break;
+ 		}
++
++		pp->ethtool_stats[i] += val;
+ 	}
+ }
+ 
+@@ -3872,7 +3903,7 @@ const struct ethtool_ops mvneta_eth_tool
+ 	.get_rxnfc	= mvneta_ethtool_get_rxnfc,
+ 	.get_rxfh	= mvneta_ethtool_get_rxfh,
+ 	.set_rxfh	= mvneta_ethtool_set_rxfh,
+-	.get_link_ksettings = phy_ethtool_get_link_ksettings,
++	.get_link_ksettings = mvneta_ethtool_get_link_ksettings,
+ 	.set_link_ksettings = mvneta_ethtool_set_link_ksettings,
+ };
+ 
+@@ -3999,14 +4030,13 @@ static int mvneta_probe(struct platform_
+ 	const struct mbus_dram_target_info *dram_target_info;
+ 	struct resource *res;
+ 	struct device_node *dn = pdev->dev.of_node;
+-	struct device_node *phy_node;
+ 	struct device_node *bm_node;
+ 	struct mvneta_port *pp;
+ 	struct net_device *dev;
++	struct phylink *phylink;
+ 	const char *dt_mac_addr;
+ 	char hw_mac_addr[ETH_ALEN];
+ 	const char *mac_from;
+-	const char *managed;
+ 	int tx_csum_limit;
+ 	int phy_mode;
+ 	int err;
+@@ -4022,31 +4052,11 @@ static int mvneta_probe(struct platform_
+ 		goto err_free_netdev;
+ 	}
+ 
+-	phy_node = of_parse_phandle(dn, "phy", 0);
+-	if (!phy_node) {
+-		if (!of_phy_is_fixed_link(dn)) {
+-			dev_err(&pdev->dev, "no PHY specified\n");
+-			err = -ENODEV;
+-			goto err_free_irq;
+-		}
+-
+-		err = of_phy_register_fixed_link(dn);
+-		if (err < 0) {
+-			dev_err(&pdev->dev, "cannot register fixed PHY\n");
+-			goto err_free_irq;
+-		}
+-
+-		/* In the case of a fixed PHY, the DT node associated
+-		 * to the PHY is the Ethernet MAC DT node.
+-		 */
+-		phy_node = of_node_get(dn);
+-	}
+-
+ 	phy_mode = of_get_phy_mode(dn);
+ 	if (phy_mode < 0) {
+ 		dev_err(&pdev->dev, "incorrect phy-mode\n");
+ 		err = -EINVAL;
+-		goto err_put_phy_node;
++		goto err_free_irq;
+ 	}
+ 
+ 	dev->tx_queue_len = MVNETA_MAX_TXD;
+@@ -4057,12 +4067,7 @@ static int mvneta_probe(struct platform_
+ 
+ 	pp = netdev_priv(dev);
+ 	spin_lock_init(&pp->lock);
+-	pp->phy_node = phy_node;
+-	pp->phy_interface = phy_mode;
+-
+-	err = of_property_read_string(dn, "managed", &managed);
+-	pp->use_inband_status = (err == 0 &&
+-				 strcmp(managed, "in-band-status") == 0);
++	pp->dn = dn;
+ 
+ 	pp->rxq_def = rxq_def;
+ 
+@@ -4073,7 +4078,7 @@ static int mvneta_probe(struct platform_
+ 		pp->clk = devm_clk_get(&pdev->dev, NULL);
+ 	if (IS_ERR(pp->clk)) {
+ 		err = PTR_ERR(pp->clk);
+-		goto err_put_phy_node;
++		goto err_free_irq;
+ 	}
+ 
+ 	clk_prepare_enable(pp->clk);
+@@ -4181,6 +4186,14 @@ static int mvneta_probe(struct platform_
+ 	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
+ 	dev->gso_max_segs = MVNETA_MAX_TSO_SEGS;
+ 
++	phylink = phylink_create(dev, dn, phy_mode, &mvneta_phylink_ops);
++	if (IS_ERR(phylink)) {
++		err = PTR_ERR(phylink);
++		goto err_free_stats;
++	}
++
++	pp->phylink = phylink;
++
+ 	err = register_netdev(dev);
+ 	if (err < 0) {
+ 		dev_err(&pdev->dev, "failed to register\n");
+@@ -4192,14 +4205,6 @@ static int mvneta_probe(struct platform_
+ 
+ 	platform_set_drvdata(pdev, pp->dev);
+ 
+-	if (pp->use_inband_status) {
+-		struct phy_device *phy = of_phy_find_device(dn);
+-
+-		mvneta_fixed_link_update(pp, phy);
+-
+-		put_device(&phy->mdio.dev);
+-	}
+-
+ 	return 0;
+ 
+ err_netdev:
+@@ -4210,16 +4215,14 @@ err_netdev:
+ 				       1 << pp->id);
+ 	}
+ err_free_stats:
++	if (pp->phylink)
++		phylink_destroy(pp->phylink);
+ 	free_percpu(pp->stats);
+ err_free_ports:
+ 	free_percpu(pp->ports);
+ err_clk:
+ 	clk_disable_unprepare(pp->clk_bus);
+ 	clk_disable_unprepare(pp->clk);
+-err_put_phy_node:
+-	of_node_put(phy_node);
+-	if (of_phy_is_fixed_link(dn))
+-		of_phy_deregister_fixed_link(dn);
+ err_free_irq:
+ 	irq_dispose_mapping(dev->irq);
+ err_free_netdev:
+@@ -4231,7 +4234,6 @@ err_free_netdev:
+ static int mvneta_remove(struct platform_device *pdev)
+ {
+ 	struct net_device  *dev = platform_get_drvdata(pdev);
+-	struct device_node *dn = pdev->dev.of_node;
+ 	struct mvneta_port *pp = netdev_priv(dev);
+ 
+ 	unregister_netdev(dev);
+@@ -4239,10 +4241,8 @@ static int mvneta_remove(struct platform
+ 	clk_disable_unprepare(pp->clk);
+ 	free_percpu(pp->ports);
+ 	free_percpu(pp->stats);
+-	if (of_phy_is_fixed_link(dn))
+-		of_phy_deregister_fixed_link(dn);
+ 	irq_dispose_mapping(dev->irq);
+-	of_node_put(pp->phy_node);
++	phylink_destroy(pp->phylink);
+ 	free_netdev(dev);
+ 
+ 	if (pp->bm_priv) {
diff --git a/target/linux/mvebu/patches-4.9/420-net-mvneta-disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch b/target/linux/mvebu/patches-4.9/420-net-mvneta-disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch
new file mode 100644
index 0000000..c0abab5
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/420-net-mvneta-disable-MVNETA_CAUSE_PSC_SYNC_CHANGE-inte.patch
@@ -0,0 +1,58 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Sat, 24 Dec 2016 10:27:08 +0000
+Subject: [PATCH] net: mvneta: disable MVNETA_CAUSE_PSC_SYNC_CHANGE
+ interrupt
+
+The PSC sync change interrupt can fire multiple times while the link is
+down.  As this isn't information we make use of, it's pointless having
+the interrupt enabled, so let's disable this interrupt.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -2629,9 +2629,11 @@ static int mvneta_poll(struct napi_struc
+ 		mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0);
+ 
+ 		if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE |
+-				  MVNETA_CAUSE_LINK_CHANGE |
+-				  MVNETA_CAUSE_PSC_SYNC_CHANGE))
++				  MVNETA_CAUSE_LINK_CHANGE)) {
++			printk(KERN_DEBUG "%s: cause 0x%08x:0x%08x\n",
++				__func__, cause_rx_tx, cause_misc);
+ 			mvneta_link_change(pp);
++		}
+ 	}
+ 
+ 	/* Release Tx descriptors */
+@@ -2948,8 +2950,7 @@ static void mvneta_start_dev(struct mvne
+ 
+ 	mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+ 		    MVNETA_CAUSE_PHY_STATUS_CHANGE |
+-		    MVNETA_CAUSE_LINK_CHANGE |
+-		    MVNETA_CAUSE_PSC_SYNC_CHANGE);
++		    MVNETA_CAUSE_LINK_CHANGE);
+ 
+ 	phylink_start(pp->phylink);
+ 	netif_tx_start_all_queues(pp->dev);
+@@ -3440,8 +3441,7 @@ static int mvneta_cpu_online(unsigned in
+ 	on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
+ 	mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+ 		    MVNETA_CAUSE_PHY_STATUS_CHANGE |
+-		    MVNETA_CAUSE_LINK_CHANGE |
+-		    MVNETA_CAUSE_PSC_SYNC_CHANGE);
++		    MVNETA_CAUSE_LINK_CHANGE);
+ 	netif_tx_start_all_queues(pp->dev);
+ 	spin_unlock(&pp->lock);
+ 	return 0;
+@@ -3482,8 +3482,7 @@ static int mvneta_cpu_dead(unsigned int
+ 	on_each_cpu(mvneta_percpu_unmask_interrupt, pp, true);
+ 	mvreg_write(pp, MVNETA_INTR_MISC_MASK,
+ 		    MVNETA_CAUSE_PHY_STATUS_CHANGE |
+-		    MVNETA_CAUSE_LINK_CHANGE |
+-		    MVNETA_CAUSE_PSC_SYNC_CHANGE);
++		    MVNETA_CAUSE_LINK_CHANGE);
+ 	netif_tx_start_all_queues(pp->dev);
+ 	return 0;
+ }
diff --git a/target/linux/mvebu/patches-4.9/421-phylink-add-ethtool-nway_reset-support.patch b/target/linux/mvebu/patches-4.9/421-phylink-add-ethtool-nway_reset-support.patch
new file mode 100644
index 0000000..d4436a7
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/421-phylink-add-ethtool-nway_reset-support.patch
@@ -0,0 +1,44 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 20:27:19 +0100
+Subject: [PATCH] phylink: add ethtool nway_reset support
+
+Add ethtool nway_reset support to phylink, to allow userspace to
+request a re-negotiation of the link.
+
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -783,6 +783,20 @@ int phylink_ethtool_ksettings_set(struct
+ }
+ EXPORT_SYMBOL_GPL(phylink_ethtool_ksettings_set);
+ 
++int phylink_ethtool_nway_reset(struct phylink *pl)
++{
++	int ret = 0;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->phydev)
++		ret = genphy_restart_aneg(pl->phydev);
++	phylink_mac_an_restart(pl);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset);
++
+ /* This emulates MII registers for a fixed-mode phy operating as per the
+  * passed in state. "aneg" defines if we report negotiation is possible.
+  *
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -86,6 +86,7 @@ int phylink_ethtool_ksettings_get(struct
+ 				  struct ethtool_link_ksettings *);
+ int phylink_ethtool_ksettings_set(struct phylink *,
+ 				  const struct ethtool_link_ksettings *);
++int phylink_ethtool_nway_reset(struct phylink *);
+ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
+ 
+ int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port,
diff --git a/target/linux/mvebu/patches-4.9/422-net-mvneta-add-nway_reset-support.patch b/target/linux/mvebu/patches-4.9/422-net-mvneta-add-nway_reset-support.patch
new file mode 100644
index 0000000..c7725b3
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/422-net-mvneta-add-nway_reset-support.patch
@@ -0,0 +1,35 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 19:40:31 +0100
+Subject: [PATCH] net: mvneta: add nway_reset support
+
+Add ethtool nway_reset support to mvneta via phylink, so that userspace
+can request the link in whatever mode to be renegotiated via
+ethtool -r ethX.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3616,6 +3616,13 @@ mvneta_ethtool_get_link_ksettings(struct
+ 	return phylink_ethtool_ksettings_get(pp->phylink, cmd);
+ }
+ 
++static int mvneta_ethtool_nway_reset(struct net_device *dev)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++
++	return phylink_ethtool_nway_reset(pp->phylink);
++}
++
+ /* Set interrupt coalescing for ethtools */
+ static int mvneta_ethtool_set_coalesce(struct net_device *dev,
+ 				       struct ethtool_coalesce *c)
+@@ -3889,6 +3896,7 @@ static const struct net_device_ops mvnet
+ };
+ 
+ const struct ethtool_ops mvneta_eth_tool_ops = {
++	.nway_reset	= mvneta_ethtool_nway_reset,
+ 	.get_link       = ethtool_op_get_link,
+ 	.set_coalesce   = mvneta_ethtool_set_coalesce,
+ 	.get_coalesce   = mvneta_ethtool_get_coalesce,
diff --git a/target/linux/mvebu/patches-4.9/423-phylink-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/423-phylink-add-flow-control-support.patch
new file mode 100644
index 0000000..b0359f1
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/423-phylink-add-flow-control-support.patch
@@ -0,0 +1,232 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 20:32:07 +0100
+Subject: [PATCH] phylink: add flow control support
+
+Add flow control support, including ethtool support, to phylink.  We
+add support to allow ethtool to get and set the current flow control
+settings, and the 802.3 specified resolution for the local and remote
+link partner abilities.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -132,6 +132,9 @@ static int phylink_parse_fixedlink(struc
+ 
+ 		if (of_property_read_bool(fixed_node, "full-duplex"))
+ 			pl->link_config.duplex = DUPLEX_FULL;
++
++		/* We treat the "pause" and "asym-pause" terminology as
++		 * defining the link partner's ability. */
+ 		if (of_property_read_bool(fixed_node, "pause"))
+ 			pl->link_config.pause |= MLO_PAUSE_SYM;
+ 		if (of_property_read_bool(fixed_node, "asym-pause"))
+@@ -277,6 +280,56 @@ static void phylink_get_fixed_state(stru
+ 		state->link = !!gpiod_get_value(pl->link_gpio);
+ }
+ 
++/* Flow control is resolved according to our and the link partners
++ * advertisments using the following drawn from the 802.3 specs:
++ *  Local device  Link partner
++ *  Pause AsymDir Pause AsymDir Result
++ *    1     X       1     X     TX+RX
++ *    0     1       1     1     RX
++ *    1     1       0     1     TX
++ */
++static void phylink_resolve_flow(struct phylink *pl,
++	struct phylink_link_state *state)
++{
++	int new_pause = 0;
++
++	if (pl->link_config.pause & MLO_PAUSE_AN) {
++		int pause = 0;
++
++		if (phylink_test(pl->link_config.advertising, Pause))
++			pause |= MLO_PAUSE_SYM;
++		if (phylink_test(pl->link_config.advertising, Asym_Pause))
++			pause |= MLO_PAUSE_ASYM;
++
++		pause &= state->pause;
++
++		if (pause & MLO_PAUSE_SYM)
++			new_pause = MLO_PAUSE_TX | MLO_PAUSE_RX;
++		else if (pause & MLO_PAUSE_ASYM)
++			new_pause = state->pause & MLO_PAUSE_SYM ?
++				 MLO_PAUSE_RX : MLO_PAUSE_TX;
++	} else {
++		new_pause = pl->link_config.pause & MLO_PAUSE_TXRX_MASK;
++	}
++
++	state->pause &= ~MLO_PAUSE_TXRX_MASK;
++	state->pause |= new_pause;
++}
++
++static const char *phylink_pause_to_str(int pause)
++{
++	switch (pause & MLO_PAUSE_TXRX_MASK) {
++	case MLO_PAUSE_TX | MLO_PAUSE_RX:
++		return "rx/tx";
++	case MLO_PAUSE_TX:
++		return "tx";
++	case MLO_PAUSE_RX:
++		return "rx";
++	default:
++		return "off";
++	}
++}
++
+ static void phylink_resolve(struct work_struct *w)
+ {
+ 	struct phylink *pl = container_of(w, struct phylink, resolve);
+@@ -290,6 +343,7 @@ static void phylink_resolve(struct work_
+ 		switch (pl->link_an_mode) {
+ 		case MLO_AN_PHY:
+ 			link_state = pl->phy_state;
++			phylink_resolve_flow(pl, &link_state);
+ 			break;
+ 
+ 		case MLO_AN_FIXED:
+@@ -298,9 +352,12 @@ static void phylink_resolve(struct work_
+ 
+ 		case MLO_AN_SGMII:
+ 			phylink_get_mac_state(pl, &link_state);
+-			if (pl->phydev)
++			if (pl->phydev) {
+ 				link_state.link = link_state.link &&
+ 						  pl->phy_state.link;
++				link_state.pause |= pl->phy_state.pause;
++				phylink_resolve_flow(pl, &link_state);
++			}
+ 			break;
+ 
+ 		case MLO_AN_8023Z:
+@@ -330,7 +387,7 @@ static void phylink_resolve(struct work_
+ 				    "Link is Up - %s/%s - flow control %s\n",
+ 				    phy_speed_to_str(link_state.speed),
+ 				    phy_duplex_to_str(link_state.duplex),
+-				    link_state.pause ? "rx/tx" : "off");
++				    phylink_pause_to_str(link_state.pause));
+ 		}
+ 	}
+ 	mutex_unlock(&pl->state_mutex);
+@@ -358,6 +415,7 @@ struct phylink *phylink_create(struct ne
+ 	pl->netdev = ndev;
+ 	pl->link_interface = iface;
+ 	pl->link_port = PORT_MII;
++	pl->link_config.pause = MLO_PAUSE_AN;
+ 	pl->link_config.speed = SPEED_UNKNOWN;
+ 	pl->link_config.duplex = DUPLEX_UNKNOWN;
+ 	pl->ops = ops;
+@@ -580,6 +638,7 @@ void phylink_start(struct phylink *pl)
+ 	 * a fixed-link to start with the correct parameters, and also
+ 	 * ensures that we set the appropriate advertisment for Serdes links.
+ 	 */
++	phylink_resolve_flow(pl, &pl->link_config);
+ 	phylink_mac_config(pl, &pl->link_config);
+ 
+ 	clear_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state);
+@@ -797,6 +856,79 @@ int phylink_ethtool_nway_reset(struct ph
+ }
+ EXPORT_SYMBOL_GPL(phylink_ethtool_nway_reset);
+ 
++void phylink_ethtool_get_pauseparam(struct phylink *pl,
++				    struct ethtool_pauseparam *pause)
++{
++	mutex_lock(&pl->config_mutex);
++
++	pause->autoneg = !!(pl->link_config.pause & MLO_PAUSE_AN);
++	pause->rx_pause = !!(pl->link_config.pause & MLO_PAUSE_RX);
++	pause->tx_pause = !!(pl->link_config.pause & MLO_PAUSE_TX);
++
++	mutex_unlock(&pl->config_mutex);
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_get_pauseparam);
++
++static int __phylink_ethtool_set_pauseparam(struct phylink *pl,
++					    struct ethtool_pauseparam *pause)
++{
++	struct phylink_link_state *config = &pl->link_config;
++
++	if (!phylink_test(pl->supported, Pause) &&
++	    !phylink_test(pl->supported, Asym_Pause))
++		return -EOPNOTSUPP;
++
++	if (!phylink_test(pl->supported, Asym_Pause) &&
++	    !pause->autoneg && pause->rx_pause != pause->tx_pause)
++		return -EINVAL;
++
++	config->pause &= ~(MLO_PAUSE_AN | MLO_PAUSE_TXRX_MASK);
++
++	if (pause->autoneg)
++		config->pause |= MLO_PAUSE_AN;
++	if (pause->rx_pause)
++		config->pause |= MLO_PAUSE_RX;
++	if (pause->tx_pause)
++		config->pause |= MLO_PAUSE_TX;
++
++	if (!test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state)) {
++		switch (pl->link_an_mode) {
++		case MLO_AN_PHY:
++			/* Silently mark the carrier down, and then trigger a resolve */
++			netif_carrier_off(pl->netdev);
++			phylink_run_resolve(pl);
++			break;
++
++		case MLO_AN_FIXED:
++			/* Should we allow fixed links to change against the config? */
++			phylink_resolve_flow(pl, config);
++			phylink_mac_config(pl, config);
++			break;
++
++		case MLO_AN_SGMII:
++		case MLO_AN_8023Z:
++			phylink_mac_config(pl, config);
++			phylink_mac_an_restart(pl);
++			break;
++		}
++	}
++
++	return 0;
++}
++
++int phylink_ethtool_set_pauseparam(struct phylink *pl,
++				   struct ethtool_pauseparam *pause)
++{
++	int ret;
++
++	mutex_lock(&pl->config_mutex);
++	ret = __phylink_ethtool_set_pauseparam(pl, pause);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
++
+ /* This emulates MII registers for a fixed-mode phy operating as per the
+  * passed in state. "aneg" defines if we report negotiation is possible.
+  *
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -13,6 +13,10 @@ enum {
+ 	MLO_PAUSE_NONE,
+ 	MLO_PAUSE_ASYM = BIT(0),
+ 	MLO_PAUSE_SYM = BIT(1),
++	MLO_PAUSE_RX = BIT(2),
++	MLO_PAUSE_TX = BIT(3),
++	MLO_PAUSE_TXRX_MASK = MLO_PAUSE_TX | MLO_PAUSE_RX,
++	MLO_PAUSE_AN = BIT(4),
+ 
+ 	MLO_AN_PHY = 0,
+ 	MLO_AN_FIXED,
+@@ -87,6 +91,10 @@ int phylink_ethtool_ksettings_get(struct
+ int phylink_ethtool_ksettings_set(struct phylink *,
+ 				  const struct ethtool_link_ksettings *);
+ int phylink_ethtool_nway_reset(struct phylink *);
++void phylink_ethtool_get_pauseparam(struct phylink *,
++				    struct ethtool_pauseparam *);
++int phylink_ethtool_set_pauseparam(struct phylink *,
++				   struct ethtool_pauseparam *);
+ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
+ 
+ int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port,
diff --git a/target/linux/mvebu/patches-4.9/424-net-mvneta-add-flow-control-support-via-phylink.patch b/target/linux/mvebu/patches-4.9/424-net-mvneta-add-flow-control-support-via-phylink.patch
new file mode 100644
index 0000000..b46359c
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/424-net-mvneta-add-flow-control-support-via-phylink.patch
@@ -0,0 +1,76 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 17:41:44 +0100
+Subject: [PATCH] net: mvneta: add flow control support via phylink
+
+Add flow control support to mvneta, including the ethtool hooks.  This
+uses the phylink code to calculate the result of autonegotiation where
+a phy is attached, and to handle the ethtool settings.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3180,6 +3180,12 @@ static int mvneta_mac_link_state(struct
+ 	state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP);
+ 	state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX);
+ 
++	state->pause = 0;
++	if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE)
++		state->pause |= MLO_PAUSE_RX;
++	if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE)
++		state->pause |= MLO_PAUSE_TX;
++
+ 	return 1;
+ }
+ 
+@@ -3222,6 +3228,8 @@ static void mvneta_mac_config(struct net
+ 
+ 	if (phylink_test(state->advertising, Pause))
+ 		new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL;
++	if (state->pause & MLO_PAUSE_TXRX_MASK)
++		new_an |= MVNETA_GMAC_CONFIG_FLOW_CTRL;
+ 
+ 	switch (mode) {
+ 	case MLO_AN_SGMII:
+@@ -3246,7 +3254,7 @@ static void mvneta_mac_config(struct net
+ 			 /* The MAC only supports FD mode */
+ 			 MVNETA_GMAC_CONFIG_FULL_DUPLEX;
+ 
+-		if (state->an_enabled)
++		if (state->pause & MLO_PAUSE_AN && state->an_enabled)
+ 			new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN;
+ 		break;
+ 
+@@ -3712,6 +3720,22 @@ static int mvneta_ethtool_set_ringparam(
+ 	return 0;
+ }
+ 
++static void mvneta_ethtool_get_pauseparam(struct net_device *dev,
++					  struct ethtool_pauseparam *pause)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++
++	phylink_ethtool_get_pauseparam(pp->phylink, pause);
++}
++
++static int mvneta_ethtool_set_pauseparam(struct net_device *dev,
++					 struct ethtool_pauseparam *pause)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++
++	return phylink_ethtool_set_pauseparam(pp->phylink, pause);
++}
++
+ static void mvneta_ethtool_get_strings(struct net_device *netdev, u32 sset,
+ 				       u8 *data)
+ {
+@@ -3903,6 +3927,8 @@ const struct ethtool_ops mvneta_eth_tool
+ 	.get_drvinfo    = mvneta_ethtool_get_drvinfo,
+ 	.get_ringparam  = mvneta_ethtool_get_ringparam,
+ 	.set_ringparam	= mvneta_ethtool_set_ringparam,
++	.get_pauseparam	= mvneta_ethtool_get_pauseparam,
++	.set_pauseparam	= mvneta_ethtool_set_pauseparam,
+ 	.get_strings	= mvneta_ethtool_get_strings,
+ 	.get_ethtool_stats = mvneta_ethtool_get_stats,
+ 	.get_sset_count	= mvneta_ethtool_get_sset_count,
diff --git a/target/linux/mvebu/patches-4.9/425-net-mvneta-enable-flow-control-for-PHY-connections.patch b/target/linux/mvebu/patches-4.9/425-net-mvneta-enable-flow-control-for-PHY-connections.patch
new file mode 100644
index 0000000..a4ba988
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/425-net-mvneta-enable-flow-control-for-PHY-connections.patch
@@ -0,0 +1,28 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 00:34:08 +0100
+Subject: [PATCH] net: mvneta: enable flow control for PHY connections
+
+Enable flow control support for PHY connections by indicating our
+support via the ethtool capabilities.  phylink takes care of the
+appropriate handling.
+
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3154,10 +3154,11 @@ static void mvneta_validate_support(stru
+ 		phylink_set(mask, 10baseT_Full);
+ 		phylink_set(mask, 100baseT_Half);
+ 		phylink_set(mask, 100baseT_Full);
+-	} else {
+-		phylink_set(mask, Pause);
+ 	}
+ 
++	if (mode != MLO_AN_FIXED)
++		phylink_set(mask, Pause);
++
+ 	bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+ }
+ 
diff --git a/target/linux/mvebu/patches-4.9/426-net-mvneta-enable-flow-control-for-fixed-connections.patch b/target/linux/mvebu/patches-4.9/426-net-mvneta-enable-flow-control-for-fixed-connections.patch
new file mode 100644
index 0000000..9c1d363
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/426-net-mvneta-enable-flow-control-for-fixed-connections.patch
@@ -0,0 +1,32 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Tue, 12 Jul 2016 00:04:13 +0100
+Subject: [PATCH] net: mvneta: enable flow control for fixed connections
+
+Allow symetric flow control to be enabled for fixed link connections as
+well as other types of connections by setting the supported and
+advertised capability bits.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3144,6 +3144,8 @@ static void mvneta_validate_support(stru
+ 	phylink_set(mask, BNC);
+ 	phylink_set(mask, Backplane);
+ 
++	/* Asymmetric pause is unsupported */
++	phylink_set(mask, Pause);
+ 	/* Half-duplex at speeds higher than 100Mbit is unsupported */
+ 	phylink_set(mask, 1000baseT_Full);
+ 	phylink_set(mask, 1000baseX_Full);
+@@ -3156,9 +3158,6 @@ static void mvneta_validate_support(stru
+ 		phylink_set(mask, 100baseT_Full);
+ 	}
+ 
+-	if (mode != MLO_AN_FIXED)
+-		phylink_set(mask, Pause);
+-
+ 	bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+ }
+ 
diff --git a/target/linux/mvebu/patches-4.9/427-phylink-add-EEE-support.patch b/target/linux/mvebu/patches-4.9/427-phylink-add-EEE-support.patch
new file mode 100644
index 0000000..27bae04
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/427-phylink-add-EEE-support.patch
@@ -0,0 +1,116 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 21:19:53 +0100
+Subject: [PATCH] phylink: add EEE support
+
+Add EEE hooks to phylink to allow the phylib EEE functions for the
+connected phy to be safely accessed.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3306,7 +3306,8 @@ static void mvneta_mac_link_down(struct
+ 	}
+ }
+ 
+-static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode)
++static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
++			       struct phy_device *phy)
+ {
+ 	struct mvneta_port *pp = netdev_priv(ndev);
+ 	u32 val;
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -379,7 +379,8 @@ static void phylink_resolve(struct work_
+ 			if (pl->phydev)
+ 				phylink_mac_config(pl, &link_state);
+ 
+-			pl->ops->mac_link_up(ndev, pl->link_an_mode);
++			pl->ops->mac_link_up(ndev, pl->link_an_mode,
++					     pl->phydev);
+ 
+ 			netif_carrier_on(ndev);
+ 
+@@ -929,6 +930,58 @@ int phylink_ethtool_set_pauseparam(struc
+ }
+ EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
+ 
++int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
++{
++	int ret = -EPROTONOSUPPORT;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->phydev)
++		ret = phy_init_eee(pl->phydev, clk_stop_enable);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_init_eee);
++
++int phylink_get_eee_err(struct phylink *pl)
++{
++	int ret = 0;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->phydev)
++		ret = phy_get_eee_err(pl->phydev);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_get_eee_err);
++
++int phylink_ethtool_get_eee(struct phylink *pl, struct ethtool_eee *eee)
++{
++	int ret = -EOPNOTSUPP;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->phydev)
++		ret = phy_ethtool_get_eee(pl->phydev, eee);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_get_eee);
++
++int phylink_ethtool_set_eee(struct phylink *pl, struct ethtool_eee *eee)
++{
++	int ret = -EOPNOTSUPP;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->phydev)
++		ret = phy_ethtool_set_eee(pl->phydev, eee);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_set_eee);
++
+ /* This emulates MII registers for a fixed-mode phy operating as per the
+  * passed in state. "aneg" defines if we report negotiation is possible.
+  *
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -70,7 +70,8 @@ struct phylink_mac_ops {
+ 	void (*mac_an_restart)(struct net_device *, unsigned int mode);
+ 
+ 	void (*mac_link_down)(struct net_device *, unsigned int mode);
+-	void (*mac_link_up)(struct net_device *, unsigned int mode);
++	void (*mac_link_up)(struct net_device *, unsigned int mode,
++			    struct phy_device *);
+ };
+ 
+ struct phylink *phylink_create(struct net_device *, struct device_node *,
+@@ -95,6 +96,10 @@ void phylink_ethtool_get_pauseparam(stru
+ 				    struct ethtool_pauseparam *);
+ int phylink_ethtool_set_pauseparam(struct phylink *,
+ 				   struct ethtool_pauseparam *);
++int phylink_init_eee(struct phylink *, bool);
++int phylink_get_eee_err(struct phylink *);
++int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *);
++int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *);
+ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
+ 
+ int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port,
diff --git a/target/linux/mvebu/patches-4.9/428-net-mvneta-add-EEE-support.patch b/target/linux/mvebu/patches-4.9/428-net-mvneta-add-EEE-support.patch
new file mode 100644
index 0000000..a8bafd9
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/428-net-mvneta-add-EEE-support.patch
@@ -0,0 +1,179 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Tue, 29 Sep 2015 15:17:39 +0100
+Subject: [PATCH] net: mvneta: add EEE support
+
+Add EEE support to mvneta.  This allows us to enable the low power idle
+support at MAC level if there is a PHY attached through phylink which
+supports LPI.  The appropriate ethtool support is provided to allow the
+feature to be controlled, including ethtool statistics for EEE wakeup
+errors.
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -243,6 +243,12 @@
+ #define MVNETA_TXQ_TOKEN_SIZE_REG(q)             (0x3e40 + ((q) << 2))
+ #define      MVNETA_TXQ_TOKEN_SIZE_MAX           0x7fffffff
+ 
++#define MVNETA_LPI_CTRL_0                        0x2cc0
++#define MVNETA_LPI_CTRL_1                        0x2cc4
++#define      MVNETA_LPI_REQUEST_ENABLE           BIT(0)
++#define MVNETA_LPI_CTRL_2                        0x2cc8
++#define MVNETA_LPI_STATUS                        0x2ccc
++
+ #define MVNETA_CAUSE_TXQ_SENT_DESC_ALL_MASK	 0xff
+ 
+ /* Descriptor ring Macros */
+@@ -316,6 +322,11 @@
+ #define MVNETA_RX_GET_BM_POOL_ID(rxd) \
+ 	(((rxd)->status & MVNETA_RXD_BM_POOL_MASK) >> MVNETA_RXD_BM_POOL_SHIFT)
+ 
++enum {
++	ETHTOOL_STAT_EEE_WAKEUP,
++	ETHTOOL_MAX_STATS,
++};
++
+ struct mvneta_statistic {
+ 	unsigned short offset;
+ 	unsigned short type;
+@@ -324,6 +335,7 @@ struct mvneta_statistic {
+ 
+ #define T_REG_32	32
+ #define T_REG_64	64
++#define T_SW		1
+ 
+ static const struct mvneta_statistic mvneta_statistics[] = {
+ 	{ 0x3000, T_REG_64, "good_octets_received", },
+@@ -358,6 +370,7 @@ static const struct mvneta_statistic mvn
+ 	{ 0x304c, T_REG_32, "broadcast_frames_sent", },
+ 	{ 0x3054, T_REG_32, "fc_sent", },
+ 	{ 0x300c, T_REG_32, "internal_mac_transmit_err", },
++	{ ETHTOOL_STAT_EEE_WAKEUP, T_SW, "eee_wakeup_errors", },
+ };
+ 
+ struct mvneta_pcpu_stats {
+@@ -416,6 +429,10 @@ struct mvneta_port {
+ 	struct mvneta_bm_pool *pool_short;
+ 	int bm_win_id;
+ 
++	bool eee_enabled;
++	bool eee_active;
++	bool tx_lpi_enabled;
++
+ 	u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
+ 
+ 	u32 indir[MVNETA_RSS_LU_TABLE_SIZE];
+@@ -3291,6 +3308,18 @@ static void mvneta_mac_config(struct net
+ 		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an);
+ }
+ 
++static void mvneta_set_eee(struct mvneta_port *pp, bool enable)
++{
++	u32 lpi_ctl1;
++
++	lpi_ctl1 = mvreg_read(pp, MVNETA_LPI_CTRL_1);
++	if (enable)
++		lpi_ctl1 |= MVNETA_LPI_REQUEST_ENABLE;
++	else
++		lpi_ctl1 &= ~MVNETA_LPI_REQUEST_ENABLE;
++	mvreg_write(pp, MVNETA_LPI_CTRL_1, lpi_ctl1);
++}
++
+ static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode)
+ {
+ 	struct mvneta_port *pp = netdev_priv(ndev);
+@@ -3304,6 +3333,9 @@ static void mvneta_mac_link_down(struct
+ 		val |= MVNETA_GMAC_FORCE_LINK_DOWN;
+ 		mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val);
+ 	}
++
++	pp->eee_active = false;
++	mvneta_set_eee(pp, false);
+ }
+ 
+ static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode,
+@@ -3320,6 +3352,11 @@ static void mvneta_mac_link_up(struct ne
+ 	}
+ 
+ 	mvneta_port_up(pp);
++
++	if (phy && pp->eee_enabled) {
++		pp->eee_active = phy_init_eee(phy, 0) >= 0;
++		mvneta_set_eee(pp, pp->eee_active && pp->tx_lpi_enabled);
++	}
+ }
+ 
+ static const struct phylink_mac_ops mvneta_phylink_ops = {
+@@ -3772,6 +3809,13 @@ static void mvneta_ethtool_update_stats(
+ 			high = readl_relaxed(base + s->offset + 4);
+ 			val = (u64)high << 32 | low;
+ 			break;
++		case T_SW:
++			switch (s->offset) {
++			case ETHTOOL_STAT_EEE_WAKEUP:
++				val = phylink_get_eee_err(pp->phylink);
++				break;
++			}
++			break;
+ 		}
+ 
+ 		pp->ethtool_stats[i] += val;
+@@ -3907,6 +3951,47 @@ static u16 mvneta_select_queue(struct ne
+ }
+ 
+ 
++static int mvneta_ethtool_get_eee(struct net_device *dev,
++				  struct ethtool_eee *eee)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++	u32 lpi_ctl0;
++
++	lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
++
++	eee->eee_enabled = pp->eee_enabled;
++	eee->eee_active = pp->eee_active;
++	eee->tx_lpi_enabled = pp->tx_lpi_enabled;
++	eee->tx_lpi_timer = (lpi_ctl0) >> 8; // * scale;
++
++	return phylink_ethtool_get_eee(pp->phylink, eee);
++}
++
++static int mvneta_ethtool_set_eee(struct net_device *dev,
++				  struct ethtool_eee *eee)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++	u32 lpi_ctl0;
++
++	/* The Armada 37x documents do not give limits for this other than
++	 * it being an 8-bit register. */
++	if (eee->tx_lpi_enabled &&
++	    (eee->tx_lpi_timer < 0 || eee->tx_lpi_timer > 255))
++		return -EINVAL;
++
++	lpi_ctl0 = mvreg_read(pp, MVNETA_LPI_CTRL_0);
++	lpi_ctl0 &= ~(0xff << 8);
++	lpi_ctl0 |= eee->tx_lpi_timer << 8;
++	mvreg_write(pp, MVNETA_LPI_CTRL_0, lpi_ctl0);
++
++	pp->eee_enabled = eee->eee_enabled;
++	pp->tx_lpi_enabled = eee->tx_lpi_enabled;
++
++	mvneta_set_eee(pp, eee->tx_lpi_enabled && eee->eee_enabled);
++
++	return phylink_ethtool_set_eee(pp->phylink, eee);
++}
++
+ static const struct net_device_ops mvneta_netdev_ops = {
+ 	.ndo_open            = mvneta_open,
+ 	.ndo_stop            = mvneta_stop,
+@@ -3939,6 +4024,8 @@ const struct ethtool_ops mvneta_eth_tool
+ 	.set_rxfh	= mvneta_ethtool_set_rxfh,
+ 	.get_link_ksettings = mvneta_ethtool_get_link_ksettings,
+ 	.set_link_ksettings = mvneta_ethtool_set_link_ksettings,
++	.get_eee	= mvneta_ethtool_get_eee,
++	.set_eee	= mvneta_ethtool_set_eee,
+ };
+ 
+ /* Initialize hw */
diff --git a/target/linux/mvebu/patches-4.9/429-phylink-add-module-EEPROM-support.patch b/target/linux/mvebu/patches-4.9/429-phylink-add-module-EEPROM-support.patch
new file mode 100644
index 0000000..88853ca
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/429-phylink-add-module-EEPROM-support.patch
@@ -0,0 +1,123 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 23:10:05 +0100
+Subject: [PATCH] phylink: add module EEPROM support
+
+Add support for reading module EEPROMs through phylink.
+
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -930,6 +930,36 @@ int phylink_ethtool_set_pauseparam(struc
+ }
+ EXPORT_SYMBOL_GPL(phylink_ethtool_set_pauseparam);
+ 
++int phylink_ethtool_get_module_info(struct phylink *pl,
++				    struct ethtool_modinfo *modinfo)
++{
++	int ret = -EOPNOTSUPP;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->module_ops)
++		ret = pl->module_ops->get_module_info(pl->module_data,
++						      modinfo);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_info);
++
++int phylink_ethtool_get_module_eeprom(struct phylink *pl,
++				      struct ethtool_eeprom *ee, u8 *buf)
++{
++	int ret = -EOPNOTSUPP;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->module_ops)
++		ret = pl->module_ops->get_module_eeprom(pl->module_data, ee,
++							buf);
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_ethtool_get_module_eeprom);
++
+ int phylink_init_eee(struct phylink *pl, bool clk_stop_enable)
+ {
+ 	int ret = -EPROTONOSUPPORT;
+@@ -1115,6 +1145,39 @@ EXPORT_SYMBOL_GPL(phylink_mii_ioctl);
+ 
+ 
+ 
++int phylink_register_module(struct phylink *pl, void *data,
++			    const struct phylink_module_ops *ops)
++{
++	int ret = -EBUSY;
++
++	mutex_lock(&pl->config_mutex);
++	if (!pl->module_ops) {
++		pl->module_ops = ops;
++		pl->module_data = data;
++		ret = 0;
++	}
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_register_module);
++
++int phylink_unregister_module(struct phylink *pl, void *data)
++{
++	int ret = -EINVAL;
++
++	mutex_lock(&pl->config_mutex);
++	if (pl->module_data == data) {
++		pl->module_ops = NULL;
++		pl->module_data = NULL;
++		ret = 0;
++	}
++	mutex_unlock(&pl->config_mutex);
++
++	return ret;
++}
++EXPORT_SYMBOL_GPL(phylink_unregister_module);
++
+ void phylink_disable(struct phylink *pl)
+ {
+ 	set_bit(PHYLINK_DISABLE_LINK, &pl->phylink_disable_state);
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -74,6 +74,11 @@ struct phylink_mac_ops {
+ 			    struct phy_device *);
+ };
+ 
++struct phylink_module_ops {
++	int (*get_module_info)(void *, struct ethtool_modinfo *);
++	int (*get_module_eeprom)(void *, struct ethtool_eeprom *, u8 *);
++};
++
+ struct phylink *phylink_create(struct net_device *, struct device_node *,
+ 	phy_interface_t iface, const struct phylink_mac_ops *ops);
+ void phylink_destroy(struct phylink *);
+@@ -96,12 +101,19 @@ void phylink_ethtool_get_pauseparam(stru
+ 				    struct ethtool_pauseparam *);
+ int phylink_ethtool_set_pauseparam(struct phylink *,
+ 				   struct ethtool_pauseparam *);
++int phylink_ethtool_get_module_info(struct phylink *, struct ethtool_modinfo *);
++int phylink_ethtool_get_module_eeprom(struct phylink *,
++				      struct ethtool_eeprom *, u8 *);
+ int phylink_init_eee(struct phylink *, bool);
+ int phylink_get_eee_err(struct phylink *);
+ int phylink_ethtool_get_eee(struct phylink *, struct ethtool_eee *);
+ int phylink_ethtool_set_eee(struct phylink *, struct ethtool_eee *);
+ int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
+ 
++int phylink_register_module(struct phylink *, void *,
++			    const struct phylink_module_ops *);
++int phylink_unregister_module(struct phylink *, void *);
++
+ int phylink_set_link(struct phylink *pl, unsigned int mode, u8 port,
+ 		     const unsigned long *support);
+ void phylink_disable(struct phylink *pl);
diff --git a/target/linux/mvebu/patches-4.9/430-net-mvneta-add-module-EEPROM-reading-support.patch b/target/linux/mvebu/patches-4.9/430-net-mvneta-add-module-EEPROM-reading-support.patch
new file mode 100644
index 0000000..a73aa85
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/430-net-mvneta-add-module-EEPROM-reading-support.patch
@@ -0,0 +1,41 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 23:32:39 +0100
+Subject: [PATCH] net: mvneta: add module EEPROM reading support
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -3951,6 +3951,22 @@ static u16 mvneta_select_queue(struct ne
+ }
+ 
+ 
++static int mvneta_ethtool_get_module_info(struct net_device *dev,
++					  struct ethtool_modinfo *modinfo)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++
++	return phylink_ethtool_get_module_info(pp->phylink, modinfo);
++}
++
++static int mvneta_ethtool_get_module_eeprom(struct net_device *dev,
++					    struct ethtool_eeprom *ee, u8 *buf)
++{
++	struct mvneta_port *pp = netdev_priv(dev);
++
++	return phylink_ethtool_get_module_eeprom(pp->phylink, ee, buf);
++}
++
+ static int mvneta_ethtool_get_eee(struct net_device *dev,
+ 				  struct ethtool_eee *eee)
+ {
+@@ -4024,6 +4040,8 @@ const struct ethtool_ops mvneta_eth_tool
+ 	.set_rxfh	= mvneta_ethtool_set_rxfh,
+ 	.get_link_ksettings = mvneta_ethtool_get_link_ksettings,
+ 	.set_link_ksettings = mvneta_ethtool_set_link_ksettings,
++	.get_module_info = mvneta_ethtool_get_module_info,
++	.get_module_eeprom = mvneta_ethtool_get_module_eeprom,
+ 	.get_eee	= mvneta_ethtool_get_eee,
+ 	.set_eee	= mvneta_ethtool_set_eee,
+ };
diff --git a/target/linux/mvebu/patches-4.9/431-sfp-phylink-hook-up-eeprom-functions.patch b/target/linux/mvebu/patches-4.9/431-sfp-phylink-hook-up-eeprom-functions.patch
new file mode 100644
index 0000000..9f049aa
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/431-sfp-phylink-hook-up-eeprom-functions.patch
@@ -0,0 +1,65 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 8 Oct 2015 23:49:47 +0100
+Subject: [PATCH] sfp/phylink: hook up eeprom functions
+
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -985,11 +985,9 @@ static void sfp_sm_event(struct sfp *sfp
+ 	mutex_unlock(&sfp->sm_mutex);
+ }
+ 
+-#if 0
+-static int sfp_phy_module_info(struct phy_device *phy,
+-			       struct ethtool_modinfo *modinfo)
++static int sfp_module_info(void *priv, struct ethtool_modinfo *modinfo)
+ {
+-	struct sfp *sfp = phy->priv;
++	struct sfp *sfp = priv;
+ 
+ 	/* locking... and check module is present */
+ 
+@@ -1003,10 +1001,9 @@ static int sfp_phy_module_info(struct ph
+ 	return 0;
+ }
+ 
+-static int sfp_phy_module_eeprom(struct phy_device *phy,
+-	struct ethtool_eeprom *ee, u8 *data)
++static int sfp_module_eeprom(void *priv, struct ethtool_eeprom *ee, u8 *data)
+ {
+-	struct sfp *sfp = phy->priv;
++	struct sfp *sfp = priv;
+ 	unsigned int first, last, len;
+ 	int ret;
+ 
+@@ -1037,7 +1034,11 @@ static int sfp_phy_module_eeprom(struct
+ 	}
+ 	return 0;
+ }
+-#endif
++
++static const struct phylink_module_ops sfp_module_ops = {
++	.get_module_info = sfp_module_info,
++	.get_module_eeprom = sfp_module_eeprom,
++};
+ 
+ static void sfp_timeout(struct work_struct *work)
+ {
+@@ -1113,6 +1114,7 @@ static int sfp_netdev_notify(struct noti
+ 	case NETDEV_UNREGISTER:
+ 		if (sfp->mod_phy && sfp->phylink)
+ 			phylink_disconnect_phy(sfp->phylink);
++		phylink_unregister_module(sfp->phylink, sfp);
+ 		sfp->phylink = NULL;
+ 		dev_put(sfp->ndev);
+ 		sfp->ndev = NULL;
+@@ -1230,6 +1232,7 @@ static int sfp_probe(struct platform_dev
+ 		}
+ 
+ 		phylink_disable(sfp->phylink);
++		phylink_register_module(sfp->phylink, sfp, &sfp_module_ops);
+ 	}
+ 
+ 	sfp->state = sfp_get_state(sfp);
diff --git a/target/linux/mvebu/patches-4.9/432-phy-marvell-88E1512-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/432-phy-marvell-88E1512-add-flow-control-support.patch
new file mode 100644
index 0000000..749d168
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/432-phy-marvell-88E1512-add-flow-control-support.patch
@@ -0,0 +1,24 @@
+From: Russell King <rmk+kernel at arm.linux.org.uk>
+Date: Thu, 1 Oct 2015 00:34:08 +0100
+Subject: [PATCH] phy: marvell: 88E1512: add flow control support
+
+The Marvell PHYs support pause frame advertisments, so we should not be
+masking their support off.  Add the necessary flag to the Marvell PHY
+to allow any MAC level pause frame support to be advertised.
+
+Reviewed-by: Florian Fainelli <f.fainelli at gmail.com>
+Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
+---
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -1671,7 +1671,8 @@ static struct phy_driver marvell_drivers
+ 		.phy_id = MARVELL_PHY_ID_88E1510,
+ 		.phy_id_mask = MARVELL_PHY_ID_MASK,
+ 		.name = "Marvell 88E1510",
+-		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE,
++		.features = PHY_GBIT_FEATURES | SUPPORTED_FIBRE |
++				SUPPORTED_Pause,
+ 		.flags = PHY_HAS_INTERRUPT,
+ 		.probe = marvell_probe,
+ 		.config_init = &m88e1510_config_init,
diff --git a/target/linux/mvebu/patches-4.9/433-phy-marvell-88E1111-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/433-phy-marvell-88E1111-add-flow-control-support.patch
new file mode 100644
index 0000000..1b8bace
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/433-phy-marvell-88E1111-add-flow-control-support.patch
@@ -0,0 +1,22 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Tue, 12 Jul 2016 16:45:43 +0100
+Subject: [PATCH] phy: marvell: 88E1111: add flow control support
+
+The Marvell PHYs support pause frame advertisments, so we should not be
+masking their support off.  Add the necessary flag to the Marvell PHY
+to allow any MAC level pause frame support to be advertised.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -1523,7 +1523,7 @@ static struct phy_driver marvell_drivers
+ 		.phy_id = MARVELL_PHY_ID_88E1111,
+ 		.phy_id_mask = MARVELL_PHY_ID_MASK,
+ 		.name = "Marvell 88E1111",
+-		.features = PHY_GBIT_FEATURES,
++		.features = PHY_GBIT_FEATURES | SUPPORTED_Pause,
+ 		.flags = PHY_HAS_INTERRUPT,
+ 		.probe = marvell_probe,
+ 		.config_init = &m88e1111_config_init,
diff --git a/target/linux/mvebu/patches-4.9/434-phy-marvell-88E1540-add-flow-control-support.patch b/target/linux/mvebu/patches-4.9/434-phy-marvell-88E1540-add-flow-control-support.patch
new file mode 100644
index 0000000..95a4f57
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/434-phy-marvell-88E1540-add-flow-control-support.patch
@@ -0,0 +1,22 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Tue, 12 Jul 2016 16:45:43 +0100
+Subject: [PATCH] phy: marvell: 88E1540: add flow control support
+
+The Marvell PHYs support pause frame advertisments, so we should not be
+masking their support off.  Add the necessary flag to the Marvell PHY
+to allow any MAC level pause frame support to be advertised.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -1691,7 +1691,7 @@ static struct phy_driver marvell_drivers
+ 		.phy_id = MARVELL_PHY_ID_88E1540,
+ 		.phy_id_mask = MARVELL_PHY_ID_MASK,
+ 		.name = "Marvell 88E1540",
+-		.features = PHY_GBIT_FEATURES,
++		.features = PHY_GBIT_FEATURES | SUPPORTED_Pause,
+ 		.flags = PHY_HAS_INTERRUPT,
+ 		.probe = marvell_probe,
+ 		.config_init = &marvell_config_init,
diff --git a/target/linux/mvebu/patches-4.9/435-net-phy-fix-marvell-phy-status-reading.patch b/target/linux/mvebu/patches-4.9/435-net-phy-fix-marvell-phy-status-reading.patch
new file mode 100644
index 0000000..f0fe26e
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/435-net-phy-fix-marvell-phy-status-reading.patch
@@ -0,0 +1,26 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Fri, 23 Dec 2016 18:54:01 +0000
+Subject: [PATCH] net: phy: fix marvell phy status reading
+
+The Marvell driver incorrectly provides phydev->lp_advertising as the
+logical and of the link partner's advert and our advert.  This is
+incorrect - this field is supposed to store the link parter's unmodified
+advertisment.
+
+This allows ethtool to report the correct link partner auto-negotiation
+status.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -1106,8 +1106,6 @@ static int marvell_read_status_page(stru
+ 		if (adv < 0)
+ 			return adv;
+ 
+-		lpa &= adv;
+-
+ 		if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
+ 			phydev->duplex = DUPLEX_FULL;
+ 		else
diff --git a/target/linux/mvebu/patches-4.9/436-phylink-propagate-PHY-interface-mode-to-MAC-driver.patch b/target/linux/mvebu/patches-4.9/436-phylink-propagate-PHY-interface-mode-to-MAC-driver.patch
new file mode 100644
index 0000000..9b872b8
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/436-phylink-propagate-PHY-interface-mode-to-MAC-driver.patch
@@ -0,0 +1,126 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Tue, 3 Jan 2017 18:34:17 +0000
+Subject: [PATCH] phylink: propagate PHY interface mode to MAC driver
+
+Some 10Gigabit PHYs automatically switch the mode of their host
+interface depending on their negotiated speed.  We need to communicate
+this to the MAC driver so the MAC can switch its host interface to
+match the PHYs new operating mode.  Provide the current PHY interface
+mode to the MAC driver.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -242,8 +242,9 @@ static void phylink_mac_config(struct ph
+ 			       const struct phylink_link_state *state)
+ {
+ 	netdev_dbg(pl->netdev,
+-		   "%s: mode=%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
++		   "%s: mode=%s/%s/%s/%s adv=%*pb pause=%02x link=%u an=%u\n",
+ 		   __func__, phylink_an_mode_str(pl->link_an_mode),
++		   phy_modes(state->interface),
+ 		   phy_speed_to_str(state->speed),
+ 		   phy_duplex_to_str(state->duplex),
+ 		   __ETHTOOL_LINK_MODE_MASK_NBITS, state->advertising,
+@@ -264,6 +265,7 @@ static int phylink_get_mac_state(struct
+ 
+ 	linkmode_copy(state->advertising, pl->link_config.advertising);
+ 	linkmode_zero(state->lp_advertising);
++	state->interface = pl->link_config.interface;
+ 	state->an_enabled = pl->link_config.an_enabled;
+ 	state->link = 1;
+ 
+@@ -344,19 +346,38 @@ static void phylink_resolve(struct work_
+ 		case MLO_AN_PHY:
+ 			link_state = pl->phy_state;
+ 			phylink_resolve_flow(pl, &link_state);
++			phylink_mac_config(pl, &link_state);
+ 			break;
+ 
+ 		case MLO_AN_FIXED:
+ 			phylink_get_fixed_state(pl, &link_state);
++			phylink_mac_config(pl, &link_state);
+ 			break;
+ 
+ 		case MLO_AN_SGMII:
+ 			phylink_get_mac_state(pl, &link_state);
+ 			if (pl->phydev) {
++				bool changed = false;
++
+ 				link_state.link = link_state.link &&
+ 						  pl->phy_state.link;
+-				link_state.pause |= pl->phy_state.pause;
+-				phylink_resolve_flow(pl, &link_state);
++
++				if (pl->phy_state.interface !=
++				    link_state.interface) {
++					link_state.interface = pl->phy_state.interface;
++					changed = true;
++				}
++
++				/* Propagate the flow control from the PHY
++				 * to the MAC. Also propagate the interface
++				 * if changed.
++				 */
++				if (pl->phy_state.link || changed) {
++					link_state.pause |= pl->phy_state.pause;
++					phylink_resolve_flow(pl, &link_state);
++
++					phylink_mac_config(pl, &link_state);
++				}
+ 			}
+ 			break;
+ 
+@@ -372,13 +393,6 @@ static void phylink_resolve(struct work_
+ 			pl->ops->mac_link_down(ndev, pl->link_an_mode);
+ 			netdev_info(ndev, "Link is Down\n");
+ 		} else {
+-			/* If we have a PHY, we need the MAC updated with
+-			 * the current link parameters (eg, in SGMII mode,
+-			 * with flow control status.)
+-			 */
+-			if (pl->phydev)
+-				phylink_mac_config(pl, &link_state);
+-
+ 			pl->ops->mac_link_up(ndev, pl->link_an_mode,
+ 					     pl->phydev);
+ 
+@@ -414,8 +428,10 @@ struct phylink *phylink_create(struct ne
+ 	mutex_init(&pl->config_mutex);
+ 	INIT_WORK(&pl->resolve, phylink_resolve);
+ 	pl->netdev = ndev;
++	pl->phy_state.interface = iface;
+ 	pl->link_interface = iface;
+ 	pl->link_port = PORT_MII;
++	pl->link_config.interface = iface;
+ 	pl->link_config.pause = MLO_PAUSE_AN;
+ 	pl->link_config.speed = SPEED_UNKNOWN;
+ 	pl->link_config.duplex = DUPLEX_UNKNOWN;
+@@ -471,12 +487,14 @@ void phylink_phy_change(struct phy_devic
+ 		pl->phy_state.pause |= MLO_PAUSE_SYM;
+ 	if (phydev->asym_pause)
+ 		pl->phy_state.pause |= MLO_PAUSE_ASYM;
++	pl->phy_state.interface = phydev->interface;
+ 	pl->phy_state.link = up;
+ 	mutex_unlock(&pl->state_mutex);
+ 
+ 	phylink_run_resolve(pl);
+ 
+-	netdev_dbg(pl->netdev, "phy link %s %s/%s\n", up ? "up" : "down",
++	netdev_dbg(pl->netdev, "phy link %s %s/%s/%s\n", up ? "up" : "down",
++	           phy_modes(phydev->interface),
+ 		   phy_speed_to_str(phydev->speed),
+ 		   phy_duplex_to_str(phydev->duplex));
+ }
+--- a/include/linux/phylink.h
++++ b/include/linux/phylink.h
+@@ -27,6 +27,7 @@ enum {
+ struct phylink_link_state {
+ 	__ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
+ 	__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
++	phy_interface_t interface;	/* PHY_INTERFACE_xxx */
+ 	int speed;
+ 	int duplex;
+ 	int pause;
diff --git a/target/linux/mvebu/patches-4.9/437-phylink-ensure-link-drops-are-reported.patch b/target/linux/mvebu/patches-4.9/437-phylink-ensure-link-drops-are-reported.patch
new file mode 100644
index 0000000..8544197
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/437-phylink-ensure-link-drops-are-reported.patch
@@ -0,0 +1,52 @@
+From: Russell King <rmk+kernel at armlinux.org.uk>
+Date: Mon, 19 Dec 2016 12:17:57 +0000
+Subject: [PATCH] phylink: ensure link drops are reported
+
+When the MAC reports a link failure, it can be momentary.  Ensure
+that the event is reported by latching the loss of link, so that the
+worker reports link down.
+
+Signed-off-by: Russell King <rmk+kernel at armlinux.org.uk>
+---
+
+--- a/drivers/net/phy/phylink.c
++++ b/drivers/net/phy/phylink.c
+@@ -60,6 +60,8 @@ struct phylink {
+ 	struct phylink_link_state phy_state;
+ 	struct work_struct resolve;
+ 
++	bool mac_link_dropped;
++
+ 	const struct phylink_module_ops *module_ops;
+ 	void *module_data;
+ };
+@@ -340,6 +342,9 @@ static void phylink_resolve(struct work_
+ 
+ 	mutex_lock(&pl->state_mutex);
+ 	if (pl->phylink_disable_state) {
++		pl->mac_link_dropped = false;
++		link_state.link = false;
++	} else if (pl->mac_link_dropped) {
+ 		link_state.link = false;
+ 	} else {
+ 		switch (pl->link_an_mode) {
+@@ -405,6 +410,10 @@ static void phylink_resolve(struct work_
+ 				    phylink_pause_to_str(link_state.pause));
+ 		}
+ 	}
++	if (!link_state.link && pl->mac_link_dropped) {
++		pl->mac_link_dropped = false;
++		queue_work(system_power_efficient_wq, &pl->resolve);
++	}
+ 	mutex_unlock(&pl->state_mutex);
+ }
+ 
+@@ -641,6 +650,8 @@ EXPORT_SYMBOL_GPL(phylink_disconnect_phy
+ 
+ void phylink_mac_change(struct phylink *pl, bool up)
+ {
++	if (!up)
++		pl->mac_link_dropped = true;
+ 	phylink_run_resolve(pl);
+ 	netdev_dbg(pl->netdev, "mac link %s\n", up ? "up" : "down");
+ }
diff --git a/target/linux/mvebu/patches-4.9/450-reprobe_sfp_phy.patch b/target/linux/mvebu/patches-4.9/450-reprobe_sfp_phy.patch
new file mode 100644
index 0000000..69eaa37
--- /dev/null
+++ b/target/linux/mvebu/patches-4.9/450-reprobe_sfp_phy.patch
@@ -0,0 +1,119 @@
+From 28baa5e2635285b178326b301f534ed95c65dd01 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jonas.gorski at gmail.com>
+Date: Thu, 29 Sep 2016 11:44:39 +0200
+Subject: [PATCH] sfp: retry phy probe if unsuccessful
+
+Some phys seem to take longer than 50 ms to come out of reset, so retry
+until we find a phy.
+
+Signed-off-by: Jonas Gorski <jonas.gorski at gmail.com>
+---
+ drivers/net/phy/sfp.c | 38 +++++++++++++++++++++++++-------------
+ 1 file changed, 25 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -491,7 +491,7 @@ static void sfp_sm_phy_detach(struct sfp
+ 	sfp->mod_phy = NULL;
+ }
+ 
+-static void sfp_sm_probe_phy(struct sfp *sfp)
++static int sfp_sm_probe_phy(struct sfp *sfp)
+ {
+ 	struct phy_device *phy;
+ 	int err;
+@@ -500,12 +500,12 @@ static void sfp_sm_probe_phy(struct sfp
+ 
+ 	phy = mdiobus_scan(sfp->i2c_mii, SFP_PHY_ADDR);
+ 	if (IS_ERR(phy)) {
++		if (PTR_ERR(phy) == -ENODEV) {
++			dev_dbg(sfp->dev, "no PHY detected\n");
++			return -EAGAIN;
++		}
+ 		dev_err(sfp->dev, "mdiobus scan returned %ld\n", PTR_ERR(phy));
+-		return;
+-	}
+-	if (!phy) {
+-		dev_info(sfp->dev, "no PHY detected\n");
+-		return;
++		return PTR_ERR(phy);
+ 	}
+ 
+ 	if (sfp->phylink) {
+@@ -515,12 +515,14 @@ static void sfp_sm_probe_phy(struct sfp
+ 			phy_device_free(phy);
+ 			dev_err(sfp->dev, "phylink_connect_phy failed: %d\n",
+ 				err);
+-			return;
++			return err;
+ 		}
+ 	}
+ 
+ 	sfp->mod_phy = phy;
+ 	phy_start(phy);
++
++	return 0;
+ }
+ 
+ static void sfp_sm_link_up(struct sfp *sfp)
+@@ -569,30 +571,41 @@ static void sfp_sm_fault(struct sfp *sfp
+ 
+ static void sfp_sm_mod_init(struct sfp *sfp)
+ {
++	int ret = 0;
++
+ 	sfp_module_tx_enable(sfp);
+ 
+-	/* Wait t_init before indicating that the link is up, provided the
+-	 * current state indicates no TX_FAULT.  If TX_FAULT clears before
+-	 * this time, that's fine too.
+-	 */
+-	sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
+-	sfp->sm_retries = 5;
++	if (!sfp->phylink)
++		return;
+ 
+-	if (sfp->phylink) {
+-		/* Setting the serdes link mode is guesswork: there's no
+-		 * field in the EEPROM which indicates what mode should
+-		 * be used.
+-		 *
+-		 * If it's a gigabit-only fiber module, it probably does
+-		 * not have a PHY, so switch to 802.3z negotiation mode.
+-		 * Otherwise, switch to SGMII mode (which is required to
+-		 * support non-gigabit speeds) and probe for a PHY.
++	/* Setting the serdes link mode is guesswork: there's no
++	 * field in the EEPROM which indicates what mode should
++	 * be used.
++	 *
++	 * If it's a gigabit-only fiber module, it probably does
++	 * not have a PHY, so switch to 802.3z negotiation mode.
++	 * Otherwise, switch to SGMII mode (which is required to
++	 * support non-gigabit speeds) and probe for a PHY.
++	 */
++	if (sfp->id.base.e1000_base_t ||
++	    sfp->id.base.e100_base_lx ||
++	    sfp->id.base.e100_base_fx)
++		ret = sfp_sm_probe_phy(sfp);
++
++	if (!ret) {
++		/* Wait t_init before indicating that the link is up, provided
++		 * the current state indicates no TX_FAULT.  If TX_FAULT clears
++		 * this time, that's fine too.
+ 		 */
+-		if (sfp->id.base.e1000_base_t ||
+-		    sfp->id.base.e100_base_lx ||
+-		    sfp->id.base.e100_base_fx)
+-			sfp_sm_probe_phy(sfp);
++		sfp_sm_next(sfp, SFP_S_INIT, T_INIT_JIFFIES);
++		sfp->sm_retries = 5;
++		return;
+ 	}
++
++	if (ret == -EAGAIN)
++		sfp_sm_set_timer(sfp, T_PROBE_RETRY);
++	else
++		sfp_sm_next(sfp, SFP_S_TX_DISABLE, 0);
+ }
+ 
+ static int sfp_sm_mod_probe(struct sfp *sfp)



More information about the lede-commits mailing list