mirror of
https://github.com/openwrt/openwrt.git
synced 2025-04-07 19:34:48 +00:00
kernel: remove support for kernel 4.9
No target uses kernel 4.9 anymore. Signed-off-by: Adrian Schmutzler <freifunk@adrianschmutzler.de>
This commit is contained in:
parent
28fd4ac512
commit
57a9633a2c
@ -6,11 +6,9 @@ ifdef CONFIG_TESTING_KERNEL
|
||||
KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER)
|
||||
endif
|
||||
|
||||
LINUX_VERSION-4.9 = .208
|
||||
LINUX_VERSION-4.14 = .162
|
||||
LINUX_VERSION-4.19 = .93
|
||||
|
||||
LINUX_KERNEL_HASH-4.9.208 = b7ad1c9841d671d026c55a4c91c77205f8b488ca5f980f838591c68662e0525a
|
||||
LINUX_KERNEL_HASH-4.14.162 = f65170224cd4359ce8b2793b492bd8127abdd0b91350484e001bce13f0c98b4b
|
||||
LINUX_KERNEL_HASH-4.19.93 = 2af5d23f82a8bf69a6e4d8407579293b7beeebd3054dc464d48bdb5d2d7f5834
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
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
|
||||
}
|
||||
+'
|
@ -1,23 +0,0 @@
|
||||
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
|
||||
@@ -407,8 +407,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
|
||||
|
@ -1,143 +0,0 @@
|
||||
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
|
||||
@@ -351,3 +351,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
|
||||
@@ -605,4 +605,23 @@ unsigned long read_word_at_a_time(const
|
||||
# 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 */
|
@ -1,88 +0,0 @@
|
||||
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,
|
@ -1,543 +0,0 @@
|
||||
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");
|
@ -1,499 +0,0 @@
|
||||
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;
|
||||
@@ -1070,12 +1103,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) &&
|
||||
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;
|
||||
}
|
||||
@@ -1090,6 +1125,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;
|
||||
@@ -1144,10 +1180,11 @@ static int br_ip6_multicast_mld2_report(
|
||||
grec->grec_type == MLD2_MODE_IS_INCLUDE) &&
|
||||
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;
|
||||
}
|
||||
@@ -1462,7 +1499,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;
|
||||
@@ -1486,7 +1524,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;
|
||||
|
||||
if (p->flags & MDB_PG_FLAGS_PERMANENT)
|
||||
@@ -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,
|
@ -1,69 +0,0 @@
|
||||
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);
|
@ -1,90 +0,0 @@
|
||||
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
|
||||
@@ -790,10 +790,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
|
||||
@@ -814,7 +814,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 :
|
||||
@@ -828,15 +828,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);
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
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
|
||||
@@ -866,6 +866,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.
|
||||
@@ -883,11 +884,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);
|
@ -1,71 +0,0 @@
|
||||
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
|
||||
@@ -773,19 +773,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);
|
||||
}
|
||||
}
|
||||
@@ -890,7 +890,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;
|
||||
@@ -2237,6 +2237,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;
|
||||
|
@ -1,38 +0,0 @@
|
||||
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
|
||||
@@ -886,6 +886,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;
|
||||
@@ -898,8 +899,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;
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
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
|
||||
@@ -2142,6 +2142,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
|
@ -1,55 +0,0 @@
|
||||
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
|
||||
@@ -1974,26 +1974,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,
|
@ -1,158 +0,0 @@
|
||||
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
|
@ -1,176 +0,0 @@
|
||||
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
|
||||
@@ -773,14 +773,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);
|
||||
@@ -803,16 +804,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);
|
||||
@@ -884,7 +884,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;
|
||||
|
||||
@@ -892,7 +892,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;
|
||||
|
||||
@@ -2151,7 +2151,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.
|
||||
@@ -2249,8 +2249,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;
|
||||
|
||||
@@ -3569,8 +3569,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
|
||||
@@ -327,7 +327,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);
|
||||
@@ -610,7 +610,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;
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
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
|
||||
@@ -775,6 +775,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 &&
|
@ -1,85 +0,0 @@
|
||||
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
|
||||
@@ -2484,6 +2484,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
|
||||
@@ -1607,7 +1607,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,
|
||||
@@ -2138,7 +2138,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;
|
||||
|
@ -1,82 +0,0 @@
|
||||
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;
|
@ -1,66 +0,0 @@
|
||||
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
|
||||
|
@ -1,75 +0,0 @@
|
||||
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
|
||||
@@ -529,6 +529,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)
|
||||
{
|
@ -1,63 +0,0 @@
|
||||
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
|
||||
|
@ -1,81 +0,0 @@
|
||||
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
|
@ -1,180 +0,0 @@
|
||||
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;
|
@ -1,112 +0,0 @@
|
||||
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;
|
@ -1,22 +0,0 @@
|
||||
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) },
|
@ -1,42 +0,0 @@
|
||||
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))) {
|
@ -1,28 +0,0 @@
|
||||
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) },
|
@ -1,23 +0,0 @@
|
||||
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) },
|
@ -1,26 +0,0 @@
|
||||
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) },
|
||||
|
@ -1,29 +0,0 @@
|
||||
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) },
|
@ -1,25 +0,0 @@
|
||||
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) },
|
@ -1,312 +0,0 @@
|
||||
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),
|
||||
};
|
||||
|
||||
/**
|
@ -1,28 +0,0 @@
|
||||
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);
|
@ -1,33 +0,0 @@
|
||||
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);
|
@ -1,187 +0,0 @@
|
||||
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. */
|
@ -1,150 +0,0 @@
|
||||
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;
|
@ -1,26 +0,0 @@
|
||||
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) },
|
@ -1,35 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
@ -1,28 +0,0 @@
|
||||
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)
|
@ -1,34 +0,0 @@
|
||||
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
|
@ -1,73 +0,0 @@
|
||||
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 u32 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,
|
@ -1,50 +0,0 @@
|
||||
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)
|
@ -1,29 +0,0 @@
|
||||
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) },
|
@ -1,77 +0,0 @@
|
||||
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);
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
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
|
||||
@@ -832,6 +832,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)
|
||||
@@ -872,16 +893,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
|
@ -1,119 +0,0 @@
|
||||
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)
|
||||
@@ -686,6 +660,48 @@ err_remove_part:
|
||||
}
|
||||
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;
|
||||
@@ -697,12 +713,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);
|
@ -1,45 +0,0 @@
|
||||
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
|
||||
@@ -671,6 +671,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;
|
||||
@@ -711,8 +713,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;
|
||||
}
|
@ -1,606 +0,0 @@
|
||||
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);
|
||||
|
||||
@@ -694,7 +700,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;
|
||||
@@ -711,7 +717,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;
|
||||
@@ -958,6 +964,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);
|
@ -1,96 +0,0 @@
|
||||
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;
|
||||
@@ -675,8 +675,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);
|
||||
@@ -691,16 +700,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;
|
||||
@@ -710,14 +719,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;
|
||||
@@ -964,6 +973,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);
|
@ -1,110 +0,0 @@
|
||||
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)
|
||||
@@ -778,6 +807,8 @@ int add_mtd_partitions(struct mtd_info *
|
||||
}
|
||||
|
||||
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 */
|
@ -1,320 +0,0 @@
|
||||
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");
|
@ -1,74 +0,0 @@
|
||||
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);
|
@ -1,93 +0,0 @@
|
||||
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;
|
||||
|
@ -1,200 +0,0 @@
|
||||
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"
|
||||
|
||||
@@ -911,6 +912,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
|
||||
@@ -942,19 +1029,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);
|
@ -1,74 +0,0 @@
|
||||
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
|
||||
@@ -957,7 +957,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");
|
||||
@@ -977,7 +977,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");
|
@ -1,44 +0,0 @@
|
||||
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,
|
@ -1,168 +0,0 @@
|
||||
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,
|
||||
@@ -998,30 +985,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;
|
||||
|
||||
@@ -1035,7 +1019,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);
|
||||
@@ -1046,13 +1030,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
|
@ -1,70 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
@ -1,85 +0,0 @@
|
||||
--- 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;
|
@ -1,139 +0,0 @@
|
||||
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);
|
@ -1,261 +0,0 @@
|
||||
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)
|
@ -1,53 +0,0 @@
|
||||
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 */
|
@ -1,50 +0,0 @@
|
||||
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) {
|
@ -1,47 +0,0 @@
|
||||
--- 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;
|
@ -1,34 +0,0 @@
|
||||
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,
|
@ -1,94 +0,0 @@
|
||||
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
|
||||
|
@ -1,41 +0,0 @@
|
||||
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
|
||||
|
@ -1,176 +0,0 @@
|
||||
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. */
|
@ -1,74 +0,0 @@
|
||||
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;
|
@ -1,87 +0,0 @@
|
||||
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 */
|
@ -1,125 +0,0 @@
|
||||
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
|
@ -1,38 +0,0 @@
|
||||
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
|
@ -1,68 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
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)
|
@ -1,89 +0,0 @@
|
||||
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
|
@ -1,121 +0,0 @@
|
||||
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,
|
@ -1,54 +0,0 @@
|
||||
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)
|
@ -1,120 +0,0 @@
|
||||
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);
|
@ -1,60 +0,0 @@
|
||||
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;
|
||||
}
|
@ -1,365 +0,0 @@
|
||||
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");
|
@ -1,173 +0,0 @@
|
||||
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");
|
@ -1,106 +0,0 @@
|
||||
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,
|
@ -1,84 +0,0 @@
|
||||
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;
|
@ -1,69 +0,0 @@
|
||||
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,
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
||||
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));
|
@ -1,31 +0,0 @@
|
||||
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)
|
@ -1,79 +0,0 @@
|
||||
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
|
||||
@@ -906,7 +906,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);
|
||||
@@ -917,6 +917,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);
|
@ -1,51 +0,0 @@
|
||||
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)
|
@ -1,32 +0,0 @@
|
||||
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,
|
@ -1,46 +0,0 @@
|
||||
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)
|
@ -1,588 +0,0 @@
|
||||
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");
|
@ -1,47 +0,0 @@
|
||||
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);
|
||||
|
@ -1,45 +0,0 @@
|
||||
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);
|
||||
|
@ -1,102 +0,0 @@
|
||||
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)
|
||||
@@ -795,8 +779,8 @@ int add_mtd_partitions(struct mtd_info *
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -877,6 +861,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,
|
||||
@@ -947,7 +937,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)
|
||||
@@ -1010,7 +1002,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++) {
|
||||
/*
|
File diff suppressed because it is too large
Load Diff
@ -1,214 +0,0 @@
|
||||
From a779a482fb9b9f8fcdf8b2519c789b4b9bb5dd05 Mon Sep 17 00:00:00 2001
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 7 Jul 2017 16:56:48 +0200
|
||||
Subject: build: add a hack for removing non-essential module info
|
||||
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
include/linux/module.h | 13 ++++++++-----
|
||||
include/linux/moduleparam.h | 15 ++++++++++++---
|
||||
init/Kconfig | 7 +++++++
|
||||
kernel/module.c | 5 ++++-
|
||||
scripts/mod/modpost.c | 12 ++++++++++++
|
||||
5 files changed, 43 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/include/linux/module.h
|
||||
+++ b/include/linux/module.h
|
||||
@@ -159,6 +159,7 @@ extern void cleanup_module(void);
|
||||
|
||||
/* Generic info of form tag = "info" */
|
||||
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
|
||||
+#define MODULE_INFO_STRIP(tag, info) __MODULE_INFO_STRIP(tag, tag, info)
|
||||
|
||||
/* For userspace: you can also call me... */
|
||||
#define MODULE_ALIAS(_alias) MODULE_INFO(alias, _alias)
|
||||
@@ -202,12 +203,12 @@ extern void cleanup_module(void);
|
||||
* Author(s), use "Name <email>" or just "Name", for multiple
|
||||
* authors use multiple MODULE_AUTHOR() statements/lines.
|
||||
*/
|
||||
-#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
|
||||
+#define MODULE_AUTHOR(_author) MODULE_INFO_STRIP(author, _author)
|
||||
|
||||
/* What your module does. */
|
||||
-#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
|
||||
+#define MODULE_DESCRIPTION(_description) MODULE_INFO_STRIP(description, _description)
|
||||
|
||||
-#ifdef MODULE
|
||||
+#if defined(MODULE) && !defined(CONFIG_MODULE_STRIPPED)
|
||||
/* Creates an alias so file2alias.c can find device table. */
|
||||
#define MODULE_DEVICE_TABLE(type, name) \
|
||||
extern const typeof(name) __mod_##type##__##name##_device_table \
|
||||
@@ -234,7 +235,9 @@ extern const typeof(name) __mod_##type##
|
||||
*/
|
||||
|
||||
#if defined(MODULE) || !defined(CONFIG_SYSFS)
|
||||
-#define MODULE_VERSION(_version) MODULE_INFO(version, _version)
|
||||
+#define MODULE_VERSION(_version) MODULE_INFO_STRIP(version, _version)
|
||||
+#elif defined(CONFIG_MODULE_STRIPPED)
|
||||
+#define MODULE_VERSION(_version) __MODULE_INFO_DISABLED(version)
|
||||
#else
|
||||
#define MODULE_VERSION(_version) \
|
||||
static struct module_version_attribute ___modver_attr = { \
|
||||
@@ -256,7 +259,7 @@ extern const typeof(name) __mod_##type##
|
||||
/* Optional firmware file (or files) needed by the module
|
||||
* format is simply firmware file name. Multiple firmware
|
||||
* files require multiple MODULE_FIRMWARE() specifiers */
|
||||
-#define MODULE_FIRMWARE(_firmware) MODULE_INFO(firmware, _firmware)
|
||||
+#define MODULE_FIRMWARE(_firmware) MODULE_INFO_STRIP(firmware, _firmware)
|
||||
|
||||
struct notifier_block;
|
||||
|
||||
--- a/include/linux/moduleparam.h
|
||||
+++ b/include/linux/moduleparam.h
|
||||
@@ -16,6 +16,16 @@
|
||||
/* Chosen so that structs with an unsigned long line up. */
|
||||
#define MAX_PARAM_PREFIX_LEN (64 - sizeof(unsigned long))
|
||||
|
||||
+/* This struct is here for syntactic coherency, it is not used */
|
||||
+#define __MODULE_INFO_DISABLED(name) \
|
||||
+ struct __UNIQUE_ID(name) {}
|
||||
+
|
||||
+#ifdef CONFIG_MODULE_STRIPPED
|
||||
+#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO_DISABLED(name)
|
||||
+#else
|
||||
+#define __MODULE_INFO_STRIP(tag, name, info) __MODULE_INFO(tag, name, info)
|
||||
+#endif
|
||||
+
|
||||
#ifdef MODULE
|
||||
#define __MODULE_INFO(tag, name, info) \
|
||||
static const char __UNIQUE_ID(name)[] \
|
||||
@@ -23,8 +33,7 @@ static const char __UNIQUE_ID(name)[]
|
||||
= __stringify(tag) "=" info
|
||||
#else /* !MODULE */
|
||||
/* This struct is here for syntactic coherency, it is not used */
|
||||
-#define __MODULE_INFO(tag, name, info) \
|
||||
- struct __UNIQUE_ID(name) {}
|
||||
+#define __MODULE_INFO(tag, name, info) __MODULE_INFO_DISABLED(name)
|
||||
#endif
|
||||
#define __MODULE_PARM_TYPE(name, _type) \
|
||||
__MODULE_INFO(parmtype, name##type, #name ":" _type)
|
||||
@@ -32,7 +41,7 @@ static const char __UNIQUE_ID(name)[]
|
||||
/* One for each parameter, describing how to use it. Some files do
|
||||
multiple of these per line, so can't just use MODULE_INFO. */
|
||||
#define MODULE_PARM_DESC(_parm, desc) \
|
||||
- __MODULE_INFO(parm, _parm, #_parm ":" desc)
|
||||
+ __MODULE_INFO_STRIP(parm, _parm, #_parm ":" desc)
|
||||
|
||||
struct kernel_param;
|
||||
|
||||
--- a/init/Kconfig
|
||||
+++ b/init/Kconfig
|
||||
@@ -2122,6 +2122,13 @@ config TRIM_UNUSED_KSYMS
|
||||
|
||||
If unsure, or if you need to build out-of-tree modules, say N.
|
||||
|
||||
+config MODULE_STRIPPED
|
||||
+ bool "Reduce module size"
|
||||
+ depends on MODULES
|
||||
+ help
|
||||
+ Remove module parameter descriptions, author info, version, aliases,
|
||||
+ device tables, etc.
|
||||
+
|
||||
endif # MODULES
|
||||
|
||||
config MODULES_TREE_LOOKUP
|
||||
--- a/kernel/module.c
|
||||
+++ b/kernel/module.c
|
||||
@@ -2970,9 +2970,11 @@ static struct module *setup_load_info(st
|
||||
|
||||
static int check_modinfo(struct module *mod, struct load_info *info, int flags)
|
||||
{
|
||||
- const char *modmagic = get_modinfo(info, "vermagic");
|
||||
int err;
|
||||
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
+ const char *modmagic = get_modinfo(info, "vermagic");
|
||||
+
|
||||
if (flags & MODULE_INIT_IGNORE_VERMAGIC)
|
||||
modmagic = NULL;
|
||||
|
||||
@@ -2993,6 +2995,7 @@ static int check_modinfo(struct module *
|
||||
mod->name);
|
||||
add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK);
|
||||
}
|
||||
+#endif
|
||||
|
||||
check_modinfo_retpoline(mod, info);
|
||||
|
||||
--- a/scripts/mod/modpost.c
|
||||
+++ b/scripts/mod/modpost.c
|
||||
@@ -1979,7 +1979,9 @@ static void read_symbols(char *modname)
|
||||
symname = remove_dot(info.strtab + sym->st_name);
|
||||
|
||||
handle_modversions(mod, &info, sym, symname);
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
handle_moddevtable(mod, &info, sym, symname);
|
||||
+#endif
|
||||
}
|
||||
if (!is_vmlinux(modname) ||
|
||||
(is_vmlinux(modname) && vmlinux_section_warnings))
|
||||
@@ -2123,7 +2125,9 @@ static void add_header(struct buffer *b,
|
||||
buf_printf(b, "#include <linux/vermagic.h>\n");
|
||||
buf_printf(b, "#include <linux/compiler.h>\n");
|
||||
buf_printf(b, "\n");
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
|
||||
+#endif
|
||||
buf_printf(b, "\n");
|
||||
buf_printf(b, "__visible struct module __this_module\n");
|
||||
buf_printf(b, "__attribute__((section(\".gnu.linkonce.this_module\"))) = {\n");
|
||||
@@ -2140,24 +2144,30 @@ static void add_header(struct buffer *b,
|
||||
|
||||
static void add_intree_flag(struct buffer *b, int is_intree)
|
||||
{
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
if (is_intree)
|
||||
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
|
||||
+#endif
|
||||
}
|
||||
|
||||
/* Cannot check for assembler */
|
||||
static void add_retpoline(struct buffer *b)
|
||||
{
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
buf_printf(b, "\n#ifdef RETPOLINE\n");
|
||||
buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n");
|
||||
buf_printf(b, "#endif\n");
|
||||
+#endif
|
||||
}
|
||||
|
||||
static void add_staging_flag(struct buffer *b, const char *name)
|
||||
{
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
static const char *staging_dir = "drivers/staging";
|
||||
|
||||
if (strncmp(staging_dir, name, strlen(staging_dir)) == 0)
|
||||
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
|
||||
+#endif
|
||||
}
|
||||
|
||||
/* In kernel, this size is defined in linux/module.h;
|
||||
@@ -2261,11 +2271,13 @@ static void add_depends(struct buffer *b
|
||||
|
||||
static void add_srcversion(struct buffer *b, struct module *mod)
|
||||
{
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
if (mod->srcversion[0]) {
|
||||
buf_printf(b, "\n");
|
||||
buf_printf(b, "MODULE_INFO(srcversion, \"%s\");\n",
|
||||
mod->srcversion);
|
||||
}
|
||||
+#endif
|
||||
}
|
||||
|
||||
static void write_if_changed(struct buffer *b, const char *fname)
|
||||
@@ -2500,7 +2512,9 @@ int main(int argc, char **argv)
|
||||
add_staging_flag(&buf, mod->name);
|
||||
err |= add_versions(&buf, mod);
|
||||
add_depends(&buf, mod, modules);
|
||||
+#ifndef CONFIG_MODULE_STRIPPED
|
||||
add_moddevtable(&buf, mod);
|
||||
+#endif
|
||||
add_srcversion(&buf, mod);
|
||||
|
||||
sprintf(fname, "%s.mod.c", mod->name);
|
@ -1,44 +0,0 @@
|
||||
From c9ef4ab0f54356ee9f91d9676ea0ec123840ddc7 Mon Sep 17 00:00:00 2001
|
||||
From: Felix Fietkau <nbd@nbd.name>
|
||||
Date: Fri, 7 Jul 2017 16:57:33 +0200
|
||||
Subject: kernel: do not build modules.order
|
||||
|
||||
It is not needed for anything on the system and skipping this saves some
|
||||
build time, especially in cases where there is nothing to do.
|
||||
|
||||
lede-commit: afc1675833a7bf5df094f59f7250369520646d04
|
||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||||
---
|
||||
Makefile | 2 --
|
||||
scripts/Makefile.build | 2 +-
|
||||
2 files changed, 1 insertion(+), 3 deletions(-)
|
||||
|
||||
--- a/Makefile
|
||||
+++ b/Makefile
|
||||
@@ -1240,7 +1240,6 @@ all: modules
|
||||
|
||||
PHONY += modules
|
||||
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
|
||||
- $(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
|
||||
@$(kecho) ' Building modules, stage 2.';
|
||||
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
|
||||
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
|
||||
@@ -1270,7 +1269,6 @@ _modinst_:
|
||||
rm -f $(MODLIB)/build ; \
|
||||
ln -s $(CURDIR) $(MODLIB)/build ; \
|
||||
fi
|
||||
- @cp -f $(objtree)/modules.order $(MODLIB)/
|
||||
@cp -f $(objtree)/modules.builtin $(MODLIB)/
|
||||
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modinst
|
||||
|
||||
--- a/scripts/Makefile.build
|
||||
+++ b/scripts/Makefile.build
|
||||
@@ -93,7 +93,7 @@ modorder-target := $(obj)/modules.order
|
||||
# We keep a list of all modules in $(MODVERDIR)
|
||||
|
||||
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
|
||||
- $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
|
||||
+ $(if $(KBUILD_MODULES),$(obj-m)) \
|
||||
$(subdir-ym) $(always)
|
||||
@:
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user