OpenWrt
/branches/18.06.1/target/linux/generic/backport-4.9/010-Kbuild-don-t-hardcode-path-to-awk-in-scripts-ld-vers.patch |
---|
@@ -0,0 +1,29 @@ |
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(-) |
--- 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 |
} |
+' |
/branches/18.06.1/target/linux/generic/backport-4.9/011-kbuild-export-SUBARCH.patch |
---|
@@ -0,0 +1,23 @@ |
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(-) |
--- a/Makefile |
+++ b/Makefile |
@@ -411,8 +411,8 @@ KERNELRELEASE = $(shell cat include/conf |
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 |
/branches/18.06.1/target/linux/generic/backport-4.9/012-kbuild-add-macro-for-controlling-warnings-to-linux-c.patch |
---|
@@ -0,0 +1,143 @@ |
From: Arnd Bergmann <arnd@arndb.de> |
Date: Tue, 19 Jun 2018 13:14:56 -0700 |
Subject: [PATCH] kbuild: add macro for controlling warnings to |
linux/compiler.h |
I have occasionally run into a situation where it would make sense to |
control a compiler warning from a source file rather than doing so from |
a Makefile using the $(cc-disable-warning, ...) or $(cc-option, ...) |
helpers. |
The approach here is similar to what glibc uses, using __diag() and |
related macros to encapsulate a _Pragma("GCC diagnostic ...") statement |
that gets turned into the respective "#pragma GCC diagnostic ..." by |
the preprocessor when the macro gets expanded. |
Like glibc, I also have an argument to pass the affected compiler |
version, but decided to actually evaluate that one. For now, this |
supports GCC_4_6, GCC_4_7, GCC_4_8, GCC_4_9, GCC_5, GCC_6, GCC_7, |
GCC_8 and GCC_9. Adding support for CLANG_5 and other interesting |
versions is straightforward here. GNU compilers starting with gcc-4.2 |
could support it in principle, but "#pragma GCC diagnostic push" |
was only added in gcc-4.6, so it seems simpler to not deal with those |
at all. The same versions show a large number of warnings already, |
so it seems easier to just leave it at that and not do a more |
fine-grained control for them. |
The use cases I found so far include: |
- turning off the gcc-8 -Wattribute-alias warning inside of the |
SYSCALL_DEFINEx() macro without having to do it globally. |
- Reducing the build time for a simple re-make after a change, |
once we move the warnings from ./Makefile and |
./scripts/Makefile.extrawarn into linux/compiler.h |
- More control over the warnings based on other configurations, |
using preprocessor syntax instead of Makefile syntax. This should make |
it easier for the average developer to understand and change things. |
- Adding an easy way to turn the W=1 option on unconditionally |
for a subdirectory or a specific file. This has been requested |
by several developers in the past that want to have their subsystems |
W=1 clean. |
- Integrating clang better into the build systems. Clang supports |
more warnings than GCC, and we probably want to classify them |
as default, W=1, W=2 etc, but there are cases in which the |
warnings should be classified differently due to excessive false |
positives from one or the other compiler. |
- Adding a way to turn the default warnings into errors (e.g. using |
a new "make E=0" tag) while not also turning the W=1 warnings into |
errors. |
This patch for now just adds the minimal infrastructure in order to |
do the first of the list above. As the #pragma GCC diagnostic |
takes precedence over command line options, the next step would be |
to convert a lot of the individual Makefiles that set nonstandard |
options to use __diag() instead. |
[paul.burton@mips.com: |
- Rebase atop current master. |
- Add __diag_GCC, or more generally __diag_<compiler>, abstraction to |
avoid code outside of linux/compiler-gcc.h needing to duplicate |
knowledge about different GCC versions. |
- Add a comment argument to __diag_{ignore,warn,error} which isn't |
used in the expansion of the macros but serves to push people to |
document the reason for using them - per feedback from Kees Cook. |
- Translate severity to GCC-specific pragmas in linux/compiler-gcc.h |
rather than using GCC-specific in linux/compiler_types.h. |
- Drop all but GCC 8 macros, since we only need to define macros for |
versions that we need to introduce pragmas for, and as of this |
series that's just GCC 8. |
- Capitalize comments in linux/compiler-gcc.h to match the style of |
the rest of the file. |
- Line up macro definitions with tabs in linux/compiler-gcc.h.] |
Signed-off-by: Arnd Bergmann <arnd@arndb.de> |
Signed-off-by: Paul Burton <paul.burton@mips.com> |
Tested-by: Christophe Leroy <christophe.leroy@c-s.fr> |
Tested-by: Stafford Horne <shorne@gmail.com> |
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> |
--- |
--- a/include/linux/compiler-gcc.h |
+++ b/include/linux/compiler-gcc.h |
@@ -338,3 +338,30 @@ |
#if GCC_VERSION >= 50100 |
#define COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW 1 |
#endif |
+ |
+ |
+/* |
+ * Turn individual warnings and errors on and off locally, depending |
+ * on version. |
+ */ |
+#define __diag_GCC(version, severity, s) \ |
+ __diag_GCC_ ## version(__diag_GCC_ ## severity s) |
+ |
+/* Severity used in pragma directives */ |
+#define __diag_GCC_ignore ignored |
+#define __diag_GCC_warn warning |
+#define __diag_GCC_error error |
+ |
+/* Compilers before gcc-4.6 do not understand "#pragma GCC diagnostic push" */ |
+#if GCC_VERSION >= 40600 |
+#define __diag_str1(s) #s |
+#define __diag_str(s) __diag_str1(s) |
+#define __diag(s) _Pragma(__diag_str(GCC diagnostic s)) |
+#endif |
+ |
+#if GCC_VERSION >= 80000 |
+#define __diag_GCC_8(s) __diag(s) |
+#else |
+#define __diag_GCC_8(s) |
+#endif |
+ |
--- a/include/linux/compiler.h |
+++ b/include/linux/compiler.h |
@@ -578,4 +578,23 @@ static __always_inline void __write_once |
# define __kprobes |
# define nokprobe_inline inline |
#endif |
+ |
+#ifndef __diag |
+#define __diag(string) |
+#endif |
+ |
+#ifndef __diag_GCC |
+#define __diag_GCC(version, severity, string) |
+#endif |
+ |
+#define __diag_push() __diag(push) |
+#define __diag_pop() __diag(pop) |
+ |
+#define __diag_ignore(compiler, version, option, comment) \ |
+ __diag_ ## compiler(version, ignore, option) |
+#define __diag_warn(compiler, version, option, comment) \ |
+ __diag_ ## compiler(version, warn, option) |
+#define __diag_error(compiler, version, option, comment) \ |
+ __diag_ ## compiler(version, error, option) |
+ |
#endif /* __LINUX_COMPILER_H */ |
/branches/18.06.1/target/linux/generic/backport-4.9/013-disable-Wattribute-alias-warning-for-SYSCALL_DEFINEx.patch |
---|
@@ -0,0 +1,88 @@ |
From: Arnd Bergmann <arnd@arndb.de> |
Date: Tue, 19 Jun 2018 13:14:57 -0700 |
Subject: [PATCH] disable -Wattribute-alias warning for SYSCALL_DEFINEx() |
gcc-8 warns for every single definition of a system call entry |
point, e.g.: |
include/linux/compat.h:56:18: error: 'compat_sys_rt_sigprocmask' alias between functions of incompatible types 'long int(int, compat_sigset_t *, compat_sigset_t *, compat_size_t)' {aka 'long int(int, struct <anonymous> *, struct <anonymous> *, unsigned int)'} and 'long int(long int, long int, long int, long int)' [-Werror=attribute-alias] |
asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\ |
^~~~~~~~~~ |
include/linux/compat.h:45:2: note: in expansion of macro 'COMPAT_SYSCALL_DEFINEx' |
COMPAT_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) |
^~~~~~~~~~~~~~~~~~~~~~ |
kernel/signal.c:2601:1: note: in expansion of macro 'COMPAT_SYSCALL_DEFINE4' |
COMPAT_SYSCALL_DEFINE4(rt_sigprocmask, int, how, compat_sigset_t __user *, nset, |
^~~~~~~~~~~~~~~~~~~~~~ |
include/linux/compat.h:60:18: note: aliased declaration here |
asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))\ |
^~~~~~~~~~ |
The new warning seems reasonable in principle, but it doesn't |
help us here, since we rely on the type mismatch to sanitize the |
system call arguments. After I reported this as GCC PR82435, a new |
-Wno-attribute-alias option was added that could be used to turn the |
warning off globally on the command line, but I'd prefer to do it a |
little more fine-grained. |
Interestingly, turning a warning off and on again inside of |
a single macro doesn't always work, in this case I had to add |
an extra statement inbetween and decided to copy the __SC_TEST |
one from the native syscall to the compat syscall macro. See |
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83256 for more details |
about this. |
[paul.burton@mips.com: |
- Rebase atop current master. |
- Split GCC & version arguments to __diag_ignore() in order to match |
changes to the preceding patch. |
- Add the comment argument to match the preceding patch.] |
Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82435 |
Signed-off-by: Arnd Bergmann <arnd@arndb.de> |
Signed-off-by: Paul Burton <paul.burton@mips.com> |
Tested-by: Christophe Leroy <christophe.leroy@c-s.fr> |
Tested-by: Stafford Horne <shorne@gmail.com> |
Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> |
--- |
--- a/include/linux/compat.h |
+++ b/include/linux/compat.h |
@@ -47,6 +47,9 @@ |
COMPAT_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) |
#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ |
+ __diag_push(); \ |
+ __diag_ignore(GCC, 8, "-Wattribute-alias", \ |
+ "Type aliasing is used to sanitize syscall arguments");\ |
asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\ |
__attribute__((alias(__stringify(compat_SyS##name)))); \ |
static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ |
@@ -55,6 +58,7 @@ |
{ \ |
return C_SYSC##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ |
} \ |
+ __diag_pop(); \ |
static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) |
#ifndef compat_user_stack_pointer |
--- a/include/linux/syscalls.h |
+++ b/include/linux/syscalls.h |
@@ -192,6 +192,9 @@ extern struct trace_event_functions exit |
#define __PROTECT(...) asmlinkage_protect(__VA_ARGS__) |
#define __SYSCALL_DEFINEx(x, name, ...) \ |
+ __diag_push(); \ |
+ __diag_ignore(GCC, 8, "-Wattribute-alias", \ |
+ "Type aliasing is used to sanitize syscall arguments");\ |
asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ |
__attribute__((alias(__stringify(SyS##name)))); \ |
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ |
@@ -203,6 +206,7 @@ extern struct trace_event_functions exit |
__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ |
return ret; \ |
} \ |
+ __diag_pop(); \ |
static inline long SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) |
asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special, |
/branches/18.06.1/target/linux/generic/backport-4.9/020-backport_netfilter_rtcache.patch |
---|
@@ -0,0 +1,543 @@ |
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 |
--- 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 { |
--- /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; |
+} |
--- 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 |
--- a/net/netfilter/Makefile |
+++ b/net/netfilter/Makefile |
@@ -16,6 +16,9 @@ obj-$(CONFIG_NETFILTER_NETLINK_LOG) += n |
# 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 |
--- /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"); |
/branches/18.06.1/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 |
@@ -173,6 +173,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) |
@@ -241,10 +264,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 |
@@ -532,7 +532,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; |
} |
@@ -1465,7 +1502,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; |
@@ -1489,7 +1527,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); |
@@ -1520,7 +1558,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) && |
@@ -1571,7 +1609,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; |
@@ -1586,14 +1625,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; |
@@ -1608,7 +1648,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 |
@@ -1651,6 +1691,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; |
@@ -1666,13 +1707,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); |
@@ -1681,7 +1723,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; |
} |
@@ -1701,6 +1743,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; |
@@ -1720,8 +1763,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); |
@@ -1730,7 +1775,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, |
/branches/18.06.1/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); |
/branches/18.06.1/target/linux/generic/backport-4.9/024-1-tcp-tsq-add-tsq_flags-tsq_enum.patch |
---|
@@ -0,0 +1,90 @@ |
From 40fc3423b983b864bf70b03199191260ae9b2ea6 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:50 -0800 |
Subject: [PATCH 01/10] tcp: tsq: add tsq_flags / tsq_enum |
This is a cleanup, to ease code review of following patches. |
Old 'enum tsq_flags' is renamed, and a new enumeration is added |
with the flags used in cmpxchg() operations as opposed to |
single bit operations. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
include/linux/tcp.h | 11 ++++++++++- |
net/ipv4/tcp_output.c | 16 ++++++++-------- |
2 files changed, 18 insertions(+), 9 deletions(-) |
--- a/include/linux/tcp.h |
+++ b/include/linux/tcp.h |
@@ -368,7 +368,7 @@ struct tcp_sock { |
u32 *saved_syn; |
}; |
-enum tsq_flags { |
+enum tsq_enum { |
TSQ_THROTTLED, |
TSQ_QUEUED, |
TCP_TSQ_DEFERRED, /* tcp_tasklet_func() found socket was owned */ |
@@ -379,6 +379,15 @@ enum tsq_flags { |
*/ |
}; |
+enum tsq_flags { |
+ TSQF_THROTTLED = (1UL << TSQ_THROTTLED), |
+ TSQF_QUEUED = (1UL << TSQ_QUEUED), |
+ TCPF_TSQ_DEFERRED = (1UL << TCP_TSQ_DEFERRED), |
+ TCPF_WRITE_TIMER_DEFERRED = (1UL << TCP_WRITE_TIMER_DEFERRED), |
+ TCPF_DELACK_TIMER_DEFERRED = (1UL << TCP_DELACK_TIMER_DEFERRED), |
+ TCPF_MTU_REDUCED_DEFERRED = (1UL << TCP_MTU_REDUCED_DEFERRED), |
+}; |
+ |
static inline struct tcp_sock *tcp_sk(const struct sock *sk) |
{ |
return (struct tcp_sock *)sk; |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -789,10 +789,10 @@ static void tcp_tasklet_func(unsigned lo |
} |
} |
-#define TCP_DEFERRED_ALL ((1UL << TCP_TSQ_DEFERRED) | \ |
- (1UL << TCP_WRITE_TIMER_DEFERRED) | \ |
- (1UL << TCP_DELACK_TIMER_DEFERRED) | \ |
- (1UL << TCP_MTU_REDUCED_DEFERRED)) |
+#define TCP_DEFERRED_ALL (TCPF_TSQ_DEFERRED | \ |
+ TCPF_WRITE_TIMER_DEFERRED | \ |
+ TCPF_DELACK_TIMER_DEFERRED | \ |
+ TCPF_MTU_REDUCED_DEFERRED) |
/** |
* tcp_release_cb - tcp release_sock() callback |
* @sk: socket |
@@ -813,7 +813,7 @@ void tcp_release_cb(struct sock *sk) |
nflags = flags & ~TCP_DEFERRED_ALL; |
} while (cmpxchg(&tp->tsq_flags, flags, nflags) != flags); |
- if (flags & (1UL << TCP_TSQ_DEFERRED)) |
+ if (flags & TCPF_TSQ_DEFERRED) |
tcp_tsq_handler(sk); |
/* Here begins the tricky part : |
@@ -827,15 +827,15 @@ void tcp_release_cb(struct sock *sk) |
*/ |
sock_release_ownership(sk); |
- if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED)) { |
+ if (flags & TCPF_WRITE_TIMER_DEFERRED) { |
tcp_write_timer_handler(sk); |
__sock_put(sk); |
} |
- if (flags & (1UL << TCP_DELACK_TIMER_DEFERRED)) { |
+ if (flags & TCPF_DELACK_TIMER_DEFERRED) { |
tcp_delack_timer_handler(sk); |
__sock_put(sk); |
} |
- if (flags & (1UL << TCP_MTU_REDUCED_DEFERRED)) { |
+ if (flags & TCPF_MTU_REDUCED_DEFERRED) { |
inet_csk(sk)->icsk_af_ops->mtu_reduced(sk); |
__sock_put(sk); |
} |
/branches/18.06.1/target/linux/generic/backport-4.9/024-2-tcp-tsq-remove-one-locked-operation-in-tcp_wfree.patch |
---|
@@ -0,0 +1,48 @@ |
From 408f0a6c21e124cc4f6c7aa370b38aa47e55428d Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:51 -0800 |
Subject: [PATCH 02/10] tcp: tsq: remove one locked operation in tcp_wfree() |
Instead of atomically clear TSQ_THROTTLED and atomically set TSQ_QUEUED |
bits, use one cmpxchg() to perform a single locked operation. |
Since the following patch will also set TCP_TSQ_DEFERRED here, |
this cmpxchg() will make this addition free. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv4/tcp_output.c | 13 ++++++++++--- |
1 file changed, 10 insertions(+), 3 deletions(-) |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -865,6 +865,7 @@ void tcp_wfree(struct sk_buff *skb) |
{ |
struct sock *sk = skb->sk; |
struct tcp_sock *tp = tcp_sk(sk); |
+ unsigned long flags, nval, oval; |
int wmem; |
/* Keep one reference on sk_wmem_alloc. |
@@ -882,11 +883,17 @@ void tcp_wfree(struct sk_buff *skb) |
if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) |
goto out; |
- if (test_and_clear_bit(TSQ_THROTTLED, &tp->tsq_flags) && |
- !test_and_set_bit(TSQ_QUEUED, &tp->tsq_flags)) { |
- unsigned long flags; |
+ for (oval = READ_ONCE(tp->tsq_flags);; oval = nval) { |
struct tsq_tasklet *tsq; |
+ if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) |
+ goto out; |
+ |
+ nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED; |
+ nval = cmpxchg(&tp->tsq_flags, oval, nval); |
+ if (nval != oval) |
+ continue; |
+ |
/* queue this socket to tasklet queue */ |
local_irq_save(flags); |
tsq = this_cpu_ptr(&tsq_tasklet); |
/branches/18.06.1/target/linux/generic/backport-4.9/024-3-tcp-tsq-add-shortcut-in-tcp_tasklet_func.patch |
---|
@@ -0,0 +1,71 @@ |
From b223feb9de2a65c533ff95c08e834fa732906ea5 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:52 -0800 |
Subject: [PATCH 03/10] tcp: tsq: add shortcut in tcp_tasklet_func() |
Under high stress, I've seen tcp_tasklet_func() consuming |
~700 usec, handling ~150 tcp sockets. |
By setting TCP_TSQ_DEFERRED in tcp_wfree(), we give a chance |
for other cpus/threads entering tcp_write_xmit() to grab it, |
allowing tcp_tasklet_func() to skip sockets that already did |
an xmit cycle. |
In the future, we might give to ACK processing an increased |
budget to reduce even more tcp_tasklet_func() amount of work. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv4/tcp_output.c | 22 ++++++++++++---------- |
1 file changed, 12 insertions(+), 10 deletions(-) |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -772,19 +772,19 @@ static void tcp_tasklet_func(unsigned lo |
list_for_each_safe(q, n, &list) { |
tp = list_entry(q, struct tcp_sock, tsq_node); |
list_del(&tp->tsq_node); |
+ clear_bit(TSQ_QUEUED, &tp->tsq_flags); |
sk = (struct sock *)tp; |
- bh_lock_sock(sk); |
- |
- if (!sock_owned_by_user(sk)) { |
- tcp_tsq_handler(sk); |
- } else { |
- /* defer the work to tcp_release_cb() */ |
- set_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); |
+ if (!sk->sk_lock.owned && |
+ test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) { |
+ bh_lock_sock(sk); |
+ if (!sock_owned_by_user(sk)) { |
+ clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); |
+ tcp_tsq_handler(sk); |
+ } |
+ bh_unlock_sock(sk); |
} |
- bh_unlock_sock(sk); |
- clear_bit(TSQ_QUEUED, &tp->tsq_flags); |
sk_free(sk); |
} |
} |
@@ -889,7 +889,7 @@ void tcp_wfree(struct sk_buff *skb) |
if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) |
goto out; |
- nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED; |
+ nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED; |
nval = cmpxchg(&tp->tsq_flags, oval, nval); |
if (nval != oval) |
continue; |
@@ -2222,6 +2222,8 @@ static bool tcp_write_xmit(struct sock * |
unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) |
break; |
+ if (test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) |
+ clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); |
if (tcp_small_queue_check(sk, skb, 0)) |
break; |
/branches/18.06.1/target/linux/generic/backport-4.9/024-4-tcp-tsq-avoid-one-atomic-in-tcp_wfree.patch |
---|
@@ -0,0 +1,38 @@ |
From a9b204d1564702b704ad6fe74f10a102c7b87ba3 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:53 -0800 |
Subject: [PATCH 04/10] tcp: tsq: avoid one atomic in tcp_wfree() |
Under high load, tcp_wfree() has an atomic operation trying |
to schedule a tasklet over and over. |
We can schedule it only if our per cpu list was empty. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv4/tcp_output.c | 5 ++++- |
1 file changed, 4 insertions(+), 1 deletion(-) |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -885,6 +885,7 @@ void tcp_wfree(struct sk_buff *skb) |
for (oval = READ_ONCE(tp->tsq_flags);; oval = nval) { |
struct tsq_tasklet *tsq; |
+ bool empty; |
if (!(oval & TSQF_THROTTLED) || (oval & TSQF_QUEUED)) |
goto out; |
@@ -897,8 +898,10 @@ void tcp_wfree(struct sk_buff *skb) |
/* queue this socket to tasklet queue */ |
local_irq_save(flags); |
tsq = this_cpu_ptr(&tsq_tasklet); |
+ empty = list_empty(&tsq->head); |
list_add(&tp->tsq_node, &tsq->head); |
- tasklet_schedule(&tsq->tasklet); |
+ if (empty) |
+ tasklet_schedule(&tsq->tasklet); |
local_irq_restore(flags); |
return; |
} |
/branches/18.06.1/target/linux/generic/backport-4.9/024-5-tcp-tsq-add-a-shortcut-in-tcp_small_queue_check.patch |
---|
@@ -0,0 +1,37 @@ |
From 75eefc6c59fd2c5f1ab95a3a113c217237d12a31 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:54 -0800 |
Subject: [PATCH 05/10] tcp: tsq: add a shortcut in tcp_small_queue_check() |
Always allow the two first skbs in write queue to be sent, |
regardless of sk_wmem_alloc/sk_pacing_rate values. |
This helps a lot in situations where TX completions are delayed either |
because of driver latencies or softirq latencies. |
Test is done with no cache line misses. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv4/tcp_output.c | 9 +++++++++ |
1 file changed, 9 insertions(+) |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -2127,6 +2127,15 @@ static bool tcp_small_queue_check(struct |
limit <<= factor; |
if (atomic_read(&sk->sk_wmem_alloc) > limit) { |
+ /* Always send the 1st or 2nd skb in write queue. |
+ * No need to wait for TX completion to call us back, |
+ * after softirq/tasklet schedule. |
+ * This helps when TX completions are delayed too much. |
+ */ |
+ if (skb == sk->sk_write_queue.next || |
+ skb->prev == sk->sk_write_queue.next) |
+ return false; |
+ |
set_bit(TSQ_THROTTLED, &tcp_sk(sk)->tsq_flags); |
/* It is possible TX completion already happened |
* before we set TSQ_THROTTLED, so we must |
/branches/18.06.1/target/linux/generic/backport-4.9/024-6-tcp-tcp_mtu_probe-is-likely-to-exit-early.patch |
---|
@@ -0,0 +1,55 @@ |
From 12a59abc22d6664f7d3944f625ceefee92de8820 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:55 -0800 |
Subject: [PATCH 06/10] tcp: tcp_mtu_probe() is likely to exit early |
Adding a likely() in tcp_mtu_probe() moves its code which used to |
be inlined in front of tcp_write_xmit() |
We still have a cache line miss to access icsk->icsk_mtup.enabled, |
we will probably have to reorganize fields to help data locality. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv4/tcp_output.c | 18 +++++++++--------- |
1 file changed, 9 insertions(+), 9 deletions(-) |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -1960,26 +1960,26 @@ static bool tcp_can_coalesce_send_queue_ |
*/ |
static int tcp_mtu_probe(struct sock *sk) |
{ |
- struct tcp_sock *tp = tcp_sk(sk); |
struct inet_connection_sock *icsk = inet_csk(sk); |
+ struct tcp_sock *tp = tcp_sk(sk); |
struct sk_buff *skb, *nskb, *next; |
struct net *net = sock_net(sk); |
- int len; |
int probe_size; |
int size_needed; |
- int copy; |
+ int copy, len; |
int mss_now; |
int interval; |
/* Not currently probing/verifying, |
* not in recovery, |
* have enough cwnd, and |
- * not SACKing (the variable headers throw things off) */ |
- if (!icsk->icsk_mtup.enabled || |
- icsk->icsk_mtup.probe_size || |
- inet_csk(sk)->icsk_ca_state != TCP_CA_Open || |
- tp->snd_cwnd < 11 || |
- tp->rx_opt.num_sacks || tp->rx_opt.dsack) |
+ * not SACKing (the variable headers throw things off) |
+ */ |
+ if (likely(!icsk->icsk_mtup.enabled || |
+ icsk->icsk_mtup.probe_size || |
+ inet_csk(sk)->icsk_ca_state != TCP_CA_Open || |
+ tp->snd_cwnd < 11 || |
+ tp->rx_opt.num_sacks || tp->rx_opt.dsack)) |
return -1; |
/* Use binary search for probe_size between tcp_mss_base, |
/branches/18.06.1/target/linux/generic/backport-4.9/024-7-net-reorganize-struct-sock-for-better-data-locality.patch |
---|
@@ -0,0 +1,158 @@ |
From 9115e8cd2a0c6eaaa900c462721f12e1d45f326c Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:56 -0800 |
Subject: [PATCH 07/10] net: reorganize struct sock for better data locality |
Group fields used in TX path, and keep some cache lines mostly read |
to permit sharing among cpus. |
Gained two 4 bytes holes on 64bit arches. |
Added a place holder for tcp tsq_flags, next to sk_wmem_alloc |
to speed up tcp_wfree() in the following patch. |
I have not added ____cacheline_aligned_in_smp, this might be done later. |
I prefer doing this once inet and tcp/udp sockets reorg is also done. |
Tested with both TCP and UDP. |
UDP receiver performance under flood increased by ~20 % : |
Accessing sk_filter/sk_wq/sk_napi_id no longer stalls because sk_drops |
was moved away from a critical cache line, now mostly read and shared. |
/* --- cacheline 4 boundary (256 bytes) --- */ |
unsigned int sk_napi_id; /* 0x100 0x4 */ |
int sk_rcvbuf; /* 0x104 0x4 */ |
struct sk_filter * sk_filter; /* 0x108 0x8 */ |
union { |
struct socket_wq * sk_wq; /* 0x8 */ |
struct socket_wq * sk_wq_raw; /* 0x8 */ |
}; /* 0x110 0x8 */ |
struct xfrm_policy * sk_policy[2]; /* 0x118 0x10 */ |
struct dst_entry * sk_rx_dst; /* 0x128 0x8 */ |
struct dst_entry * sk_dst_cache; /* 0x130 0x8 */ |
atomic_t sk_omem_alloc; /* 0x138 0x4 */ |
int sk_sndbuf; /* 0x13c 0x4 */ |
/* --- cacheline 5 boundary (320 bytes) --- */ |
int sk_wmem_queued; /* 0x140 0x4 */ |
atomic_t sk_wmem_alloc; /* 0x144 0x4 */ |
long unsigned int sk_tsq_flags; /* 0x148 0x8 */ |
struct sk_buff * sk_send_head; /* 0x150 0x8 */ |
struct sk_buff_head sk_write_queue; /* 0x158 0x18 */ |
__s32 sk_peek_off; /* 0x170 0x4 */ |
int sk_write_pending; /* 0x174 0x4 */ |
long int sk_sndtimeo; /* 0x178 0x8 */ |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Tested-by: Paolo Abeni <pabeni@redhat.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
include/net/sock.h | 51 +++++++++++++++++++++++++++------------------------ |
1 file changed, 27 insertions(+), 24 deletions(-) |
--- a/include/net/sock.h |
+++ b/include/net/sock.h |
@@ -344,6 +344,9 @@ struct sock { |
#define sk_rxhash __sk_common.skc_rxhash |
socket_lock_t sk_lock; |
+ atomic_t sk_drops; |
+ int sk_rcvlowat; |
+ struct sk_buff_head sk_error_queue; |
struct sk_buff_head sk_receive_queue; |
/* |
* The backlog queue is special, it is always used with |
@@ -360,14 +363,13 @@ struct sock { |
struct sk_buff *tail; |
} sk_backlog; |
#define sk_rmem_alloc sk_backlog.rmem_alloc |
- int sk_forward_alloc; |
- __u32 sk_txhash; |
+ int sk_forward_alloc; |
#ifdef CONFIG_NET_RX_BUSY_POLL |
- unsigned int sk_napi_id; |
unsigned int sk_ll_usec; |
+ /* ===== mostly read cache line ===== */ |
+ unsigned int sk_napi_id; |
#endif |
- atomic_t sk_drops; |
int sk_rcvbuf; |
struct sk_filter __rcu *sk_filter; |
@@ -380,11 +382,30 @@ struct sock { |
#endif |
struct dst_entry *sk_rx_dst; |
struct dst_entry __rcu *sk_dst_cache; |
- /* Note: 32bit hole on 64bit arches */ |
- atomic_t sk_wmem_alloc; |
atomic_t sk_omem_alloc; |
int sk_sndbuf; |
+ |
+ /* ===== cache line for TX ===== */ |
+ int sk_wmem_queued; |
+ atomic_t sk_wmem_alloc; |
+ unsigned long sk_tsq_flags; |
+ struct sk_buff *sk_send_head; |
struct sk_buff_head sk_write_queue; |
+ __s32 sk_peek_off; |
+ int sk_write_pending; |
+ long sk_sndtimeo; |
+ struct timer_list sk_timer; |
+ __u32 sk_priority; |
+ __u32 sk_mark; |
+ u32 sk_pacing_rate; /* bytes per second */ |
+ u32 sk_max_pacing_rate; |
+ struct page_frag sk_frag; |
+ netdev_features_t sk_route_caps; |
+ netdev_features_t sk_route_nocaps; |
+ int sk_gso_type; |
+ unsigned int sk_gso_max_size; |
+ gfp_t sk_allocation; |
+ __u32 sk_txhash; |
/* |
* Because of non atomicity rules, all |
@@ -400,31 +421,17 @@ struct sock { |
#define SK_PROTOCOL_MAX U8_MAX |
kmemcheck_bitfield_end(flags); |
- int sk_wmem_queued; |
- gfp_t sk_allocation; |
- u32 sk_pacing_rate; /* bytes per second */ |
- u32 sk_max_pacing_rate; |
- netdev_features_t sk_route_caps; |
- netdev_features_t sk_route_nocaps; |
- int sk_gso_type; |
- unsigned int sk_gso_max_size; |
u16 sk_gso_max_segs; |
- int sk_rcvlowat; |
unsigned long sk_lingertime; |
- struct sk_buff_head sk_error_queue; |
struct proto *sk_prot_creator; |
rwlock_t sk_callback_lock; |
int sk_err, |
sk_err_soft; |
u32 sk_ack_backlog; |
u32 sk_max_ack_backlog; |
- __u32 sk_priority; |
- __u32 sk_mark; |
struct pid *sk_peer_pid; |
const struct cred *sk_peer_cred; |
long sk_rcvtimeo; |
- long sk_sndtimeo; |
- struct timer_list sk_timer; |
ktime_t sk_stamp; |
#if BITS_PER_LONG==32 |
seqlock_t sk_stamp_seq; |
@@ -434,10 +441,6 @@ struct sock { |
u32 sk_tskey; |
struct socket *sk_socket; |
void *sk_user_data; |
- struct page_frag sk_frag; |
- struct sk_buff *sk_send_head; |
- __s32 sk_peek_off; |
- int sk_write_pending; |
#ifdef CONFIG_SECURITY |
void *sk_security; |
#endif |
/branches/18.06.1/target/linux/generic/backport-4.9/024-8-tcp-tsq-move-tsq_flags-close-to-sk_wmem_alloc.patch |
---|
@@ -0,0 +1,176 @@ |
From 7aa5470c2c09265902b5e4289afa82e4e7c2987e Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 3 Dec 2016 11:14:57 -0800 |
Subject: [PATCH 08/10] tcp: tsq: move tsq_flags close to sk_wmem_alloc |
tsq_flags being in the same cache line than sk_wmem_alloc |
makes a lot of sense. Both fields are changed from tcp_wfree() |
and more generally by various TSQ related functions. |
Prior patch made room in struct sock and added sk_tsq_flags, |
this patch deletes tsq_flags from struct tcp_sock. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
include/linux/tcp.h | 1 - |
net/ipv4/tcp.c | 4 ++-- |
net/ipv4/tcp_ipv4.c | 2 +- |
net/ipv4/tcp_output.c | 24 +++++++++++------------- |
net/ipv4/tcp_timer.c | 4 ++-- |
net/ipv6/tcp_ipv6.c | 2 +- |
6 files changed, 17 insertions(+), 20 deletions(-) |
--- a/include/linux/tcp.h |
+++ b/include/linux/tcp.h |
@@ -192,7 +192,6 @@ struct tcp_sock { |
u32 tsoffset; /* timestamp offset */ |
struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ |
- unsigned long tsq_flags; |
/* Data for direct copy to user */ |
struct { |
--- a/net/ipv4/tcp.c |
+++ b/net/ipv4/tcp.c |
@@ -665,9 +665,9 @@ static void tcp_push(struct sock *sk, in |
if (tcp_should_autocork(sk, skb, size_goal)) { |
/* avoid atomic op if TSQ_THROTTLED bit is already set */ |
- if (!test_bit(TSQ_THROTTLED, &tp->tsq_flags)) { |
+ if (!test_bit(TSQ_THROTTLED, &sk->sk_tsq_flags)) { |
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING); |
- set_bit(TSQ_THROTTLED, &tp->tsq_flags); |
+ set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags); |
} |
/* It is possible TX completion already happened |
* before we set TSQ_THROTTLED. |
--- a/net/ipv4/tcp_ipv4.c |
+++ b/net/ipv4/tcp_ipv4.c |
@@ -446,7 +446,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb |
if (!sock_owned_by_user(sk)) { |
tcp_v4_mtu_reduced(sk); |
} else { |
- if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags)) |
+ if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &sk->sk_tsq_flags)) |
sock_hold(sk); |
} |
goto out; |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -772,14 +772,15 @@ static void tcp_tasklet_func(unsigned lo |
list_for_each_safe(q, n, &list) { |
tp = list_entry(q, struct tcp_sock, tsq_node); |
list_del(&tp->tsq_node); |
- clear_bit(TSQ_QUEUED, &tp->tsq_flags); |
sk = (struct sock *)tp; |
+ clear_bit(TSQ_QUEUED, &sk->sk_tsq_flags); |
+ |
if (!sk->sk_lock.owned && |
- test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) { |
+ test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) { |
bh_lock_sock(sk); |
if (!sock_owned_by_user(sk)) { |
- clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); |
+ clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags); |
tcp_tsq_handler(sk); |
} |
bh_unlock_sock(sk); |
@@ -802,16 +803,15 @@ static void tcp_tasklet_func(unsigned lo |
*/ |
void tcp_release_cb(struct sock *sk) |
{ |
- struct tcp_sock *tp = tcp_sk(sk); |
unsigned long flags, nflags; |
/* perform an atomic operation only if at least one flag is set */ |
do { |
- flags = tp->tsq_flags; |
+ flags = sk->sk_tsq_flags; |
if (!(flags & TCP_DEFERRED_ALL)) |
return; |
nflags = flags & ~TCP_DEFERRED_ALL; |
- } while (cmpxchg(&tp->tsq_flags, flags, nflags) != flags); |
+ } while (cmpxchg(&sk->sk_tsq_flags, flags, nflags) != flags); |
if (flags & TCPF_TSQ_DEFERRED) |
tcp_tsq_handler(sk); |
@@ -883,7 +883,7 @@ void tcp_wfree(struct sk_buff *skb) |
if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) |
goto out; |
- for (oval = READ_ONCE(tp->tsq_flags);; oval = nval) { |
+ for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) { |
struct tsq_tasklet *tsq; |
bool empty; |
@@ -891,7 +891,7 @@ void tcp_wfree(struct sk_buff *skb) |
goto out; |
nval = (oval & ~TSQF_THROTTLED) | TSQF_QUEUED | TCPF_TSQ_DEFERRED; |
- nval = cmpxchg(&tp->tsq_flags, oval, nval); |
+ nval = cmpxchg(&sk->sk_tsq_flags, oval, nval); |
if (nval != oval) |
continue; |
@@ -2136,7 +2136,7 @@ static bool tcp_small_queue_check(struct |
skb->prev == sk->sk_write_queue.next) |
return false; |
- set_bit(TSQ_THROTTLED, &tcp_sk(sk)->tsq_flags); |
+ set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags); |
/* It is possible TX completion already happened |
* before we set TSQ_THROTTLED, so we must |
* test again the condition. |
@@ -2234,8 +2234,8 @@ static bool tcp_write_xmit(struct sock * |
unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) |
break; |
- if (test_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags)) |
- clear_bit(TCP_TSQ_DEFERRED, &tp->tsq_flags); |
+ if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) |
+ clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags); |
if (tcp_small_queue_check(sk, skb, 0)) |
break; |
@@ -3546,8 +3546,6 @@ void __tcp_send_ack(struct sock *sk, u32 |
/* We do not want pure acks influencing TCP Small Queues or fq/pacing |
* too much. |
* SKB_TRUESIZE(max(1 .. 66, MAX_TCP_HEADER)) is unfortunately ~784 |
- * We also avoid tcp_wfree() overhead (cache line miss accessing |
- * tp->tsq_flags) by using regular sock_wfree() |
*/ |
skb_set_tcp_pure_ack(buff); |
--- a/net/ipv4/tcp_timer.c |
+++ b/net/ipv4/tcp_timer.c |
@@ -326,7 +326,7 @@ static void tcp_delack_timer(unsigned lo |
inet_csk(sk)->icsk_ack.blocked = 1; |
__NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED); |
/* deleguate our work to tcp_release_cb() */ |
- if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) |
+ if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &sk->sk_tsq_flags)) |
sock_hold(sk); |
} |
bh_unlock_sock(sk); |
@@ -609,7 +609,7 @@ static void tcp_write_timer(unsigned lon |
tcp_write_timer_handler(sk); |
} else { |
/* delegate our work to tcp_release_cb() */ |
- if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags)) |
+ if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &sk->sk_tsq_flags)) |
sock_hold(sk); |
} |
bh_unlock_sock(sk); |
--- a/net/ipv6/tcp_ipv6.c |
+++ b/net/ipv6/tcp_ipv6.c |
@@ -404,7 +404,7 @@ static void tcp_v6_err(struct sk_buff *s |
if (!sock_owned_by_user(sk)) |
tcp_v6_mtu_reduced(sk); |
else if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, |
- &tp->tsq_flags)) |
+ &sk->sk_tsq_flags)) |
sock_hold(sk); |
goto out; |
} |
/branches/18.06.1/target/linux/generic/backport-4.9/024-9-tcp-add-a-missing-barrier-in-tcp_tasklet_func.patch |
---|
@@ -0,0 +1,40 @@ |
From 0a9648f1293966c838dc570da73c15a76f4c89d6 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Wed, 21 Dec 2016 05:42:43 -0800 |
Subject: [PATCH 09/10] tcp: add a missing barrier in tcp_tasklet_func() |
Madalin reported crashes happening in tcp_tasklet_func() on powerpc64 |
Before TSQ_QUEUED bit is cleared, we must ensure the changes done |
by list_del(&tp->tsq_node); are committed to memory, otherwise |
corruption might happen, as an other cpu could catch TSQ_QUEUED |
clearance too soon. |
We can notice that old kernels were immune to this bug, because |
TSQ_QUEUED was cleared after a bh_lock_sock(sk)/bh_unlock_sock(sk) |
section, but they could have missed a kick to write additional bytes, |
when NIC interrupts for a given flow are spread to multiple cpus. |
Affected TCP flows would need an incoming ACK or RTO timer to add more |
packets to the pipe. So overall situation should be better now. |
Fixes: b223feb9de2a ("tcp: tsq: add shortcut in tcp_tasklet_func()") |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Reported-by: Madalin Bucur <madalin.bucur@nxp.com> |
Tested-by: Madalin Bucur <madalin.bucur@nxp.com> |
Tested-by: Xing Lei <xing.lei@nxp.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv4/tcp_output.c | 1 + |
1 file changed, 1 insertion(+) |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -774,6 +774,7 @@ static void tcp_tasklet_func(unsigned lo |
list_del(&tp->tsq_node); |
sk = (struct sock *)tp; |
+ smp_mb__before_atomic(); |
clear_bit(TSQ_QUEUED, &sk->sk_tsq_flags); |
if (!sk->sk_lock.owned && |
/branches/18.06.1/target/linux/generic/backport-4.9/025-tcp-allow-drivers-to-tweak-TSQ-logic.patch |
---|
@@ -0,0 +1,85 @@ |
From: Eric Dumazet <edumazet@google.com> |
Date: Sat, 11 Nov 2017 15:54:12 -0800 |
Subject: [PATCH] tcp: allow drivers to tweak TSQ logic |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
I had many reports that TSQ logic breaks wifi aggregation. |
Current logic is to allow up to 1 ms of bytes to be queued into qdisc |
and drivers queues. |
But Wifi aggregation needs a bigger budget to allow bigger rates to |
be discovered by various TCP Congestion Controls algorithms. |
This patch adds an extra socket field, allowing wifi drivers to select |
another log scale to derive TCP Small Queue credit from current pacing |
rate. |
Initial value is 10, meaning that this patch does not change current |
behavior. |
We expect wifi drivers to set this field to smaller values (tests have |
been done with values from 6 to 9) |
They would have to use following template : |
if (skb->sk && skb->sk->sk_pacing_shift != MY_PACING_SHIFT) |
skb->sk->sk_pacing_shift = MY_PACING_SHIFT; |
Ref: https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1670041 |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Cc: Johannes Berg <johannes.berg@intel.com> |
Cc: Toke Høiland-Jørgensen <toke@toke.dk> |
Cc: Kir Kolyshkin <kir@openvz.org> |
--- |
--- a/include/net/sock.h |
+++ b/include/net/sock.h |
@@ -260,6 +260,7 @@ struct sock_common { |
* @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4) |
* @sk_gso_max_size: Maximum GSO segment size to build |
* @sk_gso_max_segs: Maximum number of GSO segments |
+ * @sk_pacing_shift: scaling factor for TCP Small Queues |
* @sk_lingertime: %SO_LINGER l_linger setting |
* @sk_backlog: always used with the per-socket spinlock held |
* @sk_callback_lock: used with the callbacks in the end of this struct |
@@ -422,6 +423,8 @@ struct sock { |
kmemcheck_bitfield_end(flags); |
u16 sk_gso_max_segs; |
+#define sk_pacing_shift sk_pacing_shift /* for backport checks */ |
+ u8 sk_pacing_shift; |
unsigned long sk_lingertime; |
struct proto *sk_prot_creator; |
rwlock_t sk_callback_lock; |
--- a/net/core/sock.c |
+++ b/net/core/sock.c |
@@ -2479,6 +2479,7 @@ void sock_init_data(struct socket *sock, |
sk->sk_max_pacing_rate = ~0U; |
sk->sk_pacing_rate = ~0U; |
+ sk->sk_pacing_shift = 10; |
sk->sk_incoming_cpu = -1; |
/* |
* Before updating sk_refcnt, we must commit prior changes to memory |
--- a/net/ipv4/tcp_output.c |
+++ b/net/ipv4/tcp_output.c |
@@ -1593,7 +1593,7 @@ u32 tcp_tso_autosize(const struct sock * |
{ |
u32 bytes, segs; |
- bytes = min(sk->sk_pacing_rate >> 10, |
+ bytes = min(sk->sk_pacing_rate >> sk->sk_pacing_shift, |
sk->sk_gso_max_size - 1 - MAX_TCP_HEADER); |
/* Goal is to send at least one packet per ms, |
@@ -2123,7 +2123,7 @@ static bool tcp_small_queue_check(struct |
{ |
unsigned int limit; |
- limit = max(2 * skb->truesize, sk->sk_pacing_rate >> 10); |
+ limit = max(2 * skb->truesize, sk->sk_pacing_rate >> sk->sk_pacing_shift); |
limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes); |
limit <<= factor; |
/branches/18.06.1/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; |
/branches/18.06.1/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 |
/branches/18.06.1/target/linux/generic/backport-4.9/040-mm-add-support-for-releasing-multiple-instances-of-a.patch |
---|
@@ -0,0 +1,75 @@ |
From: Alexander Duyck <alexander.h.duyck@intel.com> |
Date: Wed, 14 Dec 2016 15:05:26 -0800 |
Subject: [PATCH] mm: add support for releasing multiple instances of a page |
Add a function that allows us to batch free a page that has multiple |
references outstanding. Specifically this function can be used to drop |
a page being used in the page frag alloc cache. With this drivers can |
make use of functionality similar to the page frag alloc cache without |
having to do any workarounds for the fact that there is no function that |
frees multiple references. |
Link: http://lkml.kernel.org/r/20161110113606.76501.70752.stgit@ahduyck-blue-test.jf.intel.com |
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com> |
Cc: "David S. Miller" <davem@davemloft.net> |
Cc: "James E.J. Bottomley" <jejb@parisc-linux.org> |
Cc: Chris Metcalf <cmetcalf@mellanox.com> |
Cc: David Howells <dhowells@redhat.com> |
Cc: Geert Uytterhoeven <geert@linux-m68k.org> |
Cc: Hans-Christian Noren Egtvedt <egtvedt@samfundet.no> |
Cc: Helge Deller <deller@gmx.de> |
Cc: James Hogan <james.hogan@imgtec.com> |
Cc: Jeff Kirsher <jeffrey.t.kirsher@intel.com> |
Cc: Jonas Bonn <jonas@southpole.se> |
Cc: Keguang Zhang <keguang.zhang@gmail.com> |
Cc: Ley Foon Tan <lftan@altera.com> |
Cc: Mark Salter <msalter@redhat.com> |
Cc: Max Filippov <jcmvbkbc@gmail.com> |
Cc: Michael Ellerman <mpe@ellerman.id.au> |
Cc: Michal Simek <monstr@monstr.eu> |
Cc: Ralf Baechle <ralf@linux-mips.org> |
Cc: Rich Felker <dalias@libc.org> |
Cc: Richard Kuo <rkuo@codeaurora.org> |
Cc: Russell King <linux@armlinux.org.uk> |
Cc: Steven Miao <realmz6@gmail.com> |
Cc: Tobias Klauser <tklauser@distanz.ch> |
Cc: Vineet Gupta <vgupta@synopsys.com> |
Cc: Yoshinori Sato <ysato@users.sourceforge.jp> |
Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
--- |
--- a/include/linux/gfp.h |
+++ b/include/linux/gfp.h |
@@ -506,6 +506,8 @@ extern void free_hot_cold_page(struct pa |
extern void free_hot_cold_page_list(struct list_head *list, bool cold); |
struct page_frag_cache; |
+extern void __page_frag_drain(struct page *page, unsigned int order, |
+ unsigned int count); |
extern void *__alloc_page_frag(struct page_frag_cache *nc, |
unsigned int fragsz, gfp_t gfp_mask); |
extern void __free_page_frag(void *addr); |
--- a/mm/page_alloc.c |
+++ b/mm/page_alloc.c |
@@ -3935,6 +3935,20 @@ static struct page *__page_frag_refill(s |
return page; |
} |
+void __page_frag_drain(struct page *page, unsigned int order, |
+ unsigned int count) |
+{ |
+ VM_BUG_ON_PAGE(page_ref_count(page) == 0, page); |
+ |
+ if (page_ref_sub_and_test(page, count)) { |
+ if (order == 0) |
+ free_hot_cold_page(page, false); |
+ else |
+ __free_pages_ok(page, order); |
+ } |
+} |
+EXPORT_SYMBOL(__page_frag_drain); |
+ |
void *__alloc_page_frag(struct page_frag_cache *nc, |
unsigned int fragsz, gfp_t gfp_mask) |
{ |
/branches/18.06.1/target/linux/generic/backport-4.9/041-mm-rename-__alloc_page_frag-to-page_frag_alloc-and-_.patch |
---|
@@ -0,0 +1,137 @@ |
From: Alexander Duyck <alexander.h.duyck@intel.com> |
Date: Tue, 10 Jan 2017 16:58:06 -0800 |
Subject: [PATCH] mm: rename __alloc_page_frag to page_frag_alloc and |
__free_page_frag to page_frag_free |
Patch series "Page fragment updates", v4. |
This patch series takes care of a few cleanups for the page fragments |
API. |
First we do some renames so that things are much more consistent. First |
we move the page_frag_ portion of the name to the front of the functions |
names. Secondly we split out the cache specific functions from the |
other page fragment functions by adding the word "cache" to the name. |
Finally I added a bit of documentation that will hopefully help to |
explain some of this. I plan to revisit this later as we get things |
more ironed out in the near future with the changes planned for the DMA |
setup to support eXpress Data Path. |
This patch (of 3): |
This patch renames the page frag functions to be more consistent with |
other APIs. Specifically we place the name page_frag first in the name |
and then have either an alloc or free call name that we append as the |
suffix. This makes it a bit clearer in terms of naming. |
In addition we drop the leading double underscores since we are |
technically no longer a backing interface and instead the front end that |
is called from the networking APIs. |
Link: http://lkml.kernel.org/r/20170104023854.13451.67390.stgit@localhost.localdomain |
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com> |
Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
--- |
--- a/include/linux/gfp.h |
+++ b/include/linux/gfp.h |
@@ -508,9 +508,9 @@ extern void free_hot_cold_page_list(stru |
struct page_frag_cache; |
extern void __page_frag_drain(struct page *page, unsigned int order, |
unsigned int count); |
-extern void *__alloc_page_frag(struct page_frag_cache *nc, |
- unsigned int fragsz, gfp_t gfp_mask); |
-extern void __free_page_frag(void *addr); |
+extern void *page_frag_alloc(struct page_frag_cache *nc, |
+ unsigned int fragsz, gfp_t gfp_mask); |
+extern void page_frag_free(void *addr); |
#define __free_page(page) __free_pages((page), 0) |
#define free_page(addr) free_pages((addr), 0) |
--- a/include/linux/skbuff.h |
+++ b/include/linux/skbuff.h |
@@ -2476,7 +2476,7 @@ static inline struct sk_buff *netdev_all |
static inline void skb_free_frag(void *addr) |
{ |
- __free_page_frag(addr); |
+ page_frag_free(addr); |
} |
void *napi_alloc_frag(unsigned int fragsz); |
--- a/mm/page_alloc.c |
+++ b/mm/page_alloc.c |
@@ -3949,8 +3949,8 @@ void __page_frag_drain(struct page *page |
} |
EXPORT_SYMBOL(__page_frag_drain); |
-void *__alloc_page_frag(struct page_frag_cache *nc, |
- unsigned int fragsz, gfp_t gfp_mask) |
+void *page_frag_alloc(struct page_frag_cache *nc, |
+ unsigned int fragsz, gfp_t gfp_mask) |
{ |
unsigned int size = PAGE_SIZE; |
struct page *page; |
@@ -4001,19 +4001,19 @@ refill: |
return nc->va + offset; |
} |
-EXPORT_SYMBOL(__alloc_page_frag); |
+EXPORT_SYMBOL(page_frag_alloc); |
/* |
* Frees a page fragment allocated out of either a compound or order 0 page. |
*/ |
-void __free_page_frag(void *addr) |
+void page_frag_free(void *addr) |
{ |
struct page *page = virt_to_head_page(addr); |
if (unlikely(put_page_testzero(page))) |
__free_pages_ok(page, compound_order(page)); |
} |
-EXPORT_SYMBOL(__free_page_frag); |
+EXPORT_SYMBOL(page_frag_free); |
static void *make_alloc_exact(unsigned long addr, unsigned int order, |
size_t size) |
--- a/net/core/skbuff.c |
+++ b/net/core/skbuff.c |
@@ -369,7 +369,7 @@ static void *__netdev_alloc_frag(unsigne |
local_irq_save(flags); |
nc = this_cpu_ptr(&netdev_alloc_cache); |
- data = __alloc_page_frag(nc, fragsz, gfp_mask); |
+ data = page_frag_alloc(nc, fragsz, gfp_mask); |
local_irq_restore(flags); |
return data; |
} |
@@ -393,7 +393,7 @@ static void *__napi_alloc_frag(unsigned |
{ |
struct napi_alloc_cache *nc = this_cpu_ptr(&napi_alloc_cache); |
- return __alloc_page_frag(&nc->page, fragsz, gfp_mask); |
+ return page_frag_alloc(&nc->page, fragsz, gfp_mask); |
} |
void *napi_alloc_frag(unsigned int fragsz) |
@@ -445,7 +445,7 @@ struct sk_buff *__netdev_alloc_skb(struc |
local_irq_save(flags); |
nc = this_cpu_ptr(&netdev_alloc_cache); |
- data = __alloc_page_frag(nc, len, gfp_mask); |
+ data = page_frag_alloc(nc, len, gfp_mask); |
pfmemalloc = nc->pfmemalloc; |
local_irq_restore(flags); |
@@ -509,7 +509,7 @@ struct sk_buff *__napi_alloc_skb(struct |
if (sk_memalloc_socks()) |
gfp_mask |= __GFP_MEMALLOC; |
- data = __alloc_page_frag(&nc->page, len, gfp_mask); |
+ data = page_frag_alloc(&nc->page, len, gfp_mask); |
if (unlikely(!data)) |
return NULL; |
/branches/18.06.1/target/linux/generic/backport-4.9/042-mm-rename-__page_frag-functions-to-__page_frag_cache.patch |
---|
@@ -0,0 +1,79 @@ |
From: Alexander Duyck <alexander.h.duyck@intel.com> |
Date: Tue, 10 Jan 2017 16:58:09 -0800 |
Subject: [PATCH] mm: rename __page_frag functions to __page_frag_cache, drop |
order from drain |
This patch does two things. |
First it goes through and renames the __page_frag prefixed functions to |
__page_frag_cache so that we can be clear that we are draining or |
refilling the cache, not the frags themselves. |
Second we drop the order parameter from __page_frag_cache_drain since we |
don't actually need to pass it since all fragments are either order 0 or |
must be a compound page. |
Link: http://lkml.kernel.org/r/20170104023954.13451.5678.stgit@localhost.localdomain |
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com> |
Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
--- |
--- a/include/linux/gfp.h |
+++ b/include/linux/gfp.h |
@@ -506,8 +506,7 @@ extern void free_hot_cold_page(struct pa |
extern void free_hot_cold_page_list(struct list_head *list, bool cold); |
struct page_frag_cache; |
-extern void __page_frag_drain(struct page *page, unsigned int order, |
- unsigned int count); |
+extern void __page_frag_cache_drain(struct page *page, unsigned int count); |
extern void *page_frag_alloc(struct page_frag_cache *nc, |
unsigned int fragsz, gfp_t gfp_mask); |
extern void page_frag_free(void *addr); |
--- a/mm/page_alloc.c |
+++ b/mm/page_alloc.c |
@@ -3914,8 +3914,8 @@ EXPORT_SYMBOL(free_pages); |
* drivers to provide a backing region of memory for use as either an |
* sk_buff->head, or to be used in the "frags" portion of skb_shared_info. |
*/ |
-static struct page *__page_frag_refill(struct page_frag_cache *nc, |
- gfp_t gfp_mask) |
+static struct page *__page_frag_cache_refill(struct page_frag_cache *nc, |
+ gfp_t gfp_mask) |
{ |
struct page *page = NULL; |
gfp_t gfp = gfp_mask; |
@@ -3935,19 +3935,20 @@ static struct page *__page_frag_refill(s |
return page; |
} |
-void __page_frag_drain(struct page *page, unsigned int order, |
- unsigned int count) |
+void __page_frag_cache_drain(struct page *page, unsigned int count) |
{ |
VM_BUG_ON_PAGE(page_ref_count(page) == 0, page); |
if (page_ref_sub_and_test(page, count)) { |
+ unsigned int order = compound_order(page); |
+ |
if (order == 0) |
free_hot_cold_page(page, false); |
else |
__free_pages_ok(page, order); |
} |
} |
-EXPORT_SYMBOL(__page_frag_drain); |
+EXPORT_SYMBOL(__page_frag_cache_drain); |
void *page_frag_alloc(struct page_frag_cache *nc, |
unsigned int fragsz, gfp_t gfp_mask) |
@@ -3958,7 +3959,7 @@ void *page_frag_alloc(struct page_frag_c |
if (unlikely(!nc->va)) { |
refill: |
- page = __page_frag_refill(nc, gfp_mask); |
+ page = __page_frag_cache_refill(nc, gfp_mask); |
if (!page) |
return NULL; |
/branches/18.06.1/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 |
/branches/18.06.1/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 |
/branches/18.06.1/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; |
/branches/18.06.1/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; |
/branches/18.06.1/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 |
@@ -878,6 +878,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) }, |
/branches/18.06.1/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 |
@@ -1269,6 +1269,13 @@ static int spansion_quad_enable(struct s |
return ret; |
} |
+ 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))) { |
/branches/18.06.1/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 |
@@ -912,7 +912,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) }, |
/branches/18.06.1/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 |
@@ -928,6 +928,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) }, |
/branches/18.06.1/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) }, |
/branches/18.06.1/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 |
@@ -885,6 +885,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) }, |
/branches/18.06.1/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) }, |
/branches/18.06.1/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. |
@@ -1020,6 +1079,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) }, |
{ }, |
}; |
@@ -1060,7 +1126,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; |
@@ -1181,8 +1252,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); |
@@ -1190,8 +1276,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; |
@@ -1325,6 +1414,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; |
@@ -1373,6 +1503,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 |
*/ |
@@ -1530,6 +1668,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), |
}; |
/** |
/branches/18.06.1/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 |
@@ -1311,6 +1311,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); |
/branches/18.06.1/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 |
@@ -1269,9 +1269,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); |
/branches/18.06.1/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 |
@@ -1638,16 +1638,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. */ |
/branches/18.06.1/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) |
@@ -1634,27 +1710,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; |
/branches/18.06.1/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) }, |
/branches/18.06.1/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; |
} |
/* |
/branches/18.06.1/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) |
/branches/18.06.1/target/linux/generic/backport-4.9/063-v4.11-0001-mtd-nand-Add-Winbond-manufacturer-id.patch |
---|
@@ -0,0 +1,34 @@ |
From a4077ce5871304f8a78f80b74b18b6052a410f1a Mon Sep 17 00:00:00 2001 |
From: "Andrey Jr. Melnikov" <temnota.am@gmail.com> |
Date: Thu, 8 Dec 2016 19:57:08 +0300 |
Subject: [PATCH] mtd: nand: Add Winbond manufacturer id |
Add WINBOND manufacturer id. |
Signed-off-by: Andrey Jr. Melnikov <temnota.am@gmail.com> |
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> |
--- |
drivers/mtd/nand/nand_ids.c | 1 + |
include/linux/mtd/nand.h | 1 + |
2 files changed, 2 insertions(+) |
--- a/drivers/mtd/nand/nand_ids.c |
+++ b/drivers/mtd/nand/nand_ids.c |
@@ -182,6 +182,7 @@ struct nand_manufacturers nand_manuf_ids |
{NAND_MFR_SANDISK, "SanDisk"}, |
{NAND_MFR_INTEL, "Intel"}, |
{NAND_MFR_ATO, "ATO"}, |
+ {NAND_MFR_WINBOND, "Winbond"}, |
{0x0, "Unknown"} |
}; |
--- a/include/linux/mtd/nand.h |
+++ b/include/linux/mtd/nand.h |
@@ -928,6 +928,7 @@ static inline void nand_set_controller_d |
#define NAND_MFR_SANDISK 0x45 |
#define NAND_MFR_INTEL 0x89 |
#define NAND_MFR_ATO 0x9b |
+#define NAND_MFR_WINBOND 0xef |
/* The maximum expected count of bytes in the NAND ID sequence */ |
#define NAND_MAX_ID_LEN 8 |
/branches/18.06.1/target/linux/generic/backport-4.9/063-v4.11-0002-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, |
/branches/18.06.1/target/linux/generic/backport-4.9/063-v4.11-0003-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) |
/branches/18.06.1/target/linux/generic/backport-4.9/064-v4.12-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 |
@@ -1023,7 +1023,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) }, |
/branches/18.06.1/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); |
} |
/branches/18.06.1/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 |
/branches/18.06.1/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); |
/branches/18.06.1/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; |
} |
/branches/18.06.1/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); |
/branches/18.06.1/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); |
/branches/18.06.1/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 */ |
/branches/18.06.1/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"); |
/branches/18.06.1/target/linux/generic/backport-4.9/066-v4.17-0001-mtd-move-code-adding-master-MTD-out-of-mtd_add_devic.patch |
---|
@@ -0,0 +1,74 @@ |
From 2c77c57d22adb05b21cdb333a0c42bdfa0e19835 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Tue, 16 Jan 2018 16:45:41 +0100 |
Subject: [PATCH] mtd: move code adding master MTD out of |
mtd_add_device_partitions() |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
This change is a small cleanup of mtd_device_parse_register(). When |
using MTD_PARTITIONED_MASTER it makes sure a master MTD is registered |
before dealing with partitions. The advantage of this is not mixing |
code handling master MTD with code handling partitions. |
This commit doesn't change any behavior except from a slightly different |
failure code path. The new code may need to call del_mtd_device when |
something goes wrong. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/mtdcore.c | 25 +++++++++++++------------ |
1 file changed, 13 insertions(+), 12 deletions(-) |
--- a/drivers/mtd/mtdcore.c |
+++ b/drivers/mtd/mtdcore.c |
@@ -631,20 +631,12 @@ static int mtd_add_device_partitions(str |
{ |
const struct mtd_partition *real_parts = parts->parts; |
int nbparts = parts->nr_parts; |
- int ret; |
- if (nbparts == 0 || IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) { |
- ret = add_mtd_device(mtd); |
- if (ret) |
- return ret; |
- } |
+ if (!nbparts && !device_is_registered(&mtd->dev)) |
+ return add_mtd_device(mtd); |
- if (nbparts > 0) { |
- ret = add_mtd_partitions(mtd, real_parts, nbparts); |
- if (ret && IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) |
- del_mtd_device(mtd); |
- return ret; |
- } |
+ if (nbparts > 0) |
+ return add_mtd_partitions(mtd, real_parts, nbparts); |
return 0; |
} |
@@ -704,6 +696,12 @@ int mtd_device_parse_register(struct mtd |
mtd_set_dev_defaults(mtd); |
+ if (IS_ENABLED(CONFIG_MTD_PARTITIONED_MASTER)) { |
+ ret = add_mtd_device(mtd); |
+ if (ret) |
+ return ret; |
+ } |
+ |
memset(&parsed, 0, sizeof(parsed)); |
ret = parse_mtd_partitions(mtd, types, &parsed, parser_data); |
@@ -743,6 +741,9 @@ int mtd_device_parse_register(struct mtd |
out: |
/* Cleanup any parsed partitions */ |
mtd_part_parser_cleanup(&parsed); |
+ if (ret && device_is_registered(&mtd->dev)) |
+ del_mtd_device(mtd); |
+ |
return ret; |
} |
EXPORT_SYMBOL_GPL(mtd_device_parse_register); |
/branches/18.06.1/target/linux/generic/backport-4.9/066-v4.17-0002-mtd-get-rid-of-the-mtd_add_device_partitions.patch |
---|
@@ -0,0 +1,93 @@ |
From 0dbe4ea78d69756efeb0bba0764f6bd4a9ee9567 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Tue, 16 Jan 2018 16:45:42 +0100 |
Subject: [PATCH] mtd: get rid of the mtd_add_device_partitions() |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
This simplifies code a bit by: |
1) Avoiding an extra (tiny) function |
2) Checking for amount of parsed (found) partitions just once |
3) Avoiding clearing/filling struct mtd_partitions manually |
With this commit proper functions are called directly from the |
mtd_device_parse_register(). It doesn't need to use minor tricks like |
memsetting struct to 0 to trigger an expected |
mtd_add_device_partitions() behavior. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/mtdcore.c | 43 ++++++++++++------------------------------- |
1 file changed, 12 insertions(+), 31 deletions(-) |
--- a/drivers/mtd/mtdcore.c |
+++ b/drivers/mtd/mtdcore.c |
@@ -626,21 +626,6 @@ out_error: |
return ret; |
} |
-static int mtd_add_device_partitions(struct mtd_info *mtd, |
- struct mtd_partitions *parts) |
-{ |
- const struct mtd_partition *real_parts = parts->parts; |
- int nbparts = parts->nr_parts; |
- |
- if (!nbparts && !device_is_registered(&mtd->dev)) |
- return add_mtd_device(mtd); |
- |
- if (nbparts > 0) |
- return add_mtd_partitions(mtd, real_parts, nbparts); |
- |
- return 0; |
-} |
- |
/* |
* Set a few defaults based on the parent devices, if not provided by the |
* driver |
@@ -691,7 +676,7 @@ int mtd_device_parse_register(struct mtd |
const struct mtd_partition *parts, |
int nr_parts) |
{ |
- struct mtd_partitions parsed; |
+ struct mtd_partitions parsed = { }; |
int ret; |
mtd_set_dev_defaults(mtd); |
@@ -702,24 +687,20 @@ int mtd_device_parse_register(struct mtd |
return ret; |
} |
- memset(&parsed, 0, sizeof(parsed)); |
- |
+ /* Prefer parsed partitions over driver-provided fallback */ |
ret = parse_mtd_partitions(mtd, types, &parsed, parser_data); |
- if ((ret < 0 || parsed.nr_parts == 0) && parts && nr_parts) { |
- /* Fall back to driver-provided partitions */ |
- parsed = (struct mtd_partitions){ |
- .parts = parts, |
- .nr_parts = nr_parts, |
- }; |
- } else if (ret < 0) { |
- /* Didn't come up with parsed OR fallback partitions */ |
- pr_info("mtd: failed to find partitions; one or more parsers reports errors (%d)\n", |
- ret); |
- /* Don't abort on errors; we can still use unpartitioned MTD */ |
- memset(&parsed, 0, sizeof(parsed)); |
+ if (!ret && parsed.nr_parts) { |
+ parts = parsed.parts; |
+ nr_parts = parsed.nr_parts; |
} |
- ret = mtd_add_device_partitions(mtd, &parsed); |
+ if (nr_parts) |
+ ret = add_mtd_partitions(mtd, parts, nr_parts); |
+ else if (!device_is_registered(&mtd->dev)) |
+ ret = add_mtd_device(mtd); |
+ else |
+ ret = 0; |
+ |
if (ret) |
goto out; |
/branches/18.06.1/target/linux/generic/backport-4.9/067-v4.17-0001-mtd-partitions-add-of_match_table-parser-matching-fo.patch |
---|
@@ -0,0 +1,200 @@ |
From 5b644aa012f67fd211138a067b9f351f30bdcc60 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Wed, 14 Mar 2018 13:10:42 +0100 |
Subject: [PATCH] mtd: partitions: add of_match_table parser matching for the |
"ofpart" type |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
In order to properly support compatibility strings as described in the |
bindings/mtd/partition.txt "ofpart" type should be treated as an |
indication for looking into OF. MTD should check "compatible" property |
and search for a matching parser rather than blindly trying the one |
supporting "fixed-partitions". |
It also means that existing "fixed-partitions" parser should get renamed |
to use a more meaningful name. |
This commit achievies that aim by introducing a new mtd_part_of_parse(). |
It works by looking for a matching parser for every string in the |
"compatibility" property (starting with the most specific one). |
Please note that driver-specified parsers still take a precedence. It's |
assumed that driver providing a parser type has a good reason for that |
(e.g. having platform data with device-specific info). Also doing |
otherwise could break existing setups. The same applies to using default |
parsers (including "cmdlinepart") as some overwrite DT data with cmdline |
argument. |
Partition parsers can now provide an of_match_table to enable |
flash<-->parser matching via device tree as documented in the |
mtd/partition.txt. |
This support is currently limited to built-in parsers as it uses |
request_module() and friends. This should be sufficient for most cases |
though as compiling parsers as modules isn't a common choice. |
Signed-off-by: Brian Norris <computersforpeace@gmail.com> |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Tested-by: Peter Rosin <peda@axentia.se> |
Reviewed-by: Richard Weinberger <richard@nod.at> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/mtdpart.c | 116 +++++++++++++++++++++++++++++++++++++---- |
include/linux/mtd/partitions.h | 1 + |
2 files changed, 108 insertions(+), 9 deletions(-) |
--- a/drivers/mtd/mtdpart.c |
+++ b/drivers/mtd/mtdpart.c |
@@ -30,6 +30,7 @@ |
#include <linux/mtd/mtd.h> |
#include <linux/mtd/partitions.h> |
#include <linux/err.h> |
+#include <linux/of.h> |
#include "mtdcore.h" |
@@ -886,6 +887,92 @@ static int mtd_part_do_parse(struct mtd_ |
} |
/** |
+ * mtd_part_get_compatible_parser - find MTD parser by a compatible string |
+ * |
+ * @compat: compatible string describing partitions in a device tree |
+ * |
+ * MTD parsers can specify supported partitions by providing a table of |
+ * compatibility strings. This function finds a parser that advertises support |
+ * for a passed value of "compatible". |
+ */ |
+static struct mtd_part_parser *mtd_part_get_compatible_parser(const char *compat) |
+{ |
+ struct mtd_part_parser *p, *ret = NULL; |
+ |
+ spin_lock(&part_parser_lock); |
+ |
+ list_for_each_entry(p, &part_parsers, list) { |
+ const struct of_device_id *matches; |
+ |
+ matches = p->of_match_table; |
+ if (!matches) |
+ continue; |
+ |
+ for (; matches->compatible[0]; matches++) { |
+ if (!strcmp(matches->compatible, compat) && |
+ try_module_get(p->owner)) { |
+ ret = p; |
+ break; |
+ } |
+ } |
+ |
+ if (ret) |
+ break; |
+ } |
+ |
+ spin_unlock(&part_parser_lock); |
+ |
+ return ret; |
+} |
+ |
+static int mtd_part_of_parse(struct mtd_info *master, |
+ struct mtd_partitions *pparts) |
+{ |
+ struct mtd_part_parser *parser; |
+ struct device_node *np; |
+ struct property *prop; |
+ const char *compat; |
+ const char *fixed = "ofpart"; |
+ int ret, err = 0; |
+ |
+ np = of_get_child_by_name(mtd_get_of_node(master), "partitions"); |
+ of_property_for_each_string(np, "compatible", prop, compat) { |
+ parser = mtd_part_get_compatible_parser(compat); |
+ if (!parser) |
+ continue; |
+ ret = mtd_part_do_parse(parser, master, pparts, NULL); |
+ if (ret > 0) { |
+ of_node_put(np); |
+ return ret; |
+ } |
+ mtd_part_parser_put(parser); |
+ if (ret < 0 && !err) |
+ err = ret; |
+ } |
+ of_node_put(np); |
+ |
+ /* |
+ * For backward compatibility we have to try the "ofpart" |
+ * parser. It supports old DT format with partitions specified as a |
+ * direct subnodes of a flash device DT node without any compatibility |
+ * specified we could match. |
+ */ |
+ parser = mtd_part_parser_get(fixed); |
+ if (!parser && !request_module("%s", fixed)) |
+ parser = mtd_part_parser_get(fixed); |
+ if (parser) { |
+ ret = mtd_part_do_parse(parser, master, pparts, NULL); |
+ if (ret > 0) |
+ return ret; |
+ mtd_part_parser_put(parser); |
+ if (ret < 0 && !err) |
+ err = ret; |
+ } |
+ |
+ return err; |
+} |
+ |
+/** |
* parse_mtd_partitions - parse MTD partitions |
* @master: the master partition (describes whole MTD device) |
* @types: names of partition parsers to try or %NULL |
@@ -917,19 +1004,30 @@ int parse_mtd_partitions(struct mtd_info |
types = default_mtd_part_types; |
for ( ; *types; types++) { |
- pr_debug("%s: parsing partitions %s\n", master->name, *types); |
- parser = mtd_part_parser_get(*types); |
- if (!parser && !request_module("%s", *types)) |
+ /* |
+ * ofpart is a special type that means OF partitioning info |
+ * should be used. It requires a bit different logic so it is |
+ * handled in a separated function. |
+ */ |
+ if (!strcmp(*types, "ofpart")) { |
+ ret = mtd_part_of_parse(master, pparts); |
+ } else { |
+ pr_debug("%s: parsing partitions %s\n", master->name, |
+ *types); |
parser = mtd_part_parser_get(*types); |
- pr_debug("%s: got parser %s\n", master->name, |
- parser ? parser->name : NULL); |
- if (!parser) |
- continue; |
- ret = mtd_part_do_parse(parser, master, pparts, data); |
+ if (!parser && !request_module("%s", *types)) |
+ parser = mtd_part_parser_get(*types); |
+ pr_debug("%s: got parser %s\n", master->name, |
+ parser ? parser->name : NULL); |
+ if (!parser) |
+ continue; |
+ ret = mtd_part_do_parse(parser, master, pparts, data); |
+ if (ret <= 0) |
+ mtd_part_parser_put(parser); |
+ } |
/* Found partitions! */ |
if (ret > 0) |
return 0; |
- mtd_part_parser_put(parser); |
/* |
* Stash the first error we see; only report it if no parser |
* succeeds |
--- a/include/linux/mtd/partitions.h |
+++ b/include/linux/mtd/partitions.h |
@@ -77,6 +77,7 @@ struct mtd_part_parser { |
struct list_head list; |
struct module *owner; |
const char *name; |
+ const struct of_device_id *of_match_table; |
int (*parse_fn)(struct mtd_info *, const struct mtd_partition **, |
struct mtd_part_parser_data *); |
void (*cleanup)(const struct mtd_partition *pparts, int nr_parts); |
/branches/18.06.1/target/linux/generic/backport-4.9/067-v4.17-0002-mtd-rename-ofpart-parser-to-fixed-partitions-as-it-f.patch |
---|
@@ -0,0 +1,74 @@ |
From c0faf43482e7f7dfb6d61847cb93d17748560b24 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Wed, 14 Mar 2018 13:10:43 +0100 |
Subject: [PATCH] mtd: rename "ofpart" parser to "fixed-partitions" as it fits |
it better |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
Type "ofpart" means that OF should be used to get partitioning info and |
this driver supports "fixed-partitions" binding only. Renaming it should |
lead to less confusion especially when parsers for new compatibility |
strings start to appear. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Reviewed-by: Richard Weinberger <richard@nod.at> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/mtdpart.c | 4 ++-- |
drivers/mtd/ofpart.c | 11 ++++++----- |
2 files changed, 8 insertions(+), 7 deletions(-) |
--- a/drivers/mtd/mtdpart.c |
+++ b/drivers/mtd/mtdpart.c |
@@ -932,7 +932,7 @@ static int mtd_part_of_parse(struct mtd_ |
struct device_node *np; |
struct property *prop; |
const char *compat; |
- const char *fixed = "ofpart"; |
+ const char *fixed = "fixed-partitions"; |
int ret, err = 0; |
np = of_get_child_by_name(mtd_get_of_node(master), "partitions"); |
@@ -952,7 +952,7 @@ static int mtd_part_of_parse(struct mtd_ |
of_node_put(np); |
/* |
- * For backward compatibility we have to try the "ofpart" |
+ * For backward compatibility we have to try the "fixed-partitions" |
* parser. It supports old DT format with partitions specified as a |
* direct subnodes of a flash device DT node without any compatibility |
* specified we could match. |
--- a/drivers/mtd/ofpart.c |
+++ b/drivers/mtd/ofpart.c |
@@ -25,9 +25,9 @@ static bool node_has_compatible(struct d |
return of_get_property(pp, "compatible", NULL); |
} |
-static int parse_ofpart_partitions(struct mtd_info *master, |
- const struct mtd_partition **pparts, |
- struct mtd_part_parser_data *data) |
+static int parse_fixed_partitions(struct mtd_info *master, |
+ const struct mtd_partition **pparts, |
+ struct mtd_part_parser_data *data) |
{ |
struct mtd_partition *parts; |
struct device_node *mtd_node; |
@@ -141,8 +141,8 @@ ofpart_none: |
} |
static struct mtd_part_parser ofpart_parser = { |
- .parse_fn = parse_ofpart_partitions, |
- .name = "ofpart", |
+ .parse_fn = parse_fixed_partitions, |
+ .name = "fixed-partitions", |
}; |
static int parse_ofoldpart_partitions(struct mtd_info *master, |
@@ -230,4 +230,5 @@ MODULE_AUTHOR("Vitaly Wool, David Gibson |
* with the same name. Since we provide the ofoldpart parser, we should have |
* the corresponding alias. |
*/ |
+MODULE_ALIAS("fixed-partitions"); |
MODULE_ALIAS("ofoldpart"); |
/branches/18.06.1/target/linux/generic/backport-4.9/067-v4.17-0003-mtd-ofpart-add-of_match_table-with-fixed-partitions.patch |
---|
@@ -0,0 +1,44 @@ |
From 97b0c7c0df3efd7048ed39d7e2dee34cafd55887 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Wed, 14 Mar 2018 13:10:44 +0100 |
Subject: [PATCH] mtd: ofpart: add of_match_table with "fixed-partitions" |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
This allows using this parser with any flash driver that takes care of |
setting of_node (using mtd_set_of_node helper) correctly. Up to now |
support for "fixed-partitions" DT compatibility string was working only |
with flash drivers that were specifying "ofpart" (manually or by letting |
mtd use the default set of parsers). |
This matches existing bindings documentation. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Reviewed-by: Brian Norris <computersforpeace@gmail.com> |
Tested-by: Brian Norris <computersforpeace@gmail.com> |
Reviewed-by: Richard Weinberger <richard@nod.at> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/ofpart.c | 7 +++++++ |
1 file changed, 7 insertions(+) |
--- a/drivers/mtd/ofpart.c |
+++ b/drivers/mtd/ofpart.c |
@@ -140,9 +140,16 @@ ofpart_none: |
return ret; |
} |
+static const struct of_device_id parse_ofpart_match_table[] = { |
+ { .compatible = "fixed-partitions" }, |
+ {}, |
+}; |
+MODULE_DEVICE_TABLE(of, parse_ofpart_match_table); |
+ |
static struct mtd_part_parser ofpart_parser = { |
.parse_fn = parse_fixed_partitions, |
.name = "fixed-partitions", |
+ .of_match_table = parse_ofpart_match_table, |
}; |
static int parse_ofoldpart_partitions(struct mtd_info *master, |
/branches/18.06.1/target/linux/generic/backport-4.9/068-v4.18-0001-mtd-move-code-adding-registering-partitions-to-the-p.patch |
---|
@@ -0,0 +1,168 @@ |
From 5ac67ce36cfe38b4c104a42ce52c5c8d526f1c95 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Tue, 27 Mar 2018 22:35:41 +0200 |
Subject: [PATCH] mtd: move code adding (registering) partitions to the |
parse_mtd_partitions() |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
This commit slightly simplifies the code. Every parse_mtd_partitions() |
caller (out of two existing ones) had to add partitions & cleanup parser |
on its own. This moves that responsibility into the function. |
That change also allows dropping struct mtd_partitions argument. |
There is one minor behavior change caused by this cleanup. If |
parse_mtd_partitions() fails to add partitions (add_mtd_partitions() |
return an error) then mtd_device_parse_register() will still try to |
add (register) fallback partitions. It's a real corner case affecting |
one of uncommon error paths and shouldn't cause any harm. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/mtdcore.c | 14 ++++---------- |
drivers/mtd/mtdcore.h | 1 - |
drivers/mtd/mtdpart.c | 44 ++++++++++++++++---------------------------- |
3 files changed, 20 insertions(+), 39 deletions(-) |
--- a/drivers/mtd/mtdcore.c |
+++ b/drivers/mtd/mtdcore.c |
@@ -676,7 +676,6 @@ int mtd_device_parse_register(struct mtd |
const struct mtd_partition *parts, |
int nr_parts) |
{ |
- struct mtd_partitions parsed = { }; |
int ret; |
mtd_set_dev_defaults(mtd); |
@@ -688,13 +687,10 @@ int mtd_device_parse_register(struct mtd |
} |
/* Prefer parsed partitions over driver-provided fallback */ |
- ret = parse_mtd_partitions(mtd, types, &parsed, parser_data); |
- if (!ret && parsed.nr_parts) { |
- parts = parsed.parts; |
- nr_parts = parsed.nr_parts; |
- } |
- |
- if (nr_parts) |
+ ret = parse_mtd_partitions(mtd, types, parser_data); |
+ if (ret > 0) |
+ ret = 0; |
+ else if (nr_parts) |
ret = add_mtd_partitions(mtd, parts, nr_parts); |
else if (!device_is_registered(&mtd->dev)) |
ret = add_mtd_device(mtd); |
@@ -720,8 +716,6 @@ int mtd_device_parse_register(struct mtd |
} |
out: |
- /* Cleanup any parsed partitions */ |
- mtd_part_parser_cleanup(&parsed); |
if (ret && device_is_registered(&mtd->dev)) |
del_mtd_device(mtd); |
--- a/drivers/mtd/mtdcore.h |
+++ b/drivers/mtd/mtdcore.h |
@@ -14,7 +14,6 @@ int del_mtd_partitions(struct mtd_info * |
struct mtd_partitions; |
int parse_mtd_partitions(struct mtd_info *master, const char * const *types, |
- struct mtd_partitions *pparts, |
struct mtd_part_parser_data *data); |
void mtd_part_parser_cleanup(struct mtd_partitions *parts); |
--- a/drivers/mtd/mtdpart.c |
+++ b/drivers/mtd/mtdpart.c |
@@ -383,20 +383,7 @@ static inline void free_partition(struct |
*/ |
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; |
+ return parse_mtd_partitions(&slave->mtd, types, NULL); |
} |
static struct mtd_part *allocate_partition(struct mtd_info *parent, |
@@ -973,30 +960,27 @@ static int mtd_part_of_parse(struct mtd_ |
} |
/** |
- * parse_mtd_partitions - parse MTD partitions |
+ * parse_mtd_partitions - parse and register MTD partitions |
+ * |
* @master: the master partition (describes whole MTD device) |
* @types: names of partition parsers to try or %NULL |
- * @pparts: info about partitions found is returned here |
* @data: MTD partition parser-specific data |
* |
- * This function tries to find partition on MTD device @master. It uses MTD |
- * partition parsers, specified in @types. However, if @types is %NULL, then |
- * the default list of parsers is used. The default list contains only the |
+ * This function tries to find & register partitions on MTD device @master. It |
+ * uses MTD partition parsers, specified in @types. However, if @types is %NULL, |
+ * then the default list of parsers is used. The default list contains only the |
* "cmdlinepart" and "ofpart" parsers ATM. |
* Note: If there are more then one parser in @types, the kernel only takes the |
* partitions parsed out by the first parser. |
* |
* This function may return: |
* o a negative error code in case of failure |
- * o zero otherwise, and @pparts will describe the partitions, number of |
- * partitions, and the parser which parsed them. Caller must release |
- * resources with mtd_part_parser_cleanup() when finished with the returned |
- * data. |
+ * o number of found partitions otherwise |
*/ |
int parse_mtd_partitions(struct mtd_info *master, const char *const *types, |
- struct mtd_partitions *pparts, |
struct mtd_part_parser_data *data) |
{ |
+ struct mtd_partitions pparts = { }; |
struct mtd_part_parser *parser; |
int ret, err = 0; |
@@ -1010,7 +994,7 @@ int parse_mtd_partitions(struct mtd_info |
* handled in a separated function. |
*/ |
if (!strcmp(*types, "ofpart")) { |
- ret = mtd_part_of_parse(master, pparts); |
+ ret = mtd_part_of_parse(master, &pparts); |
} else { |
pr_debug("%s: parsing partitions %s\n", master->name, |
*types); |
@@ -1021,13 +1005,17 @@ int parse_mtd_partitions(struct mtd_info |
parser ? parser->name : NULL); |
if (!parser) |
continue; |
- ret = mtd_part_do_parse(parser, master, pparts, data); |
+ ret = mtd_part_do_parse(parser, master, &pparts, data); |
if (ret <= 0) |
mtd_part_parser_put(parser); |
} |
/* Found partitions! */ |
- if (ret > 0) |
- return 0; |
+ if (ret > 0) { |
+ err = add_mtd_partitions(master, pparts.parts, |
+ pparts.nr_parts); |
+ mtd_part_parser_cleanup(&pparts); |
+ return err ? err : pparts.nr_parts; |
+ } |
/* |
* Stash the first error we see; only report it if no parser |
* succeeds |
/branches/18.06.1/target/linux/generic/backport-4.9/069-v4.18-mtd-bcm47xxpart-improve-handling-TRX-partition-size.patch |
---|
@@ -0,0 +1,70 @@ |
From 237ea0d4762cc14d0fc80e80d61f0f08e1050c7f Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Thu, 12 Apr 2018 07:24:52 +0200 |
Subject: [PATCH] mtd: bcm47xxpart: improve handling TRX partition size |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
When bcm47xxpart finds a TRX partition (container) it's supposed to jump |
to the end of it and keep looking for more partitions. TRX and its |
subpartitions are handled by a separate parser. |
The problem with old code was relying on the length specified in a TRX |
header. That isn't reliable as TRX is commonly modified to have checksum |
cover only non-changing subpartitions. Otherwise modifying e.g. a rootfs |
would result in CRC32 mismatch and bootloader refusing to boot a |
firmware. |
Fix it by trying better to figure out a real TRX size. We can securely |
assume that TRX has to cover all subpartitions and the last one is at |
least of a block size in size. Then compare it with a length field. |
This makes code more optimal & reliable thanks to skipping data that |
shouldn't be parsed. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/bcm47xxpart.c | 22 ++++++++++++++++++---- |
1 file changed, 18 insertions(+), 4 deletions(-) |
--- a/drivers/mtd/bcm47xxpart.c |
+++ b/drivers/mtd/bcm47xxpart.c |
@@ -186,6 +186,8 @@ static int bcm47xxpart_parse(struct mtd_ |
/* TRX */ |
if (buf[0x000 / 4] == TRX_MAGIC) { |
struct trx_header *trx; |
+ uint32_t last_subpart; |
+ uint32_t trx_size; |
if (trx_num >= ARRAY_SIZE(trx_parts)) |
pr_warn("No enough space to store another TRX found at 0x%X\n", |
@@ -195,11 +197,23 @@ static int bcm47xxpart_parse(struct mtd_ |
bcm47xxpart_add_part(&parts[curr_part++], "firmware", |
offset, 0); |
- /* Jump to the end of TRX */ |
+ /* |
+ * Try to find TRX size. The "length" field isn't fully |
+ * reliable as it could be decreased to make CRC32 cover |
+ * only part of TRX data. It's commonly used as checksum |
+ * can't cover e.g. ever-changing rootfs partition. |
+ * Use offsets as helpers for assuming min TRX size. |
+ */ |
trx = (struct trx_header *)buf; |
- offset = roundup(offset + trx->length, blocksize); |
- /* Next loop iteration will increase the offset */ |
- offset -= blocksize; |
+ last_subpart = max3(trx->offset[0], trx->offset[1], |
+ trx->offset[2]); |
+ trx_size = max(trx->length, last_subpart + blocksize); |
+ |
+ /* |
+ * Skip the TRX data. Decrease offset by block size as |
+ * the next loop iteration will increase it. |
+ */ |
+ offset += roundup(trx_size, blocksize) - blocksize; |
continue; |
} |
/branches/18.06.1/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; |
/branches/18.06.1/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 |
@@ -100,12 +100,11 @@ static int bgmac_probe(struct bcma_devic |
const u8 *mac = NULL; |
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; |
@@ -292,7 +291,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 |
@@ -1460,22 +1460,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); |
@@ -1502,7 +1512,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; |
@@ -1538,8 +1548,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); |
/branches/18.06.1/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 |
@@ -166,7 +166,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) |
/branches/18.06.1/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 */ |
/branches/18.06.1/target/linux/generic/backport-4.9/071-v4.15-0001-net-bgmac-enable-master-mode-for-BCM54210E-and-B5021.patch |
---|
@@ -0,0 +1,50 @@ |
From 12acd136913ccdf394eeb2bc8686ff5505368119 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Thu, 12 Oct 2017 10:21:26 +0200 |
Subject: [PATCH] net: bgmac: enable master mode for BCM54210E and B50212E PHYs |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
There are 4 very similar PHYs: |
0x600d84a1: BCM54210E (rev B0) |
0x600d84a2: BCM54210E (rev B1) |
0x600d84a5: B50212E (rev B0) |
0x600d84a6: B50212E (rev B1) |
that need setting master mode manually. It's because they run in slave |
mode by default with Automatic Slave/Master configuration disabled which |
can lead to unreliable connection with massive ping loss. |
So far it was reported for a board with BCM47189 SoC and B50212E B1 PHY |
connected to the bgmac supported ethernet device. Telling PHY driver to |
setup PHY properly solves this issue. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
drivers/net/ethernet/broadcom/bgmac-bcma.c | 8 +++++++- |
1 file changed, 7 insertions(+), 1 deletion(-) |
--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c |
+++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c |
@@ -166,13 +166,19 @@ static int bgmac_probe(struct bcma_devic |
if (!bgmac_is_bcm4707_family(core) && |
!(ci->id == BCMA_CHIP_ID_BCM53573 && core->core_unit == 1)) { |
+ struct phy_device *phydev; |
+ |
mii_bus = bcma_mdio_mii_register(bgmac); |
if (IS_ERR(mii_bus)) { |
err = PTR_ERR(mii_bus); |
goto err; |
} |
- |
bgmac->mii_bus = mii_bus; |
+ |
+ phydev = mdiobus_get_phy(bgmac->mii_bus, bgmac->phyaddr); |
+ if (ci->id == BCMA_CHIP_ID_BCM53573 && phydev && |
+ (phydev->drv->phy_id & phydev->drv->phy_id_mask) == PHY_ID_BCM54210E) |
+ phydev->dev_flags |= PHY_BRCM_EN_MASTER_MODE; |
} |
if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) { |
/branches/18.06.1/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; |
/branches/18.06.1/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, |
/branches/18.06.1/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 |
/branches/18.06.1/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 |
/branches/18.06.1/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. */ |
/branches/18.06.1/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 |
@@ -26,6 +26,9 @@ static inline int bcm_phy_write_exp_sel( |
return bcm_phy_write_exp(phydev, reg | MII_BCM54XX_EXP_SEL_ER, val); |
} |
+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; |
/branches/18.06.1/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 |
@@ -43,5 +43,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 */ |
/branches/18.06.1/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 |
@@ -44,4 +44,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 |
/branches/18.06.1/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 |
/branches/18.06.1/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; |
} |
/branches/18.06.1/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) |
/branches/18.06.1/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 |
/branches/18.06.1/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, |
/branches/18.06.1/target/linux/generic/backport-4.9/076-v4.15-0001-net-phy-broadcom-support-new-device-flag-for-setting.patch |
---|
@@ -0,0 +1,54 @@ |
From 2355a6546a053b1c16ebefd6ce1f0cccc00e1da5 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Thu, 12 Oct 2017 10:21:25 +0200 |
Subject: [PATCH] net: phy: broadcom: support new device flag for setting |
master mode |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
Some of Broadcom's PHYs run by default in slave mode with Automatic |
Slave/Master configuration disabled. It stops them from working properly |
with some devices. |
So far it has been verified for BCM54210E and BCM50212E which don't |
work well with Intel's I217-LM and I218-LM: |
http://ark.intel.com/products/60019/Intel-Ethernet-Connection-I217-LM |
http://ark.intel.com/products/71307/Intel-Ethernet-Connection-I218-LM |
I was told there is massive ping loss. |
This commit adds support for a new flag which can be set by an ethernet |
driver to fixup PHY setup. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
drivers/net/phy/broadcom.c | 6 ++++++ |
include/linux/brcmphy.h | 1 + |
2 files changed, 7 insertions(+) |
--- a/drivers/net/phy/broadcom.c |
+++ b/drivers/net/phy/broadcom.c |
@@ -43,6 +43,12 @@ static int bcm54210e_config_init(struct |
val &= ~BCM54810_SHD_CLK_CTL_GTXCLK_EN; |
bcm_phy_write_shadow(phydev, BCM54810_SHD_CLK_CTL, val); |
+ if (phydev->dev_flags & PHY_BRCM_EN_MASTER_MODE) { |
+ val = phy_read(phydev, MII_CTRL1000); |
+ val |= CTL1000_AS_MASTER | CTL1000_ENABLE_MASTER; |
+ phy_write(phydev, MII_CTRL1000, val); |
+ } |
+ |
return 0; |
} |
--- a/include/linux/brcmphy.h |
+++ b/include/linux/brcmphy.h |
@@ -59,6 +59,7 @@ |
#define PHY_BRCM_EXT_IBND_TX_ENABLE 0x00002000 |
#define PHY_BRCM_CLEAR_RGMII_MODE 0x00004000 |
#define PHY_BRCM_DIS_TXCRXC_NOENRGY 0x00008000 |
+#define PHY_BRCM_EN_MASTER_MODE 0x00010000 |
/* Broadcom BCM7xxx specific workarounds */ |
#define PHY_BRCM_7XXX_REV(x) (((x) >> 8) & 0xff) |
/branches/18.06.1/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); |
/branches/18.06.1/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; |
} |
/branches/18.06.1/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"); |
/branches/18.06.1/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"); |
/branches/18.06.1/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, |
/branches/18.06.1/target/linux/generic/backport-4.9/085-v4.16-0001-i2c-gpio-Enable-working-over-slow-can_sleep-GPIOs.patch |
---|
@@ -0,0 +1,84 @@ |
From f11a04464ae57e8db1bb7634547842b43e36a898 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Jan=20Kundr=C3=A1t?= <jan.kundrat@cesnet.cz> |
Date: Fri, 22 Dec 2017 22:47:16 +0100 |
Subject: i2c: gpio: Enable working over slow can_sleep GPIOs |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
"Slow" GPIOs (usually those connected over an SPI or an I2C bus) are, |
well, slow in their operation. It is generally a good idea to avoid |
using them for time-critical operation, but sometimes the hardware just |
sucks, and the software has to cope. In addition to that, the I2C bus |
itself does not actually define any strict timing limits; the bus is |
free to go all the way down to DC. The timeouts (and therefore the |
slowest acceptable frequency) are present only in SMBus. |
The `can_sleep` is IMHO a wrong concept to use here. My SPI-to-quad-UART |
chip (MAX14830) is connected via a 26MHz SPI bus, and it happily drives |
SCL at 200kHz (5µs pulses) during my benchmarks. That's faster than the |
maximal allowed speed of the traditional I2C. |
The previous version of this code did not really block operation over |
slow GPIO pins, anyway. Instead, it just resorted to printing a warning |
with a backtrace each time a GPIO pin was accessed, thereby slowing |
things down even more. |
Finally, it's not just me. A similar patch was originally submitted in |
2015 [1]. |
[1] https://patchwork.ozlabs.org/patch/450956/ |
Signed-off-by: Jan Kundrát <jan.kundrat@cesnet.cz> |
Acked-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de> |
Signed-off-by: Wolfram Sang <wsa@the-dreams.de> |
--- |
drivers/i2c/busses/i2c-gpio.c | 11 +++++++---- |
1 file changed, 7 insertions(+), 4 deletions(-) |
--- a/drivers/i2c/busses/i2c-gpio.c |
+++ b/drivers/i2c/busses/i2c-gpio.c |
@@ -44,7 +44,7 @@ static void i2c_gpio_setsda_val(void *da |
{ |
struct i2c_gpio_platform_data *pdata = data; |
- gpio_set_value(pdata->sda_pin, state); |
+ gpio_set_value_cansleep(pdata->sda_pin, state); |
} |
/* Toggle SCL by changing the direction of the pin. */ |
@@ -68,21 +68,21 @@ static void i2c_gpio_setscl_val(void *da |
{ |
struct i2c_gpio_platform_data *pdata = data; |
- gpio_set_value(pdata->scl_pin, state); |
+ gpio_set_value_cansleep(pdata->scl_pin, state); |
} |
static int i2c_gpio_getsda(void *data) |
{ |
struct i2c_gpio_platform_data *pdata = data; |
- return gpio_get_value(pdata->sda_pin); |
+ return gpio_get_value_cansleep(pdata->sda_pin); |
} |
static int i2c_gpio_getscl(void *data) |
{ |
struct i2c_gpio_platform_data *pdata = data; |
- return gpio_get_value(pdata->scl_pin); |
+ return gpio_get_value_cansleep(pdata->scl_pin); |
} |
static int of_i2c_gpio_get_pins(struct device_node *np, |
@@ -175,6 +175,9 @@ static int i2c_gpio_probe(struct platfor |
memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata)); |
} |
+ if (gpiod_cansleep(gpio_to_desc(pdata->sda_pin)) || gpiod_cansleep(gpio_to_desc(pdata->scl_pin))) |
+ dev_warn(&pdev->dev, "Slow GPIO pins might wreak havoc into I2C/SMBus bus timing"); |
+ |
if (pdata->sda_is_open_drain) { |
gpio_direction_output(pdata->sda_pin, 1); |
bit_data->setsda = i2c_gpio_setsda_val; |
/branches/18.06.1/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, |
}; |
/branches/18.06.1/target/linux/generic/backport-4.9/090-net-generalize-napi_complete_done.patch |
---|
@@ -0,0 +1,1412 @@ |
From 6ad20165d376fa07919a70e4f43dfae564601829 Mon Sep 17 00:00:00 2001 |
From: Eric Dumazet <edumazet@google.com> |
Date: Mon, 30 Jan 2017 08:22:01 -0800 |
Subject: drivers: net: generalize napi_complete_done() |
napi_complete_done() allows to opt-in for gro_flush_timeout, |
added back in linux-3.19, commit 3b47d30396ba |
("net: gro: add a per device gro flush timer") |
This allows for more efficient GRO aggregation without |
sacrifying latencies. |
Signed-off-by: Eric Dumazet <edumazet@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
drivers/net/can/at91_can.c | 2 +- |
drivers/net/can/c_can/c_can.c | 2 +- |
drivers/net/can/flexcan.c | 2 +- |
drivers/net/can/ifi_canfd/ifi_canfd.c | 2 +- |
drivers/net/can/janz-ican3.c | 2 +- |
drivers/net/can/m_can/m_can.c | 2 +- |
drivers/net/can/rcar/rcar_can.c | 2 +- |
drivers/net/can/rcar/rcar_canfd.c | 2 +- |
drivers/net/can/xilinx_can.c | 2 +- |
drivers/net/ethernet/3com/typhoon.c | 2 +- |
drivers/net/ethernet/adi/bfin_mac.c | 2 +- |
drivers/net/ethernet/agere/et131x.c | 2 +- |
drivers/net/ethernet/altera/altera_tse_main.c | 2 +- |
drivers/net/ethernet/apm/xgene/xgene_enet_main.c | 2 +- |
drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 2 +- |
drivers/net/ethernet/arc/emac_main.c | 2 +- |
drivers/net/ethernet/atheros/alx/main.c | 2 +- |
drivers/net/ethernet/atheros/atl1c/atl1c_main.c | 2 +- |
drivers/net/ethernet/atheros/atl1e/atl1e_main.c | 2 +- |
drivers/net/ethernet/atheros/atlx/atl1.c | 2 +- |
drivers/net/ethernet/broadcom/b44.c | 2 +- |
drivers/net/ethernet/broadcom/bcm63xx_enet.c | 2 +- |
drivers/net/ethernet/broadcom/bgmac.c | 2 +- |
drivers/net/ethernet/broadcom/bnx2.c | 4 ++-- |
drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 2 +- |
drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- |
drivers/net/ethernet/broadcom/sb1250-mac.c | 2 +- |
drivers/net/ethernet/brocade/bna/bnad.c | 2 +- |
drivers/net/ethernet/cadence/macb.c | 2 +- |
drivers/net/ethernet/calxeda/xgmac.c | 2 +- |
drivers/net/ethernet/cavium/liquidio/lio_main.c | 2 +- |
drivers/net/ethernet/cavium/liquidio/lio_vf_main.c | 2 +- |
drivers/net/ethernet/cavium/octeon/octeon_mgmt.c | 2 +- |
drivers/net/ethernet/cavium/thunder/nicvf_main.c | 2 +- |
drivers/net/ethernet/chelsio/cxgb/sge.c | 2 +- |
drivers/net/ethernet/chelsio/cxgb3/sge.c | 4 ++-- |
drivers/net/ethernet/chelsio/cxgb4vf/sge.c | 2 +- |
drivers/net/ethernet/cisco/enic/enic_main.c | 4 ++-- |
drivers/net/ethernet/dec/tulip/interrupt.c | 6 +++--- |
drivers/net/ethernet/dnet.c | 2 +- |
drivers/net/ethernet/emulex/benet/be_main.c | 2 +- |
drivers/net/ethernet/ethoc.c | 2 +- |
drivers/net/ethernet/ezchip/nps_enet.c | 2 +- |
drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 2 +- |
drivers/net/ethernet/freescale/fec_main.c | 2 +- |
.../net/ethernet/freescale/fs_enet/fs_enet-main.c | 2 +- |
drivers/net/ethernet/freescale/gianfar.c | 4 ++-- |
drivers/net/ethernet/freescale/ucc_geth.c | 2 +- |
drivers/net/ethernet/hisilicon/hip04_eth.c | 2 +- |
drivers/net/ethernet/hisilicon/hisi_femac.c | 2 +- |
drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 2 +- |
drivers/net/ethernet/ibm/ibmveth.c | 2 +- |
drivers/net/ethernet/ibm/ibmvnic.c | 2 +- |
drivers/net/ethernet/intel/e100.c | 2 +- |
drivers/net/ethernet/intel/ixgb/ixgb_main.c | 2 +- |
drivers/net/ethernet/korina.c | 2 +- |
drivers/net/ethernet/lantiq_etop.c | 21 +++++++++------------ |
drivers/net/ethernet/marvell/mv643xx_eth.c | 2 +- |
drivers/net/ethernet/marvell/mvneta.c | 6 ++---- |
drivers/net/ethernet/marvell/mvpp2.c | 2 +- |
drivers/net/ethernet/marvell/pxa168_eth.c | 2 +- |
drivers/net/ethernet/moxa/moxart_ether.c | 2 +- |
drivers/net/ethernet/myricom/myri10ge/myri10ge.c | 2 +- |
drivers/net/ethernet/natsemi/natsemi.c | 2 +- |
drivers/net/ethernet/neterion/s2io.c | 4 ++-- |
drivers/net/ethernet/neterion/vxge/vxge-main.c | 6 +++--- |
drivers/net/ethernet/nvidia/forcedeth.c | 2 +- |
drivers/net/ethernet/nxp/lpc_eth.c | 2 +- |
.../net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c | 2 +- |
drivers/net/ethernet/pasemi/pasemi_mac.c | 2 +- |
.../net/ethernet/qlogic/netxen/netxen_nic_main.c | 2 +- |
drivers/net/ethernet/qlogic/qede/qede_fp.c | 2 +- |
drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c | 10 +++++----- |
drivers/net/ethernet/qlogic/qlge/qlge_main.c | 2 +- |
drivers/net/ethernet/qualcomm/emac/emac.c | 2 +- |
drivers/net/ethernet/realtek/r8169.c | 2 +- |
drivers/net/ethernet/rocker/rocker_main.c | 2 +- |
drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c | 2 +- |
drivers/net/ethernet/sfc/efx.c | 2 +- |
drivers/net/ethernet/sfc/falcon/efx.c | 2 +- |
drivers/net/ethernet/smsc/smsc9420.c | 2 +- |
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- |
drivers/net/ethernet/sun/niu.c | 2 +- |
drivers/net/ethernet/sun/sungem.c | 2 +- |
drivers/net/ethernet/sun/sunvnet_common.c | 2 +- |
drivers/net/ethernet/tehuti/tehuti.c | 2 +- |
drivers/net/ethernet/ti/cpsw.c | 2 +- |
drivers/net/ethernet/ti/davinci_emac.c | 2 +- |
drivers/net/ethernet/ti/netcp_core.c | 2 +- |
drivers/net/ethernet/tile/tilegx.c | 2 +- |
drivers/net/ethernet/tile/tilepro.c | 2 +- |
drivers/net/ethernet/toshiba/ps3_gelic_net.c | 2 +- |
drivers/net/ethernet/toshiba/spider_net.c | 2 +- |
drivers/net/ethernet/toshiba/tc35815.c | 2 +- |
drivers/net/ethernet/tundra/tsi108_eth.c | 2 +- |
drivers/net/ethernet/via/via-rhine.c | 2 +- |
drivers/net/ethernet/via/via-velocity.c | 2 +- |
drivers/net/ethernet/wiznet/w5100.c | 2 +- |
drivers/net/ethernet/wiznet/w5300.c | 2 +- |
drivers/net/fjes/fjes_main.c | 2 +- |
drivers/net/vmxnet3/vmxnet3_drv.c | 4 ++-- |
drivers/net/wan/fsl_ucc_hdlc.c | 2 +- |
drivers/net/wan/hd64572.c | 2 +- |
drivers/net/wireless/ath/ath10k/pci.c | 2 +- |
drivers/net/wireless/ath/wil6210/netdev.c | 2 +- |
drivers/net/xen-netback/interface.c | 2 +- |
drivers/net/xen-netfront.c | 2 +- |
drivers/staging/octeon/ethernet-rx.c | 2 +- |
drivers/staging/unisys/visornic/visornic_main.c | 2 +- |
109 files changed, 132 insertions(+), 137 deletions(-) |
--- a/drivers/net/can/at91_can.c |
+++ b/drivers/net/can/at91_can.c |
@@ -813,7 +813,7 @@ static int at91_poll(struct napi_struct |
u32 reg_ier = AT91_IRQ_ERR_FRAME; |
reg_ier |= get_irq_mb_rx(priv) & ~AT91_MB_MASK(priv->rx_next); |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
at91_write(priv, AT91_IER, reg_ier); |
} |
--- a/drivers/net/can/c_can/c_can.c |
+++ b/drivers/net/can/c_can/c_can.c |
@@ -1070,7 +1070,7 @@ static int c_can_poll(struct napi_struct |
end: |
if (work_done < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* enable all IRQs if we are not in bus off state */ |
if (priv->can.state != CAN_STATE_BUS_OFF) |
c_can_irq_control(priv, true); |
--- a/drivers/net/can/flexcan.c |
+++ b/drivers/net/can/flexcan.c |
@@ -703,7 +703,7 @@ static int flexcan_poll(struct napi_stru |
work_done += flexcan_poll_bus_err(dev, reg_esr); |
if (work_done < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* enable IRQs */ |
flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); |
flexcan_write(priv->reg_ctrl_default, ®s->ctrl); |
--- a/drivers/net/can/ifi_canfd/ifi_canfd.c |
+++ b/drivers/net/can/ifi_canfd/ifi_canfd.c |
@@ -589,7 +589,7 @@ static int ifi_canfd_poll(struct napi_st |
work_done += ifi_canfd_do_rx_poll(ndev, quota - work_done); |
if (work_done < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
ifi_canfd_irq_enable(ndev, 1); |
} |
--- a/drivers/net/can/janz-ican3.c |
+++ b/drivers/net/can/janz-ican3.c |
@@ -1475,7 +1475,7 @@ static int ican3_napi(struct napi_struct |
/* We have processed all packets that the adapter had, but it |
* was less than our budget, stop polling */ |
if (received < budget) |
- napi_complete(napi); |
+ napi_complete_done(napi, received); |
spin_lock_irqsave(&mod->lock, flags); |
--- a/drivers/net/can/m_can/m_can.c |
+++ b/drivers/net/can/m_can/m_can.c |
@@ -730,7 +730,7 @@ static int m_can_poll(struct napi_struct |
work_done += m_can_do_rx_poll(dev, (quota - work_done)); |
if (work_done < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
m_can_enable_all_interrupts(priv); |
} |
--- a/drivers/net/can/rcar/rcar_can.c |
+++ b/drivers/net/can/rcar/rcar_can.c |
@@ -698,7 +698,7 @@ static int rcar_can_rx_poll(struct napi_ |
} |
/* All packets processed */ |
if (num_pkts < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, num_pkts); |
priv->ier |= RCAR_CAN_IER_RXFIE; |
writeb(priv->ier, &priv->regs->ier); |
} |
--- a/drivers/net/can/rcar/rcar_canfd.c |
+++ b/drivers/net/can/rcar/rcar_canfd.c |
@@ -1512,7 +1512,7 @@ static int rcar_canfd_rx_poll(struct nap |
/* All packets processed */ |
if (num_pkts < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, num_pkts); |
/* Enable Rx FIFO interrupts */ |
rcar_canfd_set_bit(priv->base, RCANFD_RFCC(ridx), |
RCANFD_RFCC_RFIE); |
--- a/drivers/net/can/xilinx_can.c |
+++ b/drivers/net/can/xilinx_can.c |
@@ -838,7 +838,7 @@ static int xcan_rx_poll(struct napi_stru |
} |
if (work_done < quota) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
ier = priv->read_reg(priv, XCAN_IER_OFFSET); |
ier |= XCAN_IXR_RXNEMP_MASK; |
priv->write_reg(priv, XCAN_IER_OFFSET, ier); |
--- a/drivers/net/ethernet/3com/typhoon.c |
+++ b/drivers/net/ethernet/3com/typhoon.c |
@@ -1748,7 +1748,7 @@ typhoon_poll(struct napi_struct *napi, i |
} |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
iowrite32(TYPHOON_INTR_NONE, |
tp->ioaddr + TYPHOON_REG_INTR_MASK); |
typhoon_post_pci_writes(tp->ioaddr); |
--- a/drivers/net/ethernet/adi/bfin_mac.c |
+++ b/drivers/net/ethernet/adi/bfin_mac.c |
@@ -1274,7 +1274,7 @@ static int bfin_mac_poll(struct napi_str |
} |
if (i < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, i); |
if (test_and_clear_bit(BFIN_MAC_RX_IRQ_DISABLED, &lp->flags)) |
enable_irq(IRQ_MAC_RX); |
} |
--- a/drivers/net/ethernet/agere/et131x.c |
+++ b/drivers/net/ethernet/agere/et131x.c |
@@ -3573,7 +3573,7 @@ static int et131x_poll(struct napi_struc |
et131x_handle_send_pkts(adapter); |
if (work_done < budget) { |
- napi_complete(&adapter->napi); |
+ napi_complete_done(&adapter->napi, work_done); |
et131x_enable_interrupts(adapter); |
} |
--- a/drivers/net/ethernet/altera/altera_tse_main.c |
+++ b/drivers/net/ethernet/altera/altera_tse_main.c |
@@ -491,7 +491,7 @@ static int tse_poll(struct napi_struct * |
if (rxcomplete < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rxcomplete); |
netdev_dbg(priv->dev, |
"NAPI Complete, did %d packets with budget %d\n", |
--- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c |
+++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c |
@@ -655,7 +655,7 @@ static int xgene_enet_napi(struct napi_s |
processed = xgene_enet_process_ring(ring, budget); |
if (processed != budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, processed); |
enable_irq(ring->irq); |
} |
--- a/drivers/net/ethernet/arc/emac_main.c |
+++ b/drivers/net/ethernet/arc/emac_main.c |
@@ -284,7 +284,7 @@ static int arc_emac_poll(struct napi_str |
work_done = arc_emac_rx(ndev, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
arc_reg_or(priv, R_ENABLE, RXINT_MASK | TXINT_MASK); |
} |
--- a/drivers/net/ethernet/atheros/alx/main.c |
+++ b/drivers/net/ethernet/atheros/alx/main.c |
@@ -292,7 +292,7 @@ static int alx_poll(struct napi_struct * |
if (!tx_complete || work == budget) |
return budget; |
- napi_complete(&alx->napi); |
+ napi_complete_done(&alx->napi, work); |
/* enable interrupt */ |
if (alx->flags & ALX_FLAG_USING_MSIX) { |
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c |
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c |
@@ -1886,7 +1886,7 @@ static int atl1c_clean(struct napi_struc |
if (work_done < budget) { |
quit_polling: |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
adapter->hw.intr_mask |= ISR_RX_PKT; |
AT_WRITE_REG(&adapter->hw, REG_IMR, adapter->hw.intr_mask); |
} |
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c |
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c |
@@ -1532,7 +1532,7 @@ static int atl1e_clean(struct napi_struc |
/* If no Tx and not enough Rx work done, exit the polling mode */ |
if (work_done < budget) { |
quit_polling: |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
imr_data = AT_READ_REG(&adapter->hw, REG_IMR); |
AT_WRITE_REG(&adapter->hw, REG_IMR, imr_data | ISR_RX_EVENT); |
/* test debug */ |
--- a/drivers/net/ethernet/atheros/atlx/atl1.c |
+++ b/drivers/net/ethernet/atheros/atlx/atl1.c |
@@ -2457,7 +2457,7 @@ static int atl1_rings_clean(struct napi_ |
if (work_done >= budget) |
return work_done; |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* re-enable Interrupt */ |
if (likely(adapter->int_enabled)) |
atlx_imr_set(adapter, IMR_NORMAL_MASK); |
--- a/drivers/net/ethernet/broadcom/b44.c |
+++ b/drivers/net/ethernet/broadcom/b44.c |
@@ -902,7 +902,7 @@ static int b44_poll(struct napi_struct * |
} |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
b44_enable_ints(bp); |
} |
--- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c |
+++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c |
@@ -511,7 +511,7 @@ static int bcm_enet_poll(struct napi_str |
/* no more packet in rx/tx queue, remove device from poll |
* queue */ |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_work_done); |
/* restore rx/tx interrupt */ |
enet_dmac_writel(priv, priv->dma_chan_int_mask, |
--- a/drivers/net/ethernet/broadcom/bgmac.c |
+++ b/drivers/net/ethernet/broadcom/bgmac.c |
@@ -1146,7 +1146,7 @@ static int bgmac_poll(struct napi_struct |
return weight; |
if (handled < weight) { |
- napi_complete(napi); |
+ napi_complete_done(napi, handled); |
bgmac_chip_intrs_on(bgmac); |
} |
--- a/drivers/net/ethernet/broadcom/bnx2.c |
+++ b/drivers/net/ethernet/broadcom/bnx2.c |
@@ -3522,7 +3522,7 @@ static int bnx2_poll_msix(struct napi_st |
rmb(); |
if (likely(!bnx2_has_fast_work(bnapi))) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, bnapi->int_num | |
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | |
bnapi->last_status_idx); |
@@ -3559,7 +3559,7 @@ static int bnx2_poll(struct napi_struct |
rmb(); |
if (likely(!bnx2_has_work(bnapi))) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
if (likely(bp->flags & BNX2_FLAG_USING_MSI_OR_MSIX)) { |
BNX2_WR(bp, BNX2_PCICFG_INT_ACK_CMD, |
BNX2_PCICFG_INT_ACK_CMD_INDEX_VALID | |
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c |
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c |
@@ -3236,7 +3236,7 @@ static int bnx2x_poll(struct napi_struct |
* has been updated when NAPI was scheduled. |
*/ |
if (IS_FCOE_FP(fp)) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_work_done); |
} else { |
bnx2x_update_fpsb_idx(fp); |
/* bnx2x_has_rx_work() reads the status block, |
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c |
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c |
@@ -1781,7 +1781,7 @@ static int bnxt_poll_nitroa0(struct napi |
} |
if (!bnxt_has_work(bp, cpr) && rx_pkts < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_pkts); |
BNXT_CP_DB_REARM(cpr->cp_doorbell, cpr->cp_raw_cons); |
} |
return rx_pkts; |
--- a/drivers/net/ethernet/broadcom/sb1250-mac.c |
+++ b/drivers/net/ethernet/broadcom/sb1250-mac.c |
@@ -2545,7 +2545,7 @@ static int sbmac_poll(struct napi_struct |
sbdma_tx_process(sc, &(sc->sbm_txdma), 1); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
#ifdef CONFIG_SBMAC_COALESCE |
__raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | |
--- a/drivers/net/ethernet/brocade/bna/bnad.c |
+++ b/drivers/net/ethernet/brocade/bna/bnad.c |
@@ -1881,7 +1881,7 @@ bnad_napi_poll_rx(struct napi_struct *na |
return rcvd; |
poll_exit: |
- napi_complete(napi); |
+ napi_complete_done(napi, rcvd); |
rx_ctrl->rx_complete++; |
--- a/drivers/net/ethernet/cadence/macb.c |
+++ b/drivers/net/ethernet/cadence/macb.c |
@@ -1069,7 +1069,7 @@ static int macb_poll(struct napi_struct |
work_done = bp->macbgem_ops.mog_rx(bp, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* Packets received while interrupts were disabled */ |
status = macb_readl(bp, RSR); |
--- a/drivers/net/ethernet/calxeda/xgmac.c |
+++ b/drivers/net/ethernet/calxeda/xgmac.c |
@@ -1247,7 +1247,7 @@ static int xgmac_poll(struct napi_struct |
work_done = xgmac_rx(priv, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
__raw_writel(DMA_INTR_DEFAULT_MASK, priv->base + XGMAC_DMA_INTR_ENA); |
} |
return work_done; |
--- a/drivers/net/ethernet/cavium/liquidio/lio_main.c |
+++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c |
@@ -2433,7 +2433,7 @@ static int liquidio_napi_poll(struct nap |
} |
if ((work_done < budget) && (tx_done)) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, |
POLL_EVENT_ENABLE_INTR, 0); |
return 0; |
--- a/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c |
+++ b/drivers/net/ethernet/cavium/octeon/octeon_mgmt.c |
@@ -501,7 +501,7 @@ static int octeon_mgmt_napi_poll(struct |
if (work_done < budget) { |
/* We stopped because no more packets were available. */ |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
octeon_mgmt_enable_rx_irq(p); |
} |
octeon_mgmt_update_rx_stats(netdev); |
--- a/drivers/net/ethernet/cavium/thunder/nicvf_main.c |
+++ b/drivers/net/ethernet/cavium/thunder/nicvf_main.c |
@@ -737,7 +737,7 @@ static int nicvf_poll(struct napi_struct |
if (work_done < budget) { |
/* Slow packet rate, exit polling */ |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* Re-enable interrupts */ |
cq_head = nicvf_queue_reg_read(nic, NIC_QSET_CQ_0_7_HEAD, |
cq->cq_idx); |
--- a/drivers/net/ethernet/chelsio/cxgb/sge.c |
+++ b/drivers/net/ethernet/chelsio/cxgb/sge.c |
@@ -1605,7 +1605,7 @@ int t1_poll(struct napi_struct *napi, in |
int work_done = process_responses(adapter, budget); |
if (likely(work_done < budget)) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
writel(adapter->sge->respQ.cidx, |
adapter->regs + A_SG_SLEEPING); |
} |
--- a/drivers/net/ethernet/chelsio/cxgb3/sge.c |
+++ b/drivers/net/ethernet/chelsio/cxgb3/sge.c |
@@ -1843,7 +1843,7 @@ static int ofld_poll(struct napi_struct |
__skb_queue_head_init(&queue); |
skb_queue_splice_init(&q->rx_queue, &queue); |
if (skb_queue_empty(&queue)) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
spin_unlock_irq(&q->lock); |
return work_done; |
} |
@@ -2414,7 +2414,7 @@ static int napi_rx_handler(struct napi_s |
int work_done = process_responses(adap, qs, budget); |
if (likely(work_done < budget)) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* |
* Because we don't atomically flush the following |
--- a/drivers/net/ethernet/chelsio/cxgb4vf/sge.c |
+++ b/drivers/net/ethernet/chelsio/cxgb4vf/sge.c |
@@ -1889,7 +1889,7 @@ static int napi_rx_handler(struct napi_s |
u32 val; |
if (likely(work_done < budget)) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
intr_params = rspq->next_intr_params; |
rspq->next_intr_params = rspq->intr_params; |
} else |
--- a/drivers/net/ethernet/cisco/enic/enic_main.c |
+++ b/drivers/net/ethernet/cisco/enic/enic_main.c |
@@ -1339,7 +1339,7 @@ static int enic_poll(struct napi_struct |
* exit polling |
*/ |
- napi_complete(napi); |
+ napi_complete_done(napi, rq_work_done); |
if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) |
enic_set_int_moderation(enic, &enic->rq[0]); |
vnic_intr_unmask(&enic->intr[intr]); |
@@ -1496,7 +1496,7 @@ static int enic_poll_msix_rq(struct napi |
* exit polling |
*/ |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
if (enic->rx_coalesce_setting.use_adaptive_rx_coalesce) |
enic_set_int_moderation(enic, &enic->rq[rq]); |
vnic_intr_unmask(&enic->intr[intr]); |
--- a/drivers/net/ethernet/dec/tulip/interrupt.c |
+++ b/drivers/net/ethernet/dec/tulip/interrupt.c |
@@ -319,8 +319,8 @@ int tulip_poll(struct napi_struct *napi, |
/* Remove us from polling list and enable RX intr. */ |
- napi_complete(napi); |
- iowrite32(tulip_tbl[tp->chip_id].valid_intrs, tp->base_addr+CSR7); |
+ napi_complete_done(napi, work_done); |
+ iowrite32(tulip_tbl[tp->chip_id].valid_intrs, tp->base_addr+CSR7); |
/* The last op happens after poll completion. Which means the following: |
* 1. it can race with disabling irqs in irq handler |
@@ -355,7 +355,7 @@ int tulip_poll(struct napi_struct *napi, |
* before we did napi_complete(). See? We would lose it. */ |
/* remove ourselves from the polling list */ |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
return work_done; |
} |
--- a/drivers/net/ethernet/dnet.c |
+++ b/drivers/net/ethernet/dnet.c |
@@ -415,7 +415,7 @@ static int dnet_poll(struct napi_struct |
/* We processed all packets available. Tell NAPI it can |
* stop polling then re-enable rx interrupts. |
*/ |
- napi_complete(napi); |
+ napi_complete_done(napi, npackets); |
int_enable = dnet_readl(bp, INTR_ENB); |
int_enable |= DNET_INTR_SRC_RX_CMDFIFOAF; |
dnet_writel(bp, int_enable, INTR_ENB); |
--- a/drivers/net/ethernet/emulex/benet/be_main.c |
+++ b/drivers/net/ethernet/emulex/benet/be_main.c |
@@ -3344,7 +3344,7 @@ int be_poll(struct napi_struct *napi, in |
be_process_mcc(adapter); |
if (max_work < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, max_work); |
/* Skyhawk EQ_DB has a provision to set the rearm to interrupt |
* delay via a delay multiplier encoding value |
--- a/drivers/net/ethernet/ethoc.c |
+++ b/drivers/net/ethernet/ethoc.c |
@@ -614,7 +614,7 @@ static int ethoc_poll(struct napi_struct |
tx_work_done = ethoc_tx(priv->netdev, budget); |
if (rx_work_done < budget && tx_work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_work_done); |
ethoc_enable_irq(priv, INT_MASK_TX | INT_MASK_RX); |
} |
--- a/drivers/net/ethernet/ezchip/nps_enet.c |
+++ b/drivers/net/ethernet/ezchip/nps_enet.c |
@@ -192,7 +192,7 @@ static int nps_enet_poll(struct napi_str |
if (work_done < budget) { |
u32 buf_int_enable_value = 0; |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* set tx_done and rx_rdy bits */ |
buf_int_enable_value |= NPS_ENET_ENABLE << RX_RDY_SHIFT; |
--- a/drivers/net/ethernet/freescale/fec_main.c |
+++ b/drivers/net/ethernet/freescale/fec_main.c |
@@ -1623,7 +1623,7 @@ static int fec_enet_rx_napi(struct napi_ |
fec_enet_tx(ndev); |
if (pkts < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, pkts); |
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK); |
} |
return pkts; |
--- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c |
+++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c |
@@ -301,7 +301,7 @@ static int fs_enet_napi(struct napi_stru |
if (received < budget && tx_left) { |
/* done */ |
- napi_complete(napi); |
+ napi_complete_done(napi, received); |
(*fep->ops->napi_enable)(dev); |
return received; |
--- a/drivers/net/ethernet/freescale/gianfar.c |
+++ b/drivers/net/ethernet/freescale/gianfar.c |
@@ -3197,7 +3197,7 @@ static int gfar_poll_rx_sq(struct napi_s |
if (work_done < budget) { |
u32 imask; |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* Clear the halt bit in RSTAT */ |
gfar_write(®s->rstat, gfargrp->rstat); |
@@ -3286,7 +3286,7 @@ static int gfar_poll_rx(struct napi_stru |
if (!num_act_queues) { |
u32 imask; |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* Clear the halt bit in RSTAT */ |
gfar_write(®s->rstat, gfargrp->rstat); |
--- a/drivers/net/ethernet/freescale/ucc_geth.c |
+++ b/drivers/net/ethernet/freescale/ucc_geth.c |
@@ -3303,7 +3303,7 @@ static int ucc_geth_poll(struct napi_str |
howmany += ucc_geth_rx(ugeth, i, budget - howmany); |
if (howmany < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, howmany); |
setbits32(ugeth->uccf->p_uccm, UCCE_RX_EVENTS | UCCE_TX_EVENTS); |
} |
--- a/drivers/net/ethernet/hisilicon/hip04_eth.c |
+++ b/drivers/net/ethernet/hisilicon/hip04_eth.c |
@@ -555,7 +555,7 @@ refill: |
priv->reg_inten |= RCV_INT; |
writel_relaxed(priv->reg_inten, priv->base + PPE_INTEN); |
} |
- napi_complete(napi); |
+ napi_complete_done(napi, rx); |
done: |
/* clean up tx descriptors and start a new timer if necessary */ |
tx_remaining = hip04_tx_reclaim(ndev, false); |
--- a/drivers/net/ethernet/hisilicon/hisi_femac.c |
+++ b/drivers/net/ethernet/hisilicon/hisi_femac.c |
@@ -330,7 +330,7 @@ static int hisi_femac_poll(struct napi_s |
} while (ints & DEF_INT_MASK); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
hisi_femac_irq_enable(priv, DEF_INT_MASK & |
(~IRQ_INT_TX_PER_PACKET)); |
} |
--- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c |
+++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c |
@@ -578,7 +578,7 @@ static int hix5hd2_poll(struct napi_stru |
} while (ints & DEF_INT_MASK); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
hix5hd2_irq_enable(priv); |
} |
--- a/drivers/net/ethernet/ibm/ibmveth.c |
+++ b/drivers/net/ethernet/ibm/ibmveth.c |
@@ -1324,7 +1324,7 @@ restart_poll: |
ibmveth_replenish_task(adapter); |
if (frames_processed < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, frames_processed); |
/* We think we are done - reenable interrupts, |
* then check once more to make sure we are done. |
--- a/drivers/net/ethernet/ibm/ibmvnic.c |
+++ b/drivers/net/ethernet/ibm/ibmvnic.c |
@@ -1028,7 +1028,7 @@ restart_poll: |
if (frames_processed < budget) { |
enable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]); |
- napi_complete(napi); |
+ napi_complete_done(napi, frames_processed); |
if (pending_scrq(adapter, adapter->rx_scrq[scrq_num]) && |
napi_reschedule(napi)) { |
disable_scrq_irq(adapter, adapter->rx_scrq[scrq_num]); |
--- a/drivers/net/ethernet/intel/e100.c |
+++ b/drivers/net/ethernet/intel/e100.c |
@@ -2253,7 +2253,7 @@ static int e100_poll(struct napi_struct |
/* If budget not fully consumed, exit the polling mode */ |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
e100_enable_irq(nic); |
} |
--- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c |
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c |
@@ -1825,7 +1825,7 @@ ixgb_clean(struct napi_struct *napi, int |
/* If budget not fully consumed, exit the polling mode */ |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
if (!test_bit(__IXGB_DOWN, &adapter->flags)) |
ixgb_irq_enable(adapter); |
} |
--- a/drivers/net/ethernet/korina.c |
+++ b/drivers/net/ethernet/korina.c |
@@ -464,7 +464,7 @@ static int korina_poll(struct napi_struc |
work_done = korina_rx(dev, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
writel(readl(&lp->rx_dma_regs->dmasm) & |
~(DMA_STAT_DONE | DMA_STAT_HALT | DMA_STAT_ERR), |
--- a/drivers/net/ethernet/lantiq_etop.c |
+++ b/drivers/net/ethernet/lantiq_etop.c |
@@ -156,24 +156,21 @@ ltq_etop_poll_rx(struct napi_struct *nap |
{ |
struct ltq_etop_chan *ch = container_of(napi, |
struct ltq_etop_chan, napi); |
- int rx = 0; |
- int complete = 0; |
+ int work_done = 0; |
- while ((rx < budget) && !complete) { |
+ while (work_done < budget) { |
struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; |
- if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { |
- ltq_etop_hw_receive(ch); |
- rx++; |
- } else { |
- complete = 1; |
- } |
+ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) != LTQ_DMA_C) |
+ break; |
+ ltq_etop_hw_receive(ch); |
+ work_done++; |
} |
- if (complete || !rx) { |
- napi_complete(&ch->napi); |
+ if (work_done < budget) { |
+ napi_complete_done(&ch->napi, work_done); |
ltq_dma_ack_irq(&ch->dma); |
} |
- return rx; |
+ return work_done; |
} |
static int |
--- a/drivers/net/ethernet/marvell/mv643xx_eth.c |
+++ b/drivers/net/ethernet/marvell/mv643xx_eth.c |
@@ -2312,7 +2312,7 @@ static int mv643xx_eth_poll(struct napi_ |
if (work_done < budget) { |
if (mp->oom) |
mod_timer(&mp->rx_oom, jiffies + (HZ / 10)); |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
wrlp(mp, INT_MASK, mp->int_mask); |
} |
--- a/drivers/net/ethernet/marvell/mvneta.c |
+++ b/drivers/net/ethernet/marvell/mvneta.c |
@@ -2702,11 +2702,9 @@ static int mvneta_poll(struct napi_struc |
rx_done = mvneta_rx_swbm(pp, budget, &pp->rxqs[rx_queue]); |
} |
- budget -= rx_done; |
- |
- if (budget > 0) { |
+ if (rx_done < budget) { |
cause_rx_tx = 0; |
- napi_complete(&port->napi); |
+ napi_complete_done(&port->napi, rx_done); |
enable_percpu_irq(pp->dev->irq, 0); |
} |
--- a/drivers/net/ethernet/marvell/mvpp2.c |
+++ b/drivers/net/ethernet/marvell/mvpp2.c |
@@ -5406,7 +5406,7 @@ static int mvpp2_poll(struct napi_struct |
if (budget > 0) { |
cause_rx = 0; |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_done); |
mvpp2_interrupts_enable(port); |
} |
--- a/drivers/net/ethernet/marvell/pxa168_eth.c |
+++ b/drivers/net/ethernet/marvell/pxa168_eth.c |
@@ -1264,7 +1264,7 @@ static int pxa168_rx_poll(struct napi_st |
} |
work_done = rxq_process(dev, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
wrl(pep, INT_MASK, ALL_INTS); |
} |
--- a/drivers/net/ethernet/moxa/moxart_ether.c |
+++ b/drivers/net/ethernet/moxa/moxart_ether.c |
@@ -270,7 +270,7 @@ rx_next: |
} |
if (rx < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx); |
} |
priv->reg_imr |= RPKT_FINISH_M; |
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c |
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c |
@@ -1678,7 +1678,7 @@ static int myri10ge_poll(struct napi_str |
myri10ge_ss_unlock_napi(ss); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
put_be32(htonl(3), ss->irq_claim); |
} |
return work_done; |
--- a/drivers/net/ethernet/natsemi/natsemi.c |
+++ b/drivers/net/ethernet/natsemi/natsemi.c |
@@ -2261,7 +2261,7 @@ static int natsemi_poll(struct napi_stru |
np->intr_status = readl(ioaddr + IntrStatus); |
} while (np->intr_status); |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* Reenable interrupts providing nothing is trying to shut |
* the chip down. */ |
--- a/drivers/net/ethernet/neterion/s2io.c |
+++ b/drivers/net/ethernet/neterion/s2io.c |
@@ -2783,7 +2783,7 @@ static int s2io_poll_msix(struct napi_st |
s2io_chk_rx_buffers(nic, ring); |
if (pkts_processed < budget_org) { |
- napi_complete(napi); |
+ napi_complete_done(napi, pkts_processed); |
/*Re Enable MSI-Rx Vector*/ |
addr = (u8 __iomem *)&bar0->xmsi_mask_reg; |
addr += 7 - ring->ring_no; |
@@ -2817,7 +2817,7 @@ static int s2io_poll_inta(struct napi_st |
break; |
} |
if (pkts_processed < budget_org) { |
- napi_complete(napi); |
+ napi_complete_done(napi, pkts_processed); |
/* Re enable the Rx interrupts for the ring */ |
writeq(0, &bar0->rx_traffic_mask); |
readl(&bar0->rx_traffic_mask); |
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c |
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c |
@@ -1823,8 +1823,8 @@ static int vxge_poll_msix(struct napi_st |
vxge_hw_vpath_poll_rx(ring->handle); |
pkts_processed = ring->pkts_processed; |
- if (ring->pkts_processed < budget_org) { |
- napi_complete(napi); |
+ if (pkts_processed < budget_org) { |
+ napi_complete_done(napi, pkts_processed); |
/* Re enable the Rx interrupts for the vpath */ |
vxge_hw_channel_msix_unmask( |
@@ -1863,7 +1863,7 @@ static int vxge_poll_inta(struct napi_st |
VXGE_COMPLETE_ALL_TX(vdev); |
if (pkts_processed < budget_org) { |
- napi_complete(napi); |
+ napi_complete_done(napi, pkts_processed); |
/* Re enable the Rx interrupts for the ring */ |
vxge_hw_device_unmask_all(hldev); |
vxge_hw_device_flush_io(hldev); |
--- a/drivers/net/ethernet/nvidia/forcedeth.c |
+++ b/drivers/net/ethernet/nvidia/forcedeth.c |
@@ -3756,7 +3756,7 @@ static int nv_napi_poll(struct napi_stru |
if (rx_work < budget) { |
/* re-enable interrupts |
(msix not enabled in napi) */ |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_work); |
writel(np->irqmask, base + NvRegIrqMask); |
} |
--- a/drivers/net/ethernet/nxp/lpc_eth.c |
+++ b/drivers/net/ethernet/nxp/lpc_eth.c |
@@ -999,7 +999,7 @@ static int lpc_eth_poll(struct napi_stru |
rx_done = __lpc_handle_recv(ndev, budget); |
if (rx_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_done); |
lpc_eth_enable_int(pldat->net_base); |
} |
--- a/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c |
+++ b/drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c |
@@ -2391,7 +2391,7 @@ static int pch_gbe_napi_poll(struct napi |
poll_end_flag = true; |
if (poll_end_flag) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
pch_gbe_irq_enable(adapter); |
} |
--- a/drivers/net/ethernet/pasemi/pasemi_mac.c |
+++ b/drivers/net/ethernet/pasemi/pasemi_mac.c |
@@ -1575,7 +1575,7 @@ static int pasemi_mac_poll(struct napi_s |
pkts = pasemi_mac_clean_rx(rx_ring(mac), budget); |
if (pkts < budget) { |
/* all done, no more packets present */ |
- napi_complete(napi); |
+ napi_complete_done(napi, pkts); |
pasemi_mac_restart_rx_intr(mac); |
pasemi_mac_restart_tx_intr(mac); |
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c |
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c |
@@ -2391,7 +2391,7 @@ static int netxen_nic_poll(struct napi_s |
work_done = budget; |
if (work_done < budget) { |
- napi_complete(&sds_ring->napi); |
+ napi_complete_done(&sds_ring->napi, work_done); |
if (test_bit(__NX_DEV_UP, &adapter->state)) |
netxen_nic_enable_int(sds_ring); |
} |
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c |
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c |
@@ -975,7 +975,7 @@ static int qlcnic_poll(struct napi_struc |
work_done = budget; |
if (work_done < budget) { |
- napi_complete(&sds_ring->napi); |
+ napi_complete_done(&sds_ring->napi, work_done); |
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) { |
qlcnic_enable_sds_intr(adapter, sds_ring); |
qlcnic_enable_tx_intr(adapter, tx_ring); |
@@ -1019,7 +1019,7 @@ static int qlcnic_rx_poll(struct napi_st |
work_done = qlcnic_process_rcv_ring(sds_ring, budget); |
if (work_done < budget) { |
- napi_complete(&sds_ring->napi); |
+ napi_complete_done(&sds_ring->napi, work_done); |
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) |
qlcnic_enable_sds_intr(adapter, sds_ring); |
} |
@@ -1966,7 +1966,7 @@ static int qlcnic_83xx_msix_sriov_vf_pol |
work_done = budget; |
if (work_done < budget) { |
- napi_complete(&sds_ring->napi); |
+ napi_complete_done(&sds_ring->napi, work_done); |
qlcnic_enable_sds_intr(adapter, sds_ring); |
} |
@@ -1994,7 +1994,7 @@ static int qlcnic_83xx_poll(struct napi_ |
work_done = budget; |
if (work_done < budget) { |
- napi_complete(&sds_ring->napi); |
+ napi_complete_done(&sds_ring->napi, work_done); |
qlcnic_enable_sds_intr(adapter, sds_ring); |
} |
@@ -2032,7 +2032,7 @@ static int qlcnic_83xx_rx_poll(struct na |
adapter = sds_ring->adapter; |
work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); |
if (work_done < budget) { |
- napi_complete(&sds_ring->napi); |
+ napi_complete_done(&sds_ring->napi, work_done); |
if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) |
qlcnic_enable_sds_intr(adapter, sds_ring); |
} |
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c |
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c |
@@ -2334,7 +2334,7 @@ static int ql_napi_poll_msix(struct napi |
} |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
ql_enable_completion_interrupt(qdev, rx_ring->irq); |
} |
return work_done; |
--- a/drivers/net/ethernet/qualcomm/emac/emac.c |
+++ b/drivers/net/ethernet/qualcomm/emac/emac.c |
@@ -129,7 +129,7 @@ static int emac_napi_rtx(struct napi_str |
emac_mac_rx_process(adpt, rx_q, &work_done, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
irq->mask |= rx_q->intr; |
writel(irq->mask, adpt->base + EMAC_INT_MASK); |
--- a/drivers/net/ethernet/realtek/r8169.c |
+++ b/drivers/net/ethernet/realtek/r8169.c |
@@ -7578,7 +7578,7 @@ static int rtl8169_poll(struct napi_stru |
} |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
rtl_irq_enable(tp, enable_mask); |
mmiowb(); |
--- a/drivers/net/ethernet/rocker/rocker_main.c |
+++ b/drivers/net/ethernet/rocker/rocker_main.c |
@@ -2480,7 +2480,7 @@ static int rocker_port_poll_rx(struct na |
} |
if (credits < budget) |
- napi_complete(napi); |
+ napi_complete_done(napi, credits); |
rocker_dma_ring_credits_set(rocker, &rocker_port->rx_ring, credits); |
--- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c |
+++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_main.c |
@@ -1578,7 +1578,7 @@ static int sxgbe_poll(struct napi_struct |
work_done = sxgbe_rx(priv, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
priv->hw->dma->enable_dma_irq(priv->ioaddr, qnum); |
} |
--- a/drivers/net/ethernet/sfc/efx.c |
+++ b/drivers/net/ethernet/sfc/efx.c |
@@ -332,7 +332,7 @@ static int efx_poll(struct napi_struct * |
* since efx_nic_eventq_read_ack() will have no effect if |
* interrupts have already been disabled. |
*/ |
- napi_complete(napi); |
+ napi_complete_done(napi, spent); |
efx_nic_eventq_read_ack(channel); |
} |
--- a/drivers/net/ethernet/smsc/smsc9420.c |
+++ b/drivers/net/ethernet/smsc/smsc9420.c |
@@ -869,7 +869,7 @@ static int smsc9420_rx_poll(struct napi_ |
smsc9420_pci_flush_write(pd); |
if (work_done < budget) { |
- napi_complete(&pd->napi); |
+ napi_complete_done(&pd->napi, work_done); |
/* re-enable RX DMA interrupts */ |
dma_intr_ena = smsc9420_reg_read(pd, DMAC_INTR_ENA); |
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c |
@@ -2706,7 +2706,7 @@ static int stmmac_poll(struct napi_struc |
work_done = stmmac_rx(priv, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
stmmac_enable_dma_irq(priv); |
} |
return work_done; |
--- a/drivers/net/ethernet/sun/niu.c |
+++ b/drivers/net/ethernet/sun/niu.c |
@@ -3785,7 +3785,7 @@ static int niu_poll(struct napi_struct * |
work_done = niu_poll_core(np, lp, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
niu_ldg_rearm(np, lp, 1); |
} |
return work_done; |
--- a/drivers/net/ethernet/sun/sungem.c |
+++ b/drivers/net/ethernet/sun/sungem.c |
@@ -924,7 +924,7 @@ static int gem_poll(struct napi_struct * |
gp->status = readl(gp->regs + GREG_STAT); |
} while (gp->status & GREG_STAT_NAPI); |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
gem_enable_ints(gp); |
return work_done; |
--- a/drivers/net/ethernet/sun/sunvnet_common.c |
+++ b/drivers/net/ethernet/sun/sunvnet_common.c |
@@ -850,7 +850,7 @@ int sunvnet_poll_common(struct napi_stru |
int processed = vnet_event_napi(port, budget); |
if (processed < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, processed); |
port->rx_event &= ~LDC_EVENT_DATA_READY; |
vio_set_intr(vio->vdev->rx_ino, HV_INTR_ENABLED); |
} |
--- a/drivers/net/ethernet/tehuti/tehuti.c |
+++ b/drivers/net/ethernet/tehuti/tehuti.c |
@@ -303,7 +303,7 @@ static int bdx_poll(struct napi_struct * |
* device lock and allow waiting tasks (eg rmmod) to advance) */ |
priv->napi_stop = 0; |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
bdx_enable_interrupts(priv); |
} |
return work_done; |
--- a/drivers/net/ethernet/ti/cpsw.c |
+++ b/drivers/net/ethernet/ti/cpsw.c |
@@ -843,7 +843,7 @@ static int cpsw_rx_poll(struct napi_stru |
} |
if (num_rx < budget) { |
- napi_complete(napi_rx); |
+ napi_complete_done(napi_rx, num_rx); |
writel(0xff, &cpsw->wr_regs->rx_en); |
if (cpsw->quirk_irq && cpsw->rx_irq_disabled) { |
cpsw->rx_irq_disabled = false; |
--- a/drivers/net/ethernet/ti/davinci_emac.c |
+++ b/drivers/net/ethernet/ti/davinci_emac.c |
@@ -1295,7 +1295,7 @@ static int emac_poll(struct napi_struct |
&emac_rxhost_errcodes[cause][0], ch); |
} |
} else if (num_rx_pkts < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, num_rx_pkts); |
emac_int_enable(priv); |
} |
--- a/drivers/net/ethernet/ti/netcp_core.c |
+++ b/drivers/net/ethernet/ti/netcp_core.c |
@@ -939,7 +939,7 @@ static int netcp_rx_poll(struct napi_str |
netcp_rxpool_refill(netcp); |
if (packets < budget) { |
- napi_complete(&netcp->rx_napi); |
+ napi_complete_done(&netcp->rx_napi, packets); |
knav_queue_enable_notify(netcp->rx_queue); |
} |
--- a/drivers/net/ethernet/tile/tilegx.c |
+++ b/drivers/net/ethernet/tile/tilegx.c |
@@ -678,7 +678,7 @@ static int tile_net_poll(struct napi_str |
} |
/* There are no packets left. */ |
- napi_complete(&info_mpipe->napi); |
+ napi_complete_done(&info_mpipe->napi, work); |
md = &mpipe_data[instance]; |
/* Re-enable hypervisor interrupts. */ |
--- a/drivers/net/ethernet/tile/tilepro.c |
+++ b/drivers/net/ethernet/tile/tilepro.c |
@@ -842,7 +842,7 @@ static int tile_net_poll(struct napi_str |
} |
} |
- napi_complete(&info->napi); |
+ napi_complete_done(&info->napi, work); |
if (!priv->active) |
goto done; |
--- a/drivers/net/ethernet/toshiba/ps3_gelic_net.c |
+++ b/drivers/net/ethernet/toshiba/ps3_gelic_net.c |
@@ -1109,7 +1109,7 @@ static int gelic_net_poll(struct napi_st |
} |
if (packets_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, packets_done); |
gelic_card_rx_irq_on(card); |
} |
return packets_done; |
--- a/drivers/net/ethernet/toshiba/spider_net.c |
+++ b/drivers/net/ethernet/toshiba/spider_net.c |
@@ -1270,7 +1270,7 @@ static int spider_net_poll(struct napi_s |
/* if all packets are in the stack, enable interrupts and return 0 */ |
/* if not, return 1 */ |
if (packets_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, packets_done); |
spider_net_rx_irq_on(card); |
card->ignore_rx_ramfull = 0; |
} |
--- a/drivers/net/ethernet/toshiba/tc35815.c |
+++ b/drivers/net/ethernet/toshiba/tc35815.c |
@@ -1639,7 +1639,7 @@ static int tc35815_poll(struct napi_stru |
spin_unlock(&lp->rx_lock); |
if (received < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, received); |
/* enable interrupts */ |
tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); |
} |
--- a/drivers/net/ethernet/tundra/tsi108_eth.c |
+++ b/drivers/net/ethernet/tundra/tsi108_eth.c |
@@ -887,7 +887,7 @@ static int tsi108_poll(struct napi_struc |
if (num_received < budget) { |
data->rxpending = 0; |
- napi_complete(napi); |
+ napi_complete_done(napi, num_received); |
TSI_WRITE(TSI108_EC_INTMASK, |
TSI_READ(TSI108_EC_INTMASK) |
--- a/drivers/net/ethernet/via/via-rhine.c |
+++ b/drivers/net/ethernet/via/via-rhine.c |
@@ -861,7 +861,7 @@ static int rhine_napipoll(struct napi_st |
} |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
iowrite16(enable_mask, ioaddr + IntrEnable); |
mmiowb(); |
} |
--- a/drivers/net/ethernet/via/via-velocity.c |
+++ b/drivers/net/ethernet/via/via-velocity.c |
@@ -2160,7 +2160,7 @@ static int velocity_poll(struct napi_str |
velocity_tx_srv(vptr); |
/* If budget not fully consumed, exit the polling mode */ |
if (rx_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_done); |
mac_enable_int(vptr->mac_regs); |
} |
spin_unlock_irqrestore(&vptr->lock, flags); |
--- a/drivers/net/ethernet/wiznet/w5100.c |
+++ b/drivers/net/ethernet/wiznet/w5100.c |
@@ -915,7 +915,7 @@ static int w5100_napi_poll(struct napi_s |
} |
if (rx_count < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_count); |
w5100_enable_intr(priv); |
} |
--- a/drivers/net/ethernet/wiznet/w5300.c |
+++ b/drivers/net/ethernet/wiznet/w5300.c |
@@ -417,7 +417,7 @@ static int w5300_napi_poll(struct napi_s |
} |
if (rx_count < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_count); |
w5300_write(priv, W5300_IMR, IR_S0); |
mmiowb(); |
} |
--- a/drivers/net/fjes/fjes_main.c |
+++ b/drivers/net/fjes/fjes_main.c |
@@ -1122,7 +1122,7 @@ static int fjes_poll(struct napi_struct |
} |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
if (adapter->unset_rx_last) { |
adapter->rx_last_jiffies = jiffies; |
--- a/drivers/net/vmxnet3/vmxnet3_drv.c |
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c |
@@ -1873,7 +1873,7 @@ vmxnet3_poll(struct napi_struct *napi, i |
rxd_done = vmxnet3_do_poll(rx_queue->adapter, budget); |
if (rxd_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rxd_done); |
vmxnet3_enable_all_intrs(rx_queue->adapter); |
} |
return rxd_done; |
@@ -1904,7 +1904,7 @@ vmxnet3_poll_rx_only(struct napi_struct |
rxd_done = vmxnet3_rq_rx_complete(rq, adapter, budget); |
if (rxd_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, rxd_done); |
vmxnet3_enable_intr(adapter, rq->comp_ring.intr_idx); |
} |
return rxd_done; |
--- a/drivers/net/wan/fsl_ucc_hdlc.c |
+++ b/drivers/net/wan/fsl_ucc_hdlc.c |
@@ -573,7 +573,7 @@ static int ucc_hdlc_poll(struct napi_str |
howmany += hdlc_rx_done(priv, budget - howmany); |
if (howmany < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, howmany); |
qe_setbits32(priv->uccf->p_uccm, |
(UCCE_HDLC_RX_EVENTS | UCCE_HDLC_TX_EVENTS) << 16); |
} |
--- a/drivers/net/wan/hd64572.c |
+++ b/drivers/net/wan/hd64572.c |
@@ -341,7 +341,7 @@ static int sca_poll(struct napi_struct * |
received = sca_rx_done(port, budget); |
if (received < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, received); |
enable_intr(port); |
} |
--- a/drivers/net/wireless/ath/ath10k/pci.c |
+++ b/drivers/net/wireless/ath/ath10k/pci.c |
@@ -2804,7 +2804,7 @@ static int ath10k_pci_napi_poll(struct n |
done = ath10k_htt_txrx_compl_task(ar, budget); |
if (done < budget) { |
- napi_complete(ctx); |
+ napi_complete_done(ctx, done); |
/* In case of MSI, it is possible that interrupts are received |
* while NAPI poll is inprogress. So pending interrupts that are |
* received after processing all copy engine pipes by NAPI poll |
--- a/drivers/net/wireless/ath/wil6210/netdev.c |
+++ b/drivers/net/wireless/ath/wil6210/netdev.c |
@@ -84,7 +84,7 @@ static int wil6210_netdev_poll_rx(struct |
done = budget - quota; |
if (done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, done); |
wil6210_unmask_irq_rx(wil); |
wil_dbg_txrx(wil, "NAPI RX complete\n"); |
} |
--- a/drivers/net/xen-netback/interface.c |
+++ b/drivers/net/xen-netback/interface.c |
@@ -104,7 +104,7 @@ static int xenvif_poll(struct napi_struc |
work_done = xenvif_tx_action(queue, budget); |
if (work_done < budget) { |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
/* If the queue is rate-limited, it shall be |
* rescheduled in the timer callback. |
*/ |
--- a/drivers/net/xen-netfront.c |
+++ b/drivers/net/xen-netfront.c |
@@ -1070,7 +1070,7 @@ err: |
if (work_done < budget) { |
int more_to_do = 0; |
- napi_complete(napi); |
+ napi_complete_done(napi, work_done); |
RING_FINAL_CHECK_FOR_RESPONSES(&queue->rx, more_to_do); |
if (more_to_do) |
--- a/drivers/staging/octeon/ethernet-rx.c |
+++ b/drivers/staging/octeon/ethernet-rx.c |
@@ -429,7 +429,7 @@ static int cvm_oct_napi_poll(struct napi |
if (rx_count < budget) { |
/* No more work */ |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_count); |
enable_irq(rx_group->irq); |
} |
return rx_count; |
--- a/drivers/staging/unisys/visornic/visornic_main.c |
+++ b/drivers/staging/unisys/visornic/visornic_main.c |
@@ -1657,7 +1657,7 @@ static int visornic_poll(struct napi_str |
/* If there aren't any more packets to receive stop the poll */ |
if (rx_count < budget) |
- napi_complete(napi); |
+ napi_complete_done(napi, rx_count); |
return rx_count; |
} |
/branches/18.06.1/target/linux/generic/backport-4.9/094-v4.12-0001-ip6_tunnel-Fix-missing-tunnel-encapsulation-limit-op.patch |
---|
@@ -0,0 +1,50 @@ |
From 89a23c8b528bd2c89f3981573d6cd7d23840c8a6 Mon Sep 17 00:00:00 2001 |
From: Craig Gallek <cgallek@google.com> |
Date: Wed, 26 Apr 2017 14:37:45 -0400 |
Subject: [PATCH] ip6_tunnel: Fix missing tunnel encapsulation limit option |
The IPv6 tunneling code tries to insert IPV6_TLV_TNL_ENCAP_LIMIT and |
IPV6_TLV_PADN options when an encapsulation limit is defined (the |
default is a limit of 4). An MTU adjustment is done to account for |
these options as well. However, the options are never present in the |
generated packets. |
The issue appears to be a subtlety between IPV6_DSTOPTS and |
IPV6_RTHDRDSTOPTS defined in RFC 3542. When the IPIP tunnel driver was |
written, the encap limit options were included as IPV6_RTHDRDSTOPTS in |
dst0opt of struct ipv6_txoptions. Later, ipv6_push_nfrags_opts was |
(correctly) updated to require IPV6_RTHDR options when IPV6_RTHDRDSTOPTS |
are to be used. This caused the options to no longer be included in v6 |
encapsulated packets. |
The fix is to use IPV6_DSTOPTS (in dst1opt of struct ipv6_txoptions) |
instead. IPV6_DSTOPTS do not have the additional IPV6_RTHDR requirement. |
Fixes: 1df64a8569c7: ("[IPV6]: Add ip6ip6 tunnel driver.") |
Fixes: 333fad5364d6: ("[IPV6]: Support several new sockopt / ancillary data in Advanced API (RFC3542)") |
Signed-off-by: Craig Gallek <kraig@google.com> |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv6/ip6_tunnel.c | 4 ++-- |
1 file changed, 2 insertions(+), 2 deletions(-) |
--- a/net/ipv6/ip6_tunnel.c |
+++ b/net/ipv6/ip6_tunnel.c |
@@ -958,7 +958,7 @@ static void init_tel_txopt(struct ipv6_t |
opt->dst_opt[5] = IPV6_TLV_PADN; |
opt->dst_opt[6] = 1; |
- opt->ops.dst0opt = (struct ipv6_opt_hdr *) opt->dst_opt; |
+ opt->ops.dst1opt = (struct ipv6_opt_hdr *) opt->dst_opt; |
opt->ops.opt_nflen = 8; |
} |
@@ -1200,7 +1200,7 @@ route_lookup: |
if (encap_limit >= 0) { |
init_tel_txopt(&opt, encap_limit); |
- ipv6_push_nfrag_opts(skb, &opt.ops, &proto, NULL); |
+ ipv6_push_frag_opts(skb, &opt.ops, &proto); |
} |
skb_push(skb, sizeof(struct ipv6hdr)); |
/branches/18.06.1/target/linux/generic/backport-4.9/094-v4.12-0002-ipv6-Need-to-export-ipv6_push_frag_opts-for-tunnelin.patch |
---|
@@ -0,0 +1,31 @@ |
From 5b8481fa42ac58484d633b558579e302aead64c1 Mon Sep 17 00:00:00 2001 |
From: "David S. Miller" <davem@davemloft.net> |
Date: Mon, 1 May 2017 15:10:20 -0400 |
Subject: [PATCH] ipv6: Need to export ipv6_push_frag_opts for tunneling now. |
Since that change also made the nfrag function not necessary |
for exports, remove it. |
Fixes: 89a23c8b528b ("ip6_tunnel: Fix missing tunnel encapsulation limit option") |
Signed-off-by: David S. Miller <davem@davemloft.net> |
--- |
net/ipv6/exthdrs.c | 2 +- |
1 file changed, 1 insertion(+), 1 deletion(-) |
--- a/net/ipv6/exthdrs.c |
+++ b/net/ipv6/exthdrs.c |
@@ -729,13 +729,13 @@ void ipv6_push_nfrag_opts(struct sk_buff |
if (opt->hopopt) |
ipv6_push_exthdr(skb, proto, NEXTHDR_HOP, opt->hopopt); |
} |
-EXPORT_SYMBOL(ipv6_push_nfrag_opts); |
void ipv6_push_frag_opts(struct sk_buff *skb, struct ipv6_txoptions *opt, u8 *proto) |
{ |
if (opt->dst1opt) |
ipv6_push_exthdr(skb, proto, NEXTHDR_DEST, opt->dst1opt); |
} |
+EXPORT_SYMBOL(ipv6_push_frag_opts); |
struct ipv6_txoptions * |
ipv6_dup_options(struct sock *sk, struct ipv6_txoptions *opt) |
/branches/18.06.1/target/linux/generic/backport-4.9/095-Allow-class-e-address-assignment-via-ifconfig-ioctl.patch |
---|
@@ -0,0 +1,79 @@ |
From 46bf067870156abd61fe24d14c2486d15b8b502c Mon Sep 17 00:00:00 2001 |
From: Dave Taht <dave@taht.net> |
Date: Fri, 14 Dec 2018 18:38:40 +0000 |
Subject: [PATCH 1/1] Allow class-e address assignment in ifconfig and early |
boot |
While the linux kernel became mostly "class-e clean" a decade ago, |
and most distributions long ago switched to the iproute2 suite |
of utilities, which allow class-e (240.0.0.0/4) address assignment, |
distributions relying on busybox, toybox and other forms of |
ifconfig cannot assign class-e addresses without this kernel patch. |
With this patch, also, a boot command line on these addresses is feasible: |
(ip=248.0.1.2::248.0.1.1:255.255.255.0). |
While CIDR has been obsolete for 2 decades, and a survey of all the |
userspace open source code in the world shows most IN_whatever macros |
are also obsolete... rather than obsolete CIDR from this ioctl entirely, |
this patch merely enables class-e assignment, sanely. |
H/T to Vince Fuller and his original patch here: |
https://lkml.org/lkml/2008/1/7/370 |
Signed-off-by: Dave Taht <dave.taht@gmail.com> |
Reviewed-by: John Gilmore <gnu@toad.com> |
--- |
include/uapi/linux/in.h | 8 ++++++-- |
net/ipv4/devinet.c | 4 +++- |
net/ipv4/ipconfig.c | 2 ++ |
3 files changed, 11 insertions(+), 3 deletions(-) |
--- a/include/uapi/linux/in.h |
+++ b/include/uapi/linux/in.h |
@@ -266,8 +266,12 @@ struct sockaddr_in { |
#define IN_MULTICAST(a) IN_CLASSD(a) |
#define IN_MULTICAST_NET 0xF0000000 |
-#define IN_EXPERIMENTAL(a) ((((long int) (a)) & 0xf0000000) == 0xf0000000) |
-#define IN_BADCLASS(a) IN_EXPERIMENTAL((a)) |
+#define IN_BADCLASS(a) (((long int) (a) ) == (long int)0xffffffff) |
+#define IN_EXPERIMENTAL(a) IN_BADCLASS((a)) |
+ |
+#define IN_CLASSE(a) ((((long int) (a)) & 0xf0000000) == 0xf0000000) |
+#define IN_CLASSE_NET 0xffffffff |
+#define IN_CLASSE_NSHIFT 0 |
/* Address to accept any incoming messages. */ |
#define INADDR_ANY ((unsigned long int) 0x00000000) |
--- a/net/ipv4/devinet.c |
+++ b/net/ipv4/devinet.c |
@@ -898,7 +898,7 @@ static int inet_abc_len(__be32 addr) |
{ |
int rc = -1; /* Something else, probably a multicast. */ |
- if (ipv4_is_zeronet(addr)) |
+ if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) |
rc = 0; |
else { |
__u32 haddr = ntohl(addr); |
@@ -909,6 +909,8 @@ static int inet_abc_len(__be32 addr) |
rc = 16; |
else if (IN_CLASSC(haddr)) |
rc = 24; |
+ else if (IN_CLASSE(haddr)) |
+ rc = 32; |
} |
return rc; |
--- a/net/ipv4/ipconfig.c |
+++ b/net/ipv4/ipconfig.c |
@@ -455,6 +455,8 @@ static int __init ic_defaults(void) |
ic_netmask = htonl(IN_CLASSB_NET); |
else if (IN_CLASSC(ntohl(ic_myaddr))) |
ic_netmask = htonl(IN_CLASSC_NET); |
+ else if (IN_CLASSE(ntohl(ic_myaddr))) |
+ ic_netmask = htonl(IN_CLASSE_NET); |
else { |
pr_err("IP-Config: Unable to guess netmask for address %pI4\n", |
&ic_myaddr); |
/branches/18.06.1/target/linux/generic/backport-4.9/095-v4.13-0001-rtc-ds1307-add-ds1308-variant.patch |
---|
@@ -0,0 +1,51 @@ |
From 300a7735becf55f7fd18f8cd3dc3b945a0cab712 Mon Sep 17 00:00:00 2001 |
From: Sean Nyekjaer <sean.nyekjaer@prevas.dk> |
Date: Thu, 8 Jun 2017 12:36:54 +0200 |
Subject: rtc: ds1307: add ds1308 variant |
The ds1308 variant is very similar to the already supported ds1338 |
variant, it have more debug registers and a square wave clock output. |
Signed-off-by: Sean Nyekjaer <sean.nyekjaer@prevas.dk> |
Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com> |
--- |
drivers/rtc/rtc-ds1307.c | 12 ++++++++++++ |
1 file changed, 12 insertions(+) |
--- a/drivers/rtc/rtc-ds1307.c |
+++ b/drivers/rtc/rtc-ds1307.c |
@@ -31,6 +31,7 @@ |
*/ |
enum ds_type { |
ds_1307, |
+ ds_1308, |
ds_1337, |
ds_1338, |
ds_1339, |
@@ -144,6 +145,10 @@ static struct chip_desc chips[last_ds_ty |
.nvram_offset = 8, |
.nvram_size = 56, |
}, |
+ [ds_1308] = { |
+ .nvram_offset = 8, |
+ .nvram_size = 56, |
+ }, |
[ds_1337] = { |
.alarm = 1, |
}, |
@@ -175,6 +180,7 @@ static struct chip_desc chips[last_ds_ty |
static const struct i2c_device_id ds1307_id[] = { |
{ "ds1307", ds_1307 }, |
+ { "ds1308", ds_1308 }, |
{ "ds1337", ds_1337 }, |
{ "ds1338", ds_1338 }, |
{ "ds1339", ds_1339 }, |
@@ -1488,6 +1494,7 @@ read_rtc: |
goto read_rtc; |
} |
break; |
+ case ds_1308: |
case ds_1338: |
/* clock halted? turn it on, so clock can tick. */ |
if (tmp & DS1307_BIT_CH) |
/branches/18.06.1/target/linux/generic/backport-4.9/096-v4.20-netfilter-ipv6-Preserve-link-scope-traffic-original-.patch |
---|
@@ -0,0 +1,32 @@ |
From 508b09046c0f21678652fb66fd1e9959d55591d2 Mon Sep 17 00:00:00 2001 |
From: Alin Nastac <alin.nastac@gmail.com> |
Date: Wed, 21 Nov 2018 14:00:30 +0100 |
Subject: [PATCH] netfilter: ipv6: Preserve link scope traffic original oif |
When ip6_route_me_harder is invoked, it resets outgoing interface of: |
- link-local scoped packets sent by neighbor discovery |
- multicast packets sent by MLD host |
- multicast packets send by MLD proxy daemon that sets outgoing |
interface through IPV6_PKTINFO ipi6_ifindex |
Link-local and multicast packets must keep their original oif after |
ip6_route_me_harder is called. |
Signed-off-by: Alin Nastac <alin.nastac@gmail.com> |
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> |
--- |
net/ipv6/netfilter.c | 3 ++- |
1 file changed, 2 insertions(+), 1 deletion(-) |
--- a/net/ipv6/netfilter.c |
+++ b/net/ipv6/netfilter.c |
@@ -24,7 +24,8 @@ int ip6_route_me_harder(struct net *net, |
unsigned int hh_len; |
struct dst_entry *dst; |
struct flowi6 fl6 = { |
- .flowi6_oif = skb->sk ? skb->sk->sk_bound_dev_if : 0, |
+ .flowi6_oif = skb->sk && skb->sk->sk_bound_dev_if ? skb->sk->sk_bound_dev_if : |
+ rt6_need_strict(&iph->daddr) ? skb_dst(skb)->dev->ifindex : 0, |
.flowi6_mark = skb->mark, |
.daddr = iph->daddr, |
.saddr = iph->saddr, |
/branches/18.06.1/target/linux/generic/backport-4.9/101-arm-cns3xxx-use-actual-size-reads-for-PCIe.patch |
---|
@@ -0,0 +1,46 @@ |
From 4cc30de79d293f1e8c5f50ae3a9c005def9564a0 Mon Sep 17 00:00:00 2001 |
From: Koen Vandeputte <koen.vandeputte@ncentric.com> |
Date: Mon, 7 Jan 2019 14:14:27 +0100 |
Subject: [PATCH 2/2] arm: cns3xxx: use actual size reads for PCIe |
commit 802b7c06adc7 ("ARM: cns3xxx: Convert PCI to use generic config accessors") |
reimplemented cns3xxx_pci_read_config() using pci_generic_config_read32(), |
which preserved the property of only doing 32-bit reads. |
It also replaced cns3xxx_pci_write_config() with pci_generic_config_write(), |
so it changed writes from always being 32 bits to being the actual size, |
which works just fine. |
Due to: |
- The documentation does not mention that only 32 bit access is allowed. |
- Writes are already executed using the actual size |
- Extensive testing shows that 8b, 16b and 32b reads work as intended |
It makes perfectly sense to also swap 32 bit reading in favor of actual size. |
Fixes: 802b7c06adc7 ("ARM: cns3xxx: Convert PCI to use generic config accessors") |
Suggested-by: Bjorn Helgaas <bhelgaas@google.com> |
Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com> |
CC: Arnd Bergmann <arnd@arndb.de> |
CC: Krzysztof Halasa <khalasa@piap.pl> |
CC: Olof Johansson <olof@lixom.net> |
CC: Robin Leblon <robin.leblon@ncentric.com> |
CC: Rob Herring <robh@kernel.org> |
CC: Russell King <linux@armlinux.org.uk> |
CC: Tim Harvey <tharvey@gateworks.com> |
CC: stable@vger.kernel.org # v4.0+ |
--- |
arch/arm/mach-cns3xxx/pcie.c | 2 +- |
1 file changed, 1 insertion(+), 1 deletion(-) |
--- a/arch/arm/mach-cns3xxx/pcie.c |
+++ b/arch/arm/mach-cns3xxx/pcie.c |
@@ -93,7 +93,7 @@ static int cns3xxx_pci_read_config(struc |
u32 mask = (0x1ull << (size * 8)) - 1; |
int shift = (where % 4) * 8; |
- ret = pci_generic_config_read32(bus, devfn, where, size, val); |
+ ret = pci_generic_config_read(bus, devfn, where, size, val); |
if (ret == PCIBIOS_SUCCESSFUL && !bus->number && !devfn && |
(where & 0xffc) == PCI_CLASS_REVISION) |
/branches/18.06.1/target/linux/generic/backport-4.9/400-v4.16-leds-trigger-Introduce-a-NETDEV-trigger.patch |
---|
@@ -0,0 +1,588 @@ |
From 06f502f57d0d7728f9fa0f157ec5e4111ddb98f6 Mon Sep 17 00:00:00 2001 |
From: Ben Whitten <ben.whitten@gmail.com> |
Date: Sun, 10 Dec 2017 21:17:55 +0000 |
Subject: [PATCH] leds: trigger: Introduce a NETDEV trigger |
This commit introduces a NETDEV trigger for named device |
activity. Available triggers are link, rx, and tx. |
Signed-off-by: Ben Whitten <ben.whitten@gmail.com> |
Acked-by: Pavel Machek <pavel@ucw.cz> |
Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com> |
--- |
.../ABI/testing/sysfs-class-led-trigger-netdev | 45 ++ |
drivers/leds/trigger/Kconfig | 7 + |
drivers/leds/trigger/Makefile | 1 + |
drivers/leds/trigger/ledtrig-netdev.c | 496 +++++++++++++++++++++ |
4 files changed, 549 insertions(+) |
create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-netdev |
create mode 100644 drivers/leds/trigger/ledtrig-netdev.c |
--- /dev/null |
+++ b/Documentation/ABI/testing/sysfs-class-led-trigger-netdev |
@@ -0,0 +1,45 @@ |
+What: /sys/class/leds/<led>/device_name |
+Date: Dec 2017 |
+KernelVersion: 4.16 |
+Contact: linux-leds@vger.kernel.org |
+Description: |
+ Specifies the network device name to monitor. |
+ |
+What: /sys/class/leds/<led>/interval |
+Date: Dec 2017 |
+KernelVersion: 4.16 |
+Contact: linux-leds@vger.kernel.org |
+Description: |
+ Specifies the duration of the LED blink in milliseconds. |
+ Defaults to 50 ms. |
+ |
+What: /sys/class/leds/<led>/link |
+Date: Dec 2017 |
+KernelVersion: 4.16 |
+Contact: linux-leds@vger.kernel.org |
+Description: |
+ Signal the link state of the named network device. |
+ If set to 0 (default), the LED's normal state is off. |
+ If set to 1, the LED's normal state reflects the link state |
+ of the named network device. |
+ Setting this value also immediately changes the LED state. |
+ |
+What: /sys/class/leds/<led>/tx |
+Date: Dec 2017 |
+KernelVersion: 4.16 |
+Contact: linux-leds@vger.kernel.org |
+Description: |
+ Signal transmission of data on the named network device. |
+ If set to 0 (default), the LED will not blink on transmission. |
+ If set to 1, the LED will blink for the milliseconds specified |
+ in interval to signal transmission. |
+ |
+What: /sys/class/leds/<led>/rx |
+Date: Dec 2017 |
+KernelVersion: 4.16 |
+Contact: linux-leds@vger.kernel.org |
+Description: |
+ Signal reception of data on the named network device. |
+ If set to 0 (default), the LED will not blink on reception. |
+ If set to 1, the LED will blink for the milliseconds specified |
+ in interval to signal reception. |
--- a/drivers/leds/trigger/Kconfig |
+++ b/drivers/leds/trigger/Kconfig |
@@ -126,4 +126,11 @@ config LEDS_TRIGGER_PANIC |
a different trigger. |
If unsure, say Y. |
+config LEDS_TRIGGER_NETDEV |
+ tristate "LED Netdev Trigger" |
+ depends on NET && LEDS_TRIGGERS |
+ help |
+ This allows LEDs to be controlled by network device activity. |
+ If unsure, say Y. |
+ |
endif # LEDS_TRIGGERS |
--- a/drivers/leds/trigger/Makefile |
+++ b/drivers/leds/trigger/Makefile |
@@ -10,3 +10,4 @@ obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += |
obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o |
obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o |
obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o |
+obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o |
--- /dev/null |
+++ b/drivers/leds/trigger/ledtrig-netdev.c |
@@ -0,0 +1,496 @@ |
+// SPDX-License-Identifier: GPL-2.0 |
+// Copyright 2017 Ben Whitten <ben.whitten@gmail.com> |
+// Copyright 2007 Oliver Jowett <oliver@opencloud.com> |
+// |
+// LED Kernel Netdev Trigger |
+// |
+// Toggles the LED to reflect the link and traffic state of a named net device |
+// |
+// Derived from ledtrig-timer.c which is: |
+// Copyright 2005-2006 Openedhand Ltd. |
+// Author: Richard Purdie <rpurdie@openedhand.com> |
+ |
+#include <linux/atomic.h> |
+#include <linux/ctype.h> |
+#include <linux/device.h> |
+#include <linux/init.h> |
+#include <linux/jiffies.h> |
+#include <linux/kernel.h> |
+#include <linux/leds.h> |
+#include <linux/list.h> |
+#include <linux/module.h> |
+#include <linux/netdevice.h> |
+#include <linux/spinlock.h> |
+#include <linux/timer.h> |
+#include "../leds.h" |
+ |
+/* |
+ * Configurable sysfs attributes: |
+ * |
+ * device_name - network device name to monitor |
+ * interval - duration of LED blink, in milliseconds |
+ * link - LED's normal state reflects whether the link is up |
+ * (has carrier) or not |
+ * tx - LED blinks on transmitted data |
+ * rx - LED blinks on receive data |
+ * |
+ */ |
+ |
+struct led_netdev_data { |
+ spinlock_t lock; |
+ |
+ struct delayed_work work; |
+ struct notifier_block notifier; |
+ |
+ struct led_classdev *led_cdev; |
+ struct net_device *net_dev; |
+ |
+ char device_name[IFNAMSIZ]; |
+ atomic_t interval; |
+ unsigned int last_activity; |
+ |
+ unsigned long mode; |
+#define NETDEV_LED_LINK 0 |
+#define NETDEV_LED_TX 1 |
+#define NETDEV_LED_RX 2 |
+#define NETDEV_LED_MODE_LINKUP 3 |
+}; |
+ |
+enum netdev_led_attr { |
+ NETDEV_ATTR_LINK, |
+ NETDEV_ATTR_TX, |
+ NETDEV_ATTR_RX |
+}; |
+ |
+static void set_baseline_state(struct led_netdev_data *trigger_data) |
+{ |
+ int current_brightness; |
+ struct led_classdev *led_cdev = trigger_data->led_cdev; |
+ |
+ current_brightness = led_cdev->brightness; |
+ if (current_brightness) |
+ led_cdev->blink_brightness = current_brightness; |
+ if (!led_cdev->blink_brightness) |
+ led_cdev->blink_brightness = led_cdev->max_brightness; |
+ |
+ if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode)) |
+ led_set_brightness(led_cdev, LED_OFF); |
+ else { |
+ if (test_bit(NETDEV_LED_LINK, &trigger_data->mode)) |
+ led_set_brightness(led_cdev, |
+ led_cdev->blink_brightness); |
+ else |
+ led_set_brightness(led_cdev, LED_OFF); |
+ |
+ /* If we are looking for RX/TX start periodically |
+ * checking stats |
+ */ |
+ if (test_bit(NETDEV_LED_TX, &trigger_data->mode) || |
+ test_bit(NETDEV_LED_RX, &trigger_data->mode)) |
+ schedule_delayed_work(&trigger_data->work, 0); |
+ } |
+} |
+ |
+static ssize_t device_name_show(struct device *dev, |
+ struct device_attribute *attr, char *buf) |
+{ |
+ struct led_classdev *led_cdev = dev_get_drvdata(dev); |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ ssize_t len; |
+ |
+ spin_lock_bh(&trigger_data->lock); |
+ len = sprintf(buf, "%s\n", trigger_data->device_name); |
+ spin_unlock_bh(&trigger_data->lock); |
+ |
+ return len; |
+} |
+ |
+static ssize_t device_name_store(struct device *dev, |
+ struct device_attribute *attr, const char *buf, |
+ size_t size) |
+{ |
+ struct led_classdev *led_cdev = dev_get_drvdata(dev); |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ |
+ if (size >= IFNAMSIZ) |
+ return -EINVAL; |
+ |
+ cancel_delayed_work_sync(&trigger_data->work); |
+ |
+ spin_lock_bh(&trigger_data->lock); |
+ |
+ if (trigger_data->net_dev) { |
+ dev_put(trigger_data->net_dev); |
+ trigger_data->net_dev = NULL; |
+ } |
+ |
+ strncpy(trigger_data->device_name, buf, size); |
+ if (size > 0 && trigger_data->device_name[size - 1] == '\n') |
+ trigger_data->device_name[size - 1] = 0; |
+ |
+ if (trigger_data->device_name[0] != 0) |
+ trigger_data->net_dev = |
+ dev_get_by_name(&init_net, trigger_data->device_name); |
+ |
+ clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); |
+ if (trigger_data->net_dev != NULL) |
+ if (netif_carrier_ok(trigger_data->net_dev)) |
+ set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); |
+ |
+ trigger_data->last_activity = 0; |
+ |
+ set_baseline_state(trigger_data); |
+ spin_unlock_bh(&trigger_data->lock); |
+ |
+ return size; |
+} |
+ |
+static DEVICE_ATTR_RW(device_name); |
+ |
+static ssize_t netdev_led_attr_show(struct device *dev, char *buf, |
+ enum netdev_led_attr attr) |
+{ |
+ struct led_classdev *led_cdev = dev_get_drvdata(dev); |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ int bit; |
+ |
+ switch (attr) { |
+ case NETDEV_ATTR_LINK: |
+ bit = NETDEV_LED_LINK; |
+ break; |
+ case NETDEV_ATTR_TX: |
+ bit = NETDEV_LED_TX; |
+ break; |
+ case NETDEV_ATTR_RX: |
+ bit = NETDEV_LED_RX; |
+ break; |
+ default: |
+ return -EINVAL; |
+ } |
+ |
+ return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode)); |
+} |
+ |
+static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, |
+ size_t size, enum netdev_led_attr attr) |
+{ |
+ struct led_classdev *led_cdev = dev_get_drvdata(dev); |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ unsigned long state; |
+ int ret; |
+ int bit; |
+ |
+ ret = kstrtoul(buf, 0, &state); |
+ if (ret) |
+ return ret; |
+ |
+ switch (attr) { |
+ case NETDEV_ATTR_LINK: |
+ bit = NETDEV_LED_LINK; |
+ break; |
+ case NETDEV_ATTR_TX: |
+ bit = NETDEV_LED_TX; |
+ break; |
+ case NETDEV_ATTR_RX: |
+ bit = NETDEV_LED_RX; |
+ break; |
+ default: |
+ return -EINVAL; |
+ } |
+ |
+ cancel_delayed_work_sync(&trigger_data->work); |
+ |
+ if (state) |
+ set_bit(bit, &trigger_data->mode); |
+ else |
+ clear_bit(bit, &trigger_data->mode); |
+ |
+ set_baseline_state(trigger_data); |
+ |
+ return size; |
+} |
+ |
+static ssize_t link_show(struct device *dev, |
+ struct device_attribute *attr, char *buf) |
+{ |
+ return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK); |
+} |
+ |
+static ssize_t link_store(struct device *dev, |
+ struct device_attribute *attr, const char *buf, size_t size) |
+{ |
+ return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK); |
+} |
+ |
+static DEVICE_ATTR_RW(link); |
+ |
+static ssize_t tx_show(struct device *dev, |
+ struct device_attribute *attr, char *buf) |
+{ |
+ return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX); |
+} |
+ |
+static ssize_t tx_store(struct device *dev, |
+ struct device_attribute *attr, const char *buf, size_t size) |
+{ |
+ return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX); |
+} |
+ |
+static DEVICE_ATTR_RW(tx); |
+ |
+static ssize_t rx_show(struct device *dev, |
+ struct device_attribute *attr, char *buf) |
+{ |
+ return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX); |
+} |
+ |
+static ssize_t rx_store(struct device *dev, |
+ struct device_attribute *attr, const char *buf, size_t size) |
+{ |
+ return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX); |
+} |
+ |
+static DEVICE_ATTR_RW(rx); |
+ |
+static ssize_t interval_show(struct device *dev, |
+ struct device_attribute *attr, char *buf) |
+{ |
+ struct led_classdev *led_cdev = dev_get_drvdata(dev); |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ |
+ return sprintf(buf, "%u\n", |
+ jiffies_to_msecs(atomic_read(&trigger_data->interval))); |
+} |
+ |
+static ssize_t interval_store(struct device *dev, |
+ struct device_attribute *attr, const char *buf, |
+ size_t size) |
+{ |
+ struct led_classdev *led_cdev = dev_get_drvdata(dev); |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ unsigned long value; |
+ int ret; |
+ |
+ ret = kstrtoul(buf, 0, &value); |
+ if (ret) |
+ return ret; |
+ |
+ /* impose some basic bounds on the timer interval */ |
+ if (value >= 5 && value <= 10000) { |
+ cancel_delayed_work_sync(&trigger_data->work); |
+ |
+ atomic_set(&trigger_data->interval, msecs_to_jiffies(value)); |
+ set_baseline_state(trigger_data); /* resets timer */ |
+ } |
+ |
+ return size; |
+} |
+ |
+static DEVICE_ATTR_RW(interval); |
+ |
+static int netdev_trig_notify(struct notifier_block *nb, |
+ unsigned long evt, void *dv) |
+{ |
+ struct net_device *dev = |
+ netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv); |
+ struct led_netdev_data *trigger_data = container_of(nb, |
+ struct |
+ led_netdev_data, |
+ notifier); |
+ |
+ if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE |
+ && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER |
+ && evt != NETDEV_CHANGENAME) |
+ return NOTIFY_DONE; |
+ |
+ if (strcmp(dev->name, trigger_data->device_name)) |
+ return NOTIFY_DONE; |
+ |
+ cancel_delayed_work_sync(&trigger_data->work); |
+ |
+ spin_lock_bh(&trigger_data->lock); |
+ |
+ clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); |
+ switch (evt) { |
+ case NETDEV_REGISTER: |
+ if (trigger_data->net_dev) |
+ dev_put(trigger_data->net_dev); |
+ dev_hold(dev); |
+ trigger_data->net_dev = dev; |
+ break; |
+ case NETDEV_CHANGENAME: |
+ case NETDEV_UNREGISTER: |
+ if (trigger_data->net_dev) { |
+ dev_put(trigger_data->net_dev); |
+ trigger_data->net_dev = NULL; |
+ } |
+ break; |
+ case NETDEV_UP: |
+ case NETDEV_CHANGE: |
+ if (netif_carrier_ok(dev)) |
+ set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); |
+ break; |
+ } |
+ |
+ set_baseline_state(trigger_data); |
+ |
+ spin_unlock_bh(&trigger_data->lock); |
+ |
+ return NOTIFY_DONE; |
+} |
+ |
+/* here's the real work! */ |
+static void netdev_trig_work(struct work_struct *work) |
+{ |
+ struct led_netdev_data *trigger_data = container_of(work, |
+ struct |
+ led_netdev_data, |
+ work.work); |
+ struct rtnl_link_stats64 *dev_stats; |
+ unsigned int new_activity; |
+ struct rtnl_link_stats64 temp; |
+ unsigned long interval; |
+ int invert; |
+ |
+ /* If we dont have a device, insure we are off */ |
+ if (!trigger_data->net_dev) { |
+ led_set_brightness(trigger_data->led_cdev, LED_OFF); |
+ return; |
+ } |
+ |
+ /* If we are not looking for RX/TX then return */ |
+ if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) && |
+ !test_bit(NETDEV_LED_RX, &trigger_data->mode)) |
+ return; |
+ |
+ dev_stats = dev_get_stats(trigger_data->net_dev, &temp); |
+ new_activity = |
+ (test_bit(NETDEV_LED_TX, &trigger_data->mode) ? |
+ dev_stats->tx_packets : 0) + |
+ (test_bit(NETDEV_LED_RX, &trigger_data->mode) ? |
+ dev_stats->rx_packets : 0); |
+ |
+ if (trigger_data->last_activity != new_activity) { |
+ led_stop_software_blink(trigger_data->led_cdev); |
+ |
+ invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode); |
+ interval = jiffies_to_msecs( |
+ atomic_read(&trigger_data->interval)); |
+ /* base state is ON (link present) */ |
+ led_blink_set_oneshot(trigger_data->led_cdev, |
+ &interval, |
+ &interval, |
+ invert); |
+ trigger_data->last_activity = new_activity; |
+ } |
+ |
+ schedule_delayed_work(&trigger_data->work, |
+ (atomic_read(&trigger_data->interval)*2)); |
+} |
+ |
+static void netdev_trig_activate(struct led_classdev *led_cdev) |
+{ |
+ struct led_netdev_data *trigger_data; |
+ int rc; |
+ |
+ trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); |
+ if (!trigger_data) |
+ return; |
+ |
+ spin_lock_init(&trigger_data->lock); |
+ |
+ trigger_data->notifier.notifier_call = netdev_trig_notify; |
+ trigger_data->notifier.priority = 10; |
+ |
+ INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work); |
+ |
+ trigger_data->led_cdev = led_cdev; |
+ trigger_data->net_dev = NULL; |
+ trigger_data->device_name[0] = 0; |
+ |
+ trigger_data->mode = 0; |
+ atomic_set(&trigger_data->interval, msecs_to_jiffies(50)); |
+ trigger_data->last_activity = 0; |
+ |
+ led_cdev->trigger_data = trigger_data; |
+ |
+ rc = device_create_file(led_cdev->dev, &dev_attr_device_name); |
+ if (rc) |
+ goto err_out; |
+ rc = device_create_file(led_cdev->dev, &dev_attr_link); |
+ if (rc) |
+ goto err_out_device_name; |
+ rc = device_create_file(led_cdev->dev, &dev_attr_rx); |
+ if (rc) |
+ goto err_out_link; |
+ rc = device_create_file(led_cdev->dev, &dev_attr_tx); |
+ if (rc) |
+ goto err_out_rx; |
+ rc = device_create_file(led_cdev->dev, &dev_attr_interval); |
+ if (rc) |
+ goto err_out_tx; |
+ rc = register_netdevice_notifier(&trigger_data->notifier); |
+ if (rc) |
+ goto err_out_interval; |
+ return; |
+ |
+err_out_interval: |
+ device_remove_file(led_cdev->dev, &dev_attr_interval); |
+err_out_tx: |
+ device_remove_file(led_cdev->dev, &dev_attr_tx); |
+err_out_rx: |
+ device_remove_file(led_cdev->dev, &dev_attr_rx); |
+err_out_link: |
+ device_remove_file(led_cdev->dev, &dev_attr_link); |
+err_out_device_name: |
+ device_remove_file(led_cdev->dev, &dev_attr_device_name); |
+err_out: |
+ led_cdev->trigger_data = NULL; |
+ kfree(trigger_data); |
+} |
+ |
+static void netdev_trig_deactivate(struct led_classdev *led_cdev) |
+{ |
+ struct led_netdev_data *trigger_data = led_cdev->trigger_data; |
+ |
+ if (trigger_data) { |
+ unregister_netdevice_notifier(&trigger_data->notifier); |
+ |
+ device_remove_file(led_cdev->dev, &dev_attr_device_name); |
+ device_remove_file(led_cdev->dev, &dev_attr_link); |
+ device_remove_file(led_cdev->dev, &dev_attr_rx); |
+ device_remove_file(led_cdev->dev, &dev_attr_tx); |
+ device_remove_file(led_cdev->dev, &dev_attr_interval); |
+ |
+ cancel_delayed_work_sync(&trigger_data->work); |
+ |
+ if (trigger_data->net_dev) |
+ dev_put(trigger_data->net_dev); |
+ |
+ kfree(trigger_data); |
+ } |
+} |
+ |
+static struct led_trigger netdev_led_trigger = { |
+ .name = "netdev", |
+ .activate = netdev_trig_activate, |
+ .deactivate = netdev_trig_deactivate, |
+}; |
+ |
+static int __init netdev_trig_init(void) |
+{ |
+ return led_trigger_register(&netdev_led_trigger); |
+} |
+ |
+static void __exit netdev_trig_exit(void) |
+{ |
+ led_trigger_unregister(&netdev_led_trigger); |
+} |
+ |
+module_init(netdev_trig_init); |
+module_exit(netdev_trig_exit); |
+ |
+MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>"); |
+MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>"); |
+MODULE_DESCRIPTION("Netdev LED trigger"); |
+MODULE_LICENSE("GPL v2"); |
/branches/18.06.1/target/linux/generic/backport-4.9/400-v4.18-mtd-bcm47xxpart-add-of_match_table-with-a-new-DT-bin.patch |
---|
@@ -0,0 +1,47 @@ |
From cf589ce71e84d3b8811c65740645af254c5248c0 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Wed, 9 May 2018 10:17:29 +0200 |
Subject: [PATCH] mtd: bcm47xxpart: add of_match_table with a new DT binding |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
This allows using bcm47xxpart parser to find partitions on flash |
described in DT using the "brcm,bcm947xx-cfe-partitions" compatible |
property. It means this parser doesn't have to be explicitly selected by |
a flash driver anymore. It can be used e.g. together with a generic |
m25p80 / spi-nor if device is just properly described. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/bcm47xxpart.c | 7 +++++++ |
1 file changed, 7 insertions(+) |
--- a/drivers/mtd/bcm47xxpart.c |
+++ b/drivers/mtd/bcm47xxpart.c |
@@ -15,6 +15,7 @@ |
#include <linux/slab.h> |
#include <linux/mtd/mtd.h> |
#include <linux/mtd/partitions.h> |
+#include <linux/mod_devicetable.h> |
#include <uapi/linux/magic.h> |
@@ -304,9 +305,16 @@ static int bcm47xxpart_parse(struct mtd_ |
return curr_part; |
}; |
+static const struct of_device_id bcm47xxpart_of_match_table[] = { |
+ { .compatible = "brcm,bcm947xx-cfe-partitions" }, |
+ {}, |
+}; |
+MODULE_DEVICE_TABLE(of, bcm47xxpart_of_match_table); |
+ |
static struct mtd_part_parser bcm47xxpart_mtd_parser = { |
.parse_fn = bcm47xxpart_parse, |
.name = "bcm47xxpart", |
+ .of_match_table = bcm47xxpart_of_match_table, |
}; |
module_mtd_part_parser(bcm47xxpart_mtd_parser); |
/branches/18.06.1/target/linux/generic/backport-4.9/401-v4.19-mtd-parsers-trx-add-of_match_table-with-the-new-DT-b.patch |
---|
@@ -0,0 +1,45 @@ |
From 98534a58c8a40cdc9e3bcb04d74719fbcedfeb52 Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Tue, 26 Jun 2018 00:05:08 +0200 |
Subject: [PATCH] mtd: parsers: trx: add of_match_table with the new DT binding |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
This allows using TRX parser to find TRX partitions on flash device |
described in DT using a proper binding. It's useful for devices storing |
firmware on a separated flash and having rootfs partition in it. |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/parsers/parser_trx.c | 7 +++++++ |
1 file changed, 7 insertions(+) |
--- a/drivers/mtd/parsers/parser_trx.c |
+++ b/drivers/mtd/parsers/parser_trx.c |
@@ -13,6 +13,7 @@ |
#include <linux/slab.h> |
#include <linux/mtd/mtd.h> |
#include <linux/mtd/partitions.h> |
+#include <linux/mod_devicetable.h> |
#define TRX_PARSER_MAX_PARTS 4 |
@@ -116,9 +117,16 @@ static int parser_trx_parse(struct mtd_i |
return i; |
}; |
+static const struct of_device_id mtd_parser_trx_of_match_table[] = { |
+ { .compatible = "brcm,trx" }, |
+ {}, |
+}; |
+MODULE_DEVICE_TABLE(of, mtd_parser_trx_of_match_table); |
+ |
static struct mtd_part_parser mtd_parser_trx = { |
.parse_fn = parser_trx_parse, |
.name = "trx", |
+ .of_match_table = mtd_parser_trx_of_match_table, |
}; |
module_mtd_part_parser(mtd_parser_trx); |
/branches/18.06.1/target/linux/generic/backport-4.9/402-v4.19-mtd-partitions-use-DT-info-for-parsing-partitions-wi.patch |
---|
@@ -0,0 +1,102 @@ |
From 76a832254ab05502c9394cc51ded6f0abe0e0bee Mon Sep 17 00:00:00 2001 |
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl> |
Date: Fri, 13 Jul 2018 16:32:21 +0200 |
Subject: [PATCH] mtd: partitions: use DT info for parsing partitions with |
"compatible" prop |
MIME-Version: 1.0 |
Content-Type: text/plain; charset=UTF-8 |
Content-Transfer-Encoding: 8bit |
So far only flash devices could be described in DT regarding partitions |
parsing. That could be done with "partitions" subnode and a proper |
"compatible" string. |
Some devices may use hierarchical (multi-level) layouts and may mix used |
layouts (fixed and dynamic). Describing that in DT is done by specifying |
"compatible" for DT-represented partition plus optionally more |
properties and/or subnodes. |
To support such layouts each DT partition has to be checked for |
additional description. |
Please note this implementation will work in parallel with support for |
partition type specified for non-DT setups. That already works since |
commit 1a0915be1926 ("mtd: partitions: add support for partition |
parsers"). |
Signed-off-by: Rafał Miłecki <rafal@milecki.pl> |
Signed-off-by: Boris Brezillon <boris.brezillon@bootlin.com> |
--- |
drivers/mtd/mtdpart.c | 33 +++++++++++++-------------------- |
1 file changed, 13 insertions(+), 20 deletions(-) |
--- a/drivers/mtd/mtdpart.c |
+++ b/drivers/mtd/mtdpart.c |
@@ -370,22 +370,6 @@ 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) |
-{ |
- return parse_mtd_partitions(&slave->mtd, types, NULL); |
-} |
- |
static struct mtd_part *allocate_partition(struct mtd_info *parent, |
const struct mtd_partition *part, int partno, |
uint64_t cur_offset) |
@@ -775,8 +759,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); |
+ /* Look for subpartitions */ |
+ parse_mtd_partitions(&slave->mtd, parts[i].types, NULL); |
cur_offset = slave->offset + slave->mtd.size; |
} |
@@ -852,6 +836,12 @@ static const char * const default_mtd_pa |
NULL |
}; |
+/* Check DT only when looking for subpartitions. */ |
+static const char * const default_subpartition_types[] = { |
+ "ofpart", |
+ NULL |
+}; |
+ |
static int mtd_part_do_parse(struct mtd_part_parser *parser, |
struct mtd_info *master, |
struct mtd_partitions *pparts, |
@@ -922,7 +912,9 @@ static int mtd_part_of_parse(struct mtd_ |
const char *fixed = "fixed-partitions"; |
int ret, err = 0; |
- np = of_get_child_by_name(mtd_get_of_node(master), "partitions"); |
+ np = mtd_get_of_node(master); |
+ if (!mtd_is_partition(master)) |
+ np = of_get_child_by_name(np, "partitions"); |
of_property_for_each_string(np, "compatible", prop, compat) { |
parser = mtd_part_get_compatible_parser(compat); |
if (!parser) |
@@ -985,7 +977,8 @@ int parse_mtd_partitions(struct mtd_info |
int ret, err = 0; |
if (!types) |
- types = default_mtd_part_types; |
+ types = mtd_is_partition(master) ? default_subpartition_types : |
+ default_mtd_part_types; |
for ( ; *types; types++) { |
/* |