diff options
author | John Crispin <john@phrozen.org> | 2016-12-25 20:11:34 +0100 |
---|---|---|
committer | John Crispin <john@phrozen.org> | 2017-08-05 08:46:36 +0200 |
commit | 74d00a8c3849c1340efd713eb94b786e304c201f (patch) | |
tree | de481743de61c34da96ab5f9dba3af3edcfb8260 /target/linux/generic/backport-4.9 | |
parent | de350550ef648d9728351b986b0516fa29465c45 (diff) | |
download | upstream-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')
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[] = { + ®cache_rbtree_ops, ++#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED) + ®cache_lzo_ops, ++#endif + ®cache_flat_ops, + }; + |