aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-4.9
diff options
context:
space:
mode:
authorJohn Crispin <john@phrozen.org>2016-12-25 20:11:34 +0100
committerJohn Crispin <john@phrozen.org>2017-08-05 08:46:36 +0200
commit74d00a8c3849c1340efd713eb94b786e304c201f (patch)
treede481743de61c34da96ab5f9dba3af3edcfb8260 /target/linux/generic/backport-4.9
parentde350550ef648d9728351b986b0516fa29465c45 (diff)
downloadupstream-74d00a8c3849c1340efd713eb94b786e304c201f.tar.gz
upstream-74d00a8c3849c1340efd713eb94b786e304c201f.tar.bz2
upstream-74d00a8c3849c1340efd713eb94b786e304c201f.zip
kernel: split patches folder up into backport, pending and hack folders
* properly format/comment all patches * merge debloat patches * merge Kconfig patches * merge swconfig patches * merge hotplug patches * drop 200-fix_localversion.patch - upstream * drop 222-arm_zimage_none.patch - unused * drop 252-mv_cesa_depends.patch - no longer required * drop 410-mtd-move-forward-declaration-of-struct-mtd_info.patch - unused * drop 661-fq_codel_keep_dropped_stats.patch - outdated * drop 702-phy_add_aneg_done_function.patch - upstream * drop 840-rtc7301.patch - unused * drop 841-rtc_pt7c4338.patch - upstream * drop 921-use_preinit_as_init.patch - unused * drop spio-gpio-old and gpio-mmc - unused Signed-off-by: John Crispin <john@phrozen.org>
Diffstat (limited to 'target/linux/generic/backport-4.9')
-rw-r--r--target/linux/generic/backport-4.9/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch34
-rw-r--r--target/linux/generic/backport-4.9/011-kbuild-export-SUBARCH.patch28
-rw-r--r--target/linux/generic/backport-4.9/020-backport_netfilter_rtcache.patch558
-rw-r--r--target/linux/generic/backport-4.9/021-bridge-multicast-to-unicast.patch499
-rw-r--r--target/linux/generic/backport-4.9/022-net-add-devm-version-of-alloc_etherdev_mqs-function.patch69
-rw-r--r--target/linux/generic/backport-4.9/023-1-smsc95xx-Use-skb_cow_head-to-deal-with-cloned-skbs.patch40
-rw-r--r--target/linux/generic/backport-4.9/023-2-smsc75xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch36
-rw-r--r--target/linux/generic/backport-4.9/023-3-cx82310_eth-use-skb_cow_head-to-deal-with-cloned-skb.patch35
-rw-r--r--target/linux/generic/backport-4.9/023-4-sr9700-use-skb_cow_head-to-deal-with-cloned-skbs.patch37
-rw-r--r--target/linux/generic/backport-4.9/023-5-lan78xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch38
-rw-r--r--target/linux/generic/backport-4.9/023-6-ch9200-use-skb_cow_head-to-deal-with-cloned-skbs.patch38
-rw-r--r--target/linux/generic/backport-4.9/023-7-kaweth-use-skb_cow_head-to-deal-with-cloned-skbs.patch43
-rw-r--r--target/linux/generic/backport-4.9/030-01-ubifs-Drop-softlimit-and-delta-fields-from-struct-ub.patch82
-rw-r--r--target/linux/generic/backport-4.9/030-02-ubifs-Use-dirty_writeback_interval-value-for-wbuf-ti.patch66
-rw-r--r--target/linux/generic/backport-4.9/050-usb-dwc2-Remove-unnecessary-kfree.patch24
-rw-r--r--target/linux/generic/backport-4.9/060-0002-mtd-bcm47xxsflash-use-platform_-set-get-_drvdata.patch63
-rw-r--r--target/linux/generic/backport-4.9/060-0003-mtd-bcm47xxsflash-support-reading-flash-out-of-mappi.patch81
-rw-r--r--target/linux/generic/backport-4.9/060-0004-mtd-bcm47xxpart-move-TRX-parsing-code-to-separated-f.patch180
-rw-r--r--target/linux/generic/backport-4.9/060-0005-mtd-bcm47xxpart-support-layouts-with-multiple-TRX-pa.patch112
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0001-mtd-spi-nor-add-Macronix-mx25u25635f-to-list-of-know.patch22
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0002-mtd-spi-nor-fix-spansion-quad-enable.patch42
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0003-mtd-spi-nor-fix-flags-for-s25fl128s.patch28
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0004-mtd-spi-nor-add-support-for-s25fl208k.patch23
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0005-mtd-spi-nor-Add-at25df321-spi-nor-flash-support.patch26
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0006-mtd-spi-nor-Add-support-for-N25Q016A.patch29
-rw-r--r--target/linux/generic/backport-4.9/061-v4.10-0007-mtd-spi-nor-Add-support-for-mr25h40.patch25
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0001-mtd-spi-nor-Add-support-for-S3AN-spi-nor-devices.patch312
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0002-mtd-spi-nor-improve-macronix_quad_enable.patch28
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0003-mtd-spi-nor-remove-WARN_ONCE-message-in-spi_nor_writ.patch33
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0004-mtd-spi-nor-rename-SPINOR_OP_-macros-of-the-4-byte-a.patch187
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0005-mtd-spi-nor-add-a-stateless-method-to-support-memory.patch150
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0006-mtd-spi-nor-Add-lock-unlock-support-for-f25l32pa.patch26
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0007-mtd-spi-nor-Fix-S3AN-addressing-calculation.patch35
-rw-r--r--target/linux/generic/backport-4.9/062-v4.11-0008-mtd-spi-nor-Add-support-for-gd25q16.patch28
-rw-r--r--target/linux/generic/backport-4.9/063-mtd-spi-nor-enable-stateless-4b-op-codes-for-mx25u25.patch29
-rw-r--r--target/linux/generic/backport-4.9/064-v4.11-0001-mtd-introduce-function-max_bad_blocks.patch73
-rw-r--r--target/linux/generic/backport-4.9/064-v4.11-0002-mtd-Add-partition-device-node-to-mtd-partition-devic.patch50
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0001-mtd-handle-partitioning-on-devices-with-0-erasesize.patch77
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0002-mtd-partitions-factor-out-code-calling-parser.patch68
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0003-mtd-partitions-add-helper-for-deleting-partition.patch119
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0004-mtd-partitions-remove-sysfs-files-when-deleting-all-.patch45
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0005-mtd-partitions-rename-master-to-the-parent-where-app.patch606
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0006-mtd-partitions-add-support-for-subpartitions.patch96
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0007-mtd-partitions-add-support-for-partition-parsers.patch110
-rw-r--r--target/linux/generic/backport-4.9/065-v4.13-0008-mtd-extract-TRX-parser-out-of-bcm47xxpart-into-a-sep.patch320
-rw-r--r--target/linux/generic/backport-4.9/070-bcma-from-4.11.patch85
-rw-r--r--target/linux/generic/backport-4.9/071-v4.10-0001-net-bgmac-allocate-struct-bgmac-just-once-don-t-copy.patch139
-rw-r--r--target/linux/generic/backport-4.9/071-v4.10-0002-net-bgmac-drop-struct-bcma_mdio-we-don-t-need-anymor.patch261
-rw-r--r--target/linux/generic/backport-4.9/071-v4.10-0003-net-bgmac-use-PHY-subsystem-for-initializing-PHY.patch53
-rw-r--r--target/linux/generic/backport-4.9/072-bcma-from-4.12.patch47
-rw-r--r--target/linux/generic/backport-4.9/075-v4.10-0001-net-phy-broadcom-Update-Auxiliary-Control-Register-m.patch34
-rw-r--r--target/linux/generic/backport-4.9/075-v4.10-0002-net-phy-broadcom-Add-support-for-BCM54612E.patch94
-rw-r--r--target/linux/generic/backport-4.9/075-v4.10-0003-net-phy-broadcom-add-bcm54xx_auxctl_read.patch41
-rw-r--r--target/linux/generic/backport-4.9/075-v4.10-0004-net-phy-broadcom-Add-BCM54810-PHY-entry.patch176
-rw-r--r--target/linux/generic/backport-4.9/075-v4.10-0005-net-phy-broadcom-Move-bcm54xx_auxctl_-read-write-to-.patch74
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0001-net-phy-broadcom-Allow-enabling-or-disabling-of-EEE.patch87
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0002-net-phy-broadcom-Add-support-code-for-reading-PHY-co.patch125
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0003-net-phy-bcm7xxx-Add-entry-for-BCM7278.patch38
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0004-net-phy-bcm7xxx-Implement-EGPHY-workaround-for-7278.patch68
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0005-net-phy-broadcom-use-auxctl-reading-helper-in-BCM546.patch45
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0006-net-phy-broadcom-add-support-for-BCM54210E.patch89
-rw-r--r--target/linux/generic/backport-4.9/076-v4.11-0007-net-phy-broadcom-rehook-BCM54612E-specific-init.patch121
-rw-r--r--target/linux/generic/backport-4.9/080-0001-leds-core-add-OF-variants-of-LED-registering-functio.patch120
-rw-r--r--target/linux/generic/backport-4.9/080-0002-leds-gpio-use-OF-variant-of-LED-registering-function.patch60
-rw-r--r--target/linux/generic/backport-4.9/081-0001-thermal-bcm2835-add-thermal-driver-for-bcm2835-SoC.patch365
-rw-r--r--target/linux/generic/backport-4.9/081-0002-thermal-broadcom-add-Northstar-thermal-driver.patch173
-rw-r--r--target/linux/generic/backport-4.9/082-0001-usb-core-read-USB-ports-from-DT-in-the-usbport-LED-t.patch106
-rw-r--r--target/linux/generic/backport-4.9/087-regmap-make-LZO-cache-optional.patch69
68 files changed, 7120 insertions, 0 deletions
diff --git a/target/linux/generic/backport-4.9/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch b/target/linux/generic/backport-4.9/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch
new file mode 100644
index 0000000000..966095ed60
--- /dev/null
+++ b/target/linux/generic/backport-4.9/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch
@@ -0,0 +1,34 @@
+From 13b1ecc3401653a355798eb1dee10cc1608202f4 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 18 Jan 2016 12:27:49 +0100
+Subject: [PATCH 33/34] Kbuild: don't hardcode path to awk in
+ scripts/ld-version.sh
+
+On some systems /usr/bin/awk does not exist, or is broken. Find it via
+$PATH instead.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ scripts/ld-version.sh | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/scripts/ld-version.sh b/scripts/ld-version.sh
+index d135882e2c40..66e6e5ed1b7b 100755
+--- a/scripts/ld-version.sh
++++ b/scripts/ld-version.sh
+@@ -1,5 +1,6 @@
+-#!/usr/bin/awk -f
++#!/bin/sh
+ # extract linker version number from stdin and turn into single number
++exec awk '
+ {
+ gsub(".*\\)", "");
+ gsub(".*version ", "");
+@@ -8,3 +9,4 @@
+ print a[1]*100000000 + a[2]*1000000 + a[3]*10000;
+ exit
+ }
++'
+--
+2.11.0
+
diff --git a/target/linux/generic/backport-4.9/011-kbuild-export-SUBARCH.patch b/target/linux/generic/backport-4.9/011-kbuild-export-SUBARCH.patch
new file mode 100644
index 0000000000..6cc473d703
--- /dev/null
+++ b/target/linux/generic/backport-4.9/011-kbuild-export-SUBARCH.patch
@@ -0,0 +1,28 @@
+From 173019b66dcc9d68ad9333aa744dad1e369b5aa8 Mon Sep 17 00:00:00 2001
+From: Felix Fietkau <nbd@nbd.name>
+Date: Sun, 9 Jul 2017 00:26:53 +0200
+Subject: [PATCH 34/34] kernel: add compile fix for linux 4.9 on x86
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+ Makefile | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index dd0a67d6e392..6e37f6bb669a 100644
+--- a/Makefile
++++ b/Makefile
+@@ -420,8 +420,8 @@ KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
+ KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
+
+ export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
+-export ARCH SRCARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC
+-export CPP AR NM STRIP OBJCOPY OBJDUMP
++export ARCH SRCARCH SUBARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD
++export CC CPP AR NM STRIP OBJCOPY OBJDUMP
+ export MAKE AWK GENKSYMS INSTALLKERNEL PERL PYTHON UTS_MACHINE
+ export HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
+
+--
+2.11.0
+
diff --git a/target/linux/generic/backport-4.9/020-backport_netfilter_rtcache.patch b/target/linux/generic/backport-4.9/020-backport_netfilter_rtcache.patch
new file mode 100644
index 0000000000..5e4c844bf8
--- /dev/null
+++ b/target/linux/generic/backport-4.9/020-backport_netfilter_rtcache.patch
@@ -0,0 +1,558 @@
+From c4d66e57455f5384128753674cc0f9e02db5e1f6 Mon Sep 17 00:00:00 2001
+From: Florian Westphal <fw@strlen.de>
+Date: Sun, 9 Jul 2017 08:58:30 +0200
+Subject: [PATCH] netfilter: conntrack: cache route for forwarded connections
+
+... to avoid per-packet FIB lookup if possible.
+
+The cached dst is re-used provided the input interface
+is the same as that of the previous packet in the same direction.
+
+If not, the cached dst is invalidated.
+
+For ipv6 we also need to store sernum, else dst_check doesn't work,
+pointed out by Eric Dumazet.
+
+This should speed up forwarding when conntrack is already in use
+anyway, especially when using reverse path filtering -- active RPF
+enforces two FIB lookups for each packet.
+
+Before the routing cache removal this didn't matter since RPF was performed
+only when route cache didn't yield a result; but without route cache it
+comes at higher price.
+
+Julian Anastasov suggested to add NETDEV_UNREGISTER handler to
+avoid holding on to dsts of 'frozen' conntracks.
+
+Signed-off-by: Florian Westphal <fw@strlen.de>
+---
+ include/net/netfilter/nf_conntrack_extend.h | 4 +
+ include/net/netfilter/nf_conntrack_rtcache.h | 34 +++
+ net/netfilter/Kconfig | 12 +
+ net/netfilter/Makefile | 3 +
+ net/netfilter/nf_conntrack_rtcache.c | 413 +++++++++++++++++++++++++++
+ 5 files changed, 466 insertions(+)
+ create mode 100644 include/net/netfilter/nf_conntrack_rtcache.h
+ create mode 100644 net/netfilter/nf_conntrack_rtcache.c
+
+diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h
+index 1c3035dda31f..0988c7dfa604 100644
+--- a/include/net/netfilter/nf_conntrack_extend.h
++++ b/include/net/netfilter/nf_conntrack_extend.h
+@@ -27,6 +27,9 @@ enum nf_ct_ext_id {
+ #if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY)
+ NF_CT_EXT_SYNPROXY,
+ #endif
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
++ NF_CT_EXT_RTCACHE,
++#endif
+ NF_CT_EXT_NUM,
+ };
+
+@@ -39,6 +42,7 @@ enum nf_ct_ext_id {
+ #define NF_CT_EXT_TIMEOUT_TYPE struct nf_conn_timeout
+ #define NF_CT_EXT_LABELS_TYPE struct nf_conn_labels
+ #define NF_CT_EXT_SYNPROXY_TYPE struct nf_conn_synproxy
++#define NF_CT_EXT_RTCACHE_TYPE struct nf_conn_rtcache
+
+ /* Extensions: optional stuff which isn't permanently in struct. */
+ struct nf_ct_ext {
+diff --git a/include/net/netfilter/nf_conntrack_rtcache.h b/include/net/netfilter/nf_conntrack_rtcache.h
+new file mode 100644
+index 000000000000..e2fb30243354
+--- /dev/null
++++ b/include/net/netfilter/nf_conntrack_rtcache.h
+@@ -0,0 +1,34 @@
++#include <linux/gfp.h>
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_extend.h>
++
++struct dst_entry;
++
++struct nf_conn_dst_cache {
++ struct dst_entry *dst;
++ int iif;
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
++ u32 cookie;
++#endif
++
++};
++
++struct nf_conn_rtcache {
++ struct nf_conn_dst_cache cached_dst[IP_CT_DIR_MAX];
++};
++
++static inline
++struct nf_conn_rtcache *nf_ct_rtcache_find(const struct nf_conn *ct)
++{
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_RTCACHE)
++ return nf_ct_ext_find(ct, NF_CT_EXT_RTCACHE);
++#else
++ return NULL;
++#endif
++}
++
++static inline int nf_conn_rtcache_iif_get(const struct nf_conn_rtcache *rtc,
++ enum ip_conntrack_dir dir)
++{
++ return rtc->cached_dst[dir].iif;
++}
+diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
+index e8d56d9a4df2..82af5ba64088 100644
+--- a/net/netfilter/Kconfig
++++ b/net/netfilter/Kconfig
+@@ -114,6 +114,18 @@ config NF_CONNTRACK_EVENTS
+
+ If unsure, say `N'.
+
++config NF_CONNTRACK_RTCACHE
++ tristate "Cache route entries in conntrack objects"
++ depends on NETFILTER_ADVANCED
++ depends on NF_CONNTRACK
++ help
++ If this option is enabled, the connection tracking code will
++ cache routing information for each connection that is being
++ forwarded, at a cost of 32 bytes per conntrack object.
++
++ To compile it as a module, choose M here. If unsure, say N.
++ The module will be called nf_conntrack_rtcache.
++
+ config NF_CONNTRACK_TIMEOUT
+ bool 'Connection tracking timeout'
+ depends on NETFILTER_ADVANCED
+diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
+index c23c3c84416f..8adef51367f9 100644
+--- a/net/netfilter/Makefile
++++ b/net/netfilter/Makefile
+@@ -16,6 +16,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += nfnetlink_log.o
+ # connection tracking
+ obj-$(CONFIG_NF_CONNTRACK) += nf_conntrack.o
+
++# optional conntrack route cache extension
++obj-$(CONFIG_NF_CONNTRACK_RTCACHE) += nf_conntrack_rtcache.o
++
+ # SCTP protocol connection tracking
+ obj-$(CONFIG_NF_CT_PROTO_DCCP) += nf_conntrack_proto_dccp.o
+ obj-$(CONFIG_NF_CT_PROTO_GRE) += nf_conntrack_proto_gre.o
+diff --git a/net/netfilter/nf_conntrack_rtcache.c b/net/netfilter/nf_conntrack_rtcache.c
+new file mode 100644
+index 000000000000..fb073d01960d
+--- /dev/null
++++ b/net/netfilter/nf_conntrack_rtcache.c
+@@ -0,0 +1,413 @@
++/* route cache for netfilter.
++ *
++ * (C) 2014 Red Hat GmbH
++ *
++ * 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.
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/types.h>
++#include <linux/netfilter.h>
++#include <linux/skbuff.h>
++#include <linux/stddef.h>
++#include <linux/kernel.h>
++#include <linux/netdevice.h>
++#include <linux/export.h>
++#include <linux/module.h>
++
++#include <net/dst.h>
++
++#include <net/netfilter/nf_conntrack.h>
++#include <net/netfilter/nf_conntrack_core.h>
++#include <net/netfilter/nf_conntrack_extend.h>
++#include <net/netfilter/nf_conntrack_rtcache.h>
++
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
++#include <net/ip6_fib.h>
++#endif
++
++static void __nf_conn_rtcache_destroy(struct nf_conn_rtcache *rtc,
++ enum ip_conntrack_dir dir)
++{
++ struct dst_entry *dst = rtc->cached_dst[dir].dst;
++
++ dst_release(dst);
++}
++
++static void nf_conn_rtcache_destroy(struct nf_conn *ct)
++{
++ struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct);
++
++ if (!rtc)
++ return;
++
++ __nf_conn_rtcache_destroy(rtc, IP_CT_DIR_ORIGINAL);
++ __nf_conn_rtcache_destroy(rtc, IP_CT_DIR_REPLY);
++}
++
++static void nf_ct_rtcache_ext_add(struct nf_conn *ct)
++{
++ struct nf_conn_rtcache *rtc;
++
++ rtc = nf_ct_ext_add(ct, NF_CT_EXT_RTCACHE, GFP_ATOMIC);
++ if (rtc) {
++ rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif = -1;
++ rtc->cached_dst[IP_CT_DIR_ORIGINAL].dst = NULL;
++ rtc->cached_dst[IP_CT_DIR_REPLY].iif = -1;
++ rtc->cached_dst[IP_CT_DIR_REPLY].dst = NULL;
++ }
++}
++
++static struct nf_conn_rtcache *nf_ct_rtcache_find_usable(struct nf_conn *ct)
++{
++ if (nf_ct_is_untracked(ct))
++ return NULL;
++ return nf_ct_rtcache_find(ct);
++}
++
++static struct dst_entry *
++nf_conn_rtcache_dst_get(const struct nf_conn_rtcache *rtc,
++ enum ip_conntrack_dir dir)
++{
++ return rtc->cached_dst[dir].dst;
++}
++
++static u32 nf_rtcache_get_cookie(int pf, const struct dst_entry *dst)
++{
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
++ if (pf == NFPROTO_IPV6) {
++ const struct rt6_info *rt = (const struct rt6_info *)dst;
++
++ if (rt->rt6i_node)
++ return (u32)rt->rt6i_node->fn_sernum;
++ }
++#endif
++ return 0;
++}
++
++static void nf_conn_rtcache_dst_set(int pf,
++ struct nf_conn_rtcache *rtc,
++ struct dst_entry *dst,
++ enum ip_conntrack_dir dir, int iif)
++{
++ if (rtc->cached_dst[dir].iif != iif)
++ rtc->cached_dst[dir].iif = iif;
++
++ if (rtc->cached_dst[dir].dst != dst) {
++ struct dst_entry *old;
++
++ dst_hold(dst);
++
++ old = xchg(&rtc->cached_dst[dir].dst, dst);
++ dst_release(old);
++
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
++ if (pf == NFPROTO_IPV6)
++ rtc->cached_dst[dir].cookie =
++ nf_rtcache_get_cookie(pf, dst);
++#endif
++ }
++}
++
++static void nf_conn_rtcache_dst_obsolete(struct nf_conn_rtcache *rtc,
++ enum ip_conntrack_dir dir)
++{
++ struct dst_entry *old;
++
++ pr_debug("Invalidate iif %d for dir %d on cache %p\n",
++ rtc->cached_dst[dir].iif, dir, rtc);
++
++ old = xchg(&rtc->cached_dst[dir].dst, NULL);
++ dst_release(old);
++ rtc->cached_dst[dir].iif = -1;
++}
++
++static unsigned int nf_rtcache_in(u_int8_t pf,
++ struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ struct nf_conn_rtcache *rtc;
++ enum ip_conntrack_info ctinfo;
++ enum ip_conntrack_dir dir;
++ struct dst_entry *dst;
++ struct nf_conn *ct;
++ int iif;
++ u32 cookie;
++
++ if (skb_dst(skb) || skb->sk)
++ return NF_ACCEPT;
++
++ ct = nf_ct_get(skb, &ctinfo);
++ if (!ct)
++ return NF_ACCEPT;
++
++ rtc = nf_ct_rtcache_find_usable(ct);
++ if (!rtc)
++ return NF_ACCEPT;
++
++ /* if iif changes, don't use cache and let ip stack
++ * do route lookup.
++ *
++ * If rp_filter is enabled it might toss skb, so
++ * we don't want to avoid these checks.
++ */
++ dir = CTINFO2DIR(ctinfo);
++ iif = nf_conn_rtcache_iif_get(rtc, dir);
++ if (state->in->ifindex != iif) {
++ pr_debug("ct %p, iif %d, cached iif %d, skip cached entry\n",
++ ct, iif, state->in->ifindex);
++ return NF_ACCEPT;
++ }
++ dst = nf_conn_rtcache_dst_get(rtc, dir);
++ if (dst == NULL)
++ return NF_ACCEPT;
++
++ cookie = nf_rtcache_get_cookie(pf, dst);
++
++ dst = dst_check(dst, cookie);
++ pr_debug("obtained dst %p for skb %p, cookie %d\n", dst, skb, cookie);
++ if (likely(dst))
++ skb_dst_set_noref(skb, dst);
++ else
++ nf_conn_rtcache_dst_obsolete(rtc, dir);
++
++ return NF_ACCEPT;
++}
++
++static unsigned int nf_rtcache_forward(u_int8_t pf,
++ struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ struct nf_conn_rtcache *rtc;
++ enum ip_conntrack_info ctinfo;
++ enum ip_conntrack_dir dir;
++ struct nf_conn *ct;
++ struct dst_entry *dst = skb_dst(skb);
++ int iif;
++
++ ct = nf_ct_get(skb, &ctinfo);
++ if (!ct)
++ return NF_ACCEPT;
++
++ if (dst && dst_xfrm(dst))
++ return NF_ACCEPT;
++
++ if (!nf_ct_is_confirmed(ct)) {
++ if (WARN_ON(nf_ct_rtcache_find(ct)))
++ return NF_ACCEPT;
++ nf_ct_rtcache_ext_add(ct);
++ return NF_ACCEPT;
++ }
++
++ rtc = nf_ct_rtcache_find_usable(ct);
++ if (!rtc)
++ return NF_ACCEPT;
++
++ dir = CTINFO2DIR(ctinfo);
++ iif = nf_conn_rtcache_iif_get(rtc, dir);
++ pr_debug("ct %p, skb %p, dir %d, iif %d, cached iif %d\n",
++ ct, skb, dir, iif, state->in->ifindex);
++ if (likely(state->in->ifindex == iif))
++ return NF_ACCEPT;
++
++ nf_conn_rtcache_dst_set(pf, rtc, skb_dst(skb), dir, state->in->ifindex);
++ return NF_ACCEPT;
++}
++
++static unsigned int nf_rtcache_in4(void *priv,
++ struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ return nf_rtcache_in(NFPROTO_IPV4, skb, state);
++}
++
++static unsigned int nf_rtcache_forward4(void *priv,
++ struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ return nf_rtcache_forward(NFPROTO_IPV4, skb, state);
++}
++
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
++static unsigned int nf_rtcache_in6(void *priv,
++ struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ return nf_rtcache_in(NFPROTO_IPV6, skb, state);
++}
++
++static unsigned int nf_rtcache_forward6(void *priv,
++ struct sk_buff *skb,
++ const struct nf_hook_state *state)
++{
++ return nf_rtcache_forward(NFPROTO_IPV6, skb, state);
++}
++#endif
++
++static int nf_rtcache_dst_remove(struct nf_conn *ct, void *data)
++{
++ struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct);
++ struct net_device *dev = data;
++
++ if (!rtc)
++ return 0;
++
++ if (dev->ifindex == rtc->cached_dst[IP_CT_DIR_ORIGINAL].iif ||
++ dev->ifindex == rtc->cached_dst[IP_CT_DIR_REPLY].iif) {
++ nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_ORIGINAL);
++ nf_conn_rtcache_dst_obsolete(rtc, IP_CT_DIR_REPLY);
++ }
++
++ return 0;
++}
++
++static int nf_rtcache_netdev_event(struct notifier_block *this,
++ unsigned long event, void *ptr)
++{
++ struct net_device *dev = netdev_notifier_info_to_dev(ptr);
++ struct net *net = dev_net(dev);
++
++ if (event == NETDEV_DOWN)
++ nf_ct_iterate_cleanup(net, nf_rtcache_dst_remove, dev, 0, 0);
++
++ return NOTIFY_DONE;
++}
++
++static struct notifier_block nf_rtcache_notifier = {
++ .notifier_call = nf_rtcache_netdev_event,
++};
++
++static struct nf_hook_ops rtcache_ops[] = {
++ {
++ .hook = nf_rtcache_in4,
++ .pf = NFPROTO_IPV4,
++ .hooknum = NF_INET_PRE_ROUTING,
++ .priority = NF_IP_PRI_LAST,
++ },
++ {
++ .hook = nf_rtcache_forward4,
++ .pf = NFPROTO_IPV4,
++ .hooknum = NF_INET_FORWARD,
++ .priority = NF_IP_PRI_LAST,
++ },
++#if IS_ENABLED(CONFIG_NF_CONNTRACK_IPV6)
++ {
++ .hook = nf_rtcache_in6,
++ .pf = NFPROTO_IPV6,
++ .hooknum = NF_INET_PRE_ROUTING,
++ .priority = NF_IP_PRI_LAST,
++ },
++ {
++ .hook = nf_rtcache_forward6,
++ .pf = NFPROTO_IPV6,
++ .hooknum = NF_INET_FORWARD,
++ .priority = NF_IP_PRI_LAST,
++ },
++#endif
++};
++
++static struct nf_ct_ext_type rtcache_extend __read_mostly = {
++ .len = sizeof(struct nf_conn_rtcache),
++ .align = __alignof__(struct nf_conn_rtcache),
++ .id = NF_CT_EXT_RTCACHE,
++ .destroy = nf_conn_rtcache_destroy,
++};
++
++static int __init nf_conntrack_rtcache_init(void)
++{
++ int ret = nf_ct_extend_register(&rtcache_extend);
++
++ if (ret < 0) {
++ pr_err("nf_conntrack_rtcache: Unable to register extension\n");
++ return ret;
++ }
++
++ ret = nf_register_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops));
++ if (ret < 0) {
++ nf_ct_extend_unregister(&rtcache_extend);
++ return ret;
++ }
++
++ ret = register_netdevice_notifier(&nf_rtcache_notifier);
++ if (ret) {
++ nf_unregister_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops));
++ nf_ct_extend_unregister(&rtcache_extend);
++ }
++
++ return ret;
++}
++
++static int nf_rtcache_ext_remove(struct nf_conn *ct, void *data)
++{
++ struct nf_conn_rtcache *rtc = nf_ct_rtcache_find(ct);
++
++ return rtc != NULL;
++}
++
++static bool __exit nf_conntrack_rtcache_wait_for_dying(struct net *net)
++{
++ bool wait = false;
++ int cpu;
++
++ for_each_possible_cpu(cpu) {
++ struct nf_conntrack_tuple_hash *h;
++ struct hlist_nulls_node *n;
++ struct nf_conn *ct;
++ struct ct_pcpu *pcpu = per_cpu_ptr(net->ct.pcpu_lists, cpu);
++
++ rcu_read_lock();
++ spin_lock_bh(&pcpu->lock);
++
++ hlist_nulls_for_each_entry(h, n, &pcpu->dying, hnnode) {
++ ct = nf_ct_tuplehash_to_ctrack(h);
++ if (nf_ct_rtcache_find(ct) != NULL) {
++ wait = true;
++ break;
++ }
++ }
++ spin_unlock_bh(&pcpu->lock);
++ rcu_read_unlock();
++ }
++
++ return wait;
++}
++
++static void __exit nf_conntrack_rtcache_fini(void)
++{
++ struct net *net;
++ int count = 0;
++
++ /* remove hooks so no new connections get rtcache extension */
++ nf_unregister_hooks(rtcache_ops, ARRAY_SIZE(rtcache_ops));
++
++ synchronize_net();
++
++ unregister_netdevice_notifier(&nf_rtcache_notifier);
++
++ rtnl_lock();
++
++ /* zap all conntracks with rtcache extension */
++ for_each_net(net)
++ nf_ct_iterate_cleanup(net, nf_rtcache_ext_remove, NULL, 0, 0);
++
++ for_each_net(net) {
++ /* .. and make sure they're gone from dying list, too */
++ while (nf_conntrack_rtcache_wait_for_dying(net)) {
++ msleep(200);
++ WARN_ONCE(++count > 25, "Waiting for all rtcache conntracks to go away\n");
++ }
++ }
++
++ rtnl_unlock();
++ synchronize_net();
++ nf_ct_extend_unregister(&rtcache_extend);
++}
++module_init(nf_conntrack_rtcache_init);
++module_exit(nf_conntrack_rtcache_fini);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
++MODULE_DESCRIPTION("Conntrack route cache extension");
+--
+2.11.0
+
diff --git a/target/linux/generic/backport-4.9/021-bridge-multicast-to-unicast.patch b/target/linux/generic/backport-4.9/021-bridge-multicast-to-unicast.patch
new file mode 100644
index 0000000000..d3b4a62361
--- /dev/null
+++ b/target/linux/generic/backport-4.9/021-bridge-multicast-to-unicast.patch
@@ -0,0 +1,499 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Thu, 19 Jan 2017 03:45:10 +0100
+Subject: [PATCH] bridge: multicast to unicast
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Implements an optional, per bridge port flag and feature to deliver
+multicast packets to any host on the according port via unicast
+individually. This is done by copying the packet per host and
+changing the multicast destination MAC to a unicast one accordingly.
+
+multicast-to-unicast works on top of the multicast snooping feature of
+the bridge. Which means unicast copies are only delivered to hosts which
+are interested in it and signalized this via IGMP/MLD reports
+previously.
+
+This feature is intended for interface types which have a more reliable
+and/or efficient way to deliver unicast packets than broadcast ones
+(e.g. wifi).
+
+However, it should only be enabled on interfaces where no IGMPv2/MLDv1
+report suppression takes place. This feature is disabled by default.
+
+The initial patch and idea is from Felix Fietkau.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+[linus.luessing@c0d3.blue: various bug + style fixes, commit message]
+Signed-off-by: Linus Lüssing <linus.luessing@c0d3.blue>
+---
+
+--- a/include/linux/if_bridge.h
++++ b/include/linux/if_bridge.h
+@@ -46,6 +46,7 @@ struct br_ip_list {
+ #define BR_LEARNING_SYNC BIT(9)
+ #define BR_PROXYARP_WIFI BIT(10)
+ #define BR_MCAST_FLOOD BIT(11)
++#define BR_MULTICAST_TO_UNICAST BIT(12)
+
+ #define BR_DEFAULT_AGEING_TIME (300 * HZ)
+
+--- a/include/uapi/linux/if_link.h
++++ b/include/uapi/linux/if_link.h
+@@ -319,6 +319,7 @@ enum {
+ IFLA_BRPORT_MULTICAST_ROUTER,
+ IFLA_BRPORT_PAD,
+ IFLA_BRPORT_MCAST_FLOOD,
++ IFLA_BRPORT_MCAST_TO_UCAST,
+ __IFLA_BRPORT_MAX
+ };
+ #define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+--- a/net/bridge/br_forward.c
++++ b/net/bridge/br_forward.c
+@@ -174,6 +174,29 @@ out:
+ return p;
+ }
+
++static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb,
++ const unsigned char *addr, bool local_orig)
++{
++ struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
++ const unsigned char *src = eth_hdr(skb)->h_source;
++
++ if (!should_deliver(p, skb))
++ return;
++
++ /* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */
++ if (skb->dev == p->dev && ether_addr_equal(src, addr))
++ return;
++
++ skb = skb_copy(skb, GFP_ATOMIC);
++ if (!skb) {
++ dev->stats.tx_dropped++;
++ return;
++ }
++
++ memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN);
++ __br_forward(p, skb, local_orig);
++}
++
+ /* called under rcu_read_lock */
+ void br_flood(struct net_bridge *br, struct sk_buff *skb,
+ enum br_pkt_type pkt_type, bool local_rcv, bool local_orig)
+@@ -242,10 +265,20 @@ void br_multicast_flood(struct net_bridg
+ rport = rp ? hlist_entry(rp, struct net_bridge_port, rlist) :
+ NULL;
+
+- port = (unsigned long)lport > (unsigned long)rport ?
+- lport : rport;
++ if ((unsigned long)lport > (unsigned long)rport) {
++ port = lport;
++
++ if (p->flags & MDB_PG_FLAGS_MCAST_TO_UCAST) {
++ maybe_deliver_addr(lport, skb, p->eth_addr,
++ local_orig);
++ goto delivered;
++ }
++ } else {
++ port = rport;
++ }
+
+ prev = maybe_deliver(prev, port, skb, local_orig);
++delivered:
+ if (IS_ERR(prev))
+ goto out;
+ if (prev == port)
+--- a/net/bridge/br_mdb.c
++++ b/net/bridge/br_mdb.c
+@@ -531,7 +531,7 @@ static int br_mdb_add_group(struct net_b
+ break;
+ }
+
+- p = br_multicast_new_port_group(port, group, *pp, state);
++ p = br_multicast_new_port_group(port, group, *pp, state, NULL);
+ if (unlikely(!p))
+ return -ENOMEM;
+ rcu_assign_pointer(*pp, p);
+--- a/net/bridge/br_multicast.c
++++ b/net/bridge/br_multicast.c
+@@ -42,12 +42,14 @@ static void br_multicast_add_router(stru
+ static void br_ip4_multicast_leave_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ __be32 group,
+- __u16 vid);
++ __u16 vid,
++ const unsigned char *src);
++
+ #if IS_ENABLED(CONFIG_IPV6)
+ static void br_ip6_multicast_leave_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ const struct in6_addr *group,
+- __u16 vid);
++ __u16 vid, const unsigned char *src);
+ #endif
+ unsigned int br_mdb_rehash_seq;
+
+@@ -658,7 +660,8 @@ struct net_bridge_port_group *br_multica
+ struct net_bridge_port *port,
+ struct br_ip *group,
+ struct net_bridge_port_group __rcu *next,
+- unsigned char flags)
++ unsigned char flags,
++ const unsigned char *src)
+ {
+ struct net_bridge_port_group *p;
+
+@@ -673,12 +676,39 @@ struct net_bridge_port_group *br_multica
+ hlist_add_head(&p->mglist, &port->mglist);
+ setup_timer(&p->timer, br_multicast_port_group_expired,
+ (unsigned long)p);
++
++ if ((port->flags & BR_MULTICAST_TO_UNICAST) && src) {
++ memcpy(p->eth_addr, src, ETH_ALEN);
++ p->flags |= MDB_PG_FLAGS_MCAST_TO_UCAST;
++ }
++
+ return p;
+ }
+
++static bool br_port_group_equal(struct net_bridge_port_group *p,
++ struct net_bridge_port *port,
++ const unsigned char *src)
++{
++ if (p->port != port)
++ return false;
++
++ if (!(p->flags & MDB_PG_FLAGS_MCAST_TO_UCAST) !=
++ !(port->flags & BR_MULTICAST_TO_UNICAST))
++ return false;
++
++ if (!(p->flags & MDB_PG_FLAGS_MCAST_TO_UCAST))
++ return true;
++
++ if (!src)
++ return false;
++
++ return ether_addr_equal(src, p->eth_addr);
++}
++
+ static int br_multicast_add_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+- struct br_ip *group)
++ struct br_ip *group,
++ const unsigned char *src)
+ {
+ struct net_bridge_mdb_entry *mp;
+ struct net_bridge_port_group *p;
+@@ -705,13 +735,13 @@ static int br_multicast_add_group(struct
+ for (pp = &mp->ports;
+ (p = mlock_dereference(*pp, br)) != NULL;
+ pp = &p->next) {
+- if (p->port == port)
++ if (br_port_group_equal(p, port, src))
+ goto found;
+ if ((unsigned long)p->port < (unsigned long)port)
+ break;
+ }
+
+- p = br_multicast_new_port_group(port, group, *pp, 0);
++ p = br_multicast_new_port_group(port, group, *pp, 0, src);
+ if (unlikely(!p))
+ goto err;
+ rcu_assign_pointer(*pp, p);
+@@ -730,7 +760,8 @@ err:
+ static int br_ip4_multicast_add_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ __be32 group,
+- __u16 vid)
++ __u16 vid,
++ const unsigned char *src)
+ {
+ struct br_ip br_group;
+
+@@ -741,14 +772,15 @@ static int br_ip4_multicast_add_group(st
+ br_group.proto = htons(ETH_P_IP);
+ br_group.vid = vid;
+
+- return br_multicast_add_group(br, port, &br_group);
++ return br_multicast_add_group(br, port, &br_group, src);
+ }
+
+ #if IS_ENABLED(CONFIG_IPV6)
+ static int br_ip6_multicast_add_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ const struct in6_addr *group,
+- __u16 vid)
++ __u16 vid,
++ const unsigned char *src)
+ {
+ struct br_ip br_group;
+
+@@ -759,7 +791,7 @@ static int br_ip6_multicast_add_group(st
+ br_group.proto = htons(ETH_P_IPV6);
+ br_group.vid = vid;
+
+- return br_multicast_add_group(br, port, &br_group);
++ return br_multicast_add_group(br, port, &br_group, src);
+ }
+ #endif
+
+@@ -1028,6 +1060,7 @@ static int br_ip4_multicast_igmp3_report
+ struct sk_buff *skb,
+ u16 vid)
+ {
++ const unsigned char *src;
+ struct igmpv3_report *ih;
+ struct igmpv3_grec *grec;
+ int i;
+@@ -1068,12 +1101,14 @@ static int br_ip4_multicast_igmp3_report
+ continue;
+ }
+
++ src = eth_hdr(skb)->h_source;
+ if ((type == IGMPV3_CHANGE_TO_INCLUDE ||
+ type == IGMPV3_MODE_IS_INCLUDE) &&
+ ntohs(grec->grec_nsrcs) == 0) {
+- br_ip4_multicast_leave_group(br, port, group, vid);
++ br_ip4_multicast_leave_group(br, port, group, vid, src);
+ } else {
+- err = br_ip4_multicast_add_group(br, port, group, vid);
++ err = br_ip4_multicast_add_group(br, port, group, vid,
++ src);
+ if (err)
+ break;
+ }
+@@ -1088,6 +1123,7 @@ static int br_ip6_multicast_mld2_report(
+ struct sk_buff *skb,
+ u16 vid)
+ {
++ const unsigned char *src = eth_hdr(skb)->h_source;
+ struct icmp6hdr *icmp6h;
+ struct mld2_grec *grec;
+ int i;
+@@ -1139,10 +1175,11 @@ static int br_ip6_multicast_mld2_report(
+ grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
+ ntohs(*nsrcs) == 0) {
+ br_ip6_multicast_leave_group(br, port, &grec->grec_mca,
+- vid);
++ vid, src);
+ } else {
+ err = br_ip6_multicast_add_group(br, port,
+- &grec->grec_mca, vid);
++ &grec->grec_mca, vid,
++ src);
+ if (err)
+ break;
+ }
+@@ -1458,7 +1495,8 @@ br_multicast_leave_group(struct net_brid
+ struct net_bridge_port *port,
+ struct br_ip *group,
+ struct bridge_mcast_other_query *other_query,
+- struct bridge_mcast_own_query *own_query)
++ struct bridge_mcast_own_query *own_query,
++ const unsigned char *src)
+ {
+ struct net_bridge_mdb_htable *mdb;
+ struct net_bridge_mdb_entry *mp;
+@@ -1482,7 +1520,7 @@ br_multicast_leave_group(struct net_brid
+ for (pp = &mp->ports;
+ (p = mlock_dereference(*pp, br)) != NULL;
+ pp = &p->next) {
+- if (p->port != port)
++ if (!br_port_group_equal(p, port, src))
+ continue;
+
+ rcu_assign_pointer(*pp, p->next);
+@@ -1513,7 +1551,7 @@ br_multicast_leave_group(struct net_brid
+ for (p = mlock_dereference(mp->ports, br);
+ p != NULL;
+ p = mlock_dereference(p->next, br)) {
+- if (p->port != port)
++ if (!br_port_group_equal(p, port, src))
+ continue;
+
+ if (!hlist_unhashed(&p->mglist) &&
+@@ -1564,7 +1602,8 @@ out:
+ static void br_ip4_multicast_leave_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ __be32 group,
+- __u16 vid)
++ __u16 vid,
++ const unsigned char *src)
+ {
+ struct br_ip br_group;
+ struct bridge_mcast_own_query *own_query;
+@@ -1579,14 +1618,15 @@ static void br_ip4_multicast_leave_group
+ br_group.vid = vid;
+
+ br_multicast_leave_group(br, port, &br_group, &br->ip4_other_query,
+- own_query);
++ own_query, src);
+ }
+
+ #if IS_ENABLED(CONFIG_IPV6)
+ static void br_ip6_multicast_leave_group(struct net_bridge *br,
+ struct net_bridge_port *port,
+ const struct in6_addr *group,
+- __u16 vid)
++ __u16 vid,
++ const unsigned char *src)
+ {
+ struct br_ip br_group;
+ struct bridge_mcast_own_query *own_query;
+@@ -1601,7 +1641,7 @@ static void br_ip6_multicast_leave_group
+ br_group.vid = vid;
+
+ br_multicast_leave_group(br, port, &br_group, &br->ip6_other_query,
+- own_query);
++ own_query, src);
+ }
+ #endif
+
+@@ -1644,6 +1684,7 @@ static int br_multicast_ipv4_rcv(struct
+ u16 vid)
+ {
+ struct sk_buff *skb_trimmed = NULL;
++ const unsigned char *src;
+ struct igmphdr *ih;
+ int err;
+
+@@ -1659,13 +1700,14 @@ static int br_multicast_ipv4_rcv(struct
+ }
+
+ ih = igmp_hdr(skb);
++ src = eth_hdr(skb)->h_source;
+ BR_INPUT_SKB_CB(skb)->igmp = ih->type;
+
+ switch (ih->type) {
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ case IGMPV2_HOST_MEMBERSHIP_REPORT:
+ BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
+- err = br_ip4_multicast_add_group(br, port, ih->group, vid);
++ err = br_ip4_multicast_add_group(br, port, ih->group, vid, src);
+ break;
+ case IGMPV3_HOST_MEMBERSHIP_REPORT:
+ err = br_ip4_multicast_igmp3_report(br, port, skb_trimmed, vid);
+@@ -1674,7 +1716,7 @@ static int br_multicast_ipv4_rcv(struct
+ err = br_ip4_multicast_query(br, port, skb_trimmed, vid);
+ break;
+ case IGMP_HOST_LEAVE_MESSAGE:
+- br_ip4_multicast_leave_group(br, port, ih->group, vid);
++ br_ip4_multicast_leave_group(br, port, ih->group, vid, src);
+ break;
+ }
+
+@@ -1694,6 +1736,7 @@ static int br_multicast_ipv6_rcv(struct
+ u16 vid)
+ {
+ struct sk_buff *skb_trimmed = NULL;
++ const unsigned char *src;
+ struct mld_msg *mld;
+ int err;
+
+@@ -1713,8 +1756,10 @@ static int br_multicast_ipv6_rcv(struct
+
+ switch (mld->mld_type) {
+ case ICMPV6_MGM_REPORT:
++ src = eth_hdr(skb)->h_source;
+ BR_INPUT_SKB_CB(skb)->mrouters_only = 1;
+- err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid);
++ err = br_ip6_multicast_add_group(br, port, &mld->mld_mca, vid,
++ src);
+ break;
+ case ICMPV6_MLD2_REPORT:
+ err = br_ip6_multicast_mld2_report(br, port, skb_trimmed, vid);
+@@ -1723,7 +1768,8 @@ static int br_multicast_ipv6_rcv(struct
+ err = br_ip6_multicast_query(br, port, skb_trimmed, vid);
+ break;
+ case ICMPV6_MGM_REDUCTION:
+- br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid);
++ src = eth_hdr(skb)->h_source;
++ br_ip6_multicast_leave_group(br, port, &mld->mld_mca, vid, src);
+ break;
+ }
+
+--- a/net/bridge/br_netlink.c
++++ b/net/bridge/br_netlink.c
+@@ -123,6 +123,7 @@ static inline size_t br_port_info_size(v
+ + nla_total_size(1) /* IFLA_BRPORT_GUARD */
+ + nla_total_size(1) /* IFLA_BRPORT_PROTECT */
+ + nla_total_size(1) /* IFLA_BRPORT_FAST_LEAVE */
++ + nla_total_size(1) /* IFLA_BRPORT_MCAST_TO_UCAST */
+ + nla_total_size(1) /* IFLA_BRPORT_LEARNING */
+ + nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */
+ + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */
+@@ -173,6 +174,8 @@ static int br_port_fill_attrs(struct sk_
+ !!(p->flags & BR_ROOT_BLOCK)) ||
+ nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE,
+ !!(p->flags & BR_MULTICAST_FAST_LEAVE)) ||
++ nla_put_u8(skb, IFLA_BRPORT_MCAST_TO_UCAST,
++ !!(p->flags & BR_MULTICAST_TO_UNICAST)) ||
+ nla_put_u8(skb, IFLA_BRPORT_LEARNING, !!(p->flags & BR_LEARNING)) ||
+ nla_put_u8(skb, IFLA_BRPORT_UNICAST_FLOOD,
+ !!(p->flags & BR_FLOOD)) ||
+@@ -586,6 +589,7 @@ static const struct nla_policy br_port_p
+ [IFLA_BRPORT_PROXYARP] = { .type = NLA_U8 },
+ [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 },
+ [IFLA_BRPORT_MULTICAST_ROUTER] = { .type = NLA_U8 },
++ [IFLA_BRPORT_MCAST_TO_UCAST] = { .type = NLA_U8 },
+ };
+
+ /* Change the state of the port and notify spanning tree */
+@@ -636,6 +640,7 @@ static int br_setport(struct net_bridge_
+ br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
+ br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
+ br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
++ br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST);
+ br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
+ br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
+
+--- a/net/bridge/br_private.h
++++ b/net/bridge/br_private.h
+@@ -166,8 +166,9 @@ struct net_bridge_fdb_entry
+ struct rcu_head rcu;
+ };
+
+-#define MDB_PG_FLAGS_PERMANENT BIT(0)
+-#define MDB_PG_FLAGS_OFFLOAD BIT(1)
++#define MDB_PG_FLAGS_PERMANENT BIT(0)
++#define MDB_PG_FLAGS_OFFLOAD BIT(1)
++#define MDB_PG_FLAGS_MCAST_TO_UCAST BIT(2)
+
+ struct net_bridge_port_group {
+ struct net_bridge_port *port;
+@@ -177,6 +178,7 @@ struct net_bridge_port_group {
+ struct timer_list timer;
+ struct br_ip addr;
+ unsigned char flags;
++ unsigned char eth_addr[ETH_ALEN];
+ };
+
+ struct net_bridge_mdb_entry
+@@ -591,7 +593,7 @@ void br_multicast_free_pg(struct rcu_hea
+ struct net_bridge_port_group *
+ br_multicast_new_port_group(struct net_bridge_port *port, struct br_ip *group,
+ struct net_bridge_port_group __rcu *next,
+- unsigned char flags);
++ unsigned char flags, const unsigned char *src);
+ void br_mdb_init(void);
+ void br_mdb_uninit(void);
+ void br_mdb_notify(struct net_device *dev, struct net_bridge_port *port,
+--- a/net/bridge/br_sysfs_if.c
++++ b/net/bridge/br_sysfs_if.c
+@@ -188,6 +188,7 @@ static BRPORT_ATTR(multicast_router, S_I
+ store_multicast_router);
+
+ BRPORT_ATTR_FLAG(multicast_fast_leave, BR_MULTICAST_FAST_LEAVE);
++BRPORT_ATTR_FLAG(multicast_to_unicast, BR_MULTICAST_TO_UNICAST);
+ #endif
+
+ static const struct brport_attribute *brport_attrs[] = {
+@@ -214,6 +215,7 @@ static const struct brport_attribute *br
+ #ifdef CONFIG_BRIDGE_IGMP_SNOOPING
+ &brport_attr_multicast_router,
+ &brport_attr_multicast_fast_leave,
++ &brport_attr_multicast_to_unicast,
+ #endif
+ &brport_attr_proxyarp,
+ &brport_attr_proxyarp_wifi,
diff --git a/target/linux/generic/backport-4.9/022-net-add-devm-version-of-alloc_etherdev_mqs-function.patch b/target/linux/generic/backport-4.9/022-net-add-devm-version-of-alloc_etherdev_mqs-function.patch
new file mode 100644
index 0000000000..c5d65b7105
--- /dev/null
+++ b/target/linux/generic/backport-4.9/022-net-add-devm-version-of-alloc_etherdev_mqs-function.patch
@@ -0,0 +1,69 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Sat, 28 Jan 2017 15:15:42 +0100
+Subject: [PATCH] net: add devm version of alloc_etherdev_mqs function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This patch adds devm_alloc_etherdev_mqs function and devm_alloc_etherdev
+macro. These can be used for simpler netdev allocation without having to
+care about calling free_netdev.
+
+Thanks to this change drivers, their error paths and removal paths may
+get simpler by a bit.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/include/linux/etherdevice.h
++++ b/include/linux/etherdevice.h
+@@ -54,6 +54,11 @@ struct net_device *alloc_etherdev_mqs(in
+ #define alloc_etherdev(sizeof_priv) alloc_etherdev_mq(sizeof_priv, 1)
+ #define alloc_etherdev_mq(sizeof_priv, count) alloc_etherdev_mqs(sizeof_priv, count, count)
+
++struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
++ unsigned int txqs,
++ unsigned int rxqs);
++#define devm_alloc_etherdev(dev, sizeof_priv) devm_alloc_etherdev_mqs(dev, sizeof_priv, 1, 1)
++
+ struct sk_buff **eth_gro_receive(struct sk_buff **head,
+ struct sk_buff *skb);
+ int eth_gro_complete(struct sk_buff *skb, int nhoff);
+--- a/net/ethernet/eth.c
++++ b/net/ethernet/eth.c
+@@ -391,6 +391,34 @@ struct net_device *alloc_etherdev_mqs(in
+ }
+ EXPORT_SYMBOL(alloc_etherdev_mqs);
+
++static void devm_free_netdev(struct device *dev, void *res)
++{
++ free_netdev(*(struct net_device **)res);
++}
++
++struct net_device *devm_alloc_etherdev_mqs(struct device *dev, int sizeof_priv,
++ unsigned int txqs, unsigned int rxqs)
++{
++ struct net_device **dr;
++ struct net_device *netdev;
++
++ dr = devres_alloc(devm_free_netdev, sizeof(*dr), GFP_KERNEL);
++ if (!dr)
++ return NULL;
++
++ netdev = alloc_etherdev_mqs(sizeof_priv, txqs, rxqs);
++ if (!netdev) {
++ devres_free(dr);
++ return NULL;
++ }
++
++ *dr = netdev;
++ devres_add(dev, dr);
++
++ return netdev;
++}
++EXPORT_SYMBOL(devm_alloc_etherdev_mqs);
++
+ ssize_t sysfs_format_mac(char *buf, const unsigned char *addr, int len)
+ {
+ return scnprintf(buf, PAGE_SIZE, "%*phC\n", len, addr);
diff --git a/target/linux/generic/backport-4.9/023-1-smsc95xx-Use-skb_cow_head-to-deal-with-cloned-skbs.patch b/target/linux/generic/backport-4.9/023-1-smsc95xx-Use-skb_cow_head-to-deal-with-cloned-skbs.patch
new file mode 100644
index 0000000000..cfc9abb3b7
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-1-smsc95xx-Use-skb_cow_head-to-deal-with-cloned-skbs.patch
@@ -0,0 +1,40 @@
+From e9156cd26a495a18706e796f02a81fee41ec14f4 Mon Sep 17 00:00:00 2001
+From: James Hughes <james.hughes@raspberrypi.org>
+Date: Wed, 19 Apr 2017 11:13:40 +0100
+Subject: [PATCH] smsc95xx: Use skb_cow_head to deal with cloned skbs
+
+The driver was failing to check that the SKB wasn't cloned
+before adding checksum data.
+Replace existing handling to extend/copy the header buffer
+with skb_cow_head.
+
+Signed-off-by: James Hughes <james.hughes@raspberrypi.org>
+Acked-by: Eric Dumazet <edumazet@google.com>
+Acked-by: Woojung Huh <Woojung.Huh@microchip.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/smsc95xx.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/usb/smsc95xx.c
++++ b/drivers/net/usb/smsc95xx.c
+@@ -2001,13 +2001,13 @@ static struct sk_buff *smsc95xx_tx_fixup
+ /* We do not advertise SG, so skbs should be already linearized */
+ BUG_ON(skb_shinfo(skb)->nr_frags);
+
+- if (skb_headroom(skb) < overhead) {
+- struct sk_buff *skb2 = skb_copy_expand(skb,
+- overhead, 0, flags);
++ /* Make writable and expand header space by overhead if required */
++ if (skb_cow_head(skb, overhead)) {
++ /* Must deallocate here as returning NULL to indicate error
++ * means the skb won't be deallocated in the caller.
++ */
+ dev_kfree_skb_any(skb);
+- skb = skb2;
+- if (!skb)
+- return NULL;
++ return NULL;
+ }
+
+ if (csum) {
diff --git a/target/linux/generic/backport-4.9/023-2-smsc75xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch b/target/linux/generic/backport-4.9/023-2-smsc75xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch
new file mode 100644
index 0000000000..99d98e101c
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-2-smsc75xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch
@@ -0,0 +1,36 @@
+From b7c6d2675899cfff0180412c63fc9cbd5bacdb4d Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Wed, 19 Apr 2017 09:59:21 -0700
+Subject: [PATCH] smsc75xx: use skb_cow_head() to deal with cloned skbs
+
+We need to ensure there is enough headroom to push extra header,
+but we also need to check if we are allowed to change headers.
+
+skb_cow_head() is the proper helper to deal with this.
+
+Fixes: d0cad871703b ("smsc75xx: SMSC LAN75xx USB gigabit ethernet adapter driver")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: James Hughes <james.hughes@raspberrypi.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/smsc75xx.c | 8 ++------
+ 1 file changed, 2 insertions(+), 6 deletions(-)
+
+--- a/drivers/net/usb/smsc75xx.c
++++ b/drivers/net/usb/smsc75xx.c
+@@ -2205,13 +2205,9 @@ static struct sk_buff *smsc75xx_tx_fixup
+ {
+ u32 tx_cmd_a, tx_cmd_b;
+
+- if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) {
+- struct sk_buff *skb2 =
+- skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags);
++ if (skb_cow_head(skb, SMSC75XX_TX_OVERHEAD)) {
+ dev_kfree_skb_any(skb);
+- skb = skb2;
+- if (!skb)
+- return NULL;
++ return NULL;
+ }
+
+ tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS;
diff --git a/target/linux/generic/backport-4.9/023-3-cx82310_eth-use-skb_cow_head-to-deal-with-cloned-skb.patch b/target/linux/generic/backport-4.9/023-3-cx82310_eth-use-skb_cow_head-to-deal-with-cloned-skb.patch
new file mode 100644
index 0000000000..aee8aa9805
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-3-cx82310_eth-use-skb_cow_head-to-deal-with-cloned-skb.patch
@@ -0,0 +1,35 @@
+From a9e840a2081ed28c2b7caa6a9a0041c950b3c37d Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Wed, 19 Apr 2017 09:59:22 -0700
+Subject: [PATCH] cx82310_eth: use skb_cow_head() to deal with cloned skbs
+
+We need to ensure there is enough headroom to push extra header,
+but we also need to check if we are allowed to change headers.
+
+skb_cow_head() is the proper helper to deal with this.
+
+Fixes: cc28a20e77b2 ("introduce cx82310_eth: Conexant CX82310-based ADSL router USB ethernet driver")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: James Hughes <james.hughes@raspberrypi.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/cx82310_eth.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/usb/cx82310_eth.c
++++ b/drivers/net/usb/cx82310_eth.c
+@@ -293,12 +293,9 @@ static struct sk_buff *cx82310_tx_fixup(
+ {
+ int len = skb->len;
+
+- if (skb_headroom(skb) < 2) {
+- struct sk_buff *skb2 = skb_copy_expand(skb, 2, 0, flags);
++ if (skb_cow_head(skb, 2)) {
+ dev_kfree_skb_any(skb);
+- skb = skb2;
+- if (!skb)
+- return NULL;
++ return NULL;
+ }
+ skb_push(skb, 2);
+
diff --git a/target/linux/generic/backport-4.9/023-4-sr9700-use-skb_cow_head-to-deal-with-cloned-skbs.patch b/target/linux/generic/backport-4.9/023-4-sr9700-use-skb_cow_head-to-deal-with-cloned-skbs.patch
new file mode 100644
index 0000000000..4d0fcf6fc2
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-4-sr9700-use-skb_cow_head-to-deal-with-cloned-skbs.patch
@@ -0,0 +1,37 @@
+From d532c1082f68176363ed766d09bf187616e282fe Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Wed, 19 Apr 2017 09:59:23 -0700
+Subject: [PATCH] sr9700: use skb_cow_head() to deal with cloned skbs
+
+We need to ensure there is enough headroom to push extra header,
+but we also need to check if we are allowed to change headers.
+
+skb_cow_head() is the proper helper to deal with this.
+
+Fixes: c9b37458e956 ("USB2NET : SR9700 : One chip USB 1.1 USB2NET SR9700Device Driver Support")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: James Hughes <james.hughes@raspberrypi.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/sr9700.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/usb/sr9700.c
++++ b/drivers/net/usb/sr9700.c
+@@ -456,14 +456,9 @@ static struct sk_buff *sr9700_tx_fixup(s
+
+ len = skb->len;
+
+- if (skb_headroom(skb) < SR_TX_OVERHEAD) {
+- struct sk_buff *skb2;
+-
+- skb2 = skb_copy_expand(skb, SR_TX_OVERHEAD, 0, flags);
++ if (skb_cow_head(skb, SR_TX_OVERHEAD)) {
+ dev_kfree_skb_any(skb);
+- skb = skb2;
+- if (!skb)
+- return NULL;
++ return NULL;
+ }
+
+ __skb_push(skb, SR_TX_OVERHEAD);
diff --git a/target/linux/generic/backport-4.9/023-5-lan78xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch b/target/linux/generic/backport-4.9/023-5-lan78xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch
new file mode 100644
index 0000000000..ea8279940b
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-5-lan78xx-use-skb_cow_head-to-deal-with-cloned-skbs.patch
@@ -0,0 +1,38 @@
+From d4ca73591916b760478d2b04334d5dcadc028e9c Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Wed, 19 Apr 2017 09:59:24 -0700
+Subject: [PATCH] lan78xx: use skb_cow_head() to deal with cloned skbs
+
+We need to ensure there is enough headroom to push extra header,
+but we also need to check if we are allowed to change headers.
+
+skb_cow_head() is the proper helper to deal with this.
+
+Fixes: 55d7de9de6c3 ("Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: James Hughes <james.hughes@raspberrypi.org>
+Cc: Woojung Huh <woojung.huh@microchip.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/lan78xx.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/usb/lan78xx.c
++++ b/drivers/net/usb/lan78xx.c
+@@ -2419,14 +2419,9 @@ static struct sk_buff *lan78xx_tx_prep(s
+ {
+ u32 tx_cmd_a, tx_cmd_b;
+
+- if (skb_headroom(skb) < TX_OVERHEAD) {
+- struct sk_buff *skb2;
+-
+- skb2 = skb_copy_expand(skb, TX_OVERHEAD, 0, flags);
++ if (skb_cow_head(skb, TX_OVERHEAD)) {
+ dev_kfree_skb_any(skb);
+- skb = skb2;
+- if (!skb)
+- return NULL;
++ return NULL;
+ }
+
+ if (lan78xx_linearize(skb) < 0)
diff --git a/target/linux/generic/backport-4.9/023-6-ch9200-use-skb_cow_head-to-deal-with-cloned-skbs.patch b/target/linux/generic/backport-4.9/023-6-ch9200-use-skb_cow_head-to-deal-with-cloned-skbs.patch
new file mode 100644
index 0000000000..83de38bf8a
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-6-ch9200-use-skb_cow_head-to-deal-with-cloned-skbs.patch
@@ -0,0 +1,38 @@
+From 6bc6895bdd6744e0136eaa4a11fbdb20a7db4e40 Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Wed, 19 Apr 2017 09:59:25 -0700
+Subject: [PATCH] ch9200: use skb_cow_head() to deal with cloned skbs
+
+We need to ensure there is enough headroom to push extra header,
+but we also need to check if we are allowed to change headers.
+
+skb_cow_head() is the proper helper to deal with this.
+
+Fixes: 4a476bd6d1d9 ("usbnet: New driver for QinHeng CH9200 devices")
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: James Hughes <james.hughes@raspberrypi.org>
+Cc: Matthew Garrett <mjg59@srcf.ucam.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/ch9200.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/usb/ch9200.c
++++ b/drivers/net/usb/ch9200.c
+@@ -254,14 +254,9 @@ static struct sk_buff *ch9200_tx_fixup(s
+ tx_overhead = 0x40;
+
+ len = skb->len;
+- if (skb_headroom(skb) < tx_overhead) {
+- struct sk_buff *skb2;
+-
+- skb2 = skb_copy_expand(skb, tx_overhead, 0, flags);
++ if (skb_cow_head(skb, tx_overhead)) {
+ dev_kfree_skb_any(skb);
+- skb = skb2;
+- if (!skb)
+- return NULL;
++ return NULL;
+ }
+
+ __skb_push(skb, tx_overhead);
diff --git a/target/linux/generic/backport-4.9/023-7-kaweth-use-skb_cow_head-to-deal-with-cloned-skbs.patch b/target/linux/generic/backport-4.9/023-7-kaweth-use-skb_cow_head-to-deal-with-cloned-skbs.patch
new file mode 100644
index 0000000000..597f25b21d
--- /dev/null
+++ b/target/linux/generic/backport-4.9/023-7-kaweth-use-skb_cow_head-to-deal-with-cloned-skbs.patch
@@ -0,0 +1,43 @@
+From 39fba7835aacda65284a86e611774cbba71dac20 Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Wed, 19 Apr 2017 09:59:26 -0700
+Subject: [PATCH] kaweth: use skb_cow_head() to deal with cloned skbs
+
+We can use skb_cow_head() to properly deal with clones,
+especially the ones coming from TCP stack that allow their head being
+modified. This avoids a copy.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Cc: James Hughes <james.hughes@raspberrypi.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/kaweth.c | 18 ++++++------------
+ 1 file changed, 6 insertions(+), 12 deletions(-)
+
+--- a/drivers/net/usb/kaweth.c
++++ b/drivers/net/usb/kaweth.c
+@@ -803,18 +803,12 @@ static netdev_tx_t kaweth_start_xmit(str
+ }
+
+ /* We now decide whether we can put our special header into the sk_buff */
+- if (skb_cloned(skb) || skb_headroom(skb) < 2) {
+- /* no such luck - we make our own */
+- struct sk_buff *copied_skb;
+- copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC);
+- dev_kfree_skb_irq(skb);
+- skb = copied_skb;
+- if (!copied_skb) {
+- kaweth->stats.tx_errors++;
+- netif_start_queue(net);
+- spin_unlock_irq(&kaweth->device_lock);
+- return NETDEV_TX_OK;
+- }
++ if (skb_cow_head(skb, 2)) {
++ kaweth->stats.tx_errors++;
++ netif_start_queue(net);
++ spin_unlock_irq(&kaweth->device_lock);
++ dev_kfree_skb_any(skb);
++ return NETDEV_TX_OK;
+ }
+
+ private_header = (__le16 *)__skb_push(skb, 2);
diff --git a/target/linux/generic/backport-4.9/030-01-ubifs-Drop-softlimit-and-delta-fields-from-struct-ub.patch b/target/linux/generic/backport-4.9/030-01-ubifs-Drop-softlimit-and-delta-fields-from-struct-ub.patch
new file mode 100644
index 0000000000..8e1f312a0b
--- /dev/null
+++ b/target/linux/generic/backport-4.9/030-01-ubifs-Drop-softlimit-and-delta-fields-from-struct-ub.patch
@@ -0,0 +1,82 @@
+From 854826c9d526fd81077742c3b000e3f7fcaef3ce Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 20 Sep 2016 10:36:14 +0200
+Subject: [PATCH] ubifs: Drop softlimit and delta fields from struct ubifs_wbuf
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Values of these fields are set during init and never modified. They are
+used (read) in a single function only. There isn't really any reason to
+keep them in a struct. It only makes struct just a bit bigger without
+any visible gain.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ fs/ubifs/io.c | 18 ++++++++++--------
+ fs/ubifs/ubifs.h | 5 -----
+ 2 files changed, 10 insertions(+), 13 deletions(-)
+
+--- a/fs/ubifs/io.c
++++ b/fs/ubifs/io.c
+@@ -452,16 +452,22 @@ static enum hrtimer_restart wbuf_timer_c
+ */
+ static void new_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
+ {
++ ktime_t softlimit = ktime_set(WBUF_TIMEOUT_SOFTLIMIT, 0);
++ unsigned long long delta;
++
++ delta = WBUF_TIMEOUT_HARDLIMIT - WBUF_TIMEOUT_SOFTLIMIT;
++ delta *= 1000000000ULL;
++
+ ubifs_assert(!hrtimer_active(&wbuf->timer));
++ ubifs_assert(delta <= ULONG_MAX);
+
+ if (wbuf->no_timer)
+ return;
+ dbg_io("set timer for jhead %s, %llu-%llu millisecs",
+ dbg_jhead(wbuf->jhead),
+- div_u64(ktime_to_ns(wbuf->softlimit), USEC_PER_SEC),
+- div_u64(ktime_to_ns(wbuf->softlimit) + wbuf->delta,
+- USEC_PER_SEC));
+- hrtimer_start_range_ns(&wbuf->timer, wbuf->softlimit, wbuf->delta,
++ div_u64(ktime_to_ns(softlimit), USEC_PER_SEC),
++ div_u64(ktime_to_ns(softlimit) + delta, USEC_PER_SEC));
++ hrtimer_start_range_ns(&wbuf->timer, softlimit, delta,
+ HRTIMER_MODE_REL);
+ }
+
+@@ -1059,10 +1065,6 @@ int ubifs_wbuf_init(struct ubifs_info *c
+
+ hrtimer_init(&wbuf->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ wbuf->timer.function = wbuf_timer_callback_nolock;
+- wbuf->softlimit = ktime_set(WBUF_TIMEOUT_SOFTLIMIT, 0);
+- wbuf->delta = WBUF_TIMEOUT_HARDLIMIT - WBUF_TIMEOUT_SOFTLIMIT;
+- wbuf->delta *= 1000000000ULL;
+- ubifs_assert(wbuf->delta <= ULONG_MAX);
+ return 0;
+ }
+
+--- a/fs/ubifs/ubifs.h
++++ b/fs/ubifs/ubifs.h
+@@ -645,9 +645,6 @@ typedef int (*ubifs_lpt_scan_callback)(s
+ * @io_mutex: serializes write-buffer I/O
+ * @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes
+ * fields
+- * @softlimit: soft write-buffer timeout interval
+- * @delta: hard and soft timeouts delta (the timer expire interval is @softlimit
+- * and @softlimit + @delta)
+ * @timer: write-buffer timer
+ * @no_timer: non-zero if this write-buffer does not have a timer
+ * @need_sync: non-zero if the timer expired and the wbuf needs sync'ing
+@@ -676,8 +673,6 @@ struct ubifs_wbuf {
+ int (*sync_callback)(struct ubifs_info *c, int lnum, int free, int pad);
+ struct mutex io_mutex;
+ spinlock_t lock;
+- ktime_t softlimit;
+- unsigned long long delta;
+ struct hrtimer timer;
+ unsigned int no_timer:1;
+ unsigned int need_sync:1;
diff --git a/target/linux/generic/backport-4.9/030-02-ubifs-Use-dirty_writeback_interval-value-for-wbuf-ti.patch b/target/linux/generic/backport-4.9/030-02-ubifs-Use-dirty_writeback_interval-value-for-wbuf-ti.patch
new file mode 100644
index 0000000000..96fa441132
--- /dev/null
+++ b/target/linux/generic/backport-4.9/030-02-ubifs-Use-dirty_writeback_interval-value-for-wbuf-ti.patch
@@ -0,0 +1,66 @@
+From 1b7fc2c0069f3864a3dda15430b7aded31c0bfcc Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 20 Sep 2016 10:36:15 +0200
+Subject: [PATCH] ubifs: Use dirty_writeback_interval value for wbuf timer
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Right now wbuf timer has hardcoded timeouts and there is no place for
+manual adjustments. Some projects / cases many need that though. Few
+file systems allow doing that by respecting dirty_writeback_interval
+that can be set using sysctl (dirty_writeback_centisecs).
+
+Lowering dirty_writeback_interval could be some way of dealing with user
+space apps lacking proper fsyncs. This is definitely *not* a perfect
+solution but we don't have ideal (user space) world. There were already
+advanced discussions on this matter, mostly when ext4 was introduced and
+it wasn't behaving as ext3. Anyway, the final decision was to add some
+hacks to the ext4, as trying to fix whole user space or adding new API
+was pointless.
+
+We can't (and shouldn't?) just follow ext4. We can't e.g. sync on close
+as this would cause too many commits and flash wearing. On the other
+hand we still should allow some trade-off between -o sync and default
+wbuf timeout. Respecting dirty_writeback_interval should allow some sane
+cutomizations if used warily.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Richard Weinberger <richard@nod.at>
+---
+ fs/ubifs/io.c | 8 ++++----
+ fs/ubifs/ubifs.h | 4 ----
+ 2 files changed, 4 insertions(+), 8 deletions(-)
+
+--- a/fs/ubifs/io.c
++++ b/fs/ubifs/io.c
+@@ -452,11 +452,11 @@ static enum hrtimer_restart wbuf_timer_c
+ */
+ static void new_wbuf_timer_nolock(struct ubifs_wbuf *wbuf)
+ {
+- ktime_t softlimit = ktime_set(WBUF_TIMEOUT_SOFTLIMIT, 0);
+- unsigned long long delta;
++ ktime_t softlimit = ms_to_ktime(dirty_writeback_interval * 10);
++ unsigned long long delta = dirty_writeback_interval;
+
+- delta = WBUF_TIMEOUT_HARDLIMIT - WBUF_TIMEOUT_SOFTLIMIT;
+- delta *= 1000000000ULL;
++ /* centi to milli, milli to nano, then 10% */
++ delta *= 10ULL * NSEC_PER_MSEC / 10ULL;
+
+ ubifs_assert(!hrtimer_active(&wbuf->timer));
+ ubifs_assert(delta <= ULONG_MAX);
+--- a/fs/ubifs/ubifs.h
++++ b/fs/ubifs/ubifs.h
+@@ -83,10 +83,6 @@
+ */
+ #define BGT_NAME_PATTERN "ubifs_bgt%d_%d"
+
+-/* Write-buffer synchronization timeout interval in seconds */
+-#define WBUF_TIMEOUT_SOFTLIMIT 3
+-#define WBUF_TIMEOUT_HARDLIMIT 5
+-
+ /* Maximum possible inode number (only 32-bit inodes are supported now) */
+ #define MAX_INUM 0xFFFFFFFF
+
diff --git a/target/linux/generic/backport-4.9/050-usb-dwc2-Remove-unnecessary-kfree.patch b/target/linux/generic/backport-4.9/050-usb-dwc2-Remove-unnecessary-kfree.patch
new file mode 100644
index 0000000000..df3656dd55
--- /dev/null
+++ b/target/linux/generic/backport-4.9/050-usb-dwc2-Remove-unnecessary-kfree.patch
@@ -0,0 +1,24 @@
+From cd4b1e34655d46950c065d9284b596cd8d7b28cd Mon Sep 17 00:00:00 2001
+From: John Youn <johnyoun@synopsys.com>
+Date: Thu, 3 Nov 2016 17:55:45 -0700
+Subject: [PATCH] usb: dwc2: Remove unnecessary kfree
+
+This shouldn't be freed by the HCD as it is owned by the core and
+allocated with devm_kzalloc.
+
+Signed-off-by: John Youn <johnyoun@synopsys.com>
+Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
+---
+ drivers/usb/dwc2/hcd.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/usb/dwc2/hcd.c
++++ b/drivers/usb/dwc2/hcd.c
+@@ -5184,7 +5184,6 @@ error3:
+ error2:
+ usb_put_hcd(hcd);
+ error1:
+- kfree(hsotg->core_params);
+
+ #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
+ kfree(hsotg->last_frame_num_array);
diff --git a/target/linux/generic/backport-4.9/060-0002-mtd-bcm47xxsflash-use-platform_-set-get-_drvdata.patch b/target/linux/generic/backport-4.9/060-0002-mtd-bcm47xxsflash-use-platform_-set-get-_drvdata.patch
new file mode 100644
index 0000000000..d975a26202
--- /dev/null
+++ b/target/linux/generic/backport-4.9/060-0002-mtd-bcm47xxsflash-use-platform_-set-get-_drvdata.patch
@@ -0,0 +1,63 @@
+From be5e5099183301fb7920f8f6b66bd3ac1f820a97 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 16 Jan 2017 17:28:18 +0100
+Subject: [PATCH] mtd: bcm47xxsflash: use platform_(set|get)_drvdata
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+We have generic place & helpers for storing platform driver data so
+there is no reason for using custom priv pointer.
+
+This allows cleaning up struct bcma_sflash from unneeded fields.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Kalle Valo <kvalo@codeaurora.org>
+Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/devices/bcm47xxsflash.c | 6 +++---
+ include/linux/bcma/bcma_driver_chipcommon.h | 3 ---
+ 2 files changed, 3 insertions(+), 6 deletions(-)
+
+--- a/drivers/mtd/devices/bcm47xxsflash.c
++++ b/drivers/mtd/devices/bcm47xxsflash.c
+@@ -284,7 +284,6 @@ static int bcm47xxsflash_bcma_probe(stru
+ b47s = devm_kzalloc(dev, sizeof(*b47s), GFP_KERNEL);
+ if (!b47s)
+ return -ENOMEM;
+- sflash->priv = b47s;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+@@ -320,6 +319,8 @@ static int bcm47xxsflash_bcma_probe(stru
+ b47s->size = sflash->size;
+ bcm47xxsflash_fill_mtd(b47s, &pdev->dev);
+
++ platform_set_drvdata(pdev, b47s);
++
+ err = mtd_device_parse_register(&b47s->mtd, probes, NULL, NULL, 0);
+ if (err) {
+ pr_err("Failed to register MTD device: %d\n", err);
+@@ -335,8 +336,7 @@ static int bcm47xxsflash_bcma_probe(stru
+
+ static int bcm47xxsflash_bcma_remove(struct platform_device *pdev)
+ {
+- struct bcma_sflash *sflash = dev_get_platdata(&pdev->dev);
+- struct bcm47xxsflash *b47s = sflash->priv;
++ struct bcm47xxsflash *b47s = platform_get_drvdata(pdev);
+
+ mtd_device_unregister(&b47s->mtd);
+ iounmap(b47s->window);
+--- a/include/linux/bcma/bcma_driver_chipcommon.h
++++ b/include/linux/bcma/bcma_driver_chipcommon.h
+@@ -593,9 +593,6 @@ struct bcma_sflash {
+ u32 blocksize;
+ u16 numblocks;
+ u32 size;
+-
+- struct mtd_info *mtd;
+- void *priv;
+ };
+ #endif
+
diff --git a/target/linux/generic/backport-4.9/060-0003-mtd-bcm47xxsflash-support-reading-flash-out-of-mappi.patch b/target/linux/generic/backport-4.9/060-0003-mtd-bcm47xxsflash-support-reading-flash-out-of-mappi.patch
new file mode 100644
index 0000000000..ecdae2a871
--- /dev/null
+++ b/target/linux/generic/backport-4.9/060-0003-mtd-bcm47xxsflash-support-reading-flash-out-of-mappi.patch
@@ -0,0 +1,81 @@
+From ccc38234fdc70120be79e7fb2df5c27ca5cd4c8a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 8 Feb 2017 23:53:44 +0100
+Subject: [PATCH] mtd: bcm47xxsflash: support reading flash out of mapping
+ window
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+For reading flash content we use MMIO but it's possible to read only
+first 16 MiB this way. It's simply an arch design/limitation.
+To support flash sizes bigger than 16 MiB implement indirect access
+using ChipCommon registers.
+This has been tested using MX25L25635F.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/devices/bcm47xxsflash.c | 24 +++++++++++++++++++++---
+ drivers/mtd/devices/bcm47xxsflash.h | 3 +++
+ 2 files changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/mtd/devices/bcm47xxsflash.c
++++ b/drivers/mtd/devices/bcm47xxsflash.c
+@@ -105,15 +105,33 @@ static int bcm47xxsflash_read(struct mtd
+ size_t *retlen, u_char *buf)
+ {
+ struct bcm47xxsflash *b47s = mtd->priv;
++ size_t orig_len = len;
+
+ /* Check address range */
+ if ((from + len) > mtd->size)
+ return -EINVAL;
+
+- memcpy_fromio(buf, b47s->window + from, len);
+- *retlen = len;
++ /* Read as much as possible using fast MMIO window */
++ if (from < BCM47XXSFLASH_WINDOW_SZ) {
++ size_t memcpy_len;
+
+- return len;
++ memcpy_len = min(len, (size_t)(BCM47XXSFLASH_WINDOW_SZ - from));
++ memcpy_fromio(buf, b47s->window + from, memcpy_len);
++ from += memcpy_len;
++ len -= memcpy_len;
++ buf += memcpy_len;
++ }
++
++ /* Use indirect access for content out of the window */
++ for (; len; len--) {
++ b47s->cc_write(b47s, BCMA_CC_FLASHADDR, from++);
++ bcm47xxsflash_cmd(b47s, OPCODE_ST_READ4B);
++ *buf++ = b47s->cc_read(b47s, BCMA_CC_FLASHDATA);
++ }
++
++ *retlen = orig_len;
++
++ return orig_len;
+ }
+
+ static int bcm47xxsflash_write_st(struct mtd_info *mtd, u32 offset, size_t len,
+--- a/drivers/mtd/devices/bcm47xxsflash.h
++++ b/drivers/mtd/devices/bcm47xxsflash.h
+@@ -3,6 +3,8 @@
+
+ #include <linux/mtd/mtd.h>
+
++#define BCM47XXSFLASH_WINDOW_SZ SZ_16M
++
+ /* Used for ST flashes only. */
+ #define OPCODE_ST_WREN 0x0006 /* Write Enable */
+ #define OPCODE_ST_WRDIS 0x0004 /* Write Disable */
+@@ -16,6 +18,7 @@
+ #define OPCODE_ST_RES 0x03ab /* Read Electronic Signature */
+ #define OPCODE_ST_CSA 0x1000 /* Keep chip select asserted */
+ #define OPCODE_ST_SSE 0x0220 /* Sub-sector Erase */
++#define OPCODE_ST_READ4B 0x6313 /* Read Data Bytes in 4Byte addressing mode */
+
+ /* Used for Atmel flashes only. */
+ #define OPCODE_AT_READ 0x07e8
diff --git a/target/linux/generic/backport-4.9/060-0004-mtd-bcm47xxpart-move-TRX-parsing-code-to-separated-f.patch b/target/linux/generic/backport-4.9/060-0004-mtd-bcm47xxpart-move-TRX-parsing-code-to-separated-f.patch
new file mode 100644
index 0000000000..999f544d3b
--- /dev/null
+++ b/target/linux/generic/backport-4.9/060-0004-mtd-bcm47xxpart-move-TRX-parsing-code-to-separated-f.patch
@@ -0,0 +1,180 @@
+From b522d7b0ebe3539340c2a6d46d787ae3d33bcb92 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 10 Jan 2017 23:15:24 +0100
+Subject: [PATCH] mtd: bcm47xxpart: move TRX parsing code to separated function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This change simplifies main parsing loop logic a bit. In future it may
+be useful for moving TRX support to separated module / parser (if we
+implement support for them at some point).
+Finally parsing TRX at the end puts us in a better position as we have
+better flash layout knowledge. It may be useful e.g. if it appears there
+is more than 1 TRX partition.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/bcm47xxpart.c | 121 ++++++++++++++++++++++++++++------------------
+ 1 file changed, 74 insertions(+), 47 deletions(-)
+
+--- a/drivers/mtd/bcm47xxpart.c
++++ b/drivers/mtd/bcm47xxpart.c
+@@ -83,6 +83,67 @@ out_default:
+ return "rootfs";
+ }
+
++static int bcm47xxpart_parse_trx(struct mtd_info *master,
++ struct mtd_partition *trx,
++ struct mtd_partition *parts,
++ size_t parts_len)
++{
++ struct trx_header header;
++ size_t bytes_read;
++ int curr_part = 0;
++ int i, err;
++
++ if (parts_len < 3) {
++ pr_warn("No enough space to add TRX partitions!\n");
++ return -ENOMEM;
++ }
++
++ err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
++ (uint8_t *)&header);
++ if (err && !mtd_is_bitflip(err)) {
++ pr_err("mtd_read error while reading TRX header: %d\n", err);
++ return err;
++ }
++
++ i = 0;
++
++ /* We have LZMA loader if offset[2] points to sth */
++ if (header.offset[2]) {
++ bcm47xxpart_add_part(&parts[curr_part++], "loader",
++ trx->offset + header.offset[i], 0);
++ i++;
++ }
++
++ if (header.offset[i]) {
++ bcm47xxpart_add_part(&parts[curr_part++], "linux",
++ trx->offset + header.offset[i], 0);
++ i++;
++ }
++
++ if (header.offset[i]) {
++ size_t offset = trx->offset + header.offset[i];
++ const char *name = bcm47xxpart_trx_data_part_name(master,
++ offset);
++
++ bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
++ i++;
++ }
++
++ /*
++ * Assume that every partition ends at the beginning of the one it is
++ * followed by.
++ */
++ for (i = 0; i < curr_part; i++) {
++ u64 next_part_offset = (i < curr_part - 1) ?
++ parts[i + 1].offset :
++ trx->offset + trx->size;
++
++ parts[i].size = next_part_offset - parts[i].offset;
++ }
++
++ return curr_part;
++}
++
+ static int bcm47xxpart_parse(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+@@ -93,9 +154,7 @@ static int bcm47xxpart_parse(struct mtd_
+ size_t bytes_read;
+ uint32_t offset;
+ uint32_t blocksize = master->erasesize;
+- struct trx_header *trx;
+ int trx_part = -1;
+- int last_trx_part = -1;
+ int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
+ int err;
+
+@@ -182,54 +241,14 @@ static int bcm47xxpart_parse(struct mtd_
+
+ /* TRX */
+ if (buf[0x000 / 4] == TRX_MAGIC) {
+- if (BCM47XXPART_MAX_PARTS - curr_part < 4) {
+- pr_warn("Not enough partitions left to register trx, scanning stopped!\n");
+- break;
+- }
+-
+- trx = (struct trx_header *)buf;
++ struct trx_header *trx;
+
+ trx_part = curr_part;
+ bcm47xxpart_add_part(&parts[curr_part++], "firmware",
+ offset, 0);
+
+- i = 0;
+- /* We have LZMA loader if offset[2] points to sth */
+- if (trx->offset[2]) {
+- bcm47xxpart_add_part(&parts[curr_part++],
+- "loader",
+- offset + trx->offset[i],
+- 0);
+- i++;
+- }
+-
+- if (trx->offset[i]) {
+- bcm47xxpart_add_part(&parts[curr_part++],
+- "linux",
+- offset + trx->offset[i],
+- 0);
+- i++;
+- }
+-
+- /*
+- * Pure rootfs size is known and can be calculated as:
+- * trx->length - trx->offset[i]. We don't fill it as
+- * we want to have jffs2 (overlay) in the same mtd.
+- */
+- if (trx->offset[i]) {
+- const char *name;
+-
+- name = bcm47xxpart_trx_data_part_name(master, offset + trx->offset[i]);
+- bcm47xxpart_add_part(&parts[curr_part++],
+- name,
+- offset + trx->offset[i],
+- 0);
+- i++;
+- }
+-
+- last_trx_part = curr_part - 1;
+-
+ /* Jump to the end of TRX */
++ trx = (struct trx_header *)buf;
+ offset = roundup(offset + trx->length, blocksize);
+ /* Next loop iteration will increase the offset */
+ offset -= blocksize;
+@@ -307,9 +326,17 @@ static int bcm47xxpart_parse(struct mtd_
+ parts[i + 1].offset : master->size;
+
+ parts[i].size = next_part_offset - parts[i].offset;
+- if (i == last_trx_part && trx_part >= 0)
+- parts[trx_part].size = next_part_offset -
+- parts[trx_part].offset;
++ }
++
++ /* If there was TRX parse it now */
++ if (trx_part >= 0) {
++ int num_parts;
++
++ num_parts = bcm47xxpart_parse_trx(master, &parts[trx_part],
++ parts + curr_part,
++ BCM47XXPART_MAX_PARTS - curr_part);
++ if (num_parts > 0)
++ curr_part += num_parts;
+ }
+
+ *pparts = parts;
diff --git a/target/linux/generic/backport-4.9/060-0005-mtd-bcm47xxpart-support-layouts-with-multiple-TRX-pa.patch b/target/linux/generic/backport-4.9/060-0005-mtd-bcm47xxpart-support-layouts-with-multiple-TRX-pa.patch
new file mode 100644
index 0000000000..bcefee8e87
--- /dev/null
+++ b/target/linux/generic/backport-4.9/060-0005-mtd-bcm47xxpart-support-layouts-with-multiple-TRX-pa.patch
@@ -0,0 +1,112 @@
+From 89a0d9a9f1941a086a82bc7cd73d275cec98ba14 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 10 Jan 2017 23:15:25 +0100
+Subject: [PATCH] mtd: bcm47xxpart: support layouts with multiple TRX
+ partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some devices may have an extra TRX partition used as failsafe one. If
+we detect such partition we should set a proper name for it and don't
+parse it.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/bcm47xxpart.c | 56 ++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 46 insertions(+), 10 deletions(-)
+
+--- a/drivers/mtd/bcm47xxpart.c
++++ b/drivers/mtd/bcm47xxpart.c
+@@ -9,6 +9,7 @@
+ *
+ */
+
++#include <linux/bcm47xx_nvram.h>
+ #include <linux/module.h>
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+@@ -144,6 +145,30 @@ static int bcm47xxpart_parse_trx(struct
+ return curr_part;
+ }
+
++/**
++ * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
++ *
++ * Some devices may have more than one TRX partition. In such case one of them
++ * is the main one and another a failsafe one. Bootloader may fallback to the
++ * failsafe firmware if it detects corruption of the main image.
++ *
++ * This function provides info about currently used TRX partition. It's the one
++ * containing kernel started by the bootloader.
++ */
++static int bcm47xxpart_bootpartition(void)
++{
++ char buf[4];
++ int bootpartition;
++
++ /* Check CFE environment variable */
++ if (bcm47xx_nvram_getenv("bootpartition", buf, sizeof(buf)) > 0) {
++ if (!kstrtoint(buf, 0, &bootpartition))
++ return bootpartition;
++ }
++
++ return 0;
++}
++
+ static int bcm47xxpart_parse(struct mtd_info *master,
+ const struct mtd_partition **pparts,
+ struct mtd_part_parser_data *data)
+@@ -154,7 +179,8 @@ static int bcm47xxpart_parse(struct mtd_
+ size_t bytes_read;
+ uint32_t offset;
+ uint32_t blocksize = master->erasesize;
+- int trx_part = -1;
++ int trx_parts[2]; /* Array with indexes of TRX partitions */
++ int trx_num = 0; /* Number of found TRX partitions */
+ int possible_nvram_sizes[] = { 0x8000, 0xF000, 0x10000, };
+ int err;
+
+@@ -243,7 +269,11 @@ static int bcm47xxpart_parse(struct mtd_
+ if (buf[0x000 / 4] == TRX_MAGIC) {
+ struct trx_header *trx;
+
+- trx_part = curr_part;
++ if (trx_num >= ARRAY_SIZE(trx_parts))
++ pr_warn("No enough space to store another TRX found at 0x%X\n",
++ offset);
++ else
++ trx_parts[trx_num++] = curr_part;
+ bcm47xxpart_add_part(&parts[curr_part++], "firmware",
+ offset, 0);
+
+@@ -329,14 +359,20 @@ static int bcm47xxpart_parse(struct mtd_
+ }
+
+ /* If there was TRX parse it now */
+- if (trx_part >= 0) {
+- int num_parts;
++ for (i = 0; i < trx_num; i++) {
++ struct mtd_partition *trx = &parts[trx_parts[i]];
+
+- num_parts = bcm47xxpart_parse_trx(master, &parts[trx_part],
+- parts + curr_part,
+- BCM47XXPART_MAX_PARTS - curr_part);
+- if (num_parts > 0)
+- curr_part += num_parts;
++ if (i == bcm47xxpart_bootpartition()) {
++ int num_parts;
++
++ num_parts = bcm47xxpart_parse_trx(master, trx,
++ parts + curr_part,
++ BCM47XXPART_MAX_PARTS - curr_part);
++ if (num_parts > 0)
++ curr_part += num_parts;
++ } else {
++ trx->name = "failsafe";
++ }
+ }
+
+ *pparts = parts;
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0001-mtd-spi-nor-add-Macronix-mx25u25635f-to-list-of-know.patch b/target/linux/generic/backport-4.9/061-v4.10-0001-mtd-spi-nor-add-Macronix-mx25u25635f-to-list-of-know.patch
new file mode 100644
index 0000000000..54f950036b
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0001-mtd-spi-nor-add-Macronix-mx25u25635f-to-list-of-know.patch
@@ -0,0 +1,22 @@
+From 355445b86f0f61125409e1217be4f0a8ee362116 Mon Sep 17 00:00:00 2001
+From: Ash Benz <ash.benz@bk.ru>
+Date: Sat, 19 Nov 2016 07:51:49 +0800
+Subject: [PATCH] mtd: spi-nor: add Macronix mx25u25635f to list of known
+ devices.
+
+Signed-off-by: Ash Benz <ash.benz@bk.ru>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -872,6 +872,7 @@ static const struct flash_info spi_nor_i
+ { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+ { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
+ { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
++ { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K) },
+ { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+ { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0002-mtd-spi-nor-fix-spansion-quad-enable.patch b/target/linux/generic/backport-4.9/061-v4.10-0002-mtd-spi-nor-fix-spansion-quad-enable.patch
new file mode 100644
index 0000000000..cd0aa4660a
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0002-mtd-spi-nor-fix-spansion-quad-enable.patch
@@ -0,0 +1,42 @@
+From 807c16253319ee6ccf8873ae64f070f7eb532cd5 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jo=C3=ABl=20Esponde?= <joel.esponde@honeywell.com>
+Date: Wed, 23 Nov 2016 12:47:40 +0100
+Subject: [PATCH] mtd: spi-nor: fix spansion quad enable
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+With the S25FL127S nor flash part, each writing to the configuration
+register takes hundreds of ms. During that time, no more accesses to
+the flash should be done (even reads).
+
+This commit adds a wait loop after the register writing until the flash
+finishes its work.
+
+This issue could make rootfs mounting fail when the latter was done too
+much closely to this quad enable bit setting step. And in this case, a
+driver as UBIFS may try to recover the filesystem and may broke it
+completely.
+
+Signed-off-by: Joël Esponde <joel.esponde@honeywell.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1256,6 +1256,13 @@ static int spansion_quad_enable(struct s
+ return -EINVAL;
+ }
+
++ ret = spi_nor_wait_till_ready(nor);
++ if (ret) {
++ dev_err(nor->dev,
++ "timeout while writing configuration register\n");
++ return ret;
++ }
++
+ /* read back and check it */
+ ret = read_cr(nor);
+ if (!(ret > 0 && (ret & CR_QUAD_EN_SPAN))) {
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0003-mtd-spi-nor-fix-flags-for-s25fl128s.patch b/target/linux/generic/backport-4.9/061-v4.10-0003-mtd-spi-nor-fix-flags-for-s25fl128s.patch
new file mode 100644
index 0000000000..9c4e10d022
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0003-mtd-spi-nor-fix-flags-for-s25fl128s.patch
@@ -0,0 +1,28 @@
+From 4287916d7bab2806305d3296b4cf261fa49d959b Mon Sep 17 00:00:00 2001
+From: Heiner Kallweit <hkallweit1@gmail.com>
+Date: Thu, 27 Oct 2016 23:13:26 +0200
+Subject: [PATCH] mtd: spi-nor: fix flags for s25fl128s
+
+The Spansion S25FL128S also supports dual read mode.
+In addition remove flag SECT_4K. 4K erases are supported,
+but not uniformly.
+
+Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
+Reviewed-by: Jagan Teki <jteki@openedev.com>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -906,7 +906,7 @@ static const struct flash_info spi_nor_i
+ { "s70fl01gs", INFO(0x010221, 0x4d00, 256 * 1024, 256, 0) },
+ { "s25sl12800", INFO(0x012018, 0x0300, 256 * 1024, 64, 0) },
+ { "s25sl12801", INFO(0x012018, 0x0301, 64 * 1024, 256, 0) },
+- { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
++ { "s25fl128s", INFO6(0x012018, 0x4d0180, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl129p0", INFO(0x012018, 0x4d00, 256 * 1024, 64, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25fl129p1", INFO(0x012018, 0x4d01, 64 * 1024, 256, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+ { "s25sl004a", INFO(0x010212, 0, 64 * 1024, 8, 0) },
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0004-mtd-spi-nor-add-support-for-s25fl208k.patch b/target/linux/generic/backport-4.9/061-v4.10-0004-mtd-spi-nor-add-support-for-s25fl208k.patch
new file mode 100644
index 0000000000..955e5bb2d4
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0004-mtd-spi-nor-add-support-for-s25fl208k.patch
@@ -0,0 +1,23 @@
+From 022a400f90ceeb26405edd5e077d56e2f38c8123 Mon Sep 17 00:00:00 2001
+From: Sean Nyekjaer <sean.nyekjaer@prevas.dk>
+Date: Wed, 5 Oct 2016 10:59:49 +0200
+Subject: [PATCH] mtd: spi-nor: add support for s25fl208k
+
+Signed-off-by: Sean Nyekjaer <sean.nyekjaer@prevas.dk>
+Reviewed-by: Jagan Teki <jagan@openedev.com>
+Acked-by: Marek Vasut <marex@denx.de>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -922,6 +922,7 @@ static const struct flash_info spi_nor_i
+ { "s25fl132k", INFO(0x014016, 0, 64 * 1024, 64, SECT_4K) },
+ { "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
+ { "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
++ { "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
+
+ /* SST -- large erase sizes are "overlays", "sectors" are 4K */
+ { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0005-mtd-spi-nor-Add-at25df321-spi-nor-flash-support.patch b/target/linux/generic/backport-4.9/061-v4.10-0005-mtd-spi-nor-Add-at25df321-spi-nor-flash-support.patch
new file mode 100644
index 0000000000..b97d8c7857
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0005-mtd-spi-nor-Add-at25df321-spi-nor-flash-support.patch
@@ -0,0 +1,26 @@
+From b08618c929b289699a496b8d45a4e1a014187e56 Mon Sep 17 00:00:00 2001
+From: Jagan Teki <jteki@openedev.com>
+Date: Tue, 26 Jul 2016 14:07:54 +0530
+Subject: [PATCH] mtd: spi-nor: Add at25df321 spi-nor flash support
+
+Add Atmel at25df321 spi-nor flash to the list of spi_nor_ids.
+
+Cc: Brian Norris <computersforpeace@gmail.com>
+Cc: Wenyou Yang <wenyou.yang@atmel.com>
+Signed-off-by: Jagan Teki <jteki@openedev.com>
+Acked-by: Wenyou Yang <wenyou.yang@atmel.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -799,6 +799,7 @@ static const struct flash_info spi_nor_i
+ { "at25fs040", INFO(0x1f6604, 0, 64 * 1024, 8, SECT_4K) },
+
+ { "at25df041a", INFO(0x1f4401, 0, 64 * 1024, 8, SECT_4K) },
++ { "at25df321", INFO(0x1f4700, 0, 64 * 1024, 64, SECT_4K) },
+ { "at25df321a", INFO(0x1f4701, 0, 64 * 1024, 64, SECT_4K) },
+ { "at25df641", INFO(0x1f4800, 0, 64 * 1024, 128, SECT_4K) },
+
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0006-mtd-spi-nor-Add-support-for-N25Q016A.patch b/target/linux/generic/backport-4.9/061-v4.10-0006-mtd-spi-nor-Add-support-for-N25Q016A.patch
new file mode 100644
index 0000000000..b4e94543e5
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0006-mtd-spi-nor-Add-support-for-N25Q016A.patch
@@ -0,0 +1,29 @@
+From 61e4611864b396c7e9040b7335f25d3921bc87cd Mon Sep 17 00:00:00 2001
+From: Moritz Fischer <moritz.fischer@ettus.com>
+Date: Fri, 15 Jul 2016 10:03:48 -0700
+Subject: [PATCH] mtd: spi-nor: Add support for N25Q016A
+
+This commit adds support in the spi-nor driver for the
+N25Q016A, a 16Mbit SPI NOR flash from Micron.
+
+Cc: David Woodhouse <dwmw2@infradead.org>
+Cc: Brian Norris <computersforpeace@gmail.com>
+Cc: Jagan Teki <jteki@openedev.com>
+
+Signed-off-by: Moritz Fischer <moritz.fischer@ettus.com>
+Reviewed-by: Jagan Teki <jteki@openedev.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -879,6 +879,7 @@ static const struct flash_info spi_nor_i
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
+
+ /* Micron */
++ { "n25q016a", INFO(0x20bb15, 0, 64 * 1024, 32, SECT_4K | SPI_NOR_QUAD_READ) },
+ { "n25q032", INFO(0x20ba16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
+ { "n25q032a", INFO(0x20bb16, 0, 64 * 1024, 64, SPI_NOR_QUAD_READ) },
+ { "n25q064", INFO(0x20ba17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
diff --git a/target/linux/generic/backport-4.9/061-v4.10-0007-mtd-spi-nor-Add-support-for-mr25h40.patch b/target/linux/generic/backport-4.9/061-v4.10-0007-mtd-spi-nor-Add-support-for-mr25h40.patch
new file mode 100644
index 0000000000..3bae77db7c
--- /dev/null
+++ b/target/linux/generic/backport-4.9/061-v4.10-0007-mtd-spi-nor-Add-support-for-mr25h40.patch
@@ -0,0 +1,25 @@
+From edd0c8f4932dbf3e21036cb443ba5bdf7449d02b Mon Sep 17 00:00:00 2001
+From: IWAMOTO Masahiko <iwamoto@allied-telesis.co.jp>
+Date: Wed, 5 Oct 2016 08:22:52 +0000
+Subject: [PATCH] mtd: spi-nor: Add support for mr25h40
+
+Add Everspin mr25h40 512KB MRAM to the list of supported chips.
+
+Signed-off-by: Masahiko Iwamoto <iwamoto@allied-telesis.co.jp>
+Reviewed-by: Jagan Teki <jagan@openedev.com>
+Acked-by: Marek Vasut <marex@denx.de>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -826,6 +826,7 @@ static const struct flash_info spi_nor_i
+ /* Everspin */
+ { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "mr25h10", CAT25_INFO(128 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
++ { "mr25h40", CAT25_INFO(512 * 1024, 1, 256, 3, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+
+ /* Fujitsu */
+ { "mb85rs1mt", INFO(0x047f27, 0, 128 * 1024, 1, SPI_NOR_NO_ERASE) },
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0001-mtd-spi-nor-Add-support-for-S3AN-spi-nor-devices.patch b/target/linux/generic/backport-4.9/062-v4.11-0001-mtd-spi-nor-Add-support-for-S3AN-spi-nor-devices.patch
new file mode 100644
index 0000000000..df36f6b036
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0001-mtd-spi-nor-Add-support-for-S3AN-spi-nor-devices.patch
@@ -0,0 +1,312 @@
+From 61cba34bd6c1bddfc38f94cc3f80bdfefcc3393b Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda <ricardo.ribalda@gmail.com>
+Date: Fri, 2 Dec 2016 12:31:44 +0100
+Subject: [PATCH] mtd: spi-nor: Add support for S3AN spi-nor devices
+
+Xilinx Spartan-3AN FPGAs contain an In-System Flash where they keep
+their configuration data and (optionally) some user data.
+
+The protocol of this flash follows most of the spi-nor standard. With
+the following differences:
+
+- Page size might not be a power of two.
+- The address calculation (default addressing mode).
+- The spi nor commands used.
+
+Protocol is described on Xilinx User Guide UG333
+
+Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
+Cc: Boris Brezillon <boris.brezillon@free-electrons.com>
+Cc: Brian Norris <computersforpeace@gmail.com>
+Cc: Marek Vasut <marek.vasut@gmail.com>
+Reviewed-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 154 ++++++++++++++++++++++++++++++++++++++++--
+ include/linux/mtd/spi-nor.h | 12 ++++
+ 2 files changed, 161 insertions(+), 5 deletions(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -75,6 +75,12 @@ struct flash_info {
+ * bit. Must be used with
+ * SPI_NOR_HAS_LOCK.
+ */
++#define SPI_S3AN BIT(10) /*
++ * Xilinx Spartan 3AN In-System Flash
++ * (MFR cannot be used for probing
++ * because it has the same value as
++ * ATMEL flashes)
++ */
+ };
+
+ #define JEDEC_MFR(info) ((info)->id[0])
+@@ -217,6 +223,21 @@ static inline int set_4byte(struct spi_n
+ return nor->write_reg(nor, SPINOR_OP_BRWR, nor->cmd_buf, 1);
+ }
+ }
++
++static int s3an_sr_ready(struct spi_nor *nor)
++{
++ int ret;
++ u8 val;
++
++ ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
++ if (ret < 0) {
++ dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
++ return ret;
++ }
++
++ return !!(val & XSR_RDY);
++}
++
+ static inline int spi_nor_sr_ready(struct spi_nor *nor)
+ {
+ int sr = read_sr(nor);
+@@ -238,7 +259,11 @@ static inline int spi_nor_fsr_ready(stru
+ static int spi_nor_ready(struct spi_nor *nor)
+ {
+ int sr, fsr;
+- sr = spi_nor_sr_ready(nor);
++
++ if (nor->flags & SNOR_F_READY_XSR_RDY)
++ sr = s3an_sr_ready(nor);
++ else
++ sr = spi_nor_sr_ready(nor);
+ if (sr < 0)
+ return sr;
+ fsr = nor->flags & SNOR_F_USE_FSR ? spi_nor_fsr_ready(nor) : 1;
+@@ -320,6 +345,24 @@ static void spi_nor_unlock_and_unprep(st
+ }
+
+ /*
++ * This code converts an address to the Default Address Mode, that has non
++ * power of two page sizes. We must support this mode because it is the default
++ * mode supported by Xilinx tools, it can access the whole flash area and
++ * changing over to the Power-of-two mode is irreversible and corrupts the
++ * original data.
++ * Addr can safely be unsigned int, the biggest S3AN device is smaller than
++ * 4 MiB.
++ */
++static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
++{
++ unsigned int offset = addr;
++
++ offset %= nor->page_size;
++
++ return ((addr - offset) << 1) | offset;
++}
++
++/*
+ * Initiate the erasure of a single sector
+ */
+ static int spi_nor_erase_sector(struct spi_nor *nor, u32 addr)
+@@ -327,6 +370,9 @@ static int spi_nor_erase_sector(struct s
+ u8 buf[SPI_NOR_MAX_ADDR_WIDTH];
+ int i;
+
++ if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
++ addr = spi_nor_s3an_addr_convert(nor, addr);
++
+ if (nor->erase)
+ return nor->erase(nor, addr);
+
+@@ -368,7 +414,7 @@ static int spi_nor_erase(struct mtd_info
+ return ret;
+
+ /* whole-chip erase? */
+- if (len == mtd->size) {
++ if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
+ unsigned long timeout;
+
+ write_enable(nor);
+@@ -782,6 +828,19 @@ static int spi_nor_is_locked(struct mtd_
+ .addr_width = (_addr_width), \
+ .flags = (_flags),
+
++#define S3AN_INFO(_jedec_id, _n_sectors, _page_size) \
++ .id = { \
++ ((_jedec_id) >> 16) & 0xff, \
++ ((_jedec_id) >> 8) & 0xff, \
++ (_jedec_id) & 0xff \
++ }, \
++ .id_len = 3, \
++ .sector_size = (8*_page_size), \
++ .n_sectors = (_n_sectors), \
++ .page_size = _page_size, \
++ .addr_width = 3, \
++ .flags = SPI_NOR_NO_FR | SPI_S3AN,
++
+ /* NOTE: double check command sets and memory organization when you add
+ * more nor chips. This current list focusses on newer chips, which
+ * have been converging on command sets which including JEDEC ID.
+@@ -1014,6 +1073,13 @@ static const struct flash_info spi_nor_i
+ { "cat25c09", CAT25_INFO( 128, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "cat25c17", CAT25_INFO( 256, 8, 32, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+ { "cat25128", CAT25_INFO(2048, 8, 64, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
++
++ /* Xilinx S3AN Internal Flash */
++ { "3S50AN", S3AN_INFO(0x1f2200, 64, 264) },
++ { "3S200AN", S3AN_INFO(0x1f2400, 256, 264) },
++ { "3S400AN", S3AN_INFO(0x1f2400, 256, 264) },
++ { "3S700AN", S3AN_INFO(0x1f2500, 512, 264) },
++ { "3S1400AN", S3AN_INFO(0x1f2600, 512, 528) },
+ { },
+ };
+
+@@ -1054,7 +1120,12 @@ static int spi_nor_read(struct mtd_info
+ return ret;
+
+ while (len) {
+- ret = nor->read(nor, from, len, buf);
++ loff_t addr = from;
++
++ if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
++ addr = spi_nor_s3an_addr_convert(nor, addr);
++
++ ret = nor->read(nor, addr, len, buf);
+ if (ret == 0) {
+ /* We shouldn't see 0-length reads */
+ ret = -EIO;
+@@ -1175,8 +1246,23 @@ static int spi_nor_write(struct mtd_info
+
+ for (i = 0; i < len; ) {
+ ssize_t written;
++ loff_t addr = to + i;
+
+- page_offset = (to + i) & (nor->page_size - 1);
++ /*
++ * If page_size is a power of two, the offset can be quickly
++ * calculated with an AND operation. On the other cases we
++ * need to do a modulus operation (more expensive).
++ * Power of two numbers have only one bit set and we can use
++ * the instruction hweight32 to detect if we need to do a
++ * modulus (do_div()) or not.
++ */
++ if (hweight32(nor->page_size) == 1) {
++ page_offset = addr & (nor->page_size - 1);
++ } else {
++ uint64_t aux = addr;
++
++ page_offset = do_div(aux, nor->page_size);
++ }
+ WARN_ONCE(page_offset,
+ "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
+ page_offset);
+@@ -1184,8 +1270,11 @@ static int spi_nor_write(struct mtd_info
+ page_remain = min_t(size_t,
+ nor->page_size - page_offset, len - i);
+
++ if (nor->flags & SNOR_F_S3AN_ADDR_DEFAULT)
++ addr = spi_nor_s3an_addr_convert(nor, addr);
++
+ write_enable(nor);
+- ret = nor->write(nor, to + i, page_remain, buf + i);
++ ret = nor->write(nor, addr, page_remain, buf + i);
+ if (ret < 0)
+ goto write_err;
+ written = ret;
+@@ -1312,6 +1401,47 @@ static int spi_nor_check(struct spi_nor
+ return 0;
+ }
+
++static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
++{
++ int ret;
++ u8 val;
++
++ ret = nor->read_reg(nor, SPINOR_OP_XRDSR, &val, 1);
++ if (ret < 0) {
++ dev_err(nor->dev, "error %d reading XRDSR\n", (int) ret);
++ return ret;
++ }
++
++ nor->erase_opcode = SPINOR_OP_XSE;
++ nor->program_opcode = SPINOR_OP_XPP;
++ nor->read_opcode = SPINOR_OP_READ;
++ nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
++
++ /*
++ * This flashes have a page size of 264 or 528 bytes (known as
++ * Default addressing mode). It can be changed to a more standard
++ * Power of two mode where the page size is 256/512. This comes
++ * with a price: there is 3% less of space, the data is corrupted
++ * and the page size cannot be changed back to default addressing
++ * mode.
++ *
++ * The current addressing mode can be read from the XRDSR register
++ * and should not be changed, because is a destructive operation.
++ */
++ if (val & XSR_PAGESIZE) {
++ /* Flash in Power of 2 mode */
++ nor->page_size = (nor->page_size == 264) ? 256 : 512;
++ nor->mtd.writebufsize = nor->page_size;
++ nor->mtd.size = 8 * nor->page_size * info->n_sectors;
++ nor->mtd.erasesize = 8 * nor->page_size;
++ } else {
++ /* Flash in Default addressing mode */
++ nor->flags |= SNOR_F_S3AN_ADDR_DEFAULT;
++ }
++
++ return 0;
++}
++
+ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
+ {
+ const struct flash_info *info = NULL;
+@@ -1360,6 +1490,14 @@ int spi_nor_scan(struct spi_nor *nor, co
+ mutex_init(&nor->lock);
+
+ /*
++ * Make sure the XSR_RDY flag is set before calling
++ * spi_nor_wait_till_ready(). Xilinx S3AN share MFR
++ * with Atmel spi-nor
++ */
++ if (info->flags & SPI_S3AN)
++ nor->flags |= SNOR_F_READY_XSR_RDY;
++
++ /*
+ * Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
+ * with the software protection bits set
+ */
+@@ -1517,6 +1655,12 @@ int spi_nor_scan(struct spi_nor *nor, co
+
+ nor->read_dummy = spi_nor_read_dummy_cycles(nor);
+
++ if (info->flags & SPI_S3AN) {
++ ret = s3an_nor_scan(info, nor);
++ if (ret)
++ return ret;
++ }
++
+ dev_info(dev, "%s (%lld Kbytes)\n", info->name,
+ (long long)mtd->size >> 10);
+
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -68,6 +68,15 @@
+ #define SPINOR_OP_WRDI 0x04 /* Write disable */
+ #define SPINOR_OP_AAI_WP 0xad /* Auto address increment word program */
+
++/* Used for S3AN flashes only */
++#define SPINOR_OP_XSE 0x50 /* Sector erase */
++#define SPINOR_OP_XPP 0x82 /* Page program */
++#define SPINOR_OP_XRDSR 0xd7 /* Read status register */
++
++#define XSR_PAGESIZE BIT(0) /* Page size in Po2 or Linear */
++#define XSR_RDY BIT(7) /* Ready */
++
++
+ /* Used for Macronix and Winbond flashes. */
+ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
+ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
+@@ -119,6 +128,9 @@ enum spi_nor_ops {
+ enum spi_nor_option_flags {
+ SNOR_F_USE_FSR = BIT(0),
+ SNOR_F_HAS_SR_TB = BIT(1),
++ SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
++ SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
++ SNOR_F_READY_XSR_RDY = BIT(4),
+ };
+
+ /**
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0002-mtd-spi-nor-improve-macronix_quad_enable.patch b/target/linux/generic/backport-4.9/062-v4.11-0002-mtd-spi-nor-improve-macronix_quad_enable.patch
new file mode 100644
index 0000000000..bdd5e344d7
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0002-mtd-spi-nor-improve-macronix_quad_enable.patch
@@ -0,0 +1,28 @@
+From 1e99d0d51ec97bf48edd277658004ce030543d98 Mon Sep 17 00:00:00 2001
+From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Date: Tue, 6 Dec 2016 17:01:41 +0100
+Subject: [PATCH] mtd: spi-nor: improve macronix_quad_enable()
+
+The patch checks whether the Quad Enable bit is already set in the Status
+Register. If so, the function exits immediately with a successful return
+code.
+
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Reviewed-by: Jagan Teki <jagan@openedev.com>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1305,6 +1305,9 @@ static int macronix_quad_enable(struct s
+ val = read_sr(nor);
+ if (val < 0)
+ return val;
++ if (val & SR_QUAD_EN_MX)
++ return 0;
++
+ write_enable(nor);
+
+ write_sr(nor, val | SR_QUAD_EN_MX);
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0003-mtd-spi-nor-remove-WARN_ONCE-message-in-spi_nor_writ.patch b/target/linux/generic/backport-4.9/062-v4.11-0003-mtd-spi-nor-remove-WARN_ONCE-message-in-spi_nor_writ.patch
new file mode 100644
index 0000000000..731ef21bb8
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0003-mtd-spi-nor-remove-WARN_ONCE-message-in-spi_nor_writ.patch
@@ -0,0 +1,33 @@
+From dc176595bf184e89bf28fdf91cbc1d050dfe63b3 Mon Sep 17 00:00:00 2001
+From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Date: Tue, 6 Dec 2016 18:14:24 +0100
+Subject: [PATCH] mtd: spi-nor: remove WARN_ONCE() message in spi_nor_write()
+
+This patch removes the WARN_ONCE() test in spi_nor_write().
+This macro triggers the display of a warning message almost every time we
+use a UBI file-system because a write operation is performed at offset 64,
+which is in the middle of the SPI NOR memory page. This is a valid
+operation for ubifs.
+
+Hence this warning is pretty annoying and useless so we just remove it.
+
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Suggested-by: Richard Weinberger <richard@nod.at>
+Suggested-by: Andras Szemzo <szemzo.andras@gmail.com>
+Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1263,9 +1263,6 @@ static int spi_nor_write(struct mtd_info
+
+ page_offset = do_div(aux, nor->page_size);
+ }
+- WARN_ONCE(page_offset,
+- "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
+- page_offset);
+ /* the size of data remaining on the first page */
+ page_remain = min_t(size_t,
+ nor->page_size - page_offset, len - i);
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0004-mtd-spi-nor-rename-SPINOR_OP_-macros-of-the-4-byte-a.patch b/target/linux/generic/backport-4.9/062-v4.11-0004-mtd-spi-nor-rename-SPINOR_OP_-macros-of-the-4-byte-a.patch
new file mode 100644
index 0000000000..9bdce0493e
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0004-mtd-spi-nor-rename-SPINOR_OP_-macros-of-the-4-byte-a.patch
@@ -0,0 +1,187 @@
+From 05aba5763dcf35eddc58aaf99c9f16d19730e0a8 Mon Sep 17 00:00:00 2001
+From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Date: Thu, 27 Oct 2016 11:55:39 +0200
+Subject: [PATCH] mtd: spi-nor: rename SPINOR_OP_* macros of the 4-byte address
+ op codes
+
+This patch renames the SPINOR_OP_* macros of the 4-byte address
+instruction set so the new names all share a common pattern: the 4-byte
+address name is built from the 3-byte address name appending the "_4B"
+suffix.
+
+The patch also introduces new op codes to support other SPI protocols such
+as SPI 1-4-4 and SPI 1-2-2.
+
+This is a transitional patch and will help a later patch of spi-nor.c
+to automate the translation from the 3-byte address op codes into their
+4-byte address version.
+
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Acked-by: Mark Brown <broonie@kernel.org>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+---
+ drivers/mtd/devices/serial_flash_cmds.h | 7 -------
+ drivers/mtd/devices/st_spi_fsm.c | 28 ++++++++++++++--------------
+ drivers/mtd/spi-nor/spi-nor.c | 8 ++++----
+ drivers/spi/spi-bcm-qspi.c | 6 +++---
+ include/linux/mtd/spi-nor.h | 22 ++++++++++++++++------
+ 5 files changed, 37 insertions(+), 34 deletions(-)
+
+--- a/drivers/mtd/devices/serial_flash_cmds.h
++++ b/drivers/mtd/devices/serial_flash_cmds.h
+@@ -18,19 +18,12 @@
+ #define SPINOR_OP_RDVCR 0x85
+
+ /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
+-#define SPINOR_OP_READ_1_2_2 0xbb /* DUAL I/O READ */
+-#define SPINOR_OP_READ_1_4_4 0xeb /* QUAD I/O READ */
+-
+ #define SPINOR_OP_WRITE 0x02 /* PAGE PROGRAM */
+ #define SPINOR_OP_WRITE_1_1_2 0xa2 /* DUAL INPUT PROGRAM */
+ #define SPINOR_OP_WRITE_1_2_2 0xd2 /* DUAL INPUT EXT PROGRAM */
+ #define SPINOR_OP_WRITE_1_1_4 0x32 /* QUAD INPUT PROGRAM */
+ #define SPINOR_OP_WRITE_1_4_4 0x12 /* QUAD INPUT EXT PROGRAM */
+
+-/* READ commands with 32-bit addressing */
+-#define SPINOR_OP_READ4_1_2_2 0xbc
+-#define SPINOR_OP_READ4_1_4_4 0xec
+-
+ /* Configuration flags */
+ #define FLASH_FLAG_SINGLE 0x000000ff
+ #define FLASH_FLAG_READ_WRITE 0x00000001
+--- a/drivers/mtd/devices/st_spi_fsm.c
++++ b/drivers/mtd/devices/st_spi_fsm.c
+@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_c
+ * - 'FAST' variants configured for 8 dummy cycles (see note above.)
+ */
+ static struct seq_rw_config n25q_read4_configs[] = {
+- {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 0, 8},
+- {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
+- {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 0, 8},
+- {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
+- {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
+- {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
+- {0x00, 0, 0, 0, 0, 0x00, 0, 0},
++ {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
++ {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
++ {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
++ {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
++ {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
++ {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
++ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+ };
+
+ /*
+@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(
+ * entering a state that is incompatible with the SPIBoot Controller.
+ */
+ static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
+- {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4, 0, 4, 4, 0x00, 2, 4},
+- {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4, 0, 1, 4, 0x00, 0, 8},
+- {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2, 0, 2, 2, 0x00, 4, 0},
+- {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2, 0, 1, 2, 0x00, 0, 8},
+- {FLASH_FLAG_READ_FAST, SPINOR_OP_READ4_FAST, 0, 1, 1, 0x00, 0, 8},
+- {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4, 0, 1, 1, 0x00, 0, 0},
+- {0x00, 0, 0, 0, 0, 0x00, 0, 0},
++ {FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 2, 4},
++ {FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
++ {FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 4, 0},
++ {FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
++ {FLASH_FLAG_READ_FAST, SPINOR_OP_READ_FAST_4B, 0, 1, 1, 0x00, 0, 8},
++ {FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B, 0, 1, 1, 0x00, 0, 0},
++ {0x00, 0, 0, 0, 0, 0x00, 0, 0},
+ };
+
+ static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1625,16 +1625,16 @@ int spi_nor_scan(struct spi_nor *nor, co
+ /* Dedicated 4-byte command set */
+ switch (nor->flash_read) {
+ case SPI_NOR_QUAD:
+- nor->read_opcode = SPINOR_OP_READ4_1_1_4;
++ nor->read_opcode = SPINOR_OP_READ_1_1_4_4B;
+ break;
+ case SPI_NOR_DUAL:
+- nor->read_opcode = SPINOR_OP_READ4_1_1_2;
++ nor->read_opcode = SPINOR_OP_READ_1_1_2_4B;
+ break;
+ case SPI_NOR_FAST:
+- nor->read_opcode = SPINOR_OP_READ4_FAST;
++ nor->read_opcode = SPINOR_OP_READ_FAST_4B;
+ break;
+ case SPI_NOR_NORMAL:
+- nor->read_opcode = SPINOR_OP_READ4;
++ nor->read_opcode = SPINOR_OP_READ_4B;
+ break;
+ }
+ nor->program_opcode = SPINOR_OP_PP_4B;
+--- a/drivers/spi/spi-bcm-qspi.c
++++ b/drivers/spi/spi-bcm-qspi.c
+@@ -371,7 +371,7 @@ static int bcm_qspi_bspi_set_flex_mode(s
+ /* default mode, does not need flex_cmd */
+ flex_mode = 0;
+ else
+- command = SPINOR_OP_READ4_FAST;
++ command = SPINOR_OP_READ_FAST_4B;
+ break;
+ case SPI_NBITS_DUAL:
+ bpc = 0x00000001;
+@@ -384,7 +384,7 @@ static int bcm_qspi_bspi_set_flex_mode(s
+ } else {
+ command = SPINOR_OP_READ_1_1_2;
+ if (spans_4byte)
+- command = SPINOR_OP_READ4_1_1_2;
++ command = SPINOR_OP_READ_1_1_2_4B;
+ }
+ break;
+ case SPI_NBITS_QUAD:
+@@ -399,7 +399,7 @@ static int bcm_qspi_bspi_set_flex_mode(s
+ } else {
+ command = SPINOR_OP_READ_1_1_4;
+ if (spans_4byte)
+- command = SPINOR_OP_READ4_1_1_4;
++ command = SPINOR_OP_READ_1_1_4_4B;
+ }
+ break;
+ default:
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -43,9 +43,13 @@
+ #define SPINOR_OP_WRSR 0x01 /* Write status register 1 byte */
+ #define SPINOR_OP_READ 0x03 /* Read data bytes (low frequency) */
+ #define SPINOR_OP_READ_FAST 0x0b /* Read data bytes (high frequency) */
+-#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
+-#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
++#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual Output SPI) */
++#define SPINOR_OP_READ_1_2_2 0xbb /* Read data bytes (Dual I/O SPI) */
++#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad Output SPI) */
++#define SPINOR_OP_READ_1_4_4 0xeb /* Read data bytes (Quad I/O SPI) */
+ #define SPINOR_OP_PP 0x02 /* Page program (up to 256 bytes) */
++#define SPINOR_OP_PP_1_1_4 0x32 /* Quad page program */
++#define SPINOR_OP_PP_1_4_4 0x38 /* Quad page program */
+ #define SPINOR_OP_BE_4K 0x20 /* Erase 4KiB block */
+ #define SPINOR_OP_BE_4K_PMC 0xd7 /* Erase 4KiB block on PMC chips */
+ #define SPINOR_OP_BE_32K 0x52 /* Erase 32KiB block */
+@@ -56,11 +60,17 @@
+ #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */
+
+ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
+-#define SPINOR_OP_READ4 0x13 /* Read data bytes (low frequency) */
+-#define SPINOR_OP_READ4_FAST 0x0c /* Read data bytes (high frequency) */
+-#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
+-#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
++#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
++#define SPINOR_OP_READ_FAST_4B 0x0c /* Read data bytes (high frequency) */
++#define SPINOR_OP_READ_1_1_2_4B 0x3c /* Read data bytes (Dual Output SPI) */
++#define SPINOR_OP_READ_1_2_2_4B 0xbc /* Read data bytes (Dual I/O SPI) */
++#define SPINOR_OP_READ_1_1_4_4B 0x6c /* Read data bytes (Quad Output SPI) */
++#define SPINOR_OP_READ_1_4_4_4B 0xec /* Read data bytes (Quad I/O SPI) */
+ #define SPINOR_OP_PP_4B 0x12 /* Page program (up to 256 bytes) */
++#define SPINOR_OP_PP_1_1_4_4B 0x34 /* Quad page program */
++#define SPINOR_OP_PP_1_4_4_4B 0x3e /* Quad page program */
++#define SPINOR_OP_BE_4K_4B 0x21 /* Erase 4KiB block */
++#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */
+ #define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
+
+ /* Used for SST flashes only. */
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0005-mtd-spi-nor-add-a-stateless-method-to-support-memory.patch b/target/linux/generic/backport-4.9/062-v4.11-0005-mtd-spi-nor-add-a-stateless-method-to-support-memory.patch
new file mode 100644
index 0000000000..8e994e61be
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0005-mtd-spi-nor-add-a-stateless-method-to-support-memory.patch
@@ -0,0 +1,150 @@
+From 3274ba26f27becfc4193ec6e229288140651f240 Mon Sep 17 00:00:00 2001
+From: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Date: Thu, 27 Oct 2016 12:03:57 +0200
+Subject: [PATCH] mtd: spi-nor: add a stateless method to support memory size
+ above 128Mib
+
+This patch provides an alternative mean to support memory above 16MiB
+(128Mib) by replacing 3byte address op codes by their associated 4byte
+address versions.
+
+Using the dedicated 4byte address op codes doesn't change the internal
+state of the SPI NOR memory as opposed to using other means such as
+updating a Base Address Register (BAR) and sending command to enter/leave
+the 4byte mode.
+
+Hence when a CPU reset occurs, early bootloaders don't need to be aware
+of BAR value or 4byte mode being enabled: they can still access the first
+16MiB of the SPI NOR memory using the regular 3byte address op codes.
+
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+Tested-by: Vignesh R <vigneshr@ti.com>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 101 +++++++++++++++++++++++++++++++++---------
+ 1 file changed, 80 insertions(+), 21 deletions(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -81,6 +81,10 @@ struct flash_info {
+ * because it has the same value as
+ * ATMEL flashes)
+ */
++#define SPI_NOR_4B_OPCODES BIT(11) /*
++ * Use dedicated 4byte address op codes
++ * to support memory size above 128Mib.
++ */
+ };
+
+ #define JEDEC_MFR(info) ((info)->id[0])
+@@ -194,6 +198,78 @@ static inline struct spi_nor *mtd_to_spi
+ return mtd->priv;
+ }
+
++
++static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
++{
++ size_t i;
++
++ for (i = 0; i < size; i++)
++ if (table[i][0] == opcode)
++ return table[i][1];
++
++ /* No conversion found, keep input op code. */
++ return opcode;
++}
++
++static inline u8 spi_nor_convert_3to4_read(u8 opcode)
++{
++ static const u8 spi_nor_3to4_read[][2] = {
++ { SPINOR_OP_READ, SPINOR_OP_READ_4B },
++ { SPINOR_OP_READ_FAST, SPINOR_OP_READ_FAST_4B },
++ { SPINOR_OP_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B },
++ { SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
++ { SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
++ { SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
++ };
++
++ return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
++ ARRAY_SIZE(spi_nor_3to4_read));
++}
++
++static inline u8 spi_nor_convert_3to4_program(u8 opcode)
++{
++ static const u8 spi_nor_3to4_program[][2] = {
++ { SPINOR_OP_PP, SPINOR_OP_PP_4B },
++ { SPINOR_OP_PP_1_1_4, SPINOR_OP_PP_1_1_4_4B },
++ { SPINOR_OP_PP_1_4_4, SPINOR_OP_PP_1_4_4_4B },
++ };
++
++ return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
++ ARRAY_SIZE(spi_nor_3to4_program));
++}
++
++static inline u8 spi_nor_convert_3to4_erase(u8 opcode)
++{
++ static const u8 spi_nor_3to4_erase[][2] = {
++ { SPINOR_OP_BE_4K, SPINOR_OP_BE_4K_4B },
++ { SPINOR_OP_BE_32K, SPINOR_OP_BE_32K_4B },
++ { SPINOR_OP_SE, SPINOR_OP_SE_4B },
++ };
++
++ return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
++ ARRAY_SIZE(spi_nor_3to4_erase));
++}
++
++static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
++ const struct flash_info *info)
++{
++ /* Do some manufacturer fixups first */
++ switch (JEDEC_MFR(info)) {
++ case SNOR_MFR_SPANSION:
++ /* No small sector erase for 4-byte command set */
++ nor->erase_opcode = SPINOR_OP_SE;
++ nor->mtd.erasesize = info->sector_size;
++ break;
++
++ default:
++ break;
++ }
++
++ nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
++ nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
++ nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
++}
++
+ /* Enable/disable 4-byte addressing mode. */
+ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
+ int enable)
+@@ -1621,27 +1697,10 @@ int spi_nor_scan(struct spi_nor *nor, co
+ else if (mtd->size > 0x1000000) {
+ /* enable 4-byte addressing if the device exceeds 16MiB */
+ nor->addr_width = 4;
+- if (JEDEC_MFR(info) == SNOR_MFR_SPANSION) {
+- /* Dedicated 4-byte command set */
+- switch (nor->flash_read) {
+- case SPI_NOR_QUAD:
+- nor->read_opcode = SPINOR_OP_READ_1_1_4_4B;
+- break;
+- case SPI_NOR_DUAL:
+- nor->read_opcode = SPINOR_OP_READ_1_1_2_4B;
+- break;
+- case SPI_NOR_FAST:
+- nor->read_opcode = SPINOR_OP_READ_FAST_4B;
+- break;
+- case SPI_NOR_NORMAL:
+- nor->read_opcode = SPINOR_OP_READ_4B;
+- break;
+- }
+- nor->program_opcode = SPINOR_OP_PP_4B;
+- /* No small sector erase for 4-byte command set */
+- nor->erase_opcode = SPINOR_OP_SE_4B;
+- mtd->erasesize = info->sector_size;
+- } else
++ if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
++ info->flags & SPI_NOR_4B_OPCODES)
++ spi_nor_set_4byte_opcodes(nor, info);
++ else
+ set_4byte(nor, info, 1);
+ } else {
+ nor->addr_width = 3;
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0006-mtd-spi-nor-Add-lock-unlock-support-for-f25l32pa.patch b/target/linux/generic/backport-4.9/062-v4.11-0006-mtd-spi-nor-Add-lock-unlock-support-for-f25l32pa.patch
new file mode 100644
index 0000000000..be78682193
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0006-mtd-spi-nor-Add-lock-unlock-support-for-f25l32pa.patch
@@ -0,0 +1,26 @@
+From 252c36bb9c7b98b356f033d16ea83d20fb8b4d3e Mon Sep 17 00:00:00 2001
+From: Victor Shyba <victor1984@riseup.net>
+Date: Mon, 2 Jan 2017 22:34:30 -0300
+Subject: [PATCH] mtd: spi-nor: Add lock/unlock support for f25l32pa
+
+This chip has write protection enabled on power-up,
+so this flag is necessary to support write operations.
+
+Signed-off-by: Victor Shyba <victor1984@riseup.net>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -956,7 +956,7 @@ static const struct flash_info spi_nor_i
+ { "en25s64", INFO(0x1c3817, 0, 64 * 1024, 128, SECT_4K) },
+
+ /* ESMT */
+- { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K) },
++ { "f25l32pa", INFO(0x8c2016, 0, 64 * 1024, 64, SECT_4K | SPI_NOR_HAS_LOCK) },
+
+ /* Everspin */
+ { "mr25h256", CAT25_INFO( 32 * 1024, 1, 256, 2, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0007-mtd-spi-nor-Fix-S3AN-addressing-calculation.patch b/target/linux/generic/backport-4.9/062-v4.11-0007-mtd-spi-nor-Fix-S3AN-addressing-calculation.patch
new file mode 100644
index 0000000000..f8d0541428
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0007-mtd-spi-nor-Fix-S3AN-addressing-calculation.patch
@@ -0,0 +1,35 @@
+From 5f0e0758efddef5b06994a76d8c7f0b8a4c1daae Mon Sep 17 00:00:00 2001
+From: Ricardo Ribalda <ricardo.ribalda@gmail.com>
+Date: Wed, 18 Jan 2017 17:40:16 +0100
+Subject: [PATCH] mtd: spi-nor: Fix S3AN addressing calculation
+
+The page calculation under spi_nor_s3an_addr_convert() was wrong. On
+Default Address Mode we need to perform a divide by page_size.
+
+Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -431,11 +431,14 @@ static void spi_nor_unlock_and_unprep(st
+ */
+ static loff_t spi_nor_s3an_addr_convert(struct spi_nor *nor, unsigned int addr)
+ {
+- unsigned int offset = addr;
++ unsigned int offset;
++ unsigned int page;
+
+- offset %= nor->page_size;
++ offset = addr % nor->page_size;
++ page = addr / nor->page_size;
++ page <<= (nor->page_size > 512) ? 10 : 9;
+
+- return ((addr - offset) << 1) | offset;
++ return page | offset;
+ }
+
+ /*
diff --git a/target/linux/generic/backport-4.9/062-v4.11-0008-mtd-spi-nor-Add-support-for-gd25q16.patch b/target/linux/generic/backport-4.9/062-v4.11-0008-mtd-spi-nor-Add-support-for-gd25q16.patch
new file mode 100644
index 0000000000..5253957286
--- /dev/null
+++ b/target/linux/generic/backport-4.9/062-v4.11-0008-mtd-spi-nor-Add-support-for-gd25q16.patch
@@ -0,0 +1,28 @@
+From 4c5747a390acc9d1da3b332507c8bae7a8ddfc48 Mon Sep 17 00:00:00 2001
+From: Kamal Dasu <kdasu.kdev@gmail.com>
+Date: Fri, 20 Jan 2017 14:25:51 -0500
+Subject: [PATCH] mtd: spi-nor: Add support for gd25q16
+
+Add GigaDevice GD25Q16 (16M-bit) to supported list.
+
+Signed-off-by: Kamal Dasu <kdasu.kdev@gmail.com>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -971,6 +971,11 @@ static const struct flash_info spi_nor_i
+
+ /* GigaDevice */
+ {
++ "gd25q16", INFO(0xc84015, 0, 64 * 1024, 32,
++ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
++ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
++ },
++ {
+ "gd25q32", INFO(0xc84016, 0, 64 * 1024, 64,
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
diff --git a/target/linux/generic/backport-4.9/063-mtd-spi-nor-enable-stateless-4b-op-codes-for-mx25u25.patch b/target/linux/generic/backport-4.9/063-mtd-spi-nor-enable-stateless-4b-op-codes-for-mx25u25.patch
new file mode 100644
index 0000000000..339591500f
--- /dev/null
+++ b/target/linux/generic/backport-4.9/063-mtd-spi-nor-enable-stateless-4b-op-codes-for-mx25u25.patch
@@ -0,0 +1,29 @@
+From b0fcb4b413028376894feaaaf62bcb09ab1b52f2 Mon Sep 17 00:00:00 2001
+From: Mathias Kresin <dev@kresin.me>
+Date: Thu, 13 Apr 2017 09:23:54 +0200
+Subject: [PATCH] mtd: spi-nor: enable stateless 4b op codes for mx25u25635f
+
+All required stateless 4-byte op codes are supported by this flash
+chip. The stateless 4-byte support can't be autodetected due to a
+missing 4-byte Address Instruction Table in SFDP.
+
+Fixes hangs on reboot for SoCs expecting the flash chip in 3byte mode.
+
+Signed-off-by: Mathias Kresin <dev@kresin.me>
+Acked-by: Marek Vasut <marek.vasut@gmail.com>
+Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
+---
+ drivers/mtd/spi-nor/spi-nor.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -1017,7 +1017,7 @@ static const struct flash_info spi_nor_i
+ { "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
+ { "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
+ { "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
+- { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K) },
++ { "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
+ { "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
+ { "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
+ { "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
diff --git a/target/linux/generic/backport-4.9/064-v4.11-0001-mtd-introduce-function-max_bad_blocks.patch b/target/linux/generic/backport-4.9/064-v4.11-0001-mtd-introduce-function-max_bad_blocks.patch
new file mode 100644
index 0000000000..d2e582ef5b
--- /dev/null
+++ b/target/linux/generic/backport-4.9/064-v4.11-0001-mtd-introduce-function-max_bad_blocks.patch
@@ -0,0 +1,73 @@
+From 6080ef6e7c0a0592cbcca11200d879faf65e27d4 Mon Sep 17 00:00:00 2001
+From: Jeff Westfahl <jeff.westfahl@ni.com>
+Date: Tue, 10 Jan 2017 13:30:17 -0600
+Subject: [PATCH] mtd: introduce function max_bad_blocks
+
+If implemented, 'max_bad_blocks' returns the maximum number of bad
+blocks to reserve for a MTD. An implementation for NAND is coming soon.
+
+Signed-off-by: Jeff Westfahl <jeff.westfahl@ni.com>
+Signed-off-by: Zach Brown <zach.brown@ni.com>
+Acked-by: Boris Brezillon <boris.brezillon@free-electron.com>
+Acked-by: Brian Norris <computersforpeace@gmail.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 10 ++++++++++
+ include/linux/mtd/mtd.h | 13 +++++++++++++
+ 2 files changed, 23 insertions(+)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -349,6 +349,14 @@ static const struct mtd_ooblayout_ops pa
+ .free = part_ooblayout_free,
+ };
+
++static int part_max_bad_blocks(struct mtd_info *mtd, loff_t ofs, size_t len)
++{
++ struct mtd_part *part = mtd_to_part(mtd);
++
++ return part->master->_max_bad_blocks(part->master,
++ ofs + part->offset, len);
++}
++
+ static inline void free_partition(struct mtd_part *p)
+ {
+ kfree(p->mtd.name);
+@@ -475,6 +483,8 @@ static struct mtd_part *allocate_partiti
+ slave->mtd._block_isbad = part_block_isbad;
+ if (master->_block_markbad)
+ slave->mtd._block_markbad = part_block_markbad;
++ if (master->_max_bad_blocks)
++ slave->mtd._max_bad_blocks = part_max_bad_blocks;
+
+ if (master->_get_device)
+ slave->mtd._get_device = part_get_device;
+--- a/include/linux/mtd/mtd.h
++++ b/include/linux/mtd/mtd.h
+@@ -322,6 +322,7 @@ struct mtd_info {
+ int (*_block_isreserved) (struct mtd_info *mtd, loff_t ofs);
+ int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
+ int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
++ int (*_max_bad_blocks) (struct mtd_info *mtd, loff_t ofs, size_t len);
+ int (*_suspend) (struct mtd_info *mtd);
+ void (*_resume) (struct mtd_info *mtd);
+ void (*_reboot) (struct mtd_info *mtd);
+@@ -397,6 +398,18 @@ static inline int mtd_oobavail(struct mt
+ return ops->mode == MTD_OPS_AUTO_OOB ? mtd->oobavail : mtd->oobsize;
+ }
+
++static inline int mtd_max_bad_blocks(struct mtd_info *mtd,
++ loff_t ofs, size_t len)
++{
++ if (!mtd->_max_bad_blocks)
++ return -ENOTSUPP;
++
++ if (mtd->size < (len + ofs) || ofs < 0)
++ return -EINVAL;
++
++ return mtd->_max_bad_blocks(mtd, ofs, len);
++}
++
+ int mtd_wunit_to_pairing_info(struct mtd_info *mtd, int wunit,
+ struct mtd_pairing_info *info);
+ int mtd_pairing_info_to_wunit(struct mtd_info *mtd,
diff --git a/target/linux/generic/backport-4.9/064-v4.11-0002-mtd-Add-partition-device-node-to-mtd-partition-devic.patch b/target/linux/generic/backport-4.9/064-v4.11-0002-mtd-Add-partition-device-node-to-mtd-partition-devic.patch
new file mode 100644
index 0000000000..ea68fc474f
--- /dev/null
+++ b/target/linux/generic/backport-4.9/064-v4.11-0002-mtd-Add-partition-device-node-to-mtd-partition-devic.patch
@@ -0,0 +1,50 @@
+From 42e9401bd1467d22c4dc4d2c637347b874e6a80b Mon Sep 17 00:00:00 2001
+From: Sascha Hauer <s.hauer@pengutronix.de>
+Date: Thu, 9 Feb 2017 11:50:24 +0100
+Subject: [PATCH] mtd: Add partition device node to mtd partition devices
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The user visible change here is that mtd partitions get an of_node link
+in sysfs.
+
+Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 1 +
+ drivers/mtd/ofpart.c | 1 +
+ include/linux/mtd/partitions.h | 1 +
+ 3 files changed, 3 insertions(+)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -432,6 +432,7 @@ static struct mtd_part *allocate_partiti
+ slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
+ &master->dev :
+ master->dev.parent;
++ slave->mtd.dev.of_node = part->of_node;
+
+ slave->mtd._read = part_read;
+ slave->mtd._write = part_write;
+--- a/drivers/mtd/ofpart.c
++++ b/drivers/mtd/ofpart.c
+@@ -108,6 +108,7 @@ 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);
++ parts[i].of_node = pp;
+
+ partname = of_get_property(pp, "label", &len);
+ if (!partname)
+--- a/include/linux/mtd/partitions.h
++++ b/include/linux/mtd/partitions.h
+@@ -41,6 +41,7 @@ struct mtd_partition {
+ uint64_t size; /* partition size */
+ uint64_t offset; /* offset within the master MTD space */
+ uint32_t mask_flags; /* master MTD flags to mask out for this partition */
++ struct device_node *of_node;
+ };
+
+ #define MTDPART_OFS_RETAIN (-3)
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0001-mtd-handle-partitioning-on-devices-with-0-erasesize.patch b/target/linux/generic/backport-4.9/065-v4.13-0001-mtd-handle-partitioning-on-devices-with-0-erasesize.patch
new file mode 100644
index 0000000000..d912811850
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0001-mtd-handle-partitioning-on-devices-with-0-erasesize.patch
@@ -0,0 +1,77 @@
+From 1eeef2d7483a7e3f8d2dd2a5b9939b3b814dc549 Mon Sep 17 00:00:00 2001
+From: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Date: Fri, 9 Jun 2017 15:58:31 +1200
+Subject: [PATCH] mtd: handle partitioning on devices with 0 erasesize
+
+erasesize is meaningful for flash devices but for SRAM there is no
+concept of an erase block so erasesize is set to 0. When partitioning
+these devices instead of ensuring partitions fall on erasesize
+boundaries we ensure they fall on writesize boundaries.
+
+Helped-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 26 +++++++++++++++++---------
+ 1 file changed, 17 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -393,8 +393,12 @@ static struct mtd_part *allocate_partiti
+ const struct mtd_partition *part, int partno,
+ uint64_t cur_offset)
+ {
++ int wr_alignment = (master->flags & MTD_NO_ERASE) ? master->writesize:
++ master->erasesize;
+ struct mtd_part *slave;
++ u32 remainder;
+ char *name;
++ u64 tmp;
+
+ /* allocate the partition structure */
+ slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+@@ -499,10 +503,11 @@ static struct mtd_part *allocate_partiti
+ if (slave->offset == MTDPART_OFS_APPEND)
+ slave->offset = cur_offset;
+ if (slave->offset == MTDPART_OFS_NXTBLK) {
++ tmp = cur_offset;
+ slave->offset = cur_offset;
+- if (mtd_mod_by_eb(cur_offset, master) != 0) {
+- /* Round up to next erasesize */
+- slave->offset = (mtd_div_by_eb(cur_offset, master) + 1) * master->erasesize;
++ remainder = do_div(tmp, wr_alignment);
++ if (remainder) {
++ slave->offset += wr_alignment - remainder;
+ printk(KERN_NOTICE "Moving partition %d: "
+ "0x%012llx -> 0x%012llx\n", partno,
+ (unsigned long long)cur_offset, (unsigned long long)slave->offset);
+@@ -567,19 +572,22 @@ static struct mtd_part *allocate_partiti
+ slave->mtd.erasesize = master->erasesize;
+ }
+
+- if ((slave->mtd.flags & MTD_WRITEABLE) &&
+- mtd_mod_by_eb(slave->offset, &slave->mtd)) {
++ tmp = slave->offset;
++ remainder = do_div(tmp, wr_alignment);
++ if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
+ /* Doesn't start on a boundary of major erase size */
+ /* FIXME: Let it be writable if it is on a boundary of
+ * _minor_ erase size though */
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase block boundary -- force read-only\n",
++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't start on an erase/write block boundary -- force read-only\n",
+ part->name);
+ }
+- if ((slave->mtd.flags & MTD_WRITEABLE) &&
+- mtd_mod_by_eb(slave->mtd.size, &slave->mtd)) {
++
++ tmp = slave->mtd.size;
++ remainder = do_div(tmp, wr_alignment);
++ if ((slave->mtd.flags & MTD_WRITEABLE) && remainder) {
+ slave->mtd.flags &= ~MTD_WRITEABLE;
+- printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase block -- force read-only\n",
++ printk(KERN_WARNING"mtd: partition \"%s\" doesn't end on an erase/write block -- force read-only\n",
+ part->name);
+ }
+
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0002-mtd-partitions-factor-out-code-calling-parser.patch b/target/linux/generic/backport-4.9/065-v4.13-0002-mtd-partitions-factor-out-code-calling-parser.patch
new file mode 100644
index 0000000000..d312e081a9
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0002-mtd-partitions-factor-out-code-calling-parser.patch
@@ -0,0 +1,68 @@
+From 01f9c7240a900d5676a8496496f2974dd36996b1 Mon Sep 17 00:00:00 2001
+From: Brian Norris <computersforpeace@gmail.com>
+Date: Tue, 23 May 2017 07:30:20 +0200
+Subject: [PATCH] mtd: partitions: factor out code calling parser
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This code is going to be reused for parsers matched using OF so let's
+factor it out to make this easier.
+
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 33 ++++++++++++++++++++++++---------
+ 1 file changed, 24 insertions(+), 9 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -807,6 +807,27 @@ static const char * const default_mtd_pa
+ NULL
+ };
+
++static int mtd_part_do_parse(struct mtd_part_parser *parser,
++ struct mtd_info *master,
++ struct mtd_partitions *pparts,
++ struct mtd_part_parser_data *data)
++{
++ int ret;
++
++ ret = (*parser->parse_fn)(master, &pparts->parts, data);
++ pr_debug("%s: parser %s: %i\n", master->name, parser->name, ret);
++ if (ret <= 0)
++ return ret;
++
++ pr_notice("%d %s partitions found on MTD device %s\n", ret,
++ parser->name, master->name);
++
++ pparts->nr_parts = ret;
++ pparts->parser = parser;
++
++ return ret;
++}
++
+ /**
+ * parse_mtd_partitions - parse MTD partitions
+ * @master: the master partition (describes whole MTD device)
+@@ -847,16 +868,10 @@ int parse_mtd_partitions(struct mtd_info
+ parser ? parser->name : NULL);
+ if (!parser)
+ continue;
+- ret = (*parser->parse_fn)(master, &pparts->parts, data);
+- pr_debug("%s: parser %s: %i\n",
+- master->name, parser->name, ret);
+- if (ret > 0) {
+- printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n",
+- ret, parser->name, master->name);
+- pparts->nr_parts = ret;
+- pparts->parser = parser;
++ ret = mtd_part_do_parse(parser, master, pparts, data);
++ /* Found partitions! */
++ if (ret > 0)
+ return 0;
+- }
+ mtd_part_parser_put(parser);
+ /*
+ * Stash the first error we see; only report it if no parser
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0003-mtd-partitions-add-helper-for-deleting-partition.patch b/target/linux/generic/backport-4.9/065-v4.13-0003-mtd-partitions-add-helper-for-deleting-partition.patch
new file mode 100644
index 0000000000..d93f4ba4b5
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0003-mtd-partitions-add-helper-for-deleting-partition.patch
@@ -0,0 +1,119 @@
+From 08263a9ae664b24fa777d20b365601534842b236 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 21 Jun 2017 08:26:42 +0200
+Subject: [PATCH] mtd: partitions: add helper for deleting partition
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+There are two similar functions handling deletion. One handles single
+partition and another the whole MTD flash device. They share (duplicate)
+some code so it makes sense to add a small helper for that part.
+
+Function del_mtd_partitions has been moved a bit to keep all deleting
+stuff together.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 75 +++++++++++++++++++++++++++++----------------------
+ 1 file changed, 43 insertions(+), 32 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -363,32 +363,6 @@ static inline void free_partition(struct
+ kfree(p);
+ }
+
+-/*
+- * This function unregisters and destroy all slave MTD objects which are
+- * attached to the given master MTD object.
+- */
+-
+-int del_mtd_partitions(struct mtd_info *master)
+-{
+- struct mtd_part *slave, *next;
+- int ret, err = 0;
+-
+- mutex_lock(&mtd_partitions_mutex);
+- list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+- if (slave->master == master) {
+- ret = del_mtd_device(&slave->mtd);
+- if (ret < 0) {
+- err = ret;
+- continue;
+- }
+- list_del(&slave->list);
+- free_partition(slave);
+- }
+- mutex_unlock(&mtd_partitions_mutex);
+-
+- return err;
+-}
+-
+ static struct mtd_part *allocate_partition(struct mtd_info *master,
+ const struct mtd_partition *part, int partno,
+ uint64_t cur_offset)
+@@ -675,6 +649,48 @@ int mtd_add_partition(struct mtd_info *m
+ }
+ EXPORT_SYMBOL_GPL(mtd_add_partition);
+
++/**
++ * __mtd_del_partition - delete MTD partition
++ *
++ * @priv: internal MTD struct for partition to be deleted
++ *
++ * This function must be called with the partitions mutex locked.
++ */
++static int __mtd_del_partition(struct mtd_part *priv)
++{
++ int err;
++
++ err = del_mtd_device(&priv->mtd);
++ if (err)
++ return err;
++
++ list_del(&priv->list);
++ free_partition(priv);
++
++ return 0;
++}
++
++/*
++ * This function unregisters and destroy all slave MTD objects which are
++ * attached to the given master MTD object.
++ */
++int del_mtd_partitions(struct mtd_info *master)
++{
++ struct mtd_part *slave, *next;
++ int ret, err = 0;
++
++ mutex_lock(&mtd_partitions_mutex);
++ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
++ if (slave->master == master) {
++ ret = __mtd_del_partition(slave);
++ if (ret < 0)
++ err = ret;
++ }
++ mutex_unlock(&mtd_partitions_mutex);
++
++ return err;
++}
++
+ int mtd_del_partition(struct mtd_info *master, int partno)
+ {
+ struct mtd_part *slave, *next;
+@@ -686,12 +702,7 @@ int mtd_del_partition(struct mtd_info *m
+ (slave->mtd.index == partno)) {
+ sysfs_remove_files(&slave->mtd.dev.kobj,
+ mtd_partition_attrs);
+- ret = del_mtd_device(&slave->mtd);
+- if (ret < 0)
+- break;
+-
+- list_del(&slave->list);
+- free_partition(slave);
++ ret = __mtd_del_partition(slave);
+ break;
+ }
+ mutex_unlock(&mtd_partitions_mutex);
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0004-mtd-partitions-remove-sysfs-files-when-deleting-all-.patch b/target/linux/generic/backport-4.9/065-v4.13-0004-mtd-partitions-remove-sysfs-files-when-deleting-all-.patch
new file mode 100644
index 0000000000..0f1b502b70
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0004-mtd-partitions-remove-sysfs-files-when-deleting-all-.patch
@@ -0,0 +1,45 @@
+From c5ceaba74083daf619bdb34d4871e297a177eebf Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 21 Jun 2017 08:26:43 +0200
+Subject: [PATCH] mtd: partitions: remove sysfs files when deleting all
+ master's partitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When support for sysfs "offset" file was added it missed to update the
+del_mtd_partitions function. It deletes partitions just like
+mtd_del_partition does so both should also take care of removing sysfs
+files.
+
+This change moves sysfs_remove_files call to the shared function to fix
+this issue.
+
+Fixes: a62c24d755291 ("mtd: part: Add sysfs variable for offset of partition")
+Cc: Dan Ehrenberg <dehrenberg@chromium.org>
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -660,6 +660,8 @@ static int __mtd_del_partition(struct mt
+ {
+ int err;
+
++ sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs);
++
+ err = del_mtd_device(&priv->mtd);
+ if (err)
+ return err;
+@@ -700,8 +702,6 @@ int mtd_del_partition(struct mtd_info *m
+ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+ if ((slave->master == master) &&
+ (slave->mtd.index == partno)) {
+- sysfs_remove_files(&slave->mtd.dev.kobj,
+- mtd_partition_attrs);
+ ret = __mtd_del_partition(slave);
+ break;
+ }
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0005-mtd-partitions-rename-master-to-the-parent-where-app.patch b/target/linux/generic/backport-4.9/065-v4.13-0005-mtd-partitions-rename-master-to-the-parent-where-app.patch
new file mode 100644
index 0000000000..7951227654
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0005-mtd-partitions-rename-master-to-the-parent-where-app.patch
@@ -0,0 +1,606 @@
+From 0a9d72b69da6d8dae1abd7990c6c4c749846ef3e Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 21 Jun 2017 08:26:44 +0200
+Subject: [PATCH] mtd: partitions: rename "master" to the "parent" where
+ appropriate
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This prepares mtd subsystem for the new feature: subpartitions. In some
+cases flash device partition can be a container with extra subpartitions
+(volumes).
+
+So far there was a flat structure implemented. One master (flash device)
+could be partitioned into few partitions. Every partition got its master
+and it was enough to get things running.
+
+To support subpartitions we need to store pointer to the parent for each
+partition. This is required to implement more natural tree structure and
+handle all recursion and offsets calculation.
+
+To make code consistent this patch renamed "master" to the "parent" in
+places where we can be dealing with subpartitions.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 204 ++++++++++++++++++++++++++------------------------
+ 1 file changed, 105 insertions(+), 99 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -37,10 +37,16 @@
+ static LIST_HEAD(mtd_partitions);
+ static DEFINE_MUTEX(mtd_partitions_mutex);
+
+-/* Our partition node structure */
++/**
++ * struct mtd_part - our partition node structure
++ *
++ * @mtd: struct holding partition details
++ * @parent: parent mtd - flash device or another partition
++ * @offset: partition offset relative to the *flash device*
++ */
+ struct mtd_part {
+ struct mtd_info mtd;
+- struct mtd_info *master;
++ struct mtd_info *parent;
+ uint64_t offset;
+ struct list_head list;
+ };
+@@ -67,15 +73,15 @@ static int part_read(struct mtd_info *mt
+ struct mtd_ecc_stats stats;
+ int res;
+
+- stats = part->master->ecc_stats;
+- res = part->master->_read(part->master, from + part->offset, len,
++ stats = part->parent->ecc_stats;
++ res = part->parent->_read(part->parent, from + part->offset, len,
+ retlen, buf);
+ if (unlikely(mtd_is_eccerr(res)))
+ mtd->ecc_stats.failed +=
+- part->master->ecc_stats.failed - stats.failed;
++ part->parent->ecc_stats.failed - stats.failed;
+ else
+ mtd->ecc_stats.corrected +=
+- part->master->ecc_stats.corrected - stats.corrected;
++ part->parent->ecc_stats.corrected - stats.corrected;
+ return res;
+ }
+
+@@ -84,7 +90,7 @@ static int part_point(struct mtd_info *m
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+
+- return part->master->_point(part->master, from + part->offset, len,
++ return part->parent->_point(part->parent, from + part->offset, len,
+ retlen, virt, phys);
+ }
+
+@@ -92,7 +98,7 @@ static int part_unpoint(struct mtd_info
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+
+- return part->master->_unpoint(part->master, from + part->offset, len);
++ return part->parent->_unpoint(part->parent, from + part->offset, len);
+ }
+
+ static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
+@@ -103,7 +109,7 @@ static unsigned long part_get_unmapped_a
+ struct mtd_part *part = mtd_to_part(mtd);
+
+ offset += part->offset;
+- return part->master->_get_unmapped_area(part->master, len, offset,
++ return part->parent->_get_unmapped_area(part->parent, len, offset,
+ flags);
+ }
+
+@@ -132,7 +138,7 @@ static int part_read_oob(struct mtd_info
+ return -EINVAL;
+ }
+
+- res = part->master->_read_oob(part->master, from + part->offset, ops);
++ res = part->parent->_read_oob(part->parent, from + part->offset, ops);
+ if (unlikely(res)) {
+ if (mtd_is_bitflip(res))
+ mtd->ecc_stats.corrected++;
+@@ -146,7 +152,7 @@ static int part_read_user_prot_reg(struc
+ size_t len, size_t *retlen, u_char *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_read_user_prot_reg(part->master, from, len,
++ return part->parent->_read_user_prot_reg(part->parent, from, len,
+ retlen, buf);
+ }
+
+@@ -154,7 +160,7 @@ static int part_get_user_prot_info(struc
+ size_t *retlen, struct otp_info *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_get_user_prot_info(part->master, len, retlen,
++ return part->parent->_get_user_prot_info(part->parent, len, retlen,
+ buf);
+ }
+
+@@ -162,7 +168,7 @@ static int part_read_fact_prot_reg(struc
+ size_t len, size_t *retlen, u_char *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_read_fact_prot_reg(part->master, from, len,
++ return part->parent->_read_fact_prot_reg(part->parent, from, len,
+ retlen, buf);
+ }
+
+@@ -170,7 +176,7 @@ static int part_get_fact_prot_info(struc
+ size_t *retlen, struct otp_info *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_get_fact_prot_info(part->master, len, retlen,
++ return part->parent->_get_fact_prot_info(part->parent, len, retlen,
+ buf);
+ }
+
+@@ -178,7 +184,7 @@ static int part_write(struct mtd_info *m
+ size_t *retlen, const u_char *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_write(part->master, to + part->offset, len,
++ return part->parent->_write(part->parent, to + part->offset, len,
+ retlen, buf);
+ }
+
+@@ -186,7 +192,7 @@ static int part_panic_write(struct mtd_i
+ size_t *retlen, const u_char *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_panic_write(part->master, to + part->offset, len,
++ return part->parent->_panic_write(part->parent, to + part->offset, len,
+ retlen, buf);
+ }
+
+@@ -199,14 +205,14 @@ static int part_write_oob(struct mtd_inf
+ return -EINVAL;
+ if (ops->datbuf && to + ops->len > mtd->size)
+ return -EINVAL;
+- return part->master->_write_oob(part->master, to + part->offset, ops);
++ return part->parent->_write_oob(part->parent, to + part->offset, ops);
+ }
+
+ static int part_write_user_prot_reg(struct mtd_info *mtd, loff_t from,
+ size_t len, size_t *retlen, u_char *buf)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_write_user_prot_reg(part->master, from, len,
++ return part->parent->_write_user_prot_reg(part->parent, from, len,
+ retlen, buf);
+ }
+
+@@ -214,14 +220,14 @@ static int part_lock_user_prot_reg(struc
+ size_t len)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_lock_user_prot_reg(part->master, from, len);
++ return part->parent->_lock_user_prot_reg(part->parent, from, len);
+ }
+
+ static int part_writev(struct mtd_info *mtd, const struct kvec *vecs,
+ unsigned long count, loff_t to, size_t *retlen)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_writev(part->master, vecs, count,
++ return part->parent->_writev(part->parent, vecs, count,
+ to + part->offset, retlen);
+ }
+
+@@ -231,7 +237,7 @@ static int part_erase(struct mtd_info *m
+ int ret;
+
+ instr->addr += part->offset;
+- ret = part->master->_erase(part->master, instr);
++ ret = part->parent->_erase(part->parent, instr);
+ if (ret) {
+ if (instr->fail_addr != MTD_FAIL_ADDR_UNKNOWN)
+ instr->fail_addr -= part->offset;
+@@ -257,51 +263,51 @@ EXPORT_SYMBOL_GPL(mtd_erase_callback);
+ static int part_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_lock(part->master, ofs + part->offset, len);
++ return part->parent->_lock(part->parent, ofs + part->offset, len);
+ }
+
+ static int part_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_unlock(part->master, ofs + part->offset, len);
++ return part->parent->_unlock(part->parent, ofs + part->offset, len);
+ }
+
+ static int part_is_locked(struct mtd_info *mtd, loff_t ofs, uint64_t len)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_is_locked(part->master, ofs + part->offset, len);
++ return part->parent->_is_locked(part->parent, ofs + part->offset, len);
+ }
+
+ static void part_sync(struct mtd_info *mtd)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- part->master->_sync(part->master);
++ part->parent->_sync(part->parent);
+ }
+
+ static int part_suspend(struct mtd_info *mtd)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_suspend(part->master);
++ return part->parent->_suspend(part->parent);
+ }
+
+ static void part_resume(struct mtd_info *mtd)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- part->master->_resume(part->master);
++ part->parent->_resume(part->parent);
+ }
+
+ static int part_block_isreserved(struct mtd_info *mtd, loff_t ofs)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+ ofs += part->offset;
+- return part->master->_block_isreserved(part->master, ofs);
++ return part->parent->_block_isreserved(part->parent, ofs);
+ }
+
+ static int part_block_isbad(struct mtd_info *mtd, loff_t ofs)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+ ofs += part->offset;
+- return part->master->_block_isbad(part->master, ofs);
++ return part->parent->_block_isbad(part->parent, ofs);
+ }
+
+ static int part_block_markbad(struct mtd_info *mtd, loff_t ofs)
+@@ -310,7 +316,7 @@ static int part_block_markbad(struct mtd
+ int res;
+
+ ofs += part->offset;
+- res = part->master->_block_markbad(part->master, ofs);
++ res = part->parent->_block_markbad(part->parent, ofs);
+ if (!res)
+ mtd->ecc_stats.badblocks++;
+ return res;
+@@ -319,13 +325,13 @@ static int part_block_markbad(struct mtd
+ static int part_get_device(struct mtd_info *mtd)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- return part->master->_get_device(part->master);
++ return part->parent->_get_device(part->parent);
+ }
+
+ static void part_put_device(struct mtd_info *mtd)
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+- part->master->_put_device(part->master);
++ part->parent->_put_device(part->parent);
+ }
+
+ static int part_ooblayout_ecc(struct mtd_info *mtd, int section,
+@@ -333,7 +339,7 @@ static int part_ooblayout_ecc(struct mtd
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+
+- return mtd_ooblayout_ecc(part->master, section, oobregion);
++ return mtd_ooblayout_ecc(part->parent, section, oobregion);
+ }
+
+ static int part_ooblayout_free(struct mtd_info *mtd, int section,
+@@ -341,7 +347,7 @@ static int part_ooblayout_free(struct mt
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+
+- return mtd_ooblayout_free(part->master, section, oobregion);
++ return mtd_ooblayout_free(part->parent, section, oobregion);
+ }
+
+ static const struct mtd_ooblayout_ops part_ooblayout_ops = {
+@@ -353,7 +359,7 @@ static int part_max_bad_blocks(struct mt
+ {
+ struct mtd_part *part = mtd_to_part(mtd);
+
+- return part->master->_max_bad_blocks(part->master,
++ return part->parent->_max_bad_blocks(part->parent,
+ ofs + part->offset, len);
+ }
+
+@@ -363,12 +369,12 @@ static inline void free_partition(struct
+ kfree(p);
+ }
+
+-static struct mtd_part *allocate_partition(struct mtd_info *master,
++static struct mtd_part *allocate_partition(struct mtd_info *parent,
+ const struct mtd_partition *part, int partno,
+ uint64_t cur_offset)
+ {
+- int wr_alignment = (master->flags & MTD_NO_ERASE) ? master->writesize:
+- master->erasesize;
++ int wr_alignment = (parent->flags & MTD_NO_ERASE) ? parent->writesize:
++ parent->erasesize;
+ struct mtd_part *slave;
+ u32 remainder;
+ char *name;
+@@ -379,25 +385,25 @@ static struct mtd_part *allocate_partiti
+ name = kstrdup(part->name, GFP_KERNEL);
+ if (!name || !slave) {
+ printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",
+- master->name);
++ parent->name);
+ kfree(name);
+ kfree(slave);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* set up the MTD object for this partition */
+- slave->mtd.type = master->type;
+- slave->mtd.flags = master->flags & ~part->mask_flags;
++ slave->mtd.type = parent->type;
++ slave->mtd.flags = parent->flags & ~part->mask_flags;
+ slave->mtd.size = part->size;
+- slave->mtd.writesize = master->writesize;
+- slave->mtd.writebufsize = master->writebufsize;
+- slave->mtd.oobsize = master->oobsize;
+- slave->mtd.oobavail = master->oobavail;
+- slave->mtd.subpage_sft = master->subpage_sft;
+- slave->mtd.pairing = master->pairing;
++ slave->mtd.writesize = parent->writesize;
++ slave->mtd.writebufsize = parent->writebufsize;
++ slave->mtd.oobsize = parent->oobsize;
++ slave->mtd.oobavail = parent->oobavail;
++ slave->mtd.subpage_sft = parent->subpage_sft;
++ slave->mtd.pairing = parent->pairing;
+
+ slave->mtd.name = name;
+- slave->mtd.owner = master->owner;
++ slave->mtd.owner = parent->owner;
+
+ /* NOTE: Historically, we didn't arrange MTDs as a tree out of
+ * concern for showing the same data in multiple partitions.
+@@ -408,70 +414,70 @@ static struct mtd_part *allocate_partiti
+ * distinguish between the master and the partition in sysfs.
+ */
+ slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
+- &master->dev :
+- master->dev.parent;
++ &parent->dev :
++ parent->dev.parent;
+ slave->mtd.dev.of_node = part->of_node;
+
+ slave->mtd._read = part_read;
+ slave->mtd._write = part_write;
+
+- if (master->_panic_write)
++ if (parent->_panic_write)
+ slave->mtd._panic_write = part_panic_write;
+
+- if (master->_point && master->_unpoint) {
++ if (parent->_point && parent->_unpoint) {
+ slave->mtd._point = part_point;
+ slave->mtd._unpoint = part_unpoint;
+ }
+
+- if (master->_get_unmapped_area)
++ if (parent->_get_unmapped_area)
+ slave->mtd._get_unmapped_area = part_get_unmapped_area;
+- if (master->_read_oob)
++ if (parent->_read_oob)
+ slave->mtd._read_oob = part_read_oob;
+- if (master->_write_oob)
++ if (parent->_write_oob)
+ slave->mtd._write_oob = part_write_oob;
+- if (master->_read_user_prot_reg)
++ if (parent->_read_user_prot_reg)
+ slave->mtd._read_user_prot_reg = part_read_user_prot_reg;
+- if (master->_read_fact_prot_reg)
++ if (parent->_read_fact_prot_reg)
+ slave->mtd._read_fact_prot_reg = part_read_fact_prot_reg;
+- if (master->_write_user_prot_reg)
++ if (parent->_write_user_prot_reg)
+ slave->mtd._write_user_prot_reg = part_write_user_prot_reg;
+- if (master->_lock_user_prot_reg)
++ if (parent->_lock_user_prot_reg)
+ slave->mtd._lock_user_prot_reg = part_lock_user_prot_reg;
+- if (master->_get_user_prot_info)
++ if (parent->_get_user_prot_info)
+ slave->mtd._get_user_prot_info = part_get_user_prot_info;
+- if (master->_get_fact_prot_info)
++ if (parent->_get_fact_prot_info)
+ slave->mtd._get_fact_prot_info = part_get_fact_prot_info;
+- if (master->_sync)
++ if (parent->_sync)
+ slave->mtd._sync = part_sync;
+- if (!partno && !master->dev.class && master->_suspend &&
+- master->_resume) {
++ if (!partno && !parent->dev.class && parent->_suspend &&
++ parent->_resume) {
+ slave->mtd._suspend = part_suspend;
+ slave->mtd._resume = part_resume;
+ }
+- if (master->_writev)
++ if (parent->_writev)
+ slave->mtd._writev = part_writev;
+- if (master->_lock)
++ if (parent->_lock)
+ slave->mtd._lock = part_lock;
+- if (master->_unlock)
++ if (parent->_unlock)
+ slave->mtd._unlock = part_unlock;
+- if (master->_is_locked)
++ if (parent->_is_locked)
+ slave->mtd._is_locked = part_is_locked;
+- if (master->_block_isreserved)
++ if (parent->_block_isreserved)
+ slave->mtd._block_isreserved = part_block_isreserved;
+- if (master->_block_isbad)
++ if (parent->_block_isbad)
+ slave->mtd._block_isbad = part_block_isbad;
+- if (master->_block_markbad)
++ if (parent->_block_markbad)
+ slave->mtd._block_markbad = part_block_markbad;
+- if (master->_max_bad_blocks)
++ if (parent->_max_bad_blocks)
+ slave->mtd._max_bad_blocks = part_max_bad_blocks;
+
+- if (master->_get_device)
++ if (parent->_get_device)
+ slave->mtd._get_device = part_get_device;
+- if (master->_put_device)
++ if (parent->_put_device)
+ slave->mtd._put_device = part_put_device;
+
+ slave->mtd._erase = part_erase;
+- slave->master = master;
++ slave->parent = parent;
+ slave->offset = part->offset;
+
+ if (slave->offset == MTDPART_OFS_APPEND)
+@@ -489,25 +495,25 @@ static struct mtd_part *allocate_partiti
+ }
+ if (slave->offset == MTDPART_OFS_RETAIN) {
+ slave->offset = cur_offset;
+- if (master->size - slave->offset >= slave->mtd.size) {
+- slave->mtd.size = master->size - slave->offset
++ if (parent->size - slave->offset >= slave->mtd.size) {
++ slave->mtd.size = parent->size - slave->offset
+ - slave->mtd.size;
+ } else {
+ printk(KERN_ERR "mtd partition \"%s\" doesn't have enough space: %#llx < %#llx, disabled\n",
+- part->name, master->size - slave->offset,
++ part->name, parent->size - slave->offset,
+ slave->mtd.size);
+ /* register to preserve ordering */
+ goto out_register;
+ }
+ }
+ if (slave->mtd.size == MTDPART_SIZ_FULL)
+- slave->mtd.size = master->size - slave->offset;
++ slave->mtd.size = parent->size - slave->offset;
+
+ printk(KERN_NOTICE "0x%012llx-0x%012llx : \"%s\"\n", (unsigned long long)slave->offset,
+ (unsigned long long)(slave->offset + slave->mtd.size), slave->mtd.name);
+
+ /* let's do some sanity checks */
+- if (slave->offset >= master->size) {
++ if (slave->offset >= parent->size) {
+ /* let's register it anyway to preserve ordering */
+ slave->offset = 0;
+ slave->mtd.size = 0;
+@@ -515,16 +521,16 @@ static struct mtd_part *allocate_partiti
+ part->name);
+ goto out_register;
+ }
+- if (slave->offset + slave->mtd.size > master->size) {
+- slave->mtd.size = master->size - slave->offset;
++ if (slave->offset + slave->mtd.size > parent->size) {
++ slave->mtd.size = parent->size - slave->offset;
+ printk(KERN_WARNING"mtd: partition \"%s\" extends beyond the end of device \"%s\" -- size truncated to %#llx\n",
+- part->name, master->name, (unsigned long long)slave->mtd.size);
++ part->name, parent->name, (unsigned long long)slave->mtd.size);
+ }
+- if (master->numeraseregions > 1) {
++ if (parent->numeraseregions > 1) {
+ /* Deal with variable erase size stuff */
+- int i, max = master->numeraseregions;
++ int i, max = parent->numeraseregions;
+ u64 end = slave->offset + slave->mtd.size;
+- struct mtd_erase_region_info *regions = master->eraseregions;
++ struct mtd_erase_region_info *regions = parent->eraseregions;
+
+ /* Find the first erase regions which is part of this
+ * partition. */
+@@ -543,7 +549,7 @@ static struct mtd_part *allocate_partiti
+ BUG_ON(slave->mtd.erasesize == 0);
+ } else {
+ /* Single erase size */
+- slave->mtd.erasesize = master->erasesize;
++ slave->mtd.erasesize = parent->erasesize;
+ }
+
+ tmp = slave->offset;
+@@ -566,17 +572,17 @@ static struct mtd_part *allocate_partiti
+ }
+
+ mtd_set_ooblayout(&slave->mtd, &part_ooblayout_ops);
+- slave->mtd.ecc_step_size = master->ecc_step_size;
+- slave->mtd.ecc_strength = master->ecc_strength;
+- slave->mtd.bitflip_threshold = master->bitflip_threshold;
++ slave->mtd.ecc_step_size = parent->ecc_step_size;
++ slave->mtd.ecc_strength = parent->ecc_strength;
++ slave->mtd.bitflip_threshold = parent->bitflip_threshold;
+
+- if (master->_block_isbad) {
++ if (parent->_block_isbad) {
+ uint64_t offs = 0;
+
+ while (offs < slave->mtd.size) {
+- if (mtd_block_isreserved(master, offs + slave->offset))
++ if (mtd_block_isreserved(parent, offs + slave->offset))
+ slave->mtd.ecc_stats.bbtblocks++;
+- else if (mtd_block_isbad(master, offs + slave->offset))
++ else if (mtd_block_isbad(parent, offs + slave->offset))
+ slave->mtd.ecc_stats.badblocks++;
+ offs += slave->mtd.erasesize;
+ }
+@@ -610,7 +616,7 @@ static int mtd_add_partition_attrs(struc
+ return ret;
+ }
+
+-int mtd_add_partition(struct mtd_info *master, const char *name,
++int mtd_add_partition(struct mtd_info *parent, const char *name,
+ long long offset, long long length)
+ {
+ struct mtd_partition part;
+@@ -623,7 +629,7 @@ int mtd_add_partition(struct mtd_info *m
+ return -EINVAL;
+
+ if (length == MTDPART_SIZ_FULL)
+- length = master->size - offset;
++ length = parent->size - offset;
+
+ if (length <= 0)
+ return -EINVAL;
+@@ -633,7 +639,7 @@ int mtd_add_partition(struct mtd_info *m
+ part.size = length;
+ part.offset = offset;
+
+- new = allocate_partition(master, &part, -1, offset);
++ new = allocate_partition(parent, &part, -1, offset);
+ if (IS_ERR(new))
+ return PTR_ERR(new);
+
+@@ -683,7 +689,7 @@ int del_mtd_partitions(struct mtd_info *
+
+ mutex_lock(&mtd_partitions_mutex);
+ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+- if (slave->master == master) {
++ if (slave->parent == master) {
+ ret = __mtd_del_partition(slave);
+ if (ret < 0)
+ err = ret;
+@@ -700,7 +706,7 @@ int mtd_del_partition(struct mtd_info *m
+
+ mutex_lock(&mtd_partitions_mutex);
+ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+- if ((slave->master == master) &&
++ if ((slave->parent == master) &&
+ (slave->mtd.index == partno)) {
+ ret = __mtd_del_partition(slave);
+ break;
+@@ -933,6 +939,6 @@ uint64_t mtd_get_device_size(const struc
+ if (!mtd_is_partition(mtd))
+ return mtd->size;
+
+- return mtd_to_part(mtd)->master->size;
++ return mtd_to_part(mtd)->parent->size;
+ }
+ EXPORT_SYMBOL_GPL(mtd_get_device_size);
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0006-mtd-partitions-add-support-for-subpartitions.patch b/target/linux/generic/backport-4.9/065-v4.13-0006-mtd-partitions-add-support-for-subpartitions.patch
new file mode 100644
index 0000000000..0d3e10ac20
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0006-mtd-partitions-add-support-for-subpartitions.patch
@@ -0,0 +1,96 @@
+From 97519dc52b44af054d7654776e78eaa211cf1842 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 21 Jun 2017 08:26:45 +0200
+Subject: [PATCH] mtd: partitions: add support for subpartitions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some flash device partitions can be containers with extra subpartitions
+(volumes). All callbacks are already capable of this additional level of
+indirection.
+
+This patch makes sure we always display subpartitions using a tree
+structure and takes care of deleting subpartitions when parent gets
+removed.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 23 ++++++++++++++++-------
+ 1 file changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -413,7 +413,7 @@ static struct mtd_part *allocate_partiti
+ * parent conditional on that option. Note, this is a way to
+ * distinguish between the master and the partition in sysfs.
+ */
+- slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) ?
++ slave->mtd.dev.parent = IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER) || mtd_is_partition(parent) ?
+ &parent->dev :
+ parent->dev.parent;
+ slave->mtd.dev.of_node = part->of_node;
+@@ -664,8 +664,17 @@ EXPORT_SYMBOL_GPL(mtd_add_partition);
+ */
+ static int __mtd_del_partition(struct mtd_part *priv)
+ {
++ struct mtd_part *child, *next;
+ int err;
+
++ list_for_each_entry_safe(child, next, &mtd_partitions, list) {
++ if (child->parent == &priv->mtd) {
++ err = __mtd_del_partition(child);
++ if (err)
++ return err;
++ }
++ }
++
+ sysfs_remove_files(&priv->mtd.dev.kobj, mtd_partition_attrs);
+
+ err = del_mtd_device(&priv->mtd);
+@@ -680,16 +689,16 @@ static int __mtd_del_partition(struct mt
+
+ /*
+ * This function unregisters and destroy all slave MTD objects which are
+- * attached to the given master MTD object.
++ * attached to the given MTD object.
+ */
+-int del_mtd_partitions(struct mtd_info *master)
++int del_mtd_partitions(struct mtd_info *mtd)
+ {
+ struct mtd_part *slave, *next;
+ int ret, err = 0;
+
+ mutex_lock(&mtd_partitions_mutex);
+ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+- if (slave->parent == master) {
++ if (slave->parent == mtd) {
+ ret = __mtd_del_partition(slave);
+ if (ret < 0)
+ err = ret;
+@@ -699,14 +708,14 @@ int del_mtd_partitions(struct mtd_info *
+ return err;
+ }
+
+-int mtd_del_partition(struct mtd_info *master, int partno)
++int mtd_del_partition(struct mtd_info *mtd, int partno)
+ {
+ struct mtd_part *slave, *next;
+ int ret = -EINVAL;
+
+ mutex_lock(&mtd_partitions_mutex);
+ list_for_each_entry_safe(slave, next, &mtd_partitions, list)
+- if ((slave->parent == master) &&
++ if ((slave->parent == mtd) &&
+ (slave->mtd.index == partno)) {
+ ret = __mtd_del_partition(slave);
+ break;
+@@ -939,6 +948,6 @@ uint64_t mtd_get_device_size(const struc
+ if (!mtd_is_partition(mtd))
+ return mtd->size;
+
+- return mtd_to_part(mtd)->parent->size;
++ return mtd_get_device_size(mtd_to_part(mtd)->parent);
+ }
+ EXPORT_SYMBOL_GPL(mtd_get_device_size);
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0007-mtd-partitions-add-support-for-partition-parsers.patch b/target/linux/generic/backport-4.9/065-v4.13-0007-mtd-partitions-add-support-for-partition-parsers.patch
new file mode 100644
index 0000000000..a28ee316fa
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0007-mtd-partitions-add-support-for-partition-parsers.patch
@@ -0,0 +1,110 @@
+From 1a0915be192606fee64830b9c5d70b7ed59426b6 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 21 Jun 2017 08:26:46 +0200
+Subject: [PATCH] mtd: partitions: add support for partition parsers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some devices have partitions that are kind of containers with extra
+subpartitions / volumes instead of e.g. a simple filesystem data. To
+support such cases we need to first create normal flash device
+partitions and then take care of these special ones.
+
+It's very common case for home routers. Depending on the vendor there
+are formats like TRX, Seama, TP-Link, WRGG & more. All of them are used
+to embed few partitions into a single one / single firmware file.
+
+Ideally all vendors would use some well documented / standardized format
+like UBI (and some probably start doing so), but there are still
+countless devices on the market using these poor vendor specific
+formats.
+
+This patch extends MTD subsystem by allowing to specify list of parsers
+that should be tried for a given partition. Supporting such poor formats
+is highly unlikely to be the top priority so these changes try to
+minimize maintenance cost to the minimum. It reuses existing code for
+these new parsers and just adds a one property and one new function.
+
+This implementation requires setting partition parsers in a flash
+parser. A proper change of bcm47xxpart will follow and in the future we
+will hopefully also find a solution for doing it with ofpart
+("fixed-partitions").
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/mtdpart.c | 31 +++++++++++++++++++++++++++++++
+ include/linux/mtd/partitions.h | 7 +++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/drivers/mtd/mtdpart.c
++++ b/drivers/mtd/mtdpart.c
+@@ -369,6 +369,35 @@ static inline void free_partition(struct
+ kfree(p);
+ }
+
++/**
++ * mtd_parse_part - parse MTD partition looking for subpartitions
++ *
++ * @slave: part that is supposed to be a container and should be parsed
++ * @types: NULL-terminated array with names of partition parsers to try
++ *
++ * Some partitions are kind of containers with extra subpartitions (volumes).
++ * There can be various formats of such containers. This function tries to use
++ * specified parsers to analyze given partition and registers found
++ * subpartitions on success.
++ */
++static int mtd_parse_part(struct mtd_part *slave, const char *const *types)
++{
++ struct mtd_partitions parsed;
++ int err;
++
++ err = parse_mtd_partitions(&slave->mtd, types, &parsed, NULL);
++ if (err)
++ return err;
++ else if (!parsed.nr_parts)
++ return -ENOENT;
++
++ err = add_mtd_partitions(&slave->mtd, parsed.parts, parsed.nr_parts);
++
++ mtd_part_parser_cleanup(&parsed);
++
++ return err;
++}
++
+ static struct mtd_part *allocate_partition(struct mtd_info *parent,
+ const struct mtd_partition *part, int partno,
+ uint64_t cur_offset)
+@@ -758,6 +787,8 @@ int add_mtd_partitions(struct mtd_info *
+
+ add_mtd_device(&slave->mtd);
+ mtd_add_partition_attrs(slave);
++ if (parts[i].types)
++ mtd_parse_part(slave, parts[i].types);
+
+ cur_offset = slave->offset + slave->mtd.size;
+ }
+--- a/include/linux/mtd/partitions.h
++++ b/include/linux/mtd/partitions.h
+@@ -20,6 +20,12 @@
+ *
+ * For each partition, these fields are available:
+ * name: string that will be used to label the partition's MTD device.
++ * types: some partitions can be containers using specific format to describe
++ * embedded subpartitions / volumes. E.g. many home routers use "firmware"
++ * partition that contains at least kernel and rootfs. In such case an
++ * extra parser is needed that will detect these dynamic partitions and
++ * report them to the MTD subsystem. If set this property stores an array
++ * of parser names to use when looking for subpartitions.
+ * size: the partition size; if defined as MTDPART_SIZ_FULL, the partition
+ * will extend to the end of the master MTD device.
+ * offset: absolute starting position within the master MTD device; if
+@@ -38,6 +44,7 @@
+
+ struct mtd_partition {
+ const char *name; /* identifier string */
++ const char *const *types; /* names of parsers to use if any */
+ uint64_t size; /* partition size */
+ uint64_t offset; /* offset within the master MTD space */
+ uint32_t mask_flags; /* master MTD flags to mask out for this partition */
diff --git a/target/linux/generic/backport-4.9/065-v4.13-0008-mtd-extract-TRX-parser-out-of-bcm47xxpart-into-a-sep.patch b/target/linux/generic/backport-4.9/065-v4.13-0008-mtd-extract-TRX-parser-out-of-bcm47xxpart-into-a-sep.patch
new file mode 100644
index 0000000000..3761a46610
--- /dev/null
+++ b/target/linux/generic/backport-4.9/065-v4.13-0008-mtd-extract-TRX-parser-out-of-bcm47xxpart-into-a-sep.patch
@@ -0,0 +1,320 @@
+From 99352afe8f169c95b294b6b9a8d0e18cd9e3c2a0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 21 Jun 2017 08:26:47 +0200
+Subject: [PATCH] mtd: extract TRX parser out of bcm47xxpart into a separated
+ module
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This makes TRX parsing code reusable with other platforms and parsers.
+
+Please note this patch doesn't really change anything in the existing
+code, just moves it. There is still some place for improvement (e.g.
+working on non-hacky method of checking rootfs format) but it's not
+really a subject of this change.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Brian Norris <computersforpeace@gmail.com>
+---
+ drivers/mtd/Kconfig | 4 ++
+ drivers/mtd/Makefile | 1 +
+ drivers/mtd/bcm47xxpart.c | 99 ++----------------------------
+ drivers/mtd/parsers/Kconfig | 8 +++
+ drivers/mtd/parsers/Makefile | 1 +
+ drivers/mtd/parsers/parser_trx.c | 126 +++++++++++++++++++++++++++++++++++++++
+ 6 files changed, 145 insertions(+), 94 deletions(-)
+ create mode 100644 drivers/mtd/parsers/Kconfig
+ create mode 100644 drivers/mtd/parsers/Makefile
+ create mode 100644 drivers/mtd/parsers/parser_trx.c
+
+--- a/drivers/mtd/Kconfig
++++ b/drivers/mtd/Kconfig
+@@ -155,6 +155,10 @@ config MTD_BCM47XX_PARTS
+ This provides partitions parser for devices based on BCM47xx
+ boards.
+
++menu "Partition parsers"
++source "drivers/mtd/parsers/Kconfig"
++endmenu
++
+ comment "User Modules And Translation Layers"
+
+ #
+--- a/drivers/mtd/Makefile
++++ b/drivers/mtd/Makefile
+@@ -13,6 +13,7 @@ obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
+ obj-$(CONFIG_MTD_AR7_PARTS) += ar7part.o
+ obj-$(CONFIG_MTD_BCM63XX_PARTS) += bcm63xxpart.o
+ obj-$(CONFIG_MTD_BCM47XX_PARTS) += bcm47xxpart.o
++obj-y += parsers/
+
+ # 'Users' - code which presents functionality to userspace.
+ obj-$(CONFIG_MTD_BLKDEVS) += mtd_blkdevs.o
+--- a/drivers/mtd/bcm47xxpart.c
++++ b/drivers/mtd/bcm47xxpart.c
+@@ -43,7 +43,8 @@
+ #define ML_MAGIC2 0x26594131
+ #define TRX_MAGIC 0x30524448
+ #define SHSQ_MAGIC 0x71736873 /* shsq (weird ZTE H218N endianness) */
+-#define UBI_EC_MAGIC 0x23494255 /* UBI# */
++
++static const char * const trx_types[] = { "trx", NULL };
+
+ struct trx_header {
+ uint32_t magic;
+@@ -62,89 +63,6 @@ static void bcm47xxpart_add_part(struct
+ part->mask_flags = mask_flags;
+ }
+
+-static const char *bcm47xxpart_trx_data_part_name(struct mtd_info *master,
+- size_t offset)
+-{
+- uint32_t buf;
+- size_t bytes_read;
+- int err;
+-
+- err = mtd_read(master, offset, sizeof(buf), &bytes_read,
+- (uint8_t *)&buf);
+- if (err && !mtd_is_bitflip(err)) {
+- pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
+- offset, err);
+- goto out_default;
+- }
+-
+- if (buf == UBI_EC_MAGIC)
+- return "ubi";
+-
+-out_default:
+- return "rootfs";
+-}
+-
+-static int bcm47xxpart_parse_trx(struct mtd_info *master,
+- struct mtd_partition *trx,
+- struct mtd_partition *parts,
+- size_t parts_len)
+-{
+- struct trx_header header;
+- size_t bytes_read;
+- int curr_part = 0;
+- int i, err;
+-
+- if (parts_len < 3) {
+- pr_warn("No enough space to add TRX partitions!\n");
+- return -ENOMEM;
+- }
+-
+- err = mtd_read(master, trx->offset, sizeof(header), &bytes_read,
+- (uint8_t *)&header);
+- if (err && !mtd_is_bitflip(err)) {
+- pr_err("mtd_read error while reading TRX header: %d\n", err);
+- return err;
+- }
+-
+- i = 0;
+-
+- /* We have LZMA loader if offset[2] points to sth */
+- if (header.offset[2]) {
+- bcm47xxpart_add_part(&parts[curr_part++], "loader",
+- trx->offset + header.offset[i], 0);
+- i++;
+- }
+-
+- if (header.offset[i]) {
+- bcm47xxpart_add_part(&parts[curr_part++], "linux",
+- trx->offset + header.offset[i], 0);
+- i++;
+- }
+-
+- if (header.offset[i]) {
+- size_t offset = trx->offset + header.offset[i];
+- const char *name = bcm47xxpart_trx_data_part_name(master,
+- offset);
+-
+- bcm47xxpart_add_part(&parts[curr_part++], name, offset, 0);
+- i++;
+- }
+-
+- /*
+- * Assume that every partition ends at the beginning of the one it is
+- * followed by.
+- */
+- for (i = 0; i < curr_part; i++) {
+- u64 next_part_offset = (i < curr_part - 1) ?
+- parts[i + 1].offset :
+- trx->offset + trx->size;
+-
+- parts[i].size = next_part_offset - parts[i].offset;
+- }
+-
+- return curr_part;
+-}
+-
+ /**
+ * bcm47xxpart_bootpartition - gets index of TRX partition used by bootloader
+ *
+@@ -362,17 +280,10 @@ static int bcm47xxpart_parse(struct mtd_
+ for (i = 0; i < trx_num; i++) {
+ struct mtd_partition *trx = &parts[trx_parts[i]];
+
+- if (i == bcm47xxpart_bootpartition()) {
+- int num_parts;
+-
+- num_parts = bcm47xxpart_parse_trx(master, trx,
+- parts + curr_part,
+- BCM47XXPART_MAX_PARTS - curr_part);
+- if (num_parts > 0)
+- curr_part += num_parts;
+- } else {
++ if (i == bcm47xxpart_bootpartition())
++ trx->types = trx_types;
++ else
+ trx->name = "failsafe";
+- }
+ }
+
+ *pparts = parts;
+--- /dev/null
++++ b/drivers/mtd/parsers/Kconfig
+@@ -0,0 +1,8 @@
++config MTD_PARSER_TRX
++ tristate "Parser for TRX format partitions"
++ depends on MTD && (BCM47XX || ARCH_BCM_5301X || COMPILE_TEST)
++ help
++ TRX is a firmware format used by Broadcom on their devices. It
++ may contain up to 3/4 partitions (depending on the version).
++ This driver will parse TRX header and report at least two partitions:
++ kernel and rootfs.
+--- /dev/null
++++ b/drivers/mtd/parsers/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
+--- /dev/null
++++ b/drivers/mtd/parsers/parser_trx.c
+@@ -0,0 +1,126 @@
++/*
++ * Parser for TRX format partitions
++ *
++ * Copyright (C) 2012 - 2017 Rafał Miłecki <rafal@milecki.pl>
++ *
++ * 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/module.h>
++#include <linux/slab.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/partitions.h>
++
++#define TRX_PARSER_MAX_PARTS 4
++
++/* Magics */
++#define TRX_MAGIC 0x30524448
++#define UBI_EC_MAGIC 0x23494255 /* UBI# */
++
++struct trx_header {
++ uint32_t magic;
++ uint32_t length;
++ uint32_t crc32;
++ uint16_t flags;
++ uint16_t version;
++ uint32_t offset[3];
++} __packed;
++
++static const char *parser_trx_data_part_name(struct mtd_info *master,
++ size_t offset)
++{
++ uint32_t buf;
++ size_t bytes_read;
++ int err;
++
++ err = mtd_read(master, offset, sizeof(buf), &bytes_read,
++ (uint8_t *)&buf);
++ if (err && !mtd_is_bitflip(err)) {
++ pr_err("mtd_read error while parsing (offset: 0x%X): %d\n",
++ offset, err);
++ goto out_default;
++ }
++
++ if (buf == UBI_EC_MAGIC)
++ return "ubi";
++
++out_default:
++ return "rootfs";
++}
++
++static int parser_trx_parse(struct mtd_info *mtd,
++ const struct mtd_partition **pparts,
++ struct mtd_part_parser_data *data)
++{
++ struct mtd_partition *parts;
++ struct mtd_partition *part;
++ struct trx_header trx;
++ size_t bytes_read;
++ uint8_t curr_part = 0, i = 0;
++ int err;
++
++ parts = kzalloc(sizeof(struct mtd_partition) * TRX_PARSER_MAX_PARTS,
++ GFP_KERNEL);
++ if (!parts)
++ return -ENOMEM;
++
++ err = mtd_read(mtd, 0, sizeof(trx), &bytes_read, (uint8_t *)&trx);
++ if (err) {
++ pr_err("MTD reading error: %d\n", err);
++ kfree(parts);
++ return err;
++ }
++
++ if (trx.magic != TRX_MAGIC) {
++ kfree(parts);
++ return -ENOENT;
++ }
++
++ /* We have LZMA loader if there is address in offset[2] */
++ if (trx.offset[2]) {
++ part = &parts[curr_part++];
++ part->name = "loader";
++ part->offset = trx.offset[i];
++ i++;
++ }
++
++ if (trx.offset[i]) {
++ part = &parts[curr_part++];
++ part->name = "linux";
++ part->offset = trx.offset[i];
++ i++;
++ }
++
++ if (trx.offset[i]) {
++ part = &parts[curr_part++];
++ part->name = parser_trx_data_part_name(mtd, trx.offset[i]);
++ part->offset = trx.offset[i];
++ i++;
++ }
++
++ /*
++ * Assume that every partition ends at the beginning of the one it is
++ * followed by.
++ */
++ for (i = 0; i < curr_part; i++) {
++ u64 next_part_offset = (i < curr_part - 1) ?
++ parts[i + 1].offset : mtd->size;
++
++ parts[i].size = next_part_offset - parts[i].offset;
++ }
++
++ *pparts = parts;
++ return i;
++};
++
++static struct mtd_part_parser mtd_parser_trx = {
++ .parse_fn = parser_trx_parse,
++ .name = "trx",
++};
++module_mtd_part_parser(mtd_parser_trx);
++
++MODULE_LICENSE("GPL v2");
++MODULE_DESCRIPTION("Parser for TRX format partitions");
diff --git a/target/linux/generic/backport-4.9/070-bcma-from-4.11.patch b/target/linux/generic/backport-4.9/070-bcma-from-4.11.patch
new file mode 100644
index 0000000000..a3b0321986
--- /dev/null
+++ b/target/linux/generic/backport-4.9/070-bcma-from-4.11.patch
@@ -0,0 +1,85 @@
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -136,17 +136,17 @@ static bool bcma_is_core_needed_early(u1
+ return false;
+ }
+
+-static struct device_node *bcma_of_find_child_device(struct platform_device *parent,
++static struct device_node *bcma_of_find_child_device(struct device *parent,
+ struct bcma_device *core)
+ {
+ struct device_node *node;
+ u64 size;
+ const __be32 *reg;
+
+- if (!parent || !parent->dev.of_node)
++ if (!parent->of_node)
+ return NULL;
+
+- for_each_child_of_node(parent->dev.of_node, node) {
++ for_each_child_of_node(parent->of_node, node) {
+ reg = of_get_address(node, 0, &size, NULL);
+ if (!reg)
+ continue;
+@@ -156,7 +156,7 @@ static struct device_node *bcma_of_find_
+ return NULL;
+ }
+
+-static int bcma_of_irq_parse(struct platform_device *parent,
++static int bcma_of_irq_parse(struct device *parent,
+ struct bcma_device *core,
+ struct of_phandle_args *out_irq, int num)
+ {
+@@ -169,7 +169,7 @@ static int bcma_of_irq_parse(struct plat
+ return rc;
+ }
+
+- out_irq->np = parent->dev.of_node;
++ out_irq->np = parent->of_node;
+ out_irq->args_count = 1;
+ out_irq->args[0] = num;
+
+@@ -177,13 +177,13 @@ static int bcma_of_irq_parse(struct plat
+ return of_irq_parse_raw(laddr, out_irq);
+ }
+
+-static unsigned int bcma_of_get_irq(struct platform_device *parent,
++static unsigned int bcma_of_get_irq(struct device *parent,
+ struct bcma_device *core, int num)
+ {
+ struct of_phandle_args out_irq;
+ int ret;
+
+- if (!IS_ENABLED(CONFIG_OF_IRQ) || !parent || !parent->dev.of_node)
++ if (!IS_ENABLED(CONFIG_OF_IRQ) || !parent->of_node)
+ return 0;
+
+ ret = bcma_of_irq_parse(parent, core, &out_irq, num);
+@@ -196,7 +196,7 @@ static unsigned int bcma_of_get_irq(stru
+ return irq_create_of_mapping(&out_irq);
+ }
+
+-static void bcma_of_fill_device(struct platform_device *parent,
++static void bcma_of_fill_device(struct device *parent,
+ struct bcma_device *core)
+ {
+ struct device_node *node;
+@@ -227,7 +227,7 @@ unsigned int bcma_core_irq(struct bcma_d
+ return mips_irq <= 4 ? mips_irq + 2 : 0;
+ }
+ if (bus->host_pdev)
+- return bcma_of_get_irq(bus->host_pdev, core, num);
++ return bcma_of_get_irq(&bus->host_pdev->dev, core, num);
+ return 0;
+ case BCMA_HOSTTYPE_SDIO:
+ return 0;
+@@ -253,7 +253,8 @@ void bcma_prepare_core(struct bcma_bus *
+ if (IS_ENABLED(CONFIG_OF) && bus->host_pdev) {
+ core->dma_dev = &bus->host_pdev->dev;
+ core->dev.parent = &bus->host_pdev->dev;
+- bcma_of_fill_device(bus->host_pdev, core);
++ if (core->dev.parent)
++ bcma_of_fill_device(core->dev.parent, core);
+ } else {
+ core->dev.dma_mask = &core->dev.coherent_dma_mask;
+ core->dma_dev = &core->dev;
diff --git a/target/linux/generic/backport-4.9/071-v4.10-0001-net-bgmac-allocate-struct-bgmac-just-once-don-t-copy.patch b/target/linux/generic/backport-4.9/071-v4.10-0001-net-bgmac-allocate-struct-bgmac-just-once-don-t-copy.patch
new file mode 100644
index 0000000000..37639faf17
--- /dev/null
+++ b/target/linux/generic/backport-4.9/071-v4.10-0001-net-bgmac-allocate-struct-bgmac-just-once-don-t-copy.patch
@@ -0,0 +1,139 @@
+From 34a5102c3235c470a6c77fba16cb971964d9c136 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 31 Jan 2017 19:37:54 +0100
+Subject: [PATCH 1/3] net: bgmac: allocate struct bgmac just once & don't copy
+ it
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+So far were were allocating struct bgmac in 3 places: platform code,
+bcma code and shared bgmac_enet_probe function. The reason for this was
+bgmac_enet_probe:
+1) Requiring early-filled struct bgmac
+2) Calling alloc_etherdev on its own in order to use netdev_priv later
+
+This solution got few drawbacks:
+1) Was duplicating allocating code
+2) Required copying early-filled struct
+3) Resulted in platform/bcma code having access only to unused struct
+
+Solve this situation by simply extracting some probe code into the new
+bgmac_alloc function.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma.c | 4 +---
+ drivers/net/ethernet/broadcom/bgmac-platform.c | 2 +-
+ drivers/net/ethernet/broadcom/bgmac.c | 25 +++++++++++++++++--------
+ drivers/net/ethernet/broadcom/bgmac.h | 3 ++-
+ 4 files changed, 21 insertions(+), 13 deletions(-)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -99,12 +99,11 @@ static int bgmac_probe(struct bcma_devic
+ u8 *mac;
+ int err;
+
+- bgmac = kzalloc(sizeof(*bgmac), GFP_KERNEL);
++ bgmac = bgmac_alloc(&core->dev);
+ if (!bgmac)
+ return -ENOMEM;
+
+ bgmac->bcma.core = core;
+- bgmac->dev = &core->dev;
+ bgmac->dma_dev = core->dma_dev;
+ bgmac->irq = core->irq;
+
+@@ -285,7 +284,6 @@ static int bgmac_probe(struct bcma_devic
+ err1:
+ bcma_mdio_mii_unregister(bgmac->mii_bus);
+ err:
+- kfree(bgmac);
+ bcma_set_drvdata(core, NULL);
+
+ return err;
+--- a/drivers/net/ethernet/broadcom/bgmac-platform.c
++++ b/drivers/net/ethernet/broadcom/bgmac-platform.c
+@@ -93,7 +93,7 @@ static int bgmac_probe(struct platform_d
+ struct resource *regs;
+ const u8 *mac_addr;
+
+- bgmac = devm_kzalloc(&pdev->dev, sizeof(*bgmac), GFP_KERNEL);
++ bgmac = bgmac_alloc(&pdev->dev);
+ if (!bgmac)
+ return -ENOMEM;
+
+--- a/drivers/net/ethernet/broadcom/bgmac.c
++++ b/drivers/net/ethernet/broadcom/bgmac.c
+@@ -1459,22 +1459,32 @@ static int bgmac_phy_connect(struct bgma
+ return 0;
+ }
+
+-int bgmac_enet_probe(struct bgmac *info)
++struct bgmac *bgmac_alloc(struct device *dev)
+ {
+ struct net_device *net_dev;
+ struct bgmac *bgmac;
+- int err;
+
+ /* Allocation and references */
+- net_dev = alloc_etherdev(sizeof(*bgmac));
++ net_dev = devm_alloc_etherdev(dev, sizeof(*bgmac));
+ if (!net_dev)
+- return -ENOMEM;
++ return NULL;
+
+ net_dev->netdev_ops = &bgmac_netdev_ops;
+ net_dev->ethtool_ops = &bgmac_ethtool_ops;
++
+ bgmac = netdev_priv(net_dev);
+- memcpy(bgmac, info, sizeof(*bgmac));
++ bgmac->dev = dev;
+ bgmac->net_dev = net_dev;
++
++ return bgmac;
++}
++EXPORT_SYMBOL_GPL(bgmac_alloc);
++
++int bgmac_enet_probe(struct bgmac *bgmac)
++{
++ struct net_device *net_dev = bgmac->net_dev;
++ int err;
++
+ net_dev->irq = bgmac->irq;
+ SET_NETDEV_DEV(net_dev, bgmac->dev);
+
+@@ -1501,7 +1511,7 @@ int bgmac_enet_probe(struct bgmac *info)
+ err = bgmac_dma_alloc(bgmac);
+ if (err) {
+ dev_err(bgmac->dev, "Unable to alloc memory for DMA\n");
+- goto err_netdev_free;
++ goto err_out;
+ }
+
+ bgmac->int_mask = BGMAC_IS_ERRMASK | BGMAC_IS_RX | BGMAC_IS_TX_MASK;
+@@ -1537,8 +1547,7 @@ err_phy_disconnect:
+ phy_disconnect(net_dev->phydev);
+ err_dma_free:
+ bgmac_dma_free(bgmac);
+-err_netdev_free:
+- free_netdev(net_dev);
++err_out:
+
+ return err;
+ }
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -515,7 +515,8 @@ struct bgmac {
+ u32 set);
+ };
+
+-int bgmac_enet_probe(struct bgmac *info);
++struct bgmac *bgmac_alloc(struct device *dev);
++int bgmac_enet_probe(struct bgmac *bgmac);
+ void bgmac_enet_remove(struct bgmac *bgmac);
+
+ struct mii_bus *bcma_mdio_mii_register(struct bcma_device *core, u8 phyaddr);
diff --git a/target/linux/generic/backport-4.9/071-v4.10-0002-net-bgmac-drop-struct-bcma_mdio-we-don-t-need-anymor.patch b/target/linux/generic/backport-4.9/071-v4.10-0002-net-bgmac-drop-struct-bcma_mdio-we-don-t-need-anymor.patch
new file mode 100644
index 0000000000..7a6f3454d1
--- /dev/null
+++ b/target/linux/generic/backport-4.9/071-v4.10-0002-net-bgmac-drop-struct-bcma_mdio-we-don-t-need-anymor.patch
@@ -0,0 +1,261 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 31 Jan 2017 19:37:55 +0100
+Subject: [PATCH] net: bgmac: drop struct bcma_mdio we don't need anymore
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adding struct bcma_mdio was a workaround for bcma code not having access
+to the struct bgmac used in the core code. Now we don't duplicate this
+struct we can just use it internally in bcma code.
+
+This simplifies code & allows access to all bgmac driver details from
+all places in bcma code.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -159,7 +159,7 @@ static int bgmac_probe(struct bcma_devic
+
+ if (!bgmac_is_bcm4707_family(core) &&
+ !(ci->id == BCMA_CHIP_ID_BCM53573 && core->core_unit == 1)) {
+- mii_bus = bcma_mdio_mii_register(core, bgmac->phyaddr);
++ mii_bus = bcma_mdio_mii_register(bgmac);
+ if (IS_ERR(mii_bus)) {
+ err = PTR_ERR(mii_bus);
+ goto err;
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
+@@ -12,11 +12,6 @@
+ #include <linux/brcmphy.h>
+ #include "bgmac.h"
+
+-struct bcma_mdio {
+- struct bcma_device *core;
+- u8 phyaddr;
+-};
+-
+ static bool bcma_mdio_wait_value(struct bcma_device *core, u16 reg, u32 mask,
+ u32 value, int timeout)
+ {
+@@ -37,7 +32,7 @@ static bool bcma_mdio_wait_value(struct
+ * PHY ops
+ **************************************************/
+
+-static u16 bcma_mdio_phy_read(struct bcma_mdio *bcma_mdio, u8 phyaddr, u8 reg)
++static u16 bcma_mdio_phy_read(struct bgmac *bgmac, u8 phyaddr, u8 reg)
+ {
+ struct bcma_device *core;
+ u16 phy_access_addr;
+@@ -56,12 +51,12 @@ static u16 bcma_mdio_phy_read(struct bcm
+ BUILD_BUG_ON(BGMAC_PC_MCT_SHIFT != BCMA_GMAC_CMN_PC_MCT_SHIFT);
+ BUILD_BUG_ON(BGMAC_PC_MTE != BCMA_GMAC_CMN_PC_MTE);
+
+- if (bcma_mdio->core->id.id == BCMA_CORE_4706_MAC_GBIT) {
+- core = bcma_mdio->core->bus->drv_gmac_cmn.core;
++ if (bgmac->bcma.core->id.id == BCMA_CORE_4706_MAC_GBIT) {
++ core = bgmac->bcma.core->bus->drv_gmac_cmn.core;
+ phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS;
+ phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL;
+ } else {
+- core = bcma_mdio->core;
++ core = bgmac->bcma.core;
+ phy_access_addr = BGMAC_PHY_ACCESS;
+ phy_ctl_addr = BGMAC_PHY_CNTL;
+ }
+@@ -87,7 +82,7 @@ static u16 bcma_mdio_phy_read(struct bcm
+ }
+
+ /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphywr */
+-static int bcma_mdio_phy_write(struct bcma_mdio *bcma_mdio, u8 phyaddr, u8 reg,
++static int bcma_mdio_phy_write(struct bgmac *bgmac, u8 phyaddr, u8 reg,
+ u16 value)
+ {
+ struct bcma_device *core;
+@@ -95,12 +90,12 @@ static int bcma_mdio_phy_write(struct bc
+ u16 phy_ctl_addr;
+ u32 tmp;
+
+- if (bcma_mdio->core->id.id == BCMA_CORE_4706_MAC_GBIT) {
+- core = bcma_mdio->core->bus->drv_gmac_cmn.core;
++ if (bgmac->bcma.core->id.id == BCMA_CORE_4706_MAC_GBIT) {
++ core = bgmac->bcma.core->bus->drv_gmac_cmn.core;
+ phy_access_addr = BCMA_GMAC_CMN_PHY_ACCESS;
+ phy_ctl_addr = BCMA_GMAC_CMN_PHY_CTL;
+ } else {
+- core = bcma_mdio->core;
++ core = bgmac->bcma.core;
+ phy_access_addr = BGMAC_PHY_ACCESS;
+ phy_ctl_addr = BGMAC_PHY_CNTL;
+ }
+@@ -110,8 +105,8 @@ static int bcma_mdio_phy_write(struct bc
+ tmp |= phyaddr;
+ bcma_write32(core, phy_ctl_addr, tmp);
+
+- bcma_write32(bcma_mdio->core, BGMAC_INT_STATUS, BGMAC_IS_MDIO);
+- if (bcma_read32(bcma_mdio->core, BGMAC_INT_STATUS) & BGMAC_IS_MDIO)
++ bcma_write32(bgmac->bcma.core, BGMAC_INT_STATUS, BGMAC_IS_MDIO);
++ if (bcma_read32(bgmac->bcma.core, BGMAC_INT_STATUS) & BGMAC_IS_MDIO)
+ dev_warn(&core->dev, "Error setting MDIO int\n");
+
+ tmp = BGMAC_PA_START;
+@@ -132,39 +127,39 @@ static int bcma_mdio_phy_write(struct bc
+ }
+
+ /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyinit */
+-static void bcma_mdio_phy_init(struct bcma_mdio *bcma_mdio)
++static void bcma_mdio_phy_init(struct bgmac *bgmac)
+ {
+- struct bcma_chipinfo *ci = &bcma_mdio->core->bus->chipinfo;
++ struct bcma_chipinfo *ci = &bgmac->bcma.core->bus->chipinfo;
+ u8 i;
+
+ if (ci->id == BCMA_CHIP_ID_BCM5356) {
+ for (i = 0; i < 5; i++) {
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x008b);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x15, 0x0100);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x000f);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x12, 0x2aaa);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x000b);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x008b);
++ bcma_mdio_phy_write(bgmac, i, 0x15, 0x0100);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000f);
++ bcma_mdio_phy_write(bgmac, i, 0x12, 0x2aaa);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b);
+ }
+ }
+ if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg != 10) ||
+ (ci->id == BCMA_CHIP_ID_BCM4749 && ci->pkg != 10) ||
+ (ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg != 9)) {
+- struct bcma_drv_cc *cc = &bcma_mdio->core->bus->drv_cc;
++ struct bcma_drv_cc *cc = &bgmac->bcma.core->bus->drv_cc;
+
+ bcma_chipco_chipctl_maskset(cc, 2, ~0xc0000000, 0);
+ bcma_chipco_chipctl_maskset(cc, 4, ~0x80000000, 0);
+ for (i = 0; i < 5; i++) {
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x000f);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x16, 0x5284);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x000b);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x17, 0x0010);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x000f);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x16, 0x5296);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x17, 0x1073);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x17, 0x9073);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x16, 0x52b6);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x17, 0x9273);
+- bcma_mdio_phy_write(bcma_mdio, i, 0x1f, 0x000b);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000f);
++ bcma_mdio_phy_write(bgmac, i, 0x16, 0x5284);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b);
++ bcma_mdio_phy_write(bgmac, i, 0x17, 0x0010);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000f);
++ bcma_mdio_phy_write(bgmac, i, 0x16, 0x5296);
++ bcma_mdio_phy_write(bgmac, i, 0x17, 0x1073);
++ bcma_mdio_phy_write(bgmac, i, 0x17, 0x9073);
++ bcma_mdio_phy_write(bgmac, i, 0x16, 0x52b6);
++ bcma_mdio_phy_write(bgmac, i, 0x17, 0x9273);
++ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b);
+ }
+ }
+ }
+@@ -172,17 +167,17 @@ static void bcma_mdio_phy_init(struct bc
+ /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyreset */
+ static int bcma_mdio_phy_reset(struct mii_bus *bus)
+ {
+- struct bcma_mdio *bcma_mdio = bus->priv;
+- u8 phyaddr = bcma_mdio->phyaddr;
++ struct bgmac *bgmac = bus->priv;
++ u8 phyaddr = bgmac->phyaddr;
+
+- if (bcma_mdio->phyaddr == BGMAC_PHY_NOREGS)
++ if (phyaddr == BGMAC_PHY_NOREGS)
+ return 0;
+
+- bcma_mdio_phy_write(bcma_mdio, phyaddr, MII_BMCR, BMCR_RESET);
++ bcma_mdio_phy_write(bgmac, phyaddr, MII_BMCR, BMCR_RESET);
+ udelay(100);
+- if (bcma_mdio_phy_read(bcma_mdio, phyaddr, MII_BMCR) & BMCR_RESET)
+- dev_err(&bcma_mdio->core->dev, "PHY reset failed\n");
+- bcma_mdio_phy_init(bcma_mdio);
++ if (bcma_mdio_phy_read(bgmac, phyaddr, MII_BMCR) & BMCR_RESET)
++ dev_err(bgmac->dev, "PHY reset failed\n");
++ bcma_mdio_phy_init(bgmac);
+
+ return 0;
+ }
+@@ -202,16 +197,12 @@ static int bcma_mdio_mii_write(struct mi
+ return bcma_mdio_phy_write(bus->priv, mii_id, regnum, value);
+ }
+
+-struct mii_bus *bcma_mdio_mii_register(struct bcma_device *core, u8 phyaddr)
++struct mii_bus *bcma_mdio_mii_register(struct bgmac *bgmac)
+ {
+- struct bcma_mdio *bcma_mdio;
++ struct bcma_device *core = bgmac->bcma.core;
+ struct mii_bus *mii_bus;
+ int err;
+
+- bcma_mdio = kzalloc(sizeof(*bcma_mdio), GFP_KERNEL);
+- if (!bcma_mdio)
+- return ERR_PTR(-ENOMEM);
+-
+ mii_bus = mdiobus_alloc();
+ if (!mii_bus) {
+ err = -ENOMEM;
+@@ -221,15 +212,12 @@ struct mii_bus *bcma_mdio_mii_register(s
+ mii_bus->name = "bcma_mdio mii bus";
+ sprintf(mii_bus->id, "%s-%d-%d", "bcma_mdio", core->bus->num,
+ core->core_unit);
+- mii_bus->priv = bcma_mdio;
++ mii_bus->priv = bgmac;
+ mii_bus->read = bcma_mdio_mii_read;
+ mii_bus->write = bcma_mdio_mii_write;
+ mii_bus->reset = bcma_mdio_phy_reset;
+ mii_bus->parent = &core->dev;
+- mii_bus->phy_mask = ~(1 << phyaddr);
+-
+- bcma_mdio->core = core;
+- bcma_mdio->phyaddr = phyaddr;
++ mii_bus->phy_mask = ~(1 << bgmac->phyaddr);
+
+ err = mdiobus_register(mii_bus);
+ if (err) {
+@@ -242,23 +230,17 @@ struct mii_bus *bcma_mdio_mii_register(s
+ err_free_bus:
+ mdiobus_free(mii_bus);
+ err:
+- kfree(bcma_mdio);
+ return ERR_PTR(err);
+ }
+ EXPORT_SYMBOL_GPL(bcma_mdio_mii_register);
+
+ void bcma_mdio_mii_unregister(struct mii_bus *mii_bus)
+ {
+- struct bcma_mdio *bcma_mdio;
+-
+ if (!mii_bus)
+ return;
+
+- bcma_mdio = mii_bus->priv;
+-
+ mdiobus_unregister(mii_bus);
+ mdiobus_free(mii_bus);
+- kfree(bcma_mdio);
+ }
+ EXPORT_SYMBOL_GPL(bcma_mdio_mii_unregister);
+
+--- a/drivers/net/ethernet/broadcom/bgmac.h
++++ b/drivers/net/ethernet/broadcom/bgmac.h
+@@ -519,7 +519,7 @@ struct bgmac *bgmac_alloc(struct device
+ int bgmac_enet_probe(struct bgmac *bgmac);
+ void bgmac_enet_remove(struct bgmac *bgmac);
+
+-struct mii_bus *bcma_mdio_mii_register(struct bcma_device *core, u8 phyaddr);
++struct mii_bus *bcma_mdio_mii_register(struct bgmac *bgmac);
+ void bcma_mdio_mii_unregister(struct mii_bus *mii_bus);
+
+ static inline u32 bgmac_read(struct bgmac *bgmac, u16 offset)
diff --git a/target/linux/generic/backport-4.9/071-v4.10-0003-net-bgmac-use-PHY-subsystem-for-initializing-PHY.patch b/target/linux/generic/backport-4.9/071-v4.10-0003-net-bgmac-use-PHY-subsystem-for-initializing-PHY.patch
new file mode 100644
index 0000000000..d1be3e3f4c
--- /dev/null
+++ b/target/linux/generic/backport-4.9/071-v4.10-0003-net-bgmac-use-PHY-subsystem-for-initializing-PHY.patch
@@ -0,0 +1,53 @@
+From 8e6f31baba7e2c13ab7e954fe6179420a7545a8b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 31 Jan 2017 19:37:56 +0100
+Subject: [PATCH 3/3] net: bgmac: use PHY subsystem for initializing PHY
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This adds support for using bgmac with PHYs supported by standalone PHY
+drivers. Having any PHY initialization in bgmac is hacky and shouldn't
+be extended but rather removed if anyone has hardware to test it.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma-mdio.c
+@@ -132,6 +132,10 @@ static void bcma_mdio_phy_init(struct bg
+ struct bcma_chipinfo *ci = &bgmac->bcma.core->bus->chipinfo;
+ u8 i;
+
++ /* For some legacy hardware we do chipset-based PHY initialization here
++ * without even detecting PHY ID. It's hacky and should be cleaned as
++ * soon as someone can test it.
++ */
+ if (ci->id == BCMA_CHIP_ID_BCM5356) {
+ for (i = 0; i < 5; i++) {
+ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x008b);
+@@ -140,6 +144,7 @@ static void bcma_mdio_phy_init(struct bg
+ bcma_mdio_phy_write(bgmac, i, 0x12, 0x2aaa);
+ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b);
+ }
++ return;
+ }
+ if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg != 10) ||
+ (ci->id == BCMA_CHIP_ID_BCM4749 && ci->pkg != 10) ||
+@@ -161,7 +166,12 @@ static void bcma_mdio_phy_init(struct bg
+ bcma_mdio_phy_write(bgmac, i, 0x17, 0x9273);
+ bcma_mdio_phy_write(bgmac, i, 0x1f, 0x000b);
+ }
++ return;
+ }
++
++ /* For all other hw do initialization using PHY subsystem. */
++ if (bgmac->net_dev && bgmac->net_dev->phydev)
++ phy_init_hw(bgmac->net_dev->phydev);
+ }
+
+ /* http://bcm-v4.sipsolutions.net/mac-gbit/gmac/chipphyreset */
diff --git a/target/linux/generic/backport-4.9/072-bcma-from-4.12.patch b/target/linux/generic/backport-4.9/072-bcma-from-4.12.patch
new file mode 100644
index 0000000000..23f2656ab5
--- /dev/null
+++ b/target/linux/generic/backport-4.9/072-bcma-from-4.12.patch
@@ -0,0 +1,47 @@
+--- a/drivers/bcma/driver_gpio.c
++++ b/drivers/bcma/driver_gpio.c
+@@ -185,8 +185,7 @@ int bcma_gpio_init(struct bcma_drv_cc *c
+ chip->owner = THIS_MODULE;
+ chip->parent = bcma_bus_get_host_dev(bus);
+ #if IS_BUILTIN(CONFIG_OF)
+- if (cc->core->bus->hosttype == BCMA_HOSTTYPE_SOC)
+- chip->of_node = cc->core->dev.of_node;
++ chip->of_node = cc->core->dev.of_node;
+ #endif
+ switch (bus->chipinfo.id) {
+ case BCMA_CHIP_ID_BCM4707:
+--- a/drivers/bcma/main.c
++++ b/drivers/bcma/main.c
+@@ -201,9 +201,6 @@ static void bcma_of_fill_device(struct d
+ {
+ struct device_node *node;
+
+- if (!IS_ENABLED(CONFIG_OF_IRQ))
+- return;
+-
+ node = bcma_of_find_child_device(parent, core);
+ if (node)
+ core->dev.of_node = node;
+@@ -242,19 +239,18 @@ void bcma_prepare_core(struct bcma_bus *
+ core->dev.release = bcma_release_core_dev;
+ core->dev.bus = &bcma_bus_type;
+ dev_set_name(&core->dev, "bcma%d:%d", bus->num, core->core_index);
++ core->dev.parent = bcma_bus_get_host_dev(bus);
++ if (core->dev.parent)
++ bcma_of_fill_device(core->dev.parent, core);
+
+ switch (bus->hosttype) {
+ case BCMA_HOSTTYPE_PCI:
+- core->dev.parent = &bus->host_pci->dev;
+ core->dma_dev = &bus->host_pci->dev;
+ core->irq = bus->host_pci->irq;
+ break;
+ case BCMA_HOSTTYPE_SOC:
+ if (IS_ENABLED(CONFIG_OF) && bus->host_pdev) {
+ core->dma_dev = &bus->host_pdev->dev;
+- core->dev.parent = &bus->host_pdev->dev;
+- if (core->dev.parent)
+- bcma_of_fill_device(core->dev.parent, core);
+ } else {
+ core->dev.dma_mask = &core->dev.coherent_dma_mask;
+ core->dma_dev = &core->dev;
diff --git a/target/linux/generic/backport-4.9/075-v4.10-0001-net-phy-broadcom-Update-Auxiliary-Control-Register-m.patch b/target/linux/generic/backport-4.9/075-v4.10-0001-net-phy-broadcom-Update-Auxiliary-Control-Register-m.patch
new file mode 100644
index 0000000000..9279a7c84c
--- /dev/null
+++ b/target/linux/generic/backport-4.9/075-v4.10-0001-net-phy-broadcom-Update-Auxiliary-Control-Register-m.patch
@@ -0,0 +1,34 @@
+From: Xo Wang <xow@google.com>
+Date: Fri, 21 Oct 2016 10:20:12 -0700
+Subject: [PATCH] net: phy: broadcom: Update Auxiliary Control Register macros
+
+Add the RXD-to-RXC skew (delay) time bit in the Miscellaneous Control
+shadow register and a mask for the shadow selector field.
+
+Remove a re-definition of MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL.
+
+Signed-off-by: Xo Wang <xow@google.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Joel Stanley <joel@jms.id.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -101,6 +101,7 @@
+ * AUXILIARY CONTROL SHADOW ACCESS REGISTERS. (PHY REG 0x18)
+ */
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
++#define MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW 0x0100
+ #define MII_BCM54XX_AUXCTL_ACTL_TX_6DB 0x0400
+ #define MII_BCM54XX_AUXCTL_ACTL_SMDSP_ENA 0x0800
+
+@@ -109,7 +110,7 @@
+ #define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
+
+-#define MII_BCM54XX_AUXCTL_SHDWSEL_AUXCTL 0x0000
++#define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007
+
+ /*
+ * Broadcom LED source encodings. These are used in BCM5461, BCM5481,
diff --git a/target/linux/generic/backport-4.9/075-v4.10-0002-net-phy-broadcom-Add-support-for-BCM54612E.patch b/target/linux/generic/backport-4.9/075-v4.10-0002-net-phy-broadcom-Add-support-for-BCM54612E.patch
new file mode 100644
index 0000000000..4caa7b193d
--- /dev/null
+++ b/target/linux/generic/backport-4.9/075-v4.10-0002-net-phy-broadcom-Add-support-for-BCM54612E.patch
@@ -0,0 +1,94 @@
+From: Xo Wang <xow@google.com>
+Date: Fri, 21 Oct 2016 10:20:13 -0700
+Subject: [PATCH] net: phy: broadcom: Add support for BCM54612E
+
+This PHY has internal delays enabled after reset. This clears the
+internal delay enables unless the interface specifically requests them.
+
+Signed-off-by: Xo Wang <xow@google.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Joel Stanley <joel@jms.id.au>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -337,6 +337,41 @@ static int bcm5481_config_aneg(struct ph
+ return ret;
+ }
+
++static int bcm54612e_config_aneg(struct phy_device *phydev)
++{
++ int ret;
++
++ /* First, auto-negotiate. */
++ ret = genphy_config_aneg(phydev);
++
++ /* Clear TX internal delay unless requested. */
++ if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
++ (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
++ /* Disable TXD to GTXCLK clock delay (default set) */
++ /* Bit 9 is the only field in shadow register 00011 */
++ bcm_phy_write_shadow(phydev, 0x03, 0);
++ }
++
++ /* Clear RX internal delay unless requested. */
++ if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
++ (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
++ u16 reg;
++
++ /* Errata: reads require filling in the write selector field */
++ bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
++ MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC);
++ reg = phy_read(phydev, MII_BCM54XX_AUX_CTL);
++ /* Disable RXD to RXC delay (default set) */
++ reg &= ~MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW;
++ /* Clear shadow selector field */
++ reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
++ bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
++ MII_BCM54XX_AUXCTL_MISC_WREN | reg);
++ }
++
++ return ret;
++}
++
+ static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
+ {
+ int val;
+@@ -485,6 +520,18 @@ static struct phy_driver broadcom_driver
+ .ack_interrupt = bcm_phy_ack_intr,
+ .config_intr = bcm_phy_config_intr,
+ }, {
++ .phy_id = PHY_ID_BCM54612E,
++ .phy_id_mask = 0xfffffff0,
++ .name = "Broadcom BCM54612E",
++ .features = PHY_GBIT_FEATURES |
++ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
++ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
++ .config_init = bcm54xx_config_init,
++ .config_aneg = bcm54612e_config_aneg,
++ .read_status = genphy_read_status,
++ .ack_interrupt = bcm_phy_ack_intr,
++ .config_intr = bcm_phy_config_intr,
++}, {
+ .phy_id = PHY_ID_BCM54616S,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM54616S",
+@@ -600,6 +647,7 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_BCM5411, 0xfffffff0 },
+ { PHY_ID_BCM5421, 0xfffffff0 },
+ { PHY_ID_BCM5461, 0xfffffff0 },
++ { PHY_ID_BCM54612E, 0xfffffff0 },
+ { PHY_ID_BCM54616S, 0xfffffff0 },
+ { PHY_ID_BCM5464, 0xfffffff0 },
+ { PHY_ID_BCM5481, 0xfffffff0 },
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -18,6 +18,7 @@
+ #define PHY_ID_BCM5421 0x002060e0
+ #define PHY_ID_BCM5464 0x002060b0
+ #define PHY_ID_BCM5461 0x002060c0
++#define PHY_ID_BCM54612E 0x03625e60
+ #define PHY_ID_BCM54616S 0x03625d10
+ #define PHY_ID_BCM57780 0x03625d90
+
diff --git a/target/linux/generic/backport-4.9/075-v4.10-0003-net-phy-broadcom-add-bcm54xx_auxctl_read.patch b/target/linux/generic/backport-4.9/075-v4.10-0003-net-phy-broadcom-add-bcm54xx_auxctl_read.patch
new file mode 100644
index 0000000000..c9b3b5911e
--- /dev/null
+++ b/target/linux/generic/backport-4.9/075-v4.10-0003-net-phy-broadcom-add-bcm54xx_auxctl_read.patch
@@ -0,0 +1,41 @@
+From: Jon Mason <jon.mason@broadcom.com>
+Date: Fri, 4 Nov 2016 01:10:56 -0400
+Subject: [PATCH] net: phy: broadcom: add bcm54xx_auxctl_read
+
+Add a helper function to read the AUXCTL register for the BCM54xx. This
+mirrors the bcm54xx_auxctl_write function already present in the code.
+
+Signed-off-by: Jon Mason <jon.mason@broadcom.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -30,6 +30,16 @@ MODULE_DESCRIPTION("Broadcom PHY driver"
+ MODULE_AUTHOR("Maciej W. Rozycki");
+ MODULE_LICENSE("GPL");
+
++static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
++{
++ /* The register must be written to both the Shadow Register Select and
++ * the Shadow Read Register Selector
++ */
++ phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum |
++ regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
++ return phy_read(phydev, MII_BCM54XX_AUX_CTL);
++}
++
+ static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
+ {
+ return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -110,6 +110,7 @@
+ #define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
+ #define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
++#define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12
+
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007
+
diff --git a/target/linux/generic/backport-4.9/075-v4.10-0004-net-phy-broadcom-Add-BCM54810-PHY-entry.patch b/target/linux/generic/backport-4.9/075-v4.10-0004-net-phy-broadcom-Add-BCM54810-PHY-entry.patch
new file mode 100644
index 0000000000..3d61ec6369
--- /dev/null
+++ b/target/linux/generic/backport-4.9/075-v4.10-0004-net-phy-broadcom-Add-BCM54810-PHY-entry.patch
@@ -0,0 +1,176 @@
+From: Jon Mason <jon.mason@broadcom.com>
+Date: Fri, 4 Nov 2016 01:10:58 -0400
+Subject: [PATCH] net: phy: broadcom: Add BCM54810 PHY entry
+
+The BCM54810 PHY requires some semi-unique configuration, which results
+in some additional configuration in addition to the standard config.
+Also, some users of the BCM54810 require the PHY lanes to be swapped.
+Since there is no way to detect this, add a device tree query to see if
+it is applicable.
+
+Inspired-by: Vikas Soni <vsoni@broadcom.com>
+Signed-off-by: Jon Mason <jon.mason@broadcom.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Reviewed-by: Andrew Lunn <andrew@lunn.ch>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -18,7 +18,7 @@
+ #include <linux/module.h>
+ #include <linux/phy.h>
+ #include <linux/brcmphy.h>
+-
++#include <linux/of.h>
+
+ #define BRCM_PHY_MODEL(phydev) \
+ ((phydev)->drv->phy_id & (phydev)->drv->phy_id_mask)
+@@ -45,6 +45,34 @@ static int bcm54xx_auxctl_write(struct p
+ return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
+ }
+
++static int bcm54810_config(struct phy_device *phydev)
++{
++ int rc, val;
++
++ val = bcm_phy_read_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL);
++ val &= ~BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN;
++ rc = bcm_phy_write_exp(phydev, BCM54810_EXP_BROADREACH_LRE_MISC_CTL,
++ val);
++ if (rc < 0)
++ return rc;
++
++ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
++ val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
++ val |= MII_BCM54XX_AUXCTL_MISC_WREN;
++ rc = bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
++ val);
++ if (rc < 0)
++ return rc;
++
++ val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
++ val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
++ rc = bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
++ if (rc < 0)
++ return rc;
++
++ return 0;
++}
++
+ /* Needs SMDSP clock enabled via bcm54xx_phydsp_config() */
+ static int bcm50610_a0_workaround(struct phy_device *phydev)
+ {
+@@ -217,6 +245,12 @@ static int bcm54xx_config_init(struct ph
+ (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
+ bcm54xx_adjust_rxrefclk(phydev);
+
++ if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
++ err = bcm54810_config(phydev);
++ if (err)
++ return err;
++ }
++
+ bcm54xx_phydsp_config(phydev);
+
+ return 0;
+@@ -314,6 +348,7 @@ static int bcm5482_read_status(struct ph
+
+ static int bcm5481_config_aneg(struct phy_device *phydev)
+ {
++ struct device_node *np = phydev->mdio.dev.of_node;
+ int ret;
+
+ /* Aneg firsly. */
+@@ -344,6 +379,14 @@ static int bcm5481_config_aneg(struct ph
+ phy_write(phydev, 0x18, reg);
+ }
+
++ if (of_property_read_bool(np, "enet-phy-lane-swap")) {
++ /* Lane Swap - Undocumented register...magic! */
++ ret = bcm_phy_write_exp(phydev, MII_BCM54XX_EXP_SEL_ER + 0x9,
++ 0x11B);
++ if (ret < 0)
++ return ret;
++ }
++
+ return ret;
+ }
+
+@@ -578,6 +621,18 @@ static struct phy_driver broadcom_driver
+ .ack_interrupt = bcm_phy_ack_intr,
+ .config_intr = bcm_phy_config_intr,
+ }, {
++ .phy_id = PHY_ID_BCM54810,
++ .phy_id_mask = 0xfffffff0,
++ .name = "Broadcom BCM54810",
++ .features = PHY_GBIT_FEATURES |
++ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
++ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
++ .config_init = bcm54xx_config_init,
++ .config_aneg = bcm5481_config_aneg,
++ .read_status = genphy_read_status,
++ .ack_interrupt = bcm_phy_ack_intr,
++ .config_intr = bcm_phy_config_intr,
++}, {
+ .phy_id = PHY_ID_BCM5482,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM5482",
+@@ -661,6 +716,7 @@ static struct mdio_device_id __maybe_unu
+ { PHY_ID_BCM54616S, 0xfffffff0 },
+ { PHY_ID_BCM5464, 0xfffffff0 },
+ { PHY_ID_BCM5481, 0xfffffff0 },
++ { PHY_ID_BCM54810, 0xfffffff0 },
+ { PHY_ID_BCM5482, 0xfffffff0 },
+ { PHY_ID_BCM50610, 0xfffffff0 },
+ { PHY_ID_BCM50610M, 0xfffffff0 },
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -204,7 +204,7 @@ config BROADCOM_PHY
+ select BCM_NET_PHYLIB
+ ---help---
+ Currently supports the BCM5411, BCM5421, BCM5461, BCM54616S, BCM5464,
+- BCM5481 and BCM5482 PHYs.
++ BCM5481, BCM54810 and BCM5482 PHYs.
+
+ config CICADA_PHY
+ tristate "Cicada PHYs"
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -13,6 +13,7 @@
+ #define PHY_ID_BCM5241 0x0143bc30
+ #define PHY_ID_BCMAC131 0x0143bc70
+ #define PHY_ID_BCM5481 0x0143bca0
++#define PHY_ID_BCM54810 0x03625d00
+ #define PHY_ID_BCM5482 0x0143bcb0
+ #define PHY_ID_BCM5411 0x00206070
+ #define PHY_ID_BCM5421 0x002060e0
+@@ -56,6 +57,7 @@
+ #define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00002000
+ #define PHY_BRCM_CLEAR_RGMII_MODE 0x00004000
+ #define PHY_BRCM_DIS_TXCRXC_NOENRGY 0x00008000
++
+ /* Broadcom BCM7xxx specific workarounds */
+ #define PHY_BRCM_7XXX_REV(x) (((x) >> 8) & 0xff)
+ #define PHY_BRCM_7XXX_PATCH(x) ((x) & 0xff)
+@@ -111,6 +113,7 @@
+ #define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12
++#define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN (1 << 8)
+
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MASK 0x0007
+
+@@ -192,6 +195,12 @@
+ #define BCM5482_SSD_SGMII_SLAVE_EN 0x0002 /* Slave mode enable */
+ #define BCM5482_SSD_SGMII_SLAVE_AD 0x0001 /* Slave auto-detection */
+
++/* BCM54810 Registers */
++#define BCM54810_EXP_BROADREACH_LRE_MISC_CTL (MII_BCM54XX_EXP_SEL_ER + 0x90)
++#define BCM54810_EXP_BROADREACH_LRE_MISC_CTL_EN (1 << 0)
++#define BCM54810_SHD_CLK_CTL 0x3
++#define BCM54810_SHD_CLK_CTL_GTXCLK_EN (1 << 9)
++
+
+ /*****************************************************************************/
+ /* Fast Ethernet Transceiver definitions. */
diff --git a/target/linux/generic/backport-4.9/075-v4.10-0005-net-phy-broadcom-Move-bcm54xx_auxctl_-read-write-to-.patch b/target/linux/generic/backport-4.9/075-v4.10-0005-net-phy-broadcom-Move-bcm54xx_auxctl_-read-write-to-.patch
new file mode 100644
index 0000000000..03013a0197
--- /dev/null
+++ b/target/linux/generic/backport-4.9/075-v4.10-0005-net-phy-broadcom-Move-bcm54xx_auxctl_-read-write-to-.patch
@@ -0,0 +1,74 @@
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Tue, 22 Nov 2016 11:40:54 -0800
+Subject: [PATCH] net: phy: broadcom: Move bcm54xx_auxctl_{read, write} to
+ common library
+
+We are going to need these functions to implement support for Broadcom
+Wirespeed, aka downshift.
+
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/bcm-phy-lib.c
++++ b/drivers/net/phy/bcm-phy-lib.c
+@@ -50,6 +50,23 @@ int bcm_phy_read_exp(struct phy_device *
+ }
+ EXPORT_SYMBOL_GPL(bcm_phy_read_exp);
+
++int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
++{
++ /* The register must be written to both the Shadow Register Select and
++ * the Shadow Read Register Selector
++ */
++ phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum |
++ regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
++ return phy_read(phydev, MII_BCM54XX_AUX_CTL);
++}
++EXPORT_SYMBOL_GPL(bcm54xx_auxctl_read);
++
++int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
++{
++ return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
++}
++EXPORT_SYMBOL(bcm54xx_auxctl_write);
++
+ int bcm_phy_write_misc(struct phy_device *phydev,
+ u16 reg, u16 chl, u16 val)
+ {
+--- a/drivers/net/phy/bcm-phy-lib.h
++++ b/drivers/net/phy/bcm-phy-lib.h
+@@ -19,6 +19,9 @@
+ int bcm_phy_write_exp(struct phy_device *phydev, u16 reg, u16 val);
+ int bcm_phy_read_exp(struct phy_device *phydev, u16 reg);
+
++int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val);
++int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum);
++
+ int bcm_phy_write_misc(struct phy_device *phydev,
+ u16 reg, u16 chl, u16 value);
+ int bcm_phy_read_misc(struct phy_device *phydev,
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -30,21 +30,6 @@ MODULE_DESCRIPTION("Broadcom PHY driver"
+ MODULE_AUTHOR("Maciej W. Rozycki");
+ MODULE_LICENSE("GPL");
+
+-static int bcm54xx_auxctl_read(struct phy_device *phydev, u16 regnum)
+-{
+- /* The register must be written to both the Shadow Register Select and
+- * the Shadow Read Register Selector
+- */
+- phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum |
+- regnum << MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT);
+- return phy_read(phydev, MII_BCM54XX_AUX_CTL);
+-}
+-
+-static int bcm54xx_auxctl_write(struct phy_device *phydev, u16 regnum, u16 val)
+-{
+- return phy_write(phydev, MII_BCM54XX_AUX_CTL, regnum | val);
+-}
+-
+ static int bcm54810_config(struct phy_device *phydev)
+ {
+ int rc, val;
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0001-net-phy-broadcom-Allow-enabling-or-disabling-of-EEE.patch b/target/linux/generic/backport-4.9/076-v4.11-0001-net-phy-broadcom-Allow-enabling-or-disabling-of-EEE.patch
new file mode 100644
index 0000000000..96fb3da5b0
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0001-net-phy-broadcom-Allow-enabling-or-disabling-of-EEE.patch
@@ -0,0 +1,87 @@
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Tue, 22 Nov 2016 11:40:56 -0800
+Subject: [PATCH] net: phy: broadcom: Allow enabling or disabling of EEE
+
+In preparation for adding support for Wirespeed/downshift, we need to
+change bcm_phy_eee_enable() to allow enabling or disabling EEE, so make
+the function take an extra enable/disable boolean parameter and rename
+it to illustrate it sets EEE, not necessarily just enables it.
+
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/bcm7xxx.c
++++ b/drivers/net/phy/bcm7xxx.c
+@@ -199,7 +199,7 @@ static int bcm7xxx_28nm_config_init(stru
+ if (ret)
+ return ret;
+
+- ret = bcm_phy_enable_eee(phydev);
++ ret = bcm_phy_set_eee(phydev, true);
+ if (ret)
+ return ret;
+
+--- a/drivers/net/phy/bcm-cygnus.c
++++ b/drivers/net/phy/bcm-cygnus.c
+@@ -104,7 +104,7 @@ static int bcm_cygnus_config_init(struct
+ return rc;
+
+ /* Advertise EEE */
+- rc = bcm_phy_enable_eee(phydev);
++ rc = bcm_phy_set_eee(phydev, true);
+ if (rc)
+ return rc;
+
+--- a/drivers/net/phy/bcm-phy-lib.c
++++ b/drivers/net/phy/bcm-phy-lib.c
+@@ -195,7 +195,7 @@ int bcm_phy_enable_apd(struct phy_device
+ }
+ EXPORT_SYMBOL_GPL(bcm_phy_enable_apd);
+
+-int bcm_phy_enable_eee(struct phy_device *phydev)
++int bcm_phy_set_eee(struct phy_device *phydev, bool enable)
+ {
+ int val;
+
+@@ -205,7 +205,10 @@ int bcm_phy_enable_eee(struct phy_device
+ if (val < 0)
+ return val;
+
+- val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
++ if (enable)
++ val |= LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X;
++ else
++ val &= ~(LPI_FEATURE_EN | LPI_FEATURE_EN_DIG1000X);
+
+ phy_write_mmd_indirect(phydev, BRCM_CL45VEN_EEE_CONTROL,
+ MDIO_MMD_AN, (u32)val);
+@@ -216,14 +219,17 @@ int bcm_phy_enable_eee(struct phy_device
+ if (val < 0)
+ return val;
+
+- val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
++ if (enable)
++ val |= (MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
++ else
++ val &= ~(MDIO_AN_EEE_ADV_100TX | MDIO_AN_EEE_ADV_1000T);
+
+ phy_write_mmd_indirect(phydev, BCM_CL45VEN_EEE_ADV,
+ MDIO_MMD_AN, (u32)val);
+
+ return 0;
+ }
+-EXPORT_SYMBOL_GPL(bcm_phy_enable_eee);
++EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
+
+ MODULE_DESCRIPTION("Broadcom PHY Library");
+ MODULE_LICENSE("GPL v2");
+--- a/drivers/net/phy/bcm-phy-lib.h
++++ b/drivers/net/phy/bcm-phy-lib.h
+@@ -36,5 +36,5 @@ int bcm_phy_config_intr(struct phy_devic
+
+ int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down);
+
+-int bcm_phy_enable_eee(struct phy_device *phydev);
++int bcm_phy_set_eee(struct phy_device *phydev, bool enable);
+ #endif /* _LINUX_BCM_PHY_LIB_H */
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0002-net-phy-broadcom-Add-support-code-for-reading-PHY-co.patch b/target/linux/generic/backport-4.9/076-v4.11-0002-net-phy-broadcom-Add-support-code-for-reading-PHY-co.patch
new file mode 100644
index 0000000000..1bdecf5729
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0002-net-phy-broadcom-Add-support-code-for-reading-PHY-co.patch
@@ -0,0 +1,125 @@
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Tue, 29 Nov 2016 09:57:17 -0800
+Subject: [PATCH] net: phy: broadcom: Add support code for reading PHY counters
+
+Broadcom PHYs expose a number of PHY error counters: receive errors,
+false carrier sense, SerDes BER count, local and remote receive errors.
+Add support code to allow retrieving these error counters. Since the
+Broadcom PHY library code is used by several drivers, make it possible
+for them to specify the storage for the software copy of the statistics.
+
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/bcm-phy-lib.c
++++ b/drivers/net/phy/bcm-phy-lib.c
+@@ -17,6 +17,7 @@
+ #include <linux/mdio.h>
+ #include <linux/module.h>
+ #include <linux/phy.h>
++#include <linux/ethtool.h>
+
+ #define MII_BCM_CHANNEL_WIDTH 0x2000
+ #define BCM_CL45VEN_EEE_ADV 0x3c
+@@ -231,6 +232,75 @@ int bcm_phy_set_eee(struct phy_device *p
+ }
+ EXPORT_SYMBOL_GPL(bcm_phy_set_eee);
+
++struct bcm_phy_hw_stat {
++ const char *string;
++ u8 reg;
++ u8 shift;
++ u8 bits;
++};
++
++/* Counters freeze at either 0xffff or 0xff, better than nothing */
++static const struct bcm_phy_hw_stat bcm_phy_hw_stats[] = {
++ { "phy_receive_errors", MII_BRCM_CORE_BASE12, 0, 16 },
++ { "phy_serdes_ber_errors", MII_BRCM_CORE_BASE13, 8, 8 },
++ { "phy_false_carrier_sense_errors", MII_BRCM_CORE_BASE13, 0, 8 },
++ { "phy_local_rcvr_nok", MII_BRCM_CORE_BASE14, 8, 8 },
++ { "phy_remote_rcv_nok", MII_BRCM_CORE_BASE14, 0, 8 },
++};
++
++int bcm_phy_get_sset_count(struct phy_device *phydev)
++{
++ return ARRAY_SIZE(bcm_phy_hw_stats);
++}
++EXPORT_SYMBOL_GPL(bcm_phy_get_sset_count);
++
++void bcm_phy_get_strings(struct phy_device *phydev, u8 *data)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
++ memcpy(data + i * ETH_GSTRING_LEN,
++ bcm_phy_hw_stats[i].string, ETH_GSTRING_LEN);
++}
++EXPORT_SYMBOL_GPL(bcm_phy_get_strings);
++
++#ifndef UINT64_MAX
++#define UINT64_MAX (u64)(~((u64)0))
++#endif
++
++/* Caller is supposed to provide appropriate storage for the library code to
++ * access the shadow copy
++ */
++static u64 bcm_phy_get_stat(struct phy_device *phydev, u64 *shadow,
++ unsigned int i)
++{
++ struct bcm_phy_hw_stat stat = bcm_phy_hw_stats[i];
++ int val;
++ u64 ret;
++
++ val = phy_read(phydev, stat.reg);
++ if (val < 0) {
++ ret = UINT64_MAX;
++ } else {
++ val >>= stat.shift;
++ val = val & ((1 << stat.bits) - 1);
++ shadow[i] += val;
++ ret = shadow[i];
++ }
++
++ return ret;
++}
++
++void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
++ struct ethtool_stats *stats, u64 *data)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(bcm_phy_hw_stats); i++)
++ data[i] = bcm_phy_get_stat(phydev, shadow, i);
++}
++EXPORT_SYMBOL_GPL(bcm_phy_get_stats);
++
+ MODULE_DESCRIPTION("Broadcom PHY Library");
+ MODULE_LICENSE("GPL v2");
+ MODULE_AUTHOR("Broadcom Corporation");
+--- a/drivers/net/phy/bcm-phy-lib.h
++++ b/drivers/net/phy/bcm-phy-lib.h
+@@ -37,4 +37,10 @@ int bcm_phy_config_intr(struct phy_devic
+ int bcm_phy_enable_apd(struct phy_device *phydev, bool dll_pwr_down);
+
+ int bcm_phy_set_eee(struct phy_device *phydev, bool enable);
++
++int bcm_phy_get_sset_count(struct phy_device *phydev);
++void bcm_phy_get_strings(struct phy_device *phydev, u8 *data);
++void bcm_phy_get_stats(struct phy_device *phydev, u64 *shadow,
++ struct ethtool_stats *stats, u64 *data);
++
+ #endif /* _LINUX_BCM_PHY_LIB_H */
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -234,6 +234,9 @@
+ #define LPI_FEATURE_EN_DIG1000X 0x4000
+
+ /* Core register definitions*/
++#define MII_BRCM_CORE_BASE12 0x12
++#define MII_BRCM_CORE_BASE13 0x13
++#define MII_BRCM_CORE_BASE14 0x14
+ #define MII_BRCM_CORE_BASE1E 0x1E
+ #define MII_BRCM_CORE_EXPB0 0xB0
+ #define MII_BRCM_CORE_EXPB1 0xB1
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0003-net-phy-bcm7xxx-Add-entry-for-BCM7278.patch b/target/linux/generic/backport-4.9/076-v4.11-0003-net-phy-bcm7xxx-Add-entry-for-BCM7278.patch
new file mode 100644
index 0000000000..8034f4b217
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0003-net-phy-bcm7xxx-Add-entry-for-BCM7278.patch
@@ -0,0 +1,38 @@
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Fri, 20 Jan 2017 12:36:33 -0800
+Subject: [PATCH] net: phy: bcm7xxx: Add entry for BCM7278
+
+Add support for the BCM7278 28nm process Gigabit Ethernet PHY.
+
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/bcm7xxx.c
++++ b/drivers/net/phy/bcm7xxx.c
+@@ -334,6 +334,7 @@ static int bcm7xxx_suspend(struct phy_de
+
+ static struct phy_driver bcm7xxx_driver[] = {
+ BCM7XXX_28NM_GPHY(PHY_ID_BCM7250, "Broadcom BCM7250"),
++ BCM7XXX_28NM_GPHY(PHY_ID_BCM7278, "Broadcom BCM7278"),
+ BCM7XXX_28NM_GPHY(PHY_ID_BCM7364, "Broadcom BCM7364"),
+ BCM7XXX_28NM_GPHY(PHY_ID_BCM7366, "Broadcom BCM7366"),
+ BCM7XXX_28NM_GPHY(PHY_ID_BCM7439, "Broadcom BCM7439"),
+@@ -348,6 +349,7 @@ static struct phy_driver bcm7xxx_driver[
+
+ static struct mdio_device_id __maybe_unused bcm7xxx_tbl[] = {
+ { PHY_ID_BCM7250, 0xfffffff0, },
++ { PHY_ID_BCM7278, 0xfffffff0, },
+ { PHY_ID_BCM7364, 0xfffffff0, },
+ { PHY_ID_BCM7366, 0xfffffff0, },
+ { PHY_ID_BCM7346, 0xfffffff0, },
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -24,6 +24,7 @@
+ #define PHY_ID_BCM57780 0x03625d90
+
+ #define PHY_ID_BCM7250 0xae025280
++#define PHY_ID_BCM7278 0xae0251a0
+ #define PHY_ID_BCM7364 0xae025260
+ #define PHY_ID_BCM7366 0x600d8490
+ #define PHY_ID_BCM7346 0x600d8650
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0004-net-phy-bcm7xxx-Implement-EGPHY-workaround-for-7278.patch b/target/linux/generic/backport-4.9/076-v4.11-0004-net-phy-bcm7xxx-Implement-EGPHY-workaround-for-7278.patch
new file mode 100644
index 0000000000..7aa4ee2c79
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0004-net-phy-bcm7xxx-Implement-EGPHY-workaround-for-7278.patch
@@ -0,0 +1,68 @@
+From: Florian Fainelli <f.fainelli@gmail.com>
+Date: Fri, 20 Jan 2017 12:36:34 -0800
+Subject: [PATCH] net: phy: bcm7xxx: Implement EGPHY workaround for 7278
+
+Implement the HW design team recommended workaround in for 7278. Since
+the GPHY now returns its revision information in MII_PHYS_ID[23] we need
+to check whether the revision provided in flags is 0 or not.
+
+Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/bcm7xxx.c
++++ b/drivers/net/phy/bcm7xxx.c
+@@ -163,12 +163,43 @@ static int bcm7xxx_28nm_e0_plus_afe_conf
+ return 0;
+ }
+
++static int bcm7xxx_28nm_a0_patch_afe_config_init(struct phy_device *phydev)
++{
++ /* +1 RC_CAL codes for RL centering for both LT and HT conditions */
++ bcm_phy_write_misc(phydev, AFE_RXCONFIG_2, 0xd003);
++
++ /* Cut master bias current by 2% to compensate for RC_CAL offset */
++ bcm_phy_write_misc(phydev, DSP_TAP10, 0x791b);
++
++ /* Improve hybrid leakage */
++ bcm_phy_write_misc(phydev, AFE_HPF_TRIM_OTHERS, 0x10e3);
++
++ /* Change rx_on_tune 8 to 0xf */
++ bcm_phy_write_misc(phydev, 0x21, 0x2, 0x87f6);
++
++ /* Change 100Tx EEE bandwidth */
++ bcm_phy_write_misc(phydev, 0x22, 0x2, 0x017d);
++
++ /* Enable ffe zero detection for Vitesse interoperability */
++ bcm_phy_write_misc(phydev, 0x26, 0x2, 0x0015);
++
++ r_rc_cal_reset(phydev);
++
++ return 0;
++}
++
+ static int bcm7xxx_28nm_config_init(struct phy_device *phydev)
+ {
+ u8 rev = PHY_BRCM_7XXX_REV(phydev->dev_flags);
+ u8 patch = PHY_BRCM_7XXX_PATCH(phydev->dev_flags);
+ int ret = 0;
+
++ /* Newer devices have moved the revision information back into a
++ * standard location in MII_PHYS_ID[23]
++ */
++ if (rev == 0)
++ rev = phydev->phy_id & ~phydev->drv->phy_id_mask;
++
+ pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n",
+ phydev_name(phydev), phydev->drv->name, rev, patch);
+
+@@ -192,6 +223,9 @@ static int bcm7xxx_28nm_config_init(stru
+ case 0x10:
+ ret = bcm7xxx_28nm_e0_plus_afe_config_init(phydev);
+ break;
++ case 0x01:
++ ret = bcm7xxx_28nm_a0_patch_afe_config_init(phydev);
++ break;
+ default:
+ break;
+ }
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0005-net-phy-broadcom-use-auxctl-reading-helper-in-BCM546.patch b/target/linux/generic/backport-4.9/076-v4.11-0005-net-phy-broadcom-use-auxctl-reading-helper-in-BCM546.patch
new file mode 100644
index 0000000000..554e3117d8
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0005-net-phy-broadcom-use-auxctl-reading-helper-in-BCM546.patch
@@ -0,0 +1,45 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Wed, 25 Jan 2017 21:00:25 +0100
+Subject: [PATCH] net: phy: broadcom: use auxctl reading helper in BCM54612E
+ code
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Starting with commit 5b4e29005123 ("net: phy: broadcom: add
+bcm54xx_auxctl_read") we have a reading helper so use it and avoid code
+duplication.
+It also means we don't need MII_BCM54XX_AUXCTL_SHDWSEL_MISC define as
+it's the same as MII_BCM54XX_AUXCTL_SHDWSEL_MISC just for reading needs
+(same value shifted by 12 bits).
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -395,10 +395,8 @@ static int bcm54612e_config_aneg(struct
+ (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
+ u16 reg;
+
+- /* Errata: reads require filling in the write selector field */
+- bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
+- MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC);
+- reg = phy_read(phydev, MII_BCM54XX_AUX_CTL);
++ reg = bcm54xx_auxctl_read(phydev,
++ MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
+ /* Disable RXD to RXC delay (default set) */
+ reg &= ~MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW;
+ /* Clear shadow selector field */
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -111,7 +111,6 @@
+
+ #define MII_BCM54XX_AUXCTL_MISC_WREN 0x8000
+ #define MII_BCM54XX_AUXCTL_MISC_FORCE_AMDIX 0x0200
+-#define MII_BCM54XX_AUXCTL_MISC_RDSEL_MISC 0x7000
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC 0x0007
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_READ_SHIFT 12
+ #define MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN (1 << 8)
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0006-net-phy-broadcom-add-support-for-BCM54210E.patch b/target/linux/generic/backport-4.9/076-v4.11-0006-net-phy-broadcom-add-support-for-BCM54210E.patch
new file mode 100644
index 0000000000..5fa7e88c53
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0006-net-phy-broadcom-add-support-for-BCM54210E.patch
@@ -0,0 +1,89 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Fri, 27 Jan 2017 14:07:01 +0100
+Subject: [PATCH] net: phy: broadcom: add support for BCM54210E
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+It's Broadcom PHY simply described as single-port
+RGMII 10/100/1000BASE-T PHY. It requires disabling delay skew and GTXCLK
+bits.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -30,6 +30,22 @@ MODULE_DESCRIPTION("Broadcom PHY driver"
+ MODULE_AUTHOR("Maciej W. Rozycki");
+ MODULE_LICENSE("GPL");
+
++static int bcm54210e_config_init(struct phy_device *phydev)
++{
++ int val;
++
++ val = bcm54xx_auxctl_read(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
++ val &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
++ val |= MII_BCM54XX_AUXCTL_MISC_WREN;
++ bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC, val);
++
++ val = bcm_phy_read_shadow(phydev, BCM54810_SHD_CLK_CTL);
++ val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN;
++ bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val);
++
++ return 0;
++}
++
+ static int bcm54810_config(struct phy_device *phydev)
+ {
+ int rc, val;
+@@ -230,7 +246,11 @@ static int bcm54xx_config_init(struct ph
+ (phydev->dev_flags & PHY_BRCM_AUTO_PWRDWN_ENABLE))
+ bcm54xx_adjust_rxrefclk(phydev);
+
+- if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
++ if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54210E) {
++ err = bcm54210e_config_init(phydev);
++ if (err)
++ return err;
++ } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
+ err = bcm54810_config(phydev);
+ if (err)
+ return err;
+@@ -544,6 +564,17 @@ static struct phy_driver broadcom_driver
+ .ack_interrupt = bcm_phy_ack_intr,
+ .config_intr = bcm_phy_config_intr,
+ }, {
++ .phy_id = PHY_ID_BCM54210E,
++ .phy_id_mask = 0xfffffff0,
++ .name = "Broadcom BCM54210E",
++ .features = PHY_GBIT_FEATURES,
++ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
++ .config_init = bcm54xx_config_init,
++ .config_aneg = genphy_config_aneg,
++ .read_status = genphy_read_status,
++ .ack_interrupt = bcm_phy_ack_intr,
++ .config_intr = bcm_phy_config_intr,
++}, {
+ .phy_id = PHY_ID_BCM5461,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM5461",
+@@ -694,6 +725,7 @@ module_phy_driver(broadcom_drivers);
+ static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
+ { PHY_ID_BCM5411, 0xfffffff0 },
+ { PHY_ID_BCM5421, 0xfffffff0 },
++ { PHY_ID_BCM54210E, 0xfffffff0 },
+ { PHY_ID_BCM5461, 0xfffffff0 },
+ { PHY_ID_BCM54612E, 0xfffffff0 },
+ { PHY_ID_BCM54616S, 0xfffffff0 },
+--- a/include/linux/brcmphy.h
++++ b/include/linux/brcmphy.h
+@@ -17,6 +17,7 @@
+ #define PHY_ID_BCM5482 0x0143bcb0
+ #define PHY_ID_BCM5411 0x00206070
+ #define PHY_ID_BCM5421 0x002060e0
++#define PHY_ID_BCM54210E 0x600d84a0
+ #define PHY_ID_BCM5464 0x002060b0
+ #define PHY_ID_BCM5461 0x002060c0
+ #define PHY_ID_BCM54612E 0x03625e60
diff --git a/target/linux/generic/backport-4.9/076-v4.11-0007-net-phy-broadcom-rehook-BCM54612E-specific-init.patch b/target/linux/generic/backport-4.9/076-v4.11-0007-net-phy-broadcom-rehook-BCM54612E-specific-init.patch
new file mode 100644
index 0000000000..b7326c3df4
--- /dev/null
+++ b/target/linux/generic/backport-4.9/076-v4.11-0007-net-phy-broadcom-rehook-BCM54612E-specific-init.patch
@@ -0,0 +1,121 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Tue, 31 Jan 2017 22:54:54 +0100
+Subject: [PATCH] net: phy: broadcom: rehook BCM54612E specific init
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This extra BCM54612E code in PHY driver isn't really aneg specific. Even
+without it aneg works OK but the problem is no packets pass through PHY.
+
+Moreover putting this code inside config_aneg callback didn't allow
+resuming PHY correctly. When driver called phy_stop and phy_start it was
+putting PHY machine into RESUMING state. After that machine was
+switching into AN and NOLINK without ever calling phy_start_aneg. This
+prevented this extra setup from being called and PHY didn't work.
+
+This change has been verified to fix network on BCM47186B0 SoC device
+with BCM54612E.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -46,6 +46,34 @@ static int bcm54210e_config_init(struct
+ return 0;
+ }
+
++static int bcm54612e_config_init(struct phy_device *phydev)
++{
++ /* Clear TX internal delay unless requested. */
++ if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
++ (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
++ /* Disable TXD to GTXCLK clock delay (default set) */
++ /* Bit 9 is the only field in shadow register 00011 */
++ bcm_phy_write_shadow(phydev, 0x03, 0);
++ }
++
++ /* Clear RX internal delay unless requested. */
++ if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
++ (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
++ u16 reg;
++
++ reg = bcm54xx_auxctl_read(phydev,
++ MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
++ /* Disable RXD to RXC delay (default set) */
++ reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MISC_RGMII_SKEW_EN;
++ /* Clear shadow selector field */
++ reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
++ bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
++ MII_BCM54XX_AUXCTL_MISC_WREN | reg);
++ }
++
++ return 0;
++}
++
+ static int bcm54810_config(struct phy_device *phydev)
+ {
+ int rc, val;
+@@ -250,6 +278,10 @@ static int bcm54xx_config_init(struct ph
+ err = bcm54210e_config_init(phydev);
+ if (err)
+ return err;
++ } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54612E) {
++ err = bcm54612e_config_init(phydev);
++ if (err)
++ return err;
+ } else if (BRCM_PHY_MODEL(phydev) == PHY_ID_BCM54810) {
+ err = bcm54810_config(phydev);
+ if (err)
+@@ -395,39 +427,6 @@ static int bcm5481_config_aneg(struct ph
+ return ret;
+ }
+
+-static int bcm54612e_config_aneg(struct phy_device *phydev)
+-{
+- int ret;
+-
+- /* First, auto-negotiate. */
+- ret = genphy_config_aneg(phydev);
+-
+- /* Clear TX internal delay unless requested. */
+- if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
+- (phydev->interface != PHY_INTERFACE_MODE_RGMII_TXID)) {
+- /* Disable TXD to GTXCLK clock delay (default set) */
+- /* Bit 9 is the only field in shadow register 00011 */
+- bcm_phy_write_shadow(phydev, 0x03, 0);
+- }
+-
+- /* Clear RX internal delay unless requested. */
+- if ((phydev->interface != PHY_INTERFACE_MODE_RGMII_ID) &&
+- (phydev->interface != PHY_INTERFACE_MODE_RGMII_RXID)) {
+- u16 reg;
+-
+- reg = bcm54xx_auxctl_read(phydev,
+- MII_BCM54XX_AUXCTL_SHDWSEL_MISC);
+- /* Disable RXD to RXC delay (default set) */
+- reg &= ~MII_BCM54XX_AUXCTL_MISC_RXD_RXC_SKEW;
+- /* Clear shadow selector field */
+- reg &= ~MII_BCM54XX_AUXCTL_SHDWSEL_MASK;
+- bcm54xx_auxctl_write(phydev, MII_BCM54XX_AUXCTL_SHDWSEL_MISC,
+- MII_BCM54XX_AUXCTL_MISC_WREN | reg);
+- }
+-
+- return ret;
+-}
+-
+ static int brcm_phy_setbits(struct phy_device *phydev, int reg, int set)
+ {
+ int val;
+@@ -594,7 +593,7 @@ static struct phy_driver broadcom_driver
+ SUPPORTED_Pause | SUPPORTED_Asym_Pause,
+ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .config_init = bcm54xx_config_init,
+- .config_aneg = bcm54612e_config_aneg,
++ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = bcm_phy_ack_intr,
+ .config_intr = bcm_phy_config_intr,
diff --git a/target/linux/generic/backport-4.9/080-0001-leds-core-add-OF-variants-of-LED-registering-functio.patch b/target/linux/generic/backport-4.9/080-0001-leds-core-add-OF-variants-of-LED-registering-functio.patch
new file mode 100644
index 0000000000..5b1b307aec
--- /dev/null
+++ b/target/linux/generic/backport-4.9/080-0001-leds-core-add-OF-variants-of-LED-registering-functio.patch
@@ -0,0 +1,120 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 6 Mar 2017 06:19:44 +0100
+Subject: [PATCH] leds: core: add OF variants of LED registering functions
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+These new functions allow passing an additional device_node argument
+that will be internally set for created LED device. Thanks to this LED
+core code and triggers will be able to access DT node for reading extra
+info.
+
+The easiest solution for achieving this was reworking old functions to
+more generic ones & adding simple defines for API compatibility.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
+---
+ drivers/leds/led-class.c | 26 ++++++++++++++++----------
+ include/linux/leds.h | 14 ++++++++++----
+ 2 files changed, 26 insertions(+), 14 deletions(-)
+
+--- a/drivers/leds/led-class.c
++++ b/drivers/leds/led-class.c
+@@ -181,11 +181,14 @@ static int led_classdev_next_name(const
+ }
+
+ /**
+- * led_classdev_register - register a new object of led_classdev class.
+- * @parent: The device to register.
++ * of_led_classdev_register - register a new object of led_classdev class.
++ *
++ * @parent: parent of LED device
+ * @led_cdev: the led_classdev structure for this device.
++ * @np: DT node describing this LED
+ */
+-int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
++int of_led_classdev_register(struct device *parent, struct device_node *np,
++ struct led_classdev *led_cdev)
+ {
+ char name[64];
+ int ret;
+@@ -198,6 +201,7 @@ int led_classdev_register(struct device
+ led_cdev, led_cdev->groups, "%s", name);
+ if (IS_ERR(led_cdev->dev))
+ return PTR_ERR(led_cdev->dev);
++ led_cdev->dev->of_node = np;
+
+ if (ret)
+ dev_warn(parent, "Led %s renamed to %s due to name collision",
+@@ -228,7 +232,7 @@ int led_classdev_register(struct device
+
+ return 0;
+ }
+-EXPORT_SYMBOL_GPL(led_classdev_register);
++EXPORT_SYMBOL_GPL(of_led_classdev_register);
+
+ /**
+ * led_classdev_unregister - unregisters a object of led_properties class.
+@@ -270,12 +274,14 @@ static void devm_led_classdev_release(st
+ }
+
+ /**
+- * devm_led_classdev_register - resource managed led_classdev_register()
+- * @parent: The device to register.
++ * devm_of_led_classdev_register - resource managed led_classdev_register()
++ *
++ * @parent: parent of LED device
+ * @led_cdev: the led_classdev structure for this device.
+ */
+-int devm_led_classdev_register(struct device *parent,
+- struct led_classdev *led_cdev)
++int devm_of_led_classdev_register(struct device *parent,
++ struct device_node *np,
++ struct led_classdev *led_cdev)
+ {
+ struct led_classdev **dr;
+ int rc;
+@@ -284,7 +290,7 @@ int devm_led_classdev_register(struct de
+ if (!dr)
+ return -ENOMEM;
+
+- rc = led_classdev_register(parent, led_cdev);
++ rc = of_led_classdev_register(parent, np, led_cdev);
+ if (rc) {
+ devres_free(dr);
+ return rc;
+@@ -295,7 +301,7 @@ int devm_led_classdev_register(struct de
+
+ return 0;
+ }
+-EXPORT_SYMBOL_GPL(devm_led_classdev_register);
++EXPORT_SYMBOL_GPL(devm_of_led_classdev_register);
+
+ static int devm_led_classdev_match(struct device *dev, void *res, void *data)
+ {
+--- a/include/linux/leds.h
++++ b/include/linux/leds.h
+@@ -109,10 +109,16 @@ struct led_classdev {
+ struct mutex led_access;
+ };
+
+-extern int led_classdev_register(struct device *parent,
+- struct led_classdev *led_cdev);
+-extern int devm_led_classdev_register(struct device *parent,
+- struct led_classdev *led_cdev);
++extern int of_led_classdev_register(struct device *parent,
++ struct device_node *np,
++ struct led_classdev *led_cdev);
++#define led_classdev_register(parent, led_cdev) \
++ of_led_classdev_register(parent, NULL, led_cdev)
++extern int devm_of_led_classdev_register(struct device *parent,
++ struct device_node *np,
++ struct led_classdev *led_cdev);
++#define devm_led_classdev_register(parent, led_cdev) \
++ devm_of_led_classdev_register(parent, NULL, led_cdev)
+ extern void led_classdev_unregister(struct led_classdev *led_cdev);
+ extern void devm_led_classdev_unregister(struct device *parent,
+ struct led_classdev *led_cdev);
diff --git a/target/linux/generic/backport-4.9/080-0002-leds-gpio-use-OF-variant-of-LED-registering-function.patch b/target/linux/generic/backport-4.9/080-0002-leds-gpio-use-OF-variant-of-LED-registering-function.patch
new file mode 100644
index 0000000000..0247fffb2a
--- /dev/null
+++ b/target/linux/generic/backport-4.9/080-0002-leds-gpio-use-OF-variant-of-LED-registering-function.patch
@@ -0,0 +1,60 @@
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 6 Mar 2017 06:19:45 +0100
+Subject: [PATCH] leds: gpio: use OF variant of LED registering function
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+In leds-gpio we support LEDs specified in DT so we should use
+(devm_)of_led_classdev_register. This allows passing DT node as argument
+for use by the LED subsystem.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Acked-by: Pavel Machek <pavel@ucw.cz>
+Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
+---
+ drivers/leds/leds-gpio.c | 12 ++++++------
+ 1 file changed, 6 insertions(+), 6 deletions(-)
+
+--- a/drivers/leds/leds-gpio.c
++++ b/drivers/leds/leds-gpio.c
+@@ -77,7 +77,7 @@ static int gpio_blink_set(struct led_cla
+
+ static int create_gpio_led(const struct gpio_led *template,
+ struct gpio_led_data *led_dat, struct device *parent,
+- gpio_blink_set_t blink_set)
++ struct device_node *np, gpio_blink_set_t blink_set)
+ {
+ int ret, state;
+
+@@ -139,7 +139,7 @@ static int create_gpio_led(const struct
+ if (ret < 0)
+ return ret;
+
+- return devm_led_classdev_register(parent, &led_dat->cdev);
++ return devm_of_led_classdev_register(parent, np, &led_dat->cdev);
+ }
+
+ struct gpio_leds_priv {
+@@ -206,7 +206,7 @@ static struct gpio_leds_priv *gpio_leds_
+ if (fwnode_property_present(child, "panic-indicator"))
+ led.panic_indicator = 1;
+
+- ret = create_gpio_led(&led, led_dat, dev, NULL);
++ ret = create_gpio_led(&led, led_dat, dev, np, NULL);
+ if (ret < 0) {
+ fwnode_handle_put(child);
+ return ERR_PTR(ret);
+@@ -240,9 +240,9 @@ static int gpio_led_probe(struct platfor
+
+ priv->num_leds = pdata->num_leds;
+ for (i = 0; i < priv->num_leds; i++) {
+- ret = create_gpio_led(&pdata->leds[i],
+- &priv->leds[i],
+- &pdev->dev, pdata->gpio_blink_set);
++ ret = create_gpio_led(&pdata->leds[i], &priv->leds[i],
++ &pdev->dev, NULL,
++ pdata->gpio_blink_set);
+ if (ret < 0)
+ return ret;
+ }
diff --git a/target/linux/generic/backport-4.9/081-0001-thermal-bcm2835-add-thermal-driver-for-bcm2835-SoC.patch b/target/linux/generic/backport-4.9/081-0001-thermal-bcm2835-add-thermal-driver-for-bcm2835-SoC.patch
new file mode 100644
index 0000000000..d1ef21ed0a
--- /dev/null
+++ b/target/linux/generic/backport-4.9/081-0001-thermal-bcm2835-add-thermal-driver-for-bcm2835-SoC.patch
@@ -0,0 +1,365 @@
+From bcb7dd9ef206f7d646ed8dac6fe7772083714253 Mon Sep 17 00:00:00 2001
+From: Stefan Wahren <stefan.wahren@i2se.com>
+Date: Fri, 31 Mar 2017 20:03:06 +0000
+Subject: [PATCH] thermal: bcm2835: add thermal driver for bcm2835 SoC
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Add basic thermal driver for bcm2835 SoC.
+
+This driver currently make sure that tsense HW block is set up
+correctly.
+
+Tested-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
+Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
+Acked-by: Eric Anholt <eric@anholt.net>
+Acked-by: Eduardo Valentin <edubezval@gmail.com>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+---
+ drivers/thermal/Kconfig | 8 +
+ drivers/thermal/Makefile | 1 +
+ drivers/thermal/bcm2835_thermal.c | 314 ++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 323 insertions(+)
+ create mode 100644 drivers/thermal/bcm2835_thermal.c
+
+--- a/drivers/thermal/Kconfig
++++ b/drivers/thermal/Kconfig
+@@ -434,4 +434,12 @@ depends on (ARCH_QCOM && OF) || COMPILE_
+ source "drivers/thermal/qcom/Kconfig"
+ endmenu
+
++config BCM2835_THERMAL
++ tristate "Thermal sensors on bcm2835 SoC"
++ depends on ARCH_BCM2835 || COMPILE_TEST
++ depends on HAS_IOMEM
++ depends on THERMAL_OF
++ help
++ Support for thermal sensors on Broadcom bcm2835 SoCs.
++
+ endif
+--- a/drivers/thermal/Makefile
++++ b/drivers/thermal/Makefile
+@@ -55,3 +55,4 @@ obj-$(CONFIG_TEGRA_SOCTHERM) += tegra/
+ obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
+ obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
+ obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
++obj-$(CONFIG_BCM2835_THERMAL) += bcm2835_thermal.o
+--- /dev/null
++++ b/drivers/thermal/bcm2835_thermal.c
+@@ -0,0 +1,314 @@
++/*
++ * Driver for Broadcom BCM2835 SoC temperature sensor
++ *
++ * Copyright (C) 2016 Martin Sperl
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#include <linux/clk.h>
++#include <linux/debugfs.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/thermal.h>
++
++#define BCM2835_TS_TSENSCTL 0x00
++#define BCM2835_TS_TSENSSTAT 0x04
++
++#define BCM2835_TS_TSENSCTL_PRWDW BIT(0)
++#define BCM2835_TS_TSENSCTL_RSTB BIT(1)
++
++/*
++ * bandgap reference voltage in 6 mV increments
++ * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV
++ */
++#define BCM2835_TS_TSENSCTL_CTRL_BITS 3
++#define BCM2835_TS_TSENSCTL_CTRL_SHIFT 2
++#define BCM2835_TS_TSENSCTL_CTRL_MASK \
++ GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS + \
++ BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \
++ BCM2835_TS_TSENSCTL_CTRL_SHIFT)
++#define BCM2835_TS_TSENSCTL_CTRL_DEFAULT 1
++#define BCM2835_TS_TSENSCTL_EN_INT BIT(5)
++#define BCM2835_TS_TSENSCTL_DIRECT BIT(6)
++#define BCM2835_TS_TSENSCTL_CLR_INT BIT(7)
++#define BCM2835_TS_TSENSCTL_THOLD_SHIFT 8
++#define BCM2835_TS_TSENSCTL_THOLD_BITS 10
++#define BCM2835_TS_TSENSCTL_THOLD_MASK \
++ GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS + \
++ BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \
++ BCM2835_TS_TSENSCTL_THOLD_SHIFT)
++/*
++ * time how long the block to be asserted in reset
++ * which based on a clock counter (TSENS clock assumed)
++ */
++#define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT 18
++#define BCM2835_TS_TSENSCTL_RSTDELAY_BITS 8
++#define BCM2835_TS_TSENSCTL_REGULEN BIT(26)
++
++#define BCM2835_TS_TSENSSTAT_DATA_BITS 10
++#define BCM2835_TS_TSENSSTAT_DATA_SHIFT 0
++#define BCM2835_TS_TSENSSTAT_DATA_MASK \
++ GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS + \
++ BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \
++ BCM2835_TS_TSENSSTAT_DATA_SHIFT)
++#define BCM2835_TS_TSENSSTAT_VALID BIT(10)
++#define BCM2835_TS_TSENSSTAT_INTERRUPT BIT(11)
++
++struct bcm2835_thermal_data {
++ struct thermal_zone_device *tz;
++ void __iomem *regs;
++ struct clk *clk;
++ struct dentry *debugfsdir;
++};
++
++static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope)
++{
++ return offset + slope * adc;
++}
++
++static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)
++{
++ temp -= offset;
++ temp /= slope;
++
++ if (temp < 0)
++ temp = 0;
++ if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))
++ temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;
++
++ return temp;
++}
++
++static int bcm2835_thermal_get_temp(void *d, int *temp)
++{
++ struct bcm2835_thermal_data *data = d;
++ u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);
++
++ if (!(val & BCM2835_TS_TSENSSTAT_VALID))
++ return -EIO;
++
++ val &= BCM2835_TS_TSENSSTAT_DATA_MASK;
++
++ *temp = bcm2835_thermal_adc2temp(
++ val,
++ thermal_zone_get_offset(data->tz),
++ thermal_zone_get_slope(data->tz));
++
++ return 0;
++}
++
++static const struct debugfs_reg32 bcm2835_thermal_regs[] = {
++ {
++ .name = "ctl",
++ .offset = 0
++ },
++ {
++ .name = "stat",
++ .offset = 4
++ }
++};
++
++static void bcm2835_thermal_debugfs(struct platform_device *pdev)
++{
++ struct thermal_zone_device *tz = platform_get_drvdata(pdev);
++ struct bcm2835_thermal_data *data = tz->devdata;
++ struct debugfs_regset32 *regset;
++
++ data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);
++ if (!data->debugfsdir)
++ return;
++
++ regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL);
++ if (!regset)
++ return;
++
++ regset->regs = bcm2835_thermal_regs;
++ regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);
++ regset->base = data->regs;
++
++ debugfs_create_regset32("regset", 0444, data->debugfsdir, regset);
++}
++
++static struct thermal_zone_of_device_ops bcm2835_thermal_ops = {
++ .get_temp = bcm2835_thermal_get_temp,
++};
++
++/*
++ * Note: as per Raspberry Foundation FAQ
++ * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature)
++ * the recommended temperature range for the SoC -40C to +85C
++ * so the trip limit is set to 80C.
++ * this applies to all the BCM283X SoC
++ */
++
++static const struct of_device_id bcm2835_thermal_of_match_table[] = {
++ {
++ .compatible = "brcm,bcm2835-thermal",
++ },
++ {
++ .compatible = "brcm,bcm2836-thermal",
++ },
++ {
++ .compatible = "brcm,bcm2837-thermal",
++ },
++ {},
++};
++MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);
++
++static int bcm2835_thermal_probe(struct platform_device *pdev)
++{
++ const struct of_device_id *match;
++ struct thermal_zone_device *tz;
++ struct bcm2835_thermal_data *data;
++ struct resource *res;
++ int err = 0;
++ u32 val;
++ unsigned long rate;
++
++ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++
++ match = of_match_device(bcm2835_thermal_of_match_table,
++ &pdev->dev);
++ if (!match)
++ return -EINVAL;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ data->regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(data->regs)) {
++ err = PTR_ERR(data->regs);
++ dev_err(&pdev->dev, "Could not get registers: %d\n", err);
++ return err;
++ }
++
++ data->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(data->clk)) {
++ err = PTR_ERR(data->clk);
++ if (err != -EPROBE_DEFER)
++ dev_err(&pdev->dev, "Could not get clk: %d\n", err);
++ return err;
++ }
++
++ err = clk_prepare_enable(data->clk);
++ if (err)
++ return err;
++
++ rate = clk_get_rate(data->clk);
++ if ((rate < 1920000) || (rate > 5000000))
++ dev_warn(&pdev->dev,
++ "Clock %pCn running at %pCr Hz is outside of the recommended range: 1.92 to 5MHz\n",
++ data->clk, data->clk);
++
++ /* register of thermal sensor and get info from DT */
++ tz = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
++ &bcm2835_thermal_ops);
++ if (IS_ERR(tz)) {
++ err = PTR_ERR(tz);
++ dev_err(&pdev->dev,
++ "Failed to register the thermal device: %d\n",
++ err);
++ goto err_clk;
++ }
++
++ /*
++ * right now the FW does set up the HW-block, so we are not
++ * touching the configuration registers.
++ * But if the HW is not enabled, then set it up
++ * using "sane" values used by the firmware right now.
++ */
++ val = readl(data->regs + BCM2835_TS_TSENSCTL);
++ if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {
++ int trip_temp, offset, slope;
++
++ slope = thermal_zone_get_slope(tz);
++ offset = thermal_zone_get_offset(tz);
++ /*
++ * For now we deal only with critical, otherwise
++ * would need to iterate
++ */
++ err = tz->ops->get_trip_temp(tz, 0, &trip_temp);
++ if (err < 0) {
++ err = PTR_ERR(tz);
++ dev_err(&pdev->dev,
++ "Not able to read trip_temp: %d\n",
++ err);
++ goto err_tz;
++ }
++
++ /* set bandgap reference voltage and enable voltage regulator */
++ val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<
++ BCM2835_TS_TSENSCTL_CTRL_SHIFT) |
++ BCM2835_TS_TSENSCTL_REGULEN;
++
++ /* use the recommended reset duration */
++ val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);
++
++ /* trip_adc value from info */
++ val |= bcm2835_thermal_temp2adc(trip_temp,
++ offset,
++ slope)
++ << BCM2835_TS_TSENSCTL_THOLD_SHIFT;
++
++ /* write the value back to the register as 2 steps */
++ writel(val, data->regs + BCM2835_TS_TSENSCTL);
++ val |= BCM2835_TS_TSENSCTL_RSTB;
++ writel(val, data->regs + BCM2835_TS_TSENSCTL);
++ }
++
++ data->tz = tz;
++
++ platform_set_drvdata(pdev, tz);
++
++ bcm2835_thermal_debugfs(pdev);
++
++ return 0;
++err_tz:
++ thermal_zone_of_sensor_unregister(&pdev->dev, tz);
++err_clk:
++ clk_disable_unprepare(data->clk);
++
++ return err;
++}
++
++static int bcm2835_thermal_remove(struct platform_device *pdev)
++{
++ struct thermal_zone_device *tz = platform_get_drvdata(pdev);
++ struct bcm2835_thermal_data *data = tz->devdata;
++
++ debugfs_remove_recursive(data->debugfsdir);
++ thermal_zone_of_sensor_unregister(&pdev->dev, tz);
++ clk_disable_unprepare(data->clk);
++
++ return 0;
++}
++
++static struct platform_driver bcm2835_thermal_driver = {
++ .probe = bcm2835_thermal_probe,
++ .remove = bcm2835_thermal_remove,
++ .driver = {
++ .name = "bcm2835_thermal",
++ .of_match_table = bcm2835_thermal_of_match_table,
++ },
++};
++module_platform_driver(bcm2835_thermal_driver);
++
++MODULE_AUTHOR("Martin Sperl");
++MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
++MODULE_LICENSE("GPL");
diff --git a/target/linux/generic/backport-4.9/081-0002-thermal-broadcom-add-Northstar-thermal-driver.patch b/target/linux/generic/backport-4.9/081-0002-thermal-broadcom-add-Northstar-thermal-driver.patch
new file mode 100644
index 0000000000..8ba8cb57eb
--- /dev/null
+++ b/target/linux/generic/backport-4.9/081-0002-thermal-broadcom-add-Northstar-thermal-driver.patch
@@ -0,0 +1,173 @@
+From a94cb7eeecc4104a6874339f90c5d0647359c102 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Mon, 3 Apr 2017 17:48:29 +0200
+Subject: [PATCH] thermal: broadcom: add Northstar thermal driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Northstar is a SoC family commonly used in home routers. This commit
+adds a driver for checking CPU temperature. As Northstar Plus seems to
+also have this IP block this new symbol gets ARCH_BCM_IPROC dependency.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Jon Mason <jon.mason@broadcom.com>
+Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
+---
+ drivers/thermal/Kconfig | 5 ++
+ drivers/thermal/Makefile | 1 +
+ drivers/thermal/broadcom/Kconfig | 8 +++
+ drivers/thermal/broadcom/Makefile | 1 +
+ drivers/thermal/broadcom/ns-thermal.c | 105 ++++++++++++++++++++++++++++++++++
+ 5 files changed, 120 insertions(+)
+ create mode 100644 drivers/thermal/broadcom/Kconfig
+ create mode 100644 drivers/thermal/broadcom/Makefile
+ create mode 100644 drivers/thermal/broadcom/ns-thermal.c
+
+--- a/drivers/thermal/Kconfig
++++ b/drivers/thermal/Kconfig
+@@ -381,6 +381,11 @@ config MTK_THERMAL
+ Enable this option if you want to have support for thermal management
+ controller present in Mediatek SoCs
+
++menu "Broadcom thermal drivers"
++depends on ARCH_BCM || COMPILE_TEST
++source "drivers/thermal/broadcom/Kconfig"
++endmenu
++
+ menu "Texas Instruments thermal drivers"
+ depends on ARCH_HAS_BANDGAP || COMPILE_TEST
+ depends on HAS_IOMEM
+--- a/drivers/thermal/Makefile
++++ b/drivers/thermal/Makefile
+@@ -26,6 +26,7 @@ thermal_sys-$(CONFIG_CLOCK_THERMAL) += c
+ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
+
+ # platform thermal drivers
++obj-y += broadcom/
+ obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom-spmi-temp-alarm.o
+ obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
+ obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
+--- /dev/null
++++ b/drivers/thermal/broadcom/Kconfig
+@@ -0,0 +1,8 @@
++config BCM_NS_THERMAL
++ tristate "Northstar thermal driver"
++ depends on ARCH_BCM_IPROC || COMPILE_TEST
++ help
++ Northstar is a family of SoCs that includes e.g. BCM4708, BCM47081,
++ BCM4709 and BCM47094. It contains DMU (Device Management Unit) block
++ with a thermal sensor that allows checking CPU temperature. This
++ driver provides support for it.
+--- /dev/null
++++ b/drivers/thermal/broadcom/Makefile
+@@ -0,0 +1 @@
++obj-$(CONFIG_BCM_NS_THERMAL) += ns-thermal.o
+--- /dev/null
++++ b/drivers/thermal/broadcom/ns-thermal.c
+@@ -0,0 +1,105 @@
++/*
++ * Copyright (C) 2017 Rafał Miłecki <rafal@milecki.pl>
++ *
++ * 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/module.h>
++#include <linux/of_address.h>
++#include <linux/platform_device.h>
++#include <linux/thermal.h>
++
++#define PVTMON_CONTROL0 0x00
++#define PVTMON_CONTROL0_SEL_MASK 0x0000000e
++#define PVTMON_CONTROL0_SEL_TEMP_MONITOR 0x00000000
++#define PVTMON_CONTROL0_SEL_TEST_MODE 0x0000000e
++#define PVTMON_STATUS 0x08
++
++struct ns_thermal {
++ struct thermal_zone_device *tz;
++ void __iomem *pvtmon;
++};
++
++static int ns_thermal_get_temp(void *data, int *temp)
++{
++ struct ns_thermal *ns_thermal = data;
++ int offset = thermal_zone_get_offset(ns_thermal->tz);
++ int slope = thermal_zone_get_slope(ns_thermal->tz);
++ u32 val;
++
++ val = readl(ns_thermal->pvtmon + PVTMON_CONTROL0);
++ if ((val & PVTMON_CONTROL0_SEL_MASK) != PVTMON_CONTROL0_SEL_TEMP_MONITOR) {
++ /* Clear current mode selection */
++ val &= ~PVTMON_CONTROL0_SEL_MASK;
++
++ /* Set temp monitor mode (it's the default actually) */
++ val |= PVTMON_CONTROL0_SEL_TEMP_MONITOR;
++
++ writel(val, ns_thermal->pvtmon + PVTMON_CONTROL0);
++ }
++
++ val = readl(ns_thermal->pvtmon + PVTMON_STATUS);
++ *temp = slope * val + offset;
++
++ return 0;
++}
++
++static const struct thermal_zone_of_device_ops ns_thermal_ops = {
++ .get_temp = ns_thermal_get_temp,
++};
++
++static int ns_thermal_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct ns_thermal *ns_thermal;
++
++ ns_thermal = devm_kzalloc(dev, sizeof(*ns_thermal), GFP_KERNEL);
++ if (!ns_thermal)
++ return -ENOMEM;
++
++ ns_thermal->pvtmon = of_iomap(dev_of_node(dev), 0);
++ if (WARN_ON(!ns_thermal->pvtmon))
++ return -ENOENT;
++
++ ns_thermal->tz = devm_thermal_zone_of_sensor_register(dev, 0,
++ ns_thermal,
++ &ns_thermal_ops);
++ if (IS_ERR(ns_thermal->tz)) {
++ iounmap(ns_thermal->pvtmon);
++ return PTR_ERR(ns_thermal->tz);
++ }
++
++ platform_set_drvdata(pdev, ns_thermal);
++
++ return 0;
++}
++
++static int ns_thermal_remove(struct platform_device *pdev)
++{
++ struct ns_thermal *ns_thermal = platform_get_drvdata(pdev);
++
++ iounmap(ns_thermal->pvtmon);
++
++ return 0;
++}
++
++static const struct of_device_id ns_thermal_of_match[] = {
++ { .compatible = "brcm,ns-thermal", },
++ {},
++};
++MODULE_DEVICE_TABLE(of, ns_thermal_of_match);
++
++static struct platform_driver ns_thermal_driver = {
++ .probe = ns_thermal_probe,
++ .remove = ns_thermal_remove,
++ .driver = {
++ .name = "ns-thermal",
++ .of_match_table = ns_thermal_of_match,
++ },
++};
++module_platform_driver(ns_thermal_driver);
++
++MODULE_DESCRIPTION("Northstar thermal driver");
++MODULE_LICENSE("GPL v2");
diff --git a/target/linux/generic/backport-4.9/082-0001-usb-core-read-USB-ports-from-DT-in-the-usbport-LED-t.patch b/target/linux/generic/backport-4.9/082-0001-usb-core-read-USB-ports-from-DT-in-the-usbport-LED-t.patch
new file mode 100644
index 0000000000..65d17c8a9e
--- /dev/null
+++ b/target/linux/generic/backport-4.9/082-0001-usb-core-read-USB-ports-from-DT-in-the-usbport-LED-t.patch
@@ -0,0 +1,106 @@
+From 4f04c210d031667e503d6538a72345a36f3b5d71 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
+Date: Thu, 8 Jun 2017 18:08:32 +0200
+Subject: [PATCH] usb: core: read USB ports from DT in the usbport LED trigger
+ driver
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This uses DT info to read relation description of LEDs and USB ports. If
+DT has properly described LEDs, trigger will know when to turn them on.
+
+Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/ledtrig-usbport.c | 56 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 56 insertions(+)
+
+--- a/drivers/usb/core/ledtrig-usbport.c
++++ b/drivers/usb/core/ledtrig-usbport.c
+@@ -11,8 +11,10 @@
+ #include <linux/device.h>
+ #include <linux/leds.h>
+ #include <linux/module.h>
++#include <linux/of.h>
+ #include <linux/slab.h>
+ #include <linux/usb.h>
++#include <linux/usb/of.h>
+
+ struct usbport_trig_data {
+ struct led_classdev *led_cdev;
+@@ -123,6 +125,57 @@ static const struct attribute_group port
+ * Adding & removing ports
+ ***************************************/
+
++/**
++ * usbport_trig_port_observed - Check if port should be observed
++ */
++static bool usbport_trig_port_observed(struct usbport_trig_data *usbport_data,
++ struct usb_device *usb_dev, int port1)
++{
++ struct device *dev = usbport_data->led_cdev->dev;
++ struct device_node *led_np = dev->of_node;
++ struct of_phandle_args args;
++ struct device_node *port_np;
++ int count, i;
++
++ if (!led_np)
++ return false;
++
++ /* Get node of port being added */
++ port_np = usb_of_get_child_node(usb_dev->dev.of_node, port1);
++ if (!port_np)
++ return false;
++
++ /* Amount of trigger sources for this LED */
++ count = of_count_phandle_with_args(led_np, "trigger-sources",
++ "#trigger-source-cells");
++ if (count < 0) {
++ dev_warn(dev, "Failed to get trigger sources for %s\n",
++ led_np->full_name);
++ return false;
++ }
++
++ /* Check list of sources for this specific port */
++ for (i = 0; i < count; i++) {
++ int err;
++
++ err = of_parse_phandle_with_args(led_np, "trigger-sources",
++ "#trigger-source-cells", i,
++ &args);
++ if (err) {
++ dev_err(dev, "Failed to get trigger source phandle at index %d: %d\n",
++ i, err);
++ continue;
++ }
++
++ of_node_put(args.np);
++
++ if (args.np == port_np)
++ return true;
++ }
++
++ return false;
++}
++
+ static int usbport_trig_add_port(struct usbport_trig_data *usbport_data,
+ struct usb_device *usb_dev,
+ const char *hub_name, int portnum)
+@@ -141,6 +194,8 @@ static int usbport_trig_add_port(struct
+ port->data = usbport_data;
+ port->hub = usb_dev;
+ port->portnum = portnum;
++ port->observed = usbport_trig_port_observed(usbport_data, usb_dev,
++ portnum);
+
+ len = strlen(hub_name) + 8;
+ port->port_name = kzalloc(len, GFP_KERNEL);
+@@ -255,6 +310,7 @@ static void usbport_trig_activate(struct
+ if (err)
+ goto err_free;
+ usb_for_each_dev(usbport_data, usbport_trig_add_usb_dev_ports);
++ usbport_trig_update_count(usbport_data);
+
+ /* Notifications */
+ usbport_data->nb.notifier_call = usbport_trig_notify,
diff --git a/target/linux/generic/backport-4.9/087-regmap-make-LZO-cache-optional.patch b/target/linux/generic/backport-4.9/087-regmap-make-LZO-cache-optional.patch
new file mode 100644
index 0000000000..c26994ee35
--- /dev/null
+++ b/target/linux/generic/backport-4.9/087-regmap-make-LZO-cache-optional.patch
@@ -0,0 +1,69 @@
+From de88e9b0354c2e3ff8eae3f97afe43a34f5ed239 Mon Sep 17 00:00:00 2001
+From: Jonas Gorski <jonas.gorski@gmail.com>
+Date: Sat, 13 May 2017 13:03:21 +0200
+Subject: [PATCH] regmap: make LZO cache optional
+
+Commit 2cbbb579bcbe3 ("regmap: Add the LZO cache support") added support
+for LZO compression in regcache, but there were never any users added
+afterwards. Since LZO support itself has its own size, it currently is
+rather a deoptimization.
+
+So make it optional by introducing a symbol that can be selected by
+drivers wanting to make use of it.
+
+Saves e.g. ~46 kB on MIPS (size of LZO support + regcache LZO code).
+
+Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
+---
+I tried using google to find any users (even out-of-tree ones), but at
+best I found a single driver submission that was switched to RBTREE in
+subsequent resubmissions (MFD_SMSC).
+
+One could maybe also just drop the code because of no users for 5 years,
+but that would be up to the maintainer(s) to decide.
+
+ drivers/base/regmap/Kconfig | 5 ++++-
+ drivers/base/regmap/Makefile | 3 ++-
+ drivers/base/regmap/regcache.c | 2 ++
+ 3 files changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/base/regmap/Kconfig
++++ b/drivers/base/regmap/Kconfig
+@@ -4,9 +4,12 @@
+
+ config REGMAP
+ default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
++ select IRQ_DOMAIN if REGMAP_IRQ
++ bool
++
++config REGCACHE_COMPRESSED
+ select LZO_COMPRESS
+ select LZO_DECOMPRESS
+- select IRQ_DOMAIN if REGMAP_IRQ
+ bool
+
+ config REGMAP_AC97
+--- a/drivers/base/regmap/Makefile
++++ b/drivers/base/regmap/Makefile
+@@ -2,7 +2,8 @@
+ CFLAGS_regmap.o := -I$(src)
+
+ obj-$(CONFIG_REGMAP) += regmap.o regcache.o
+-obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
++obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
++obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
+ obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+ obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
+ obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+--- a/drivers/base/regmap/regcache.c
++++ b/drivers/base/regmap/regcache.c
+@@ -21,7 +21,9 @@
+
+ static const struct regcache_ops *cache_types[] = {
+ &regcache_rbtree_ops,
++#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED)
+ &regcache_lzo_ops,
++#endif
+ &regcache_flat_ops,
+ };
+